GeezerVison is showing github.com — module: support pattern trailers · nodejs/node@607bc74
File treeExpand file treeCollapse file tree Open diff view settings fixtures/node_modules/pkgexports
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: doc/api/esm.md +41-14Lines changed: 41 additions & 14 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -1169,25 +1169,36 @@ The resolver can throw the following errors: | 1169 | 1169 | **PACKAGE_IMPORTS_EXPORTS_RESOLVE**(_matchKey_, _matchObj_, _packageURL_, | 1170 | 1170 | _isImports_, _conditions_) | 1171 | 1171 |
| 1172 | | -> 1. If _matchKey_ is a key of _matchObj_, and does not end in _"*"_, then | | 1172 | +> 1. If _matchKey_ is a key of _matchObj_ and does not end in _"/"_ or contain | | 1173 | +> _"*"_, then | 1173 | 1174 | > 1. Let _target_ be the value of _matchObj_\[_matchKey_\]. | 1174 | 1175 | > 1. Let _resolved_ be the result of **PACKAGE_TARGET_RESOLVE**( | 1175 | 1176 | > _packageURL_, _target_, _""_, **false**, _isImports_, _conditions_). | 1176 | 1177 | > 1. Return the object _{ resolved, exact: **true** }_. | 1177 | | -> 1. Let _expansionKeys_ be the list of keys of _matchObj_ ending in _"/"_ | 1178 | | -> or _"*"_, sorted by length descending. | | 1178 | +> 1. Let _expansionKeys_ be the list of keys of _matchObj_ either ending in | | 1179 | +> _"/"_ or containing only a single _"*"_, sorted by the sorting function | | 1180 | +> **PATTERN_KEY_COMPARE** which orders in descending order of specificity. | 1179 | 1181 | > 1. For each key _expansionKey_ in _expansionKeys_, do | 1180 | | -> 1. If _expansionKey_ ends in _"*"_ and _matchKey_ starts with but is | 1181 | | -> not equal to the substring of _expansionKey_ excluding the last _"*"_ | 1182 | | -> character, then | 1183 | | -> 1. Let _target_ be the value of _matchObj_\[_expansionKey_\]. | 1184 | | -> 1. Let _subpath_ be the substring of _matchKey_ starting at the | 1185 | | -> index of the length of _expansionKey_ minus one. | 1186 | | -> 1. Let _resolved_ be the result of **PACKAGE_TARGET_RESOLVE**( | 1187 | | -> _packageURL_, _target_, _subpath_, **true**, _isImports_, | 1188 | | -> _conditions_). | 1189 | | -> 1. Return the object _{ resolved, exact: **true** }_. | 1190 | | -> 1. If _matchKey_ starts with _expansionKey_, then | | 1182 | +> 1. Let _patternBase_ be **null**. | | 1183 | +> 1. If _expansionKey_ contains _"*"_, set _patternBase_ to the substring of | | 1184 | +> _expansionKey_ up to but excluding the first _"*"_ character. | | 1185 | +> 1. If _patternBase_ is not **null** and _matchKey_ starts with but is not | | 1186 | +> equal to _patternBase_, then | | 1187 | +> 1. Let _patternTrailer_ be the substring of _expansionKey_ from the | | 1188 | +> index after the first _"*"_ character. | | 1189 | +> 1. If _patternTrailer_ has zero length, or if _matchKey_ ends with | | 1190 | +> _patternTrailer_ and the length of _matchKey_ is greater than or | | 1191 | +> equal to the length of _expansionKey_, then | | 1192 | +> 1. Let _target_ be the value of _matchObj_\[_expansionKey_\]. | | 1193 | +> 1. Let _subpath_ be the substring of _matchKey_ starting at the | | 1194 | +> index of the length of _patternBase_ up to the length of | | 1195 | +> _matchKey_ minus the length of _patternTrailer_. | | 1196 | +> 1. Let _resolved_ be the result of **PACKAGE_TARGET_RESOLVE**( | | 1197 | +> _packageURL_, _target_, _subpath_, **true**, _isImports_, | | 1198 | +> _conditions_). | | 1199 | +> 1. Return the object _{ resolved, exact: **true** }_. | | 1200 | +> 1. Otherwise if _patternBase_ is **null** and _matchKey_ starts with | | 1201 | +> _expansionKey_, then | 1191 | 1202 | > 1. Let _target_ be the value of _matchObj_\[_expansionKey_\]. | 1192 | 1203 | > 1. Let _subpath_ be the substring of _matchKey_ starting at the | 1193 | 1204 | > index of the length of _expansionKey_. | @@ -1197,6 +1208,22 @@ _isImports_, _conditions_) | 1197 | 1208 | > 1. Return the object _{ resolved, exact: **false** }_. | 1198 | 1209 | > 1. Return the object _{ resolved: **null**, exact: **true** }_. | 1199 | 1210 |
| | 1211 | +**PATTERN_KEY_COMPARE**(_keyA_, _keyB_) | | 1212 | + | | 1213 | +> 1. Assert: _keyA_ ends with _"/"_ or contains only a single _"*"_. | | 1214 | +> 1. Assert: _keyB_ ends with _"/"_ or contains only a single _"*"_. | | 1215 | +> 1. Let _baseLengthA_ be the index of _"*"_ in _keyA_ plus one, if _keyA_ | | 1216 | +> contains _"*"_, or the length of _keyA_ otherwise. | | 1217 | +> 1. Let _baseLengthB_ be the index of _"*"_ in _keyB_ plus one, if _keyB_ | | 1218 | +> contains _"*"_, or the length of _keyB_ otherwise. | | 1219 | +> 1. If _baseLengthA_ is greater than _baseLengthB_, return -1. | | 1220 | +> 1. If _baseLengthB_ is greater than _baseLengthA_, return 1. | | 1221 | +> 1. If _keyA_ does not contain _"*"_, return 1. | | 1222 | +> 1. If _keyB_ does not contain _"*"_, return -1. | | 1223 | +> 1. If the length of _keyA_ is greater than the length of _keyB_, return -1. | | 1224 | +> 1. If the length of _keyB_ is greater than the length of _keyA_, return 1. | | 1225 | +> 1. Return 0. | | 1226 | + | 1200 | 1227 | **PACKAGE_TARGET_RESOLVE**(_packageURL_, _target_, _subpath_, _pattern_, | 1201 | 1228 | _internal_, _conditions_) | 1202 | 1229 |
| |
Collapse file Copy file name to clipboardExpand all lines: doc/api/packages.md +2-3Lines changed: 2 additions & 3 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -360,9 +360,8 @@ For these use cases, subpath export patterns can be used instead: | 360 | 360 | **`*` maps expose nested subpaths as it is a string replacement syntax | 361 | 361 | only.** | 362 | 362 |
| 363 | | -The left hand matching pattern must always end in `*`. All instances of `*` on | 364 | | -the right hand side will then be replaced with this value, including if it | 365 | | -contains any `/` separators. | | 363 | +All instances of `*` on the right hand side will then be replaced with this | | 364 | +value, including if it contains any `/` separators. | 366 | 365 |
| 367 | 366 | ```js | 368 | 367 | import featureX from 'es-module-package/features/x'; | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/resolve.js +39-13Lines changed: 39 additions & 13 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -15,7 +15,9 @@ const { | 15 | 15 | SafeSet, | 16 | 16 | String, | 17 | 17 | StringPrototypeEndsWith, | | 18 | + StringPrototypeIncludes, | 18 | 19 | StringPrototypeIndexOf, | | 20 | + StringPrototypeLastIndexOf, | 19 | 21 | StringPrototypeReplace, | 20 | 22 | StringPrototypeSlice, | 21 | 23 | StringPrototypeSplit, | @@ -502,7 +504,9 @@ function packageExportsResolve( | 502 | 504 | if (isConditionalExportsMainSugar(exports, packageJSONUrl, base)) | 503 | 505 | exports = { '.': exports }; | 504 | 506 |
| 505 | | - if (ObjectPrototypeHasOwnProperty(exports, packageSubpath)) { | | 507 | + if (ObjectPrototypeHasOwnProperty(exports, packageSubpath) && | | 508 | + !StringPrototypeIncludes(packageSubpath, '*') && | | 509 | + !StringPrototypeEndsWith(packageSubpath, '/')) { | 506 | 510 | const target = exports[packageSubpath]; | 507 | 511 | const resolved = resolvePackageTarget( | 508 | 512 | packageJSONUrl, target, '', packageSubpath, base, false, false, conditions | @@ -513,30 +517,38 @@ function packageExportsResolve( | 513 | 517 | } | 514 | 518 |
| 515 | 519 | let bestMatch = ''; | | 520 | + let bestMatchSubpath; | 516 | 521 | const keys = ObjectGetOwnPropertyNames(exports); | 517 | 522 | for (let i = 0; i < keys.length; i++) { | 518 | 523 | const key = keys[i]; | 519 | | - if (key[key.length - 1] === '*' && | | 524 | + const patternIndex = StringPrototypeIndexOf(key, '*'); | | 525 | + if (patternIndex !== -1 && | 520 | 526 | StringPrototypeStartsWith(packageSubpath, | 521 | | - StringPrototypeSlice(key, 0, -1)) && | 522 | | - packageSubpath.length >= key.length && | 523 | | - key.length > bestMatch.length) { | 524 | | - bestMatch = key; | | 527 | + StringPrototypeSlice(key, 0, patternIndex))) { | | 528 | + const patternTrailer = StringPrototypeSlice(key, patternIndex + 1); | | 529 | + if (packageSubpath.length >= key.length && | | 530 | + StringPrototypeEndsWith(packageSubpath, patternTrailer) && | | 531 | + patternKeyCompare(bestMatch, key) === 1 && | | 532 | + StringPrototypeLastIndexOf(key, '*') === patternIndex) { | | 533 | + bestMatch = key; | | 534 | + bestMatchSubpath = StringPrototypeSlice( | | 535 | + packageSubpath, patternIndex, | | 536 | + packageSubpath.length - patternTrailer.length); | | 537 | + } | 525 | 538 | } else if (key[key.length - 1] === '/' && | 526 | 539 | StringPrototypeStartsWith(packageSubpath, key) && | 527 | | - key.length > bestMatch.length) { | | 540 | + patternKeyCompare(bestMatch, key) === 1) { | 528 | 541 | bestMatch = key; | | 542 | + bestMatchSubpath = StringPrototypeSlice(packageSubpath, key.length); | 529 | 543 | } | 530 | 544 | } | 531 | 545 |
| 532 | 546 | if (bestMatch) { | 533 | 547 | const target = exports[bestMatch]; | 534 | | - const pattern = bestMatch[bestMatch.length - 1] === '*'; | 535 | | - const subpath = StringPrototypeSubstr(packageSubpath, bestMatch.length - | 536 | | - (pattern ? 1 : 0)); | 537 | | - const resolved = resolvePackageTarget(packageJSONUrl, target, subpath, | 538 | | - bestMatch, base, pattern, false, | 539 | | - conditions); | | 548 | + const pattern = StringPrototypeIncludes(bestMatch, '*'); | | 549 | + const resolved = resolvePackageTarget(packageJSONUrl, target, | | 550 | + bestMatchSubpath, bestMatch, base, | | 551 | + pattern, false, conditions); | 540 | 552 | if (resolved === null || resolved === undefined) | 541 | 553 | throwExportsNotFound(packageSubpath, packageJSONUrl, base); | 542 | 554 | return { resolved, exact: pattern }; | @@ -545,6 +557,20 @@ function packageExportsResolve( | 545 | 557 | throwExportsNotFound(packageSubpath, packageJSONUrl, base); | 546 | 558 | } | 547 | 559 |
| | 560 | +function patternKeyCompare(a, b) { | | 561 | + const aPatternIndex = StringPrototypeIndexOf(a, '*'); | | 562 | + const bPatternIndex = StringPrototypeIndexOf(b, '*'); | | 563 | + const baseLenA = aPatternIndex === -1 ? a.length : aPatternIndex + 1; | | 564 | + const baseLenB = bPatternIndex === -1 ? b.length : bPatternIndex + 1; | | 565 | + if (baseLenA > baseLenB) return -1; | | 566 | + if (baseLenB > baseLenA) return 1; | | 567 | + if (aPatternIndex === -1) return 1; | | 568 | + if (bPatternIndex === -1) return -1; | | 569 | + if (a.length > b.length) return -1; | | 570 | + if (b.length > a.length) return 1; | | 571 | + return 0; | | 572 | +} | | 573 | + | 548 | 574 | function packageImportsResolve(name, base, conditions) { | 549 | 575 | if (name === '#' || StringPrototypeStartsWith(name, '#/')) { | 550 | 576 | const reason = 'is not a valid internal imports specifier name'; | |
Collapse file Copy file name to clipboardExpand all lines: test/es-module/test-esm-exports.mjs +9Lines changed: 9 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -35,7 +35,12 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js'; | 35 | 35 | ['pkgexports-sugar', { default: 'main' }], | 36 | 36 | // Path patterns | 37 | 37 | ['pkgexports/subpath/sub-dir1', { default: 'main' }], | | 38 | + ['pkgexports/subpath/sub-dir1.js', { default: 'main' }], | 38 | 39 | ['pkgexports/features/dir1', { default: 'main' }], | | 40 | + ['pkgexports/dir1/dir1/trailer', { default: 'main' }], | | 41 | + ['pkgexports/dir2/dir2/trailer', { default: 'index' }], | | 42 | + ['pkgexports/a/dir1/dir1', { default: 'main' }], | | 43 | + ['pkgexports/a/b/dir1/dir1', { default: 'main' }], | 39 | 44 | ]); | 40 | 45 |
| 41 | 46 | if (isRequire) { | @@ -77,6 +82,8 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js'; | 77 | 82 | ['pkgexports/null/subpath', './null/subpath'], | 78 | 83 | // Empty fallback | 79 | 84 | ['pkgexports/nofallback1', './nofallback1'], | | 85 | + // Non pattern matches | | 86 | + ['pkgexports/trailer', './trailer'], | 80 | 87 | ]); | 81 | 88 |
| 82 | 89 | const invalidExports = new Map([ | @@ -147,6 +154,8 @@ import fromInside from '../fixtures/node_modules/pkgexports/lib/hole.js'; | 147 | 154 | ['pkgexports/sub/not-a-file.js', `pkgexports${sep}not-a-file.js`], | 148 | 155 | // No extension lookups | 149 | 156 | ['pkgexports/no-ext', `pkgexports${sep}asdf`], | | 157 | + // Pattern specificity | | 158 | + ['pkgexports/dir2/trailer', `subpath${sep}dir2.js`], | 150 | 159 | ]); | 151 | 160 |
| 152 | 161 | if (!isRequire) { | |
Collapse file Copy file name to clipboardExpand all lines: test/fixtures/node_modules/pkgexports/package.json +8Lines changed: 8 additions & 0 deletions
|