@vmz/vmz 0.0.4 → 0.1.1
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/build-assemble.d.ts +52 -0
- package/dist/build-assemble.js +192 -0
- package/dist/cdn-policy.d.ts +22 -4
- package/dist/cdn-policy.js +109 -13
- package/dist/cli.js +122 -27
- package/dist/content-addressed-assets.js +2 -1
- package/dist/delivery-profile.d.ts +84 -0
- package/dist/delivery-profile.js +348 -0
- package/dist/dev-session.d.ts +6 -0
- package/dist/dev-session.js +167 -40
- package/dist/document-build.js +33 -32
- package/dist/document-cmd.js +2 -9
- package/dist/document-enrich.js +8 -0
- package/dist/document-integrate.js +10 -17
- package/dist/embedded-packaging.d.ts +22 -0
- package/dist/embedded-packaging.js +109 -0
- package/dist/index.d.ts +26 -1
- package/dist/index.js +69 -2
- package/dist/locale-check.js +39 -66
- package/dist/locale-cmd.js +12 -44
- package/dist/locale-route-emit.d.ts +37 -0
- package/dist/locale-route-emit.js +109 -0
- package/dist/locale-router.d.ts +20 -0
- package/dist/locale-router.js +68 -0
- package/dist/log.d.ts +2 -2
- package/dist/log.js +11 -3
- package/dist/mini-host.d.ts +47 -0
- package/dist/mini-host.js +202 -0
- package/dist/native-addon.d.ts +9 -0
- package/dist/native-addon.js +84 -0
- package/dist/pack-client-packages.d.ts +25 -0
- package/dist/pack-client-packages.js +399 -0
- package/dist/pack.d.ts +58 -0
- package/dist/pack.js +123 -0
- package/dist/plugin-host.d.ts +1 -1
- package/dist/plugin-host.js +2 -2
- package/dist/pretty-json.d.ts +19 -0
- package/dist/pretty-json.js +43 -0
- package/dist/production-observability.js +3 -2
- package/dist/production-test-pack.d.ts +0 -14
- package/dist/production-test-pack.js +27 -31
- package/dist/release-pack.js +17 -21
- package/dist/route-path.d.ts +35 -0
- package/dist/route-path.js +77 -0
- package/dist/server-artifact.d.ts +140 -0
- package/dist/server-artifact.js +204 -0
- package/dist/server-language-backend.d.ts +89 -0
- package/dist/server-language-backend.js +121 -0
- package/dist/site-delivery.js +3 -2
- package/dist/static-emit.d.ts +10 -1
- package/dist/static-emit.js +192 -97
- package/dist/test-cmd.js +2 -1
- package/dist/wechat-packaging.d.ts +22 -0
- package/dist/wechat-packaging.js +59 -0
- package/package.json +12 -12
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser-safe client package lowering (Pack stage thin slice).
|
|
3
|
+
*
|
|
4
|
+
* Bare npm/workspace imports are legal on the author surface (01/04).
|
|
5
|
+
* Browser ESM cannot resolve them. Pack materializes reachable package
|
|
6
|
+
* modules under `dist/vendor/<pkg>/…` and rewrites importers to relative paths.
|
|
7
|
+
*
|
|
8
|
+
* Not full oxc chunk-split/minify (`oxc-pending` remains for release minify).
|
|
9
|
+
*/
|
|
10
|
+
// @ts-nocheck
|
|
11
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs';
|
|
12
|
+
import path from 'node:path';
|
|
13
|
+
import { createRequire } from 'node:module';
|
|
14
|
+
import { resolvePackageRoot } from './packages.js';
|
|
15
|
+
const VENDOR_DIR = 'vendor';
|
|
16
|
+
const SKIP_PREFIXES = ['node:', 'nodejs:', 'cloudflare:', 'data:', 'http:', 'https:', 'vmz:', '#'];
|
|
17
|
+
/**
|
|
18
|
+
* @param {string} outDir
|
|
19
|
+
* @param {{ projectRoot?: string | null }} [opts]
|
|
20
|
+
* @returns {{ rewrittenFiles: number, vendoredModules: string[], bareSpecs: string[] }}
|
|
21
|
+
*/
|
|
22
|
+
export function packClientBareImports(outDir, opts = {}) {
|
|
23
|
+
const projectRoot = opts.projectRoot ? path.resolve(opts.projectRoot) : path.dirname(path.resolve(outDir));
|
|
24
|
+
/** @type {Map<string, string>} bareSpec → absolute vendored .js path */
|
|
25
|
+
const bareToVendor = new Map();
|
|
26
|
+
/** @type {Set<string>} absolute source files already materialized */
|
|
27
|
+
const materializedSources = new Set();
|
|
28
|
+
/** @type {string[]} */
|
|
29
|
+
const bareQueue = [];
|
|
30
|
+
/** @type {string[]} */
|
|
31
|
+
const unresolved = [];
|
|
32
|
+
/** @type {string[]} */
|
|
33
|
+
const skippedVmz = [];
|
|
34
|
+
for (const file of listClientJs(outDir)) {
|
|
35
|
+
for (const spec of collectBareSpecs(readFileSync(file, 'utf8'))) {
|
|
36
|
+
if (!bareQueue.includes(spec))
|
|
37
|
+
bareQueue.push(spec);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
while (bareQueue.length) {
|
|
41
|
+
const spec = bareQueue.shift();
|
|
42
|
+
if (bareToVendor.has(spec))
|
|
43
|
+
continue;
|
|
44
|
+
const resolved = resolveBareToSource(projectRoot, spec);
|
|
45
|
+
if (!resolved) {
|
|
46
|
+
if (!unresolved.includes(spec))
|
|
47
|
+
unresolved.push(spec);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (/\.vmz$/i.test(resolved.sourceFile)) {
|
|
51
|
+
if (!skippedVmz.includes(spec))
|
|
52
|
+
skippedVmz.push(spec);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const destAbs = materializeSourceTree(outDir, resolved, materializedSources, bareQueue);
|
|
56
|
+
if (destAbs)
|
|
57
|
+
bareToVendor.set(spec, destAbs);
|
|
58
|
+
else if (!unresolved.includes(spec))
|
|
59
|
+
unresolved.push(spec);
|
|
60
|
+
}
|
|
61
|
+
/** @type {string[]} */
|
|
62
|
+
const rewritten = [];
|
|
63
|
+
for (const file of [...listClientJs(outDir), ...listVendorJs(outDir)]) {
|
|
64
|
+
const before = readFileSync(file, 'utf8');
|
|
65
|
+
let after = rewriteBareImports(before, file, bareToVendor);
|
|
66
|
+
after = rewriteRelativeTsSpecs(after);
|
|
67
|
+
if (after !== before) {
|
|
68
|
+
writeFileSync(file, after, 'utf8');
|
|
69
|
+
rewritten.push(path.relative(outDir, file).replace(/\\/g, '/'));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Any bare that remains in client (non-vendor) JS after rewrite is still browser-broken.
|
|
73
|
+
/** @type {string[]} */
|
|
74
|
+
const remaining = [];
|
|
75
|
+
for (const file of listClientJs(outDir)) {
|
|
76
|
+
for (const spec of collectBareSpecs(readFileSync(file, 'utf8'))) {
|
|
77
|
+
if (!remaining.includes(spec))
|
|
78
|
+
remaining.push(spec);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
rewrittenFiles: rewritten.length,
|
|
83
|
+
vendoredModules: [...bareToVendor.values()].map((abs) => path.relative(outDir, abs).replace(/\\/g, '/')),
|
|
84
|
+
bareSpecs: [...bareToVendor.keys()],
|
|
85
|
+
unresolvedBareSpecs: unresolved,
|
|
86
|
+
skippedVmzExports: skippedVmz,
|
|
87
|
+
remainingBareSpecs: remaining,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Materialize `resolved.sourceFile` and its relative import closure under vendor/.
|
|
92
|
+
* @returns {string | null} vendor path for the entry source
|
|
93
|
+
*/
|
|
94
|
+
function materializeSourceTree(outDir, resolved, materializedSources, bareQueue) {
|
|
95
|
+
/** @type {string[]} */
|
|
96
|
+
const sourceQueue = [resolved.sourceFile];
|
|
97
|
+
// Subpath bare specs often land on a file already vendored via a relative
|
|
98
|
+
// import from the package root — still map the bare name to that vendor path.
|
|
99
|
+
/** @type {string | null} */
|
|
100
|
+
let entryDest = vendorPathForSource(outDir, resolved.pkgName, resolved.pkgRoot, resolved.sourceFile);
|
|
101
|
+
while (sourceQueue.length) {
|
|
102
|
+
const sourceFile = sourceQueue.shift();
|
|
103
|
+
if (materializedSources.has(sourceFile))
|
|
104
|
+
continue;
|
|
105
|
+
materializedSources.add(sourceFile);
|
|
106
|
+
const destAbs = vendorPathForSource(outDir, resolved.pkgName, resolved.pkgRoot, sourceFile);
|
|
107
|
+
mkdirSync(path.dirname(destAbs), { recursive: true });
|
|
108
|
+
const js = materializeModule(sourceFile);
|
|
109
|
+
writeFileSync(destAbs, js, 'utf8');
|
|
110
|
+
if (sourceFile === resolved.sourceFile)
|
|
111
|
+
entryDest = destAbs;
|
|
112
|
+
for (const bare of collectBareSpecs(js)) {
|
|
113
|
+
if (!bareQueue.includes(bare))
|
|
114
|
+
bareQueue.push(bare);
|
|
115
|
+
}
|
|
116
|
+
for (const rel of collectRelativeSpecs(js)) {
|
|
117
|
+
const target = resolveRelativeSource(path.dirname(sourceFile), rel);
|
|
118
|
+
if (target && !materializedSources.has(target))
|
|
119
|
+
sourceQueue.push(target);
|
|
120
|
+
}
|
|
121
|
+
// Also follow relative imports as written in the *source* (before transpile),
|
|
122
|
+
// so `./catalog.ts` is discovered even if transpile already rewrote to `.js`.
|
|
123
|
+
const raw = readFileSync(sourceFile, 'utf8');
|
|
124
|
+
for (const rel of collectRelativeSpecs(raw)) {
|
|
125
|
+
const target = resolveRelativeSource(path.dirname(sourceFile), rel);
|
|
126
|
+
if (target && !materializedSources.has(target))
|
|
127
|
+
sourceQueue.push(target);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return entryDest;
|
|
131
|
+
}
|
|
132
|
+
function listClientJs(outDir) {
|
|
133
|
+
/** @type {string[]} */
|
|
134
|
+
const out = [];
|
|
135
|
+
walk(outDir, (file) => {
|
|
136
|
+
const rel = path.relative(outDir, file).replace(/\\/g, '/');
|
|
137
|
+
if (rel.startsWith('_vmz/') || rel.startsWith(`${VENDOR_DIR}/`) || rel.startsWith('#server/') || rel.startsWith('_vmz_server/')) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (rel.endsWith('.js') || rel.endsWith('.mjs'))
|
|
141
|
+
out.push(file);
|
|
142
|
+
});
|
|
143
|
+
return out;
|
|
144
|
+
}
|
|
145
|
+
function listVendorJs(outDir) {
|
|
146
|
+
const root = path.join(outDir, VENDOR_DIR);
|
|
147
|
+
if (!existsSync(root))
|
|
148
|
+
return [];
|
|
149
|
+
/** @type {string[]} */
|
|
150
|
+
const out = [];
|
|
151
|
+
walk(root, (file) => {
|
|
152
|
+
if (file.endsWith('.js') || file.endsWith('.mjs'))
|
|
153
|
+
out.push(file);
|
|
154
|
+
});
|
|
155
|
+
return out;
|
|
156
|
+
}
|
|
157
|
+
function walk(dir, visit) {
|
|
158
|
+
if (!existsSync(dir))
|
|
159
|
+
return;
|
|
160
|
+
for (const name of readdirSync(dir)) {
|
|
161
|
+
if (name === 'node_modules')
|
|
162
|
+
continue;
|
|
163
|
+
const full = path.join(dir, name);
|
|
164
|
+
let st;
|
|
165
|
+
try {
|
|
166
|
+
st = statSync(full);
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (st.isDirectory())
|
|
172
|
+
walk(full, visit);
|
|
173
|
+
else
|
|
174
|
+
visit(full);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
/** @param {string} js */
|
|
178
|
+
export function collectBareSpecs(js) {
|
|
179
|
+
/** @type {Set<string>} */
|
|
180
|
+
const specs = new Set();
|
|
181
|
+
const add = (spec) => {
|
|
182
|
+
if (!spec || isRelativeOrAbsolute(spec) || shouldSkipBare(spec))
|
|
183
|
+
return;
|
|
184
|
+
specs.add(spec);
|
|
185
|
+
};
|
|
186
|
+
// `from 'x'` covers import/export … from
|
|
187
|
+
let m;
|
|
188
|
+
const fromRe = /\bfrom\s+['"]([^'"]+)['"]/g;
|
|
189
|
+
while ((m = fromRe.exec(js)))
|
|
190
|
+
add(m[1]);
|
|
191
|
+
const dynRe = /\bimport\s*\(\s*['"]([^'"]+)['"]\s*\)/g;
|
|
192
|
+
while ((m = dynRe.exec(js)))
|
|
193
|
+
add(m[1]);
|
|
194
|
+
// side-effect: import 'x' (not import( and not import … from)
|
|
195
|
+
const sideRe = /\bimport\s+['"]([^'"]+)['"]/g;
|
|
196
|
+
while ((m = sideRe.exec(js)))
|
|
197
|
+
add(m[1]);
|
|
198
|
+
return [...specs];
|
|
199
|
+
}
|
|
200
|
+
function collectRelativeSpecs(js) {
|
|
201
|
+
/** @type {Set<string>} */
|
|
202
|
+
const specs = new Set();
|
|
203
|
+
const re = /(?:from\s+|import\s*\(\s*)['"](\.[^'"]+)['"]/g;
|
|
204
|
+
let m;
|
|
205
|
+
while ((m = re.exec(js)))
|
|
206
|
+
specs.add(m[1]);
|
|
207
|
+
return [...specs];
|
|
208
|
+
}
|
|
209
|
+
function isRelativeOrAbsolute(spec) {
|
|
210
|
+
return spec.startsWith('.') || spec.startsWith('/') || spec.startsWith('\\');
|
|
211
|
+
}
|
|
212
|
+
function shouldSkipBare(spec) {
|
|
213
|
+
return SKIP_PREFIXES.some((p) => spec.startsWith(p));
|
|
214
|
+
}
|
|
215
|
+
function resolveBareToSource(projectRoot, spec) {
|
|
216
|
+
const { pkgName, subpath } = splitPackageSpec(spec);
|
|
217
|
+
let pkgRoot = resolvePackageRoot(projectRoot, pkgName);
|
|
218
|
+
if (!pkgRoot) {
|
|
219
|
+
let cur = projectRoot;
|
|
220
|
+
for (let i = 0; i < 8; i++) {
|
|
221
|
+
pkgRoot = resolvePackageRoot(cur, pkgName);
|
|
222
|
+
if (pkgRoot)
|
|
223
|
+
break;
|
|
224
|
+
const parent = path.dirname(cur);
|
|
225
|
+
if (parent === cur)
|
|
226
|
+
break;
|
|
227
|
+
cur = parent;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (!pkgRoot)
|
|
231
|
+
return null;
|
|
232
|
+
const sourceFile = resolveExportFile(pkgRoot, subpath);
|
|
233
|
+
if (!sourceFile)
|
|
234
|
+
return null;
|
|
235
|
+
return { pkgName, pkgRoot, subpath, sourceFile };
|
|
236
|
+
}
|
|
237
|
+
function splitPackageSpec(spec) {
|
|
238
|
+
if (spec.startsWith('@')) {
|
|
239
|
+
const parts = spec.split('/');
|
|
240
|
+
if (parts.length < 2)
|
|
241
|
+
return { pkgName: spec, subpath: '' };
|
|
242
|
+
return { pkgName: `${parts[0]}/${parts[1]}`, subpath: parts.slice(2).join('/') };
|
|
243
|
+
}
|
|
244
|
+
const i = spec.indexOf('/');
|
|
245
|
+
if (i < 0)
|
|
246
|
+
return { pkgName: spec, subpath: '' };
|
|
247
|
+
return { pkgName: spec.slice(0, i), subpath: spec.slice(i + 1) };
|
|
248
|
+
}
|
|
249
|
+
function resolveExportFile(pkgRoot, subpath) {
|
|
250
|
+
const pkgPath = path.join(pkgRoot, 'package.json');
|
|
251
|
+
if (!existsSync(pkgPath))
|
|
252
|
+
return null;
|
|
253
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
254
|
+
const key = subpath ? `./${subpath}` : '.';
|
|
255
|
+
let target = null;
|
|
256
|
+
if (pkg.exports && typeof pkg.exports === 'object' && !Array.isArray(pkg.exports)) {
|
|
257
|
+
const entry = pkg.exports[key] ?? (!subpath ? pkg.exports['.'] : null);
|
|
258
|
+
target = flattenExportTarget(entry);
|
|
259
|
+
}
|
|
260
|
+
if (!target && !subpath)
|
|
261
|
+
target = pkg.module || pkg.main || './index.js';
|
|
262
|
+
if (!target && subpath) {
|
|
263
|
+
for (const cand of [
|
|
264
|
+
path.join(pkgRoot, 'src', `${subpath}.ts`),
|
|
265
|
+
path.join(pkgRoot, 'src', `${subpath}.js`),
|
|
266
|
+
path.join(pkgRoot, `${subpath}.ts`),
|
|
267
|
+
path.join(pkgRoot, `${subpath}.js`),
|
|
268
|
+
path.join(pkgRoot, 'src', subpath, 'index.ts'),
|
|
269
|
+
path.join(pkgRoot, 'src', subpath, 'index.js'),
|
|
270
|
+
]) {
|
|
271
|
+
if (existsSync(cand) && statSync(cand).isFile())
|
|
272
|
+
return cand;
|
|
273
|
+
}
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
if (!target || typeof target !== 'string')
|
|
277
|
+
return null;
|
|
278
|
+
const abs = path.resolve(pkgRoot, target);
|
|
279
|
+
if (existsSync(abs) && statSync(abs).isFile())
|
|
280
|
+
return abs;
|
|
281
|
+
for (const ext of ['.ts', '.tsx', '.js', '.mjs']) {
|
|
282
|
+
const c = abs.endsWith(ext) ? abs : abs + ext;
|
|
283
|
+
if (existsSync(c) && statSync(c).isFile())
|
|
284
|
+
return c;
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
function flattenExportTarget(entry) {
|
|
289
|
+
if (typeof entry === 'string')
|
|
290
|
+
return entry;
|
|
291
|
+
if (!entry || typeof entry !== 'object')
|
|
292
|
+
return null;
|
|
293
|
+
const v = entry.import || entry.default || entry.require || entry.module || null;
|
|
294
|
+
if (typeof v === 'string')
|
|
295
|
+
return v;
|
|
296
|
+
if (v && typeof v === 'object')
|
|
297
|
+
return flattenExportTarget(v);
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
function vendorPathForSource(outDir, pkgName, pkgRoot, sourceFile) {
|
|
301
|
+
const rel = path.relative(pkgRoot, sourceFile).replace(/\\/g, '/');
|
|
302
|
+
return path.join(outDir, VENDOR_DIR, packageDirName(pkgName), rewriteTsExt(rel));
|
|
303
|
+
}
|
|
304
|
+
function packageDirName(pkgName) {
|
|
305
|
+
return pkgName.replace(/^@/, '');
|
|
306
|
+
}
|
|
307
|
+
function materializeModule(sourceFile) {
|
|
308
|
+
const ext = path.extname(sourceFile).toLowerCase();
|
|
309
|
+
const raw = readFileSync(sourceFile, 'utf8');
|
|
310
|
+
if (ext === '.ts' || ext === '.tsx')
|
|
311
|
+
return transpileTs(raw, sourceFile);
|
|
312
|
+
return rewriteRelativeTsSpecs(raw);
|
|
313
|
+
}
|
|
314
|
+
function transpileTs(source, filename) {
|
|
315
|
+
try {
|
|
316
|
+
const require = createRequire(import.meta.url);
|
|
317
|
+
const ts = require('typescript');
|
|
318
|
+
const out = ts.transpileModule(source, {
|
|
319
|
+
fileName: filename,
|
|
320
|
+
compilerOptions: {
|
|
321
|
+
module: ts.ModuleKind.ESNext,
|
|
322
|
+
target: ts.ScriptTarget.ES2022,
|
|
323
|
+
moduleResolution: ts.ModuleResolutionKind.Bundler,
|
|
324
|
+
esModuleInterop: true,
|
|
325
|
+
skipLibCheck: true,
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
return rewriteRelativeTsSpecs(out.outputText || '');
|
|
329
|
+
}
|
|
330
|
+
catch (err) {
|
|
331
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
332
|
+
return `/* vmz-pack: typescript transpile failed (${msg}) */\n${rewriteRelativeTsSpecs(source)}`;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function rewriteTsExt(p) {
|
|
336
|
+
return String(p)
|
|
337
|
+
.replace(/\.tsx$/i, '.js')
|
|
338
|
+
.replace(/\.ts$/i, '.js');
|
|
339
|
+
}
|
|
340
|
+
export function rewriteRelativeTsSpecs(js) {
|
|
341
|
+
return js
|
|
342
|
+
.replace(/(from\s+['"])(\.[^'"]+)\.tsx(['"])/g, '$1$2.js$3')
|
|
343
|
+
.replace(/(from\s+['"])(\.[^'"]+)\.ts(['"])/g, '$1$2.js$3')
|
|
344
|
+
.replace(/(import\s*\(\s*['"])(\.[^'"]+)\.tsx(['"]\s*\))/g, '$1$2.js$3')
|
|
345
|
+
.replace(/(import\s*\(\s*['"])(\.[^'"]+)\.ts(['"]\s*\))/g, '$1$2.js$3');
|
|
346
|
+
}
|
|
347
|
+
function resolveRelativeSource(fromDir, rel) {
|
|
348
|
+
const cleaned = rel.replace(/\?.*$/, '').replace(/#.*$/, '');
|
|
349
|
+
const base = path.resolve(fromDir, cleaned);
|
|
350
|
+
for (const cand of [
|
|
351
|
+
base,
|
|
352
|
+
`${base}.ts`,
|
|
353
|
+
`${base}.tsx`,
|
|
354
|
+
`${base}.js`,
|
|
355
|
+
`${base}.mjs`,
|
|
356
|
+
path.join(base, 'index.ts'),
|
|
357
|
+
path.join(base, 'index.js'),
|
|
358
|
+
]) {
|
|
359
|
+
try {
|
|
360
|
+
if (existsSync(cand) && statSync(cand).isFile())
|
|
361
|
+
return cand;
|
|
362
|
+
}
|
|
363
|
+
catch {
|
|
364
|
+
/* ignore */
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return null;
|
|
368
|
+
}
|
|
369
|
+
function rewriteBareImports(js, fromFile, bareToVendor) {
|
|
370
|
+
const toRel = (spec) => {
|
|
371
|
+
const dest = bareToVendor.get(spec);
|
|
372
|
+
if (!dest)
|
|
373
|
+
return null;
|
|
374
|
+
let rel = path.relative(path.dirname(fromFile), dest).replace(/\\/g, '/');
|
|
375
|
+
if (!rel.startsWith('.'))
|
|
376
|
+
rel = `./${rel}`;
|
|
377
|
+
return rel;
|
|
378
|
+
};
|
|
379
|
+
let out = js;
|
|
380
|
+
out = out.replace(/\bimport\s*\(\s*(['"])([^'"]+)\1\s*\)/g, (full, quote, spec) => {
|
|
381
|
+
if (isRelativeOrAbsolute(spec) || shouldSkipBare(spec))
|
|
382
|
+
return full;
|
|
383
|
+
const rel = toRel(spec);
|
|
384
|
+
return rel ? `import(${quote}${rel}${quote})` : full;
|
|
385
|
+
});
|
|
386
|
+
out = out.replace(/\bfrom\s+(['"])([^'"]+)\1/g, (full, quote, spec) => {
|
|
387
|
+
if (isRelativeOrAbsolute(spec) || shouldSkipBare(spec))
|
|
388
|
+
return full;
|
|
389
|
+
const rel = toRel(spec);
|
|
390
|
+
return rel ? `from ${quote}${rel}${quote}` : full;
|
|
391
|
+
});
|
|
392
|
+
out = out.replace(/\bimport\s+(['"])([^'"]+)\1/g, (full, quote, spec) => {
|
|
393
|
+
if (isRelativeOrAbsolute(spec) || shouldSkipBare(spec))
|
|
394
|
+
return full;
|
|
395
|
+
const rel = toRel(spec);
|
|
396
|
+
return rel ? `import ${quote}${rel}${quote}` : full;
|
|
397
|
+
});
|
|
398
|
+
return out;
|
|
399
|
+
}
|
package/dist/pack.d.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pack stage: consume Deployment IR (VPG-owned units), emit pack manifest,
|
|
3
|
+
* and lower browser-unreachable bare package imports to `dist/vendor/**`.
|
|
4
|
+
* Full oxc minify/chunk-split lands progressively (`oxc-pending` on release minify).
|
|
5
|
+
*/
|
|
6
|
+
export declare const PACK_MANIFEST_SCHEMA = "vmz.pack.manifest.v0";
|
|
7
|
+
/**
|
|
8
|
+
* Ensure dom split companions sit next to vmz-dom.js (barrel imports ./dom-core.js).
|
|
9
|
+
* Always refresh from `@vmz/core` when present so SSR/runtime fixes are not sticky in outDir.
|
|
10
|
+
* @param {string} outDir
|
|
11
|
+
* @param {string | null | undefined} coreDist `@vmz/core` dist root
|
|
12
|
+
* @returns {string[]} copied relative names
|
|
13
|
+
*/
|
|
14
|
+
export declare function ensureRuntimeCompanions(outDir: any, coreDist: any): any[];
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} outDir
|
|
17
|
+
* @param {{
|
|
18
|
+
* release?: boolean,
|
|
19
|
+
* profileId?: string,
|
|
20
|
+
* assembly?: string,
|
|
21
|
+
* preferredClientFace?: string,
|
|
22
|
+
* coreDist?: string | null,
|
|
23
|
+
* projectRoot?: string | null,
|
|
24
|
+
* }} [opts]
|
|
25
|
+
*/
|
|
26
|
+
export declare function packFromDeploymentIr(outDir: any, opts?: {}): {
|
|
27
|
+
manifest: {
|
|
28
|
+
schema: string;
|
|
29
|
+
profileId: any;
|
|
30
|
+
assembly: any;
|
|
31
|
+
release: boolean;
|
|
32
|
+
preferredClientFace: any;
|
|
33
|
+
deploymentSchema: any;
|
|
34
|
+
unitCount: number;
|
|
35
|
+
units: any[];
|
|
36
|
+
minify: string;
|
|
37
|
+
treeShakeBasis: string;
|
|
38
|
+
bundler: string;
|
|
39
|
+
clientPackageLowering: {
|
|
40
|
+
status: string;
|
|
41
|
+
rewrittenFiles: number;
|
|
42
|
+
bareSpecs: any[];
|
|
43
|
+
vendoredModules: string[];
|
|
44
|
+
unresolvedBareSpecs: any[];
|
|
45
|
+
skippedVmzExports: any[];
|
|
46
|
+
remainingBareSpecs: any[];
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
path: string;
|
|
50
|
+
clientPackages: {
|
|
51
|
+
rewrittenFiles: number;
|
|
52
|
+
vendoredModules: string[];
|
|
53
|
+
bareSpecs: any[];
|
|
54
|
+
unresolvedBareSpecs: any[];
|
|
55
|
+
skippedVmzExports: any[];
|
|
56
|
+
remainingBareSpecs: any[];
|
|
57
|
+
};
|
|
58
|
+
};
|
package/dist/pack.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pack stage: consume Deployment IR (VPG-owned units), emit pack manifest,
|
|
3
|
+
* and lower browser-unreachable bare package imports to `dist/vendor/**`.
|
|
4
|
+
* Full oxc minify/chunk-split lands progressively (`oxc-pending` on release minify).
|
|
5
|
+
*/
|
|
6
|
+
// @ts-nocheck
|
|
7
|
+
import crypto from 'node:crypto';
|
|
8
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { loadDeploymentIr, planBundleInputs } from './bundler-adapter.js';
|
|
11
|
+
import { packClientBareImports } from './pack-client-packages.js';
|
|
12
|
+
import { writePrettyJsonFile } from './pretty-json.js';
|
|
13
|
+
export const PACK_MANIFEST_SCHEMA = 'vmz.pack.manifest.v0';
|
|
14
|
+
/**
|
|
15
|
+
* Ensure dom split companions sit next to vmz-dom.js (barrel imports ./dom-core.js).
|
|
16
|
+
* Always refresh from `@vmz/core` when present so SSR/runtime fixes are not sticky in outDir.
|
|
17
|
+
* @param {string} outDir
|
|
18
|
+
* @param {string | null | undefined} coreDist `@vmz/core` dist root
|
|
19
|
+
* @returns {string[]} copied relative names
|
|
20
|
+
*/
|
|
21
|
+
export function ensureRuntimeCompanions(outDir, coreDist) {
|
|
22
|
+
if (!coreDist)
|
|
23
|
+
return [];
|
|
24
|
+
const names = ['dom-core.js', 'dom-ssr.js', 'dom.client.js'];
|
|
25
|
+
const copied = [];
|
|
26
|
+
for (const name of names) {
|
|
27
|
+
const src = path.join(coreDist, name);
|
|
28
|
+
if (!existsSync(src))
|
|
29
|
+
continue;
|
|
30
|
+
const dest = path.join(outDir, name);
|
|
31
|
+
copyFileSync(src, dest);
|
|
32
|
+
copied.push(name);
|
|
33
|
+
}
|
|
34
|
+
return copied;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} outDir
|
|
38
|
+
* @param {{
|
|
39
|
+
* release?: boolean,
|
|
40
|
+
* profileId?: string,
|
|
41
|
+
* assembly?: string,
|
|
42
|
+
* preferredClientFace?: string,
|
|
43
|
+
* coreDist?: string | null,
|
|
44
|
+
* projectRoot?: string | null,
|
|
45
|
+
* }} [opts]
|
|
46
|
+
*/
|
|
47
|
+
export function packFromDeploymentIr(outDir, opts = {}) {
|
|
48
|
+
ensureRuntimeCompanions(outDir, opts.coreDist);
|
|
49
|
+
// Browser ESM cannot resolve bare npm specs — materialize reachable packages first
|
|
50
|
+
// so digests below reflect the rewritten graph.
|
|
51
|
+
const clientPackages = packClientBareImports(outDir, { projectRoot: opts.projectRoot || null });
|
|
52
|
+
const ir = loadDeploymentIr(outDir);
|
|
53
|
+
const inputs = planBundleInputs(outDir, ir);
|
|
54
|
+
const units = [];
|
|
55
|
+
for (const entry of inputs) {
|
|
56
|
+
const abs = entry.entry;
|
|
57
|
+
let digest = null;
|
|
58
|
+
let bytes = 0;
|
|
59
|
+
let present = false;
|
|
60
|
+
if (existsSync(abs)) {
|
|
61
|
+
present = true;
|
|
62
|
+
const buf = readFileSync(abs);
|
|
63
|
+
bytes = buf.length;
|
|
64
|
+
digest = crypto.createHash('sha256').update(buf).digest('hex');
|
|
65
|
+
}
|
|
66
|
+
units.push({
|
|
67
|
+
chunkId: entry.chunkId,
|
|
68
|
+
kind: entry.kind,
|
|
69
|
+
entry: path.relative(outDir, abs).replace(/\\/g, '/'),
|
|
70
|
+
programIr: path.relative(outDir, entry.programIr).replace(/\\/g, '/'),
|
|
71
|
+
source: entry.source,
|
|
72
|
+
present,
|
|
73
|
+
bytes,
|
|
74
|
+
digest,
|
|
75
|
+
rebuilt: Boolean(entry.rebuilt),
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
const body = {
|
|
79
|
+
schema: PACK_MANIFEST_SCHEMA,
|
|
80
|
+
profileId: opts.profileId || null,
|
|
81
|
+
assembly: opts.assembly || null,
|
|
82
|
+
release: Boolean(opts.release),
|
|
83
|
+
preferredClientFace: opts.preferredClientFace || '@vmz/core/dom/client',
|
|
84
|
+
deploymentSchema: ir.schema,
|
|
85
|
+
unitCount: units.length,
|
|
86
|
+
units,
|
|
87
|
+
minify: opts.release ? 'oxc-pending' : 'dev-identity',
|
|
88
|
+
treeShakeBasis: 'vpg-deployment-ir',
|
|
89
|
+
bundler: 'vmz-pack',
|
|
90
|
+
clientPackageLowering: {
|
|
91
|
+
status: 'thin',
|
|
92
|
+
rewrittenFiles: clientPackages.rewrittenFiles,
|
|
93
|
+
bareSpecs: clientPackages.bareSpecs,
|
|
94
|
+
vendoredModules: clientPackages.vendoredModules,
|
|
95
|
+
unresolvedBareSpecs: clientPackages.unresolvedBareSpecs || [],
|
|
96
|
+
skippedVmzExports: clientPackages.skippedVmzExports || [],
|
|
97
|
+
remainingBareSpecs: clientPackages.remainingBareSpecs || [],
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
body.packDigest = sha256Hex(stableStringify({ ...body }));
|
|
101
|
+
const vmzDir = path.join(outDir, '_vmz');
|
|
102
|
+
mkdirSync(vmzDir, { recursive: true });
|
|
103
|
+
const file = path.join(vmzDir, 'pack-manifest.json');
|
|
104
|
+
writePrettyJsonFile(file, body);
|
|
105
|
+
return { manifest: body, path: file, clientPackages };
|
|
106
|
+
}
|
|
107
|
+
function stableStringify(value) {
|
|
108
|
+
return JSON.stringify(sortKeys(value));
|
|
109
|
+
}
|
|
110
|
+
function sortKeys(value) {
|
|
111
|
+
if (Array.isArray(value))
|
|
112
|
+
return value.map(sortKeys);
|
|
113
|
+
if (value && typeof value === 'object') {
|
|
114
|
+
const out = {};
|
|
115
|
+
for (const k of Object.keys(value).sort())
|
|
116
|
+
out[k] = sortKeys(value[k]);
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
return value;
|
|
120
|
+
}
|
|
121
|
+
function sha256Hex(text) {
|
|
122
|
+
return crypto.createHash('sha256').update(text, 'utf8').digest('hex');
|
|
123
|
+
}
|
package/dist/plugin-host.d.ts
CHANGED
|
@@ -15,7 +15,7 @@ export declare function importMaybeTs(full: any): Promise<any>;
|
|
|
15
15
|
* @returns {Promise<{
|
|
16
16
|
* plugins: import('@vmz/plugin').VmzPlugin[],
|
|
17
17
|
* engines: import('@vmz/plugin').VmzEngines,
|
|
18
|
-
* delivery: import('@vmz/plugin').
|
|
18
|
+
* delivery: import('@vmz/plugin').DeliveryAuthoring | null,
|
|
19
19
|
* application: { id?: string } | null,
|
|
20
20
|
* path: string | null,
|
|
21
21
|
* pluginPath: string | null,
|
package/dist/plugin-host.js
CHANGED
|
@@ -35,7 +35,7 @@ export async function importMaybeTs(full) {
|
|
|
35
35
|
* @returns {Promise<{
|
|
36
36
|
* plugins: import('@vmz/plugin').VmzPlugin[],
|
|
37
37
|
* engines: import('@vmz/plugin').VmzEngines,
|
|
38
|
-
* delivery: import('@vmz/plugin').
|
|
38
|
+
* delivery: import('@vmz/plugin').DeliveryAuthoring | null,
|
|
39
39
|
* application: { id?: string } | null,
|
|
40
40
|
* path: string | null,
|
|
41
41
|
* pluginPath: string | null,
|
|
@@ -46,7 +46,7 @@ export async function loadVmzConfig(project) {
|
|
|
46
46
|
const plugins = [];
|
|
47
47
|
/** @type {import('@vmz/plugin').VmzEngines} */
|
|
48
48
|
let engines = {};
|
|
49
|
-
/** @type {import('@vmz/plugin').
|
|
49
|
+
/** @type {import('@vmz/plugin').DeliveryAuthoring | null} */
|
|
50
50
|
let delivery = null;
|
|
51
51
|
/** @type {{ id?: string } | null} */
|
|
52
52
|
let application = null;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pretty-print a value through `vmz-generator` (N-API).
|
|
3
|
+
* @param {unknown} value
|
|
4
|
+
* @returns {string} Pretty JSON without trailing newline.
|
|
5
|
+
*/
|
|
6
|
+
export declare function generatePrettyJson(value: any): any;
|
|
7
|
+
/**
|
|
8
|
+
* Write a pretty JSON artifact with a trailing newline.
|
|
9
|
+
* @param {string} filePath
|
|
10
|
+
* @param {unknown} value
|
|
11
|
+
*/
|
|
12
|
+
export declare function writePrettyJsonFile(filePath: any, value: any): void;
|
|
13
|
+
/**
|
|
14
|
+
* CLI `--json` helper: write to a path when `target` is a string, else stdout.
|
|
15
|
+
* @param {string | boolean | undefined} target
|
|
16
|
+
* @param {unknown} value
|
|
17
|
+
* @param {{ logWrote?: (path: string) => void }} [opts]
|
|
18
|
+
*/
|
|
19
|
+
export declare function emitPrettyJson(target: any, value: any, opts?: {}): void;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Production JSON artifact printer — always via N-API JsonCodeGenerator.
|
|
4
|
+
* Do not use `JSON.stringify(x, null, 2)` for on-disk artifacts.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import { requireNativeAddon } from './native-addon.js';
|
|
8
|
+
/**
|
|
9
|
+
* Pretty-print a value through `vmz-generator` (N-API).
|
|
10
|
+
* @param {unknown} value
|
|
11
|
+
* @returns {string} Pretty JSON without trailing newline.
|
|
12
|
+
*/
|
|
13
|
+
export function generatePrettyJson(value) {
|
|
14
|
+
const native = requireNativeAddon();
|
|
15
|
+
if (typeof native.generatePrettyJson !== 'function') {
|
|
16
|
+
throw new Error('vmz native addon missing generatePrettyJson — rebuild with `pnpm napi:build`');
|
|
17
|
+
}
|
|
18
|
+
return native.generatePrettyJson(JSON.stringify(value));
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Write a pretty JSON artifact with a trailing newline.
|
|
22
|
+
* @param {string} filePath
|
|
23
|
+
* @param {unknown} value
|
|
24
|
+
*/
|
|
25
|
+
export function writePrettyJsonFile(filePath, value) {
|
|
26
|
+
fs.writeFileSync(filePath, `${generatePrettyJson(value)}\n`, 'utf8');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* CLI `--json` helper: write to a path when `target` is a string, else stdout.
|
|
30
|
+
* @param {string | boolean | undefined} target
|
|
31
|
+
* @param {unknown} value
|
|
32
|
+
* @param {{ logWrote?: (path: string) => void }} [opts]
|
|
33
|
+
*/
|
|
34
|
+
export function emitPrettyJson(target, value, opts = {}) {
|
|
35
|
+
const text = generatePrettyJson(value);
|
|
36
|
+
if (typeof target === 'string') {
|
|
37
|
+
fs.writeFileSync(target, `${text}\n`, 'utf8');
|
|
38
|
+
if (typeof opts.logWrote === 'function')
|
|
39
|
+
opts.logWrote(target);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
console.log(text);
|
|
43
|
+
}
|
|
@@ -7,6 +7,7 @@ import crypto from 'node:crypto';
|
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { canonicalJson, sha256Hex } from './release-pack.js';
|
|
10
|
+
import { writePrettyJsonFile } from './pretty-json.js';
|
|
10
11
|
export const PRODUCTION_OBSERVABILITY_SCHEMA = 'vmz.production.observability.v0';
|
|
11
12
|
export const PRODUCTION_TRACE_SCHEMA = 'vmz.production.trace.v0';
|
|
12
13
|
/** Facets that production traces must be able to carry (08 A5). */
|
|
@@ -457,8 +458,8 @@ export function emitProductionObservability(distDir, overrides = {}, meta = {})
|
|
|
457
458
|
fs.mkdirSync(vmzDir, { recursive: true });
|
|
458
459
|
const contractPath = path.join(vmzDir, 'production-observability.json');
|
|
459
460
|
const tracePath = path.join(vmzDir, 'production-trace.sample.json');
|
|
460
|
-
|
|
461
|
-
|
|
461
|
+
writePrettyJsonFile(contractPath, contract);
|
|
462
|
+
writePrettyJsonFile(tracePath, trace);
|
|
462
463
|
return { contract, trace, contractPath, tracePath };
|
|
463
464
|
}
|
|
464
465
|
export function observabilityDigest(contract) {
|