diff --git a/deno.lock b/deno.lock
index 476e8585fe7..b0c99ebecd8 100644
--- a/deno.lock
+++ b/deno.lock
@@ -1,6 +1,7 @@
{
"version": "5",
"specifiers": {
+ "jsr:@astral/astral@0.5.6": "0.5.6",
"jsr:@astral/astral@~0.5.6": "0.5.6",
"jsr:@deno-library/progress@^1.5.1": "1.5.1",
"jsr:@deno/cache-dir@0.14": "0.14.0",
@@ -6110,6 +6111,17 @@
"npm:vite@^7.1.4"
]
}
+ },
+ "links": {
+ "npm:@deno/vite-plugin@2.0.2": {
+ "dependencies": [
+ "npm:@jsr/deno__loader@0.5",
+ "npm:@jsr/std__jsonc@1"
+ ],
+ "peerDependencies": [
+ "npm:vite@5 || 6 || 7 || 8"
+ ]
+ }
}
}
}
diff --git a/docs/latest/concepts/islands.md b/docs/latest/concepts/islands.md
index b2f88a7efbc..57d10edd28b 100644
--- a/docs/latest/concepts/islands.md
+++ b/docs/latest/concepts/islands.md
@@ -118,12 +118,34 @@ import OtherIsland from "../islands/other-island.tsx";
;
```
-## Rendering islands on client only
+## Client-only islands
-When using client-only APIs, like `EventSource` or `navigator.getUserMedia`, the
-component would error during server-side rendering. Use the `IS_BROWSER`
-constant from `fresh/runtime` to guard browser-only code. It is `false` on the
-server and `true` in the browser:
+Some libraries (e.g. Monaco Editor, certain charting libraries) reference
+browser globals like `document` at the module top level, which crashes during
+server-side rendering. You can mark an island as **client-only** by adding
+`export const clientOnly = true`. Fresh will skip executing the component on the
+server and render an empty placeholder instead. On the client, the component
+renders normally.
+
+```tsx islands/my-editor.tsx
+export const clientOnly = true;
+
+export default function MyEditor() {
+ // Safe to use document, window, etc. — this code never runs on the server.
+ return
{/* ... */}
;
+}
+```
+
+> [warn]: Client-only islands produce no meaningful HTML on the server. This
+> means search engines will not see their content, and users will see an empty
+> placeholder until JavaScript loads. Use this only when the component truly
+> cannot run on the server.
+
+### Using `IS_BROWSER` for a custom fallback
+
+If the module itself can be loaded on the server but you only need to guard
+certain API calls, use the `IS_BROWSER` constant from `fresh/runtime` instead.
+This lets you return a meaningful SSR fallback:
```tsx islands/my-island.tsx
import { IS_BROWSER } from "fresh/runtime";
diff --git a/packages/fresh/src/build_cache.ts b/packages/fresh/src/build_cache.ts
index 6a8161a2b77..2af9bd794a7 100644
--- a/packages/fresh/src/build_cache.ts
+++ b/packages/fresh/src/build_cache.ts
@@ -103,7 +103,9 @@ export class IslandPreparer {
chunkName: string,
modName: string,
css: string[],
+ clientOnly?: boolean,
) {
+ const isClientOnly = clientOnly ?? mod.clientOnly === true;
for (const [name, value] of Object.entries(mod)) {
if (typeof value !== "function") continue;
@@ -117,6 +119,7 @@ export class IslandPreparer {
fn,
name: uniqueName,
css,
+ clientOnly: isClientOnly,
});
}
}
diff --git a/packages/fresh/src/context.ts b/packages/fresh/src/context.ts
index 3c2d9c07815..3af466a0007 100644
--- a/packages/fresh/src/context.ts
+++ b/packages/fresh/src/context.ts
@@ -91,6 +91,7 @@ export interface Island {
exportName: string;
fn: ComponentType;
css: string[];
+ clientOnly: boolean;
}
export type ServerIslandRegistry = Map;
diff --git a/packages/fresh/src/dev/dev_build_cache.ts b/packages/fresh/src/dev/dev_build_cache.ts
index dd985551113..c69ed660f65 100644
--- a/packages/fresh/src/dev/dev_build_cache.ts
+++ b/packages/fresh/src/dev/dev_build_cache.ts
@@ -35,6 +35,7 @@ export interface IslandModChunk {
server: string;
browser: string | null;
css: string[];
+ clientOnly?: boolean;
}
export type FsRouteFileNoMod = Omit, "mod"> & {
@@ -493,6 +494,9 @@ export async function generateSnapshotServer(
const browser = JSON.stringify(item.browser);
const name = JSON.stringify(item.name);
const css = JSON.stringify(item.css);
+ if (item.clientOnly) {
+ return `islandPreparer.prepare(islands, ${item.name}, ${browser}, ${name}, ${css}, true);`;
+ }
return `islandPreparer.prepare(islands, ${item.name}, ${browser}, ${name}, ${css});`;
}).join("\n");
diff --git a/packages/fresh/src/runtime/client/reviver.ts b/packages/fresh/src/runtime/client/reviver.ts
index dda11e8aa4b..83d32e15b91 100644
--- a/packages/fresh/src/runtime/client/reviver.ts
+++ b/packages/fresh/src/runtime/client/reviver.ts
@@ -21,6 +21,7 @@ interface IslandReq {
name: string;
propsIdx: number;
key: string | null;
+ clientOnly: boolean;
start: Comment | Text;
end: Comment | Text | null;
}
@@ -269,11 +270,13 @@ function _walkInner(
const name = parts[2];
const propsIdx = parts[3];
const key = parts[4];
+ const clientOnly = parts[5] === "c";
const found: IslandReq = {
kind: RootKind.Island,
name,
propsIdx: Number(propsIdx),
key: key === "" ? null : key,
+ clientOnly,
start: node as Comment,
end: null,
};
diff --git a/packages/fresh/src/runtime/server/preact_hooks.ts b/packages/fresh/src/runtime/server/preact_hooks.ts
index 8070db536a6..d8dda1de445 100644
--- a/packages/fresh/src/runtime/server/preact_hooks.ts
+++ b/packages/fresh/src/runtime/server/preact_hooks.ts
@@ -300,15 +300,19 @@ options[OptionsType.DIFF] = (vnode) => {
}
const propsIdx = islandProps.push({ slots: [], props }) - 1;
- const child = h(originalType, props);
+ const key = normalizeKey(vnode.key);
+ const markerData = island!.clientOnly
+ ? `${island!.name}:${propsIdx}:${key}:c`
+ : `${island!.name}:${propsIdx}:${key}`;
+
+ // For client-only islands, render an empty placeholder
+ // instead of executing the component on the server.
+ const child = island!.clientOnly
+ ? h("div", null)
+ : h(originalType, props);
PATCHED.add(child);
- const key = normalizeKey(vnode.key);
- return wrapWithMarker(
- child,
- "island",
- `${island!.name}:${propsIdx}:${key}`,
- );
+ return wrapWithMarker(child, "island", markerData);
};
}
} else if (typeof vnode.type === "string") {
diff --git a/packages/fresh/tests/fixtures_islands/ClientOnlyIsland.tsx b/packages/fresh/tests/fixtures_islands/ClientOnlyIsland.tsx
new file mode 100644
index 00000000000..adfe3338722
--- /dev/null
+++ b/packages/fresh/tests/fixtures_islands/ClientOnlyIsland.tsx
@@ -0,0 +1,25 @@
+import { useSignal } from "@preact/signals";
+import { useEffect } from "preact/hooks";
+
+export const clientOnly = true;
+
+export default function ClientOnlyIsland(
+ props: { id?: string; label?: string },
+) {
+ const active = useSignal(false);
+ useEffect(() => {
+ active.value = true;
+ }, []);
+
+ return (
+
+
{props.label ?? "rendered on client"}
+
+ {typeof document !== "undefined" ? "has-document" : "no-document"}
+
+
+ );
+}
diff --git a/packages/fresh/tests/islands_test.tsx b/packages/fresh/tests/islands_test.tsx
index b5031df0d95..c9512177f2c 100644
--- a/packages/fresh/tests/islands_test.tsx
+++ b/packages/fresh/tests/islands_test.tsx
@@ -30,6 +30,7 @@ import { FakeServer } from "../src/test_utils.ts";
import { PARTIAL_SEARCH_PARAM } from "../src/constants.ts";
import { ComputedSignal } from "./fixtures_islands/Computed.tsx";
import { EnvIsland } from "./fixtures_islands/EnvIsland.tsx";
+import ClientOnlyIsland from "./fixtures_islands/ClientOnlyIsland.tsx";
Deno.env.set("FRESH_PUBLIC_TEST_FOO", "test-env-value");
Deno.env.set("FRESH_PRIVATE_TEST_FOO", "i-should-not-be-visible");
@@ -847,3 +848,58 @@ Deno.test({
});
},
});
+
+Deno.test({
+ name: "islands - client-only island renders placeholder in SSR",
+ fn: async () => {
+ const app = testApp()
+ .get("/", (ctx) => {
+ return ctx.render(
+
+
+ ,
+ );
+ });
+
+ const server = new FakeServer(app.handler());
+ const res = await server.get("/");
+ const html = await res.text();
+
+ // SSR should contain a placeholder div, not the island's actual content
+ const doc = parseHtml(html);
+ expect(doc.querySelector(".client-only")).toBeNull();
+ expect(doc.querySelector(".label")).toBeNull();
+
+ // The marker comment should have the :c flag for client-only
+ expect(html).toContain("::c-->");
+ },
+});
+
+Deno.test({
+ name: "islands - client-only island renders on client",
+ fn: async () => {
+ const app = testApp()
+ .get("/", (ctx) => {
+ return ctx.render(
+
+
+ ,
+ );
+ });
+
+ await withBrowserApp(app, async (page, address) => {
+ await page.goto(address, { waitUntil: "load" });
+ await page.locator("#co.ready").wait();
+
+ const label = await page.evaluate(() => {
+ return document.querySelector("#co .label")?.textContent;
+ });
+ expect(label).toEqual("hello");
+
+ const check = await page.evaluate(() => {
+ return document.querySelector("#co .check")?.textContent;
+ });
+ expect(check).toEqual("has-document");
+ });
+ },
+});
diff --git a/packages/plugin-vite/demo/fixtures/commonjs_mod.cjs b/packages/plugin-vite/demo/fixtures/commonjs_mod.cjs
deleted file mode 100644
index 1f6a76bc478..00000000000
--- a/packages/plugin-vite/demo/fixtures/commonjs_mod.cjs
+++ /dev/null
@@ -1 +0,0 @@
-exports.value = "ok";
diff --git a/packages/plugin-vite/demo/fixtures/commonjs_mod.js b/packages/plugin-vite/demo/fixtures/commonjs_mod.js
new file mode 100644
index 00000000000..44d44847d02
--- /dev/null
+++ b/packages/plugin-vite/demo/fixtures/commonjs_mod.js
@@ -0,0 +1 @@
+export const value = "ok";
diff --git a/packages/plugin-vite/demo/fixtures/maxmind.cjs b/packages/plugin-vite/demo/fixtures/maxmind.cjs
deleted file mode 100644
index bbef3806916..00000000000
--- a/packages/plugin-vite/demo/fixtures/maxmind.cjs
+++ /dev/null
@@ -1,8 +0,0 @@
-"use strict";
-// deno-lint-ignore no-var
-var __importDefault = (this && this.__importDefault) || function (mod) {
- return (mod && mod.__esModule) ? mod : { "default": mod };
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-const assert_1 = __importDefault(require("assert"));
-(0, assert_1.default)(true);
diff --git a/packages/plugin-vite/demo/fixtures/maxmind.js b/packages/plugin-vite/demo/fixtures/maxmind.js
new file mode 100644
index 00000000000..8710fa40a4c
--- /dev/null
+++ b/packages/plugin-vite/demo/fixtures/maxmind.js
@@ -0,0 +1,2 @@
+import assert from "node:assert";
+assert(true);
diff --git a/packages/plugin-vite/demo/routes/tests/commonjs.tsx b/packages/plugin-vite/demo/routes/tests/commonjs.tsx
index 8f73c79b934..32308633baf 100644
--- a/packages/plugin-vite/demo/routes/tests/commonjs.tsx
+++ b/packages/plugin-vite/demo/routes/tests/commonjs.tsx
@@ -1,4 +1,4 @@
-import { value } from "../../fixtures/commonjs_mod.cjs";
+import { value } from "../../fixtures/commonjs_mod.js";
export default function Page() {
return {value}
;
diff --git a/packages/plugin-vite/demo/routes/tests/maxmind.tsx b/packages/plugin-vite/demo/routes/tests/maxmind.tsx
index b42a8ada37f..8ac0424445b 100644
--- a/packages/plugin-vite/demo/routes/tests/maxmind.tsx
+++ b/packages/plugin-vite/demo/routes/tests/maxmind.tsx
@@ -1,4 +1,4 @@
-import * as maxmind from "../../fixtures/maxmind.cjs";
+import * as maxmind from "../../fixtures/maxmind.js";
export default function Page() {
// deno-lint-ignore no-console
diff --git a/packages/plugin-vite/src/mod.ts b/packages/plugin-vite/src/mod.ts
index c49c244526c..8fde99478c6 100644
--- a/packages/plugin-vite/src/mod.ts
+++ b/packages/plugin-vite/src/mod.ts
@@ -82,15 +82,43 @@ export function fresh(config?: FreshViteConfig): Plugin[] {
});
let isDev = false;
+ let freshMode = "development";
const plugins: Plugin[] = [
{
name: "fresh",
sharedDuringBuild: true,
- config(config, env) {
+ async config(config, env) {
isDev = env.command === "serve";
+ freshMode = isDev ? "development" : "production";
+
+ // Load env files early so define entries are available
+ const root = config.root ? path.resolve(config.root) : Deno.cwd();
+ const envDir = config.envDir ? path.resolve(root, config.envDir) : root;
+ await loadEnvFile(path.join(envDir, ".env"));
+ await loadEnvFile(path.join(envDir, ".env.local"));
+ await loadEnvFile(path.join(envDir, `.env.${freshMode}`));
+ await loadEnvFile(path.join(envDir, `.env.${freshMode}.local`));
+
+ // Build define map for FRESH_PUBLIC_* env vars
+ // Replaces the Babel inlineEnvVarsPlugin with Vite's native define
+ const envDefine: Record = {};
+ for (const [key, value] of Object.entries(Deno.env.toObject())) {
+ if (key.startsWith("FRESH_PUBLIC_")) {
+ envDefine[`process.env.${key}`] = JSON.stringify(value);
+ envDefine[`import.meta.env.${key}`] = JSON.stringify(value);
+ }
+ }
return {
+ define: envDefine,
+ ssr: {
+ // Bundle all deps in SSR so that resolve.alias
+ // (react -> preact/compat) is applied consistently.
+ // CJS packages are handled by the deno plugin's load
+ // hook which wraps them in an ESM-compatible shim.
+ noExternal: true,
+ },
server: {
watch: {
// Ignore temp files, editor swap files, and Vite timestamp
@@ -119,14 +147,15 @@ export function fresh(config?: FreshViteConfig): Plugin[] {
"react-dom": "preact/compat",
react: "preact/compat",
},
- // Disallow externals, because it leads to duplicate
- // modules with `preact` vs `npm:preact@*` in the server
- // environment.
- noExternal: true,
},
+
optimizeDeps: {
- // Optimize deps somehow leads to duplicate modules or them
- // being placed in the wrong chunks...
+ // Disable dep optimizer because deno.ts handles all
+ // module resolution. The optimizer causes duplicate
+ // module instances when remote (JSR) islands resolve
+ // deps to /@fs/ paths while the optimizer bundles to
+ // /.vite/deps/. CJS packages in client-side islands
+ // are handled by deno.ts's load hook.
noDiscovery: true,
},
@@ -192,14 +221,6 @@ export function fresh(config?: FreshViteConfig): Plugin[] {
return;
}
- // Ignore commonjs optional exports
- if (
- warning.code === "MISSING_EXPORT" &&
- warning.message.includes("__require")
- ) {
- return;
- }
-
// Ignore this warnings
if (warning.code === "THIS_IS_UNDEFINED") {
return;
@@ -221,7 +242,7 @@ export function fresh(config?: FreshViteConfig): Plugin[] {
},
};
},
- async configResolved(vConfig) {
+ configResolved(vConfig) {
// Run update check in background
updateCheck(UPDATE_INTERVAL).catch(() => {});
@@ -236,19 +257,45 @@ export function fresh(config?: FreshViteConfig): Plugin[] {
const name = fConfig.namer.getUniqueName(specName);
fConfig.islandSpecifiers.set(spec, name);
});
+ },
+ },
+ // Lightweight replacement for Deno.env.get() calls with FRESH_PUBLIC_*
+ // and NODE_ENV values. Replaces the Babel inlineEnvVarsPlugin for this
+ // pattern which can't be handled by Vite's define (it's a call expression).
+ {
+ name: "fresh:deno-env",
+ sharedDuringBuild: true,
+ applyToEnvironment() {
+ return true;
+ },
+ transform: {
+ filter: {
+ id: /\.([tj]sx?|[mc]?[tj]s)(\?.*)?$/,
+ },
+ handler(code) {
+ if (!code.includes("Deno.env.get(")) return;
- const envDir = pathWithRoot(
- vConfig.envDir || vConfig.root,
- vConfig.root,
- );
+ const allEnv = Deno.env.toObject();
+ let modified = false;
+ const result = code.replace(
+ /Deno\.env\.get\(\s*["']([^"']+)["']\s*\)/g,
+ (match: string, name: string) => {
+ if (name === "NODE_ENV") {
+ modified = true;
+ return JSON.stringify(freshMode);
+ }
+ if (name.startsWith("FRESH_PUBLIC_") && name in allEnv) {
+ modified = true;
+ return JSON.stringify(allEnv[name]);
+ }
+ return match;
+ },
+ );
- await loadEnvFile(path.join(envDir, ".env"));
- await loadEnvFile(path.join(envDir, ".env.local"));
- const mode = isDev ? "development" : "production";
- await loadEnvFile(path.join(envDir, `.env.${mode}`));
- await loadEnvFile(path.join(envDir, `.env.${mode}.local`));
+ if (modified) return { code: result };
+ },
},
- },
+ } satisfies Plugin,
serverEntryPlugin(fConfig),
patches(),
...serverSnapshot(fConfig),
diff --git a/packages/plugin-vite/src/plugins/deno.ts b/packages/plugin-vite/src/plugins/deno.ts
index 6cefed7e463..016529bb593 100644
--- a/packages/plugin-vite/src/plugins/deno.ts
+++ b/packages/plugin-vite/src/plugins/deno.ts
@@ -9,7 +9,7 @@ import {
import * as path from "@std/path";
import * as babel from "@babel/core";
import { httpAbsolute } from "./patches/http_absolute.ts";
-import { JS_REG, JSX_REG } from "../utils.ts";
+import { JSX_REG } from "../utils.ts";
import { builtinModules } from "node:module";
// @ts-ignore Workaround for https://github.com/denoland/deno/issues/30850
@@ -17,10 +17,6 @@ const { default: babelReact } = await import("@babel/preset-react");
const BUILTINS = new Set(builtinModules);
-interface DenoState {
- type: RequestedModuleType;
-}
-
export function deno(): Plugin {
let ssrLoader: Loader;
let browserLoader: Loader;
@@ -85,6 +81,21 @@ export function deno(): Plugin {
id = `${url.origin}${id}`;
}
+ // Apply resolve.alias before Deno resolution so that
+ // react -> preact/compat works even in externalized packages.
+ // Vite normalizes alias config to { find, replacement }[] format.
+ const aliases = this.environment?.config?.resolve?.alias;
+ if (aliases) {
+ const list = Array.isArray(aliases) ? aliases : [];
+ for (const alias of list) {
+ const find = alias.find;
+ if (typeof find === "string" ? find === id : find?.test?.(id)) {
+ id = typeof alias.replacement === "string" ? alias.replacement : id;
+ break;
+ }
+ }
+ }
+
// We still want to allow other plugins to participate in
// resolution, with us being in front due to `enforce: "pre"`.
// But we still want to ignore everything `vite:resolve` does
@@ -155,14 +166,13 @@ export function deno(): Plugin {
resolved = path.fromFileUrl(resolved);
}
- return {
- id: resolved,
- meta: {
- deno: {
- type,
- },
- },
- };
+ // For file:// resolved modules (npm packages in node_modules,
+ // local files), let Vite handle loading natively. This allows
+ // Vite to externalize CJS packages in SSR mode (Node.js handles
+ // them with native require()) and avoids needing a custom CJS
+ // transform. Only \0deno:: virtual modules (jsr:, non-default
+ // types) need Fresh's custom load hook.
+ return { id: resolved };
} catch {
// ignore
}
@@ -172,6 +182,80 @@ export function deno(): Plugin {
? ssrLoader
: browserLoader;
+ // In dev mode, CJS files need to be wrapped in an ESM shim:
+ // - SSR: module runner evaluates as ESM, needs module/exports/require
+ // - Client: browser evaluates as ESM, needs module/exports
+ // In build mode, Rollup's @rollup/plugin-commonjs handles CJS.
+ if (
+ isDev &&
+ !id.startsWith("\0") &&
+ id.includes("node_modules") &&
+ /\.(c?js|cjs)$/.test(id)
+ ) {
+ try {
+ const code = await Deno.readTextFile(id);
+ // Quick heuristic: if file has CJS patterns and no ESM
+ if (
+ !code.includes("export ") &&
+ !code.includes("import ") &&
+ (code.includes("module.exports") ||
+ code.includes("exports.") ||
+ code.includes("require("))
+ ) {
+ const isServer = this.environment.config.consumer === "server";
+
+ if (isServer) {
+ // SSR: use Node.js createRequire for full CJS compat
+ const wrapped = `
+import { createRequire as __cjs_createRequire } from "node:module";
+import { fileURLToPath as __cjs_fileURLToPath } from "node:url";
+import { dirname as __cjs_dirname } from "node:path";
+var __filename = __cjs_fileURLToPath(import.meta.url);
+var __dirname = __cjs_dirname(__filename);
+var require = __cjs_createRequire(import.meta.url);
+var module = { exports: {} };
+var exports = module.exports;
+
+${code}
+
+export default module.exports;
+`;
+ return { code: wrapped };
+ }
+
+ // Client: convert require() calls to ESM imports so
+ // browsers can load them. Hoist static require() calls
+ // to import statements at the top.
+ const imports: string[] = [];
+ let idx = 0;
+ const transformed = code.replace(
+ /\brequire\(["']([^"']+)["']\)/g,
+ (_match: string, spec: string) => {
+ const varName = `__cjs_import_${idx++}`;
+ imports.push(
+ `import ${varName} from ${JSON.stringify(spec)};`,
+ );
+ return `(${varName}.default ?? ${varName})`;
+ },
+ );
+
+ const wrapped = `${imports.join("\n")}
+var module = { exports: {} };
+var exports = module.exports;
+var __filename = "";
+var __dirname = "";
+
+${transformed}
+
+export default module.exports;
+`;
+ return { code: wrapped };
+ }
+ } catch {
+ // Fall through to default loading
+ }
+ }
+
if (isDenoSpecifier(id)) {
const { type, specifier } = parseDenoSpecifier(id);
@@ -197,49 +281,6 @@ export function deno(): Plugin {
code,
};
}
-
- if (id.startsWith("\0")) {
- id = id.slice(1);
- }
-
- const meta = this.getModuleInfo(id)?.meta.deno as
- | DenoState
- | undefined
- | null;
-
- if (meta === null || meta === undefined) return;
-
- // Skip for non-js files like `.css`
- if (
- meta.type === RequestedModuleType.Default &&
- !JS_REG.test(id)
- ) {
- return;
- }
-
- const url = path.toFileUrl(id);
-
- const result = await loader.load(url.href, meta.type);
- if (result.kind === "external") {
- return null;
- }
-
- const code = new TextDecoder().decode(result.code);
-
- const maybeJsx = babelTransform({
- ssr: this.environment.config.consumer === "server",
- media: result.mediaType,
- id,
- code,
- isDev,
- });
- if (maybeJsx) {
- return maybeJsx;
- }
-
- return {
- code,
- };
},
transform: {
filter: {
diff --git a/packages/plugin-vite/src/plugins/patches.ts b/packages/plugin-vite/src/plugins/patches.ts
index 167ac7bc862..b6970bdbc42 100644
--- a/packages/plugin-vite/src/plugins/patches.ts
+++ b/packages/plugin-vite/src/plugins/patches.ts
@@ -1,8 +1,6 @@
import type { Plugin } from "vite";
import * as babel from "@babel/core";
-import { cjsPlugin } from "./patches/commonjs.ts";
import { jsxComments } from "./patches/jsx_comment.ts";
-import { inlineEnvVarsPlugin } from "./patches/inline_env_vars.ts";
import { removePolyfills } from "./patches/remove_polyfills.ts";
import { JS_REG, JSX_REG } from "../utils.ts";
import { codeEvalPlugin } from "./patches/code_eval.ts";
@@ -41,10 +39,8 @@ export function patches(): Plugin {
const plugins: babel.PluginItem[] = [
codeEvalPlugin(this.environment.config.consumer, env),
- cjsPlugin,
removePolyfills,
jsxComments,
- inlineEnvVarsPlugin(env, Deno.env.toObject()),
];
const res = babel.transformSync(code, {
diff --git a/packages/plugin-vite/src/plugins/patches/commonjs.ts b/packages/plugin-vite/src/plugins/patches/commonjs.ts
deleted file mode 100644
index 83a58bb2d5b..00000000000
--- a/packages/plugin-vite/src/plugins/patches/commonjs.ts
+++ /dev/null
@@ -1,959 +0,0 @@
-import type { NodePath, PluginObj, types } from "@babel/core";
-import { builtinModules } from "node:module";
-
-const BUILTINS = new Set(builtinModules);
-
-export function cjsPlugin(
- { types: t }: { types: typeof types },
-): PluginObj {
- const HAS_ES_MODULE = "esModule";
- const REQUIRE_CALLS = "requireCalls";
- const ROOT_SCOPE = "rootScope";
- const EXPORTED = "exported";
- const EXPORTED_NAMESPACES = "exported_namespaces";
- const ALIASED = "aliased";
- const REEXPORT = "re-export";
- const NEEDS_REQUIRE_IMPORT = "needsRequireImport";
- const NEEDS_DIRNAME_IMPORT = "needsDirnameImport";
- const IS_ESM = "isESM";
-
- return {
- name: "fresh-cjs-esm",
- pre(file) {
- const filename = file.opts.filename;
- if (filename) {
- if (filename.endsWith(".mjs") || filename.endsWith(".mts")) {
- this.set(IS_ESM, true);
- } else if (filename.endsWith(".cjs") || filename.endsWith(".cts")) {
- this.set(IS_ESM, false);
- }
- }
- },
- visitor: {
- Program: {
- enter(path, state) {
- state.set(ROOT_SCOPE, path.scope);
- state.set(EXPORTED, new Set());
- state.set(EXPORTED_NAMESPACES, new Set());
- state.set(REEXPORT, null);
-
- path.traverse({
- Import(_path, state) {
- state.set(IS_ESM, true);
- },
- ImportDeclaration(_path, state) {
- state.set(IS_ESM, true);
- },
- ExportAllDeclaration(_path, state) {
- state.set(IS_ESM, true);
- },
- ExportDefaultDeclaration(_path, state) {
- state.set(IS_ESM, true);
- },
- ExportNamedDeclaration(_path, state) {
- state.set(IS_ESM, true);
- },
- }, state);
- },
- exit(path, state) {
- const isESM = state.get(IS_ESM);
- if (isESM) return;
-
- const body = path.get("body");
- const requires = state.get(REQUIRE_CALLS);
- if (requires !== undefined) {
- for (let i = 0; i < requires.length; i++) {
- const { specifier, id } = requires[i];
- path.unshiftContainer(
- "body",
- t.importDeclaration(
- [t.importNamespaceSpecifier(id)],
- specifier,
- ),
- );
- }
- }
-
- const reexport = state.get(REEXPORT);
- const exported = state.get(EXPORTED);
- const exportedNs = state.get(EXPORTED_NAMESPACES);
- const needsRequireImport = state.get(NEEDS_REQUIRE_IMPORT);
- const hasEsModule = state.get(HAS_ES_MODULE);
-
- if (needsRequireImport) {
- // Inject:
- // ```ts
- // import { createRequire } from "node:module";
- // const require = createRequire(import.meta.url);
- // ```
- const id = t.identifier("createRequire");
- path.unshiftContainer(
- "body",
- t.variableDeclaration("const", [
- t.variableDeclarator(
- t.identifier("require"),
- t.callExpression(t.identifier("createRequire"), [
- t.memberExpression(
- t.metaProperty(
- t.identifier("import"),
- t.identifier("meta"),
- ),
- t.identifier("url"),
- ),
- ]),
- ),
- ]),
- );
- path.unshiftContainer(
- "body",
- t.importDeclaration(
- [t.importSpecifier(id, id)],
- t.stringLiteral("node:module"),
- ),
- );
- }
-
- const needsDirnameImport = state.get(NEEDS_DIRNAME_IMPORT);
- if (needsDirnameImport) {
- // Inject:
- // ```ts
- // import { fileURLToPath as __cjs_fileURLToPath } from "node:url";
- // import { dirname as __cjs_dirname } from "node:path";
- // const __filename = __cjs_fileURLToPath(import.meta.url);
- // const __dirname = __cjs_dirname(__filename);
- // ```
- const fileURLToPathId = t.identifier("__cjs_fileURLToPath");
- const dirnameId = t.identifier("__cjs_dirname");
- const importMetaUrl = t.memberExpression(
- t.metaProperty(
- t.identifier("import"),
- t.identifier("meta"),
- ),
- t.identifier("url"),
- );
-
- path.unshiftContainer(
- "body",
- t.variableDeclaration("var", [
- t.variableDeclarator(
- t.identifier("__dirname"),
- t.callExpression(dirnameId, [t.identifier("__filename")]),
- ),
- ]),
- );
- path.unshiftContainer(
- "body",
- t.variableDeclaration("var", [
- t.variableDeclarator(
- t.identifier("__filename"),
- t.callExpression(fileURLToPathId, [importMetaUrl]),
- ),
- ]),
- );
- path.unshiftContainer(
- "body",
- t.importDeclaration(
- [t.importSpecifier(dirnameId, t.identifier("dirname"))],
- t.stringLiteral("node:path"),
- ),
- );
- path.unshiftContainer(
- "body",
- t.importDeclaration(
- [
- t.importSpecifier(
- fileURLToPathId,
- t.identifier("fileURLToPath"),
- ),
- ],
- t.stringLiteral("node:url"),
- ),
- );
- }
-
- if (reexport !== null) {
- path.unshiftContainer(
- "body",
- t.exportAllDeclaration(t.cloneNode(reexport, true)),
- );
- }
-
- const mappedNs: string[] = [];
-
- for (const spec of exportedNs.values()) {
- const id = path.scope.generateUidIdentifier("__ns");
- mappedNs.push(id.name);
-
- path.unshiftContainer(
- "body",
- t.importDeclaration(
- [t.importNamespaceSpecifier(id)],
- t.stringLiteral(spec),
- ),
- );
- }
-
- if (exported.size > 0 || exportedNs.size > 0 || hasEsModule) {
- path.unshiftContainer(
- "body",
- t.expressionStatement(
- t.callExpression(
- t.memberExpression(
- t.identifier("Object"),
- t.identifier("defineProperty"),
- ),
- [
- t.identifier("exports"),
- t.stringLiteral("__esModule"),
- t.objectExpression([
- t.objectProperty(
- t.identifier("value"),
- t.booleanLiteral(true),
- ),
- ]),
- ],
- ),
- ),
- );
- path.unshiftContainer(
- "body",
- t.expressionStatement(
- t.callExpression(
- t.memberExpression(
- t.identifier("Object"),
- t.identifier("defineProperty"),
- ),
- [
- t.identifier("module"),
- t.stringLiteral("exports"),
- t.objectExpression([
- t.objectMethod(
- "method",
- t.identifier("get"),
- [],
- t.blockStatement([
- t.returnStatement(t.identifier("exports")),
- ]),
- ),
- t.objectMethod(
- "method",
- t.identifier("set"),
- [t.identifier("value")],
- t.blockStatement([
- t.expressionStatement(
- t.assignmentExpression(
- "=",
- t.identifier("exports"),
- t.identifier("value"),
- ),
- ),
- ]),
- ),
- ]),
- ],
- ),
- ),
- );
- path.unshiftContainer(
- "body",
- t.variableDeclaration("var", [
- t.variableDeclarator(
- t.identifier("exports"),
- t.objectExpression([]),
- ),
- t.variableDeclarator(
- t.identifier("module"),
- t.objectExpression([]),
- ),
- ]),
- );
- }
-
- const idExports: types.ExportSpecifier[] = [];
- for (const name of exported) {
- if (name === "default") {
- continue;
- }
-
- const id = path.scope.generateUidIdentifier(name);
-
- path.pushContainer(
- "body",
- t.variableDeclaration(
- "var",
- [t.variableDeclarator(
- id,
- t.memberExpression(
- t.identifier("exports"),
- t.identifier(name),
- ),
- )],
- ),
- );
- idExports.push(
- t.exportSpecifier(id, t.identifier(name)),
- );
- }
-
- if (idExports.length > 0) {
- path.pushContainer(
- "body",
- t.exportNamedDeclaration(null, idExports),
- );
- }
-
- if (exported.size > 0 || exportedNs.size > 0 || hasEsModule) {
- const id = path.scope.generateUidIdentifier("__default");
-
- // Use `var` instead of `const` to avoid TDZ errors when
- // Rollup reorders declarations in the bundled output.
- path.pushContainer(
- "body",
- t.variableDeclaration("var", [
- t.variableDeclarator(
- id,
- ),
- ]),
- );
-
- path.pushContainer(
- "body",
- t.ifStatement(
- t.logicalExpression(
- "&&",
- t.logicalExpression(
- "&&",
- t.binaryExpression(
- "===",
- t.unaryExpression("typeof", t.identifier("exports")),
- t.stringLiteral("object"),
- ),
- t.binaryExpression(
- "!==",
- t.identifier("exports"),
- t.nullLiteral(),
- ),
- ),
- t.binaryExpression(
- "in",
- t.stringLiteral("default"),
- t.identifier("exports"),
- ),
- ),
- t.blockStatement([
- t.expressionStatement(
- t.assignmentExpression(
- "=",
- id,
- t.memberExpression(
- t.identifier("exports"),
- t.identifier("default"),
- ),
- ),
- ),
- ]),
- t.blockStatement([
- t.expressionStatement(
- t.assignmentExpression("=", id, t.identifier("exports")),
- ),
- ]),
- ),
- );
-
- for (let i = 0; i < mappedNs.length; i++) {
- const mapped = mappedNs[i];
-
- const key = path.scope.generateUid("k");
- // Only spread namespace properties when the module has no
- // explicit default export (i.e. "default" not in exports).
- path.pushContainer(
- "body",
- t.ifStatement(
- t.logicalExpression(
- "&&",
- t.logicalExpression(
- "&&",
- t.binaryExpression(
- "===",
- t.unaryExpression("typeof", t.identifier("exports")),
- t.stringLiteral("object"),
- ),
- t.binaryExpression(
- "!==",
- t.identifier("exports"),
- t.nullLiteral(),
- ),
- ),
- t.unaryExpression(
- "!",
- t.binaryExpression(
- "in",
- t.stringLiteral("default"),
- t.identifier("exports"),
- ),
- ),
- ),
- t.forInStatement(
- t.variableDeclaration("var", [
- t.variableDeclarator(t.identifier(key)),
- ]),
- t.identifier(mapped),
- t.ifStatement(
- t.logicalExpression(
- "&&",
- t.logicalExpression(
- "&&",
- t.binaryExpression(
- "!==",
- t.identifier(key),
- t.stringLiteral("default"),
- ),
- t.binaryExpression(
- "!==",
- t.identifier(key),
- t.stringLiteral("__esModule"),
- ),
- ),
- t.callExpression(
- t.memberExpression(
- t.memberExpression(
- t.memberExpression(
- t.identifier("Object"),
- t.identifier("prototype"),
- ),
- t.identifier("hasOwnProperty"),
- ),
- t.identifier("call"),
- ),
- [t.identifier(mapped), t.identifier(key)],
- ),
- ),
- t.expressionStatement(
- t.assignmentExpression(
- "=",
- t.memberExpression(
- t.cloneNode(id, true),
- t.identifier(key),
- true,
- ),
- t.memberExpression(
- t.identifier(mapped),
- t.identifier(key),
- true,
- ),
- ),
- ),
- ),
- ),
- ),
- );
- }
-
- path.pushContainer("body", t.exportDefaultDeclaration(id));
- path.pushContainer(
- "body",
- t.exportNamedDeclaration(
- t.variableDeclaration("var", [
- t.variableDeclarator(
- t.identifier("__require"),
- t.identifier("exports"),
- ),
- ]),
- ),
- );
- }
-
- if (body.length === 0 && hasEsModule) {
- path.pushContainer("body", t.exportNamedDeclaration(null));
- } else if (hasEsModule) {
- path.pushContainer(
- "body",
- t.exportNamedDeclaration(
- t.variableDeclaration(
- "var",
- [t.variableDeclarator(
- t.identifier("__esModule"),
- t.memberExpression(
- t.identifier("exports"),
- t.identifier("__esModule"),
- ),
- )],
- ),
- ),
- );
- }
- },
- },
- CallExpression(path, state) {
- if (state.get(IS_ESM)) return;
- const exported = state.get(EXPORTED);
-
- if (isObjEsModuleFlag(t, path.node)) {
- state.set(HAS_ES_MODULE, true);
- return;
- }
-
- // Handle require.resolve() by injecting createRequire
- if (
- t.isMemberExpression(path.node.callee) &&
- t.isIdentifier(path.node.callee.object) &&
- path.node.callee.object.name === "require" &&
- t.isIdentifier(path.node.callee.property) &&
- path.node.callee.property.name === "resolve"
- ) {
- state.set(NEEDS_REQUIRE_IMPORT, true);
- return;
- }
-
- if (
- t.isIdentifier(path.node.callee) &&
- path.node.callee.name === "require"
- ) {
- const root = state.get(ROOT_SCOPE);
- const id = root.generateUidIdentifier("mod");
-
- const mods = state.get(REQUIRE_CALLS) ?? [];
- state.set(REQUIRE_CALLS, mods);
-
- const source = path.node.arguments[0];
- if (t.isStringLiteral(source)) {
- // Check if we can hoist it or if we need to keep it.
- let canImport = true;
- let parent: NodePath | null = path.parentPath;
- while (parent !== null) {
- if (
- t.isTryStatement(parent.node) || t.isIfStatement(parent.node) ||
- t.isConditionalExpression(parent.node)
- ) {
- canImport = false;
- break;
- }
- parent = parent.parentPath;
- }
-
- if (!canImport) {
- state.set(NEEDS_REQUIRE_IMPORT, true);
- return;
- }
-
- mods.push({
- id,
- specifier: t.cloneNode(path.node.arguments[0], true),
- });
-
- if (
- path.parentPath?.isVariableDeclarator() &&
- path.parentPath?.get("id").isIdentifier() ||
- path.parentPath?.isCallExpression()
- ) {
- // Vite json processing always adds a default property.
- if (source.value.endsWith(".json")) {
- path.replaceWith(
- t.logicalExpression(
- "??",
- t.memberExpression(
- t.cloneNode(id, true),
- t.identifier("default"),
- ),
- t.cloneNode(id, true),
- ),
- );
- } else if (
- path.parentPath?.isCallExpression() &&
- t.isIdentifier(path.parentPath.node.callee) &&
- path.parentPath.node.callee.name === "__importDefault"
- ) {
- if (isNodeBuiltin(source.value)) {
- path.replaceWith(t.objectExpression([
- t.objectProperty(
- t.identifier("__esModule"),
- t.booleanLiteral(true),
- ),
- t.objectProperty(
- t.identifier("default"),
- t.logicalExpression(
- "??",
- t.memberExpression(
- t.cloneNode(id, true),
- t.identifier("default"),
- ),
- t.cloneNode(id, true),
- ),
- ),
- ]));
- } else {
- path.replaceWith(t.cloneNode(id, true));
- }
- } else {
- path.replaceWith(
- t.logicalExpression(
- "??",
- t.memberExpression(
- t.cloneNode(id, true),
- t.identifier("__require"),
- ),
- t.logicalExpression(
- "??",
- t.memberExpression(
- t.cloneNode(id, true),
- t.identifier("default"),
- ),
- t.cloneNode(id, true),
- ),
- ),
- );
- }
- return;
- }
-
- path.replaceWith(t.cloneNode(id, true));
- } else {
- state.set(NEEDS_REQUIRE_IMPORT, true);
- }
- } else if (
- t.isMemberExpression(path.node.callee) &&
- t.isIdentifier(path.node.callee.object) &&
- path.node.callee.object.name === "Object" &&
- t.isIdentifier(path.node.callee.property) &&
- path.node.callee.property.name === "defineProperty" &&
- path.node.arguments.length > 0 &&
- t.isIdentifier(path.node.arguments[0]) &&
- path.node.arguments[0].name === "exports" &&
- t.isStringLiteral(path.node.arguments[1])
- ) {
- const name = path.node.arguments[1].value;
- exported.add(name);
- }
- },
- EmptyStatement(path) {
- path.remove();
- },
- MemberExpression: {
- exit(path, state) {
- if (state.get(IS_ESM)) return;
- if (
- t.isIdentifier(path.node.property) &&
- path.node.property.name !== "__esModule"
- ) {
- // Track both `exports.X` and `module.exports.X`
- if (
- t.isIdentifier(path.node.object) &&
- path.node.object.name === "exports"
- ) {
- state.get(EXPORTED).add(path.node.property.name);
- } else if (
- t.isMemberExpression(path.node.object) &&
- isModuleExports(t, path.node.object)
- ) {
- state.get(EXPORTED).add(path.node.property.name);
- }
- }
- },
- },
- ExpressionStatement: {
- enter(path, state) {
- if (state.get(IS_ESM)) return;
- // Check: Object.defineProperty(module.exports) "__esModule" ...)
- // Check: Object.defineProperty(exports) "__esModule" ...)
- // Check: a({}, "__esModule", ...)
- if (
- t.isCallExpression(path.node.expression) &&
- path.node.expression.arguments.length === 3 &&
- t.isStringLiteral(path.node.expression.arguments[1]) &&
- path.node.expression.arguments[1].value === "__esModule"
- ) {
- state.set(HAS_ES_MODULE, true);
- return;
- }
-
- if (
- t.isExpressionStatement(path.node) &&
- t.isCallExpression(path.node.expression) &&
- t.isIdentifier(path.node.expression.callee) &&
- path.node.expression.callee.name === "__exportStar" &&
- path.node.expression.arguments.length > 0 &&
- t.isCallExpression(path.node.expression.arguments[0]) &&
- t.isIdentifier(path.node.expression.arguments[0].callee) &&
- path.node.expression.arguments[0].callee.name === "require" &&
- t.isStringLiteral(path.node.expression.arguments[0].arguments[0])
- ) {
- const spec = t.cloneNode(
- path.node.expression.arguments[0].arguments[0],
- true,
- );
- state.get(EXPORTED_NAMESPACES).add(spec.value);
- path.replaceWith(t.exportAllDeclaration(spec));
- } else if (
- t.isExpressionStatement(path.node) &&
- t.isCallExpression(path.node.expression) &&
- t.isFunctionExpression(path.node.expression.callee)
- ) {
- if (
- path.node.expression.callee.params.length > 0 &&
- t.isIdentifier(path.node.expression.callee.params[0])
- ) {
- const alias = path.node.expression.callee.params[0].name;
- state.set(ALIASED, alias);
- }
- } else if (
- // Check: Object.defineProperty(exports, "foo", { enumerable: true, get: function () { return foo; } });
- t.isCallExpression(path.node.expression) &&
- t.isMemberExpression(path.node.expression.callee) &&
- t.isIdentifier(path.node.expression.callee.object) &&
- path.node.expression.callee.object.name === "Object" &&
- t.isIdentifier(path.node.expression.callee.property) &&
- path.node.expression.callee.property.name === "defineProperty" &&
- path.node.expression.arguments.length >= 2 &&
- t.isIdentifier(path.node.expression.arguments[0]) &&
- path.node.expression.arguments[0].name === "exports" &&
- t.isStringLiteral(path.node.expression.arguments[1]) &&
- t.isObjectExpression(path.node.expression.arguments[2])
- ) {
- const exported = path.node.expression.arguments[1].value;
- const obj = path.node.expression.arguments[2];
- for (let i = 0; i < obj.properties.length; i++) {
- const prop = obj.properties[i];
-
- if (
- t.isObjectProperty(prop) && t.isIdentifier(prop.key) &&
- prop.key.name === "get" && t.isFunctionExpression(prop.value) &&
- t.isBlockStatement(prop.value.body) &&
- prop.value.body.body.length === 1 &&
- t.isReturnStatement(prop.value.body.body[0])
- ) {
- const expr = prop.value.body.body[0].argument;
- if (expr !== null && expr !== undefined) {
- path.replaceWith(
- t.assignmentExpression(
- "=",
- t.memberExpression(
- t.identifier("exports"),
- t.identifier(exported),
- ),
- t.cloneNode(expr, true),
- ),
- );
- }
- } else if (
- t.isObjectMethod(prop) && t.isIdentifier(prop.key) &&
- prop.key.name === "get" && t.isBlockStatement(prop.body) &&
- prop.body.body.length === 1 &&
- t.isReturnStatement(prop.body.body[0])
- ) {
- const expr = prop.body.body[0].argument;
- if (expr !== null && expr !== undefined) {
- path.replaceWith(
- t.assignmentExpression(
- "=",
- t.memberExpression(
- t.identifier("exports"),
- t.identifier(exported),
- ),
- t.cloneNode(expr, true),
- ),
- );
- }
- }
- }
- } else if (
- // Check: module.exports = require(...)
- t.isAssignmentExpression(path.node.expression) &&
- t.isMemberExpression(path.node.expression.left) &&
- t.isIdentifier(path.node.expression.left.object) &&
- t.isIdentifier(path.node.expression.left.property) &&
- path.node.expression.left.object.name === "module" &&
- path.node.expression.left.property.name === "exports" &&
- t.isCallExpression(path.node.expression.right) &&
- t.isIdentifier(path.node.expression.right.callee) &&
- path.node.expression.right.callee.name === "require" &&
- path.node.expression.right.arguments.length === 1 &&
- t.isStringLiteral(path.node.expression.right.arguments[0])
- ) {
- const source = path.node.expression.right.arguments[0];
- state.set(REEXPORT, source);
- } else {
- let depth = 0;
- let current = path.node.expression;
-
- while (
- t.isAssignmentExpression(current) &&
- t.isMemberExpression(current.left) &&
- t.isIdentifier(current.left.object) &&
- current.left.object.name === "exports"
- ) {
- if (
- t.isUnaryExpression(current.right) &&
- current.right.operator === "void" &&
- t.isNumericLiteral(current.right.argument) &&
- current.right.argument.value === 0
- ) {
- if (depth > 0) {
- path.remove();
- }
-
- break;
- }
-
- depth++;
- current = current.right;
- }
- }
- },
- exit(path, state) {
- if (state.get(IS_ESM)) return;
- const exported = state.get(EXPORTED);
- const expr = path.get("expression");
-
- if (expr.isAssignmentExpression()) {
- const left = expr.get("left");
-
- if (isEsModuleFlag(t, expr.node)) {
- state.set(HAS_ES_MODULE, true);
- } else if (left.isMemberExpression()) {
- if (isModuleExports(t, left.node)) {
- // Should always try to create synthetic default export in this case.
- exported.add("default");
-
- if (t.isObjectExpression(expr.node.right)) {
- const properties = expr.node.right.properties;
- for (let i = 0; i < properties.length; i++) {
- const prop = properties[i];
- if (t.isObjectProperty(prop)) {
- if (t.isIdentifier(prop.key)) {
- if (prop.key.name === "__esModule") {
- continue;
- }
-
- exported.add(prop.key.name);
- }
- }
- }
- }
- } else {
- const named = getExportsAssignName(t, left.node);
- if (named === null) return;
- exported.add(named);
- }
- }
- } else if (expr.isCallExpression()) {
- if (isObjEsModuleFlag(t, expr.node)) {
- state.set(HAS_ES_MODULE, true);
- }
- }
- },
- },
- VariableDeclaration(path) {
- if (path.node.declarations.length === 0) {
- path.remove();
- }
- },
- ConditionalExpression(path, state) {
- if (state.get(IS_ESM)) return;
-
- if (
- t.isBinaryExpression(path.node.test) &&
- t.isUnaryExpression(path.node.test.left) &&
- path.node.test.left.operator === "typeof" &&
- t.isIdentifier(path.node.test.left.argument) &&
- path.node.test.left.argument.name === "exports" &&
- path.node.test.operator === "==="
- ) {
- path.replaceWith(t.cloneNode(path.node.alternate, true));
- }
- },
- Identifier(path, state) {
- if (state.get(IS_ESM)) return;
-
- const name = path.node.name;
- if (name !== "__dirname" && name !== "__filename") return;
-
- // Skip if this is already a declaration (e.g. our own polyfill)
- if (
- path.parentPath?.isVariableDeclarator() &&
- path.parentPath.get("id") === path
- ) return;
-
- state.set(NEEDS_DIRNAME_IMPORT, true);
- },
- AssignmentExpression(path, state) {
- if (state.get(IS_ESM)) return;
-
- const exported = state.get(EXPORTED);
- const aliased = state.get(ALIASED);
- if (aliased === undefined) return;
-
- if (
- path.node.operator === "=" && t.isMemberExpression(path.node.left) &&
- t.isIdentifier(path.node.left.object) &&
- path.node.left.object.name === aliased &&
- t.isIdentifier(path.node.left.property)
- ) {
- const name = path.node.left.property.name;
- exported.add(name);
- }
- },
- },
- };
-}
-
-function isModuleExports(
- t: typeof types,
- node: types.MemberExpression,
-): boolean {
- return t.isIdentifier(node.object) && node.object.name === "module" &&
- t.isIdentifier(node.property) && node.property.name === "exports";
-}
-
-function getExportsAssignName(
- t: typeof types,
- node: types.MemberExpression,
-): string | null {
- if (
- (t.isMemberExpression(node.object) &&
- isModuleExports(t, node.object) ||
- t.isIdentifier(node.object) && node.object.name === "exports") &&
- t.isIdentifier(node.property)
- ) {
- return node.property.name;
- }
-
- return null;
-}
-
-/**
- * Detect `exports.__esModule = true;`
- */
-function isEsModuleFlag(
- t: typeof types,
- node: types.AssignmentExpression,
-): boolean {
- if (!t.isMemberExpression(node.left)) return false;
-
- const { left, right } = node;
- return (t.isMemberExpression(left.object) &&
- isModuleExports(t, left.object) ||
- t.isIdentifier(left.object) && left.object.name === "exports") &&
- t.isIdentifier(left.property) && left.property.name === "__esModule" &&
- t.isBooleanLiteral(right);
-}
-
-/**
- * Check for `Object.defineProperty(exports, '__esModule', { value: true })`
- */
-function isObjEsModuleFlag(
- t: typeof types,
- node: types.CallExpression,
-): boolean {
- return node.arguments.length === 3 &&
- t.isStringLiteral(node.arguments[1]) &&
- node.arguments[1].value === "__esModule" &&
- t.isObjectExpression(node.arguments[2]);
-}
-
-function isNodeBuiltin(specifier: string): boolean {
- return BUILTINS.has(specifier) || (
- specifier.startsWith("node:")
- ? BUILTINS.has(specifier.slice("node:".length))
- : BUILTINS.has(`node:${specifier}`)
- );
-}
diff --git a/packages/plugin-vite/src/plugins/patches/commonjs_test.ts b/packages/plugin-vite/src/plugins/patches/commonjs_test.ts
deleted file mode 100644
index 4c337babca2..00000000000
--- a/packages/plugin-vite/src/plugins/patches/commonjs_test.ts
+++ /dev/null
@@ -1,835 +0,0 @@
-import { expect } from "@std/expect/expect";
-import * as babel from "@babel/core";
-import { cjsPlugin } from "../patches/commonjs.ts";
-
-function runTest(
- options: { input: string; expected: string; filename?: string },
-) {
- const res = babel.transformSync(options.input, {
- filename: options.filename ?? "foo.js",
- babelrc: false,
- plugins: [cjsPlugin],
- });
-
- const output = res?.code ?? "";
- expect(output).toEqual(options.expected);
-}
-
-const INIT = `var exports = {},
- module = {};
-Object.defineProperty(module, "exports", {
- get() {
- return exports;
- },
- set(value) {
- exports = value;
- }
-});
-Object.defineProperty(exports, "__esModule", {
- value: true
-});`;
-
-const DEFAULT_EXPORT = `var _default;
-if (typeof exports === "object" && exports !== null && "default" in exports) {
- _default = exports.default;
-} else {
- _default = exports;
-}`;
-
-const DEFAULT_EXPORT_END = `export default _default;
-export var __require = exports;`;
-const IMPORT_REQUIRE = `import { createRequire } from "node:module";
-const require = createRequire(import.meta.url);`;
-const EXPORT_ES_MODULE = `export var __esModule = exports.__esModule;`;
-
-Deno.test("commonjs - module.exports default", () => {
- runTest({
- input: `module.exports = async function () {};`,
- expected: `${INIT}
-module.exports = async function () {};
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - module.exports default primitive", () => {
- runTest({
- input: `module.exports = 42;`,
- expected: `${INIT}
-module.exports = 42;
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - exports with default + named", () => {
- runTest({
- input: `exports.__esModule = true;
-exports.default = 'x';
-exports.foo = 'foo';`,
- expected: `${INIT}
-exports.__esModule = true;
-exports.default = 'x';
-exports.foo = 'foo';
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - module.exports with default + named", () => {
- runTest({
- input: `module.exports.__esModule = true;
-module.exports.default = 'x';
-module.exports.foo = 'foo';`,
- expected: `${INIT}
-module.exports.__esModule = true;
-module.exports.default = 'x';
-module.exports.foo = 'foo';
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - Object es module flag with named clash", () => {
- runTest({
- input: `Object.defineProperty(exports, '__esModule', { value: true });
-exports.foo = 'bar';
-const foo = 'also bar';
-`,
- expected: `${INIT}
-Object.defineProperty(exports, '__esModule', {
- value: true
-});
-exports.foo = 'bar';
-const foo = 'also bar';
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - Object es module flag with named + default", () => {
- runTest({
- input: `Object.defineProperty(exports, '__esModule', { value: true });
-exports.default = 'foo';
-exports.foo = 'bar';
-`,
- expected: `${INIT}
-Object.defineProperty(exports, '__esModule', {
- value: true
-});
-exports.default = 'foo';
-exports.foo = 'bar';
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - esModule flag only", () => {
- runTest({
- input: `Object.defineProperty(exports, "__esModule", { value: true });`,
- expected: `${INIT}
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - esModule flag only #2", () => {
- runTest({
- input:
- `Object.defineProperty(module.exports, "__esModule", { value: true });`,
- expected: `${INIT}
-Object.defineProperty(module.exports, "__esModule", {
- value: true
-});
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - esModule flag only minified #3", () => {
- runTest({
- input: `Object.defineProperty(exports, '__esModule', { value: !0 });`,
- expected: `${INIT}
-Object.defineProperty(exports, '__esModule', {
- value: !0
-});
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - exports only named", () => {
- runTest({
- input: `Object.defineProperty(exports, '__esModule', { value: true });
-exports.foo = 'bar';
-exports.bar = 'foo';
-`,
- expected: `${INIT}
-Object.defineProperty(exports, '__esModule', {
- value: true
-});
-exports.foo = 'bar';
-exports.bar = 'foo';
-var _foo = exports.foo;
-var _bar = exports.bar;
-export { _foo as foo, _bar as bar };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - require", () => {
- runTest({
- input: `var foo = require("tape");
-console.log(foo);
-`,
- expected: `import * as _mod from "tape";
-var foo = _mod.__require ?? _mod.default ?? _mod;
-console.log(foo);`,
- });
-});
-
-Deno.test("commonjs - require destructure", () => {
- runTest({
- input: `var { foo } = require("tape");
-console.log(foo);
-`,
- expected: `import * as _mod from "tape";
-var {
- foo
-} = _mod;
-console.log(foo);`,
- });
-});
-
-Deno.test("commonjs - require assign", () => {
- runTest({
- input: `foo = require("tape");
-console.log(foo);
-`,
- expected: `import * as _mod from "tape";
-foo = _mod;
-console.log(foo);`,
- });
-});
-
-Deno.test("commonjs - require assign pattern", () => {
- runTest({
- input: `foo = require("tape");
-console.log(foo);
-`,
- expected: `import * as _mod from "tape";
-foo = _mod;
-console.log(foo);`,
- });
-});
-
-Deno.test("commonjs - require function call", () => {
- runTest({
- input: `var a = require('./a')()`,
- expected: `import * as _mod from './a';
-var a = (_mod.__require ?? _mod.default ?? _mod)();`,
- });
-});
-
-Deno.test("commonjs - require var decls", () => {
- runTest({
- input: `var a = require('./a'), b = 42;`,
- expected: `import * as _mod from './a';
-var a = _mod.__require ?? _mod.default ?? _mod,
- b = 42;`,
- });
-});
-
-Deno.test("commonjs - duplicate exports", () => {
- runTest({
- input: `Object.defineProperty(exports, "__esModule", { value: true });
-exports.trace = void 0;
-exports.trace = 'foo'`,
- expected: `${INIT}
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.trace = void 0;
-exports.trace = 'foo';
-var _trace = exports.trace;
-export { _trace as trace };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - cleared exports", () => {
- runTest({
- input: `Object.defineProperty(exports, "__esModule", { value: true });
-exports.foo = exports.bar = void 0;
-exports.foo = 'foo'`,
- expected: `${INIT}
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.foo = 'foo';
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - define exports", () => {
- runTest({
- input: `var utils_1 = require("./bar");
-Object.defineProperty(exports, "foo", { enumerable: true, get: function () { return utils_1.foo; } });`,
- expected: `${INIT}
-import * as _mod from "./bar";
-var utils_1 = _mod.__require ?? _mod.default ?? _mod;
-exports.foo = utils_1.foo;
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - define exports #2", () => {
- runTest({
- input: `var utils_1 = require("./bar");
-Object.defineProperty(exports, "foo", { enumerable: true, get() { return utils_1.foo; } });`,
- expected: `${INIT}
-import * as _mod from "./bar";
-var utils_1 = _mod.__require ?? _mod.default ?? _mod;
-exports.foo = utils_1.foo;
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - define exports #3", () => {
- runTest({
- input: `Object.defineProperty(exports, "__esModule", { value: true });
-exports._globalThis = void 0;
-exports._globalThis = typeof globalThis === 'object' ? globalThis : global;`,
- expected: `${INIT}
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports._globalThis = void 0;
-exports._globalThis = typeof globalThis === 'object' ? globalThis : global;
-var _globalThis = exports._globalThis;
-export { _globalThis };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - named function", () => {
- runTest({
- input: `Object.defineProperty(exports, "__esModule", { value: true });
-function foo() {};
-exports.foo = foo;`,
- expected: `${INIT}
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-function foo() {}
-exports.foo = foo;
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - detect esbuild shims", () => {
- runTest({
- input: `__exportStar(require("./globalThis"), exports);`,
- expected: `${INIT}
-import * as _ns from "./globalThis";
-export * from "./globalThis";
-${DEFAULT_EXPORT}
-if (typeof exports === "object" && exports !== null && !("default" in exports)) for (var _k in _ns) if (_k !== "default" && _k !== "__esModule" && Object.prototype.hasOwnProperty.call(_ns, _k)) _default[_k] = _ns[_k];
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - exports.default", () => {
- runTest({
- input: `exports.default = {}`,
- expected: `${INIT}
-exports.default = {};
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - multiple same name", () => {
- runTest({
- input: `exports.VERSION = void 0;
-exports.VERSION = '1.9.0';`,
- expected: `${INIT}
-exports.VERSION = void 0;
-exports.VERSION = '1.9.0';
-var _VERSION = exports.VERSION;
-export { _VERSION as VERSION };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - export enum", () => {
- runTest({
- input: `Object.defineProperty(exports, "__esModule", { value: true });
-exports.DiagLogLevel = void 0;
-var DiagLogLevel;
-(function (DiagLogLevel) {
- DiagLogLevel[DiagLogLevel["ALL"] = 9999] = "ALL";
-})(DiagLogLevel = exports.DiagLogLevel || (exports.DiagLogLevel = {}));`,
- expected: `${INIT}
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-exports.DiagLogLevel = void 0;
-var DiagLogLevel;
-(function (DiagLogLevel) {
- DiagLogLevel[DiagLogLevel["ALL"] = 9999] = "ALL";
-})(DiagLogLevel = exports.DiagLogLevel || (exports.DiagLogLevel = {}));
-var _DiagLogLevel = exports.DiagLogLevel;
-export { _DiagLogLevel as DiagLogLevel };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - require", () => {
- runTest({
- input: `module.exports = { __esModule: true, default: { foo: 'bar' }}`,
- expected: `${INIT}
-module.exports = {
- __esModule: true,
- default: {
- foo: 'bar'
- }
-};
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - export default object", () => {
- runTest({
- input: `Object.defineProperty(exports, '__esModule', { value: true });
-module.exports = { foo: 'bar' };
-`,
- expected: `${INIT}
-Object.defineProperty(exports, '__esModule', {
- value: true
-});
-module.exports = {
- foo: 'bar'
-};
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - detect iife wrapper", () => {
- runTest({
- input: `;(function (sax) {
- sax.foo = "foo";
-})(typeof exports === 'undefined' ? this.sax = {} : exports);`,
- expected: `${INIT}
-(function (sax) {
- sax.foo = "foo";
-})(exports);
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - re-export", () => {
- runTest({
- input:
- `;var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
-}) : (function(o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- o[k2] = m[k];
-}));
-var __exportStar = (this && this.__exportStar) || function(m, exports) {
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-__exportStar(require("./node"), exports);`,
- expected: `${INIT}
-import * as _ns from "./node";
-var __createBinding = this && this.__createBinding || (Object.create ? function (o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- Object.defineProperty(o, k2, {
- enumerable: true,
- get: function () {
- return m[k];
- }
- });
-} : function (o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- o[k2] = m[k];
-});
-var __exportStar = this && this.__exportStar || function (m, exports) {
- for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
-};
-Object.defineProperty(exports, "__esModule", {
- value: true
-});
-export * from "./node";
-${DEFAULT_EXPORT}
-if (typeof exports === "object" && exports !== null && !("default" in exports)) for (var _k in _ns) if (_k !== "default" && _k !== "__esModule" && Object.prototype.hasOwnProperty.call(_ns, _k)) _default[_k] = _ns[_k];
-${DEFAULT_EXPORT_END}
-${EXPORT_ES_MODULE}`,
- });
-});
-
-Deno.test("commonjs - assign module.exports", () => {
- runTest({
- input: `module.exports = { foo: 1 };`,
- expected: `${INIT}
-module.exports = {
- foo: 1
-};
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - require non-analyzable arg", () => {
- runTest({
- input: `const pkg = require(path.join(basedir, "package.json"))`,
- expected: `import { createRequire } from "node:module";
-const require = createRequire(import.meta.url);
-const pkg = require(path.join(basedir, "package.json"));`,
- });
-});
-
-Deno.test("commonjs - keep binding", () => {
- runTest({
- input: `export var __createBinding = Object.create ? 1 : 2;`,
- expected: `export var __createBinding = Object.create ? 1 : 2;`,
- });
-});
-
-Deno.test("commonjs - require lazy import", () => {
- runTest({
- input: `if (typeof process.env.NODE_PG_FORCE_NATIVE !== 'undefined') {
- module.exports = new PG(require('./native'))
-} else {
- module.exports = new PG(Client)
-
- // lazy require native module...the native module may not have installed
- Object.defineProperty(module.exports, 'native', {
- configurable: true,
- enumerable: false,
- get() {
- let native = null
- try {
- native = new PG(require('./native'))
- } catch (err) {
- if (err.code !== 'MODULE_NOT_FOUND') {
- throw err
- }
- }
-
- // overwrite module.exports.native so that getter is never called again
- Object.defineProperty(module.exports, 'native', {
- value: native,
- })
-
- return native
- },
- })
-}`,
- expected: `${INIT}
-${IMPORT_REQUIRE}
-if (typeof process.env.NODE_PG_FORCE_NATIVE !== 'undefined') {
- module.exports = new PG(require('./native'));
-} else {
- module.exports = new PG(Client);
-
- // lazy require native module...the native module may not have installed
- Object.defineProperty(module.exports, 'native', {
- configurable: true,
- enumerable: false,
- get() {
- let native = null;
- try {
- native = new PG(require('./native'));
- } catch (err) {
- if (err.code !== 'MODULE_NOT_FOUND') {
- throw err;
- }
- }
-
- // overwrite module.exports.native so that getter is never called again
- Object.defineProperty(module.exports, 'native', {
- value: native
- });
- return native;
- }
- });
-}
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - wrapped iife binding", () => {
- runTest({
- input: `"production" !== process.env.NODE_ENV && (function() {
- exports.foo = 123
-})()`,
- expected: `${INIT}
-"production" !== process.env.NODE_ENV && function () {
- exports.foo = 123;
-}();
-var _foo = exports.foo;
-export { _foo as foo };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - re-export #2", () => {
- runTest({
- input: `module.exports = require("foo");`,
- expected: `${INIT}
-export * from "foo";
-import * as _mod from "foo";
-module.exports = _mod;
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - keep require() in .mjs", () => {
- runTest({
- filename: "foo.mjs",
- input: `try { require("foo"); } catch {}`,
- expected: `try {
- require("foo");
-} catch {}`,
- });
-});
-
-Deno.test("commonjs - keep conditional require() in ESM file", () => {
- runTest({
- filename: "foo.mjs",
- input: `try { require("foo"); } catch {};
-export {};`,
- expected: `try {
- require("foo");
-} catch {}
-export {};`,
- });
-});
-
-Deno.test("commonjs - CJS turned ESM module", () => {
- runTest({
- filename: "foo.mjs",
- input: `module.exports.create = confettiCannon;
-export default module.exports;
-export var create = module.exports.create;`,
- expected: `module.exports.create = confettiCannon;
-export default module.exports;
-export var create = module.exports.create;`,
- });
-});
-
-Deno.test("commonjs - minified __esModule", () => {
- runTest({
- filename: "foo.js",
- input: `
-const m = module.exports;
-const a = Object.defineProperty;
-a(m, "__esModule", { value: !0 });`,
- expected: `${INIT}
-const m = module.exports;
-const a = Object.defineProperty;
-a(m, "__esModule", {
- value: !0
-});
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}
-export var __esModule = exports.__esModule;`,
- });
-});
-
-Deno.test("commonjs - esbuild __importDefault", () => {
- runTest({
- input:
- `var __importDefault = (this && this.__importDefault) || function (mod) {
- return (mod && mod.__esModule) ? mod : { "default": mod };
-};
-const node_events_1 = __importDefault(require("node:events"));`,
- expected: `import * as _mod from "node:events";
-var __importDefault = this && this.__importDefault || function (mod) {
- return mod && mod.__esModule ? mod : {
- "default": mod
- };
-};
-const node_events_1 = __importDefault({
- __esModule: true,
- default: _mod.default ?? _mod
-});`,
- });
-});
-
-// --- New tests for CJS transform fixes ---
-
-Deno.test("commonjs - require.resolve injects createRequire", () => {
- runTest({
- input: `var resolved = require.resolve("some-package");`,
- expected: `${IMPORT_REQUIRE}
-var resolved = require.resolve("some-package");`,
- });
-});
-
-Deno.test("commonjs - require.resolve with require() both inject createRequire once", () => {
- runTest({
- input: `var resolved = require.resolve("some-package");
-if (true) {
- var mod = require("other");
-}`,
- expected: `${IMPORT_REQUIRE}
-var resolved = require.resolve("some-package");
-if (true) {
- var mod = require("other");
-}`,
- });
-});
-
-Deno.test("commonjs - .mts file treated as ESM", () => {
- runTest({
- filename: "foo.mts",
- input: `export const x = 1;`,
- expected: `export const x = 1;`,
- });
-});
-
-Deno.test("commonjs - .cts file treated as CJS", () => {
- runTest({
- filename: "foo.cts",
- input: `module.exports = 42;`,
- expected: `${INIT}
-module.exports = 42;
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - __dirname and __filename polyfill", () => {
- const DIRNAME_IMPORT =
- `import { fileURLToPath as __cjs_fileURLToPath } from "node:url";
-import { dirname as __cjs_dirname } from "node:path";
-var __filename = __cjs_fileURLToPath(import.meta.url);
-var __dirname = __cjs_dirname(__filename);`;
- runTest({
- input: `var dir = __dirname;
-var file = __filename;
-module.exports = { dir: dir, file: file };`,
- expected: `${INIT}
-${DIRNAME_IMPORT}
-var dir = __dirname;
-var file = __filename;
-module.exports = {
- dir: dir,
- file: file
-};
-var _dir = exports.dir;
-var _file = exports.file;
-export { _dir as dir, _file as file };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - module.exports.X tracked as named export", () => {
- runTest({
- input: `module.exports = function parse() {};
-module.exports.parse = module.exports;
-module.exports.stringify = function stringify() {};`,
- expected: `${INIT}
-module.exports = function parse() {};
-module.exports.parse = module.exports;
-module.exports.stringify = function stringify() {};
-var _parse = exports.parse;
-var _stringify = exports.stringify;
-export { _parse as parse, _stringify as stringify };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs imitating esm - default export exists", () => {
- runTest({
- input: `module.exports = {
- 'default': 'string',
- otherExport: 1
-};
-`,
- expected: `${INIT}
-module.exports = {
- 'default': 'string',
- otherExport: 1
-};
-var _otherExport = exports.otherExport;
-export { _otherExport as otherExport };
-${DEFAULT_EXPORT}
-${DEFAULT_EXPORT_END}`,
- });
-});
-
-Deno.test("commonjs - primitive module.exports with namespace re-export guards assignment", () => {
- runTest({
- input: `__exportStar(require("./utils"), exports);
-module.exports = "RFC3986";`,
- expected: `${INIT}
-import * as _ns from "./utils";
-export * from "./utils";
-module.exports = "RFC3986";
-${DEFAULT_EXPORT}
-if (typeof exports === "object" && exports !== null && !("default" in exports)) for (var _k in _ns) if (_k !== "default" && _k !== "__esModule" && Object.prototype.hasOwnProperty.call(_ns, _k)) _default[_k] = _ns[_k];
-${DEFAULT_EXPORT_END}`,
- });
-});
diff --git a/packages/plugin-vite/src/plugins/patches/inline_env_vars.ts b/packages/plugin-vite/src/plugins/patches/inline_env_vars.ts
deleted file mode 100644
index 820247d1345..00000000000
--- a/packages/plugin-vite/src/plugins/patches/inline_env_vars.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-import type { NodePath, PluginObj, types } from "@babel/core";
-
-export function inlineEnvVarsPlugin(mode: string, env: Record) {
- const allowed = new Map();
- for (const [name, value] of Object.entries(env)) {
- if (name.startsWith("FRESH_PUBLIC_")) {
- allowed.set(name, value);
- }
- }
-
- allowed.set("NODE_ENV", mode);
-
- return (
- { types: t }: { types: typeof types },
- ): PluginObj => {
- function replace(path: NodePath, name: string) {
- if (allowed.has(name)) {
- const value = allowed.get(name);
-
- if (value !== undefined) {
- path.replaceWith(t.stringLiteral(value));
- } else {
- path.replaceWith(t.identifier("undefined"));
- }
- }
- }
-
- return {
- name: "fresh-env-var",
- visitor: {
- MemberExpression(path) {
- // Check: process.env.*
- if (
- t.isMemberExpression(path.node.object) &&
- t.isIdentifier(path.node.object.object) &&
- path.node.object.object.name === "process" &&
- t.isIdentifier(path.node.object.property) &&
- path.node.object.property.name === "env" &&
- t.isIdentifier(path.node.property)
- ) {
- const name = path.node.property.name;
- replace(path, name);
- }
-
- // Check: import.meta.env.*
- if (
- t.isIdentifier(path.node.property) &&
- t.isMemberExpression(path.node.object) &&
- t.isIdentifier(path.node.object.property) &&
- path.node.object.property.name === "env" &&
- t.isMetaProperty(path.node.object.object)
- ) {
- const name = path.node.property.name;
- replace(path, name);
- }
- },
- CallExpression(path) {
- // Check: Deno.env.get("")
- if (
- t.isMemberExpression(path.node.callee) &&
- t.isMemberExpression(path.node.callee.object) &&
- t.isIdentifier(path.node.callee.object.object) &&
- path.node.callee.object.object.name === "Deno" &&
- t.isIdentifier(path.node.callee.object.property) &&
- path.node.callee.object.property.name === "env" &&
- t.isIdentifier(path.node.callee.property) &&
- path.node.callee.property.name === "get" &&
- path.node.arguments.length > 0 &&
- t.isStringLiteral(path.node.arguments[0])
- ) {
- const name = path.node.arguments[0].value;
- replace(path, name);
- }
- },
- },
- };
- };
-}
diff --git a/packages/plugin-vite/src/plugins/patches/inline_env_vars_test.ts b/packages/plugin-vite/src/plugins/patches/inline_env_vars_test.ts
deleted file mode 100644
index 424fb2501fd..00000000000
--- a/packages/plugin-vite/src/plugins/patches/inline_env_vars_test.ts
+++ /dev/null
@@ -1,79 +0,0 @@
-import { expect } from "@std/expect/expect";
-import * as babel from "@babel/core";
-import { inlineEnvVarsPlugin } from "./inline_env_vars.ts";
-
-function runTest(
- options: {
- input: string;
- expected: string;
- mode?: string;
- env?: Record;
- },
-) {
- const res = babel.transformSync(options.input, {
- filename: "foo.js",
- babelrc: false,
- plugins: [
- inlineEnvVarsPlugin(options.mode ?? "development", options.env ?? {}),
- ],
- });
-
- const output = res?.code ?? "";
- expect(output).toEqual(options.expected);
-}
-
-Deno.test("env vars - inline NODE_ENV mode", () => {
- runTest({
- input: `() => process.env.NODE_ENV`,
- expected: `() => "asdf";`,
- mode: "asdf",
- });
-});
-
-Deno.test("env vars - inline custom process.env.*", () => {
- runTest({
- input: `() => process.env.FRESH_PUBLIC_FOO`,
- expected: `() => "a";`,
- env: {
- FRESH_PUBLIC_FOO: "a",
- },
- });
-});
-
-Deno.test("env vars - inline Deno.env.get()", () => {
- runTest({
- input: `() => Deno.env.get("FRESH_PUBLIC_FOO")`,
- expected: `() => "b";`,
- env: {
- FRESH_PUBLIC_FOO: "b",
- },
- });
-});
-
-Deno.test("env vars - inline Deno.env.get(NODE_ENV)", () => {
- runTest({
- input: `() => Deno.env.get("NODE_ENV")`,
- expected: `() => "c";`,
- mode: "c",
- });
-});
-
-Deno.test("env vars - inline const _ = Deno.env.get()", () => {
- runTest({
- input: `const deno = Deno.env.get("FRESH_PUBLIC_FOO");`,
- expected: `const deno = "test";`,
- env: {
- FRESH_PUBLIC_FOO: "test",
- },
- });
-});
-
-Deno.test("env vars - inline import.meta.env.FRESH_PUBLIC_FOO", () => {
- runTest({
- input: `() => import.meta.env.FRESH_PUBLIC_FOO;`,
- expected: `() => "test";`,
- env: {
- FRESH_PUBLIC_FOO: "test",
- },
- });
-});
diff --git a/packages/plugin-vite/tests/config_test.ts b/packages/plugin-vite/tests/config_test.ts
index cd650105de8..366275c4bd6 100644
--- a/packages/plugin-vite/tests/config_test.ts
+++ b/packages/plugin-vite/tests/config_test.ts
@@ -2,7 +2,7 @@ import { expect } from "@std/expect";
import { fresh } from "../src/mod.ts";
import type { Plugin } from "vite";
-Deno.test("fresh plugin - sets server.watch.ignored patterns", () => {
+Deno.test("fresh plugin - sets server.watch.ignored patterns", async () => {
const plugins = fresh() as Plugin[];
const freshPlugin = plugins.find((p) => p.name === "fresh");
expect(freshPlugin).toBeDefined();
@@ -10,7 +10,7 @@ Deno.test("fresh plugin - sets server.watch.ignored patterns", () => {
// Call the config hook as Vite would during dev
// deno-lint-ignore no-explicit-any
const configFn = freshPlugin!.config as any;
- const result = configFn({}, { command: "serve" });
+ const result = await configFn({}, { command: "serve" });
const ignored = result?.server?.watch?.ignored;
expect(ignored).toBeDefined();