diff --git a/compiler/forget/src/Babel/BabelPlugin.ts b/compiler/forget/src/Babel/BabelPlugin.ts index 10f2122749..005d0f85ff 100644 --- a/compiler/forget/src/Babel/BabelPlugin.ts +++ b/compiler/forget/src/Babel/BabelPlugin.ts @@ -7,40 +7,9 @@ /// -import type * as BabelCore from "@babel/core"; import jsx from "@babel/plugin-syntax-jsx"; -import * as t from "@babel/types"; -import { - CompilerError, - CompilerErrorDetail, - ErrorSeverity, -} from "../CompilerError"; -import { compile } from "../CompilerPipeline"; -import { GeneratedSource } from "../HIR"; -import { addInstrumentForget } from "./InstrumentForgetBabelPlugin"; -import { - ExternalFunction, - PluginOptions, - parsePluginOptions, -} from "./PluginOptions"; - -type BabelPluginPass = { - opts: PluginOptions; - filename: string | null; -}; - -function hasUseForgetDirective(directive: t.Directive): boolean { - return directive.value.value === "use forget"; -} - -function hasAnyUseForgetDirectives(directives: t.Directive[]): boolean { - for (const directive of directives) { - if (hasUseForgetDirective(directive)) { - return true; - } - } - return false; -} +import type * as BabelCore from "@babel/core"; +import { compileProgram } from "../CompilerEntrypoint"; /** * The React Forget Babel Plugin @@ -50,150 +19,6 @@ function hasAnyUseForgetDirectives(directives: t.Directive[]): boolean { export default function ReactForgetBabelPlugin( _babel: typeof BabelCore ): BabelCore.PluginObj { - let hasForgetCompiledCode: boolean = false; - - function visitFn( - fn: BabelCore.NodePath, - pass: BabelPluginPass - ): void { - try { - const compiled = compile(fn, pass.opts.environment); - if (fn.node.id == null) { - CompilerError.invariant( - "FunctionDeclaration must have a name", - fn.node.loc ?? GeneratedSource - ); - } - const originalIdent = fn.node.id; - - if (pass.opts.gating != null) { - // Rename existing function - fn.node.id = addSuffix(fn.node.id, "_uncompiled"); - - // Rename and append compiled function - if (compiled.id == null) { - CompilerError.invariant( - "FunctionDeclaration must produce a name", - fn.node.loc ?? GeneratedSource - ); - } - compiled.id = addSuffix(compiled.id, "_forget"); - const compiledFn = fn.insertAfter(compiled)[0]; - compiledFn.skip(); - - // Build and append gating test - compiledFn.insertAfter( - buildGatingTest({ - originalFnDecl: fn, - compiledIdent: compiled.id, - originalIdent, - gating: pass.opts.gating, - }) - ); - if (pass.opts.instrumentForget != null) { - const gatingIdentifierName = - pass.opts.instrumentForget.gating.importSpecifierName; - const instrumentFnName = - pass.opts.instrumentForget.instrumentFn.importSpecifierName; - addInstrumentForget( - fn, - originalIdent.name, - gatingIdentifierName, - instrumentFnName - ); - addInstrumentForget( - compiledFn, - originalIdent.name, - gatingIdentifierName, - instrumentFnName - ); - } - } else { - fn.replaceWith(compiled); - if (pass.opts.instrumentForget != null) { - const gatingIdentifierName = - pass.opts.instrumentForget.gating.importSpecifierName; - const instrumentFnName = - pass.opts.instrumentForget.instrumentFn.importSpecifierName; - addInstrumentForget( - fn, - originalIdent.name, - gatingIdentifierName, - instrumentFnName - ); - } - } - - hasForgetCompiledCode = true; - } catch (err) { - if (pass.opts.logger && err) { - pass.opts.logger.logEvent("err", err); - } - /** Always throw if the flag is enabled, otherwise we only throw if the error is critical - * (eg an invariant is broken, meaning the compiler may be buggy). See - * {@link CompilerError.isCritical} for mappings. - * */ - if ( - pass.opts.panicOnBailout || - !(err instanceof CompilerError) || - (err instanceof CompilerError && err.isCritical()) - ) { - throw err; - } else { - if (pass.opts.isDev) { - log(err, pass.filename ?? null); - } - } - } finally { - // We are generating a new FunctionDeclaration node, so we must skip over it or this - // traversal will loop infinitely. - fn.skip(); - } - } - - const visitor = { - FunctionDeclaration( - fn: BabelCore.NodePath, - pass: BabelPluginPass - ): void { - if (!shouldCompile(fn, pass)) { - return; - } - - visitFn(fn, pass); - }, - - ArrowFunctionExpression( - fn: BabelCore.NodePath, - pass: BabelPluginPass - ): void { - if (!shouldCompile(fn, pass)) { - return; - } - - const loweredFn = buildFunctionDeclaration(fn); - if (loweredFn instanceof CompilerError) { - const error = loweredFn; - - const options = parsePluginOptions(pass.opts); - if (options.logger != null) { - options.logger.logEvent("err", error); - } - - if (options.panicOnBailout || error.isCritical()) { - throw error; - } else { - if (pass.opts.isDev) { - log(error, pass.filename); - } - } - return; - } - - visitFn(loweredFn, pass); - }, - }; - return { name: "react-forget", inherits: jsx, @@ -201,347 +26,9 @@ export default function ReactForgetBabelPlugin( // Note: Babel does some "smart" merging of visitors across plugins, so even if A is inserted // prior to B, if A does not have a Program visitor and B does, B will run first. We always // want Forget to run true to source as possible. - Program(path, pass): void { - const options = parsePluginOptions(pass.opts); - - const violations = []; - const fileComments = pass.file.ast.comments; - let fileHasUseForgetDirective = false; - if (Array.isArray(fileComments)) { - for (const comment of fileComments) { - if ( - /eslint-disable(-next-line)? react-hooks\/(exhaustive-deps|rules-of-hooks)/.test( - comment.value - ) - ) { - violations.push(comment); - } - } - } - - if (violations.length > 0) { - path.traverse({ - Directive(path) { - if (hasUseForgetDirective(path.node)) { - fileHasUseForgetDirective = true; - } - }, - }); - - const reason = `One or more React eslint rules is disabled`; - const error = new CompilerError(); - for (const violation of violations) { - if (options.logger != null) { - options.logger.logEvent("err", { - reason, - filename: pass.filename, - violation, - }); - } - - error.pushErrorDetail( - new CompilerErrorDetail({ - reason, - description: violation.value.trim(), - severity: ErrorSeverity.UnsafeInput, - codeframe: null, - loc: violation.loc ?? null, - }) - ); - } - - if (fileHasUseForgetDirective) { - if (options.panicOnBailout || error.isCritical()) { - throw error; - } else { - if (options.isDev) { - log(error, pass.filename ?? null); - } - } - } - - return; - } - - path.traverse(visitor, { - ...pass, - opts: { ...pass.opts, ...options }, - filename: pass.filename ?? null, - }); - - // If there isn't already an import of * as React, insert it so useMemoCache doesn't - // throw - if (hasForgetCompiledCode) { - let didInsertUseMemoCache = false; - let hasExistingReactImport = false; - path.traverse({ - CallExpression(callExprPath) { - const callee = callExprPath.get("callee"); - const args = callExprPath.get("arguments"); - if ( - callee.isIdentifier() && - callee.node.name === "useMemoCache" && - args.length === 1 && - args[0].isNumericLiteral() - ) { - didInsertUseMemoCache = true; - } - }, - ImportDeclaration(importDeclPath) { - if (isNonNamespacedImportOfReact(importDeclPath)) { - hasExistingReactImport = true; - } - }, - }); - // If Forget did successfully compile inject/update an import of - // `import {unstable_useMemoCache as useMemoCache} from 'react'` and rename - // `React.unstable_useMemoCache(n)` to `useMemoCache(n)`; - if (didInsertUseMemoCache) { - if (hasExistingReactImport) { - let didUpdateImport = false; - path.traverse({ - ImportDeclaration(importDeclPath) { - if (isNonNamespacedImportOfReact(importDeclPath)) { - importDeclPath.pushContainer( - "specifiers", - t.importSpecifier( - t.identifier("useMemoCache"), - t.identifier("unstable_useMemoCache") - ) - ); - didUpdateImport = true; - } - }, - }); - if (didUpdateImport === false) { - throw new Error( - "Expected an ImportDeclaration of react in order to update ImportSpecifiers with useMemoCache" - ); - } - } else { - path.unshiftContainer( - "body", - t.importDeclaration( - [ - t.importSpecifier( - t.identifier("useMemoCache"), - t.identifier("unstable_useMemoCache") - ), - ], - t.stringLiteral("react") - ) - ); - } - } - // TODO: check for duplicate import specifiers - if (options.gating != null) { - path.unshiftContainer( - "body", - buildImportForExternalFunction(options.gating) - ); - } - if (options.instrumentForget != null) { - path.unshiftContainer( - "body", - buildImportForExternalFunction(options.instrumentForget.gating) - ); - path.unshiftContainer( - "body", - buildImportForExternalFunction( - options.instrumentForget.instrumentFn - ) - ); - } - } + Program(prog, pass): void { + compileProgram(prog, pass); }, }, }; } - -function shouldCompile( - fn: BabelCore.NodePath, - pass: BabelPluginPass -): boolean { - if (pass.opts.enableOnlyOnUseForgetDirective) { - const body = fn.get("body"); - if (!body.isBlockStatement()) { - return false; - } - if (!hasAnyUseForgetDirectives(body.node.directives)) { - return false; - } - } - - if (fn.scope.getProgramParent() !== fn.scope.parent) { - return false; - } - - return true; -} - -function log(error: CompilerError, filename: string | null): void { - const filenameStr = filename ? `in ${filename}` : ""; - console.log( - error.details - .map( - (e) => - `[ReactForget] Skipping compilation of component ${filenameStr}: ${e.printErrorMessage()}` - ) - .join("\n") - ); -} - -function makeError( - reason: string, - loc: t.SourceLocation | null -): CompilerError { - const error = new CompilerError(); - error.pushErrorDetail( - new CompilerErrorDetail({ - reason, - description: null, - severity: ErrorSeverity.InvalidInput, - codeframe: null, - loc, - }) - ); - return error; -} - -function buildFunctionDeclaration( - fn: BabelCore.NodePath -): BabelCore.NodePath | CompilerError { - if (!fn.parentPath.isVariableDeclarator()) { - return makeError( - "ArrowFunctionExpression must be declared in variable declaration", - fn.node.loc ?? null - ); - } - const variableDeclarator = fn.parentPath; - - if (!variableDeclarator.parentPath.isVariableDeclaration()) { - return makeError( - "ArrowFunctionExpression must be a single declaration", - fn.node.loc ?? null - ); - } - const variableDeclaration = variableDeclarator.parentPath; - - const id = variableDeclarator.get("id"); - if (!id.isIdentifier()) { - return makeError( - "ArrowFunctionExpression must have an id", - fn.node.loc ?? null - ); - } - - const rewrittenFn = variableDeclaration.replaceWith( - t.functionDeclaration( - id.node, - fn.node.params, - buildBlockStatement(fn), - fn.node.generator, - fn.node.async - ) - )[0]; - fn.skip(); - return rewrittenFn; -} - -function buildBlockStatement( - fn: BabelCore.NodePath -): t.BlockStatement { - const body = fn.get("body"); - if (body.isExpression()) { - const wrappedBody = body.replaceWith( - t.blockStatement([t.returnStatement(body.node)]) - )[0]; - body.skip(); - - return wrappedBody.node; - } - - if (!body.isBlockStatement()) { - CompilerError.invariant( - "Body must be a BlockStatement", - body.node.loc ?? GeneratedSource - ); - } - return body.node; -} - -type GatingTestOptions = { - originalFnDecl: BabelCore.NodePath; - compiledIdent: t.Identifier; - originalIdent: t.Identifier; - gating: ExternalFunction; -}; -function buildGatingTest({ - originalFnDecl, - compiledIdent, - originalIdent, - gating, -}: GatingTestOptions): t.Node | t.Node[] { - const testVarDecl = t.variableDeclaration("const", [ - t.variableDeclarator( - originalIdent, - t.conditionalExpression( - t.callExpression(buildSpecifierIdent(gating), []), - compiledIdent, - originalFnDecl.node.id! - ) - ), - ]); - - // Re-export new declaration - const parent = originalFnDecl.parentPath; - if (t.isExportDefaultDeclaration(parent)) { - // Re-add uncompiled function - parent.replaceWith(originalFnDecl)[0].skip(); - - // Add test and synthesize new export - return [testVarDecl, t.exportDefaultDeclaration(originalIdent)]; - } else if (t.isExportNamedDeclaration(parent)) { - // Re-add uncompiled function - parent.replaceWith(originalFnDecl)[0].skip(); - - // Add and export test - return t.exportNamedDeclaration(testVarDecl); - } - - // Just add the test, no need for re-export - return testVarDecl; -} - -function addSuffix(id: t.Identifier, suffix: string): t.Identifier { - return t.identifier(`${id.name}${suffix}`); -} - -function buildImportForExternalFunction( - gating: ExternalFunction -): t.ImportDeclaration { - const specifierIdent = buildSpecifierIdent(gating); - return t.importDeclaration( - [t.importSpecifier(specifierIdent, specifierIdent)], - t.stringLiteral(gating.source) - ); -} - -function buildSpecifierIdent(gating: ExternalFunction): t.Identifier { - return t.identifier(gating.importSpecifierName); -} - -/** - * Matches `import { ... } from 'react';` - * but not `import * as React from 'react';` - */ -function isNonNamespacedImportOfReact( - importDeclPath: BabelCore.NodePath -): boolean { - return ( - importDeclPath.get("source").node.value === "react" && - importDeclPath - .get("specifiers") - .every((specifier) => specifier.isImportSpecifier()) - ); -} diff --git a/compiler/forget/src/Babel/InstrumentForgetBabelPlugin.ts b/compiler/forget/src/Babel/InstrumentForgetBabelPlugin.ts deleted file mode 100644 index 12c79d62bf..0000000000 --- a/compiler/forget/src/Babel/InstrumentForgetBabelPlugin.ts +++ /dev/null @@ -1,34 +0,0 @@ -/** - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -import { NodePath } from "@babel/core"; -import * as t from "@babel/types"; -import "./BabelPlugin"; - -export function addInstrumentForget( - fn: NodePath, - fnName: string, - gatingIdentifierName: string, - instrumentFnName: string -): void { - const fnBody = fn.get("body"); - // Technically, this is a conditional hook call. However, we expect - // __DEV__ and gatingIdentifier to be runtime constants - const testExpr: t.Node = t.ifStatement( - t.logicalExpression( - "&&", - t.identifier("__DEV__"), - t.identifier(gatingIdentifierName) - ), - t.expressionStatement( - t.callExpression(t.identifier(instrumentFnName), [ - t.stringLiteral(fnName), - ]) - ) - ); - fnBody.unshiftContainer("body", testExpr); -} diff --git a/compiler/forget/src/Babel/RunReactForgetBabelPlugin.ts b/compiler/forget/src/Babel/RunReactForgetBabelPlugin.ts index b904fad9a2..9feb00a767 100644 --- a/compiler/forget/src/Babel/RunReactForgetBabelPlugin.ts +++ b/compiler/forget/src/Babel/RunReactForgetBabelPlugin.ts @@ -11,7 +11,7 @@ import * as parser from "@babel/parser"; import invariant from "invariant"; import prettier from "prettier"; import ReactForgetBabelPlugin from "./BabelPlugin"; -import { PluginOptions } from "./PluginOptions"; +import type { PluginOptions } from "../CompilerOptions"; type ReactForgetBabelPluginResult = { ast: BabelCore.BabelFileResult["ast"]; diff --git a/compiler/forget/src/CompilerEntrypoint.ts b/compiler/forget/src/CompilerEntrypoint.ts index 217a0ebebc..e64c25fe91 100644 --- a/compiler/forget/src/CompilerEntrypoint.ts +++ b/compiler/forget/src/CompilerEntrypoint.ts @@ -5,8 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import type * as BabelCore from "@babel/core"; -import jsx from "@babel/plugin-syntax-jsx"; +import { NodePath, PluginPass } from "@babel/core"; import * as t from "@babel/types"; import { CompilerError, @@ -20,7 +19,7 @@ import { ExternalFunction, PluginOptions, parsePluginOptions, -} from "./Babel/PluginOptions"; +} from "./CompilerOptions"; type BabelPluginPass = { opts: PluginOptions; @@ -40,18 +39,14 @@ function hasAnyUseForgetDirectives(directives: t.Directive[]): boolean { return false; } -/** - * The React Forget Babel Plugin - * @param {*} _babel - * @returns - */ -export default function ReactForgetBabelPlugin( - _babel: typeof BabelCore -): BabelCore.PluginObj { +export function compileProgram( + program: NodePath, + pass: PluginPass +): void { let hasForgetCompiledCode: boolean = false; function visitFn( - fn: BabelCore.NodePath, + fn: NodePath, pass: BabelPluginPass ): void { try { @@ -151,7 +146,7 @@ export default function ReactForgetBabelPlugin( const visitor = { FunctionDeclaration( - fn: BabelCore.NodePath, + fn: NodePath, pass: BabelPluginPass ): void { if (!shouldCompile(fn, pass)) { @@ -162,7 +157,7 @@ export default function ReactForgetBabelPlugin( }, ArrowFunctionExpression( - fn: BabelCore.NodePath, + fn: NodePath, pass: BabelPluginPass ): void { if (!shouldCompile(fn, pass)) { @@ -192,172 +187,159 @@ export default function ReactForgetBabelPlugin( }, }; - return { - name: "react-forget", - inherits: jsx, - visitor: { - // Note: Babel does some "smart" merging of visitors across plugins, so even if A is inserted - // prior to B, if A does not have a Program visitor and B does, B will run first. We always - // want Forget to run true to source as possible. - Program(path, pass): void { - const options = parsePluginOptions(pass.opts); + const options = parsePluginOptions(pass.opts); - const violations = []; - const fileComments = pass.file.ast.comments; - let fileHasUseForgetDirective = false; - if (Array.isArray(fileComments)) { - for (const comment of fileComments) { - if ( - /eslint-disable(-next-line)? react-hooks\/(exhaustive-deps|rules-of-hooks)/.test( - comment.value - ) - ) { - violations.push(comment); - } - } - } + const violations = []; + const fileComments = pass.file.ast.comments; + let fileHasUseForgetDirective = false; + if (Array.isArray(fileComments)) { + for (const comment of fileComments) { + if ( + /eslint-disable(-next-line)? react-hooks\/(exhaustive-deps|rules-of-hooks)/.test( + comment.value + ) + ) { + violations.push(comment); + } + } + } - if (violations.length > 0) { - path.traverse({ - Directive(path) { - if (hasUseForgetDirective(path.node)) { - fileHasUseForgetDirective = true; - } - }, - }); - - const reason = `One or more React eslint rules is disabled`; - const error = new CompilerError(); - for (const violation of violations) { - if (options.logger != null) { - options.logger.logEvent("err", { - reason, - filename: pass.filename, - violation, - }); - } - - error.pushErrorDetail( - new CompilerErrorDetail({ - reason, - description: violation.value.trim(), - severity: ErrorSeverity.UnsafeInput, - codeframe: null, - loc: violation.loc ?? null, - }) - ); - } - - if (fileHasUseForgetDirective) { - if (options.panicOnBailout || error.isCritical()) { - throw error; - } else { - if (options.isDev) { - log(error, pass.filename ?? null); - } - } - } - - return; - } - - path.traverse(visitor, { - ...pass, - opts: { ...pass.opts, ...options }, - filename: pass.filename ?? null, - }); - - // If there isn't already an import of * as React, insert it so useMemoCache doesn't - // throw - if (hasForgetCompiledCode) { - let didInsertUseMemoCache = false; - let hasExistingReactImport = false; - path.traverse({ - CallExpression(callExprPath) { - const callee = callExprPath.get("callee"); - const args = callExprPath.get("arguments"); - if ( - callee.isIdentifier() && - callee.node.name === "useMemoCache" && - args.length === 1 && - args[0].isNumericLiteral() - ) { - didInsertUseMemoCache = true; - } - }, - ImportDeclaration(importDeclPath) { - if (isNonNamespacedImportOfReact(importDeclPath)) { - hasExistingReactImport = true; - } - }, - }); - // If Forget did successfully compile inject/update an import of - // `import {unstable_useMemoCache as useMemoCache} from 'react'` and rename - // `React.unstable_useMemoCache(n)` to `useMemoCache(n)`; - if (didInsertUseMemoCache) { - if (hasExistingReactImport) { - let didUpdateImport = false; - path.traverse({ - ImportDeclaration(importDeclPath) { - if (isNonNamespacedImportOfReact(importDeclPath)) { - importDeclPath.pushContainer( - "specifiers", - t.importSpecifier( - t.identifier("useMemoCache"), - t.identifier("unstable_useMemoCache") - ) - ); - didUpdateImport = true; - } - }, - }); - if (didUpdateImport === false) { - throw new Error( - "Expected an ImportDeclaration of react in order to update ImportSpecifiers with useMemoCache" - ); - } - } else { - path.unshiftContainer( - "body", - t.importDeclaration( - [ - t.importSpecifier( - t.identifier("useMemoCache"), - t.identifier("unstable_useMemoCache") - ), - ], - t.stringLiteral("react") - ) - ); - } - } - // TODO: check for duplicate import specifiers - if (options.gating != null) { - path.unshiftContainer( - "body", - buildImportForExternalFunction(options.gating) - ); - } - if (options.instrumentForget != null) { - path.unshiftContainer( - "body", - buildImportForExternalFunction(options.instrumentForget.gating) - ); - path.unshiftContainer( - "body", - buildImportForExternalFunction( - options.instrumentForget.instrumentFn - ) - ); - } + if (violations.length > 0) { + program.traverse({ + Directive(directive) { + if (hasUseForgetDirective(directive.node)) { + fileHasUseForgetDirective = true; } }, - }, - }; + }); + + const reason = `One or more React eslint rules is disabled`; + const error = new CompilerError(); + for (const violation of violations) { + if (options.logger != null) { + options.logger.logEvent("err", { + reason, + filename: pass.filename, + violation, + }); + } + + error.pushErrorDetail( + new CompilerErrorDetail({ + reason, + description: violation.value.trim(), + severity: ErrorSeverity.UnsafeInput, + codeframe: null, + loc: violation.loc ?? null, + }) + ); + } + + if (fileHasUseForgetDirective) { + if (options.panicOnBailout || error.isCritical()) { + throw error; + } else { + if (options.isDev) { + log(error, pass.filename ?? null); + } + } + } + + return; + } + + program.traverse(visitor, { + ...pass, + opts: { ...pass.opts, ...options }, + filename: pass.filename ?? null, + }); + + // If there isn't already an import of * as React, insert it so useMemoCache doesn't + // throw + if (hasForgetCompiledCode) { + let didInsertUseMemoCache = false; + let hasExistingReactImport = false; + program.traverse({ + CallExpression(callExprPath) { + const callee = callExprPath.get("callee"); + const args = callExprPath.get("arguments"); + if ( + callee.isIdentifier() && + callee.node.name === "useMemoCache" && + args.length === 1 && + args[0].isNumericLiteral() + ) { + didInsertUseMemoCache = true; + } + }, + ImportDeclaration(importDeclPath) { + if (isNonNamespacedImportOfReact(importDeclPath)) { + hasExistingReactImport = true; + } + }, + }); + // If Forget did successfully compile inject/update an import of + // `import {unstable_useMemoCache as useMemoCache} from 'react'` and rename + // `React.unstable_useMemoCache(n)` to `useMemoCache(n)`; + if (didInsertUseMemoCache) { + if (hasExistingReactImport) { + let didUpdateImport = false; + program.traverse({ + ImportDeclaration(importDeclPath) { + if (isNonNamespacedImportOfReact(importDeclPath)) { + importDeclPath.pushContainer( + "specifiers", + t.importSpecifier( + t.identifier("useMemoCache"), + t.identifier("unstable_useMemoCache") + ) + ); + didUpdateImport = true; + } + }, + }); + if (didUpdateImport === false) { + throw new Error( + "Expected an ImportDeclaration of react in order to update ImportSpecifiers with useMemoCache" + ); + } + } else { + program.unshiftContainer( + "body", + t.importDeclaration( + [ + t.importSpecifier( + t.identifier("useMemoCache"), + t.identifier("unstable_useMemoCache") + ), + ], + t.stringLiteral("react") + ) + ); + } + } + // TODO: check for duplicate import specifiers + if (options.gating != null) { + program.unshiftContainer( + "body", + buildImportForExternalFunction(options.gating) + ); + } + if (options.instrumentForget != null) { + program.unshiftContainer( + "body", + buildImportForExternalFunction(options.instrumentForget.gating) + ); + program.unshiftContainer( + "body", + buildImportForExternalFunction(options.instrumentForget.instrumentFn) + ); + } + } } function shouldCompile( - fn: BabelCore.NodePath, + fn: NodePath, pass: BabelPluginPass ): boolean { if (pass.opts.enableOnlyOnUseForgetDirective) { @@ -407,8 +389,8 @@ function makeError( } function buildFunctionDeclaration( - fn: BabelCore.NodePath -): BabelCore.NodePath | CompilerError { + fn: NodePath +): NodePath | CompilerError { if (!fn.parentPath.isVariableDeclarator()) { return makeError( "ArrowFunctionExpression must be declared in variable declaration", @@ -447,7 +429,7 @@ function buildFunctionDeclaration( } function buildBlockStatement( - fn: BabelCore.NodePath + fn: NodePath ): t.BlockStatement { const body = fn.get("body"); if (body.isExpression()) { @@ -469,7 +451,7 @@ function buildBlockStatement( } type GatingTestOptions = { - originalFnDecl: BabelCore.NodePath; + originalFnDecl: NodePath; compiledIdent: t.Identifier; originalIdent: t.Identifier; gating: ExternalFunction; @@ -534,7 +516,7 @@ function buildSpecifierIdent(gating: ExternalFunction): t.Identifier { * but not `import * as React from 'react';` */ function isNonNamespacedImportOfReact( - importDeclPath: BabelCore.NodePath + importDeclPath: NodePath ): boolean { return ( importDeclPath.get("source").node.value === "react" && diff --git a/compiler/forget/src/Babel/PluginOptions.ts b/compiler/forget/src/CompilerOptions.ts similarity index 98% rename from compiler/forget/src/Babel/PluginOptions.ts rename to compiler/forget/src/CompilerOptions.ts index 37145c2019..8db4c4f789 100644 --- a/compiler/forget/src/Babel/PluginOptions.ts +++ b/compiler/forget/src/CompilerOptions.ts @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import { EnvironmentConfig } from "../HIR/Environment"; +import { EnvironmentConfig } from "./HIR/Environment"; export type ExternalFunction = { /**