ripple 0.2.67 → 0.2.69
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/compiler/phases/1-parse/index.js +131 -0
- package/src/compiler/phases/2-analyze/index.js +34 -12
- package/src/compiler/phases/3-transform/index.js +133 -82
- package/src/compiler/scope.js +403 -394
- package/src/compiler/utils.js +28 -0
- package/src/constants.js +4 -0
- package/src/runtime/array.js +33 -24
- package/src/runtime/internal/client/for.js +69 -31
- package/src/runtime/internal/client/render.js +9 -3
- package/src/runtime/internal/client/template.js +51 -5
- package/src/utils/ast.js +2 -2
- package/tests/__snapshots__/basic.test.ripple.snap +18 -0
- package/tests/__snapshots__/for.test.ripple.snap +78 -0
- package/tests/basic.test.ripple +15 -0
- package/tests/for.test.ripple +32 -2
- package/tests/svg.test.ripple +282 -0
package/package.json
CHANGED
|
@@ -192,6 +192,135 @@ function RipplePlugin(config) {
|
|
|
192
192
|
return super.parseExportDefaultDeclaration();
|
|
193
193
|
}
|
|
194
194
|
|
|
195
|
+
parseForStatement(node) {
|
|
196
|
+
this.next()
|
|
197
|
+
let awaitAt = (this.options.ecmaVersion >= 9 && this.canAwait && this.eatContextual("await")) ? this.lastTokStart : -1
|
|
198
|
+
this.labels.push({kind: "loop"})
|
|
199
|
+
this.enterScope(0)
|
|
200
|
+
this.expect(tt.parenL)
|
|
201
|
+
|
|
202
|
+
if (this.type === tt.semi) {
|
|
203
|
+
if (awaitAt > -1) this.unexpected(awaitAt)
|
|
204
|
+
return this.parseFor(node, null)
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
let isLet = this.isLet()
|
|
208
|
+
if (this.type === tt._var || this.type === tt._const || isLet) {
|
|
209
|
+
let init = this.startNode(), kind = isLet ? "let" : this.value
|
|
210
|
+
this.next()
|
|
211
|
+
this.parseVar(init, true, kind)
|
|
212
|
+
this.finishNode(init, "VariableDeclaration")
|
|
213
|
+
return this.parseForAfterInitWithIndex(node, init, awaitAt)
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Handle other cases like using declarations if they exist
|
|
217
|
+
let startsWithLet = this.isContextual("let"), isForOf = false
|
|
218
|
+
let usingKind = (this.isUsing && this.isUsing(true)) ? "using" : (this.isAwaitUsing && this.isAwaitUsing(true)) ? "await using" : null
|
|
219
|
+
if (usingKind) {
|
|
220
|
+
let init = this.startNode()
|
|
221
|
+
this.next()
|
|
222
|
+
if (usingKind === "await using") {
|
|
223
|
+
if (!this.canAwait) {
|
|
224
|
+
this.raise(this.start, "Await using cannot appear outside of async function")
|
|
225
|
+
}
|
|
226
|
+
this.next()
|
|
227
|
+
}
|
|
228
|
+
this.parseVar(init, true, usingKind)
|
|
229
|
+
this.finishNode(init, "VariableDeclaration")
|
|
230
|
+
return this.parseForAfterInitWithIndex(node, init, awaitAt)
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
let containsEsc = this.containsEsc
|
|
234
|
+
let refDestructuringErrors = {}
|
|
235
|
+
let initPos = this.start
|
|
236
|
+
let init = awaitAt > -1
|
|
237
|
+
? this.parseExprSubscripts(refDestructuringErrors, "await")
|
|
238
|
+
: this.parseExpression(true, refDestructuringErrors)
|
|
239
|
+
|
|
240
|
+
if (this.type === tt._in || (isForOf = this.options.ecmaVersion >= 6 && this.isContextual("of"))) {
|
|
241
|
+
if (awaitAt > -1) { // implies `ecmaVersion >= 9`
|
|
242
|
+
if (this.type === tt._in) this.unexpected(awaitAt)
|
|
243
|
+
node.await = true
|
|
244
|
+
} else if (isForOf && this.options.ecmaVersion >= 8) {
|
|
245
|
+
if (init.start === initPos && !containsEsc && init.type === "Identifier" && init.name === "async") this.unexpected()
|
|
246
|
+
else if (this.options.ecmaVersion >= 9) node.await = false
|
|
247
|
+
}
|
|
248
|
+
if (startsWithLet && isForOf) this.raise(init.start, "The left-hand side of a for-of loop may not start with 'let'.")
|
|
249
|
+
this.toAssignable(init, false, refDestructuringErrors)
|
|
250
|
+
this.checkLValPattern(init)
|
|
251
|
+
return this.parseForInWithIndex(node, init)
|
|
252
|
+
} else {
|
|
253
|
+
this.checkExpressionErrors(refDestructuringErrors, true)
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (awaitAt > -1) this.unexpected(awaitAt)
|
|
257
|
+
return this.parseFor(node, init)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
parseForAfterInitWithIndex(node, init, awaitAt) {
|
|
261
|
+
if ((this.type === tt._in || (this.options.ecmaVersion >= 6 && this.isContextual("of"))) && init.declarations.length === 1) {
|
|
262
|
+
if (this.options.ecmaVersion >= 9) {
|
|
263
|
+
if (this.type === tt._in) {
|
|
264
|
+
if (awaitAt > -1) this.unexpected(awaitAt)
|
|
265
|
+
} else node.await = awaitAt > -1
|
|
266
|
+
}
|
|
267
|
+
return this.parseForInWithIndex(node, init)
|
|
268
|
+
}
|
|
269
|
+
if (awaitAt > -1) this.unexpected(awaitAt)
|
|
270
|
+
return this.parseFor(node, init)
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
parseForInWithIndex(node, init) {
|
|
274
|
+
const isForIn = this.type === tt._in
|
|
275
|
+
this.next()
|
|
276
|
+
|
|
277
|
+
if (
|
|
278
|
+
init.type === "VariableDeclaration" &&
|
|
279
|
+
init.declarations[0].init != null &&
|
|
280
|
+
(
|
|
281
|
+
!isForIn ||
|
|
282
|
+
this.options.ecmaVersion < 8 ||
|
|
283
|
+
this.strict ||
|
|
284
|
+
init.kind !== "var" ||
|
|
285
|
+
init.declarations[0].id.type !== "Identifier"
|
|
286
|
+
)
|
|
287
|
+
) {
|
|
288
|
+
this.raise(
|
|
289
|
+
init.start,
|
|
290
|
+
`${isForIn ? "for-in" : "for-of"} loop variable declaration may not have an initializer`
|
|
291
|
+
)
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
node.left = init
|
|
295
|
+
node.right = isForIn ? this.parseExpression() : this.parseMaybeAssign()
|
|
296
|
+
|
|
297
|
+
// Check for our extended syntax: "; index varName"
|
|
298
|
+
if (!isForIn && this.type === tt.semi) {
|
|
299
|
+
this.next() // consume ';'
|
|
300
|
+
|
|
301
|
+
if (this.isContextual('index')) {
|
|
302
|
+
this.next() // consume 'index'
|
|
303
|
+
|
|
304
|
+
if (this.type === tt.name) {
|
|
305
|
+
node.index = this.parseIdent()
|
|
306
|
+
} else {
|
|
307
|
+
this.raise(this.start, 'Expected identifier after "index" keyword')
|
|
308
|
+
}
|
|
309
|
+
} else {
|
|
310
|
+
this.raise(this.start, 'Expected "index" keyword after semicolon in for-of loop')
|
|
311
|
+
}
|
|
312
|
+
} else if (!isForIn) {
|
|
313
|
+
// Set index to null for standard for-of loops
|
|
314
|
+
node.index = null
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
this.expect(tt.parenR)
|
|
318
|
+
node.body = this.parseStatement("for")
|
|
319
|
+
this.exitScope()
|
|
320
|
+
this.labels.pop()
|
|
321
|
+
return this.finishNode(node, isForIn ? "ForInStatement" : "ForOfStatement")
|
|
322
|
+
}
|
|
323
|
+
|
|
195
324
|
shouldParseExportStatement() {
|
|
196
325
|
if (super.shouldParseExportStatement()) {
|
|
197
326
|
return true;
|
|
@@ -791,6 +920,8 @@ function RipplePlugin(config) {
|
|
|
791
920
|
return node;
|
|
792
921
|
}
|
|
793
922
|
|
|
923
|
+
|
|
924
|
+
|
|
794
925
|
if (this.type.label === '@') {
|
|
795
926
|
// Try to parse as an expression statement first using tryParse
|
|
796
927
|
// This allows us to handle Ripple @ syntax like @count++ without
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
get_delegated_event,
|
|
6
6
|
is_element_dom_element,
|
|
7
7
|
is_inside_component,
|
|
8
|
-
|
|
8
|
+
is_ripple_track_call,
|
|
9
9
|
is_void_element,
|
|
10
10
|
} from '../../utils.js';
|
|
11
11
|
import { extract_paths } from '../../../utils/ast.js';
|
|
@@ -87,6 +87,10 @@ const visitors = {
|
|
|
87
87
|
next(scope !== undefined && scope !== state.scope ? { ...state, scope } : state);
|
|
88
88
|
},
|
|
89
89
|
|
|
90
|
+
Program(_, context) {
|
|
91
|
+
return context.next({ ...context.state, function_depth: 0, expression: null });
|
|
92
|
+
},
|
|
93
|
+
|
|
90
94
|
Identifier(node, context) {
|
|
91
95
|
const binding = context.state.scope.get(node.name);
|
|
92
96
|
const parent = context.path.at(-1);
|
|
@@ -133,6 +137,16 @@ const visitors = {
|
|
|
133
137
|
},
|
|
134
138
|
|
|
135
139
|
CallExpression(node, context) {
|
|
140
|
+
const callee = node.callee;
|
|
141
|
+
|
|
142
|
+
if (context.state.function_depth === 0 && is_ripple_track_call(callee, context)) {
|
|
143
|
+
error(
|
|
144
|
+
'`track` can only be used within a reactive context, such as a component, function or class that is used or created from a component',
|
|
145
|
+
context.state.analysis.module.filename,
|
|
146
|
+
node,
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
136
150
|
if (context.state.metadata?.tracking === false) {
|
|
137
151
|
context.state.metadata.tracking = true;
|
|
138
152
|
}
|
|
@@ -145,27 +159,20 @@ const visitors = {
|
|
|
145
159
|
},
|
|
146
160
|
|
|
147
161
|
VariableDeclaration(node, context) {
|
|
148
|
-
const { state, visit
|
|
162
|
+
const { state, visit } = context;
|
|
149
163
|
|
|
150
164
|
for (const declarator of node.declarations) {
|
|
151
165
|
if (is_inside_component(context) && node.kind === 'var') {
|
|
152
166
|
error(
|
|
153
|
-
'var declarations are not allowed in components, use let or const instead',
|
|
167
|
+
'`var` declarations are not allowed in components, use let or const instead',
|
|
154
168
|
state.analysis.module.filename,
|
|
155
169
|
declarator,
|
|
156
170
|
);
|
|
157
171
|
}
|
|
158
172
|
const metadata = { tracking: false, await: false };
|
|
159
|
-
const parent = path.at(-1);
|
|
160
|
-
const init_is_untracked =
|
|
161
|
-
declarator.init !== null &&
|
|
162
|
-
declarator.init.type === 'CallExpression' &&
|
|
163
|
-
is_ripple_import(declarator.init.callee, context) &&
|
|
164
|
-
declarator.init.callee.type === 'Identifier' &&
|
|
165
|
-
(declarator.init.callee.name === 'untrack' || declarator.init.callee.name === 'deferred');
|
|
166
173
|
|
|
167
174
|
if (declarator.id.type === 'Identifier') {
|
|
168
|
-
|
|
175
|
+
visit(declarator, state);
|
|
169
176
|
} else {
|
|
170
177
|
const paths = extract_paths(declarator.id);
|
|
171
178
|
|
|
@@ -234,7 +241,7 @@ const visitors = {
|
|
|
234
241
|
}
|
|
235
242
|
const elements = [];
|
|
236
243
|
|
|
237
|
-
context.next({ ...context.state, elements });
|
|
244
|
+
context.next({ ...context.state, elements, function_depth: context.state.function_depth + 1 });
|
|
238
245
|
|
|
239
246
|
const css = node.css;
|
|
240
247
|
|
|
@@ -262,6 +269,21 @@ const visitors = {
|
|
|
262
269
|
return context.next();
|
|
263
270
|
}
|
|
264
271
|
|
|
272
|
+
if (node.index) {
|
|
273
|
+
const state = context.state;
|
|
274
|
+
const scope = state.scopes.get(node);
|
|
275
|
+
const binding = scope.get(node.index.name);
|
|
276
|
+
binding.kind = 'index'
|
|
277
|
+
|
|
278
|
+
if (binding !== null) {
|
|
279
|
+
binding.transform = {
|
|
280
|
+
read: (node) => {
|
|
281
|
+
return b.call('_$_.get', node)
|
|
282
|
+
},
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
265
287
|
node.metadata = {
|
|
266
288
|
has_template: false,
|
|
267
289
|
};
|