@intlayer/config 9.0.0-canary.12 → 9.0.0-canary.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. package/dist/cjs/loadEnvFile.cjs +44 -7
  2. package/dist/cjs/loadEnvFile.cjs.map +1 -1
  3. package/dist/cjs/loadExternalFile/index.cjs +1 -0
  4. package/dist/cjs/loadExternalFile/loadExternalFile.cjs +7 -5
  5. package/dist/cjs/loadExternalFile/loadExternalFile.cjs.map +1 -1
  6. package/dist/cjs/loadExternalFile/parseFileContent.cjs +52 -17
  7. package/dist/cjs/loadExternalFile/parseFileContent.cjs.map +1 -1
  8. package/dist/cjs/loadExternalFile/transpileTSToCJS.cjs +99 -9
  9. package/dist/cjs/loadExternalFile/transpileTSToCJS.cjs.map +1 -1
  10. package/dist/esm/loadEnvFile.mjs +45 -8
  11. package/dist/esm/loadEnvFile.mjs.map +1 -1
  12. package/dist/esm/loadExternalFile/index.mjs +2 -2
  13. package/dist/esm/loadExternalFile/loadExternalFile.mjs +7 -5
  14. package/dist/esm/loadExternalFile/loadExternalFile.mjs.map +1 -1
  15. package/dist/esm/loadExternalFile/parseFileContent.mjs +52 -17
  16. package/dist/esm/loadExternalFile/parseFileContent.mjs.map +1 -1
  17. package/dist/esm/loadExternalFile/transpileTSToCJS.mjs +102 -13
  18. package/dist/esm/loadExternalFile/transpileTSToCJS.mjs.map +1 -1
  19. package/dist/types/configFile/configurationSchema.d.ts +7 -7
  20. package/dist/types/loadEnvFile.d.ts +1 -1
  21. package/dist/types/loadEnvFile.d.ts.map +1 -1
  22. package/dist/types/loadExternalFile/index.d.ts +2 -2
  23. package/dist/types/loadExternalFile/loadExternalFile.d.ts.map +1 -1
  24. package/dist/types/loadExternalFile/parseFileContent.d.ts +9 -0
  25. package/dist/types/loadExternalFile/parseFileContent.d.ts.map +1 -1
  26. package/dist/types/loadExternalFile/transpileTSToCJS.d.ts +3 -1
  27. package/dist/types/loadExternalFile/transpileTSToCJS.d.ts.map +1 -1
  28. package/package.json +2 -2
@@ -14,16 +14,53 @@ const getEnvFilePath = (env = process.env.NODE_ENV ?? "development", envFile) =>
14
14
  ".env"
15
15
  ]).find(node_fs.existsSync);
16
16
  };
17
- const loadEnvFile = (options) => {
18
- const envFiles = getEnvFilePath(options?.env ?? DEFAULT_ENV, options?.envFile);
19
- if (!envFiles) return {};
20
- const result = {};
17
+ /**
18
+ * Cache of parsed env files keyed by `cwd|env|envFile`. Loading content
19
+ * declarations calls `loadEnvFile` once per file; without this every call pays
20
+ * up to 4 `existsSync` probes plus a dotenv read+parse. A cached hit is
21
+ * validated with a single `stat` so edits to the env file are still picked up.
22
+ */
23
+ const envFileCache = /* @__PURE__ */ new Map();
24
+ const parseEnvFile = (envFilePath) => {
25
+ const parsedEnv = {};
21
26
  dotenv.default.config({
22
- path: envFiles,
23
- processEnv: result,
27
+ path: envFilePath,
28
+ processEnv: parsedEnv,
24
29
  quiet: true
25
30
  });
26
- return result;
31
+ return parsedEnv;
32
+ };
33
+ const loadEnvFile = (options) => {
34
+ const env = options?.env ?? DEFAULT_ENV;
35
+ const cacheKey = `${process.cwd()}|${env}|${options?.envFile ?? ""}`;
36
+ const cachedEntry = envFileCache.get(cacheKey);
37
+ if (cachedEntry) if (cachedEntry.envFilePath === void 0) {
38
+ if (!getEnvFilePath(env, options?.envFile)) return cachedEntry.parsedEnv;
39
+ } else try {
40
+ const stats = (0, node_fs.statSync)(cachedEntry.envFilePath);
41
+ if (stats.mtimeMs === cachedEntry.mtimeMs && stats.size === cachedEntry.size) return cachedEntry.parsedEnv;
42
+ } catch {}
43
+ const envFilePath = getEnvFilePath(env, options?.envFile);
44
+ if (!envFilePath) {
45
+ envFileCache.set(cacheKey, {
46
+ envFilePath: void 0,
47
+ mtimeMs: 0,
48
+ size: 0,
49
+ parsedEnv: {}
50
+ });
51
+ return {};
52
+ }
53
+ const parsedEnv = parseEnvFile(envFilePath);
54
+ try {
55
+ const stats = (0, node_fs.statSync)(envFilePath);
56
+ envFileCache.set(cacheKey, {
57
+ envFilePath,
58
+ mtimeMs: stats.mtimeMs,
59
+ size: stats.size,
60
+ parsedEnv
61
+ });
62
+ } catch {}
63
+ return parsedEnv;
27
64
  };
28
65
 
29
66
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"loadEnvFile.cjs","names":["existsSync"],"sources":["../../src/loadEnvFile.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport dotenv from 'dotenv';\n\nconst DEFAULT_ENV = process.env.NODE_ENV ?? 'development';\n\nexport type LoadEnvFileOptions = {\n env?: string;\n envFile?: string;\n};\n\nexport const getEnvFilePath = (\n env: string = process.env.NODE_ENV ?? 'development',\n envFile?: string\n): string | undefined => {\n const envFiles = envFile\n ? [envFile]\n : [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env'];\n\n return envFiles.find(existsSync); // Returns the first existing env file\n};\n\nexport const loadEnvFile = (options?: Partial<LoadEnvFileOptions>) => {\n const env = options?.env ?? DEFAULT_ENV;\n\n const envFiles = getEnvFilePath(env, options?.envFile);\n\n if (!envFiles) {\n return {};\n }\n\n const result = {};\n\n dotenv.config({\n path: envFiles,\n processEnv: result,\n quiet: true,\n });\n\n return result; // Return the parsed env object\n};\n"],"mappings":";;;;;;;AAGA,MAAM,cAAc,QAAQ,IAAI,YAAY;AAO5C,MAAa,kBACX,MAAc,QAAQ,IAAI,YAAY,eACtC,YACuB;AAKvB,SAJiB,UACb,CAAC,QAAQ,GACT;EAAC,QAAQ,IAAI;EAAS,QAAQ;EAAO;EAAc;EAAO,EAE9C,KAAKA,mBAAW;;AAGlC,MAAa,eAAe,YAA0C;CAGpE,MAAM,WAAW,eAFL,SAAS,OAAO,aAES,SAAS,QAAQ;AAEtD,KAAI,CAAC,SACH,QAAO,EAAE;CAGX,MAAM,SAAS,EAAE;AAEjB,gBAAO,OAAO;EACZ,MAAM;EACN,YAAY;EACZ,OAAO;EACR,CAAC;AAEF,QAAO"}
1
+ {"version":3,"file":"loadEnvFile.cjs","names":["existsSync"],"sources":["../../src/loadEnvFile.ts"],"sourcesContent":["import { existsSync, statSync } from 'node:fs';\nimport dotenv from 'dotenv';\n\nconst DEFAULT_ENV = process.env.NODE_ENV ?? 'development';\n\nexport type LoadEnvFileOptions = {\n env?: string;\n envFile?: string;\n};\n\nexport const getEnvFilePath = (\n env: string = process.env.NODE_ENV ?? 'development',\n envFile?: string\n): string | undefined => {\n const envFiles = envFile\n ? [envFile]\n : [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env'];\n\n return envFiles.find(existsSync); // Returns the first existing env file\n};\n\ntype EnvFileCacheEntry = {\n /** The env file that was resolved and parsed (undefined if none exists). */\n envFilePath: string | undefined;\n /** Fingerprint of the parsed file, used to detect edits. */\n mtimeMs: number;\n size: number;\n /** The parsed env variables. */\n parsedEnv: Record<string, string>;\n};\n\n/**\n * Cache of parsed env files keyed by `cwd|env|envFile`. Loading content\n * declarations calls `loadEnvFile` once per file; without this every call pays\n * up to 4 `existsSync` probes plus a dotenv read+parse. A cached hit is\n * validated with a single `stat` so edits to the env file are still picked up.\n */\nconst envFileCache = new Map<string, EnvFileCacheEntry>();\n\nconst parseEnvFile = (envFilePath: string): Record<string, string> => {\n const parsedEnv: Record<string, string> = {};\n\n dotenv.config({\n path: envFilePath,\n processEnv: parsedEnv,\n quiet: true,\n });\n\n return parsedEnv;\n};\n\nexport const loadEnvFile = (\n options?: Partial<LoadEnvFileOptions>\n): Record<string, string> => {\n const env = options?.env ?? DEFAULT_ENV;\n\n // Env file candidates are resolved relative to the working directory\n const cacheKey = `${process.cwd()}|${env}|${options?.envFile ?? ''}`;\n\n const cachedEntry = envFileCache.get(cacheKey);\n\n if (cachedEntry) {\n if (cachedEntry.envFilePath === undefined) {\n // No env file existed. Re-probe cheaply in case one was created since.\n const envFilePath = getEnvFilePath(env, options?.envFile);\n if (!envFilePath) return cachedEntry.parsedEnv;\n } else {\n try {\n const stats = statSync(cachedEntry.envFilePath);\n if (\n stats.mtimeMs === cachedEntry.mtimeMs &&\n stats.size === cachedEntry.size\n ) {\n return cachedEntry.parsedEnv;\n }\n } catch {\n // File was removed — fall through and re-resolve\n }\n }\n }\n\n const envFilePath = getEnvFilePath(env, options?.envFile);\n\n if (!envFilePath) {\n envFileCache.set(cacheKey, {\n envFilePath: undefined,\n mtimeMs: 0,\n size: 0,\n parsedEnv: {},\n });\n return {};\n }\n\n const parsedEnv = parseEnvFile(envFilePath);\n\n try {\n const stats = statSync(envFilePath);\n envFileCache.set(cacheKey, {\n envFilePath,\n mtimeMs: stats.mtimeMs,\n size: stats.size,\n parsedEnv,\n });\n } catch {\n // Race: file removed between parse and stat — skip caching this round\n }\n\n return parsedEnv; // Return the parsed env object\n};\n"],"mappings":";;;;;;;AAGA,MAAM,cAAc,QAAQ,IAAI,YAAY;AAO5C,MAAa,kBACX,MAAc,QAAQ,IAAI,YAAY,eACtC,YACuB;AAKvB,SAJiB,UACb,CAAC,QAAQ,GACT;EAAC,QAAQ,IAAI;EAAS,QAAQ;EAAO;EAAc;EAAO,EAE9C,KAAKA,mBAAW;;;;;;;;AAmBlC,MAAM,+BAAe,IAAI,KAAgC;AAEzD,MAAM,gBAAgB,gBAAgD;CACpE,MAAM,YAAoC,EAAE;AAE5C,gBAAO,OAAO;EACZ,MAAM;EACN,YAAY;EACZ,OAAO;EACR,CAAC;AAEF,QAAO;;AAGT,MAAa,eACX,YAC2B;CAC3B,MAAM,MAAM,SAAS,OAAO;CAG5B,MAAM,WAAW,GAAG,QAAQ,KAAK,CAAC,GAAG,IAAI,GAAG,SAAS,WAAW;CAEhE,MAAM,cAAc,aAAa,IAAI,SAAS;AAE9C,KAAI,YACF,KAAI,YAAY,gBAAgB,QAG9B;MAAI,CADgB,eAAe,KAAK,SAAS,QACjC,CAAE,QAAO,YAAY;OAErC,KAAI;EACF,MAAM,8BAAiB,YAAY,YAAY;AAC/C,MACE,MAAM,YAAY,YAAY,WAC9B,MAAM,SAAS,YAAY,KAE3B,QAAO,YAAY;SAEf;CAMZ,MAAM,cAAc,eAAe,KAAK,SAAS,QAAQ;AAEzD,KAAI,CAAC,aAAa;AAChB,eAAa,IAAI,UAAU;GACzB,aAAa;GACb,SAAS;GACT,MAAM;GACN,WAAW,EAAE;GACd,CAAC;AACF,SAAO,EAAE;;CAGX,MAAM,YAAY,aAAa,YAAY;AAE3C,KAAI;EACF,MAAM,8BAAiB,YAAY;AACnC,eAAa,IAAI,UAAU;GACzB;GACA,SAAS,MAAM;GACf,MAAM,MAAM;GACZ;GACD,CAAC;SACI;AAIR,QAAO"}
@@ -8,6 +8,7 @@ const require_loadExternalFile_bundleJSFile = require('./bundleJSFile.cjs');
8
8
  exports.bundleFile = require_loadExternalFile_bundleFile.bundleFile;
9
9
  exports.bundleFileSync = require_loadExternalFile_bundleFile.bundleFileSync;
10
10
  exports.bundleJSFile = require_loadExternalFile_bundleJSFile.bundleJSFile;
11
+ exports.clearTranspileCache = require_loadExternalFile_transpileTSToCJS.clearTranspileCache;
11
12
  exports.getLoader = require_loadExternalFile_bundleFile.getLoader;
12
13
  exports.getSandBoxContext = require_loadExternalFile_parseFileContent.getSandBoxContext;
13
14
  exports.loadExternalFile = require_loadExternalFile_loadExternalFile.loadExternalFile;
@@ -25,13 +25,13 @@ const loadExternalFileSync = (filePath, options) => {
25
25
  require_logger.logger("File could not be loaded.", { level: "error" });
26
26
  return;
27
27
  }
28
- const fileContent = require_loadExternalFile_parseFileContent.parseFileContent(moduleResultString, {
28
+ const fileContent = withPreloadedGlobals(options?.preloadGlobals, () => require_loadExternalFile_parseFileContent.parseFileContent(moduleResultString, {
29
29
  projectRequire: options?.projectRequire,
30
30
  envVarOptions: options?.envVarOptions,
31
31
  additionalEnvVars: options?.additionalEnvVars,
32
32
  mocks: options?.mocks,
33
33
  aliases: options?.aliases
34
- });
34
+ }));
35
35
  if (typeof fileContent === "undefined") {
36
36
  require_logger.logger(`File could not be loaded. Path : ${filePath}`);
37
37
  return;
@@ -44,15 +44,17 @@ const loadExternalFileSync = (filePath, options) => {
44
44
  const withPreloadedGlobals = (globals, fn) => {
45
45
  if (!globals) return fn();
46
46
  const globalVars = globalThis;
47
- const prev = {};
47
+ const previousValues = {};
48
+ const previouslyPresent = {};
48
49
  for (const [key, value] of Object.entries(globals)) {
49
- prev[key] = globalVars[key];
50
+ previouslyPresent[key] = key in globalVars;
51
+ previousValues[key] = globalVars[key];
50
52
  globalVars[key] = value;
51
53
  }
52
54
  try {
53
55
  return fn();
54
56
  } finally {
55
- for (const key of Object.keys(globals)) if (prev[key] !== void 0) globalVars[key] = prev[key];
57
+ for (const key of Object.keys(globals)) if (previouslyPresent[key]) globalVars[key] = previousValues[key];
56
58
  else delete globalVars[key];
57
59
  }
58
60
  };
@@ -1 +1 @@
1
- {"version":3,"file":"loadExternalFile.cjs","names":["JSON5","transpileTSToCJSSync","parseFileContent","transpileTSToCJS","colorizePath"],"sources":["../../../src/loadExternalFile/loadExternalFile.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { extname } from 'node:path';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport JSON5 from 'json5';\nimport { colorizePath, logger } from '../logger';\nimport {\n parseFileContent,\n type SandBoxContextOptions,\n} from './parseFileContent';\nimport {\n type TranspileOptions,\n transpileTSToCJS,\n transpileTSToCJSSync,\n} from './transpileTSToCJS';\n\n// CJS MJS cross usage\nconst parseJSON5 = JSON5.parse || (JSON5 as any).default?.parse;\n\nexport type LoadExternalFileOptions = {\n configuration?: IntlayerConfig;\n buildOptions?: TranspileOptions;\n logError?: boolean;\n /**\n * Key-value pairs to temporarily set on the main Node.js `globalThis` for the\n * synchronous duration of `parseFileContent` / `runInNewContext`. External modules\n * loaded via `require()` inside the VM (e.g. `@intlayer/core`'s `file()` helper)\n * run in the main context and read from the real `globalThis`, not the VM sandbox.\n * Values are restored (or deleted) after `runInNewContext` returns.\n */\n preloadGlobals?: Record<string, unknown>;\n} & SandBoxContextOptions;\n\n/**\n * Load the content declaration from the given path\n *\n * Accepts JSON, JS, MJS and TS files as configuration\n */\nexport const loadExternalFileSync = (\n filePath: string,\n options?: LoadExternalFileOptions\n): any | undefined => {\n const fileExtension = extname(filePath) || '.json';\n\n try {\n if (\n fileExtension === '.json' ||\n fileExtension === '.json5' ||\n fileExtension === '.jsonc'\n ) {\n // Assume JSON\n return parseJSON5(readFileSync(filePath, 'utf-8'));\n }\n\n // Rest is JS, MJS or TS\n const code = readFileSync(filePath, 'utf-8');\n\n const moduleResultString: string | undefined = transpileTSToCJSSync(\n code,\n filePath,\n options?.buildOptions\n );\n\n if (!moduleResultString) {\n logger('File could not be loaded.', { level: 'error' });\n return undefined;\n }\n\n const fileContent = parseFileContent(moduleResultString, {\n projectRequire: options?.projectRequire,\n envVarOptions: options?.envVarOptions,\n additionalEnvVars: options?.additionalEnvVars,\n mocks: options?.mocks,\n aliases: options?.aliases,\n });\n\n if (typeof fileContent === 'undefined') {\n logger(`File could not be loaded. Path : ${filePath}`);\n return undefined;\n }\n\n return fileContent;\n } catch (error) {\n logger(\n [\n `Error: ${(error as Error).message} - `,\n JSON.stringify((error as Error).stack, null, 2),\n ],\n {\n level: 'error',\n }\n );\n }\n};\n\nconst withPreloadedGlobals = <T>(\n globals: Record<string, unknown> | undefined,\n fn: () => T\n): T => {\n if (!globals) return fn();\n\n const globalVars = globalThis as Record<string, unknown>;\n const prev: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(globals)) {\n prev[key] = globalVars[key];\n globalVars[key] = value;\n }\n\n try {\n return fn();\n } finally {\n for (const key of Object.keys(globals)) {\n if (prev[key] !== undefined) {\n globalVars[key] = prev[key];\n } else {\n delete globalVars[key];\n }\n }\n }\n};\n\n/**\n * Load the content declaration from the given path\n *\n * Accepts JSON, JS, MJS and TS files as configuration\n */\nexport const loadExternalFile = async (\n filePath: string,\n options?: LoadExternalFileOptions\n): Promise<any | undefined> => {\n const fileExtension = extname(filePath);\n\n try {\n if (\n fileExtension === '.json' ||\n fileExtension === '.json5' ||\n fileExtension === '.jsonc'\n ) {\n // Remove cache to force getting fresh content\n const fileContent = await readFile(filePath, 'utf-8');\n return parseJSON5(fileContent);\n }\n\n // Rest is JS, MJS or TS\n const code = await readFile(filePath, 'utf-8');\n\n const moduleResultString: string | undefined = await transpileTSToCJS(\n code,\n filePath,\n options?.buildOptions\n );\n\n if (!moduleResultString) {\n logger('File could not be loaded.', { level: 'error' });\n return undefined;\n }\n\n // parseFileContent/runInNewContext is synchronous, so withPreloadedGlobals\n // has no interleaving risk even when multiple files are processed concurrently.\n const fileContent = withPreloadedGlobals(options?.preloadGlobals, () =>\n parseFileContent(moduleResultString, {\n projectRequire: options?.projectRequire,\n envVarOptions: options?.envVarOptions,\n additionalEnvVars: options?.additionalEnvVars,\n mocks: options?.mocks,\n aliases: options?.aliases,\n })\n );\n\n if (typeof fileContent === 'undefined') {\n logger(`File could not be loaded. Path : ${colorizePath(filePath)}`);\n return undefined;\n }\n\n return fileContent;\n } catch (error) {\n if (options?.logError ?? true) {\n logger(\n [\n `Error: ${(error as Error).message} - `,\n JSON.stringify((error as Error).stack, null, 2),\n ],\n {\n level: 'error',\n }\n );\n }\n }\n};\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAM,aAAaA,cAAM,SAAUA,cAAc,SAAS;;;;;;AAqB1D,MAAa,wBACX,UACA,YACoB;CACpB,MAAM,uCAAwB,SAAS,IAAI;AAE3C,KAAI;AACF,MACE,kBAAkB,WAClB,kBAAkB,YAClB,kBAAkB,SAGlB,QAAO,qCAAwB,UAAU,QAAQ,CAAC;EAMpD,MAAM,qBAAyCC,yFAFrB,UAAU,QAG9B,EACJ,UACA,SAAS,aACV;AAED,MAAI,CAAC,oBAAoB;AACvB,yBAAO,6BAA6B,EAAE,OAAO,SAAS,CAAC;AACvD;;EAGF,MAAM,cAAcC,2DAAiB,oBAAoB;GACvD,gBAAgB,SAAS;GACzB,eAAe,SAAS;GACxB,mBAAmB,SAAS;GAC5B,OAAO,SAAS;GAChB,SAAS,SAAS;GACnB,CAAC;AAEF,MAAI,OAAO,gBAAgB,aAAa;AACtC,yBAAO,oCAAoC,WAAW;AACtD;;AAGF,SAAO;UACA,OAAO;AACd,wBACE,CACE,UAAW,MAAgB,QAAQ,MACnC,KAAK,UAAW,MAAgB,OAAO,MAAM,EAAE,CAChD,EACD,EACE,OAAO,SACR,CACF;;;AAIL,MAAM,wBACJ,SACA,OACM;AACN,KAAI,CAAC,QAAS,QAAO,IAAI;CAEzB,MAAM,aAAa;CACnB,MAAM,OAAgC,EAAE;AAExC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;AAClD,OAAK,OAAO,WAAW;AACvB,aAAW,OAAO;;AAGpB,KAAI;AACF,SAAO,IAAI;WACH;AACR,OAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CACpC,KAAI,KAAK,SAAS,OAChB,YAAW,OAAO,KAAK;MAEvB,QAAO,WAAW;;;;;;;;AAW1B,MAAa,mBAAmB,OAC9B,UACA,YAC6B;CAC7B,MAAM,uCAAwB,SAAS;AAEvC,KAAI;AACF,MACE,kBAAkB,WAClB,kBAAkB,YAClB,kBAAkB,SAIlB,QAAO,WAAW,qCADiB,UAAU,QAAQ,CACvB;EAMhC,MAAM,qBAAyC,MAAMC,2DACnD,qCAH0B,UAAU,QAAQ,EAI5C,UACA,SAAS,aACV;AAED,MAAI,CAAC,oBAAoB;AACvB,yBAAO,6BAA6B,EAAE,OAAO,SAAS,CAAC;AACvD;;EAKF,MAAM,cAAc,qBAAqB,SAAS,sBAChDD,2DAAiB,oBAAoB;GACnC,gBAAgB,SAAS;GACzB,eAAe,SAAS;GACxB,mBAAmB,SAAS;GAC5B,OAAO,SAAS;GAChB,SAAS,SAAS;GACnB,CAAC,CACH;AAED,MAAI,OAAO,gBAAgB,aAAa;AACtC,yBAAO,oCAAoCE,4BAAa,SAAS,GAAG;AACpE;;AAGF,SAAO;UACA,OAAO;AACd,MAAI,SAAS,YAAY,KACvB,uBACE,CACE,UAAW,MAAgB,QAAQ,MACnC,KAAK,UAAW,MAAgB,OAAO,MAAM,EAAE,CAChD,EACD,EACE,OAAO,SACR,CACF"}
1
+ {"version":3,"file":"loadExternalFile.cjs","names":["JSON5","transpileTSToCJSSync","parseFileContent","transpileTSToCJS","colorizePath"],"sources":["../../../src/loadExternalFile/loadExternalFile.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { extname } from 'node:path';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport JSON5 from 'json5';\nimport { colorizePath, logger } from '../logger';\nimport {\n parseFileContent,\n type SandBoxContextOptions,\n} from './parseFileContent';\nimport {\n type TranspileOptions,\n transpileTSToCJS,\n transpileTSToCJSSync,\n} from './transpileTSToCJS';\n\n// CJS MJS cross usage\nconst parseJSON5 = JSON5.parse || (JSON5 as any).default?.parse;\n\nexport type LoadExternalFileOptions = {\n configuration?: IntlayerConfig;\n buildOptions?: TranspileOptions;\n logError?: boolean;\n /**\n * Key-value pairs to temporarily set on the main Node.js `globalThis` for the\n * synchronous duration of `parseFileContent` / `runInNewContext`. External modules\n * loaded via `require()` inside the VM (e.g. `@intlayer/core`'s `file()` helper)\n * run in the main context and read from the real `globalThis`, not the VM sandbox.\n * Values are restored (or deleted) after `runInNewContext` returns.\n */\n preloadGlobals?: Record<string, unknown>;\n} & SandBoxContextOptions;\n\n/**\n * Load the content declaration from the given path\n *\n * Accepts JSON, JS, MJS and TS files as configuration\n */\nexport const loadExternalFileSync = (\n filePath: string,\n options?: LoadExternalFileOptions\n): any | undefined => {\n const fileExtension = extname(filePath) || '.json';\n\n try {\n if (\n fileExtension === '.json' ||\n fileExtension === '.json5' ||\n fileExtension === '.jsonc'\n ) {\n // Assume JSON\n return parseJSON5(readFileSync(filePath, 'utf-8'));\n }\n\n // Rest is JS, MJS or TS\n const code = readFileSync(filePath, 'utf-8');\n\n const moduleResultString: string | undefined = transpileTSToCJSSync(\n code,\n filePath,\n options?.buildOptions\n );\n\n if (!moduleResultString) {\n logger('File could not be loaded.', { level: 'error' });\n return undefined;\n }\n\n // parseFileContent/runInNewContext is synchronous, so withPreloadedGlobals\n // fully restores the globals before returning.\n const fileContent = withPreloadedGlobals(options?.preloadGlobals, () =>\n parseFileContent(moduleResultString, {\n projectRequire: options?.projectRequire,\n envVarOptions: options?.envVarOptions,\n additionalEnvVars: options?.additionalEnvVars,\n mocks: options?.mocks,\n aliases: options?.aliases,\n })\n );\n\n if (typeof fileContent === 'undefined') {\n logger(`File could not be loaded. Path : ${filePath}`);\n return undefined;\n }\n\n return fileContent;\n } catch (error) {\n logger(\n [\n `Error: ${(error as Error).message} - `,\n JSON.stringify((error as Error).stack, null, 2),\n ],\n {\n level: 'error',\n }\n );\n }\n};\n\nconst withPreloadedGlobals = <T>(\n globals: Record<string, unknown> | undefined,\n fn: () => T\n): T => {\n if (!globals) return fn();\n\n const globalVars = globalThis as Record<string, unknown>;\n const previousValues: Record<string, unknown> = {};\n const previouslyPresent: Record<string, boolean> = {};\n\n for (const [key, value] of Object.entries(globals)) {\n previouslyPresent[key] = key in globalVars;\n previousValues[key] = globalVars[key];\n globalVars[key] = value;\n }\n\n try {\n return fn();\n } finally {\n for (const key of Object.keys(globals)) {\n // `in` check (not `!== undefined`) so a global that legitimately held\n // `undefined` is restored rather than deleted.\n if (previouslyPresent[key]) {\n globalVars[key] = previousValues[key];\n } else {\n delete globalVars[key];\n }\n }\n }\n};\n\n/**\n * Load the content declaration from the given path\n *\n * Accepts JSON, JS, MJS and TS files as configuration\n */\nexport const loadExternalFile = async (\n filePath: string,\n options?: LoadExternalFileOptions\n): Promise<any | undefined> => {\n const fileExtension = extname(filePath);\n\n try {\n if (\n fileExtension === '.json' ||\n fileExtension === '.json5' ||\n fileExtension === '.jsonc'\n ) {\n // Remove cache to force getting fresh content\n const fileContent = await readFile(filePath, 'utf-8');\n return parseJSON5(fileContent);\n }\n\n // Rest is JS, MJS or TS\n const code = await readFile(filePath, 'utf-8');\n\n const moduleResultString: string | undefined = await transpileTSToCJS(\n code,\n filePath,\n options?.buildOptions\n );\n\n if (!moduleResultString) {\n logger('File could not be loaded.', { level: 'error' });\n return undefined;\n }\n\n // parseFileContent/runInNewContext is synchronous, so withPreloadedGlobals\n // has no interleaving risk even when multiple files are processed concurrently.\n const fileContent = withPreloadedGlobals(options?.preloadGlobals, () =>\n parseFileContent(moduleResultString, {\n projectRequire: options?.projectRequire,\n envVarOptions: options?.envVarOptions,\n additionalEnvVars: options?.additionalEnvVars,\n mocks: options?.mocks,\n aliases: options?.aliases,\n })\n );\n\n if (typeof fileContent === 'undefined') {\n logger(`File could not be loaded. Path : ${colorizePath(filePath)}`);\n return undefined;\n }\n\n return fileContent;\n } catch (error) {\n if (options?.logError ?? true) {\n logger(\n [\n `Error: ${(error as Error).message} - `,\n JSON.stringify((error as Error).stack, null, 2),\n ],\n {\n level: 'error',\n }\n );\n }\n }\n};\n"],"mappings":";;;;;;;;;;;;AAiBA,MAAM,aAAaA,cAAM,SAAUA,cAAc,SAAS;;;;;;AAqB1D,MAAa,wBACX,UACA,YACoB;CACpB,MAAM,uCAAwB,SAAS,IAAI;AAE3C,KAAI;AACF,MACE,kBAAkB,WAClB,kBAAkB,YAClB,kBAAkB,SAGlB,QAAO,qCAAwB,UAAU,QAAQ,CAAC;EAMpD,MAAM,qBAAyCC,yFAFrB,UAAU,QAG9B,EACJ,UACA,SAAS,aACV;AAED,MAAI,CAAC,oBAAoB;AACvB,yBAAO,6BAA6B,EAAE,OAAO,SAAS,CAAC;AACvD;;EAKF,MAAM,cAAc,qBAAqB,SAAS,sBAChDC,2DAAiB,oBAAoB;GACnC,gBAAgB,SAAS;GACzB,eAAe,SAAS;GACxB,mBAAmB,SAAS;GAC5B,OAAO,SAAS;GAChB,SAAS,SAAS;GACnB,CAAC,CACH;AAED,MAAI,OAAO,gBAAgB,aAAa;AACtC,yBAAO,oCAAoC,WAAW;AACtD;;AAGF,SAAO;UACA,OAAO;AACd,wBACE,CACE,UAAW,MAAgB,QAAQ,MACnC,KAAK,UAAW,MAAgB,OAAO,MAAM,EAAE,CAChD,EACD,EACE,OAAO,SACR,CACF;;;AAIL,MAAM,wBACJ,SACA,OACM;AACN,KAAI,CAAC,QAAS,QAAO,IAAI;CAEzB,MAAM,aAAa;CACnB,MAAM,iBAA0C,EAAE;CAClD,MAAM,oBAA6C,EAAE;AAErD,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;AAClD,oBAAkB,OAAO,OAAO;AAChC,iBAAe,OAAO,WAAW;AACjC,aAAW,OAAO;;AAGpB,KAAI;AACF,SAAO,IAAI;WACH;AACR,OAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CAGpC,KAAI,kBAAkB,KACpB,YAAW,OAAO,eAAe;MAEjC,QAAO,WAAW;;;;;;;;AAW1B,MAAa,mBAAmB,OAC9B,UACA,YAC6B;CAC7B,MAAM,uCAAwB,SAAS;AAEvC,KAAI;AACF,MACE,kBAAkB,WAClB,kBAAkB,YAClB,kBAAkB,SAIlB,QAAO,WAAW,qCADiB,UAAU,QAAQ,CACvB;EAMhC,MAAM,qBAAyC,MAAMC,2DACnD,qCAH0B,UAAU,QAAQ,EAI5C,UACA,SAAS,aACV;AAED,MAAI,CAAC,oBAAoB;AACvB,yBAAO,6BAA6B,EAAE,OAAO,SAAS,CAAC;AACvD;;EAKF,MAAM,cAAc,qBAAqB,SAAS,sBAChDD,2DAAiB,oBAAoB;GACnC,gBAAgB,SAAS;GACzB,eAAe,SAAS;GACxB,mBAAmB,SAAS;GAC5B,OAAO,SAAS;GAChB,SAAS,SAAS;GACnB,CAAC,CACH;AAED,MAAI,OAAO,gBAAgB,aAAa;AACtC,yBAAO,oCAAoCE,4BAAa,SAAS,GAAG;AACpE;;AAGF,SAAO;UACA,OAAO;AACd,MAAI,SAAS,YAAY,KACvB,uBACE,CACE,UAAW,MAAgB,QAAQ,MACnC,KAAK,UAAW,MAAgB,OAAO,MAAM,EAAE,CAChD,EACD,EACE,OAAO,SACR,CACF"}
@@ -27,9 +27,58 @@ const NODE_GLOBALS = [
27
27
  "crypto",
28
28
  "structuredClone"
29
29
  ];
30
+ /**
31
+ * Cache of the project's React module (or the JSX-capturing fallback) keyed by
32
+ * the require's resolution paths. Node does not cache *failed* resolutions, so
33
+ * without this every sandbox creation in a non-React project (Vue, Svelte,
34
+ * Angular…) pays a full node_modules walk that ends in a throw.
35
+ */
36
+ const reactModuleCache = /* @__PURE__ */ new Map();
37
+ /**
38
+ * Fallback React implementation used when React is not installed in the
39
+ * project. It captures JSX elements as plain objects so JSX can be used in
40
+ * content declarations (esbuild's tsx loader defaults to React.createElement).
41
+ */
42
+ const createFallbackReact = () => ({
43
+ createElement: (type, props, ...children) => ({
44
+ type,
45
+ props: {
46
+ ...props,
47
+ children: children.length <= 1 ? children[0] : children
48
+ }
49
+ }),
50
+ Fragment: Symbol.for("react.fragment")
51
+ });
52
+ const getReactGlobal = (baseRequire) => {
53
+ const resolutionKey = baseRequire.resolve.paths?.("react")?.join("|") ?? "<default>";
54
+ const cachedReactModule = reactModuleCache.get(resolutionKey);
55
+ if (cachedReactModule) return cachedReactModule;
56
+ let reactGlobal;
57
+ try {
58
+ reactGlobal = { React: baseRequire("react") };
59
+ } catch (_err) {
60
+ reactGlobal = { React: createFallbackReact() };
61
+ }
62
+ reactModuleCache.set(resolutionKey, reactGlobal);
63
+ return reactGlobal;
64
+ };
65
+ /**
66
+ * Snapshot of the non-env `process` properties, taken once at module load.
67
+ * Spreading `process` enumerates ~100 properties (some behind getters); doing
68
+ * it per sandbox is wasteful since everything except `env` is static.
69
+ */
70
+ const processSnapshot = { ...process };
71
+ /**
72
+ * Builds the global context handed to `runInNewContext`.
73
+ *
74
+ * SECURITY NOTE: this sandbox exists for global-scope hygiene (fresh
75
+ * module/exports, controlled env), NOT for containment. It exposes the real
76
+ * project `require`, a copy of `process`, and `fetch` — code executed here has
77
+ * full host privileges. Only ever run trusted, build-time project files
78
+ * (same trust model as `vite.config.ts`).
79
+ */
30
80
  const getSandBoxContext = (options) => {
31
81
  const { envVarOptions, projectRequire, additionalEnvVars, mocks, aliases } = options ?? {};
32
- let additionalGlobalVar = {};
33
82
  const baseRequire = typeof projectRequire === "function" ? projectRequire : require_utils_ESMxCJSHelpers.getProjectRequire();
34
83
  const mockedRequire = (() => {
35
84
  const mockTable = Object.assign({ esbuild }, mocks);
@@ -46,25 +95,11 @@ const getSandBoxContext = (options) => {
46
95
  wrappedRequire.cache = baseRequire.cache;
47
96
  return wrappedRequire;
48
97
  })();
49
- try {
50
- additionalGlobalVar = { React: baseRequire("react") };
51
- } catch (_err) {
52
- additionalGlobalVar = { React: {
53
- createElement: (type, props, ...children) => ({
54
- type,
55
- props: {
56
- ...props,
57
- children: children.length <= 1 ? children[0] : children
58
- }
59
- }),
60
- Fragment: Symbol.for("react.fragment")
61
- } };
62
- }
63
98
  const sandboxContext = {
64
99
  exports: { default: {} },
65
100
  module: { exports: {} },
66
101
  process: {
67
- ...process,
102
+ ...processSnapshot,
68
103
  env: {
69
104
  ...process.env,
70
105
  ...require_loadEnvFile.loadEnvFile(envVarOptions),
@@ -73,7 +108,7 @@ const getSandBoxContext = (options) => {
73
108
  },
74
109
  console,
75
110
  require: mockedRequire,
76
- ...additionalGlobalVar
111
+ ...getReactGlobal(baseRequire)
77
112
  };
78
113
  for (const key of NODE_GLOBALS) if (!(key in sandboxContext) && key in globalThis) sandboxContext[key] = globalThis[key];
79
114
  return sandboxContext;
@@ -1 +1 @@
1
- {"version":3,"file":"parseFileContent.cjs","names":["getProjectRequire","loadEnvFile"],"sources":["../../../src/loadExternalFile/parseFileContent.ts"],"sourcesContent":["import { type Context, runInNewContext } from 'node:vm';\nimport * as esbuild from 'esbuild';\nimport { type LoadEnvFileOptions, loadEnvFile } from '../loadEnvFile';\nimport { getProjectRequire } from '../utils/ESMxCJSHelpers';\n\nexport type SandBoxContextOptions = {\n envVarOptions?: LoadEnvFileOptions;\n projectRequire?: NodeJS.Require;\n additionalEnvVars?: Record<string, string>;\n /**\n * Map of specifier -> mocked export to be returned when code in the VM calls require(specifier).\n * Example:\n * mocks: {\n * '@intlayer/config/built': { getConfig: () => ({}), Locales: {} }\n * }\n */\n mocks?: Record<string, any>;\n /**\n * Optional alias map if you want to redirect specifiers.\n * Useful when user code imports a subpath you want to collapse.\n * Example:\n * aliases: { '@intlayer/config/built': '@intlayer/config' }\n */\n aliases?: Record<string, string>;\n};\n\n// Inject only Node.js-specific globals that are absent from a plain V8 context.\n// JS built-ins (Object, Array, Promise, Math, Date, JSON, Symbol, etc.) are\n// provided automatically by runInNewContext — no need to copy them.\n// Copying all of globalThis would retain hundreds of references (including the\n// full module cache via `global`) inside every sandbox, causing a memory leak.\nconst NODE_GLOBALS = [\n 'Buffer',\n 'setTimeout',\n 'clearTimeout',\n 'setInterval',\n 'clearInterval',\n 'setImmediate',\n 'clearImmediate',\n 'queueMicrotask',\n 'URL',\n 'URLSearchParams',\n 'TextEncoder',\n 'TextDecoder',\n 'AbortController',\n 'AbortSignal',\n 'performance',\n 'fetch',\n 'crypto',\n 'structuredClone',\n] as const;\n\nexport const getSandBoxContext = (options?: SandBoxContextOptions): Context => {\n const { envVarOptions, projectRequire, additionalEnvVars, mocks, aliases } =\n options ?? {};\n\n let additionalGlobalVar = {};\n\n const baseRequire: NodeJS.Require =\n typeof projectRequire === 'function' ? projectRequire : getProjectRequire();\n\n // Wrap require to honor mocks and aliases inside the VM\n const mockedRequire: NodeJS.Require = (() => {\n const mockTable = Object.assign(\n {\n esbuild,\n },\n mocks\n );\n const aliasTable = Object.assign({}, aliases);\n\n const wrappedRequire = function mockableRequire(id: string) {\n const target = aliasTable?.[id] ? aliasTable[id] : id;\n\n if (mockTable && Object.hasOwn(mockTable, target)) {\n return mockTable[target];\n }\n\n // If the original id was aliased, allow mocks to be defined on either key.\n if (target !== id && mockTable && Object.hasOwn(mockTable, id)) {\n return mockTable[id];\n }\n\n return baseRequire(target);\n } as NodeJS.Require;\n\n // Mirror NodeJS.Require properties\n wrappedRequire.resolve = baseRequire.resolve.bind(baseRequire);\n wrappedRequire.main = baseRequire.main;\n wrappedRequire.extensions = baseRequire.extensions;\n wrappedRequire.cache = baseRequire.cache;\n\n return wrappedRequire;\n })();\n\n try {\n // Dynamically try to require React if it's installed in the project\n additionalGlobalVar = {\n React: baseRequire('react'),\n };\n } catch (_err) {\n // React is not installed, so we inject a dummy React object to capture JSX elements\n // This allows using JSX in content declarations even if React is not installed (e.g. in Solid.js or Vue projects)\n // because esbuild's tsx loader defaults to React.createElement.\n additionalGlobalVar = {\n React: {\n createElement: (type: any, props: any, ...children: any[]) => ({\n type,\n props: {\n ...props,\n children: children.length <= 1 ? children[0] : children,\n },\n }),\n Fragment: Symbol.for('react.fragment'),\n },\n };\n }\n\n const sandboxContext: Context = {\n exports: {\n default: {},\n },\n module: {\n exports: {},\n },\n process: {\n ...process,\n env: {\n ...process.env,\n ...loadEnvFile(envVarOptions),\n ...additionalEnvVars,\n },\n },\n console,\n require: mockedRequire,\n ...additionalGlobalVar,\n };\n\n for (const key of NODE_GLOBALS) {\n if (!(key in sandboxContext) && key in globalThis) {\n (sandboxContext as Record<string, unknown>)[key] =\n globalThis[key as keyof typeof globalThis];\n }\n }\n\n return sandboxContext;\n};\n\nexport const parseFileContent = <T>(\n fileContentString: string,\n options?: SandBoxContextOptions\n): T | undefined => {\n const sandboxContext = getSandBoxContext(options);\n\n // Force strict mode so illegal writes throw instead of silently failing.\n runInNewContext(`\"use strict\";\\n${fileContentString}`, sandboxContext);\n\n const candidates: unknown[] = [\n sandboxContext.exports?.default,\n sandboxContext.module?.exports?.defaults,\n sandboxContext.module?.exports?.default,\n sandboxContext.module?.exports,\n ];\n\n let result: T | undefined;\n for (const candidate of candidates) {\n if (\n candidate &&\n typeof candidate === 'object' &&\n Object.keys(candidate as object).length > 0\n ) {\n result = candidate as T;\n break;\n }\n }\n\n // Drop heavy references so the V8 context created by runInNewContext can be\n // garbage-collected promptly. The extracted `result` is a plain data object\n // and does not retain the sandbox.\n (sandboxContext as Record<string, unknown>).require = undefined;\n (sandboxContext as Record<string, unknown>).process = undefined;\n (sandboxContext as Record<string, unknown>).React = undefined;\n (sandboxContext as Record<string, unknown>).module = undefined;\n (sandboxContext as Record<string, unknown>).exports = undefined;\n\n return result;\n};\n"],"mappings":";;;;;;;;;AA+BA,MAAM,eAAe;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAa,qBAAqB,YAA6C;CAC7E,MAAM,EAAE,eAAe,gBAAgB,mBAAmB,OAAO,YAC/D,WAAW,EAAE;CAEf,IAAI,sBAAsB,EAAE;CAE5B,MAAM,cACJ,OAAO,mBAAmB,aAAa,iBAAiBA,gDAAmB;CAG7E,MAAM,uBAAuC;EAC3C,MAAM,YAAY,OAAO,OACvB,EACE,SACD,EACD,MACD;EACD,MAAM,aAAa,OAAO,OAAO,EAAE,EAAE,QAAQ;EAE7C,MAAM,iBAAiB,SAAS,gBAAgB,IAAY;GAC1D,MAAM,SAAS,aAAa,MAAM,WAAW,MAAM;AAEnD,OAAI,aAAa,OAAO,OAAO,WAAW,OAAO,CAC/C,QAAO,UAAU;AAInB,OAAI,WAAW,MAAM,aAAa,OAAO,OAAO,WAAW,GAAG,CAC5D,QAAO,UAAU;AAGnB,UAAO,YAAY,OAAO;;AAI5B,iBAAe,UAAU,YAAY,QAAQ,KAAK,YAAY;AAC9D,iBAAe,OAAO,YAAY;AAClC,iBAAe,aAAa,YAAY;AACxC,iBAAe,QAAQ,YAAY;AAEnC,SAAO;KACL;AAEJ,KAAI;AAEF,wBAAsB,EACpB,OAAO,YAAY,QAAQ,EAC5B;UACM,MAAM;AAIb,wBAAsB,EACpB,OAAO;GACL,gBAAgB,MAAW,OAAY,GAAG,cAAqB;IAC7D;IACA,OAAO;KACL,GAAG;KACH,UAAU,SAAS,UAAU,IAAI,SAAS,KAAK;KAChD;IACF;GACD,UAAU,OAAO,IAAI,iBAAiB;GACvC,EACF;;CAGH,MAAM,iBAA0B;EAC9B,SAAS,EACP,SAAS,EAAE,EACZ;EACD,QAAQ,EACN,SAAS,EAAE,EACZ;EACD,SAAS;GACP,GAAG;GACH,KAAK;IACH,GAAG,QAAQ;IACX,GAAGC,gCAAY,cAAc;IAC7B,GAAG;IACJ;GACF;EACD;EACA,SAAS;EACT,GAAG;EACJ;AAED,MAAK,MAAM,OAAO,aAChB,KAAI,EAAE,OAAO,mBAAmB,OAAO,WACrC,CAAC,eAA2C,OAC1C,WAAW;AAIjB,QAAO;;AAGT,MAAa,oBACX,mBACA,YACkB;CAClB,MAAM,iBAAiB,kBAAkB,QAAQ;AAGjD,8BAAgB,kBAAkB,qBAAqB,eAAe;CAEtE,MAAM,aAAwB;EAC5B,eAAe,SAAS;EACxB,eAAe,QAAQ,SAAS;EAChC,eAAe,QAAQ,SAAS;EAChC,eAAe,QAAQ;EACxB;CAED,IAAI;AACJ,MAAK,MAAM,aAAa,WACtB,KACE,aACA,OAAO,cAAc,YACrB,OAAO,KAAK,UAAoB,CAAC,SAAS,GAC1C;AACA,WAAS;AACT;;AAOJ,CAAC,eAA2C,UAAU;AACtD,CAAC,eAA2C,UAAU;AACtD,CAAC,eAA2C,QAAQ;AACpD,CAAC,eAA2C,SAAS;AACrD,CAAC,eAA2C,UAAU;AAEtD,QAAO"}
1
+ {"version":3,"file":"parseFileContent.cjs","names":["getProjectRequire","loadEnvFile"],"sources":["../../../src/loadExternalFile/parseFileContent.ts"],"sourcesContent":["import { type Context, runInNewContext } from 'node:vm';\nimport * as esbuild from 'esbuild';\nimport { type LoadEnvFileOptions, loadEnvFile } from '../loadEnvFile';\nimport { getProjectRequire } from '../utils/ESMxCJSHelpers';\n\nexport type SandBoxContextOptions = {\n envVarOptions?: LoadEnvFileOptions;\n projectRequire?: NodeJS.Require;\n additionalEnvVars?: Record<string, string>;\n /**\n * Map of specifier -> mocked export to be returned when code in the VM calls require(specifier).\n * Example:\n * mocks: {\n * '@intlayer/config/built': { getConfig: () => ({}), Locales: {} }\n * }\n */\n mocks?: Record<string, any>;\n /**\n * Optional alias map if you want to redirect specifiers.\n * Useful when user code imports a subpath you want to collapse.\n * Example:\n * aliases: { '@intlayer/config/built': '@intlayer/config' }\n */\n aliases?: Record<string, string>;\n};\n\n// Inject only Node.js-specific globals that are absent from a plain V8 context.\n// JS built-ins (Object, Array, Promise, Math, Date, JSON, Symbol, etc.) are\n// provided automatically by runInNewContext — no need to copy them.\n// Copying all of globalThis would retain hundreds of references (including the\n// full module cache via `global`) inside every sandbox, causing a memory leak.\nconst NODE_GLOBALS = [\n 'Buffer',\n 'setTimeout',\n 'clearTimeout',\n 'setInterval',\n 'clearInterval',\n 'setImmediate',\n 'clearImmediate',\n 'queueMicrotask',\n 'URL',\n 'URLSearchParams',\n 'TextEncoder',\n 'TextDecoder',\n 'AbortController',\n 'AbortSignal',\n 'performance',\n 'fetch',\n 'crypto',\n 'structuredClone',\n] as const;\n\n/**\n * Cache of the project's React module (or the JSX-capturing fallback) keyed by\n * the require's resolution paths. Node does not cache *failed* resolutions, so\n * without this every sandbox creation in a non-React project (Vue, Svelte,\n * Angular…) pays a full node_modules walk that ends in a throw.\n */\nconst reactModuleCache = new Map<string, { React: unknown }>();\n\n/**\n * Fallback React implementation used when React is not installed in the\n * project. It captures JSX elements as plain objects so JSX can be used in\n * content declarations (esbuild's tsx loader defaults to React.createElement).\n */\nconst createFallbackReact = () => ({\n createElement: (type: any, props: any, ...children: any[]) => ({\n type,\n props: {\n ...props,\n children: children.length <= 1 ? children[0] : children,\n },\n }),\n Fragment: Symbol.for('react.fragment'),\n});\n\nconst getReactGlobal = (baseRequire: NodeJS.Require): { React: unknown } => {\n // resolve.paths() is computed in-memory (no fs access) and identifies the\n // node_modules chain this require instance resolves against.\n const resolutionKey =\n baseRequire.resolve.paths?.('react')?.join('|') ?? '<default>';\n\n const cachedReactModule = reactModuleCache.get(resolutionKey);\n if (cachedReactModule) return cachedReactModule;\n\n let reactGlobal: { React: unknown };\n try {\n // Dynamically try to require React if it's installed in the project\n reactGlobal = { React: baseRequire('react') };\n } catch (_err) {\n reactGlobal = { React: createFallbackReact() };\n }\n\n reactModuleCache.set(resolutionKey, reactGlobal);\n\n return reactGlobal;\n};\n\n/**\n * Snapshot of the non-env `process` properties, taken once at module load.\n * Spreading `process` enumerates ~100 properties (some behind getters); doing\n * it per sandbox is wasteful since everything except `env` is static.\n */\nconst processSnapshot = { ...process };\n\n/**\n * Builds the global context handed to `runInNewContext`.\n *\n * SECURITY NOTE: this sandbox exists for global-scope hygiene (fresh\n * module/exports, controlled env), NOT for containment. It exposes the real\n * project `require`, a copy of `process`, and `fetch` — code executed here has\n * full host privileges. Only ever run trusted, build-time project files\n * (same trust model as `vite.config.ts`).\n */\nexport const getSandBoxContext = (options?: SandBoxContextOptions): Context => {\n const { envVarOptions, projectRequire, additionalEnvVars, mocks, aliases } =\n options ?? {};\n\n const baseRequire: NodeJS.Require =\n typeof projectRequire === 'function' ? projectRequire : getProjectRequire();\n\n // Wrap require to honor mocks and aliases inside the VM\n const mockedRequire: NodeJS.Require = (() => {\n const mockTable = Object.assign(\n {\n esbuild,\n },\n mocks\n );\n const aliasTable = Object.assign({}, aliases);\n\n const wrappedRequire = function mockableRequire(id: string) {\n const target = aliasTable?.[id] ? aliasTable[id] : id;\n\n if (mockTable && Object.hasOwn(mockTable, target)) {\n return mockTable[target];\n }\n\n // If the original id was aliased, allow mocks to be defined on either key.\n if (target !== id && mockTable && Object.hasOwn(mockTable, id)) {\n return mockTable[id];\n }\n\n return baseRequire(target);\n } as NodeJS.Require;\n\n // Mirror NodeJS.Require properties\n wrappedRequire.resolve = baseRequire.resolve.bind(baseRequire);\n wrappedRequire.main = baseRequire.main;\n wrappedRequire.extensions = baseRequire.extensions;\n wrappedRequire.cache = baseRequire.cache;\n\n return wrappedRequire;\n })();\n\n const sandboxContext: Context = {\n exports: {\n default: {},\n },\n module: {\n exports: {},\n },\n process: {\n ...processSnapshot,\n env: {\n ...process.env,\n ...loadEnvFile(envVarOptions),\n ...additionalEnvVars,\n },\n },\n console,\n require: mockedRequire,\n ...getReactGlobal(baseRequire),\n };\n\n for (const key of NODE_GLOBALS) {\n if (!(key in sandboxContext) && key in globalThis) {\n (sandboxContext as Record<string, unknown>)[key] =\n globalThis[key as keyof typeof globalThis];\n }\n }\n\n return sandboxContext;\n};\n\nexport const parseFileContent = <T>(\n fileContentString: string,\n options?: SandBoxContextOptions\n): T | undefined => {\n const sandboxContext = getSandBoxContext(options);\n\n // Force strict mode so illegal writes throw instead of silently failing.\n runInNewContext(`\"use strict\";\\n${fileContentString}`, sandboxContext);\n\n const candidates: unknown[] = [\n sandboxContext.exports?.default,\n sandboxContext.module?.exports?.defaults,\n sandboxContext.module?.exports?.default,\n sandboxContext.module?.exports,\n ];\n\n let result: T | undefined;\n for (const candidate of candidates) {\n if (\n candidate &&\n typeof candidate === 'object' &&\n Object.keys(candidate as object).length > 0\n ) {\n result = candidate as T;\n break;\n }\n }\n\n // Drop heavy references so the V8 context created by runInNewContext can be\n // garbage-collected promptly. The extracted `result` is a plain data object\n // and does not retain the sandbox.\n (sandboxContext as Record<string, unknown>).require = undefined;\n (sandboxContext as Record<string, unknown>).process = undefined;\n (sandboxContext as Record<string, unknown>).React = undefined;\n (sandboxContext as Record<string, unknown>).module = undefined;\n (sandboxContext as Record<string, unknown>).exports = undefined;\n\n return result;\n};\n"],"mappings":";;;;;;;;;AA+BA,MAAM,eAAe;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;AAQD,MAAM,mCAAmB,IAAI,KAAiC;;;;;;AAO9D,MAAM,6BAA6B;CACjC,gBAAgB,MAAW,OAAY,GAAG,cAAqB;EAC7D;EACA,OAAO;GACL,GAAG;GACH,UAAU,SAAS,UAAU,IAAI,SAAS,KAAK;GAChD;EACF;CACD,UAAU,OAAO,IAAI,iBAAiB;CACvC;AAED,MAAM,kBAAkB,gBAAoD;CAG1E,MAAM,gBACJ,YAAY,QAAQ,QAAQ,QAAQ,EAAE,KAAK,IAAI,IAAI;CAErD,MAAM,oBAAoB,iBAAiB,IAAI,cAAc;AAC7D,KAAI,kBAAmB,QAAO;CAE9B,IAAI;AACJ,KAAI;AAEF,gBAAc,EAAE,OAAO,YAAY,QAAQ,EAAE;UACtC,MAAM;AACb,gBAAc,EAAE,OAAO,qBAAqB,EAAE;;AAGhD,kBAAiB,IAAI,eAAe,YAAY;AAEhD,QAAO;;;;;;;AAQT,MAAM,kBAAkB,EAAE,GAAG,SAAS;;;;;;;;;;AAWtC,MAAa,qBAAqB,YAA6C;CAC7E,MAAM,EAAE,eAAe,gBAAgB,mBAAmB,OAAO,YAC/D,WAAW,EAAE;CAEf,MAAM,cACJ,OAAO,mBAAmB,aAAa,iBAAiBA,gDAAmB;CAG7E,MAAM,uBAAuC;EAC3C,MAAM,YAAY,OAAO,OACvB,EACE,SACD,EACD,MACD;EACD,MAAM,aAAa,OAAO,OAAO,EAAE,EAAE,QAAQ;EAE7C,MAAM,iBAAiB,SAAS,gBAAgB,IAAY;GAC1D,MAAM,SAAS,aAAa,MAAM,WAAW,MAAM;AAEnD,OAAI,aAAa,OAAO,OAAO,WAAW,OAAO,CAC/C,QAAO,UAAU;AAInB,OAAI,WAAW,MAAM,aAAa,OAAO,OAAO,WAAW,GAAG,CAC5D,QAAO,UAAU;AAGnB,UAAO,YAAY,OAAO;;AAI5B,iBAAe,UAAU,YAAY,QAAQ,KAAK,YAAY;AAC9D,iBAAe,OAAO,YAAY;AAClC,iBAAe,aAAa,YAAY;AACxC,iBAAe,QAAQ,YAAY;AAEnC,SAAO;KACL;CAEJ,MAAM,iBAA0B;EAC9B,SAAS,EACP,SAAS,EAAE,EACZ;EACD,QAAQ,EACN,SAAS,EAAE,EACZ;EACD,SAAS;GACP,GAAG;GACH,KAAK;IACH,GAAG,QAAQ;IACX,GAAGC,gCAAY,cAAc;IAC7B,GAAG;IACJ;GACF;EACD;EACA,SAAS;EACT,GAAG,eAAe,YAAY;EAC/B;AAED,MAAK,MAAM,OAAO,aAChB,KAAI,EAAE,OAAO,mBAAmB,OAAO,WACrC,CAAC,eAA2C,OAC1C,WAAW;AAIjB,QAAO;;AAGT,MAAa,oBACX,mBACA,YACkB;CAClB,MAAM,iBAAiB,kBAAkB,QAAQ;AAGjD,8BAAgB,kBAAkB,qBAAqB,eAAe;CAEtE,MAAM,aAAwB;EAC5B,eAAe,SAAS;EACxB,eAAe,QAAQ,SAAS;EAChC,eAAe,QAAQ,SAAS;EAChC,eAAe,QAAQ;EACxB;CAED,IAAI;AACJ,MAAK,MAAM,aAAa,WACtB,KACE,aACA,OAAO,cAAc,YACrB,OAAO,KAAK,UAAoB,CAAC,SAAS,GAC1C;AACA,WAAS;AACT;;AAOJ,CAAC,eAA2C,UAAU;AACtD,CAAC,eAA2C,UAAU;AACtD,CAAC,eAA2C,QAAQ;AACpD,CAAC,eAA2C,SAAS;AACrD,CAAC,eAA2C,UAAU;AAEtD,QAAO"}
@@ -1,5 +1,6 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
+ const require_utils_cacheMemory = require('../utils/cacheMemory.cjs');
3
4
  const require_utils_getPackageJsonPath = require('../utils/getPackageJsonPath.cjs');
4
5
  const require_loadExternalFile_bundleFile = require('./bundleFile.cjs');
5
6
  let node_fs = require("node:fs");
@@ -9,9 +10,23 @@ let esbuild = require("esbuild");
9
10
  let node_url = require("node:url");
10
11
 
11
12
  //#region src/loadExternalFile/transpileTSToCJS.ts
13
+ /**
14
+ * Cache of resolved tsconfig paths keyed by the file's directory.
15
+ * Avoids re-walking to package.json + existsSync on every transpilation.
16
+ */
17
+ const tsConfigPathCache = /* @__PURE__ */ new Map();
12
18
  const getTsConfigPath = (filePath) => {
13
- const tsconfigPath = (0, node_path.join)(require_utils_getPackageJsonPath.getPackageJsonPath((0, node_path.dirname)(filePath)).baseDir, "tsconfig.json");
14
- return (0, node_fs.existsSync)(tsconfigPath) ? tsconfigPath : void 0;
19
+ const fileDirectory = (0, node_path.dirname)(filePath);
20
+ if (tsConfigPathCache.has(fileDirectory)) return tsConfigPathCache.get(fileDirectory);
21
+ let resolvedPath;
22
+ try {
23
+ const tsconfigPath = (0, node_path.join)(require_utils_getPackageJsonPath.getPackageJsonPath(fileDirectory).baseDir, "tsconfig.json");
24
+ resolvedPath = (0, node_fs.existsSync)(tsconfigPath) ? tsconfigPath : void 0;
25
+ } catch {
26
+ resolvedPath = void 0;
27
+ }
28
+ tsConfigPathCache.set(fileDirectory, resolvedPath);
29
+ return resolvedPath;
15
30
  };
16
31
  const getTransformationOptions = (filePath) => ({
17
32
  loader: {
@@ -31,15 +46,85 @@ const getTransformationOptions = (filePath) => ({
31
46
  write: false,
32
47
  packages: "external",
33
48
  bundle: true,
49
+ metafile: true,
34
50
  tsconfig: getTsConfigPath(filePath),
35
51
  define: {
36
52
  "import.meta.url": JSON.stringify((0, node_url.pathToFileURL)(filePath).href),
37
53
  "import.meta.env": "process.env"
38
54
  }
39
55
  });
56
+ /**
57
+ * In-memory cache of transpiled outputs keyed by entry file path (one entry
58
+ * per file, so re-transpilations replace rather than accumulate).
59
+ * The entry file is validated by content hash; bundled relative imports are
60
+ * validated by mtime + size so an edited dependency invalidates the cache.
61
+ */
62
+ const transpileCache = /* @__PURE__ */ new Map();
63
+ const isInputUnchanged = (input) => {
64
+ try {
65
+ const stats = (0, node_fs.statSync)(input.path);
66
+ return stats.mtimeMs === input.mtimeMs && stats.size === input.size;
67
+ } catch {
68
+ return false;
69
+ }
70
+ };
71
+ const getCachedTranspilation = (filePath, codeHash, optionsKey) => {
72
+ const cacheEntry = transpileCache.get(filePath);
73
+ if (!cacheEntry) return void 0;
74
+ if (cacheEntry.codeHash !== codeHash) return void 0;
75
+ if (cacheEntry.optionsKey !== optionsKey) return void 0;
76
+ if (!cacheEntry.inputs.every(isInputUnchanged)) return void 0;
77
+ return cacheEntry.output;
78
+ };
79
+ /**
80
+ * Extracts the fingerprints of every bundled input from the esbuild metafile.
81
+ * Returns `undefined` when an input cannot be fingerprinted (the result should
82
+ * then not be cached, as invalidation would be unreliable).
83
+ */
84
+ const getInputFingerprints = (metafile, entryFilePath) => {
85
+ if (!metafile) return void 0;
86
+ const resolvedEntryFilePath = (0, node_path.resolve)(entryFilePath);
87
+ const inputs = [];
88
+ for (const inputPath of Object.keys(metafile.inputs)) {
89
+ if (inputPath === "<stdin>") continue;
90
+ if (inputPath.includes(":") && !(0, node_path.isAbsolute)(inputPath)) return void 0;
91
+ const absoluteInputPath = (0, node_path.isAbsolute)(inputPath) ? inputPath : (0, node_path.resolve)(process.cwd(), inputPath);
92
+ if (absoluteInputPath === resolvedEntryFilePath) continue;
93
+ try {
94
+ const stats = (0, node_fs.statSync)(absoluteInputPath);
95
+ inputs.push({
96
+ path: absoluteInputPath,
97
+ mtimeMs: stats.mtimeMs,
98
+ size: stats.size
99
+ });
100
+ } catch {
101
+ return;
102
+ }
103
+ }
104
+ return inputs;
105
+ };
106
+ const setCachedTranspilation = (filePath, codeHash, optionsKey, moduleResult, output) => {
107
+ const inputs = getInputFingerprints(moduleResult.metafile, filePath);
108
+ if (!inputs) return;
109
+ transpileCache.set(filePath, {
110
+ codeHash,
111
+ optionsKey,
112
+ output,
113
+ inputs
114
+ });
115
+ };
116
+ /** Clears the in-memory transpilation cache (mainly for tests). */
117
+ const clearTranspileCache = () => {
118
+ transpileCache.clear();
119
+ tsConfigPathCache.clear();
120
+ };
40
121
  const transpileTSToCJSSync = (code, filePath, options) => {
41
122
  const loader = require_loadExternalFile_bundleFile.getLoader((0, node_path.extname)(filePath));
42
123
  const { esbuildInstance, ...buildOptions } = options ?? {};
124
+ const codeHash = require_utils_cacheMemory.computeKeyId([code]);
125
+ const optionsKey = require_utils_cacheMemory.computeKeyId([buildOptions]);
126
+ const cachedOutput = getCachedTranspilation(filePath, codeHash, optionsKey);
127
+ if (typeof cachedOutput === "string") return cachedOutput;
43
128
  const esbuildBuildSync = esbuildInstance?.buildSync ?? esbuild.buildSync;
44
129
  const g = globalThis;
45
130
  const hadFilename = typeof g.__filename === "string";
@@ -70,12 +155,18 @@ const transpileTSToCJSSync = (code, filePath, options) => {
70
155
  else g.__dirname = prevDirname;
71
156
  }
72
157
  }
73
- return moduleResult.outputFiles?.[0]?.text;
158
+ const moduleResultString = moduleResult.outputFiles?.[0]?.text;
159
+ if (typeof moduleResultString === "string") setCachedTranspilation(filePath, codeHash, optionsKey, moduleResult, moduleResultString);
160
+ return moduleResultString;
74
161
  };
75
162
  const transpileTSToCJS = async (code, filePath, options) => {
76
163
  const loader = require_loadExternalFile_bundleFile.getLoader((0, node_path.extname)(filePath));
77
164
  const { esbuildInstance, ...buildOptions } = options ?? {};
78
- const ctx = await (esbuildInstance?.context ?? esbuild.context)({
165
+ const codeHash = require_utils_cacheMemory.computeKeyId([code]);
166
+ const optionsKey = require_utils_cacheMemory.computeKeyId([buildOptions]);
167
+ const cachedOutput = getCachedTranspilation(filePath, codeHash, optionsKey);
168
+ if (typeof cachedOutput === "string") return cachedOutput;
169
+ const moduleResult = await (esbuildInstance?.build ?? esbuild.build)({
79
170
  stdin: {
80
171
  contents: code,
81
172
  loader,
@@ -85,14 +176,13 @@ const transpileTSToCJS = async (code, filePath, options) => {
85
176
  ...getTransformationOptions(filePath),
86
177
  ...buildOptions
87
178
  });
88
- try {
89
- return (await ctx.rebuild()).outputFiles?.[0].text;
90
- } finally {
91
- await ctx.dispose();
92
- }
179
+ const moduleResultString = moduleResult.outputFiles?.[0]?.text;
180
+ if (typeof moduleResultString === "string") setCachedTranspilation(filePath, codeHash, optionsKey, moduleResult, moduleResultString);
181
+ return moduleResultString;
93
182
  };
94
183
 
95
184
  //#endregion
185
+ exports.clearTranspileCache = clearTranspileCache;
96
186
  exports.transpileTSToCJS = transpileTSToCJS;
97
187
  exports.transpileTSToCJSSync = transpileTSToCJSSync;
98
188
  //# sourceMappingURL=transpileTSToCJS.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"transpileTSToCJS.cjs","names":["getPackageJsonPath","getLoader","buildSync","context"],"sources":["../../../src/loadExternalFile/transpileTSToCJS.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, extname, join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport {\n type BuildOptions,\n type BuildResult,\n buildSync,\n context,\n} from 'esbuild';\nimport { getPackageJsonPath } from '../utils/getPackageJsonPath';\nimport { getLoader } from './bundleFile';\n\nexport type TranspileOptions = BuildOptions & {\n /**\n * Optional custom esbuild instance to use for transpilation.\n * Useful in environments (e.g. VS Code extensions) where the bundled\n * esbuild binary may not match the host platform.\n * When provided, its `buildSync`/`build` methods are used instead of\n * the ones imported from the `esbuild` package.\n */\n esbuildInstance?: typeof import('esbuild');\n};\n\nconst getTsConfigPath = (filePath: string): string | undefined => {\n const tsconfigPath = join(\n getPackageJsonPath(dirname(filePath)).baseDir,\n 'tsconfig.json'\n );\n\n // Only return the tsconfig path if it exists\n return existsSync(tsconfigPath) ? tsconfigPath : undefined;\n};\n\nconst getTransformationOptions = (filePath: string): BuildOptions => ({\n loader: {\n '.js': 'js',\n '.jsx': 'jsx',\n '.mjs': 'js',\n '.ts': 'ts',\n '.tsx': 'tsx',\n '.cjs': 'js',\n '.json': 'json',\n '.md': 'text',\n '.mdx': 'text',\n },\n format: 'cjs',\n target: 'node20',\n platform: 'node',\n write: false,\n packages: 'external',\n bundle: true,\n tsconfig: getTsConfigPath(filePath),\n define: {\n 'import.meta.url': JSON.stringify(pathToFileURL(filePath).href),\n 'import.meta.env': 'process.env',\n },\n});\n\nexport const transpileTSToCJSSync = (\n code: string,\n filePath: string,\n options?: TranspileOptions\n): string | undefined => {\n const extension = extname(filePath);\n const loader = getLoader(extension);\n\n const { esbuildInstance, ...buildOptions } = options ?? {};\n const esbuildBuildSync = esbuildInstance?.buildSync ?? buildSync;\n\n // esbuild's worker thread service calls `new Worker(__filename, …)` on first use.\n // In Vite's SSR module runner the SSR-optimised chunk is ESM and __filename is\n // never declared (confirmed: accessing it throws ReferenceError, not undefined).\n // Because there is no local declaration the bare `__filename` lookup falls\n // through to globalThis, so we set it there to esbuild's own CJS entry-point –\n // the exact path esbuild would use if it were loaded in a normal CJS context.\n //\n // IMPORTANT: We save/restore the globals so this temporary shim does not leak\n // to other Vite plugins (e.g. `@vitejs/plugin-react-swc`) that check\n // `typeof __dirname !== \"undefined\"` to locate their own assets.\n const g = globalThis as Record<string, unknown>;\n const hadFilename = typeof g.__filename === 'string';\n const prevFilename = g.__filename;\n const prevDirname = g.__dirname;\n\n if (!hadFilename) {\n try {\n const _require = createRequire(import.meta.url);\n const esbuildEntry = _require.resolve('esbuild');\n g.__filename = esbuildEntry;\n g.__dirname = dirname(esbuildEntry);\n } catch {\n // Best-effort: if esbuild can't be resolved the caller's catch handles it\n }\n }\n\n let moduleResult: BuildResult;\n try {\n moduleResult = esbuildBuildSync({\n stdin: {\n contents: code,\n loader,\n resolveDir: dirname(filePath), // Add resolveDir to resolve imports relative to the file's location\n sourcefile: filePath, // Add sourcefile for better error messages\n },\n ...getTransformationOptions(filePath),\n ...buildOptions,\n });\n } finally {\n // Always restore the previous values so the globals don't linger.\n if (!hadFilename) {\n if (prevFilename === undefined) {\n delete g.__filename;\n } else {\n g.__filename = prevFilename;\n }\n if (prevDirname === undefined) {\n delete g.__dirname;\n } else {\n g.__dirname = prevDirname;\n }\n }\n }\n\n const moduleResultString = moduleResult!.outputFiles?.[0]?.text;\n\n return moduleResultString;\n};\n\nexport const transpileTSToCJS = async (\n code: string,\n filePath: string,\n options?: TranspileOptions\n): Promise<string | undefined> => {\n const extension = extname(filePath);\n const loader = getLoader(extension);\n\n const { esbuildInstance, ...buildOptions } = options ?? {};\n // Use context() + rebuild() + dispose() so esbuild deterministically releases\n // Go-subprocess resources for each one-shot transpilation, preventing them\n // from accumulating between rapid HMR-driven file changes.\n const esbuildContext = esbuildInstance?.context ?? context;\n\n const ctx = await esbuildContext({\n stdin: {\n contents: code,\n loader,\n resolveDir: dirname(filePath),\n sourcefile: filePath,\n },\n ...getTransformationOptions(filePath),\n ...buildOptions,\n });\n\n try {\n const moduleResult = await ctx.rebuild();\n return moduleResult.outputFiles?.[0].text;\n } finally {\n await ctx.dispose();\n }\n};\n"],"mappings":";;;;;;;;;;;AAwBA,MAAM,mBAAmB,aAAyC;CAChE,MAAM,mCACJA,2EAA2B,SAAS,CAAC,CAAC,SACtC,gBACD;AAGD,gCAAkB,aAAa,GAAG,eAAe;;AAGnD,MAAM,4BAA4B,cAAoC;CACpE,QAAQ;EACN,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;EACT;CACD,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,OAAO;CACP,UAAU;CACV,QAAQ;CACR,UAAU,gBAAgB,SAAS;CACnC,QAAQ;EACN,mBAAmB,KAAK,sCAAwB,SAAS,CAAC,KAAK;EAC/D,mBAAmB;EACpB;CACF;AAED,MAAa,wBACX,MACA,UACA,YACuB;CAEvB,MAAM,SAASC,qEADW,SACQ,CAAC;CAEnC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,WAAW,EAAE;CAC1D,MAAM,mBAAmB,iBAAiB,aAAaC;CAYvD,MAAM,IAAI;CACV,MAAM,cAAc,OAAO,EAAE,eAAe;CAC5C,MAAM,eAAe,EAAE;CACvB,MAAM,cAAc,EAAE;AAEtB,KAAI,CAAC,YACH,KAAI;EAEF,MAAM,4FAAuB,CAAC,QAAQ,UAAU;AAChD,IAAE,aAAa;AACf,IAAE,mCAAoB,aAAa;SAC7B;CAKV,IAAI;AACJ,KAAI;AACF,iBAAe,iBAAiB;GAC9B,OAAO;IACL,UAAU;IACV;IACA,mCAAoB,SAAS;IAC7B,YAAY;IACb;GACD,GAAG,yBAAyB,SAAS;GACrC,GAAG;GACJ,CAAC;WACM;AAER,MAAI,CAAC,aAAa;AAChB,OAAI,iBAAiB,OACnB,QAAO,EAAE;OAET,GAAE,aAAa;AAEjB,OAAI,gBAAgB,OAClB,QAAO,EAAE;OAET,GAAE,YAAY;;;AAOpB,QAF2B,aAAc,cAAc,IAAI;;AAK7D,MAAa,mBAAmB,OAC9B,MACA,UACA,YACgC;CAEhC,MAAM,SAASD,qEADW,SACQ,CAAC;CAEnC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,WAAW,EAAE;CAM1D,MAAM,MAAM,OAFW,iBAAiB,WAAWE,iBAElB;EAC/B,OAAO;GACL,UAAU;GACV;GACA,mCAAoB,SAAS;GAC7B,YAAY;GACb;EACD,GAAG,yBAAyB,SAAS;EACrC,GAAG;EACJ,CAAC;AAEF,KAAI;AAEF,UAAO,MADoB,IAAI,SAAS,EACpB,cAAc,GAAG;WAC7B;AACR,QAAM,IAAI,SAAS"}
1
+ {"version":3,"file":"transpileTSToCJS.cjs","names":["getPackageJsonPath","getLoader","computeKeyId","buildSync","build"],"sources":["../../../src/loadExternalFile/transpileTSToCJS.ts"],"sourcesContent":["import { existsSync, statSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, extname, isAbsolute, join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport {\n type BuildOptions,\n type BuildResult,\n build,\n buildSync,\n type Metafile,\n} from 'esbuild';\nimport { computeKeyId } from '../utils/cacheMemory';\nimport { getPackageJsonPath } from '../utils/getPackageJsonPath';\nimport { getLoader } from './bundleFile';\n\nexport type TranspileOptions = BuildOptions & {\n /**\n * Optional custom esbuild instance to use for transpilation.\n * Useful in environments (e.g. VS Code extensions) where the bundled\n * esbuild binary may not match the host platform.\n * When provided, its `buildSync`/`build` methods are used instead of\n * the ones imported from the `esbuild` package.\n */\n esbuildInstance?: typeof import('esbuild');\n};\n\n/**\n * Cache of resolved tsconfig paths keyed by the file's directory.\n * Avoids re-walking to package.json + existsSync on every transpilation.\n */\nconst tsConfigPathCache = new Map<string, string | undefined>();\n\nconst getTsConfigPath = (filePath: string): string | undefined => {\n const fileDirectory = dirname(filePath);\n\n if (tsConfigPathCache.has(fileDirectory)) {\n return tsConfigPathCache.get(fileDirectory);\n }\n\n let resolvedPath: string | undefined;\n try {\n const tsconfigPath = join(\n getPackageJsonPath(fileDirectory).baseDir,\n 'tsconfig.json'\n );\n\n // Only return the tsconfig path if it exists\n resolvedPath = existsSync(tsconfigPath) ? tsconfigPath : undefined;\n } catch {\n // No package.json found up the tree — transpile without a tsconfig\n resolvedPath = undefined;\n }\n\n tsConfigPathCache.set(fileDirectory, resolvedPath);\n\n return resolvedPath;\n};\n\nconst getTransformationOptions = (filePath: string): BuildOptions => ({\n loader: {\n '.js': 'js',\n '.jsx': 'jsx',\n '.mjs': 'js',\n '.ts': 'ts',\n '.tsx': 'tsx',\n '.cjs': 'js',\n '.json': 'json',\n '.md': 'text',\n '.mdx': 'text',\n },\n format: 'cjs',\n target: 'node20',\n platform: 'node',\n write: false,\n packages: 'external',\n bundle: true,\n // The metafile lists every bundled input, which the transpile cache uses to\n // invalidate when an imported file changes (the entry itself is keyed by hash).\n metafile: true,\n tsconfig: getTsConfigPath(filePath),\n define: {\n 'import.meta.url': JSON.stringify(pathToFileURL(filePath).href),\n 'import.meta.env': 'process.env',\n },\n});\n\n/** Fingerprint of a bundled input file, used to detect changes without re-reading it. */\ntype TranspileCacheInput = {\n path: string;\n mtimeMs: number;\n size: number;\n};\n\ntype TranspileCacheEntry = {\n /** Hash of the entry file's source code. */\n codeHash: string;\n /** Hash of the build options, so different option sets don't collide. */\n optionsKey: string;\n /** The transpiled CJS output. */\n output: string;\n /** Fingerprints of every non-entry input bundled into the output. */\n inputs: TranspileCacheInput[];\n};\n\n/**\n * In-memory cache of transpiled outputs keyed by entry file path (one entry\n * per file, so re-transpilations replace rather than accumulate).\n * The entry file is validated by content hash; bundled relative imports are\n * validated by mtime + size so an edited dependency invalidates the cache.\n */\nconst transpileCache = new Map<string, TranspileCacheEntry>();\n\nconst isInputUnchanged = (input: TranspileCacheInput): boolean => {\n try {\n const stats = statSync(input.path);\n return stats.mtimeMs === input.mtimeMs && stats.size === input.size;\n } catch {\n // File removed or unreadable — treat as changed\n return false;\n }\n};\n\nconst getCachedTranspilation = (\n filePath: string,\n codeHash: string,\n optionsKey: string\n): string | undefined => {\n const cacheEntry = transpileCache.get(filePath);\n\n if (!cacheEntry) return undefined;\n if (cacheEntry.codeHash !== codeHash) return undefined;\n if (cacheEntry.optionsKey !== optionsKey) return undefined;\n if (!cacheEntry.inputs.every(isInputUnchanged)) return undefined;\n\n return cacheEntry.output;\n};\n\n/**\n * Extracts the fingerprints of every bundled input from the esbuild metafile.\n * Returns `undefined` when an input cannot be fingerprinted (the result should\n * then not be cached, as invalidation would be unreliable).\n */\nconst getInputFingerprints = (\n metafile: Metafile | undefined,\n entryFilePath: string\n): TranspileCacheInput[] | undefined => {\n if (!metafile) return undefined;\n\n const resolvedEntryFilePath = resolve(entryFilePath);\n const inputs: TranspileCacheInput[] = [];\n\n for (const inputPath of Object.keys(metafile.inputs)) {\n // The entry itself is provided via stdin and validated by content hash.\n // esbuild labels it \"<stdin>\", or with the sourcefile path when set.\n if (inputPath === '<stdin>') continue;\n // Virtual modules from plugins (e.g. \"plugin:...\") cannot be fingerprinted\n if (inputPath.includes(':') && !isAbsolute(inputPath)) return undefined;\n\n const absoluteInputPath = isAbsolute(inputPath)\n ? inputPath\n : resolve(process.cwd(), inputPath);\n\n if (absoluteInputPath === resolvedEntryFilePath) continue;\n\n try {\n const stats = statSync(absoluteInputPath);\n inputs.push({\n path: absoluteInputPath,\n mtimeMs: stats.mtimeMs,\n size: stats.size,\n });\n } catch {\n return undefined;\n }\n }\n\n return inputs;\n};\n\nconst setCachedTranspilation = (\n filePath: string,\n codeHash: string,\n optionsKey: string,\n moduleResult: BuildResult,\n output: string\n): void => {\n const inputs = getInputFingerprints(moduleResult.metafile, filePath);\n\n // Without a usable metafile the cache cannot be invalidated reliably — skip\n if (!inputs) return;\n\n transpileCache.set(filePath, { codeHash, optionsKey, output, inputs });\n};\n\n/** Clears the in-memory transpilation cache (mainly for tests). */\nexport const clearTranspileCache = (): void => {\n transpileCache.clear();\n tsConfigPathCache.clear();\n};\n\nexport const transpileTSToCJSSync = (\n code: string,\n filePath: string,\n options?: TranspileOptions\n): string | undefined => {\n const extension = extname(filePath);\n const loader = getLoader(extension);\n\n const { esbuildInstance, ...buildOptions } = options ?? {};\n\n const codeHash = computeKeyId([code]);\n const optionsKey = computeKeyId([buildOptions]);\n\n const cachedOutput = getCachedTranspilation(filePath, codeHash, optionsKey);\n if (typeof cachedOutput === 'string') return cachedOutput;\n\n const esbuildBuildSync = esbuildInstance?.buildSync ?? buildSync;\n\n // esbuild's worker thread service calls `new Worker(__filename, …)` on first use.\n // In Vite's SSR module runner the SSR-optimised chunk is ESM and __filename is\n // never declared (confirmed: accessing it throws ReferenceError, not undefined).\n // Because there is no local declaration the bare `__filename` lookup falls\n // through to globalThis, so we set it there to esbuild's own CJS entry-point –\n // the exact path esbuild would use if it were loaded in a normal CJS context.\n //\n // IMPORTANT: We save/restore the globals so this temporary shim does not leak\n // to other Vite plugins (e.g. `@vitejs/plugin-react-swc`) that check\n // `typeof __dirname !== \"undefined\"` to locate their own assets.\n const g = globalThis as Record<string, unknown>;\n const hadFilename = typeof g.__filename === 'string';\n const prevFilename = g.__filename;\n const prevDirname = g.__dirname;\n\n if (!hadFilename) {\n try {\n const _require = createRequire(import.meta.url);\n const esbuildEntry = _require.resolve('esbuild');\n g.__filename = esbuildEntry;\n g.__dirname = dirname(esbuildEntry);\n } catch {\n // Best-effort: if esbuild can't be resolved the caller's catch handles it\n }\n }\n\n let moduleResult: BuildResult;\n try {\n moduleResult = esbuildBuildSync({\n stdin: {\n contents: code,\n loader,\n resolveDir: dirname(filePath), // Add resolveDir to resolve imports relative to the file's location\n sourcefile: filePath, // Add sourcefile for better error messages\n },\n ...getTransformationOptions(filePath),\n ...buildOptions,\n });\n } finally {\n // Always restore the previous values so the globals don't linger.\n if (!hadFilename) {\n if (prevFilename === undefined) {\n // biome-ignore lint/performance/noDelete: the global must be truly absent — assigning undefined would leave the key present and break `typeof __filename` checks in other tools\n delete g.__filename;\n } else {\n g.__filename = prevFilename;\n }\n if (prevDirname === undefined) {\n // biome-ignore lint/performance/noDelete: same as __filename above\n delete g.__dirname;\n } else {\n g.__dirname = prevDirname;\n }\n }\n }\n\n const moduleResultString = moduleResult!.outputFiles?.[0]?.text;\n\n if (typeof moduleResultString === 'string') {\n setCachedTranspilation(\n filePath,\n codeHash,\n optionsKey,\n moduleResult!,\n moduleResultString\n );\n }\n\n return moduleResultString;\n};\n\nexport const transpileTSToCJS = async (\n code: string,\n filePath: string,\n options?: TranspileOptions\n): Promise<string | undefined> => {\n const extension = extname(filePath);\n const loader = getLoader(extension);\n\n const { esbuildInstance, ...buildOptions } = options ?? {};\n\n const codeHash = computeKeyId([code]);\n const optionsKey = computeKeyId([buildOptions]);\n\n const cachedOutput = getCachedTranspilation(filePath, codeHash, optionsKey);\n if (typeof cachedOutput === 'string') return cachedOutput;\n\n // A one-shot build() releases its Go-side resources on completion and costs a\n // single service round-trip (context()+rebuild()+dispose() costs three).\n const esbuildBuild = esbuildInstance?.build ?? build;\n\n const moduleResult = await esbuildBuild({\n stdin: {\n contents: code,\n loader,\n resolveDir: dirname(filePath),\n sourcefile: filePath,\n },\n ...getTransformationOptions(filePath),\n ...buildOptions,\n });\n\n const moduleResultString = moduleResult.outputFiles?.[0]?.text;\n\n if (typeof moduleResultString === 'string') {\n setCachedTranspilation(\n filePath,\n codeHash,\n optionsKey,\n moduleResult,\n moduleResultString\n );\n }\n\n return moduleResultString;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AA8BA,MAAM,oCAAoB,IAAI,KAAiC;AAE/D,MAAM,mBAAmB,aAAyC;CAChE,MAAM,uCAAwB,SAAS;AAEvC,KAAI,kBAAkB,IAAI,cAAc,CACtC,QAAO,kBAAkB,IAAI,cAAc;CAG7C,IAAI;AACJ,KAAI;EACF,MAAM,mCACJA,oDAAmB,cAAc,CAAC,SAClC,gBACD;AAGD,yCAA0B,aAAa,GAAG,eAAe;SACnD;AAEN,iBAAe;;AAGjB,mBAAkB,IAAI,eAAe,aAAa;AAElD,QAAO;;AAGT,MAAM,4BAA4B,cAAoC;CACpE,QAAQ;EACN,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;EACT;CACD,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,OAAO;CACP,UAAU;CACV,QAAQ;CAGR,UAAU;CACV,UAAU,gBAAgB,SAAS;CACnC,QAAQ;EACN,mBAAmB,KAAK,sCAAwB,SAAS,CAAC,KAAK;EAC/D,mBAAmB;EACpB;CACF;;;;;;;AA0BD,MAAM,iCAAiB,IAAI,KAAkC;AAE7D,MAAM,oBAAoB,UAAwC;AAChE,KAAI;EACF,MAAM,8BAAiB,MAAM,KAAK;AAClC,SAAO,MAAM,YAAY,MAAM,WAAW,MAAM,SAAS,MAAM;SACzD;AAEN,SAAO;;;AAIX,MAAM,0BACJ,UACA,UACA,eACuB;CACvB,MAAM,aAAa,eAAe,IAAI,SAAS;AAE/C,KAAI,CAAC,WAAY,QAAO;AACxB,KAAI,WAAW,aAAa,SAAU,QAAO;AAC7C,KAAI,WAAW,eAAe,WAAY,QAAO;AACjD,KAAI,CAAC,WAAW,OAAO,MAAM,iBAAiB,CAAE,QAAO;AAEvD,QAAO,WAAW;;;;;;;AAQpB,MAAM,wBACJ,UACA,kBACsC;AACtC,KAAI,CAAC,SAAU,QAAO;CAEtB,MAAM,+CAAgC,cAAc;CACpD,MAAM,SAAgC,EAAE;AAExC,MAAK,MAAM,aAAa,OAAO,KAAK,SAAS,OAAO,EAAE;AAGpD,MAAI,cAAc,UAAW;AAE7B,MAAI,UAAU,SAAS,IAAI,IAAI,2BAAY,UAAU,CAAE,QAAO;EAE9D,MAAM,8CAA+B,UAAU,GAC3C,mCACQ,QAAQ,KAAK,EAAE,UAAU;AAErC,MAAI,sBAAsB,sBAAuB;AAEjD,MAAI;GACF,MAAM,8BAAiB,kBAAkB;AACzC,UAAO,KAAK;IACV,MAAM;IACN,SAAS,MAAM;IACf,MAAM,MAAM;IACb,CAAC;UACI;AACN;;;AAIJ,QAAO;;AAGT,MAAM,0BACJ,UACA,UACA,YACA,cACA,WACS;CACT,MAAM,SAAS,qBAAqB,aAAa,UAAU,SAAS;AAGpE,KAAI,CAAC,OAAQ;AAEb,gBAAe,IAAI,UAAU;EAAE;EAAU;EAAY;EAAQ;EAAQ,CAAC;;;AAIxE,MAAa,4BAAkC;AAC7C,gBAAe,OAAO;AACtB,mBAAkB,OAAO;;AAG3B,MAAa,wBACX,MACA,UACA,YACuB;CAEvB,MAAM,SAASC,qEADW,SACQ,CAAC;CAEnC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,WAAW,EAAE;CAE1D,MAAM,WAAWC,uCAAa,CAAC,KAAK,CAAC;CACrC,MAAM,aAAaA,uCAAa,CAAC,aAAa,CAAC;CAE/C,MAAM,eAAe,uBAAuB,UAAU,UAAU,WAAW;AAC3E,KAAI,OAAO,iBAAiB,SAAU,QAAO;CAE7C,MAAM,mBAAmB,iBAAiB,aAAaC;CAYvD,MAAM,IAAI;CACV,MAAM,cAAc,OAAO,EAAE,eAAe;CAC5C,MAAM,eAAe,EAAE;CACvB,MAAM,cAAc,EAAE;AAEtB,KAAI,CAAC,YACH,KAAI;EAEF,MAAM,4FAAuB,CAAC,QAAQ,UAAU;AAChD,IAAE,aAAa;AACf,IAAE,mCAAoB,aAAa;SAC7B;CAKV,IAAI;AACJ,KAAI;AACF,iBAAe,iBAAiB;GAC9B,OAAO;IACL,UAAU;IACV;IACA,mCAAoB,SAAS;IAC7B,YAAY;IACb;GACD,GAAG,yBAAyB,SAAS;GACrC,GAAG;GACJ,CAAC;WACM;AAER,MAAI,CAAC,aAAa;AAChB,OAAI,iBAAiB,OAEnB,QAAO,EAAE;OAET,GAAE,aAAa;AAEjB,OAAI,gBAAgB,OAElB,QAAO,EAAE;OAET,GAAE,YAAY;;;CAKpB,MAAM,qBAAqB,aAAc,cAAc,IAAI;AAE3D,KAAI,OAAO,uBAAuB,SAChC,wBACE,UACA,UACA,YACA,cACA,mBACD;AAGH,QAAO;;AAGT,MAAa,mBAAmB,OAC9B,MACA,UACA,YACgC;CAEhC,MAAM,SAASF,qEADW,SACQ,CAAC;CAEnC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,WAAW,EAAE;CAE1D,MAAM,WAAWC,uCAAa,CAAC,KAAK,CAAC;CACrC,MAAM,aAAaA,uCAAa,CAAC,aAAa,CAAC;CAE/C,MAAM,eAAe,uBAAuB,UAAU,UAAU,WAAW;AAC3E,KAAI,OAAO,iBAAiB,SAAU,QAAO;CAM7C,MAAM,eAAe,OAFA,iBAAiB,SAASE,eAEP;EACtC,OAAO;GACL,UAAU;GACV;GACA,mCAAoB,SAAS;GAC7B,YAAY;GACb;EACD,GAAG,yBAAyB,SAAS;EACrC,GAAG;EACJ,CAAC;CAEF,MAAM,qBAAqB,aAAa,cAAc,IAAI;AAE1D,KAAI,OAAO,uBAAuB,SAChC,wBACE,UACA,UACA,YACA,cACA,mBACD;AAGH,QAAO"}
@@ -1,4 +1,4 @@
1
- import { existsSync } from "node:fs";
1
+ import { existsSync, statSync } from "node:fs";
2
2
  import dotenv from "dotenv";
3
3
 
4
4
  //#region src/loadEnvFile.ts
@@ -11,16 +11,53 @@ const getEnvFilePath = (env = "development", envFile) => {
11
11
  ".env"
12
12
  ]).find(existsSync);
13
13
  };
14
- const loadEnvFile = (options) => {
15
- const envFiles = getEnvFilePath(options?.env ?? DEFAULT_ENV, options?.envFile);
16
- if (!envFiles) return {};
17
- const result = {};
14
+ /**
15
+ * Cache of parsed env files keyed by `cwd|env|envFile`. Loading content
16
+ * declarations calls `loadEnvFile` once per file; without this every call pays
17
+ * up to 4 `existsSync` probes plus a dotenv read+parse. A cached hit is
18
+ * validated with a single `stat` so edits to the env file are still picked up.
19
+ */
20
+ const envFileCache = /* @__PURE__ */ new Map();
21
+ const parseEnvFile = (envFilePath) => {
22
+ const parsedEnv = {};
18
23
  dotenv.config({
19
- path: envFiles,
20
- processEnv: result,
24
+ path: envFilePath,
25
+ processEnv: parsedEnv,
21
26
  quiet: true
22
27
  });
23
- return result;
28
+ return parsedEnv;
29
+ };
30
+ const loadEnvFile = (options) => {
31
+ const env = options?.env ?? DEFAULT_ENV;
32
+ const cacheKey = `${process.cwd()}|${env}|${options?.envFile ?? ""}`;
33
+ const cachedEntry = envFileCache.get(cacheKey);
34
+ if (cachedEntry) if (cachedEntry.envFilePath === void 0) {
35
+ if (!getEnvFilePath(env, options?.envFile)) return cachedEntry.parsedEnv;
36
+ } else try {
37
+ const stats = statSync(cachedEntry.envFilePath);
38
+ if (stats.mtimeMs === cachedEntry.mtimeMs && stats.size === cachedEntry.size) return cachedEntry.parsedEnv;
39
+ } catch {}
40
+ const envFilePath = getEnvFilePath(env, options?.envFile);
41
+ if (!envFilePath) {
42
+ envFileCache.set(cacheKey, {
43
+ envFilePath: void 0,
44
+ mtimeMs: 0,
45
+ size: 0,
46
+ parsedEnv: {}
47
+ });
48
+ return {};
49
+ }
50
+ const parsedEnv = parseEnvFile(envFilePath);
51
+ try {
52
+ const stats = statSync(envFilePath);
53
+ envFileCache.set(cacheKey, {
54
+ envFilePath,
55
+ mtimeMs: stats.mtimeMs,
56
+ size: stats.size,
57
+ parsedEnv
58
+ });
59
+ } catch {}
60
+ return parsedEnv;
24
61
  };
25
62
 
26
63
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"loadEnvFile.mjs","names":[],"sources":["../../src/loadEnvFile.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport dotenv from 'dotenv';\n\nconst DEFAULT_ENV = process.env.NODE_ENV ?? 'development';\n\nexport type LoadEnvFileOptions = {\n env?: string;\n envFile?: string;\n};\n\nexport const getEnvFilePath = (\n env: string = process.env.NODE_ENV ?? 'development',\n envFile?: string\n): string | undefined => {\n const envFiles = envFile\n ? [envFile]\n : [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env'];\n\n return envFiles.find(existsSync); // Returns the first existing env file\n};\n\nexport const loadEnvFile = (options?: Partial<LoadEnvFileOptions>) => {\n const env = options?.env ?? DEFAULT_ENV;\n\n const envFiles = getEnvFilePath(env, options?.envFile);\n\n if (!envFiles) {\n return {};\n }\n\n const result = {};\n\n dotenv.config({\n path: envFiles,\n processEnv: result,\n quiet: true,\n });\n\n return result; // Return the parsed env object\n};\n"],"mappings":";;;;AAGA,MAAM;AAON,MAAa,kBACX,qBACA,YACuB;AAKvB,SAJiB,UACb,CAAC,QAAQ,GACT;EAAC,QAAQ,IAAI;EAAS,QAAQ;EAAO;EAAc;EAAO,EAE9C,KAAK,WAAW;;AAGlC,MAAa,eAAe,YAA0C;CAGpE,MAAM,WAAW,eAFL,SAAS,OAAO,aAES,SAAS,QAAQ;AAEtD,KAAI,CAAC,SACH,QAAO,EAAE;CAGX,MAAM,SAAS,EAAE;AAEjB,QAAO,OAAO;EACZ,MAAM;EACN,YAAY;EACZ,OAAO;EACR,CAAC;AAEF,QAAO"}
1
+ {"version":3,"file":"loadEnvFile.mjs","names":[],"sources":["../../src/loadEnvFile.ts"],"sourcesContent":["import { existsSync, statSync } from 'node:fs';\nimport dotenv from 'dotenv';\n\nconst DEFAULT_ENV = process.env.NODE_ENV ?? 'development';\n\nexport type LoadEnvFileOptions = {\n env?: string;\n envFile?: string;\n};\n\nexport const getEnvFilePath = (\n env: string = process.env.NODE_ENV ?? 'development',\n envFile?: string\n): string | undefined => {\n const envFiles = envFile\n ? [envFile]\n : [`.env.${env}.local`, `.env.${env}`, '.env.local', '.env'];\n\n return envFiles.find(existsSync); // Returns the first existing env file\n};\n\ntype EnvFileCacheEntry = {\n /** The env file that was resolved and parsed (undefined if none exists). */\n envFilePath: string | undefined;\n /** Fingerprint of the parsed file, used to detect edits. */\n mtimeMs: number;\n size: number;\n /** The parsed env variables. */\n parsedEnv: Record<string, string>;\n};\n\n/**\n * Cache of parsed env files keyed by `cwd|env|envFile`. Loading content\n * declarations calls `loadEnvFile` once per file; without this every call pays\n * up to 4 `existsSync` probes plus a dotenv read+parse. A cached hit is\n * validated with a single `stat` so edits to the env file are still picked up.\n */\nconst envFileCache = new Map<string, EnvFileCacheEntry>();\n\nconst parseEnvFile = (envFilePath: string): Record<string, string> => {\n const parsedEnv: Record<string, string> = {};\n\n dotenv.config({\n path: envFilePath,\n processEnv: parsedEnv,\n quiet: true,\n });\n\n return parsedEnv;\n};\n\nexport const loadEnvFile = (\n options?: Partial<LoadEnvFileOptions>\n): Record<string, string> => {\n const env = options?.env ?? DEFAULT_ENV;\n\n // Env file candidates are resolved relative to the working directory\n const cacheKey = `${process.cwd()}|${env}|${options?.envFile ?? ''}`;\n\n const cachedEntry = envFileCache.get(cacheKey);\n\n if (cachedEntry) {\n if (cachedEntry.envFilePath === undefined) {\n // No env file existed. Re-probe cheaply in case one was created since.\n const envFilePath = getEnvFilePath(env, options?.envFile);\n if (!envFilePath) return cachedEntry.parsedEnv;\n } else {\n try {\n const stats = statSync(cachedEntry.envFilePath);\n if (\n stats.mtimeMs === cachedEntry.mtimeMs &&\n stats.size === cachedEntry.size\n ) {\n return cachedEntry.parsedEnv;\n }\n } catch {\n // File was removed — fall through and re-resolve\n }\n }\n }\n\n const envFilePath = getEnvFilePath(env, options?.envFile);\n\n if (!envFilePath) {\n envFileCache.set(cacheKey, {\n envFilePath: undefined,\n mtimeMs: 0,\n size: 0,\n parsedEnv: {},\n });\n return {};\n }\n\n const parsedEnv = parseEnvFile(envFilePath);\n\n try {\n const stats = statSync(envFilePath);\n envFileCache.set(cacheKey, {\n envFilePath,\n mtimeMs: stats.mtimeMs,\n size: stats.size,\n parsedEnv,\n });\n } catch {\n // Race: file removed between parse and stat — skip caching this round\n }\n\n return parsedEnv; // Return the parsed env object\n};\n"],"mappings":";;;;AAGA,MAAM;AAON,MAAa,kBACX,qBACA,YACuB;AAKvB,SAJiB,UACb,CAAC,QAAQ,GACT;EAAC,QAAQ,IAAI;EAAS,QAAQ;EAAO;EAAc;EAAO,EAE9C,KAAK,WAAW;;;;;;;;AAmBlC,MAAM,+BAAe,IAAI,KAAgC;AAEzD,MAAM,gBAAgB,gBAAgD;CACpE,MAAM,YAAoC,EAAE;AAE5C,QAAO,OAAO;EACZ,MAAM;EACN,YAAY;EACZ,OAAO;EACR,CAAC;AAEF,QAAO;;AAGT,MAAa,eACX,YAC2B;CAC3B,MAAM,MAAM,SAAS,OAAO;CAG5B,MAAM,WAAW,GAAG,QAAQ,KAAK,CAAC,GAAG,IAAI,GAAG,SAAS,WAAW;CAEhE,MAAM,cAAc,aAAa,IAAI,SAAS;AAE9C,KAAI,YACF,KAAI,YAAY,gBAAgB,QAG9B;MAAI,CADgB,eAAe,KAAK,SAAS,QACjC,CAAE,QAAO,YAAY;OAErC,KAAI;EACF,MAAM,QAAQ,SAAS,YAAY,YAAY;AAC/C,MACE,MAAM,YAAY,YAAY,WAC9B,MAAM,SAAS,YAAY,KAE3B,QAAO,YAAY;SAEf;CAMZ,MAAM,cAAc,eAAe,KAAK,SAAS,QAAQ;AAEzD,KAAI,CAAC,aAAa;AAChB,eAAa,IAAI,UAAU;GACzB,aAAa;GACb,SAAS;GACT,MAAM;GACN,WAAW,EAAE;GACd,CAAC;AACF,SAAO,EAAE;;CAGX,MAAM,YAAY,aAAa,YAAY;AAE3C,KAAI;EACF,MAAM,QAAQ,SAAS,YAAY;AACnC,eAAa,IAAI,UAAU;GACzB;GACA,SAAS,MAAM;GACf,MAAM,MAAM;GACZ;GACD,CAAC;SACI;AAIR,QAAO"}
@@ -1,7 +1,7 @@
1
1
  import { getSandBoxContext, parseFileContent } from "./parseFileContent.mjs";
2
2
  import { bundleFile, bundleFileSync, getLoader } from "./bundleFile.mjs";
3
- import { transpileTSToCJS, transpileTSToCJSSync } from "./transpileTSToCJS.mjs";
3
+ import { clearTranspileCache, transpileTSToCJS, transpileTSToCJSSync } from "./transpileTSToCJS.mjs";
4
4
  import { loadExternalFile, loadExternalFileSync } from "./loadExternalFile.mjs";
5
5
  import { bundleJSFile } from "./bundleJSFile.mjs";
6
6
 
7
- export { bundleFile, bundleFileSync, bundleJSFile, getLoader, getSandBoxContext, loadExternalFile, loadExternalFileSync, parseFileContent, transpileTSToCJS, transpileTSToCJSSync };
7
+ export { bundleFile, bundleFileSync, bundleJSFile, clearTranspileCache, getLoader, getSandBoxContext, loadExternalFile, loadExternalFileSync, parseFileContent, transpileTSToCJS, transpileTSToCJSSync };
@@ -22,13 +22,13 @@ const loadExternalFileSync = (filePath, options) => {
22
22
  logger("File could not be loaded.", { level: "error" });
23
23
  return;
24
24
  }
25
- const fileContent = parseFileContent(moduleResultString, {
25
+ const fileContent = withPreloadedGlobals(options?.preloadGlobals, () => parseFileContent(moduleResultString, {
26
26
  projectRequire: options?.projectRequire,
27
27
  envVarOptions: options?.envVarOptions,
28
28
  additionalEnvVars: options?.additionalEnvVars,
29
29
  mocks: options?.mocks,
30
30
  aliases: options?.aliases
31
- });
31
+ }));
32
32
  if (typeof fileContent === "undefined") {
33
33
  logger(`File could not be loaded. Path : ${filePath}`);
34
34
  return;
@@ -41,15 +41,17 @@ const loadExternalFileSync = (filePath, options) => {
41
41
  const withPreloadedGlobals = (globals, fn) => {
42
42
  if (!globals) return fn();
43
43
  const globalVars = globalThis;
44
- const prev = {};
44
+ const previousValues = {};
45
+ const previouslyPresent = {};
45
46
  for (const [key, value] of Object.entries(globals)) {
46
- prev[key] = globalVars[key];
47
+ previouslyPresent[key] = key in globalVars;
48
+ previousValues[key] = globalVars[key];
47
49
  globalVars[key] = value;
48
50
  }
49
51
  try {
50
52
  return fn();
51
53
  } finally {
52
- for (const key of Object.keys(globals)) if (prev[key] !== void 0) globalVars[key] = prev[key];
54
+ for (const key of Object.keys(globals)) if (previouslyPresent[key]) globalVars[key] = previousValues[key];
53
55
  else delete globalVars[key];
54
56
  }
55
57
  };
@@ -1 +1 @@
1
- {"version":3,"file":"loadExternalFile.mjs","names":[],"sources":["../../../src/loadExternalFile/loadExternalFile.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { extname } from 'node:path';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport JSON5 from 'json5';\nimport { colorizePath, logger } from '../logger';\nimport {\n parseFileContent,\n type SandBoxContextOptions,\n} from './parseFileContent';\nimport {\n type TranspileOptions,\n transpileTSToCJS,\n transpileTSToCJSSync,\n} from './transpileTSToCJS';\n\n// CJS MJS cross usage\nconst parseJSON5 = JSON5.parse || (JSON5 as any).default?.parse;\n\nexport type LoadExternalFileOptions = {\n configuration?: IntlayerConfig;\n buildOptions?: TranspileOptions;\n logError?: boolean;\n /**\n * Key-value pairs to temporarily set on the main Node.js `globalThis` for the\n * synchronous duration of `parseFileContent` / `runInNewContext`. External modules\n * loaded via `require()` inside the VM (e.g. `@intlayer/core`'s `file()` helper)\n * run in the main context and read from the real `globalThis`, not the VM sandbox.\n * Values are restored (or deleted) after `runInNewContext` returns.\n */\n preloadGlobals?: Record<string, unknown>;\n} & SandBoxContextOptions;\n\n/**\n * Load the content declaration from the given path\n *\n * Accepts JSON, JS, MJS and TS files as configuration\n */\nexport const loadExternalFileSync = (\n filePath: string,\n options?: LoadExternalFileOptions\n): any | undefined => {\n const fileExtension = extname(filePath) || '.json';\n\n try {\n if (\n fileExtension === '.json' ||\n fileExtension === '.json5' ||\n fileExtension === '.jsonc'\n ) {\n // Assume JSON\n return parseJSON5(readFileSync(filePath, 'utf-8'));\n }\n\n // Rest is JS, MJS or TS\n const code = readFileSync(filePath, 'utf-8');\n\n const moduleResultString: string | undefined = transpileTSToCJSSync(\n code,\n filePath,\n options?.buildOptions\n );\n\n if (!moduleResultString) {\n logger('File could not be loaded.', { level: 'error' });\n return undefined;\n }\n\n const fileContent = parseFileContent(moduleResultString, {\n projectRequire: options?.projectRequire,\n envVarOptions: options?.envVarOptions,\n additionalEnvVars: options?.additionalEnvVars,\n mocks: options?.mocks,\n aliases: options?.aliases,\n });\n\n if (typeof fileContent === 'undefined') {\n logger(`File could not be loaded. Path : ${filePath}`);\n return undefined;\n }\n\n return fileContent;\n } catch (error) {\n logger(\n [\n `Error: ${(error as Error).message} - `,\n JSON.stringify((error as Error).stack, null, 2),\n ],\n {\n level: 'error',\n }\n );\n }\n};\n\nconst withPreloadedGlobals = <T>(\n globals: Record<string, unknown> | undefined,\n fn: () => T\n): T => {\n if (!globals) return fn();\n\n const globalVars = globalThis as Record<string, unknown>;\n const prev: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(globals)) {\n prev[key] = globalVars[key];\n globalVars[key] = value;\n }\n\n try {\n return fn();\n } finally {\n for (const key of Object.keys(globals)) {\n if (prev[key] !== undefined) {\n globalVars[key] = prev[key];\n } else {\n delete globalVars[key];\n }\n }\n }\n};\n\n/**\n * Load the content declaration from the given path\n *\n * Accepts JSON, JS, MJS and TS files as configuration\n */\nexport const loadExternalFile = async (\n filePath: string,\n options?: LoadExternalFileOptions\n): Promise<any | undefined> => {\n const fileExtension = extname(filePath);\n\n try {\n if (\n fileExtension === '.json' ||\n fileExtension === '.json5' ||\n fileExtension === '.jsonc'\n ) {\n // Remove cache to force getting fresh content\n const fileContent = await readFile(filePath, 'utf-8');\n return parseJSON5(fileContent);\n }\n\n // Rest is JS, MJS or TS\n const code = await readFile(filePath, 'utf-8');\n\n const moduleResultString: string | undefined = await transpileTSToCJS(\n code,\n filePath,\n options?.buildOptions\n );\n\n if (!moduleResultString) {\n logger('File could not be loaded.', { level: 'error' });\n return undefined;\n }\n\n // parseFileContent/runInNewContext is synchronous, so withPreloadedGlobals\n // has no interleaving risk even when multiple files are processed concurrently.\n const fileContent = withPreloadedGlobals(options?.preloadGlobals, () =>\n parseFileContent(moduleResultString, {\n projectRequire: options?.projectRequire,\n envVarOptions: options?.envVarOptions,\n additionalEnvVars: options?.additionalEnvVars,\n mocks: options?.mocks,\n aliases: options?.aliases,\n })\n );\n\n if (typeof fileContent === 'undefined') {\n logger(`File could not be loaded. Path : ${colorizePath(filePath)}`);\n return undefined;\n }\n\n return fileContent;\n } catch (error) {\n if (options?.logError ?? true) {\n logger(\n [\n `Error: ${(error as Error).message} - `,\n JSON.stringify((error as Error).stack, null, 2),\n ],\n {\n level: 'error',\n }\n );\n }\n }\n};\n"],"mappings":";;;;;;;;;AAiBA,MAAM,aAAa,MAAM,SAAU,MAAc,SAAS;;;;;;AAqB1D,MAAa,wBACX,UACA,YACoB;CACpB,MAAM,gBAAgB,QAAQ,SAAS,IAAI;AAE3C,KAAI;AACF,MACE,kBAAkB,WAClB,kBAAkB,YAClB,kBAAkB,SAGlB,QAAO,WAAW,aAAa,UAAU,QAAQ,CAAC;EAMpD,MAAM,qBAAyC,qBAFlC,aAAa,UAAU,QAG9B,EACJ,UACA,SAAS,aACV;AAED,MAAI,CAAC,oBAAoB;AACvB,UAAO,6BAA6B,EAAE,OAAO,SAAS,CAAC;AACvD;;EAGF,MAAM,cAAc,iBAAiB,oBAAoB;GACvD,gBAAgB,SAAS;GACzB,eAAe,SAAS;GACxB,mBAAmB,SAAS;GAC5B,OAAO,SAAS;GAChB,SAAS,SAAS;GACnB,CAAC;AAEF,MAAI,OAAO,gBAAgB,aAAa;AACtC,UAAO,oCAAoC,WAAW;AACtD;;AAGF,SAAO;UACA,OAAO;AACd,SACE,CACE,UAAW,MAAgB,QAAQ,MACnC,KAAK,UAAW,MAAgB,OAAO,MAAM,EAAE,CAChD,EACD,EACE,OAAO,SACR,CACF;;;AAIL,MAAM,wBACJ,SACA,OACM;AACN,KAAI,CAAC,QAAS,QAAO,IAAI;CAEzB,MAAM,aAAa;CACnB,MAAM,OAAgC,EAAE;AAExC,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;AAClD,OAAK,OAAO,WAAW;AACvB,aAAW,OAAO;;AAGpB,KAAI;AACF,SAAO,IAAI;WACH;AACR,OAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CACpC,KAAI,KAAK,SAAS,OAChB,YAAW,OAAO,KAAK;MAEvB,QAAO,WAAW;;;;;;;;AAW1B,MAAa,mBAAmB,OAC9B,UACA,YAC6B;CAC7B,MAAM,gBAAgB,QAAQ,SAAS;AAEvC,KAAI;AACF,MACE,kBAAkB,WAClB,kBAAkB,YAClB,kBAAkB,SAIlB,QAAO,WAAW,MADQ,SAAS,UAAU,QAAQ,CACvB;EAMhC,MAAM,qBAAyC,MAAM,iBACnD,MAHiB,SAAS,UAAU,QAAQ,EAI5C,UACA,SAAS,aACV;AAED,MAAI,CAAC,oBAAoB;AACvB,UAAO,6BAA6B,EAAE,OAAO,SAAS,CAAC;AACvD;;EAKF,MAAM,cAAc,qBAAqB,SAAS,sBAChD,iBAAiB,oBAAoB;GACnC,gBAAgB,SAAS;GACzB,eAAe,SAAS;GACxB,mBAAmB,SAAS;GAC5B,OAAO,SAAS;GAChB,SAAS,SAAS;GACnB,CAAC,CACH;AAED,MAAI,OAAO,gBAAgB,aAAa;AACtC,UAAO,oCAAoC,aAAa,SAAS,GAAG;AACpE;;AAGF,SAAO;UACA,OAAO;AACd,MAAI,SAAS,YAAY,KACvB,QACE,CACE,UAAW,MAAgB,QAAQ,MACnC,KAAK,UAAW,MAAgB,OAAO,MAAM,EAAE,CAChD,EACD,EACE,OAAO,SACR,CACF"}
1
+ {"version":3,"file":"loadExternalFile.mjs","names":[],"sources":["../../../src/loadExternalFile/loadExternalFile.ts"],"sourcesContent":["import { readFileSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\nimport { extname } from 'node:path';\nimport type { IntlayerConfig } from '@intlayer/types/config';\nimport JSON5 from 'json5';\nimport { colorizePath, logger } from '../logger';\nimport {\n parseFileContent,\n type SandBoxContextOptions,\n} from './parseFileContent';\nimport {\n type TranspileOptions,\n transpileTSToCJS,\n transpileTSToCJSSync,\n} from './transpileTSToCJS';\n\n// CJS MJS cross usage\nconst parseJSON5 = JSON5.parse || (JSON5 as any).default?.parse;\n\nexport type LoadExternalFileOptions = {\n configuration?: IntlayerConfig;\n buildOptions?: TranspileOptions;\n logError?: boolean;\n /**\n * Key-value pairs to temporarily set on the main Node.js `globalThis` for the\n * synchronous duration of `parseFileContent` / `runInNewContext`. External modules\n * loaded via `require()` inside the VM (e.g. `@intlayer/core`'s `file()` helper)\n * run in the main context and read from the real `globalThis`, not the VM sandbox.\n * Values are restored (or deleted) after `runInNewContext` returns.\n */\n preloadGlobals?: Record<string, unknown>;\n} & SandBoxContextOptions;\n\n/**\n * Load the content declaration from the given path\n *\n * Accepts JSON, JS, MJS and TS files as configuration\n */\nexport const loadExternalFileSync = (\n filePath: string,\n options?: LoadExternalFileOptions\n): any | undefined => {\n const fileExtension = extname(filePath) || '.json';\n\n try {\n if (\n fileExtension === '.json' ||\n fileExtension === '.json5' ||\n fileExtension === '.jsonc'\n ) {\n // Assume JSON\n return parseJSON5(readFileSync(filePath, 'utf-8'));\n }\n\n // Rest is JS, MJS or TS\n const code = readFileSync(filePath, 'utf-8');\n\n const moduleResultString: string | undefined = transpileTSToCJSSync(\n code,\n filePath,\n options?.buildOptions\n );\n\n if (!moduleResultString) {\n logger('File could not be loaded.', { level: 'error' });\n return undefined;\n }\n\n // parseFileContent/runInNewContext is synchronous, so withPreloadedGlobals\n // fully restores the globals before returning.\n const fileContent = withPreloadedGlobals(options?.preloadGlobals, () =>\n parseFileContent(moduleResultString, {\n projectRequire: options?.projectRequire,\n envVarOptions: options?.envVarOptions,\n additionalEnvVars: options?.additionalEnvVars,\n mocks: options?.mocks,\n aliases: options?.aliases,\n })\n );\n\n if (typeof fileContent === 'undefined') {\n logger(`File could not be loaded. Path : ${filePath}`);\n return undefined;\n }\n\n return fileContent;\n } catch (error) {\n logger(\n [\n `Error: ${(error as Error).message} - `,\n JSON.stringify((error as Error).stack, null, 2),\n ],\n {\n level: 'error',\n }\n );\n }\n};\n\nconst withPreloadedGlobals = <T>(\n globals: Record<string, unknown> | undefined,\n fn: () => T\n): T => {\n if (!globals) return fn();\n\n const globalVars = globalThis as Record<string, unknown>;\n const previousValues: Record<string, unknown> = {};\n const previouslyPresent: Record<string, boolean> = {};\n\n for (const [key, value] of Object.entries(globals)) {\n previouslyPresent[key] = key in globalVars;\n previousValues[key] = globalVars[key];\n globalVars[key] = value;\n }\n\n try {\n return fn();\n } finally {\n for (const key of Object.keys(globals)) {\n // `in` check (not `!== undefined`) so a global that legitimately held\n // `undefined` is restored rather than deleted.\n if (previouslyPresent[key]) {\n globalVars[key] = previousValues[key];\n } else {\n delete globalVars[key];\n }\n }\n }\n};\n\n/**\n * Load the content declaration from the given path\n *\n * Accepts JSON, JS, MJS and TS files as configuration\n */\nexport const loadExternalFile = async (\n filePath: string,\n options?: LoadExternalFileOptions\n): Promise<any | undefined> => {\n const fileExtension = extname(filePath);\n\n try {\n if (\n fileExtension === '.json' ||\n fileExtension === '.json5' ||\n fileExtension === '.jsonc'\n ) {\n // Remove cache to force getting fresh content\n const fileContent = await readFile(filePath, 'utf-8');\n return parseJSON5(fileContent);\n }\n\n // Rest is JS, MJS or TS\n const code = await readFile(filePath, 'utf-8');\n\n const moduleResultString: string | undefined = await transpileTSToCJS(\n code,\n filePath,\n options?.buildOptions\n );\n\n if (!moduleResultString) {\n logger('File could not be loaded.', { level: 'error' });\n return undefined;\n }\n\n // parseFileContent/runInNewContext is synchronous, so withPreloadedGlobals\n // has no interleaving risk even when multiple files are processed concurrently.\n const fileContent = withPreloadedGlobals(options?.preloadGlobals, () =>\n parseFileContent(moduleResultString, {\n projectRequire: options?.projectRequire,\n envVarOptions: options?.envVarOptions,\n additionalEnvVars: options?.additionalEnvVars,\n mocks: options?.mocks,\n aliases: options?.aliases,\n })\n );\n\n if (typeof fileContent === 'undefined') {\n logger(`File could not be loaded. Path : ${colorizePath(filePath)}`);\n return undefined;\n }\n\n return fileContent;\n } catch (error) {\n if (options?.logError ?? true) {\n logger(\n [\n `Error: ${(error as Error).message} - `,\n JSON.stringify((error as Error).stack, null, 2),\n ],\n {\n level: 'error',\n }\n );\n }\n }\n};\n"],"mappings":";;;;;;;;;AAiBA,MAAM,aAAa,MAAM,SAAU,MAAc,SAAS;;;;;;AAqB1D,MAAa,wBACX,UACA,YACoB;CACpB,MAAM,gBAAgB,QAAQ,SAAS,IAAI;AAE3C,KAAI;AACF,MACE,kBAAkB,WAClB,kBAAkB,YAClB,kBAAkB,SAGlB,QAAO,WAAW,aAAa,UAAU,QAAQ,CAAC;EAMpD,MAAM,qBAAyC,qBAFlC,aAAa,UAAU,QAG9B,EACJ,UACA,SAAS,aACV;AAED,MAAI,CAAC,oBAAoB;AACvB,UAAO,6BAA6B,EAAE,OAAO,SAAS,CAAC;AACvD;;EAKF,MAAM,cAAc,qBAAqB,SAAS,sBAChD,iBAAiB,oBAAoB;GACnC,gBAAgB,SAAS;GACzB,eAAe,SAAS;GACxB,mBAAmB,SAAS;GAC5B,OAAO,SAAS;GAChB,SAAS,SAAS;GACnB,CAAC,CACH;AAED,MAAI,OAAO,gBAAgB,aAAa;AACtC,UAAO,oCAAoC,WAAW;AACtD;;AAGF,SAAO;UACA,OAAO;AACd,SACE,CACE,UAAW,MAAgB,QAAQ,MACnC,KAAK,UAAW,MAAgB,OAAO,MAAM,EAAE,CAChD,EACD,EACE,OAAO,SACR,CACF;;;AAIL,MAAM,wBACJ,SACA,OACM;AACN,KAAI,CAAC,QAAS,QAAO,IAAI;CAEzB,MAAM,aAAa;CACnB,MAAM,iBAA0C,EAAE;CAClD,MAAM,oBAA6C,EAAE;AAErD,MAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,QAAQ,EAAE;AAClD,oBAAkB,OAAO,OAAO;AAChC,iBAAe,OAAO,WAAW;AACjC,aAAW,OAAO;;AAGpB,KAAI;AACF,SAAO,IAAI;WACH;AACR,OAAK,MAAM,OAAO,OAAO,KAAK,QAAQ,CAGpC,KAAI,kBAAkB,KACpB,YAAW,OAAO,eAAe;MAEjC,QAAO,WAAW;;;;;;;;AAW1B,MAAa,mBAAmB,OAC9B,UACA,YAC6B;CAC7B,MAAM,gBAAgB,QAAQ,SAAS;AAEvC,KAAI;AACF,MACE,kBAAkB,WAClB,kBAAkB,YAClB,kBAAkB,SAIlB,QAAO,WAAW,MADQ,SAAS,UAAU,QAAQ,CACvB;EAMhC,MAAM,qBAAyC,MAAM,iBACnD,MAHiB,SAAS,UAAU,QAAQ,EAI5C,UACA,SAAS,aACV;AAED,MAAI,CAAC,oBAAoB;AACvB,UAAO,6BAA6B,EAAE,OAAO,SAAS,CAAC;AACvD;;EAKF,MAAM,cAAc,qBAAqB,SAAS,sBAChD,iBAAiB,oBAAoB;GACnC,gBAAgB,SAAS;GACzB,eAAe,SAAS;GACxB,mBAAmB,SAAS;GAC5B,OAAO,SAAS;GAChB,SAAS,SAAS;GACnB,CAAC,CACH;AAED,MAAI,OAAO,gBAAgB,aAAa;AACtC,UAAO,oCAAoC,aAAa,SAAS,GAAG;AACpE;;AAGF,SAAO;UACA,OAAO;AACd,MAAI,SAAS,YAAY,KACvB,QACE,CACE,UAAW,MAAgB,QAAQ,MACnC,KAAK,UAAW,MAAgB,OAAO,MAAM,EAAE,CAChD,EACD,EACE,OAAO,SACR,CACF"}
@@ -24,9 +24,58 @@ const NODE_GLOBALS = [
24
24
  "crypto",
25
25
  "structuredClone"
26
26
  ];
27
+ /**
28
+ * Cache of the project's React module (or the JSX-capturing fallback) keyed by
29
+ * the require's resolution paths. Node does not cache *failed* resolutions, so
30
+ * without this every sandbox creation in a non-React project (Vue, Svelte,
31
+ * Angular…) pays a full node_modules walk that ends in a throw.
32
+ */
33
+ const reactModuleCache = /* @__PURE__ */ new Map();
34
+ /**
35
+ * Fallback React implementation used when React is not installed in the
36
+ * project. It captures JSX elements as plain objects so JSX can be used in
37
+ * content declarations (esbuild's tsx loader defaults to React.createElement).
38
+ */
39
+ const createFallbackReact = () => ({
40
+ createElement: (type, props, ...children) => ({
41
+ type,
42
+ props: {
43
+ ...props,
44
+ children: children.length <= 1 ? children[0] : children
45
+ }
46
+ }),
47
+ Fragment: Symbol.for("react.fragment")
48
+ });
49
+ const getReactGlobal = (baseRequire) => {
50
+ const resolutionKey = baseRequire.resolve.paths?.("react")?.join("|") ?? "<default>";
51
+ const cachedReactModule = reactModuleCache.get(resolutionKey);
52
+ if (cachedReactModule) return cachedReactModule;
53
+ let reactGlobal;
54
+ try {
55
+ reactGlobal = { React: baseRequire("react") };
56
+ } catch (_err) {
57
+ reactGlobal = { React: createFallbackReact() };
58
+ }
59
+ reactModuleCache.set(resolutionKey, reactGlobal);
60
+ return reactGlobal;
61
+ };
62
+ /**
63
+ * Snapshot of the non-env `process` properties, taken once at module load.
64
+ * Spreading `process` enumerates ~100 properties (some behind getters); doing
65
+ * it per sandbox is wasteful since everything except `env` is static.
66
+ */
67
+ const processSnapshot = { ...process };
68
+ /**
69
+ * Builds the global context handed to `runInNewContext`.
70
+ *
71
+ * SECURITY NOTE: this sandbox exists for global-scope hygiene (fresh
72
+ * module/exports, controlled env), NOT for containment. It exposes the real
73
+ * project `require`, a copy of `process`, and `fetch` — code executed here has
74
+ * full host privileges. Only ever run trusted, build-time project files
75
+ * (same trust model as `vite.config.ts`).
76
+ */
27
77
  const getSandBoxContext = (options) => {
28
78
  const { envVarOptions, projectRequire, additionalEnvVars, mocks, aliases } = options ?? {};
29
- let additionalGlobalVar = {};
30
79
  const baseRequire = typeof projectRequire === "function" ? projectRequire : getProjectRequire();
31
80
  const mockedRequire = (() => {
32
81
  const mockTable = Object.assign({ esbuild }, mocks);
@@ -43,25 +92,11 @@ const getSandBoxContext = (options) => {
43
92
  wrappedRequire.cache = baseRequire.cache;
44
93
  return wrappedRequire;
45
94
  })();
46
- try {
47
- additionalGlobalVar = { React: baseRequire("react") };
48
- } catch (_err) {
49
- additionalGlobalVar = { React: {
50
- createElement: (type, props, ...children) => ({
51
- type,
52
- props: {
53
- ...props,
54
- children: children.length <= 1 ? children[0] : children
55
- }
56
- }),
57
- Fragment: Symbol.for("react.fragment")
58
- } };
59
- }
60
95
  const sandboxContext = {
61
96
  exports: { default: {} },
62
97
  module: { exports: {} },
63
98
  process: {
64
- ...process,
99
+ ...processSnapshot,
65
100
  env: {
66
101
  ...process.env,
67
102
  ...loadEnvFile(envVarOptions),
@@ -70,7 +105,7 @@ const getSandBoxContext = (options) => {
70
105
  },
71
106
  console,
72
107
  require: mockedRequire,
73
- ...additionalGlobalVar
108
+ ...getReactGlobal(baseRequire)
74
109
  };
75
110
  for (const key of NODE_GLOBALS) if (!(key in sandboxContext) && key in globalThis) sandboxContext[key] = globalThis[key];
76
111
  return sandboxContext;
@@ -1 +1 @@
1
- {"version":3,"file":"parseFileContent.mjs","names":[],"sources":["../../../src/loadExternalFile/parseFileContent.ts"],"sourcesContent":["import { type Context, runInNewContext } from 'node:vm';\nimport * as esbuild from 'esbuild';\nimport { type LoadEnvFileOptions, loadEnvFile } from '../loadEnvFile';\nimport { getProjectRequire } from '../utils/ESMxCJSHelpers';\n\nexport type SandBoxContextOptions = {\n envVarOptions?: LoadEnvFileOptions;\n projectRequire?: NodeJS.Require;\n additionalEnvVars?: Record<string, string>;\n /**\n * Map of specifier -> mocked export to be returned when code in the VM calls require(specifier).\n * Example:\n * mocks: {\n * '@intlayer/config/built': { getConfig: () => ({}), Locales: {} }\n * }\n */\n mocks?: Record<string, any>;\n /**\n * Optional alias map if you want to redirect specifiers.\n * Useful when user code imports a subpath you want to collapse.\n * Example:\n * aliases: { '@intlayer/config/built': '@intlayer/config' }\n */\n aliases?: Record<string, string>;\n};\n\n// Inject only Node.js-specific globals that are absent from a plain V8 context.\n// JS built-ins (Object, Array, Promise, Math, Date, JSON, Symbol, etc.) are\n// provided automatically by runInNewContext — no need to copy them.\n// Copying all of globalThis would retain hundreds of references (including the\n// full module cache via `global`) inside every sandbox, causing a memory leak.\nconst NODE_GLOBALS = [\n 'Buffer',\n 'setTimeout',\n 'clearTimeout',\n 'setInterval',\n 'clearInterval',\n 'setImmediate',\n 'clearImmediate',\n 'queueMicrotask',\n 'URL',\n 'URLSearchParams',\n 'TextEncoder',\n 'TextDecoder',\n 'AbortController',\n 'AbortSignal',\n 'performance',\n 'fetch',\n 'crypto',\n 'structuredClone',\n] as const;\n\nexport const getSandBoxContext = (options?: SandBoxContextOptions): Context => {\n const { envVarOptions, projectRequire, additionalEnvVars, mocks, aliases } =\n options ?? {};\n\n let additionalGlobalVar = {};\n\n const baseRequire: NodeJS.Require =\n typeof projectRequire === 'function' ? projectRequire : getProjectRequire();\n\n // Wrap require to honor mocks and aliases inside the VM\n const mockedRequire: NodeJS.Require = (() => {\n const mockTable = Object.assign(\n {\n esbuild,\n },\n mocks\n );\n const aliasTable = Object.assign({}, aliases);\n\n const wrappedRequire = function mockableRequire(id: string) {\n const target = aliasTable?.[id] ? aliasTable[id] : id;\n\n if (mockTable && Object.hasOwn(mockTable, target)) {\n return mockTable[target];\n }\n\n // If the original id was aliased, allow mocks to be defined on either key.\n if (target !== id && mockTable && Object.hasOwn(mockTable, id)) {\n return mockTable[id];\n }\n\n return baseRequire(target);\n } as NodeJS.Require;\n\n // Mirror NodeJS.Require properties\n wrappedRequire.resolve = baseRequire.resolve.bind(baseRequire);\n wrappedRequire.main = baseRequire.main;\n wrappedRequire.extensions = baseRequire.extensions;\n wrappedRequire.cache = baseRequire.cache;\n\n return wrappedRequire;\n })();\n\n try {\n // Dynamically try to require React if it's installed in the project\n additionalGlobalVar = {\n React: baseRequire('react'),\n };\n } catch (_err) {\n // React is not installed, so we inject a dummy React object to capture JSX elements\n // This allows using JSX in content declarations even if React is not installed (e.g. in Solid.js or Vue projects)\n // because esbuild's tsx loader defaults to React.createElement.\n additionalGlobalVar = {\n React: {\n createElement: (type: any, props: any, ...children: any[]) => ({\n type,\n props: {\n ...props,\n children: children.length <= 1 ? children[0] : children,\n },\n }),\n Fragment: Symbol.for('react.fragment'),\n },\n };\n }\n\n const sandboxContext: Context = {\n exports: {\n default: {},\n },\n module: {\n exports: {},\n },\n process: {\n ...process,\n env: {\n ...process.env,\n ...loadEnvFile(envVarOptions),\n ...additionalEnvVars,\n },\n },\n console,\n require: mockedRequire,\n ...additionalGlobalVar,\n };\n\n for (const key of NODE_GLOBALS) {\n if (!(key in sandboxContext) && key in globalThis) {\n (sandboxContext as Record<string, unknown>)[key] =\n globalThis[key as keyof typeof globalThis];\n }\n }\n\n return sandboxContext;\n};\n\nexport const parseFileContent = <T>(\n fileContentString: string,\n options?: SandBoxContextOptions\n): T | undefined => {\n const sandboxContext = getSandBoxContext(options);\n\n // Force strict mode so illegal writes throw instead of silently failing.\n runInNewContext(`\"use strict\";\\n${fileContentString}`, sandboxContext);\n\n const candidates: unknown[] = [\n sandboxContext.exports?.default,\n sandboxContext.module?.exports?.defaults,\n sandboxContext.module?.exports?.default,\n sandboxContext.module?.exports,\n ];\n\n let result: T | undefined;\n for (const candidate of candidates) {\n if (\n candidate &&\n typeof candidate === 'object' &&\n Object.keys(candidate as object).length > 0\n ) {\n result = candidate as T;\n break;\n }\n }\n\n // Drop heavy references so the V8 context created by runInNewContext can be\n // garbage-collected promptly. The extracted `result` is a plain data object\n // and does not retain the sandbox.\n (sandboxContext as Record<string, unknown>).require = undefined;\n (sandboxContext as Record<string, unknown>).process = undefined;\n (sandboxContext as Record<string, unknown>).React = undefined;\n (sandboxContext as Record<string, unknown>).module = undefined;\n (sandboxContext as Record<string, unknown>).exports = undefined;\n\n return result;\n};\n"],"mappings":";;;;;;AA+BA,MAAM,eAAe;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAa,qBAAqB,YAA6C;CAC7E,MAAM,EAAE,eAAe,gBAAgB,mBAAmB,OAAO,YAC/D,WAAW,EAAE;CAEf,IAAI,sBAAsB,EAAE;CAE5B,MAAM,cACJ,OAAO,mBAAmB,aAAa,iBAAiB,mBAAmB;CAG7E,MAAM,uBAAuC;EAC3C,MAAM,YAAY,OAAO,OACvB,EACE,SACD,EACD,MACD;EACD,MAAM,aAAa,OAAO,OAAO,EAAE,EAAE,QAAQ;EAE7C,MAAM,iBAAiB,SAAS,gBAAgB,IAAY;GAC1D,MAAM,SAAS,aAAa,MAAM,WAAW,MAAM;AAEnD,OAAI,aAAa,OAAO,OAAO,WAAW,OAAO,CAC/C,QAAO,UAAU;AAInB,OAAI,WAAW,MAAM,aAAa,OAAO,OAAO,WAAW,GAAG,CAC5D,QAAO,UAAU;AAGnB,UAAO,YAAY,OAAO;;AAI5B,iBAAe,UAAU,YAAY,QAAQ,KAAK,YAAY;AAC9D,iBAAe,OAAO,YAAY;AAClC,iBAAe,aAAa,YAAY;AACxC,iBAAe,QAAQ,YAAY;AAEnC,SAAO;KACL;AAEJ,KAAI;AAEF,wBAAsB,EACpB,OAAO,YAAY,QAAQ,EAC5B;UACM,MAAM;AAIb,wBAAsB,EACpB,OAAO;GACL,gBAAgB,MAAW,OAAY,GAAG,cAAqB;IAC7D;IACA,OAAO;KACL,GAAG;KACH,UAAU,SAAS,UAAU,IAAI,SAAS,KAAK;KAChD;IACF;GACD,UAAU,OAAO,IAAI,iBAAiB;GACvC,EACF;;CAGH,MAAM,iBAA0B;EAC9B,SAAS,EACP,SAAS,EAAE,EACZ;EACD,QAAQ,EACN,SAAS,EAAE,EACZ;EACD,SAAS;GACP,GAAG;GACH,KAAK;IACH,GAAG,QAAQ;IACX,GAAG,YAAY,cAAc;IAC7B,GAAG;IACJ;GACF;EACD;EACA,SAAS;EACT,GAAG;EACJ;AAED,MAAK,MAAM,OAAO,aAChB,KAAI,EAAE,OAAO,mBAAmB,OAAO,WACrC,CAAC,eAA2C,OAC1C,WAAW;AAIjB,QAAO;;AAGT,MAAa,oBACX,mBACA,YACkB;CAClB,MAAM,iBAAiB,kBAAkB,QAAQ;AAGjD,iBAAgB,kBAAkB,qBAAqB,eAAe;CAEtE,MAAM,aAAwB;EAC5B,eAAe,SAAS;EACxB,eAAe,QAAQ,SAAS;EAChC,eAAe,QAAQ,SAAS;EAChC,eAAe,QAAQ;EACxB;CAED,IAAI;AACJ,MAAK,MAAM,aAAa,WACtB,KACE,aACA,OAAO,cAAc,YACrB,OAAO,KAAK,UAAoB,CAAC,SAAS,GAC1C;AACA,WAAS;AACT;;AAOJ,CAAC,eAA2C,UAAU;AACtD,CAAC,eAA2C,UAAU;AACtD,CAAC,eAA2C,QAAQ;AACpD,CAAC,eAA2C,SAAS;AACrD,CAAC,eAA2C,UAAU;AAEtD,QAAO"}
1
+ {"version":3,"file":"parseFileContent.mjs","names":[],"sources":["../../../src/loadExternalFile/parseFileContent.ts"],"sourcesContent":["import { type Context, runInNewContext } from 'node:vm';\nimport * as esbuild from 'esbuild';\nimport { type LoadEnvFileOptions, loadEnvFile } from '../loadEnvFile';\nimport { getProjectRequire } from '../utils/ESMxCJSHelpers';\n\nexport type SandBoxContextOptions = {\n envVarOptions?: LoadEnvFileOptions;\n projectRequire?: NodeJS.Require;\n additionalEnvVars?: Record<string, string>;\n /**\n * Map of specifier -> mocked export to be returned when code in the VM calls require(specifier).\n * Example:\n * mocks: {\n * '@intlayer/config/built': { getConfig: () => ({}), Locales: {} }\n * }\n */\n mocks?: Record<string, any>;\n /**\n * Optional alias map if you want to redirect specifiers.\n * Useful when user code imports a subpath you want to collapse.\n * Example:\n * aliases: { '@intlayer/config/built': '@intlayer/config' }\n */\n aliases?: Record<string, string>;\n};\n\n// Inject only Node.js-specific globals that are absent from a plain V8 context.\n// JS built-ins (Object, Array, Promise, Math, Date, JSON, Symbol, etc.) are\n// provided automatically by runInNewContext — no need to copy them.\n// Copying all of globalThis would retain hundreds of references (including the\n// full module cache via `global`) inside every sandbox, causing a memory leak.\nconst NODE_GLOBALS = [\n 'Buffer',\n 'setTimeout',\n 'clearTimeout',\n 'setInterval',\n 'clearInterval',\n 'setImmediate',\n 'clearImmediate',\n 'queueMicrotask',\n 'URL',\n 'URLSearchParams',\n 'TextEncoder',\n 'TextDecoder',\n 'AbortController',\n 'AbortSignal',\n 'performance',\n 'fetch',\n 'crypto',\n 'structuredClone',\n] as const;\n\n/**\n * Cache of the project's React module (or the JSX-capturing fallback) keyed by\n * the require's resolution paths. Node does not cache *failed* resolutions, so\n * without this every sandbox creation in a non-React project (Vue, Svelte,\n * Angular…) pays a full node_modules walk that ends in a throw.\n */\nconst reactModuleCache = new Map<string, { React: unknown }>();\n\n/**\n * Fallback React implementation used when React is not installed in the\n * project. It captures JSX elements as plain objects so JSX can be used in\n * content declarations (esbuild's tsx loader defaults to React.createElement).\n */\nconst createFallbackReact = () => ({\n createElement: (type: any, props: any, ...children: any[]) => ({\n type,\n props: {\n ...props,\n children: children.length <= 1 ? children[0] : children,\n },\n }),\n Fragment: Symbol.for('react.fragment'),\n});\n\nconst getReactGlobal = (baseRequire: NodeJS.Require): { React: unknown } => {\n // resolve.paths() is computed in-memory (no fs access) and identifies the\n // node_modules chain this require instance resolves against.\n const resolutionKey =\n baseRequire.resolve.paths?.('react')?.join('|') ?? '<default>';\n\n const cachedReactModule = reactModuleCache.get(resolutionKey);\n if (cachedReactModule) return cachedReactModule;\n\n let reactGlobal: { React: unknown };\n try {\n // Dynamically try to require React if it's installed in the project\n reactGlobal = { React: baseRequire('react') };\n } catch (_err) {\n reactGlobal = { React: createFallbackReact() };\n }\n\n reactModuleCache.set(resolutionKey, reactGlobal);\n\n return reactGlobal;\n};\n\n/**\n * Snapshot of the non-env `process` properties, taken once at module load.\n * Spreading `process` enumerates ~100 properties (some behind getters); doing\n * it per sandbox is wasteful since everything except `env` is static.\n */\nconst processSnapshot = { ...process };\n\n/**\n * Builds the global context handed to `runInNewContext`.\n *\n * SECURITY NOTE: this sandbox exists for global-scope hygiene (fresh\n * module/exports, controlled env), NOT for containment. It exposes the real\n * project `require`, a copy of `process`, and `fetch` — code executed here has\n * full host privileges. Only ever run trusted, build-time project files\n * (same trust model as `vite.config.ts`).\n */\nexport const getSandBoxContext = (options?: SandBoxContextOptions): Context => {\n const { envVarOptions, projectRequire, additionalEnvVars, mocks, aliases } =\n options ?? {};\n\n const baseRequire: NodeJS.Require =\n typeof projectRequire === 'function' ? projectRequire : getProjectRequire();\n\n // Wrap require to honor mocks and aliases inside the VM\n const mockedRequire: NodeJS.Require = (() => {\n const mockTable = Object.assign(\n {\n esbuild,\n },\n mocks\n );\n const aliasTable = Object.assign({}, aliases);\n\n const wrappedRequire = function mockableRequire(id: string) {\n const target = aliasTable?.[id] ? aliasTable[id] : id;\n\n if (mockTable && Object.hasOwn(mockTable, target)) {\n return mockTable[target];\n }\n\n // If the original id was aliased, allow mocks to be defined on either key.\n if (target !== id && mockTable && Object.hasOwn(mockTable, id)) {\n return mockTable[id];\n }\n\n return baseRequire(target);\n } as NodeJS.Require;\n\n // Mirror NodeJS.Require properties\n wrappedRequire.resolve = baseRequire.resolve.bind(baseRequire);\n wrappedRequire.main = baseRequire.main;\n wrappedRequire.extensions = baseRequire.extensions;\n wrappedRequire.cache = baseRequire.cache;\n\n return wrappedRequire;\n })();\n\n const sandboxContext: Context = {\n exports: {\n default: {},\n },\n module: {\n exports: {},\n },\n process: {\n ...processSnapshot,\n env: {\n ...process.env,\n ...loadEnvFile(envVarOptions),\n ...additionalEnvVars,\n },\n },\n console,\n require: mockedRequire,\n ...getReactGlobal(baseRequire),\n };\n\n for (const key of NODE_GLOBALS) {\n if (!(key in sandboxContext) && key in globalThis) {\n (sandboxContext as Record<string, unknown>)[key] =\n globalThis[key as keyof typeof globalThis];\n }\n }\n\n return sandboxContext;\n};\n\nexport const parseFileContent = <T>(\n fileContentString: string,\n options?: SandBoxContextOptions\n): T | undefined => {\n const sandboxContext = getSandBoxContext(options);\n\n // Force strict mode so illegal writes throw instead of silently failing.\n runInNewContext(`\"use strict\";\\n${fileContentString}`, sandboxContext);\n\n const candidates: unknown[] = [\n sandboxContext.exports?.default,\n sandboxContext.module?.exports?.defaults,\n sandboxContext.module?.exports?.default,\n sandboxContext.module?.exports,\n ];\n\n let result: T | undefined;\n for (const candidate of candidates) {\n if (\n candidate &&\n typeof candidate === 'object' &&\n Object.keys(candidate as object).length > 0\n ) {\n result = candidate as T;\n break;\n }\n }\n\n // Drop heavy references so the V8 context created by runInNewContext can be\n // garbage-collected promptly. The extracted `result` is a plain data object\n // and does not retain the sandbox.\n (sandboxContext as Record<string, unknown>).require = undefined;\n (sandboxContext as Record<string, unknown>).process = undefined;\n (sandboxContext as Record<string, unknown>).React = undefined;\n (sandboxContext as Record<string, unknown>).module = undefined;\n (sandboxContext as Record<string, unknown>).exports = undefined;\n\n return result;\n};\n"],"mappings":";;;;;;AA+BA,MAAM,eAAe;CACnB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;;;;AAQD,MAAM,mCAAmB,IAAI,KAAiC;;;;;;AAO9D,MAAM,6BAA6B;CACjC,gBAAgB,MAAW,OAAY,GAAG,cAAqB;EAC7D;EACA,OAAO;GACL,GAAG;GACH,UAAU,SAAS,UAAU,IAAI,SAAS,KAAK;GAChD;EACF;CACD,UAAU,OAAO,IAAI,iBAAiB;CACvC;AAED,MAAM,kBAAkB,gBAAoD;CAG1E,MAAM,gBACJ,YAAY,QAAQ,QAAQ,QAAQ,EAAE,KAAK,IAAI,IAAI;CAErD,MAAM,oBAAoB,iBAAiB,IAAI,cAAc;AAC7D,KAAI,kBAAmB,QAAO;CAE9B,IAAI;AACJ,KAAI;AAEF,gBAAc,EAAE,OAAO,YAAY,QAAQ,EAAE;UACtC,MAAM;AACb,gBAAc,EAAE,OAAO,qBAAqB,EAAE;;AAGhD,kBAAiB,IAAI,eAAe,YAAY;AAEhD,QAAO;;;;;;;AAQT,MAAM,kBAAkB,EAAE,GAAG,SAAS;;;;;;;;;;AAWtC,MAAa,qBAAqB,YAA6C;CAC7E,MAAM,EAAE,eAAe,gBAAgB,mBAAmB,OAAO,YAC/D,WAAW,EAAE;CAEf,MAAM,cACJ,OAAO,mBAAmB,aAAa,iBAAiB,mBAAmB;CAG7E,MAAM,uBAAuC;EAC3C,MAAM,YAAY,OAAO,OACvB,EACE,SACD,EACD,MACD;EACD,MAAM,aAAa,OAAO,OAAO,EAAE,EAAE,QAAQ;EAE7C,MAAM,iBAAiB,SAAS,gBAAgB,IAAY;GAC1D,MAAM,SAAS,aAAa,MAAM,WAAW,MAAM;AAEnD,OAAI,aAAa,OAAO,OAAO,WAAW,OAAO,CAC/C,QAAO,UAAU;AAInB,OAAI,WAAW,MAAM,aAAa,OAAO,OAAO,WAAW,GAAG,CAC5D,QAAO,UAAU;AAGnB,UAAO,YAAY,OAAO;;AAI5B,iBAAe,UAAU,YAAY,QAAQ,KAAK,YAAY;AAC9D,iBAAe,OAAO,YAAY;AAClC,iBAAe,aAAa,YAAY;AACxC,iBAAe,QAAQ,YAAY;AAEnC,SAAO;KACL;CAEJ,MAAM,iBAA0B;EAC9B,SAAS,EACP,SAAS,EAAE,EACZ;EACD,QAAQ,EACN,SAAS,EAAE,EACZ;EACD,SAAS;GACP,GAAG;GACH,KAAK;IACH,GAAG,QAAQ;IACX,GAAG,YAAY,cAAc;IAC7B,GAAG;IACJ;GACF;EACD;EACA,SAAS;EACT,GAAG,eAAe,YAAY;EAC/B;AAED,MAAK,MAAM,OAAO,aAChB,KAAI,EAAE,OAAO,mBAAmB,OAAO,WACrC,CAAC,eAA2C,OAC1C,WAAW;AAIjB,QAAO;;AAGT,MAAa,oBACX,mBACA,YACkB;CAClB,MAAM,iBAAiB,kBAAkB,QAAQ;AAGjD,iBAAgB,kBAAkB,qBAAqB,eAAe;CAEtE,MAAM,aAAwB;EAC5B,eAAe,SAAS;EACxB,eAAe,QAAQ,SAAS;EAChC,eAAe,QAAQ,SAAS;EAChC,eAAe,QAAQ;EACxB;CAED,IAAI;AACJ,MAAK,MAAM,aAAa,WACtB,KACE,aACA,OAAO,cAAc,YACrB,OAAO,KAAK,UAAoB,CAAC,SAAS,GAC1C;AACA,WAAS;AACT;;AAOJ,CAAC,eAA2C,UAAU;AACtD,CAAC,eAA2C,UAAU;AACtD,CAAC,eAA2C,QAAQ;AACpD,CAAC,eAA2C,SAAS;AACrD,CAAC,eAA2C,UAAU;AAEtD,QAAO"}
@@ -1,15 +1,30 @@
1
+ import { computeKeyId } from "../utils/cacheMemory.mjs";
1
2
  import { getPackageJsonPath } from "../utils/getPackageJsonPath.mjs";
2
3
  import { getLoader } from "./bundleFile.mjs";
3
- import { existsSync } from "node:fs";
4
- import { dirname, extname, join } from "node:path";
4
+ import { existsSync, statSync } from "node:fs";
5
+ import { dirname, extname, isAbsolute, join, resolve } from "node:path";
5
6
  import { createRequire } from "node:module";
6
- import { buildSync, context } from "esbuild";
7
+ import { build, buildSync } from "esbuild";
7
8
  import { pathToFileURL } from "node:url";
8
9
 
9
10
  //#region src/loadExternalFile/transpileTSToCJS.ts
11
+ /**
12
+ * Cache of resolved tsconfig paths keyed by the file's directory.
13
+ * Avoids re-walking to package.json + existsSync on every transpilation.
14
+ */
15
+ const tsConfigPathCache = /* @__PURE__ */ new Map();
10
16
  const getTsConfigPath = (filePath) => {
11
- const tsconfigPath = join(getPackageJsonPath(dirname(filePath)).baseDir, "tsconfig.json");
12
- return existsSync(tsconfigPath) ? tsconfigPath : void 0;
17
+ const fileDirectory = dirname(filePath);
18
+ if (tsConfigPathCache.has(fileDirectory)) return tsConfigPathCache.get(fileDirectory);
19
+ let resolvedPath;
20
+ try {
21
+ const tsconfigPath = join(getPackageJsonPath(fileDirectory).baseDir, "tsconfig.json");
22
+ resolvedPath = existsSync(tsconfigPath) ? tsconfigPath : void 0;
23
+ } catch {
24
+ resolvedPath = void 0;
25
+ }
26
+ tsConfigPathCache.set(fileDirectory, resolvedPath);
27
+ return resolvedPath;
13
28
  };
14
29
  const getTransformationOptions = (filePath) => ({
15
30
  loader: {
@@ -29,15 +44,85 @@ const getTransformationOptions = (filePath) => ({
29
44
  write: false,
30
45
  packages: "external",
31
46
  bundle: true,
47
+ metafile: true,
32
48
  tsconfig: getTsConfigPath(filePath),
33
49
  define: {
34
50
  "import.meta.url": JSON.stringify(pathToFileURL(filePath).href),
35
51
  "import.meta.env": "process.env"
36
52
  }
37
53
  });
54
+ /**
55
+ * In-memory cache of transpiled outputs keyed by entry file path (one entry
56
+ * per file, so re-transpilations replace rather than accumulate).
57
+ * The entry file is validated by content hash; bundled relative imports are
58
+ * validated by mtime + size so an edited dependency invalidates the cache.
59
+ */
60
+ const transpileCache = /* @__PURE__ */ new Map();
61
+ const isInputUnchanged = (input) => {
62
+ try {
63
+ const stats = statSync(input.path);
64
+ return stats.mtimeMs === input.mtimeMs && stats.size === input.size;
65
+ } catch {
66
+ return false;
67
+ }
68
+ };
69
+ const getCachedTranspilation = (filePath, codeHash, optionsKey) => {
70
+ const cacheEntry = transpileCache.get(filePath);
71
+ if (!cacheEntry) return void 0;
72
+ if (cacheEntry.codeHash !== codeHash) return void 0;
73
+ if (cacheEntry.optionsKey !== optionsKey) return void 0;
74
+ if (!cacheEntry.inputs.every(isInputUnchanged)) return void 0;
75
+ return cacheEntry.output;
76
+ };
77
+ /**
78
+ * Extracts the fingerprints of every bundled input from the esbuild metafile.
79
+ * Returns `undefined` when an input cannot be fingerprinted (the result should
80
+ * then not be cached, as invalidation would be unreliable).
81
+ */
82
+ const getInputFingerprints = (metafile, entryFilePath) => {
83
+ if (!metafile) return void 0;
84
+ const resolvedEntryFilePath = resolve(entryFilePath);
85
+ const inputs = [];
86
+ for (const inputPath of Object.keys(metafile.inputs)) {
87
+ if (inputPath === "<stdin>") continue;
88
+ if (inputPath.includes(":") && !isAbsolute(inputPath)) return void 0;
89
+ const absoluteInputPath = isAbsolute(inputPath) ? inputPath : resolve(process.cwd(), inputPath);
90
+ if (absoluteInputPath === resolvedEntryFilePath) continue;
91
+ try {
92
+ const stats = statSync(absoluteInputPath);
93
+ inputs.push({
94
+ path: absoluteInputPath,
95
+ mtimeMs: stats.mtimeMs,
96
+ size: stats.size
97
+ });
98
+ } catch {
99
+ return;
100
+ }
101
+ }
102
+ return inputs;
103
+ };
104
+ const setCachedTranspilation = (filePath, codeHash, optionsKey, moduleResult, output) => {
105
+ const inputs = getInputFingerprints(moduleResult.metafile, filePath);
106
+ if (!inputs) return;
107
+ transpileCache.set(filePath, {
108
+ codeHash,
109
+ optionsKey,
110
+ output,
111
+ inputs
112
+ });
113
+ };
114
+ /** Clears the in-memory transpilation cache (mainly for tests). */
115
+ const clearTranspileCache = () => {
116
+ transpileCache.clear();
117
+ tsConfigPathCache.clear();
118
+ };
38
119
  const transpileTSToCJSSync = (code, filePath, options) => {
39
120
  const loader = getLoader(extname(filePath));
40
121
  const { esbuildInstance, ...buildOptions } = options ?? {};
122
+ const codeHash = computeKeyId([code]);
123
+ const optionsKey = computeKeyId([buildOptions]);
124
+ const cachedOutput = getCachedTranspilation(filePath, codeHash, optionsKey);
125
+ if (typeof cachedOutput === "string") return cachedOutput;
41
126
  const esbuildBuildSync = esbuildInstance?.buildSync ?? buildSync;
42
127
  const g = globalThis;
43
128
  const hadFilename = typeof g.__filename === "string";
@@ -68,12 +153,18 @@ const transpileTSToCJSSync = (code, filePath, options) => {
68
153
  else g.__dirname = prevDirname;
69
154
  }
70
155
  }
71
- return moduleResult.outputFiles?.[0]?.text;
156
+ const moduleResultString = moduleResult.outputFiles?.[0]?.text;
157
+ if (typeof moduleResultString === "string") setCachedTranspilation(filePath, codeHash, optionsKey, moduleResult, moduleResultString);
158
+ return moduleResultString;
72
159
  };
73
160
  const transpileTSToCJS = async (code, filePath, options) => {
74
161
  const loader = getLoader(extname(filePath));
75
162
  const { esbuildInstance, ...buildOptions } = options ?? {};
76
- const ctx = await (esbuildInstance?.context ?? context)({
163
+ const codeHash = computeKeyId([code]);
164
+ const optionsKey = computeKeyId([buildOptions]);
165
+ const cachedOutput = getCachedTranspilation(filePath, codeHash, optionsKey);
166
+ if (typeof cachedOutput === "string") return cachedOutput;
167
+ const moduleResult = await (esbuildInstance?.build ?? build)({
77
168
  stdin: {
78
169
  contents: code,
79
170
  loader,
@@ -83,13 +174,11 @@ const transpileTSToCJS = async (code, filePath, options) => {
83
174
  ...getTransformationOptions(filePath),
84
175
  ...buildOptions
85
176
  });
86
- try {
87
- return (await ctx.rebuild()).outputFiles?.[0].text;
88
- } finally {
89
- await ctx.dispose();
90
- }
177
+ const moduleResultString = moduleResult.outputFiles?.[0]?.text;
178
+ if (typeof moduleResultString === "string") setCachedTranspilation(filePath, codeHash, optionsKey, moduleResult, moduleResultString);
179
+ return moduleResultString;
91
180
  };
92
181
 
93
182
  //#endregion
94
- export { transpileTSToCJS, transpileTSToCJSSync };
183
+ export { clearTranspileCache, transpileTSToCJS, transpileTSToCJSSync };
95
184
  //# sourceMappingURL=transpileTSToCJS.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"transpileTSToCJS.mjs","names":[],"sources":["../../../src/loadExternalFile/transpileTSToCJS.ts"],"sourcesContent":["import { existsSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, extname, join } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport {\n type BuildOptions,\n type BuildResult,\n buildSync,\n context,\n} from 'esbuild';\nimport { getPackageJsonPath } from '../utils/getPackageJsonPath';\nimport { getLoader } from './bundleFile';\n\nexport type TranspileOptions = BuildOptions & {\n /**\n * Optional custom esbuild instance to use for transpilation.\n * Useful in environments (e.g. VS Code extensions) where the bundled\n * esbuild binary may not match the host platform.\n * When provided, its `buildSync`/`build` methods are used instead of\n * the ones imported from the `esbuild` package.\n */\n esbuildInstance?: typeof import('esbuild');\n};\n\nconst getTsConfigPath = (filePath: string): string | undefined => {\n const tsconfigPath = join(\n getPackageJsonPath(dirname(filePath)).baseDir,\n 'tsconfig.json'\n );\n\n // Only return the tsconfig path if it exists\n return existsSync(tsconfigPath) ? tsconfigPath : undefined;\n};\n\nconst getTransformationOptions = (filePath: string): BuildOptions => ({\n loader: {\n '.js': 'js',\n '.jsx': 'jsx',\n '.mjs': 'js',\n '.ts': 'ts',\n '.tsx': 'tsx',\n '.cjs': 'js',\n '.json': 'json',\n '.md': 'text',\n '.mdx': 'text',\n },\n format: 'cjs',\n target: 'node20',\n platform: 'node',\n write: false,\n packages: 'external',\n bundle: true,\n tsconfig: getTsConfigPath(filePath),\n define: {\n 'import.meta.url': JSON.stringify(pathToFileURL(filePath).href),\n 'import.meta.env': 'process.env',\n },\n});\n\nexport const transpileTSToCJSSync = (\n code: string,\n filePath: string,\n options?: TranspileOptions\n): string | undefined => {\n const extension = extname(filePath);\n const loader = getLoader(extension);\n\n const { esbuildInstance, ...buildOptions } = options ?? {};\n const esbuildBuildSync = esbuildInstance?.buildSync ?? buildSync;\n\n // esbuild's worker thread service calls `new Worker(__filename, …)` on first use.\n // In Vite's SSR module runner the SSR-optimised chunk is ESM and __filename is\n // never declared (confirmed: accessing it throws ReferenceError, not undefined).\n // Because there is no local declaration the bare `__filename` lookup falls\n // through to globalThis, so we set it there to esbuild's own CJS entry-point –\n // the exact path esbuild would use if it were loaded in a normal CJS context.\n //\n // IMPORTANT: We save/restore the globals so this temporary shim does not leak\n // to other Vite plugins (e.g. `@vitejs/plugin-react-swc`) that check\n // `typeof __dirname !== \"undefined\"` to locate their own assets.\n const g = globalThis as Record<string, unknown>;\n const hadFilename = typeof g.__filename === 'string';\n const prevFilename = g.__filename;\n const prevDirname = g.__dirname;\n\n if (!hadFilename) {\n try {\n const _require = createRequire(import.meta.url);\n const esbuildEntry = _require.resolve('esbuild');\n g.__filename = esbuildEntry;\n g.__dirname = dirname(esbuildEntry);\n } catch {\n // Best-effort: if esbuild can't be resolved the caller's catch handles it\n }\n }\n\n let moduleResult: BuildResult;\n try {\n moduleResult = esbuildBuildSync({\n stdin: {\n contents: code,\n loader,\n resolveDir: dirname(filePath), // Add resolveDir to resolve imports relative to the file's location\n sourcefile: filePath, // Add sourcefile for better error messages\n },\n ...getTransformationOptions(filePath),\n ...buildOptions,\n });\n } finally {\n // Always restore the previous values so the globals don't linger.\n if (!hadFilename) {\n if (prevFilename === undefined) {\n delete g.__filename;\n } else {\n g.__filename = prevFilename;\n }\n if (prevDirname === undefined) {\n delete g.__dirname;\n } else {\n g.__dirname = prevDirname;\n }\n }\n }\n\n const moduleResultString = moduleResult!.outputFiles?.[0]?.text;\n\n return moduleResultString;\n};\n\nexport const transpileTSToCJS = async (\n code: string,\n filePath: string,\n options?: TranspileOptions\n): Promise<string | undefined> => {\n const extension = extname(filePath);\n const loader = getLoader(extension);\n\n const { esbuildInstance, ...buildOptions } = options ?? {};\n // Use context() + rebuild() + dispose() so esbuild deterministically releases\n // Go-subprocess resources for each one-shot transpilation, preventing them\n // from accumulating between rapid HMR-driven file changes.\n const esbuildContext = esbuildInstance?.context ?? context;\n\n const ctx = await esbuildContext({\n stdin: {\n contents: code,\n loader,\n resolveDir: dirname(filePath),\n sourcefile: filePath,\n },\n ...getTransformationOptions(filePath),\n ...buildOptions,\n });\n\n try {\n const moduleResult = await ctx.rebuild();\n return moduleResult.outputFiles?.[0].text;\n } finally {\n await ctx.dispose();\n }\n};\n"],"mappings":";;;;;;;;;AAwBA,MAAM,mBAAmB,aAAyC;CAChE,MAAM,eAAe,KACnB,mBAAmB,QAAQ,SAAS,CAAC,CAAC,SACtC,gBACD;AAGD,QAAO,WAAW,aAAa,GAAG,eAAe;;AAGnD,MAAM,4BAA4B,cAAoC;CACpE,QAAQ;EACN,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;EACT;CACD,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,OAAO;CACP,UAAU;CACV,QAAQ;CACR,UAAU,gBAAgB,SAAS;CACnC,QAAQ;EACN,mBAAmB,KAAK,UAAU,cAAc,SAAS,CAAC,KAAK;EAC/D,mBAAmB;EACpB;CACF;AAED,MAAa,wBACX,MACA,UACA,YACuB;CAEvB,MAAM,SAAS,UADG,QAAQ,SACQ,CAAC;CAEnC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,WAAW,EAAE;CAC1D,MAAM,mBAAmB,iBAAiB,aAAa;CAYvD,MAAM,IAAI;CACV,MAAM,cAAc,OAAO,EAAE,eAAe;CAC5C,MAAM,eAAe,EAAE;CACvB,MAAM,cAAc,EAAE;AAEtB,KAAI,CAAC,YACH,KAAI;EAEF,MAAM,eADW,cAAc,OAAO,KAAK,IACd,CAAC,QAAQ,UAAU;AAChD,IAAE,aAAa;AACf,IAAE,YAAY,QAAQ,aAAa;SAC7B;CAKV,IAAI;AACJ,KAAI;AACF,iBAAe,iBAAiB;GAC9B,OAAO;IACL,UAAU;IACV;IACA,YAAY,QAAQ,SAAS;IAC7B,YAAY;IACb;GACD,GAAG,yBAAyB,SAAS;GACrC,GAAG;GACJ,CAAC;WACM;AAER,MAAI,CAAC,aAAa;AAChB,OAAI,iBAAiB,OACnB,QAAO,EAAE;OAET,GAAE,aAAa;AAEjB,OAAI,gBAAgB,OAClB,QAAO,EAAE;OAET,GAAE,YAAY;;;AAOpB,QAF2B,aAAc,cAAc,IAAI;;AAK7D,MAAa,mBAAmB,OAC9B,MACA,UACA,YACgC;CAEhC,MAAM,SAAS,UADG,QAAQ,SACQ,CAAC;CAEnC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,WAAW,EAAE;CAM1D,MAAM,MAAM,OAFW,iBAAiB,WAAW,SAElB;EAC/B,OAAO;GACL,UAAU;GACV;GACA,YAAY,QAAQ,SAAS;GAC7B,YAAY;GACb;EACD,GAAG,yBAAyB,SAAS;EACrC,GAAG;EACJ,CAAC;AAEF,KAAI;AAEF,UAAO,MADoB,IAAI,SAAS,EACpB,cAAc,GAAG;WAC7B;AACR,QAAM,IAAI,SAAS"}
1
+ {"version":3,"file":"transpileTSToCJS.mjs","names":[],"sources":["../../../src/loadExternalFile/transpileTSToCJS.ts"],"sourcesContent":["import { existsSync, statSync } from 'node:fs';\nimport { createRequire } from 'node:module';\nimport { dirname, extname, isAbsolute, join, resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\nimport {\n type BuildOptions,\n type BuildResult,\n build,\n buildSync,\n type Metafile,\n} from 'esbuild';\nimport { computeKeyId } from '../utils/cacheMemory';\nimport { getPackageJsonPath } from '../utils/getPackageJsonPath';\nimport { getLoader } from './bundleFile';\n\nexport type TranspileOptions = BuildOptions & {\n /**\n * Optional custom esbuild instance to use for transpilation.\n * Useful in environments (e.g. VS Code extensions) where the bundled\n * esbuild binary may not match the host platform.\n * When provided, its `buildSync`/`build` methods are used instead of\n * the ones imported from the `esbuild` package.\n */\n esbuildInstance?: typeof import('esbuild');\n};\n\n/**\n * Cache of resolved tsconfig paths keyed by the file's directory.\n * Avoids re-walking to package.json + existsSync on every transpilation.\n */\nconst tsConfigPathCache = new Map<string, string | undefined>();\n\nconst getTsConfigPath = (filePath: string): string | undefined => {\n const fileDirectory = dirname(filePath);\n\n if (tsConfigPathCache.has(fileDirectory)) {\n return tsConfigPathCache.get(fileDirectory);\n }\n\n let resolvedPath: string | undefined;\n try {\n const tsconfigPath = join(\n getPackageJsonPath(fileDirectory).baseDir,\n 'tsconfig.json'\n );\n\n // Only return the tsconfig path if it exists\n resolvedPath = existsSync(tsconfigPath) ? tsconfigPath : undefined;\n } catch {\n // No package.json found up the tree — transpile without a tsconfig\n resolvedPath = undefined;\n }\n\n tsConfigPathCache.set(fileDirectory, resolvedPath);\n\n return resolvedPath;\n};\n\nconst getTransformationOptions = (filePath: string): BuildOptions => ({\n loader: {\n '.js': 'js',\n '.jsx': 'jsx',\n '.mjs': 'js',\n '.ts': 'ts',\n '.tsx': 'tsx',\n '.cjs': 'js',\n '.json': 'json',\n '.md': 'text',\n '.mdx': 'text',\n },\n format: 'cjs',\n target: 'node20',\n platform: 'node',\n write: false,\n packages: 'external',\n bundle: true,\n // The metafile lists every bundled input, which the transpile cache uses to\n // invalidate when an imported file changes (the entry itself is keyed by hash).\n metafile: true,\n tsconfig: getTsConfigPath(filePath),\n define: {\n 'import.meta.url': JSON.stringify(pathToFileURL(filePath).href),\n 'import.meta.env': 'process.env',\n },\n});\n\n/** Fingerprint of a bundled input file, used to detect changes without re-reading it. */\ntype TranspileCacheInput = {\n path: string;\n mtimeMs: number;\n size: number;\n};\n\ntype TranspileCacheEntry = {\n /** Hash of the entry file's source code. */\n codeHash: string;\n /** Hash of the build options, so different option sets don't collide. */\n optionsKey: string;\n /** The transpiled CJS output. */\n output: string;\n /** Fingerprints of every non-entry input bundled into the output. */\n inputs: TranspileCacheInput[];\n};\n\n/**\n * In-memory cache of transpiled outputs keyed by entry file path (one entry\n * per file, so re-transpilations replace rather than accumulate).\n * The entry file is validated by content hash; bundled relative imports are\n * validated by mtime + size so an edited dependency invalidates the cache.\n */\nconst transpileCache = new Map<string, TranspileCacheEntry>();\n\nconst isInputUnchanged = (input: TranspileCacheInput): boolean => {\n try {\n const stats = statSync(input.path);\n return stats.mtimeMs === input.mtimeMs && stats.size === input.size;\n } catch {\n // File removed or unreadable — treat as changed\n return false;\n }\n};\n\nconst getCachedTranspilation = (\n filePath: string,\n codeHash: string,\n optionsKey: string\n): string | undefined => {\n const cacheEntry = transpileCache.get(filePath);\n\n if (!cacheEntry) return undefined;\n if (cacheEntry.codeHash !== codeHash) return undefined;\n if (cacheEntry.optionsKey !== optionsKey) return undefined;\n if (!cacheEntry.inputs.every(isInputUnchanged)) return undefined;\n\n return cacheEntry.output;\n};\n\n/**\n * Extracts the fingerprints of every bundled input from the esbuild metafile.\n * Returns `undefined` when an input cannot be fingerprinted (the result should\n * then not be cached, as invalidation would be unreliable).\n */\nconst getInputFingerprints = (\n metafile: Metafile | undefined,\n entryFilePath: string\n): TranspileCacheInput[] | undefined => {\n if (!metafile) return undefined;\n\n const resolvedEntryFilePath = resolve(entryFilePath);\n const inputs: TranspileCacheInput[] = [];\n\n for (const inputPath of Object.keys(metafile.inputs)) {\n // The entry itself is provided via stdin and validated by content hash.\n // esbuild labels it \"<stdin>\", or with the sourcefile path when set.\n if (inputPath === '<stdin>') continue;\n // Virtual modules from plugins (e.g. \"plugin:...\") cannot be fingerprinted\n if (inputPath.includes(':') && !isAbsolute(inputPath)) return undefined;\n\n const absoluteInputPath = isAbsolute(inputPath)\n ? inputPath\n : resolve(process.cwd(), inputPath);\n\n if (absoluteInputPath === resolvedEntryFilePath) continue;\n\n try {\n const stats = statSync(absoluteInputPath);\n inputs.push({\n path: absoluteInputPath,\n mtimeMs: stats.mtimeMs,\n size: stats.size,\n });\n } catch {\n return undefined;\n }\n }\n\n return inputs;\n};\n\nconst setCachedTranspilation = (\n filePath: string,\n codeHash: string,\n optionsKey: string,\n moduleResult: BuildResult,\n output: string\n): void => {\n const inputs = getInputFingerprints(moduleResult.metafile, filePath);\n\n // Without a usable metafile the cache cannot be invalidated reliably — skip\n if (!inputs) return;\n\n transpileCache.set(filePath, { codeHash, optionsKey, output, inputs });\n};\n\n/** Clears the in-memory transpilation cache (mainly for tests). */\nexport const clearTranspileCache = (): void => {\n transpileCache.clear();\n tsConfigPathCache.clear();\n};\n\nexport const transpileTSToCJSSync = (\n code: string,\n filePath: string,\n options?: TranspileOptions\n): string | undefined => {\n const extension = extname(filePath);\n const loader = getLoader(extension);\n\n const { esbuildInstance, ...buildOptions } = options ?? {};\n\n const codeHash = computeKeyId([code]);\n const optionsKey = computeKeyId([buildOptions]);\n\n const cachedOutput = getCachedTranspilation(filePath, codeHash, optionsKey);\n if (typeof cachedOutput === 'string') return cachedOutput;\n\n const esbuildBuildSync = esbuildInstance?.buildSync ?? buildSync;\n\n // esbuild's worker thread service calls `new Worker(__filename, …)` on first use.\n // In Vite's SSR module runner the SSR-optimised chunk is ESM and __filename is\n // never declared (confirmed: accessing it throws ReferenceError, not undefined).\n // Because there is no local declaration the bare `__filename` lookup falls\n // through to globalThis, so we set it there to esbuild's own CJS entry-point –\n // the exact path esbuild would use if it were loaded in a normal CJS context.\n //\n // IMPORTANT: We save/restore the globals so this temporary shim does not leak\n // to other Vite plugins (e.g. `@vitejs/plugin-react-swc`) that check\n // `typeof __dirname !== \"undefined\"` to locate their own assets.\n const g = globalThis as Record<string, unknown>;\n const hadFilename = typeof g.__filename === 'string';\n const prevFilename = g.__filename;\n const prevDirname = g.__dirname;\n\n if (!hadFilename) {\n try {\n const _require = createRequire(import.meta.url);\n const esbuildEntry = _require.resolve('esbuild');\n g.__filename = esbuildEntry;\n g.__dirname = dirname(esbuildEntry);\n } catch {\n // Best-effort: if esbuild can't be resolved the caller's catch handles it\n }\n }\n\n let moduleResult: BuildResult;\n try {\n moduleResult = esbuildBuildSync({\n stdin: {\n contents: code,\n loader,\n resolveDir: dirname(filePath), // Add resolveDir to resolve imports relative to the file's location\n sourcefile: filePath, // Add sourcefile for better error messages\n },\n ...getTransformationOptions(filePath),\n ...buildOptions,\n });\n } finally {\n // Always restore the previous values so the globals don't linger.\n if (!hadFilename) {\n if (prevFilename === undefined) {\n // biome-ignore lint/performance/noDelete: the global must be truly absent — assigning undefined would leave the key present and break `typeof __filename` checks in other tools\n delete g.__filename;\n } else {\n g.__filename = prevFilename;\n }\n if (prevDirname === undefined) {\n // biome-ignore lint/performance/noDelete: same as __filename above\n delete g.__dirname;\n } else {\n g.__dirname = prevDirname;\n }\n }\n }\n\n const moduleResultString = moduleResult!.outputFiles?.[0]?.text;\n\n if (typeof moduleResultString === 'string') {\n setCachedTranspilation(\n filePath,\n codeHash,\n optionsKey,\n moduleResult!,\n moduleResultString\n );\n }\n\n return moduleResultString;\n};\n\nexport const transpileTSToCJS = async (\n code: string,\n filePath: string,\n options?: TranspileOptions\n): Promise<string | undefined> => {\n const extension = extname(filePath);\n const loader = getLoader(extension);\n\n const { esbuildInstance, ...buildOptions } = options ?? {};\n\n const codeHash = computeKeyId([code]);\n const optionsKey = computeKeyId([buildOptions]);\n\n const cachedOutput = getCachedTranspilation(filePath, codeHash, optionsKey);\n if (typeof cachedOutput === 'string') return cachedOutput;\n\n // A one-shot build() releases its Go-side resources on completion and costs a\n // single service round-trip (context()+rebuild()+dispose() costs three).\n const esbuildBuild = esbuildInstance?.build ?? build;\n\n const moduleResult = await esbuildBuild({\n stdin: {\n contents: code,\n loader,\n resolveDir: dirname(filePath),\n sourcefile: filePath,\n },\n ...getTransformationOptions(filePath),\n ...buildOptions,\n });\n\n const moduleResultString = moduleResult.outputFiles?.[0]?.text;\n\n if (typeof moduleResultString === 'string') {\n setCachedTranspilation(\n filePath,\n codeHash,\n optionsKey,\n moduleResult,\n moduleResultString\n );\n }\n\n return moduleResultString;\n};\n"],"mappings":";;;;;;;;;;;;;;AA8BA,MAAM,oCAAoB,IAAI,KAAiC;AAE/D,MAAM,mBAAmB,aAAyC;CAChE,MAAM,gBAAgB,QAAQ,SAAS;AAEvC,KAAI,kBAAkB,IAAI,cAAc,CACtC,QAAO,kBAAkB,IAAI,cAAc;CAG7C,IAAI;AACJ,KAAI;EACF,MAAM,eAAe,KACnB,mBAAmB,cAAc,CAAC,SAClC,gBACD;AAGD,iBAAe,WAAW,aAAa,GAAG,eAAe;SACnD;AAEN,iBAAe;;AAGjB,mBAAkB,IAAI,eAAe,aAAa;AAElD,QAAO;;AAGT,MAAM,4BAA4B,cAAoC;CACpE,QAAQ;EACN,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,OAAO;EACP,QAAQ;EACR,QAAQ;EACR,SAAS;EACT,OAAO;EACP,QAAQ;EACT;CACD,QAAQ;CACR,QAAQ;CACR,UAAU;CACV,OAAO;CACP,UAAU;CACV,QAAQ;CAGR,UAAU;CACV,UAAU,gBAAgB,SAAS;CACnC,QAAQ;EACN,mBAAmB,KAAK,UAAU,cAAc,SAAS,CAAC,KAAK;EAC/D,mBAAmB;EACpB;CACF;;;;;;;AA0BD,MAAM,iCAAiB,IAAI,KAAkC;AAE7D,MAAM,oBAAoB,UAAwC;AAChE,KAAI;EACF,MAAM,QAAQ,SAAS,MAAM,KAAK;AAClC,SAAO,MAAM,YAAY,MAAM,WAAW,MAAM,SAAS,MAAM;SACzD;AAEN,SAAO;;;AAIX,MAAM,0BACJ,UACA,UACA,eACuB;CACvB,MAAM,aAAa,eAAe,IAAI,SAAS;AAE/C,KAAI,CAAC,WAAY,QAAO;AACxB,KAAI,WAAW,aAAa,SAAU,QAAO;AAC7C,KAAI,WAAW,eAAe,WAAY,QAAO;AACjD,KAAI,CAAC,WAAW,OAAO,MAAM,iBAAiB,CAAE,QAAO;AAEvD,QAAO,WAAW;;;;;;;AAQpB,MAAM,wBACJ,UACA,kBACsC;AACtC,KAAI,CAAC,SAAU,QAAO;CAEtB,MAAM,wBAAwB,QAAQ,cAAc;CACpD,MAAM,SAAgC,EAAE;AAExC,MAAK,MAAM,aAAa,OAAO,KAAK,SAAS,OAAO,EAAE;AAGpD,MAAI,cAAc,UAAW;AAE7B,MAAI,UAAU,SAAS,IAAI,IAAI,CAAC,WAAW,UAAU,CAAE,QAAO;EAE9D,MAAM,oBAAoB,WAAW,UAAU,GAC3C,YACA,QAAQ,QAAQ,KAAK,EAAE,UAAU;AAErC,MAAI,sBAAsB,sBAAuB;AAEjD,MAAI;GACF,MAAM,QAAQ,SAAS,kBAAkB;AACzC,UAAO,KAAK;IACV,MAAM;IACN,SAAS,MAAM;IACf,MAAM,MAAM;IACb,CAAC;UACI;AACN;;;AAIJ,QAAO;;AAGT,MAAM,0BACJ,UACA,UACA,YACA,cACA,WACS;CACT,MAAM,SAAS,qBAAqB,aAAa,UAAU,SAAS;AAGpE,KAAI,CAAC,OAAQ;AAEb,gBAAe,IAAI,UAAU;EAAE;EAAU;EAAY;EAAQ;EAAQ,CAAC;;;AAIxE,MAAa,4BAAkC;AAC7C,gBAAe,OAAO;AACtB,mBAAkB,OAAO;;AAG3B,MAAa,wBACX,MACA,UACA,YACuB;CAEvB,MAAM,SAAS,UADG,QAAQ,SACQ,CAAC;CAEnC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,WAAW,EAAE;CAE1D,MAAM,WAAW,aAAa,CAAC,KAAK,CAAC;CACrC,MAAM,aAAa,aAAa,CAAC,aAAa,CAAC;CAE/C,MAAM,eAAe,uBAAuB,UAAU,UAAU,WAAW;AAC3E,KAAI,OAAO,iBAAiB,SAAU,QAAO;CAE7C,MAAM,mBAAmB,iBAAiB,aAAa;CAYvD,MAAM,IAAI;CACV,MAAM,cAAc,OAAO,EAAE,eAAe;CAC5C,MAAM,eAAe,EAAE;CACvB,MAAM,cAAc,EAAE;AAEtB,KAAI,CAAC,YACH,KAAI;EAEF,MAAM,eADW,cAAc,OAAO,KAAK,IACd,CAAC,QAAQ,UAAU;AAChD,IAAE,aAAa;AACf,IAAE,YAAY,QAAQ,aAAa;SAC7B;CAKV,IAAI;AACJ,KAAI;AACF,iBAAe,iBAAiB;GAC9B,OAAO;IACL,UAAU;IACV;IACA,YAAY,QAAQ,SAAS;IAC7B,YAAY;IACb;GACD,GAAG,yBAAyB,SAAS;GACrC,GAAG;GACJ,CAAC;WACM;AAER,MAAI,CAAC,aAAa;AAChB,OAAI,iBAAiB,OAEnB,QAAO,EAAE;OAET,GAAE,aAAa;AAEjB,OAAI,gBAAgB,OAElB,QAAO,EAAE;OAET,GAAE,YAAY;;;CAKpB,MAAM,qBAAqB,aAAc,cAAc,IAAI;AAE3D,KAAI,OAAO,uBAAuB,SAChC,wBACE,UACA,UACA,YACA,cACA,mBACD;AAGH,QAAO;;AAGT,MAAa,mBAAmB,OAC9B,MACA,UACA,YACgC;CAEhC,MAAM,SAAS,UADG,QAAQ,SACQ,CAAC;CAEnC,MAAM,EAAE,iBAAiB,GAAG,iBAAiB,WAAW,EAAE;CAE1D,MAAM,WAAW,aAAa,CAAC,KAAK,CAAC;CACrC,MAAM,aAAa,aAAa,CAAC,aAAa,CAAC;CAE/C,MAAM,eAAe,uBAAuB,UAAU,UAAU,WAAW;AAC3E,KAAI,OAAO,iBAAiB,SAAU,QAAO;CAM7C,MAAM,eAAe,OAFA,iBAAiB,SAAS,OAEP;EACtC,OAAO;GACL,UAAU;GACV;GACA,YAAY,QAAQ,SAAS;GAC7B,YAAY;GACb;EACD,GAAG,yBAAyB,SAAS;EACrC,GAAG;EACJ,CAAC;CAEF,MAAM,qBAAqB,aAAa,cAAc,IAAI;AAE1D,KAAI,OAAO,uBAAuB,SAChC,wBACE,UACA,UACA,YACA,cACA,mBACD;AAGH,QAAO"}
@@ -20,9 +20,9 @@ declare const cookiesAttributesSchema: z.ZodObject<{
20
20
  secure: z.ZodOptional<z.ZodBoolean>;
21
21
  httpOnly: z.ZodOptional<z.ZodBoolean>;
22
22
  sameSite: z.ZodOptional<z.ZodEnum<{
23
- none: "none";
24
23
  strict: "strict";
25
24
  lax: "lax";
25
+ none: "none";
26
26
  }>>;
27
27
  expires: z.ZodOptional<z.ZodUnion<readonly [z.ZodCustom<Date, Date>, z.ZodNumber, z.ZodString]>>;
28
28
  maxAge: z.ZodOptional<z.ZodNumber>;
@@ -48,9 +48,9 @@ declare const storageSchema: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodEnum
48
48
  secure: z.ZodOptional<z.ZodBoolean>;
49
49
  httpOnly: z.ZodOptional<z.ZodBoolean>;
50
50
  sameSite: z.ZodOptional<z.ZodEnum<{
51
- none: "none";
52
51
  strict: "strict";
53
52
  lax: "lax";
53
+ none: "none";
54
54
  }>>;
55
55
  expires: z.ZodOptional<z.ZodUnion<readonly [z.ZodCustom<Date, Date>, z.ZodNumber, z.ZodString]>>;
56
56
  maxAge: z.ZodOptional<z.ZodNumber>;
@@ -74,9 +74,9 @@ declare const storageSchema: z.ZodUnion<readonly [z.ZodLiteral<false>, z.ZodEnum
74
74
  secure: z.ZodOptional<z.ZodBoolean>;
75
75
  httpOnly: z.ZodOptional<z.ZodBoolean>;
76
76
  sameSite: z.ZodOptional<z.ZodEnum<{
77
- none: "none";
78
77
  strict: "strict";
79
78
  lax: "lax";
79
+ none: "none";
80
80
  }>>;
81
81
  expires: z.ZodOptional<z.ZodUnion<readonly [z.ZodCustom<Date, Date>, z.ZodNumber, z.ZodString]>>;
82
82
  maxAge: z.ZodOptional<z.ZodNumber>;
@@ -159,9 +159,9 @@ declare const routingSchema: z.ZodObject<{
159
159
  secure: z.ZodOptional<z.ZodBoolean>;
160
160
  httpOnly: z.ZodOptional<z.ZodBoolean>;
161
161
  sameSite: z.ZodOptional<z.ZodEnum<{
162
- none: "none";
163
162
  strict: "strict";
164
163
  lax: "lax";
164
+ none: "none";
165
165
  }>>;
166
166
  expires: z.ZodOptional<z.ZodUnion<readonly [z.ZodCustom<Date, Date>, z.ZodNumber, z.ZodString]>>;
167
167
  maxAge: z.ZodOptional<z.ZodNumber>;
@@ -185,9 +185,9 @@ declare const routingSchema: z.ZodObject<{
185
185
  secure: z.ZodOptional<z.ZodBoolean>;
186
186
  httpOnly: z.ZodOptional<z.ZodBoolean>;
187
187
  sameSite: z.ZodOptional<z.ZodEnum<{
188
- none: "none";
189
188
  strict: "strict";
190
189
  lax: "lax";
190
+ none: "none";
191
191
  }>>;
192
192
  expires: z.ZodOptional<z.ZodUnion<readonly [z.ZodCustom<Date, Date>, z.ZodNumber, z.ZodString]>>;
193
193
  maxAge: z.ZodOptional<z.ZodNumber>;
@@ -357,9 +357,9 @@ declare const intlayerConfigSchema: z.ZodObject<{
357
357
  secure: z.ZodOptional<z.ZodBoolean>;
358
358
  httpOnly: z.ZodOptional<z.ZodBoolean>;
359
359
  sameSite: z.ZodOptional<z.ZodEnum<{
360
- none: "none";
361
360
  strict: "strict";
362
361
  lax: "lax";
362
+ none: "none";
363
363
  }>>;
364
364
  expires: z.ZodOptional<z.ZodUnion<readonly [z.ZodCustom<Date, Date>, z.ZodNumber, z.ZodString]>>;
365
365
  maxAge: z.ZodOptional<z.ZodNumber>;
@@ -383,9 +383,9 @@ declare const intlayerConfigSchema: z.ZodObject<{
383
383
  secure: z.ZodOptional<z.ZodBoolean>;
384
384
  httpOnly: z.ZodOptional<z.ZodBoolean>;
385
385
  sameSite: z.ZodOptional<z.ZodEnum<{
386
- none: "none";
387
386
  strict: "strict";
388
387
  lax: "lax";
388
+ none: "none";
389
389
  }>>;
390
390
  expires: z.ZodOptional<z.ZodUnion<readonly [z.ZodCustom<Date, Date>, z.ZodNumber, z.ZodString]>>;
391
391
  maxAge: z.ZodOptional<z.ZodNumber>;
@@ -4,7 +4,7 @@ type LoadEnvFileOptions = {
4
4
  envFile?: string;
5
5
  };
6
6
  declare const getEnvFilePath: (env?: string, envFile?: string) => string | undefined;
7
- declare const loadEnvFile: (options?: Partial<LoadEnvFileOptions>) => {};
7
+ declare const loadEnvFile: (options?: Partial<LoadEnvFileOptions>) => Record<string, string>;
8
8
  //#endregion
9
9
  export { LoadEnvFileOptions, getEnvFilePath, loadEnvFile };
10
10
  //# sourceMappingURL=loadEnvFile.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"loadEnvFile.d.ts","names":[],"sources":["../../src/loadEnvFile.ts"],"mappings":";KAKY,kBAAA;EACV,GAAA;EACA,OAAA;AAAA;AAAA,cAGW,cAAA,GACX,GAAA,WACA,OAAA;AAAA,cASW,WAAA,GAAe,OAAA,GAAU,OAAA,CAAQ,kBAAA"}
1
+ {"version":3,"file":"loadEnvFile.d.ts","names":[],"sources":["../../src/loadEnvFile.ts"],"mappings":";KAKY,kBAAA;EACV,GAAA;EACA,OAAA;AAAA;AAAA,cAGW,cAAA,GACX,GAAA,WACA,OAAA;AAAA,cAuCW,WAAA,GACX,OAAA,GAAU,OAAA,CAAQ,kBAAA,MACjB,MAAA"}
@@ -1,6 +1,6 @@
1
1
  import { SandBoxContextOptions, getSandBoxContext, parseFileContent } from "./parseFileContent.js";
2
- import { TranspileOptions, transpileTSToCJS, transpileTSToCJSSync } from "./transpileTSToCJS.js";
2
+ import { TranspileOptions, clearTranspileCache, transpileTSToCJS, transpileTSToCJSSync } from "./transpileTSToCJS.js";
3
3
  import { LoadExternalFileOptions, loadExternalFile, loadExternalFileSync } from "./loadExternalFile.js";
4
4
  import { ESBuildPlugin, bundleFile, bundleFileSync, getLoader } from "./bundleFile.js";
5
5
  import { bundleJSFile } from "./bundleJSFile.js";
6
- export { ESBuildPlugin, LoadExternalFileOptions, SandBoxContextOptions, TranspileOptions, bundleFile, bundleFileSync, bundleJSFile, getLoader, getSandBoxContext, loadExternalFile, loadExternalFileSync, parseFileContent, transpileTSToCJS, transpileTSToCJSSync };
6
+ export { ESBuildPlugin, LoadExternalFileOptions, SandBoxContextOptions, TranspileOptions, bundleFile, bundleFileSync, bundleJSFile, clearTranspileCache, getLoader, getSandBoxContext, loadExternalFile, loadExternalFileSync, parseFileContent, transpileTSToCJS, transpileTSToCJSSync };
@@ -1 +1 @@
1
- {"version":3,"file":"loadExternalFile.d.ts","names":[],"sources":["../../../src/loadExternalFile/loadExternalFile.ts"],"mappings":";;;;;KAmBY,uBAAA;EACV,aAAA,GAAgB,cAAA;EAChB,YAAA,GAAe,gBAAA;EACf,QAAA;;;;;;;;EAQA,cAAA,GAAiB,MAAA;AAAA,IACf,qBAAA;;;;;;cAOS,oBAAA,GACX,QAAA,UACA,OAAA,GAAU,uBAAA;;;AAFZ;;;cAyFa,gBAAA,GACX,QAAA,UACA,OAAA,GAAU,uBAAA,KACT,OAAA"}
1
+ {"version":3,"file":"loadExternalFile.d.ts","names":[],"sources":["../../../src/loadExternalFile/loadExternalFile.ts"],"mappings":";;;;;KAmBY,uBAAA;EACV,aAAA,GAAgB,cAAA;EAChB,YAAA,GAAe,gBAAA;EACf,QAAA;;;;;;;;EAQA,cAAA,GAAiB,MAAA;AAAA,IACf,qBAAA;;;;;;cAOS,oBAAA,GACX,QAAA,UACA,OAAA,GAAU,uBAAA;;;AAFZ;;;cAiGa,gBAAA,GACX,QAAA,UACA,OAAA,GAAU,uBAAA,KACT,OAAA"}
@@ -22,6 +22,15 @@ type SandBoxContextOptions = {
22
22
  */
23
23
  aliases?: Record<string, string>;
24
24
  };
25
+ /**
26
+ * Builds the global context handed to `runInNewContext`.
27
+ *
28
+ * SECURITY NOTE: this sandbox exists for global-scope hygiene (fresh
29
+ * module/exports, controlled env), NOT for containment. It exposes the real
30
+ * project `require`, a copy of `process`, and `fetch` — code executed here has
31
+ * full host privileges. Only ever run trusted, build-time project files
32
+ * (same trust model as `vite.config.ts`).
33
+ */
25
34
  declare const getSandBoxContext: (options?: SandBoxContextOptions) => Context;
26
35
  declare const parseFileContent: <T>(fileContentString: string, options?: SandBoxContextOptions) => T | undefined;
27
36
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"parseFileContent.d.ts","names":[],"sources":["../../../src/loadExternalFile/parseFileContent.ts"],"mappings":";;;;KAKY,qBAAA;EACV,aAAA,GAAgB,kBAAA;EAChB,cAAA,GAAiB,MAAA,CAAO,OAAA;EACxB,iBAAA,GAAoB,MAAA;;;;;;;;EAQpB,KAAA,GAAQ,MAAA;EAVR;;;;;;EAiBA,OAAA,GAAU,MAAA;AAAA;AAAA,cA6BC,iBAAA,GAAqB,OAAA,GAAU,qBAAA,KAAwB,OAAA;AAAA,cAgGvD,gBAAA,MACX,iBAAA,UACA,OAAA,GAAU,qBAAA,KACT,CAAA"}
1
+ {"version":3,"file":"parseFileContent.d.ts","names":[],"sources":["../../../src/loadExternalFile/parseFileContent.ts"],"mappings":";;;;KAKY,qBAAA;EACV,aAAA,GAAgB,kBAAA;EAChB,cAAA,GAAiB,MAAA,CAAO,OAAA;EACxB,iBAAA,GAAoB,MAAA;;;;;;;;EAQpB,KAAA,GAAQ,MAAA;EAVR;;;;;;EAiBA,OAAA,GAAU,MAAA;AAAA;;;;;;AA2FZ;;;;cAAa,iBAAA,GAAqB,OAAA,GAAU,qBAAA,KAAwB,OAAA;AAAA,cAuEvD,gBAAA,MACX,iBAAA,UACA,OAAA,GAAU,qBAAA,KACT,CAAA"}
@@ -12,8 +12,10 @@ type TranspileOptions = BuildOptions & {
12
12
  */
13
13
  esbuildInstance?: typeof _$esbuild;
14
14
  };
15
+ /** Clears the in-memory transpilation cache (mainly for tests). */
16
+ declare const clearTranspileCache: () => void;
15
17
  declare const transpileTSToCJSSync: (code: string, filePath: string, options?: TranspileOptions) => string | undefined;
16
18
  declare const transpileTSToCJS: (code: string, filePath: string, options?: TranspileOptions) => Promise<string | undefined>;
17
19
  //#endregion
18
- export { TranspileOptions, transpileTSToCJS, transpileTSToCJSSync };
20
+ export { TranspileOptions, clearTranspileCache, transpileTSToCJS, transpileTSToCJSSync };
19
21
  //# sourceMappingURL=transpileTSToCJS.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"transpileTSToCJS.d.ts","names":[],"sources":["../../../src/loadExternalFile/transpileTSToCJS.ts"],"mappings":";;;;KAaY,gBAAA,GAAmB,YAAA;;;AAA/B;;;;;EAQE,eAAA,UARyC,SAAA;AAAA;AAAA,cA8C9B,oBAAA,GACX,IAAA,UACA,QAAA,UACA,OAAA,GAAU,gBAAA;AAAA,cAmEC,gBAAA,GACX,IAAA,UACA,QAAA,UACA,OAAA,GAAU,gBAAA,KACT,OAAA"}
1
+ {"version":3,"file":"transpileTSToCJS.d.ts","names":[],"sources":["../../../src/loadExternalFile/transpileTSToCJS.ts"],"mappings":";;;;KAeY,gBAAA,GAAmB,YAAA;;;AAA/B;;;;;EAQE,eAAA,UARyC,SAAA;AAAA;;cAoL9B,mBAAA;AAAA,cAKA,oBAAA,GACX,IAAA,UACA,QAAA,UACA,OAAA,GAAU,gBAAA;AAAA,cAsFC,gBAAA,GACX,IAAA,UACA,QAAA,UACA,OAAA,GAAU,gBAAA,KACT,OAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/config",
3
- "version": "9.0.0-canary.12",
3
+ "version": "9.0.0-canary.15",
4
4
  "private": false,
5
5
  "description": "Retrieve Intlayer configurations and manage environment variables for both server-side and client-side environments.",
6
6
  "keywords": [
@@ -165,7 +165,7 @@
165
165
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
166
166
  },
167
167
  "dependencies": {
168
- "@intlayer/types": "9.0.0-canary.12",
168
+ "@intlayer/types": "9.0.0-canary.15",
169
169
  "defu": "6.1.7",
170
170
  "dotenv": "17.4.2",
171
171
  "esbuild": "0.28.1",