@tsrx/core 0.1.46 → 0.1.48
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/package.json +1 -1
- package/src/index.js +3 -2
- package/src/parse/index.js +3 -2
- package/src/plugin.js +110 -26
- package/src/transform/imports.js +96 -0
- package/src/transform/jsx/ast-builders.js +131 -110
- package/src/transform/jsx/helpers.js +77 -81
- package/src/transform/jsx/index.js +342 -360
- package/src/transform/lazy.js +5 -5
- package/src/transform/segments.js +29 -28
- package/src/transform/style-ref.js +9 -13
- package/src/utils/ast.js +11 -0
- package/src/utils/builders.js +3 -3
- package/types/index.d.ts +95 -12
- package/types/jsx-platform.d.ts +88 -54
- package/types/parse.d.ts +64 -4
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -89,6 +89,7 @@ export {
|
|
|
89
89
|
is_function_node as isFunctionNode,
|
|
90
90
|
is_function_or_class_node as isFunctionOrClassNode,
|
|
91
91
|
is_function_or_component_node as isFunctionOrComponentNode,
|
|
92
|
+
has_location,
|
|
92
93
|
is_inside_component as isInsideComponent,
|
|
93
94
|
is_template_directive as isTemplateDirective,
|
|
94
95
|
is_tsrx_render_output_node as isTsrxRenderOutputNode,
|
|
@@ -148,6 +149,7 @@ export { normalize_css_property_name as normalizeCssPropertyName } from './utils
|
|
|
148
149
|
export { escape, escape_script as escapeScript } from './utils/escaping.js';
|
|
149
150
|
|
|
150
151
|
// Transform
|
|
152
|
+
export { with_deferred_imports as withDeferredImports } from './transform/imports.js';
|
|
151
153
|
export {
|
|
152
154
|
add_jsx_setup_declaration as addJsxSetupDeclaration,
|
|
153
155
|
clone_switch_helper_invocation as cloneSwitchHelperInvocation,
|
|
@@ -167,7 +169,6 @@ export {
|
|
|
167
169
|
plan_switch_lift as planSwitchLift,
|
|
168
170
|
return_value_body_to_expression as returnValueBodyToExpression,
|
|
169
171
|
rewrite_loop_continues_to_bare_returns as rewriteLoopContinuesToBareReturns,
|
|
170
|
-
to_jsx_attribute as toJsxAttribute,
|
|
171
172
|
validate_at_most_one_ref_attribute as validateAtMostOneRefAttribute,
|
|
172
173
|
wrap_edge_whitespace as wrapEdgeWhitespace,
|
|
173
174
|
} from './transform/jsx/index.js';
|
|
@@ -189,7 +190,7 @@ export {
|
|
|
189
190
|
} from './transform/style-ref.js';
|
|
190
191
|
export {
|
|
191
192
|
add_extra_source_mappings_from_matching_expression,
|
|
192
|
-
|
|
193
|
+
clone_ast_node,
|
|
193
194
|
clone_identifier,
|
|
194
195
|
clone_jsx_name,
|
|
195
196
|
contains_component_jsx,
|
package/src/parse/index.js
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
import * as acorn from 'acorn';
|
|
9
9
|
import { tsPlugin } from '@sveltejs/acorn-typescript';
|
|
10
10
|
import { walk } from 'zimmerframe';
|
|
11
|
+
import { has_location } from '../utils/ast.js';
|
|
11
12
|
|
|
12
13
|
/**
|
|
13
14
|
* @typedef {(BaseParser: typeof acorn.Parser) => typeof acorn.Parser} AcornPlugin
|
|
@@ -607,8 +608,8 @@ export function get_comment_handlers(source, comments, index = 0) {
|
|
|
607
608
|
}
|
|
608
609
|
|
|
609
610
|
const ancestorElements = path
|
|
610
|
-
.filter(
|
|
611
|
-
.
|
|
611
|
+
.filter(has_location)
|
|
612
|
+
.filter(isNativeTemplateNode)
|
|
612
613
|
.sort((a, b) => a.loc.start.line - b.loc.start.line);
|
|
613
614
|
|
|
614
615
|
const targetAncestor = ancestorElements.find(
|
package/src/plugin.js
CHANGED
|
@@ -4688,6 +4688,67 @@ export function TSRXPlugin(config) {
|
|
|
4688
4688
|
this.parseTemplateBody(body);
|
|
4689
4689
|
}
|
|
4690
4690
|
|
|
4691
|
+
/**
|
|
4692
|
+
* Parse the argument list of a deferred dynamic import,
|
|
4693
|
+
* `import.defer(specifier, options?)`, starting at the opening paren.
|
|
4694
|
+
*
|
|
4695
|
+
* This mirrors Acorn's ES2025 `import(...)` grammar (optional `options`
|
|
4696
|
+
* argument, optional trailing comma), which neither inherited parser
|
|
4697
|
+
* produces here: acorn-typescript's `parseDynamicImport` emits legacy
|
|
4698
|
+
* `arguments`, and Acorn's own only enables the `options` shape at
|
|
4699
|
+
* `ecmaVersion >= 16` while TSRX parses at 13.
|
|
4700
|
+
*
|
|
4701
|
+
* @param {AST.ImportExpression} node
|
|
4702
|
+
* @returns {AST.ImportExpression}
|
|
4703
|
+
*/
|
|
4704
|
+
parseDeferredDynamicImport(node) {
|
|
4705
|
+
this.next(); // `(`
|
|
4706
|
+
node.source = this.parseMaybeAssign();
|
|
4707
|
+
node.options = null;
|
|
4708
|
+
|
|
4709
|
+
if (!this.eat(tt.parenR)) {
|
|
4710
|
+
this.expect(tt.comma);
|
|
4711
|
+
if (!this.afterTrailingComma(tt.parenR)) {
|
|
4712
|
+
node.options = this.parseMaybeAssign();
|
|
4713
|
+
if (!this.eat(tt.parenR)) {
|
|
4714
|
+
this.expect(tt.comma);
|
|
4715
|
+
if (!this.afterTrailingComma(tt.parenR)) this.unexpected();
|
|
4716
|
+
}
|
|
4717
|
+
}
|
|
4718
|
+
}
|
|
4719
|
+
|
|
4720
|
+
return this.finishNode(node, 'ImportExpression');
|
|
4721
|
+
}
|
|
4722
|
+
|
|
4723
|
+
/**
|
|
4724
|
+
* Recognize the deferred dynamic-import form
|
|
4725
|
+
* `import.defer(specifier, options?)` before Acorn parses `import.<name>`
|
|
4726
|
+
* as an `import.meta` member access. Ordinary `import()` and `import.meta`
|
|
4727
|
+
* fall through to Acorn unchanged, so the proposal never alters their
|
|
4728
|
+
* existing AST shape.
|
|
4729
|
+
* @type {Parse.Parser['parseExprImport']}
|
|
4730
|
+
*/
|
|
4731
|
+
parseExprImport(forNew) {
|
|
4732
|
+
if (
|
|
4733
|
+
!forNew &&
|
|
4734
|
+
this.lookahead().type === tt.dot &&
|
|
4735
|
+
this.isContextualWithState('defer', this.lookahead(2))
|
|
4736
|
+
) {
|
|
4737
|
+
const node = /** @type {AST.ImportExpression} */ (this.startNode());
|
|
4738
|
+
if (this.containsEsc) {
|
|
4739
|
+
this.raiseRecoverable(this.start, 'Escape sequence in keyword import');
|
|
4740
|
+
}
|
|
4741
|
+
this.next(); // `import`
|
|
4742
|
+
this.next(); // `.`
|
|
4743
|
+
this.next(); // `defer`
|
|
4744
|
+
node.phase = 'defer';
|
|
4745
|
+
if (this.type !== tt.parenL) this.unexpected();
|
|
4746
|
+
return this.parseDeferredDynamicImport(node);
|
|
4747
|
+
}
|
|
4748
|
+
|
|
4749
|
+
return super.parseExprImport(forNew);
|
|
4750
|
+
}
|
|
4751
|
+
|
|
4691
4752
|
/**
|
|
4692
4753
|
* Parse proposal-style imports from an inline module declaration:
|
|
4693
4754
|
* `import { foo } from server;`
|
|
@@ -4698,54 +4759,77 @@ export function TSRXPlugin(config) {
|
|
|
4698
4759
|
* @type {Parse.Parser['parseImport']}
|
|
4699
4760
|
*/
|
|
4700
4761
|
parseImport(node) {
|
|
4701
|
-
const tokenIsIdentifier =
|
|
4702
|
-
|
|
4703
|
-
|
|
4704
|
-
let
|
|
4705
|
-
|
|
4706
|
-
|
|
4762
|
+
const tokenIsIdentifier = Parser.acornTypeScript.tokenIsIdentifier;
|
|
4763
|
+
let enterHead = this.lookahead();
|
|
4764
|
+
let deferred = false;
|
|
4765
|
+
let defer_start = -1;
|
|
4766
|
+
node.importKind = 'value';
|
|
4767
|
+
this.importOrExportOuterKind = 'value';
|
|
4707
4768
|
if (tokenIsIdentifier(enterHead.type) || this.match(tt.star) || this.match(tt.braceL)) {
|
|
4708
|
-
let ahead =
|
|
4709
|
-
|
|
4769
|
+
let ahead = this.lookahead(2);
|
|
4770
|
+
// `defer` and `type` are only phase/kind modifiers when the following
|
|
4771
|
+
// token cannot continue a default import (`, `/`from`) or an
|
|
4772
|
+
// import-equals declaration (`=`); otherwise they are ordinary bindings.
|
|
4773
|
+
const head_modifies =
|
|
4710
4774
|
ahead.type !== tt.comma &&
|
|
4711
|
-
!
|
|
4712
|
-
ahead.type !== tt.eq
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
4717
|
-
|
|
4718
|
-
|
|
4775
|
+
!this.isContextualWithState('from', ahead) &&
|
|
4776
|
+
ahead.type !== tt.eq;
|
|
4777
|
+
// The namespace-only restriction is checked after parsing the clause,
|
|
4778
|
+
// which also gives invalid named/default deferred imports a focused
|
|
4779
|
+
// diagnostic.
|
|
4780
|
+
if (head_modifies && this.isContextualWithState('defer', enterHead)) {
|
|
4781
|
+
deferred = true;
|
|
4782
|
+
defer_start = enterHead.start;
|
|
4783
|
+
node.phase = 'defer';
|
|
4784
|
+
this.ts_eatContextualWithState('defer', 1, enterHead);
|
|
4785
|
+
enterHead = this.lookahead();
|
|
4786
|
+
ahead = this.lookahead(2);
|
|
4787
|
+
} else if (head_modifies && this.ts_eatContextualWithState('type', 1, enterHead)) {
|
|
4788
|
+
this.importOrExportOuterKind = 'type';
|
|
4789
|
+
node.importKind = 'type';
|
|
4790
|
+
enterHead = this.lookahead();
|
|
4791
|
+
ahead = this.lookahead(2);
|
|
4719
4792
|
}
|
|
4720
4793
|
if (tokenIsIdentifier(enterHead.type) && ahead.type === tt.eq) {
|
|
4721
4794
|
this.next();
|
|
4722
|
-
const importNode =
|
|
4723
|
-
|
|
4795
|
+
const importNode = this.tsParseImportEqualsDeclaration(node);
|
|
4796
|
+
this.importOrExportOuterKind = 'value';
|
|
4724
4797
|
return importNode;
|
|
4725
4798
|
}
|
|
4726
4799
|
}
|
|
4727
4800
|
this.next();
|
|
4728
4801
|
if (this.type === tt.string) {
|
|
4729
|
-
|
|
4730
|
-
|
|
4802
|
+
node.specifiers = [];
|
|
4803
|
+
node.source = /** @type {AST.Literal} */ (this.parseExprAtom());
|
|
4731
4804
|
} else {
|
|
4732
|
-
|
|
4805
|
+
node.specifiers = this.parseImportSpecifiers();
|
|
4733
4806
|
this.expectContextual('from');
|
|
4734
4807
|
if (this.type === tt.string) {
|
|
4735
|
-
|
|
4808
|
+
node.source = /** @type {AST.Literal} */ (this.parseExprAtom());
|
|
4736
4809
|
} else if (tokenIsIdentifier(this.type)) {
|
|
4737
4810
|
const source = this.parseIdent(false);
|
|
4738
4811
|
source.metadata ??= { path: [] };
|
|
4739
|
-
|
|
4812
|
+
node.source = source;
|
|
4740
4813
|
} else {
|
|
4741
4814
|
this.unexpected();
|
|
4742
4815
|
}
|
|
4743
4816
|
}
|
|
4744
|
-
|
|
4817
|
+
if (
|
|
4818
|
+
deferred &&
|
|
4819
|
+
(node.specifiers.length !== 1 ||
|
|
4820
|
+
node.specifiers[0].type !== 'ImportNamespaceSpecifier' ||
|
|
4821
|
+
node.source.type !== 'Literal')
|
|
4822
|
+
) {
|
|
4823
|
+
this.raise(
|
|
4824
|
+
defer_start,
|
|
4825
|
+
'`import defer` only supports a namespace import from a string literal.',
|
|
4826
|
+
);
|
|
4827
|
+
}
|
|
4828
|
+
this.parseMaybeImportAttributes(node);
|
|
4745
4829
|
this.semicolon();
|
|
4746
4830
|
this.finishNode(node, 'ImportDeclaration');
|
|
4747
|
-
|
|
4748
|
-
return
|
|
4831
|
+
this.importOrExportOuterKind = 'value';
|
|
4832
|
+
return node;
|
|
4749
4833
|
}
|
|
4750
4834
|
|
|
4751
4835
|
/**
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/** @import * as AST from 'estree' */
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Add TSRX import-phase support to an esrap TS/TSX visitor set. esrap 2.3
|
|
5
|
+
* understands the rest of ImportDeclaration but does not print its Stage 3
|
|
6
|
+
* `phase` field yet, so delegating would silently turn a deferred import into
|
|
7
|
+
* an eager one.
|
|
8
|
+
*
|
|
9
|
+
* @template {Record<string, any>} T
|
|
10
|
+
* @param {T} visitors
|
|
11
|
+
* @returns {T}
|
|
12
|
+
*/
|
|
13
|
+
export function with_deferred_imports(visitors) {
|
|
14
|
+
const print_import_declaration = visitors.ImportDeclaration;
|
|
15
|
+
const print_import_expression = visitors.ImportExpression;
|
|
16
|
+
if (
|
|
17
|
+
typeof print_import_declaration !== 'function' ||
|
|
18
|
+
typeof print_import_expression !== 'function'
|
|
19
|
+
) {
|
|
20
|
+
throw new TypeError('Deferred imports require a complete esrap TS or TSX visitor set.');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return /** @type {T} */ ({
|
|
24
|
+
...visitors,
|
|
25
|
+
/**
|
|
26
|
+
* @param {AST.ImportDeclaration} node
|
|
27
|
+
* @param {import('esrap').Context} context
|
|
28
|
+
*/
|
|
29
|
+
ImportDeclaration(node, context) {
|
|
30
|
+
const import_node = /** @type {AST.ImportDeclaration & { phase?: 'defer' | null }} */ (node);
|
|
31
|
+
if (import_node.phase !== 'defer') {
|
|
32
|
+
print_import_declaration(node, context);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const [specifier] = node.specifiers;
|
|
37
|
+
if (node.specifiers.length !== 1 || specifier.type !== 'ImportNamespaceSpecifier') {
|
|
38
|
+
throw new Error('`import defer` only supports a namespace import.');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (node.loc) context.location(node.loc.start.line, node.loc.start.column);
|
|
42
|
+
context.write('import defer ');
|
|
43
|
+
if (specifier.loc) {
|
|
44
|
+
context.location(specifier.loc.start.line, specifier.loc.start.column);
|
|
45
|
+
}
|
|
46
|
+
context.write('* as ');
|
|
47
|
+
context.visit(specifier.local);
|
|
48
|
+
context.write(' from ');
|
|
49
|
+
context.visit(node.source);
|
|
50
|
+
|
|
51
|
+
const attributes =
|
|
52
|
+
/** @type {Array<{ key: AST.Identifier | AST.Literal, value: AST.Literal }>} */ (
|
|
53
|
+
/** @type {any} */ (node).attributes ?? /** @type {any} */ (node).assertions ?? []
|
|
54
|
+
);
|
|
55
|
+
if (attributes.length > 0) {
|
|
56
|
+
context.write(' with { ');
|
|
57
|
+
for (let index = 0; index < attributes.length; index++) {
|
|
58
|
+
context.visit(attributes[index].key);
|
|
59
|
+
context.write(': ');
|
|
60
|
+
context.visit(attributes[index].value);
|
|
61
|
+
if (index + 1 !== attributes.length) context.write(', ');
|
|
62
|
+
}
|
|
63
|
+
context.write(' }');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
context.write(';');
|
|
67
|
+
if (node.loc) context.location(node.loc.end.line, node.loc.end.column);
|
|
68
|
+
},
|
|
69
|
+
/**
|
|
70
|
+
* @param {AST.ImportExpression} node
|
|
71
|
+
* @param {import('esrap').Context} context
|
|
72
|
+
*/
|
|
73
|
+
ImportExpression(node, context) {
|
|
74
|
+
const import_node = /** @type {AST.ImportExpression & { phase?: 'defer' | null }} */ (node);
|
|
75
|
+
if (import_node.phase !== 'defer') {
|
|
76
|
+
print_import_expression(node, context);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (node.loc) context.location(node.loc.start.line, node.loc.start.column);
|
|
81
|
+
context.write('import.defer(');
|
|
82
|
+
context.visit(node.source);
|
|
83
|
+
|
|
84
|
+
const options =
|
|
85
|
+
node.options ??
|
|
86
|
+
/** @type {AST.Expression | undefined} */ (/** @type {any} */ (node).arguments?.[0]);
|
|
87
|
+
if (options) {
|
|
88
|
+
context.write(', ');
|
|
89
|
+
context.visit(options);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
context.write(')');
|
|
93
|
+
if (node.loc) context.location(node.loc.end.line, node.loc.end.column);
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|