angular/tools/esm-interop/esm-extension-loader.mjs
Paul Gschwendtner abfef9992a build: update dev-infra build tooling and properly handle builtin node: imports (#48521)
* Updates build-tooling to benefit from the latest `spec_bundle`
  improvements.
* Updates the ESM extension loader to not attempt adding extensions to
  builtin `node:` specifiers. This seems to be disallowed and cannot be
  handled gracefully (the attempts are part of a try/catch).

```

Use --sandbox_debug to see verbose messages from the sandbox
Error [ERR_UNKNOWN_BUILTIN_MODULE]: No such built-in module: node:fs.mjs
    at new NodeError (node:internal/errors:371:5)
    at ESMLoader.builtinStrategy (node:internal/modules/esm/translators:276:11)
    at ESMLoader.moduleProvider (node:internal/modules/esm/loader:236:14)
```

PR Close #48521
2022-12-19 19:50:44 +00:00

58 lines
2.3 KiB
JavaScript

/**
* @license
* Copyright Google LLC All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
const explicitExtensionRe = /\.[mc]?js$/;
const explicitJsExtensionRe = /\.js$/;
const builtinNamespaceImportRe = /^node:/;
/*
* NodeJS resolver that enables the interop with the current Bazel setup.
*
* The loader will attempt resolution by replacing explicit extension with
* their ESM variants. It also tries completing import specifiers in case no
* extension of explicit file is specified.
*
* There are a few cases:
*
* * Relative imports without an extension. This happens because our
* source files cannot use explicit `.mjs` extensions yet.
* * Relative imports with an explicit extension to `.js`. This may
* be generated by TypeScript as we have `.ts` source files.
* * Local module imports. In NPM, extensions are not needed as the
* `package.json` `exports` help resolving. In Bazel when dealing with
* 1st-party packages- `package.json` is not consulted in resolution.
* 1. The NPM artifacts differ from the source compilation output.
* 2. It results in additional churn, having to put `package.json` into `bin`.
*/
export async function resolve(specifier, context, nextResolve) {
const interopAttempts = [];
if (explicitJsExtensionRe.test(specifier)) {
interopAttempts.push(specifier.replace(explicitJsExtensionRe, '.mjs'));
}
// If there is no explicit extension and we are not dealing with a
// builtin namespace module, attempt various subpaths to prioritize ESM.
if (!explicitExtensionRe.test(specifier) && !builtinNamespaceImportRe.test(specifier)) {
interopAttempts.push(`${specifier}.mjs`);
interopAttempts.push(`${specifier}/index.mjs`);
// Last attempts are normal `.js` extensions. These could still
// be valid ESM when there is an type:module `package.json` file
interopAttempts.push(`${specifier}.js`);
interopAttempts.push(`${specifier}/index.js`);
}
for (const attempt of interopAttempts) {
try {
return await nextResolve(attempt, context);
} catch {}
}
// Original specifier is attempted at the end because
// we want to prioritize the ESM variants first.
return await nextResolve(specifier, context);
}