This commit is contained in:
Kismet Hasanaj
2026-05-02 20:07:02 +02:00
parent ce8672e283
commit 34dc9aec52
9428 changed files with 1733330 additions and 0 deletions
+54
View File
@@ -0,0 +1,54 @@
import Module from 'node:module';
import { readFileSync } from 'node:fs';
import { dirname } from 'node:path';
const oldJSHook = require.extensions['.js'];
const extensions = [
'.ts',
'.cts',
'.mts',
'.cjs',
'.mjs'
];
export function registerHook(swcOptions) {
// lazy require swc since it loads React before even setting NODE_ENV
// resulting loading Development React on Production
const { transformSync } = require('../swc');
require.extensions['.js'] = function(mod, oldFilename) {
try {
return oldJSHook(mod, oldFilename);
} catch (error) {
if (error.code !== 'ERR_REQUIRE_ESM') {
throw error;
}
// calling oldJSHook throws ERR_REQUIRE_ESM, so run _compile manually
// TODO: investigate if we can remove readFileSync
const content = readFileSync(oldFilename, 'utf8');
const { code } = transformSync(content, swcOptions);
mod._compile(code, oldFilename);
}
};
for (const ext of extensions){
const oldHook = require.extensions[ext] ?? oldJSHook;
require.extensions[ext] = function(mod, oldFilename) {
const _compile = mod._compile;
mod._compile = function(code, filename) {
const swc = transformSync(code, swcOptions);
return _compile.call(this, swc.code, filename);
};
return oldHook(mod, oldFilename);
};
}
}
export function deregisterHook() {
require.extensions['.js'] = oldJSHook;
extensions.forEach((ext)=>delete require.extensions[ext]);
}
export function requireFromString(code, filename) {
const paths = Module._nodeModulePaths(dirname(filename));
const m = new Module(filename, module.parent);
m.paths = paths;
m._compile(code, filename);
return m.exports;
}
//# sourceMappingURL=require-hook.js.map
+1
View File
@@ -0,0 +1 @@
{"version":3,"sources":["../../../../src/build/next-config-ts/require-hook.ts"],"sourcesContent":["import type { Options as SWCOptions } from '@swc/core'\nimport Module from 'node:module'\nimport { readFileSync } from 'node:fs'\nimport { dirname } from 'node:path'\n\nconst oldJSHook = require.extensions['.js']\nconst extensions = ['.ts', '.cts', '.mts', '.cjs', '.mjs']\n\nexport function registerHook(swcOptions: SWCOptions) {\n // lazy require swc since it loads React before even setting NODE_ENV\n // resulting loading Development React on Production\n const { transformSync } = require('../swc') as typeof import('../swc')\n\n require.extensions['.js'] = function (mod: any, oldFilename) {\n try {\n return oldJSHook(mod, oldFilename)\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'ERR_REQUIRE_ESM') {\n throw error\n }\n\n // calling oldJSHook throws ERR_REQUIRE_ESM, so run _compile manually\n // TODO: investigate if we can remove readFileSync\n const content = readFileSync(oldFilename, 'utf8')\n const { code } = transformSync(content, swcOptions)\n mod._compile(code, oldFilename)\n }\n }\n\n for (const ext of extensions) {\n const oldHook = require.extensions[ext] ?? oldJSHook\n require.extensions[ext] = function (mod: any, oldFilename) {\n const _compile = mod._compile\n\n mod._compile = function (code: string, filename: string) {\n const swc = transformSync(code, swcOptions)\n return _compile.call(this, swc.code, filename)\n }\n\n return oldHook(mod, oldFilename)\n }\n }\n}\n\nexport function deregisterHook() {\n require.extensions['.js'] = oldJSHook\n extensions.forEach((ext) => delete require.extensions[ext])\n}\n\nexport function requireFromString(code: string, filename: string) {\n const paths = (Module as any)._nodeModulePaths(dirname(filename))\n const m = new Module(filename, module.parent!) as any\n m.paths = paths\n m._compile(code, filename)\n return m.exports\n}\n"],"names":["Module","readFileSync","dirname","oldJSHook","require","extensions","registerHook","swcOptions","transformSync","mod","oldFilename","error","code","content","_compile","ext","oldHook","filename","swc","call","deregisterHook","forEach","requireFromString","paths","_nodeModulePaths","m","module","parent","exports"],"mappings":"AACA,OAAOA,YAAY,cAAa;AAChC,SAASC,YAAY,QAAQ,UAAS;AACtC,SAASC,OAAO,QAAQ,YAAW;AAEnC,MAAMC,YAAYC,QAAQC,UAAU,CAAC,MAAM;AAC3C,MAAMA,aAAa;IAAC;IAAO;IAAQ;IAAQ;IAAQ;CAAO;AAE1D,OAAO,SAASC,aAAaC,UAAsB;IACjD,qEAAqE;IACrE,oDAAoD;IACpD,MAAM,EAAEC,aAAa,EAAE,GAAGJ,QAAQ;IAElCA,QAAQC,UAAU,CAAC,MAAM,GAAG,SAAUI,GAAQ,EAAEC,WAAW;QACzD,IAAI;YACF,OAAOP,UAAUM,KAAKC;QACxB,EAAE,OAAOC,OAAO;YACd,IAAI,AAACA,MAAgCC,IAAI,KAAK,mBAAmB;gBAC/D,MAAMD;YACR;YAEA,qEAAqE;YACrE,kDAAkD;YAClD,MAAME,UAAUZ,aAAaS,aAAa;YAC1C,MAAM,EAAEE,IAAI,EAAE,GAAGJ,cAAcK,SAASN;YACxCE,IAAIK,QAAQ,CAACF,MAAMF;QACrB;IACF;IAEA,KAAK,MAAMK,OAAOV,WAAY;QAC5B,MAAMW,UAAUZ,QAAQC,UAAU,CAACU,IAAI,IAAIZ;QAC3CC,QAAQC,UAAU,CAACU,IAAI,GAAG,SAAUN,GAAQ,EAAEC,WAAW;YACvD,MAAMI,WAAWL,IAAIK,QAAQ;YAE7BL,IAAIK,QAAQ,GAAG,SAAUF,IAAY,EAAEK,QAAgB;gBACrD,MAAMC,MAAMV,cAAcI,MAAML;gBAChC,OAAOO,SAASK,IAAI,CAAC,IAAI,EAAED,IAAIN,IAAI,EAAEK;YACvC;YAEA,OAAOD,QAAQP,KAAKC;QACtB;IACF;AACF;AAEA,OAAO,SAASU;IACdhB,QAAQC,UAAU,CAAC,MAAM,GAAGF;IAC5BE,WAAWgB,OAAO,CAAC,CAACN,MAAQ,OAAOX,QAAQC,UAAU,CAACU,IAAI;AAC5D;AAEA,OAAO,SAASO,kBAAkBV,IAAY,EAAEK,QAAgB;IAC9D,MAAMM,QAAQ,AAACvB,OAAewB,gBAAgB,CAACtB,QAAQe;IACvD,MAAMQ,IAAI,IAAIzB,OAAOiB,UAAUS,OAAOC,MAAM;IAC5CF,EAAEF,KAAK,GAAGA;IACVE,EAAEX,QAAQ,CAACF,MAAMK;IACjB,OAAOQ,EAAEG,OAAO;AAClB","ignoreList":[0]}
+195
View File
@@ -0,0 +1,195 @@
import path from 'node:path';
import { readFileSync, existsSync } from 'node:fs';
import { pathToFileURL } from 'node:url';
import * as CommentJson from 'next/dist/compiled/comment-json';
import { deregisterHook, registerHook, requireFromString } from './require-hook';
import { warn, warnOnce } from '../output/log';
import { getNodeOptionsArgs } from '../../server/lib/utils';
function resolveSWCOptions(cwd, compilerOptions) {
var _process_versions, _process;
return {
jsc: {
parser: {
syntax: 'typescript'
},
...compilerOptions.paths ? {
paths: compilerOptions.paths
} : {},
...compilerOptions.baseUrl ? {
baseUrl: path.resolve(cwd, compilerOptions.baseUrl)
} : compilerOptions.paths ? {
baseUrl: cwd
} : {}
},
module: {
type: 'commonjs'
},
isModule: 'unknown',
env: {
targets: {
// Setting the Node.js version can reduce unnecessary code generation.
node: ((_process = process) == null ? void 0 : (_process_versions = _process.versions) == null ? void 0 : _process_versions.node) ?? '20.19.0'
}
}
};
}
function resolveExtends(extendsPath, currentConfigDir) {
// Relative paths are resolved relative to the current config's directory
if (extendsPath.startsWith('./') || extendsPath.startsWith('../') || path.isAbsolute(extendsPath)) {
const resolved = path.resolve(currentConfigDir, extendsPath);
// TypeScript allows omitting .json extension
if (existsSync(resolved)) {
return resolved;
}
if (!resolved.endsWith('.json') && existsSync(resolved + '.json')) {
return resolved + '.json';
}
return resolved;
}
// Package paths - use require.resolve to find the package
try {
// Try resolving as a direct path within the package
return require.resolve(extendsPath, {
paths: [
currentConfigDir
]
});
} catch {
// If that fails, try appending tsconfig.json for package names like "@tsconfig/node18"
try {
return require.resolve(extendsPath + '/tsconfig.json', {
paths: [
currentConfigDir
]
});
} catch {
// Return the original path and let it fail later with a clear error
return path.resolve(currentConfigDir, extendsPath);
}
}
}
function loadTsConfigFile(configPath, visited) {
const resolvedPath = path.resolve(configPath);
if (visited.has(resolvedPath)) {
return {};
}
visited.add(resolvedPath);
if (!existsSync(resolvedPath)) {
return {};
}
const configContent = readFileSync(resolvedPath, 'utf8');
const config = CommentJson.parse(configContent);
const configDir = path.dirname(resolvedPath);
let mergedOptions = {};
// Note that config options from `extends` should get overwritten, not merged
if (config.extends) {
const extendsList = Array.isArray(config.extends) ? config.extends : [
config.extends
];
for (const extendsPath of extendsList){
const parentConfigPath = resolveExtends(extendsPath, configDir);
const parentOptions = loadTsConfigFile(parentConfigPath, visited);
mergedOptions = {
...mergedOptions,
...parentOptions
};
}
}
const currentOptions = config.compilerOptions ?? {};
mergedOptions = {
...mergedOptions,
paths: currentOptions.paths ?? mergedOptions.paths,
baseUrl: currentOptions.baseUrl ?? mergedOptions.baseUrl
};
return mergedOptions;
}
async function loadTsConfig(dir) {
// NOTE: This doesn't fully cover the edge case for setting
// "typescript.tsconfigPath" in next config which is currently
// a restriction.
// It's a chicken-and-egg problem since we need to transpile
// the next config to get that value.
const resolvedTsConfigPath = path.join(dir, 'tsconfig.json');
if (!existsSync(resolvedTsConfigPath)) {
return {};
}
return loadTsConfigFile(resolvedTsConfigPath, new Set());
}
export async function transpileConfig({ nextConfigPath, dir }) {
try {
// envs are passed to the workers and preserve the flag
if (process.env.__NEXT_NODE_NATIVE_TS_LOADER_ENABLED === 'true') {
try {
// Node.js v22.10.0+
// Value is 'strip' or 'transform' based on how the feature is enabled.
// https://nodejs.org/api/process.html#processfeaturestypescript
// TODO: Remove `as any` once we bump @types/node to v22.10.0+
if (process.features.typescript) {
// Run import() here to catch errors and fallback to legacy resolution.
return (await import(pathToFileURL(nextConfigPath).href)).default;
}
if (getNodeOptionsArgs().includes('--no-experimental-strip-types') || process.execArgv.includes('--no-experimental-strip-types')) {
warnOnce(`Skipped resolving "${path.basename(nextConfigPath)}" using Node.js native TypeScript resolution because it was disabled by the "--no-experimental-strip-types" flag.` + ' Falling back to legacy resolution.' + ' Learn more: https://nextjs.org/docs/app/api-reference/config/typescript#using-nodejs-native-typescript-resolver-for-nextconfigts');
}
// Feature is not enabled, fallback to legacy resolution for current session.
process.env.__NEXT_NODE_NATIVE_TS_LOADER_ENABLED = 'false';
} catch (cause) {
warnOnce(`Failed to import "${path.basename(nextConfigPath)}" using Node.js native TypeScript resolution.` + ' Falling back to legacy resolution.' + ' Learn more: https://nextjs.org/docs/app/api-reference/config/typescript#using-nodejs-native-typescript-resolver-for-nextconfigts', {
cause
});
// Once failed, fallback to legacy resolution for current session.
process.env.__NEXT_NODE_NATIVE_TS_LOADER_ENABLED = 'false';
}
}
const compilerOptions = await loadTsConfig(dir);
return handleCJS({
dir,
nextConfigPath,
compilerOptions
});
} catch (cause) {
throw Object.defineProperty(new Error(`Failed to transpile "${path.basename(nextConfigPath)}".`, {
cause
}), "__NEXT_ERROR_CODE", {
value: "E797",
enumerable: false,
configurable: true
});
}
}
async function handleCJS({ dir, nextConfigPath, compilerOptions }) {
const swcOptions = resolveSWCOptions(dir, compilerOptions);
let hasRequire = false;
try {
var _config_experimental;
const nextConfigString = readFileSync(nextConfigPath, 'utf8');
// lazy require swc since it loads React before even setting NODE_ENV
// resulting loading Development React on Production
const { loadBindings } = require('../swc');
const bindings = await loadBindings();
const { code } = await bindings.transform(nextConfigString, swcOptions);
// register require hook only if require exists
if (code.includes('require(')) {
registerHook(swcOptions);
hasRequire = true;
}
// filename & extension don't matter here
const config = requireFromString(code, path.resolve(dir, 'next.config.compiled.js'));
// At this point we have already loaded the bindings without this configuration setting due to the `transform` call above.
// Possibly we fell back to wasm in which case, it all works out but if not we need to warn
// that the configuration was ignored.
if ((config == null ? void 0 : (_config_experimental = config.experimental) == null ? void 0 : _config_experimental.useWasmBinary) && !bindings.isWasm) {
warn('Using a next.config.ts file is incompatible with `experimental.useWasmBinary` unless ' + '`--experimental-next-config-strip-types` is also passed.\nSetting `useWasmBinary` to `false');
config.experimental.useWasmBinary = false;
}
return config;
} catch (error) {
throw error;
} finally{
if (hasRequire) {
deregisterHook();
}
}
}
//# sourceMappingURL=transpile-config.js.map
File diff suppressed because one or more lines are too long