@jsenv/core 41.4.27 → 41.4.29

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