@jsenv/core 41.4.26 → 41.4.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4863,2371 +4863,2426 @@ const bufferToEtag = (buffer) => {
4863
4863
  return `"${length.toString(16)}-${hashBase64StringSubset}"`;
4864
4864
  };
4865
4865
 
4866
- const assertImportMap = (value) => {
4867
- if (value === null) {
4868
- throw new TypeError(`an importMap must be an object, got null`);
4869
- }
4870
-
4871
- const type = typeof value;
4872
- if (type !== "object") {
4873
- throw new TypeError(`an importMap must be an object, received ${value}`);
4874
- }
4875
-
4876
- if (Array.isArray(value)) {
4877
- throw new TypeError(
4878
- `an importMap must be an object, received array ${value}`,
4879
- );
4880
- }
4881
- };
4882
-
4883
- // duplicated from @jsenv/log to avoid the dependency
4884
- const createDetailedMessage = (message, details = {}) => {
4885
- let string = `${message}`;
4886
-
4887
- Object.keys(details).forEach((key) => {
4888
- const value = details[key];
4889
- string += `
4890
- --- ${key} ---
4891
- ${
4892
- Array.isArray(value)
4893
- ? value.join(`
4894
- `)
4895
- : value
4896
- }`;
4897
- });
4898
-
4899
- return string;
4900
- };
4901
-
4902
- const hasScheme = (string) => {
4903
- return /^[a-zA-Z]{2,}:/.test(string);
4904
- };
4905
-
4906
- const pathnameToParentPathname = (pathname) => {
4907
- const slashLastIndex = pathname.lastIndexOf("/");
4908
- if (slashLastIndex === -1) {
4909
- return "/";
4910
- }
4911
-
4912
- return pathname.slice(0, slashLastIndex + 1);
4913
- };
4914
-
4915
- const urlToScheme = (urlString) => {
4916
- const colonIndex = urlString.indexOf(":");
4917
- if (colonIndex === -1) return "";
4918
- return urlString.slice(0, colonIndex);
4866
+ // The floor a browser project targets when it says nothing: high enough that
4867
+ // the css jsenv and its components write is shipped as written — nesting,
4868
+ // light-dark(), and the module features that keep <script type="module">,
4869
+ // importmap and top-level await intact. light-dark() is the last gate to
4870
+ // close, and Safari 17.5 is where it does, so it sets the line; the other
4871
+ // runtimes are their releases of that same moment.
4872
+ //
4873
+ // Raising it further is a matter of moving that line, not of adding runtimes:
4874
+ // a project needing a lower one declares "browserslist" in its package.json or
4875
+ // passes runtimeCompat, and both dev and build read it.
4876
+ const browserDefaultRuntimeCompat = {
4877
+ chrome: "125",
4878
+ edge: "125",
4879
+ firefox: "126",
4880
+ ios_safari: "17.5",
4881
+ opera: "110",
4882
+ safari: "17.5",
4883
+ samsung: "25",
4919
4884
  };
4920
4885
 
4921
- const urlToOrigin = (urlString) => {
4922
- const scheme = urlToScheme(urlString);
4923
-
4924
- if (scheme === "file") {
4925
- return "file://";
4886
+ const versionFromValue = (value) => {
4887
+ if (typeof value === "number") {
4888
+ return numberToVersion(value);
4926
4889
  }
4927
-
4928
- if (scheme === "http" || scheme === "https") {
4929
- const secondProtocolSlashIndex = scheme.length + "://".length;
4930
- const pathnameSlashIndex = urlString.indexOf("/", secondProtocolSlashIndex);
4931
-
4932
- if (pathnameSlashIndex === -1) return urlString;
4933
- return urlString.slice(0, pathnameSlashIndex);
4890
+ if (typeof value === "string") {
4891
+ return stringToVersion(value);
4934
4892
  }
4935
-
4936
- return urlString.slice(0, scheme.length + 1);
4893
+ throw new TypeError(`version must be a number or a string, got ${value}`);
4937
4894
  };
4938
4895
 
4939
- const urlToPathname = (urlString) => {
4940
- return ressourceToPathname(urlToRessource(urlString));
4896
+ const numberToVersion = (number) => {
4897
+ return {
4898
+ major: number,
4899
+ minor: 0,
4900
+ patch: 0,
4901
+ };
4941
4902
  };
4942
4903
 
4943
- const urlToRessource = (urlString) => {
4944
- const scheme = urlToScheme(urlString);
4945
-
4946
- if (scheme === "file") {
4947
- return urlString.slice("file://".length);
4904
+ const stringToVersion = (string) => {
4905
+ if (string.indexOf(".") > -1) {
4906
+ const parts = string.split(".");
4907
+ return {
4908
+ major: Number(parts[0]),
4909
+ minor: parts[1] ? Number(parts[1]) : 0,
4910
+ patch: parts[2] ? Number(parts[2]) : 0,
4911
+ };
4948
4912
  }
4949
4913
 
4950
- if (scheme === "https" || scheme === "http") {
4951
- // remove origin
4952
- const afterProtocol = urlString.slice(scheme.length + "://".length);
4953
- const pathnameSlashIndex = afterProtocol.indexOf("/", "://".length);
4954
- return afterProtocol.slice(pathnameSlashIndex);
4914
+ if (isNaN(string)) {
4915
+ return {
4916
+ major: 0,
4917
+ minor: 0,
4918
+ patch: 0,
4919
+ };
4955
4920
  }
4956
4921
 
4957
- return urlString.slice(scheme.length + 1);
4958
- };
4959
-
4960
- const ressourceToPathname = (ressource) => {
4961
- const searchSeparatorIndex = ressource.indexOf("?");
4962
- return searchSeparatorIndex === -1
4963
- ? ressource
4964
- : ressource.slice(0, searchSeparatorIndex);
4922
+ return {
4923
+ major: Number(string),
4924
+ minor: 0,
4925
+ patch: 0,
4926
+ };
4965
4927
  };
4966
4928
 
4967
- // could be useful: https://url.spec.whatwg.org/#url-miscellaneous
4968
-
4969
-
4970
- const resolveUrl = (specifier, baseUrl) => {
4971
- if (baseUrl) {
4972
- if (typeof baseUrl !== "string") {
4973
- throw new TypeError(writeBaseUrlMustBeAString({ baseUrl, specifier }));
4974
- }
4975
- if (!hasScheme(baseUrl)) {
4976
- throw new Error(writeBaseUrlMustBeAbsolute({ baseUrl, specifier }));
4977
- }
4978
- }
4979
-
4980
- if (hasScheme(specifier)) {
4981
- return specifier;
4982
- }
4983
-
4984
- if (!baseUrl) {
4985
- throw new Error(writeBaseUrlRequired({ baseUrl, specifier }));
4986
- }
4987
-
4988
- // scheme relative
4989
- if (specifier.slice(0, 2) === "//") {
4990
- return `${urlToScheme(baseUrl)}:${specifier}`;
4991
- }
4992
-
4993
- // origin relative
4994
- if (specifier[0] === "/") {
4995
- return `${urlToOrigin(baseUrl)}${specifier}`;
4929
+ const compareTwoVersions = (versionA, versionB) => {
4930
+ const semanticVersionA = versionFromValue(versionA);
4931
+ const semanticVersionB = versionFromValue(versionB);
4932
+ const majorDiff = semanticVersionA.major - semanticVersionB.major;
4933
+ if (majorDiff > 0) {
4934
+ return majorDiff;
4996
4935
  }
4997
-
4998
- const baseOrigin = urlToOrigin(baseUrl);
4999
- const basePathname = urlToPathname(baseUrl);
5000
-
5001
- if (specifier === ".") {
5002
- const baseDirectoryPathname = pathnameToParentPathname(basePathname);
5003
- return `${baseOrigin}${baseDirectoryPathname}`;
4936
+ if (majorDiff < 0) {
4937
+ return majorDiff;
5004
4938
  }
5005
-
5006
- // pathname relative inside
5007
- if (specifier.slice(0, 2) === "./") {
5008
- const baseDirectoryPathname = pathnameToParentPathname(basePathname);
5009
- return `${baseOrigin}${baseDirectoryPathname}${specifier.slice(2)}`;
4939
+ const minorDiff = semanticVersionA.minor - semanticVersionB.minor;
4940
+ if (minorDiff > 0) {
4941
+ return minorDiff;
5010
4942
  }
5011
-
5012
- // pathname relative outside
5013
- if (specifier.slice(0, 3) === "../") {
5014
- let unresolvedPathname = specifier;
5015
- const importerFolders = basePathname.split("/");
5016
- importerFolders.pop();
5017
-
5018
- while (unresolvedPathname.slice(0, 3) === "../") {
5019
- unresolvedPathname = unresolvedPathname.slice(3);
5020
- // when there is no folder left to resolved
5021
- // we just ignore '../'
5022
- if (importerFolders.length) {
5023
- importerFolders.pop();
5024
- }
5025
- }
5026
-
5027
- const resolvedPathname = `${importerFolders.join(
5028
- "/",
5029
- )}/${unresolvedPathname}`;
5030
- return `${baseOrigin}${resolvedPathname}`;
4943
+ if (minorDiff < 0) {
4944
+ return minorDiff;
5031
4945
  }
5032
-
5033
- // bare
5034
- if (basePathname === "") {
5035
- return `${baseOrigin}/${specifier}`;
4946
+ const patchDiff = semanticVersionA.patch - semanticVersionB.patch;
4947
+ if (patchDiff > 0) {
4948
+ return patchDiff;
5036
4949
  }
5037
- if (basePathname[basePathname.length] === "/") {
5038
- return `${baseOrigin}${basePathname}${specifier}`;
4950
+ if (patchDiff < 0) {
4951
+ return patchDiff;
5039
4952
  }
5040
- return `${baseOrigin}${pathnameToParentPathname(basePathname)}${specifier}`;
4953
+ return 0;
5041
4954
  };
5042
4955
 
5043
- const writeBaseUrlMustBeAString = ({
5044
- baseUrl,
5045
- specifier,
5046
- }) => `baseUrl must be a string.
5047
- --- base url ---
5048
- ${baseUrl}
5049
- --- specifier ---
5050
- ${specifier}`;
5051
-
5052
- const writeBaseUrlMustBeAbsolute = ({
5053
- baseUrl,
5054
- specifier,
5055
- }) => `baseUrl must be absolute.
5056
- --- base url ---
5057
- ${baseUrl}
5058
- --- specifier ---
5059
- ${specifier}`;
5060
-
5061
- const writeBaseUrlRequired = ({
5062
- baseUrl,
5063
- specifier,
5064
- }) => `baseUrl required to resolve relative specifier.
5065
- --- base url ---
5066
- ${baseUrl}
5067
- --- specifier ---
5068
- ${specifier}`;
5069
-
5070
- const tryUrlResolution = (string, url) => {
5071
- const result = resolveUrl(string, url);
5072
- return hasScheme(result) ? result : null;
4956
+ const versionIsBelow = (versionSupposedBelow, versionSupposedAbove) => {
4957
+ return compareTwoVersions(versionSupposedBelow, versionSupposedAbove) < 0;
5073
4958
  };
5074
4959
 
5075
- const resolveSpecifier = (specifier, importer) => {
5076
- if (
5077
- specifier === "." ||
5078
- specifier[0] === "/" ||
5079
- specifier.startsWith("./") ||
5080
- specifier.startsWith("../")
5081
- ) {
5082
- return resolveUrl(specifier, importer);
5083
- }
4960
+ const findHighestVersion = (...values) => {
4961
+ if (values.length === 0) throw new Error(`missing argument`);
4962
+ return values.reduce((highestVersion, value) => {
4963
+ if (versionIsBelow(highestVersion, value)) {
4964
+ return value;
4965
+ }
4966
+ return highestVersion;
4967
+ });
4968
+ };
5084
4969
 
5085
- if (hasScheme(specifier)) {
5086
- return specifier;
5087
- }
5088
-
5089
- return null;
5090
- };
5091
-
5092
- const applyImportMap = ({
5093
- importMap,
5094
- specifier,
5095
- importer,
5096
- createBareSpecifierError = ({ specifier, importer }) => {
5097
- return new Error(
5098
- createDetailedMessage(`Unmapped bare specifier.`, {
5099
- specifier,
5100
- importer,
5101
- }),
5102
- );
4970
+ const featuresCompatMap = {
4971
+ script_type_module: {
4972
+ edge: "16",
4973
+ firefox: "60",
4974
+ chrome: "61",
4975
+ safari: "10.1",
4976
+ opera: "48",
4977
+ ios_safari: "10.3",
4978
+ android: "61",
4979
+ samsung: "8.2",
5103
4980
  },
5104
- onImportMapping = () => {},
5105
- }) => {
5106
- assertImportMap(importMap);
5107
- if (typeof specifier !== "string") {
5108
- throw new TypeError(
5109
- createDetailedMessage("specifier must be a string.", {
5110
- specifier,
5111
- importer,
5112
- }),
5113
- );
5114
- }
5115
- if (importer) {
5116
- if (typeof importer !== "string") {
5117
- throw new TypeError(
5118
- createDetailedMessage("importer must be a string.", {
5119
- importer,
5120
- specifier,
5121
- }),
5122
- );
5123
- }
5124
- if (!hasScheme(importer)) {
5125
- throw new Error(
5126
- createDetailedMessage(`importer must be an absolute url.`, {
5127
- importer,
5128
- specifier,
5129
- }),
5130
- );
5131
- }
5132
- }
4981
+ document_current_script: {
4982
+ edge: "12",
4983
+ firefox: "4",
4984
+ chrome: "29",
4985
+ safari: "8",
4986
+ opera: "16",
4987
+ android: "4.4",
4988
+ samsung: "4",
4989
+ },
4990
+ // https://caniuse.com/?search=import.meta
4991
+ import_meta: {
4992
+ android: "9",
4993
+ chrome: "64",
4994
+ edge: "79",
4995
+ firefox: "62",
4996
+ ios_safari: "12",
4997
+ opera: "51",
4998
+ safari: "11.1",
4999
+ samsung: "9.2",
5000
+ },
5001
+ import_meta_resolve: {
5002
+ chrome: "107",
5003
+ edge: "105",
5004
+ firefox: "106",
5005
+ node: "20.0.0",
5006
+ },
5007
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#browser_compatibility
5008
+ import_dynamic: {
5009
+ android: "8",
5010
+ chrome: "63",
5011
+ edge: "79",
5012
+ firefox: "67",
5013
+ ios_safari: "11.3",
5014
+ opera: "50",
5015
+ safari: "11.3",
5016
+ samsung: "8.0",
5017
+ node: "13.2",
5018
+ },
5019
+ top_level_await: {
5020
+ edge: "89",
5021
+ chrome: "89",
5022
+ firefox: "89",
5023
+ opera: "75",
5024
+ safari: "15",
5025
+ samsung: "15",
5026
+ ios_safari: "15",
5027
+ node: "14.8",
5028
+ },
5029
+ // https://caniuse.com/import-maps
5030
+ importmap: {
5031
+ edge: "89",
5032
+ chrome: "89",
5033
+ opera: "76",
5034
+ samsung: "15",
5035
+ firefox: "108",
5036
+ safari: "16.4",
5037
+ },
5038
+ import_type_json: {
5039
+ chrome: "123",
5040
+ safari: "17.2",
5041
+ },
5042
+ import_type_css: {
5043
+ chrome: "123",
5044
+ },
5045
+ import_type_text: {},
5046
+ // https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet#browser_compatibility
5047
+ new_stylesheet: {
5048
+ chrome: "73",
5049
+ edge: "79",
5050
+ opera: "53",
5051
+ android: "73",
5052
+ },
5053
+ // https://caniuse.com/?search=worker
5054
+ worker: {
5055
+ ie: "10",
5056
+ edge: "12",
5057
+ firefox: "3.5",
5058
+ chrome: "4",
5059
+ opera: "11.5",
5060
+ safari: "4",
5061
+ ios_safari: "5",
5062
+ android: "4.4",
5063
+ },
5064
+ // https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker#browser_compatibility
5065
+ worker_type_module: {
5066
+ chrome: "80",
5067
+ edge: "80",
5068
+ opera: "67",
5069
+ android: "80",
5070
+ },
5071
+ worker_importmap: {},
5072
+ service_worker: {
5073
+ edge: "17",
5074
+ firefox: "44",
5075
+ chrome: "40",
5076
+ safari: "11.1",
5077
+ opera: "27",
5078
+ ios_safari: "11.3",
5079
+ android: "12.12",
5080
+ },
5081
+ service_worker_type_module: {
5082
+ chrome: "80",
5083
+ edge: "80",
5084
+ opera: "67",
5085
+ android: "80",
5086
+ },
5087
+ service_worker_importmap: {},
5088
+ shared_worker: {
5089
+ chrome: "4",
5090
+ edge: "79",
5091
+ firefox: "29",
5092
+ opera: "10.6",
5093
+ },
5094
+ shared_worker_type_module: {
5095
+ chrome: "80",
5096
+ edge: "80",
5097
+ opera: "67",
5098
+ },
5099
+ shared_worker_importmap: {},
5100
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis#browser_compatibility
5101
+ global_this: {
5102
+ edge: "79",
5103
+ firefox: "65",
5104
+ chrome: "71",
5105
+ safari: "12.1",
5106
+ opera: "58",
5107
+ ios_safari: "12.2",
5108
+ android: "94",
5109
+ node: "12",
5110
+ },
5111
+ async_generator_function: {
5112
+ chrome: "63",
5113
+ opera: "50",
5114
+ edge: "79",
5115
+ firefox: "57",
5116
+ safari: "12",
5117
+ node: "10",
5118
+ ios_safari: "12",
5119
+ samsung: "8",
5120
+ electron: "3",
5121
+ },
5122
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#browser_compatibility
5123
+ template_literals: {
5124
+ chrome: "41",
5125
+ edge: "12",
5126
+ firefox: "34",
5127
+ opera: "28",
5128
+ safari: "9",
5129
+ ios_safari: "9",
5130
+ android: "4",
5131
+ node: "4",
5132
+ },
5133
+ arrow_function: {
5134
+ chrome: "47",
5135
+ opera: "34",
5136
+ edge: "13",
5137
+ firefox: "45",
5138
+ safari: "10",
5139
+ node: "6",
5140
+ ios_safari: "10",
5141
+ samsung: "5",
5142
+ electron: "0.36",
5143
+ },
5144
+ const_bindings: {
5145
+ chrome: "41",
5146
+ opera: "28",
5147
+ edge: "12",
5148
+ firefox: "46",
5149
+ safari: "10",
5150
+ node: "4",
5151
+ ie: "11",
5152
+ ios_safari: "10",
5153
+ samsung: "3.4",
5154
+ electron: "0.22",
5155
+ },
5156
+ object_properties_shorthand: {
5157
+ chrome: "43",
5158
+ opera: "30",
5159
+ edge: "12",
5160
+ firefox: "33",
5161
+ safari: "9",
5162
+ node: "4",
5163
+ ios_safari: "9",
5164
+ samsung: "4",
5165
+ electron: "0.28",
5166
+ },
5167
+ reserved_words: {
5168
+ chrome: "13",
5169
+ opera: "10.50",
5170
+ edge: "12",
5171
+ firefox: "2",
5172
+ safari: "3.1",
5173
+ node: "0.10",
5174
+ ie: "9",
5175
+ android: "4.4",
5176
+ ios_safari: "6",
5177
+ phantom: "2",
5178
+ samsung: "1",
5179
+ electron: "0.20",
5180
+ },
5181
+ symbols: {
5182
+ chrome: "38",
5183
+ opera: "25",
5184
+ edge: "12",
5185
+ firefox: "36",
5186
+ safari: "9",
5187
+ ios_safari: "9",
5188
+ samsung: "4",
5189
+ node: "0.12",
5190
+ },
5191
+ };
5133
5192
 
5134
- const specifierUrl = resolveSpecifier(specifier, importer);
5135
- const specifierNormalized = specifierUrl || specifier;
5193
+ const RUNTIME_COMPAT = {
5194
+ featuresCompatMap,
5136
5195
 
5137
- const { scopes } = importMap;
5138
- if (scopes && importer) {
5139
- const scopeSpecifierMatching = Object.keys(scopes).find(
5140
- (scopeSpecifier) => {
5141
- return (
5142
- scopeSpecifier === importer ||
5143
- specifierIsPrefixOf(scopeSpecifier, importer)
5196
+ add: (originalRuntimeCompat, feature) => {
5197
+ const featureCompat = getFeatureCompat(feature);
5198
+ const runtimeCompat = {
5199
+ ...originalRuntimeCompat,
5200
+ };
5201
+ Object.keys(originalRuntimeCompat).forEach((runtimeName) => {
5202
+ const secondVersion = featureCompat[runtimeName]; // the version supported by the feature
5203
+ if (secondVersion) {
5204
+ const firstVersion = originalRuntimeCompat[runtimeName];
5205
+ runtimeCompat[runtimeName] = findHighestVersion(
5206
+ firstVersion,
5207
+ secondVersion,
5144
5208
  );
5145
- },
5146
- );
5147
- if (scopeSpecifierMatching) {
5148
- const scopeMappings = scopes[scopeSpecifierMatching];
5149
- const mappingFromScopes = applyMappings(
5150
- scopeMappings,
5151
- specifierNormalized,
5152
- scopeSpecifierMatching,
5153
- onImportMapping,
5154
- );
5155
- if (mappingFromScopes !== null) {
5156
- return mappingFromScopes;
5157
5209
  }
5158
- }
5159
- }
5210
+ });
5211
+ return runtimeCompat;
5212
+ },
5213
+
5214
+ isSupported: (
5215
+ runtimeCompat,
5216
+ feature,
5217
+ featureCompat = getFeatureCompat(feature),
5218
+ ) => {
5219
+ const runtimeNames = Object.keys(runtimeCompat);
5220
+ const runtimeWithoutCompat = runtimeNames.find((runtimeName) => {
5221
+ const runtimeVersion = runtimeCompat[runtimeName];
5222
+ const runtimeVersionCompatible = featureCompat[runtimeName] || "Infinity";
5223
+ const highestVersion = findHighestVersion(
5224
+ runtimeVersion,
5225
+ runtimeVersionCompatible,
5226
+ );
5227
+ return highestVersion !== runtimeVersion;
5228
+ });
5229
+ return !runtimeWithoutCompat;
5230
+ },
5231
+ };
5160
5232
 
5161
- const { imports } = importMap;
5162
- if (imports) {
5163
- const mappingFromImports = applyMappings(
5164
- imports,
5165
- specifierNormalized,
5166
- undefined,
5167
- onImportMapping,
5168
- );
5169
- if (mappingFromImports !== null) {
5170
- return mappingFromImports;
5233
+ const getFeatureCompat = (feature) => {
5234
+ if (typeof feature === "string") {
5235
+ const compat = featuresCompatMap[feature];
5236
+ if (!compat) {
5237
+ throw new Error(`"${feature}" feature is unknown`);
5171
5238
  }
5239
+ return compat;
5172
5240
  }
5173
-
5174
- if (specifierUrl) {
5175
- return specifierUrl;
5241
+ if (typeof feature !== "object") {
5242
+ throw new TypeError(
5243
+ `feature must be a string or an object, got ${feature}`,
5244
+ );
5176
5245
  }
5177
-
5178
- throw createBareSpecifierError({ specifier, importer });
5246
+ return feature;
5179
5247
  };
5180
5248
 
5181
- const applyMappings = (
5182
- mappings,
5183
- specifierNormalized,
5184
- scope,
5185
- onImportMapping,
5249
+ const inferRuntimeCompatFromClosestPackage = async (
5250
+ sourceUrl,
5251
+ { runtimeType },
5186
5252
  ) => {
5187
- const specifierCandidates = Object.keys(mappings);
5253
+ const packageDirectoryUrl = lookupPackageDirectory(sourceUrl);
5254
+ if (!packageDirectoryUrl) {
5255
+ return null;
5256
+ }
5257
+ const packageJSON = readPackageAtOrNull(packageDirectoryUrl);
5258
+ if (!packageJSON) {
5259
+ return null;
5260
+ }
5188
5261
 
5189
- let i = 0;
5190
- while (i < specifierCandidates.length) {
5191
- const specifierCandidate = specifierCandidates[i];
5192
- i++;
5193
- if (specifierCandidate === specifierNormalized) {
5194
- const address = mappings[specifierCandidate];
5195
- onImportMapping({
5196
- scope,
5197
- from: specifierCandidate,
5198
- to: address,
5199
- before: specifierNormalized,
5200
- after: address,
5201
- });
5202
- return address;
5262
+ {
5263
+ const browserslistQuery = packageJSON.browserslist;
5264
+ if (!browserslistQuery) {
5265
+ return null;
5203
5266
  }
5204
- if (specifierIsPrefixOf(specifierCandidate, specifierNormalized)) {
5205
- const address = mappings[specifierCandidate];
5206
- const afterSpecifier = specifierNormalized.slice(
5207
- specifierCandidate.length,
5208
- );
5209
- const addressFinal = tryUrlResolution(afterSpecifier, address);
5210
- onImportMapping({
5211
- scope,
5212
- from: specifierCandidate,
5213
- to: address,
5214
- before: specifierNormalized,
5215
- after: addressFinal,
5216
- });
5217
- return addressFinal;
5267
+ const namespace = await import("./browserslist_index/browserslist_index.js");
5268
+ const browserslist = namespace.default;
5269
+ const browserslistResult = browserslist(browserslistQuery);
5270
+ const runtimeCompat = {};
5271
+ for (const browserNameAndVersion of browserslistResult) {
5272
+ let [name, version] = browserNameAndVersion.split(" ");
5273
+ if (name === "ios_saf") {
5274
+ name = "ios_safari";
5275
+ }
5276
+ if (Object.keys(browserDefaultRuntimeCompat).includes(name)) {
5277
+ runtimeCompat[name] = version;
5278
+ }
5218
5279
  }
5280
+ return runtimeCompat;
5219
5281
  }
5220
-
5221
- return null;
5222
- };
5223
-
5224
- const specifierIsPrefixOf = (specifierHref, href) => {
5225
- return (
5226
- specifierHref[specifierHref.length - 1] === "/" &&
5227
- href.startsWith(specifierHref)
5228
- );
5229
5282
  };
5230
5283
 
5231
- // https://github.com/systemjs/systemjs/blob/89391f92dfeac33919b0223bbf834a1f4eea5750/src/common.js#L136
5232
-
5233
- const composeTwoImportMaps = (leftImportMap, rightImportMap) => {
5234
- assertImportMap(leftImportMap);
5235
- assertImportMap(rightImportMap);
5236
-
5237
- const importMap = {};
5284
+ const assertImportMap = (value) => {
5285
+ if (value === null) {
5286
+ throw new TypeError(`an importMap must be an object, got null`);
5287
+ }
5238
5288
 
5239
- const leftImports = leftImportMap.imports;
5240
- const rightImports = rightImportMap.imports;
5241
- const leftHasImports = Boolean(leftImports);
5242
- const rightHasImports = Boolean(rightImports);
5243
- if (leftHasImports && rightHasImports) {
5244
- importMap.imports = composeTwoMappings(leftImports, rightImports);
5245
- } else if (leftHasImports) {
5246
- importMap.imports = { ...leftImports };
5247
- } else if (rightHasImports) {
5248
- importMap.imports = { ...rightImports };
5289
+ const type = typeof value;
5290
+ if (type !== "object") {
5291
+ throw new TypeError(`an importMap must be an object, received ${value}`);
5249
5292
  }
5250
5293
 
5251
- const leftScopes = leftImportMap.scopes;
5252
- const rightScopes = rightImportMap.scopes;
5253
- const leftHasScopes = Boolean(leftScopes);
5254
- const rightHasScopes = Boolean(rightScopes);
5255
- if (leftHasScopes && rightHasScopes) {
5256
- importMap.scopes = composeTwoScopes(
5257
- leftScopes,
5258
- rightScopes,
5259
- importMap.imports || {},
5294
+ if (Array.isArray(value)) {
5295
+ throw new TypeError(
5296
+ `an importMap must be an object, received array ${value}`,
5260
5297
  );
5261
- } else if (leftHasScopes) {
5262
- importMap.scopes = { ...leftScopes };
5263
- } else if (rightHasScopes) {
5264
- importMap.scopes = { ...rightScopes };
5265
5298
  }
5266
-
5267
- return importMap;
5268
5299
  };
5269
5300
 
5270
- const composeTwoMappings = (leftMappings, rightMappings) => {
5271
- const mappings = {};
5301
+ // duplicated from @jsenv/log to avoid the dependency
5302
+ const createDetailedMessage = (message, details = {}) => {
5303
+ let string = `${message}`;
5272
5304
 
5273
- Object.keys(leftMappings).forEach((leftSpecifier) => {
5274
- if (objectHasKey(rightMappings, leftSpecifier)) {
5275
- // will be overidden
5276
- return;
5277
- }
5278
- const leftAddress = leftMappings[leftSpecifier];
5279
- const rightSpecifier = Object.keys(rightMappings).find((rightSpecifier) => {
5280
- return compareAddressAndSpecifier(leftAddress, rightSpecifier);
5281
- });
5282
- mappings[leftSpecifier] = rightSpecifier
5283
- ? rightMappings[rightSpecifier]
5284
- : leftAddress;
5305
+ Object.keys(details).forEach((key) => {
5306
+ const value = details[key];
5307
+ string += `
5308
+ --- ${key} ---
5309
+ ${
5310
+ Array.isArray(value)
5311
+ ? value.join(`
5312
+ `)
5313
+ : value
5314
+ }`;
5285
5315
  });
5286
5316
 
5287
- Object.keys(rightMappings).forEach((rightSpecifier) => {
5288
- mappings[rightSpecifier] = rightMappings[rightSpecifier];
5289
- });
5317
+ return string;
5318
+ };
5290
5319
 
5291
- return mappings;
5320
+ const hasScheme = (string) => {
5321
+ return /^[a-zA-Z]{2,}:/.test(string);
5292
5322
  };
5293
5323
 
5294
- const objectHasKey = (object, key) =>
5295
- Object.prototype.hasOwnProperty.call(object, key);
5324
+ const pathnameToParentPathname = (pathname) => {
5325
+ const slashLastIndex = pathname.lastIndexOf("/");
5326
+ if (slashLastIndex === -1) {
5327
+ return "/";
5328
+ }
5296
5329
 
5297
- const compareAddressAndSpecifier = (address, specifier) => {
5298
- const addressUrl = resolveUrl(address, "file:///");
5299
- const specifierUrl = resolveUrl(specifier, "file:///");
5300
- return addressUrl === specifierUrl;
5330
+ return pathname.slice(0, slashLastIndex + 1);
5301
5331
  };
5302
5332
 
5303
- const composeTwoScopes = (leftScopes, rightScopes, imports) => {
5304
- const scopes = {};
5333
+ const urlToScheme = (urlString) => {
5334
+ const colonIndex = urlString.indexOf(":");
5335
+ if (colonIndex === -1) return "";
5336
+ return urlString.slice(0, colonIndex);
5337
+ };
5305
5338
 
5306
- Object.keys(leftScopes).forEach((leftScopeKey) => {
5307
- if (objectHasKey(rightScopes, leftScopeKey)) {
5308
- // will be merged
5309
- scopes[leftScopeKey] = leftScopes[leftScopeKey];
5310
- return;
5311
- }
5312
- const topLevelSpecifier = Object.keys(imports).find(
5313
- (topLevelSpecifierCandidate) => {
5314
- return compareAddressAndSpecifier(
5315
- leftScopeKey,
5316
- topLevelSpecifierCandidate,
5317
- );
5318
- },
5319
- );
5320
- if (topLevelSpecifier) {
5321
- scopes[imports[topLevelSpecifier]] = leftScopes[leftScopeKey];
5322
- } else {
5323
- scopes[leftScopeKey] = leftScopes[leftScopeKey];
5324
- }
5325
- });
5339
+ const urlToOrigin = (urlString) => {
5340
+ const scheme = urlToScheme(urlString);
5326
5341
 
5327
- Object.keys(rightScopes).forEach((rightScopeKey) => {
5328
- if (objectHasKey(scopes, rightScopeKey)) {
5329
- scopes[rightScopeKey] = composeTwoMappings(
5330
- scopes[rightScopeKey],
5331
- rightScopes[rightScopeKey],
5332
- );
5333
- } else {
5334
- scopes[rightScopeKey] = {
5335
- ...rightScopes[rightScopeKey],
5336
- };
5337
- }
5338
- });
5342
+ if (scheme === "file") {
5343
+ return "file://";
5344
+ }
5339
5345
 
5340
- return scopes;
5341
- };
5346
+ if (scheme === "http" || scheme === "https") {
5347
+ const secondProtocolSlashIndex = scheme.length + "://".length;
5348
+ const pathnameSlashIndex = urlString.indexOf("/", secondProtocolSlashIndex);
5342
5349
 
5343
- const sortImports = (imports) => {
5344
- const mappingsSorted = {};
5350
+ if (pathnameSlashIndex === -1) return urlString;
5351
+ return urlString.slice(0, pathnameSlashIndex);
5352
+ }
5345
5353
 
5346
- Object.keys(imports)
5347
- .sort(compareLengthOrLocaleCompare)
5348
- .forEach((name) => {
5349
- mappingsSorted[name] = imports[name];
5350
- });
5354
+ return urlString.slice(0, scheme.length + 1);
5355
+ };
5351
5356
 
5352
- return mappingsSorted;
5357
+ const urlToPathname = (urlString) => {
5358
+ return ressourceToPathname(urlToRessource(urlString));
5353
5359
  };
5354
5360
 
5355
- const sortScopes = (scopes) => {
5356
- const scopesSorted = {};
5361
+ const urlToRessource = (urlString) => {
5362
+ const scheme = urlToScheme(urlString);
5357
5363
 
5358
- Object.keys(scopes)
5359
- .sort(compareLengthOrLocaleCompare)
5360
- .forEach((scopeSpecifier) => {
5361
- scopesSorted[scopeSpecifier] = sortImports(scopes[scopeSpecifier]);
5362
- });
5364
+ if (scheme === "file") {
5365
+ return urlString.slice("file://".length);
5366
+ }
5367
+
5368
+ if (scheme === "https" || scheme === "http") {
5369
+ // remove origin
5370
+ const afterProtocol = urlString.slice(scheme.length + "://".length);
5371
+ const pathnameSlashIndex = afterProtocol.indexOf("/", "://".length);
5372
+ return afterProtocol.slice(pathnameSlashIndex);
5373
+ }
5363
5374
 
5364
- return scopesSorted;
5375
+ return urlString.slice(scheme.length + 1);
5365
5376
  };
5366
5377
 
5367
- const compareLengthOrLocaleCompare = (a, b) => {
5368
- return b.length - a.length || a.localeCompare(b);
5378
+ const ressourceToPathname = (ressource) => {
5379
+ const searchSeparatorIndex = ressource.indexOf("?");
5380
+ return searchSeparatorIndex === -1
5381
+ ? ressource
5382
+ : ressource.slice(0, searchSeparatorIndex);
5369
5383
  };
5370
5384
 
5371
- const normalizeImportMap = (importMap, baseUrl) => {
5372
- assertImportMap(importMap);
5373
-
5374
- if (!isStringOrUrl(baseUrl)) {
5375
- throw new TypeError(formulateBaseUrlMustBeStringOrUrl({ baseUrl }));
5376
- }
5385
+ // could be useful: https://url.spec.whatwg.org/#url-miscellaneous
5377
5386
 
5378
- const { imports, scopes } = importMap;
5379
5387
 
5380
- return {
5381
- imports: imports ? normalizeMappings(imports, baseUrl) : undefined,
5382
- scopes: scopes ? normalizeScopes(scopes, baseUrl) : undefined,
5383
- };
5384
- };
5388
+ const resolveUrl = (specifier, baseUrl) => {
5389
+ if (baseUrl) {
5390
+ if (typeof baseUrl !== "string") {
5391
+ throw new TypeError(writeBaseUrlMustBeAString({ baseUrl, specifier }));
5392
+ }
5393
+ if (!hasScheme(baseUrl)) {
5394
+ throw new Error(writeBaseUrlMustBeAbsolute({ baseUrl, specifier }));
5395
+ }
5396
+ }
5385
5397
 
5386
- const isStringOrUrl = (value) => {
5387
- if (typeof value === "string") {
5388
- return true;
5398
+ if (hasScheme(specifier)) {
5399
+ return specifier;
5389
5400
  }
5390
5401
 
5391
- if (typeof URL === "function" && value instanceof URL) {
5392
- return true;
5402
+ if (!baseUrl) {
5403
+ throw new Error(writeBaseUrlRequired({ baseUrl, specifier }));
5393
5404
  }
5394
5405
 
5395
- return false;
5396
- };
5406
+ // scheme relative
5407
+ if (specifier.slice(0, 2) === "//") {
5408
+ return `${urlToScheme(baseUrl)}:${specifier}`;
5409
+ }
5397
5410
 
5398
- const normalizeMappings = (mappings, baseUrl) => {
5399
- const mappingsNormalized = {};
5411
+ // origin relative
5412
+ if (specifier[0] === "/") {
5413
+ return `${urlToOrigin(baseUrl)}${specifier}`;
5414
+ }
5400
5415
 
5401
- Object.keys(mappings).forEach((specifier) => {
5402
- const address = mappings[specifier];
5416
+ const baseOrigin = urlToOrigin(baseUrl);
5417
+ const basePathname = urlToPathname(baseUrl);
5403
5418
 
5404
- if (typeof address !== "string") {
5405
- console.warn(
5406
- formulateAddressMustBeAString({
5407
- address,
5408
- specifier,
5409
- }),
5410
- );
5411
- return;
5412
- }
5419
+ if (specifier === ".") {
5420
+ const baseDirectoryPathname = pathnameToParentPathname(basePathname);
5421
+ return `${baseOrigin}${baseDirectoryPathname}`;
5422
+ }
5413
5423
 
5414
- const specifierResolved = resolveSpecifier(specifier, baseUrl) || specifier;
5424
+ // pathname relative inside
5425
+ if (specifier.slice(0, 2) === "./") {
5426
+ const baseDirectoryPathname = pathnameToParentPathname(basePathname);
5427
+ return `${baseOrigin}${baseDirectoryPathname}${specifier.slice(2)}`;
5428
+ }
5415
5429
 
5416
- const addressUrl = tryUrlResolution(address, baseUrl);
5417
- if (addressUrl === null) {
5418
- console.warn(
5419
- formulateAdressResolutionFailed({
5420
- address,
5421
- baseUrl,
5422
- specifier,
5423
- }),
5424
- );
5425
- return;
5426
- }
5430
+ // pathname relative outside
5431
+ if (specifier.slice(0, 3) === "../") {
5432
+ let unresolvedPathname = specifier;
5433
+ const importerFolders = basePathname.split("/");
5434
+ importerFolders.pop();
5427
5435
 
5428
- if (specifier.endsWith("/") && !addressUrl.endsWith("/")) {
5429
- console.warn(
5430
- formulateAddressUrlRequiresTrailingSlash({
5431
- address,
5432
- specifier,
5433
- }),
5434
- );
5435
- return;
5436
+ while (unresolvedPathname.slice(0, 3) === "../") {
5437
+ unresolvedPathname = unresolvedPathname.slice(3);
5438
+ // when there is no folder left to resolved
5439
+ // we just ignore '../'
5440
+ if (importerFolders.length) {
5441
+ importerFolders.pop();
5442
+ }
5436
5443
  }
5437
- mappingsNormalized[specifierResolved] = addressUrl;
5438
- });
5439
5444
 
5440
- return sortImports(mappingsNormalized);
5441
- };
5442
-
5443
- const normalizeScopes = (scopes, baseUrl) => {
5444
- const scopesNormalized = {};
5445
-
5446
- Object.keys(scopes).forEach((scopeSpecifier) => {
5447
- const scopeMappings = scopes[scopeSpecifier];
5448
- const scopeUrl = tryUrlResolution(scopeSpecifier, baseUrl);
5449
- if (scopeUrl === null) {
5450
- console.warn(
5451
- formulateScopeResolutionFailed({
5452
- scope: scopeSpecifier,
5453
- baseUrl,
5454
- }),
5455
- );
5456
- return;
5457
- }
5458
- const scopeValueNormalized = normalizeMappings(scopeMappings, baseUrl);
5459
- scopesNormalized[scopeUrl] = scopeValueNormalized;
5460
- });
5445
+ const resolvedPathname = `${importerFolders.join(
5446
+ "/",
5447
+ )}/${unresolvedPathname}`;
5448
+ return `${baseOrigin}${resolvedPathname}`;
5449
+ }
5461
5450
 
5462
- return sortScopes(scopesNormalized);
5451
+ // bare
5452
+ if (basePathname === "") {
5453
+ return `${baseOrigin}/${specifier}`;
5454
+ }
5455
+ if (basePathname[basePathname.length] === "/") {
5456
+ return `${baseOrigin}${basePathname}${specifier}`;
5457
+ }
5458
+ return `${baseOrigin}${pathnameToParentPathname(basePathname)}${specifier}`;
5463
5459
  };
5464
5460
 
5465
- const formulateBaseUrlMustBeStringOrUrl = ({
5461
+ const writeBaseUrlMustBeAString = ({
5466
5462
  baseUrl,
5467
- }) => `baseUrl must be a string or an url.
5468
- --- base url ---
5469
- ${baseUrl}`;
5470
-
5471
- const formulateAddressMustBeAString = ({
5472
5463
  specifier,
5473
- address,
5474
- }) => `Address must be a string.
5475
- --- address ---
5476
- ${address}
5464
+ }) => `baseUrl must be a string.
5465
+ --- base url ---
5466
+ ${baseUrl}
5477
5467
  --- specifier ---
5478
5468
  ${specifier}`;
5479
5469
 
5480
- const formulateAdressResolutionFailed = ({
5481
- address,
5470
+ const writeBaseUrlMustBeAbsolute = ({
5482
5471
  baseUrl,
5483
5472
  specifier,
5484
- }) => `Address url resolution failed.
5485
- --- address ---
5486
- ${address}
5473
+ }) => `baseUrl must be absolute.
5487
5474
  --- base url ---
5488
5475
  ${baseUrl}
5489
5476
  --- specifier ---
5490
5477
  ${specifier}`;
5491
5478
 
5492
- const formulateAddressUrlRequiresTrailingSlash = ({
5493
- addressURL,
5494
- address,
5479
+ const writeBaseUrlRequired = ({
5480
+ baseUrl,
5495
5481
  specifier,
5496
- }) => `Address must end with /.
5497
- --- address url ---
5498
- ${addressURL}
5499
- --- address ---
5500
- ${address}
5482
+ }) => `baseUrl required to resolve relative specifier.
5483
+ --- base url ---
5484
+ ${baseUrl}
5501
5485
  --- specifier ---
5502
5486
  ${specifier}`;
5503
5487
 
5504
- const formulateScopeResolutionFailed = ({
5505
- scope,
5506
- baseUrl,
5507
- }) => `Scope url resolution failed.
5508
- --- scope ---
5509
- ${scope}
5510
- --- base url ---
5511
- ${baseUrl}`;
5488
+ const tryUrlResolution = (string, url) => {
5489
+ const result = resolveUrl(string, url);
5490
+ return hasScheme(result) ? result : null;
5491
+ };
5512
5492
 
5513
- const pathnameToExtension = (pathname) => {
5514
- const slashLastIndex = pathname.lastIndexOf("/");
5515
- if (slashLastIndex !== -1) {
5516
- pathname = pathname.slice(slashLastIndex + 1);
5493
+ const resolveSpecifier = (specifier, importer) => {
5494
+ if (
5495
+ specifier === "." ||
5496
+ specifier[0] === "/" ||
5497
+ specifier.startsWith("./") ||
5498
+ specifier.startsWith("../")
5499
+ ) {
5500
+ return resolveUrl(specifier, importer);
5501
+ }
5502
+
5503
+ if (hasScheme(specifier)) {
5504
+ return specifier;
5505
+ }
5506
+
5507
+ return null;
5508
+ };
5509
+
5510
+ const applyImportMap = ({
5511
+ importMap,
5512
+ specifier,
5513
+ importer,
5514
+ createBareSpecifierError = ({ specifier, importer }) => {
5515
+ return new Error(
5516
+ createDetailedMessage(`Unmapped bare specifier.`, {
5517
+ specifier,
5518
+ importer,
5519
+ }),
5520
+ );
5521
+ },
5522
+ onImportMapping = () => {},
5523
+ }) => {
5524
+ assertImportMap(importMap);
5525
+ if (typeof specifier !== "string") {
5526
+ throw new TypeError(
5527
+ createDetailedMessage("specifier must be a string.", {
5528
+ specifier,
5529
+ importer,
5530
+ }),
5531
+ );
5532
+ }
5533
+ if (importer) {
5534
+ if (typeof importer !== "string") {
5535
+ throw new TypeError(
5536
+ createDetailedMessage("importer must be a string.", {
5537
+ importer,
5538
+ specifier,
5539
+ }),
5540
+ );
5541
+ }
5542
+ if (!hasScheme(importer)) {
5543
+ throw new Error(
5544
+ createDetailedMessage(`importer must be an absolute url.`, {
5545
+ importer,
5546
+ specifier,
5547
+ }),
5548
+ );
5549
+ }
5517
5550
  }
5518
5551
 
5519
- const dotLastIndex = pathname.lastIndexOf(".");
5520
- if (dotLastIndex === -1) return "";
5521
- // if (dotLastIndex === pathname.length - 1) return ""
5522
- return pathname.slice(dotLastIndex);
5523
- };
5552
+ const specifierUrl = resolveSpecifier(specifier, importer);
5553
+ const specifierNormalized = specifierUrl || specifier;
5554
+
5555
+ const { scopes } = importMap;
5556
+ if (scopes && importer) {
5557
+ const scopeSpecifierMatching = Object.keys(scopes).find(
5558
+ (scopeSpecifier) => {
5559
+ return (
5560
+ scopeSpecifier === importer ||
5561
+ specifierIsPrefixOf(scopeSpecifier, importer)
5562
+ );
5563
+ },
5564
+ );
5565
+ if (scopeSpecifierMatching) {
5566
+ const scopeMappings = scopes[scopeSpecifierMatching];
5567
+ const mappingFromScopes = applyMappings(
5568
+ scopeMappings,
5569
+ specifierNormalized,
5570
+ scopeSpecifierMatching,
5571
+ onImportMapping,
5572
+ );
5573
+ if (mappingFromScopes !== null) {
5574
+ return mappingFromScopes;
5575
+ }
5576
+ }
5577
+ }
5524
5578
 
5525
- const resolveImport = ({
5526
- specifier,
5527
- importer,
5528
- importMap,
5529
- defaultExtension = false,
5530
- createBareSpecifierError,
5531
- onImportMapping = () => {},
5532
- }) => {
5533
- let url;
5534
- if (importMap) {
5535
- url = applyImportMap({
5536
- importMap,
5537
- specifier,
5538
- importer,
5539
- createBareSpecifierError,
5579
+ const { imports } = importMap;
5580
+ if (imports) {
5581
+ const mappingFromImports = applyMappings(
5582
+ imports,
5583
+ specifierNormalized,
5584
+ undefined,
5540
5585
  onImportMapping,
5541
- });
5542
- } else {
5543
- url = resolveUrl(specifier, importer);
5586
+ );
5587
+ if (mappingFromImports !== null) {
5588
+ return mappingFromImports;
5589
+ }
5544
5590
  }
5545
5591
 
5546
- if (defaultExtension) {
5547
- url = applyDefaultExtension({ url, importer, defaultExtension });
5592
+ if (specifierUrl) {
5593
+ return specifierUrl;
5548
5594
  }
5549
5595
 
5550
- return url;
5596
+ throw createBareSpecifierError({ specifier, importer });
5551
5597
  };
5552
5598
 
5553
- const applyDefaultExtension = ({ url, importer, defaultExtension }) => {
5554
- if (urlToPathname(url).endsWith("/")) {
5555
- return url;
5556
- }
5599
+ const applyMappings = (
5600
+ mappings,
5601
+ specifierNormalized,
5602
+ scope,
5603
+ onImportMapping,
5604
+ ) => {
5605
+ const specifierCandidates = Object.keys(mappings);
5557
5606
 
5558
- if (typeof defaultExtension === "string") {
5559
- const extension = pathnameToExtension(url);
5560
- if (extension === "") {
5561
- return `${url}${defaultExtension}`;
5607
+ let i = 0;
5608
+ while (i < specifierCandidates.length) {
5609
+ const specifierCandidate = specifierCandidates[i];
5610
+ i++;
5611
+ if (specifierCandidate === specifierNormalized) {
5612
+ const address = mappings[specifierCandidate];
5613
+ onImportMapping({
5614
+ scope,
5615
+ from: specifierCandidate,
5616
+ to: address,
5617
+ before: specifierNormalized,
5618
+ after: address,
5619
+ });
5620
+ return address;
5562
5621
  }
5563
- return url;
5564
- }
5565
-
5566
- if (defaultExtension === true) {
5567
- const extension = pathnameToExtension(url);
5568
- if (extension === "" && importer) {
5569
- const importerPathname = urlToPathname(importer);
5570
- const importerExtension = pathnameToExtension(importerPathname);
5571
- return `${url}${importerExtension}`;
5622
+ if (specifierIsPrefixOf(specifierCandidate, specifierNormalized)) {
5623
+ const address = mappings[specifierCandidate];
5624
+ const afterSpecifier = specifierNormalized.slice(
5625
+ specifierCandidate.length,
5626
+ );
5627
+ const addressFinal = tryUrlResolution(afterSpecifier, address);
5628
+ onImportMapping({
5629
+ scope,
5630
+ from: specifierCandidate,
5631
+ to: address,
5632
+ before: specifierNormalized,
5633
+ after: addressFinal,
5634
+ });
5635
+ return addressFinal;
5572
5636
  }
5573
5637
  }
5574
5638
 
5575
- return url;
5639
+ return null;
5576
5640
  };
5577
5641
 
5578
- const isEscaped = (i, string) => {
5579
- let backslashBeforeCount = 0;
5580
- while (i--) {
5581
- const previousChar = string[i];
5582
- if (previousChar === "\\") {
5583
- backslashBeforeCount++;
5584
- }
5585
- break;
5642
+ const specifierIsPrefixOf = (specifierHref, href) => {
5643
+ return (
5644
+ specifierHref[specifierHref.length - 1] === "/" &&
5645
+ href.startsWith(specifierHref)
5646
+ );
5647
+ };
5648
+
5649
+ // https://github.com/systemjs/systemjs/blob/89391f92dfeac33919b0223bbf834a1f4eea5750/src/common.js#L136
5650
+
5651
+ const composeTwoImportMaps = (leftImportMap, rightImportMap) => {
5652
+ assertImportMap(leftImportMap);
5653
+ assertImportMap(rightImportMap);
5654
+
5655
+ const importMap = {};
5656
+
5657
+ const leftImports = leftImportMap.imports;
5658
+ const rightImports = rightImportMap.imports;
5659
+ const leftHasImports = Boolean(leftImports);
5660
+ const rightHasImports = Boolean(rightImports);
5661
+ if (leftHasImports && rightHasImports) {
5662
+ importMap.imports = composeTwoMappings(leftImports, rightImports);
5663
+ } else if (leftHasImports) {
5664
+ importMap.imports = { ...leftImports };
5665
+ } else if (rightHasImports) {
5666
+ importMap.imports = { ...rightImports };
5586
5667
  }
5587
- const isEven = backslashBeforeCount % 2 === 0;
5588
- return !isEven;
5668
+
5669
+ const leftScopes = leftImportMap.scopes;
5670
+ const rightScopes = rightImportMap.scopes;
5671
+ const leftHasScopes = Boolean(leftScopes);
5672
+ const rightHasScopes = Boolean(rightScopes);
5673
+ if (leftHasScopes && rightHasScopes) {
5674
+ importMap.scopes = composeTwoScopes(
5675
+ leftScopes,
5676
+ rightScopes,
5677
+ importMap.imports || {},
5678
+ );
5679
+ } else if (leftHasScopes) {
5680
+ importMap.scopes = { ...leftScopes };
5681
+ } else if (rightHasScopes) {
5682
+ importMap.scopes = { ...rightScopes };
5683
+ }
5684
+
5685
+ return importMap;
5589
5686
  };
5590
5687
 
5591
- const JS_QUOTES = {
5592
- pickBest: (string, { canUseTemplateString, defaultQuote = DOUBLE } = {}) => {
5593
- // check default first, once tested do no re-test it
5594
- if (!string.includes(defaultQuote)) {
5595
- return defaultQuote;
5596
- }
5597
- if (defaultQuote !== DOUBLE && !string.includes(DOUBLE)) {
5598
- return DOUBLE;
5599
- }
5600
- if (defaultQuote !== SINGLE && !string.includes(SINGLE)) {
5601
- return SINGLE;
5602
- }
5603
- if (
5604
- canUseTemplateString &&
5605
- defaultQuote !== BACKTICK &&
5606
- !string.includes(BACKTICK)
5607
- ) {
5608
- return BACKTICK;
5609
- }
5610
- return defaultQuote;
5611
- },
5688
+ const composeTwoMappings = (leftMappings, rightMappings) => {
5689
+ const mappings = {};
5612
5690
 
5613
- escapeSpecialChars: (
5614
- string,
5615
- {
5616
- quote = "pickBest",
5617
- canUseTemplateString,
5618
- defaultQuote,
5619
- allowEscapeForVersioning = false,
5620
- },
5621
- ) => {
5622
- quote =
5623
- quote === "pickBest"
5624
- ? JS_QUOTES.pickBest(string, { canUseTemplateString, defaultQuote })
5625
- : quote;
5626
- const replacements = JS_QUOTE_REPLACEMENTS[quote];
5627
- let result = "";
5628
- let last = 0;
5629
- let i = 0;
5630
- while (i < string.length) {
5631
- const char = string[i];
5632
- i++;
5633
- if (isEscaped(i - 1, string)) continue;
5634
- const replacement = replacements[char];
5635
- if (replacement) {
5636
- if (
5637
- allowEscapeForVersioning &&
5638
- char === quote &&
5639
- string.slice(i, i + 6) === "+__v__"
5640
- ) {
5641
- let isVersioningConcatenation = false;
5642
- let j = i + 6; // start after the +
5643
- while (j < string.length) {
5644
- const lookAheadChar = string[j];
5645
- j++;
5646
- if (
5647
- lookAheadChar === "+" &&
5648
- string[j] === quote &&
5649
- !isEscaped(j - 1, string)
5650
- ) {
5651
- isVersioningConcatenation = true;
5652
- break;
5653
- }
5654
- }
5655
- if (isVersioningConcatenation) {
5656
- // it's a concatenation
5657
- // skip until the end of concatenation (the second +)
5658
- // and resume from there
5659
- i = j + 1;
5660
- continue;
5661
- }
5662
- }
5663
- if (last === i - 1) {
5664
- result += replacement;
5665
- } else {
5666
- result += `${string.slice(last, i - 1)}${replacement}`;
5667
- }
5668
- last = i;
5669
- }
5670
- }
5671
- if (last !== string.length) {
5672
- result += string.slice(last);
5691
+ Object.keys(leftMappings).forEach((leftSpecifier) => {
5692
+ if (objectHasKey(rightMappings, leftSpecifier)) {
5693
+ // will be overidden
5694
+ return;
5673
5695
  }
5674
- return `${quote}${result}${quote}`;
5675
- },
5676
- };
5696
+ const leftAddress = leftMappings[leftSpecifier];
5697
+ const rightSpecifier = Object.keys(rightMappings).find((rightSpecifier) => {
5698
+ return compareAddressAndSpecifier(leftAddress, rightSpecifier);
5699
+ });
5700
+ mappings[leftSpecifier] = rightSpecifier
5701
+ ? rightMappings[rightSpecifier]
5702
+ : leftAddress;
5703
+ });
5677
5704
 
5678
- const DOUBLE = `"`;
5679
- const SINGLE = `'`;
5680
- const BACKTICK = "`";
5681
- const lineEndingEscapes = {
5682
- "\n": "\\n",
5683
- "\r": "\\r",
5684
- "\u2028": "\\u2028",
5685
- "\u2029": "\\u2029",
5705
+ Object.keys(rightMappings).forEach((rightSpecifier) => {
5706
+ mappings[rightSpecifier] = rightMappings[rightSpecifier];
5707
+ });
5708
+
5709
+ return mappings;
5686
5710
  };
5687
- const JS_QUOTE_REPLACEMENTS = {
5688
- [DOUBLE]: {
5689
- '"': '\\"',
5690
- ...lineEndingEscapes,
5691
- },
5692
- [SINGLE]: {
5693
- "'": "\\'",
5694
- ...lineEndingEscapes,
5695
- },
5696
- [BACKTICK]: {
5697
- "`": "\\`",
5698
- "$": "\\$",
5699
- },
5711
+
5712
+ const objectHasKey = (object, key) =>
5713
+ Object.prototype.hasOwnProperty.call(object, key);
5714
+
5715
+ const compareAddressAndSpecifier = (address, specifier) => {
5716
+ const addressUrl = resolveUrl(address, "file:///");
5717
+ const specifierUrl = resolveUrl(specifier, "file:///");
5718
+ return addressUrl === specifierUrl;
5700
5719
  };
5701
5720
 
5702
- // https://nodejs.org/api/packages.html#resolving-user-conditions
5703
- const readCustomConditionsFromProcessArgs = () => {
5704
- if (process.env.IGNORE_PACKAGE_CONDITIONS) {
5705
- return [];
5706
- }
5707
- const packageConditions = [];
5708
- for (const arg of process.execArgv) {
5709
- if (arg.includes("-C=")) {
5710
- const packageCondition = arg.slice(0, "-C=".length);
5711
- packageConditions.push(packageCondition);
5721
+ const composeTwoScopes = (leftScopes, rightScopes, imports) => {
5722
+ const scopes = {};
5723
+
5724
+ Object.keys(leftScopes).forEach((leftScopeKey) => {
5725
+ if (objectHasKey(rightScopes, leftScopeKey)) {
5726
+ // will be merged
5727
+ scopes[leftScopeKey] = leftScopes[leftScopeKey];
5728
+ return;
5712
5729
  }
5713
- if (arg.includes("--conditions=")) {
5714
- const packageCondition = arg.slice("--conditions=".length);
5715
- packageConditions.push(packageCondition);
5730
+ const topLevelSpecifier = Object.keys(imports).find(
5731
+ (topLevelSpecifierCandidate) => {
5732
+ return compareAddressAndSpecifier(
5733
+ leftScopeKey,
5734
+ topLevelSpecifierCandidate,
5735
+ );
5736
+ },
5737
+ );
5738
+ if (topLevelSpecifier) {
5739
+ scopes[imports[topLevelSpecifier]] = leftScopes[leftScopeKey];
5740
+ } else {
5741
+ scopes[leftScopeKey] = leftScopes[leftScopeKey];
5716
5742
  }
5717
- }
5718
- return packageConditions;
5743
+ });
5744
+
5745
+ Object.keys(rightScopes).forEach((rightScopeKey) => {
5746
+ if (objectHasKey(scopes, rightScopeKey)) {
5747
+ scopes[rightScopeKey] = composeTwoMappings(
5748
+ scopes[rightScopeKey],
5749
+ rightScopes[rightScopeKey],
5750
+ );
5751
+ } else {
5752
+ scopes[rightScopeKey] = {
5753
+ ...rightScopes[rightScopeKey],
5754
+ };
5755
+ }
5756
+ });
5757
+
5758
+ return scopes;
5719
5759
  };
5720
5760
 
5721
- const asDirectoryUrl = (url) => {
5722
- const { pathname } = new URL(url);
5723
- if (pathname.endsWith("/")) {
5724
- return url;
5725
- }
5726
- return new URL("./", url).href;
5761
+ const sortImports = (imports) => {
5762
+ const mappingsSorted = {};
5763
+
5764
+ Object.keys(imports)
5765
+ .sort(compareLengthOrLocaleCompare)
5766
+ .forEach((name) => {
5767
+ mappingsSorted[name] = imports[name];
5768
+ });
5769
+
5770
+ return mappingsSorted;
5727
5771
  };
5728
5772
 
5729
- const getParentUrl = (url) => {
5730
- if (url.startsWith("file://")) {
5731
- // With node.js new URL('../', 'file:///C:/').href
5732
- // returns "file:///C:/" instead of "file:///"
5733
- const resource = url.slice("file://".length);
5734
- const slashLastIndex = resource.lastIndexOf("/");
5735
- if (slashLastIndex === -1) {
5736
- return url;
5737
- }
5738
- const lastCharIndex = resource.length - 1;
5739
- if (slashLastIndex === lastCharIndex) {
5740
- const slashBeforeLastIndex = resource.lastIndexOf(
5741
- "/",
5742
- slashLastIndex - 1,
5743
- );
5744
- if (slashBeforeLastIndex === -1) {
5745
- return url;
5746
- }
5747
- return `file://${resource.slice(0, slashBeforeLastIndex + 1)}`;
5748
- }
5773
+ const sortScopes = (scopes) => {
5774
+ const scopesSorted = {};
5749
5775
 
5750
- return `file://${resource.slice(0, slashLastIndex + 1)}`;
5776
+ Object.keys(scopes)
5777
+ .sort(compareLengthOrLocaleCompare)
5778
+ .forEach((scopeSpecifier) => {
5779
+ scopesSorted[scopeSpecifier] = sortImports(scopes[scopeSpecifier]);
5780
+ });
5781
+
5782
+ return scopesSorted;
5783
+ };
5784
+
5785
+ const compareLengthOrLocaleCompare = (a, b) => {
5786
+ return b.length - a.length || a.localeCompare(b);
5787
+ };
5788
+
5789
+ const normalizeImportMap = (importMap, baseUrl) => {
5790
+ assertImportMap(importMap);
5791
+
5792
+ if (!isStringOrUrl(baseUrl)) {
5793
+ throw new TypeError(formulateBaseUrlMustBeStringOrUrl({ baseUrl }));
5751
5794
  }
5752
- return new URL(url.endsWith("/") ? "../" : "./", url).href;
5795
+
5796
+ const { imports, scopes } = importMap;
5797
+
5798
+ return {
5799
+ imports: imports ? normalizeMappings(imports, baseUrl) : undefined,
5800
+ scopes: scopes ? normalizeScopes(scopes, baseUrl) : undefined,
5801
+ };
5753
5802
  };
5754
5803
 
5755
- const isValidUrl = (url) => {
5756
- try {
5757
- // eslint-disable-next-line no-new
5758
- new URL(url);
5804
+ const isStringOrUrl = (value) => {
5805
+ if (typeof value === "string") {
5759
5806
  return true;
5760
- } catch {
5761
- return false;
5762
5807
  }
5763
- };
5764
5808
 
5765
- const urlToFilename = (url) => {
5766
- const { pathname } = new URL(url);
5767
- const pathnameBeforeLastSlash = pathname.endsWith("/")
5768
- ? pathname.slice(0, -1)
5769
- : pathname;
5770
- const slashLastIndex = pathnameBeforeLastSlash.lastIndexOf("/");
5771
- const filename =
5772
- slashLastIndex === -1
5773
- ? pathnameBeforeLastSlash
5774
- : pathnameBeforeLastSlash.slice(slashLastIndex + 1);
5775
- return filename;
5776
- };
5809
+ if (typeof URL === "function" && value instanceof URL) {
5810
+ return true;
5811
+ }
5777
5812
 
5778
- const urlToExtension = (url) => {
5779
- const filename = urlToFilename(url);
5780
- const dotLastIndex = filename.lastIndexOf(".");
5781
- if (dotLastIndex === -1) return "";
5782
- // if (dotLastIndex === pathname.length - 1) return ""
5783
- const extension = filename.slice(dotLastIndex);
5784
- return extension;
5813
+ return false;
5785
5814
  };
5786
5815
 
5787
- const defaultLookupPackageScope = (url) => {
5788
- let scopeUrl = asDirectoryUrl(url);
5789
- while (scopeUrl !== "file:///") {
5790
- if (scopeUrl.endsWith("node_modules/")) {
5791
- return null;
5816
+ const normalizeMappings = (mappings, baseUrl) => {
5817
+ const mappingsNormalized = {};
5818
+
5819
+ Object.keys(mappings).forEach((specifier) => {
5820
+ const address = mappings[specifier];
5821
+
5822
+ if (typeof address !== "string") {
5823
+ console.warn(
5824
+ formulateAddressMustBeAString({
5825
+ address,
5826
+ specifier,
5827
+ }),
5828
+ );
5829
+ return;
5792
5830
  }
5793
- const packageJsonUrlObject = new URL("package.json", scopeUrl);
5794
- if (existsSync(packageJsonUrlObject)) {
5795
- return scopeUrl;
5831
+
5832
+ const specifierResolved = resolveSpecifier(specifier, baseUrl) || specifier;
5833
+
5834
+ const addressUrl = tryUrlResolution(address, baseUrl);
5835
+ if (addressUrl === null) {
5836
+ console.warn(
5837
+ formulateAdressResolutionFailed({
5838
+ address,
5839
+ baseUrl,
5840
+ specifier,
5841
+ }),
5842
+ );
5843
+ return;
5844
+ }
5845
+
5846
+ if (specifier.endsWith("/") && !addressUrl.endsWith("/")) {
5847
+ console.warn(
5848
+ formulateAddressUrlRequiresTrailingSlash({
5849
+ address,
5850
+ specifier,
5851
+ }),
5852
+ );
5853
+ return;
5854
+ }
5855
+ mappingsNormalized[specifierResolved] = addressUrl;
5856
+ });
5857
+
5858
+ return sortImports(mappingsNormalized);
5859
+ };
5860
+
5861
+ const normalizeScopes = (scopes, baseUrl) => {
5862
+ const scopesNormalized = {};
5863
+
5864
+ Object.keys(scopes).forEach((scopeSpecifier) => {
5865
+ const scopeMappings = scopes[scopeSpecifier];
5866
+ const scopeUrl = tryUrlResolution(scopeSpecifier, baseUrl);
5867
+ if (scopeUrl === null) {
5868
+ console.warn(
5869
+ formulateScopeResolutionFailed({
5870
+ scope: scopeSpecifier,
5871
+ baseUrl,
5872
+ }),
5873
+ );
5874
+ return;
5796
5875
  }
5797
- scopeUrl = getParentUrl(scopeUrl);
5798
- }
5799
- return null;
5800
- };
5876
+ const scopeValueNormalized = normalizeMappings(scopeMappings, baseUrl);
5877
+ scopesNormalized[scopeUrl] = scopeValueNormalized;
5878
+ });
5801
5879
 
5802
- const defaultReadPackageJson = (packageUrl) => {
5803
- const packageJsonFileUrl = new URL("./package.json", packageUrl);
5804
- let packageJsonFileContentBuffer;
5805
- try {
5806
- packageJsonFileContentBuffer = readFileSync(packageJsonFileUrl, "utf8");
5807
- } catch (e) {
5808
- if (e.code === "ENOENT") {
5809
- return null;
5810
- }
5811
- throw e;
5812
- }
5813
- const packageJsonFileContentString = String(packageJsonFileContentBuffer);
5814
- try {
5815
- const packageJsonFileContentObject = JSON.parse(
5816
- packageJsonFileContentString,
5817
- );
5818
- return packageJsonFileContentObject;
5819
- } catch {
5820
- throw new Error(`Invalid package configuration at ${packageJsonFileUrl}`);
5821
- }
5880
+ return sortScopes(scopesNormalized);
5822
5881
  };
5823
5882
 
5824
- // https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/tools/node_modules/eslint/node_modules/%40babel/core/lib/vendor/import-meta-resolve.js#L2473
5883
+ const formulateBaseUrlMustBeStringOrUrl = ({
5884
+ baseUrl,
5885
+ }) => `baseUrl must be a string or an url.
5886
+ --- base url ---
5887
+ ${baseUrl}`;
5825
5888
 
5826
- const createInvalidModuleSpecifierError = (
5827
- reason,
5889
+ const formulateAddressMustBeAString = ({
5828
5890
  specifier,
5829
- { parentUrl },
5830
- ) => {
5831
- const error = new Error(
5832
- `Invalid module "${specifier}" ${reason} imported from ${fileURLToPath(
5833
- parentUrl,
5834
- )}`,
5835
- );
5836
- error.code = "INVALID_MODULE_SPECIFIER";
5837
- return error;
5838
- };
5891
+ address,
5892
+ }) => `Address must be a string.
5893
+ --- address ---
5894
+ ${address}
5895
+ --- specifier ---
5896
+ ${specifier}`;
5839
5897
 
5840
- const createInvalidPackageTargetError = (
5841
- reason,
5842
- target,
5843
- { parentUrl, packageDirectoryUrl, key, isImport },
5844
- ) => {
5845
- let message;
5846
- if (key === ".") {
5847
- message = `Invalid "exports" main target defined in ${fileURLToPath(
5848
- packageDirectoryUrl,
5849
- )}package.json imported from ${fileURLToPath(parentUrl)}; ${reason}`;
5850
- } else {
5851
- message = `Invalid "${
5852
- isImport ? "imports" : "exports"
5853
- }" target ${JSON.stringify(target)} defined for "${key}" in ${fileURLToPath(
5854
- packageDirectoryUrl,
5855
- )}package.json imported from ${fileURLToPath(parentUrl)}; ${reason}`;
5856
- }
5857
- const error = new Error(message);
5858
- error.code = "INVALID_PACKAGE_TARGET";
5859
- return error;
5860
- };
5898
+ const formulateAdressResolutionFailed = ({
5899
+ address,
5900
+ baseUrl,
5901
+ specifier,
5902
+ }) => `Address url resolution failed.
5903
+ --- address ---
5904
+ ${address}
5905
+ --- base url ---
5906
+ ${baseUrl}
5907
+ --- specifier ---
5908
+ ${specifier}`;
5861
5909
 
5862
- const createPackagePathNotExportedError = (
5863
- subpath,
5864
- { parentUrl, packageDirectoryUrl },
5865
- ) => {
5866
- let message;
5867
- if (subpath === ".") {
5868
- message = `No "exports" main defined in ${fileURLToPath(
5869
- packageDirectoryUrl,
5870
- )}package.json imported from ${fileURLToPath(parentUrl)}`;
5871
- } else {
5872
- message = `Package subpath "${subpath}" is not defined by "exports" in ${fileURLToPath(
5873
- packageDirectoryUrl,
5874
- )}package.json imported from ${fileURLToPath(parentUrl)}`;
5875
- }
5876
- const error = new Error(message);
5877
- error.code = "PACKAGE_PATH_NOT_EXPORTED";
5878
- return error;
5879
- };
5910
+ const formulateAddressUrlRequiresTrailingSlash = ({
5911
+ addressURL,
5912
+ address,
5913
+ specifier,
5914
+ }) => `Address must end with /.
5915
+ --- address url ---
5916
+ ${addressURL}
5917
+ --- address ---
5918
+ ${address}
5919
+ --- specifier ---
5920
+ ${specifier}`;
5880
5921
 
5881
- const createModuleNotFoundError = (specifier, { parentUrl }) => {
5882
- const error = new Error(
5883
- `Cannot find "${specifier}" imported from ${fileURLToPath(parentUrl)}`,
5884
- );
5885
- error.code = "MODULE_NOT_FOUND";
5886
- return error;
5887
- };
5922
+ const formulateScopeResolutionFailed = ({
5923
+ scope,
5924
+ baseUrl,
5925
+ }) => `Scope url resolution failed.
5926
+ --- scope ---
5927
+ ${scope}
5928
+ --- base url ---
5929
+ ${baseUrl}`;
5888
5930
 
5889
- const createPackageImportNotDefinedError = (
5890
- specifier,
5891
- { parentUrl, packageDirectoryUrl },
5892
- ) => {
5893
- const error = new Error(
5894
- `Package import specifier "${specifier}" is not defined in ${fileURLToPath(
5895
- packageDirectoryUrl,
5896
- )}package.json imported from ${fileURLToPath(parentUrl)}`,
5897
- );
5898
- error.code = "PACKAGE_IMPORT_NOT_DEFINED";
5899
- return error;
5900
- };
5931
+ const pathnameToExtension = (pathname) => {
5932
+ const slashLastIndex = pathname.lastIndexOf("/");
5933
+ if (slashLastIndex !== -1) {
5934
+ pathname = pathname.slice(slashLastIndex + 1);
5935
+ }
5901
5936
 
5902
- const isSpecifierForNodeBuiltin = (specifier) => {
5903
- return (
5904
- specifier.startsWith("node:") ||
5905
- NODE_BUILTIN_MODULE_SPECIFIER_SET.has(specifier)
5906
- );
5937
+ const dotLastIndex = pathname.lastIndexOf(".");
5938
+ if (dotLastIndex === -1) return "";
5939
+ // if (dotLastIndex === pathname.length - 1) return ""
5940
+ return pathname.slice(dotLastIndex);
5907
5941
  };
5908
5942
 
5909
- const NODE_BUILTIN_MODULE_SPECIFIER_SET = new Set([
5910
- "assert",
5911
- "assert/strict",
5912
- "async_hooks",
5913
- "buffer",
5914
- "child_process",
5915
- "cluster",
5916
- "console",
5917
- "constants",
5918
- "crypto",
5919
- "diagnostics_channel",
5920
- "dgram",
5921
- "dns",
5922
- "dns/promises",
5923
- "domain",
5924
- "events",
5925
- "fs",
5926
- "fs/promises",
5927
- "_http_agent",
5928
- "_http_client",
5929
- "_http_common",
5930
- "_http_incoming",
5931
- "_http_outgoing",
5932
- "_http_server",
5933
- "http",
5934
- "http2",
5935
- "https",
5936
- "inspector",
5937
- "inspector/promises",
5938
- "module",
5939
- "net",
5940
- "os",
5941
- "path",
5942
- "path/posix",
5943
- "path/win32",
5944
- "perf_hooks",
5945
- "process",
5946
- "punycode",
5947
- "querystring",
5948
- "readline",
5949
- "readline/promises",
5950
- "repl",
5951
- "sea",
5952
- "sqlite",
5953
- "_stream_duplex",
5954
- "_stream_transform",
5955
- "_stream_wrap",
5956
- "_stream_passthrough",
5957
- "_stream_readable",
5958
- "_stream_writable",
5959
- "stream",
5960
- "stream/consumers",
5961
- "stream/promises",
5962
- "stream/web",
5963
- "string_decoder",
5964
- "sys",
5965
- "test",
5966
- "test/reporters",
5967
- "timers",
5968
- "timers/promises",
5969
- "_tls_common",
5970
- "_tls_legacy",
5971
- "_tls_wrap",
5972
- "tls",
5973
- "trace_events",
5974
- "tty",
5975
- "url",
5976
- "util",
5977
- "util/types",
5978
- "v8",
5979
- "vm",
5980
- "wasi",
5981
- "worker_threads",
5982
- "zlib",
5983
- // global is special
5984
- "global",
5985
- ]);
5943
+ const resolveImport = ({
5944
+ specifier,
5945
+ importer,
5946
+ importMap,
5947
+ defaultExtension = false,
5948
+ createBareSpecifierError,
5949
+ onImportMapping = () => {},
5950
+ }) => {
5951
+ let url;
5952
+ if (importMap) {
5953
+ url = applyImportMap({
5954
+ importMap,
5955
+ specifier,
5956
+ importer,
5957
+ createBareSpecifierError,
5958
+ onImportMapping,
5959
+ });
5960
+ } else {
5961
+ url = resolveUrl(specifier, importer);
5962
+ }
5986
5963
 
5987
- /*
5988
- * https://nodejs.org/api/esm.html#resolver-algorithm-specification
5989
- * https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/lib/internal/modules/esm/resolve.js#L1
5990
- * deviations from the spec:
5991
- * - take into account "browser", "module" and "jsnext"
5992
- * - the check for isDirectory -> throw is delayed is descoped to the caller
5993
- * - the call to real path ->
5994
- * delayed to the caller so that we can decide to
5995
- * maintain symlink as facade url when it's outside project directory
5996
- * or use the real path when inside
5997
- */
5964
+ if (defaultExtension) {
5965
+ url = applyDefaultExtension({ url, importer, defaultExtension });
5966
+ }
5998
5967
 
5999
- const applyNodeEsmResolution = ({
6000
- specifier,
6001
- parentUrl,
6002
- conditions = [...readCustomConditionsFromProcessArgs(), "node", "import"],
6003
- lookupPackageScope = defaultLookupPackageScope,
6004
- readPackageJson = defaultReadPackageJson,
6005
- preservesSymlink = false,
6006
- }) => {
6007
- const resolution = applyPackageSpecifierResolution(specifier, {
6008
- parentUrl: String(parentUrl),
6009
- conditions,
6010
- lookupPackageScope,
6011
- readPackageJson,
6012
- preservesSymlink,
6013
- });
6014
- const { url } = resolution;
6015
- if (url.startsWith("file:")) {
6016
- if (url.includes("%2F") || url.includes("%5C")) {
6017
- throw createInvalidModuleSpecifierError(
6018
- `must not include encoded "/" or "\\" characters`,
6019
- specifier,
6020
- {
6021
- parentUrl,
6022
- },
6023
- );
5968
+ return url;
5969
+ };
5970
+
5971
+ const applyDefaultExtension = ({ url, importer, defaultExtension }) => {
5972
+ if (urlToPathname(url).endsWith("/")) {
5973
+ return url;
5974
+ }
5975
+
5976
+ if (typeof defaultExtension === "string") {
5977
+ const extension = pathnameToExtension(url);
5978
+ if (extension === "") {
5979
+ return `${url}${defaultExtension}`;
6024
5980
  }
6025
- return resolution;
5981
+ return url;
6026
5982
  }
6027
- return resolution;
6028
- };
6029
5983
 
6030
- const createResolutionResult = (data) => {
6031
- return data;
5984
+ if (defaultExtension === true) {
5985
+ const extension = pathnameToExtension(url);
5986
+ if (extension === "" && importer) {
5987
+ const importerPathname = urlToPathname(importer);
5988
+ const importerExtension = pathnameToExtension(importerPathname);
5989
+ return `${url}${importerExtension}`;
5990
+ }
5991
+ }
5992
+
5993
+ return url;
6032
5994
  };
6033
5995
 
6034
- const applyPackageSpecifierResolution = (specifier, resolutionContext) => {
6035
- const { parentUrl } = resolutionContext;
6036
- // relative specifier
6037
- if (
6038
- specifier[0] === "/" ||
6039
- specifier.startsWith("./") ||
6040
- specifier.startsWith("../")
6041
- ) {
6042
- if (specifier[0] !== "/") {
6043
- const browserFieldResolution = applyBrowserFieldResolution(
6044
- specifier,
6045
- resolutionContext,
6046
- );
6047
- if (browserFieldResolution) {
6048
- return browserFieldResolution;
6049
- }
5996
+ const isEscaped = (i, string) => {
5997
+ let backslashBeforeCount = 0;
5998
+ while (i--) {
5999
+ const previousChar = string[i];
6000
+ if (previousChar === "\\") {
6001
+ backslashBeforeCount++;
6050
6002
  }
6051
- return createResolutionResult({
6052
- type: "relative_specifier",
6053
- url: new URL(specifier, parentUrl).href,
6054
- });
6055
- }
6056
- if (specifier[0] === "#") {
6057
- return applyPackageImportsResolution(specifier, resolutionContext);
6003
+ break;
6058
6004
  }
6059
- try {
6060
- const urlObject = new URL(specifier);
6061
- if (specifier.startsWith("node:")) {
6062
- return createResolutionResult({
6063
- type: "node_builtin_specifier",
6064
- url: specifier,
6065
- });
6005
+ const isEven = backslashBeforeCount % 2 === 0;
6006
+ return !isEven;
6007
+ };
6008
+
6009
+ const JS_QUOTES = {
6010
+ pickBest: (string, { canUseTemplateString, defaultQuote = DOUBLE } = {}) => {
6011
+ // check default first, once tested do no re-test it
6012
+ if (!string.includes(defaultQuote)) {
6013
+ return defaultQuote;
6066
6014
  }
6067
- return createResolutionResult({
6068
- type: "absolute_specifier",
6069
- url: urlObject.href,
6070
- });
6071
- } catch {
6072
- // bare specifier
6073
- const browserFieldResolution = applyBrowserFieldResolution(
6074
- specifier,
6075
- resolutionContext,
6076
- );
6077
- if (browserFieldResolution) {
6078
- return browserFieldResolution;
6015
+ if (defaultQuote !== DOUBLE && !string.includes(DOUBLE)) {
6016
+ return DOUBLE;
6079
6017
  }
6080
- const packageResolution = applyPackageResolve(specifier, resolutionContext);
6081
- const search = new URL(specifier, "file:///").search;
6082
- if (search && !new URL(packageResolution.url).search) {
6083
- packageResolution.url = `${packageResolution.url}${search}`;
6018
+ if (defaultQuote !== SINGLE && !string.includes(SINGLE)) {
6019
+ return SINGLE;
6084
6020
  }
6085
- return packageResolution;
6086
- }
6021
+ if (
6022
+ canUseTemplateString &&
6023
+ defaultQuote !== BACKTICK &&
6024
+ !string.includes(BACKTICK)
6025
+ ) {
6026
+ return BACKTICK;
6027
+ }
6028
+ return defaultQuote;
6029
+ },
6030
+
6031
+ escapeSpecialChars: (
6032
+ string,
6033
+ {
6034
+ quote = "pickBest",
6035
+ canUseTemplateString,
6036
+ defaultQuote,
6037
+ allowEscapeForVersioning = false,
6038
+ },
6039
+ ) => {
6040
+ quote =
6041
+ quote === "pickBest"
6042
+ ? JS_QUOTES.pickBest(string, { canUseTemplateString, defaultQuote })
6043
+ : quote;
6044
+ const replacements = JS_QUOTE_REPLACEMENTS[quote];
6045
+ let result = "";
6046
+ let last = 0;
6047
+ let i = 0;
6048
+ while (i < string.length) {
6049
+ const char = string[i];
6050
+ i++;
6051
+ if (isEscaped(i - 1, string)) continue;
6052
+ const replacement = replacements[char];
6053
+ if (replacement) {
6054
+ if (
6055
+ allowEscapeForVersioning &&
6056
+ char === quote &&
6057
+ string.slice(i, i + 6) === "+__v__"
6058
+ ) {
6059
+ let isVersioningConcatenation = false;
6060
+ let j = i + 6; // start after the +
6061
+ while (j < string.length) {
6062
+ const lookAheadChar = string[j];
6063
+ j++;
6064
+ if (
6065
+ lookAheadChar === "+" &&
6066
+ string[j] === quote &&
6067
+ !isEscaped(j - 1, string)
6068
+ ) {
6069
+ isVersioningConcatenation = true;
6070
+ break;
6071
+ }
6072
+ }
6073
+ if (isVersioningConcatenation) {
6074
+ // it's a concatenation
6075
+ // skip until the end of concatenation (the second +)
6076
+ // and resume from there
6077
+ i = j + 1;
6078
+ continue;
6079
+ }
6080
+ }
6081
+ if (last === i - 1) {
6082
+ result += replacement;
6083
+ } else {
6084
+ result += `${string.slice(last, i - 1)}${replacement}`;
6085
+ }
6086
+ last = i;
6087
+ }
6088
+ }
6089
+ if (last !== string.length) {
6090
+ result += string.slice(last);
6091
+ }
6092
+ return `${quote}${result}${quote}`;
6093
+ },
6087
6094
  };
6088
6095
 
6089
- const applyBrowserFieldResolution = (specifier, resolutionContext) => {
6090
- const { parentUrl, conditions, lookupPackageScope, readPackageJson } =
6091
- resolutionContext;
6092
- const browserCondition = conditions.includes("browser");
6093
- if (!browserCondition) {
6094
- return null;
6095
- }
6096
- const packageDirectoryUrl = lookupPackageScope(parentUrl);
6097
- if (!packageDirectoryUrl) {
6098
- return null;
6099
- }
6100
- const packageJson = readPackageJson(packageDirectoryUrl);
6101
- if (!packageJson) {
6102
- return null;
6103
- }
6104
- const { browser } = packageJson;
6105
- if (!browser) {
6106
- return null;
6107
- }
6108
- if (typeof browser !== "object") {
6109
- return null;
6096
+ const DOUBLE = `"`;
6097
+ const SINGLE = `'`;
6098
+ const BACKTICK = "`";
6099
+ const lineEndingEscapes = {
6100
+ "\n": "\\n",
6101
+ "\r": "\\r",
6102
+ "\u2028": "\\u2028",
6103
+ "\u2029": "\\u2029",
6104
+ };
6105
+ const JS_QUOTE_REPLACEMENTS = {
6106
+ [DOUBLE]: {
6107
+ '"': '\\"',
6108
+ ...lineEndingEscapes,
6109
+ },
6110
+ [SINGLE]: {
6111
+ "'": "\\'",
6112
+ ...lineEndingEscapes,
6113
+ },
6114
+ [BACKTICK]: {
6115
+ "`": "\\`",
6116
+ "$": "\\$",
6117
+ },
6118
+ };
6119
+
6120
+ // https://nodejs.org/api/packages.html#resolving-user-conditions
6121
+ const readCustomConditionsFromProcessArgs = () => {
6122
+ if (process.env.IGNORE_PACKAGE_CONDITIONS) {
6123
+ return [];
6110
6124
  }
6111
- let url;
6112
- if (specifier.startsWith(".")) {
6113
- const specifierUrl = new URL(specifier, parentUrl).href;
6114
- const specifierRelativeUrl = specifierUrl.slice(packageDirectoryUrl.length);
6115
- const secifierRelativeNotation = `./${specifierRelativeUrl}`;
6116
- const browserMapping = browser[secifierRelativeNotation];
6117
- if (typeof browserMapping === "string") {
6118
- url = new URL(browserMapping, packageDirectoryUrl).href;
6119
- } else if (browserMapping === false) {
6120
- url = `file:///@ignore/${specifierUrl.slice("file:///")}`;
6125
+ const packageConditions = [];
6126
+ for (const arg of process.execArgv) {
6127
+ if (arg.includes("-C=")) {
6128
+ const packageCondition = arg.slice(0, "-C=".length);
6129
+ packageConditions.push(packageCondition);
6121
6130
  }
6122
- } else {
6123
- const browserMapping = browser[specifier];
6124
- if (typeof browserMapping === "string") {
6125
- url = new URL(browserMapping, packageDirectoryUrl).href;
6126
- } else if (browserMapping === false) {
6127
- url = `file:///@ignore/${specifier}`;
6131
+ if (arg.includes("--conditions=")) {
6132
+ const packageCondition = arg.slice("--conditions=".length);
6133
+ packageConditions.push(packageCondition);
6128
6134
  }
6129
6135
  }
6130
- if (url) {
6131
- return createResolutionResult({
6132
- type: "field:browser",
6133
- isMain: true,
6134
- packageDirectoryUrl,
6135
- packageJson,
6136
- url,
6137
- });
6138
- }
6139
- return null;
6136
+ return packageConditions;
6140
6137
  };
6141
6138
 
6142
- const applyPackageImportsResolution = (
6143
- internalSpecifier,
6144
- resolutionContext,
6145
- ) => {
6146
- const { parentUrl, lookupPackageScope, readPackageJson } = resolutionContext;
6147
- if (internalSpecifier === "#" || internalSpecifier.startsWith("#/")) {
6148
- throw createInvalidModuleSpecifierError(
6149
- "not a valid internal imports specifier name",
6150
- internalSpecifier,
6151
- resolutionContext,
6152
- );
6139
+ const asDirectoryUrl = (url) => {
6140
+ const { pathname } = new URL(url);
6141
+ if (pathname.endsWith("/")) {
6142
+ return url;
6153
6143
  }
6154
- const packageDirectoryUrl = lookupPackageScope(parentUrl);
6155
- if (packageDirectoryUrl !== null) {
6156
- const packageJson = readPackageJson(packageDirectoryUrl);
6157
- const { imports } = packageJson;
6158
- if (imports !== null && typeof imports === "object") {
6159
- const resolved = applyPackageImportsExportsResolution(internalSpecifier, {
6160
- ...resolutionContext,
6161
- packageDirectoryUrl,
6162
- packageJson,
6163
- isImport: true,
6164
- });
6165
- if (resolved) {
6166
- return resolved;
6144
+ return new URL("./", url).href;
6145
+ };
6146
+
6147
+ const getParentUrl = (url) => {
6148
+ if (url.startsWith("file://")) {
6149
+ // With node.js new URL('../', 'file:///C:/').href
6150
+ // returns "file:///C:/" instead of "file:///"
6151
+ const resource = url.slice("file://".length);
6152
+ const slashLastIndex = resource.lastIndexOf("/");
6153
+ if (slashLastIndex === -1) {
6154
+ return url;
6155
+ }
6156
+ const lastCharIndex = resource.length - 1;
6157
+ if (slashLastIndex === lastCharIndex) {
6158
+ const slashBeforeLastIndex = resource.lastIndexOf(
6159
+ "/",
6160
+ slashLastIndex - 1,
6161
+ );
6162
+ if (slashBeforeLastIndex === -1) {
6163
+ return url;
6167
6164
  }
6165
+ return `file://${resource.slice(0, slashBeforeLastIndex + 1)}`;
6168
6166
  }
6167
+
6168
+ return `file://${resource.slice(0, slashLastIndex + 1)}`;
6169
6169
  }
6170
- throw createPackageImportNotDefinedError(internalSpecifier, {
6171
- ...resolutionContext,
6172
- packageDirectoryUrl,
6173
- });
6170
+ return new URL(url.endsWith("/") ? "../" : "./", url).href;
6174
6171
  };
6175
6172
 
6176
- const applyPackageResolve = (packageSpecifier, resolutionContext) => {
6177
- const { parentUrl, conditions, readPackageJson, preservesSymlink, isImport } =
6178
- resolutionContext;
6179
- if (packageSpecifier === "") {
6180
- throw new Error("invalid module specifier");
6181
- }
6182
- // "node:" prefixed specifiers always resolve to node builtins
6183
- if (packageSpecifier.startsWith("node:")) {
6184
- return createResolutionResult({
6185
- type: "node_builtin_specifier",
6186
- url: packageSpecifier,
6187
- });
6188
- }
6189
- let { packageName, packageSubpath } = parsePackageSpecifier(packageSpecifier);
6190
- if (
6191
- packageName[0] === "." ||
6192
- packageName.includes("\\") ||
6193
- packageName.includes("%")
6194
- ) {
6195
- throw createInvalidModuleSpecifierError(
6196
- `is not a valid package name`,
6197
- packageName,
6198
- resolutionContext,
6199
- );
6200
- }
6201
- if (isImport && packageSubpath.endsWith("/")) {
6202
- throw new Error("invalid module specifier");
6203
- }
6204
- const questionCharIndex = packageName.indexOf("?");
6205
- if (questionCharIndex > -1) {
6206
- packageName = packageName.slice(0, questionCharIndex);
6207
- }
6208
- const selfResolution = applyPackageSelfResolution(packageSubpath, {
6209
- ...resolutionContext,
6210
- packageName,
6211
- });
6212
- if (selfResolution) {
6213
- return selfResolution;
6173
+ const isValidUrl = (url) => {
6174
+ try {
6175
+ // eslint-disable-next-line no-new
6176
+ new URL(url);
6177
+ return true;
6178
+ } catch {
6179
+ return false;
6214
6180
  }
6215
- let currentUrl = parentUrl;
6216
- while (currentUrl !== "file:///") {
6217
- const packageDirectoryFacadeUrl = new URL(
6218
- `node_modules/${packageName}/`,
6219
- currentUrl,
6220
- ).href;
6221
- if (!existsSync(new URL(packageDirectoryFacadeUrl))) {
6222
- currentUrl = getParentUrl(currentUrl);
6223
- continue;
6181
+ };
6182
+
6183
+ const urlToFilename = (url) => {
6184
+ const { pathname } = new URL(url);
6185
+ const pathnameBeforeLastSlash = pathname.endsWith("/")
6186
+ ? pathname.slice(0, -1)
6187
+ : pathname;
6188
+ const slashLastIndex = pathnameBeforeLastSlash.lastIndexOf("/");
6189
+ const filename =
6190
+ slashLastIndex === -1
6191
+ ? pathnameBeforeLastSlash
6192
+ : pathnameBeforeLastSlash.slice(slashLastIndex + 1);
6193
+ return filename;
6194
+ };
6195
+
6196
+ const urlToExtension = (url) => {
6197
+ const filename = urlToFilename(url);
6198
+ const dotLastIndex = filename.lastIndexOf(".");
6199
+ if (dotLastIndex === -1) return "";
6200
+ // if (dotLastIndex === pathname.length - 1) return ""
6201
+ const extension = filename.slice(dotLastIndex);
6202
+ return extension;
6203
+ };
6204
+
6205
+ const defaultLookupPackageScope = (url) => {
6206
+ let scopeUrl = asDirectoryUrl(url);
6207
+ while (scopeUrl !== "file:///") {
6208
+ if (scopeUrl.endsWith("node_modules/")) {
6209
+ return null;
6224
6210
  }
6225
- const packageDirectoryUrl = preservesSymlink
6226
- ? packageDirectoryFacadeUrl
6227
- : resolvePackageSymlink(packageDirectoryFacadeUrl);
6228
- const packageJson = readPackageJson(packageDirectoryUrl);
6229
- if (packageJson !== null) {
6230
- const { exports } = packageJson;
6231
- if (exports !== null && exports !== undefined) {
6232
- return applyPackageExportsResolution(packageSubpath, {
6233
- ...resolutionContext,
6234
- packageDirectoryUrl,
6235
- packageJson,
6236
- exports,
6237
- });
6238
- }
6211
+ const packageJsonUrlObject = new URL("package.json", scopeUrl);
6212
+ if (existsSync(packageJsonUrlObject)) {
6213
+ return scopeUrl;
6239
6214
  }
6240
- return applyLegacySubpathResolution(packageSubpath, {
6241
- ...resolutionContext,
6242
- packageDirectoryUrl,
6243
- packageJson,
6244
- });
6215
+ scopeUrl = getParentUrl(scopeUrl);
6245
6216
  }
6246
- // Bare builtin names (without "node:" prefix) are valid only if no local package found
6247
- // Local packages always take priority over builtins with the same name
6248
- if (
6249
- conditions.includes("node") &&
6250
- isSpecifierForNodeBuiltin(packageSpecifier)
6251
- ) {
6252
- return createResolutionResult({
6253
- type: "node_builtin_specifier",
6254
- url: `node:${packageSpecifier}`,
6255
- });
6217
+ return null;
6218
+ };
6219
+
6220
+ const defaultReadPackageJson = (packageUrl) => {
6221
+ const packageJsonFileUrl = new URL("./package.json", packageUrl);
6222
+ let packageJsonFileContentBuffer;
6223
+ try {
6224
+ packageJsonFileContentBuffer = readFileSync(packageJsonFileUrl, "utf8");
6225
+ } catch (e) {
6226
+ if (e.code === "ENOENT") {
6227
+ return null;
6228
+ }
6229
+ throw e;
6230
+ }
6231
+ const packageJsonFileContentString = String(packageJsonFileContentBuffer);
6232
+ try {
6233
+ const packageJsonFileContentObject = JSON.parse(
6234
+ packageJsonFileContentString,
6235
+ );
6236
+ return packageJsonFileContentObject;
6237
+ } catch {
6238
+ throw new Error(`Invalid package configuration at ${packageJsonFileUrl}`);
6239
+ }
6240
+ };
6241
+
6242
+ // https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/tools/node_modules/eslint/node_modules/%40babel/core/lib/vendor/import-meta-resolve.js#L2473
6243
+
6244
+ const createInvalidModuleSpecifierError = (
6245
+ reason,
6246
+ specifier,
6247
+ { parentUrl },
6248
+ ) => {
6249
+ const error = new Error(
6250
+ `Invalid module "${specifier}" ${reason} imported from ${fileURLToPath(
6251
+ parentUrl,
6252
+ )}`,
6253
+ );
6254
+ error.code = "INVALID_MODULE_SPECIFIER";
6255
+ return error;
6256
+ };
6257
+
6258
+ const createInvalidPackageTargetError = (
6259
+ reason,
6260
+ target,
6261
+ { parentUrl, packageDirectoryUrl, key, isImport },
6262
+ ) => {
6263
+ let message;
6264
+ if (key === ".") {
6265
+ message = `Invalid "exports" main target defined in ${fileURLToPath(
6266
+ packageDirectoryUrl,
6267
+ )}package.json imported from ${fileURLToPath(parentUrl)}; ${reason}`;
6268
+ } else {
6269
+ message = `Invalid "${
6270
+ isImport ? "imports" : "exports"
6271
+ }" target ${JSON.stringify(target)} defined for "${key}" in ${fileURLToPath(
6272
+ packageDirectoryUrl,
6273
+ )}package.json imported from ${fileURLToPath(parentUrl)}; ${reason}`;
6256
6274
  }
6257
- throw createModuleNotFoundError(packageName, resolutionContext);
6275
+ const error = new Error(message);
6276
+ error.code = "INVALID_PACKAGE_TARGET";
6277
+ return error;
6258
6278
  };
6259
6279
 
6260
- const applyPackageSelfResolution = (packageSubpath, resolutionContext) => {
6261
- const { parentUrl, packageName, lookupPackageScope, readPackageJson } =
6262
- resolutionContext;
6263
- const packageDirectoryUrl = lookupPackageScope(parentUrl);
6264
- if (!packageDirectoryUrl) {
6265
- return undefined;
6266
- }
6267
- const packageJson = readPackageJson(packageDirectoryUrl);
6268
- if (!packageJson) {
6269
- return undefined;
6270
- }
6271
- if (packageJson.name !== packageName) {
6272
- return undefined;
6273
- }
6274
- const { exports } = packageJson;
6275
- if (!exports) {
6276
- const subpathResolution = applyLegacySubpathResolution(packageSubpath, {
6277
- ...resolutionContext,
6280
+ const createPackagePathNotExportedError = (
6281
+ subpath,
6282
+ { parentUrl, packageDirectoryUrl },
6283
+ ) => {
6284
+ let message;
6285
+ if (subpath === ".") {
6286
+ message = `No "exports" main defined in ${fileURLToPath(
6278
6287
  packageDirectoryUrl,
6279
- packageJson,
6280
- });
6281
- if (subpathResolution && subpathResolution.type !== "subpath") {
6282
- return subpathResolution;
6283
- }
6284
- return undefined;
6288
+ )}package.json imported from ${fileURLToPath(parentUrl)}`;
6289
+ } else {
6290
+ message = `Package subpath "${subpath}" is not defined by "exports" in ${fileURLToPath(
6291
+ packageDirectoryUrl,
6292
+ )}package.json imported from ${fileURLToPath(parentUrl)}`;
6285
6293
  }
6286
- return applyPackageExportsResolution(packageSubpath, {
6287
- ...resolutionContext,
6288
- packageDirectoryUrl,
6289
- packageJson,
6290
- });
6294
+ const error = new Error(message);
6295
+ error.code = "PACKAGE_PATH_NOT_EXPORTED";
6296
+ return error;
6291
6297
  };
6292
6298
 
6293
- // https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/lib/internal/modules/esm/resolve.js#L642
6294
- const applyPackageExportsResolution = (packageSubpath, resolutionContext) => {
6295
- if (packageSubpath === ".") {
6296
- const mainExport = applyMainExportResolution(resolutionContext);
6297
- if (!mainExport) {
6298
- throw createPackagePathNotExportedError(
6299
- packageSubpath,
6300
- resolutionContext,
6301
- );
6302
- }
6303
- const resolved = applyPackageTargetResolution(mainExport, {
6304
- ...resolutionContext,
6305
- key: ".",
6306
- });
6307
- if (resolved) {
6308
- return resolved;
6309
- }
6310
- throw createPackagePathNotExportedError(packageSubpath, resolutionContext);
6311
- }
6312
- const packageExportsInfo = readExports(resolutionContext);
6313
- if (
6314
- packageExportsInfo.type === "object" &&
6315
- packageExportsInfo.allKeysAreRelative
6316
- ) {
6317
- const resolved = applyPackageImportsExportsResolution(packageSubpath, {
6318
- ...resolutionContext,
6319
- isImport: false,
6320
- });
6321
- if (resolved) {
6322
- return resolved;
6323
- }
6324
- }
6325
- throw createPackagePathNotExportedError(packageSubpath, resolutionContext);
6299
+ const createModuleNotFoundError = (specifier, { parentUrl }) => {
6300
+ const error = new Error(
6301
+ `Cannot find "${specifier}" imported from ${fileURLToPath(parentUrl)}`,
6302
+ );
6303
+ error.code = "MODULE_NOT_FOUND";
6304
+ return error;
6326
6305
  };
6327
6306
 
6328
- const applyPackageImportsExportsResolution = (matchKey, resolutionContext) => {
6329
- const { packageJson, isImport } = resolutionContext;
6330
- const matchObject = isImport ? packageJson.imports : packageJson.exports;
6307
+ const createPackageImportNotDefinedError = (
6308
+ specifier,
6309
+ { parentUrl, packageDirectoryUrl },
6310
+ ) => {
6311
+ const error = new Error(
6312
+ `Package import specifier "${specifier}" is not defined in ${fileURLToPath(
6313
+ packageDirectoryUrl,
6314
+ )}package.json imported from ${fileURLToPath(parentUrl)}`,
6315
+ );
6316
+ error.code = "PACKAGE_IMPORT_NOT_DEFINED";
6317
+ return error;
6318
+ };
6331
6319
 
6332
- if (!matchKey.includes("*") && matchObject.hasOwnProperty(matchKey)) {
6333
- const target = matchObject[matchKey];
6334
- return applyPackageTargetResolution(target, {
6335
- ...resolutionContext,
6336
- key: matchKey,
6337
- isImport,
6338
- });
6339
- }
6340
- const expansionKeys = Object.keys(matchObject)
6341
- .filter((key) => key.split("*").length === 2)
6342
- .sort(comparePatternKeys);
6343
- for (const expansionKey of expansionKeys) {
6344
- const [patternBase, patternTrailer] = expansionKey.split("*");
6345
- if (matchKey === patternBase) continue;
6346
- if (!matchKey.startsWith(patternBase)) continue;
6347
- if (patternTrailer.length > 0) {
6348
- if (!matchKey.endsWith(patternTrailer)) continue;
6349
- if (matchKey.length < expansionKey.length) continue;
6350
- }
6351
- const target = matchObject[expansionKey];
6352
- const subpath = matchKey.slice(
6353
- patternBase.length,
6354
- matchKey.length - patternTrailer.length,
6355
- );
6356
- return applyPackageTargetResolution(target, {
6357
- ...resolutionContext,
6358
- key: matchKey,
6359
- subpath,
6360
- pattern: true,
6361
- isImport,
6362
- });
6320
+ const isSpecifierForNodeBuiltin = (specifier) => {
6321
+ return (
6322
+ specifier.startsWith("node:") ||
6323
+ NODE_BUILTIN_MODULE_SPECIFIER_SET.has(specifier)
6324
+ );
6325
+ };
6326
+
6327
+ const NODE_BUILTIN_MODULE_SPECIFIER_SET = new Set([
6328
+ "assert",
6329
+ "assert/strict",
6330
+ "async_hooks",
6331
+ "buffer",
6332
+ "child_process",
6333
+ "cluster",
6334
+ "console",
6335
+ "constants",
6336
+ "crypto",
6337
+ "diagnostics_channel",
6338
+ "dgram",
6339
+ "dns",
6340
+ "dns/promises",
6341
+ "domain",
6342
+ "events",
6343
+ "fs",
6344
+ "fs/promises",
6345
+ "_http_agent",
6346
+ "_http_client",
6347
+ "_http_common",
6348
+ "_http_incoming",
6349
+ "_http_outgoing",
6350
+ "_http_server",
6351
+ "http",
6352
+ "http2",
6353
+ "https",
6354
+ "inspector",
6355
+ "inspector/promises",
6356
+ "module",
6357
+ "net",
6358
+ "os",
6359
+ "path",
6360
+ "path/posix",
6361
+ "path/win32",
6362
+ "perf_hooks",
6363
+ "process",
6364
+ "punycode",
6365
+ "querystring",
6366
+ "readline",
6367
+ "readline/promises",
6368
+ "repl",
6369
+ "sea",
6370
+ "sqlite",
6371
+ "_stream_duplex",
6372
+ "_stream_transform",
6373
+ "_stream_wrap",
6374
+ "_stream_passthrough",
6375
+ "_stream_readable",
6376
+ "_stream_writable",
6377
+ "stream",
6378
+ "stream/consumers",
6379
+ "stream/promises",
6380
+ "stream/web",
6381
+ "string_decoder",
6382
+ "sys",
6383
+ "test",
6384
+ "test/reporters",
6385
+ "timers",
6386
+ "timers/promises",
6387
+ "_tls_common",
6388
+ "_tls_legacy",
6389
+ "_tls_wrap",
6390
+ "tls",
6391
+ "trace_events",
6392
+ "tty",
6393
+ "url",
6394
+ "util",
6395
+ "util/types",
6396
+ "v8",
6397
+ "vm",
6398
+ "wasi",
6399
+ "worker_threads",
6400
+ "zlib",
6401
+ // global is special
6402
+ "global",
6403
+ ]);
6404
+
6405
+ /*
6406
+ * https://nodejs.org/api/esm.html#resolver-algorithm-specification
6407
+ * https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/lib/internal/modules/esm/resolve.js#L1
6408
+ * deviations from the spec:
6409
+ * - take into account "browser", "module" and "jsnext"
6410
+ * - the check for isDirectory -> throw is delayed is descoped to the caller
6411
+ * - the call to real path ->
6412
+ * delayed to the caller so that we can decide to
6413
+ * maintain symlink as facade url when it's outside project directory
6414
+ * or use the real path when inside
6415
+ */
6416
+
6417
+ const applyNodeEsmResolution = ({
6418
+ specifier,
6419
+ parentUrl,
6420
+ conditions = [...readCustomConditionsFromProcessArgs(), "node", "import"],
6421
+ lookupPackageScope = defaultLookupPackageScope,
6422
+ readPackageJson = defaultReadPackageJson,
6423
+ preservesSymlink = false,
6424
+ }) => {
6425
+ const resolution = applyPackageSpecifierResolution(specifier, {
6426
+ parentUrl: String(parentUrl),
6427
+ conditions,
6428
+ lookupPackageScope,
6429
+ readPackageJson,
6430
+ preservesSymlink,
6431
+ });
6432
+ const { url } = resolution;
6433
+ if (url.startsWith("file:")) {
6434
+ if (url.includes("%2F") || url.includes("%5C")) {
6435
+ throw createInvalidModuleSpecifierError(
6436
+ `must not include encoded "/" or "\\" characters`,
6437
+ specifier,
6438
+ {
6439
+ parentUrl,
6440
+ },
6441
+ );
6442
+ }
6443
+ return resolution;
6363
6444
  }
6364
- return null;
6445
+ return resolution;
6365
6446
  };
6366
6447
 
6367
- const applyPackageTargetResolution = (target, resolutionContext) => {
6368
- const {
6369
- conditions,
6370
- packageDirectoryUrl,
6371
- packageJson,
6372
- key,
6373
- subpath = "",
6374
- pattern = false,
6375
- isImport = false,
6376
- } = resolutionContext;
6448
+ const createResolutionResult = (data) => {
6449
+ return data;
6450
+ };
6377
6451
 
6378
- if (typeof target === "string") {
6379
- if (pattern === false && subpath !== "" && !target.endsWith("/")) {
6380
- throw new Error("invalid module specifier");
6381
- }
6382
- if (target.startsWith("./")) {
6383
- const targetUrl = new URL(target, packageDirectoryUrl).href;
6384
- if (!targetUrl.startsWith(packageDirectoryUrl)) {
6385
- throw createInvalidPackageTargetError(
6386
- `target must be inside package`,
6387
- target,
6388
- resolutionContext,
6389
- );
6390
- }
6391
- return createResolutionResult({
6392
- type: isImport ? "field:imports" : "field:exports",
6393
- isMain: subpath === "" || subpath === ".",
6394
- packageDirectoryUrl,
6395
- packageJson,
6396
- url: pattern
6397
- ? targetUrl.replaceAll("*", subpath)
6398
- : new URL(subpath, targetUrl).href,
6399
- });
6400
- }
6401
- if (!isImport || target.startsWith("../") || isValidUrl(target)) {
6402
- throw createInvalidPackageTargetError(
6403
- `target must starst with "./"`,
6404
- target,
6452
+ const applyPackageSpecifierResolution = (specifier, resolutionContext) => {
6453
+ const { parentUrl } = resolutionContext;
6454
+ // relative specifier
6455
+ if (
6456
+ specifier[0] === "/" ||
6457
+ specifier.startsWith("./") ||
6458
+ specifier.startsWith("../")
6459
+ ) {
6460
+ if (specifier[0] !== "/") {
6461
+ const browserFieldResolution = applyBrowserFieldResolution(
6462
+ specifier,
6405
6463
  resolutionContext,
6406
6464
  );
6407
- }
6408
- return applyPackageResolve(
6409
- pattern ? target.replaceAll("*", subpath) : `${target}${subpath}`,
6410
- {
6411
- ...resolutionContext,
6412
- parentUrl: packageDirectoryUrl,
6413
- },
6414
- );
6415
- }
6416
- if (Array.isArray(target)) {
6417
- if (target.length === 0) {
6418
- return null;
6419
- }
6420
- let lastResult;
6421
- let i = 0;
6422
- while (i < target.length) {
6423
- const targetValue = target[i];
6424
- i++;
6425
- try {
6426
- const resolved = applyPackageTargetResolution(targetValue, {
6427
- ...resolutionContext,
6428
- key: `${key}[${i}]`,
6429
- subpath,
6430
- pattern,
6431
- isImport,
6432
- });
6433
- if (resolved) {
6434
- return resolved;
6435
- }
6436
- lastResult = resolved;
6437
- } catch (e) {
6438
- if (e.code === "INVALID_PACKAGE_TARGET") {
6439
- continue;
6440
- }
6441
- lastResult = e;
6442
- }
6443
- }
6444
- if (lastResult) {
6445
- throw lastResult;
6446
- }
6447
- return null;
6448
- }
6449
- if (target === null) {
6450
- return null;
6451
- }
6452
- if (typeof target === "object") {
6453
- const keys = Object.keys(target);
6454
- for (const key of keys) {
6455
- if (Number.isInteger(key)) {
6456
- throw new Error("Invalid package configuration");
6457
- }
6458
- let matched;
6459
- if (key === "default") {
6460
- matched = true;
6461
- } else {
6462
- for (const conditionCandidate of conditions) {
6463
- if (conditionCandidate === key) {
6464
- matched = true;
6465
- break;
6466
- }
6467
- if (conditionCandidate.includes("*")) {
6468
- const conditionCandidateRegex = new RegExp(
6469
- `^${conditionCandidate.replace(/\*/g, "(.*)")}$`,
6470
- );
6471
- if (conditionCandidateRegex.test(key)) {
6472
- matched = true;
6473
- break;
6474
- }
6475
- }
6476
- }
6477
- }
6478
- if (matched) {
6479
- const targetValue = target[key];
6480
- const resolved = applyPackageTargetResolution(targetValue, {
6481
- ...resolutionContext,
6482
- key,
6483
- subpath,
6484
- pattern,
6485
- isImport,
6486
- });
6487
- if (resolved) {
6488
- return resolved;
6489
- }
6465
+ if (browserFieldResolution) {
6466
+ return browserFieldResolution;
6490
6467
  }
6491
6468
  }
6492
- return null;
6493
- }
6494
- throw createInvalidPackageTargetError(
6495
- `target must be a string, array, object or null`,
6496
- target,
6497
- resolutionContext,
6498
- );
6499
- };
6500
-
6501
- const readExports = ({ packageDirectoryUrl, packageJson }) => {
6502
- const packageExports = packageJson.exports;
6503
- if (Array.isArray(packageExports)) {
6504
- return {
6505
- type: "array",
6506
- };
6507
- }
6508
- if (packageExports === null) {
6509
- return {};
6510
- }
6511
- if (typeof packageExports === "object") {
6512
- const keys = Object.keys(packageExports);
6513
- const relativeKeys = [];
6514
- const conditionalKeys = [];
6515
- keys.forEach((availableKey) => {
6516
- if (availableKey.startsWith(".")) {
6517
- relativeKeys.push(availableKey);
6518
- } else {
6519
- conditionalKeys.push(availableKey);
6520
- }
6469
+ return createResolutionResult({
6470
+ type: "relative_specifier",
6471
+ url: new URL(specifier, parentUrl).href,
6521
6472
  });
6522
- const hasRelativeKey = relativeKeys.length > 0;
6523
- if (hasRelativeKey && conditionalKeys.length > 0) {
6524
- throw new Error(
6525
- `Invalid package configuration: cannot mix relative and conditional keys in package.exports
6526
- --- unexpected keys ---
6527
- ${conditionalKeys.map((key) => `"${key}"`).join("\n")}
6528
- --- package directory url ---
6529
- ${packageDirectoryUrl}`,
6530
- );
6531
- }
6532
- return {
6533
- type: "object",
6534
- hasRelativeKey,
6535
- allKeysAreRelative: relativeKeys.length === keys.length,
6536
- };
6537
6473
  }
6538
- if (typeof packageExports === "string") {
6539
- return { type: "string" };
6474
+ if (specifier[0] === "#") {
6475
+ return applyPackageImportsResolution(specifier, resolutionContext);
6540
6476
  }
6541
- return {};
6542
- };
6543
-
6544
- const parsePackageSpecifier = (packageSpecifier) => {
6545
- if (packageSpecifier[0] === "@") {
6546
- const firstSlashIndex = packageSpecifier.indexOf("/");
6547
- if (firstSlashIndex === -1) {
6548
- throw new Error("invalid module specifier");
6549
- }
6550
- const secondSlashIndex = packageSpecifier.indexOf("/", firstSlashIndex + 1);
6551
- if (secondSlashIndex === -1) {
6552
- return {
6553
- packageName: packageSpecifier,
6554
- packageSubpath: ".",
6555
- isScoped: true,
6556
- };
6477
+ try {
6478
+ const urlObject = new URL(specifier);
6479
+ if (specifier.startsWith("node:")) {
6480
+ return createResolutionResult({
6481
+ type: "node_builtin_specifier",
6482
+ url: specifier,
6483
+ });
6557
6484
  }
6558
- const packageName = packageSpecifier.slice(0, secondSlashIndex);
6559
- const afterSecondSlash = packageSpecifier.slice(secondSlashIndex + 1);
6560
- const packageSubpath = `./${afterSecondSlash}`;
6561
- return {
6562
- packageName,
6563
- packageSubpath,
6564
- isScoped: true,
6565
- };
6566
- }
6567
- const firstSlashIndex = packageSpecifier.indexOf("/");
6568
- if (firstSlashIndex === -1) {
6569
- return {
6570
- packageName: packageSpecifier,
6571
- packageSubpath: ".",
6572
- };
6573
- }
6574
- const packageName = packageSpecifier.slice(0, firstSlashIndex);
6575
- const afterFirstSlash = packageSpecifier.slice(firstSlashIndex + 1);
6576
- if (afterFirstSlash === "") {
6577
- return {
6578
- packageName,
6579
- packageSubpath: "/",
6580
- };
6485
+ return createResolutionResult({
6486
+ type: "absolute_specifier",
6487
+ url: urlObject.href,
6488
+ });
6489
+ } catch {
6490
+ // bare specifier
6491
+ const browserFieldResolution = applyBrowserFieldResolution(
6492
+ specifier,
6493
+ resolutionContext,
6494
+ );
6495
+ if (browserFieldResolution) {
6496
+ return browserFieldResolution;
6497
+ }
6498
+ const packageResolution = applyPackageResolve(specifier, resolutionContext);
6499
+ const search = new URL(specifier, "file:///").search;
6500
+ if (search && !new URL(packageResolution.url).search) {
6501
+ packageResolution.url = `${packageResolution.url}${search}`;
6502
+ }
6503
+ return packageResolution;
6581
6504
  }
6582
- const packageSubpath = `./${afterFirstSlash}`;
6583
- return {
6584
- packageName,
6585
- packageSubpath,
6586
- };
6587
6505
  };
6588
6506
 
6589
- const applyMainExportResolution = (resolutionContext) => {
6590
- const { packageJson } = resolutionContext;
6591
- const packageExportsInfo = readExports(resolutionContext);
6592
- if (
6593
- packageExportsInfo.type === "array" ||
6594
- packageExportsInfo.type === "string"
6595
- ) {
6596
- return packageJson.exports;
6507
+ const applyBrowserFieldResolution = (specifier, resolutionContext) => {
6508
+ const { parentUrl, conditions, lookupPackageScope, readPackageJson } =
6509
+ resolutionContext;
6510
+ const browserCondition = conditions.includes("browser");
6511
+ if (!browserCondition) {
6512
+ return null;
6597
6513
  }
6598
- if (packageExportsInfo.type === "object") {
6599
- if (packageExportsInfo.hasRelativeKey) {
6600
- return packageJson.exports["."];
6601
- }
6602
- return packageJson.exports;
6514
+ const packageDirectoryUrl = lookupPackageScope(parentUrl);
6515
+ if (!packageDirectoryUrl) {
6516
+ return null;
6603
6517
  }
6604
- return undefined;
6605
- };
6606
-
6607
- const applyLegacySubpathResolution = (packageSubpath, resolutionContext) => {
6608
- const { packageDirectoryUrl, packageJson } = resolutionContext;
6609
-
6610
- if (packageSubpath === ".") {
6611
- return applyLegacyMainResolution(packageSubpath, resolutionContext);
6518
+ const packageJson = readPackageJson(packageDirectoryUrl);
6519
+ if (!packageJson) {
6520
+ return null;
6612
6521
  }
6613
- const browserFieldResolution = applyBrowserFieldResolution(
6614
- packageSubpath,
6615
- resolutionContext,
6616
- );
6617
- if (browserFieldResolution) {
6618
- return browserFieldResolution;
6522
+ const { browser } = packageJson;
6523
+ if (!browser) {
6524
+ return null;
6619
6525
  }
6620
- return createResolutionResult({
6621
- type: "subpath",
6622
- isMain: packageSubpath === ".",
6623
- packageDirectoryUrl,
6624
- packageJson,
6625
- url: new URL(packageSubpath, packageDirectoryUrl).href,
6626
- });
6526
+ if (typeof browser !== "object") {
6527
+ return null;
6528
+ }
6529
+ let url;
6530
+ if (specifier.startsWith(".")) {
6531
+ const specifierUrl = new URL(specifier, parentUrl).href;
6532
+ const specifierRelativeUrl = specifierUrl.slice(packageDirectoryUrl.length);
6533
+ const secifierRelativeNotation = `./${specifierRelativeUrl}`;
6534
+ const browserMapping = browser[secifierRelativeNotation];
6535
+ if (typeof browserMapping === "string") {
6536
+ url = new URL(browserMapping, packageDirectoryUrl).href;
6537
+ } else if (browserMapping === false) {
6538
+ url = `file:///@ignore/${specifierUrl.slice("file:///")}`;
6539
+ }
6540
+ } else {
6541
+ const browserMapping = browser[specifier];
6542
+ if (typeof browserMapping === "string") {
6543
+ url = new URL(browserMapping, packageDirectoryUrl).href;
6544
+ } else if (browserMapping === false) {
6545
+ url = `file:///@ignore/${specifier}`;
6546
+ }
6547
+ }
6548
+ if (url) {
6549
+ return createResolutionResult({
6550
+ type: "field:browser",
6551
+ isMain: true,
6552
+ packageDirectoryUrl,
6553
+ packageJson,
6554
+ url,
6555
+ });
6556
+ }
6557
+ return null;
6627
6558
  };
6628
6559
 
6629
- const applyLegacyMainResolution = (packageSubpath, resolutionContext) => {
6630
- const { conditions, packageDirectoryUrl, packageJson } = resolutionContext;
6631
- if (packageJson) {
6632
- for (const condition of conditions) {
6633
- const conditionResolver = mainLegacyResolvers[condition];
6634
- if (!conditionResolver) {
6635
- continue;
6636
- }
6637
- const resolved = conditionResolver(resolutionContext);
6560
+ const applyPackageImportsResolution = (
6561
+ internalSpecifier,
6562
+ resolutionContext,
6563
+ ) => {
6564
+ const { parentUrl, lookupPackageScope, readPackageJson } = resolutionContext;
6565
+ if (internalSpecifier === "#" || internalSpecifier.startsWith("#/")) {
6566
+ throw createInvalidModuleSpecifierError(
6567
+ "not a valid internal imports specifier name",
6568
+ internalSpecifier,
6569
+ resolutionContext,
6570
+ );
6571
+ }
6572
+ const packageDirectoryUrl = lookupPackageScope(parentUrl);
6573
+ if (packageDirectoryUrl !== null) {
6574
+ const packageJson = readPackageJson(packageDirectoryUrl);
6575
+ const { imports } = packageJson;
6576
+ if (imports !== null && typeof imports === "object") {
6577
+ const resolved = applyPackageImportsExportsResolution(internalSpecifier, {
6578
+ ...resolutionContext,
6579
+ packageDirectoryUrl,
6580
+ packageJson,
6581
+ isImport: true,
6582
+ });
6638
6583
  if (resolved) {
6639
- return createResolutionResult({
6640
- type: resolved.type,
6641
- isMain: resolved.isMain,
6642
- packageDirectoryUrl,
6643
- packageJson,
6644
- url: new URL(resolved.path, packageDirectoryUrl).href,
6645
- });
6584
+ return resolved;
6646
6585
  }
6647
6586
  }
6648
6587
  }
6649
- return createResolutionResult({
6650
- type: "field:main", // the absence of "main" field
6651
- isMain: true,
6588
+ throw createPackageImportNotDefinedError(internalSpecifier, {
6589
+ ...resolutionContext,
6652
6590
  packageDirectoryUrl,
6653
- packageJson,
6654
- url: new URL("index.js", packageDirectoryUrl).href,
6655
6591
  });
6656
6592
  };
6657
- const mainLegacyResolvers = {
6658
- import: ({ packageJson }) => {
6659
- if (typeof packageJson.module === "string") {
6660
- return { type: "field:module", isMain: true, path: packageJson.module };
6661
- }
6662
- if (typeof packageJson.jsnext === "string") {
6663
- return { type: "field:jsnext", isMain: true, path: packageJson.jsnext };
6664
- }
6665
- if (typeof packageJson.main === "string") {
6666
- return { type: "field:main", isMain: true, path: packageJson.main };
6667
- }
6668
- return null;
6669
- },
6670
- browser: ({ packageDirectoryUrl, packageJson }) => {
6671
- const browserMain = (() => {
6672
- if (typeof packageJson.browser === "string") {
6673
- return packageJson.browser;
6674
- }
6675
- if (
6676
- typeof packageJson.browser === "object" &&
6677
- packageJson.browser !== null
6678
- ) {
6679
- return packageJson.browser["."];
6680
- }
6681
- return "";
6682
- })();
6683
6593
 
6684
- if (!browserMain) {
6685
- if (typeof packageJson.module === "string") {
6686
- return {
6687
- type: "field:module",
6688
- isMain: true,
6689
- path: packageJson.module,
6690
- };
6691
- }
6692
- return null;
6693
- }
6694
- if (
6695
- typeof packageJson.module !== "string" ||
6696
- packageJson.module === browserMain
6697
- ) {
6698
- return {
6699
- type: "field:browser",
6700
- isMain: true,
6701
- path: browserMain,
6702
- };
6703
- }
6704
- const browserMainUrlObject = new URL(browserMain, packageDirectoryUrl);
6705
- const content = readFileSync(browserMainUrlObject, "utf-8");
6706
- if (
6707
- (/typeof exports\s*==/.test(content) &&
6708
- /typeof module\s*==/.test(content)) ||
6709
- /module\.exports\s*=/.test(content)
6710
- ) {
6711
- return {
6712
- type: "field:module",
6713
- isMain: true,
6714
- path: packageJson.module,
6715
- };
6716
- }
6717
- return {
6718
- type: "field:browser",
6719
- isMain: true,
6720
- path: browserMain,
6721
- };
6722
- },
6723
- node: ({ packageJson, conditions }) => {
6724
- if (conditions.includes("import") && !conditions.includes("require")) {
6725
- if (typeof packageJson.module === "string") {
6726
- return { type: "field:module", isMain: true, path: packageJson.module };
6727
- }
6728
- if (typeof packageJson.jsnext === "string") {
6729
- return { type: "field:jsnext", isMain: true, path: packageJson.jsnext };
6730
- }
6594
+ const applyPackageResolve = (packageSpecifier, resolutionContext) => {
6595
+ const { parentUrl, conditions, readPackageJson, preservesSymlink, isImport } =
6596
+ resolutionContext;
6597
+ if (packageSpecifier === "") {
6598
+ throw new Error("invalid module specifier");
6599
+ }
6600
+ // "node:" prefixed specifiers always resolve to node builtins
6601
+ if (packageSpecifier.startsWith("node:")) {
6602
+ return createResolutionResult({
6603
+ type: "node_builtin_specifier",
6604
+ url: packageSpecifier,
6605
+ });
6606
+ }
6607
+ let { packageName, packageSubpath } = parsePackageSpecifier(packageSpecifier);
6608
+ if (
6609
+ packageName[0] === "." ||
6610
+ packageName.includes("\\") ||
6611
+ packageName.includes("%")
6612
+ ) {
6613
+ throw createInvalidModuleSpecifierError(
6614
+ `is not a valid package name`,
6615
+ packageName,
6616
+ resolutionContext,
6617
+ );
6618
+ }
6619
+ if (isImport && packageSubpath.endsWith("/")) {
6620
+ throw new Error("invalid module specifier");
6621
+ }
6622
+ const questionCharIndex = packageName.indexOf("?");
6623
+ if (questionCharIndex > -1) {
6624
+ packageName = packageName.slice(0, questionCharIndex);
6625
+ }
6626
+ const selfResolution = applyPackageSelfResolution(packageSubpath, {
6627
+ ...resolutionContext,
6628
+ packageName,
6629
+ });
6630
+ if (selfResolution) {
6631
+ return selfResolution;
6632
+ }
6633
+ let currentUrl = parentUrl;
6634
+ while (currentUrl !== "file:///") {
6635
+ const packageDirectoryFacadeUrl = new URL(
6636
+ `node_modules/${packageName}/`,
6637
+ currentUrl,
6638
+ ).href;
6639
+ if (!existsSync(new URL(packageDirectoryFacadeUrl))) {
6640
+ currentUrl = getParentUrl(currentUrl);
6641
+ continue;
6731
6642
  }
6732
- if (typeof packageJson.main === "string") {
6733
- return { type: "field:main", isMain: true, path: packageJson.main };
6643
+ const packageDirectoryUrl = preservesSymlink
6644
+ ? packageDirectoryFacadeUrl
6645
+ : resolvePackageSymlink(packageDirectoryFacadeUrl);
6646
+ const packageJson = readPackageJson(packageDirectoryUrl);
6647
+ if (packageJson !== null) {
6648
+ const { exports } = packageJson;
6649
+ if (exports !== null && exports !== undefined) {
6650
+ return applyPackageExportsResolution(packageSubpath, {
6651
+ ...resolutionContext,
6652
+ packageDirectoryUrl,
6653
+ packageJson,
6654
+ exports,
6655
+ });
6656
+ }
6734
6657
  }
6735
- return null;
6736
- },
6737
- };
6738
- mainLegacyResolvers.require = mainLegacyResolvers.node;
6739
-
6740
- const comparePatternKeys = (keyA, keyB) => {
6741
- if (!keyA.endsWith("/") && !keyA.includes("*")) {
6742
- throw new Error("Invalid package configuration");
6658
+ return applyLegacySubpathResolution(packageSubpath, {
6659
+ ...resolutionContext,
6660
+ packageDirectoryUrl,
6661
+ packageJson,
6662
+ });
6743
6663
  }
6744
- if (!keyB.endsWith("/") && !keyB.includes("*")) {
6745
- throw new Error("Invalid package configuration");
6664
+ // Bare builtin names (without "node:" prefix) are valid only if no local package found
6665
+ // Local packages always take priority over builtins with the same name
6666
+ if (
6667
+ conditions.includes("node") &&
6668
+ isSpecifierForNodeBuiltin(packageSpecifier)
6669
+ ) {
6670
+ return createResolutionResult({
6671
+ type: "node_builtin_specifier",
6672
+ url: `node:${packageSpecifier}`,
6673
+ });
6746
6674
  }
6747
- const aStarIndex = keyA.indexOf("*");
6748
- const baseLengthA = aStarIndex > -1 ? aStarIndex + 1 : keyA.length;
6749
- const bStarIndex = keyB.indexOf("*");
6750
- const baseLengthB = bStarIndex > -1 ? bStarIndex + 1 : keyB.length;
6751
- if (baseLengthA > baseLengthB) {
6752
- return -1;
6675
+ throw createModuleNotFoundError(packageName, resolutionContext);
6676
+ };
6677
+
6678
+ const applyPackageSelfResolution = (packageSubpath, resolutionContext) => {
6679
+ const { parentUrl, packageName, lookupPackageScope, readPackageJson } =
6680
+ resolutionContext;
6681
+ const packageDirectoryUrl = lookupPackageScope(parentUrl);
6682
+ if (!packageDirectoryUrl) {
6683
+ return undefined;
6753
6684
  }
6754
- if (baseLengthB > baseLengthA) {
6755
- return 1;
6685
+ const packageJson = readPackageJson(packageDirectoryUrl);
6686
+ if (!packageJson) {
6687
+ return undefined;
6756
6688
  }
6757
- if (aStarIndex === -1) {
6758
- return 1;
6689
+ if (packageJson.name !== packageName) {
6690
+ return undefined;
6759
6691
  }
6760
- if (bStarIndex === -1) {
6761
- return -1;
6692
+ const { exports } = packageJson;
6693
+ if (!exports) {
6694
+ const subpathResolution = applyLegacySubpathResolution(packageSubpath, {
6695
+ ...resolutionContext,
6696
+ packageDirectoryUrl,
6697
+ packageJson,
6698
+ });
6699
+ if (subpathResolution && subpathResolution.type !== "subpath") {
6700
+ return subpathResolution;
6701
+ }
6702
+ return undefined;
6762
6703
  }
6763
- if (keyA.length > keyB.length) {
6764
- return -1;
6704
+ return applyPackageExportsResolution(packageSubpath, {
6705
+ ...resolutionContext,
6706
+ packageDirectoryUrl,
6707
+ packageJson,
6708
+ });
6709
+ };
6710
+
6711
+ // https://github.com/nodejs/node/blob/0367b5c35ea0f98b323175a4aaa8e651af7a91e7/lib/internal/modules/esm/resolve.js#L642
6712
+ const applyPackageExportsResolution = (packageSubpath, resolutionContext) => {
6713
+ if (packageSubpath === ".") {
6714
+ const mainExport = applyMainExportResolution(resolutionContext);
6715
+ if (!mainExport) {
6716
+ throw createPackagePathNotExportedError(
6717
+ packageSubpath,
6718
+ resolutionContext,
6719
+ );
6720
+ }
6721
+ const resolved = applyPackageTargetResolution(mainExport, {
6722
+ ...resolutionContext,
6723
+ key: ".",
6724
+ });
6725
+ if (resolved) {
6726
+ return resolved;
6727
+ }
6728
+ throw createPackagePathNotExportedError(packageSubpath, resolutionContext);
6765
6729
  }
6766
- if (keyB.length > keyA.length) {
6767
- return 1;
6730
+ const packageExportsInfo = readExports(resolutionContext);
6731
+ if (
6732
+ packageExportsInfo.type === "object" &&
6733
+ packageExportsInfo.allKeysAreRelative
6734
+ ) {
6735
+ const resolved = applyPackageImportsExportsResolution(packageSubpath, {
6736
+ ...resolutionContext,
6737
+ isImport: false,
6738
+ });
6739
+ if (resolved) {
6740
+ return resolved;
6741
+ }
6768
6742
  }
6769
- return 0;
6743
+ throw createPackagePathNotExportedError(packageSubpath, resolutionContext);
6770
6744
  };
6771
6745
 
6772
- const resolvePackageSymlink = (packageDirectoryUrl) => {
6773
- const packageDirectoryPath = realpathSync(new URL(packageDirectoryUrl));
6774
- const packageDirectoryResolvedUrl = pathToFileURL(packageDirectoryPath).href;
6775
- return `${packageDirectoryResolvedUrl}/`;
6746
+ const applyPackageImportsExportsResolution = (matchKey, resolutionContext) => {
6747
+ const { packageJson, isImport } = resolutionContext;
6748
+ const matchObject = isImport ? packageJson.imports : packageJson.exports;
6749
+
6750
+ if (!matchKey.includes("*") && matchObject.hasOwnProperty(matchKey)) {
6751
+ const target = matchObject[matchKey];
6752
+ return applyPackageTargetResolution(target, {
6753
+ ...resolutionContext,
6754
+ key: matchKey,
6755
+ isImport,
6756
+ });
6757
+ }
6758
+ const expansionKeys = Object.keys(matchObject)
6759
+ .filter((key) => key.split("*").length === 2)
6760
+ .sort(comparePatternKeys);
6761
+ for (const expansionKey of expansionKeys) {
6762
+ const [patternBase, patternTrailer] = expansionKey.split("*");
6763
+ if (matchKey === patternBase) continue;
6764
+ if (!matchKey.startsWith(patternBase)) continue;
6765
+ if (patternTrailer.length > 0) {
6766
+ if (!matchKey.endsWith(patternTrailer)) continue;
6767
+ if (matchKey.length < expansionKey.length) continue;
6768
+ }
6769
+ const target = matchObject[expansionKey];
6770
+ const subpath = matchKey.slice(
6771
+ patternBase.length,
6772
+ matchKey.length - patternTrailer.length,
6773
+ );
6774
+ return applyPackageTargetResolution(target, {
6775
+ ...resolutionContext,
6776
+ key: matchKey,
6777
+ subpath,
6778
+ pattern: true,
6779
+ isImport,
6780
+ });
6781
+ }
6782
+ return null;
6776
6783
  };
6777
6784
 
6778
- const applyFileSystemMagicResolution = (
6779
- fileUrl,
6780
- { fileStat, magicDirectoryIndex, magicExtensions },
6781
- ) => {
6782
- const result = {
6783
- stat: null,
6784
- url: fileUrl,
6785
- magicExtension: "",
6786
- magicDirectoryIndex: false,
6787
- lastENOENTError: null,
6788
- };
6785
+ const applyPackageTargetResolution = (target, resolutionContext) => {
6786
+ const {
6787
+ conditions,
6788
+ packageDirectoryUrl,
6789
+ packageJson,
6790
+ key,
6791
+ subpath = "",
6792
+ pattern = false,
6793
+ isImport = false,
6794
+ } = resolutionContext;
6789
6795
 
6790
- if (fileStat === undefined) {
6791
- try {
6792
- fileStat = readEntryStatSync(new URL(fileUrl));
6793
- } catch (e) {
6794
- if (e.code === "ENOENT") {
6795
- result.lastENOENTError = e;
6796
- fileStat = null;
6797
- } else {
6798
- throw e;
6796
+ if (typeof target === "string") {
6797
+ if (pattern === false && subpath !== "" && !target.endsWith("/")) {
6798
+ throw new Error("invalid module specifier");
6799
+ }
6800
+ if (target.startsWith("./")) {
6801
+ const targetUrl = new URL(target, packageDirectoryUrl).href;
6802
+ if (!targetUrl.startsWith(packageDirectoryUrl)) {
6803
+ throw createInvalidPackageTargetError(
6804
+ `target must be inside package`,
6805
+ target,
6806
+ resolutionContext,
6807
+ );
6808
+ }
6809
+ return createResolutionResult({
6810
+ type: isImport ? "field:imports" : "field:exports",
6811
+ isMain: subpath === "" || subpath === ".",
6812
+ packageDirectoryUrl,
6813
+ packageJson,
6814
+ url: pattern
6815
+ ? targetUrl.replaceAll("*", subpath)
6816
+ : new URL(subpath, targetUrl).href,
6817
+ });
6818
+ }
6819
+ if (!isImport || target.startsWith("../") || isValidUrl(target)) {
6820
+ throw createInvalidPackageTargetError(
6821
+ `target must starst with "./"`,
6822
+ target,
6823
+ resolutionContext,
6824
+ );
6825
+ }
6826
+ return applyPackageResolve(
6827
+ pattern ? target.replaceAll("*", subpath) : `${target}${subpath}`,
6828
+ {
6829
+ ...resolutionContext,
6830
+ parentUrl: packageDirectoryUrl,
6831
+ },
6832
+ );
6833
+ }
6834
+ if (Array.isArray(target)) {
6835
+ if (target.length === 0) {
6836
+ return null;
6837
+ }
6838
+ let lastResult;
6839
+ let i = 0;
6840
+ while (i < target.length) {
6841
+ const targetValue = target[i];
6842
+ i++;
6843
+ try {
6844
+ const resolved = applyPackageTargetResolution(targetValue, {
6845
+ ...resolutionContext,
6846
+ key: `${key}[${i}]`,
6847
+ subpath,
6848
+ pattern,
6849
+ isImport,
6850
+ });
6851
+ if (resolved) {
6852
+ return resolved;
6853
+ }
6854
+ lastResult = resolved;
6855
+ } catch (e) {
6856
+ if (e.code === "INVALID_PACKAGE_TARGET") {
6857
+ continue;
6858
+ }
6859
+ lastResult = e;
6799
6860
  }
6800
6861
  }
6801
- }
6802
-
6803
- if (fileStat && fileStat.isFile()) {
6804
- result.stat = fileStat;
6805
- result.url = fileUrl;
6806
- return result;
6807
- }
6808
- if (fileStat && fileStat.isDirectory()) {
6809
- if (magicDirectoryIndex) {
6810
- const indexFileSuffix = fileUrl.endsWith("/") ? "index" : "/index";
6811
- const indexFileUrl = `${fileUrl}${indexFileSuffix}`;
6812
- const subResult = applyFileSystemMagicResolution(indexFileUrl, {
6813
- magicDirectoryIndex: false,
6814
- magicExtensions,
6815
- });
6816
- return {
6817
- ...result,
6818
- ...subResult,
6819
- magicDirectoryIndex: true,
6820
- };
6862
+ if (lastResult) {
6863
+ throw lastResult;
6821
6864
  }
6822
- result.stat = fileStat;
6823
- result.url = fileUrl;
6824
- return result;
6865
+ return null;
6825
6866
  }
6826
-
6827
- if (magicExtensions && magicExtensions.length) {
6828
- const parentUrl = new URL("./", fileUrl).href;
6829
- const urlFilename = urlToFilename(fileUrl);
6830
- for (const extensionToTry of magicExtensions) {
6831
- const urlCandidate = `${parentUrl}${urlFilename}${extensionToTry}`;
6832
- let stat;
6833
- try {
6834
- stat = readEntryStatSync(new URL(urlCandidate));
6835
- } catch (e) {
6836
- if (e.code === "ENOENT") {
6837
- stat = null;
6838
- } else {
6839
- throw e;
6867
+ if (target === null) {
6868
+ return null;
6869
+ }
6870
+ if (typeof target === "object") {
6871
+ const keys = Object.keys(target);
6872
+ for (const key of keys) {
6873
+ if (Number.isInteger(key)) {
6874
+ throw new Error("Invalid package configuration");
6875
+ }
6876
+ let matched;
6877
+ if (key === "default") {
6878
+ matched = true;
6879
+ } else {
6880
+ for (const conditionCandidate of conditions) {
6881
+ if (conditionCandidate === key) {
6882
+ matched = true;
6883
+ break;
6884
+ }
6885
+ if (conditionCandidate.includes("*")) {
6886
+ const conditionCandidateRegex = new RegExp(
6887
+ `^${conditionCandidate.replace(/\*/g, "(.*)")}$`,
6888
+ );
6889
+ if (conditionCandidateRegex.test(key)) {
6890
+ matched = true;
6891
+ break;
6892
+ }
6893
+ }
6840
6894
  }
6841
6895
  }
6842
- if (stat) {
6843
- result.stat = stat;
6844
- result.url = `${fileUrl}${extensionToTry}`;
6845
- result.magicExtension = extensionToTry;
6846
- return result;
6896
+ if (matched) {
6897
+ const targetValue = target[key];
6898
+ const resolved = applyPackageTargetResolution(targetValue, {
6899
+ ...resolutionContext,
6900
+ key,
6901
+ subpath,
6902
+ pattern,
6903
+ isImport,
6904
+ });
6905
+ if (resolved) {
6906
+ return resolved;
6907
+ }
6847
6908
  }
6848
6909
  }
6910
+ return null;
6849
6911
  }
6850
- // magic extension not found
6851
- return result;
6912
+ throw createInvalidPackageTargetError(
6913
+ `target must be a string, array, object or null`,
6914
+ target,
6915
+ resolutionContext,
6916
+ );
6852
6917
  };
6853
6918
 
6854
- const getExtensionsToTry = (magicExtensions, importer) => {
6855
- if (!magicExtensions) {
6856
- return [];
6919
+ const readExports = ({ packageDirectoryUrl, packageJson }) => {
6920
+ const packageExports = packageJson.exports;
6921
+ if (Array.isArray(packageExports)) {
6922
+ return {
6923
+ type: "array",
6924
+ };
6857
6925
  }
6858
- const extensionsSet = new Set();
6859
- magicExtensions.forEach((magicExtension) => {
6860
- if (magicExtension === "inherit") {
6861
- const importerExtension = urlToExtension(importer);
6862
- extensionsSet.add(importerExtension);
6863
- } else {
6864
- extensionsSet.add(magicExtension);
6926
+ if (packageExports === null) {
6927
+ return {};
6928
+ }
6929
+ if (typeof packageExports === "object") {
6930
+ const keys = Object.keys(packageExports);
6931
+ const relativeKeys = [];
6932
+ const conditionalKeys = [];
6933
+ keys.forEach((availableKey) => {
6934
+ if (availableKey.startsWith(".")) {
6935
+ relativeKeys.push(availableKey);
6936
+ } else {
6937
+ conditionalKeys.push(availableKey);
6938
+ }
6939
+ });
6940
+ const hasRelativeKey = relativeKeys.length > 0;
6941
+ if (hasRelativeKey && conditionalKeys.length > 0) {
6942
+ throw new Error(
6943
+ `Invalid package configuration: cannot mix relative and conditional keys in package.exports
6944
+ --- unexpected keys ---
6945
+ ${conditionalKeys.map((key) => `"${key}"`).join("\n")}
6946
+ --- package directory url ---
6947
+ ${packageDirectoryUrl}`,
6948
+ );
6865
6949
  }
6866
- });
6867
- return Array.from(extensionsSet.values());
6868
- };
6869
-
6870
- const versionFromValue = (value) => {
6871
- if (typeof value === "number") {
6872
- return numberToVersion(value);
6950
+ return {
6951
+ type: "object",
6952
+ hasRelativeKey,
6953
+ allKeysAreRelative: relativeKeys.length === keys.length,
6954
+ };
6873
6955
  }
6874
- if (typeof value === "string") {
6875
- return stringToVersion(value);
6956
+ if (typeof packageExports === "string") {
6957
+ return { type: "string" };
6876
6958
  }
6877
- throw new TypeError(`version must be a number or a string, got ${value}`);
6878
- };
6879
-
6880
- const numberToVersion = (number) => {
6881
- return {
6882
- major: number,
6883
- minor: 0,
6884
- patch: 0,
6885
- };
6959
+ return {};
6886
6960
  };
6887
6961
 
6888
- const stringToVersion = (string) => {
6889
- if (string.indexOf(".") > -1) {
6890
- const parts = string.split(".");
6962
+ const parsePackageSpecifier = (packageSpecifier) => {
6963
+ if (packageSpecifier[0] === "@") {
6964
+ const firstSlashIndex = packageSpecifier.indexOf("/");
6965
+ if (firstSlashIndex === -1) {
6966
+ throw new Error("invalid module specifier");
6967
+ }
6968
+ const secondSlashIndex = packageSpecifier.indexOf("/", firstSlashIndex + 1);
6969
+ if (secondSlashIndex === -1) {
6970
+ return {
6971
+ packageName: packageSpecifier,
6972
+ packageSubpath: ".",
6973
+ isScoped: true,
6974
+ };
6975
+ }
6976
+ const packageName = packageSpecifier.slice(0, secondSlashIndex);
6977
+ const afterSecondSlash = packageSpecifier.slice(secondSlashIndex + 1);
6978
+ const packageSubpath = `./${afterSecondSlash}`;
6891
6979
  return {
6892
- major: Number(parts[0]),
6893
- minor: parts[1] ? Number(parts[1]) : 0,
6894
- patch: parts[2] ? Number(parts[2]) : 0,
6980
+ packageName,
6981
+ packageSubpath,
6982
+ isScoped: true,
6895
6983
  };
6896
6984
  }
6897
-
6898
- if (isNaN(string)) {
6985
+ const firstSlashIndex = packageSpecifier.indexOf("/");
6986
+ if (firstSlashIndex === -1) {
6899
6987
  return {
6900
- major: 0,
6901
- minor: 0,
6902
- patch: 0,
6988
+ packageName: packageSpecifier,
6989
+ packageSubpath: ".",
6903
6990
  };
6904
6991
  }
6905
-
6992
+ const packageName = packageSpecifier.slice(0, firstSlashIndex);
6993
+ const afterFirstSlash = packageSpecifier.slice(firstSlashIndex + 1);
6994
+ if (afterFirstSlash === "") {
6995
+ return {
6996
+ packageName,
6997
+ packageSubpath: "/",
6998
+ };
6999
+ }
7000
+ const packageSubpath = `./${afterFirstSlash}`;
6906
7001
  return {
6907
- major: Number(string),
6908
- minor: 0,
6909
- patch: 0,
7002
+ packageName,
7003
+ packageSubpath,
6910
7004
  };
6911
7005
  };
6912
7006
 
6913
- const compareTwoVersions = (versionA, versionB) => {
6914
- const semanticVersionA = versionFromValue(versionA);
6915
- const semanticVersionB = versionFromValue(versionB);
6916
- const majorDiff = semanticVersionA.major - semanticVersionB.major;
6917
- if (majorDiff > 0) {
6918
- return majorDiff;
6919
- }
6920
- if (majorDiff < 0) {
6921
- return majorDiff;
6922
- }
6923
- const minorDiff = semanticVersionA.minor - semanticVersionB.minor;
6924
- if (minorDiff > 0) {
6925
- return minorDiff;
7007
+ const applyMainExportResolution = (resolutionContext) => {
7008
+ const { packageJson } = resolutionContext;
7009
+ const packageExportsInfo = readExports(resolutionContext);
7010
+ if (
7011
+ packageExportsInfo.type === "array" ||
7012
+ packageExportsInfo.type === "string"
7013
+ ) {
7014
+ return packageJson.exports;
6926
7015
  }
6927
- if (minorDiff < 0) {
6928
- return minorDiff;
7016
+ if (packageExportsInfo.type === "object") {
7017
+ if (packageExportsInfo.hasRelativeKey) {
7018
+ return packageJson.exports["."];
7019
+ }
7020
+ return packageJson.exports;
6929
7021
  }
6930
- const patchDiff = semanticVersionA.patch - semanticVersionB.patch;
6931
- if (patchDiff > 0) {
6932
- return patchDiff;
7022
+ return undefined;
7023
+ };
7024
+
7025
+ const applyLegacySubpathResolution = (packageSubpath, resolutionContext) => {
7026
+ const { packageDirectoryUrl, packageJson } = resolutionContext;
7027
+
7028
+ if (packageSubpath === ".") {
7029
+ return applyLegacyMainResolution(packageSubpath, resolutionContext);
6933
7030
  }
6934
- if (patchDiff < 0) {
6935
- return patchDiff;
7031
+ const browserFieldResolution = applyBrowserFieldResolution(
7032
+ packageSubpath,
7033
+ resolutionContext,
7034
+ );
7035
+ if (browserFieldResolution) {
7036
+ return browserFieldResolution;
6936
7037
  }
6937
- return 0;
6938
- };
6939
-
6940
- const versionIsBelow = (versionSupposedBelow, versionSupposedAbove) => {
6941
- return compareTwoVersions(versionSupposedBelow, versionSupposedAbove) < 0;
7038
+ return createResolutionResult({
7039
+ type: "subpath",
7040
+ isMain: packageSubpath === ".",
7041
+ packageDirectoryUrl,
7042
+ packageJson,
7043
+ url: new URL(packageSubpath, packageDirectoryUrl).href,
7044
+ });
6942
7045
  };
6943
7046
 
6944
- const findHighestVersion = (...values) => {
6945
- if (values.length === 0) throw new Error(`missing argument`);
6946
- return values.reduce((highestVersion, value) => {
6947
- if (versionIsBelow(highestVersion, value)) {
6948
- return value;
7047
+ const applyLegacyMainResolution = (packageSubpath, resolutionContext) => {
7048
+ const { conditions, packageDirectoryUrl, packageJson } = resolutionContext;
7049
+ if (packageJson) {
7050
+ for (const condition of conditions) {
7051
+ const conditionResolver = mainLegacyResolvers[condition];
7052
+ if (!conditionResolver) {
7053
+ continue;
7054
+ }
7055
+ const resolved = conditionResolver(resolutionContext);
7056
+ if (resolved) {
7057
+ return createResolutionResult({
7058
+ type: resolved.type,
7059
+ isMain: resolved.isMain,
7060
+ packageDirectoryUrl,
7061
+ packageJson,
7062
+ url: new URL(resolved.path, packageDirectoryUrl).href,
7063
+ });
7064
+ }
6949
7065
  }
6950
- return highestVersion;
7066
+ }
7067
+ return createResolutionResult({
7068
+ type: "field:main", // the absence of "main" field
7069
+ isMain: true,
7070
+ packageDirectoryUrl,
7071
+ packageJson,
7072
+ url: new URL("index.js", packageDirectoryUrl).href,
6951
7073
  });
6952
7074
  };
6953
-
6954
- const featuresCompatMap = {
6955
- script_type_module: {
6956
- edge: "16",
6957
- firefox: "60",
6958
- chrome: "61",
6959
- safari: "10.1",
6960
- opera: "48",
6961
- ios_safari: "10.3",
6962
- android: "61",
6963
- samsung: "8.2",
6964
- },
6965
- document_current_script: {
6966
- edge: "12",
6967
- firefox: "4",
6968
- chrome: "29",
6969
- safari: "8",
6970
- opera: "16",
6971
- android: "4.4",
6972
- samsung: "4",
6973
- },
6974
- // https://caniuse.com/?search=import.meta
6975
- import_meta: {
6976
- android: "9",
6977
- chrome: "64",
6978
- edge: "79",
6979
- firefox: "62",
6980
- ios_safari: "12",
6981
- opera: "51",
6982
- safari: "11.1",
6983
- samsung: "9.2",
6984
- },
6985
- import_meta_resolve: {
6986
- chrome: "107",
6987
- edge: "105",
6988
- firefox: "106",
6989
- node: "20.0.0",
6990
- },
6991
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#browser_compatibility
6992
- import_dynamic: {
6993
- android: "8",
6994
- chrome: "63",
6995
- edge: "79",
6996
- firefox: "67",
6997
- ios_safari: "11.3",
6998
- opera: "50",
6999
- safari: "11.3",
7000
- samsung: "8.0",
7001
- node: "13.2",
7002
- },
7003
- top_level_await: {
7004
- edge: "89",
7005
- chrome: "89",
7006
- firefox: "89",
7007
- opera: "75",
7008
- safari: "15",
7009
- samsung: "15",
7010
- ios_safari: "15",
7011
- node: "14.8",
7012
- },
7013
- // https://caniuse.com/import-maps
7014
- importmap: {
7015
- edge: "89",
7016
- chrome: "89",
7017
- opera: "76",
7018
- samsung: "15",
7019
- firefox: "108",
7020
- safari: "16.4",
7021
- },
7022
- import_type_json: {
7023
- chrome: "123",
7024
- safari: "17.2",
7025
- },
7026
- import_type_css: {
7027
- chrome: "123",
7028
- },
7029
- import_type_text: {},
7030
- // https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet#browser_compatibility
7031
- new_stylesheet: {
7032
- chrome: "73",
7033
- edge: "79",
7034
- opera: "53",
7035
- android: "73",
7036
- },
7037
- // https://caniuse.com/?search=worker
7038
- worker: {
7039
- ie: "10",
7040
- edge: "12",
7041
- firefox: "3.5",
7042
- chrome: "4",
7043
- opera: "11.5",
7044
- safari: "4",
7045
- ios_safari: "5",
7046
- android: "4.4",
7047
- },
7048
- // https://developer.mozilla.org/en-US/docs/Web/API/Worker/Worker#browser_compatibility
7049
- worker_type_module: {
7050
- chrome: "80",
7051
- edge: "80",
7052
- opera: "67",
7053
- android: "80",
7054
- },
7055
- worker_importmap: {},
7056
- service_worker: {
7057
- edge: "17",
7058
- firefox: "44",
7059
- chrome: "40",
7060
- safari: "11.1",
7061
- opera: "27",
7062
- ios_safari: "11.3",
7063
- android: "12.12",
7064
- },
7065
- service_worker_type_module: {
7066
- chrome: "80",
7067
- edge: "80",
7068
- opera: "67",
7069
- android: "80",
7070
- },
7071
- service_worker_importmap: {},
7072
- shared_worker: {
7073
- chrome: "4",
7074
- edge: "79",
7075
- firefox: "29",
7076
- opera: "10.6",
7077
- },
7078
- shared_worker_type_module: {
7079
- chrome: "80",
7080
- edge: "80",
7081
- opera: "67",
7082
- },
7083
- shared_worker_importmap: {},
7084
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis#browser_compatibility
7085
- global_this: {
7086
- edge: "79",
7087
- firefox: "65",
7088
- chrome: "71",
7089
- safari: "12.1",
7090
- opera: "58",
7091
- ios_safari: "12.2",
7092
- android: "94",
7093
- node: "12",
7094
- },
7095
- async_generator_function: {
7096
- chrome: "63",
7097
- opera: "50",
7098
- edge: "79",
7099
- firefox: "57",
7100
- safari: "12",
7101
- node: "10",
7102
- ios_safari: "12",
7103
- samsung: "8",
7104
- electron: "3",
7105
- },
7106
- // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals#browser_compatibility
7107
- template_literals: {
7108
- chrome: "41",
7109
- edge: "12",
7110
- firefox: "34",
7111
- opera: "28",
7112
- safari: "9",
7113
- ios_safari: "9",
7114
- android: "4",
7115
- node: "4",
7116
- },
7117
- arrow_function: {
7118
- chrome: "47",
7119
- opera: "34",
7120
- edge: "13",
7121
- firefox: "45",
7122
- safari: "10",
7123
- node: "6",
7124
- ios_safari: "10",
7125
- samsung: "5",
7126
- electron: "0.36",
7127
- },
7128
- const_bindings: {
7129
- chrome: "41",
7130
- opera: "28",
7131
- edge: "12",
7132
- firefox: "46",
7133
- safari: "10",
7134
- node: "4",
7135
- ie: "11",
7136
- ios_safari: "10",
7137
- samsung: "3.4",
7138
- electron: "0.22",
7139
- },
7140
- object_properties_shorthand: {
7141
- chrome: "43",
7142
- opera: "30",
7143
- edge: "12",
7144
- firefox: "33",
7145
- safari: "9",
7146
- node: "4",
7147
- ios_safari: "9",
7148
- samsung: "4",
7149
- electron: "0.28",
7075
+ const mainLegacyResolvers = {
7076
+ import: ({ packageJson }) => {
7077
+ if (typeof packageJson.module === "string") {
7078
+ return { type: "field:module", isMain: true, path: packageJson.module };
7079
+ }
7080
+ if (typeof packageJson.jsnext === "string") {
7081
+ return { type: "field:jsnext", isMain: true, path: packageJson.jsnext };
7082
+ }
7083
+ if (typeof packageJson.main === "string") {
7084
+ return { type: "field:main", isMain: true, path: packageJson.main };
7085
+ }
7086
+ return null;
7150
7087
  },
7151
- reserved_words: {
7152
- chrome: "13",
7153
- opera: "10.50",
7154
- edge: "12",
7155
- firefox: "2",
7156
- safari: "3.1",
7157
- node: "0.10",
7158
- ie: "9",
7159
- android: "4.4",
7160
- ios_safari: "6",
7161
- phantom: "2",
7162
- samsung: "1",
7163
- electron: "0.20",
7088
+ browser: ({ packageDirectoryUrl, packageJson }) => {
7089
+ const browserMain = (() => {
7090
+ if (typeof packageJson.browser === "string") {
7091
+ return packageJson.browser;
7092
+ }
7093
+ if (
7094
+ typeof packageJson.browser === "object" &&
7095
+ packageJson.browser !== null
7096
+ ) {
7097
+ return packageJson.browser["."];
7098
+ }
7099
+ return "";
7100
+ })();
7101
+
7102
+ if (!browserMain) {
7103
+ if (typeof packageJson.module === "string") {
7104
+ return {
7105
+ type: "field:module",
7106
+ isMain: true,
7107
+ path: packageJson.module,
7108
+ };
7109
+ }
7110
+ return null;
7111
+ }
7112
+ if (
7113
+ typeof packageJson.module !== "string" ||
7114
+ packageJson.module === browserMain
7115
+ ) {
7116
+ return {
7117
+ type: "field:browser",
7118
+ isMain: true,
7119
+ path: browserMain,
7120
+ };
7121
+ }
7122
+ const browserMainUrlObject = new URL(browserMain, packageDirectoryUrl);
7123
+ const content = readFileSync(browserMainUrlObject, "utf-8");
7124
+ if (
7125
+ (/typeof exports\s*==/.test(content) &&
7126
+ /typeof module\s*==/.test(content)) ||
7127
+ /module\.exports\s*=/.test(content)
7128
+ ) {
7129
+ return {
7130
+ type: "field:module",
7131
+ isMain: true,
7132
+ path: packageJson.module,
7133
+ };
7134
+ }
7135
+ return {
7136
+ type: "field:browser",
7137
+ isMain: true,
7138
+ path: browserMain,
7139
+ };
7164
7140
  },
7165
- symbols: {
7166
- chrome: "38",
7167
- opera: "25",
7168
- edge: "12",
7169
- firefox: "36",
7170
- safari: "9",
7171
- ios_safari: "9",
7172
- samsung: "4",
7173
- node: "0.12",
7141
+ node: ({ packageJson, conditions }) => {
7142
+ if (conditions.includes("import") && !conditions.includes("require")) {
7143
+ if (typeof packageJson.module === "string") {
7144
+ return { type: "field:module", isMain: true, path: packageJson.module };
7145
+ }
7146
+ if (typeof packageJson.jsnext === "string") {
7147
+ return { type: "field:jsnext", isMain: true, path: packageJson.jsnext };
7148
+ }
7149
+ }
7150
+ if (typeof packageJson.main === "string") {
7151
+ return { type: "field:main", isMain: true, path: packageJson.main };
7152
+ }
7153
+ return null;
7174
7154
  },
7175
7155
  };
7156
+ mainLegacyResolvers.require = mainLegacyResolvers.node;
7176
7157
 
7177
- const RUNTIME_COMPAT = {
7178
- featuresCompatMap,
7158
+ const comparePatternKeys = (keyA, keyB) => {
7159
+ if (!keyA.endsWith("/") && !keyA.includes("*")) {
7160
+ throw new Error("Invalid package configuration");
7161
+ }
7162
+ if (!keyB.endsWith("/") && !keyB.includes("*")) {
7163
+ throw new Error("Invalid package configuration");
7164
+ }
7165
+ const aStarIndex = keyA.indexOf("*");
7166
+ const baseLengthA = aStarIndex > -1 ? aStarIndex + 1 : keyA.length;
7167
+ const bStarIndex = keyB.indexOf("*");
7168
+ const baseLengthB = bStarIndex > -1 ? bStarIndex + 1 : keyB.length;
7169
+ if (baseLengthA > baseLengthB) {
7170
+ return -1;
7171
+ }
7172
+ if (baseLengthB > baseLengthA) {
7173
+ return 1;
7174
+ }
7175
+ if (aStarIndex === -1) {
7176
+ return 1;
7177
+ }
7178
+ if (bStarIndex === -1) {
7179
+ return -1;
7180
+ }
7181
+ if (keyA.length > keyB.length) {
7182
+ return -1;
7183
+ }
7184
+ if (keyB.length > keyA.length) {
7185
+ return 1;
7186
+ }
7187
+ return 0;
7188
+ };
7179
7189
 
7180
- add: (originalRuntimeCompat, feature) => {
7181
- const featureCompat = getFeatureCompat(feature);
7182
- const runtimeCompat = {
7183
- ...originalRuntimeCompat,
7184
- };
7185
- Object.keys(originalRuntimeCompat).forEach((runtimeName) => {
7186
- const secondVersion = featureCompat[runtimeName]; // the version supported by the feature
7187
- if (secondVersion) {
7188
- const firstVersion = originalRuntimeCompat[runtimeName];
7189
- runtimeCompat[runtimeName] = findHighestVersion(
7190
- firstVersion,
7191
- secondVersion,
7192
- );
7190
+ const resolvePackageSymlink = (packageDirectoryUrl) => {
7191
+ const packageDirectoryPath = realpathSync(new URL(packageDirectoryUrl));
7192
+ const packageDirectoryResolvedUrl = pathToFileURL(packageDirectoryPath).href;
7193
+ return `${packageDirectoryResolvedUrl}/`;
7194
+ };
7195
+
7196
+ const applyFileSystemMagicResolution = (
7197
+ fileUrl,
7198
+ { fileStat, magicDirectoryIndex, magicExtensions },
7199
+ ) => {
7200
+ const result = {
7201
+ stat: null,
7202
+ url: fileUrl,
7203
+ magicExtension: "",
7204
+ magicDirectoryIndex: false,
7205
+ lastENOENTError: null,
7206
+ };
7207
+
7208
+ if (fileStat === undefined) {
7209
+ try {
7210
+ fileStat = readEntryStatSync(new URL(fileUrl));
7211
+ } catch (e) {
7212
+ if (e.code === "ENOENT") {
7213
+ result.lastENOENTError = e;
7214
+ fileStat = null;
7215
+ } else {
7216
+ throw e;
7193
7217
  }
7194
- });
7195
- return runtimeCompat;
7196
- },
7218
+ }
7219
+ }
7197
7220
 
7198
- isSupported: (
7199
- runtimeCompat,
7200
- feature,
7201
- featureCompat = getFeatureCompat(feature),
7202
- ) => {
7203
- const runtimeNames = Object.keys(runtimeCompat);
7204
- const runtimeWithoutCompat = runtimeNames.find((runtimeName) => {
7205
- const runtimeVersion = runtimeCompat[runtimeName];
7206
- const runtimeVersionCompatible = featureCompat[runtimeName] || "Infinity";
7207
- const highestVersion = findHighestVersion(
7208
- runtimeVersion,
7209
- runtimeVersionCompatible,
7210
- );
7211
- return highestVersion !== runtimeVersion;
7212
- });
7213
- return !runtimeWithoutCompat;
7214
- },
7215
- };
7221
+ if (fileStat && fileStat.isFile()) {
7222
+ result.stat = fileStat;
7223
+ result.url = fileUrl;
7224
+ return result;
7225
+ }
7226
+ if (fileStat && fileStat.isDirectory()) {
7227
+ if (magicDirectoryIndex) {
7228
+ const indexFileSuffix = fileUrl.endsWith("/") ? "index" : "/index";
7229
+ const indexFileUrl = `${fileUrl}${indexFileSuffix}`;
7230
+ const subResult = applyFileSystemMagicResolution(indexFileUrl, {
7231
+ magicDirectoryIndex: false,
7232
+ magicExtensions,
7233
+ });
7234
+ return {
7235
+ ...result,
7236
+ ...subResult,
7237
+ magicDirectoryIndex: true,
7238
+ };
7239
+ }
7240
+ result.stat = fileStat;
7241
+ result.url = fileUrl;
7242
+ return result;
7243
+ }
7216
7244
 
7217
- const getFeatureCompat = (feature) => {
7218
- if (typeof feature === "string") {
7219
- const compat = featuresCompatMap[feature];
7220
- if (!compat) {
7221
- throw new Error(`"${feature}" feature is unknown`);
7245
+ if (magicExtensions && magicExtensions.length) {
7246
+ const parentUrl = new URL("./", fileUrl).href;
7247
+ const urlFilename = urlToFilename(fileUrl);
7248
+ for (const extensionToTry of magicExtensions) {
7249
+ const urlCandidate = `${parentUrl}${urlFilename}${extensionToTry}`;
7250
+ let stat;
7251
+ try {
7252
+ stat = readEntryStatSync(new URL(urlCandidate));
7253
+ } catch (e) {
7254
+ if (e.code === "ENOENT") {
7255
+ stat = null;
7256
+ } else {
7257
+ throw e;
7258
+ }
7259
+ }
7260
+ if (stat) {
7261
+ result.stat = stat;
7262
+ result.url = `${fileUrl}${extensionToTry}`;
7263
+ result.magicExtension = extensionToTry;
7264
+ return result;
7265
+ }
7222
7266
  }
7223
- return compat;
7224
7267
  }
7225
- if (typeof feature !== "object") {
7226
- throw new TypeError(
7227
- `feature must be a string or an object, got ${feature}`,
7228
- );
7268
+ // magic extension not found
7269
+ return result;
7270
+ };
7271
+
7272
+ const getExtensionsToTry = (magicExtensions, importer) => {
7273
+ if (!magicExtensions) {
7274
+ return [];
7229
7275
  }
7230
- return feature;
7276
+ const extensionsSet = new Set();
7277
+ magicExtensions.forEach((magicExtension) => {
7278
+ if (magicExtension === "inherit") {
7279
+ const importerExtension = urlToExtension(importer);
7280
+ extensionsSet.add(importerExtension);
7281
+ } else {
7282
+ extensionsSet.add(magicExtension);
7283
+ }
7284
+ });
7285
+ return Array.from(extensionsSet.values());
7231
7286
  };
7232
7287
 
7233
7288
  const isSupportedAlgorithm = (algo) => {
@@ -7348,4 +7403,4 @@ const isResponseEligibleForIntegrityValidation = (response) => {
7348
7403
  return ["basic", "cors", "default"].includes(response.type);
7349
7404
  };
7350
7405
 
7351
- export { ANSI, CONTENT_TYPE, DATA_URL, JS_QUOTES, RUNTIME_COMPAT, URL_META, applyFileSystemMagicResolution, applyNodeEsmResolution, asSpecifierWithoutSearch, asUrlWithoutSearch, assertAndNormalizeDirectoryUrl, bufferToEtag, collectFiles, compareFileUrls, composeTwoImportMaps, createDetailedMessage$1 as createDetailedMessage, createLogger, createTaskLog, ensurePathnameTrailingSlash, ensureWindowsDriveLetter, errorToHTML, formatError, generateContentFrame, getCallerPosition, getExtensionsToTry, injectQueryParams, injectQueryParamsIntoSpecifier, isFileSystemPath, isSpecifierForNodeBuiltin, lookupPackageDirectory, moveUrl, normalizeImportMap, normalizeUrl, readCustomConditionsFromProcessArgs, readEntryStatSync, readPackageAtOrNull, registerDirectoryLifecycle, registerFileLifecycle, resolveImport, setUrlBasename, setUrlExtension, setUrlFilename, stringifyUrlSite, urlIsOrIsInsideOf, urlToBasename, urlToExtension$1 as urlToExtension, urlToFileSystemPath, urlToFilename$1 as urlToFilename, urlToPathname$1 as urlToPathname, urlToRelativeUrl, validateResponseIntegrity, writeFileSync };
7406
+ export { ANSI, CONTENT_TYPE, DATA_URL, JS_QUOTES, RUNTIME_COMPAT, URL_META, applyFileSystemMagicResolution, applyNodeEsmResolution, asSpecifierWithoutSearch, asUrlWithoutSearch, assertAndNormalizeDirectoryUrl, browserDefaultRuntimeCompat, bufferToEtag, collectFiles, compareFileUrls, composeTwoImportMaps, createDetailedMessage$1 as createDetailedMessage, createLogger, createTaskLog, ensurePathnameTrailingSlash, ensureWindowsDriveLetter, errorToHTML, formatError, generateContentFrame, getCallerPosition, getExtensionsToTry, inferRuntimeCompatFromClosestPackage, injectQueryParams, injectQueryParamsIntoSpecifier, isFileSystemPath, isSpecifierForNodeBuiltin, lookupPackageDirectory, moveUrl, normalizeImportMap, normalizeUrl, readCustomConditionsFromProcessArgs, readEntryStatSync, readPackageAtOrNull, registerDirectoryLifecycle, registerFileLifecycle, resolveImport, setUrlBasename, setUrlExtension, setUrlFilename, stringifyUrlSite, urlIsOrIsInsideOf, urlToBasename, urlToExtension$1 as urlToExtension, urlToFileSystemPath, urlToFilename$1 as urlToFilename, urlToPathname$1 as urlToPathname, urlToRelativeUrl, validateResponseIntegrity, writeFileSync };