@embroider/core 3.4.9 → 3.4.10-unstable.706d56f

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.
Files changed (43) hide show
  1. package/package.json +13 -11
  2. package/src/app-files.d.ts +2 -3
  3. package/src/app-files.js +1 -1
  4. package/src/app-files.js.map +1 -1
  5. package/src/asset.d.ts +2 -0
  6. package/src/describe-exports.js.map +1 -1
  7. package/src/ember-html.d.ts +2 -0
  8. package/src/ember-html.js.map +1 -1
  9. package/src/html-entrypoint.js.map +1 -1
  10. package/src/html-placeholder.js.map +1 -1
  11. package/src/index.d.ts +1 -1
  12. package/src/index.js.map +1 -1
  13. package/src/messages.js.map +1 -1
  14. package/src/module-resolver.d.ts +25 -15
  15. package/src/module-resolver.js +281 -191
  16. package/src/module-resolver.js.map +1 -1
  17. package/src/node-resolve.d.ts +33 -0
  18. package/src/node-resolve.js +131 -0
  19. package/src/node-resolve.js.map +1 -0
  20. package/src/options.js.map +1 -1
  21. package/src/packager.js.map +1 -1
  22. package/src/portable-babel-config.js.map +1 -1
  23. package/src/portable-babel-launcher.js.map +1 -1
  24. package/src/portable.js.map +1 -1
  25. package/src/resolver-loader.js.map +1 -1
  26. package/src/to-broccoli-plugin.js.map +1 -1
  27. package/src/virtual-content.d.ts +6 -2
  28. package/src/virtual-content.js +104 -42
  29. package/src/virtual-content.js.map +1 -1
  30. package/src/virtual-test-support-styles.d.ts +4 -0
  31. package/src/virtual-test-support-styles.js +65 -0
  32. package/src/virtual-test-support-styles.js.map +1 -0
  33. package/src/virtual-test-support.d.ts +4 -0
  34. package/src/virtual-test-support.js +69 -0
  35. package/src/virtual-test-support.js.map +1 -0
  36. package/src/virtual-vendor-styles.d.ts +4 -0
  37. package/src/virtual-vendor-styles.js +72 -0
  38. package/src/virtual-vendor-styles.js.map +1 -0
  39. package/src/virtual-vendor.d.ts +4 -0
  40. package/src/virtual-vendor.js +73 -0
  41. package/src/virtual-vendor.js.map +1 -0
  42. package/src/wait-for-trees.js.map +1 -1
  43. package/LICENSE +0 -21
@@ -15,63 +15,59 @@ const path_1 = require("path");
15
15
  const shared_internals_2 = require("@embroider/shared-internals");
16
16
  const debug_1 = __importDefault(require("debug"));
17
17
  const assert_never_1 = __importDefault(require("assert-never"));
18
- const resolve_1 = __importDefault(require("resolve"));
18
+ const reverse_exports_1 = __importDefault(require("@embroider/reverse-exports"));
19
+ const resolve_exports_1 = require("resolve.exports");
19
20
  const virtual_content_1 = require("./virtual-content");
20
21
  const typescript_memoize_1 = require("typescript-memoize");
21
22
  const describe_exports_1 = require("./describe-exports");
22
23
  const fs_1 = require("fs");
24
+ const node_resolve_1 = require("./node-resolve");
23
25
  const debug = (0, debug_1.default)('embroider:resolver');
26
+ // Using a formatter makes this work lazy so nothing happens when we aren't
27
+ // logging. It is unfortunate that formatters are a globally mutable config and
28
+ // you can only use single character names, but oh well.
29
+ debug_1.default.formatters.p = (s) => {
30
+ let cwd = process.cwd();
31
+ if (s.startsWith(cwd)) {
32
+ return s.slice(cwd.length + 1);
33
+ }
34
+ return s;
35
+ };
24
36
  function logTransition(reason, before, after = before) {
25
37
  if (after.isVirtual) {
26
- debug(`virtualized %s in %s because %s`, before.specifier, before.fromFile, reason);
38
+ debug(`[%s:virtualized] %s because %s\n in %p`, before.debugType, before.specifier, reason, before.fromFile);
39
+ }
40
+ else if (after.resolvedTo) {
41
+ debug(`[%s:resolvedTo] %s because %s\n in %p`, before.debugType, before.specifier, reason, before.fromFile);
27
42
  }
28
43
  else if (before.specifier !== after.specifier) {
29
44
  if (before.fromFile !== after.fromFile) {
30
- debug(`aliased and rehomed: %s to %s, from %s to %s because %s`, before.specifier, after.specifier, before.fromFile, after.fromFile, reason);
45
+ debug(`[%s:aliased and rehomed] %s to %s\n because %s\n from %p\n to %p`, before.debugType, before.specifier, after.specifier, reason, before.fromFile, after.fromFile);
31
46
  }
32
47
  else {
33
- debug(`aliased: %s to %s in %s because`, before.specifier, after.specifier, before.fromFile, reason);
48
+ debug(`[%s:aliased] %s to %s\n because %s`, before.debugType, before.specifier, after.specifier, reason);
34
49
  }
35
50
  }
36
51
  else if (before.fromFile !== after.fromFile) {
37
- debug(`rehomed: %s from %s to %s because`, before.specifier, before.fromFile, after.fromFile, reason);
52
+ debug(`[%s:rehomed] %s, because %s\n from %p\n to %p`, before.debugType, before.specifier, reason, before.fromFile, after.fromFile);
53
+ }
54
+ else if (after.isNotFound) {
55
+ debug(`[%s:not-found] %s because %s\n in %p`, before.debugType, before.specifier, reason, before.fromFile);
38
56
  }
39
57
  else {
40
- debug(`unchanged: %s in %s because %s`, before.specifier, before.fromFile, reason);
58
+ debug(`[%s:unchanged] %s because %s\n in %p`, before.debugType, before.specifier, reason, before.fromFile);
41
59
  }
42
60
  return after;
43
61
  }
44
- const compatPattern = /#embroider_compat\/(?<type>[^\/]+)\/(?<rest>.*)/;
45
- class NodeModuleRequest {
46
- constructor(specifier, fromFile, isVirtual, meta) {
47
- this.specifier = specifier;
48
- this.fromFile = fromFile;
49
- this.isVirtual = isVirtual;
50
- this.meta = meta;
51
- }
52
- alias(specifier) {
53
- return new NodeModuleRequest(specifier, this.fromFile, false, this.meta);
54
- }
55
- rehome(fromFile) {
56
- if (this.fromFile === fromFile) {
57
- return this;
58
- }
59
- else {
60
- return new NodeModuleRequest(this.specifier, fromFile, false, this.meta);
61
- }
62
- }
63
- virtualize(filename) {
64
- return new NodeModuleRequest(filename, this.fromFile, true, this.meta);
65
- }
66
- withMeta(meta) {
67
- return new NodeModuleRequest(this.specifier, this.fromFile, this.isVirtual, meta);
68
- }
62
+ function isTerminal(request) {
63
+ return request.isVirtual || request.isNotFound || Boolean(request.resolvedTo);
69
64
  }
65
+ const compatPattern = /#embroider_compat\/(?<type>[^\/]+)\/(?<rest>.*)/;
70
66
  class Resolver {
71
67
  constructor(options) {
72
68
  this.options = options;
73
69
  }
74
- beforeResolve(request) {
70
+ async beforeResolve(request) {
75
71
  if (request.specifier === '@embroider/macros') {
76
72
  // the macros package is always handled directly within babel (not
77
73
  // necessarily as a real resolvable package), so we should not mess with it.
@@ -79,10 +75,17 @@ class Resolver {
79
75
  // why we need to know about it.
80
76
  return logTransition('early exit', request);
81
77
  }
78
+ if (request.specifier === 'require') {
79
+ return this.external('early require', request, request.specifier);
80
+ }
82
81
  request = this.handleFastbootSwitch(request);
83
- request = this.handleGlobalsCompat(request);
82
+ request = await this.handleGlobalsCompat(request);
84
83
  request = this.handleImplicitModules(request);
84
+ request = this.handleImplicitTestScripts(request);
85
+ request = this.handleVendorStyles(request);
86
+ request = this.handleTestSupportStyles(request);
85
87
  request = this.handleRenaming(request);
88
+ request = this.handleVendor(request);
86
89
  // we expect the specifier to be app relative at this point - must be after handleRenaming
87
90
  request = this.generateFastbootSwitch(request);
88
91
  request = this.preHandleExternal(request);
@@ -96,95 +99,45 @@ class Resolver {
96
99
  // that calls your build system's normal module resolver, this does both pre-
97
100
  // and post-resolution adjustments as needed to implement our compatibility
98
101
  // rules.
99
- //
100
- // Depending on the plugin architecture you're working in, it may be easier to
101
- // call beforeResolve and fallbackResolve directly, in which case matching the
102
- // details of the recursion to what this method does are your responsibility.
103
- async resolve(request, defaultResolve) {
104
- let gen = this.internalResolve(request, defaultResolve);
105
- let out = gen.next();
106
- while (!out.done) {
107
- out = gen.next(await out.value);
108
- }
109
- return out.value;
110
- }
111
- // synchronous alternative to resolve() above. Because our own internals are
112
- // all synchronous, you can use this if your defaultResolve function is
113
- // synchronous.
114
- resolveSync(request, defaultResolve) {
115
- let gen = this.internalResolve(request, defaultResolve);
116
- let out = gen.next();
117
- while (!out.done) {
118
- out = gen.next(out.value);
119
- }
120
- return out.value;
121
- }
122
- // Our core implementation is a generator so it can power both resolve() and
123
- // resolveSync()
124
- *internalResolve(request, defaultResolve) {
125
- request = this.beforeResolve(request);
126
- let resolution = yield defaultResolve(request);
102
+ async resolve(request) {
103
+ request = await this.beforeResolve(request);
104
+ if (request.resolvedTo) {
105
+ return request.resolvedTo;
106
+ }
107
+ let resolution = await request.defaultResolve();
127
108
  switch (resolution.type) {
128
109
  case 'found':
110
+ case 'ignored':
129
111
  return resolution;
130
112
  case 'not_found':
131
113
  break;
132
114
  default:
133
115
  throw (0, assert_never_1.default)(resolution);
134
116
  }
135
- let nextRequest = this.fallbackResolve(request);
117
+ let nextRequest = await this.fallbackResolve(request);
136
118
  if (nextRequest === request) {
137
119
  // no additional fallback is available.
138
120
  return resolution;
139
121
  }
122
+ if (nextRequest.resolvedTo) {
123
+ return nextRequest.resolvedTo;
124
+ }
140
125
  if (nextRequest.fromFile === request.fromFile && nextRequest.specifier === request.specifier) {
141
126
  throw new Error('Bug Discovered! New request is not === original request but has the same fromFile and specifier. This will likely create a loop.');
142
127
  }
143
- if (nextRequest.isVirtual) {
144
- // virtual requests are terminal, there is no more beforeResolve or
145
- // fallbackResolve around them. The defaultResolve is expected to know how
146
- // to implement them.
147
- return yield defaultResolve(nextRequest);
128
+ if (nextRequest.isVirtual || nextRequest.isNotFound) {
129
+ // virtual and NotFound requests are terminal, there is no more
130
+ // beforeResolve or fallbackResolve around them. The defaultResolve is
131
+ // expected to know how to implement them.
132
+ return nextRequest.defaultResolve();
148
133
  }
149
- return yield* this.internalResolve(nextRequest, defaultResolve);
134
+ return this.resolve(nextRequest);
150
135
  }
151
136
  // Use standard NodeJS resolving, with our required compatibility rules on
152
137
  // top. This is a convenience method for calling resolveSync with the
153
138
  // defaultResolve already configured to be "do the normal node thing".
154
- nodeResolve(specifier, fromFile) {
155
- let resolution = this.resolveSync(new NodeModuleRequest(specifier, fromFile, false, undefined), request => {
156
- if (request.isVirtual) {
157
- return {
158
- type: 'found',
159
- result: {
160
- type: 'virtual',
161
- content: (0, virtual_content_1.virtualContent)(request.specifier, this),
162
- filename: request.specifier,
163
- },
164
- };
165
- }
166
- try {
167
- let filename = resolve_1.default.sync(request.specifier, {
168
- basedir: (0, path_1.dirname)(request.fromFile),
169
- extensions: this.options.resolvableExtensions,
170
- });
171
- return { type: 'found', result: { type: 'real', filename } };
172
- }
173
- catch (err) {
174
- if (err.code !== 'MODULE_NOT_FOUND') {
175
- throw err;
176
- }
177
- return { type: 'not_found', err };
178
- }
179
- });
180
- switch (resolution.type) {
181
- case 'not_found':
182
- return resolution;
183
- case 'found':
184
- return resolution.result;
185
- default:
186
- throw (0, assert_never_1.default)(resolution);
187
- }
139
+ async nodeResolve(specifier, fromFile) {
140
+ return (0, node_resolve_1.nodeResolve)(this, specifier, fromFile);
188
141
  }
189
142
  get packageCache() {
190
143
  return shared_internals_2.RewrittenPackageCache.shared('embroider', this.options.appRoot);
@@ -201,6 +154,9 @@ class Resolver {
201
154
  return owningPackage;
202
155
  }
203
156
  generateFastbootSwitch(request) {
157
+ if (isTerminal(request)) {
158
+ return request;
159
+ }
204
160
  let pkg = this.packageCache.ownerOfFile(request.fromFile);
205
161
  if (!pkg) {
206
162
  return request;
@@ -218,7 +174,9 @@ class Resolver {
218
174
  let fastbootFile = engineConfig.fastbootFiles[candidate];
219
175
  if (fastbootFile) {
220
176
  if (fastbootFile.shadowedFilename) {
221
- let { names } = (0, describe_exports_1.describeExports)((0, fs_1.readFileSync)((0, path_1.resolve)(pkg.root, fastbootFile.shadowedFilename), 'utf8'), {});
177
+ let { names } = (0, describe_exports_1.describeExports)((0, fs_1.readFileSync)((0, path_1.resolve)(pkg.root, fastbootFile.shadowedFilename), 'utf8'), {
178
+ configFile: false,
179
+ });
222
180
  let switchFile = (0, virtual_content_1.fastbootSwitch)(candidate, (0, path_1.resolve)(pkg.root, 'package.json'), names);
223
181
  if (switchFile === request.fromFile) {
224
182
  return logTransition('internal lookup from fastbootSwitch', request);
@@ -237,6 +195,9 @@ class Resolver {
237
195
  }
238
196
  handleFastbootSwitch(request) {
239
197
  var _a;
198
+ if (isTerminal(request)) {
199
+ return request;
200
+ }
240
201
  let match = (0, virtual_content_1.decodeFastbootSwitch)(request.fromFile);
241
202
  if (!match) {
242
203
  return request;
@@ -275,12 +236,15 @@ class Resolver {
275
236
  }
276
237
  let entry = (_a = this.getEntryFromMergeMap(rel, pkg.root)) === null || _a === void 0 ? void 0 : _a.entry;
277
238
  if ((entry === null || entry === void 0 ? void 0 : entry.type) === 'both') {
278
- return logTransition('matched addon entry', request, request.alias(entry[section].localPath).rehome((0, path_1.resolve)(entry[section].packageRoot, 'package.json')));
239
+ return logTransition('matched addon entry', request, request.alias(entry[section].specifier).rehome(entry[section].fromFile));
279
240
  }
280
241
  }
281
242
  return logTransition('failed to match in fastboot switch', request);
282
243
  }
283
244
  handleImplicitModules(request) {
245
+ if (isTerminal(request)) {
246
+ return request;
247
+ }
284
248
  let im = (0, virtual_content_1.decodeImplicitModules)(request.specifier);
285
249
  if (!im) {
286
250
  return request;
@@ -298,7 +262,42 @@ class Resolver {
298
262
  return logTransition(`own implicit modules`, request, request.virtualize((0, path_1.resolve)(pkg.root, `-embroider-${im.type}.js`)));
299
263
  }
300
264
  }
301
- handleGlobalsCompat(request) {
265
+ handleImplicitTestScripts(request) {
266
+ //TODO move the extra forwardslash handling out into the vite plugin
267
+ const candidates = [
268
+ '@embroider/core/test-support.js',
269
+ '/@embroider/core/test-support.js',
270
+ './@embroider/core/test-support.js',
271
+ ];
272
+ if (!candidates.includes(request.specifier)) {
273
+ return request;
274
+ }
275
+ let pkg = this.packageCache.ownerOfFile(request.fromFile);
276
+ if ((pkg === null || pkg === void 0 ? void 0 : pkg.root) !== this.options.engines[0].root) {
277
+ throw new Error(`bug: found an import of ${request.specifier} in ${request.fromFile}, but this is not the top-level Ember app. The top-level Ember app is the only one that has support for @embroider/core/test-support.js. If you think something should be fixed in Embroider, please open an issue on https://github.com/embroider-build/embroider/issues.`);
278
+ }
279
+ return logTransition('test-support', request, request.virtualize((0, path_1.resolve)(pkg.root, '-embroider-test-support.js')));
280
+ }
281
+ handleTestSupportStyles(request) {
282
+ //TODO move the extra forwardslash handling out into the vite plugin
283
+ const candidates = [
284
+ '@embroider/core/test-support.css',
285
+ '/@embroider/core/test-support.css',
286
+ './@embroider/core/test-support.css',
287
+ ];
288
+ if (!candidates.includes(request.specifier)) {
289
+ return request;
290
+ }
291
+ let pkg = this.packageCache.ownerOfFile(request.fromFile);
292
+ if ((pkg === null || pkg === void 0 ? void 0 : pkg.root) !== this.options.engines[0].root) {
293
+ throw new Error(`bug: found an import of ${request.specifier} in ${request.fromFile}, but this is not the top-level Ember app. The top-level Ember app is the only one that has support for @embroider/core/test-support.css. If you think something should be fixed in Embroider, please open an issue on https://github.com/embroider-build/embroider/issues.`);
294
+ }
295
+ return logTransition('test-support-styles', request, request.virtualize((0, path_1.resolve)(pkg.root, '-embroider-test-support-styles.css')));
296
+ }
297
+ async handleGlobalsCompat(request) {
298
+ if (isTerminal(request)) {
299
+ return request;
300
+ }
302
301
  let match = compatPattern.exec(request.specifier);
303
302
  if (!match) {
304
303
  return request;
@@ -322,56 +321,76 @@ class Resolver {
322
321
  throw new Error(`bug: unexepected #embroider_compat specifier: ${request.specifier}`);
323
322
  }
324
323
  }
324
+ handleVendorStyles(request) {
325
+ //TODO move the extra forwardslash handling out into the vite plugin
326
+ const candidates = ['@embroider/core/vendor.css', '/@embroider/core/vendor.css', './@embroider/core/vendor.css'];
327
+ if (!candidates.includes(request.specifier)) {
328
+ return request;
329
+ }
330
+ let pkg = this.packageCache.ownerOfFile(request.fromFile);
331
+ if ((pkg === null || pkg === void 0 ? void 0 : pkg.root) !== this.options.engines[0].root) {
332
+ throw new Error(`bug: found an import of ${request.specifier} in ${request.fromFile}, but this is not the top-level Ember app. The top-level Ember app is the only one that has support for @embroider/core/vendor.css. If you think something should be fixed in Embroider, please open an issue on https://github.com/embroider-build/embroider/issues.`);
333
+ }
334
+ return logTransition('vendor-styles', request, request.virtualize((0, path_1.resolve)(pkg.root, '-embroider-vendor-styles.css')));
335
+ }
325
336
  resolveHelper(path, inEngine, request) {
326
337
  let target = this.parseGlobalPath(path, inEngine);
327
338
  return logTransition('resolveHelper', request, request.alias(`${target.packageName}/helpers/${target.memberName}`).rehome((0, path_1.resolve)(inEngine.root, 'package.json')));
328
339
  }
329
- resolveComponent(path, inEngine, request) {
340
+ async resolveComponent(path, inEngine, request) {
330
341
  let target = this.parseGlobalPath(path, inEngine);
331
342
  let hbsModule = null;
332
343
  let jsModule = null;
333
344
  // first, the various places our template might be.
334
345
  for (let candidate of this.componentTemplateCandidates(target.packageName)) {
335
- let resolution = this.nodeResolve(`${target.packageName}${candidate.prefix}${target.memberName}${candidate.suffix}`, target.from);
336
- if (resolution.type === 'real') {
337
- hbsModule = resolution.filename;
346
+ let candidateSpecifier = `${target.packageName}${candidate.prefix}${target.memberName}${candidate.suffix}`;
347
+ let resolution = await this.resolve(request.alias(candidateSpecifier).rehome(target.from).withMeta({
348
+ runtimeFallback: false,
349
+ }));
350
+ if (resolution.type === 'found') {
351
+ hbsModule = resolution;
338
352
  break;
339
353
  }
340
354
  }
341
355
  // then the various places our javascript might be.
342
356
  for (let candidate of this.componentJSCandidates(target.packageName)) {
343
- let resolution = this.nodeResolve(`${target.packageName}${candidate.prefix}${target.memberName}${candidate.suffix}`, target.from);
357
+ let candidateSpecifier = `${target.packageName}${candidate.prefix}${target.memberName}${candidate.suffix}`;
358
+ let resolution = await this.resolve(request.alias(candidateSpecifier).rehome(target.from).withMeta({
359
+ runtimeFallback: false,
360
+ }));
344
361
  // .hbs is a resolvable extension for us, so we need to exclude it here.
345
362
  // It matches as a priority lower than .js, so finding an .hbs means
346
363
  // there's definitely not a .js.
347
- if (resolution.type === 'real' && !resolution.filename.endsWith('.hbs')) {
348
- jsModule = resolution.filename;
364
+ if (resolution.type === 'found' && !resolution.filename.endsWith('.hbs')) {
365
+ jsModule = resolution;
349
366
  break;
350
367
  }
351
368
  }
352
369
  if (hbsModule) {
353
- return logTransition(`resolveComponent found legacy HBS`, request, request.virtualize((0, virtual_content_1.virtualPairComponent)(hbsModule, jsModule)));
370
+ return logTransition(`resolveComponent found legacy HBS`, request, request.virtualize((0, virtual_content_1.virtualPairComponent)(hbsModule.filename, jsModule === null || jsModule === void 0 ? void 0 : jsModule.filename)));
354
371
  }
355
372
  else if (jsModule) {
356
- return logTransition(`resolveComponent found only JS`, request, request.alias(jsModule).rehome(target.from));
373
+ return logTransition(`resolving to resolveComponent found only JS`, request, request.resolveTo(jsModule));
357
374
  }
358
375
  else {
359
376
  return logTransition(`resolveComponent failed`, request);
360
377
  }
361
378
  }
362
- resolveHelperOrComponent(path, inEngine, request) {
379
+ async resolveHelperOrComponent(path, inEngine, request) {
363
380
  // resolveHelper just rewrites our request to one that should target the
364
381
  // component, so here to resolve the ambiguity we need to actually resolve
365
382
  // that candidate to see if it works.
366
383
  let helperCandidate = this.resolveHelper(path, inEngine, request);
367
- let helperMatch = this.nodeResolve(helperCandidate.specifier, helperCandidate.fromFile);
368
- if (helperMatch.type === 'real') {
369
- return logTransition('ambiguous case matched a helper', request, helperCandidate);
384
+ let helperMatch = await this.resolve(request.alias(helperCandidate.specifier).rehome(helperCandidate.fromFile).withMeta({
385
+ runtimeFallback: false,
386
+ }));
387
+ if (helperMatch.type === 'found') {
388
+ return logTransition('resolve to ambiguous case matched a helper', request, request.resolveTo(helperMatch));
370
389
  }
371
390
  // unlike resolveHelper, resolveComponent already does pre-resolution in
372
391
  // order to deal with its own internal ambiguity around JS vs HBS vs
373
392
  // colocation.≥
374
- let componentMatch = this.resolveComponent(path, inEngine, request);
393
+ let componentMatch = await this.resolveComponent(path, inEngine, request);
375
394
  if (componentMatch !== request) {
376
395
  return logTransition('ambiguous case matched a cmoponent', request, componentMatch);
377
396
  }
@@ -396,6 +415,7 @@ class Resolver {
396
415
  }
397
416
  *componentJSCandidates(inPackageName) {
398
417
  yield { prefix: '/components/', suffix: '' };
418
+ yield { prefix: '/components/', suffix: '/index' };
399
419
  yield { prefix: '/components/', suffix: '/component' };
400
420
  let pods = this.podPrefix(inPackageName);
401
421
  if (pods) {
@@ -414,10 +434,10 @@ class Resolver {
414
434
  parseGlobalPath(path, inEngine) {
415
435
  let parts = path.split('@');
416
436
  if (parts.length > 1 && parts[0].length > 0) {
417
- return { packageName: parts[0], memberName: parts[1], from: (0, path_1.resolve)(inEngine.root, 'pacakge.json') };
437
+ return { packageName: parts[0], memberName: parts[1], from: (0, path_1.resolve)(inEngine.root, 'package.json') };
418
438
  }
419
439
  else {
420
- return { packageName: inEngine.packageName, memberName: path, from: (0, path_1.resolve)(inEngine.root, 'pacakge.json') };
440
+ return { packageName: inEngine.packageName, memberName: path, from: (0, path_1.resolve)(inEngine.root, 'package.json') };
421
441
  }
422
442
  }
423
443
  engineConfig(packageName) {
@@ -449,8 +469,8 @@ class Resolver {
449
469
  engineModules.set(inEngineName, {
450
470
  type: 'app-only',
451
471
  'app-js': {
452
- localPath: inAddonName,
453
- packageRoot: addon.root,
472
+ specifier: (0, reverse_exports_1.default)(addon.packageJSON, inAddonName),
473
+ fromFile: addonConfig.canResolveFromFile,
454
474
  fromPackageName: addon.name,
455
475
  },
456
476
  });
@@ -463,8 +483,8 @@ class Resolver {
463
483
  engineModules.set(inEngineName, {
464
484
  type: 'both',
465
485
  'app-js': {
466
- localPath: inAddonName,
467
- packageRoot: addon.root,
486
+ specifier: (0, reverse_exports_1.default)(addon.packageJSON, inAddonName),
487
+ fromFile: addonConfig.canResolveFromFile,
468
488
  fromPackageName: addon.name,
469
489
  },
470
490
  'fastboot-js': prevEntry['fastboot-js'],
@@ -488,8 +508,8 @@ class Resolver {
488
508
  engineModules.set(inEngineName, {
489
509
  type: 'fastboot-only',
490
510
  'fastboot-js': {
491
- localPath: inAddonName,
492
- packageRoot: addon.root,
511
+ specifier: (0, reverse_exports_1.default)(addon.packageJSON, inAddonName),
512
+ fromFile: addonConfig.canResolveFromFile,
493
513
  fromPackageName: addon.name,
494
514
  },
495
515
  });
@@ -502,8 +522,8 @@ class Resolver {
502
522
  engineModules.set(inEngineName, {
503
523
  type: 'both',
504
524
  'fastboot-js': {
505
- localPath: inAddonName,
506
- packageRoot: addon.root,
525
+ specifier: (0, reverse_exports_1.default)(addon.packageJSON, inAddonName),
526
+ fromFile: addonConfig.canResolveFromFile,
507
527
  fromPackageName: addon.name,
508
528
  },
509
529
  'app-js': prevEntry['app-js'],
@@ -525,7 +545,7 @@ class Resolver {
525
545
  return owningEngine;
526
546
  }
527
547
  handleRewrittenPackages(request) {
528
- if (request.isVirtual) {
548
+ if (isTerminal(request)) {
529
549
  return request;
530
550
  }
531
551
  let requestingPkg = this.packageCache.ownerOfFile(request.fromFile);
@@ -544,10 +564,6 @@ class Resolver {
544
564
  targetPkg = this.packageCache.resolve(packageName, requestingPkg);
545
565
  }
546
566
  catch (err) {
547
- // this is not the place to report resolution failures. If the thing
548
- // doesn't resolve, we're just not interested in redirecting it for
549
- // backward-compat, that's all. The rest of the system will take care of
550
- // reporting a failure to resolve (or handling it a different way)
551
567
  if (err.code !== 'MODULE_NOT_FOUND') {
552
568
  throw err;
553
569
  }
@@ -563,14 +579,26 @@ class Resolver {
563
579
  return logTransition('request targets a moved package', request, this.resolveWithinMovedPackage(request, targetPkg));
564
580
  }
565
581
  else if (originalRequestingPkg !== requestingPkg) {
566
- // in this case, the requesting package is moved but its destination is
567
- // not, so we need to rehome the request back to the original location.
568
- return logTransition('outbound request from moved package', request, request.withMeta({ wasMovedTo: request.fromFile }).rehome((0, path_1.resolve)(originalRequestingPkg.root, 'package.json')));
582
+ if (targetPkg) {
583
+ // in this case, the requesting package is moved but its destination is
584
+ // not, so we need to rehome the request back to the original location.
585
+ return logTransition('outbound request from moved package', request, request
586
+ // setting meta here because if this fails, we want the fallback
587
+ // logic to revert our rehome and continue from the *moved* package.
588
+ .withMeta({ originalFromFile: request.fromFile })
589
+ .rehome((0, path_1.resolve)(originalRequestingPkg.root, 'package.json')));
590
+ }
591
+ else {
592
+ // requesting package was moved and we failed to find its target. We
593
+ // can't let that accidentally succeed in the defaultResolve because we
594
+ // could escape the moved package system.
595
+ return logTransition('missing outbound request from moved package', request, request.notFound());
596
+ }
569
597
  }
570
598
  return request;
571
599
  }
572
600
  handleRenaming(request) {
573
- if (request.isVirtual) {
601
+ if (isTerminal(request)) {
574
602
  return request;
575
603
  }
576
604
  let packageName = (0, shared_internals_1.packageName)(request.specifier);
@@ -603,30 +631,65 @@ class Resolver {
603
631
  return logTransition(`renamePackages`, request, request.alias(request.specifier.replace(packageName, this.options.renamePackages[packageName])));
604
632
  }
605
633
  }
606
- if (pkg.meta['auto-upgraded'] && pkg.name === packageName) {
607
- // we found a self-import, resolve it for them. Only auto-upgraded
608
- // packages get this help, v2 packages are natively supposed to make their
609
- // own modules resolvable, and we want to push them all to do that
610
- // correctly.
611
- return logTransition(`v1 self-import`, request, request.alias(request.specifier.replace(pkg.name, '.')).rehome((0, path_1.resolve)(pkg.root, 'package.json')));
634
+ if (pkg.name === packageName) {
635
+ // we found a self-import
636
+ if (pkg.meta['auto-upgraded']) {
637
+ // auto-upgraded packages always get automatically adjusted. They never
638
+ // supported fancy package.json exports features so this direct mapping
639
+ // to the root is always right.
640
+ // "my-package/foo" -> "./foo"
641
+ // "my-package" -> "./" (this can't be just "." because node's require.resolve doesn't reliable support that)
642
+ let selfImportPath = request.specifier === pkg.name ? './' : request.specifier.replace(pkg.name, '.');
643
+ return logTransition(`v1 self-import`, request, request.alias(selfImportPath).rehome((0, path_1.resolve)(pkg.root, 'package.json')));
644
+ }
645
+ else {
646
+ // v2 packages are supposed to use package.json `exports` to enable
647
+ // self-imports, but not all build tools actually follow the spec. This
648
+ // is a workaround for badly behaved packagers.
649
+ //
650
+ // Known upstream bugs this works around:
651
+ // - https://github.com/vitejs/vite/issues/9731
652
+ if (pkg.packageJSON.exports) {
653
+ let found = (0, resolve_exports_1.exports)(pkg.packageJSON, request.specifier, {
654
+ browser: true,
655
+ conditions: ['default', 'imports'],
656
+ });
657
+ if (found === null || found === void 0 ? void 0 : found[0]) {
658
+ return logTransition(`v2 self-import with package.json exports`, request, request.alias(found === null || found === void 0 ? void 0 : found[0]).rehome((0, path_1.resolve)(pkg.root, 'package.json')));
659
+ }
660
+ }
661
+ }
612
662
  }
613
663
  return request;
614
664
  }
665
+ handleVendor(request) {
666
+ //TODO move the extra forwardslash handling out into the vite plugin
667
+ const candidates = ['@embroider/core/vendor.js', '/@embroider/core/vendor.js', './@embroider/core/vendor.js'];
668
+ if (!candidates.includes(request.specifier)) {
669
+ return request;
670
+ }
671
+ let pkg = this.packageCache.ownerOfFile(request.fromFile);
672
+ if ((pkg === null || pkg === void 0 ? void 0 : pkg.root) !== this.options.engines[0].root) {
673
+ throw new Error(`bug: found an import of ${request.specifier} in ${request.fromFile}, but this is not the top-level Ember app. The top-level Ember app is the only one that has support for @embroider/core/vendor.js. If you think something should be fixed in Embroider, please open an issue on https://github.com/embroider-build/embroider/issues.`);
674
+ }
675
+ return logTransition('vendor', request, request.virtualize((0, path_1.resolve)(pkg.root, '-embroider-vendor.js')));
676
+ }
615
677
  resolveWithinMovedPackage(request, pkg) {
616
678
  let levels = ['..'];
617
679
  if (pkg.name.startsWith('@')) {
618
680
  levels.push('..');
619
681
  }
682
+ let originalFromFile = request.fromFile;
620
683
  let newRequest = request.rehome((0, path_1.resolve)(pkg.root, ...levels, 'moved-package-target.js'));
621
684
  if (newRequest === request) {
622
685
  return request;
623
686
  }
624
- return newRequest.withMeta({
625
- resolvedWithinPackage: pkg.root,
626
- });
687
+ // setting meta because if this fails, we want the fallback to pick up back
688
+ // in the original requesting package.
689
+ return newRequest.withMeta({ originalFromFile });
627
690
  }
628
691
  preHandleExternal(request) {
629
- if (request.isVirtual) {
692
+ if (isTerminal(request)) {
630
693
  return request;
631
694
  }
632
695
  let { specifier, fromFile } = request;
@@ -659,7 +722,15 @@ class Resolver {
659
722
  // engine
660
723
  let logicalLocation = this.reverseSearchAppTree(pkg, request.fromFile);
661
724
  if (logicalLocation) {
662
- return logTransition('beforeResolve: relative import in app-js', request, request.rehome((0, path_1.resolve)(logicalLocation.owningEngine.root, logicalLocation.inAppName)));
725
+ return logTransition('beforeResolve: relative import in app-js', request, request
726
+ .alias('./' + path_1.posix.join((0, path_1.dirname)(logicalLocation.inAppName), request.specifier))
727
+ // it's important that we're rehoming this to the root of the engine
728
+ // (which we know really exists), and not to a subdir like
729
+ // logicalLocation.inAppName (which might not physically exist),
730
+ // because some environments (including node's require.resolve) will
731
+ // refuse to do resolution from a notional path that doesn't
732
+ // physically exist.
733
+ .rehome((0, path_1.resolve)(logicalLocation.owningEngine.root, 'package.json')));
663
734
  }
664
735
  return request;
665
736
  }
@@ -674,11 +745,11 @@ class Resolver {
674
745
  if (shared_internals_1.emberVirtualPeerDeps.has(packageName) && !pkg.hasDependency(packageName)) {
675
746
  // addons (whether auto-upgraded or not) may use the app's
676
747
  // emberVirtualPeerDeps, like "@glimmer/component" etc.
677
- if (!this.options.activeAddons[packageName]) {
678
- throw new Error(`${pkg.name} is trying to import the app's ${packageName} package, but it seems to be missing`);
748
+ let addon = this.locateActiveAddon(packageName);
749
+ if (!addon) {
750
+ throw new Error(`${pkg.name} is trying to import the emberVirtualPeerDep "${packageName}", but it seems to be missing`);
679
751
  }
680
- let newHome = (0, path_1.resolve)(this.packageCache.maybeMoved(this.packageCache.get(this.options.appRoot)).root, 'package.json');
681
- return logTransition(`emberVirtualPeerDeps in v2 addon`, request, request.rehome(newHome));
752
+ return logTransition(`emberVirtualPeerDeps`, request, request.rehome(addon.canResolveFromFile));
682
753
  }
683
754
  // if this file is part of an addon's app-js, it's really the logical
684
755
  // package to which it belongs (normally the app) that affects some policy
@@ -709,6 +780,22 @@ class Resolver {
709
780
  }
710
781
  return request;
711
782
  }
783
+ locateActiveAddon(packageName) {
784
+ if (packageName === this.options.modulePrefix) {
785
+ // the app itself is something that addon's can classically resolve if they know it's name.
786
+ return {
787
+ root: this.options.appRoot,
788
+ canResolveFromFile: (0, path_1.resolve)(this.packageCache.maybeMoved(this.packageCache.get(this.options.appRoot)).root, 'package.json'),
789
+ };
790
+ }
791
+ for (let engine of this.options.engines) {
792
+ for (let addon of engine.activeAddons) {
793
+ if (addon.name === packageName) {
794
+ return addon;
795
+ }
796
+ }
797
+ }
798
+ }
712
799
  external(label, request, specifier) {
713
800
  if (this.options.amdCompatibility === 'cjs') {
714
801
  let filename = (0, virtual_content_1.virtualExternalCJSModule)(specifier);
@@ -741,8 +828,11 @@ class Resolver {
741
828
  throw new Error(`Embroider's amdCompatibility option is disabled, but something tried to use it to access "${request.specifier}"`);
742
829
  }
743
830
  }
744
- fallbackResolve(request) {
831
+ async fallbackResolve(request) {
745
832
  var _a, _b, _c;
833
+ if (request.isVirtual) {
834
+ throw new Error('Build tool bug detected! Fallback resolve should never see a virtual request. It is expected that the defaultResolve for your bundler has already resolved this request');
835
+ }
746
836
  if (request.specifier === '@embroider/macros') {
747
837
  // the macros package is always handled directly within babel (not
748
838
  // necessarily as a real resolvable package), so we should not mess with it.
@@ -750,8 +840,7 @@ class Resolver {
750
840
  // why we need to know about it.
751
841
  return logTransition('fallback early exit', request);
752
842
  }
753
- let { specifier, fromFile } = request;
754
- if (compatPattern.test(specifier)) {
843
+ if (compatPattern.test(request.specifier)) {
755
844
  // Some kinds of compat requests get rewritten into other things
756
845
  // deterministically. For example, "#embroider_compat/helpers/whatever"
757
846
  // means only "the-current-engine/helpers/whatever", and if that doesn't
@@ -767,39 +856,33 @@ class Resolver {
767
856
  // here.
768
857
  return request;
769
858
  }
770
- if (fromFile.endsWith('moved-package-target.js')) {
771
- if (!((_a = request.meta) === null || _a === void 0 ? void 0 : _a.resolvedWithinPackage)) {
772
- throw new Error(`bug: embroider resolver's meta is not propagating`);
773
- }
774
- fromFile = (0, path_1.resolve)((_b = request.meta) === null || _b === void 0 ? void 0 : _b.resolvedWithinPackage, 'package.json');
775
- }
776
- let pkg = this.packageCache.ownerOfFile(fromFile);
859
+ let pkg = this.packageCache.ownerOfFile(request.fromFile);
777
860
  if (!pkg) {
778
861
  return logTransition('no identifiable owningPackage', request);
779
862
  }
780
- // if we rehomed this request to its un-rewritten location in order to try
781
- // to do the defaultResolve from there, now we refer back to the rewritten
782
- // location because that's what we want to use when asking things like
783
- // isV2Ember()
863
+ // meta.originalFromFile gets set when we want to try to rehome a request
864
+ // but then come back to the original location here in the fallback when the
865
+ // rehomed request fails
784
866
  let movedPkg = this.packageCache.maybeMoved(pkg);
785
867
  if (movedPkg !== pkg) {
786
- if (!((_c = request.meta) === null || _c === void 0 ? void 0 : _c.wasMovedTo)) {
868
+ let originalFromFile = (_a = request.meta) === null || _a === void 0 ? void 0 : _a.originalFromFile;
869
+ if (typeof originalFromFile !== 'string') {
787
870
  throw new Error(`bug: embroider resolver's meta is not propagating`);
788
871
  }
789
- fromFile = request.meta.wasMovedTo;
872
+ request = request.rehome(originalFromFile);
790
873
  pkg = movedPkg;
791
874
  }
792
875
  if (!pkg.isV2Ember()) {
793
876
  return logTransition('fallbackResolve: not in an ember package', request);
794
877
  }
795
- let packageName = (0, shared_internals_1.packageName)(specifier);
878
+ let packageName = (0, shared_internals_1.packageName)(request.specifier);
796
879
  if (!packageName) {
797
880
  // this is a relative import
798
881
  let withinEngine = this.engineConfig(pkg.name);
799
882
  if (withinEngine) {
800
883
  // it's a relative import inside an engine (which also means app), which
801
884
  // means we may need to satisfy the request via app tree merging.
802
- let appJSMatch = this.searchAppTree(request, withinEngine, (0, shared_internals_2.explicitRelative)(pkg.root, (0, path_1.resolve)((0, path_1.dirname)(fromFile), specifier)));
885
+ let appJSMatch = await this.searchAppTree(request, withinEngine, (0, shared_internals_2.explicitRelative)(pkg.root, (0, path_1.resolve)((0, path_1.dirname)(request.fromFile), request.specifier)));
803
886
  if (appJSMatch) {
804
887
  return logTransition('fallbackResolve: relative appJsMatch', request, appJSMatch);
805
888
  }
@@ -813,40 +896,49 @@ class Resolver {
813
896
  }
814
897
  }
815
898
  // auto-upgraded packages can fall back to the set of known active addons
816
- if (pkg.meta['auto-upgraded'] && this.options.activeAddons[packageName]) {
817
- const rehomed = this.resolveWithinMovedPackage(request, this.packageCache.get(this.options.activeAddons[packageName]));
818
- if (rehomed !== request) {
819
- return logTransition(`activeAddons`, request, rehomed);
899
+ if (pkg.meta['auto-upgraded']) {
900
+ let addon = this.locateActiveAddon(packageName);
901
+ if (addon) {
902
+ const rehomed = request.rehome(addon.canResolveFromFile);
903
+ if (rehomed !== request) {
904
+ return logTransition(`activeAddons`, request, rehomed);
905
+ }
820
906
  }
821
907
  }
822
- let logicalLocation = this.reverseSearchAppTree(pkg, fromFile);
908
+ let logicalLocation = this.reverseSearchAppTree(pkg, request.fromFile);
823
909
  if (logicalLocation) {
824
910
  // the requesting file is in an addon's appTree. We didn't succeed in
825
911
  // resolving this (non-relative) request from inside the actual addon, so
826
912
  // next try to resolve it from the corresponding logical location in the
827
913
  // app.
828
- return logTransition('fallbackResolve: retry from logical home of app-js file', request, request.rehome((0, path_1.resolve)(logicalLocation.owningEngine.root, logicalLocation.inAppName)));
914
+ return logTransition('fallbackResolve: retry from logical home of app-js file', request,
915
+ // it might look more precise to rehome into logicalLocation.inAppName
916
+ // rather than package.json. But that logical location may not actually
917
+ // exist, and some systems (including node's require.resolve) will be
918
+ // mad about trying to resolve from notional paths that don't really
919
+ // exist.
920
+ request.rehome((0, path_1.resolve)(logicalLocation.owningEngine.root, 'package.json')));
829
921
  }
830
922
  let targetingEngine = this.engineConfig(packageName);
831
923
  if (targetingEngine) {
832
- let appJSMatch = this.searchAppTree(request, targetingEngine, specifier.replace(packageName, '.'));
924
+ let appJSMatch = await this.searchAppTree(request, targetingEngine, request.specifier.replace(packageName, '.'));
833
925
  if (appJSMatch) {
834
926
  return logTransition('fallbackResolve: non-relative appJsMatch', request, appJSMatch);
835
927
  }
836
928
  }
837
- if (pkg.meta['auto-upgraded']) {
929
+ if (pkg.meta['auto-upgraded'] && ((_c = (_b = request.meta) === null || _b === void 0 ? void 0 : _b.runtimeFallback) !== null && _c !== void 0 ? _c : true)) {
838
930
  // auto-upgraded packages can fall back to attempting to find dependencies at
839
931
  // runtime. Native v2 packages can only get this behavior in the
840
932
  // isExplicitlyExternal case above because they need to explicitly ask for
841
933
  // externals.
842
- return this.external('v1 catch-all fallback', request, specifier);
934
+ return this.external('v1 catch-all fallback', request, request.specifier);
843
935
  }
844
936
  else {
845
937
  // native v2 packages don't automatically externalize *everything* the way
846
938
  // auto-upgraded packages do, but they still externalize known and approved
847
939
  // ember virtual packages (like @ember/component)
848
940
  if (shared_internals_1.emberVirtualPackages.has(packageName)) {
849
- return this.external('emberVirtualPackages', request, specifier);
941
+ return this.external('emberVirtualPackages', request, request.specifier);
850
942
  }
851
943
  }
852
944
  // this is falling through with the original specifier which was
@@ -873,25 +965,23 @@ class Resolver {
873
965
  }
874
966
  }
875
967
  }
876
- searchAppTree(request, engine, inEngineSpecifier) {
968
+ async searchAppTree(request, engine, inEngineSpecifier) {
877
969
  let matched = this.getEntryFromMergeMap(inEngineSpecifier, engine.root);
878
970
  switch (matched === null || matched === void 0 ? void 0 : matched.entry.type) {
879
971
  case undefined:
880
972
  return undefined;
881
973
  case 'app-only':
882
- return request
883
- .alias(matched.entry['app-js'].localPath)
884
- .rehome((0, path_1.resolve)(matched.entry['app-js'].packageRoot, 'package.json'));
974
+ return request.alias(matched.entry['app-js'].specifier).rehome(matched.entry['app-js'].fromFile);
885
975
  case 'fastboot-only':
886
- return request
887
- .alias(matched.entry['fastboot-js'].localPath)
888
- .rehome((0, path_1.resolve)(matched.entry['fastboot-js'].packageRoot, 'package.json'));
976
+ return request.alias(matched.entry['fastboot-js'].specifier).rehome(matched.entry['fastboot-js'].fromFile);
889
977
  case 'both':
890
- let foundAppJS = this.nodeResolve(matched.entry['app-js'].localPath, (0, path_1.resolve)(matched.entry['app-js'].packageRoot, 'package.json'));
891
- if (foundAppJS.type !== 'real') {
978
+ let foundAppJS = await this.resolve(request.alias(matched.entry['app-js'].specifier).rehome(matched.entry['app-js'].fromFile).withMeta({
979
+ runtimeFallback: false,
980
+ }));
981
+ if (foundAppJS.type !== 'found') {
892
982
  throw new Error(`${matched.entry['app-js'].fromPackageName} declared ${inEngineSpecifier} in packageJSON.ember-addon.app-js, but that module does not exist`);
893
983
  }
894
- let { names } = (0, describe_exports_1.describeExports)((0, fs_1.readFileSync)(foundAppJS.filename, 'utf8'), {});
984
+ let { names } = (0, describe_exports_1.describeExports)((0, fs_1.readFileSync)(foundAppJS.filename, 'utf8'), { configFile: false });
895
985
  return request.virtualize((0, virtual_content_1.fastbootSwitch)(matched.matched, (0, path_1.resolve)(engine.root, 'package.json'), names));
896
986
  }
897
987
  }