GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — module: resolve self-references · nodejs/node@df86793

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
  • doc/api
    • cli.md
    • esm.md
    • modules.md
  • lib/internal/modules/cjs
    • loader.js
  • src
    • env.h
    • module_wrap.cc
    • node_options.cc
    • node_options.h
  • test
    • es-module
      • test-esm-exports.mjs
    • fixtures/node_modules/pkgexports
      • lib
        • hole.js
      • not-exported.js
      • package.json
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎doc/api/cli.md‎

Copy file name to clipboardExpand all lines: doc/api/cli.md
+9Lines changed: 9 additions & 0 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,14 @@ added: v11.8.0
196196

197197
Enable experimental diagnostic report feature.
198198

199+
### `--experimental-resolve-self`
200+
<!-- YAML
201+
added: REPLACEME
202+
-->
203+
204+
Enable experimental support for a package using `require` or `import` to load
205+
itself.
206+
199207
### `--experimental-vm-modules`
200208
<!-- YAML
201209
added: v9.6.0
@@ -1053,6 +1061,7 @@ Node.js options that are allowed are:
10531061
* `--experimental-policy`
10541062
* `--experimental-repl-await`
10551063
* `--experimental-report`
1064+
* `--experimental-resolve-self`
10561065
* `--experimental-vm-modules`
10571066
* `--experimental-wasm-modules`
10581067
* `--force-context-aware`
Collapse file

‎doc/api/esm.md‎

Copy file name to clipboardExpand all lines: doc/api/esm.md
+24-4Lines changed: 24 additions & 4 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -838,9 +838,6 @@ _isMain_ is **true** when resolving the Node.js application entry point.
838838
> 1. Let _packageSubpath_ be *undefined*.
839839
> 1. If _packageSpecifier_ is an empty string, then
840840
> 1. Throw an _Invalid Specifier_ error.
841-
> 1. If _packageSpecifier_ does not start with _"@"_, then
842-
> 1. Set _packageName_ to the substring of _packageSpecifier_ until the
843-
> first _"/"_ separator or the end of the string.
844841
> 1. Otherwise,
845842
> 1. If _packageSpecifier_ does not contain a _"/"_ separator, then
846843
> 1. Throw an _Invalid Specifier_ error.
@@ -854,7 +851,7 @@ _isMain_ is **true** when resolving the Node.js application entry point.
854851
> 1. Set _packageSubpath_ to _"."_ concatenated with the substring of
855852
> _packageSpecifier_ from the position at the length of _packageName_.
856853
> 1. If _packageSubpath_ contains any _"."_ or _".."_ segments or percent
857-
> encoded strings for _"/"_ or _"\\"_ then,
854+
> encoded strings for _"/"_ or _"\\"_, then
858855
> 1. Throw an _Invalid Specifier_ error.
859856
> 1. If _packageSubpath_ is _undefined_ and _packageName_ is a Node.js builtin
860857
> module, then
@@ -877,8 +874,31 @@ _isMain_ is **true** when resolving the Node.js application entry point.
877874
> 1. Return **PACKAGE_EXPORTS_RESOLVE**(_packageURL_,
878875
> _packageSubpath_, _pjson.exports_).
879876
> 1. Return the URL resolution of _packageSubpath_ in _packageURL_.
877+
> 1. Set _selfUrl_ to the result of
878+
> **SELF_REFERENCE_RESOLE**(_packageSpecifier_, _parentURL_).
879+
> 1. If _selfUrl_ isn't empty, return _selfUrl_.
880880
> 1. Throw a _Module Not Found_ error.
881881
882+
**SELF_REFERENCE_RESOLVE**(_specifier_, _parentURL_)
883+
884+
> 1. Let _packageURL_ be the result of **READ_PACKAGE_SCOPE**(_parentURL_).
885+
> 1. If _packageURL_ is **null**, then
886+
> 1. Return an empty result.
887+
> 1. Let _pjson_ be the result of **READ_PACKAGE_JSON**(_packageURL_).
888+
> 1. Set _name_ to _pjson.name_.
889+
> 1. If _name_ is empty, then return an empty result.
890+
> 1. If _name_ is equal to _specifier_, then
891+
> 1. Return the result of **PACKAGE_MAIN_RESOLVE**(_packageURL_, _pjson_).
892+
> 1. If _specifier_ starts with _name_ followed by "/", then
893+
> 1. Set _subpath_ to everything after the "/".
894+
> 1. If _pjson_ is not **null** and _pjson_ has an _"exports"_ key, then
895+
> 1. Let _exports_ be _pjson.exports_.
896+
> 1. If _exports_ is not **null** or **undefined**, then
897+
> 1. Return **PACKAGE_EXPORTS_RESOLVE**(_packageURL_, _subpath_,
898+
> _pjson.exports_).
899+
> 1. Return the URL resolution of _subpath_ in _packageURL_.
900+
> 1. Otherwise return an empty result.
901+
882902
**PACKAGE_MAIN_RESOLVE**(_packageURL_, _pjson_)
883903
884904
> 1. If _pjson_ is **null**, then
Collapse file

‎doc/api/modules.md‎

Copy file name to clipboardExpand all lines: doc/api/modules.md
+10-2Lines changed: 10 additions & 2 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -160,8 +160,9 @@ require(X) from module at path Y
160160
a. LOAD_AS_FILE(Y + X)
161161
b. LOAD_AS_DIRECTORY(Y + X)
162162
c. THROW "not found"
163-
4. LOAD_NODE_MODULES(X, dirname(Y))
164-
5. THROW "not found"
163+
5. LOAD_NODE_MODULES(X, dirname(Y))
164+
4. LOAD_SELF_REFERENCE(X, dirname(Y))
165+
6. THROW "not found"
165166
166167
LOAD_AS_FILE(X)
167168
1. If X is a file, load X as JavaScript text. STOP
@@ -201,6 +202,13 @@ NODE_MODULES_PATHS(START)
201202
c. DIRS = DIRS + DIR
202203
d. let I = I - 1
203204
5. return DIRS
205+
206+
LOAD_SELF_REFERENCE(X, START)
207+
1. Find the closest package scope to START.
208+
2. If no scope was found, throw "not found".
209+
3. If the name in `package.json` isn't a prefix of X, throw "not found".
210+
4. Otherwise, resolve the remainder of X relative to this package as if it
211+
was loaded via `LOAD_NODE_MODULES` with a name in `package.json`.
204212
```
205213

206214
Node.js allows packages loaded via
Collapse file

‎lib/internal/modules/cjs/loader.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/loader.js
+129-37Lines changed: 129 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ const enableSourceMaps = getOptionValue('--enable-source-maps');
5959
const preserveSymlinks = getOptionValue('--preserve-symlinks');
6060
const preserveSymlinksMain = getOptionValue('--preserve-symlinks-main');
6161
const experimentalModules = getOptionValue('--experimental-modules');
62+
const experimentalSelf = getOptionValue('--experimental-resolve-self');
6263
const manifest = getOptionValue('--experimental-policy') ?
6364
require('internal/process/policy').manifest :
6465
null;
@@ -237,6 +238,7 @@ function readPackage(requestPath) {
237238
try {
238239
const parsed = JSON.parse(json);
239240
const filtered = {
241+
name: parsed.name,
240242
main: parsed.main,
241243
exports: parsed.exports,
242244
type: parsed.type
@@ -366,6 +368,125 @@ function findLongestRegisteredExtension(filename) {
366368
return '.js';
367369
}
368370

371+
function resolveBasePath(basePath, exts, isMain, trailingSlash, request) {
372+
let filename;
373+
374+
const rc = stat(basePath);
375+
if (!trailingSlash) {
376+
if (rc === 0) { // File.
377+
if (!isMain) {
378+
if (preserveSymlinks) {
379+
filename = path.resolve(basePath);
380+
} else {
381+
filename = toRealPath(basePath);
382+
}
383+
} else if (preserveSymlinksMain) {
384+
// For the main module, we use the preserveSymlinksMain flag instead
385+
// mainly for backward compatibility, as the preserveSymlinks flag
386+
// historically has not applied to the main module. Most likely this
387+
// was intended to keep .bin/ binaries working, as following those
388+
// symlinks is usually required for the imports in the corresponding
389+
// files to resolve; that said, in some use cases following symlinks
390+
// causes bigger problems which is why the preserveSymlinksMain option
391+
// is needed.
392+
filename = path.resolve(basePath);
393+
} else {
394+
filename = toRealPath(basePath);
395+
}
396+
}
397+
398+
if (!filename) {
399+
// Try it with each of the extensions
400+
if (exts === undefined)
401+
exts = Object.keys(Module._extensions);
402+
filename = tryExtensions(basePath, exts, isMain);
403+
}
404+
}
405+
406+
if (!filename && rc === 1) { // Directory.
407+
// try it with each of the extensions at "index"
408+
if (exts === undefined)
409+
exts = Object.keys(Module._extensions);
410+
filename = tryPackage(basePath, exts, isMain, request);
411+
}
412+
413+
return filename;
414+
}
415+
416+
function trySelf(paths, exts, isMain, trailingSlash, request) {
417+
if (!experimentalSelf) {
418+
return false;
419+
}
420+
421+
const { data: pkg, path: basePath } = readPackageScope(paths[0]);
422+
if (!pkg) return false;
423+
if (typeof pkg.name !== 'string') return false;
424+
425+
let expansion;
426+
if (request === pkg.name) {
427+
expansion = '';
428+
} else if (StringPrototype.startsWith(request, `${pkg.name}/`)) {
429+
expansion = StringPrototype.slice(request, pkg.name.length);
430+
} else {
431+
return false;
432+
}
433+
434+
if (exts === undefined)
435+
exts = Object.keys(Module._extensions);
436+
437+
if (expansion) {
438+
// Use exports
439+
const fromExports = applyExports(basePath, expansion);
440+
if (!fromExports) return false;
441+
return resolveBasePath(fromExports, exts, isMain, trailingSlash, request);
442+
} else {
443+
// Use main field
444+
return tryPackage(basePath, exts, isMain, request);
445+
}
446+
}
447+
448+
function applyExports(basePath, expansion) {
449+
const pkgExports = readPackageExports(basePath);
450+
const mappingKey = `.${expansion}`;
451+
452+
if (typeof pkgExports === 'object' && pkgExports !== null) {
453+
if (ObjectPrototype.hasOwnProperty(pkgExports, mappingKey)) {
454+
const mapping = pkgExports[mappingKey];
455+
return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping, '',
456+
basePath, mappingKey);
457+
}
458+
459+
let dirMatch = '';
460+
for (const candidateKey of Object.keys(pkgExports)) {
461+
if (candidateKey[candidateKey.length - 1] !== '/') continue;
462+
if (candidateKey.length > dirMatch.length &&
463+
StringPrototype.startsWith(mappingKey, candidateKey)) {
464+
dirMatch = candidateKey;
465+
}
466+
}
467+
468+
if (dirMatch !== '') {
469+
const mapping = pkgExports[dirMatch];
470+
const subpath = StringPrototype.slice(mappingKey, dirMatch.length);
471+
return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping,
472+
subpath, basePath, mappingKey);
473+
}
474+
}
475+
if (mappingKey === '.' && typeof pkgExports === 'string') {
476+
return resolveExportsTarget(pathToFileURL(basePath + '/'), pkgExports,
477+
'', basePath, mappingKey);
478+
}
479+
if (pkgExports != null) {
480+
// eslint-disable-next-line no-restricted-syntax
481+
const e = new Error(`Package exports for '${basePath}' do not define ` +
482+
`a '${mappingKey}' subpath`);
483+
e.code = 'MODULE_NOT_FOUND';
484+
throw e;
485+
}
486+
487+
return path.resolve(basePath, mappingKey);
488+
}
489+
369490
// This only applies to requests of a specific form:
370491
// 1. name/.*
371492
// 2. @scope/name/.*
@@ -380,43 +501,7 @@ function resolveExports(nmPath, request, absoluteRequest) {
380501
}
381502

382503
const basePath = path.resolve(nmPath, name);
383-
const pkgExports = readPackageExports(basePath);
384-
const mappingKey = `.${expansion}`;
385-
386-
if (typeof pkgExports === 'object' && pkgExports !== null) {
387-
if (ObjectPrototype.hasOwnProperty(pkgExports, mappingKey)) {
388-
const mapping = pkgExports[mappingKey];
389-
return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping, '',
390-
basePath, mappingKey);
391-
}
392-
393-
let dirMatch = '';
394-
for (const candidateKey of Object.keys(pkgExports)) {
395-
if (candidateKey[candidateKey.length - 1] !== '/') continue;
396-
if (candidateKey.length > dirMatch.length &&
397-
StringPrototype.startsWith(mappingKey, candidateKey)) {
398-
dirMatch = candidateKey;
399-
}
400-
}
401-
402-
if (dirMatch !== '') {
403-
const mapping = pkgExports[dirMatch];
404-
const subpath = StringPrototype.slice(mappingKey, dirMatch.length);
405-
return resolveExportsTarget(pathToFileURL(basePath + '/'), mapping,
406-
subpath, basePath, mappingKey);
407-
}
408-
}
409-
if (mappingKey === '.' && typeof pkgExports === 'string') {
410-
return resolveExportsTarget(pathToFileURL(basePath + '/'), pkgExports,
411-
'', basePath, mappingKey);
412-
}
413-
if (pkgExports != null) {
414-
// eslint-disable-next-line no-restricted-syntax
415-
const e = new Error(`Package exports for '${basePath}' do not define ` +
416-
`a '${mappingKey}' subpath`);
417-
e.code = 'MODULE_NOT_FOUND';
418-
throw e;
419-
}
504+
return applyExports(basePath, expansion);
420505
}
421506

422507
return path.resolve(nmPath, request);
@@ -532,6 +617,13 @@ Module._findPath = function(request, paths, isMain) {
532617
return filename;
533618
}
534619
}
620+
621+
const selfFilename = trySelf(paths, exts, isMain, trailingSlash, request);
622+
if (selfFilename) {
623+
Module._pathCache[cacheKey] = selfFilename;
624+
return selfFilename;
625+
}
626+
535627
return false;
536628
};
537629

Collapse file

‎src/env.h‎

Copy file name to clipboardExpand all lines: src/env.h
+3Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,15 @@ struct PackageConfig {
9393
enum class Exists { Yes, No };
9494
enum class IsValid { Yes, No };
9595
enum class HasMain { Yes, No };
96+
enum class HasName { Yes, No };
9697
enum PackageType : uint32_t { None = 0, CommonJS, Module };
9798

9899
const Exists exists;
99100
const IsValid is_valid;
100101
const HasMain has_main;
101102
const std::string main;
103+
const HasName has_name;
104+
const std::string name;
102105
const PackageType type;
103106

104107
v8::Global<v8::Value> exports;

0 commit comments

Comments
0 (0)
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.