@reactra/babel-plugin 0.1.0-alpha.0
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/LICENSE +21 -0
- package/README.md +17 -0
- package/dist/ast/index.d.ts +2 -0
- package/dist/ast/index.d.ts.map +1 -0
- package/dist/ast/index.js +3 -0
- package/dist/ast/index.js.map +1 -0
- package/dist/ast/nodes.d.ts +437 -0
- package/dist/ast/nodes.d.ts.map +1 -0
- package/dist/ast/nodes.js +35 -0
- package/dist/ast/nodes.js.map +1 -0
- package/dist/behaviours/index.d.ts +18 -0
- package/dist/behaviours/index.d.ts.map +1 -0
- package/dist/behaviours/index.js +36 -0
- package/dist/behaviours/index.js.map +1 -0
- package/dist/behaviours/plugin.d.ts +22 -0
- package/dist/behaviours/plugin.d.ts.map +1 -0
- package/dist/behaviours/plugin.js +70 -0
- package/dist/behaviours/plugin.js.map +1 -0
- package/dist/behaviours/replayable.d.ts +10 -0
- package/dist/behaviours/replayable.d.ts.map +1 -0
- package/dist/behaviours/replayable.js +86 -0
- package/dist/behaviours/replayable.js.map +1 -0
- package/dist/behaviours/types.d.ts +77 -0
- package/dist/behaviours/types.d.ts.map +1 -0
- package/dist/behaviours/types.js +10 -0
- package/dist/behaviours/types.js.map +1 -0
- package/dist/behaviours/undoable.d.ts +10 -0
- package/dist/behaviours/undoable.d.ts.map +1 -0
- package/dist/behaviours/undoable.js +62 -0
- package/dist/behaviours/undoable.js.map +1 -0
- package/dist/compile.d.ts +69 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +75 -0
- package/dist/compile.js.map +1 -0
- package/dist/conventions/index.d.ts +110 -0
- package/dist/conventions/index.d.ts.map +1 -0
- package/dist/conventions/index.js +193 -0
- package/dist/conventions/index.js.map +1 -0
- package/dist/index.d.ts +48 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +77 -0
- package/dist/index.js.map +1 -0
- package/dist/passes/index.d.ts +5 -0
- package/dist/passes/index.d.ts.map +1 -0
- package/dist/passes/index.js +6 -0
- package/dist/passes/index.js.map +1 -0
- package/dist/passes/pass-1-parse.d.ts +3 -0
- package/dist/passes/pass-1-parse.d.ts.map +1 -0
- package/dist/passes/pass-1-parse.js +21 -0
- package/dist/passes/pass-1-parse.js.map +1 -0
- package/dist/passes/pass-2-extract.d.ts +4 -0
- package/dist/passes/pass-2-extract.d.ts.map +1 -0
- package/dist/passes/pass-2-extract.js +762 -0
- package/dist/passes/pass-2-extract.js.map +1 -0
- package/dist/passes/pass-3-readset.d.ts +11 -0
- package/dist/passes/pass-3-readset.d.ts.map +1 -0
- package/dist/passes/pass-3-readset.js +338 -0
- package/dist/passes/pass-3-readset.js.map +1 -0
- package/dist/passes/pass-9-codegen.d.ts +27 -0
- package/dist/passes/pass-9-codegen.d.ts.map +1 -0
- package/dist/passes/pass-9-codegen.js +2755 -0
- package/dist/passes/pass-9-codegen.js.map +1 -0
- package/dist/preprocess/helpers.d.ts +71 -0
- package/dist/preprocess/helpers.d.ts.map +1 -0
- package/dist/preprocess/helpers.js +342 -0
- package/dist/preprocess/helpers.js.map +1 -0
- package/dist/preprocess/index.d.ts +6 -0
- package/dist/preprocess/index.d.ts.map +1 -0
- package/dist/preprocess/index.js +11 -0
- package/dist/preprocess/index.js.map +1 -0
- package/dist/preprocess/keywords.d.ts +28 -0
- package/dist/preprocess/keywords.d.ts.map +1 -0
- package/dist/preprocess/keywords.js +99 -0
- package/dist/preprocess/keywords.js.map +1 -0
- package/dist/preprocess/lexer.d.ts +8 -0
- package/dist/preprocess/lexer.d.ts.map +1 -0
- package/dist/preprocess/lexer.js +143 -0
- package/dist/preprocess/lexer.js.map +1 -0
- package/dist/preprocess/preprocess.d.ts +3 -0
- package/dist/preprocess/preprocess.d.ts.map +1 -0
- package/dist/preprocess/preprocess.js +568 -0
- package/dist/preprocess/preprocess.js.map +1 -0
- package/dist/preprocess/rewriters.d.ts +35 -0
- package/dist/preprocess/rewriters.d.ts.map +1 -0
- package/dist/preprocess/rewriters.js +1391 -0
- package/dist/preprocess/rewriters.js.map +1 -0
- package/dist/preprocess/source-map.d.ts +70 -0
- package/dist/preprocess/source-map.d.ts.map +1 -0
- package/dist/preprocess/source-map.js +253 -0
- package/dist/preprocess/source-map.js.map +1 -0
- package/dist/preprocess/types.d.ts +57 -0
- package/dist/preprocess/types.d.ts.map +1 -0
- package/dist/preprocess/types.js +7 -0
- package/dist/preprocess/types.js.map +1 -0
- package/dist/sidecar.d.ts +137 -0
- package/dist/sidecar.d.ts.map +1 -0
- package/dist/sidecar.js +172 -0
- package/dist/sidecar.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,762 @@
|
|
|
1
|
+
// Pass 2 — extract Reactra metadata from the parsed Babel AST.
|
|
2
|
+
//
|
|
3
|
+
// Walks the AST, finds the top-level `__reactra_{component|service|store}__`
|
|
4
|
+
// wrappers, then walks each one's factory body finding the per-marker calls
|
|
5
|
+
// (state, derived, action, view, ...). Produces a FileGraph that later
|
|
6
|
+
// passes consume.
|
|
7
|
+
//
|
|
8
|
+
// Owner spec: reactra-compiler-spec.md §4 Pass 2, Pass 2a.
|
|
9
|
+
import _traverse from "@babel/traverse";
|
|
10
|
+
import * as t from "@babel/types";
|
|
11
|
+
// §B1: marker vocabulary + await arg order come from the shared conventions home
|
|
12
|
+
// (CONTAINER_MARKERS is re-exported there from ast/nodes.ts — one home).
|
|
13
|
+
import { AWAIT_ARG_ORDER, CONTAINER_MARKERS } from "../conventions/index.js";
|
|
14
|
+
// @babel/traverse is a CJS module that exports its default via .default
|
|
15
|
+
// when imported from ESM. The dual cast keeps both interop paths happy.
|
|
16
|
+
// CJS/ESM interop: @babel/traverse exports `module.exports = traverse` (CJS).
|
|
17
|
+
// Under moduleResolution:NodeNext the default import resolves to the module
|
|
18
|
+
// namespace (not callable). The runtime `.default` unwrap + any-cast recovers it.
|
|
19
|
+
// The dev typecheck (moduleResolution:Bundler) validates call-site types.
|
|
20
|
+
const traverse = (_traverse.default ?? _traverse);
|
|
21
|
+
/** Slice the original source between two AST positions. */
|
|
22
|
+
const sliceSource = (source, node) => {
|
|
23
|
+
if (node.start == null || node.end == null)
|
|
24
|
+
return "";
|
|
25
|
+
return source.slice(node.start, node.end);
|
|
26
|
+
};
|
|
27
|
+
/** Returns the string literal value of the first argument, or undefined. */
|
|
28
|
+
const firstStringArg = (call) => {
|
|
29
|
+
const a = call.arguments[0];
|
|
30
|
+
if (a && t.isStringLiteral(a))
|
|
31
|
+
return a.value;
|
|
32
|
+
return undefined;
|
|
33
|
+
};
|
|
34
|
+
/** Returns the arrow function at the given argument index, or undefined. */
|
|
35
|
+
const arrowArg = (call, idx) => {
|
|
36
|
+
const a = call.arguments[idx];
|
|
37
|
+
if (a && t.isArrowFunctionExpression(a))
|
|
38
|
+
return a;
|
|
39
|
+
return undefined;
|
|
40
|
+
};
|
|
41
|
+
/** Extract the expression body of a thunk arrow `() => (expr)`. */
|
|
42
|
+
const thunkBody = (arrow) => {
|
|
43
|
+
const body = arrow.body;
|
|
44
|
+
if (!t.isBlockStatement(body))
|
|
45
|
+
return body;
|
|
46
|
+
// Single-return form `() => { return expr }`
|
|
47
|
+
if (body.body.length === 1) {
|
|
48
|
+
const stmt = body.body[0];
|
|
49
|
+
if (t.isReturnStatement(stmt) && stmt.argument)
|
|
50
|
+
return stmt.argument;
|
|
51
|
+
}
|
|
52
|
+
return undefined;
|
|
53
|
+
};
|
|
54
|
+
export const extractGraph = (source, preprocessed, ast) => {
|
|
55
|
+
// AST positions are relative to `preprocessed` (what Babel parsed).
|
|
56
|
+
// Use that for source-slicing; `source` is kept for source-map work later.
|
|
57
|
+
const sliceFrom = preprocessed;
|
|
58
|
+
const containers = [];
|
|
59
|
+
// Top-level `import …` statements — emitted verbatim by Pass 9 above
|
|
60
|
+
// its own auto-injected imports so user modules (api, utils, peer
|
|
61
|
+
// components imported into a JSX view) stay in scope. Looking at
|
|
62
|
+
// `ast.program.body` directly avoids walking the whole tree.
|
|
63
|
+
const userImports = [];
|
|
64
|
+
// §1.1 — top-level `interface`/`type` declarations are pure type-level, so we
|
|
65
|
+
// preserve them verbatim (Pass 9 re-emits after imports) and a DSL file can
|
|
66
|
+
// declare its own props types in-file. `export`ed variants included.
|
|
67
|
+
const userTypes = [];
|
|
68
|
+
const isTypeDecl = (n) => t.isTSInterfaceDeclaration(n) || t.isTSTypeAliasDeclaration(n);
|
|
69
|
+
for (const stmt of ast.program.body) {
|
|
70
|
+
if (t.isImportDeclaration(stmt)) {
|
|
71
|
+
userImports.push(sliceSource(sliceFrom, stmt));
|
|
72
|
+
}
|
|
73
|
+
else if (isTypeDecl(stmt)) {
|
|
74
|
+
userTypes.push(sliceSource(sliceFrom, stmt));
|
|
75
|
+
}
|
|
76
|
+
else if (t.isExportNamedDeclaration(stmt) && stmt.declaration && isTypeDecl(stmt.declaration)) {
|
|
77
|
+
userTypes.push(sliceSource(sliceFrom, stmt));
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
traverse(ast, {
|
|
81
|
+
VariableDeclaration(path) {
|
|
82
|
+
// Top-level declarations only — direct children of Program.
|
|
83
|
+
if (!t.isProgram(path.parent) && !t.isExportNamedDeclaration(path.parent))
|
|
84
|
+
return;
|
|
85
|
+
for (const declarator of path.node.declarations) {
|
|
86
|
+
const init = declarator.init;
|
|
87
|
+
if (!t.isCallExpression(init))
|
|
88
|
+
continue;
|
|
89
|
+
const callee = init.callee;
|
|
90
|
+
if (!t.isIdentifier(callee))
|
|
91
|
+
continue;
|
|
92
|
+
const kind = CONTAINER_MARKERS[callee.name];
|
|
93
|
+
if (!kind)
|
|
94
|
+
continue;
|
|
95
|
+
const name = firstStringArg(init);
|
|
96
|
+
if (!name)
|
|
97
|
+
continue;
|
|
98
|
+
// The factory arrow is the LAST argument (subtree stores have
|
|
99
|
+
// `(name, "/path", factory)`; keyed have `(name, (key) => {})`).
|
|
100
|
+
const factory = init.arguments[init.arguments.length - 1];
|
|
101
|
+
if (!t.isArrowFunctionExpression(factory))
|
|
102
|
+
continue;
|
|
103
|
+
// subtree path: 2nd arg is a string for `__reactra_route_store_subtree__`
|
|
104
|
+
let subtreePath;
|
|
105
|
+
if (callee.name === "__reactra_route_store_subtree__") {
|
|
106
|
+
const second = init.arguments[1];
|
|
107
|
+
if (t.isStringLiteral(second))
|
|
108
|
+
subtreePath = second.value;
|
|
109
|
+
}
|
|
110
|
+
// keyed sig — emitted by the preprocessor as the factory's first param
|
|
111
|
+
let keySig;
|
|
112
|
+
if (callee.name === "__reactra_route_store_keyed__" && factory.params.length === 1) {
|
|
113
|
+
keySig = sliceSource(sliceFrom, factory.params[0]);
|
|
114
|
+
}
|
|
115
|
+
// Wave 3 §2b — service modifier (`scoped` / `server`). The
|
|
116
|
+
// preprocessor differentiates by emitting separate marker
|
|
117
|
+
// function names; here we map them back to the field on
|
|
118
|
+
// ContainerNode for Pass 9 to consume.
|
|
119
|
+
let serviceModifier;
|
|
120
|
+
if (callee.name === "__reactra_service_scoped__")
|
|
121
|
+
serviceModifier = "scoped";
|
|
122
|
+
else if (callee.name === "__reactra_service_server__")
|
|
123
|
+
serviceModifier = "server";
|
|
124
|
+
const exported = t.isExportNamedDeclaration(path.parent);
|
|
125
|
+
const container = {
|
|
126
|
+
kind,
|
|
127
|
+
name,
|
|
128
|
+
exported,
|
|
129
|
+
call: init,
|
|
130
|
+
factory,
|
|
131
|
+
states: [],
|
|
132
|
+
deriveds: [],
|
|
133
|
+
actions: [],
|
|
134
|
+
commands: [],
|
|
135
|
+
resources: [],
|
|
136
|
+
refs: [],
|
|
137
|
+
effects: [],
|
|
138
|
+
mounts: [],
|
|
139
|
+
cleanups: [],
|
|
140
|
+
injects: [],
|
|
141
|
+
provides: [],
|
|
142
|
+
params: [],
|
|
143
|
+
queries: [],
|
|
144
|
+
storeUses: [],
|
|
145
|
+
inputs: [],
|
|
146
|
+
awaitBlocks: [],
|
|
147
|
+
subtreePath,
|
|
148
|
+
keySig,
|
|
149
|
+
serviceModifier,
|
|
150
|
+
};
|
|
151
|
+
// §1.1 — component props parameter. The preprocessor emitted it as the
|
|
152
|
+
// factory arrow's first param; record the verbatim text (Pass 9 re-emits
|
|
153
|
+
// it) and the binding names (Pass 3 adds them to the reactive read-set).
|
|
154
|
+
if (kind === "component" && factory.params.length > 0) {
|
|
155
|
+
const p = factory.params[0];
|
|
156
|
+
container.propsParam = sliceSource(sliceFrom, p);
|
|
157
|
+
const names = [];
|
|
158
|
+
collectPatternNames(p, names);
|
|
159
|
+
container.propNames = names;
|
|
160
|
+
}
|
|
161
|
+
extractContainerBody(sliceFrom, factory, container);
|
|
162
|
+
// Day 23 / `#7b`: walk the captured view (if any) and surface
|
|
163
|
+
// every `await(r) { ... } pending { ... } error(e) { ... }`
|
|
164
|
+
// block into `container.awaitBlocks`. Runtime path is still
|
|
165
|
+
// pass-9-codegen's inline Suspense+ErrorBoundary rewrite; this
|
|
166
|
+
// list exists so sidecar consumers (devtools / LSP / future
|
|
167
|
+
// passes) can enumerate await sites without re-parsing the
|
|
168
|
+
// view AST.
|
|
169
|
+
extractAwaitBlocks(sliceFrom, container);
|
|
170
|
+
containers.push(container);
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
return { source, preprocessed, containers, userImports, userTypes };
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Collect every binding name introduced by a component's props parameter
|
|
178
|
+
* (Component DSL §1.1) into `out`. A bag identifier `(props)` contributes
|
|
179
|
+
* `props`; a destructuring pattern `({ a, b = d, ...rest })` contributes each
|
|
180
|
+
* leaf binding (`a`, `b`, `rest`). These are the names Pass 3 treats as reactive
|
|
181
|
+
* reads — for the bag form, `props` then absorbs `props.x` member reads.
|
|
182
|
+
*/
|
|
183
|
+
const collectPatternNames = (node, out) => {
|
|
184
|
+
if (t.isIdentifier(node))
|
|
185
|
+
out.push(node.name);
|
|
186
|
+
else if (t.isAssignmentPattern(node))
|
|
187
|
+
collectPatternNames(node.left, out);
|
|
188
|
+
else if (t.isRestElement(node))
|
|
189
|
+
collectPatternNames(node.argument, out);
|
|
190
|
+
else if (t.isObjectPattern(node)) {
|
|
191
|
+
for (const prop of node.properties) {
|
|
192
|
+
if (t.isObjectProperty(prop))
|
|
193
|
+
collectPatternNames(prop.value, out);
|
|
194
|
+
else
|
|
195
|
+
collectPatternNames(prop, out); // RestElement
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else if (t.isArrayPattern(node)) {
|
|
199
|
+
for (const el of node.elements)
|
|
200
|
+
if (el)
|
|
201
|
+
collectPatternNames(el, out);
|
|
202
|
+
}
|
|
203
|
+
};
|
|
204
|
+
/** Walk the factory body of a container, classifying each top-level marker call. */
|
|
205
|
+
const extractContainerBody = (source, factory, container) => {
|
|
206
|
+
const body = factory.body;
|
|
207
|
+
if (!t.isBlockStatement(body))
|
|
208
|
+
return;
|
|
209
|
+
for (const stmt of body.body) {
|
|
210
|
+
if (!t.isExpressionStatement(stmt))
|
|
211
|
+
continue;
|
|
212
|
+
const expr = stmt.expression;
|
|
213
|
+
if (!t.isCallExpression(expr))
|
|
214
|
+
continue;
|
|
215
|
+
classifyBodyMarker(source, expr, container);
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
/** Dispatch a body-level call to the right node-builder. */
|
|
219
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
220
|
+
const classifyBodyMarker = (source, call, c) => {
|
|
221
|
+
const callee = call.callee;
|
|
222
|
+
if (!t.isIdentifier(callee))
|
|
223
|
+
return;
|
|
224
|
+
switch (callee.name) {
|
|
225
|
+
case "__reactra_state__":
|
|
226
|
+
c.states.push(buildState(source, call));
|
|
227
|
+
return;
|
|
228
|
+
case "__reactra_derived__":
|
|
229
|
+
c.deriveds.push(buildDerived(source, call));
|
|
230
|
+
return;
|
|
231
|
+
case "__reactra_action__":
|
|
232
|
+
c.actions.push(buildAction(call, false));
|
|
233
|
+
return;
|
|
234
|
+
case "__reactra_action_async__":
|
|
235
|
+
c.actions.push(buildAction(call, true));
|
|
236
|
+
return;
|
|
237
|
+
case "__reactra_command__":
|
|
238
|
+
c.commands.push(buildCommand(call, "block"));
|
|
239
|
+
return;
|
|
240
|
+
case "__reactra_command_arrow__":
|
|
241
|
+
c.commands.push(buildCommand(call, "arrow"));
|
|
242
|
+
return;
|
|
243
|
+
case "__reactra_resource__":
|
|
244
|
+
c.resources.push(buildResource(source, call));
|
|
245
|
+
return;
|
|
246
|
+
case "__reactra_ref__":
|
|
247
|
+
c.refs.push(buildRef(source, call));
|
|
248
|
+
return;
|
|
249
|
+
case "__reactra_mount__":
|
|
250
|
+
c.mounts.push(buildMount(call));
|
|
251
|
+
return;
|
|
252
|
+
case "__reactra_cleanup__":
|
|
253
|
+
c.cleanups.push(buildCleanup(call));
|
|
254
|
+
return;
|
|
255
|
+
case "__reactra_effect__":
|
|
256
|
+
case "__reactra_effect_on__":
|
|
257
|
+
c.effects.push(buildEffect(call, callee.name === "__reactra_effect_on__"));
|
|
258
|
+
return;
|
|
259
|
+
case "__reactra_view__":
|
|
260
|
+
c.view = buildView(call);
|
|
261
|
+
return;
|
|
262
|
+
case "__reactra_uses__":
|
|
263
|
+
// One `uses` clause per container (Component DSL §9). A second marker
|
|
264
|
+
// means the source carried BOTH a header clause (lowered first by
|
|
265
|
+
// Pass 1.1) and a body line — error rather than silently overwrite.
|
|
266
|
+
// Code-less: no `R` code is minted for this structural band.
|
|
267
|
+
if (c.uses) {
|
|
268
|
+
throw new Error(`[reactra:compile] BHV002: ${c.kind} "${c.name}" declares more than one \`uses\` clause ` +
|
|
269
|
+
`(header form + body line). Declare ONE — either in the header ` +
|
|
270
|
+
`(\`component ${c.name} uses ${c.uses.names.join(", ")} {\`) or as a body line. ` +
|
|
271
|
+
`(Component DSL §9; Behaviour Plugin spec §8.)`);
|
|
272
|
+
}
|
|
273
|
+
c.uses = buildUses(call);
|
|
274
|
+
return;
|
|
275
|
+
case "__reactra_inject__":
|
|
276
|
+
c.injects.push(buildInject(call));
|
|
277
|
+
return;
|
|
278
|
+
case "__reactra_provide__":
|
|
279
|
+
c.provides.push(buildProvide(source, call));
|
|
280
|
+
return;
|
|
281
|
+
case "__reactra_param__":
|
|
282
|
+
c.params.push(buildParam(call));
|
|
283
|
+
return;
|
|
284
|
+
case "__reactra_query__":
|
|
285
|
+
c.queries.push(buildQuery(source, call));
|
|
286
|
+
return;
|
|
287
|
+
case "__reactra_meta__":
|
|
288
|
+
c.meta = buildMeta(call);
|
|
289
|
+
return;
|
|
290
|
+
case "__reactra_prefetch__":
|
|
291
|
+
c.prefetch = buildPrefetch(call);
|
|
292
|
+
return;
|
|
293
|
+
case "__reactra_transition__":
|
|
294
|
+
c.transition = buildTransition(call);
|
|
295
|
+
return;
|
|
296
|
+
case "__reactra_inject_store__":
|
|
297
|
+
c.storeUses.push(buildStoreUseBare(call));
|
|
298
|
+
return;
|
|
299
|
+
case "__reactra_inject_store_args__":
|
|
300
|
+
c.storeUses.push(buildStoreUseArgs(call));
|
|
301
|
+
return;
|
|
302
|
+
case "__reactra_inject_store_keyed__":
|
|
303
|
+
c.storeUses.push(buildStoreUseKeyed(call));
|
|
304
|
+
return;
|
|
305
|
+
case "__reactra_error_boundary__":
|
|
306
|
+
c.errorBoundary = buildErrorBoundary(call);
|
|
307
|
+
return;
|
|
308
|
+
case "__reactra_suspense__":
|
|
309
|
+
c.suspense = buildSuspense(call);
|
|
310
|
+
return;
|
|
311
|
+
case "__reactra_input__":
|
|
312
|
+
c.inputs.push(buildInput(source, call));
|
|
313
|
+
return;
|
|
314
|
+
case "__reactra_preserved__":
|
|
315
|
+
// v2: each `preserved state X = init` emits one __reactra_preserved__("X") call
|
|
316
|
+
// with a single field; multiple calls accumulate on the same node. v1 emitted
|
|
317
|
+
// a single call with all fields as string args — both forms are handled by
|
|
318
|
+
// merging into the existing `preserved` node's field list.
|
|
319
|
+
if (c.preserved) {
|
|
320
|
+
const extra = buildPreserved(call);
|
|
321
|
+
for (const f of extra.fields) {
|
|
322
|
+
if (!c.preserved.fields.includes(f))
|
|
323
|
+
c.preserved.fields.push(f);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
else {
|
|
327
|
+
c.preserved = buildPreserved(call);
|
|
328
|
+
}
|
|
329
|
+
return;
|
|
330
|
+
default:
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
};
|
|
334
|
+
// ---------- per-marker builders ----------
|
|
335
|
+
const buildState = (source, call) => {
|
|
336
|
+
const name = firstStringArg(call) ?? "";
|
|
337
|
+
const init = (call.arguments[1] ?? t.identifier("undefined"));
|
|
338
|
+
return {
|
|
339
|
+
kind: "state",
|
|
340
|
+
name,
|
|
341
|
+
initializer: init,
|
|
342
|
+
initializerText: sliceSource(source, init),
|
|
343
|
+
call,
|
|
344
|
+
};
|
|
345
|
+
};
|
|
346
|
+
const buildDerived = (source, call) => {
|
|
347
|
+
const name = firstStringArg(call) ?? "";
|
|
348
|
+
const thunk = arrowArg(call, 1);
|
|
349
|
+
const expr = thunkBody(thunk);
|
|
350
|
+
return {
|
|
351
|
+
kind: "derived",
|
|
352
|
+
name,
|
|
353
|
+
thunk,
|
|
354
|
+
exprText: expr ? sliceSource(source, expr) : "",
|
|
355
|
+
deps: [], // populated by Pass 3 (read-set analysis)
|
|
356
|
+
call,
|
|
357
|
+
};
|
|
358
|
+
};
|
|
359
|
+
const buildAction = (call, isAsync) => ({
|
|
360
|
+
kind: "action",
|
|
361
|
+
name: firstStringArg(call) ?? "",
|
|
362
|
+
isAsync,
|
|
363
|
+
arrow: arrowArg(call, 1),
|
|
364
|
+
deps: [], // populated by Pass 3 (read-set analysis)
|
|
365
|
+
call,
|
|
366
|
+
});
|
|
367
|
+
/** The value of an object-literal property by key, or undefined. */
|
|
368
|
+
const objProp = (obj, key) => {
|
|
369
|
+
for (const p of obj.properties) {
|
|
370
|
+
if (t.isObjectProperty(p) && t.isIdentifier(p.key, { name: key }))
|
|
371
|
+
return p.value;
|
|
372
|
+
}
|
|
373
|
+
return undefined;
|
|
374
|
+
};
|
|
375
|
+
/**
|
|
376
|
+
* Resource names from an `invalidate [a, b]` array. Each entry names a resource —
|
|
377
|
+
* a bare identifier (`posts`) or a string literal (`"posts"`, the useMutation
|
|
378
|
+
* muscle-memory form); both mean the same cache key. Any OTHER element kind
|
|
379
|
+
* (member expression, call, spread) is NOT a resource name — warn loudly rather
|
|
380
|
+
* than silently dropping it, so a malformed `invalidate` can't quietly skip a
|
|
381
|
+
* refetch (a cache-coherence footgun). Holes (`[a, ,]`) are ignored silently.
|
|
382
|
+
*/
|
|
383
|
+
const invalidateNames = (commandName, arr) => {
|
|
384
|
+
const names = [];
|
|
385
|
+
for (const e of arr.elements) {
|
|
386
|
+
if (e == null)
|
|
387
|
+
continue; // an array hole (trailing/extra comma) — harmless
|
|
388
|
+
if (t.isIdentifier(e))
|
|
389
|
+
names.push(e.name);
|
|
390
|
+
else if (t.isStringLiteral(e))
|
|
391
|
+
names.push(e.value);
|
|
392
|
+
else {
|
|
393
|
+
console.warn(`[reactra:compile] command "${commandName}" — \`invalidate\` accepts bare resource ` +
|
|
394
|
+
`names (e.g. \`invalidate [posts]\` or \`invalidate ["posts"]\`); ignoring an ` +
|
|
395
|
+
`unsupported entry of kind "${e.type}".`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return names;
|
|
399
|
+
};
|
|
400
|
+
const buildCommand = (call, form) => {
|
|
401
|
+
// Arrow form may carry a 3rd arg — the clause options object
|
|
402
|
+
// `{ optimistic: () => {…}, invalidate: [a, b], rollback: (e) => {…} }`.
|
|
403
|
+
const opts = form === "arrow" && t.isObjectExpression(call.arguments[2]) ? call.arguments[2] : undefined;
|
|
404
|
+
const name = firstStringArg(call) ?? "";
|
|
405
|
+
const optimistic = opts && objProp(opts, "optimistic");
|
|
406
|
+
const invalidate = opts && objProp(opts, "invalidate");
|
|
407
|
+
const rollback = opts && objProp(opts, "rollback");
|
|
408
|
+
return {
|
|
409
|
+
kind: "command",
|
|
410
|
+
name,
|
|
411
|
+
form,
|
|
412
|
+
arrow: arrowArg(call, 1),
|
|
413
|
+
...(optimistic && t.isArrowFunctionExpression(optimistic) ? { optimistic } : {}),
|
|
414
|
+
...(invalidate && t.isArrayExpression(invalidate) ? { invalidate: invalidateNames(name, invalidate) } : {}),
|
|
415
|
+
...(rollback && t.isArrowFunctionExpression(rollback) ? { rollback } : {}),
|
|
416
|
+
call,
|
|
417
|
+
};
|
|
418
|
+
};
|
|
419
|
+
const buildResource = (source, call) => {
|
|
420
|
+
// Stage 3 — the 4th arg, when present, is an ObjectExpression literal the
|
|
421
|
+
// preprocessor emits for modifier opt-ins (`cache`, `swr`, `retry`). We read
|
|
422
|
+
// both the flags AND the parameterized values: `cache: { ttlMs }` (hard-expiry)
|
|
423
|
+
// vs `cache: { staleWhileRevalidate, ttlMs }` (swr) vs `cache: true` (bare); and
|
|
424
|
+
// the `retry` count. Values are sliced as source so an expression (`retry: MAX`)
|
|
425
|
+
// survives. Absent → all default (Pass 9 fills `ttlMs: 60_000` / `retry: 3`).
|
|
426
|
+
const optsArg = call.arguments[3];
|
|
427
|
+
let optCache;
|
|
428
|
+
let optSwr;
|
|
429
|
+
let optRetry;
|
|
430
|
+
let cacheTtl;
|
|
431
|
+
let retryCount;
|
|
432
|
+
let select;
|
|
433
|
+
if (optsArg && t.isObjectExpression(optsArg)) {
|
|
434
|
+
const cacheVal = objProp(optsArg, "cache");
|
|
435
|
+
const retryVal = objProp(optsArg, "retry");
|
|
436
|
+
if (cacheVal && t.isObjectExpression(cacheVal)) {
|
|
437
|
+
const isSwr = objProp(cacheVal, "staleWhileRevalidate") !== undefined;
|
|
438
|
+
if (isSwr)
|
|
439
|
+
optSwr = true;
|
|
440
|
+
else
|
|
441
|
+
optCache = true;
|
|
442
|
+
const ttl = objProp(cacheVal, "ttlMs");
|
|
443
|
+
if (ttl)
|
|
444
|
+
cacheTtl = sliceSource(source, ttl);
|
|
445
|
+
}
|
|
446
|
+
else if (cacheVal) {
|
|
447
|
+
optCache = true; // `cache: true` (bare, no TTL)
|
|
448
|
+
}
|
|
449
|
+
if (retryVal) {
|
|
450
|
+
optRetry = true;
|
|
451
|
+
retryCount = sliceSource(source, retryVal);
|
|
452
|
+
}
|
|
453
|
+
const selectVal = objProp(optsArg, "select");
|
|
454
|
+
if (selectVal && t.isArrowFunctionExpression(selectVal)) {
|
|
455
|
+
select = sliceSource(source, selectVal);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return {
|
|
459
|
+
kind: "resource",
|
|
460
|
+
name: firstStringArg(call) ?? "",
|
|
461
|
+
depsThunk: arrowArg(call, 1),
|
|
462
|
+
fetcher: arrowArg(call, 2),
|
|
463
|
+
optCache,
|
|
464
|
+
optSwr,
|
|
465
|
+
optRetry,
|
|
466
|
+
...(cacheTtl !== undefined ? { cacheTtl } : {}),
|
|
467
|
+
...(retryCount !== undefined ? { retryCount } : {}),
|
|
468
|
+
...(select !== undefined ? { select } : {}),
|
|
469
|
+
call,
|
|
470
|
+
};
|
|
471
|
+
};
|
|
472
|
+
const buildRef = (source, call) => {
|
|
473
|
+
const name = firstStringArg(call) ?? "";
|
|
474
|
+
const init = (call.arguments[1] ?? t.nullLiteral());
|
|
475
|
+
return {
|
|
476
|
+
kind: "ref",
|
|
477
|
+
name,
|
|
478
|
+
initializer: init,
|
|
479
|
+
initializerText: sliceSource(source, init),
|
|
480
|
+
call,
|
|
481
|
+
};
|
|
482
|
+
};
|
|
483
|
+
const buildMount = (call) => ({
|
|
484
|
+
kind: "mount",
|
|
485
|
+
body: arrowArg(call, 0),
|
|
486
|
+
call,
|
|
487
|
+
});
|
|
488
|
+
const buildCleanup = (call) => ({
|
|
489
|
+
kind: "cleanup",
|
|
490
|
+
body: arrowArg(call, 0),
|
|
491
|
+
call,
|
|
492
|
+
});
|
|
493
|
+
const buildEffect = (call, hasWatch) => ({
|
|
494
|
+
kind: "effect",
|
|
495
|
+
watch: hasWatch ? arrowArg(call, 0) : undefined,
|
|
496
|
+
body: hasWatch ? arrowArg(call, 1) : arrowArg(call, 0),
|
|
497
|
+
call,
|
|
498
|
+
});
|
|
499
|
+
const buildView = (call) => ({
|
|
500
|
+
kind: "view",
|
|
501
|
+
body: arrowArg(call, 0),
|
|
502
|
+
call,
|
|
503
|
+
});
|
|
504
|
+
const buildUses = (call) => {
|
|
505
|
+
const names = [];
|
|
506
|
+
for (const arg of call.arguments) {
|
|
507
|
+
if (t.isStringLiteral(arg))
|
|
508
|
+
names.push(arg.value);
|
|
509
|
+
}
|
|
510
|
+
return { kind: "uses", names, call };
|
|
511
|
+
};
|
|
512
|
+
/** Read a string-valued property `key` off an ObjectExpression argument. */
|
|
513
|
+
const objStringProp = (arg, key) => {
|
|
514
|
+
if (!arg || !t.isObjectExpression(arg))
|
|
515
|
+
return undefined;
|
|
516
|
+
for (const prop of arg.properties) {
|
|
517
|
+
if (!t.isObjectProperty(prop) || !t.isIdentifier(prop.key) || prop.key.name !== key)
|
|
518
|
+
continue;
|
|
519
|
+
if (t.isStringLiteral(prop.value))
|
|
520
|
+
return prop.value.value;
|
|
521
|
+
}
|
|
522
|
+
return undefined;
|
|
523
|
+
};
|
|
524
|
+
/**
|
|
525
|
+
* Read a `{ fields: ["a", { source: "b", local: "localB" }] }` argument into a
|
|
526
|
+
* `StoreField[]` (undefined if absent). A string element is a bare field; an
|
|
527
|
+
* object element is a `{ source, local, as? }` rename (Store v4.7 §2.5).
|
|
528
|
+
*/
|
|
529
|
+
const readFieldsArg = (arg) => {
|
|
530
|
+
if (!arg || !t.isObjectExpression(arg))
|
|
531
|
+
return undefined;
|
|
532
|
+
for (const prop of arg.properties) {
|
|
533
|
+
if (!t.isObjectProperty(prop) || !t.isIdentifier(prop.key) || prop.key.name !== "fields")
|
|
534
|
+
continue;
|
|
535
|
+
if (!t.isArrayExpression(prop.value))
|
|
536
|
+
return undefined;
|
|
537
|
+
const out = [];
|
|
538
|
+
for (const e of prop.value.elements) {
|
|
539
|
+
if (e == null)
|
|
540
|
+
continue;
|
|
541
|
+
if (t.isStringLiteral(e)) {
|
|
542
|
+
out.push(e.value);
|
|
543
|
+
}
|
|
544
|
+
else if (t.isObjectExpression(e)) {
|
|
545
|
+
const source = objStringProp(e, "source");
|
|
546
|
+
const local = objStringProp(e, "local");
|
|
547
|
+
if (source && local)
|
|
548
|
+
out.push({ source, local, as: objBoolProp(e, "as") });
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return out.length > 0 ? out : undefined;
|
|
552
|
+
}
|
|
553
|
+
return undefined;
|
|
554
|
+
};
|
|
555
|
+
/** Read a boolean object property (`{ as: true }`) — true only when literally `true`. */
|
|
556
|
+
const objBoolProp = (obj, key) => {
|
|
557
|
+
if (!obj || !t.isObjectExpression(obj))
|
|
558
|
+
return false;
|
|
559
|
+
for (const prop of obj.properties) {
|
|
560
|
+
if (t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name === key) {
|
|
561
|
+
return t.isBooleanLiteral(prop.value) && prop.value.value === true;
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
return false;
|
|
565
|
+
};
|
|
566
|
+
const buildInject = (call) => {
|
|
567
|
+
const opts = call.arguments[1];
|
|
568
|
+
const serviceKind = objStringProp(opts, "kind") === "service" ? "service" : undefined;
|
|
569
|
+
return {
|
|
570
|
+
kind: "inject",
|
|
571
|
+
name: firstStringArg(call) ?? "",
|
|
572
|
+
serviceKind,
|
|
573
|
+
source: objStringProp(opts, "source"),
|
|
574
|
+
alias: objStringProp(opts, "alias"),
|
|
575
|
+
call,
|
|
576
|
+
};
|
|
577
|
+
};
|
|
578
|
+
const buildProvide = (source, call) => {
|
|
579
|
+
const name = firstStringArg(call) ?? "";
|
|
580
|
+
const value = (call.arguments[1] ?? t.nullLiteral());
|
|
581
|
+
return {
|
|
582
|
+
kind: "provide",
|
|
583
|
+
name,
|
|
584
|
+
value,
|
|
585
|
+
valueText: sliceSource(source, value),
|
|
586
|
+
call,
|
|
587
|
+
};
|
|
588
|
+
};
|
|
589
|
+
const buildParam = (call) => ({
|
|
590
|
+
kind: "param",
|
|
591
|
+
name: firstStringArg(call) ?? "",
|
|
592
|
+
call,
|
|
593
|
+
});
|
|
594
|
+
const buildQuery = (source, call) => {
|
|
595
|
+
const name = firstStringArg(call) ?? "";
|
|
596
|
+
// `__reactra_query__("name", "<type>"[, default[, parser]])` — arg[1] is the
|
|
597
|
+
// declared type string (preprocessor always emits it; "string" when unannotated),
|
|
598
|
+
// arg[2] the optional default, arg[3] the optional custom-from parser identifier.
|
|
599
|
+
// When `from parseFoo` is present without a default, arg[2] is the literal
|
|
600
|
+
// `undefined` (a placeholder so the parser stays at arg[3]).
|
|
601
|
+
const typeArg = call.arguments[1];
|
|
602
|
+
const tsType = t.isStringLiteral(typeArg) ? typeArg.value : "string";
|
|
603
|
+
const def = call.arguments[2];
|
|
604
|
+
const isUndefinedPlaceholder = def != null && t.isIdentifier(def) && def.name === "undefined";
|
|
605
|
+
const parserArg = call.arguments[3];
|
|
606
|
+
const fromParser = parserArg != null && t.isIdentifier(parserArg) ? parserArg.name : undefined;
|
|
607
|
+
return {
|
|
608
|
+
kind: "query",
|
|
609
|
+
name,
|
|
610
|
+
tsType,
|
|
611
|
+
defaultExpr: isUndefinedPlaceholder ? undefined : def,
|
|
612
|
+
defaultText: def && !isUndefinedPlaceholder ? sliceSource(source, def) : undefined,
|
|
613
|
+
fromParser,
|
|
614
|
+
call,
|
|
615
|
+
};
|
|
616
|
+
};
|
|
617
|
+
const buildMeta = (call) => ({
|
|
618
|
+
kind: "meta",
|
|
619
|
+
object: call.arguments[0],
|
|
620
|
+
call,
|
|
621
|
+
});
|
|
622
|
+
const buildPrefetch = (call) => {
|
|
623
|
+
const trigger = firstStringArg(call) ?? "none";
|
|
624
|
+
return {
|
|
625
|
+
kind: "prefetch",
|
|
626
|
+
trigger: trigger,
|
|
627
|
+
call,
|
|
628
|
+
};
|
|
629
|
+
};
|
|
630
|
+
const buildTransition = (call) => {
|
|
631
|
+
const name = firstStringArg(call) ?? "";
|
|
632
|
+
const second = call.arguments[1];
|
|
633
|
+
const durationMs = second && t.isNumericLiteral(second) ? second.value : undefined;
|
|
634
|
+
return { kind: "transition", name, durationMs, call };
|
|
635
|
+
};
|
|
636
|
+
const buildStoreUseBare = (call) => ({
|
|
637
|
+
kind: "store-use",
|
|
638
|
+
storeName: firstStringArg(call) ?? "",
|
|
639
|
+
classification: "bare",
|
|
640
|
+
// __reactra_inject_store__("X" [, { fields: [...], typeOverride: "T", alias: "Y" }])
|
|
641
|
+
fields: readFieldsArg(call.arguments[1]),
|
|
642
|
+
typeOverride: objStringProp(call.arguments[1], "typeOverride"),
|
|
643
|
+
alias: objStringProp(call.arguments[1], "alias"),
|
|
644
|
+
call,
|
|
645
|
+
});
|
|
646
|
+
const buildStoreUseArgs = (call) => ({
|
|
647
|
+
kind: "store-use",
|
|
648
|
+
storeName: firstStringArg(call) ?? "",
|
|
649
|
+
classification: "argumented",
|
|
650
|
+
// __reactra_inject_store_args__("X", { inputs } [, { fields: [...], typeOverride: "T" }])
|
|
651
|
+
args: call.arguments[1],
|
|
652
|
+
fields: readFieldsArg(call.arguments[2]),
|
|
653
|
+
typeOverride: objStringProp(call.arguments[2], "typeOverride"),
|
|
654
|
+
call,
|
|
655
|
+
});
|
|
656
|
+
const buildStoreUseKeyed = (call) => ({
|
|
657
|
+
kind: "store-use",
|
|
658
|
+
storeName: firstStringArg(call) ?? "",
|
|
659
|
+
classification: "keyed",
|
|
660
|
+
// __reactra_inject_store_keyed__("X", key, { inputs } [, { fields: [...], typeOverride: "T" }])
|
|
661
|
+
key: call.arguments[1],
|
|
662
|
+
args: call.arguments[2],
|
|
663
|
+
fields: readFieldsArg(call.arguments[3]),
|
|
664
|
+
typeOverride: objStringProp(call.arguments[3], "typeOverride"),
|
|
665
|
+
call,
|
|
666
|
+
});
|
|
667
|
+
const buildErrorBoundary = (call) => {
|
|
668
|
+
const body = arrowArg(call, 0);
|
|
669
|
+
const paramNode = body.params[0];
|
|
670
|
+
const errorParam = paramNode && t.isIdentifier(paramNode) ? paramNode.name : "e";
|
|
671
|
+
return { kind: "error-boundary", errorParam, body, call };
|
|
672
|
+
};
|
|
673
|
+
const buildSuspense = (call) => ({
|
|
674
|
+
kind: "suspense",
|
|
675
|
+
body: arrowArg(call, 0),
|
|
676
|
+
call,
|
|
677
|
+
});
|
|
678
|
+
const buildInput = (source, call) => {
|
|
679
|
+
const name = firstStringArg(call) ?? "";
|
|
680
|
+
let requirement = "required";
|
|
681
|
+
let defaultExpr;
|
|
682
|
+
let defaultText;
|
|
683
|
+
const opts = call.arguments[1];
|
|
684
|
+
if (opts && t.isObjectExpression(opts)) {
|
|
685
|
+
for (const prop of opts.properties) {
|
|
686
|
+
if (!t.isObjectProperty(prop) || !t.isIdentifier(prop.key))
|
|
687
|
+
continue;
|
|
688
|
+
if (prop.key.name === "optional" && t.isBooleanLiteral(prop.value) && prop.value.value) {
|
|
689
|
+
requirement = "optional";
|
|
690
|
+
}
|
|
691
|
+
else if (prop.key.name === "default") {
|
|
692
|
+
requirement = "default";
|
|
693
|
+
defaultExpr = prop.value;
|
|
694
|
+
defaultText = sliceSource(source, prop.value);
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
}
|
|
698
|
+
// arg[1].tsType — the declared type string (Run 2 / D3), present only when the
|
|
699
|
+
// source annotated the input. Shadow-emitter only; Pass-9 ignores it.
|
|
700
|
+
const tsType = objStringProp(opts, "tsType");
|
|
701
|
+
return { kind: "input", name, requirement, defaultExpr, defaultText, tsType, call };
|
|
702
|
+
};
|
|
703
|
+
const buildPreserved = (call) => {
|
|
704
|
+
const fields = [];
|
|
705
|
+
for (const arg of call.arguments) {
|
|
706
|
+
if (t.isStringLiteral(arg))
|
|
707
|
+
fields.push(arg.value);
|
|
708
|
+
}
|
|
709
|
+
return { kind: "preserved", fields, call };
|
|
710
|
+
};
|
|
711
|
+
/**
|
|
712
|
+
* Day 23 / `#7b`: walk the component's view body for `__reactra_await__(...)`
|
|
713
|
+
* CallExpressions and append one `AwaitBlockNode` per match to
|
|
714
|
+
* `container.awaitBlocks`. Mirrors `scanAwaits` in pass-9-codegen but
|
|
715
|
+
* keeps the extracted data on the FileGraph so the sidecar builder
|
|
716
|
+
* (and future devtools/LSP consumers) can read it without
|
|
717
|
+
* re-traversing the view AST.
|
|
718
|
+
*
|
|
719
|
+
* `resourceName` is the text of the first arrow's body — typically a
|
|
720
|
+
* bare identifier (e.g. `"customer"`) but tolerates richer expressions
|
|
721
|
+
* (e.g. `"customer.data"`). `hasPending` / `hasError` mirror whether
|
|
722
|
+
* the optional 3rd / 4th arrow arguments are present (the preprocessor
|
|
723
|
+
* emits arrows for both unconditionally if the source declares them).
|
|
724
|
+
*/
|
|
725
|
+
const extractAwaitBlocks = (source, container) => {
|
|
726
|
+
if (!container.view)
|
|
727
|
+
return;
|
|
728
|
+
const viewBody = container.view.body.body;
|
|
729
|
+
// Wrap the view body's expression in a synthetic file so we can
|
|
730
|
+
// traverse it the same way pass-9-codegen does. (Direct traversal
|
|
731
|
+
// of a bare Expression isn't supported by @babel/traverse.)
|
|
732
|
+
traverse(t.file(t.program([t.expressionStatement(viewBody)])), {
|
|
733
|
+
CallExpression(path) {
|
|
734
|
+
const callee = path.node.callee;
|
|
735
|
+
if (!t.isIdentifier(callee) || callee.name !== "__reactra_await__")
|
|
736
|
+
return;
|
|
737
|
+
// §B1 MAJOR-2: index via the shared AWAIT_ARG_ORDER (this extract-side
|
|
738
|
+
// reader feeds `awaitBlocks` to devtools/LSP/sidecar — it must agree with
|
|
739
|
+
// the Pass-9 + shadow await wiring, not carry its own magic positions).
|
|
740
|
+
const args = path.node.arguments;
|
|
741
|
+
const resourceArrow = args[AWAIT_ARG_ORDER.resource];
|
|
742
|
+
const pendingArrow = args[AWAIT_ARG_ORDER.pending];
|
|
743
|
+
const errorArrow = args[AWAIT_ARG_ORDER.error];
|
|
744
|
+
if (!t.isArrowFunctionExpression(resourceArrow))
|
|
745
|
+
return;
|
|
746
|
+
// Resource expression text — the body of the first arrow.
|
|
747
|
+
// Slice from the original source via the node's start/end
|
|
748
|
+
// (sliceSource tolerates missing positions by returning "").
|
|
749
|
+
const resourceName = sliceSource(source, resourceArrow.body).trim();
|
|
750
|
+
if (resourceName.length === 0)
|
|
751
|
+
return;
|
|
752
|
+
const block = {
|
|
753
|
+
kind: "await-block",
|
|
754
|
+
resourceName,
|
|
755
|
+
hasPending: t.isArrowFunctionExpression(pendingArrow),
|
|
756
|
+
hasError: t.isArrowFunctionExpression(errorArrow),
|
|
757
|
+
};
|
|
758
|
+
container.awaitBlocks.push(block);
|
|
759
|
+
},
|
|
760
|
+
});
|
|
761
|
+
};
|
|
762
|
+
//# sourceMappingURL=pass-2-extract.js.map
|