UNPKG

nx

Version:

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

87 lines (86 loc) 4.56 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.handleImport = handleImport; const path_1 = require("path"); const STRIP_TYPES_DOCS_URL = 'https://nx.dev/docs/reference/environment-variables#nx-prefer-node-strip-types'; /** * Dynamically imports a module using CJS require(). * Provides a single point of change for future ESM migration. * * Falls back to real import() for ESM-only packages that * throw ERR_REQUIRE_ESM. When loading a workspace plugin's `.ts` entry * under native TypeScript stripping, also falls back to swc/ts-node if * the file uses constructs Node can't strip (enum, runtime namespace, * legacy decorators, etc.). * * @param modulePath - The module specifier (relative, absolute, or package name) * @param relativeTo - The directory to resolve relative paths against. * Pass `__dirname` from the call site when using relative paths like './foo.js'. */ async function handleImport(modulePath, relativeTo) { let resolvedPath = modulePath; if (relativeTo && (modulePath.startsWith('./') || modulePath.startsWith('../'))) { resolvedPath = (0, path_1.resolve)(relativeTo, modulePath); } const normalizedPath = (0, path_1.extname)(resolvedPath) === '.js' ? resolvedPath.slice(0, -3) : resolvedPath; try { return require(normalizedPath); } catch (e) { // ERR_REQUIRE_ESM (legacy) and ERR_REQUIRE_ASYNC_MODULE (Node 22.12+, // ESM with top-level await) both indicate the module must be loaded via // dynamic import(). Native strip handles `.ts` extension resolution on // the import() path, so this recovers TLA plugin entry points without // requiring swc-node or ts-node to be installed. if (e.code === 'ERR_REQUIRE_ESM' || e.code === 'ERR_REQUIRE_ASYNC_MODULE') { return import(resolvedPath); } // Mirror `loadTsFile`'s fallback set (register.ts). Plugin loads hit a // wider failure surface than .ts config loads because plugin sources are // often ESM and import type-only symbols from `@nx/devkit` as runtime // named exports. Lazy-require the matchers to keep register/transpiler // (and their daemon/logger deps) out of module-eval-time graphs. const matchers = require('../plugins/js/utils/register'); if (e?.code === 'ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX' || e?.code === 'MODULE_NOT_FOUND' || e?.code === 'ERR_MODULE_NOT_FOUND' || matchers.isTsEsmSyntaxError(e, resolvedPath) || matchers.isTsEsmNamedExportLinkageError(e, resolvedPath) || matchers.isCjsSyntaxError(e, resolvedPath) || matchers.isRequireInEsmScopeError(e, resolvedPath)) { // Lazy-require to avoid pulling register/transpiler (and their // daemon/logger transitive deps) into module-eval-time graphs. const { forceRegisterPluginTSTranspiler } = require('../project-graph/plugins/transpiler'); forceRegisterPluginTSTranspiler(); try { delete require.cache[require.resolve(normalizedPath)]; } catch { // require.resolve may throw if the failed load never reached cache } try { return require(normalizedPath); } catch (retryErr) { // Mirror the initial-catch behavior: if the compiled output surfaces // TLA or ESM-only-as-CJS, dispatch to dynamic import() instead of // re-throwing. Without this, a plugin whose source needed the // transpiler fallback AND emits top-level await fails here instead of // loading. if (retryErr?.code === 'ERR_REQUIRE_ESM' || retryErr?.code === 'ERR_REQUIRE_ASYNC_MODULE') { return import(resolvedPath); } if (retryErr instanceof Error) { // Lazy-require NX_PREFIX so we don't pull logger -> daemon into // module-eval-time graphs. const { NX_PREFIX } = require('./logger'); retryErr.message = `${retryErr.message}\n\n${NX_PREFIX} Failed to load ${normalizedPath} under Node's native TypeScript stripping. Set NX_PREFER_NODE_STRIP_TYPES=false to opt out and use swc/ts-node instead. See ${STRIP_TYPES_DOCS_URL}`; } throw retryErr; } } throw e; } }