@gtkx/cli 2.0.0-beta.7 → 2.0.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/deploy.d.ts +4 -0
- package/dist/commands/deploy.d.ts.map +1 -1
- package/dist/commands/deploy.js +6 -0
- package/dist/commands/deploy.js.map +1 -1
- package/dist/deploy/depends.d.ts.map +1 -1
- package/dist/deploy/depends.js +24 -2
- package/dist/deploy/depends.js.map +1 -1
- package/dist/deploy/download.d.ts +7 -3
- package/dist/deploy/download.d.ts.map +1 -1
- package/dist/deploy/download.js +19 -15
- package/dist/deploy/download.js.map +1 -1
- package/dist/deploy/native-addon.d.ts +5 -0
- package/dist/deploy/native-addon.d.ts.map +1 -0
- package/dist/deploy/native-addon.js +134 -0
- package/dist/deploy/native-addon.js.map +1 -0
- package/dist/deploy/node-runtime/download.js +2 -2
- package/dist/deploy/node-runtime/download.js.map +1 -1
- package/dist/deploy/node-runtime/elf.d.ts +3 -1
- package/dist/deploy/node-runtime/elf.d.ts.map +1 -1
- package/dist/deploy/node-runtime/elf.js +3 -1
- package/dist/deploy/node-runtime/elf.js.map +1 -1
- package/dist/deploy/node-runtime/guard.d.ts.map +1 -1
- package/dist/deploy/node-runtime/guard.js +7 -14
- package/dist/deploy/node-runtime/guard.js.map +1 -1
- package/dist/deploy/node-runtime/index.d.ts +2 -1
- package/dist/deploy/node-runtime/index.d.ts.map +1 -1
- package/dist/deploy/node-runtime/index.js +29 -6
- package/dist/deploy/node-runtime/index.js.map +1 -1
- package/dist/deploy/node-runtime/sonames.d.ts +5 -0
- package/dist/deploy/node-runtime/sonames.d.ts.map +1 -0
- package/dist/deploy/node-runtime/sonames.js +13 -0
- package/dist/deploy/node-runtime/sonames.js.map +1 -0
- package/dist/deploy/payload/stage.d.ts +2 -1
- package/dist/deploy/payload/stage.d.ts.map +1 -1
- package/dist/deploy/payload/stage.js +22 -2
- package/dist/deploy/payload/stage.js.map +1 -1
- package/dist/deploy/run-deploy.d.ts +1 -0
- package/dist/deploy/run-deploy.d.ts.map +1 -1
- package/dist/deploy/run-deploy.js +139 -80
- package/dist/deploy/run-deploy.js.map +1 -1
- package/dist/deploy/settings/arch.d.ts +9 -3
- package/dist/deploy/settings/arch.d.ts.map +1 -1
- package/dist/deploy/settings/arch.js +27 -6
- package/dist/deploy/settings/arch.js.map +1 -1
- package/dist/deploy/settings/index.d.ts +2 -1
- package/dist/deploy/settings/index.d.ts.map +1 -1
- package/dist/deploy/settings/index.js +2 -1
- package/dist/deploy/settings/index.js.map +1 -1
- package/dist/deploy/settings/paths.d.ts +2 -1
- package/dist/deploy/settings/paths.d.ts.map +1 -1
- package/dist/deploy/settings/paths.js +6 -5
- package/dist/deploy/settings/paths.js.map +1 -1
- package/dist/deploy/targets/appimage.d.ts.map +1 -1
- package/dist/deploy/targets/appimage.js +1 -0
- package/dist/deploy/targets/appimage.js.map +1 -1
- package/dist/deploy/targets/flatpak-pnpm.js +4 -4
- package/dist/deploy/targets/flatpak-pnpm.js.map +1 -1
- package/dist/deploy/targets/flatpak.d.ts.map +1 -1
- package/dist/deploy/targets/flatpak.js +1 -0
- package/dist/deploy/targets/flatpak.js.map +1 -1
- package/dist/deploy/targets/nfpm-target.d.ts.map +1 -1
- package/dist/deploy/targets/nfpm-target.js +1 -0
- package/dist/deploy/targets/nfpm-target.js.map +1 -1
- package/dist/deploy/types.d.ts +4 -2
- package/dist/deploy/types.d.ts.map +1 -1
- package/dist/deploy/types.js.map +1 -1
- package/dist/deploy/vendored/appimagetool.js +2 -2
- package/dist/deploy/vendored/appimagetool.js.map +1 -1
- package/dist/deploy/vendored/nfpm.js +1 -1
- package/dist/deploy/vendored/nfpm.js.map +1 -1
- package/dist/dev/font-dir.d.ts +1 -1
- package/dist/dev/font-dir.d.ts.map +1 -1
- package/dist/dev/font-dir.js +0 -3
- package/dist/dev/font-dir.js.map +1 -1
- package/dist/dev/runner.d.ts.map +1 -1
- package/dist/dev/runner.js +19 -6
- package/dist/dev/runner.js.map +1 -1
- package/dist/internal/font-staging.d.ts +5 -2
- package/dist/internal/font-staging.d.ts.map +1 -1
- package/dist/internal/font-staging.js +76 -27
- package/dist/internal/font-staging.js.map +1 -1
- package/dist/internal/icon-staging.d.ts +3 -0
- package/dist/internal/icon-staging.d.ts.map +1 -0
- package/dist/internal/icon-staging.js +40 -0
- package/dist/internal/icon-staging.js.map +1 -0
- package/dist/internal/source-imports.js +1 -1
- package/dist/internal/source-imports.js.map +1 -1
- package/dist/vite-plugins/font-worker-env.d.ts.map +1 -1
- package/dist/vite-plugins/font-worker-env.js +2 -5
- package/dist/vite-plugins/font-worker-env.js.map +1 -1
- package/dist/vite-plugins/font.d.ts.map +1 -1
- package/dist/vite-plugins/font.js +16 -4
- package/dist/vite-plugins/font.js.map +1 -1
- package/dist/vite-plugins/icon-worker-env.d.ts +4 -0
- package/dist/vite-plugins/icon-worker-env.d.ts.map +1 -0
- package/dist/vite-plugins/icon-worker-env.js +23 -0
- package/dist/vite-plugins/icon-worker-env.js.map +1 -0
- package/dist/vite-plugins/icons.js +2 -2
- package/dist/vite-plugins/icons.js.map +1 -1
- package/dist/vite-plugins/react-compiler.js +2 -2
- package/dist/vite-plugins/react-compiler.js.map +1 -1
- package/dist/vite-plugins/resources.js +2 -2
- package/dist/vite-plugins/resources.js.map +1 -1
- package/dist/vite-plugins/settings-worker-env.js +2 -2
- package/dist/vite-plugins/settings-worker-env.js.map +1 -1
- package/dist/vite-plugins/store-links.d.ts.map +1 -1
- package/dist/vite-plugins/store-links.js +2 -1
- package/dist/vite-plugins/store-links.js.map +1 -1
- package/dist/vite-plugins/undeclared-library.js +2 -2
- package/dist/vite-plugins/undeclared-library.js.map +1 -1
- package/dist/vite-plugins/worker.d.ts.map +1 -1
- package/dist/vite-plugins/worker.js +21 -13
- package/dist/vite-plugins/worker.js.map +1 -1
- package/dist/vitest-plugin.d.ts +2 -2
- package/dist/vitest-plugin.d.ts.map +1 -1
- package/dist/vitest-plugin.js +4 -2
- package/dist/vitest-plugin.js.map +1 -1
- package/package.json +13 -13
- package/src/commands/deploy.ts +6 -0
- package/src/deploy/depends.ts +30 -3
- package/src/deploy/download.ts +33 -17
- package/src/deploy/native-addon.ts +183 -0
- package/src/deploy/node-runtime/download.ts +2 -2
- package/src/deploy/node-runtime/elf.ts +4 -1
- package/src/deploy/node-runtime/guard.ts +10 -14
- package/src/deploy/node-runtime/index.ts +42 -7
- package/src/deploy/node-runtime/sonames.ts +17 -0
- package/src/deploy/payload/stage.ts +34 -2
- package/src/deploy/run-deploy.ts +209 -94
- package/src/deploy/settings/arch.ts +40 -8
- package/src/deploy/settings/index.ts +4 -1
- package/src/deploy/settings/paths.ts +8 -6
- package/src/deploy/targets/appimage.ts +1 -0
- package/src/deploy/targets/flatpak-pnpm.ts +4 -4
- package/src/deploy/targets/flatpak.ts +1 -0
- package/src/deploy/targets/nfpm-target.ts +1 -0
- package/src/deploy/types.ts +4 -1
- package/src/deploy/vendored/appimagetool.ts +2 -2
- package/src/deploy/vendored/nfpm.ts +1 -1
- package/src/dev/font-dir.ts +1 -6
- package/src/dev/runner.ts +24 -6
- package/src/internal/font-staging.ts +99 -27
- package/src/internal/icon-staging.ts +58 -0
- package/src/internal/source-imports.ts +1 -1
- package/src/vite-plugins/font-worker-env.ts +2 -6
- package/src/vite-plugins/font.ts +21 -3
- package/src/vite-plugins/icon-worker-env.ts +36 -0
- package/src/vite-plugins/icons.ts +2 -2
- package/src/vite-plugins/react-compiler.ts +2 -2
- package/src/vite-plugins/resources.ts +2 -2
- package/src/vite-plugins/settings-worker-env.ts +2 -2
- package/src/vite-plugins/store-links.ts +2 -1
- package/src/vite-plugins/undeclared-library.ts +2 -2
- package/src/vite-plugins/worker.ts +31 -15
- package/src/vitest-plugin.ts +4 -2
|
@@ -33,9 +33,10 @@ const nonCodeRanges = (code) => code
|
|
|
33
33
|
const isCodePosition = (scan, index) => scan.nonCode.every((range) => index < range.start || index >= range.end);
|
|
34
34
|
const isWorkerConstruction = (code, matchIndex) => WORKER_CONSTRUCTION.test(code.slice(0, matchIndex));
|
|
35
35
|
const hasWorkerCandidate = (code) => code.includes("import.meta.url") && code.includes("Worker");
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
const workerUrlReference = (referenceId) => `import.meta.ROLLUP_FILE_URL_${referenceId}`;
|
|
37
|
+
const workerUrlExpression = (relativePath) => {
|
|
38
|
+
const specifier = relativePath.startsWith(".") ? relativePath : `./${relativePath}`;
|
|
39
|
+
return `new URL(${JSON.stringify(specifier)}, import.meta.url)`;
|
|
39
40
|
};
|
|
40
41
|
const inlineWorkerExample = (specifier) => `new Worker(new URL(${JSON.stringify(specifier)}, import.meta.url))`;
|
|
41
42
|
const hoistedWorkerError = (scan, hoisted) => new Error(`${stripQuery(scan.id)}: the worker URL for ${JSON.stringify(hoisted.specifier)} is bound to ` +
|
|
@@ -59,14 +60,18 @@ const suggestionFor = async (emit, scan, specifier) => {
|
|
|
59
60
|
return null;
|
|
60
61
|
};
|
|
61
62
|
const claimWorker = (emit, resolvedId) => {
|
|
62
|
-
const existing = emit.
|
|
63
|
+
const existing = emit.references.byModule.get(resolvedId);
|
|
63
64
|
if (existing !== undefined) {
|
|
64
65
|
return existing;
|
|
65
66
|
}
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
const referenceId = emit.context.emitFile({
|
|
68
|
+
type: "chunk",
|
|
69
|
+
id: resolvedId,
|
|
70
|
+
fileName: workerFileName(resolvedId),
|
|
71
|
+
});
|
|
72
|
+
emit.references.byModule.set(resolvedId, referenceId);
|
|
73
|
+
emit.references.claimed.add(referenceId);
|
|
74
|
+
return referenceId;
|
|
70
75
|
};
|
|
71
76
|
const isRewritableUrl = (scan, match) => isCodePosition(scan, match.index) && isWorkerConstruction(scan.code, match.index);
|
|
72
77
|
const replacementFor = async (emit, scan, match) => {
|
|
@@ -78,7 +83,7 @@ const replacementFor = async (emit, scan, match) => {
|
|
|
78
83
|
if (resolved === null) {
|
|
79
84
|
throw unresolvedWorkerError(scan, specifier, await suggestionFor(emit, scan, specifier));
|
|
80
85
|
}
|
|
81
|
-
return { start: match.index, end: match.index + match[0].length,
|
|
86
|
+
return { start: match.index, end: match.index + match[0].length, referenceId: claimWorker(emit, resolved.id) };
|
|
82
87
|
};
|
|
83
88
|
const collectReplacements = async (emit, scan) => {
|
|
84
89
|
const pending = scan.code.matchAll(WORKER_URL).map((match) => replacementFor(emit, scan, match)).toArray();
|
|
@@ -105,8 +110,8 @@ const findHoistedWorkerUrl = (scan) => scan.code
|
|
|
105
110
|
const applyReplacements = (code, replacements) => {
|
|
106
111
|
let output = "";
|
|
107
112
|
let cursor = 0;
|
|
108
|
-
for (const { start, end,
|
|
109
|
-
output += code.slice(cursor, start) +
|
|
113
|
+
for (const { start, end, referenceId } of replacements) {
|
|
114
|
+
output += code.slice(cursor, start) + workerUrlReference(referenceId);
|
|
110
115
|
cursor = end;
|
|
111
116
|
}
|
|
112
117
|
return output + code.slice(cursor);
|
|
@@ -124,12 +129,15 @@ const transformWorkerUrls = async (emit, code, id) => {
|
|
|
124
129
|
return replacements.length === 0 ? null : { code: applyReplacements(code, replacements), map: null };
|
|
125
130
|
};
|
|
126
131
|
function gtkxWorker() {
|
|
127
|
-
const
|
|
132
|
+
const references = { byModule: new Map(), claimed: new Set() };
|
|
128
133
|
return {
|
|
129
134
|
name: "gtkx:worker",
|
|
130
135
|
apply: "build",
|
|
131
136
|
transform(code, id) {
|
|
132
|
-
return transformWorkerUrls({ context: this,
|
|
137
|
+
return transformWorkerUrls({ context: this, references }, code, id);
|
|
138
|
+
},
|
|
139
|
+
resolveFileUrl({ referenceId, relativePath }) {
|
|
140
|
+
return references.claimed.has(referenceId) ? workerUrlExpression(relativePath) : null;
|
|
133
141
|
},
|
|
134
142
|
};
|
|
135
143
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/vite-plugins/worker.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Plugin, Rollup } from \"vite\";\nimport { createHash } from \"node:crypto\";\nimport { basename, extname } from \"node:path\";\nimport { ESM_EXTENSION } from \"./esm-extension.js\";\nimport { stripQuery } from \"./strip-query.js\";\n\ntype WorkerReplacement = {\n start: number;\n end: number;\n fileName: string;\n};\n\ntype EmitContext = {\n context: Rollup.PluginContext;\n emitted: Map<string, string>;\n};\n\ntype SourceRange = {\n start: number;\n end: number;\n};\n\ntype ScanContext = {\n code: string;\n id: string;\n nonCode: SourceRange[];\n};\n\ntype HoistedWorkerUrl = {\n name: string;\n specifier: string;\n};\n\ntype TransformResult = {\n code: string;\n map: null;\n} | null;\n\nconst RELATIVE_SPECIFIER = String.raw`(?<quote>[\"'])(?<specifier>\\.{1,2}\\/[^\"']+)\\k<quote>`;\nconst URL_CALL = String.raw`new\\s+URL\\s*\\(\\s*${RELATIVE_SPECIFIER}\\s*,\\s*import\\.meta\\.url\\s*\\)`;\nconst URL_DECLARATION = String.raw`(?:const|let|var)\\s+(?<name>[A-Za-z_$][\\w$]*)\\s*(?::[^=;\\n]+)?=\\s*`;\nconst BLOCK_COMMENT = String.raw`\\/\\*[\\s\\S]*?\\*\\/`;\nconst LINE_COMMENT = String.raw`\\/\\/[^\\n]*`;\nconst DOUBLE_QUOTED = String.raw`\"(?:[^\"\\\\\\n]|\\\\[\\s\\S])*\"`;\nconst SINGLE_QUOTED = String.raw`'(?:[^'\\\\\\n]|\\\\[\\s\\S])*'`;\nconst TEMPLATE_LITERAL = String.raw`\\`(?:[^\\`\\\\]|\\\\[\\s\\S])*\\``;\nconst WORKER_URL = new RegExp(URL_CALL, \"g\");\nconst HOISTED_WORKER_URL = new RegExp(URL_DECLARATION + URL_CALL, \"g\");\nconst WORKER_CONSTRUCTION = /new\\s+(?:\\w+\\.)?Worker\\s*\\(\\s*$/;\n\nconst NON_CODE = new RegExp(\n [BLOCK_COMMENT, LINE_COMMENT, DOUBLE_QUOTED, SINGLE_QUOTED, TEMPLATE_LITERAL].join(\"|\"),\n \"g\",\n);\n\nconst WORKER_DIR = \"workers\";\n\nconst SUGGESTED_EXTENSIONS: Record<string, string[]> = {\n \".js\": [\".ts\", \".tsx\"],\n \".jsx\": [\".tsx\"],\n \".mjs\": [\".mts\"],\n};\n\nconst workerFileName = (id: string): string => {\n const path = stripQuery(id);\n const stem = basename(path, extname(path));\n const digest = createHash(\"sha256\").update(id).digest(\"hex\").slice(0, 8);\n\n return `${WORKER_DIR}/${stem}-${digest}${ESM_EXTENSION}`;\n};\n\nconst nonCodeRanges = (code: string): SourceRange[] =>\n code\n .matchAll(NON_CODE)\n .map((match) => ({ start: match.index, end: match.index + match[0].length }))\n .toArray();\n\nconst isCodePosition = (scan: ScanContext, index: number): boolean =>\n scan.nonCode.every((range) => index < range.start || index >= range.end);\n\nconst isWorkerConstruction = (code: string, matchIndex: number): boolean =>\n WORKER_CONSTRUCTION.test(code.slice(0, matchIndex));\n\nconst hasWorkerCandidate = (code: string): boolean => code.includes(\"import.meta.url\") && code.includes(\"Worker\");\n\nconst workerUrlExpression = (fileName: string): string => {\n const specifier = JSON.stringify(`./${fileName}`);\n\n return `new URL(${specifier}, import.meta.url)`;\n};\n\nconst inlineWorkerExample = (specifier: string): string =>\n `new Worker(new URL(${JSON.stringify(specifier)}, import.meta.url))`;\n\nconst hoistedWorkerError = (scan: ScanContext, hoisted: HoistedWorkerUrl): Error =>\n new Error(\n `${stripQuery(scan.id)}: the worker URL for ${JSON.stringify(hoisted.specifier)} is bound to ` +\n `\"${hoisted.name}\" before it reaches \"new Worker\". gtkx build emits a worker chunk only for a URL ` +\n `written inside the construction. Write ${inlineWorkerExample(hoisted.specifier)} instead.`,\n );\n\nconst unresolvedWorkerError = (scan: ScanContext, specifier: string, suggestion: string | null): Error =>\n new Error(\n `${stripQuery(scan.id)}: the worker specifier ${JSON.stringify(specifier)} does not resolve to a module, ` +\n \"so no worker chunk can be emitted. \" +\n (suggestion === null\n ? \"Correct the path so it names the worker source file as it exists on disk.\"\n : `Write ${inlineWorkerExample(suggestion)} instead.`),\n );\n\nconst suggestionFor = async (emit: EmitContext, scan: ScanContext, specifier: string): Promise<string | null> => {\n const extension = extname(specifier);\n const stem = specifier.slice(0, specifier.length - extension.length);\n const candidates = SUGGESTED_EXTENSIONS[extension] ?? [];\n\n for (const candidate of candidates) {\n const resolved = await emit.context.resolve(stem + candidate, scan.id);\n\n if (resolved !== null) {\n return stem + candidate;\n }\n }\n\n return null;\n};\n\nconst claimWorker = (emit: EmitContext, resolvedId: string): string => {\n const existing = emit.emitted.get(resolvedId);\n\n if (existing !== undefined) {\n return existing;\n }\n\n const fileName = workerFileName(resolvedId);\n emit.emitted.set(resolvedId, fileName);\n emit.context.emitFile({ type: \"chunk\", id: resolvedId, fileName });\n\n return fileName;\n};\n\nconst isRewritableUrl = (scan: ScanContext, match: RegExpExecArray): boolean =>\n isCodePosition(scan, match.index) && isWorkerConstruction(scan.code, match.index);\n\nconst replacementFor = async (\n emit: EmitContext,\n scan: ScanContext,\n match: RegExpExecArray,\n): Promise<WorkerReplacement | null> => {\n const specifier = match.groups?.specifier;\n\n if (specifier === undefined || !isRewritableUrl(scan, match)) {\n return null;\n }\n\n const resolved = await emit.context.resolve(specifier, scan.id);\n\n if (resolved === null) {\n throw unresolvedWorkerError(scan, specifier, await suggestionFor(emit, scan, specifier));\n }\n\n return { start: match.index, end: match.index + match[0].length, fileName: claimWorker(emit, resolved.id) };\n};\n\nconst collectReplacements = async (emit: EmitContext, scan: ScanContext): Promise<WorkerReplacement[]> => {\n const pending = scan.code.matchAll(WORKER_URL).map((match) => replacementFor(emit, scan, match)).toArray();\n const found = await Promise.all(pending);\n\n return found.filter((entry) => entry !== null);\n};\n\nconst hasWorkerArgument = (scan: ScanContext, name: string): boolean => {\n const escaped = name.replaceAll(\"$\", String.raw`\\$`);\n const pattern = new RegExp(String.raw`new\\s+(?:\\w+\\.)?Worker\\s*\\(\\s*${escaped}\\s*[,)]`, \"g\");\n\n return scan.code.matchAll(pattern).some((match) => isCodePosition(scan, match.index));\n};\n\nconst hoistedUrlFor = (scan: ScanContext, match: RegExpExecArray): HoistedWorkerUrl | null => {\n const name = match.groups?.name;\n const specifier = match.groups?.specifier;\n\n if (name === undefined || specifier === undefined || !isCodePosition(scan, match.index)) {\n return null;\n }\n\n return { name, specifier };\n};\n\nconst findHoistedWorkerUrl = (scan: ScanContext): HoistedWorkerUrl | null =>\n scan.code\n .matchAll(HOISTED_WORKER_URL)\n .map((match) => hoistedUrlFor(scan, match))\n .find((entry) => entry !== null && hasWorkerArgument(scan, entry.name)) ?? null;\n\nconst applyReplacements = (code: string, replacements: WorkerReplacement[]): string => {\n let output = \"\";\n let cursor = 0;\n\n for (const { start, end, fileName } of replacements) {\n output += code.slice(cursor, start) + workerUrlExpression(fileName);\n cursor = end;\n }\n\n return output + code.slice(cursor);\n};\n\nconst transformWorkerUrls = async (emit: EmitContext, code: string, id: string): Promise<TransformResult> => {\n if (!hasWorkerCandidate(code)) {\n return null;\n }\n\n const scan: ScanContext = { code, id, nonCode: nonCodeRanges(code) };\n const hoisted = findHoistedWorkerUrl(scan);\n\n if (hoisted !== null) {\n throw hoistedWorkerError(scan, hoisted);\n }\n\n const replacements = await collectReplacements(emit, scan);\n\n return replacements.length === 0 ? null : { code: applyReplacements(code, replacements), map: null };\n};\n\nfunction gtkxWorker(): Plugin {\n const emitted: Map<string, string> = new Map();\n\n return {\n name: \"gtkx:worker\",\n apply: \"build\",\n\n transform(code, id) {\n return transformWorkerUrls({ context: this, emitted }, code, id);\n },\n };\n}\n\nexport { gtkxWorker };\n"]}
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../../src/vite-plugins/worker.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Plugin, Rollup } from \"vite\";\nimport { createHash } from \"node:crypto\";\nimport { basename, extname } from \"node:path\";\nimport { ESM_EXTENSION } from \"./esm-extension.js\";\nimport { stripQuery } from \"./strip-query.js\";\n\ntype WorkerReplacement = {\n start: number;\n end: number;\n referenceId: string;\n};\n\ntype WorkerReferences = {\n byModule: Map<string, string>;\n claimed: Set<string>;\n};\n\ntype EmitContext = {\n context: Rollup.PluginContext;\n references: WorkerReferences;\n};\n\ntype SourceRange = {\n start: number;\n end: number;\n};\n\ntype ScanContext = {\n code: string;\n id: string;\n nonCode: SourceRange[];\n};\n\ntype HoistedWorkerUrl = {\n name: string;\n specifier: string;\n};\n\ntype TransformResult = {\n code: string;\n map: null;\n} | null;\n\nconst RELATIVE_SPECIFIER = String.raw`(?<quote>[\"'])(?<specifier>\\.{1,2}\\/[^\"']+)\\k<quote>`;\nconst URL_CALL = String.raw`new\\s+URL\\s*\\(\\s*${RELATIVE_SPECIFIER}\\s*,\\s*import\\.meta\\.url\\s*\\)`;\nconst URL_DECLARATION = String.raw`(?:const|let|var)\\s+(?<name>[A-Za-z_$][\\w$]*)\\s*(?::[^=;\\n]+)?=\\s*`;\nconst BLOCK_COMMENT = String.raw`\\/\\*[\\s\\S]*?\\*\\/`;\nconst LINE_COMMENT = String.raw`\\/\\/[^\\n]*`;\nconst DOUBLE_QUOTED = String.raw`\"(?:[^\"\\\\\\n]|\\\\[\\s\\S])*\"`;\nconst SINGLE_QUOTED = String.raw`'(?:[^'\\\\\\n]|\\\\[\\s\\S])*'`;\nconst TEMPLATE_LITERAL = String.raw`\\`(?:[^\\`\\\\]|\\\\[\\s\\S])*\\``;\nconst WORKER_URL = new RegExp(URL_CALL, \"g\");\nconst HOISTED_WORKER_URL = new RegExp(URL_DECLARATION + URL_CALL, \"g\");\nconst WORKER_CONSTRUCTION = /new\\s+(?:\\w+\\.)?Worker\\s*\\(\\s*$/;\n\nconst NON_CODE = new RegExp(\n [BLOCK_COMMENT, LINE_COMMENT, DOUBLE_QUOTED, SINGLE_QUOTED, TEMPLATE_LITERAL].join(\"|\"),\n \"g\",\n);\n\nconst WORKER_DIR = \"workers\";\n\nconst SUGGESTED_EXTENSIONS: Record<string, string[]> = {\n \".js\": [\".ts\", \".tsx\"],\n \".jsx\": [\".tsx\"],\n \".mjs\": [\".mts\"],\n};\n\nconst workerFileName = (id: string): string => {\n const path = stripQuery(id);\n const stem = basename(path, extname(path));\n const digest = createHash(\"sha256\").update(id).digest(\"hex\").slice(0, 8);\n\n return `${WORKER_DIR}/${stem}-${digest}${ESM_EXTENSION}`;\n};\n\nconst nonCodeRanges = (code: string): SourceRange[] =>\n code\n .matchAll(NON_CODE)\n .map((match) => ({ start: match.index, end: match.index + match[0].length }))\n .toArray();\n\nconst isCodePosition = (scan: ScanContext, index: number): boolean =>\n scan.nonCode.every((range) => index < range.start || index >= range.end);\n\nconst isWorkerConstruction = (code: string, matchIndex: number): boolean =>\n WORKER_CONSTRUCTION.test(code.slice(0, matchIndex));\n\nconst hasWorkerCandidate = (code: string): boolean => code.includes(\"import.meta.url\") && code.includes(\"Worker\");\n\nconst workerUrlReference = (referenceId: string): string => `import.meta.ROLLUP_FILE_URL_${referenceId}`;\n\nconst workerUrlExpression = (relativePath: string): string => {\n const specifier = relativePath.startsWith(\".\") ? relativePath : `./${relativePath}`;\n\n return `new URL(${JSON.stringify(specifier)}, import.meta.url)`;\n};\n\nconst inlineWorkerExample = (specifier: string): string =>\n `new Worker(new URL(${JSON.stringify(specifier)}, import.meta.url))`;\n\nconst hoistedWorkerError = (scan: ScanContext, hoisted: HoistedWorkerUrl): Error =>\n new Error(\n `${stripQuery(scan.id)}: the worker URL for ${JSON.stringify(hoisted.specifier)} is bound to ` +\n `\"${hoisted.name}\" before it reaches \"new Worker\". gtkx build emits a worker chunk only for a URL ` +\n `written inside the construction. Write ${inlineWorkerExample(hoisted.specifier)} instead.`,\n );\n\nconst unresolvedWorkerError = (scan: ScanContext, specifier: string, suggestion: string | null): Error =>\n new Error(\n `${stripQuery(scan.id)}: the worker specifier ${JSON.stringify(specifier)} does not resolve to a module, ` +\n \"so no worker chunk can be emitted. \" +\n (suggestion === null\n ? \"Correct the path so it names the worker source file as it exists on disk.\"\n : `Write ${inlineWorkerExample(suggestion)} instead.`),\n );\n\nconst suggestionFor = async (emit: EmitContext, scan: ScanContext, specifier: string): Promise<string | null> => {\n const extension = extname(specifier);\n const stem = specifier.slice(0, specifier.length - extension.length);\n const candidates = SUGGESTED_EXTENSIONS[extension] ?? [];\n\n for (const candidate of candidates) {\n const resolved = await emit.context.resolve(stem + candidate, scan.id);\n\n if (resolved !== null) {\n return stem + candidate;\n }\n }\n\n return null;\n};\n\nconst claimWorker = (emit: EmitContext, resolvedId: string): string => {\n const existing = emit.references.byModule.get(resolvedId);\n\n if (existing !== undefined) {\n return existing;\n }\n\n const referenceId = emit.context.emitFile({\n type: \"chunk\",\n id: resolvedId,\n fileName: workerFileName(resolvedId),\n });\n\n emit.references.byModule.set(resolvedId, referenceId);\n emit.references.claimed.add(referenceId);\n\n return referenceId;\n};\n\nconst isRewritableUrl = (scan: ScanContext, match: RegExpExecArray): boolean =>\n isCodePosition(scan, match.index) && isWorkerConstruction(scan.code, match.index);\n\nconst replacementFor = async (\n emit: EmitContext,\n scan: ScanContext,\n match: RegExpExecArray,\n): Promise<WorkerReplacement | null> => {\n const specifier = match.groups?.specifier;\n\n if (specifier === undefined || !isRewritableUrl(scan, match)) {\n return null;\n }\n\n const resolved = await emit.context.resolve(specifier, scan.id);\n\n if (resolved === null) {\n throw unresolvedWorkerError(scan, specifier, await suggestionFor(emit, scan, specifier));\n }\n\n return { start: match.index, end: match.index + match[0].length, referenceId: claimWorker(emit, resolved.id) };\n};\n\nconst collectReplacements = async (emit: EmitContext, scan: ScanContext): Promise<WorkerReplacement[]> => {\n const pending = scan.code.matchAll(WORKER_URL).map((match) => replacementFor(emit, scan, match)).toArray();\n const found = await Promise.all(pending);\n\n return found.filter((entry) => entry !== null);\n};\n\nconst hasWorkerArgument = (scan: ScanContext, name: string): boolean => {\n const escaped = name.replaceAll(\"$\", String.raw`\\$`);\n const pattern = new RegExp(String.raw`new\\s+(?:\\w+\\.)?Worker\\s*\\(\\s*${escaped}\\s*[,)]`, \"g\");\n\n return scan.code.matchAll(pattern).some((match) => isCodePosition(scan, match.index));\n};\n\nconst hoistedUrlFor = (scan: ScanContext, match: RegExpExecArray): HoistedWorkerUrl | null => {\n const name = match.groups?.name;\n const specifier = match.groups?.specifier;\n\n if (name === undefined || specifier === undefined || !isCodePosition(scan, match.index)) {\n return null;\n }\n\n return { name, specifier };\n};\n\nconst findHoistedWorkerUrl = (scan: ScanContext): HoistedWorkerUrl | null =>\n scan.code\n .matchAll(HOISTED_WORKER_URL)\n .map((match) => hoistedUrlFor(scan, match))\n .find((entry) => entry !== null && hasWorkerArgument(scan, entry.name)) ?? null;\n\nconst applyReplacements = (code: string, replacements: WorkerReplacement[]): string => {\n let output = \"\";\n let cursor = 0;\n\n for (const { start, end, referenceId } of replacements) {\n output += code.slice(cursor, start) + workerUrlReference(referenceId);\n cursor = end;\n }\n\n return output + code.slice(cursor);\n};\n\nconst transformWorkerUrls = async (emit: EmitContext, code: string, id: string): Promise<TransformResult> => {\n if (!hasWorkerCandidate(code)) {\n return null;\n }\n\n const scan: ScanContext = { code, id, nonCode: nonCodeRanges(code) };\n const hoisted = findHoistedWorkerUrl(scan);\n\n if (hoisted !== null) {\n throw hoistedWorkerError(scan, hoisted);\n }\n\n const replacements = await collectReplacements(emit, scan);\n\n return replacements.length === 0 ? null : { code: applyReplacements(code, replacements), map: null };\n};\n\nfunction gtkxWorker(): Plugin {\n const references: WorkerReferences = { byModule: new Map(), claimed: new Set() };\n\n return {\n name: \"gtkx:worker\",\n apply: \"build\",\n\n transform(code, id) {\n return transformWorkerUrls({ context: this, references }, code, id);\n },\n\n resolveFileUrl({ referenceId, relativePath }) {\n return references.claimed.has(referenceId) ? workerUrlExpression(relativePath) : null;\n },\n };\n}\n\nexport { gtkxWorker };\n"]}
|
package/dist/vitest-plugin.d.ts
CHANGED
|
@@ -2,8 +2,8 @@ import type { Plugin } from "vite";
|
|
|
2
2
|
import { type PluginOptions } from "@gtkx/vitest";
|
|
3
3
|
/**
|
|
4
4
|
* Vite plugin stack for testing a GTKX application: the generated bindings the tests import,
|
|
5
|
-
* the plugins the app is built with, the project's compiled GSettings schemas
|
|
6
|
-
* test worker, and a headless display per worker.
|
|
5
|
+
* the plugins the app is built with, the project's compiled GSettings schemas and its configured
|
|
6
|
+
* application icon exposed to every test worker, and a headless display per worker.
|
|
7
7
|
*
|
|
8
8
|
* @returns The plugins to spread into a Vitest config.
|
|
9
9
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitest-plugin.d.ts","sourceRoot":"","sources":["../src/vitest-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"vitest-plugin.d.ts","sourceRoot":"","sources":["../src/vitest-plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AACnC,OAAmB,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAC;AAO9D;;;;;;GAMG;AACH,QAAA,MAAM,IAAI,GAAI,UAAS,aAAkB,KAAG,MAAM,EAOjD,CAAC;AAEF,eAAe,IAAI,CAAC"}
|
package/dist/vitest-plugin.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import gtkxVitest, {} from "@gtkx/vitest";
|
|
2
2
|
import { gtkxEnsureStore } from "./vite-plugins/ensure-store.js";
|
|
3
3
|
import { gtkxFontWorkerEnv } from "./vite-plugins/font-worker-env.js";
|
|
4
|
+
import { gtkxIconWorkerEnv } from "./vite-plugins/icon-worker-env.js";
|
|
4
5
|
import { gtkxVitePlugins } from "./vite-plugins/index.js";
|
|
5
6
|
import { gtkxSettingsWorkerEnv } from "./vite-plugins/settings-worker-env.js";
|
|
6
7
|
/**
|
|
7
8
|
* Vite plugin stack for testing a GTKX application: the generated bindings the tests import,
|
|
8
|
-
* the plugins the app is built with, the project's compiled GSettings schemas
|
|
9
|
-
* test worker, and a headless display per worker.
|
|
9
|
+
* the plugins the app is built with, the project's compiled GSettings schemas and its configured
|
|
10
|
+
* application icon exposed to every test worker, and a headless display per worker.
|
|
10
11
|
*
|
|
11
12
|
* @returns The plugins to spread into a Vitest config.
|
|
12
13
|
*/
|
|
@@ -15,6 +16,7 @@ const gtkx = (options = {}) => [
|
|
|
15
16
|
...gtkxVitePlugins({ configFile: options.configFile }),
|
|
16
17
|
gtkxSettingsWorkerEnv(options.configFile),
|
|
17
18
|
gtkxFontWorkerEnv(options.configFile),
|
|
19
|
+
gtkxIconWorkerEnv(options.configFile),
|
|
18
20
|
gtkxVitest(options),
|
|
19
21
|
];
|
|
20
22
|
export default gtkx;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vitest-plugin.js","sourceRoot":"","sources":["../src/vitest-plugin.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,EAAE,EAAsB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,IAAI,GAAG,CAAC,UAAyB,EAAE,EAAY,EAAE,CAAC;IACpD,eAAe,CAAC,OAAO,CAAC,UAAU,CAAC;IACnC,GAAG,eAAe,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;IACtD,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC;IACzC,iBAAiB,CAAC,OAAO,CAAC,UAAU,CAAC;IACrC,UAAU,CAAC,OAAO,CAAC;CACtB,CAAC;AAEF,eAAe,IAAI,CAAC","sourcesContent":["import type { Plugin } from \"vite\";\nimport gtkxVitest, { type PluginOptions } from \"@gtkx/vitest\";\nimport { gtkxEnsureStore } from \"./vite-plugins/ensure-store.js\";\nimport { gtkxFontWorkerEnv } from \"./vite-plugins/font-worker-env.js\";\nimport { gtkxVitePlugins } from \"./vite-plugins/index.js\";\nimport { gtkxSettingsWorkerEnv } from \"./vite-plugins/settings-worker-env.js\";\n\n/**\n * Vite plugin stack for testing a GTKX application: the generated bindings the tests import,\n * the plugins the app is built with, the project's compiled GSettings schemas
|
|
1
|
+
{"version":3,"file":"vitest-plugin.js","sourceRoot":"","sources":["../src/vitest-plugin.ts"],"names":[],"mappings":"AACA,OAAO,UAAU,EAAE,EAAsB,MAAM,cAAc,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AACtE,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,uCAAuC,CAAC;AAE9E;;;;;;GAMG;AACH,MAAM,IAAI,GAAG,CAAC,UAAyB,EAAE,EAAY,EAAE,CAAC;IACpD,eAAe,CAAC,OAAO,CAAC,UAAU,CAAC;IACnC,GAAG,eAAe,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,EAAE,CAAC;IACtD,qBAAqB,CAAC,OAAO,CAAC,UAAU,CAAC;IACzC,iBAAiB,CAAC,OAAO,CAAC,UAAU,CAAC;IACrC,iBAAiB,CAAC,OAAO,CAAC,UAAU,CAAC;IACrC,UAAU,CAAC,OAAO,CAAC;CACtB,CAAC;AAEF,eAAe,IAAI,CAAC","sourcesContent":["import type { Plugin } from \"vite\";\nimport gtkxVitest, { type PluginOptions } from \"@gtkx/vitest\";\nimport { gtkxEnsureStore } from \"./vite-plugins/ensure-store.js\";\nimport { gtkxFontWorkerEnv } from \"./vite-plugins/font-worker-env.js\";\nimport { gtkxIconWorkerEnv } from \"./vite-plugins/icon-worker-env.js\";\nimport { gtkxVitePlugins } from \"./vite-plugins/index.js\";\nimport { gtkxSettingsWorkerEnv } from \"./vite-plugins/settings-worker-env.js\";\n\n/**\n * Vite plugin stack for testing a GTKX application: the generated bindings the tests import,\n * the plugins the app is built with, the project's compiled GSettings schemas and its configured\n * application icon exposed to every test worker, and a headless display per worker.\n *\n * @returns The plugins to spread into a Vitest config.\n */\nconst gtkx = (options: PluginOptions = {}): Plugin[] => [\n gtkxEnsureStore(options.configFile),\n ...gtkxVitePlugins({ configFile: options.configFile }),\n gtkxSettingsWorkerEnv(options.configFile),\n gtkxFontWorkerEnv(options.configFile),\n gtkxIconWorkerEnv(options.configFile),\n gtkxVitest(options),\n];\n\nexport default gtkx;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gtkx/cli",
|
|
3
|
-
"version": "2.0.0-beta.
|
|
3
|
+
"version": "2.0.0-beta.9",
|
|
4
4
|
"description": "Adwaita-first GNOME app toolchain for React and TypeScript.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"gtkx",
|
|
@@ -72,33 +72,33 @@
|
|
|
72
72
|
"dependencies": {
|
|
73
73
|
"@babel/core": "^8.0.1",
|
|
74
74
|
"@babel/preset-typescript": "^8.0.1",
|
|
75
|
-
"@gtkx/codegen": "2.0.0-beta.
|
|
76
|
-
"@gtkx/config": "2.0.0-beta.
|
|
77
|
-
"@gtkx/mcp": "2.0.0-beta.
|
|
78
|
-
"@gtkx/native": "2.0.0-beta.
|
|
79
|
-
"@gtkx/runtime": "2.0.0-beta.
|
|
80
|
-
"@gtkx/utils": "2.0.0-beta.
|
|
81
|
-
"@gtkx/vitest": "2.0.0-beta.
|
|
75
|
+
"@gtkx/codegen": "2.0.0-beta.9",
|
|
76
|
+
"@gtkx/config": "2.0.0-beta.9",
|
|
77
|
+
"@gtkx/mcp": "2.0.0-beta.9",
|
|
78
|
+
"@gtkx/native": "2.0.0-beta.9",
|
|
79
|
+
"@gtkx/runtime": "2.0.0-beta.9",
|
|
80
|
+
"@gtkx/utils": "2.0.0-beta.9",
|
|
81
|
+
"@gtkx/vitest": "2.0.0-beta.9",
|
|
82
82
|
"@swc/core": "^1.16.2",
|
|
83
83
|
"babel-plugin-react-compiler": "^1.0.0",
|
|
84
84
|
"citty": "^0.2.2",
|
|
85
|
-
"confbox": "^0.
|
|
86
|
-
"create-gtkx": "2.0.0-beta.
|
|
85
|
+
"confbox": "^0.3.1",
|
|
86
|
+
"create-gtkx": "2.0.0-beta.9",
|
|
87
87
|
"fast-xml-builder": "^1.3.1",
|
|
88
88
|
"fast-xml-parser": "^5.11.1",
|
|
89
89
|
"hosted-git-info": "^10.1.1",
|
|
90
|
-
"i18next-cli": "1.
|
|
90
|
+
"i18next-cli": "1.73.1",
|
|
91
91
|
"jiti": "^2.7.0",
|
|
92
92
|
"react-refresh": "^0.18.0",
|
|
93
93
|
"vite": "^8.2.2",
|
|
94
94
|
"yaml": "^2.9.0"
|
|
95
95
|
},
|
|
96
96
|
"devDependencies": {
|
|
97
|
-
"@gtkx/testing": "2.0.0-beta.
|
|
97
|
+
"@gtkx/testing": "2.0.0-beta.9",
|
|
98
98
|
"@types/babel__core": "^7.20.5",
|
|
99
99
|
"@types/hosted-git-info": "^3.0.5",
|
|
100
100
|
"@types/react-refresh": "^0.14.7",
|
|
101
|
-
"vitest": "^
|
|
101
|
+
"vitest": "^5.0.0"
|
|
102
102
|
},
|
|
103
103
|
"peerDependencies": {
|
|
104
104
|
"react": "^19"
|
package/src/commands/deploy.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { defineCommand } from "citty";
|
|
2
2
|
import { KNOWN_NAMES } from "../deploy/registry.js";
|
|
3
3
|
import { runDeploy } from "../deploy/run-deploy.js";
|
|
4
|
+
import { KNOWN_ARCH_NAMES } from "../deploy/settings/arch.js";
|
|
4
5
|
import { configArg, entryArg } from "../internal/entry-arg.js";
|
|
5
6
|
import { resolveProject } from "../internal/prepare-project.js";
|
|
6
7
|
|
|
@@ -18,6 +19,10 @@ const deploy = defineCommand({
|
|
|
18
19
|
type: "string",
|
|
19
20
|
description: `Comma-separated package formats to build (${KNOWN_NAMES})`,
|
|
20
21
|
},
|
|
22
|
+
arch: {
|
|
23
|
+
type: "string",
|
|
24
|
+
description: `Comma-separated architectures to build (${KNOWN_ARCH_NAMES}; default: the host)`,
|
|
25
|
+
},
|
|
21
26
|
out: {
|
|
22
27
|
type: "string",
|
|
23
28
|
description: "Output directory relative to the project root (default: build)",
|
|
@@ -39,6 +44,7 @@ const deploy = defineCommand({
|
|
|
39
44
|
cwd,
|
|
40
45
|
configFile,
|
|
41
46
|
targets: args.target,
|
|
47
|
+
arches: args.arch,
|
|
42
48
|
outDir: args.out,
|
|
43
49
|
shouldPrintManifests: args["print-manifests"] === true,
|
|
44
50
|
shouldSkipBuild: args["skip-build"] === true,
|
package/src/deploy/depends.ts
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import type { DeploySettings } from "./types.js";
|
|
2
|
+
import { PACKAGED_SONAMES, type PackagedSoname } from "./node-runtime/sonames.js";
|
|
2
3
|
|
|
3
4
|
type Relations = {
|
|
4
5
|
deb: string[];
|
|
5
6
|
rpm: string[];
|
|
6
7
|
};
|
|
7
8
|
|
|
8
|
-
type
|
|
9
|
+
type PackageNames = {
|
|
9
10
|
deb: string;
|
|
10
11
|
rpm: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
type LibraryPackages = PackageNames & {
|
|
11
15
|
extra: Relations;
|
|
12
16
|
};
|
|
13
17
|
|
|
@@ -25,6 +29,17 @@ const DEPENDS_BY_LIBRARY: Record<string, LibraryPackages> = {
|
|
|
25
29
|
"Gtk-4.0": { deb: "libgtk-4-1", rpm: "gtk4", extra: { deb: [], rpm: [GLES_SONAME] } },
|
|
26
30
|
};
|
|
27
31
|
|
|
32
|
+
const RUNTIME_PACKAGES: Record<PackagedSoname, PackageNames> = {
|
|
33
|
+
"libatomic.so.1": { deb: "libatomic1", rpm: `libatomic.so.1${SONAME_SUFFIX}` },
|
|
34
|
+
"libgcc_s.so.1": { deb: "libgcc-s1", rpm: `libgcc_s.so.1${SONAME_SUFFIX}` },
|
|
35
|
+
"libstdc++.so.6": { deb: "libstdc++6", rpm: `libstdc++.so.6${SONAME_SUFFIX}` },
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const RUNTIME_DEPENDS: Relations = {
|
|
39
|
+
deb: PACKAGED_SONAMES.map((soname) => RUNTIME_PACKAGES[soname].deb),
|
|
40
|
+
rpm: PACKAGED_SONAMES.map((soname) => RUNTIME_PACKAGES[soname].rpm),
|
|
41
|
+
};
|
|
42
|
+
|
|
28
43
|
const debRelation = (name: string, minimum: string | undefined): string =>
|
|
29
44
|
minimum === undefined ? name : `${name} (>= ${minimum})`;
|
|
30
45
|
|
|
@@ -63,8 +78,20 @@ const resolveDepends = (settings: DeploySettings, glibcMinimum: string | null):
|
|
|
63
78
|
const extra = settings.deploy.depends ?? {};
|
|
64
79
|
|
|
65
80
|
return {
|
|
66
|
-
deb: dedupe([
|
|
67
|
-
|
|
81
|
+
deb: dedupe([
|
|
82
|
+
...fromLibraries.deb,
|
|
83
|
+
...BASE_DEPENDS.deb,
|
|
84
|
+
...fromGlibc.deb,
|
|
85
|
+
...RUNTIME_DEPENDS.deb,
|
|
86
|
+
...(extra.deb ?? []),
|
|
87
|
+
]),
|
|
88
|
+
rpm: dedupe([
|
|
89
|
+
...fromLibraries.rpm,
|
|
90
|
+
...BASE_DEPENDS.rpm,
|
|
91
|
+
...fromGlibc.rpm,
|
|
92
|
+
...RUNTIME_DEPENDS.rpm,
|
|
93
|
+
...(extra.rpm ?? []),
|
|
94
|
+
]),
|
|
68
95
|
};
|
|
69
96
|
};
|
|
70
97
|
|
package/src/deploy/download.ts
CHANGED
|
@@ -11,17 +11,23 @@ type DigestRequest = {
|
|
|
11
11
|
subject: string;
|
|
12
12
|
};
|
|
13
13
|
|
|
14
|
+
type DigestAlgorithm = "sha256" | "sha512";
|
|
15
|
+
|
|
14
16
|
type DownloadRequest = {
|
|
15
17
|
url: string;
|
|
16
18
|
dest: string;
|
|
17
19
|
label: string;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
+
digest: string;
|
|
21
|
+
algorithm?: DigestAlgorithm | undefined;
|
|
22
|
+
freshDigest?: (() => Promise<string>) | undefined;
|
|
20
23
|
mode?: number | undefined;
|
|
21
24
|
};
|
|
22
25
|
|
|
23
26
|
const CACHE_NAMESPACE = "gtkx";
|
|
24
|
-
const DIGEST_PATTERN =
|
|
27
|
+
const DIGEST_PATTERN: Record<DigestAlgorithm, RegExp> = {
|
|
28
|
+
sha256: /^[\da-f]{64}$/,
|
|
29
|
+
sha512: /^[\da-f]{128}$/,
|
|
30
|
+
};
|
|
25
31
|
|
|
26
32
|
const cacheRoot = (): string => {
|
|
27
33
|
const base = process.env.XDG_CACHE_HOME;
|
|
@@ -36,10 +42,11 @@ const cacheDir = (segments: string[]): string => {
|
|
|
36
42
|
return dir;
|
|
37
43
|
};
|
|
38
44
|
|
|
39
|
-
const getDigest = (contents: Buffer): string =>
|
|
45
|
+
const getDigest = (contents: Buffer, algorithm: DigestAlgorithm): string =>
|
|
46
|
+
createHash(algorithm).update(contents).digest("hex");
|
|
40
47
|
|
|
41
|
-
const assertDigest = (url: string, contents: Buffer, expected: string): void => {
|
|
42
|
-
const actual = getDigest(contents);
|
|
48
|
+
const assertDigest = (url: string, contents: Buffer, expected: string, algorithm: DigestAlgorithm): void => {
|
|
49
|
+
const actual = getDigest(contents, algorithm);
|
|
43
50
|
|
|
44
51
|
if (actual !== expected) {
|
|
45
52
|
throw new Error(`Checksum mismatch for ${url}\n expected ${expected}\n received ${actual}`);
|
|
@@ -62,11 +69,11 @@ const fetchText = async (url: string): Promise<string> => {
|
|
|
62
69
|
return bytes.toString("utf8");
|
|
63
70
|
};
|
|
64
71
|
|
|
65
|
-
const readCachedDigest = (dest: string): string | undefined => {
|
|
72
|
+
const readCachedDigest = (dest: string, algorithm: DigestAlgorithm): string | undefined => {
|
|
66
73
|
try {
|
|
67
74
|
const cached = readFileSync(dest, "utf8").trim();
|
|
68
75
|
|
|
69
|
-
return DIGEST_PATTERN.test(cached) ? cached : undefined;
|
|
76
|
+
return DIGEST_PATTERN[algorithm].test(cached) ? cached : undefined;
|
|
70
77
|
} catch {
|
|
71
78
|
return undefined;
|
|
72
79
|
}
|
|
@@ -82,7 +89,7 @@ const publishedDigest = async (request: DigestRequest): Promise<string> => {
|
|
|
82
89
|
const checksums = await fetchText(request.url);
|
|
83
90
|
const digest = digestFromChecksums(checksums, request.assetName, request.subject);
|
|
84
91
|
|
|
85
|
-
if (!DIGEST_PATTERN.test(digest)) {
|
|
92
|
+
if (!DIGEST_PATTERN.sha256.test(digest)) {
|
|
86
93
|
throw new Error(`${request.subject} published a malformed checksum for ${request.assetName}: "${digest}"`);
|
|
87
94
|
}
|
|
88
95
|
|
|
@@ -92,17 +99,17 @@ const publishedDigest = async (request: DigestRequest): Promise<string> => {
|
|
|
92
99
|
};
|
|
93
100
|
|
|
94
101
|
const cachedDigest = async (request: DigestRequest): Promise<string> => {
|
|
95
|
-
const cached = readCachedDigest(request.dest);
|
|
102
|
+
const cached = readCachedDigest(request.dest, "sha256");
|
|
96
103
|
|
|
97
104
|
return cached ?? publishedDigest(request);
|
|
98
105
|
};
|
|
99
106
|
|
|
100
|
-
const isCacheUsable = (dest: string,
|
|
107
|
+
const isCacheUsable = (dest: string, digest: string, algorithm: DigestAlgorithm): boolean => {
|
|
101
108
|
if (!existsSync(dest)) {
|
|
102
109
|
return false;
|
|
103
110
|
}
|
|
104
111
|
|
|
105
|
-
if (getDigest(readFileSync(dest)) ===
|
|
112
|
+
if (getDigest(readFileSync(dest), algorithm) === digest) {
|
|
106
113
|
return true;
|
|
107
114
|
}
|
|
108
115
|
|
|
@@ -112,16 +119,17 @@ const isCacheUsable = (dest: string, sha256: string): boolean => {
|
|
|
112
119
|
};
|
|
113
120
|
|
|
114
121
|
const downloadFile = async (request: DownloadRequest): Promise<string> => {
|
|
115
|
-
const { url, dest, label,
|
|
122
|
+
const { url, dest, label, digest, mode } = request;
|
|
123
|
+
const algorithm = request.algorithm ?? "sha256";
|
|
116
124
|
|
|
117
|
-
if (isCacheUsable(dest,
|
|
125
|
+
if (isCacheUsable(dest, digest, algorithm)) {
|
|
118
126
|
return dest;
|
|
119
127
|
}
|
|
120
128
|
|
|
121
129
|
info(`Downloading ${label}`);
|
|
122
|
-
const expected = request.
|
|
130
|
+
const expected = request.freshDigest === undefined ? digest : await request.freshDigest();
|
|
123
131
|
const contents = await fetchBytes(url);
|
|
124
|
-
assertDigest(url, contents, expected);
|
|
132
|
+
assertDigest(url, contents, expected, algorithm);
|
|
125
133
|
const staging = `${dest}.partial`;
|
|
126
134
|
writeFileSync(staging, contents);
|
|
127
135
|
|
|
@@ -145,4 +153,12 @@ const digestFromChecksums = (checksums: string, assetName: string, subject: stri
|
|
|
145
153
|
return digest;
|
|
146
154
|
};
|
|
147
155
|
|
|
148
|
-
export {
|
|
156
|
+
export {
|
|
157
|
+
cacheDir,
|
|
158
|
+
cachedDigest,
|
|
159
|
+
type DigestRequest,
|
|
160
|
+
downloadFile,
|
|
161
|
+
publishedDigest,
|
|
162
|
+
readCachedDigest,
|
|
163
|
+
writeAtomically,
|
|
164
|
+
};
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import type { DeployArchName, DeploySettings } from "./types.js";
|
|
5
|
+
import { runCliTool } from "../internal/run-cli-tool.js";
|
|
6
|
+
import { cacheDir, downloadFile, readCachedDigest, writeAtomically } from "./download.js";
|
|
7
|
+
import { isHostArch } from "./settings/arch.js";
|
|
8
|
+
|
|
9
|
+
type ProjectRequire = ReturnType<typeof createRequire>;
|
|
10
|
+
type Tarball = { url: string; digest: string };
|
|
11
|
+
|
|
12
|
+
const BINDING_FILENAME = "gtkx.node";
|
|
13
|
+
const DEFAULT_REGISTRY = "https://registry.npmjs.org";
|
|
14
|
+
const NATIVE_MANIFEST = "@gtkx/native/package.json";
|
|
15
|
+
const PACKAGE_ROOT = "package";
|
|
16
|
+
const REQUEST_TIMEOUT = 30_000;
|
|
17
|
+
const SRI_PREFIX = "sha512-";
|
|
18
|
+
const STRIP_COMPONENTS = "1";
|
|
19
|
+
const VERSION_PATTERN = /^[\w.+-]+$/;
|
|
20
|
+
|
|
21
|
+
const binaryFilename = (arch: DeployArchName): string => `native.linux-${arch}-gnu.node`;
|
|
22
|
+
const platformStem = (arch: DeployArchName): string => `native-linux-${arch}-gnu`;
|
|
23
|
+
const platformPackage = (arch: DeployArchName): string => `@gtkx/${platformStem(arch)}`;
|
|
24
|
+
|
|
25
|
+
const withoutTrailingSlash = (value: string): string => {
|
|
26
|
+
let end = value.length;
|
|
27
|
+
|
|
28
|
+
while (end > 0 && value[end - 1] === "/") {
|
|
29
|
+
end -= 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return value.slice(0, end);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const registryUrl = (): string => {
|
|
36
|
+
const configured = process.env.npm_config_registry;
|
|
37
|
+
|
|
38
|
+
return withoutTrailingSlash(configured !== undefined && configured.length > 0 ? configured : DEFAULT_REGISTRY);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const projectRequireFor = (root: string): ProjectRequire => createRequire(join(root, "package.json"));
|
|
42
|
+
|
|
43
|
+
const localBinary = (projectRequire: ProjectRequire, arch: DeployArchName): string | null => {
|
|
44
|
+
const nativeRoot = dirname(projectRequire.resolve(NATIVE_MANIFEST));
|
|
45
|
+
const candidate = join(nativeRoot, binaryFilename(arch));
|
|
46
|
+
|
|
47
|
+
return existsSync(candidate) ? candidate : null;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const installedBinary = (projectRequire: ProjectRequire, arch: DeployArchName): string | null => {
|
|
51
|
+
try {
|
|
52
|
+
return projectRequire.resolve(platformPackage(arch));
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const manifestVersion = (manifest: unknown): string | null => {
|
|
59
|
+
if (typeof manifest !== "object" || manifest === null || !("version" in manifest)) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const version = manifest.version;
|
|
64
|
+
|
|
65
|
+
return typeof version === "string" && VERSION_PATTERN.test(version) ? version : null;
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
const nativeVersion = (projectRequire: ProjectRequire): string => {
|
|
69
|
+
const parsed: unknown = JSON.parse(readFileSync(projectRequire.resolve(NATIVE_MANIFEST), "utf8"));
|
|
70
|
+
const version = manifestVersion(parsed);
|
|
71
|
+
|
|
72
|
+
if (version === null) {
|
|
73
|
+
throw new Error("Cannot read a usable version from @gtkx/native; is it installed?");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return version;
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const hexFromIntegrity = (integrity: unknown, subject: string): string => {
|
|
80
|
+
if (typeof integrity !== "string" || !integrity.startsWith(SRI_PREFIX)) {
|
|
81
|
+
throw new Error(`The npm registry publishes no sha512 integrity for ${subject}`);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return Buffer.from(integrity.slice(SRI_PREFIX.length), "base64").toString("hex");
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const fetchVersionDocument = async (url: string, subject: string): Promise<Record<string, unknown>> => {
|
|
88
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(REQUEST_TIMEOUT) });
|
|
89
|
+
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
throw new Error(
|
|
92
|
+
`Cannot fetch ${subject}: HTTP ${String(response.status)} ${response.statusText}. ` +
|
|
93
|
+
`Install ${subject} directly, or drop that architecture from --arch.`,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const document: unknown = await response.json();
|
|
98
|
+
|
|
99
|
+
if (typeof document !== "object" || document === null) {
|
|
100
|
+
throw new Error(`The npm registry returned no metadata for ${subject}`);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return document as Record<string, unknown>;
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
const publishedTarball = async (arch: DeployArchName, version: string): Promise<Tarball> => {
|
|
107
|
+
const registry = registryUrl();
|
|
108
|
+
const subject = `${platformPackage(arch)}@${version}`;
|
|
109
|
+
const url = `${registry}/${encodeURIComponent(platformPackage(arch))}/${version}`;
|
|
110
|
+
const document = await fetchVersionDocument(url, subject);
|
|
111
|
+
const dist: unknown = document.dist;
|
|
112
|
+
|
|
113
|
+
if (typeof dist !== "object" || dist === null) {
|
|
114
|
+
throw new Error(`The npm registry returned no tarball for ${subject}`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const entry = dist as Record<string, unknown>;
|
|
118
|
+
const tarball = entry.tarball;
|
|
119
|
+
|
|
120
|
+
if (typeof tarball !== "string" || new URL(tarball).origin !== new URL(registry).origin) {
|
|
121
|
+
throw new Error(`The npm registry returned an unusable tarball URL for ${subject}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return { url: tarball, digest: hexFromIntegrity(entry.integrity, subject) };
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const extractAddon = (archive: string, dir: string, arch: DeployArchName): void => {
|
|
128
|
+
runCliTool({
|
|
129
|
+
tool: "tar",
|
|
130
|
+
args: ["-xzf", archive, "-C", dir, "--strip-components", STRIP_COMPONENTS,
|
|
131
|
+
`${PACKAGE_ROOT}/${binaryFilename(arch)}`],
|
|
132
|
+
target: platformPackage(arch),
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
const cachedTarballUrl = (arch: DeployArchName, version: string): string =>
|
|
137
|
+
`${registryUrl()}/${platformPackage(arch)}/-/${platformStem(arch)}-${version}.tgz`;
|
|
138
|
+
|
|
139
|
+
const tarballFor = async (arch: DeployArchName, version: string, sidecar: string): Promise<Tarball> => {
|
|
140
|
+
const cached = readCachedDigest(sidecar, "sha512");
|
|
141
|
+
|
|
142
|
+
if (cached !== undefined) {
|
|
143
|
+
return { url: cachedTarballUrl(arch, version), digest: cached };
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const published = await publishedTarball(arch, version);
|
|
147
|
+
writeAtomically(sidecar, published.digest);
|
|
148
|
+
|
|
149
|
+
return published;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const downloadedBinary = async (arch: DeployArchName, version: string): Promise<string> => {
|
|
153
|
+
const dir = cacheDir(["native", `${version}-linux-${arch}`]);
|
|
154
|
+
const archive = join(dir, `${platformStem(arch)}-${version}.tgz`);
|
|
155
|
+
const tarball = await tarballFor(arch, version, `${archive}.sha512`);
|
|
156
|
+
|
|
157
|
+
await downloadFile({
|
|
158
|
+
url: tarball.url,
|
|
159
|
+
dest: archive,
|
|
160
|
+
label: `${platformPackage(arch)} ${version}`,
|
|
161
|
+
digest: tarball.digest,
|
|
162
|
+
algorithm: "sha512",
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
extractAddon(archive, dir, arch);
|
|
166
|
+
|
|
167
|
+
return join(dir, binaryFilename(arch));
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const resolveStagedAddon = async (settings: DeploySettings): Promise<string | null> => {
|
|
171
|
+
const arch = settings.arch.node;
|
|
172
|
+
|
|
173
|
+
if (isHostArch(arch)) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const projectRequire = projectRequireFor(settings.paths.root);
|
|
178
|
+
const resolved = localBinary(projectRequire, arch) ?? installedBinary(projectRequire, arch);
|
|
179
|
+
|
|
180
|
+
return resolved ?? await downloadedBinary(arch, nativeVersion(projectRequire));
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export { BINDING_FILENAME, resolveStagedAddon };
|