44 JSONParse,
55 ObjectEntries,
66 SafeMap,
7- SafeSet,
87 StringPrototypeIndexOf,
98 StringPrototypeSlice,
109} = primordials ;
@@ -19,7 +18,7 @@ const getPackageMapPath = getLazy(() =>
1918const fs = require ( 'fs' ) ;
2019
2120const {
22- ERR_PACKAGE_MAP_ACCESS_DENIED ,
21+ ERR_PACKAGE_MAP_EXTERNAL_FILE ,
2322 ERR_PACKAGE_MAP_INVALID ,
2423 ERR_PACKAGE_MAP_KEY_NOT_FOUND ,
2524} = require ( 'internal/errors' ) . codes ;
@@ -33,7 +32,6 @@ let emittedWarning = false;
3332 * @typedef {object } PackageMapEntry
3433 * @property {string } path - Absolute path to package on disk
3534 * @property {Map<string, string> } dependencies - Map from bare specifier to package key
36- * @property {string|undefined } name - Package name (undefined = nameless package)
3735 */
3836
3937class PackageMap {
@@ -43,14 +41,10 @@ class PackageMap {
4341 #basePath;
4442 /** @type {Map<string, PackageMapEntry> } */
4543 #packages;
46- /** @type {Map<string, Set<string>> } */
47- #nameToKeys;
48- /** @type {Map<string, Set<string>> } */
49- #pathToKeys;
44+ /** @type {Map<string, string> } */
45+ #pathToKey;
5046 /** @type {Map<string, string|null> } */
5147 #pathToKeyCache;
52- /** @type {Map<string, object> } */
53- #resolveCache;
5448
5549 /**
5650 * @param {string } configPath
@@ -60,10 +54,8 @@ class PackageMap {
6054 this . #configPath = configPath ;
6155 this . #basePath = dirname ( configPath ) ;
6256 this . #packages = new SafeMap ( ) ;
63- this . #nameToKeys = new SafeMap ( ) ;
64- this . #pathToKeys = new SafeMap ( ) ;
57+ this . #pathToKey = new SafeMap ( ) ;
6558 this . #pathToKeyCache = new SafeMap ( ) ;
66- this . #resolveCache = new SafeMap ( ) ;
6759
6860 this . #parse( data ) ;
6961 }
@@ -86,22 +78,19 @@ class PackageMap {
8678 this . #packages. set ( key , {
8779 path : absolutePath ,
8880 dependencies : new SafeMap ( ObjectEntries ( entry . dependencies ?? { } ) ) ,
89- name : entry . name , // Undefined for nameless packages
9081 } ) ;
9182
92- // Index by name (only named packages)
93- if ( entry . name !== undefined ) {
94- if ( ! this . #nameToKeys. has ( entry . name ) ) {
95- this . #nameToKeys. set ( entry . name , new SafeSet ( ) ) ;
96- }
97- this . #nameToKeys. get ( entry . name ) . add ( key ) ;
83+ // TODO(arcanis): Duplicates should be tolerated, but it requires changing module IDs
84+ // to include the package ID whenever a package map is used.
85+ if ( this . #pathToKey. has ( absolutePath ) ) {
86+ const existingKey = this . #pathToKey. get ( absolutePath ) ;
87+ throw new ERR_PACKAGE_MAP_INVALID (
88+ this . #configPath,
89+ `packages "${ existingKey } " and "${ key } " have the same path "${ entry . path } "; this will be supported in the future` ,
90+ ) ;
9891 }
9992
100- // Index by path
101- if ( ! this . #pathToKeys. has ( absolutePath ) ) {
102- this . #pathToKeys. set ( absolutePath , new SafeSet ( ) ) ;
103- }
104- this . #pathToKeys. get ( absolutePath ) . add ( key ) ;
93+ this . #pathToKey. set ( absolutePath , key ) ;
10594 }
10695 }
10796
@@ -117,9 +106,8 @@ class PackageMap {
117106 // Walk up to find containing package
118107 let checkPath = filePath ;
119108 while ( true ) {
120- const keys = this . #pathToKeys. get ( checkPath ) ;
121- if ( keys && keys . size > 0 ) {
122- const key = keys . values ( ) . next ( ) . value ;
109+ const key = this . #pathToKey. get ( checkPath ) ;
110+ if ( key !== undefined ) {
123111 this . #pathToKeyCache. set ( filePath , key ) ;
124112 return key ;
125113 }
@@ -135,49 +123,26 @@ class PackageMap {
135123 /**
136124 * Main resolution method.
137125 * Returns the package path and subpath for the specifier, or undefined if
138- * resolution should fall back to standard resolution.
126+ * the specifier is not in the parent package's dependencies.
127+ * Throws ERR_PACKAGE_MAP_EXTERNAL_FILE if parentPath is not within any
128+ * mapped package.
139129 * @param {string } specifier
140130 * @param {string } parentPath - File path of the importing module
141131 * @returns {{packagePath: string, subpath: string}|undefined }
142132 */
143133 resolve ( specifier , parentPath ) {
144134 const parentKey = this . #getKeyForPath( parentPath ) ;
145135
146- // Parent not in map - let the caller throw the appropriate error
147- if ( parentKey === null ) { return undefined ; }
148-
149- // Check cache
150- const cacheKey = `${ parentKey } \0${ specifier } ` ;
151- if ( this . #resolveCache. has ( cacheKey ) ) {
152- return this . #resolveCache. get ( cacheKey ) ;
136+ if ( parentKey === null ) {
137+ throw new ERR_PACKAGE_MAP_EXTERNAL_FILE ( specifier , parentPath , this . #configPath) ;
153138 }
154139
155- const result = this . #resolveUncached( specifier , parentKey ) ;
156- this . #resolveCache. set ( cacheKey , result ) ;
157- return result ;
158- }
159-
160- /**
161- * @param {string } specifier
162- * @param {string } parentKey
163- * @returns {{packagePath: string, subpath: string}|undefined }
164- */
165- #resolveUncached( specifier , parentKey ) {
166140 const { packageName, subpath } = parsePackageName ( specifier ) ;
167141 const parentEntry = this . #packages. get ( parentKey ) ;
168142
169143 const targetKey = parentEntry . dependencies . get ( packageName ) ;
170-
171144 if ( targetKey === undefined ) {
172- // Check if package exists anywhere in map but isn't accessible
173- if ( this . #nameToKeys. has ( packageName ) ) {
174- throw new ERR_PACKAGE_MAP_ACCESS_DENIED (
175- specifier ,
176- parentKey ,
177- this . #configPath,
178- ) ;
179- }
180- // Package not in map at all - let the caller throw the appropriate error
145+ // Package not in parent's dependencies - let the caller throw the appropriate error
181146 return undefined ;
182147 }
183148
0 commit comments