@fluffjs/cli 0.0.8 → 0.1.1
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/BabelHelpers.d.ts +26 -0
- package/BabelHelpers.js +65 -0
- package/Cli.d.ts +7 -10
- package/Cli.js +139 -52
- package/CodeGenerator.d.ts +53 -39
- package/CodeGenerator.js +330 -725
- package/ComponentCompiler.d.ts +14 -16
- package/ComponentCompiler.js +193 -257
- package/DomPreProcessor.d.ts +36 -0
- package/DomPreProcessor.js +645 -0
- package/ErrorHelpers.d.ts +5 -0
- package/ErrorHelpers.js +8 -0
- package/ExpressionTransformer.d.ts +38 -28
- package/ExpressionTransformer.js +558 -230
- package/Generator.d.ts +1 -5
- package/Generator.js +128 -67
- package/GetterDependencyExtractor.d.ts +4 -0
- package/GetterDependencyExtractor.js +73 -0
- package/IndexHtmlTransformer.d.ts +6 -7
- package/IndexHtmlTransformer.js +82 -88
- package/Parse5Helpers.d.ts +17 -0
- package/Parse5Helpers.js +95 -0
- package/TemplateParser.d.ts +39 -21
- package/TemplateParser.js +462 -268
- package/Typeguards.d.ts +24 -0
- package/Typeguards.js +30 -0
- package/babel-plugin-class-transform.d.ts +3 -18
- package/babel-plugin-class-transform.js +3 -11
- package/babel-plugin-component.d.ts +4 -13
- package/babel-plugin-component.js +7 -0
- package/babel-plugin-imports.d.ts +3 -11
- package/babel-plugin-imports.js +5 -31
- package/babel-plugin-reactive.d.ts +2 -19
- package/babel-plugin-reactive.js +21 -76
- package/bin.js +2 -2
- package/fluff-esbuild-plugin.d.ts +2 -5
- package/fluff-esbuild-plugin.js +4 -1
- package/index.d.ts +6 -2
- package/index.js +1 -1
- package/interfaces/BabelPluginClassTransformState.d.ts +5 -0
- package/interfaces/BabelPluginComponentState.d.ts +4 -0
- package/interfaces/BabelPluginComponentState.js +1 -0
- package/interfaces/BabelPluginImportsState.d.ts +5 -0
- package/interfaces/BabelPluginImportsState.js +1 -0
- package/interfaces/BabelPluginReactiveState.d.ts +13 -0
- package/interfaces/BabelPluginReactiveState.js +1 -0
- package/interfaces/BabelPluginReactiveWatchCallInfo.d.ts +7 -0
- package/interfaces/BabelPluginReactiveWatchCallInfo.js +1 -0
- package/interfaces/BabelPluginReactiveWatchInfo.d.ts +5 -0
- package/interfaces/BabelPluginReactiveWatchInfo.js +1 -0
- package/interfaces/BabelToken.d.ts +8 -0
- package/interfaces/BabelToken.js +1 -0
- package/interfaces/BindingInfo.d.ts +12 -0
- package/interfaces/BindingInfo.js +1 -0
- package/interfaces/BreakMarkerConfig.d.ts +4 -0
- package/interfaces/BreakMarkerConfig.js +1 -0
- package/interfaces/BreakNode.d.ts +4 -0
- package/interfaces/BreakNode.js +1 -0
- package/interfaces/BundleOptions.d.ts +9 -0
- package/interfaces/BundleOptions.js +1 -0
- package/interfaces/ClassTransformOptions.d.ts +10 -0
- package/interfaces/ClassTransformOptions.js +1 -0
- package/interfaces/CliOptions.d.ts +8 -0
- package/interfaces/CliOptions.js +1 -0
- package/interfaces/CommentNode.d.ts +5 -0
- package/interfaces/CommentNode.js +1 -0
- package/interfaces/CompileResult.d.ts +6 -0
- package/interfaces/CompileResult.js +1 -0
- package/interfaces/CompilerOptions.d.ts +6 -0
- package/interfaces/CompilerOptions.js +1 -0
- package/interfaces/ComponentInfo.d.ts +8 -0
- package/interfaces/ComponentInfo.js +1 -0
- package/interfaces/ComponentMetadata.d.ts +9 -0
- package/interfaces/ComponentMetadata.js +1 -0
- package/interfaces/ControlFlow.d.ts +19 -0
- package/interfaces/ControlFlow.js +1 -0
- package/interfaces/ControlFlowNode.d.ts +6 -0
- package/interfaces/ControlFlowNode.js +1 -0
- package/interfaces/ControlFlowParseResult.d.ts +10 -0
- package/interfaces/ControlFlowParseResult.js +1 -0
- package/interfaces/ElementNode.d.ts +11 -0
- package/interfaces/ElementNode.js +1 -0
- package/interfaces/FluffConfigInterface.d.ts +7 -0
- package/interfaces/FluffConfigInterface.js +1 -0
- package/interfaces/FluffPluginOptions.d.ts +9 -0
- package/interfaces/FluffPluginOptions.js +1 -0
- package/interfaces/FluffTarget.d.ts +15 -0
- package/interfaces/FluffTarget.js +1 -0
- package/interfaces/ForMarkerConfig.d.ts +9 -0
- package/interfaces/ForMarkerConfig.js +1 -0
- package/interfaces/ForNode.d.ts +13 -0
- package/interfaces/ForNode.js +1 -0
- package/interfaces/GeneratorOptions.d.ts +5 -0
- package/interfaces/GeneratorOptions.js +1 -0
- package/interfaces/HtmlTransformOptions.d.ts +10 -0
- package/interfaces/HtmlTransformOptions.js +1 -0
- package/interfaces/IfBranch.d.ts +8 -0
- package/interfaces/IfBranch.js +1 -0
- package/interfaces/IfMarkerConfig.d.ts +8 -0
- package/interfaces/IfMarkerConfig.js +1 -0
- package/interfaces/IfNode.d.ts +7 -0
- package/interfaces/IfNode.js +1 -0
- package/interfaces/ImportTransformOptions.d.ts +7 -0
- package/interfaces/ImportTransformOptions.js +1 -0
- package/interfaces/InterpolationNode.d.ts +12 -0
- package/interfaces/InterpolationNode.js +1 -0
- package/interfaces/ParsedTemplate.d.ts +6 -0
- package/interfaces/ParsedTemplate.js +1 -0
- package/interfaces/ParsedTemplateOld.d.ts +9 -0
- package/interfaces/ParsedTemplateOld.js +1 -0
- package/interfaces/PropertyChain.d.ts +2 -0
- package/interfaces/PropertyChain.js +1 -0
- package/interfaces/Scope.d.ts +5 -0
- package/interfaces/Scope.js +1 -0
- package/interfaces/ServeOptions.d.ts +5 -0
- package/interfaces/ServeOptions.js +1 -0
- package/interfaces/SwitchCase.d.ts +8 -0
- package/interfaces/SwitchCase.js +1 -0
- package/interfaces/SwitchMarkerConfig.d.ts +11 -0
- package/interfaces/SwitchMarkerConfig.js +1 -0
- package/interfaces/SwitchNode.d.ts +10 -0
- package/interfaces/SwitchNode.js +1 -0
- package/interfaces/TemplateBinding.d.ts +10 -0
- package/interfaces/TemplateBinding.js +1 -0
- package/interfaces/TemplateNode.d.ts +7 -0
- package/interfaces/TemplateNode.js +1 -0
- package/interfaces/TextMarkerConfig.d.ts +10 -0
- package/interfaces/TextMarkerConfig.js +1 -0
- package/interfaces/TextNode.d.ts +5 -0
- package/interfaces/TextNode.js +1 -0
- package/interfaces/TokenizeResult.d.ts +6 -0
- package/interfaces/TokenizeResult.js +1 -0
- package/interfaces/TransformOptions.d.ts +11 -0
- package/interfaces/TransformOptions.js +1 -0
- package/interfaces/index.d.ts +34 -0
- package/interfaces/index.js +1 -0
- package/package.json +9 -1
- package/types/FluffConfig.d.ts +5 -27
- package/ControlFlowParser.d.ts +0 -55
- package/ControlFlowParser.js +0 -279
- package/types.d.ts +0 -46
- /package/{types.js → interfaces/BabelPluginClassTransformState.js} +0 -0
package/ExpressionTransformer.js
CHANGED
|
@@ -1,276 +1,604 @@
|
|
|
1
|
-
import _generate from '@babel/generator';
|
|
2
1
|
import { parse } from '@babel/parser';
|
|
3
|
-
import _traverse from '@babel/traverse';
|
|
4
2
|
import * as t from '@babel/types';
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
path.replaceWith(replacementExpr);
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
if (shouldAddThisPrefix) {
|
|
38
|
-
path.replaceWith(t.memberExpression(t.thisExpression(), t.identifier(name)));
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
if (nullSafe) {
|
|
3
|
+
import { generate, traverse } from './BabelHelpers.js';
|
|
4
|
+
import { ErrorHelpers } from './ErrorHelpers.js';
|
|
5
|
+
export class ExpressionTransformer {
|
|
6
|
+
static RESERVED_KEYWORDS = [
|
|
7
|
+
'true', 'false', 'null', 'undefined', 'this',
|
|
8
|
+
'void', 'typeof', 'delete', 'new', 'instanceof', 'in',
|
|
9
|
+
'if', 'else', 'for', 'while', 'do', 'switch', 'case', 'break', 'continue', 'return', 'throw',
|
|
10
|
+
'try', 'catch', 'finally', 'function', 'class', 'extends', 'super',
|
|
11
|
+
'import', 'export', 'default', 'const', 'let', 'var', 'async', 'await', 'yield',
|
|
12
|
+
'debugger', 'with',
|
|
13
|
+
'$event'
|
|
14
|
+
];
|
|
15
|
+
static GLOBAL_OBJECTS = [
|
|
16
|
+
'Array', 'Object', 'String', 'Number', 'Boolean', 'Symbol', 'BigInt',
|
|
17
|
+
'Math', 'Date', 'JSON', 'RegExp', 'Error', 'Map', 'Set', 'WeakMap', 'WeakSet',
|
|
18
|
+
'Promise', 'Proxy', 'Reflect', 'Intl',
|
|
19
|
+
'console', 'window', 'document', 'navigator', 'location', 'history',
|
|
20
|
+
'localStorage', 'sessionStorage', 'fetch', 'XMLHttpRequest',
|
|
21
|
+
'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval', 'requestAnimationFrame',
|
|
22
|
+
'parseInt', 'parseFloat', 'isNaN', 'isFinite', 'encodeURI', 'decodeURI',
|
|
23
|
+
'encodeURIComponent', 'decodeURIComponent', 'atob', 'btoa',
|
|
24
|
+
'Infinity', 'NaN', 'globalThis'
|
|
25
|
+
];
|
|
26
|
+
static transformExpression(expr, options = {}) {
|
|
27
|
+
const { addThisPrefix: shouldAddThisPrefix = true, nullSafe = false, iteratorName, iteratorReplacement, localVars = [], localsObjectName, eventReplacementName, templateRefs = [] } = options;
|
|
28
|
+
try {
|
|
29
|
+
const ast = parse(`(${expr})`, {
|
|
30
|
+
sourceType: 'module', plugins: ['typescript']
|
|
31
|
+
});
|
|
43
32
|
traverse(ast, {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
33
|
+
Identifier(path) {
|
|
34
|
+
const { name } = path.node;
|
|
35
|
+
if (localsObjectName && name === localsObjectName) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (t.isMemberExpression(path.parent) && path.parent.property === path.node && !path.parent.computed) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (t.isOptionalMemberExpression(path.parent) && path.parent.property === path.node) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (t.isObjectProperty(path.parent) && path.parent.key === path.node && !path.parent.computed) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (name === '$event' && eventReplacementName) {
|
|
48
|
+
path.replaceWith(t.identifier(eventReplacementName));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
if (eventReplacementName && name === eventReplacementName) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (ExpressionTransformer.RESERVED_KEYWORDS.includes(name) || ExpressionTransformer.GLOBAL_OBJECTS.includes(name)) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (localVars.includes(name)) {
|
|
58
|
+
if (localsObjectName) {
|
|
59
|
+
path.replaceWith(t.memberExpression(t.identifier(localsObjectName), t.identifier(name)));
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (templateRefs.includes(name)) {
|
|
64
|
+
path.replaceWith(t.callExpression(t.memberExpression(t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('__getShadowRoot')), []), t.identifier('querySelector')), [t.stringLiteral(`[data-ref="${name}"]`)]));
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (t.isObjectProperty(path.parent) && path.parent.shorthand && path.parent.value === path.node) {
|
|
68
|
+
const grandParent = path.parentPath?.parentPath?.node;
|
|
69
|
+
if (grandParent && t.isObjectPattern(grandParent)) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
const collectFunctionParams = (startPath) => {
|
|
74
|
+
const params = new Set();
|
|
75
|
+
let cur = startPath.parentPath;
|
|
76
|
+
while (cur !== null) {
|
|
77
|
+
if (t.isArrowFunctionExpression(cur.node) || t.isFunctionExpression(cur.node) || t.isFunctionDeclaration(cur.node)) {
|
|
78
|
+
for (const param of cur.node.params) {
|
|
79
|
+
if (t.isIdentifier(param)) {
|
|
80
|
+
params.add(param.name);
|
|
81
|
+
}
|
|
82
|
+
else if (t.isAssignmentPattern(param) && t.isIdentifier(param.left)) {
|
|
83
|
+
params.add(param.left.name);
|
|
84
|
+
}
|
|
85
|
+
else if (t.isRestElement(param) && t.isIdentifier(param.argument)) {
|
|
86
|
+
params.add(param.argument.name);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const next = cur.parentPath;
|
|
91
|
+
if (next === null) {
|
|
92
|
+
break;
|
|
55
93
|
}
|
|
94
|
+
cur = next;
|
|
56
95
|
}
|
|
96
|
+
return params;
|
|
97
|
+
};
|
|
98
|
+
if (collectFunctionParams(path)
|
|
99
|
+
.has(name)) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (iteratorName && name === iteratorName && iteratorReplacement) {
|
|
103
|
+
const replacementAst = parse(`(${iteratorReplacement})`, { sourceType: 'module' });
|
|
104
|
+
const [firstStmt] = replacementAst.program.body;
|
|
105
|
+
if (!t.isExpressionStatement(firstStmt))
|
|
106
|
+
return;
|
|
107
|
+
const replacementExpr = firstStmt.expression;
|
|
108
|
+
path.replaceWith(replacementExpr);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
if (shouldAddThisPrefix) {
|
|
112
|
+
path.replaceWith(t.memberExpression(t.thisExpression(), t.identifier(name)));
|
|
57
113
|
}
|
|
58
114
|
}
|
|
59
115
|
});
|
|
116
|
+
if (nullSafe) {
|
|
117
|
+
traverse(ast, {
|
|
118
|
+
MemberExpression: {
|
|
119
|
+
exit(path) {
|
|
120
|
+
const obj = path.node.object;
|
|
121
|
+
const isThisMember = t.isMemberExpression(obj) && t.isThisExpression(obj.object);
|
|
122
|
+
const isOptionalChain = t.isOptionalMemberExpression(obj);
|
|
123
|
+
const isDeepChain = t.isMemberExpression(obj);
|
|
124
|
+
if (!path.node.optional && (isThisMember || isOptionalChain || isDeepChain)) {
|
|
125
|
+
const prop = path.node.property;
|
|
126
|
+
if (t.isExpression(prop)) {
|
|
127
|
+
const newNode = t.optionalMemberExpression(path.node.object, prop, path.node.computed, true);
|
|
128
|
+
path.replaceWith(newNode);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
const output = generate(ast, { compact: true });
|
|
136
|
+
let { code } = output;
|
|
137
|
+
code = code.replace(/;+$/, '');
|
|
138
|
+
const isWrappedParens = (s) => {
|
|
139
|
+
if (!s.startsWith('(') || !s.endsWith(')'))
|
|
140
|
+
return false;
|
|
141
|
+
let depth = 0;
|
|
142
|
+
for (let i = 0; i < s.length; i++) {
|
|
143
|
+
if (s[i] === '(')
|
|
144
|
+
depth++;
|
|
145
|
+
else if (s[i] === ')')
|
|
146
|
+
depth--;
|
|
147
|
+
if (depth === 0 && i < s.length - 1)
|
|
148
|
+
return false;
|
|
149
|
+
}
|
|
150
|
+
return depth === 0;
|
|
151
|
+
};
|
|
152
|
+
if (isWrappedParens(code)) {
|
|
153
|
+
code = code.slice(1, -1);
|
|
154
|
+
}
|
|
155
|
+
return code;
|
|
60
156
|
}
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
code = code.slice(1, -2);
|
|
65
|
-
}
|
|
66
|
-
else if (code.startsWith('(') && code.endsWith(')')) {
|
|
67
|
-
code = code.slice(1, -1);
|
|
157
|
+
catch (e) {
|
|
158
|
+
const message = ErrorHelpers.getErrorMessage(e);
|
|
159
|
+
throw new Error(`Failed to parse expression "${expr}": ${message}`);
|
|
68
160
|
}
|
|
69
|
-
code = code.replace(/;+$/, '');
|
|
70
|
-
return code;
|
|
71
161
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
throw new Error(`Failed to parse expression "${expr}": ${message}`);
|
|
162
|
+
static addThisPrefix(expr) {
|
|
163
|
+
return ExpressionTransformer.transformExpression(expr, { addThisPrefix: true, nullSafe: false });
|
|
75
164
|
}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
165
|
+
static addThisPrefixSafe(expr) {
|
|
166
|
+
return ExpressionTransformer.transformExpression(expr, { addThisPrefix: true, nullSafe: true });
|
|
167
|
+
}
|
|
168
|
+
static transformForExpression(expr, iteratorName, iteratorReplacement) {
|
|
169
|
+
return ExpressionTransformer.transformExpression(expr, {
|
|
170
|
+
addThisPrefix: true,
|
|
171
|
+
nullSafe: false,
|
|
172
|
+
iteratorName,
|
|
173
|
+
iteratorReplacement,
|
|
174
|
+
localVars: ['__items', '__idx', '__item']
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
static transformForExpressionKeepIterator(expr, iteratorName) {
|
|
178
|
+
return ExpressionTransformer.transformExpression(expr, {
|
|
179
|
+
addThisPrefix: true,
|
|
180
|
+
nullSafe: false,
|
|
181
|
+
localVars: [iteratorName, '__items', '__idx', '__item', '__currentItems']
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
static hasTokens(ast) {
|
|
185
|
+
return ast !== null && typeof ast === 'object' && 'tokens' in ast && Array.isArray(ast.tokens);
|
|
186
|
+
}
|
|
187
|
+
static hasLocIndex(e) {
|
|
188
|
+
if (e === null || typeof e !== 'object' || !('loc' in e))
|
|
189
|
+
return false;
|
|
190
|
+
const { loc } = e;
|
|
191
|
+
return loc !== null && typeof loc === 'object' && 'index' in loc && typeof loc.index === 'number';
|
|
192
|
+
}
|
|
193
|
+
static tokenizeExpression(code, startPos = 0) {
|
|
194
|
+
const substring = code.slice(startPos);
|
|
195
|
+
let tokens = [];
|
|
196
|
+
try {
|
|
197
|
+
const ast = parse(substring, {
|
|
198
|
+
sourceType: 'module',
|
|
199
|
+
tokens: true
|
|
200
|
+
});
|
|
201
|
+
if (ExpressionTransformer.hasTokens(ast)) {
|
|
202
|
+
({ tokens } = ast);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (e) {
|
|
206
|
+
if (ExpressionTransformer.hasLocIndex(e)) {
|
|
207
|
+
const partialCode = substring.substring(0, e.loc.index);
|
|
110
208
|
try {
|
|
111
|
-
parse(
|
|
112
|
-
|
|
113
|
-
|
|
209
|
+
const partialAst = parse(partialCode, {
|
|
210
|
+
sourceType: 'module',
|
|
211
|
+
tokens: true
|
|
212
|
+
});
|
|
213
|
+
if (ExpressionTransformer.hasTokens(partialAst)) {
|
|
214
|
+
({ tokens } = partialAst);
|
|
215
|
+
}
|
|
114
216
|
}
|
|
115
217
|
catch {
|
|
218
|
+
return { index: startPos, tokenCount: 0, stopReason: 'end' };
|
|
116
219
|
}
|
|
117
220
|
}
|
|
118
221
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
pipes.unshift({ name: pipeName, args });
|
|
128
|
-
remaining = remaining.slice(0, pipeIdx)
|
|
129
|
-
.trim();
|
|
130
|
-
}
|
|
131
|
-
return { expression: remaining, pipes };
|
|
132
|
-
}
|
|
133
|
-
export function transformInterpolation(expr, iteratorVar) {
|
|
134
|
-
const localVars = iteratorVar ? [iteratorVar] : [];
|
|
135
|
-
const { expression: baseExpr, pipes } = parsePipedExpression(expr);
|
|
136
|
-
if (pipes.length > 0) {
|
|
137
|
-
let result = transformExpression(baseExpr, { addThisPrefix: true, nullSafe: true, localVars });
|
|
138
|
-
for (const pipe of pipes) {
|
|
139
|
-
const argsStr = pipe.args.length > 0 ? ', ' + pipe.args.join(', ') : '';
|
|
140
|
-
result = `this.__pipe('${pipe.name}', ${result}${argsStr})`;
|
|
141
|
-
}
|
|
142
|
-
return `${result} ?? ''`;
|
|
143
|
-
}
|
|
144
|
-
const transformed = transformExpression(expr, { addThisPrefix: true, nullSafe: true, localVars });
|
|
145
|
-
return `${transformed} ?? ''`;
|
|
146
|
-
}
|
|
147
|
-
export function extractRootIdentifier(expr) {
|
|
148
|
-
try {
|
|
149
|
-
const ast = parse(expr, { sourceType: 'module' });
|
|
150
|
-
const [stmt] = ast.program.body;
|
|
151
|
-
if (!t.isExpressionStatement(stmt))
|
|
152
|
-
return null;
|
|
153
|
-
let node = stmt.expression;
|
|
154
|
-
while (true) {
|
|
155
|
-
if (t.isIdentifier(node)) {
|
|
156
|
-
return node.name;
|
|
222
|
+
let depth = 0;
|
|
223
|
+
let stopIndex = substring.length;
|
|
224
|
+
let stopReason = 'end';
|
|
225
|
+
let tokenCount = 0;
|
|
226
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
227
|
+
const token = tokens[i];
|
|
228
|
+
if (token.type.label === '(' || token.type.label === '[' || token.type.label === '{' || token.type.label === '${') {
|
|
229
|
+
depth++;
|
|
157
230
|
}
|
|
158
|
-
|
|
159
|
-
|
|
231
|
+
if (token.type.label === ')' || token.type.label === ']' || token.type.label === '}') {
|
|
232
|
+
depth--;
|
|
160
233
|
}
|
|
161
|
-
|
|
162
|
-
|
|
234
|
+
if (depth < 0) {
|
|
235
|
+
stopIndex = token.start;
|
|
236
|
+
stopReason = 'negative_depth';
|
|
237
|
+
tokenCount = i;
|
|
238
|
+
break;
|
|
163
239
|
}
|
|
164
|
-
|
|
165
|
-
|
|
240
|
+
if (depth === 0 && (token.type.label === '|' || token.type.label === ';' || token.type.label === ':')) {
|
|
241
|
+
stopIndex = token.start;
|
|
242
|
+
stopReason = 'delimiter';
|
|
243
|
+
tokenCount = i;
|
|
244
|
+
break;
|
|
166
245
|
}
|
|
167
|
-
|
|
168
|
-
|
|
246
|
+
tokenCount = i + 1;
|
|
247
|
+
}
|
|
248
|
+
if (stopReason === 'end' && tokens.length > 0) {
|
|
249
|
+
stopIndex = tokens[tokens.length - 1].end;
|
|
250
|
+
}
|
|
251
|
+
return { index: startPos + stopIndex, tokenCount, stopReason };
|
|
252
|
+
}
|
|
253
|
+
static parseSecondaryExpression(input, startPos = 0) {
|
|
254
|
+
const substring = input.slice(startPos);
|
|
255
|
+
try {
|
|
256
|
+
parse(substring, { sourceType: 'module' });
|
|
257
|
+
return {
|
|
258
|
+
expression: substring.trim(),
|
|
259
|
+
endPos: input.length
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
if (!(e instanceof Error) || !('pos' in e)) {
|
|
264
|
+
return null;
|
|
169
265
|
}
|
|
170
|
-
|
|
171
|
-
|
|
266
|
+
const errorPos = typeof e.pos === 'number' ? e.pos : null;
|
|
267
|
+
if (errorPos === null || errorPos === 0) {
|
|
268
|
+
return null;
|
|
172
269
|
}
|
|
173
|
-
|
|
270
|
+
const candidate = substring.slice(0, errorPos)
|
|
271
|
+
.trim();
|
|
272
|
+
if (candidate.length === 0) {
|
|
273
|
+
return null;
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
parse(candidate, { sourceType: 'module' });
|
|
277
|
+
return {
|
|
278
|
+
expression: candidate,
|
|
279
|
+
endPos: startPos + errorPos
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
174
283
|
return null;
|
|
175
284
|
}
|
|
176
285
|
}
|
|
177
286
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
287
|
+
static parsePipeIdentifier(str, startPos) {
|
|
288
|
+
let pos = startPos;
|
|
289
|
+
while (pos < str.length && /\s/.test(str[pos])) {
|
|
290
|
+
pos++;
|
|
291
|
+
}
|
|
292
|
+
if (pos >= str.length)
|
|
293
|
+
return null;
|
|
294
|
+
const firstChar = str[pos];
|
|
295
|
+
if (!/[a-zA-Z_$]/.test(firstChar))
|
|
296
|
+
return null;
|
|
297
|
+
let name = firstChar;
|
|
298
|
+
pos++;
|
|
299
|
+
while (pos < str.length && /[a-zA-Z0-9_$]/.test(str[pos])) {
|
|
300
|
+
name += str[pos];
|
|
301
|
+
pos++;
|
|
302
|
+
}
|
|
303
|
+
return { name, endPos: pos };
|
|
181
304
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
305
|
+
static parsePrimaryExpression(text, startPos) {
|
|
306
|
+
const offset = startPos ?? 0;
|
|
307
|
+
const pipes = [];
|
|
308
|
+
const tokenResult = ExpressionTransformer.tokenizeExpression(text, offset);
|
|
309
|
+
if (tokenResult.tokenCount === 0) {
|
|
310
|
+
return {
|
|
311
|
+
expression: text.slice(offset)
|
|
312
|
+
.trim(), pipes: [], endPos: text.length
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
const baseCandidate = text.slice(offset, tokenResult.index)
|
|
316
|
+
.trim();
|
|
317
|
+
const baseResult = ExpressionTransformer.parseSecondaryExpression(baseCandidate);
|
|
318
|
+
if (!baseResult) {
|
|
319
|
+
return { expression: baseCandidate, pipes: [], endPos: tokenResult.index };
|
|
320
|
+
}
|
|
321
|
+
const baseExpression = baseResult.expression;
|
|
322
|
+
if (tokenResult.stopReason !== 'delimiter') {
|
|
323
|
+
return { expression: baseExpression, pipes: [], endPos: tokenResult.index };
|
|
324
|
+
}
|
|
325
|
+
const delimChar = text[tokenResult.index];
|
|
326
|
+
if (delimChar !== '|') {
|
|
327
|
+
return { expression: baseExpression, pipes: [], endPos: tokenResult.index };
|
|
328
|
+
}
|
|
329
|
+
let pos = tokenResult.index + 1;
|
|
330
|
+
while (pos < text.length) {
|
|
331
|
+
const pipeId = ExpressionTransformer.parsePipeIdentifier(text, pos);
|
|
332
|
+
if (!pipeId || pipeId.name.length === 0)
|
|
206
333
|
break;
|
|
334
|
+
const pipeName = pipeId.name;
|
|
335
|
+
const args = [];
|
|
336
|
+
pos = pipeId.endPos;
|
|
337
|
+
while (pos < text.length && /\s/.test(text[pos])) {
|
|
338
|
+
pos++;
|
|
207
339
|
}
|
|
208
|
-
|
|
340
|
+
while (pos < text.length && text[pos] === ':') {
|
|
341
|
+
pos++;
|
|
342
|
+
while (pos < text.length && /\s/.test(text[pos])) {
|
|
343
|
+
pos++;
|
|
344
|
+
}
|
|
345
|
+
const prefix = `_arg${args.length + 1}=`;
|
|
346
|
+
const argText = prefix + text.slice(pos);
|
|
347
|
+
const argTokenResult = ExpressionTransformer.tokenizeExpression(argText, 0);
|
|
348
|
+
const argEndPos = argTokenResult.index - prefix.length;
|
|
349
|
+
if (argTokenResult.tokenCount > 0) {
|
|
350
|
+
const argCandidate = text.slice(pos, pos + argEndPos)
|
|
351
|
+
.trim();
|
|
352
|
+
const argParsed = ExpressionTransformer.parseSecondaryExpression(argCandidate);
|
|
353
|
+
if (argParsed) {
|
|
354
|
+
args.push(argParsed.expression);
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
args.push(argCandidate);
|
|
358
|
+
}
|
|
359
|
+
pos = pos + argEndPos;
|
|
360
|
+
if (argTokenResult.stopReason === 'delimiter' && argText[argTokenResult.index] === '|') {
|
|
361
|
+
break;
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
else {
|
|
365
|
+
break;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
pipes.push({ name: pipeName, args });
|
|
369
|
+
while (pos < text.length && /\s/.test(text[pos])) {
|
|
370
|
+
pos++;
|
|
371
|
+
}
|
|
372
|
+
if (pos < text.length && text[pos] === '|') {
|
|
373
|
+
pos++;
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
break;
|
|
209
377
|
}
|
|
210
378
|
}
|
|
211
|
-
|
|
212
|
-
searchStart = openIdx + 2;
|
|
213
|
-
}
|
|
379
|
+
return { expression: baseExpression, pipes, endPos: pos };
|
|
214
380
|
}
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
381
|
+
static transformInterpolation(expr, iteratorVar) {
|
|
382
|
+
const localVars = iteratorVar ? [iteratorVar] : [];
|
|
383
|
+
const { expression: baseExpr, pipes } = ExpressionTransformer.parsePrimaryExpression(expr);
|
|
384
|
+
if (pipes.length > 0) {
|
|
385
|
+
const resultStr = ExpressionTransformer.transformExpression(baseExpr, {
|
|
386
|
+
addThisPrefix: true,
|
|
387
|
+
nullSafe: true,
|
|
388
|
+
localVars
|
|
389
|
+
});
|
|
390
|
+
let resultExpr = ExpressionTransformer.parseExpressionToAst(resultStr);
|
|
391
|
+
for (const pipe of pipes) {
|
|
392
|
+
const transformedArgs = pipe.args.map(arg => ExpressionTransformer.parseExpressionToAst(ExpressionTransformer.transformExpression(arg, {
|
|
393
|
+
addThisPrefix: true,
|
|
394
|
+
nullSafe: true,
|
|
395
|
+
localVars
|
|
396
|
+
})));
|
|
397
|
+
resultExpr = t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('__pipe')), [t.stringLiteral(pipe.name), resultExpr, ...transformedArgs]);
|
|
398
|
+
}
|
|
399
|
+
const nullishExpr = t.logicalExpression('??', resultExpr, t.stringLiteral(''));
|
|
400
|
+
return generate(t.program([t.expressionStatement(nullishExpr)]), { compact: true })
|
|
401
|
+
.code
|
|
402
|
+
.replace(/;$/, '');
|
|
403
|
+
}
|
|
404
|
+
const transformed = ExpressionTransformer.transformExpression(expr, {
|
|
405
|
+
addThisPrefix: true,
|
|
406
|
+
nullSafe: true,
|
|
407
|
+
localVars
|
|
408
|
+
});
|
|
409
|
+
const transformedExpr = ExpressionTransformer.parseExpressionToAst(transformed);
|
|
410
|
+
const nullishExpr = t.logicalExpression('??', transformedExpr, t.stringLiteral(''));
|
|
411
|
+
return generate(t.program([t.expressionStatement(nullishExpr)]), { compact: true })
|
|
412
|
+
.code
|
|
413
|
+
.replace(/;$/, '');
|
|
221
414
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
415
|
+
static parseExpressionToAst(expr) {
|
|
416
|
+
const ast = parse(`(${expr})`, { sourceType: 'module' });
|
|
417
|
+
const [stmt] = ast.program.body;
|
|
418
|
+
if (t.isExpressionStatement(stmt)) {
|
|
419
|
+
return stmt.expression;
|
|
420
|
+
}
|
|
421
|
+
return t.identifier('undefined');
|
|
229
422
|
}
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
423
|
+
static extractRootIdentifier(expr) {
|
|
424
|
+
try {
|
|
425
|
+
const ast = parse(`(${expr})`, { sourceType: 'module' });
|
|
426
|
+
const [stmt] = ast.program.body;
|
|
427
|
+
if (!t.isExpressionStatement(stmt))
|
|
428
|
+
return null;
|
|
429
|
+
let node = stmt.expression;
|
|
430
|
+
while (true) {
|
|
431
|
+
if (t.isIdentifier(node)) {
|
|
432
|
+
return node.name;
|
|
433
|
+
}
|
|
434
|
+
else if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) {
|
|
435
|
+
node = node.object;
|
|
436
|
+
}
|
|
437
|
+
else if (t.isCallExpression(node) || t.isOptionalCallExpression(node)) {
|
|
438
|
+
node = node.callee;
|
|
439
|
+
}
|
|
440
|
+
else if (t.isBinaryExpression(node) || t.isLogicalExpression(node)) {
|
|
441
|
+
node = node.left;
|
|
442
|
+
}
|
|
443
|
+
else if (t.isUnaryExpression(node)) {
|
|
444
|
+
node = node.argument;
|
|
445
|
+
}
|
|
446
|
+
else if (t.isConditionalExpression(node)) {
|
|
447
|
+
node = node.test;
|
|
448
|
+
}
|
|
449
|
+
else if (t.isAwaitExpression(node)) {
|
|
450
|
+
node = node.argument;
|
|
451
|
+
}
|
|
452
|
+
else if (t.isAssignmentExpression(node)) {
|
|
453
|
+
node = node.left;
|
|
454
|
+
}
|
|
455
|
+
else if (t.isParenthesizedExpression(node)) {
|
|
456
|
+
node = node.expression;
|
|
457
|
+
}
|
|
458
|
+
else if (t.isObjectExpression(node)) {
|
|
459
|
+
let foundSpread = undefined;
|
|
460
|
+
for (const prop of node.properties) {
|
|
461
|
+
if (t.isSpreadElement(prop)) {
|
|
462
|
+
foundSpread = prop;
|
|
463
|
+
break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (foundSpread) {
|
|
467
|
+
node = foundSpread.argument;
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
return null;
|
|
240
471
|
}
|
|
241
|
-
|
|
472
|
+
}
|
|
473
|
+
else {
|
|
474
|
+
return null;
|
|
242
475
|
}
|
|
243
476
|
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
477
|
+
}
|
|
478
|
+
catch (e) {
|
|
479
|
+
const message = ErrorHelpers.getErrorMessage(e);
|
|
480
|
+
throw new Error(`Failed to parse expression "${expr}": ${message}`);
|
|
481
|
+
}
|
|
249
482
|
}
|
|
250
|
-
|
|
251
|
-
const
|
|
252
|
-
|
|
483
|
+
static parseInterpolations(text) {
|
|
484
|
+
const results = [];
|
|
485
|
+
let searchStart = 0;
|
|
486
|
+
while (searchStart < text.length) {
|
|
487
|
+
const openIdx = text.indexOf('{{', searchStart);
|
|
488
|
+
if (openIdx === -1)
|
|
489
|
+
break;
|
|
490
|
+
let foundValid = false;
|
|
491
|
+
for (let closeIdx = openIdx + 3; closeIdx <= text.length; closeIdx++) {
|
|
492
|
+
if (text[closeIdx - 2] !== '}' || text[closeIdx - 1] !== '}')
|
|
493
|
+
continue;
|
|
494
|
+
const candidate = text.slice(openIdx + 2, closeIdx - 2)
|
|
495
|
+
.trim();
|
|
496
|
+
if (!candidate)
|
|
497
|
+
continue;
|
|
498
|
+
const { expression: baseExpr } = ExpressionTransformer.parsePrimaryExpression(candidate);
|
|
499
|
+
try {
|
|
500
|
+
parse(`(${baseExpr})`, { sourceType: 'module' });
|
|
501
|
+
results.push({
|
|
502
|
+
start: openIdx, end: closeIdx, expr: candidate
|
|
503
|
+
});
|
|
504
|
+
searchStart = closeIdx;
|
|
505
|
+
foundValid = true;
|
|
506
|
+
break;
|
|
507
|
+
}
|
|
508
|
+
catch {
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
if (!foundValid) {
|
|
512
|
+
searchStart = openIdx + 2;
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
return results;
|
|
253
516
|
}
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
517
|
+
static transformPipedExpression(expr, addThisPrefixToExpr = true) {
|
|
518
|
+
const { expression, pipes } = ExpressionTransformer.parsePrimaryExpression(expr);
|
|
519
|
+
if (pipes.length === 0) {
|
|
520
|
+
return expression;
|
|
521
|
+
}
|
|
522
|
+
const resultStr = addThisPrefixToExpr ? ExpressionTransformer.transformExpression(expression, {
|
|
523
|
+
addThisPrefix: true,
|
|
524
|
+
nullSafe: true
|
|
525
|
+
}) : expression;
|
|
526
|
+
let resultExpr = ExpressionTransformer.parseExpressionToAst(resultStr);
|
|
527
|
+
for (const pipe of pipes) {
|
|
528
|
+
const transformedArgs = addThisPrefixToExpr
|
|
529
|
+
? pipe.args.map(arg => ExpressionTransformer.parseExpressionToAst(ExpressionTransformer.transformExpression(arg, {
|
|
530
|
+
addThisPrefix: true,
|
|
531
|
+
nullSafe: true
|
|
532
|
+
})))
|
|
533
|
+
: pipe.args.map(arg => ExpressionTransformer.parseExpressionToAst(arg));
|
|
534
|
+
resultExpr = t.callExpression(t.memberExpression(t.thisExpression(), t.identifier('__pipe')), [t.stringLiteral(pipe.name), resultExpr, ...transformedArgs]);
|
|
535
|
+
}
|
|
536
|
+
return generate(t.program([t.expressionStatement(resultExpr)]), { compact: true })
|
|
537
|
+
.code
|
|
538
|
+
.replace(/;$/, '');
|
|
539
|
+
}
|
|
540
|
+
static renameVariable(expr, oldName, newName) {
|
|
541
|
+
try {
|
|
542
|
+
const ast = parse(expr, { sourceType: 'module' });
|
|
543
|
+
traverse(ast, {
|
|
544
|
+
Identifier(path) {
|
|
545
|
+
if (path.node.name === oldName) {
|
|
546
|
+
if (t.isMemberExpression(path.parent) && path.parent.property === path.node && !path.parent.computed) {
|
|
547
|
+
return;
|
|
548
|
+
}
|
|
549
|
+
path.node.name = newName;
|
|
264
550
|
}
|
|
265
|
-
found = true;
|
|
266
|
-
path.stop();
|
|
267
551
|
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
|
|
552
|
+
});
|
|
553
|
+
let { code } = generate(ast, { compact: false });
|
|
554
|
+
if (code.endsWith(';'))
|
|
555
|
+
code = code.slice(0, -1);
|
|
556
|
+
return code;
|
|
557
|
+
}
|
|
558
|
+
catch (e) {
|
|
559
|
+
const message = ErrorHelpers.getErrorMessage(e);
|
|
560
|
+
throw new Error(`Failed to parse expression "${expr}": ${message}`);
|
|
561
|
+
}
|
|
271
562
|
}
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
563
|
+
static expressionUsesVariable(expr, varName) {
|
|
564
|
+
try {
|
|
565
|
+
const { expression: baseExpr } = ExpressionTransformer.parsePrimaryExpression(expr);
|
|
566
|
+
const ast = parse(`(${baseExpr})`, { sourceType: 'module' });
|
|
567
|
+
let found = false;
|
|
568
|
+
traverse(ast, {
|
|
569
|
+
Identifier(path) {
|
|
570
|
+
if (path.node.name === varName) {
|
|
571
|
+
if (t.isMemberExpression(path.parent) && path.parent.property === path.node && !path.parent.computed) {
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
found = true;
|
|
575
|
+
path.stop();
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
return found;
|
|
580
|
+
}
|
|
581
|
+
catch (e) {
|
|
582
|
+
const message = ErrorHelpers.getErrorMessage(e);
|
|
583
|
+
throw new Error(`Failed to parse expression "${expr}": ${message}`);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
static replaceThisExpression(expr, replacement) {
|
|
587
|
+
try {
|
|
588
|
+
const ast = parse(expr, { sourceType: 'module' });
|
|
589
|
+
traverse(ast, {
|
|
590
|
+
ThisExpression(path) {
|
|
591
|
+
path.replaceWith(t.identifier(replacement));
|
|
592
|
+
}
|
|
593
|
+
});
|
|
594
|
+
let { code } = generate(ast, { compact: false });
|
|
595
|
+
if (code.endsWith(';'))
|
|
596
|
+
code = code.slice(0, -1);
|
|
597
|
+
return code;
|
|
598
|
+
}
|
|
599
|
+
catch (e) {
|
|
600
|
+
const message = ErrorHelpers.getErrorMessage(e);
|
|
601
|
+
throw new Error(`Failed to parse expression "${expr}": ${message}`);
|
|
602
|
+
}
|
|
275
603
|
}
|
|
276
604
|
}
|