first commit
This commit is contained in:
commit
db5762abd4
687 files changed
+171307
No files matched your search
@@ -0,0 +1,649 @@
|
||||
/** One-time source reconstruction. Input is explicit; generated app has no runtime link to it. */
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { parse } from "@babel/parser";
|
||||
import generator from "@babel/generator";
|
||||
import traverser from "@babel/traverse";
|
||||
import * as t from "@babel/types";
|
||||
import { format } from "prettier";
|
||||
|
||||
const generate = generator.default;
|
||||
const traverse = traverser.default;
|
||||
const input = path.resolve(process.argv[2] || "MISSING_INPUT");
|
||||
const root = path.resolve(import.meta.dirname, "..");
|
||||
if (!fs.existsSync(path.join(input, "server/chunks/virtual/precomputed.mjs")))
|
||||
throw Error("Pass the active .output directory");
|
||||
if (fs.existsSync(path.join(root, "app/pages")))
|
||||
throw Error(
|
||||
"Refusing to overwrite editable source; this importer is only for a new empty project",
|
||||
);
|
||||
const pc = (
|
||||
await import(
|
||||
pathToFileURL(path.join(input, "server/chunks/virtual/precomputed.mjs"))
|
||||
)
|
||||
).default;
|
||||
const chunkDir = path.join(input, "public/_nuxt");
|
||||
const asts = new Map();
|
||||
const files = [
|
||||
...new Set(
|
||||
Object.values(pc.modules)
|
||||
.filter((v) => v.resourceType === "script")
|
||||
.map((v) => v.file),
|
||||
),
|
||||
];
|
||||
for (const file of files)
|
||||
asts.set(
|
||||
file,
|
||||
parse(fs.readFileSync(path.join(chunkDir, file), "utf8"), {
|
||||
sourceType: "module",
|
||||
}),
|
||||
);
|
||||
const key = (file, name) => `${file}:${name}`;
|
||||
const textValue = (n) =>
|
||||
t.isTemplateLiteral(n) && n.expressions.length === 0
|
||||
? n.quasis[0].value.cooked
|
||||
: n?.value;
|
||||
function resolve(file, exported, seen = new Set()) {
|
||||
if (seen.has(key(file, exported)))
|
||||
throw Error(`Cyclic export ${file}:${exported}`);
|
||||
seen.add(key(file, exported));
|
||||
const ast = asts.get(file);
|
||||
if (!ast) throw Error(`Missing ${file}`);
|
||||
const imports = new Map();
|
||||
for (const n of ast.program.body)
|
||||
if (t.isImportDeclaration(n))
|
||||
for (const s of n.specifiers)
|
||||
imports.set(s.local.name, {
|
||||
file: path.basename(n.source.value),
|
||||
name: t.isImportDefaultSpecifier(s)
|
||||
? "default"
|
||||
: s.imported?.name || s.imported?.value,
|
||||
});
|
||||
for (const n of ast.program.body) {
|
||||
if (t.isExportDefaultDeclaration(n) && exported === "default")
|
||||
return {
|
||||
file,
|
||||
name: exported,
|
||||
local: t.isIdentifier(n.declaration) ? n.declaration.name : null,
|
||||
};
|
||||
if (t.isExportNamedDeclaration(n))
|
||||
for (const s of n.specifiers)
|
||||
if ((s.exported.name || s.exported.value) === exported) {
|
||||
const imp = n.source
|
||||
? { file: path.basename(n.source.value), name: s.local.name }
|
||||
: imports.get(s.local.name);
|
||||
if (imp?.file.endsWith(".js"))
|
||||
return resolve(imp.file, imp.name, seen);
|
||||
return { file, name: exported, local: s.local.name };
|
||||
}
|
||||
}
|
||||
throw Error(`No export ${file}:${exported}`);
|
||||
}
|
||||
const replacements = new Map();
|
||||
const replace = (file, alias, source, imported) =>
|
||||
replacements.set(key(file, alias), { source, imported });
|
||||
const vue = {
|
||||
$: "watchSyncEffect",
|
||||
A: "nextTick",
|
||||
B: "provide",
|
||||
Bt: "toDisplayString",
|
||||
C: "guardReactiveProps",
|
||||
D: "isVNode",
|
||||
E: "inject",
|
||||
F: "onErrorCaptured",
|
||||
G: "resolveDirective",
|
||||
H: "renderList",
|
||||
I: "onMounted",
|
||||
J: "useId",
|
||||
K: "resolveDynamicComponent",
|
||||
L: "onUnmounted",
|
||||
Lt: "normalizeClass",
|
||||
M: "onBeforeMount",
|
||||
N: "onBeforeUnmount",
|
||||
O: "mergeModels",
|
||||
P: "onDeactivated",
|
||||
Q: "watchEffect",
|
||||
R: "onUpdated",
|
||||
Rt: "normalizeProps",
|
||||
S: "getCurrentInstance",
|
||||
T: "hasInjectionContext",
|
||||
U: "renderSlot",
|
||||
V: "queuePostFlushCb",
|
||||
W: "resolveComponent",
|
||||
X: "warn",
|
||||
Y: "useModel",
|
||||
Z: "watch",
|
||||
_: "createStaticVNode",
|
||||
_t: "toValue",
|
||||
a: "Suspense",
|
||||
at: "isReadonly",
|
||||
b: "defineAsyncComponent",
|
||||
c: "cloneVNode",
|
||||
ct: "onScopeDispose",
|
||||
d: "createBlock",
|
||||
dt: "ref",
|
||||
et: "withCtx",
|
||||
f: "createCommentVNode",
|
||||
ft: "shallowReactive",
|
||||
g: "createSlots",
|
||||
gt: "toRefs",
|
||||
h: "createRenderer",
|
||||
ht: "toRef",
|
||||
i: "KeepAlive",
|
||||
it: "isReactive",
|
||||
j: "onActivated",
|
||||
k: "mergeProps",
|
||||
l: "computed",
|
||||
lt: "reactive",
|
||||
m: "createHydrationRenderer",
|
||||
mt: "toRaw",
|
||||
n: "BaseTransitionPropsValidators",
|
||||
nt: "effectScope",
|
||||
o: "Teleport",
|
||||
ot: "isRef",
|
||||
p: "createElementBlock",
|
||||
pt: "shallowRef",
|
||||
q: "toHandlers",
|
||||
r: "Fragment",
|
||||
rt: "getCurrentScope",
|
||||
s: "callWithAsyncErrorHandling",
|
||||
st: "markRaw",
|
||||
t: "BaseTransition",
|
||||
tt: "withDirectives",
|
||||
u: "createElementVNode",
|
||||
ut: "readonly",
|
||||
v: "createTextVNode",
|
||||
vt: "unref",
|
||||
w: "h",
|
||||
x: "defineComponent",
|
||||
y: "createVNode",
|
||||
z: "openBlock",
|
||||
zt: "normalizeStyle",
|
||||
};
|
||||
const shared = {
|
||||
At: "isPlainObject",
|
||||
Ct: "includeBooleanAttr",
|
||||
Dt: "isModelListener",
|
||||
Et: "isFunction",
|
||||
Ft: "looseEqual",
|
||||
It: "looseToNumber",
|
||||
Mt: "isSpecialBooleanAttr",
|
||||
Nt: "isString",
|
||||
Ot: "isObject",
|
||||
Pt: "isSymbol",
|
||||
St: "hyphenate",
|
||||
Tt: "isArray",
|
||||
Vt: "toNumber",
|
||||
bt: "capitalize",
|
||||
jt: "isPromise",
|
||||
kt: "isOn",
|
||||
wt: "invokeArrayFns",
|
||||
xt: "extend",
|
||||
yt: "camelize",
|
||||
};
|
||||
for (const [a, n] of Object.entries(vue)) replace("CaDPEW5H.js", a, "vue", n);
|
||||
for (const [a, n] of Object.entries(shared))
|
||||
replace("CaDPEW5H.js", a, "@vue/shared", n);
|
||||
for (const [a, n] of Object.entries({
|
||||
a: "vModelText",
|
||||
c: "withModifiers",
|
||||
i: "vModelRadio",
|
||||
n: "createApp",
|
||||
o: "vShow",
|
||||
r: "createSSRApp",
|
||||
s: "withKeys",
|
||||
t: "Transition",
|
||||
}))
|
||||
replace("CHOHVZjb.js", a, "vue", n);
|
||||
for (const [file, map] of Object.entries({
|
||||
"BXrXAUMD.js": { a: "useNuxtApp", o: "useRuntimeConfig" },
|
||||
"DNcWRfqH.js": { f: "useRoute", p: "useRouter", u: "navigateTo" },
|
||||
"BJ9CQb77.js": { n: "useHead" },
|
||||
"ClFYPW8t.js": { t: "useState" },
|
||||
}))
|
||||
for (const [a, n] of Object.entries(map)) replace(file, a, "#app", n);
|
||||
replace("Cq-ZALh2.js", "D", "vue-router", "onBeforeRouteLeave");
|
||||
replace("Gudm9e0k.js", "t", "#components", "NuxtLink");
|
||||
replace("BnCkphz4.js", "t", "#components", "Icon");
|
||||
replace("uBIymjUX.js", "t", "~/utils/load-module.js", "loadModule");
|
||||
replace("BLvPXbVl.js", "_", "pinia", "createPinia");
|
||||
replace("BLvPXbVl.js", "v", "pinia", "setActivePinia");
|
||||
replace("BLvPXbVl.js", "y", "pinia", "shouldHydrate");
|
||||
replace("BLvPXbVl.js", "s", "~/utils/crypto.js", "getCrypto");
|
||||
replace("BSkUL2SB.js", "n", "primevue/usetoast", "useToast");
|
||||
replace("iGbquyGC.js", "t", "primevue/tooltip", "default");
|
||||
replace("DEP0rg1v.js", "t", "~/utils/brand.js", "logoUrl");
|
||||
replace("ecKN0m8a.js", "r", "#internal/nuxt/paths", "publicAssetsURL");
|
||||
|
||||
const owners = new Map();
|
||||
const own = Object.entries(pc.modules).filter(([p]) =>
|
||||
/^(components|layouts|pages)\//.test(p),
|
||||
);
|
||||
for (const [source, mod] of own) {
|
||||
const r = resolve(mod.file, "default");
|
||||
if (owners.has(r.file) && owners.get(r.file).source !== source)
|
||||
throw Error(`Multiple owners ${r.file}`);
|
||||
owners.set(r.file, { source, exported: r.name, local: r.local });
|
||||
replacements.set(key(r.file, r.name), {
|
||||
source: `~/${source}`,
|
||||
imported: "default",
|
||||
label: path.basename(source, ".vue"),
|
||||
});
|
||||
}
|
||||
// Official libraries replace bundled copies. Resolve tiny re-export wrappers first.
|
||||
for (const [source, mod] of Object.entries(pc.modules)) {
|
||||
if (!source.includes("/node_modules/") || mod.resourceType !== "script")
|
||||
continue;
|
||||
let npm = source.slice(source.lastIndexOf("/node_modules/") + 14);
|
||||
if (npm.startsWith("nuxt/") || npm.startsWith("@nuxt/icon")) continue;
|
||||
npm = npm.replace(/\/index\.mjs$/, "");
|
||||
if (npm === "@novnc/novnc/core/rfb.js") npm = "@novnc/novnc";
|
||||
const r = resolve(mod.file, "default");
|
||||
if (!owners.has(r.file))
|
||||
replacements.set(key(r.file, r.name), {
|
||||
source: npm,
|
||||
imported: "default",
|
||||
label: npm.split("/").at(-1),
|
||||
});
|
||||
}
|
||||
// The existing chart component is recovered separately; ECharts stays a library dependency.
|
||||
const chartOwner = [...owners.entries()].find(
|
||||
([, v]) => v.source === "components/ChartBox.vue",
|
||||
);
|
||||
if (chartOwner)
|
||||
replacements.set(key(chartOwner[0], chartOwner[1].exported), {
|
||||
source: "~/components/ChartBox.vue",
|
||||
imported: "default",
|
||||
label: "ChartBox",
|
||||
});
|
||||
const names = new Map();
|
||||
for (const d of Object.values(pc.dependencies))
|
||||
for (const v of Object.values(d.preload || {}))
|
||||
if (v.name && v.file?.endsWith(".js") && !names.has(v.file))
|
||||
names.set(v.file, v.name);
|
||||
const paths = new Map();
|
||||
const taken = new Set();
|
||||
for (const file of files) {
|
||||
let output = owners.has(file)
|
||||
? `app/${owners.get(file).source}`
|
||||
: `app/lib/${(names.get(file) || "module").replace(/[^\w-]/g, "-")}.js`;
|
||||
let i = 2;
|
||||
const base = output;
|
||||
while (taken.has(output)) output = base.replace(/\.js$/, `-${i++}.js`);
|
||||
paths.set(file, output);
|
||||
taken.add(output);
|
||||
}
|
||||
const authExports = {
|
||||
a: "loginErrorMessage",
|
||||
c: "ApiError",
|
||||
d: "apiCacheKey",
|
||||
f: "apiCacheVersion",
|
||||
g: "sanitizeMessage",
|
||||
h: "operationMessage",
|
||||
i: "securityMethods",
|
||||
l: "useApi",
|
||||
m: "writeApiCache",
|
||||
n: "assertSecurityComplete",
|
||||
o: "isValidJwt",
|
||||
p: "readApiCache",
|
||||
r: "securityChallenge",
|
||||
t: "useAuth",
|
||||
u: "clearApiCache",
|
||||
};
|
||||
const exportNames = new Map(
|
||||
Object.entries(authExports).map(([a, n]) => [key("BLvPXbVl.js", a), n]),
|
||||
);
|
||||
function lookup(file, exported) {
|
||||
const direct = replacements.get(key(file, exported));
|
||||
if (direct) return direct;
|
||||
const r = resolve(file, exported);
|
||||
return (
|
||||
replacements.get(key(r.file, r.name)) || {
|
||||
source: "~/" + paths.get(r.file).replace(/^app\//, ""),
|
||||
imported: exportNames.get(key(r.file, r.name)) || r.name,
|
||||
file: r.file,
|
||||
label: names.get(r.file),
|
||||
}
|
||||
);
|
||||
}
|
||||
const emitted = new Set();
|
||||
const pending = [...owners.keys()].filter((f) => f !== chartOwner?.[0]);
|
||||
const patchFiles = [];
|
||||
async function add(file, code, parser) {
|
||||
const formatted = await format(code, {
|
||||
parser:
|
||||
parser ||
|
||||
(file.endsWith(".vue")
|
||||
? "vue"
|
||||
: file.endsWith(".json")
|
||||
? "json"
|
||||
: "babel"),
|
||||
printWidth: 110,
|
||||
});
|
||||
patchFiles.push({ file, code: formatted });
|
||||
}
|
||||
// Save route metadata without compiled preload machinery; Nuxt owns the new module graph.
|
||||
const entry = asts.get("CNMaXYQo.js");
|
||||
let routes;
|
||||
traverse(entry, {
|
||||
VariableDeclarator(p) {
|
||||
if (p.node.id.name === "Qi") routes = t.cloneNode(p.node.init, true);
|
||||
},
|
||||
});
|
||||
for (const route of routes.elements) {
|
||||
let file;
|
||||
for (const prop of route.properties)
|
||||
if (prop.key.name === "component")
|
||||
traverse(
|
||||
t.file(
|
||||
t.program([t.expressionStatement(t.cloneNode(prop.value, true))]),
|
||||
),
|
||||
{
|
||||
CallExpression(p) {
|
||||
if (p.node.callee.type === "Import")
|
||||
file = path.basename(textValue(p.node.arguments[0]));
|
||||
},
|
||||
},
|
||||
);
|
||||
const owner = owners.get(resolve(file, "default").file);
|
||||
const metaProps =
|
||||
route.properties.find((p) => p.key.name === "meta")?.value.properties || [];
|
||||
const alias = route.properties.find((p) => p.key.name === "alias");
|
||||
if (alias)
|
||||
metaProps.push(t.objectProperty(t.identifier("alias"), alias.value));
|
||||
const redirect = route.properties.find((p) => p.key.name === "redirect");
|
||||
if (redirect)
|
||||
metaProps.push(t.objectProperty(t.identifier("redirect"), redirect.value));
|
||||
route._source = owner.source;
|
||||
route._meta = metaProps;
|
||||
}
|
||||
while (pending.length) {
|
||||
const file = pending.shift();
|
||||
if (emitted.has(file)) continue;
|
||||
emitted.add(file);
|
||||
if (!asts.has(file)) throw Error(`No AST ${file}`);
|
||||
let ast = t.cloneNode(asts.get(file), true);
|
||||
if (file === "BDbvURLy.js") {
|
||||
ast.program.body = ast.program.body.filter(
|
||||
(n) =>
|
||||
!(t.isFunctionDeclaration(n) && n.id.name === "y") &&
|
||||
!(t.isImportDeclaration(n) && n.source.value === "./j97ZVn8z.js"),
|
||||
);
|
||||
ast.program.body.unshift(
|
||||
t.importDeclaration(
|
||||
[t.importSpecifier(t.identifier("y"), t.identifier("reloadNuxtApp"))],
|
||||
t.stringLiteral("#app"),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (file === "BLvPXbVl.js") {
|
||||
// Business logic only; Pinia, file downloads and cryptography are npm dependencies.
|
||||
let keep = false;
|
||||
const body = [];
|
||||
for (const n of ast.program.body) {
|
||||
if (t.isImportDeclaration(n)) {
|
||||
body.push(n);
|
||||
continue;
|
||||
}
|
||||
if (t.isVariableDeclaration(n)) {
|
||||
for (const d of n.declarations) {
|
||||
if (d.id.name === "ce") keep = true;
|
||||
if (d.id.name === "Oe") keep = false;
|
||||
if (d.id.name === "ot") keep = true;
|
||||
if (keep) body.push(t.variableDeclaration(n.kind, [d]));
|
||||
}
|
||||
} else if (t.isExportNamedDeclaration(n)) {
|
||||
n.specifiers = n.specifiers.filter(
|
||||
(s) => !["_", "v", "y", "s"].includes(s.exported.name),
|
||||
);
|
||||
body.push(n);
|
||||
} else if (keep) body.push(n);
|
||||
}
|
||||
body.unshift(
|
||||
t.importDeclaration(
|
||||
[t.importDefaultSpecifier(t.identifier("CryptoJS"))],
|
||||
t.stringLiteral("crypto-js"),
|
||||
),
|
||||
t.importDeclaration(
|
||||
[t.importSpecifier(t.identifier("se"), t.identifier("defineStore"))],
|
||||
t.stringLiteral("pinia"),
|
||||
),
|
||||
);
|
||||
ast.program.body = body;
|
||||
traverse(ast, {
|
||||
MemberExpression(p) {
|
||||
if (
|
||||
t.isIdentifier(p.node.object, { name: "at" }) &&
|
||||
t.isIdentifier(p.node.property, { name: "default" })
|
||||
)
|
||||
p.replaceWith(t.identifier("CryptoJS"));
|
||||
},
|
||||
});
|
||||
}
|
||||
// Discard old dependency indexes: Vite reconstructs preload URLs from new imports.
|
||||
traverse(ast, {
|
||||
CallExpression(p) {
|
||||
if (t.isIdentifier(p.node.callee, { name: "__vite__mapDeps" }))
|
||||
p.replaceWith(t.arrayExpression([]));
|
||||
},
|
||||
VariableDeclarator(p) {
|
||||
if (p.node.id.name === "__vite__mapDeps") p.remove();
|
||||
},
|
||||
});
|
||||
const owner = owners.get(file);
|
||||
const dependencies = [];
|
||||
const bindingNames = new Set();
|
||||
traverse(ast, {
|
||||
BindingIdentifier(p) {
|
||||
bindingNames.add(p.node.name);
|
||||
},
|
||||
});
|
||||
traverse(ast, {
|
||||
Program(p) {
|
||||
p.scope.crawl();
|
||||
for (const stmt of [...p.get("body")]) {
|
||||
if (!stmt.isImportDeclaration()) continue;
|
||||
const original = stmt.node.source.value;
|
||||
if (!original.startsWith(".")) continue;
|
||||
const dep = path.basename(original);
|
||||
const imports = [];
|
||||
for (const spec of [...stmt.node.specifiers]) {
|
||||
if (!p.scope.getBinding(spec.local.name)?.referenced) continue;
|
||||
const alias = t.isImportDefaultSpecifier(spec)
|
||||
? "default"
|
||||
: spec.imported?.name || spec.imported?.value;
|
||||
const target = lookup(dep, alias);
|
||||
if (target.file) dependencies.push(target.file);
|
||||
let name =
|
||||
target.imported === "default"
|
||||
? target.label || path.basename(target.source) || "Component"
|
||||
: target.imported;
|
||||
if (name.length < 3 && target.label) name = target.label + "_" + name;
|
||||
name = name.replace(/[^\w$]/g, "_");
|
||||
if (!/^[A-Za-z_$]/.test(name)) name = "module_" + name;
|
||||
if (name !== spec.local.name) {
|
||||
while (bindingNames.has(name) || p.scope.hasGlobal(name))
|
||||
name += "Imported";
|
||||
p.scope.rename(spec.local.name, name);
|
||||
bindingNames.add(name);
|
||||
}
|
||||
const local = t.identifier(spec.local.name);
|
||||
imports.push(
|
||||
t.importDeclaration(
|
||||
[
|
||||
target.imported === "default"
|
||||
? t.importDefaultSpecifier(local)
|
||||
: t.importSpecifier(local, t.identifier(target.imported)),
|
||||
],
|
||||
t.stringLiteral(target.source),
|
||||
),
|
||||
);
|
||||
}
|
||||
if (!stmt.node.specifiers.length) {
|
||||
if (file === "BY4y5R3Q.js" && dep === "DNcWRfqH.js") {
|
||||
stmt.remove();
|
||||
continue;
|
||||
}
|
||||
throw Error(`Side effect import ${file} ${original}`);
|
||||
}
|
||||
stmt.replaceWithMultiple(imports);
|
||||
}
|
||||
// Semantic exported API names are stable throughout the reconstructed project.
|
||||
for (const s of p.node.body)
|
||||
if (t.isExportNamedDeclaration(s))
|
||||
for (const spec of s.specifiers) {
|
||||
const n = exportNames.get(key(file, spec.exported.name));
|
||||
if (n) spec.exported = t.identifier(n);
|
||||
}
|
||||
},
|
||||
CallExpression(p) {
|
||||
if (p.node.callee.type !== "Import") return;
|
||||
const value = textValue(p.node.arguments[0]);
|
||||
if (!value?.startsWith("./")) return;
|
||||
const dep = path.basename(value);
|
||||
const target = lookup(dep, "default");
|
||||
if (target.file) dependencies.push(target.file);
|
||||
p.node.arguments[0] = t.stringLiteral(target.source);
|
||||
},
|
||||
});
|
||||
for (const dep of dependencies) if (!emitted.has(dep)) pending.push(dep);
|
||||
if (owner) {
|
||||
const defaultExport = ast.program.body.find((n) =>
|
||||
t.isExportDefaultDeclaration(n),
|
||||
);
|
||||
if (!defaultExport) {
|
||||
const exportNode = ast.program.body.find(
|
||||
(n) =>
|
||||
t.isExportNamedDeclaration(n) &&
|
||||
n.specifiers.some((s) => s.exported.name === owner.exported),
|
||||
);
|
||||
const spec = exportNode?.specifiers.find(
|
||||
(s) => s.exported.name === owner.exported,
|
||||
);
|
||||
if (!spec) throw Error(`Missing own export ${owner.source}`);
|
||||
exportNode.specifiers = exportNode.specifiers.filter((s) => s !== spec);
|
||||
ast.program.body.push(
|
||||
t.exportDefaultDeclaration(t.identifier(spec.local.name)),
|
||||
);
|
||||
}
|
||||
const route = routes.elements.find((r) => r._source === owner.source);
|
||||
if (route?._meta.length)
|
||||
ast.program.body.unshift(
|
||||
t.expressionStatement(
|
||||
t.callExpression(t.identifier("definePageMeta"), [
|
||||
t.objectExpression(route._meta),
|
||||
]),
|
||||
),
|
||||
);
|
||||
const styles = Object.values(
|
||||
pc.dependencies[owner.source]?.styles || {},
|
||||
).map((v) => v.file);
|
||||
for (const css of styles)
|
||||
ast.program.body.unshift(
|
||||
t.importDeclaration([], t.stringLiteral("~/assets/styles/" + css)),
|
||||
);
|
||||
await add(
|
||||
paths.get(file),
|
||||
`<script>\n${generate(ast, { comments: true }).code}\n</script>\n`,
|
||||
);
|
||||
} else await add(paths.get(file), generate(ast, { comments: true }).code);
|
||||
}
|
||||
await add(
|
||||
"app/utils/load-module.js",
|
||||
"export function loadModule(load) { return load(); }",
|
||||
);
|
||||
await add(
|
||||
"app/utils/crypto.js",
|
||||
"import CryptoJS from 'crypto-js'; export function getCrypto() { return CryptoJS.SHA256; }",
|
||||
);
|
||||
await add(
|
||||
"app/utils/brand.js",
|
||||
"export const logoUrl = '/images/brand/logo.png';",
|
||||
);
|
||||
await add(
|
||||
"docs/module-inventory.json",
|
||||
JSON.stringify(
|
||||
{
|
||||
pages: own.filter(([p]) => p.startsWith("pages/")).map(([p]) => p),
|
||||
components: own
|
||||
.filter(([p]) => p.startsWith("components/"))
|
||||
.map(([p]) => p),
|
||||
layouts: own.filter(([p]) => p.startsWith("layouts/")).map(([p]) => p),
|
||||
shared: [...emitted]
|
||||
.filter((f) => !owners.has(f))
|
||||
.map((f) => paths.get(f)),
|
||||
routes: routes.elements.map((r) => ({
|
||||
source: r._source,
|
||||
path: textValue(r.properties.find((p) => p.key.name === "path").value),
|
||||
})),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
);
|
||||
for (const { file, code } of patchFiles) {
|
||||
const target = path.join(root, file);
|
||||
fs.mkdirSync(path.dirname(target), { recursive: true });
|
||||
const patch = `*** Begin Patch\n*** Add File: ${target}\n${code
|
||||
.split("\n")
|
||||
.map((l) => "+" + l)
|
||||
.join("\n")}\n*** End Patch\n`;
|
||||
const result = spawnSync("apply_patch", [], {
|
||||
input: patch,
|
||||
encoding: "utf8",
|
||||
});
|
||||
if (result.status !== 0) throw Error(result.stdout + result.stderr);
|
||||
}
|
||||
// Static binary assets retain their deployed paths; no JS bundle is copied.
|
||||
const cssFiles = [
|
||||
...new Set(
|
||||
Object.values(pc.modules)
|
||||
.filter((v) => v.resourceType === "style")
|
||||
.map((v) => v.file),
|
||||
),
|
||||
];
|
||||
fs.mkdirSync(path.join(root, "app/assets/styles"), { recursive: true });
|
||||
for (const file of cssFiles) {
|
||||
let css = fs.readFileSync(path.join(chunkDir, file), "utf8");
|
||||
css = css.replace(
|
||||
/url\(([^)]*primeicons[^)]*)\)/g,
|
||||
(_, v) =>
|
||||
`url(${v.replace(/^["']?|["']?$/g, "").replace(/^\.\//, "/icons/")})`,
|
||||
);
|
||||
const out = path.join(root, "app/assets/styles", file);
|
||||
const patch = `*** Begin Patch\n*** Add File: ${out}\n+${css.replace(/\n/g, "\n+")}\n*** End Patch\n`;
|
||||
const result = spawnSync("apply_patch", [], {
|
||||
input: patch,
|
||||
encoding: "utf8",
|
||||
});
|
||||
if (result.status !== 0) throw Error(result.stdout + result.stderr);
|
||||
}
|
||||
for (const name of fs.readdirSync(path.join(input, "public")))
|
||||
if (
|
||||
name !== "_nuxt" &&
|
||||
!name.startsWith("._") &&
|
||||
!["builds"].includes(name)
|
||||
) {
|
||||
const src = path.join(input, "public", name);
|
||||
if (fs.lstatSync(src).isDirectory() || !name.endsWith(".js"))
|
||||
fs.cpSync(src, path.join(root, "public", name), {
|
||||
recursive: true,
|
||||
filter: (p) => !path.basename(p).startsWith("._"),
|
||||
});
|
||||
}
|
||||
fs.mkdirSync(path.join(root, "public/icons"), { recursive: true });
|
||||
for (const name of fs.readdirSync(chunkDir))
|
||||
if (name.startsWith("primeicons.") && !name.endsWith(".js"))
|
||||
fs.copyFileSync(
|
||||
path.join(chunkDir, name),
|
||||
path.join(root, "public/icons", name),
|
||||
);
|
||||
console.log(
|
||||
JSON.stringify({
|
||||
sourceFiles: patchFiles.length,
|
||||
businessModules: emitted.size,
|
||||
css: cssFiles.length,
|
||||
}),
|
||||
);
|
||||
Reference in new issue
Block a user