From d618bd551f0d0c25fecab9c70cf3f0bb173b894f Mon Sep 17 00:00:00 2001 From: Pedro Cattori Date: Wed, 20 Mar 2024 13:55:47 -0400 Subject: [PATCH 1/2] wip --- packages/remix-dev/package.json | 5 ++ packages/remix-dev/vite/plugin.ts | 110 ++--------------------- pnpm-lock.yaml | 139 ++++++++++++++++++++++++++++++ 3 files changed, 149 insertions(+), 105 deletions(-) diff --git a/packages/remix-dev/package.json b/packages/remix-dev/package.json index d6c59042c93..45d0a87ed9a 100644 --- a/packages/remix-dev/package.json +++ b/packages/remix-dev/package.json @@ -103,6 +103,7 @@ "strip-ansi": "^6.0.1", "tiny-invariant": "^1.2.0", "vite": "5.1.3", + "@vitejs/plugin-react-swc": "^3.6.0", "wrangler": "^3.28.2" }, "peerDependencies": { @@ -110,6 +111,7 @@ "@remix-run/serve": "^2.8.1", "typescript": "^5.1.0", "vite": "^5.1.0", + "@vitejs/plugin-react-swc": "^3.6.0", "wrangler": "^3.28.2" }, "peerDependenciesMeta": { @@ -122,6 +124,9 @@ "vite": { "optional": true }, + "@vitejs/plugin-react-swc": { + "optional": true + }, "wrangler": { "optional": true } diff --git a/packages/remix-dev/vite/plugin.ts b/packages/remix-dev/vite/plugin.ts index 8dc1a266b3f..21f8894e181 100644 --- a/packages/remix-dev/vite/plugin.ts +++ b/packages/remix-dev/vite/plugin.ts @@ -5,7 +5,7 @@ import { type BinaryLike, createHash } from "node:crypto"; import * as path from "node:path"; import * as url from "node:url"; import * as fse from "fs-extra"; -import babel from "@babel/core"; +import reactPlugin from "@vitejs/plugin-react-swc"; import { type ServerBuild, unstable_setDevServerHooks as setDevServerHooks, @@ -601,7 +601,9 @@ let deepFreeze = (o: any) => { return o; }; -export type RemixVitePlugin = (config?: VitePluginConfig) => Vite.Plugin[]; +export type RemixVitePlugin = ( + config?: VitePluginConfig +) => Vite.PluginOption[]; export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { // Prevent mutations to the user config remixUserConfig = deepFreeze(remixUserConfig); @@ -1676,46 +1678,7 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { ].join("\n"); }, }, - { - name: "remix-react-refresh-babel", - async transform(code, id, options) { - if (viteCommand !== "serve") return; - if (id.includes("/node_modules/")) return; - - let [filepath] = id.split("?"); - let extensionsRE = /\.(jsx?|tsx?|mdx?)$/; - if (!extensionsRE.test(filepath)) return; - - let devRuntime = "react/jsx-dev-runtime"; - let ssr = options?.ssr === true; - let isJSX = filepath.endsWith("x"); - let useFastRefresh = !ssr && (isJSX || code.includes(devRuntime)); - if (!useFastRefresh) return; - - if (isClientRoute(id)) { - return { code: addRefreshWrapper(ctx.remixConfig, code, id) }; - } - - let result = await babel.transformAsync(code, { - filename: id, - sourceFileName: filepath, - parserOpts: { - sourceType: "module", - allowAwaitOutsideFunction: true, - }, - plugins: [[require("react-refresh/babel"), { skipEnvCheck: true }]], - sourceMaps: true, - }); - if (result === null) return; - - code = result.code!; - let refreshContentRE = /\$Refresh(?:Reg|Sig)\$\(/; - if (refreshContentRE.test(code)) { - code = addRefreshWrapper(ctx.remixConfig, code, id); - } - return { code, map: result.map }; - }, - }, + reactPlugin(), { name: "remix-hmr-updates", async handleHotUpdate({ server, file, modules, read }) { @@ -1772,69 +1735,6 @@ function isEqualJson(v1: unknown, v2: unknown) { return JSON.stringify(v1) === JSON.stringify(v2); } -function addRefreshWrapper( - remixConfig: ResolvedVitePluginConfig, - code: string, - id: string -): string { - let route = getRoute(remixConfig, id); - let acceptExports = - route || isClientRoute(id) - ? [ - "clientAction", - "clientLoader", - "handle", - "meta", - "links", - "shouldRevalidate", - ] - : []; - return ( - REACT_REFRESH_HEADER.replaceAll("__SOURCE__", JSON.stringify(id)) + - code + - REACT_REFRESH_FOOTER.replaceAll("__SOURCE__", JSON.stringify(id)) - .replaceAll("__ACCEPT_EXPORTS__", JSON.stringify(acceptExports)) - .replaceAll("__ROUTE_ID__", JSON.stringify(route?.id)) - ); -} - -const REACT_REFRESH_HEADER = ` -import RefreshRuntime from "${hmrRuntimeId}"; - -const inWebWorker = typeof WorkerGlobalScope !== 'undefined' && self instanceof WorkerGlobalScope; -let prevRefreshReg; -let prevRefreshSig; - -if (import.meta.hot && !inWebWorker) { - if (!window.__vite_plugin_react_preamble_installed__) { - throw new Error( - "Remix Vite plugin can't detect preamble. Something is wrong." - ); - } - - prevRefreshReg = window.$RefreshReg$; - prevRefreshSig = window.$RefreshSig$; - window.$RefreshReg$ = (type, id) => { - RefreshRuntime.register(type, __SOURCE__ + " " + id) - }; - window.$RefreshSig$ = RefreshRuntime.createSignatureFunctionForTransform; -}`.replace(/\n+/g, ""); - -const REACT_REFRESH_FOOTER = ` -if (import.meta.hot && !inWebWorker) { - window.$RefreshReg$ = prevRefreshReg; - window.$RefreshSig$ = prevRefreshSig; - RefreshRuntime.__hmr_import(import.meta.url).then((currentExports) => { - RefreshRuntime.registerExportsForReactRefresh(__SOURCE__, currentExports); - import.meta.hot.accept((nextExports) => { - if (!nextExports) return; - __ROUTE_ID__ && window.__remixRouteModuleUpdates.set(__ROUTE_ID__, nextExports); - const invalidateMessage = RefreshRuntime.validateRefreshBoundaryAndEnqueueUpdate(currentExports, nextExports, __ACCEPT_EXPORTS__); - if (invalidateMessage) import.meta.hot.invalidate(invalidateMessage); - }); - }); -}`; - function getRoute( pluginConfig: ResolvedVitePluginConfig, file: string diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9db271986dd..8578065eeb3 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1063,6 +1063,9 @@ importers: '@types/ws': specifier: ^7.4.1 version: 7.4.7 + '@vitejs/plugin-react-swc': + specifier: ^3.6.0 + version: 3.6.0(vite@5.1.3) esbuild-register: specifier: ^3.3.2 version: 3.5.0(esbuild@0.17.6) @@ -4463,6 +4466,131 @@ packages: dependencies: '@sinonjs/commons': 3.0.0 + /@swc/core-darwin-arm64@1.4.8: + resolution: {integrity: sha512-hhQCffRTgzpTIbngSnC30vV6IJVTI9FFBF954WEsshsecVoCGFiMwazBbrkLG+RwXENTrMhgeREEFh6R3KRgKQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-darwin-x64@1.4.8: + resolution: {integrity: sha512-P3ZBw8Jr8rKhY/J8d+6WqWriqngGTgHwtFeJ8MIakQJTbdYbFgXSZxcvDiERg3psbGeFXaUaPI0GO6BXv9k/OQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm-gnueabihf@1.4.8: + resolution: {integrity: sha512-PP9JIJt19bUWhAGcQW6qMwTjZOcMyzkvZa0/LWSlDm0ORYVLmDXUoeQbGD3e0Zju9UiZxyulnpjEN0ZihJgPTA==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-gnu@1.4.8: + resolution: {integrity: sha512-HvEWnwKHkoVUr5iftWirTApFJ13hGzhAY2CMw4lz9lur2m+zhPviRRED0FCI6T95Knpv7+8eUOr98Z7ctrG6DQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-arm64-musl@1.4.8: + resolution: {integrity: sha512-kY8+qa7k/dEeBq9p0Hrta18QnJPpsiJvDQSLNaTIFpdM3aEM9zbkshWz8gaX5VVGUEALowCBUWqmzO4VaqM+2w==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-gnu@1.4.8: + resolution: {integrity: sha512-0WWyIw432wpO/zeGblwq4f2YWam4pn8Z/Ig4KzHMgthR/KmiLU3f0Z7eo45eVmq5vcU7Os1zi/Zb65OOt09q/w==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-linux-x64-musl@1.4.8: + resolution: {integrity: sha512-p4yxvVS05rBNCrBaSTa20KK88vOwtg8ifTW7ec/yoab0bD5EwzzB8KbDmLLxE6uziFa0sdjF0dfRDwSZPex37Q==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-arm64-msvc@1.4.8: + resolution: {integrity: sha512-jKuXihxAaqUnbFfvPxtmxjdJfs87F1GdBf33il+VUmSyWCP4BE6vW+/ReDAe8sRNsKyrZ3UH1vI5q1n64csBUA==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-ia32-msvc@1.4.8: + resolution: {integrity: sha512-O0wT4AGHrX8aBeH6c2ADMHgagAJc5Kf6W48U5moyYDAkkVnKvtSc4kGhjWhe1Yl0sI0cpYh2In2FxvYsb44eWw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core-win32-x64-msvc@1.4.8: + resolution: {integrity: sha512-C2AYc3A2o+ECciqsJWRgIpp83Vk5EaRzHe7ed/xOWzVd0MsWR+fweEsyOjlmzHfpUxJSi46Ak3/BIZJlhZbXbg==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@swc/core@1.4.8: + resolution: {integrity: sha512-uY2RSJcFPgNOEg12RQZL197LZX+MunGiKxsbxmh22VfVxrOYGRvh4mPANFlrD1yb38CgmW1wI6YgIi8LkIwmWg==} + engines: {node: '>=10'} + requiresBuild: true + peerDependencies: + '@swc/helpers': ^0.5.0 + peerDependenciesMeta: + '@swc/helpers': + optional: true + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.6 + optionalDependencies: + '@swc/core-darwin-arm64': 1.4.8 + '@swc/core-darwin-x64': 1.4.8 + '@swc/core-linux-arm-gnueabihf': 1.4.8 + '@swc/core-linux-arm64-gnu': 1.4.8 + '@swc/core-linux-arm64-musl': 1.4.8 + '@swc/core-linux-x64-gnu': 1.4.8 + '@swc/core-linux-x64-musl': 1.4.8 + '@swc/core-win32-arm64-msvc': 1.4.8 + '@swc/core-win32-ia32-msvc': 1.4.8 + '@swc/core-win32-x64-msvc': 1.4.8 + dev: true + + /@swc/counter@0.1.3: + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + dev: true + + /@swc/types@0.1.6: + resolution: {integrity: sha512-/JLo/l2JsT/LRd80C3HfbmVpxOAJ11FO2RCEslFrgzLltoP9j8XIbsyDcfCt2WWyX+CM96rBoNM+IToAkFOugg==} + dependencies: + '@swc/counter': 0.1.3 + dev: true + /@testing-library/cypress@8.0.2(cypress@9.6.0): resolution: {integrity: sha512-KVdm7n37sg/A4e3wKMD4zUl0NpzzVhx06V9Tf0hZHZ7nrZ4yFva6Zwg2EFF1VzHkEfN/ahUzRtT1qiW+vuWnJw==} engines: {node: '>=12', npm: '>=6'} @@ -5283,6 +5411,17 @@ packages: - terser - ts-node + /@vitejs/plugin-react-swc@3.6.0(vite@5.1.3): + resolution: {integrity: sha512-XFRbsGgpGxGzEV5i5+vRiro1bwcIaZDIdBRP16qwm+jP68ue/S8FJTBEgOeojtVDYrbSua3XFp71kC8VJE6v+g==} + peerDependencies: + vite: ^4 || ^5 + dependencies: + '@swc/core': 1.4.8 + vite: 5.1.3(@types/node@18.17.1) + transitivePeerDependencies: + - '@swc/helpers' + dev: true + /@web3-storage/multipart-parser@1.0.0: resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==} dev: false From 268e2d9ab2c41783849cd040f25311ea1ee967fc Mon Sep 17 00:00:00 2001 From: Pedro Cattori Date: Tue, 26 Mar 2024 13:40:08 -0400 Subject: [PATCH 2/2] wip --- packages/remix-dev/vite/plugin.ts | 50 +++++++------------------------ 1 file changed, 11 insertions(+), 39 deletions(-) diff --git a/packages/remix-dev/vite/plugin.ts b/packages/remix-dev/vite/plugin.ts index 21f8894e181..5739f5ab333 100644 --- a/packages/remix-dev/vite/plugin.ts +++ b/packages/remix-dev/vite/plugin.ts @@ -287,7 +287,6 @@ let serverBuildId = VirtualModule.id("server-build"); let serverManifestId = VirtualModule.id("server-manifest"); let browserManifestId = VirtualModule.id("browser-manifest"); let hmrRuntimeId = VirtualModule.id("hmr-runtime"); -let injectHmrRuntimeId = VirtualModule.id("inject-hmr-runtime"); const resolveRelativeRouteFilePath = ( route: ConfigRoute, @@ -983,7 +982,7 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { hmr: { runtime: path.posix.join( ctx.remixConfig.publicPath, - VirtualModule.url(injectHmrRuntimeId) + VirtualModule.url(hmrRuntimeId) ), }, entry: { @@ -1589,6 +1588,7 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { } }, }, + ...reactPlugin(), { name: "remix-route-exports", async transform(code, id, options) { @@ -1631,56 +1631,28 @@ export const remixVitePlugin: RemixVitePlugin = (remixUserConfig = {}) => { }; }, }, + // TODO: combine HMR plugins { - name: "remix-inject-hmr-runtime", + name: "remix-hmr", enforce: "pre", resolveId(id) { - if (id === injectHmrRuntimeId) - return VirtualModule.resolve(injectHmrRuntimeId); + if (id === hmrRuntimeId) return VirtualModule.resolve(hmrRuntimeId); }, async load(id) { - if (id !== VirtualModule.resolve(injectHmrRuntimeId)) return; + if (id !== VirtualModule.resolve(hmrRuntimeId)) return; return [ - `import RefreshRuntime from "${hmrRuntimeId}"`, + // preamble + `import * as RefreshRuntime from "/@react-refresh"`, "RefreshRuntime.injectIntoGlobalHook(window)", "window.$RefreshReg$ = () => {}", "window.$RefreshSig$ = () => (type) => type", "window.__vite_plugin_react_preamble_installed__ = true", + // framework integration + `window.__registerBeforePerformReactRefresh(() => console.log('[remix] before react refresh'))`, + `window.__getReactRefreshIgnoredExports = ({id}) => {console.log('[remix] ignored exports: ', id); return ['meta', 'links', 'handle', 'clientLoader'];}`, ].join("\n"); }, - }, - { - name: "remix-hmr-runtime", - enforce: "pre", - resolveId(id) { - if (id === hmrRuntimeId) return VirtualModule.resolve(hmrRuntimeId); - }, - async load(id) { - if (id !== VirtualModule.resolve(hmrRuntimeId)) return; - - let reactRefreshDir = path.dirname( - require.resolve("react-refresh/package.json") - ); - let reactRefreshRuntimePath = path.join( - reactRefreshDir, - "cjs/react-refresh-runtime.development.js" - ); - - return [ - "const exports = {}", - await fse.readFile(reactRefreshRuntimePath, "utf8"), - await fse.readFile( - require.resolve("./static/refresh-utils.cjs"), - "utf8" - ), - "export default exports", - ].join("\n"); - }, - }, - reactPlugin(), - { - name: "remix-hmr-updates", async handleHotUpdate({ server, file, modules, read }) { let route = getRoute(ctx.remixConfig, file);