13
CompilerSuggestionOperation,
14
ErrorSeverity,
15
} from "../CompilerError";
16
-import { GeneratedSource } from "../HIR";
16
+import { CodegenFunction } from "../ReactiveScopes";
17
import { isComponentDeclaration } from "../Utils/ComponentDeclaration";
18
import { assertExhaustive } from "../Utils/utils";
19
import { insertGatedFunctionDeclaration } from "./Gating";
60
* Returns a boolean denoting if the AST was mutated or not.
61
*/
62
function compileAndInsertNewFunctionDeclaration(
63
- fnPath: NodePath<t.FunctionDeclaration>,
63
+ fnPath: NodePath<
64
+ t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
65
+ >,
66
pass: CompilerPass
67
): boolean {
66
- let compiledFn: t.FunctionDeclaration | null = null;
68
+ let compiledFn: CodegenFunction | null = null;
69
let hasForgetMutatedOriginalSource = false;
70
try {
71
compiledFn = compileFn(fnPath, pass.opts.environment);
100
// traversal will loop infinitely.
101
fnPath.skip();
102
101
- CompilerError.invariant(fnPath.node.id != null, {
102
- reason: "FunctionDeclaration must have a name",
103
- description: null,
104
- loc: fnPath.node.loc ?? GeneratedSource,
105
- suggestions: null,
106
- });
107
- const originalIdent = fnPath.node.id;
103
+ let transformedFunction;
104
+ switch (fnPath.node.type) {
105
+ case "FunctionDeclaration": {
106
+ const fn: t.FunctionDeclaration = {
107
+ type: "FunctionDeclaration",
108
+ id: compiledFn.id,
109
+ loc: fnPath.node.loc ?? null,
110
+ async: compiledFn.async,
111
+ generator: compiledFn.generator,
112
+ params: compiledFn.params,
113
+ body: compiledFn.body,
114
+ };
115
+ transformedFunction = fn;
116
+ break;
117
+ }
118
+ case "ArrowFunctionExpression": {
119
+ const fn: t.ArrowFunctionExpression = {
120
+ type: "ArrowFunctionExpression",
121
+ loc: fnPath.node.loc ?? null,
122
+ async: compiledFn.async,
123
+ generator: compiledFn.generator,
124
+ params: compiledFn.params,
125
+ expression: fnPath.node.expression,
126
+ body: compiledFn.body,
127
+ };
128
+ transformedFunction = fn;
129
+ break;
130
+ }
131
+ case "FunctionExpression": {
132
+ const fn: t.FunctionExpression = {
133
+ type: "FunctionExpression",
134
+ id: compiledFn.id,
135
+ loc: fnPath.node.loc ?? null,
136
+ async: compiledFn.async,
137
+ generator: compiledFn.generator,
138
+ params: compiledFn.params,
139
+ body: compiledFn.body,
140
+ };
141
+ transformedFunction = fn;
142
+ break;
143
+ }
144
+ }
145
109
- insertNewFunctionDeclaration(fnPath, originalIdent, compiledFn, pass);
146
+ insertNewFunctionDeclaration(fnPath, transformedFunction, pass);
147
hasForgetMutatedOriginalSource = true;
148
}
149
151
}
152
153
function insertNewFunctionDeclaration(
117
- fnPath: NodePath<t.FunctionDeclaration>,
118
- originalIdent: t.Identifier,
119
- compiledFn: t.FunctionDeclaration,
154
+ fnPath: NodePath<
155
+ t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
156
+ >,
157
+ compiledFn:
158
+ | t.FunctionDeclaration
159
+ | t.ArrowFunctionExpression
160
+ | t.FunctionExpression,
161
pass: CompilerPass
162
): void {
122
- let gatedFn = null;
123
- if (pass.opts.gating != null) {
124
- gatedFn = insertGatedFunctionDeclaration(
125
- fnPath,
126
- compiledFn,
127
- originalIdent,
128
- pass.opts.gating
129
- );
130
- } else {
131
- fnPath.replaceWith(compiledFn);
132
- }
133
-
163
if (pass.opts.instrumentForget != null) {
164
const instrumentFnName = pass.opts.instrumentForget.importSpecifierName;
136
- addInstrumentForget(fnPath, originalIdent.name, instrumentFnName);
165
+ addInstrumentForget(compiledFn, instrumentFnName);
166
+ }
167
+ if (pass.opts)
168
if (pass.opts.gating != null) {
138
- CompilerError.invariant(gatedFn != null, {
139
- reason: "Should have inserted a gated function declaration",
140
- description: null,
141
- loc: null,
142
- suggestions: null,
143
- });
144
- addInstrumentForget(gatedFn, originalIdent.name, instrumentFnName);
169
+ if (pass.opts.instrumentForget != null) {
170
+ const instrumentFnName = pass.opts.instrumentForget.importSpecifierName;
171
+ addInstrumentForget(fnPath.node, instrumentFnName);
172
+ }
173
+ insertGatedFunctionDeclaration(fnPath, compiledFn, pass.opts.gating);
174
+ } else {
175
+ fnPath.replaceWith(compiledFn);
176
}
146
- }
177
}
178
179
export function compileProgram(
273
return;
274
}
275
246
- const loweredFn = buildFunctionDeclaration(fn);
247
- if (loweredFn instanceof CompilerErrorDetail) {
248
- const error = new CompilerError();
249
- error.pushErrorDetail(loweredFn);
250
-
251
- const options = parsePluginOptions(pass.opts);
252
- if (options.logger != null) {
253
- options.logger.logEvent("err", error);
254
- }
255
-
256
- if (options.panicOnBailout || error.isCritical()) {
257
- throw error;
258
- } else {
259
- if (pass.opts.isDev) {
260
- log(error, pass.filename);
261
- }
262
- }
263
- return;
264
- }
265
-
266
- if (compileAndInsertNewFunctionDeclaration(loweredFn, pass) === true) {
276
+ if (compileAndInsertNewFunctionDeclaration(fn, pass) === true) {
277
hasForgetMutatedOriginalSource = true;
278
}
279
},
322
}
323
324
function shouldVisitNode(
315
- fn: NodePath<t.FunctionDeclaration | t.ArrowFunctionExpression>,
325
+ fn: NodePath<
326
+ t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
327
+ >,
328
pass: CompilerPass
329
): boolean {
330
if (fn.node.body.type === "BlockStatement") {
374
);
375
}
376
365
-function buildFunctionDeclaration(
366
- fn: NodePath<t.ArrowFunctionExpression>
367
-): NodePath<t.FunctionDeclaration> | CompilerErrorDetail {
368
- if (!fn.parentPath.isVariableDeclarator()) {
369
- return new CompilerErrorDetail({
370
- reason:
371
- "ArrowFunctionExpression was not declared in a variable declaration",
372
- severity: ErrorSeverity.Todo,
373
- description: `Handle ${fn.parentPath.type}`,
374
- loc: fn.node.loc ?? null,
375
- suggestions: null,
376
- });
377
- }
378
- const variableDeclarator = fn.parentPath;
379
-
380
- if (!variableDeclarator.parentPath.isVariableDeclaration()) {
381
- return new CompilerErrorDetail({
382
- reason: "ArrowFunctionExpression was not a single declaration",
383
- severity: ErrorSeverity.Todo,
384
- description: `Handle ${variableDeclarator.parentPath.type}`,
385
- loc: fn.node.loc ?? null,
386
- suggestions: null,
387
- });
388
- }
389
- const variableDeclaration = variableDeclarator.parentPath;
390
-
391
- const id = variableDeclarator.get("id");
392
- if (!id.isIdentifier()) {
393
- return new CompilerErrorDetail({
394
- reason: "ArrowFunctionExpression was not an identifier",
395
- severity: ErrorSeverity.Todo,
396
- description: `Handle ${id.type}`,
397
- loc: fn.node.loc ?? null,
398
- suggestions: null,
399
- });
400
- }
401
-
402
- const rewrittenFn = variableDeclaration.replaceWith(
403
- t.functionDeclaration(
404
- id.node,
405
- fn.node.params,
406
- buildBlockStatement(fn),
407
- fn.node.generator,
408
- fn.node.async
409
- )
410
- )[0];
411
- fn.skip();
412
- return rewrittenFn;
413
-}
414
-
415
-function buildBlockStatement(
416
- fn: NodePath<t.ArrowFunctionExpression>
417
-): t.BlockStatement {
418
- const body = fn.get("body");
419
- if (body.isExpression()) {
420
- const wrappedBody = body.replaceWith(
421
- t.blockStatement([t.returnStatement(body.node)])
422
- )[0];
423
- body.skip();
424
-
425
- return wrappedBody.node;
426
- }
427
-
428
- CompilerError.invariant(body.isBlockStatement(), {
429
- reason: "Body must be a BlockStatement",
430
- description: null,
431
- loc: body.node.loc ?? GeneratedSource,
432
- suggestions: null,
433
- });
434
-
435
- return body.node;
436
-}
437
-
377
function isHookName(s: string): boolean {
378
return /^use[A-Z0-9]/.test(s);
379
}
450
}
451
452
function isReactFunctionLike(
514
- node: NodePath<t.FunctionDeclaration | t.ArrowFunctionExpression>
453
+ node: NodePath<
454
+ t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
455
+ >
456
): boolean {
457
const functionName = getFunctionName(node);
458
if (functionName !== null) {
495
*/
496
497
function getFunctionName(
557
- path: NodePath<t.FunctionDeclaration | t.ArrowFunctionExpression>
498
+ path: NodePath<
499
+ t.FunctionDeclaration | t.ArrowFunctionExpression | t.FunctionExpression
500
+ >
501
): NodePath<t.Expression> | null {
502
if (path.isFunctionDeclaration()) {
503
const id = path.get("id");