Skip to content

Commit 45e4f82

Browse files
anonriglemire
authored andcommitted
src: move package resolver to c++
Co-authored-by: Daniel Lemire <daniel@lemire.me> PR-URL: #50322 Reviewed-By: Jacob Smith <jacob@frende.me> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
1 parent 0dd53da commit 45e4f82

26 files changed

Lines changed: 798 additions & 347 deletions

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

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ ObjectDefineProperty(Module, '_readPackage', {
427427
* @param {string} originalPath The specifier passed to `require`
428428
*/
429429
functiontryPackage(requestPath,exts,isMain,originalPath){
430-
constpkg=_readPackage(requestPath).main;
430+
const{main: pkg, pjsonPath }=_readPackage(requestPath);
431431

432432
if(!pkg){
433433
returntryExtensions(path.resolve(requestPath,'index'),exts,isMain);
@@ -446,14 +446,13 @@ function tryPackage(requestPath, exts, isMain, originalPath) {
446446
'Please verify that the package.json has a valid "main" entry',
447447
);
448448
err.code='MODULE_NOT_FOUND';
449-
err.path=path.resolve(requestPath,'package.json');
449+
err.path=pjsonPath;
450450
err.requestPath=originalPath;
451451
// TODO(BridgeAR): Add the requireStack as well.
452452
throwerr;
453453
}else{
454-
constjsonPath=path.resolve(requestPath,'package.json');
455454
process.emitWarning(
456-
`Invalid 'main' field in '${jsonPath}' of '${pkg}'. `+
455+
`Invalid 'main' field in '${pjsonPath}' of '${pkg}'. `+
457456
'Please either fix that or report it to the module author',
458457
'DeprecationWarning',
459458
'DEP0128',
@@ -539,28 +538,28 @@ function trySelfParentPath(parent) {
539538
functiontrySelf(parentPath,request){
540539
if(!parentPath){returnfalse;}
541540

542-
const{data: pkg,path: pkgPath}=packageJsonReader.readPackageScope(parentPath);
543-
if(!pkg||pkg.exports==null||pkg.name===undefined){
541+
constpkg=packageJsonReader.getNearestParentPackageJSON(parentPath);
542+
if(pkg?.data.exports===undefined||pkg.data.name===undefined){
544543
returnfalse;
545544
}
546545

547546
letexpansion;
548-
if(request===pkg.name){
547+
if(request===pkg.data.name){
549548
expansion='.';
550-
}elseif(StringPrototypeStartsWith(request,`${pkg.name}/`)){
551-
expansion='.'+StringPrototypeSlice(request,pkg.name.length);
549+
}elseif(StringPrototypeStartsWith(request,`${pkg.data.name}/`)){
550+
expansion='.'+StringPrototypeSlice(request,pkg.data.name.length);
552551
}else{
553552
returnfalse;
554553
}
555554

556555
try{
557556
const{ packageExportsResolve }=require('internal/modules/esm/resolve');
558557
returnfinalizeEsmResolution(packageExportsResolve(
559-
pathToFileURL(pkgPath+'/package.json'),expansion,pkg,
560-
pathToFileURL(parentPath),getCjsConditions()),parentPath,pkgPath);
558+
pathToFileURL(pkg.path+'/package.json'),expansion,pkg.data,
559+
pathToFileURL(parentPath),getCjsConditions()),parentPath,pkg.path);
561560
}catch(e){
562561
if(e.code==='ERR_MODULE_NOT_FOUND'){
563-
throwcreateEsmNotFoundErr(request,pkgPath+'/package.json');
562+
throwcreateEsmNotFoundErr(request,pkg.path+'/package.json');
564563
}
565564
throwe;
566565
}
@@ -1099,7 +1098,7 @@ Module._resolveFilename = function(request, parent, isMain, options) {
10991098

11001099
if(request[0]==='#'&&(parent?.filename||parent?.id==='<repl>')){
11011100
constparentPath=parent?.filename??process.cwd()+path.sep;
1102-
constpkg=packageJsonReader.readPackageScope(parentPath)||{__proto__: null};
1101+
constpkg=packageJsonReader.getNearestParentPackageJSON(parentPath)||{__proto__: null};
11031102
if(pkg.data?.imports!=null){
11041103
try{
11051104
const{ packageImportsResolve }=require('internal/modules/esm/resolve');
@@ -1397,9 +1396,9 @@ Module._extensions['.js'] = function(module, filename) {
13971396
content=fs.readFileSync(filename,'utf8');
13981397
}
13991398
if(StringPrototypeEndsWith(filename,'.js')){
1400-
constpkg=packageJsonReader.readPackageScope(filename)||{__proto__: null};
1399+
constpkg=packageJsonReader.getNearestParentPackageJSON(filename);
14011400
// Function require shouldn't be used in ES modules.
1402-
if(pkg.data?.type==='module'){
1401+
if(pkg?.data.type==='module'){
14031402
// This is an error path because `require` of a `.js` file in a `"type": "module"` scope is not allowed.
14041403
constparent=moduleParentCache.get(module);
14051404
constparentPath=parent?.filename;

‎lib/internal/modules/esm/get_format.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ const {
1919
constexperimentalNetworkImports=
2020
getOptionValue('--experimental-network-imports');
2121
const{ containsModuleSyntax }=internalBinding('contextify');
22-
const{ getPackageType }=require('internal/modules/esm/resolve');
22+
const{ getPackageType }=require('internal/modules/esm/package_config');
2323
const{ fileURLToPath }=require('internal/url');
2424
const{ERR_UNKNOWN_FILE_EXTENSION}=require('internal/errors').codes;
2525

‎lib/internal/modules/esm/module_job.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ class ModuleJob {
228228
constpackageConfig=
229229
StringPrototypeStartsWith(this.module.url,'file://')&&
230230
RegExpPrototypeExec(/\.js(\?[^#]*)?(#.*)?$/,this.module.url)!==null&&
231-
require('internal/modules/esm/resolve')
231+
require('internal/modules/esm/package_config')
232232
.getPackageScopeConfig(this.module.url);
233233
if(packageConfig.type==='module'){
234234
e.message+=
Lines changed: 22 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,44 @@
11
'use strict';
22

3-
const{
4-
StringPrototypeEndsWith,
5-
}=primordials;
6-
const{URL, fileURLToPath }=require('internal/url');
7-
constpackageJsonReader=require('internal/modules/package_json_reader');
3+
const{ ArrayIsArray }=primordials;
4+
constmodulesBinding=internalBinding('modules');
5+
const{ deserializePackageJSON }=require('internal/modules/package_json_reader');
86

9-
/**
10-
* @typedef {object} PackageConfig
11-
* @property {string} pjsonPath - The path to the package.json file.
12-
* @property {boolean} exists - Whether the package.json file exists.
13-
* @property {'none' | 'commonjs' | 'module'} type - The type of the package.
14-
* @property {string} [name] - The name of the package.
15-
* @property {string} [main] - The main entry point of the package.
16-
* @property {PackageTarget} [exports] - The exports configuration of the package.
17-
* @property {Record<string, string | Record<string, string>>} [imports] - The imports configuration of the package.
18-
*/
19-
/**
20-
* @typedef {string | string[] | Record<string, string | Record<string, string>>} PackageTarget
21-
*/
7+
// TODO(@anonrig): Merge this file with internal/esm/package_json_reader.js
228

239
/**
2410
* Returns the package configuration for the given resolved URL.
2511
* @param {URL | string} resolved - The resolved URL.
26-
* @returns {PackageConfig} - The package configuration.
12+
* @returns {import('typings/internalBinding/modules').PackageConfig} - The package configuration.
2713
*/
2814
functiongetPackageScopeConfig(resolved){
29-
letpackageJSONUrl=newURL('./package.json',resolved);
30-
while(true){
31-
constpackageJSONPath=packageJSONUrl.pathname;
32-
if(StringPrototypeEndsWith(packageJSONPath,'node_modules/package.json')){
33-
break;
34-
}
35-
constpackageConfig=packageJsonReader.read(fileURLToPath(packageJSONUrl),{
36-
__proto__: null,
37-
specifier: resolved,
38-
isESM: true,
39-
});
40-
if(packageConfig.exists){
41-
returnpackageConfig;
42-
}
43-
44-
constlastPackageJSONUrl=packageJSONUrl;
45-
packageJSONUrl=newURL('../package.json',packageJSONUrl);
15+
constresult=modulesBinding.getPackageScopeConfig(`${resolved}`);
4616

47-
// Terminates at root where ../package.json equals ../../package.json
48-
// (can't just check "/package.json" for Windows support).
49-
if(packageJSONUrl.pathname===lastPackageJSONUrl.pathname){
50-
break;
51-
}
17+
if(ArrayIsArray(result)){
18+
returndeserializePackageJSON(`${resolved}`,result,false/* checkIntegrity */);
5219
}
53-
constpackageJSONPath=fileURLToPath(packageJSONUrl);
20+
21+
// This means that the response is a string
22+
// and it is the path to the package.json file
5423
return{
5524
__proto__: null,
56-
pjsonPath: packageJSONPath,
25+
pjsonPath: result,
5726
exists: false,
58-
main: undefined,
59-
name: undefined,
6027
type: 'none',
61-
exports: undefined,
62-
imports: undefined,
6328
};
6429
}
6530

31+
/**
32+
* Returns the package type for a given URL.
33+
* @param {URL} url - The URL to get the package type for.
34+
*/
35+
functiongetPackageType(url){
36+
// TODO(@anonrig): Write a C++ function that returns only "type".
37+
returngetPackageScopeConfig(url).type;
38+
}
39+
6640

6741
module.exports={
6842
getPackageScopeConfig,
43+
getPackageType,
6944
};

‎lib/internal/modules/esm/resolve.js‎

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ const legacyMainResolveExtensionsIndexes = {
198198
* 4. TRY(pkg_url/index.js, pkg_url/index.json, pkg_url/index.node)
199199
* 5. NOT_FOUND
200200
* @param {URL} packageJSONUrl
201-
* @param {PackageConfig} packageConfig
201+
* @param {import('typings/internalBinding/modules').PackageConfig} packageConfig
202202
* @param {string | URL | undefined} base
203203
* @returns {URL}
204204
*/
@@ -502,7 +502,7 @@ function resolvePackageTarget(packageJSONUrl, target, subpath, packageSubpath,
502502
}
503503
returnresolveResult;
504504
}
505-
if(lastException===undefined||lastException===null){
505+
if(lastException==null){
506506
returnlastException;
507507
}
508508
throwlastException;
@@ -575,7 +575,7 @@ function isConditionalExportsMainSugar(exports, packageJSONUrl, base) {
575575
*/
576576
functionpackageExportsResolve(
577577
packageJSONUrl,packageSubpath,packageConfig,base,conditions){
578-
letexports=packageConfig.exports;
578+
let{exports }=packageConfig;
579579
if(isConditionalExportsMainSugar(exports,packageJSONUrl,base)){
580580
exports={'.': exports};
581581
}
@@ -740,15 +740,6 @@ function packageImportsResolve(name, base, conditions) {
740740
throwimportNotDefined(name,packageJSONUrl,base);
741741
}
742742

743-
/**
744-
* Returns the package type for a given URL.
745-
* @param {URL} url - The URL to get the package type for.
746-
*/
747-
functiongetPackageType(url){
748-
constpackageConfig=getPackageScopeConfig(url);
749-
returnpackageConfig.type;
750-
}
751-
752743
/**
753744
* Parse a package name from a specifier.
754745
* @param {string} specifier - The import specifier.
@@ -796,6 +787,7 @@ function parsePackageName(specifier, base) {
796787
* @returns {URL} - The resolved URL.
797788
*/
798789
functionpackageResolve(specifier,base,conditions){
790+
// TODO(@anonrig): Move this to a C++ function.
799791
if(BuiltinModule.canBeRequiredWithoutScheme(specifier)){
800792
returnnewURL('node:'+specifier);
801793
}
@@ -1179,8 +1171,6 @@ module.exports = {
11791171
decorateErrorWithCommonJSHints,
11801172
defaultResolve,
11811173
encodedSepRegEx,
1182-
getPackageScopeConfig,
1183-
getPackageType,
11841174
packageExportsResolve,
11851175
packageImportsResolve,
11861176
throwIfInvalidParentURL,

0 commit comments

Comments
 (0)