@lwrjs/loader 0.12.0-alpha.7 → 0.12.0-alpha.9

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.
@@ -4,7 +4,7 @@
4
4
  * SPDX-License-Identifier: MIT
5
5
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/MIT
6
6
  */
7
- /* LWR Module Loader v0.12.0-alpha.7 */
7
+ /* LWR Module Loader v0.12.0-alpha.9 */
8
8
  const templateRegex = /\{([0-9]+)\}/g;
9
9
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
10
  function templateString(template, args) {
@@ -798,13 +798,22 @@ async function evaluateLoadHook(
798
798
 
799
799
 
800
800
 
801
-
802
-
803
801
 
804
802
 
805
803
 
806
804
  class ModuleRegistry {
807
805
 
806
+
807
+ // A registry for named AMD defines containing the *metadata* of AMD module
808
+ __init() {this.namedDefineRegistry = new Map();}
809
+ // The evaluated module registry where the module identifier (name or URL?) is the key
810
+ __init2() {this.moduleRegistry = new Map();}
811
+ // Aliases of modules in the registry
812
+ __init3() {this.aliases = new Map();}
813
+
814
+
815
+
816
+
808
817
 
809
818
  constructor(config) {ModuleRegistry.prototype.__init.call(this);ModuleRegistry.prototype.__init2.call(this);ModuleRegistry.prototype.__init3.call(this);
810
819
  this.profiler = config.profiler;
@@ -814,6 +823,9 @@ class ModuleRegistry {
814
823
  );
815
824
  }
816
825
 
826
+ /**
827
+ * Module import
828
+ */
817
829
  async load(id, importer) {
818
830
  const metadata = importer ? { importer } : {};
819
831
  this.profiler.logOperationStart({
@@ -821,18 +833,23 @@ class ModuleRegistry {
821
833
  specifier: id,
822
834
  metadata,
823
835
  });
836
+
824
837
  const resolvedId = await this.resolve(id, importer);
825
- const moduleRecord = await this.getModuleRecord(resolvedId, id);
838
+ const moduleRecord = this.getModuleRecord(resolvedId, id);
839
+
826
840
  if (moduleRecord.evaluated) {
827
841
  return moduleRecord.module;
828
842
  } else {
829
843
  if (!moduleRecord.evaluationPromise) {
830
- moduleRecord.evaluationPromise = this.topLevelEvaluation(moduleRecord);
844
+ moduleRecord.evaluationPromise = this.evaluateModule(moduleRecord, {});
831
845
  }
832
846
  return moduleRecord.evaluationPromise;
833
847
  }
834
848
  }
835
849
 
850
+ /**
851
+ * Resolve id for a module
852
+ */
836
853
  async resolve(id, importer) {
837
854
  const parentUrl = this.resolver.getBaseUrl(); // only support baseUrl for now
838
855
 
@@ -848,7 +865,7 @@ class ModuleRegistry {
848
865
  // eslint-disable-next-line no-await-in-loop
849
866
  result = isResponseAPromise(response) ? await response : response;
850
867
  }
851
- if (!this.isValidResolveResponse(result)) {
868
+ if (!isValidResolveResponse(result)) {
852
869
  throw new LoaderError(INVALID_LOADER_SERVICE_RESPONSE);
853
870
  }
854
871
 
@@ -934,6 +951,9 @@ class ModuleRegistry {
934
951
  return this.moduleRegistry.has(id);
935
952
  }
936
953
 
954
+ /**
955
+ * Module entry point LWR.define()
956
+ */
937
957
  define(name, dependencies, exporter) {
938
958
  const mod = this.namedDefineRegistry.get(name);
939
959
  // Don't allow redefining a module.
@@ -1000,19 +1020,6 @@ class ModuleRegistry {
1000
1020
  });
1001
1021
  }
1002
1022
 
1003
-
1004
-
1005
- // A registry for named AMD defines containing the *metadata* of AMD module
1006
- __init() {this.namedDefineRegistry = new Map();}
1007
-
1008
- // The evaluated module registry where the module identifier (name or URL?) is the key
1009
- __init2() {this.moduleRegistry = new Map();}
1010
-
1011
- // Aliases of modules in the registry
1012
- __init3() {this.aliases = new Map();}
1013
-
1014
-
1015
-
1016
1023
  getImportMetadataResolver() {
1017
1024
  return this.resolver;
1018
1025
  }
@@ -1038,7 +1045,7 @@ class ModuleRegistry {
1038
1045
  return moduleRecord;
1039
1046
  }
1040
1047
 
1041
- async getModuleRecord(resolvedId, id) {
1048
+ getModuleRecord(resolvedId, id) {
1042
1049
  // Look for an existing record
1043
1050
  const existingRecord = this.getExistingModuleRecord(resolvedId, id);
1044
1051
  if (existingRecord) {
@@ -1046,29 +1053,10 @@ class ModuleRegistry {
1046
1053
  return existingRecord;
1047
1054
  }
1048
1055
 
1049
- // Create a new Module Record
1050
- const instantiation = this.getModuleDef(resolvedId, id);
1051
- const dependencyRecords = instantiation.then((moduleDef) => {
1052
- const dependencies = moduleDef.dependencies || [];
1053
- // get dep and filter out exports
1054
- const filtered = dependencies
1055
- .map((dep) => {
1056
- if (dep === 'exports') {
1057
- return;
1058
- }
1059
- invariant(dep !== 'require', NO_AMD_REQUIRE);
1060
- return this.getModuleDependencyRecord.call(this, dep);
1061
- })
1062
- .filter((depRecord) => depRecord !== undefined) ;
1063
-
1064
- return Promise.all(filtered);
1065
- });
1066
-
1067
1056
  const newModuleRecord = {
1068
1057
  id: resolvedId,
1069
1058
  module: Object.create(null),
1070
- dependencyRecords,
1071
- instantiation,
1059
+ instantiation: this.getModuleDef(resolvedId, id),
1072
1060
  evaluated: false,
1073
1061
  evaluationPromise: null,
1074
1062
  };
@@ -1076,8 +1064,7 @@ class ModuleRegistry {
1076
1064
  this.moduleRegistry.set(resolvedId, newModuleRecord);
1077
1065
  this.storeModuleAlias(id, resolvedId);
1078
1066
 
1079
- // Wait for the dependencies to resolve the return the moduleRecord
1080
- return dependencyRecords.then(() => newModuleRecord);
1067
+ return newModuleRecord;
1081
1068
  }
1082
1069
 
1083
1070
  storeModuleAlias(aliasId, resolvedId) {
@@ -1098,95 +1085,87 @@ class ModuleRegistry {
1098
1085
  }
1099
1086
  }
1100
1087
 
1101
- async getModuleDependencyRecord(dependency) {
1102
- const resolvedDepId = await this.resolve(dependency);
1103
- return this.getModuleRecord(resolvedDepId, dependency);
1104
- }
1088
+ /**
1089
+ * Evaluate all module dependencies
1090
+ */
1091
+ async evaluateDependencies(
1092
+ dependencies,
1093
+ evaluationMap,
1094
+ ) {
1095
+ const exports = {};
1096
+ const promiseArray = [];
1097
+
1098
+ if (dependencies) {
1099
+ for (const dep of dependencies) {
1100
+ if (dep === 'exports') {
1101
+ promiseArray.push(Promise.resolve(exports));
1102
+ } else {
1103
+ invariant(dep !== 'require', NO_AMD_REQUIRE);
1104
+ promiseArray.push(this.evaluateDependent(dep, evaluationMap));
1105
+ }
1106
+ }
1107
+ }
1105
1108
 
1106
- // execute the "top-level code" (the code outside of functions) of a module
1107
- async topLevelEvaluation(moduleRecord) {
1108
- await this.instantiateAll(moduleRecord, {});
1109
- return this.evaluateModule(moduleRecord, {});
1109
+ return Promise.all(promiseArray).then((results) => {
1110
+ const depsMapped = results.filter((result) => result !== undefined);
1111
+ return { depsMapped, exports };
1112
+ });
1110
1113
  }
1111
1114
 
1112
- // Returns a promise when a module and all of it's dependencies have finished instantiation
1113
- async instantiateAll(
1114
- moduleRecord,
1115
- instantiatedMap,
1116
- ) {
1117
- if (!instantiatedMap[moduleRecord.id]) {
1118
- instantiatedMap[moduleRecord.id] = true;
1119
- const dependencyModuleRecords = await moduleRecord.dependencyRecords;
1120
- if (dependencyModuleRecords) {
1121
- for (let i = 0; i < dependencyModuleRecords.length; i++) {
1122
- const depRecord = dependencyModuleRecords[i];
1123
- // eslint-disable-next-line no-await-in-loop
1124
- await this.instantiateAll(depRecord, instantiatedMap);
1125
- }
1115
+ async evaluateDependent(dep, evaluationMap) {
1116
+ const resolvedDepId = await this.resolve(dep);
1117
+
1118
+ const depModuleRecord = this.getModuleRecord(resolvedDepId, dep);
1119
+ let module = depModuleRecord.module;
1120
+
1121
+ const handleReturn = (module) => {
1122
+ if (module) {
1123
+ return module.__defaultInterop ? module.default : module;
1126
1124
  }
1125
+ throw new LoaderError(FAILED_DEP, [resolvedDepId]);
1126
+ };
1127
+
1128
+ // If evaluated return the module
1129
+ if (depModuleRecord.evaluated) {
1130
+ return handleReturn(module);
1131
+ }
1132
+ /**
1133
+ * Circular dependencies are handled properly when named exports are used,
1134
+ * however, for default exports there is a bug: https://github.com/rollup/rollup/issues/3384
1135
+ *
1136
+ * The workaround below applies for circular dependencies (!moduleRecord.evaluated)
1137
+ */
1138
+ if (!evaluationMap[depModuleRecord.id]) {
1139
+ // If we have not started dependency evaluation kick it off
1140
+ if (!depModuleRecord.evaluationPromise) {
1141
+ depModuleRecord.evaluationPromise = this.evaluateModule(depModuleRecord, evaluationMap);
1142
+ }
1143
+ return depModuleRecord.evaluationPromise.then((module) => {
1144
+ return handleReturn(module);
1145
+ });
1146
+ } else {
1147
+ // Otherwise return a dummy circular module wrapper
1148
+ module = getCircularDependencyWrapper(module);
1127
1149
  }
1150
+ return handleReturn(module);
1128
1151
  }
1129
1152
 
1130
1153
  async evaluateModule(
1131
1154
  moduleRecord,
1132
1155
  evaluationMap,
1133
1156
  ) {
1134
- const dependencyModuleRecords = await moduleRecord.dependencyRecords;
1135
- if (dependencyModuleRecords.length > 0) {
1136
- evaluationMap[moduleRecord.id] = true;
1137
- // evaluate dependencies first
1138
- await this.evaluateModuleDependencies(dependencyModuleRecords, evaluationMap);
1139
- }
1157
+ // Create a evaluationMap to detect cycles in this dep chain
1158
+ const chainMap = { ...evaluationMap };
1159
+ chainMap[moduleRecord.id] = true;
1140
1160
 
1161
+ // Wait for load to finish
1141
1162
  const { exporter, dependencies } = await moduleRecord.instantiation;
1142
- // The exports object automatically gets filled in by the exporter evaluation
1143
- const exports = {};
1144
- const depsMapped = dependencies
1145
- ? await Promise.all(
1146
- dependencies.map(async (dep) => {
1147
- if (dep === 'exports') {
1148
- return exports;
1149
- }
1150
- const resolvedDepId = await this.resolve(dep);
1151
-
1152
- const moduleRecord = this.moduleRegistry.get(resolvedDepId) ;
1153
- if (!moduleRecord) {
1154
- throw new LoaderError(FAILED_DEP, [resolvedDepId]);
1155
- }
1156
-
1157
- const module = moduleRecord.module;
1158
-
1159
- /**
1160
- * Circular dependencies are handled properly when named exports are used,
1161
- * however, for default exports there is a bug: https://github.com/rollup/rollup/issues/3384
1162
- *
1163
- * The workaround below applies for circular dependencies (!moduleRecord.evaluated)
1164
- */
1165
- if (!moduleRecord.evaluated) {
1166
- return this.getCircularDependencyWrapper(module);
1167
- }
1168
-
1169
- if (module) {
1170
- return module.__defaultInterop ? module.default : module;
1171
- }
1172
-
1173
- throw new LoaderError(FAILED_DEP, [resolvedDepId]);
1174
- }),
1175
- )
1176
- : [];
1177
-
1178
- // W-10029836 - In the case where we could be instantiating multiple graphs at the same time lets make sure the module have not already been evaluated
1179
- if (moduleRecord.evaluated) {
1180
- return moduleRecord.module;
1181
- }
1163
+
1164
+ // Evaluate all it's dependents
1165
+ const { depsMapped, exports } = await this.evaluateDependencies(dependencies, chainMap);
1182
1166
 
1183
1167
  // evaluates the module function
1184
- let moduleDefault;
1185
- try {
1186
- moduleDefault = exporter(...depsMapped);
1187
- } catch (e) {
1188
- throw new LoaderError(EXPORTER_ERROR, [moduleRecord.id, e.message || e]);
1189
- }
1168
+ let moduleDefault = this.evaluateModuleCode(exporter, depsMapped, moduleRecord);
1190
1169
  // value is returned from exporter, then we are not using named exports
1191
1170
  if (moduleDefault !== undefined) {
1192
1171
  moduleDefault = { default: moduleDefault };
@@ -1198,7 +1177,7 @@ class ModuleRegistry {
1198
1177
  // if no return value, then we are using the exports object
1199
1178
  else {
1200
1179
  // handle only default export with Rollup forced named exports
1201
- if (this.isNamedExportDefaultOnly(exports)) {
1180
+ if (isNamedExportDefaultOnly(exports)) {
1202
1181
  Object.defineProperty(exports, '__useDefault', { value: true });
1203
1182
  }
1204
1183
  }
@@ -1230,42 +1209,17 @@ class ModuleRegistry {
1230
1209
  Object.defineProperty(moduleRecord.module, '__esModule', { value: true });
1231
1210
  }
1232
1211
 
1233
- moduleRecord.evaluated = true;
1234
1212
  Object.freeze(moduleRecord.module);
1213
+ moduleRecord.evaluated = true;
1214
+ moduleRecord.evaluationPromise = null;
1235
1215
  return moduleRecord.module;
1236
1216
  }
1237
1217
 
1238
- // Determines if named exports module has only default export
1239
- isNamedExportDefaultOnly(exports) {
1240
- return (
1241
- exports !== undefined &&
1242
- Object.getOwnPropertyNames(exports).length === 2 &&
1243
- Object.prototype.hasOwnProperty.call(exports, 'default') &&
1244
- Object.prototype.hasOwnProperty.call(exports, '__esModule')
1245
- );
1246
- }
1247
-
1248
- // Wrap the dependency in a function that can be called and detected by __circular__ property.
1249
- // The LWC engine checks for __circular__ to detect circular dependencies.
1250
- getCircularDependencyWrapper(module) {
1251
- const tmp = () => {
1252
- return module.__useDefault || module.__defaultInterop ? module.default : module;
1253
- };
1254
- tmp.__circular__ = true;
1255
- return tmp;
1256
- }
1257
-
1258
- async evaluateModuleDependencies(
1259
- dependencyModuleRecords,
1260
- evaluationMap,
1261
- ) {
1262
- for (let i = 0; i < dependencyModuleRecords.length; i++) {
1263
- const depRecord = dependencyModuleRecords[i];
1264
- if (!depRecord.evaluated && !evaluationMap[depRecord.id]) {
1265
- evaluationMap[depRecord.id] = true;
1266
- // eslint-disable-next-line no-await-in-loop
1267
- await this.evaluateModule(depRecord, evaluationMap);
1268
- }
1218
+ evaluateModuleCode(evaluatedExporter, depsMapped, moduleRecord) {
1219
+ try {
1220
+ return evaluatedExporter(...depsMapped);
1221
+ } catch (e) {
1222
+ throw new LoaderError(EXPORTER_ERROR, [moduleRecord.id, e.message || e]);
1269
1223
  }
1270
1224
  }
1271
1225
 
@@ -1345,9 +1299,6 @@ class ModuleRegistry {
1345
1299
  });
1346
1300
  }
1347
1301
 
1348
-
1349
-
1350
-
1351
1302
  addLoaderPlugin(hooks) {
1352
1303
  if (typeof hooks !== 'object') {
1353
1304
  throw new LoaderError(INVALID_HOOK);
@@ -1389,7 +1340,6 @@ class ModuleRegistry {
1389
1340
  }
1390
1341
  }
1391
1342
 
1392
-
1393
1343
  registerHandleStaleModuleHook(handleStaleModule) {
1394
1344
  if (this.handleStaleModuleHook) {
1395
1345
  this.handleStaleModuleHook.push(handleStaleModule);
@@ -1397,12 +1347,30 @@ class ModuleRegistry {
1397
1347
  this.handleStaleModuleHook = [handleStaleModule];
1398
1348
  }
1399
1349
  }
1350
+ }
1400
1351
 
1401
- isValidResolveResponse(res) {
1402
- return (
1403
- res === null || typeof res === 'string' || (res && typeof (res ).url === 'string')
1404
- );
1405
- }
1352
+ // Determines if named exports module has only default export
1353
+ function isNamedExportDefaultOnly(exports) {
1354
+ return (
1355
+ exports !== undefined &&
1356
+ Object.getOwnPropertyNames(exports).length === 2 &&
1357
+ Object.prototype.hasOwnProperty.call(exports, 'default') &&
1358
+ Object.prototype.hasOwnProperty.call(exports, '__esModule')
1359
+ );
1360
+ }
1361
+
1362
+ // Wrap the dependency in a function that can be called and detected by __circular__ property.
1363
+ // The LWC engine checks for __circular__ to detect circular dependencies.
1364
+ function getCircularDependencyWrapper(module) {
1365
+ const tmp = () => {
1366
+ return module.__useDefault || module.__defaultInterop ? module.default : module;
1367
+ };
1368
+ tmp.__circular__ = true;
1369
+ return tmp;
1370
+ }
1371
+
1372
+ function isValidResolveResponse(res) {
1373
+ return res === null || typeof res === 'string' || (res && typeof (res ).url === 'string');
1406
1374
  }
1407
1375
 
1408
1376
  /**