Start of making globals and hook declarations configurable
Some refactoring to allow the environment options to be passed in from the outside.
Joe Savona committed
Feb 16, 2023 at 14:06 UTC
eece73262e134a1351d3069a3b79da014e078e46
6 files changed
+53
-12
compiler/forget/src/Babel/BabelPlugin.ts
+3
-3
@@ -7,12 +7,12 @@
7
8
/// <reference path="./plugin-syntax-jsx.d.ts" />
9
10
-import * as t from "@babel/types";
10
import type * as BabelCore from "@babel/core";
11
import generate from "@babel/generator";
12
import jsx from "@babel/plugin-syntax-jsx";
14
-import { CompilerFlags, parseCompilerFlags } from "../CompilerFlags";
13
+import * as t from "@babel/types";
14
import prettier from "prettier";
15
+import { CompilerFlags, parseCompilerFlags } from "../CompilerFlags";
16
import { compile } from "../CompilerPipeline";
17
18
type BabelPluginPass = {
@@ -47,7 +47,7 @@ export default function ReactForgetBabelPlugin(
47
if (fn.scope.getProgramParent() !== fn.scope.parent) {
48
return;
49
}
50
- const ast = compile(fn);
50
+ const ast = compile(fn, null);
51
52
// We are generating a new FunctionDeclaration node, so we must skip over it or this
53
// traversal will loop infinitely.
compiler/forget/src/CompilerPipeline.ts
+9
-4
@@ -12,6 +12,7 @@ import {
12
mergeConsecutiveBlocks,
13
ReactiveFunction,
14
} from "./HIR";
15
+import { EnvironmentOptions } from "./HIR/Environment";
16
import {
17
analyseFunctions,
18
dropMemoCalls,
@@ -47,9 +48,10 @@ export type CompilerPipelineValue =
48
| { kind: "reactive"; name: string; value: ReactiveFunction };
49
50
export function* run(
50
- func: NodePath<t.FunctionDeclaration>
51
+ func: NodePath<t.FunctionDeclaration>,
52
+ options?: EnvironmentOptions | null
53
): Generator<CompilerPipelineValue, t.Function> {
52
- const hir = lower(func).unwrap();
54
+ const hir = lower(func, options ?? null).unwrap();
55
yield log({ kind: "hir", name: "HIR", value: hir });
56
57
mergeConsecutiveBlocks(hir);
@@ -186,8 +188,11 @@ export function* run(
188
return ast;
189
}
190
189
-export function compile(func: NodePath<t.FunctionDeclaration>): t.Function {
190
- let generator = run(func);
191
+export function compile(
192
+ func: NodePath<t.FunctionDeclaration>,
193
+ options?: EnvironmentOptions | null
194
+): t.Function {
195
+ let generator = run(func, options);
196
while (true) {
197
const next = generator.next();
198
if (next.done) {
compiler/forget/src/HIR/BuildHIR.ts
+4
-3
@@ -11,7 +11,7 @@ import invariant from "invariant";
11
import { CompilerError, ErrorSeverity } from "../CompilerError";
12
import { Err, Ok, Result } from "../Utils/Result";
13
import { assertExhaustive } from "../Utils/utils";
14
-import { Environment } from "./Environment";
14
+import { Environment, EnvironmentOptions } from "./Environment";
15
import {
16
BlockId,
17
BranchTerminal,
@@ -51,9 +51,10 @@ import HIRBuilder from "./HIRBuilder";
51
*/
52
export function lower(
53
func: NodePath<t.Function>,
54
+ options: EnvironmentOptions | null,
55
capturedRefs: t.Identifier[] = []
56
): Result<HIRFunction, CompilerError> {
56
- const env = new Environment();
57
+ const env = new Environment(options);
58
const builder = new HIRBuilder(env, capturedRefs);
59
const context: Place[] = [];
60
@@ -1334,7 +1335,7 @@ function lowerExpression(
1335
//
1336
// This isn't a problem in practice because use Babel's scope analysis to
1337
// identify the correct references.
1337
- const lowering = lower(expr, [
1338
+ const lowering = lower(expr, builder.environment.options, [
1339
...builder.context,
1340
...captured.identifiers,
1341
]);
compiler/forget/src/HIR/Environment.ts
+31
@@ -1,9 +1,40 @@
1
import { IdentifierId, makeIdentifierId } from "./HIR";
2
+import { BUILTIN_HOOKS, Hook } from "./Hooks";
3
+
4
+const HOOK_PATTERN = /^_?use/;
5
+
6
+export type EnvironmentOptions = {
7
+ customHooks: Map<string, Hook>;
8
+};
9
+
10
+const DEFAULT_OPTIONS: EnvironmentOptions = {
11
+ customHooks: new Map(),
12
+};
13
14
export class Environment {
15
+ #options: EnvironmentOptions;
16
#nextIdentifer: number = 0;
17
18
+ constructor(options: EnvironmentOptions | null) {
19
+ this.#options = options ?? DEFAULT_OPTIONS;
20
+ }
21
+
22
+ get options(): EnvironmentOptions {
23
+ return this.#options;
24
+ }
25
+
26
get nextIdentifierId(): IdentifierId {
27
return makeIdentifierId(this.#nextIdentifer++);
28
}
29
+
30
+ getHookDeclaration(name: string): Hook | null {
31
+ if (!name.match(HOOK_PATTERN)) {
32
+ return null;
33
+ }
34
+ const hook = BUILTIN_HOOKS.get(name) ?? this.#options.customHooks.get(name);
35
+ if (hook !== undefined) {
36
+ return hook;
37
+ }
38
+ return null;
39
+ }
40
}
compiler/forget/src/HIR/HIRBuilder.ts
+4
@@ -93,6 +93,10 @@ export default class HIRBuilder {
93
return this.#context;
94
}
95
96
+ get environment(): Environment {
97
+ return this.#env;
98
+ }
99
+
100
constructor(env: Environment, context: t.Identifier[]) {
101
this.#env = env;
102
this.#context = context;
compiler/forget/src/HIR/Hooks.ts
+2
-2
@@ -7,7 +7,7 @@
7
8
import { Effect, Place, ValueKind } from "./HIR";
9
10
-export const HOOKS: Map<string, Hook> = new Map([
10
+export const BUILTIN_HOOKS: Map<string, Hook> = new Map([
11
[
12
"useState",
13
{
@@ -68,7 +68,7 @@ export function parseHookCall(place: Place): Hook | null {
68
if (name === null || !name.match(/^_?use/)) {
69
return null;
70
}
71
- const hook = HOOKS.get(name);
71
+ const hook = BUILTIN_HOOKS.get(name);
72
if (hook != null) {
73
return hook;
74
}