General source code refactoring
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
import { mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import composition from "./compose.cjs";
|
||||
|
||||
const rootDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
||||
const srcDir = path.join(rootDir, "src");
|
||||
const distDir = path.join(rootDir, "dist");
|
||||
const contentScript = await readFile(path.join(srcDir, "content.js"), "utf8");
|
||||
const backgroundScript = await readFile(path.join(srcDir, "background.js"), "utf8");
|
||||
const contentScript = composition.composeScript("content");
|
||||
const backgroundScript = composition.composeScript("background");
|
||||
const checkOnly = process.argv.includes("--check");
|
||||
|
||||
const baseManifest = {
|
||||
@@ -70,6 +70,11 @@ for (const target of targets) {
|
||||
JSON.parse(manifestJson);
|
||||
|
||||
if (checkOnly) {
|
||||
const expected = { "manifest.json": manifestJson, "content.js": contentScript, "background.js": backgroundScript };
|
||||
for (const [file, contents] of Object.entries(expected)) {
|
||||
const actual = await readFile(path.join(distDir, target.name, file), "utf8");
|
||||
if (actual !== contents) throw new Error(`Stale output: dist/${target.name}/${file}. Run npm run build.`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
29
scripts/compose.cjs
Normal file
29
scripts/compose.cjs
Normal file
@@ -0,0 +1,29 @@
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const vm = require("node:vm");
|
||||
const sources = require("./sources.cjs");
|
||||
|
||||
const root = path.resolve(__dirname, "..");
|
||||
|
||||
/** Compose classic scripts at build time; never load or evaluate code at runtime. */
|
||||
function composeScript(target, { bootstrap = true } = {}) {
|
||||
const files = sources[target];
|
||||
if (!files) throw new Error(`Unknown script target: ${target}`);
|
||||
if (new Set(files).size !== files.length) throw new Error(`Duplicate source in ${target}`);
|
||||
const parts = files.map(file => {
|
||||
const source = fs.readFileSync(path.join(root, file), "utf8");
|
||||
new vm.Script(source, { filename: file });
|
||||
return `// Source: ${file}\n${source}`;
|
||||
});
|
||||
if (target === "content") {
|
||||
const css = fs.readFileSync(path.join(root, "src/content/styles/redwood.css"), "utf8");
|
||||
parts.unshift(`const redwoodStyles = ${JSON.stringify(css)};`);
|
||||
}
|
||||
const factory = target === "content" ? "createContentApplication" : "createBackgroundApplication";
|
||||
const ending = bootstrap ? `${factory}().start();` : `return ${factory};`;
|
||||
const output = `(function () {\n "use strict";\n${parts.join("\n")}\n${ending}\n})();\n`;
|
||||
new vm.Script(output, { filename: `${target}.js` });
|
||||
return output;
|
||||
}
|
||||
|
||||
module.exports = { composeScript, sources };
|
||||
38
scripts/preview.mjs
Normal file
38
scripts/preview.mjs
Normal file
@@ -0,0 +1,38 @@
|
||||
import http from "node:http";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import composition from "./compose.cjs";
|
||||
|
||||
// Development-only routes. Never expose the workspace or forward API requests.
|
||||
const routes = new Map([
|
||||
["/", ["../tests/fixtures/extension.html", "text/html"]],
|
||||
["/extension.js", ["../tests/fixtures/extension.js", "text/javascript"]],
|
||||
["/consumption-trend.html", ["../tests/fixtures/consumption-trend.html", "text/html"]]
|
||||
]);
|
||||
const server = http.createServer(async (request, response) => {
|
||||
const pathname = new URL(request.url, "http://127.0.0.1").pathname;
|
||||
response.setHeader("Cache-Control", "no-store");
|
||||
response.setHeader("X-Content-Type-Options", "nosniff");
|
||||
if (request.method !== "GET") {
|
||||
response.writeHead(405).end();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if (pathname === "/fixture-runtime.js") {
|
||||
response.setHeader("Content-Type", "text/javascript");
|
||||
response.end(`window.createFixtureApplication = ${composition.composeScript("content", { bootstrap: false })}`);
|
||||
return;
|
||||
}
|
||||
const route = routes.get(pathname);
|
||||
if (!route) {
|
||||
response.writeHead(404).end();
|
||||
return;
|
||||
}
|
||||
response.setHeader("Content-Type", `${route[1]}; charset=utf-8`);
|
||||
response.end(await readFile(new URL(route[0], import.meta.url)));
|
||||
} catch {
|
||||
response.writeHead(500).end("Fixture could not be loaded.");
|
||||
}
|
||||
});
|
||||
server.listen(Number(process.env.PORT) || 0, "127.0.0.1", () => {
|
||||
console.log(`Synthetic extension preview: http://127.0.0.1:${server.address().port}/`);
|
||||
});
|
||||
72
scripts/sources.cjs
Normal file
72
scripts/sources.cjs
Normal file
@@ -0,0 +1,72 @@
|
||||
module.exports = {
|
||||
"content": [
|
||||
"src/shared/messages.js",
|
||||
"src/content/contracts.js",
|
||||
"src/auth/session-state.js",
|
||||
"src/auth/sales-common-state.js",
|
||||
"src/features/opportunities/query-state.js",
|
||||
"src/features/opportunities/table-state.js",
|
||||
"src/features/opportunities/filter-state.js",
|
||||
"src/features/preferences/state.js",
|
||||
"src/features/products/content-state.js",
|
||||
"src/features/ai-summary/state.js",
|
||||
"src/features/consumption/plans-state.js",
|
||||
"src/features/consumption/trend-state.js",
|
||||
"src/features/opportunities/dashboard-state.js",
|
||||
"src/features/debug/state.js",
|
||||
"src/features/opportunities/columns.js",
|
||||
"src/content/fusion-page.js",
|
||||
"src/content/modal.js",
|
||||
"src/content/theme.js",
|
||||
"src/content/icons.js",
|
||||
"src/content/svg.js",
|
||||
"src/content/styles.js",
|
||||
"src/auth/content-session.js",
|
||||
"src/auth/sales-common.js",
|
||||
"src/auth/token-relay.js",
|
||||
"src/auth/xsrf-content.js",
|
||||
"src/platform/content-messages.js",
|
||||
"src/features/opportunities/controller.js",
|
||||
"src/services/fusion/opportunities.js",
|
||||
"src/features/opportunities/fiscal-periods.js",
|
||||
"src/features/opportunities/table.js",
|
||||
"src/features/opportunities/dashboard.js",
|
||||
"src/features/opportunities/filters.js",
|
||||
"src/features/opportunities/formatters.js",
|
||||
"src/features/opportunities/links.js",
|
||||
"src/features/preferences/storage.js",
|
||||
"src/features/preferences/view.js",
|
||||
"src/features/products/content-jobs.js",
|
||||
"src/features/products/details.js",
|
||||
"src/features/ai-summary/content.js",
|
||||
"src/features/consumption/plans-action.js",
|
||||
"src/features/consumption/plans-view.js",
|
||||
"src/features/consumption/trend-controller.js",
|
||||
"src/features/consumption/chart.js",
|
||||
"src/features/consumption/model.js",
|
||||
"src/features/debug/content-log.js",
|
||||
"src/features/debug/content-view.js",
|
||||
"src/content/main.js",
|
||||
"src/content/application.js"
|
||||
],
|
||||
"background": [
|
||||
"src/shared/messages.js",
|
||||
"src/background/contracts.js",
|
||||
"src/features/products/cache-state.js",
|
||||
"src/auth/consumer-session.js",
|
||||
"src/auth/spa-credentials.js",
|
||||
"src/services/spa/accounts.js",
|
||||
"src/services/spa/consumption.js",
|
||||
"src/services/spa/response-shapes.js",
|
||||
"src/services/http/background-json.js",
|
||||
"src/features/products/background-jobs.js",
|
||||
"src/features/products/cache.js",
|
||||
"src/services/fusion/products.js",
|
||||
"src/auth/xsrf-recovery.js",
|
||||
"src/auth/xsrf-cookies.js",
|
||||
"src/platform/background-api.js",
|
||||
"src/background/main.js",
|
||||
"src/background/messages.js",
|
||||
"src/background/application.js"
|
||||
]
|
||||
};
|
||||
13
scripts/test.cjs
Normal file
13
scripts/test.cjs
Normal file
@@ -0,0 +1,13 @@
|
||||
const fs = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const { spawnSync } = require("node:child_process");
|
||||
|
||||
const root = path.resolve(__dirname, "..");
|
||||
const files = fs.readdirSync(path.join(root, "tests")).filter(name => name.endsWith(".test.cjs")).sort();
|
||||
let failed = false;
|
||||
for (const file of files) {
|
||||
const result = spawnSync(process.execPath, [path.join(root, "tests", file)], { cwd: root, stdio: "inherit" });
|
||||
if (result.error) console.error(result.error.message);
|
||||
if (result.status !== 0) failed = true;
|
||||
}
|
||||
process.exitCode = failed ? 1 : 0;
|
||||
Reference in New Issue
Block a user