@vmz/vmz 0.1.13 → 0.1.14
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.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 +2 -1
- package/dist/document-build.js +104 -82
- 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 +13 -0
- package/dist/document-routing-config.js +40 -0
- 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 +28 -61
- package/dist/locale-cmd.js +10 -42
- 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 +12 -12
package/dist/release-pack.js
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
import crypto from 'node:crypto';
|
|
10
10
|
import fs from 'node:fs';
|
|
11
11
|
import path from 'node:path';
|
|
12
|
+
import { writePrettyJsonFile } from './pretty-json.js';
|
|
13
|
+
import { listPublicPageUnits, unitBrowserPathPattern } from './route-path.js';
|
|
12
14
|
export const RELEASE_ENVELOPE_SCHEMA = 'vmz.release.envelope.v0';
|
|
13
15
|
export const APPLICATION_ARTIFACT_SCHEMA = 'vmz.application.artifact.v0';
|
|
14
16
|
export const DELIVERY_ARTIFACT_MANIFEST_SCHEMA = 'vmz.profile.delivery_artifact_manifest.v0';
|
|
@@ -88,21 +90,6 @@ function listContentFiles(distDir) {
|
|
|
88
90
|
out.sort();
|
|
89
91
|
return out;
|
|
90
92
|
}
|
|
91
|
-
/**
|
|
92
|
-
* @param {string} chunkId
|
|
93
|
-
*/
|
|
94
|
-
function pathPatternFromChunk(chunkId) {
|
|
95
|
-
const rel = chunkId.replace(/^pages\//, '');
|
|
96
|
-
const parts = rel.split('/').filter(Boolean);
|
|
97
|
-
const segs = [];
|
|
98
|
-
for (let i = 0; i < parts.length; i++) {
|
|
99
|
-
const p = parts[i];
|
|
100
|
-
if (p === 'index' && i === parts.length - 1)
|
|
101
|
-
continue;
|
|
102
|
-
segs.push(p);
|
|
103
|
-
}
|
|
104
|
-
return segs.length ? `/${segs.join('/')}` : '/';
|
|
105
|
-
}
|
|
106
93
|
/**
|
|
107
94
|
* Pack `dist/` into `_vmz` manifests + release envelope (filesystem Delivery Profile).
|
|
108
95
|
* @param {string} distDir
|
|
@@ -124,13 +111,13 @@ export function packRelease(distDir, opts = {}) {
|
|
|
124
111
|
for (const rel of files) {
|
|
125
112
|
fileDigests[rel] = sha256File(path.join(abs, ...rel.split('/')));
|
|
126
113
|
}
|
|
127
|
-
const pages = (deployment
|
|
114
|
+
const pages = listPublicPageUnits(deployment);
|
|
128
115
|
const routeRealization = {
|
|
129
116
|
schema: ROUTE_REALIZATION_TABLE_SCHEMA,
|
|
130
117
|
routes: pages.map((u) => ({
|
|
131
118
|
routeId: String(u.chunkId),
|
|
132
119
|
chunkId: String(u.chunkId),
|
|
133
|
-
pathPattern:
|
|
120
|
+
pathPattern: unitBrowserPathPattern(u),
|
|
134
121
|
clientEntry: u.clientEntry || null,
|
|
135
122
|
programIr: u.programIr || null,
|
|
136
123
|
})),
|
|
@@ -200,7 +187,7 @@ export function packRelease(distDir, opts = {}) {
|
|
|
200
187
|
* @param {unknown} value
|
|
201
188
|
*/
|
|
202
189
|
function writeJson(file, value) {
|
|
203
|
-
|
|
190
|
+
writePrettyJsonFile(file, value);
|
|
204
191
|
}
|
|
205
192
|
/**
|
|
206
193
|
* @param {string} pointerPath
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser HTTP path projection from Route Graph / `vmz-deployment.json`.
|
|
3
|
+
* Mini pack ignores this and lowers RouteId → chunk id → page stem.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isRouteBoundaryStem(stem: string): boolean;
|
|
6
|
+
export declare function isRouteGroupDir(seg: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* File-route fallback (`pages/home` → `/home`, `pages/index` → `/`).
|
|
9
|
+
* Used only when a deployment unit has no `pathPattern`.
|
|
10
|
+
*/
|
|
11
|
+
export declare function filePathPatternFromChunk(chunkId: string): string;
|
|
12
|
+
export type DeploymentPageUnit = {
|
|
13
|
+
kind?: string;
|
|
14
|
+
chunkId?: string;
|
|
15
|
+
clientEntry?: string;
|
|
16
|
+
programIr?: string;
|
|
17
|
+
pathPattern?: string;
|
|
18
|
+
routeId?: string;
|
|
19
|
+
};
|
|
20
|
+
/** Canonical Browser HTTP pattern for a page unit. Mini must not read this. */
|
|
21
|
+
export declare function unitBrowserPathPattern(unit: DeploymentPageUnit | null | undefined): string;
|
|
22
|
+
export type PathSeg = {
|
|
23
|
+
kind: 'static';
|
|
24
|
+
value: string;
|
|
25
|
+
} | {
|
|
26
|
+
kind: 'param';
|
|
27
|
+
name: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: 'catch';
|
|
30
|
+
name: string;
|
|
31
|
+
};
|
|
32
|
+
export declare function parsePathPattern(pattern: string): PathSeg[];
|
|
33
|
+
export declare function listPublicPageUnits(deployment: {
|
|
34
|
+
units?: DeploymentPageUnit[];
|
|
35
|
+
} | null | undefined): DeploymentPageUnit[];
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Browser HTTP path projection from Route Graph / `vmz-deployment.json`.
|
|
3
|
+
* Mini pack ignores this and lowers RouteId → chunk id → page stem.
|
|
4
|
+
*/
|
|
5
|
+
export function isRouteBoundaryStem(stem) {
|
|
6
|
+
return stem === 'Layout' || stem === 'Loading' || stem === 'Error' || stem === 'NotFound';
|
|
7
|
+
}
|
|
8
|
+
export function isRouteGroupDir(seg) {
|
|
9
|
+
return typeof seg === 'string' && seg.startsWith('(') && seg.endsWith(')') && seg.length > 2;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* File-route fallback (`pages/home` → `/home`, `pages/index` → `/`).
|
|
13
|
+
* Used only when a deployment unit has no `pathPattern`.
|
|
14
|
+
*/
|
|
15
|
+
export function filePathPatternFromChunk(chunkId) {
|
|
16
|
+
const rel = String(chunkId || '').replace(/^pages\//, '');
|
|
17
|
+
const parts = rel.split('/').filter(Boolean);
|
|
18
|
+
const segs = [];
|
|
19
|
+
for (let i = 0; i < parts.length; i++) {
|
|
20
|
+
const p = parts[i];
|
|
21
|
+
if (isRouteGroupDir(p))
|
|
22
|
+
continue;
|
|
23
|
+
if (p === 'index' && i === parts.length - 1)
|
|
24
|
+
continue;
|
|
25
|
+
if (isRouteBoundaryStem(p))
|
|
26
|
+
continue;
|
|
27
|
+
segs.push(p);
|
|
28
|
+
}
|
|
29
|
+
return segs.length ? `/${segs.join('/')}` : '/';
|
|
30
|
+
}
|
|
31
|
+
/** Canonical Browser HTTP pattern for a page unit. Mini must not read this. */
|
|
32
|
+
export function unitBrowserPathPattern(unit) {
|
|
33
|
+
const explicit = String(unit?.pathPattern || '').trim();
|
|
34
|
+
if (explicit)
|
|
35
|
+
return explicit.startsWith('/') ? explicit : `/${explicit}`;
|
|
36
|
+
return filePathPatternFromChunk(String(unit?.chunkId || ''));
|
|
37
|
+
}
|
|
38
|
+
export function parsePathPattern(pattern) {
|
|
39
|
+
const raw = String(pattern || '').trim();
|
|
40
|
+
if (!raw || raw === '/')
|
|
41
|
+
return [];
|
|
42
|
+
const parts = raw.replace(/^\/+/, '').replace(/\/+$/, '').split('/').filter(Boolean);
|
|
43
|
+
const segs = [];
|
|
44
|
+
for (const p of parts) {
|
|
45
|
+
if (isRouteGroupDir(p))
|
|
46
|
+
continue;
|
|
47
|
+
segs.push(parsePathSegment(p));
|
|
48
|
+
}
|
|
49
|
+
return segs;
|
|
50
|
+
}
|
|
51
|
+
function parsePathSegment(p) {
|
|
52
|
+
const catchAll = /^\[\.\.\.([^\]]+)\]$/.exec(p);
|
|
53
|
+
const star = /^\*([A-Za-z_][\w]*)$/.exec(p);
|
|
54
|
+
const param = /^\[([^\]]+)\]$/.exec(p);
|
|
55
|
+
const colon = /^:([A-Za-z_][\w]*)$/.exec(p);
|
|
56
|
+
if (catchAll)
|
|
57
|
+
return { kind: 'catch', name: catchAll[1] };
|
|
58
|
+
if (star)
|
|
59
|
+
return { kind: 'catch', name: star[1] };
|
|
60
|
+
if (param)
|
|
61
|
+
return { kind: 'param', name: param[1] };
|
|
62
|
+
if (colon)
|
|
63
|
+
return { kind: 'param', name: colon[1] };
|
|
64
|
+
return { kind: 'static', value: p.toLowerCase() };
|
|
65
|
+
}
|
|
66
|
+
export function listPublicPageUnits(deployment) {
|
|
67
|
+
const units = Array.isArray(deployment?.units) ? deployment.units : [];
|
|
68
|
+
return units.filter((u) => {
|
|
69
|
+
if (u?.kind !== 'page')
|
|
70
|
+
return false;
|
|
71
|
+
const chunkId = String(u.chunkId || '').replace(/\\/g, '/');
|
|
72
|
+
if (!chunkId.startsWith('pages/'))
|
|
73
|
+
return false;
|
|
74
|
+
const stem = chunkId.split('/').pop() || '';
|
|
75
|
+
return !isRouteBoundaryStem(stem);
|
|
76
|
+
});
|
|
77
|
+
}
|
package/dist/server-artifact.js
CHANGED
|
@@ -4,9 +4,10 @@
|
|
|
4
4
|
*/
|
|
5
5
|
// @ts-nocheck
|
|
6
6
|
import crypto from 'node:crypto';
|
|
7
|
-
import { existsSync, mkdirSync, readFileSync
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { SERVER_RUNTIMES } from './delivery-profile.js';
|
|
10
|
+
import { writePrettyJsonFile } from './pretty-json.js';
|
|
10
11
|
export const SERVER_ARTIFACT_SCHEMA = 'vmz.server.artifact.v0';
|
|
11
12
|
export const HTTP_CONTRACT_SCHEMA = 'vmz.http.contract.v0';
|
|
12
13
|
export const SERVER_RUNTIME_ADAPTER_SCHEMA = 'vmz.server.runtime_adapter.v0';
|
|
@@ -116,14 +117,14 @@ export function emitServerArtifact(outDir, opts = {}) {
|
|
|
116
117
|
const vmzDir = path.join(outDir, '_vmz');
|
|
117
118
|
mkdirSync(vmzDir, { recursive: true });
|
|
118
119
|
const file = path.join(vmzDir, 'server-artifact.json');
|
|
119
|
-
|
|
120
|
+
writePrettyJsonFile(file, artifact);
|
|
120
121
|
const adapterDir = path.join(vmzDir, 'adapters');
|
|
121
122
|
mkdirSync(adapterDir, { recursive: true });
|
|
122
123
|
for (const adapterId of ['worker', 'rust-host']) {
|
|
123
124
|
const projection = projectServerRuntimeAdapter(artifact, adapterId);
|
|
124
125
|
const dir = path.join(adapterDir, adapterId);
|
|
125
126
|
mkdirSync(dir, { recursive: true });
|
|
126
|
-
|
|
127
|
+
writePrettyJsonFile(path.join(dir, 'adapter.json'), projection);
|
|
127
128
|
}
|
|
128
129
|
return { artifact, path: file, httpContractDigest };
|
|
129
130
|
}
|
package/dist/site-delivery.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import crypto from 'node:crypto';
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
+
import { writePrettyJsonFile } from './pretty-json.js';
|
|
9
10
|
export const SITE_DELIVERY_CONTRACT_SCHEMA = 'vmz.site.delivery_contract.v0';
|
|
10
11
|
export const SITE_DELIVERY_RESOLUTION_SCHEMA = 'vmz.site.delivery_resolution.v0';
|
|
11
12
|
/**
|
|
@@ -135,7 +136,7 @@ export function normalizeSiteDelivery(raw, opts = {}) {
|
|
|
135
136
|
artifact: String(d.artifact),
|
|
136
137
|
expectedCompatibility: d.expectedCompatibility || {
|
|
137
138
|
runtime: 'vmz',
|
|
138
|
-
deliveryProfiles: ['
|
|
139
|
+
deliveryProfiles: ['static', 'filesystem'],
|
|
139
140
|
},
|
|
140
141
|
sources,
|
|
141
142
|
resolutionPolicy: {
|
|
@@ -318,11 +319,11 @@ export function emitSiteDelivery(outDir, deliveryRaw, opts = {}) {
|
|
|
318
319
|
}
|
|
319
320
|
const vmzDir = path.join(outDir, '_vmz');
|
|
320
321
|
fs.mkdirSync(vmzDir, { recursive: true });
|
|
321
|
-
|
|
322
|
+
writePrettyJsonFile(path.join(vmzDir, 'site-delivery-contract.json'), norm.contract);
|
|
322
323
|
let resolution = null;
|
|
323
324
|
if (opts.probes) {
|
|
324
325
|
resolution = resolveSiteRelease(norm.contract, opts.probes);
|
|
325
|
-
|
|
326
|
+
writePrettyJsonFile(path.join(vmzDir, 'site-delivery-resolution.json'), resolution);
|
|
326
327
|
}
|
|
327
328
|
return { contract: norm.contract, resolution };
|
|
328
329
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site favicon: author SVG → PNG + ICO + `_vmz/site-favicon.json` head links.
|
|
3
|
+
* Convention: `assets/favicon.svg` (or `favicon.svg`) under project / already in dist.
|
|
4
|
+
*/
|
|
5
|
+
export declare const SITE_FAVICON_SCHEMA = "vmz.site_favicon.v0";
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} distDir
|
|
8
|
+
* @param {{ projectRoot?: string, skipNative?: boolean }} [opts]
|
|
9
|
+
*/
|
|
10
|
+
export declare function emitSiteFavicon(distDir: any, opts?: {}): {
|
|
11
|
+
schema: string;
|
|
12
|
+
status: string;
|
|
13
|
+
source: string;
|
|
14
|
+
svg: string;
|
|
15
|
+
ico: string;
|
|
16
|
+
png: string[];
|
|
17
|
+
headHtml: string;
|
|
18
|
+
} | {
|
|
19
|
+
status: string;
|
|
20
|
+
reason: string;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Read ready head HTML for static/SSR shells (empty when missing).
|
|
24
|
+
* @param {string} distDir
|
|
25
|
+
*/
|
|
26
|
+
export declare function readSiteFaviconHeadHtml(distDir: any): any;
|
|
27
|
+
/**
|
|
28
|
+
* PNG-in-ICO (Vista+); widely accepted by modern browsers.
|
|
29
|
+
* @param {Array<{ png: Buffer, size: number }>} images
|
|
30
|
+
*/
|
|
31
|
+
export declare function packPngsIntoIco(images: any): Buffer<ArrayBuffer>;
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Site favicon: author SVG → PNG + ICO + `_vmz/site-favicon.json` head links.
|
|
3
|
+
* Convention: `assets/favicon.svg` (or `favicon.svg`) under project / already in dist.
|
|
4
|
+
*/
|
|
5
|
+
// @ts-nocheck
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { requireNativeAddon } from './native-addon.js';
|
|
9
|
+
import { writePrettyJsonFile } from './pretty-json.js';
|
|
10
|
+
export const SITE_FAVICON_SCHEMA = 'vmz.site_favicon.v0';
|
|
11
|
+
const ICO_SIZES = [16, 32, 48];
|
|
12
|
+
/**
|
|
13
|
+
* @param {string} distDir
|
|
14
|
+
* @param {{ projectRoot?: string, skipNative?: boolean }} [opts]
|
|
15
|
+
*/
|
|
16
|
+
export function emitSiteFavicon(distDir, opts = {}) {
|
|
17
|
+
const absDist = path.resolve(distDir);
|
|
18
|
+
const projectRoot = opts.projectRoot ? path.resolve(opts.projectRoot) : null;
|
|
19
|
+
const src = discoverFaviconSvg(absDist, projectRoot);
|
|
20
|
+
if (!src) {
|
|
21
|
+
return { status: 'skipped', reason: 'no assets/favicon.svg' };
|
|
22
|
+
}
|
|
23
|
+
const svgText = fs.readFileSync(src.path, 'utf8');
|
|
24
|
+
const assetsDir = path.join(absDist, 'assets');
|
|
25
|
+
fs.mkdirSync(assetsDir, { recursive: true });
|
|
26
|
+
const svgDest = path.join(assetsDir, 'favicon.svg');
|
|
27
|
+
if (path.resolve(src.path) !== path.resolve(svgDest)) {
|
|
28
|
+
fs.copyFileSync(src.path, svgDest);
|
|
29
|
+
}
|
|
30
|
+
/** @type {Buffer[]} */
|
|
31
|
+
const pngs = [];
|
|
32
|
+
if (!opts.skipNative) {
|
|
33
|
+
const native = requireNativeAddon();
|
|
34
|
+
if (typeof native.rasterizeSvgPng !== 'function') {
|
|
35
|
+
throw new Error('vmz native addon missing rasterizeSvgPng — rebuild with `pnpm napi:build`');
|
|
36
|
+
}
|
|
37
|
+
for (const px of ICO_SIZES) {
|
|
38
|
+
const buf = Buffer.from(native.rasterizeSvgPng(svgText, px));
|
|
39
|
+
pngs.push(buf);
|
|
40
|
+
fs.writeFileSync(path.join(assetsDir, `favicon-${px}.png`), buf);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
let icoPath = null;
|
|
44
|
+
if (pngs.length) {
|
|
45
|
+
const ico = packPngsIntoIco(pngs.map((png, i) => ({ png, size: ICO_SIZES[i] })));
|
|
46
|
+
icoPath = path.join(absDist, 'favicon.ico');
|
|
47
|
+
fs.writeFileSync(icoPath, ico);
|
|
48
|
+
}
|
|
49
|
+
const headParts = [];
|
|
50
|
+
if (icoPath) {
|
|
51
|
+
headParts.push('<link rel="icon" href="/favicon.ico" sizes="any">');
|
|
52
|
+
}
|
|
53
|
+
headParts.push('<link rel="icon" href="/assets/favicon.svg" type="image/svg+xml">');
|
|
54
|
+
for (const px of ICO_SIZES) {
|
|
55
|
+
if (fs.existsSync(path.join(assetsDir, `favicon-${px}.png`))) {
|
|
56
|
+
headParts.push(`<link rel="icon" type="image/png" sizes="${px}x${px}" href="/assets/favicon-${px}.png">`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const headHtml = `${headParts.join('\n')}\n`;
|
|
60
|
+
const artifact = {
|
|
61
|
+
schema: SITE_FAVICON_SCHEMA,
|
|
62
|
+
status: 'ready',
|
|
63
|
+
source: src.rel,
|
|
64
|
+
svg: 'assets/favicon.svg',
|
|
65
|
+
ico: icoPath ? 'favicon.ico' : null,
|
|
66
|
+
png: ICO_SIZES.filter((px) => fs.existsSync(path.join(assetsDir, `favicon-${px}.png`))).map((px) => `assets/favicon-${px}.png`),
|
|
67
|
+
headHtml,
|
|
68
|
+
};
|
|
69
|
+
const vmzDir = path.join(absDist, '_vmz');
|
|
70
|
+
fs.mkdirSync(vmzDir, { recursive: true });
|
|
71
|
+
writePrettyJsonFile(path.join(vmzDir, 'site-favicon.json'), artifact);
|
|
72
|
+
return artifact;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Read ready head HTML for static/SSR shells (empty when missing).
|
|
76
|
+
* @param {string} distDir
|
|
77
|
+
*/
|
|
78
|
+
export function readSiteFaviconHeadHtml(distDir) {
|
|
79
|
+
try {
|
|
80
|
+
const p = path.join(distDir, '_vmz', 'site-favicon.json');
|
|
81
|
+
if (!fs.existsSync(p))
|
|
82
|
+
return '';
|
|
83
|
+
const j = JSON.parse(fs.readFileSync(p, 'utf8'));
|
|
84
|
+
if (j?.status !== 'ready' || typeof j.headHtml !== 'string')
|
|
85
|
+
return '';
|
|
86
|
+
return j.headHtml;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return '';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* @param {string} distDir
|
|
94
|
+
* @param {string|null} projectRoot
|
|
95
|
+
*/
|
|
96
|
+
function discoverFaviconSvg(distDir, projectRoot) {
|
|
97
|
+
const candidates = [path.join(distDir, 'assets', 'favicon.svg'), path.join(distDir, 'favicon.svg')];
|
|
98
|
+
if (projectRoot) {
|
|
99
|
+
candidates.push(path.join(projectRoot, 'assets', 'favicon.svg'), path.join(projectRoot, 'favicon.svg'), path.join(projectRoot, 'public', 'assets', 'favicon.svg'), path.join(projectRoot, 'public', 'favicon.svg'));
|
|
100
|
+
}
|
|
101
|
+
for (const abs of candidates) {
|
|
102
|
+
if (fs.existsSync(abs) && fs.statSync(abs).isFile()) {
|
|
103
|
+
return {
|
|
104
|
+
path: abs,
|
|
105
|
+
rel: path.relative(projectRoot || distDir, abs).replace(/\\/g, '/') || 'assets/favicon.svg',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* PNG-in-ICO (Vista+); widely accepted by modern browsers.
|
|
113
|
+
* @param {Array<{ png: Buffer, size: number }>} images
|
|
114
|
+
*/
|
|
115
|
+
export function packPngsIntoIco(images) {
|
|
116
|
+
const count = images.length;
|
|
117
|
+
const header = Buffer.alloc(6);
|
|
118
|
+
header.writeUInt16LE(0, 0);
|
|
119
|
+
header.writeUInt16LE(1, 2);
|
|
120
|
+
header.writeUInt16LE(count, 4);
|
|
121
|
+
const entries = Buffer.alloc(16 * count);
|
|
122
|
+
/** @type {Buffer[]} */
|
|
123
|
+
const blobs = [];
|
|
124
|
+
let offset = 6 + 16 * count;
|
|
125
|
+
for (let i = 0; i < count; i += 1) {
|
|
126
|
+
const { png, size } = images[i];
|
|
127
|
+
const o = i * 16;
|
|
128
|
+
entries.writeUInt8(size >= 256 ? 0 : size, o);
|
|
129
|
+
entries.writeUInt8(size >= 256 ? 0 : size, o + 1);
|
|
130
|
+
entries.writeUInt8(0, o + 2);
|
|
131
|
+
entries.writeUInt8(0, o + 3);
|
|
132
|
+
entries.writeUInt16LE(1, o + 4);
|
|
133
|
+
entries.writeUInt16LE(32, o + 6);
|
|
134
|
+
entries.writeUInt32LE(png.length, o + 8);
|
|
135
|
+
entries.writeUInt32LE(offset, o + 12);
|
|
136
|
+
blobs.push(png);
|
|
137
|
+
offset += png.length;
|
|
138
|
+
}
|
|
139
|
+
return Buffer.concat([header, entries, ...blobs]);
|
|
140
|
+
}
|
package/dist/static-emit.d.ts
CHANGED
|
@@ -43,6 +43,12 @@ export declare function emitWebStatic(distDir: any, opts?: {}): Promise<{
|
|
|
43
43
|
sitemap: string;
|
|
44
44
|
robots: string;
|
|
45
45
|
};
|
|
46
|
+
publicAssets: {
|
|
47
|
+
schema: string;
|
|
48
|
+
status: string;
|
|
49
|
+
fileCount: number;
|
|
50
|
+
source: any;
|
|
51
|
+
};
|
|
46
52
|
};
|
|
47
53
|
htmlFiles: any[];
|
|
48
54
|
skipped: any[];
|
|
@@ -55,6 +61,21 @@ export declare function emitWebStatic(distDir: any, opts?: {}): Promise<{
|
|
|
55
61
|
objects: any[];
|
|
56
62
|
rewrittenHtml: number;
|
|
57
63
|
};
|
|
64
|
+
publicAssets: {
|
|
65
|
+
schema: string;
|
|
66
|
+
status: string;
|
|
67
|
+
reason: string;
|
|
68
|
+
fileCount: number;
|
|
69
|
+
files: any[];
|
|
70
|
+
skippedConflicts: any[];
|
|
71
|
+
} | {
|
|
72
|
+
schema: string;
|
|
73
|
+
status: string;
|
|
74
|
+
source: string;
|
|
75
|
+
fileCount: number;
|
|
76
|
+
files: any[];
|
|
77
|
+
skippedConflicts: any[];
|
|
78
|
+
};
|
|
58
79
|
cdnPolicy: {
|
|
59
80
|
schema: string;
|
|
60
81
|
applicationId: any;
|