From 492846a793f20163864e917da2d2729729850d9a Mon Sep 17 00:00:00 2001 From: Maximo Mussini Date: Wed, 3 Nov 2021 09:51:40 -0300 Subject: [PATCH] perf: do a single-pass babel transform for JSX and Hook Names --- demo/src/Compat.tsx | 10 ++++- src/hook-names.ts | 49 --------------------- src/index.ts | 103 ++++++++++++++++++++++---------------------- 3 files changed, 60 insertions(+), 102 deletions(-) delete mode 100644 src/hook-names.ts diff --git a/demo/src/Compat.tsx b/demo/src/Compat.tsx index 0fb6819..1524f42 100644 --- a/demo/src/Compat.tsx +++ b/demo/src/Compat.tsx @@ -1,9 +1,17 @@ // @ts-ignore -import React, { useState } from "react"; +import React, { useMemo, useState } from "react"; export function ReactComponent() { const [v, set] = useState(0); + // NOTE: To check in devtools that babel-plugin-transform-hook-names can + // extract the variable names from typed expressions. + const _unusedState = useState(0 as any); + const _unusedMemo: number = useMemo( + () => _unusedState[0], + [_unusedState[0]], + ); + return (

Counter: {v}

diff --git a/src/hook-names.ts b/src/hook-names.ts deleted file mode 100644 index 130fdce..0000000 --- a/src/hook-names.ts +++ /dev/null @@ -1,49 +0,0 @@ -import { transformAsync } from "@babel/core"; -import { Plugin, ResolvedConfig } from "vite"; -import type { RollupFilter } from "./utils.js"; -import { parseId } from "./utils.js"; - -export interface PreactHookNamesPluginOptions { - shouldTransform: RollupFilter; -} - -export function hookNamesPlugin({ - shouldTransform, -}: PreactHookNamesPluginOptions): Plugin { - let config: ResolvedConfig; - - return { - name: "preact:hook-names", - configResolved(resolvedConfig) { - config = resolvedConfig; - }, - async transform(code, url) { - if (config.isProduction) { - return; - } - - const { id } = parseId(url); - - if (!shouldTransform(id)) { - return; - } - - const res = await transformAsync(code, { - plugins: ["babel-plugin-transform-hook-names"], - filename: id, - sourceMaps: true, - }); - - // TODO: When does this happen? The babel documentation isn't - // clear about this. - if (res === null) { - return; - } - - return { - code: res.code || code, - map: res.map, - }; - }, - }; -} diff --git a/src/index.ts b/src/index.ts index 8123298..4b0445f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,11 +1,10 @@ -import type { Plugin } from "vite"; +import type { Plugin, ResolvedConfig } from "vite"; import type { FilterPattern } from "@rollup/pluginutils"; import type { ParserPlugin } from "@babel/parser"; import resolve from "resolve"; import prefresh from "@prefresh/vite"; import { preactDevtoolsPlugin } from "./devtools.js"; -import { hookNamesPlugin } from "./hook-names.js"; import { createFilter, parseId } from "./utils.js"; import { transformAsync } from "@babel/core"; @@ -32,7 +31,7 @@ export default function preactPlugin({ include, exclude, }: PreactPluginOptions = {}): Plugin[] { - let projectRoot: string = process.cwd(); + let config: ResolvedConfig; const shouldTransform = createFilter( include || [/\.[tj]sx?$/], @@ -49,8 +48,8 @@ export default function preactPlugin({ }, }; }, - configResolved(config) { - projectRoot = config.root; + configResolved(resolvedConfig) { + config = resolvedConfig; }, resolveId(id: string) { return id === "preact/jsx-runtime" ? id : null; @@ -58,7 +57,7 @@ export default function preactPlugin({ load(id: string) { if (id === "preact/jsx-runtime") { const runtimePath = resolve.sync("preact/jsx-runtime", { - basedir: projectRoot, + basedir: config.root, }); const exports = ["jsx", "jsxs", "Fragment"]; return [ @@ -74,55 +73,56 @@ export default function preactPlugin({ // Ignore query parameters, as in Vue SFC virtual modules. const { id } = parseId(url); - if (shouldTransform(id)) { - const parserPlugins = [ - "importMeta", - // This plugin is applied before esbuild transforms the code, - // so we need to enable some stage 3 syntax that is supported in - // TypeScript and some environments already. - "topLevelAwait", - "classProperties", - "classPrivateProperties", - "classPrivateMethods", - !id.endsWith(".ts") && "jsx", - /\.tsx?$/.test(id) && "typescript", - ].filter(Boolean) as ParserPlugin[]; + if (!shouldTransform(id)) return; - const result = await transformAsync(code, { - babelrc: false, - configFile: false, - ast: true, - root: projectRoot, - filename: id, - parserOpts: { - sourceType: "module", - allowAwaitOutsideFunction: true, - plugins: parserPlugins, - }, - generatorOpts: { - decoratorsBeforeExport: true, - }, - plugins: [ - [ - "@babel/plugin-transform-react-jsx", - { - runtime: "automatic", - importSource: "preact", - }, - ], + const parserPlugins = [ + "importMeta", + // This plugin is applied before esbuild transforms the code, + // so we need to enable some stage 3 syntax that is supported in + // TypeScript and some environments already. + "topLevelAwait", + "classProperties", + "classPrivateProperties", + "classPrivateMethods", + !id.endsWith(".ts") && "jsx", + /\.tsx?$/.test(id) && "typescript", + ].filter(Boolean) as ParserPlugin[]; + + const result = await transformAsync(code, { + babelrc: false, + configFile: false, + ast: true, + root: config.root, + filename: id, + parserOpts: { + sourceType: "module", + allowAwaitOutsideFunction: true, + plugins: parserPlugins, + }, + generatorOpts: { + decoratorsBeforeExport: true, + }, + plugins: [ + [ + "@babel/plugin-transform-react-jsx", + { + runtime: "automatic", + importSource: "preact", + }, ], - sourceMaps: true, - inputSourceMap: false as any, - }); + ...(config.isProduction ? [] : ["babel-plugin-transform-hook-names"]), + ], + sourceMaps: true, + inputSourceMap: false as any, + }); - if (!result) return { code }; + // NOTE: Since no config file is being loaded, this path wouldn't occur. + if (!result) return; - return { - code: result.code || code, - map: result.map, - }; - } - return undefined; + return { + code: result.code || code, + map: result.map, + }; }, }; return [ @@ -143,6 +143,5 @@ export default function preactPlugin({ jsxPlugin, preactDevtoolsPlugin({ injectInProd: devtoolsInProd, shouldTransform }), prefresh(), - hookNamesPlugin({ shouldTransform }), ]; }