diff --git a/deno.lock b/deno.lock
index 2cfe7d53bde..3d5970098a8 100644
--- a/deno.lock
+++ b/deno.lock
@@ -1,7 +1,6 @@
{
"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",
diff --git a/packages/plugin-vite/demo/fixtures/commonjs_mod.cjs b/packages/plugin-vite/demo/fixtures/commonjs_mod.cjs
new file mode 100644
index 00000000000..1f6a76bc478
--- /dev/null
+++ b/packages/plugin-vite/demo/fixtures/commonjs_mod.cjs
@@ -0,0 +1 @@
+exports.value = "ok";
diff --git a/packages/plugin-vite/demo/fixtures/commonjs_mod.js b/packages/plugin-vite/demo/fixtures/commonjs_mod.js
deleted file mode 100644
index 44d44847d02..00000000000
--- a/packages/plugin-vite/demo/fixtures/commonjs_mod.js
+++ /dev/null
@@ -1 +0,0 @@
-export const value = "ok";
diff --git a/packages/plugin-vite/demo/fixtures/maxmind.cjs b/packages/plugin-vite/demo/fixtures/maxmind.cjs
new file mode 100644
index 00000000000..bbef3806916
--- /dev/null
+++ b/packages/plugin-vite/demo/fixtures/maxmind.cjs
@@ -0,0 +1,8 @@
+"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
deleted file mode 100644
index 8710fa40a4c..00000000000
--- a/packages/plugin-vite/demo/fixtures/maxmind.js
+++ /dev/null
@@ -1,2 +0,0 @@
-import assert from "node:assert";
-assert(true);
diff --git a/packages/plugin-vite/demo/routes/tests/cjs_npm.tsx b/packages/plugin-vite/demo/routes/tests/cjs_npm.tsx
deleted file mode 100644
index e8a333346ba..00000000000
--- a/packages/plugin-vite/demo/routes/tests/cjs_npm.tsx
+++ /dev/null
@@ -1,6 +0,0 @@
-import qs from "qs";
-
-export default function Page() {
- const parsed = qs.parse("a=1&b=2");
- return
{parsed.a === "1" ? "qs-ok" : "qs-fail"}
;
-}
diff --git a/packages/plugin-vite/demo/routes/tests/commonjs.tsx b/packages/plugin-vite/demo/routes/tests/commonjs.tsx
index 32308633baf..8f73c79b934 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.js";
+import { value } from "../../fixtures/commonjs_mod.cjs";
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 8ac0424445b..b42a8ada37f 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.js";
+import * as maxmind from "../../fixtures/maxmind.cjs";
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 8fde99478c6..c49c244526c 100644
--- a/packages/plugin-vite/src/mod.ts
+++ b/packages/plugin-vite/src/mod.ts
@@ -82,43 +82,15 @@ export function fresh(config?: FreshViteConfig): Plugin[] {
});
let isDev = false;
- let freshMode = "development";
const plugins: Plugin[] = [
{
name: "fresh",
sharedDuringBuild: true,
- async config(config, env) {
+ 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
@@ -147,15 +119,14 @@ 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: {
- // 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.
+ // Optimize deps somehow leads to duplicate modules or them
+ // being placed in the wrong chunks...
noDiscovery: true,
},
@@ -221,6 +192,14 @@ 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;
@@ -242,7 +221,7 @@ export function fresh(config?: FreshViteConfig): Plugin[] {
},
};
},
- configResolved(vConfig) {
+ async configResolved(vConfig) {
// Run update check in background
updateCheck(UPDATE_INTERVAL).catch(() => {});
@@ -257,45 +236,19 @@ 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 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;
- },
- );
+ const envDir = pathWithRoot(
+ vConfig.envDir || vConfig.root,
+ vConfig.root,
+ );
- if (modified) return { code: result };
- },
+ 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`));
},
- } 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 2ab1e22ac95..6cefed7e463 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 { JSX_REG } from "../utils.ts";
+import { JS_REG, JSX_REG } from "../utils.ts";
import { builtinModules } from "node:module";
// @ts-ignore Workaround for https://github.com/denoland/deno/issues/30850
@@ -17,43 +17,16 @@ 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;
let isDev = false;
- // Cache for package.json "type" field lookups. Per Node.js semantics,
- // .js files in node_modules are CJS unless the nearest package.json
- // has "type": "module".
- const pkgTypeCache = new Map();
- async function isEsmPackage(filePath: string): Promise {
- let dir = path.dirname(filePath);
- while (true) {
- const cached = pkgTypeCache.get(dir);
- if (cached !== undefined) return cached;
-
- try {
- const text = await Deno.readTextFile(path.join(dir, "package.json"));
- const isEsm = JSON.parse(text).type === "module";
- pkgTypeCache.set(dir, isEsm);
- return isEsm;
- } catch {
- const parent = path.dirname(dir);
- if (parent === dir) break;
- dir = parent;
- }
- }
- return false;
- }
-
- // Detect actual ESM export/import statements at the statement level.
- // More robust than code.includes("export ") which matches comments
- // and strings (e.g. "// Remove export in next version" would trick it).
- // Handles both formatted and minified ESM (e.g. "import{" with no space).
- const ESM_STMT_RE =
- /(?:^|[\n;])\s*(?:export\s*[{*]|export\s+(?:default|const|let|var|function|class|async)\b|import\s*[{*"']|import\s+[a-zA-Z_$])/;
-
return {
name: "deno",
sharedDuringBuild: true,
@@ -112,21 +85,6 @@ 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
@@ -197,13 +155,14 @@ export function deno(): Plugin {
resolved = path.fromFileUrl(resolved);
}
- // 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 };
+ return {
+ id: resolved,
+ meta: {
+ deno: {
+ type,
+ },
+ },
+ };
} catch {
// ignore
}
@@ -213,87 +172,6 @@ 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.
- //
- // CJS detection uses Node.js semantics (package.json "type" field)
- // instead of content heuristics, which can be fooled by comments
- // or strings containing "export"/"import".
- if (
- isDev &&
- !id.startsWith("\0") &&
- id.includes("node_modules") &&
- /\.(c?js|cjs)$/.test(id)
- ) {
- // .cjs is always CJS. For .js files, check the nearest
- // package.json "type" field first (Node.js semantics), then
- // fall back to content-based detection for dual CJS/ESM
- // packages that ship ESM in .js without "type": "module".
- if (id.endsWith(".cjs") || !(await isEsmPackage(id))) {
- try {
- const code = await Deno.readTextFile(id);
-
- // Skip if the file contains actual ESM syntax. Some packages
- // (e.g. @opentelemetry/api) ship both CJS and ESM as .js
- // without "type": "module" in package.json.
- if (!ESM_STMT_RE.test(code)) {
- 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);
@@ -319,6 +197,49 @@ export default module.exports;
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 b6970bdbc42..167ac7bc862 100644
--- a/packages/plugin-vite/src/plugins/patches.ts
+++ b/packages/plugin-vite/src/plugins/patches.ts
@@ -1,6 +1,8 @@
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";
@@ -39,8 +41,10 @@ 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
new file mode 100644
index 00000000000..83a58bb2d5b
--- /dev/null
+++ b/packages/plugin-vite/src/plugins/patches/commonjs.ts
@@ -0,0 +1,959 @@
+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
new file mode 100644
index 00000000000..4c337babca2
--- /dev/null
+++ b/packages/plugin-vite/src/plugins/patches/commonjs_test.ts
@@ -0,0 +1,835 @@
+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
new file mode 100644
index 00000000000..820247d1345
--- /dev/null
+++ b/packages/plugin-vite/src/plugins/patches/inline_env_vars.ts
@@ -0,0 +1,78 @@
+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
new file mode 100644
index 00000000000..424fb2501fd
--- /dev/null
+++ b/packages/plugin-vite/src/plugins/patches/inline_env_vars_test.ts
@@ -0,0 +1,79 @@
+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 366275c4bd6..cd650105de8 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", async () => {
+Deno.test("fresh plugin - sets server.watch.ignored patterns", () => {
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", async () => {
// Call the config hook as Vite would during dev
// deno-lint-ignore no-explicit-any
const configFn = freshPlugin!.config as any;
- const result = await configFn({}, { command: "serve" });
+ const result = configFn({}, { command: "serve" });
const ignored = result?.server?.watch?.ignored;
expect(ignored).toBeDefined();
diff --git a/packages/plugin-vite/tests/dev_server_test.ts b/packages/plugin-vite/tests/dev_server_test.ts
index 3eb1d8f6bd9..ffc30f92ee2 100644
--- a/packages/plugin-vite/tests/dev_server_test.ts
+++ b/packages/plugin-vite/tests/dev_server_test.ts
@@ -452,17 +452,6 @@ Deno.test({
sanitizeResources: false,
});
-Deno.test({
- name: "vite dev - CJS npm package (qs)",
- fn: async () => {
- const res = await fetch(`${demoServer.address()}/tests/cjs_npm`);
- const text = await res.text();
- expect(text).toContain("qs-ok
");
- },
- sanitizeOps: false,
- sanitizeResources: false,
-});
-
// issue: https://github.com/denoland/fresh/issues/3666
integrationTest(
"vite dev - basePath does not intercept Vite URLs",