UNPKG

nx

Version:

The core Nx plugin contains the core functionality of Nx like the project graph, nx commands and task orchestration.

301 lines (300 loc) 14.1 kB
import type { TsConfigOptions } from 'ts-node'; import type { CompilerOptions } from 'typescript'; /** * Force-register an ESM loader (`@swc-node/register/esm` if available, else * `ts-node/esm`) via `Module.register` so dynamic `import()` of TS files * goes through a transpiler. * * **IMPORTANT — global side effect:** `Module.register` is one-shot per * process and applies to *every* subsequent ESM resolution in the process. * Calling this trades native Node.js TypeScript stripping for transpiled * loading on the dynamic-import path for the rest of the run. CJS * `require()` is unaffected (different hook), so `.cts` files via require * keep using native strip + swc-node's `Module._extensions` hook. * * Required for the niche case where an ESM config (`.mts` or `.ts` resolved * as ESM) combines top-level await with TypeScript syntax that native strip * can't handle (`enum`, runtime `namespace`, etc.). TLA forces dynamic * `import()`, which bypasses the CJS hook chain - the only way to intercept * is `Module.register`. * * Idempotent: subsequent calls are no-ops. * * Throws if neither `@swc-node/register` nor `ts-node` is installed. */ export declare function forceRegisterEsmLoader(): void; /** * Source of a minimal ESM resolution hook that rewrites TypeScript NodeNext * `.js`/`.mjs`/`.cjs` relative specifiers to their `.ts`/`.mts`/`.cts` sources. * Inlined as a string so it can be registered as a self-contained `data:` * module - it relies only on Node's default resolver (no ts-node/swc-node) and * defers loading to Node's native TypeScript stripping. The ESM counterpart to * the CJS `ensureCjsResolverPatched`. * * Only rewrites when the default resolution fails with ERR_MODULE_NOT_FOUND, * the importing module is itself TypeScript, and the specifier is relative, so * it never hijacks resolution that would otherwise succeed. * * Exported so the hook can be exercised directly in unit tests. */ export declare const NODENEXT_ESM_RESOLVER_SOURCE = "\nconst EXT_FALLBACK = { '.js': ['.ts', '.tsx'], '.mjs': ['.mts'], '.cjs': ['.cts'] };\nconst TS_PARENT_RE = /\\.(?:ts|tsx|mts|cts)(?:$|\\?)/;\nexport async function resolve(specifier, context, nextResolve) {\n try {\n return await nextResolve(specifier, context);\n } catch (err) {\n if (err?.code !== 'ERR_MODULE_NOT_FOUND') throw err;\n const parent = context.parentURL;\n if (!parent || !TS_PARENT_RE.test(parent)) throw err;\n if (!(specifier.startsWith('./') || specifier.startsWith('../') || specifier.startsWith('file:'))) throw err;\n const m = specifier.match(/(\\.(?:js|mjs|cjs))($|\\?)/);\n if (!m) throw err;\n const fallbacks = EXT_FALLBACK[m[1]];\n if (!fallbacks) throw err;\n const base = specifier.slice(0, m.index);\n const suffix = specifier.slice(m.index + m[1].length);\n for (const ext of fallbacks) {\n try { return await nextResolve(base + ext + suffix, context); } catch {}\n }\n throw err;\n }\n}\n"; /** * Register a self-contained ESM resolution hook (via `Module.register`) that * rewrites TypeScript NodeNext-style `.js`/`.mjs`/`.cjs` relative specifiers to * their `.ts`/`.mts`/`.cts` sources. This is the ESM counterpart to * `ensureCjsResolverPatched`: Node's native type stripping loads the `.ts` * file, but neither native strip nor Node's ESM resolver rewrites the * extension, so `import './foo.js'` from a `.ts` source where only `foo.ts` * exists fails with ERR_MODULE_NOT_FOUND without it. * * The hook is inlined as a `data:` module (see `NODENEXT_ESM_RESOLVER_SOURCE`) * and relies only on Node's default resolver, so it needs no ts-node/swc-node. * * Idempotent and best-effort: a no-op when `Module.register` is unavailable, * when a TypeScript transpiler is already preloaded (see * `isTsTranspilerPreloaded`), or if registration fails. */ export declare function ensureNodeNextEsmResolverRegistered(): void; /** * Patches Node's CJS resolver to fall back from `.js`/`.mjs`/`.cjs` to the * corresponding TypeScript source extension (`.ts`/`.tsx`, `.mts`, `.cts`) * when the requesting file is itself a `.ts`/`.tsx`/`.mts`/`.cts` source. * * Required for TypeScript NodeNext-style relative imports: `import './foo.js'` * inside a `.ts` file resolves to `./foo.ts` at compile time, but the `.js` * specifier survives transpilation to CJS. Node's native CJS resolver doesn't * rewrite extensions and there is no officially-supported Node API for this — * Node's native strip-types deliberately doesn't either — so a resolver patch * is the prevailing solution in the ecosystem (used by `tsx` and ts-node's * `experimentalResolver`). * * Patches `Module._resolveFilename` (not `_findPath`, matching tsx's narrower * surface). Gates on the requesting file being TS so vanilla `.js` code * requesting missing `.js` files keeps failing — no silent hijack. Only fires * the fallback on `MODULE_NOT_FOUND` so existing `.js` resolution is * unaffected when both files exist. Idempotent on repeat calls. */ export declare function ensureCjsResolverPatched(): void; /** * Whether Nx will defer to Node's native TypeScript stripping for the next * `.ts` load. Mirrors the gate used by `loadTsFile`/`registerTsProject` so * other registration sites (e.g. plugin transpiler) can stay aligned. */ export declare function isNativeStripPreferred(): boolean; /** * This function registers either ts-node or swc-node to transpile TypeScript files on the fly. * It also registers tsconfig-paths to handle path mapping based on the provided tsconfig. * * The TypeScript transpiler registration is done regardless of NX_PREFER_NODE_STRIP_TYPES. * If you want to skip transpiler registration, it is recommended that you check `process.features.typescript`. * * @returns cleanup function */ export declare function registerTsProject(tsConfigPath: string): () => void; export declare function getSwcTranspiler(compilerOptions: CompilerOptions): (...args: unknown[]) => unknown; export declare function getTsNodeTranspiler(compilerOptions: CompilerOptions, tsNodeOptions?: TsConfigOptions, preferTsNode?: boolean): (...args: unknown[]) => unknown; export declare function getTranspiler(compilerOptions: CompilerOptions, tsConfigRaw?: unknown): () => (...args: unknown[]) => unknown; /** * Node.js throws this code when native type stripping hits an unsupported * construct (enum, runtime namespace, legacy decorators, import = require, * parameter properties on older Node, etc.). * * Exported for tests. */ export declare function isNativeTypeStripError(err: unknown): boolean; /** * A SyntaxError thrown while parsing a forced-CJS file (`.cts`/`.cjs`) as * CommonJS - typically ESM syntax in a CJS file (e.g. `export default` in * `.cts`). Pre-v23 this worked because swc-node's CJS hook compiled away the * ESM syntax; under native strip swc-node isn't registered, so the file * reaches Node's strict CJS parser. swc-node tolerates ESM syntax in `.cts` * (`register()` forces `module: commonjs` regardless of extension), so * escalating to the swc/ts-node fallback recovers the legacy behavior. */ export declare function isCjsSyntaxError(err: unknown, filePath: string): boolean; /** * A ReferenceError from Node treating a `.ts`/`.mts` file as ESM and the file * relying on a CJS-only global: `require`, `__dirname`, or `__filename`. * Pre-v23 swc-node compiled `.ts` to CJS where these globals exist; under * native strip Node detects ESM via `import`/`export` syntax and these globals * are undefined. Registering swc/ts-node compiles ESM->CJS and restores the * legacy globals. */ export declare function isRequireInEsmScopeError(err: unknown, filePath: string): boolean; export declare function isTsEsmSyntaxError(err: unknown, filePath: string): boolean; export declare function isTsEsmNamedExportLinkageError(err: unknown, filePath: string): boolean; /** * Load a TypeScript file via `require()`. * * When the runtime exposes native TypeScript stripping * (`process.features.typescript`) and the user hasn't opted out via * `NX_PREFER_NODE_STRIP_TYPES=false`, the file loads directly with no * swc/ts-node and no tsconfig-paths registration. If Node throws on an * unsupported construct (enum, runtime namespace, legacy decorators, etc.), * this registers swc/ts-node + tsconfig-paths and retries - matching the * pre-v23 registration. Set `NX_DISABLE_TSCONFIG_PATHS=true` to skip * tsconfig-paths even on fallback (useful when relying on package manager * workspaces). Set `NX_VERBOSE_LOGGING=true` to log when fallback triggers. * * When native strip is opted out (`NX_PREFER_NODE_STRIP_TYPES=false` or * unsupported Node), uses the legacy `registerTsProject` path. * * `tsConfigPath` is only consulted on the swc/ts-node fallback path (for * compilerOptions) and for tsconfig-paths registration. Native strip ignores * it. When omitted, defaults to the workspace root tsconfig. * * Note on ESM: Node 22.12+ supports `require()` of synchronous ESM by default, * so most ESM `.ts` configs load via this function without issue. Modules * that use top-level await throw `ERR_REQUIRE_ASYNC_MODULE` and must be * loaded with dynamic `import()` instead. `ERR_REQUIRE_ESM` (legacy code) * bubbles unchanged for the rare case it still fires, so async-aware callers * can dispatch to `import()`. * * @returns the loaded module */ export declare function loadTsFile<T = any>(filePath: string, tsConfigPath?: string): T; /** * Plain `require()` with a lazy `tsconfig-paths` fallback. Use for files that * are NOT TypeScript (no transpilation needed) but may still import workspace * packages through TS path aliases (e.g. a `.js` changelog renderer that * `require`s `@my-org/lib`). * * `tsconfig-paths` is only registered after the first `require()` fails with * a module-resolution error, so workspaces that resolve aliases through * package-manager symlinks pay nothing. Set `NX_DISABLE_TSCONFIG_PATHS=true` * to skip the fallback entirely. * * @returns the loaded module */ export declare function requireWithTsconfigFallback<T = any>(filePath: string, tsConfigPath?: string): T; /** * Register ts-node or swc-node given a set of compiler options. * * Note: Several options require enums from typescript. To avoid importing typescript, * use import type + raw values * * @returns cleanup method */ export declare function registerTranspiler(compilerOptions: CompilerOptions, tsConfigRaw?: unknown): () => void; /** * @param tsConfigPath Adds the paths from a tsconfig file into node resolutions * @returns cleanup function */ export declare function registerTsConfigPaths(tsConfigPath: any): () => void; /** * ts-node requires string values for enum based typescript options. * `register`'s signature just types the field as `object`, so we * unfortunately do not get any kind of type safety on this. */ export declare function getTsNodeCompilerOptions(compilerOptions: CompilerOptions): { [x: string]: any; allowImportingTsExtensions?: any; allowJs?: any; allowArbitraryExtensions?: any; allowSyntheticDefaultImports?: any; allowUmdGlobalAccess?: any; allowUnreachableCode?: any; allowUnusedLabels?: any; alwaysStrict?: any; baseUrl?: any; charset?: any; checkJs?: any; customConditions?: any; declaration?: any; declarationMap?: any; emitDeclarationOnly?: any; declarationDir?: any; disableSizeLimit?: any; disableSourceOfProjectReferenceRedirect?: any; disableSolutionSearching?: any; disableReferencedProjectLoad?: any; downlevelIteration?: any; emitBOM?: any; emitDecoratorMetadata?: any; exactOptionalPropertyTypes?: any; experimentalDecorators?: any; forceConsistentCasingInFileNames?: any; ignoreDeprecations?: any; importHelpers?: any; importsNotUsedAsValues?: any; inlineSourceMap?: any; inlineSources?: any; isolatedModules?: any; isolatedDeclarations?: any; jsx?: any; keyofStringsOnly?: any; lib?: any; libReplacement?: any; locale?: any; mapRoot?: any; maxNodeModuleJsDepth?: any; module?: any; moduleResolution?: any; moduleSuffixes?: any; moduleDetection?: any; newLine?: any; noEmit?: any; noCheck?: any; noEmitHelpers?: any; noEmitOnError?: any; noErrorTruncation?: any; noFallthroughCasesInSwitch?: any; noImplicitAny?: any; noImplicitReturns?: any; noImplicitThis?: any; noStrictGenericChecks?: any; noUnusedLocals?: any; noUnusedParameters?: any; noImplicitUseStrict?: any; noPropertyAccessFromIndexSignature?: any; assumeChangesOnlyAffectDirectDependencies?: any; noLib?: any; noResolve?: any; noUncheckedIndexedAccess?: any; out?: any; outDir?: any; outFile?: any; paths?: any; preserveConstEnums?: any; noImplicitOverride?: any; preserveSymlinks?: any; preserveValueImports?: any; project?: any; reactNamespace?: any; jsxFactory?: any; jsxFragmentFactory?: any; jsxImportSource?: any; composite?: any; incremental?: any; tsBuildInfoFile?: any; removeComments?: any; resolvePackageJsonExports?: any; resolvePackageJsonImports?: any; rewriteRelativeImportExtensions?: any; rootDir?: any; rootDirs?: any; skipLibCheck?: any; skipDefaultLibCheck?: any; sourceMap?: any; sourceRoot?: any; strict?: any; strictFunctionTypes?: any; strictBindCallApply?: any; strictNullChecks?: any; strictPropertyInitialization?: any; strictBuiltinIteratorReturn?: any; stripInternal?: any; suppressExcessPropertyErrors?: any; suppressImplicitAnyIndexErrors?: any; target?: any; traceResolution?: any; useUnknownInCatchVariables?: any; noUncheckedSideEffectImports?: any; resolveJsonModule?: any; types?: any; typeRoots?: any; verbatimModuleSyntax?: any; erasableSyntaxOnly?: any; esModuleInterop?: any; useDefineForClassFields?: any; };