@reckona/mreact-compiler 0.0.144 → 0.0.145
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/diagnostics.d.ts +1 -0
- package/dist/diagnostics.d.ts.map +1 -1
- package/dist/diagnostics.js +7 -0
- package/dist/diagnostics.js.map +1 -1
- package/dist/emit-client.js +7 -3
- package/dist/emit-client.js.map +1 -1
- package/dist/emit-compat.js +137 -27
- package/dist/emit-compat.js.map +1 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +9 -0
- package/dist/transform.js.map +1 -1
- package/package.json +2 -2
- package/src/diagnostics.ts +8 -0
- package/src/emit-client.ts +10 -3
- package/src/emit-compat.ts +202 -20
- package/src/transform.ts +14 -0
package/src/emit-compat.ts
CHANGED
|
@@ -69,6 +69,10 @@ function collectComponentImportSpecifiers(ir: ModuleIr, dev: boolean): string[]
|
|
|
69
69
|
const specifiers = new Set<string>();
|
|
70
70
|
|
|
71
71
|
for (const component of ir.components) {
|
|
72
|
+
if (collectDirectTextBindings(component).length > 0) {
|
|
73
|
+
specifiers.add("REACTIVE_TEXT_BINDING_META");
|
|
74
|
+
}
|
|
75
|
+
|
|
72
76
|
visit(component.root, (node) => {
|
|
73
77
|
if (node.kind === "fragment") {
|
|
74
78
|
specifiers.add("Fragment");
|
|
@@ -89,11 +93,18 @@ function collectComponentImportSpecifiers(ir: ModuleIr, dev: boolean): string[]
|
|
|
89
93
|
|
|
90
94
|
interface CompatHelperNames {
|
|
91
95
|
Fragment?: string;
|
|
96
|
+
REACTIVE_TEXT_BINDING_META?: string;
|
|
92
97
|
jsx?: string;
|
|
93
98
|
jsxDEV?: string;
|
|
94
99
|
jsxs?: string;
|
|
95
100
|
}
|
|
96
101
|
|
|
102
|
+
interface DirectTextBinding {
|
|
103
|
+
stateName: string;
|
|
104
|
+
tupleName: string;
|
|
105
|
+
bindingName: string;
|
|
106
|
+
}
|
|
107
|
+
|
|
97
108
|
function allocateHelperNames(
|
|
98
109
|
ir: ModuleIr,
|
|
99
110
|
specifiers: readonly string[],
|
|
@@ -107,6 +118,11 @@ function allocateHelperNames(
|
|
|
107
118
|
continue;
|
|
108
119
|
}
|
|
109
120
|
|
|
121
|
+
if (specifier === "REACTIVE_TEXT_BINDING_META") {
|
|
122
|
+
helperNames.REACTIVE_TEXT_BINDING_META = allocator("_REACTIVE_TEXT_BINDING_META");
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
|
|
110
126
|
if (specifier === "jsx") {
|
|
111
127
|
helperNames.jsx = allocator("_jsx");
|
|
112
128
|
continue;
|
|
@@ -206,7 +222,7 @@ function parseCompatRuntimeImportLine(
|
|
|
206
222
|
return specifierText.split(",").flatMap((rawSpecifier): CompatRuntimeImportSpecifier[] => {
|
|
207
223
|
const specifier = rawSpecifier.trim();
|
|
208
224
|
const aliasMatch = specifier.match(
|
|
209
|
-
/^(?<importedName>Fragment|jsx|jsxDEV|jsxs)\s+as\s+(?<localName>[A-Za-z_$][\w$]*)$/,
|
|
225
|
+
/^(?<importedName>Fragment|REACTIVE_TEXT_BINDING_META|jsx|jsxDEV|jsxs)\s+as\s+(?<localName>[A-Za-z_$][\w$]*)$/,
|
|
210
226
|
);
|
|
211
227
|
|
|
212
228
|
if (aliasMatch?.groups !== undefined) {
|
|
@@ -223,7 +239,7 @@ function parseCompatRuntimeImportLine(
|
|
|
223
239
|
}];
|
|
224
240
|
}
|
|
225
241
|
|
|
226
|
-
return /^(Fragment|jsx|jsxDEV|jsxs)$/.test(specifier)
|
|
242
|
+
return /^(Fragment|REACTIVE_TEXT_BINDING_META|jsx|jsxDEV|jsxs)$/.test(specifier)
|
|
227
243
|
? [{ importedName: specifier, localName: specifier, source }]
|
|
228
244
|
: [];
|
|
229
245
|
});
|
|
@@ -253,6 +269,12 @@ function createImportGroups(
|
|
|
253
269
|
continue;
|
|
254
270
|
}
|
|
255
271
|
|
|
272
|
+
if (specifier === "REACTIVE_TEXT_BINDING_META") {
|
|
273
|
+
const localName = helperNames.REACTIVE_TEXT_BINDING_META ?? "_REACTIVE_TEXT_BINDING_META";
|
|
274
|
+
addImportSpecifier(groups, componentImportSource, "REACTIVE_TEXT_BINDING_META", localName);
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
|
|
256
278
|
const localName = helperNames[specifier as "jsx" | "jsxDEV" | "jsxs"] ?? `_${specifier}`;
|
|
257
279
|
addImportSpecifier(groups, componentImportSource, specifier, localName);
|
|
258
280
|
}
|
|
@@ -300,13 +322,19 @@ function emitComponent(
|
|
|
300
322
|
helperNames: CompatHelperNames,
|
|
301
323
|
dev: boolean,
|
|
302
324
|
): string {
|
|
303
|
-
const
|
|
325
|
+
const directTextBindings = collectDirectTextBindings(component);
|
|
326
|
+
const body = component.bodyStatements.map((statement) =>
|
|
327
|
+
` ${rewriteDirectTextBindingStatement(statement, directTextBindings, helperNames)}`
|
|
328
|
+
);
|
|
304
329
|
const parameters = component.parameters.join(", ");
|
|
330
|
+
const functionKeyword = `${component.exportDefault === true ? "export default " : component.exported === false ? "" : "export "}${
|
|
331
|
+
component.async === true ? "async " : ""
|
|
332
|
+
}function`;
|
|
305
333
|
|
|
306
334
|
return [
|
|
307
|
-
`${
|
|
335
|
+
`${functionKeyword} ${component.name}(${parameters}) {`,
|
|
308
336
|
...body,
|
|
309
|
-
` return ${emitJsxNode(component.root, helperNames, dev)};`,
|
|
337
|
+
` return ${emitJsxNode(component.root, helperNames, dev, directTextBindings)};`,
|
|
310
338
|
`}`,
|
|
311
339
|
].join("\n");
|
|
312
340
|
}
|
|
@@ -315,6 +343,7 @@ function emitJsxNode(
|
|
|
315
343
|
node: JsxNodeIr,
|
|
316
344
|
helperNames: CompatHelperNames,
|
|
317
345
|
dev: boolean,
|
|
346
|
+
directTextBindings: readonly DirectTextBinding[] = [],
|
|
318
347
|
): string {
|
|
319
348
|
if (node.kind === "text") {
|
|
320
349
|
return JSON.stringify(node.value);
|
|
@@ -325,8 +354,8 @@ function emitJsxNode(
|
|
|
325
354
|
}
|
|
326
355
|
|
|
327
356
|
if (node.kind === "conditional") {
|
|
328
|
-
const whenTrue = emitCompatChildren(node.whenTrue, helperNames, dev);
|
|
329
|
-
const whenFalse = emitCompatChildren(node.whenFalse, helperNames, dev);
|
|
357
|
+
const whenTrue = emitCompatChildren(node.whenTrue, helperNames, dev, directTextBindings);
|
|
358
|
+
const whenFalse = emitCompatChildren(node.whenFalse, helperNames, dev, directTextBindings);
|
|
330
359
|
|
|
331
360
|
return node.conditionValueName === undefined
|
|
332
361
|
? `(${node.conditionCode}) ? ${whenTrue} : ${whenFalse}`
|
|
@@ -335,7 +364,7 @@ function emitJsxNode(
|
|
|
335
364
|
|
|
336
365
|
if (node.kind === "list") {
|
|
337
366
|
const parameters = emitListParameters(node);
|
|
338
|
-
return `(${node.itemsCode}).map(${emitListRenderer(node, parameters, helperNames, dev)})`;
|
|
367
|
+
return `(${node.itemsCode}).map(${emitListRenderer(node, parameters, helperNames, dev, directTextBindings)})`;
|
|
339
368
|
}
|
|
340
369
|
|
|
341
370
|
if (node.kind === "fragment") {
|
|
@@ -345,7 +374,7 @@ function emitJsxNode(
|
|
|
345
374
|
if (node.kind === "component") {
|
|
346
375
|
const keyArgument =
|
|
347
376
|
node.keyCode === undefined ? undefined : `(${node.keyCode})`;
|
|
348
|
-
const props = emitComponentProps(node.props, node.children, helperNames, dev);
|
|
377
|
+
const props = emitComponentProps(node.props, node.children, helperNames, dev, directTextBindings);
|
|
349
378
|
return dev
|
|
350
379
|
? emitJsxDevCall(helperNames.jsxDEV ?? "_jsxDEV", node.name, props, keyArgument, node.children.length > 1)
|
|
351
380
|
: `${helperNames.jsx ?? "_jsx"}(${node.name}, ${props}${keyArgument === undefined ? "" : `, ${keyArgument}`})`;
|
|
@@ -355,23 +384,24 @@ function emitJsxNode(
|
|
|
355
384
|
return "null";
|
|
356
385
|
}
|
|
357
386
|
|
|
358
|
-
return emitJsxCall(JSON.stringify(node.tagName), node, helperNames, dev);
|
|
387
|
+
return emitJsxCall(JSON.stringify(node.tagName), node, helperNames, dev, directTextBindings);
|
|
359
388
|
}
|
|
360
389
|
|
|
361
390
|
function emitCompatChildren(
|
|
362
391
|
children: JsxNodeIr[],
|
|
363
392
|
helperNames: CompatHelperNames,
|
|
364
393
|
dev: boolean,
|
|
394
|
+
directTextBindings: readonly DirectTextBinding[] = [],
|
|
365
395
|
): string {
|
|
366
396
|
if (children.length === 0) {
|
|
367
397
|
return "null";
|
|
368
398
|
}
|
|
369
399
|
|
|
370
400
|
if (children.length === 1) {
|
|
371
|
-
return emitJsxNode(children[0] as JsxNodeIr, helperNames, dev);
|
|
401
|
+
return emitJsxNode(children[0] as JsxNodeIr, helperNames, dev, directTextBindings);
|
|
372
402
|
}
|
|
373
403
|
|
|
374
|
-
return `[${children.map((child) => emitJsxNode(child, helperNames, dev)).join(", ")}]`;
|
|
404
|
+
return `[${children.map((child) => emitJsxNode(child, helperNames, dev, directTextBindings)).join(", ")}]`;
|
|
375
405
|
}
|
|
376
406
|
|
|
377
407
|
function emitListRenderer(
|
|
@@ -379,8 +409,9 @@ function emitListRenderer(
|
|
|
379
409
|
parameters: string,
|
|
380
410
|
helperNames: CompatHelperNames,
|
|
381
411
|
dev: boolean,
|
|
412
|
+
directTextBindings: readonly DirectTextBinding[] = [],
|
|
382
413
|
): string {
|
|
383
|
-
const valueExpression = emitCompatChildren(node.children, helperNames, dev);
|
|
414
|
+
const valueExpression = emitCompatChildren(node.children, helperNames, dev, directTextBindings);
|
|
384
415
|
|
|
385
416
|
if (node.bodyStatements === undefined || node.bodyStatements.length === 0) {
|
|
386
417
|
return `(${parameters}) => ${valueExpression}`;
|
|
@@ -400,6 +431,7 @@ function emitJsxCall(
|
|
|
400
431
|
node: JsxElementIr | JsxFragmentIr,
|
|
401
432
|
helperNames: CompatHelperNames,
|
|
402
433
|
dev: boolean,
|
|
434
|
+
directTextBindings: readonly DirectTextBinding[] = [],
|
|
403
435
|
): string {
|
|
404
436
|
if (dev) {
|
|
405
437
|
const keyArgument =
|
|
@@ -409,7 +441,7 @@ function emitJsxCall(
|
|
|
409
441
|
return emitJsxDevCall(
|
|
410
442
|
helperNames.jsxDEV ?? "_jsxDEV",
|
|
411
443
|
typeExpression,
|
|
412
|
-
emitProps(node, helperNames, dev),
|
|
444
|
+
emitProps(node, helperNames, dev, directTextBindings),
|
|
413
445
|
keyArgument,
|
|
414
446
|
node.children.length > 1,
|
|
415
447
|
);
|
|
@@ -424,7 +456,7 @@ function emitJsxCall(
|
|
|
424
456
|
? `, (${node.keyCode})`
|
|
425
457
|
: "";
|
|
426
458
|
|
|
427
|
-
return `${callee}(${typeExpression}, ${emitProps(node, helperNames, dev)}${keyArgument})`;
|
|
459
|
+
return `${callee}(${typeExpression}, ${emitProps(node, helperNames, dev, directTextBindings)}${keyArgument})`;
|
|
428
460
|
}
|
|
429
461
|
|
|
430
462
|
function emitJsxDevCall(
|
|
@@ -441,15 +473,25 @@ function emitProps(
|
|
|
441
473
|
node: JsxElementIr | JsxFragmentIr,
|
|
442
474
|
helperNames: CompatHelperNames,
|
|
443
475
|
dev: boolean,
|
|
476
|
+
directTextBindings: readonly DirectTextBinding[] = [],
|
|
444
477
|
): string {
|
|
445
478
|
const entries =
|
|
446
479
|
node.kind === "element" ? node.attributes.map(emitAttribute) : [];
|
|
447
|
-
const children = emitChildren(node.children, helperNames, dev);
|
|
480
|
+
const children = emitChildren(node.children, helperNames, dev, directTextBindings);
|
|
481
|
+
const directTextBinding = node.kind === "element"
|
|
482
|
+
? findDirectTextBindingForChildren(node.children, directTextBindings)
|
|
483
|
+
: undefined;
|
|
448
484
|
|
|
449
485
|
if (children !== undefined) {
|
|
450
486
|
entries.push(`children: ${children}`);
|
|
451
487
|
}
|
|
452
488
|
|
|
489
|
+
if (directTextBinding !== undefined) {
|
|
490
|
+
entries.push(
|
|
491
|
+
`[${helperNames.REACTIVE_TEXT_BINDING_META ?? "_REACTIVE_TEXT_BINDING_META"}]: ${directTextBinding.bindingName}`,
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
453
495
|
return `{ ${entries.join(", ")} }`;
|
|
454
496
|
}
|
|
455
497
|
|
|
@@ -457,16 +499,17 @@ function emitChildren(
|
|
|
457
499
|
children: JsxNodeIr[],
|
|
458
500
|
helperNames: CompatHelperNames,
|
|
459
501
|
dev: boolean,
|
|
502
|
+
directTextBindings: readonly DirectTextBinding[] = [],
|
|
460
503
|
): string | undefined {
|
|
461
504
|
if (children.length === 0) {
|
|
462
505
|
return undefined;
|
|
463
506
|
}
|
|
464
507
|
|
|
465
508
|
if (children.length === 1) {
|
|
466
|
-
return emitJsxNode(children[0] as JsxNodeIr, helperNames, dev);
|
|
509
|
+
return emitJsxNode(children[0] as JsxNodeIr, helperNames, dev, directTextBindings);
|
|
467
510
|
}
|
|
468
511
|
|
|
469
|
-
return `[${children.map((child) => emitJsxNode(child, helperNames, dev)).join(", ")}]`;
|
|
512
|
+
return `[${children.map((child) => emitJsxNode(child, helperNames, dev, directTextBindings)).join(", ")}]`;
|
|
470
513
|
}
|
|
471
514
|
|
|
472
515
|
function emitAttribute(attr: AttributeIr): string {
|
|
@@ -490,6 +533,7 @@ function emitComponentProps(
|
|
|
490
533
|
children: JsxNodeIr[],
|
|
491
534
|
helperNames: CompatHelperNames,
|
|
492
535
|
dev: boolean,
|
|
536
|
+
directTextBindings: readonly DirectTextBinding[] = [],
|
|
493
537
|
): string {
|
|
494
538
|
const entries = props
|
|
495
539
|
.map((prop) => {
|
|
@@ -498,7 +542,8 @@ function emitComponentProps(
|
|
|
498
542
|
}
|
|
499
543
|
|
|
500
544
|
if (prop.kind === "render-prop") {
|
|
501
|
-
const renderedChildren =
|
|
545
|
+
const renderedChildren =
|
|
546
|
+
emitChildren(prop.children, helperNames, dev, directTextBindings) ?? "null";
|
|
502
547
|
return prop.valueName === undefined
|
|
503
548
|
? `${emitPropName(prop.name)}: ${renderedChildren}`
|
|
504
549
|
: `${emitPropName(prop.name)}: (${prop.valueName}) => ${renderedChildren}`;
|
|
@@ -509,12 +554,149 @@ function emitComponentProps(
|
|
|
509
554
|
.filter(Boolean);
|
|
510
555
|
|
|
511
556
|
if (children.length > 0) {
|
|
512
|
-
entries.push(`children: ${emitChildren(children, helperNames, dev) ?? "null"}`);
|
|
557
|
+
entries.push(`children: ${emitChildren(children, helperNames, dev, directTextBindings) ?? "null"}`);
|
|
513
558
|
}
|
|
514
559
|
|
|
515
560
|
return `{ ${entries.join(", ")} }`;
|
|
516
561
|
}
|
|
517
562
|
|
|
563
|
+
function collectDirectTextBindings(component: ComponentIr): DirectTextBinding[] {
|
|
564
|
+
const candidates: DirectTextBinding[] = [];
|
|
565
|
+
|
|
566
|
+
for (const statement of component.bodyStatements) {
|
|
567
|
+
const match = statement.match(
|
|
568
|
+
/^\s*const\s+\[\s*(?<stateName>[A-Za-z_$][\w$]*)\s*,\s*[A-Za-z_$][\w$]*\s*\]\s*=\s*useState\(.+\);\s*$/,
|
|
569
|
+
);
|
|
570
|
+
const stateName = match?.groups?.stateName;
|
|
571
|
+
|
|
572
|
+
if (stateName === undefined) {
|
|
573
|
+
continue;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
candidates.push({
|
|
577
|
+
stateName,
|
|
578
|
+
tupleName: `_${stateName}StateTuple`,
|
|
579
|
+
bindingName: `_${stateName}TextBinding`,
|
|
580
|
+
});
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
return candidates.filter((candidate) => directTextBindingIsSafe(component, candidate));
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function directTextBindingIsSafe(
|
|
587
|
+
component: ComponentIr,
|
|
588
|
+
candidate: DirectTextBinding,
|
|
589
|
+
): boolean {
|
|
590
|
+
let directTextUses = 0;
|
|
591
|
+
let unsafe = false;
|
|
592
|
+
|
|
593
|
+
visit(component.root, (node) => {
|
|
594
|
+
if (node.kind === "expr" && node.code === candidate.stateName) {
|
|
595
|
+
directTextUses += 1;
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (node.kind === "expr" && containsIdentifier(node.code, candidate.stateName)) {
|
|
600
|
+
unsafe = true;
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (node.kind === "element") {
|
|
605
|
+
for (const attr of node.attributes) {
|
|
606
|
+
if (attr.kind === "static-attr") {
|
|
607
|
+
continue;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
if (containsIdentifier(attr.code, candidate.stateName)) {
|
|
611
|
+
unsafe = true;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
for (const statement of component.bodyStatements) {
|
|
618
|
+
if (isDirectTextBindingDeclaration(statement, candidate.stateName)) {
|
|
619
|
+
continue;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (containsIdentifier(statement, candidate.stateName)) {
|
|
623
|
+
unsafe = true;
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
return directTextUses === 1 && !unsafe && hasDirectTextBindingHost(component.root, candidate);
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
function isDirectTextBindingDeclaration(statement: string, stateName: string): boolean {
|
|
631
|
+
return new RegExp(
|
|
632
|
+
`^\\s*const\\s+\\[\\s*${stateName}\\s*,\\s*[A-Za-z_$][\\w$]*\\s*\\]\\s*=\\s*useState\\(.+\\);\\s*$`,
|
|
633
|
+
).test(statement);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
function hasDirectTextBindingHost(
|
|
637
|
+
node: JsxNodeIr,
|
|
638
|
+
candidate: DirectTextBinding,
|
|
639
|
+
): boolean {
|
|
640
|
+
let found = false;
|
|
641
|
+
|
|
642
|
+
visit(node, (current) => {
|
|
643
|
+
if (
|
|
644
|
+
current.kind === "element" &&
|
|
645
|
+
findDirectTextBindingForChildren(current.children, [candidate]) !== undefined
|
|
646
|
+
) {
|
|
647
|
+
found = true;
|
|
648
|
+
}
|
|
649
|
+
});
|
|
650
|
+
|
|
651
|
+
return found;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
function rewriteDirectTextBindingStatement(
|
|
655
|
+
statement: string,
|
|
656
|
+
directTextBindings: readonly DirectTextBinding[],
|
|
657
|
+
helperNames: CompatHelperNames,
|
|
658
|
+
): string {
|
|
659
|
+
for (const binding of directTextBindings) {
|
|
660
|
+
const match = statement.match(
|
|
661
|
+
/^\s*const\s+\[\s*(?<stateName>[A-Za-z_$][\w$]*)\s*,\s*(?<setterName>[A-Za-z_$][\w$]*)\s*\]\s*=\s*(?<initializer>useState\(.+\));\s*$/,
|
|
662
|
+
);
|
|
663
|
+
|
|
664
|
+
if (match?.groups?.stateName !== binding.stateName) {
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
const metadataName = helperNames.REACTIVE_TEXT_BINDING_META ?? "_REACTIVE_TEXT_BINDING_META";
|
|
669
|
+
return [
|
|
670
|
+
`const ${binding.tupleName} = ${match.groups.initializer};`,
|
|
671
|
+
` const [${binding.stateName}, ${match.groups.setterName}] = ${binding.tupleName};`,
|
|
672
|
+
` const ${binding.bindingName} = ${binding.tupleName}[${metadataName}];`,
|
|
673
|
+
].join("\n");
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
return statement;
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
function findDirectTextBindingForChildren(
|
|
680
|
+
children: readonly JsxNodeIr[],
|
|
681
|
+
directTextBindings: readonly DirectTextBinding[],
|
|
682
|
+
): DirectTextBinding | undefined {
|
|
683
|
+
if (children.length !== 1) {
|
|
684
|
+
return undefined;
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
const child = children[0];
|
|
688
|
+
|
|
689
|
+
if (child?.kind !== "expr") {
|
|
690
|
+
return undefined;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
return directTextBindings.find((binding) => binding.stateName === child.code);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
function containsIdentifier(code: string, name: string): boolean {
|
|
697
|
+
return new RegExp(`(^|[^A-Za-z_$\\d])${name}([^A-Za-z_$\\d]|$)`).test(code);
|
|
698
|
+
}
|
|
699
|
+
|
|
518
700
|
function emitPropName(name: string): string {
|
|
519
701
|
return /^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name);
|
|
520
702
|
}
|
package/src/transform.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { emitCompat } from "./emit-compat.js";
|
|
|
3
3
|
import { emitServer } from "./emit-server.js";
|
|
4
4
|
import { emitServerStream } from "./emit-server-stream.js";
|
|
5
5
|
import { analyzeCompilerModuleContextWithOxc, analyzeWithOxc } from "./oxc.js";
|
|
6
|
+
import { unsupportedClientAsyncComponentDiagnostic } from "./diagnostics.js";
|
|
6
7
|
import type { ComponentIr, JsxNodeIr } from "./ir.js";
|
|
7
8
|
import type { AnalyzeToIrInput, AnalyzeToIrOutput, CompilerModuleContext } from "./internal.js";
|
|
8
9
|
import type { AnalyzeModuleOptions } from "./types.js";
|
|
@@ -74,6 +75,11 @@ function transformWithAnalyzer(
|
|
|
74
75
|
} as const;
|
|
75
76
|
const analyzed = analyze(analyzeTarget, analyzeOptions);
|
|
76
77
|
const diagnostics = [...analyzed.diagnostics];
|
|
78
|
+
|
|
79
|
+
if (input.target === "client") {
|
|
80
|
+
diagnostics.push(...collectClientAsyncComponentDiagnostics(analyzed.ir.components));
|
|
81
|
+
}
|
|
82
|
+
|
|
77
83
|
const emitted =
|
|
78
84
|
mode === "compat" && input.target === "client"
|
|
79
85
|
? emitCompat(analyzed.ir, { dev: input.dev })
|
|
@@ -169,6 +175,14 @@ function transformWithAnalyzer(
|
|
|
169
175
|
};
|
|
170
176
|
}
|
|
171
177
|
|
|
178
|
+
function collectClientAsyncComponentDiagnostics(
|
|
179
|
+
components: readonly ComponentIr[],
|
|
180
|
+
): TransformOutput["diagnostics"] {
|
|
181
|
+
return components
|
|
182
|
+
.filter((component) => component.async === true)
|
|
183
|
+
.map((component) => unsupportedClientAsyncComponentDiagnostic(component.name));
|
|
184
|
+
}
|
|
185
|
+
|
|
172
186
|
function createSourceMap(input: TransformInput, outputCode: string): string {
|
|
173
187
|
const generatedMap = createSegmentMappings(outputCode, input.code);
|
|
174
188
|
|