diff --git a/compiler/forget/src/Babel/BabelPlugin.ts b/compiler/forget/src/Babel/BabelPlugin.ts
index bf5f0a86ab..59a70c4bc6 100644
--- a/compiler/forget/src/Babel/BabelPlugin.ts
+++ b/compiler/forget/src/Babel/BabelPlugin.ts
@@ -7,12 +7,12 @@
///
-import * as t from "@babel/types";
import type * as BabelCore from "@babel/core";
import generate from "@babel/generator";
import jsx from "@babel/plugin-syntax-jsx";
-import { CompilerFlags, parseCompilerFlags } from "../CompilerFlags";
+import * as t from "@babel/types";
import prettier from "prettier";
+import { CompilerFlags, parseCompilerFlags } from "../CompilerFlags";
import { compile } from "../CompilerPipeline";
type BabelPluginPass = {
@@ -47,7 +47,7 @@ export default function ReactForgetBabelPlugin(
if (fn.scope.getProgramParent() !== fn.scope.parent) {
return;
}
- const ast = compile(fn);
+ const ast = compile(fn, null);
// We are generating a new FunctionDeclaration node, so we must skip over it or this
// traversal will loop infinitely.
diff --git a/compiler/forget/src/CompilerPipeline.ts b/compiler/forget/src/CompilerPipeline.ts
index 5f7c8b93d6..829fa3a16e 100644
--- a/compiler/forget/src/CompilerPipeline.ts
+++ b/compiler/forget/src/CompilerPipeline.ts
@@ -12,6 +12,7 @@ import {
mergeConsecutiveBlocks,
ReactiveFunction,
} from "./HIR";
+import { EnvironmentOptions } from "./HIR/Environment";
import {
analyseFunctions,
dropMemoCalls,
@@ -47,9 +48,10 @@ export type CompilerPipelineValue =
| { kind: "reactive"; name: string; value: ReactiveFunction };
export function* run(
- func: NodePath
+ func: NodePath,
+ options?: EnvironmentOptions | null
): Generator {
- const hir = lower(func).unwrap();
+ const hir = lower(func, options ?? null).unwrap();
yield log({ kind: "hir", name: "HIR", value: hir });
mergeConsecutiveBlocks(hir);
@@ -186,8 +188,11 @@ export function* run(
return ast;
}
-export function compile(func: NodePath): t.Function {
- let generator = run(func);
+export function compile(
+ func: NodePath,
+ options?: EnvironmentOptions | null
+): t.Function {
+ let generator = run(func, options);
while (true) {
const next = generator.next();
if (next.done) {
diff --git a/compiler/forget/src/HIR/BuildHIR.ts b/compiler/forget/src/HIR/BuildHIR.ts
index b2827a535f..05eb6b3585 100644
--- a/compiler/forget/src/HIR/BuildHIR.ts
+++ b/compiler/forget/src/HIR/BuildHIR.ts
@@ -11,7 +11,7 @@ import invariant from "invariant";
import { CompilerError, ErrorSeverity } from "../CompilerError";
import { Err, Ok, Result } from "../Utils/Result";
import { assertExhaustive } from "../Utils/utils";
-import { Environment } from "./Environment";
+import { Environment, EnvironmentOptions } from "./Environment";
import {
BlockId,
BranchTerminal,
@@ -51,9 +51,10 @@ import HIRBuilder from "./HIRBuilder";
*/
export function lower(
func: NodePath,
+ options: EnvironmentOptions | null,
capturedRefs: t.Identifier[] = []
): Result {
- const env = new Environment();
+ const env = new Environment(options);
const builder = new HIRBuilder(env, capturedRefs);
const context: Place[] = [];
@@ -1334,7 +1335,7 @@ function lowerExpression(
//
// This isn't a problem in practice because use Babel's scope analysis to
// identify the correct references.
- const lowering = lower(expr, [
+ const lowering = lower(expr, builder.environment.options, [
...builder.context,
...captured.identifiers,
]);
diff --git a/compiler/forget/src/HIR/Environment.ts b/compiler/forget/src/HIR/Environment.ts
index 4b72b43037..23518271d9 100644
--- a/compiler/forget/src/HIR/Environment.ts
+++ b/compiler/forget/src/HIR/Environment.ts
@@ -1,9 +1,40 @@
import { IdentifierId, makeIdentifierId } from "./HIR";
+import { BUILTIN_HOOKS, Hook } from "./Hooks";
+
+const HOOK_PATTERN = /^_?use/;
+
+export type EnvironmentOptions = {
+ customHooks: Map;
+};
+
+const DEFAULT_OPTIONS: EnvironmentOptions = {
+ customHooks: new Map(),
+};
export class Environment {
+ #options: EnvironmentOptions;
#nextIdentifer: number = 0;
+ constructor(options: EnvironmentOptions | null) {
+ this.#options = options ?? DEFAULT_OPTIONS;
+ }
+
+ get options(): EnvironmentOptions {
+ return this.#options;
+ }
+
get nextIdentifierId(): IdentifierId {
return makeIdentifierId(this.#nextIdentifer++);
}
+
+ getHookDeclaration(name: string): Hook | null {
+ if (!name.match(HOOK_PATTERN)) {
+ return null;
+ }
+ const hook = BUILTIN_HOOKS.get(name) ?? this.#options.customHooks.get(name);
+ if (hook !== undefined) {
+ return hook;
+ }
+ return null;
+ }
}
diff --git a/compiler/forget/src/HIR/HIRBuilder.ts b/compiler/forget/src/HIR/HIRBuilder.ts
index 58c8372870..8aac83fe6f 100644
--- a/compiler/forget/src/HIR/HIRBuilder.ts
+++ b/compiler/forget/src/HIR/HIRBuilder.ts
@@ -93,6 +93,10 @@ export default class HIRBuilder {
return this.#context;
}
+ get environment(): Environment {
+ return this.#env;
+ }
+
constructor(env: Environment, context: t.Identifier[]) {
this.#env = env;
this.#context = context;
diff --git a/compiler/forget/src/HIR/Hooks.ts b/compiler/forget/src/HIR/Hooks.ts
index 59e4d81339..916560fbac 100644
--- a/compiler/forget/src/HIR/Hooks.ts
+++ b/compiler/forget/src/HIR/Hooks.ts
@@ -7,7 +7,7 @@
import { Effect, Place, ValueKind } from "./HIR";
-export const HOOKS: Map = new Map([
+export const BUILTIN_HOOKS: Map = new Map([
[
"useState",
{
@@ -68,7 +68,7 @@ export function parseHookCall(place: Place): Hook | null {
if (name === null || !name.match(/^_?use/)) {
return null;
}
- const hook = HOOKS.get(name);
+ const hook = BUILTIN_HOOKS.get(name);
if (hook != null) {
return hook;
}