@vmz/vmz 0.1.13 → 0.1.15
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/author-input.d.ts +19 -0
- package/dist/author-input.js +52 -0
- package/dist/build-assemble.js +18 -7
- package/dist/cdn-policy.js +5 -4
- package/dist/cli.js +106 -38
- package/dist/content-addressed-assets.d.ts +23 -2
- package/dist/content-addressed-assets.js +178 -32
- package/dist/delivery-profile.d.ts +30 -2
- package/dist/delivery-profile.js +139 -5
- package/dist/dev-session.d.ts +1 -0
- package/dist/dev-session.js +269 -65
- package/dist/dev-watch-roots.d.ts +80 -0
- package/dist/dev-watch-roots.js +245 -0
- package/dist/document-build.d.ts +4 -3
- package/dist/document-build.js +104 -82
- package/dist/document-check.d.ts +24 -5
- package/dist/document-check.js +64 -101
- package/dist/document-cmd.js +2 -9
- package/dist/document-enrich.d.ts +2 -1
- package/dist/document-enrich.js +15 -4
- package/dist/document-integrate.js +29 -21
- package/dist/document-layout-render.d.ts +20 -0
- package/dist/document-layout-render.js +87 -0
- package/dist/document-routing-config.d.ts +17 -0
- package/dist/document-routing-config.js +31 -0
- package/dist/document-schema.js +8 -1
- package/dist/embedded-packaging.js +2 -1
- package/dist/index.d.ts +18 -2
- package/dist/index.js +63 -4
- package/dist/locale-check.js +81 -183
- package/dist/locale-cmd.js +13 -45
- package/dist/locale-route-emit.d.ts +4 -1
- package/dist/locale-route-emit.js +7 -32
- package/dist/locale-router.js +26 -8
- package/dist/native-addon.d.ts +9 -0
- package/dist/native-addon.js +84 -0
- package/dist/pack.js +3 -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.js +4 -3
- package/dist/public-static-assets.d.ts +24 -0
- package/dist/public-static-assets.js +140 -0
- package/dist/release-pack.js +5 -18
- package/dist/route-path.d.ts +35 -0
- package/dist/route-path.js +77 -0
- package/dist/server-artifact.js +4 -3
- package/dist/site-delivery.js +4 -3
- package/dist/site-favicon.d.ts +31 -0
- package/dist/site-favicon.js +140 -0
- package/dist/static-emit.d.ts +21 -0
- package/dist/static-emit.js +134 -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 +13 -14
package/dist/static-emit.js
CHANGED
|
@@ -7,9 +7,17 @@ import crypto from 'node:crypto';
|
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { pathToFileURL } from 'node:url';
|
|
10
|
+
import { createRenderHost } from '@vmz/core/render-host';
|
|
11
|
+
import { resolveRouteLayoutChain } from '@vmz/core/route-layout-chain';
|
|
12
|
+
import { listClientComponentsSync } from '@vmz/core/component-registry';
|
|
10
13
|
import { emitCdnPolicy } from './cdn-policy.js';
|
|
11
14
|
import { emitContentAddressedAssets } from './content-addressed-assets.js';
|
|
12
15
|
import { absoluteUrl, buildLocalePageMeta, localizeBodyLinks } from './locale-router.js';
|
|
16
|
+
import { requireNativeAddon } from './native-addon.js';
|
|
17
|
+
import { writePrettyJsonFile } from './pretty-json.js';
|
|
18
|
+
import { emitPublicStaticAssets } from './public-static-assets.js';
|
|
19
|
+
import { filePathPatternFromChunk, isRouteBoundaryStem, listPublicPageUnits, parsePathPattern, unitBrowserPathPattern } from './route-path.js';
|
|
20
|
+
import { emitSiteFavicon, readSiteFaviconHeadHtml } from './site-favicon.js';
|
|
13
21
|
export const STATIC_DELIVERY_MANIFEST_SCHEMA = 'vmz.static.delivery_manifest.v0';
|
|
14
22
|
/**
|
|
15
23
|
* @param {string} distDir
|
|
@@ -26,7 +34,11 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
26
34
|
if (!fs.existsSync(domPath)) {
|
|
27
35
|
throw new Error(`emitWebStatic: missing ${domPath} — run vmz build first`);
|
|
28
36
|
}
|
|
29
|
-
|
|
37
|
+
emitSiteFavicon(distDir, { projectRoot: opts.projectRoot });
|
|
38
|
+
const faviconHead = readSiteFaviconHeadHtml(distDir);
|
|
39
|
+
const publicAssets = emitPublicStaticAssets(distDir, { projectRoot: opts.projectRoot });
|
|
40
|
+
const host = await createRenderHost(distDir, { strictDeployment: true, preload: 'none' });
|
|
41
|
+
const { renderToString, renderToStream } = host;
|
|
30
42
|
const pageCatalog = listPageClientFiles(distDir);
|
|
31
43
|
/** @type {Array<{
|
|
32
44
|
* routeId: string,
|
|
@@ -43,7 +55,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
43
55
|
/** @type {Array<{ routeId: string, path: string, chunkId: string, classification: string, reason: string }>} */
|
|
44
56
|
const skipped = [];
|
|
45
57
|
for (const page of pageCatalog) {
|
|
46
|
-
const pattern = patternFromSegs(page.segs);
|
|
58
|
+
const pattern = page.pathPattern || patternFromSegs(page.segs);
|
|
47
59
|
const routeId = guessRouteId(distDir, page.chunkId);
|
|
48
60
|
if (page.segs.some((s) => s.kind === 'param' || s.kind === 'catch')) {
|
|
49
61
|
skipped.push({
|
|
@@ -94,7 +106,8 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
94
106
|
}
|
|
95
107
|
}
|
|
96
108
|
const meta = await resolvePageMeta(Page, { params, props, pathname: pattern, origin });
|
|
97
|
-
const layoutChain =
|
|
109
|
+
const layoutChain = resolveRouteLayoutChain(distDir, page.chunkId);
|
|
110
|
+
await host.ensureComponents([page.chunkId, ...layoutChain]);
|
|
98
111
|
let bodyHtml = '';
|
|
99
112
|
for await (const chunk of renderToStream(Page, props, {})) {
|
|
100
113
|
bodyHtml += chunk;
|
|
@@ -127,6 +140,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
127
140
|
props,
|
|
128
141
|
meta: gen.meta,
|
|
129
142
|
cssEntry: readCssEntry(distDir),
|
|
143
|
+
headExtraHtml: faviconHead,
|
|
130
144
|
});
|
|
131
145
|
fs.writeFileSync(absHtml, html, 'utf8');
|
|
132
146
|
generations.push({
|
|
@@ -158,11 +172,16 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
158
172
|
},
|
|
159
173
|
cssEntry: readCssEntry(distDir),
|
|
160
174
|
isErrorDocument: true,
|
|
175
|
+
headExtraHtml: faviconHead,
|
|
161
176
|
});
|
|
162
177
|
fs.writeFileSync(path.join(distDir, '404.html'), notFoundHtml, 'utf8');
|
|
163
178
|
const sitemap = buildSitemap(origin, generations);
|
|
164
179
|
fs.writeFileSync(path.join(distDir, 'sitemap.xml'), sitemap, 'utf8');
|
|
165
|
-
const
|
|
180
|
+
const nativeRobots = requireNativeAddon();
|
|
181
|
+
if (typeof nativeRobots.generateRobotsTxt !== 'function') {
|
|
182
|
+
throw new Error('vmz native addon missing generateRobotsTxt — rebuild with `pnpm napi:build`');
|
|
183
|
+
}
|
|
184
|
+
const robots = nativeRobots.generateRobotsTxt(`${origin}/sitemap.xml`);
|
|
166
185
|
fs.writeFileSync(path.join(distDir, 'robots.txt'), robots, 'utf8');
|
|
167
186
|
// Hard rule: no SPA fallback shim in artifact.
|
|
168
187
|
for (const bad of ['_redirects', 'vercel.json', 'netlify.toml']) {
|
|
@@ -179,7 +198,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
179
198
|
const manifest = {
|
|
180
199
|
schema: STATIC_DELIVERY_MANIFEST_SCHEMA,
|
|
181
200
|
applicationId,
|
|
182
|
-
deliveryProfile: '
|
|
201
|
+
deliveryProfile: 'static',
|
|
183
202
|
origin,
|
|
184
203
|
generatedAt: new Date().toISOString(),
|
|
185
204
|
spaFallback: false,
|
|
@@ -204,10 +223,17 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
204
223
|
sitemap: 'sitemap.xml',
|
|
205
224
|
robots: 'robots.txt',
|
|
206
225
|
},
|
|
226
|
+
publicAssets: {
|
|
227
|
+
schema: publicAssets.schema,
|
|
228
|
+
status: publicAssets.status,
|
|
229
|
+
fileCount: publicAssets.fileCount ?? 0,
|
|
230
|
+
source: publicAssets.source || null,
|
|
231
|
+
},
|
|
207
232
|
};
|
|
208
233
|
const digest = sha256Hex(canonicalJson(manifest));
|
|
209
234
|
manifest.manifestDigest = digest;
|
|
210
|
-
|
|
235
|
+
writePrettyJsonFile(path.join(vmzDir, 'static-delivery-manifest.json'), manifest);
|
|
236
|
+
emitStaticClientEntries(distDir, pageCatalog);
|
|
211
237
|
const assets = emitContentAddressedAssets(distDir);
|
|
212
238
|
manifest.contentAddressedAssets = {
|
|
213
239
|
schema: assets.manifest.schema,
|
|
@@ -218,7 +244,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
218
244
|
// Re-stamp static manifest after linking asset digest (HTML already rewritten on disk).
|
|
219
245
|
delete manifest.manifestDigest;
|
|
220
246
|
manifest.manifestDigest = sha256Hex(canonicalJson(manifest));
|
|
221
|
-
|
|
247
|
+
writePrettyJsonFile(path.join(vmzDir, 'static-delivery-manifest.json'), manifest);
|
|
222
248
|
const cdn = emitCdnPolicy(distDir, manifest);
|
|
223
249
|
return {
|
|
224
250
|
manifest,
|
|
@@ -226,6 +252,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
226
252
|
skipped,
|
|
227
253
|
digest: manifest.manifestDigest,
|
|
228
254
|
assets: assets.manifest,
|
|
255
|
+
publicAssets,
|
|
229
256
|
cdnPolicy: cdn.policy,
|
|
230
257
|
cdnAdapters: cdn.adapters,
|
|
231
258
|
};
|
|
@@ -258,8 +285,11 @@ function sortKeys(value) {
|
|
|
258
285
|
* @param {string} distDir
|
|
259
286
|
*/
|
|
260
287
|
function listPageClientFiles(distDir) {
|
|
288
|
+
const fromDep = listPagesFromDeployment(distDir);
|
|
289
|
+
if (fromDep.length)
|
|
290
|
+
return fromDep;
|
|
261
291
|
const root = path.join(distDir, 'pages');
|
|
262
|
-
/** @type {Array<{ chunkId: string, segs: ReturnType<typeof
|
|
292
|
+
/** @type {Array<{ chunkId: string, segs: ReturnType<typeof parsePathPattern>, pathPattern: string }>} */
|
|
263
293
|
const out = [];
|
|
264
294
|
function walk(abs, relParts) {
|
|
265
295
|
let ents;
|
|
@@ -274,10 +304,11 @@ function listPageClientFiles(distDir) {
|
|
|
274
304
|
walk(path.join(abs, e.name), [...relParts, e.name]);
|
|
275
305
|
else if (e.isFile() && e.name.endsWith('.client.js')) {
|
|
276
306
|
const stem = e.name.replace(/\.client\.js$/, '');
|
|
277
|
-
if (stem
|
|
307
|
+
if (isRouteBoundaryStem(stem))
|
|
278
308
|
continue;
|
|
279
309
|
const chunkId = ['pages', ...relParts, stem].join('/');
|
|
280
|
-
|
|
310
|
+
const pathPattern = filePathPatternFromChunk(chunkId);
|
|
311
|
+
out.push({ chunkId, pathPattern, segs: parsePathPattern(pathPattern) });
|
|
281
312
|
}
|
|
282
313
|
}
|
|
283
314
|
}
|
|
@@ -285,32 +316,26 @@ function listPageClientFiles(distDir) {
|
|
|
285
316
|
return out;
|
|
286
317
|
}
|
|
287
318
|
/**
|
|
288
|
-
* @param {string}
|
|
319
|
+
* @param {string} distDir
|
|
289
320
|
*/
|
|
290
|
-
function
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
segs.push({ kind: 'catch', name: catchAll[1] });
|
|
305
|
-
else if (param)
|
|
306
|
-
segs.push({ kind: 'param', name: param[1] });
|
|
307
|
-
else
|
|
308
|
-
segs.push({ kind: 'static', value: p.toLowerCase() });
|
|
321
|
+
function listPagesFromDeployment(distDir) {
|
|
322
|
+
const deploymentPath = path.join(distDir, 'vmz-deployment.json');
|
|
323
|
+
if (!fs.existsSync(deploymentPath))
|
|
324
|
+
return [];
|
|
325
|
+
try {
|
|
326
|
+
const deployment = JSON.parse(fs.readFileSync(deploymentPath, 'utf8'));
|
|
327
|
+
return listPublicPageUnits(deployment).map((u) => {
|
|
328
|
+
const chunkId = String(u.chunkId || '').replace(/\\/g, '/');
|
|
329
|
+
const pathPattern = unitBrowserPathPattern(u);
|
|
330
|
+
return { chunkId, pathPattern, segs: parsePathPattern(pathPattern) };
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
catch {
|
|
334
|
+
return [];
|
|
309
335
|
}
|
|
310
|
-
return segs;
|
|
311
336
|
}
|
|
312
337
|
/**
|
|
313
|
-
* @param {ReturnType<typeof
|
|
338
|
+
* @param {ReturnType<typeof parsePathPattern>} segs
|
|
314
339
|
*/
|
|
315
340
|
function patternFromSegs(segs) {
|
|
316
341
|
if (!segs.length)
|
|
@@ -343,24 +368,6 @@ async function loadCtor(distDir, chunkId) {
|
|
|
343
368
|
const mod = await import(`${href}?t=${Date.now()}`);
|
|
344
369
|
return mod.default;
|
|
345
370
|
}
|
|
346
|
-
/**
|
|
347
|
-
* @param {string} distDir
|
|
348
|
-
* @param {string} pageChunkId
|
|
349
|
-
*/
|
|
350
|
-
function resolveLayoutChain(distDir, pageChunkId) {
|
|
351
|
-
const rel = pageChunkId.replace(/^pages\//, '');
|
|
352
|
-
const parts = rel.split('/').filter(Boolean);
|
|
353
|
-
parts.pop();
|
|
354
|
-
/** @type {string[]} */
|
|
355
|
-
const chain = [];
|
|
356
|
-
for (let i = parts.length; i >= 0; i--) {
|
|
357
|
-
const dirParts = parts.slice(0, i);
|
|
358
|
-
const layoutChunk = ['pages', ...dirParts, 'Layout'].join('/');
|
|
359
|
-
if (fs.existsSync(path.join(distDir, `${layoutChunk}.client.js`)))
|
|
360
|
-
chain.unshift(layoutChunk);
|
|
361
|
-
}
|
|
362
|
-
return chain;
|
|
363
|
-
}
|
|
364
371
|
/**
|
|
365
372
|
* @param {string} distDir
|
|
366
373
|
* @param {string} chunkId
|
|
@@ -389,8 +396,8 @@ async function resolvePageMeta(Page, ctx) {
|
|
|
389
396
|
else if (Page.meta && typeof Page.meta === 'object') {
|
|
390
397
|
raw = Page.meta;
|
|
391
398
|
}
|
|
392
|
-
const title = String(raw.title ||
|
|
393
|
-
const description = String(raw.description ||
|
|
399
|
+
const title = String(raw.title || guessTitle(ctx.pathname) || 'App');
|
|
400
|
+
const description = String(raw.description || '');
|
|
394
401
|
const canonical = String(raw.canonical || `${ctx.origin}${ctx.pathname === '/' ? '/' : ctx.pathname}`);
|
|
395
402
|
const robots = String(raw.robots || 'index,follow');
|
|
396
403
|
const lang = String(raw.lang || 'en');
|
|
@@ -510,63 +517,93 @@ function readCssEntry(distDir) {
|
|
|
510
517
|
* meta: { title: string, description: string, canonical: string, robots: string, lang: string, dir?: string, alternates?: Array<{ hreflang: string, href: string }> },
|
|
511
518
|
* cssEntry: string | null,
|
|
512
519
|
* isErrorDocument?: boolean,
|
|
520
|
+
* headExtraHtml?: string,
|
|
513
521
|
* }} input
|
|
514
522
|
*/
|
|
515
523
|
function wrapDocument(input) {
|
|
516
524
|
const propsJson = JSON.stringify(input.props ?? {});
|
|
517
|
-
const layoutAttr = input.layoutChain.length ? ` data-vmz-layout="${escapeAttr(input.layoutChain.join(','))}"` : '';
|
|
518
|
-
const pageAttr = input.chunkId ? ` data-vmz-page="${escapeAttr(input.chunkId)}"` : '';
|
|
519
525
|
const localeId = input.meta.lang || 'en';
|
|
520
526
|
const dir = input.meta.dir || 'ltr';
|
|
521
|
-
const
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
.
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
</html>
|
|
545
|
-
`;
|
|
527
|
+
const native = requireNativeAddon();
|
|
528
|
+
if (typeof native.generatePageShell !== 'function') {
|
|
529
|
+
throw new Error('vmz native addon missing generatePageShell — rebuild with `pnpm napi:build`');
|
|
530
|
+
}
|
|
531
|
+
return native.generatePageShell({
|
|
532
|
+
bodyHtml: input.bodyHtml,
|
|
533
|
+
chunkId: input.chunkId || '',
|
|
534
|
+
layoutChain: input.layoutChain || [],
|
|
535
|
+
propsJson,
|
|
536
|
+
meta: {
|
|
537
|
+
title: input.meta.title,
|
|
538
|
+
description: input.meta.description,
|
|
539
|
+
canonical: input.meta.canonical,
|
|
540
|
+
robots: input.meta.robots,
|
|
541
|
+
lang: localeId,
|
|
542
|
+
dir,
|
|
543
|
+
alternates: input.meta.alternates || [],
|
|
544
|
+
},
|
|
545
|
+
// napi Option<String>: omit/undefined = None; null is rejected as String
|
|
546
|
+
...(input.cssEntry ? { cssEntry: String(input.cssEntry) } : {}),
|
|
547
|
+
isErrorDocument: !!input.isErrorDocument,
|
|
548
|
+
...(input.headExtraHtml ? { headExtraHtml: String(input.headExtraHtml) } : {}),
|
|
549
|
+
});
|
|
546
550
|
}
|
|
547
551
|
/**
|
|
548
552
|
* @param {string} origin
|
|
549
553
|
* @param {Array<{ canonical: string, robots: string }>} generations
|
|
550
554
|
*/
|
|
551
|
-
function buildSitemap(
|
|
552
|
-
const urls = generations
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
559
|
-
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
560
|
-
${urls}
|
|
561
|
-
</urlset>
|
|
562
|
-
`;
|
|
555
|
+
function buildSitemap(_origin, generations) {
|
|
556
|
+
const urls = generations.filter((g) => !String(g.robots).includes('noindex')).map((g) => ({ loc: g.canonical }));
|
|
557
|
+
const native = requireNativeAddon();
|
|
558
|
+
if (typeof native.generateSitemapXml !== 'function') {
|
|
559
|
+
throw new Error('vmz native addon missing generateSitemapXml — rebuild with `pnpm napi:build`');
|
|
560
|
+
}
|
|
561
|
+
return native.generateSitemapXml(urls);
|
|
563
562
|
}
|
|
564
|
-
|
|
565
|
-
|
|
563
|
+
/**
|
|
564
|
+
* Static CDN must ship entry-client/event like serve-host (content-addressed + HTML rewrite).
|
|
565
|
+
* @param {string} distDir
|
|
566
|
+
* @param {Array<{ chunkId: string }>} pageCatalog
|
|
567
|
+
*/
|
|
568
|
+
function emitStaticClientEntries(distDir, pageCatalog) {
|
|
569
|
+
const componentEntries = listClientComponentsSync(distDir, { strict: true });
|
|
570
|
+
const indexChunk = pageCatalog.find((p) => p.chunkId === 'pages/index')?.chunkId || pageCatalog[0]?.chunkId || 'pages/index';
|
|
571
|
+
const resumeEntries = loadPageResumeEntriesSync(distDir, indexChunk);
|
|
572
|
+
const lazySet = new Set(resumeEntries
|
|
573
|
+
.filter((e) => isEventResumeStrategy(e.strategy))
|
|
574
|
+
.map((e) => e.component)
|
|
575
|
+
.filter(Boolean));
|
|
576
|
+
const eager = componentEntries.filter((e) => !lazySet.has(e.name));
|
|
577
|
+
const lazy = componentEntries.filter((e) => lazySet.has(e.name));
|
|
578
|
+
const native = requireNativeAddon();
|
|
579
|
+
if (typeof native.generateServeEntryClient !== 'function') {
|
|
580
|
+
throw new Error('vmz native addon missing generateServeEntryClient — rebuild with `pnpm napi:build`');
|
|
581
|
+
}
|
|
582
|
+
fs.writeFileSync(path.join(distDir, 'entry-client.js'), native.generateServeEntryClient(eager, lazy, ''), 'utf8');
|
|
583
|
+
if (typeof native.generateServeEntryEvent === 'function') {
|
|
584
|
+
fs.writeFileSync(path.join(distDir, 'entry-event.js'), native.generateServeEntryEvent(''), 'utf8');
|
|
585
|
+
}
|
|
566
586
|
}
|
|
567
|
-
|
|
568
|
-
|
|
587
|
+
/**
|
|
588
|
+
* @param {string} distDir
|
|
589
|
+
* @param {string} chunkId
|
|
590
|
+
*/
|
|
591
|
+
function loadPageResumeEntriesSync(distDir, chunkId) {
|
|
592
|
+
try {
|
|
593
|
+
const dep = JSON.parse(fs.readFileSync(path.join(distDir, 'vmz-deployment.json'), 'utf8'));
|
|
594
|
+
const units = Array.isArray(dep.units) ? dep.units : [];
|
|
595
|
+
const page = units.find((u) => u.chunkId === chunkId) || units.find((u) => u.chunkId === 'pages/index') || units.find((u) => u.kind === 'page');
|
|
596
|
+
const entries = Array.isArray(page?.resumeEntries) ? page.resumeEntries : [];
|
|
597
|
+
return entries.map((e) => ({
|
|
598
|
+
component: String(e.component || ''),
|
|
599
|
+
strategy: String(e.strategy || ''),
|
|
600
|
+
}));
|
|
601
|
+
}
|
|
602
|
+
catch {
|
|
603
|
+
return [];
|
|
604
|
+
}
|
|
569
605
|
}
|
|
570
|
-
|
|
571
|
-
|
|
606
|
+
/** @param {string} strategy */
|
|
607
|
+
function isEventResumeStrategy(strategy) {
|
|
608
|
+
return strategy === 'event' || strategy === 'click' || String(strategy).startsWith('event:');
|
|
572
609
|
}
|
package/dist/test-cmd.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { createWorkspace } from './index.js';
|
|
8
8
|
import { log } from './log.js';
|
|
9
|
+
import { generatePrettyJson } from './pretty-json.js';
|
|
9
10
|
/**
|
|
10
11
|
* @returns {Promise<typeof import('@vmz/test')>}
|
|
11
12
|
*/
|
|
@@ -317,7 +318,7 @@ export async function cmdTest(args) {
|
|
|
317
318
|
return errors.length ? 2 : 0;
|
|
318
319
|
}
|
|
319
320
|
if (wantJson) {
|
|
320
|
-
const text = `${
|
|
321
|
+
const text = `${generatePrettyJson(report)}\n`;
|
|
321
322
|
if (typeof args.json === 'string' && args.json !== 'true') {
|
|
322
323
|
const { writeFileSync } = await import('node:fs');
|
|
323
324
|
writeFileSync(path.resolve(String(args.json)), text);
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Materialize `defineConfig({ delivery: { packaging: { wechat } } })` for wechat_pack.
|
|
3
|
+
* Pure data only. Writes `dist/_vmz/wechat-packaging.json` (not a second config entry).
|
|
4
|
+
*/
|
|
5
|
+
export declare const WECHAT_PACKAGING_SCHEMA = "vmz.target.wechat_packaging.v0";
|
|
6
|
+
export declare const WECHAT_PACKAGING_REL = "dist/_vmz/wechat-packaging.json";
|
|
7
|
+
/**
|
|
8
|
+
* @param {unknown} delivery
|
|
9
|
+
* @returns {{ schema: string, appId: string, projectName?: string, title?: string }}
|
|
10
|
+
*/
|
|
11
|
+
export declare function wechatPackagingFromDelivery(delivery: any): {
|
|
12
|
+
schema: string;
|
|
13
|
+
appId: any;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Load `vmz.config.*` and write the WeChat packaging contract for the Rust packer.
|
|
17
|
+
* @param {string} project
|
|
18
|
+
*/
|
|
19
|
+
export declare function materializeWechatPackaging(project: any): {
|
|
20
|
+
schema: string;
|
|
21
|
+
appId: any;
|
|
22
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Materialize `defineConfig({ delivery: { packaging: { wechat } } })` for wechat_pack.
|
|
3
|
+
* Pure data only. Writes `dist/_vmz/wechat-packaging.json` (not a second config entry).
|
|
4
|
+
*/
|
|
5
|
+
// @ts-nocheck
|
|
6
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { createJiti } from 'jiti';
|
|
9
|
+
import { pickDeliveryPackaging } from './delivery-profile.js';
|
|
10
|
+
import { writePrettyJsonFile } from './pretty-json.js';
|
|
11
|
+
export const WECHAT_PACKAGING_SCHEMA = 'vmz.target.wechat_packaging.v0';
|
|
12
|
+
export const WECHAT_PACKAGING_REL = 'dist/_vmz/wechat-packaging.json';
|
|
13
|
+
const CONFIG_NAMES = ['vmz.config.ts', 'vmz.config.mts', 'vmz.config.mjs', 'vmz.config.js'];
|
|
14
|
+
function loadConfigSync(project) {
|
|
15
|
+
for (const name of CONFIG_NAMES) {
|
|
16
|
+
const full = path.join(project, name);
|
|
17
|
+
if (!existsSync(full))
|
|
18
|
+
continue;
|
|
19
|
+
const jiti = createJiti(import.meta.url, {
|
|
20
|
+
interopDefault: true,
|
|
21
|
+
moduleCache: false,
|
|
22
|
+
});
|
|
23
|
+
return jiti(full);
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* @param {unknown} delivery
|
|
29
|
+
* @returns {{ schema: string, appId: string, projectName?: string, title?: string }}
|
|
30
|
+
*/
|
|
31
|
+
export function wechatPackagingFromDelivery(delivery) {
|
|
32
|
+
const diagnostics = [];
|
|
33
|
+
const packaging = pickDeliveryPackaging(delivery && typeof delivery === 'object' ? delivery : {}, diagnostics);
|
|
34
|
+
const wechat = packaging && packaging.wechat ? packaging.wechat : {};
|
|
35
|
+
/** @type {{ schema: string, appId: string, projectName?: string, title?: string }} */
|
|
36
|
+
const out = {
|
|
37
|
+
schema: WECHAT_PACKAGING_SCHEMA,
|
|
38
|
+
appId: typeof wechat.appId === 'string' && wechat.appId.trim() ? wechat.appId.trim() : 'touristappid',
|
|
39
|
+
};
|
|
40
|
+
if (typeof wechat.projectName === 'string' && wechat.projectName.trim()) {
|
|
41
|
+
out.projectName = wechat.projectName.trim();
|
|
42
|
+
}
|
|
43
|
+
if (typeof wechat.title === 'string' && wechat.title.trim()) {
|
|
44
|
+
out.title = wechat.title.trim();
|
|
45
|
+
}
|
|
46
|
+
return out;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Load `vmz.config.*` and write the WeChat packaging contract for the Rust packer.
|
|
50
|
+
* @param {string} project
|
|
51
|
+
*/
|
|
52
|
+
export function materializeWechatPackaging(project) {
|
|
53
|
+
const cfg = loadConfigSync(project);
|
|
54
|
+
const spec = wechatPackagingFromDelivery(cfg?.delivery);
|
|
55
|
+
const abs = path.join(project, WECHAT_PACKAGING_REL);
|
|
56
|
+
mkdirSync(path.dirname(abs), { recursive: true });
|
|
57
|
+
writePrettyJsonFile(abs, spec);
|
|
58
|
+
return spec;
|
|
59
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vmz/vmz",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.15",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "VMZ Node toolchain — N-API workspace session + CLI (publish name @vmz/vmz)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,15 +48,14 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@vmz/core": "0.1.
|
|
52
|
-
"@vmz/plugin": "0.1.
|
|
53
|
-
"@vmz/protocol": "0.1.
|
|
54
|
-
"jiti": "^2.6.1"
|
|
55
|
-
"json5": "^2.2.3"
|
|
51
|
+
"@vmz/core": "0.1.15",
|
|
52
|
+
"@vmz/plugin": "0.1.15",
|
|
53
|
+
"@vmz/protocol": "0.1.15",
|
|
54
|
+
"jiti": "^2.6.1"
|
|
56
55
|
},
|
|
57
56
|
"peerDependencies": {
|
|
58
|
-
"@vmz/plugin-markdown-it": "0.1.
|
|
59
|
-
"@vmz/test": "0.1.
|
|
57
|
+
"@vmz/plugin-markdown-it": "0.1.15",
|
|
58
|
+
"@vmz/test": "0.1.15",
|
|
60
59
|
"typescript": "^5.8.3"
|
|
61
60
|
},
|
|
62
61
|
"peerDependenciesMeta": {
|
|
@@ -90,12 +89,12 @@
|
|
|
90
89
|
"cli"
|
|
91
90
|
],
|
|
92
91
|
"optionalDependencies": {
|
|
93
|
-
"@vmz/vmz-win32-x64": "0.1.
|
|
94
|
-
"@vmz/vmz-win32-arm64": "0.1.
|
|
95
|
-
"@vmz/vmz-darwin-x64": "0.1.
|
|
96
|
-
"@vmz/vmz-darwin-arm64": "0.1.
|
|
97
|
-
"@vmz/vmz-linux-x64": "0.1.
|
|
98
|
-
"@vmz/vmz-linux-arm64": "0.1.
|
|
92
|
+
"@vmz/vmz-win32-x64": "0.1.15",
|
|
93
|
+
"@vmz/vmz-win32-arm64": "0.1.15",
|
|
94
|
+
"@vmz/vmz-darwin-x64": "0.1.15",
|
|
95
|
+
"@vmz/vmz-darwin-arm64": "0.1.15",
|
|
96
|
+
"@vmz/vmz-linux-x64": "0.1.15",
|
|
97
|
+
"@vmz/vmz-linux-arm64": "0.1.15"
|
|
99
98
|
},
|
|
100
99
|
"publishConfig": {
|
|
101
100
|
"access": "public"
|