octane 0.1.13 → 0.1.16
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/compiler/bundler.js +14 -9
- package/dist/compiler/client-only-server.js +195 -54
- package/dist/compiler/compile-renderer-boundaries.js +448 -536
- package/dist/compiler/compile-universal.js +1659 -1765
- package/dist/compiler/compile.js +5873 -3090
- package/dist/compiler/fat-segments.js +196 -0
- package/dist/compiler/hook-deps.js +115 -24
- package/dist/compiler/hydrate-boundaries.js +456 -631
- package/dist/compiler/native-change-diagnostics.js +2 -1
- package/dist/compiler/renderer-boundaries.js +2 -2
- package/dist/compiler/slot-hooks.js +11 -7
- package/dist/compiler/vite.d.ts +6 -2
- package/dist/compiler/vite.js +27 -2
- package/dist/compiler/volar.js +237 -18
- package/dist/devtools-hook.d.ts +64 -0
- package/dist/devtools-hook.js +162 -0
- package/dist/head-ownership.d.ts +12 -0
- package/dist/head-ownership.js +31 -0
- package/dist/jsx-runtime.d.ts +570 -0
- package/dist/runtime.d.ts +12 -1
- package/dist/runtime.js +597 -161
- package/dist/runtime.server.js +23 -11
- package/dist/server/rpc.js +16 -1
- package/dist/universal-core.d.ts +13 -3
- package/dist/universal-core.js +1117 -100
- package/dist/universal-dom-boundary.js +49 -6
- package/package.json +17 -3
package/dist/compiler/bundler.js
CHANGED
|
@@ -193,10 +193,14 @@ function normalizeHmrDialect(value) {
|
|
|
193
193
|
*/
|
|
194
194
|
export function findVoidComponentExports(source, id) {
|
|
195
195
|
let ast;
|
|
196
|
-
|
|
197
|
-
ast =
|
|
198
|
-
}
|
|
199
|
-
|
|
196
|
+
if (source && typeof source === 'object' && source.type === 'Program') {
|
|
197
|
+
ast = source;
|
|
198
|
+
} else {
|
|
199
|
+
try {
|
|
200
|
+
ast = parseModule(source, id);
|
|
201
|
+
} catch {
|
|
202
|
+
return [];
|
|
203
|
+
}
|
|
200
204
|
}
|
|
201
205
|
const memoLocals = new Set();
|
|
202
206
|
const declarations = [];
|
|
@@ -867,8 +871,9 @@ class OctaneBundlerCompiler {
|
|
|
867
871
|
if (environment === 'server' && clientReference !== null) {
|
|
868
872
|
const stub = createClientOnlyServerStub(code, filename, renderer.id);
|
|
869
873
|
return {
|
|
874
|
+
ast: stub.ast,
|
|
870
875
|
code: stub.code,
|
|
871
|
-
map:
|
|
876
|
+
map: stub.map,
|
|
872
877
|
kind: 'client-only-stub',
|
|
873
878
|
renderer,
|
|
874
879
|
clientReference,
|
|
@@ -906,11 +911,11 @@ class OctaneBundlerCompiler {
|
|
|
906
911
|
const collectVoidComponentExports =
|
|
907
912
|
environment === 'client' && options.collectVoidComponentExports === true;
|
|
908
913
|
let out;
|
|
909
|
-
let
|
|
914
|
+
let voidComponentAst = null;
|
|
910
915
|
if (collectVoidComponentExports) {
|
|
911
916
|
const compilation = compileForBundler(code, compileFilename, compileOptions);
|
|
912
917
|
out = compilation.result;
|
|
913
|
-
|
|
918
|
+
voidComponentAst = compilation.hydrateAst;
|
|
914
919
|
} else {
|
|
915
920
|
out = compile(code, compileFilename, compileOptions);
|
|
916
921
|
}
|
|
@@ -923,10 +928,10 @@ class OctaneBundlerCompiler {
|
|
|
923
928
|
renderer,
|
|
924
929
|
...(out.universalRuntime === undefined ? null : { universalRuntime: out.universalRuntime }),
|
|
925
930
|
...(clientReference === null ? null : { clientReference }),
|
|
926
|
-
...(
|
|
931
|
+
...(voidComponentAst === null
|
|
927
932
|
? null
|
|
928
933
|
: {
|
|
929
|
-
voidComponentExports: findVoidComponentExports(
|
|
934
|
+
voidComponentExports: findVoidComponentExports(voidComponentAst, filename),
|
|
930
935
|
}),
|
|
931
936
|
...finishMetadata(collected),
|
|
932
937
|
};
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
* regions have been omitted, so any remaining reference receives a source
|
|
9
9
|
* diagnostic before a sentinel can affect server behavior.
|
|
10
10
|
*/
|
|
11
|
-
import { parseModule } from '@tsrx/core';
|
|
11
|
+
import { builders as b, parseModule } from '@tsrx/core';
|
|
12
|
+
import { print as esrapPrint } from 'esrap';
|
|
13
|
+
import esrapTsx from 'esrap/languages/tsx';
|
|
12
14
|
|
|
13
15
|
const CLIENT_REFERENCE_VERSION = 1;
|
|
14
16
|
export const CLIENT_REFERENCE_MANIFEST_FILENAME = 'octane-client-references.json';
|
|
@@ -26,10 +28,6 @@ function astName(node) {
|
|
|
26
28
|
return typeof node?.value === 'string' ? node.value : null;
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
function printExportName(name) {
|
|
30
|
-
return /^[$A-Z_a-z][$\w]*$/u.test(name) || name === 'default' ? name : JSON.stringify(name);
|
|
31
|
-
}
|
|
32
|
-
|
|
33
31
|
function addPatternNames(pattern, output) {
|
|
34
32
|
if (!pattern) return;
|
|
35
33
|
if (pattern.type === 'TSParameterProperty') {
|
|
@@ -182,7 +180,10 @@ export function findStaticRuntimeImportRequests(source, filename = 'unknown') {
|
|
|
182
180
|
}
|
|
183
181
|
|
|
184
182
|
function collectRuntimeExports(ast, filename) {
|
|
185
|
-
const exports = new
|
|
183
|
+
const exports = new Map();
|
|
184
|
+
const addExport = (name, origin) => {
|
|
185
|
+
if (!exports.has(name)) exports.set(name, origin);
|
|
186
|
+
};
|
|
186
187
|
for (const statement of ast.body ?? []) {
|
|
187
188
|
if (statement.type === 'ExportDefaultDeclaration') {
|
|
188
189
|
if (
|
|
@@ -190,7 +191,7 @@ function collectRuntimeExports(ast, filename) {
|
|
|
190
191
|
statement.declaration?.declare !== true &&
|
|
191
192
|
statement.declaration?.type !== 'TSInterfaceDeclaration'
|
|
192
193
|
) {
|
|
193
|
-
|
|
194
|
+
addExport('default', statement);
|
|
194
195
|
}
|
|
195
196
|
continue;
|
|
196
197
|
}
|
|
@@ -208,14 +209,14 @@ function collectRuntimeExports(ast, filename) {
|
|
|
208
209
|
statement.importKind !== 'type' &&
|
|
209
210
|
statement.id?.name
|
|
210
211
|
) {
|
|
211
|
-
|
|
212
|
+
addExport(statement.id.name, statement.id);
|
|
212
213
|
continue;
|
|
213
214
|
}
|
|
214
215
|
if (statement.type === 'ExportAllDeclaration') {
|
|
215
216
|
if (statement.exportKind === 'type') continue;
|
|
216
217
|
const exported = astName(statement.exported);
|
|
217
218
|
if (exported !== null) {
|
|
218
|
-
|
|
219
|
+
addExport(exported, statement.exported ?? statement);
|
|
219
220
|
continue;
|
|
220
221
|
}
|
|
221
222
|
throw diagnosticError(
|
|
@@ -230,7 +231,11 @@ function collectRuntimeExports(ast, filename) {
|
|
|
230
231
|
const declaration = statement.declaration;
|
|
231
232
|
if (declaration?.declare === true) continue;
|
|
232
233
|
if (declaration?.type === 'VariableDeclaration') {
|
|
233
|
-
for (const item of declaration.declarations ?? [])
|
|
234
|
+
for (const item of declaration.declarations ?? []) {
|
|
235
|
+
const names = new Set();
|
|
236
|
+
addPatternNames(item.id, names);
|
|
237
|
+
for (const name of names) addExport(name, item.id);
|
|
238
|
+
}
|
|
234
239
|
} else if (
|
|
235
240
|
declaration?.id &&
|
|
236
241
|
(declaration.type === 'FunctionDeclaration' ||
|
|
@@ -238,15 +243,92 @@ function collectRuntimeExports(ast, filename) {
|
|
|
238
243
|
declaration.type === 'TSEnumDeclaration' ||
|
|
239
244
|
declaration.type === 'TSModuleDeclaration')
|
|
240
245
|
) {
|
|
241
|
-
|
|
246
|
+
addExport(declaration.id.name, declaration.id);
|
|
242
247
|
}
|
|
243
248
|
for (const specifier of statement.specifiers ?? []) {
|
|
244
249
|
if (specifier.exportKind === 'type') continue;
|
|
245
250
|
const exported = astName(specifier.exported);
|
|
246
|
-
if (exported !== null)
|
|
251
|
+
if (exported !== null) addExport(exported, specifier.exported ?? specifier);
|
|
247
252
|
}
|
|
248
253
|
}
|
|
249
|
-
return [...exports]
|
|
254
|
+
return [...exports]
|
|
255
|
+
.map(([name, origin]) => ({ name, origin }))
|
|
256
|
+
.sort((left, right) => (left.name < right.name ? -1 : left.name > right.name ? 1 : 0));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Generated stub scaffolding has no authored syntax of its own, but every
|
|
261
|
+
* printed node still inherits a source origin so the auxiliary module carries
|
|
262
|
+
* a useful esrap map. The tree is compiler-owned; parsed nodes are never
|
|
263
|
+
* embedded or mutated.
|
|
264
|
+
*/
|
|
265
|
+
function inheritGeneratedOrigin(root, origin) {
|
|
266
|
+
const seen = new WeakSet();
|
|
267
|
+
const visit = (value) => {
|
|
268
|
+
if (!value || typeof value !== 'object' || seen.has(value)) return;
|
|
269
|
+
seen.add(value);
|
|
270
|
+
if (Array.isArray(value)) {
|
|
271
|
+
for (const item of value) visit(item);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
if (typeof value.type === 'string' && value.loc == null && origin?.loc != null) {
|
|
275
|
+
value.start = origin.start;
|
|
276
|
+
value.end = origin.end;
|
|
277
|
+
value.loc = origin.loc;
|
|
278
|
+
}
|
|
279
|
+
for (const [key, child] of Object.entries(value)) {
|
|
280
|
+
if (key !== 'loc' && key !== 'metadata') visit(child);
|
|
281
|
+
}
|
|
282
|
+
};
|
|
283
|
+
visit(root);
|
|
284
|
+
return root;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
function createProgram(body, origin) {
|
|
288
|
+
return inheritGeneratedOrigin(
|
|
289
|
+
{
|
|
290
|
+
type: 'Program',
|
|
291
|
+
sourceType: 'module',
|
|
292
|
+
body,
|
|
293
|
+
metadata: { path: [] },
|
|
294
|
+
},
|
|
295
|
+
origin,
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const jsonStringifyCall = (expression) => b.call(b.member(b.id('JSON'), 'stringify'), expression);
|
|
300
|
+
|
|
301
|
+
function clientOnlyStubVisitors() {
|
|
302
|
+
const visitors = esrapTsx();
|
|
303
|
+
const printExportSpecifier = visitors.ExportSpecifier;
|
|
304
|
+
return {
|
|
305
|
+
...visitors,
|
|
306
|
+
ExportSpecifier(node, context) {
|
|
307
|
+
// ESTree represents quoted export names as Literals. esrap 2.3's TS
|
|
308
|
+
// visitor currently prints only Identifier→Identifier aliases; retain
|
|
309
|
+
// the standard AST and teach this one print the missing literal arm.
|
|
310
|
+
if (node.exported.type === 'Literal') {
|
|
311
|
+
context.visit(node.local);
|
|
312
|
+
context.write(' as ');
|
|
313
|
+
context.visit(node.exported);
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
316
|
+
printExportSpecifier(node, context);
|
|
317
|
+
},
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function printClientOnlyServerStub(program, source, filename, exports) {
|
|
322
|
+
const printed = esrapPrint(program, clientOnlyStubVisitors(), {
|
|
323
|
+
sourceMapSource: filename,
|
|
324
|
+
sourceMapContent: source,
|
|
325
|
+
});
|
|
326
|
+
return {
|
|
327
|
+
ast: program,
|
|
328
|
+
code: printed.code,
|
|
329
|
+
map: printed.map,
|
|
330
|
+
exports,
|
|
331
|
+
};
|
|
250
332
|
}
|
|
251
333
|
|
|
252
334
|
/**
|
|
@@ -255,48 +337,102 @@ function collectRuntimeExports(ast, filename) {
|
|
|
255
337
|
*/
|
|
256
338
|
export function createClientOnlyServerStub(source, filename, renderer) {
|
|
257
339
|
const ast = parseModule(source, filename);
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
'function __octaneClientOnlyExport(name) {',
|
|
263
|
-
'\tconst fail = () => {',
|
|
264
|
-
'\t\tconst error = new Error(`Client-only export ${JSON.stringify(name)} from ${JSON.stringify(__octaneClientOnlyModule)} (renderer ${JSON.stringify(__octaneClientOnlyRenderer)}) was used by the server graph.`);',
|
|
265
|
-
"\t\terror.code = 'OCTANE_CLIENT_ONLY_SERVER_USE';",
|
|
266
|
-
'\t\terror.filename = __octaneClientOnlyModule;',
|
|
267
|
-
'\t\tthrow error;',
|
|
268
|
-
'\t};',
|
|
269
|
-
'\treturn new Proxy(fail, {',
|
|
270
|
-
'\t\tapply: fail,',
|
|
271
|
-
'\t\tconstruct: fail,',
|
|
272
|
-
'\t\tdefineProperty: fail,',
|
|
273
|
-
'\t\tdeleteProperty: fail,',
|
|
274
|
-
'\t\tget: fail,',
|
|
275
|
-
'\t\tgetOwnPropertyDescriptor: fail,',
|
|
276
|
-
'\t\tgetPrototypeOf: fail,',
|
|
277
|
-
'\t\thas: fail,',
|
|
278
|
-
'\t\tisExtensible: fail,',
|
|
279
|
-
'\t\townKeys: fail,',
|
|
280
|
-
'\t\tpreventExtensions: fail,',
|
|
281
|
-
'\t\tset: fail,',
|
|
282
|
-
'\t\tsetPrototypeOf: fail,',
|
|
283
|
-
'\t});',
|
|
284
|
-
'}',
|
|
285
|
-
];
|
|
286
|
-
for (let index = 0; index < exports.length; index++) {
|
|
287
|
-
lines.push(
|
|
288
|
-
`const __octaneClientOnlyExport${index} = __octaneClientOnlyExport(${JSON.stringify(exports[index])});`,
|
|
289
|
-
);
|
|
340
|
+
const exportEntries = collectRuntimeExports(ast, filename);
|
|
341
|
+
const exports = exportEntries.map((entry) => entry.name);
|
|
342
|
+
if (exportEntries.length === 0) {
|
|
343
|
+
return printClientOnlyServerStub(createProgram([], ast), source, filename, exports);
|
|
290
344
|
}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
345
|
+
const message = b.template(
|
|
346
|
+
[
|
|
347
|
+
b.quasi('Client-only export '),
|
|
348
|
+
b.quasi(' from '),
|
|
349
|
+
b.quasi(' (renderer '),
|
|
350
|
+
b.quasi(') was used by the server graph.', true),
|
|
351
|
+
],
|
|
352
|
+
[
|
|
353
|
+
jsonStringifyCall(b.id('name')),
|
|
354
|
+
jsonStringifyCall(b.id('__octaneClientOnlyModule')),
|
|
355
|
+
jsonStringifyCall(b.id('__octaneClientOnlyRenderer')),
|
|
356
|
+
],
|
|
357
|
+
);
|
|
358
|
+
const fail = b.arrow(
|
|
359
|
+
[],
|
|
360
|
+
b.block([
|
|
361
|
+
b.const('error', b.new('Error', undefined, message)),
|
|
362
|
+
b.stmt(
|
|
363
|
+
b.assignment(
|
|
364
|
+
'=',
|
|
365
|
+
b.member(b.id('error'), 'code'),
|
|
366
|
+
b.literal('OCTANE_CLIENT_ONLY_SERVER_USE'),
|
|
367
|
+
),
|
|
368
|
+
),
|
|
369
|
+
b.stmt(
|
|
370
|
+
b.assignment('=', b.member(b.id('error'), 'filename'), b.id('__octaneClientOnlyModule')),
|
|
371
|
+
),
|
|
372
|
+
{ type: 'ThrowStatement', argument: b.id('error'), metadata: { path: [] } },
|
|
373
|
+
]),
|
|
374
|
+
);
|
|
375
|
+
const traps = [
|
|
376
|
+
'apply',
|
|
377
|
+
'construct',
|
|
378
|
+
'defineProperty',
|
|
379
|
+
'deleteProperty',
|
|
380
|
+
'get',
|
|
381
|
+
'getOwnPropertyDescriptor',
|
|
382
|
+
'getPrototypeOf',
|
|
383
|
+
'has',
|
|
384
|
+
'isExtensible',
|
|
385
|
+
'ownKeys',
|
|
386
|
+
'preventExtensions',
|
|
387
|
+
'set',
|
|
388
|
+
'setPrototypeOf',
|
|
389
|
+
];
|
|
390
|
+
const body = [
|
|
391
|
+
b.const('__octaneClientOnlyModule', b.literal(filename)),
|
|
392
|
+
b.const('__octaneClientOnlyRenderer', b.literal(renderer)),
|
|
393
|
+
b.function_declaration(
|
|
394
|
+
b.id('__octaneClientOnlyExport'),
|
|
395
|
+
[b.id('name')],
|
|
396
|
+
b.block([
|
|
397
|
+
b.const('fail', fail),
|
|
398
|
+
b.return(
|
|
399
|
+
b.new(
|
|
400
|
+
'Proxy',
|
|
401
|
+
undefined,
|
|
402
|
+
b.id('fail'),
|
|
403
|
+
b.object(traps.map((name) => b.init(name, b.id('fail')))),
|
|
404
|
+
),
|
|
405
|
+
),
|
|
406
|
+
]),
|
|
407
|
+
),
|
|
408
|
+
];
|
|
409
|
+
for (let index = 0; index < exportEntries.length; index++) {
|
|
410
|
+
body.push(
|
|
411
|
+
inheritGeneratedOrigin(
|
|
412
|
+
b.const(
|
|
413
|
+
`__octaneClientOnlyExport${index}`,
|
|
414
|
+
b.call('__octaneClientOnlyExport', b.literal(exportEntries[index].name)),
|
|
415
|
+
),
|
|
416
|
+
exportEntries[index].origin,
|
|
417
|
+
),
|
|
297
418
|
);
|
|
298
419
|
}
|
|
299
|
-
|
|
420
|
+
const exportSpecifiers = exportEntries.map((entry, index) =>
|
|
421
|
+
inheritGeneratedOrigin(
|
|
422
|
+
b.export_specifier(
|
|
423
|
+
`__octaneClientOnlyExport${index}`,
|
|
424
|
+
/^[$A-Z_a-z][$\w]*$/u.test(entry.name) ? b.id(entry.name) : b.literal(entry.name),
|
|
425
|
+
),
|
|
426
|
+
entry.origin,
|
|
427
|
+
),
|
|
428
|
+
);
|
|
429
|
+
body.push(b.export(null, exportSpecifiers));
|
|
430
|
+
// A Program can end at column zero when the source has a trailing newline.
|
|
431
|
+
// Structural nodes need an authored syntax range whose end is a real token:
|
|
432
|
+
// esrap anchors a generated closing delimiter immediately before `loc.end`.
|
|
433
|
+
const scaffoldOrigin = exportEntries[0].origin;
|
|
434
|
+
const program = createProgram(body, scaffoldOrigin);
|
|
435
|
+
return printClientOnlyServerStub(program, source, filename, exports);
|
|
300
436
|
}
|
|
301
437
|
|
|
302
438
|
function bindingNames(pattern) {
|
|
@@ -371,10 +507,15 @@ function importedName(specifier) {
|
|
|
371
507
|
* rewrite. Side-effect imports and unused bindings are safe because their
|
|
372
508
|
* target compiles to a no-op stub.
|
|
373
509
|
*/
|
|
374
|
-
export function assertNoLiveClientOnlyImports(
|
|
510
|
+
export function assertNoLiveClientOnlyImports(
|
|
511
|
+
source,
|
|
512
|
+
filename,
|
|
513
|
+
clientOnlyImports = [],
|
|
514
|
+
parsedAst = null,
|
|
515
|
+
) {
|
|
375
516
|
if (!Array.isArray(clientOnlyImports) || clientOnlyImports.length === 0) return;
|
|
376
517
|
const byRequest = new Map(clientOnlyImports.map((entry) => [entry.request, entry]));
|
|
377
|
-
const ast = parseModule(source, filename);
|
|
518
|
+
const ast = parsedAst ?? parseModule(source, filename);
|
|
378
519
|
const imported = new Map();
|
|
379
520
|
|
|
380
521
|
for (const statement of ast.body ?? []) {
|