@zenithbuild/cli 0.7.3 → 0.7.4
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/README.md +14 -11
- package/dist/adapters/adapter-netlify.js +1 -0
- package/dist/adapters/adapter-node.js +8 -0
- package/dist/adapters/adapter-vercel.js +1 -0
- package/dist/build/compiler-runtime.d.ts +10 -9
- package/dist/build/compiler-runtime.js +51 -1
- package/dist/build/compiler-signal-expression.d.ts +1 -0
- package/dist/build/compiler-signal-expression.js +155 -0
- package/dist/build/expression-rewrites.d.ts +1 -6
- package/dist/build/expression-rewrites.js +61 -65
- package/dist/build/page-component-loop.d.ts +3 -13
- package/dist/build/page-component-loop.js +21 -46
- package/dist/build/page-ir-normalization.d.ts +0 -8
- package/dist/build/page-ir-normalization.js +13 -234
- package/dist/build/page-loop-state.d.ts +6 -9
- package/dist/build/page-loop-state.js +9 -8
- package/dist/build/page-loop.js +27 -22
- package/dist/build/scoped-identifier-rewrite.d.ts +37 -44
- package/dist/build/scoped-identifier-rewrite.js +28 -128
- package/dist/build/server-script.d.ts +2 -1
- package/dist/build/server-script.js +29 -3
- package/dist/build.js +5 -3
- package/dist/component-instance-ir.js +158 -52
- package/dist/dev-build-session.js +20 -6
- package/dist/dev-server.js +82 -39
- package/dist/framework-components/Image.zen +1 -1
- package/dist/images/materialization-plan.d.ts +1 -0
- package/dist/images/materialization-plan.js +6 -0
- package/dist/images/materialize.d.ts +5 -3
- package/dist/images/materialize.js +24 -109
- package/dist/images/router-manifest.d.ts +1 -0
- package/dist/images/router-manifest.js +49 -0
- package/dist/index.js +8 -2
- package/dist/manifest.js +3 -2
- package/dist/preview.d.ts +4 -3
- package/dist/preview.js +87 -53
- package/dist/request-body.d.ts +2 -0
- package/dist/request-body.js +13 -0
- package/dist/request-origin.d.ts +2 -0
- package/dist/request-origin.js +45 -0
- package/dist/route-check-support.d.ts +1 -0
- package/dist/route-check-support.js +4 -0
- package/dist/server-contract.d.ts +15 -0
- package/dist/server-contract.js +102 -32
- package/dist/server-error.d.ts +4 -0
- package/dist/server-error.js +34 -0
- package/dist/server-output.d.ts +2 -0
- package/dist/server-output.js +13 -0
- package/dist/server-runtime/node-server.js +33 -27
- package/dist/server-runtime/route-render.d.ts +3 -3
- package/dist/server-runtime/route-render.js +20 -31
- package/dist/server-script-composition.d.ts +11 -5
- package/dist/server-script-composition.js +25 -10
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -2,26 +2,30 @@
|
|
|
2
2
|
|
|
3
3
|
> **⚠️ Internal API:** This package is an internal implementation detail of the Zenith framework. It is not intended for public use and its API may break without warning. Please use `@zenithbuild/core` instead.
|
|
4
4
|
|
|
5
|
-
|
|
6
5
|
The command-line interface for developing and building Zenith applications.
|
|
7
6
|
|
|
8
7
|
## Canonical Docs
|
|
9
8
|
|
|
10
|
-
- CLI contract:
|
|
11
|
-
- Deployment targets guide:
|
|
12
|
-
-
|
|
9
|
+
- CLI contract: `../../docs/documentation/cli-contract.md`
|
|
10
|
+
- Deployment targets guide: `../../docs/documentation/guides/deployment-targets.md`
|
|
11
|
+
- Route protection: `../../docs/documentation/routing/route-protection.md`
|
|
13
12
|
- Server output contract: `./SERVER_OUTPUT_CONTRACT.md`
|
|
14
13
|
|
|
15
14
|
## Overview
|
|
16
15
|
|
|
17
|
-
`@zenithbuild/cli`
|
|
16
|
+
`@zenithbuild/cli` is Zenith's deterministic project orchestrator. It owns the daily development loop:
|
|
17
|
+
|
|
18
|
+
- `zenith dev`
|
|
19
|
+
- `zenith build`
|
|
20
|
+
- `zenith preview`
|
|
21
|
+
|
|
22
|
+
It does not ship a public plugin-management surface.
|
|
18
23
|
|
|
19
24
|
## Features
|
|
20
25
|
|
|
21
26
|
- **Dev Server**: Instant HMR (Hot Module Replacement) powered by Bun.
|
|
22
|
-
- **Build System**:
|
|
23
|
-
- **
|
|
24
|
-
- **Preview**: Test your production builds locally.
|
|
27
|
+
- **Build System**: deterministic build output and adapter packaging.
|
|
28
|
+
- **Preview**: target-aware verification of built output.
|
|
25
29
|
|
|
26
30
|
## Config Baseline
|
|
27
31
|
|
|
@@ -78,6 +82,8 @@ Current limitations:
|
|
|
78
82
|
|
|
79
83
|
- There is no separate `assetPrefix` knob. Assets intentionally follow `basePath`.
|
|
80
84
|
- `vercel` and `netlify` do not yet emit a deployed `/_zenith/image` endpoint. The `node` target does.
|
|
85
|
+
- Image materialization is route-artifact-driven. Build, preview, and server render consume structured `image_materialization` metadata instead of executing page assets, and dynamic image props are currently unsupported until the compiler emits a dedicated image-props artifact.
|
|
86
|
+
- There is no shipped plugin install/remove command surface in this CLI.
|
|
81
87
|
|
|
82
88
|
## Commands
|
|
83
89
|
|
|
@@ -90,9 +96,6 @@ Compiles and bundles your application for production.
|
|
|
90
96
|
### `zenith preview`
|
|
91
97
|
Previews the locally built target contract for verification. Static targets serve built files; `target: 'node'` boots the built Node artifact.
|
|
92
98
|
|
|
93
|
-
### `zenith add <plugin>`
|
|
94
|
-
Installs and configures a Zenith plugin.
|
|
95
|
-
|
|
96
99
|
## Installation
|
|
97
100
|
|
|
98
101
|
Typically installed as a dev dependency in your Zenith project:
|
|
@@ -110,6 +110,7 @@ export const netlifyAdapter = {
|
|
|
110
110
|
await cp(join(options.coreOutput, 'server', 'images'), join(functionsDir, '_zenith', 'images'), { recursive: true, force: true });
|
|
111
111
|
await cp(join(options.coreOutput, 'server', 'base-path.js'), join(functionsDir, '_zenith', 'base-path.js'), { force: true });
|
|
112
112
|
await cp(join(options.coreOutput, 'server', 'server-contract.js'), join(functionsDir, '_zenith', 'server-contract.js'), { force: true });
|
|
113
|
+
await cp(join(options.coreOutput, 'server', 'server-error.js'), join(functionsDir, '_zenith', 'server-error.js'), { force: true });
|
|
113
114
|
for (const route of serverRoutes) {
|
|
114
115
|
await cp(join(options.coreOutput, 'server', 'routes', route.name), join(functionsDir, '_zenith', 'routes', route.name), { recursive: true, force: true });
|
|
115
116
|
await writeFile(join(functionsDir, `__zenith_${route.name}.mjs`), createFunctionSource(route), 'utf8');
|
|
@@ -12,6 +12,14 @@ const NODE_RUNTIME_FILES = [
|
|
|
12
12
|
from: new URL('../server/resolve-request-route.js', import.meta.url),
|
|
13
13
|
to: 'runtime/resolve-request-route.js'
|
|
14
14
|
},
|
|
15
|
+
{
|
|
16
|
+
from: new URL('../request-origin.js', import.meta.url),
|
|
17
|
+
to: 'request-origin.js'
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
from: new URL('../server-error.js', import.meta.url),
|
|
21
|
+
to: 'server-error.js'
|
|
22
|
+
},
|
|
15
23
|
{
|
|
16
24
|
from: new URL('../images/service.js', import.meta.url),
|
|
17
25
|
to: 'images/service.js'
|
|
@@ -84,6 +84,7 @@ export const vercelAdapter = {
|
|
|
84
84
|
await cp(join(options.coreOutput, 'server', 'images'), join(functionDir, 'images'), { recursive: true, force: true });
|
|
85
85
|
await cp(join(options.coreOutput, 'server', 'base-path.js'), join(functionDir, 'base-path.js'), { force: true });
|
|
86
86
|
await cp(join(options.coreOutput, 'server', 'server-contract.js'), join(functionDir, 'server-contract.js'), { force: true });
|
|
87
|
+
await cp(join(options.coreOutput, 'server', 'server-error.js'), join(functionDir, 'server-error.js'), { force: true });
|
|
87
88
|
await cp(join(options.coreOutput, 'server', 'routes', route.name), functionDir, { recursive: true, force: true });
|
|
88
89
|
await writeFile(join(functionDir, 'package.json'), '{\n "type": "module"\n}\n', 'utf8');
|
|
89
90
|
await writeFile(join(functionDir, 'index.js'), createFunctionSource(route), 'utf8');
|
|
@@ -42,17 +42,18 @@ export function createTimedCompilerRunner(startupProfile: ReturnType<typeof impo
|
|
|
42
42
|
* @param {object | null} [logger]
|
|
43
43
|
* @param {boolean} [showInfo]
|
|
44
44
|
* @param {string|object} [bundlerBin]
|
|
45
|
-
* @param {{ devStableAssets?: boolean, rebuildStrategy?: 'full'|'bundle-only'|'page-only', changedRoutes?: string[], fastPath?: boolean, globalGraphHash?: string, basePath?: string }} [bundlerOptions]
|
|
45
|
+
* @param {{ devStableAssets?: boolean, rebuildStrategy?: 'full'|'bundle-only'|'page-only', changedRoutes?: string[], fastPath?: boolean, globalGraphHash?: string, basePath?: string, routeCheck?: boolean }} [bundlerOptions]
|
|
46
46
|
* @returns {Promise<void>}
|
|
47
47
|
*/
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Merge compiler-owned static image materialization rows from marker bindings + rewritten props literals.
|
|
50
|
+
* @param {object} pageIr
|
|
51
|
+
* @param {string[]} literals
|
|
52
|
+
* @param {object} [compilerRunOptions]
|
|
53
|
+
* @returns {object}
|
|
54
|
+
*/
|
|
55
|
+
export function mergePageImageMaterialization(pageIr: object, literals: string[], compilerRunOptions?: object): object;
|
|
56
|
+
export function runBundler(envelope: any, outDir: any, projectRoot: any, logger?: null, showInfo?: boolean, bundlerBin?: string, bundlerOptions?: {}): Promise<any>;
|
|
56
57
|
/**
|
|
57
58
|
* @param {string} rootDir
|
|
58
59
|
* @returns {Promise<string[]>}
|
|
@@ -171,9 +171,56 @@ export function createTimedCompilerRunner(startupProfile, compilerTotals) {
|
|
|
171
171
|
* @param {object | null} [logger]
|
|
172
172
|
* @param {boolean} [showInfo]
|
|
173
173
|
* @param {string|object} [bundlerBin]
|
|
174
|
-
* @param {{ devStableAssets?: boolean, rebuildStrategy?: 'full'|'bundle-only'|'page-only', changedRoutes?: string[], fastPath?: boolean, globalGraphHash?: string, basePath?: string }} [bundlerOptions]
|
|
174
|
+
* @param {{ devStableAssets?: boolean, rebuildStrategy?: 'full'|'bundle-only'|'page-only', changedRoutes?: string[], fastPath?: boolean, globalGraphHash?: string, basePath?: string, routeCheck?: boolean }} [bundlerOptions]
|
|
175
175
|
* @returns {Promise<void>}
|
|
176
176
|
*/
|
|
177
|
+
/**
|
|
178
|
+
* Merge compiler-owned static image materialization rows from marker bindings + rewritten props literals.
|
|
179
|
+
* @param {object} pageIr
|
|
180
|
+
* @param {string[]} literals
|
|
181
|
+
* @param {object} [compilerRunOptions]
|
|
182
|
+
* @returns {object}
|
|
183
|
+
*/
|
|
184
|
+
export function mergePageImageMaterialization(pageIr, literals, compilerRunOptions = {}) {
|
|
185
|
+
if (!Array.isArray(literals) || literals.length === 0) {
|
|
186
|
+
return pageIr;
|
|
187
|
+
}
|
|
188
|
+
const compilerToolchain = compilerRunOptions.compilerToolchain
|
|
189
|
+
|| (compilerRunOptions.compilerBin && typeof compilerRunOptions.compilerBin === 'object'
|
|
190
|
+
? compilerRunOptions.compilerBin
|
|
191
|
+
: null);
|
|
192
|
+
const compilerBin = !compilerToolchain && typeof compilerRunOptions.compilerBin === 'string'
|
|
193
|
+
? compilerRunOptions.compilerBin
|
|
194
|
+
: null;
|
|
195
|
+
const payload = JSON.stringify({
|
|
196
|
+
marker_bindings: pageIr.marker_bindings,
|
|
197
|
+
literals
|
|
198
|
+
});
|
|
199
|
+
const args = ['--merge-image-materialization'];
|
|
200
|
+
const opts = {
|
|
201
|
+
encoding: 'utf8',
|
|
202
|
+
maxBuffer: COMPILER_SPAWN_MAX_BUFFER,
|
|
203
|
+
input: payload
|
|
204
|
+
};
|
|
205
|
+
const result = compilerToolchain
|
|
206
|
+
? runToolchainSync(compilerToolchain, args, opts).result
|
|
207
|
+
: (compilerBin
|
|
208
|
+
? spawnSync(compilerBin, args, opts)
|
|
209
|
+
: runToolchainSync(createCompilerToolchain({
|
|
210
|
+
logger: compilerRunOptions.logger || null
|
|
211
|
+
}), args, opts).result);
|
|
212
|
+
if (result.error) {
|
|
213
|
+
throw new Error(`merge image materialization spawn failed: ${result.error.message}`);
|
|
214
|
+
}
|
|
215
|
+
if (result.status !== 0) {
|
|
216
|
+
throw new Error(`merge image materialization failed with exit code ${result.status}\n${result.stderr || ''}`);
|
|
217
|
+
}
|
|
218
|
+
const parsed = JSON.parse(result.stdout);
|
|
219
|
+
return {
|
|
220
|
+
...pageIr,
|
|
221
|
+
image_materialization: parsed.image_materialization
|
|
222
|
+
};
|
|
223
|
+
}
|
|
177
224
|
export function runBundler(envelope, outDir, projectRoot, logger = null, showInfo = true, bundlerBin = resolveBundlerBin(projectRoot), bundlerOptions = {}) {
|
|
178
225
|
return new Promise((resolvePromise, rejectPromise) => {
|
|
179
226
|
const useStructuredLogger = Boolean(logger && typeof logger.childLine === 'function');
|
|
@@ -192,6 +239,9 @@ export function runBundler(envelope, outDir, projectRoot, logger = null, showInf
|
|
|
192
239
|
if (typeof bundlerOptions.basePath === 'string' && bundlerOptions.basePath.length > 0) {
|
|
193
240
|
bundlerArgs.push('--base-path', bundlerOptions.basePath);
|
|
194
241
|
}
|
|
242
|
+
if (bundlerOptions.routeCheck === true) {
|
|
243
|
+
bundlerArgs.push('--route-check');
|
|
244
|
+
}
|
|
195
245
|
if (bundlerOptions.devStableAssets === true) {
|
|
196
246
|
bundlerArgs.push('--dev-stable-assets');
|
|
197
247
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function rewriteCompilerSignalMapReferences(compiledExpr: any, buildReplacement: any): string | null;
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import { loadTypeScriptApi } from './compiler-runtime.js';
|
|
2
|
+
import { normalizeTypeScriptExpression } from './typescript-expression-utils.js';
|
|
3
|
+
function collectBindingNames(ts, name, target) {
|
|
4
|
+
if (ts.isIdentifier(name)) {
|
|
5
|
+
target.add(name.text);
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
if (ts.isObjectBindingPattern(name) || ts.isArrayBindingPattern(name)) {
|
|
9
|
+
for (const element of name.elements) {
|
|
10
|
+
if (ts.isBindingElement(element)) {
|
|
11
|
+
collectBindingNames(ts, element.name, target);
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function collectDirectBlockBindings(ts, block, target) {
|
|
17
|
+
const statements = Array.isArray(block?.statements) ? block.statements : [];
|
|
18
|
+
for (const statement of statements) {
|
|
19
|
+
if (ts.isVariableStatement(statement)) {
|
|
20
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
21
|
+
collectBindingNames(ts, declaration.name, target);
|
|
22
|
+
}
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if ((ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) && statement.name) {
|
|
26
|
+
target.add(statement.name.text);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function isNestedBlockScope(ts, node) {
|
|
31
|
+
return (ts.isBlock(node) || ts.isModuleBlock(node)) && !ts.isSourceFile(node.parent);
|
|
32
|
+
}
|
|
33
|
+
function resolveCompilerSignalIndex(ts, node, localBindings) {
|
|
34
|
+
if (!ts.isCallExpression(node)) {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
if (node.arguments.length !== 1) {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const expression = node.expression;
|
|
41
|
+
if (!ts.isPropertyAccessExpression(expression) || expression.name.text !== 'get') {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (!ts.isIdentifier(expression.expression) || expression.expression.text !== 'signalMap') {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
if (localBindings.has('signalMap')) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
const [indexArg] = node.arguments;
|
|
51
|
+
if (!ts.isNumericLiteral(indexArg)) {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
const signalIndex = Number.parseInt(indexArg.text, 10);
|
|
55
|
+
return Number.isInteger(signalIndex) ? signalIndex : null;
|
|
56
|
+
}
|
|
57
|
+
export function rewriteCompilerSignalMapReferences(compiledExpr, buildReplacement) {
|
|
58
|
+
const source = typeof compiledExpr === 'string' ? compiledExpr.trim() : '';
|
|
59
|
+
if (!source) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
if (!source.includes('signalMap.get(') || typeof buildReplacement !== 'function') {
|
|
63
|
+
return normalizeTypeScriptExpression(source);
|
|
64
|
+
}
|
|
65
|
+
const ts = loadTypeScriptApi();
|
|
66
|
+
if (!ts) {
|
|
67
|
+
return normalizeTypeScriptExpression(source);
|
|
68
|
+
}
|
|
69
|
+
let sourceFile;
|
|
70
|
+
try {
|
|
71
|
+
sourceFile = ts.createSourceFile('zenith-compiled-expression.ts', `const __zenith_expr__ = (${source});`, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return normalizeTypeScriptExpression(source);
|
|
75
|
+
}
|
|
76
|
+
const nextScopeBindings = (node, localBindings) => {
|
|
77
|
+
if (ts.isSourceFile(node)) {
|
|
78
|
+
return localBindings;
|
|
79
|
+
}
|
|
80
|
+
if (ts.isFunctionLike(node)) {
|
|
81
|
+
const next = new Set(localBindings);
|
|
82
|
+
if (node.name && ts.isIdentifier(node.name) && !ts.isSourceFile(node.parent)) {
|
|
83
|
+
next.add(node.name.text);
|
|
84
|
+
}
|
|
85
|
+
for (const param of node.parameters) {
|
|
86
|
+
collectBindingNames(ts, param.name, next);
|
|
87
|
+
}
|
|
88
|
+
return next;
|
|
89
|
+
}
|
|
90
|
+
if (isNestedBlockScope(ts, node)) {
|
|
91
|
+
const next = new Set(localBindings);
|
|
92
|
+
collectDirectBlockBindings(ts, node, next);
|
|
93
|
+
return next;
|
|
94
|
+
}
|
|
95
|
+
if (ts.isCatchClause(node) && node.variableDeclaration) {
|
|
96
|
+
const next = new Set(localBindings);
|
|
97
|
+
collectBindingNames(ts, node.variableDeclaration.name, next);
|
|
98
|
+
return next;
|
|
99
|
+
}
|
|
100
|
+
if ((ts.isForStatement(node) || ts.isForInStatement(node) || ts.isForOfStatement(node))
|
|
101
|
+
&& node.initializer
|
|
102
|
+
&& ts.isVariableDeclarationList(node.initializer)) {
|
|
103
|
+
const next = new Set(localBindings);
|
|
104
|
+
for (const declaration of node.initializer.declarations) {
|
|
105
|
+
collectBindingNames(ts, declaration.name, next);
|
|
106
|
+
}
|
|
107
|
+
return next;
|
|
108
|
+
}
|
|
109
|
+
return localBindings;
|
|
110
|
+
};
|
|
111
|
+
const transformer = (context) => {
|
|
112
|
+
const visit = (node, localBindings) => {
|
|
113
|
+
const scopeBindings = nextScopeBindings(node, localBindings);
|
|
114
|
+
if (ts.isCallExpression(node) && node.arguments.length === 0) {
|
|
115
|
+
const expression = node.expression;
|
|
116
|
+
if (ts.isPropertyAccessExpression(expression) && expression.name.text === 'get') {
|
|
117
|
+
const signalIndex = resolveCompilerSignalIndex(ts, expression.expression, scopeBindings);
|
|
118
|
+
if (Number.isInteger(signalIndex)) {
|
|
119
|
+
const replacement = buildReplacement({ ts, signalIndex, valueRead: true });
|
|
120
|
+
if (replacement) {
|
|
121
|
+
return replacement;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const signalIndex = resolveCompilerSignalIndex(ts, node, scopeBindings);
|
|
127
|
+
if (Number.isInteger(signalIndex)) {
|
|
128
|
+
const replacement = buildReplacement({ ts, signalIndex, valueRead: false });
|
|
129
|
+
if (replacement) {
|
|
130
|
+
return replacement;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return ts.visitEachChild(node, (child) => visit(child, scopeBindings), context);
|
|
134
|
+
};
|
|
135
|
+
return (node) => ts.visitNode(node, (child) => visit(child, new Set()));
|
|
136
|
+
};
|
|
137
|
+
const result = ts.transform(sourceFile, [transformer]);
|
|
138
|
+
try {
|
|
139
|
+
const printed = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed })
|
|
140
|
+
.printFile(result.transformed[0])
|
|
141
|
+
.trimEnd();
|
|
142
|
+
const prefix = 'const __zenith_expr__ = (';
|
|
143
|
+
const suffix = ');';
|
|
144
|
+
if (!printed.startsWith(prefix) || !printed.endsWith(suffix)) {
|
|
145
|
+
return normalizeTypeScriptExpression(source);
|
|
146
|
+
}
|
|
147
|
+
return normalizeTypeScriptExpression(printed.slice(prefix.length, printed.length - suffix.length));
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
return normalizeTypeScriptExpression(source);
|
|
151
|
+
}
|
|
152
|
+
finally {
|
|
153
|
+
result.dispose();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* @param {string} compPath
|
|
3
|
-
* @param {string} componentSource
|
|
4
2
|
* @param {object} compIr
|
|
5
|
-
* @param {object} compilerOpts
|
|
6
|
-
* @param {string|object} compilerBin
|
|
7
|
-
* @param {Map<string, string[]> | null} [templateExpressionCache]
|
|
8
3
|
* @param {Record<string, number> | null} [rewriteMetrics]
|
|
9
4
|
* @returns {{
|
|
10
5
|
* map: Map<string, string>,
|
|
@@ -22,7 +17,7 @@
|
|
|
22
17
|
* sequence: Array<{ raw: string, rewritten: string, binding: object | null }>
|
|
23
18
|
* }}
|
|
24
19
|
*/
|
|
25
|
-
export function buildComponentExpressionRewrite(
|
|
20
|
+
export function buildComponentExpressionRewrite(compIr: object, rewriteMetrics?: Record<string, number> | null): {
|
|
26
21
|
map: Map<string, string>;
|
|
27
22
|
bindings: Map<string, {
|
|
28
23
|
compiled_expr: string | null;
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { performance } from 'node:perf_hooks';
|
|
2
|
-
import {
|
|
3
|
-
import { runCompiler } from './compiler-runtime.js';
|
|
2
|
+
import { rewriteCompilerSignalMapReferences } from './compiler-signal-expression.js';
|
|
4
3
|
/**
|
|
5
|
-
* @param {string} compPath
|
|
6
|
-
* @param {string} componentSource
|
|
7
4
|
* @param {object} compIr
|
|
8
|
-
* @param {object} compilerOpts
|
|
9
|
-
* @param {string|object} compilerBin
|
|
10
|
-
* @param {Map<string, string[]> | null} [templateExpressionCache]
|
|
11
5
|
* @param {Record<string, number> | null} [rewriteMetrics]
|
|
12
6
|
* @returns {{
|
|
13
7
|
* map: Map<string, string>,
|
|
@@ -25,7 +19,9 @@ import { runCompiler } from './compiler-runtime.js';
|
|
|
25
19
|
* sequence: Array<{ raw: string, rewritten: string, binding: object | null }>
|
|
26
20
|
* }}
|
|
27
21
|
*/
|
|
28
|
-
export function buildComponentExpressionRewrite(
|
|
22
|
+
export function buildComponentExpressionRewrite(compIr, rewriteMetrics = null) {
|
|
23
|
+
// Downstream is only allowed to read compiler-owned raw->rewritten pairs here.
|
|
24
|
+
// It must not synthesize new identifier meaning beyond this mapping.
|
|
29
25
|
const out = {
|
|
30
26
|
map: new Map(),
|
|
31
27
|
bindings: new Map(),
|
|
@@ -39,60 +35,19 @@ export function buildComponentExpressionRewrite(compPath, componentSource, compI
|
|
|
39
35
|
if (rewrittenExpressions.length === 0) {
|
|
40
36
|
return out;
|
|
41
37
|
}
|
|
42
|
-
const hoistedState = Array.isArray(compIr?.hoisted?.state) ? compIr.hoisted.state : [];
|
|
43
|
-
const hoistedFunctions = Array.isArray(compIr?.hoisted?.functions) ? compIr.hoisted.functions : [];
|
|
44
|
-
const hoistedDeclarations = Array.isArray(compIr?.hoisted?.declarations) ? compIr.hoisted.declarations : [];
|
|
45
|
-
const signals = Array.isArray(compIr?.signals) ? compIr.signals : [];
|
|
46
|
-
if (hoistedState.length === 0 &&
|
|
47
|
-
hoistedFunctions.length === 0 &&
|
|
48
|
-
hoistedDeclarations.length === 0 &&
|
|
49
|
-
signals.length === 0) {
|
|
50
|
-
return out;
|
|
51
|
-
}
|
|
52
38
|
if (rewriteMetrics && typeof rewriteMetrics === 'object') {
|
|
53
39
|
rewriteMetrics.calls += 1;
|
|
54
40
|
}
|
|
55
|
-
|
|
56
|
-
if (!templateOnly.trim()) {
|
|
57
|
-
return out;
|
|
58
|
-
}
|
|
59
|
-
const cacheKey = `${compPath}\u0000${templateOnly}`;
|
|
60
|
-
let rawExpressions = null;
|
|
61
|
-
if (templateExpressionCache instanceof Map && templateExpressionCache.has(cacheKey)) {
|
|
62
|
-
rawExpressions = templateExpressionCache.get(cacheKey);
|
|
63
|
-
if (rewriteMetrics && typeof rewriteMetrics === 'object') {
|
|
64
|
-
rewriteMetrics.cacheHits += 1;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
let templateIr;
|
|
69
|
-
const templateCompileStartedAt = performance.now();
|
|
70
|
-
try {
|
|
71
|
-
templateIr = runCompiler(compPath, templateOnly, compilerOpts, {
|
|
72
|
-
suppressWarnings: true,
|
|
73
|
-
compilerToolchain: compilerBin
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
catch {
|
|
77
|
-
return out;
|
|
78
|
-
}
|
|
79
|
-
rawExpressions = Array.isArray(templateIr?.expressions) ? templateIr.expressions : [];
|
|
80
|
-
if (templateExpressionCache instanceof Map) {
|
|
81
|
-
templateExpressionCache.set(cacheKey, rawExpressions);
|
|
82
|
-
}
|
|
83
|
-
if (rewriteMetrics && typeof rewriteMetrics === 'object') {
|
|
84
|
-
rewriteMetrics.cacheMisses += 1;
|
|
85
|
-
rewriteMetrics.templateCompileMs += Math.round((performance.now() - templateCompileStartedAt) * 100) / 100;
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const count = Math.min(rawExpressions.length, rewrittenExpressions.length);
|
|
89
|
-
for (let i = 0; i < count; i++) {
|
|
90
|
-
const raw = rawExpressions[i];
|
|
41
|
+
for (let i = 0; i < rewrittenExpressions.length; i++) {
|
|
91
42
|
const rewritten = rewrittenExpressions[i];
|
|
92
|
-
if (typeof
|
|
43
|
+
if (typeof rewritten !== 'string') {
|
|
93
44
|
continue;
|
|
94
45
|
}
|
|
95
46
|
const binding = rewrittenBindings[i];
|
|
47
|
+
// Compiler emits the raw source literal for exact lookup; CLI only transports it.
|
|
48
|
+
const raw = typeof binding?.literal === 'string' && binding.literal.length > 0
|
|
49
|
+
? binding.literal
|
|
50
|
+
: rewritten;
|
|
96
51
|
const normalizedBinding = binding && typeof binding === 'object'
|
|
97
52
|
? {
|
|
98
53
|
compiled_expr: typeof binding.compiled_expr === 'string' ? binding.compiled_expr : null,
|
|
@@ -133,6 +88,10 @@ export function buildComponentExpressionRewrite(compPath, componentSource, compI
|
|
|
133
88
|
}
|
|
134
89
|
}
|
|
135
90
|
}
|
|
91
|
+
if (rewriteMetrics && typeof rewriteMetrics === 'object') {
|
|
92
|
+
rewriteMetrics.compilerOwnedBindings += out.sequence.length;
|
|
93
|
+
rewriteMetrics.ambiguousBindings += out.ambiguous.size;
|
|
94
|
+
}
|
|
136
95
|
return out;
|
|
137
96
|
}
|
|
138
97
|
/**
|
|
@@ -143,27 +102,28 @@ export function buildComponentExpressionRewrite(compPath, componentSource, compI
|
|
|
143
102
|
* @returns {string | null}
|
|
144
103
|
*/
|
|
145
104
|
export function remapCompiledExpressionSignals(compiledExpr, componentSignals, componentStateBindings, pageSignalIndexByStateKey) {
|
|
105
|
+
// This is a mechanical index remap across page merge boundaries, not a second compiler pass.
|
|
146
106
|
if (typeof compiledExpr !== 'string' || compiledExpr.length === 0) {
|
|
147
107
|
return null;
|
|
148
108
|
}
|
|
149
|
-
return compiledExpr
|
|
150
|
-
const
|
|
151
|
-
if (!Number.isInteger(localIndex)) {
|
|
152
|
-
return full;
|
|
153
|
-
}
|
|
154
|
-
const signal = componentSignals[localIndex];
|
|
109
|
+
return rewriteCompilerSignalMapReferences(compiledExpr, ({ ts, signalIndex, valueRead }) => {
|
|
110
|
+
const signal = componentSignals[signalIndex];
|
|
155
111
|
if (!signal || !Number.isInteger(signal.state_index)) {
|
|
156
|
-
return
|
|
112
|
+
return null;
|
|
157
113
|
}
|
|
158
114
|
const stateKey = componentStateBindings[signal.state_index]?.key;
|
|
159
115
|
if (typeof stateKey !== 'string' || stateKey.length === 0) {
|
|
160
|
-
return
|
|
116
|
+
return null;
|
|
161
117
|
}
|
|
162
118
|
const pageIndex = pageSignalIndexByStateKey.get(stateKey);
|
|
163
119
|
if (!Number.isInteger(pageIndex)) {
|
|
164
|
-
return
|
|
120
|
+
return null;
|
|
165
121
|
}
|
|
166
|
-
|
|
122
|
+
const signalMapRead = ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(ts.factory.createIdentifier('signalMap'), 'get'), undefined, [ts.factory.createNumericLiteral(String(pageIndex))]);
|
|
123
|
+
if (valueRead) {
|
|
124
|
+
return ts.factory.createCallExpression(ts.factory.createPropertyAccessExpression(signalMapRead, 'get'), undefined, []);
|
|
125
|
+
}
|
|
126
|
+
return signalMapRead;
|
|
167
127
|
});
|
|
168
128
|
}
|
|
169
129
|
/**
|
|
@@ -319,6 +279,15 @@ export function mergeExpressionRewriteMaps(pageMap, pageBindingMap, pageAmbiguou
|
|
|
319
279
|
const resolved = resolveRewrittenBindingMetadata(pageBindingContext, componentRewrite, binding, bindingResolutionMetrics);
|
|
320
280
|
const existing = pageBindingMap.get(raw);
|
|
321
281
|
if (existing && JSON.stringify(existing) !== JSON.stringify(resolved)) {
|
|
282
|
+
const existingWeight = measureBindingSpecificity(existing, raw);
|
|
283
|
+
const resolvedWeight = measureBindingSpecificity(resolved, raw);
|
|
284
|
+
if (resolvedWeight > existingWeight && existingWeight === 0) {
|
|
285
|
+
pageBindingMap.set(raw, resolved);
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
if (existingWeight > resolvedWeight && resolvedWeight === 0) {
|
|
289
|
+
continue;
|
|
290
|
+
}
|
|
322
291
|
pageAmbiguous.add(raw);
|
|
323
292
|
pageMap.delete(raw);
|
|
324
293
|
pageBindingMap.delete(raw);
|
|
@@ -340,6 +309,33 @@ export function mergeExpressionRewriteMaps(pageMap, pageBindingMap, pageAmbiguou
|
|
|
340
309
|
pageMap.set(raw, rewritten);
|
|
341
310
|
}
|
|
342
311
|
}
|
|
312
|
+
function measureBindingSpecificity(binding, raw) {
|
|
313
|
+
if (!binding || typeof binding !== 'object') {
|
|
314
|
+
return 0;
|
|
315
|
+
}
|
|
316
|
+
let score = 0;
|
|
317
|
+
if (typeof binding.compiled_expr === 'string' &&
|
|
318
|
+
binding.compiled_expr.length > 0 &&
|
|
319
|
+
binding.compiled_expr !== raw) {
|
|
320
|
+
score += 4;
|
|
321
|
+
}
|
|
322
|
+
if (Number.isInteger(binding.signal_index)) {
|
|
323
|
+
score += 2;
|
|
324
|
+
}
|
|
325
|
+
if (Array.isArray(binding.signal_indices) && binding.signal_indices.length > 0) {
|
|
326
|
+
score += 2;
|
|
327
|
+
}
|
|
328
|
+
if (Number.isInteger(binding.state_index)) {
|
|
329
|
+
score += 1;
|
|
330
|
+
}
|
|
331
|
+
if (typeof binding.component_instance === 'string' && binding.component_instance.length > 0) {
|
|
332
|
+
score += 1;
|
|
333
|
+
}
|
|
334
|
+
if (typeof binding.component_binding === 'string' && binding.component_binding.length > 0) {
|
|
335
|
+
score += 1;
|
|
336
|
+
}
|
|
337
|
+
return score;
|
|
338
|
+
}
|
|
343
339
|
/**
|
|
344
340
|
* @param {string} identifier
|
|
345
341
|
* @param {Array<{ key?: string }>} stateBindings
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function buildPageOwnerContext({ componentOccurrences, sourceFile, pageOwnerSource, compilerOpts, compilerBin, timedRunCompiler, cooperativeYield,
|
|
1
|
+
export function buildPageOwnerContext({ componentOccurrences, sourceFile, pageOwnerSource, compilerOpts, compilerBin, timedRunCompiler, cooperativeYield, expressionRewriteMetrics, startupProfile }: {
|
|
2
2
|
componentOccurrences: any;
|
|
3
3
|
sourceFile: any;
|
|
4
4
|
pageOwnerSource: any;
|
|
@@ -6,7 +6,6 @@ export function buildPageOwnerContext({ componentOccurrences, sourceFile, pageOw
|
|
|
6
6
|
compilerBin: any;
|
|
7
7
|
timedRunCompiler: any;
|
|
8
8
|
cooperativeYield: any;
|
|
9
|
-
templateExpressionCache: any;
|
|
10
9
|
expressionRewriteMetrics: any;
|
|
11
10
|
startupProfile: any;
|
|
12
11
|
}): Promise<{
|
|
@@ -19,10 +18,6 @@ export function buildPageOwnerContext({ componentOccurrences, sourceFile, pageOw
|
|
|
19
18
|
ambiguous: Set<any>;
|
|
20
19
|
sequence: never[];
|
|
21
20
|
};
|
|
22
|
-
pageOwnerScopeRewrite: {
|
|
23
|
-
map: Map<any, any>;
|
|
24
|
-
ambiguous: Set<any>;
|
|
25
|
-
};
|
|
26
21
|
} | {
|
|
27
22
|
pageOwnerCompileMs: any;
|
|
28
23
|
pageOwnerExpressionRewrite: {
|
|
@@ -51,12 +46,8 @@ export function buildPageOwnerContext({ componentOccurrences, sourceFile, pageOw
|
|
|
51
46
|
binding: object | null;
|
|
52
47
|
}>;
|
|
53
48
|
};
|
|
54
|
-
pageOwnerScopeRewrite: {
|
|
55
|
-
map: Map<string, string>;
|
|
56
|
-
ambiguous: Set<string>;
|
|
57
|
-
};
|
|
58
49
|
}>;
|
|
59
|
-
export function runPageComponentLoop({ componentOccurrences, occurrenceCountByPath, sourceFile, registry, compilerOpts, compilerBin, timedRunCompiler, cooperativeYield, startupProfile, compilerTotals, emitCompilerWarning, componentIrCache, componentDocumentModeCache, componentExpressionRewriteCache,
|
|
50
|
+
export function runPageComponentLoop({ componentOccurrences, occurrenceCountByPath, sourceFile, registry, compilerOpts, compilerBin, timedRunCompiler, cooperativeYield, startupProfile, compilerTotals, emitCompilerWarning, componentIrCache, componentDocumentModeCache, componentExpressionRewriteCache, expressionRewriteMetrics, pageOwnerExpressionRewrite, pageIr, pageIrMergeCache, seenStaticImports, pageExpressionRewriteMap, pageExpressionBindingMap, pageAmbiguousExpressionMap, knownRefKeys, componentOccurrencePlans, imagePropsLiterals, pagePhase, pageBindingResolutionBreakdown, pageMergeBreakdown, pageComponentLoopBreakdown, hoistedCodeTransformCache, pageStats }: {
|
|
60
51
|
componentOccurrences: any;
|
|
61
52
|
occurrenceCountByPath: any;
|
|
62
53
|
sourceFile: any;
|
|
@@ -71,10 +62,8 @@ export function runPageComponentLoop({ componentOccurrences, occurrenceCountByPa
|
|
|
71
62
|
componentIrCache: any;
|
|
72
63
|
componentDocumentModeCache: any;
|
|
73
64
|
componentExpressionRewriteCache: any;
|
|
74
|
-
templateExpressionCache: any;
|
|
75
65
|
expressionRewriteMetrics: any;
|
|
76
66
|
pageOwnerExpressionRewrite: any;
|
|
77
|
-
pageOwnerScopeRewrite: any;
|
|
78
67
|
pageIr: any;
|
|
79
68
|
pageIrMergeCache: any;
|
|
80
69
|
seenStaticImports: any;
|
|
@@ -83,6 +72,7 @@ export function runPageComponentLoop({ componentOccurrences, occurrenceCountByPa
|
|
|
83
72
|
pageAmbiguousExpressionMap: any;
|
|
84
73
|
knownRefKeys: any;
|
|
85
74
|
componentOccurrencePlans: any;
|
|
75
|
+
imagePropsLiterals: any;
|
|
86
76
|
pagePhase: any;
|
|
87
77
|
pageBindingResolutionBreakdown: any;
|
|
88
78
|
pageMergeBreakdown: any;
|