@embroider/core 3.4.5 → 3.4.6-unstable.2afc470

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,9 +75,13 @@ 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
85
  request = this.handleRenaming(request);
86
86
  // we expect the specifier to be app relative at this point - must be after handleRenaming
87
87
  request = this.generateFastbootSwitch(request);
@@ -96,95 +96,45 @@ class Resolver {
96
96
  // that calls your build system's normal module resolver, this does both pre-
97
97
  // and post-resolution adjustments as needed to implement our compatibility
98
98
  // 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);
99
+ async resolve(request) {
100
+ request = await this.beforeResolve(request);
101
+ if (request.resolvedTo) {
102
+ return request.resolvedTo;
103
+ }
104
+ let resolution = await request.defaultResolve();
127
105
  switch (resolution.type) {
128
106
  case 'found':
107
+ case 'ignored':
129
108
  return resolution;
130
109
  case 'not_found':
131
110
  break;
132
111
  default:
133
112
  throw (0, assert_never_1.default)(resolution);
134
113
  }
135
- let nextRequest = this.fallbackResolve(request);
114
+ let nextRequest = await this.fallbackResolve(request);
136
115
  if (nextRequest === request) {
137
116
  // no additional fallback is available.
138
117
  return resolution;
139
118
  }
119
+ if (nextRequest.resolvedTo) {
120
+ return nextRequest.resolvedTo;
121
+ }
140
122
  if (nextRequest.fromFile === request.fromFile && nextRequest.specifier === request.specifier) {
141
123
  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
124
  }
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);
125
+ if (nextRequest.isVirtual || nextRequest.isNotFound) {
126
+ // virtual and NotFound requests are terminal, there is no more
127
+ // beforeResolve or fallbackResolve around them. The defaultResolve is
128
+ // expected to know how to implement them.
129
+ return nextRequest.defaultResolve();
148
130
  }
149
- return yield* this.internalResolve(nextRequest, defaultResolve);
131
+ return this.resolve(nextRequest);
150
132
  }
151
133
  // Use standard NodeJS resolving, with our required compatibility rules on
152
134
  // top. This is a convenience method for calling resolveSync with the
153
135
  // 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
- }
136
+ async nodeResolve(specifier, fromFile) {
137
+ return (0, node_resolve_1.nodeResolve)(this, specifier, fromFile);
188
138
  }
189
139
  get packageCache() {
190
140
  return shared_internals_2.RewrittenPackageCache.shared('embroider', this.options.appRoot);
@@ -201,6 +151,9 @@ class Resolver {
201
151
  return owningPackage;
202
152
  }
203
153
  generateFastbootSwitch(request) {
154
+ if (isTerminal(request)) {
155
+ return request;
156
+ }
204
157
  let pkg = this.packageCache.ownerOfFile(request.fromFile);
205
158
  if (!pkg) {
206
159
  return request;
@@ -218,7 +171,9 @@ class Resolver {
218
171
  let fastbootFile = engineConfig.fastbootFiles[candidate];
219
172
  if (fastbootFile) {
220
173
  if (fastbootFile.shadowedFilename) {
221
- let { names } = (0, describe_exports_1.describeExports)((0, fs_1.readFileSync)((0, path_1.resolve)(pkg.root, fastbootFile.shadowedFilename), 'utf8'), {});
174
+ let { names } = (0, describe_exports_1.describeExports)((0, fs_1.readFileSync)((0, path_1.resolve)(pkg.root, fastbootFile.shadowedFilename), 'utf8'), {
175
+ configFile: false,
176
+ });
222
177
  let switchFile = (0, virtual_content_1.fastbootSwitch)(candidate, (0, path_1.resolve)(pkg.root, 'package.json'), names);
223
178
  if (switchFile === request.fromFile) {
224
179
  return logTransition('internal lookup from fastbootSwitch', request);
@@ -237,6 +192,9 @@ class Resolver {
237
192
  }
238
193
  handleFastbootSwitch(request) {
239
194
  var _a;
195
+ if (isTerminal(request)) {
196
+ return request;
197
+ }
240
198
  let match = (0, virtual_content_1.decodeFastbootSwitch)(request.fromFile);
241
199
  if (!match) {
242
200
  return request;
@@ -275,12 +233,15 @@ class Resolver {
275
233
  }
276
234
  let entry = (_a = this.getEntryFromMergeMap(rel, pkg.root)) === null || _a === void 0 ? void 0 : _a.entry;
277
235
  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')));
236
+ return logTransition('matched addon entry', request, request.alias(entry[section].specifier).rehome(entry[section].fromFile));
279
237
  }
280
238
  }
281
239
  return logTransition('failed to match in fastboot switch', request);
282
240
  }
283
241
  handleImplicitModules(request) {
242
+ if (isTerminal(request)) {
243
+ return request;
244
+ }
284
245
  let im = (0, virtual_content_1.decodeImplicitModules)(request.specifier);
285
246
  if (!im) {
286
247
  return request;
@@ -298,7 +259,26 @@ class Resolver {
298
259
  return logTransition(`own implicit modules`, request, request.virtualize((0, path_1.resolve)(pkg.root, `-embroider-${im.type}.js`)));
299
260
  }
300
261
  }
301
- handleGlobalsCompat(request) {
262
+ handleImplicitTestScripts(request) {
263
+ //TODO move the extra forwardslash handling out into the vite plugin
264
+ const candidates = [
265
+ '@embroider/core/test-support.js',
266
+ '/@embroider/core/test-support.js',
267
+ './@embroider/core/test-support.js',
268
+ ];
269
+ if (!candidates.includes(request.specifier)) {
270
+ return request;
271
+ }
272
+ let pkg = this.packageCache.ownerOfFile(request.fromFile);
273
+ if ((pkg === null || pkg === void 0 ? void 0 : pkg.root) !== this.options.engines[0].root) {
274
+ 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.`);
275
+ }
276
+ return logTransition('test-support', request, request.virtualize((0, path_1.resolve)(pkg.root, '-embroider-test-support.js')));
277
+ }
278
+ async handleGlobalsCompat(request) {
279
+ if (isTerminal(request)) {
280
+ return request;
281
+ }
302
282
  let match = compatPattern.exec(request.specifier);
303
283
  if (!match) {
304
284
  return request;
@@ -326,52 +306,60 @@ class Resolver {
326
306
  let target = this.parseGlobalPath(path, inEngine);
327
307
  return logTransition('resolveHelper', request, request.alias(`${target.packageName}/helpers/${target.memberName}`).rehome((0, path_1.resolve)(inEngine.root, 'package.json')));
328
308
  }
329
- resolveComponent(path, inEngine, request) {
309
+ async resolveComponent(path, inEngine, request) {
330
310
  let target = this.parseGlobalPath(path, inEngine);
331
311
  let hbsModule = null;
332
312
  let jsModule = null;
333
313
  // first, the various places our template might be.
334
314
  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;
315
+ let candidateSpecifier = `${target.packageName}${candidate.prefix}${target.memberName}${candidate.suffix}`;
316
+ let resolution = await this.resolve(request.alias(candidateSpecifier).rehome(target.from).withMeta({
317
+ runtimeFallback: false,
318
+ }));
319
+ if (resolution.type === 'found') {
320
+ hbsModule = resolution;
338
321
  break;
339
322
  }
340
323
  }
341
324
  // then the various places our javascript might be.
342
325
  for (let candidate of this.componentJSCandidates(target.packageName)) {
343
- let resolution = this.nodeResolve(`${target.packageName}${candidate.prefix}${target.memberName}${candidate.suffix}`, target.from);
326
+ let candidateSpecifier = `${target.packageName}${candidate.prefix}${target.memberName}${candidate.suffix}`;
327
+ let resolution = await this.resolve(request.alias(candidateSpecifier).rehome(target.from).withMeta({
328
+ runtimeFallback: false,
329
+ }));
344
330
  // .hbs is a resolvable extension for us, so we need to exclude it here.
345
331
  // It matches as a priority lower than .js, so finding an .hbs means
346
332
  // there's definitely not a .js.
347
- if (resolution.type === 'real' && !resolution.filename.endsWith('.hbs')) {
348
- jsModule = resolution.filename;
333
+ if (resolution.type === 'found' && !resolution.filename.endsWith('.hbs')) {
334
+ jsModule = resolution;
349
335
  break;
350
336
  }
351
337
  }
352
338
  if (hbsModule) {
353
- return logTransition(`resolveComponent found legacy HBS`, request, request.virtualize((0, virtual_content_1.virtualPairComponent)(hbsModule, jsModule)));
339
+ 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
340
  }
355
341
  else if (jsModule) {
356
- return logTransition(`resolveComponent found only JS`, request, request.alias(jsModule).rehome(target.from));
342
+ return logTransition(`resolving to resolveComponent found only JS`, request, request.resolveTo(jsModule));
357
343
  }
358
344
  else {
359
345
  return logTransition(`resolveComponent failed`, request);
360
346
  }
361
347
  }
362
- resolveHelperOrComponent(path, inEngine, request) {
348
+ async resolveHelperOrComponent(path, inEngine, request) {
363
349
  // resolveHelper just rewrites our request to one that should target the
364
350
  // component, so here to resolve the ambiguity we need to actually resolve
365
351
  // that candidate to see if it works.
366
352
  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);
353
+ let helperMatch = await this.resolve(request.alias(helperCandidate.specifier).rehome(helperCandidate.fromFile).withMeta({
354
+ runtimeFallback: false,
355
+ }));
356
+ if (helperMatch.type === 'found') {
357
+ return logTransition('resolve to ambiguous case matched a helper', request, request.resolveTo(helperMatch));
370
358
  }
371
359
  // unlike resolveHelper, resolveComponent already does pre-resolution in
372
360
  // order to deal with its own internal ambiguity around JS vs HBS vs
373
361
  // colocation.≥
374
- let componentMatch = this.resolveComponent(path, inEngine, request);
362
+ let componentMatch = await this.resolveComponent(path, inEngine, request);
375
363
  if (componentMatch !== request) {
376
364
  return logTransition('ambiguous case matched a cmoponent', request, componentMatch);
377
365
  }
@@ -396,6 +384,7 @@ class Resolver {
396
384
  }
397
385
  *componentJSCandidates(inPackageName) {
398
386
  yield { prefix: '/components/', suffix: '' };
387
+ yield { prefix: '/components/', suffix: '/index' };
399
388
  yield { prefix: '/components/', suffix: '/component' };
400
389
  let pods = this.podPrefix(inPackageName);
401
390
  if (pods) {
@@ -414,10 +403,10 @@ class Resolver {
414
403
  parseGlobalPath(path, inEngine) {
415
404
  let parts = path.split('@');
416
405
  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') };
406
+ return { packageName: parts[0], memberName: parts[1], from: (0, path_1.resolve)(inEngine.root, 'package.json') };
418
407
  }
419
408
  else {
420
- return { packageName: inEngine.packageName, memberName: path, from: (0, path_1.resolve)(inEngine.root, 'pacakge.json') };
409
+ return { packageName: inEngine.packageName, memberName: path, from: (0, path_1.resolve)(inEngine.root, 'package.json') };
421
410
  }
422
411
  }
423
412
  engineConfig(packageName) {
@@ -449,8 +438,8 @@ class Resolver {
449
438
  engineModules.set(inEngineName, {
450
439
  type: 'app-only',
451
440
  'app-js': {
452
- localPath: inAddonName,
453
- packageRoot: addon.root,
441
+ specifier: (0, reverse_exports_1.default)(addon.packageJSON, inAddonName),
442
+ fromFile: addonConfig.canResolveFromFile,
454
443
  fromPackageName: addon.name,
455
444
  },
456
445
  });
@@ -463,8 +452,8 @@ class Resolver {
463
452
  engineModules.set(inEngineName, {
464
453
  type: 'both',
465
454
  'app-js': {
466
- localPath: inAddonName,
467
- packageRoot: addon.root,
455
+ specifier: (0, reverse_exports_1.default)(addon.packageJSON, inAddonName),
456
+ fromFile: addonConfig.canResolveFromFile,
468
457
  fromPackageName: addon.name,
469
458
  },
470
459
  'fastboot-js': prevEntry['fastboot-js'],
@@ -488,8 +477,8 @@ class Resolver {
488
477
  engineModules.set(inEngineName, {
489
478
  type: 'fastboot-only',
490
479
  'fastboot-js': {
491
- localPath: inAddonName,
492
- packageRoot: addon.root,
480
+ specifier: (0, reverse_exports_1.default)(addon.packageJSON, inAddonName),
481
+ fromFile: addonConfig.canResolveFromFile,
493
482
  fromPackageName: addon.name,
494
483
  },
495
484
  });
@@ -502,8 +491,8 @@ class Resolver {
502
491
  engineModules.set(inEngineName, {
503
492
  type: 'both',
504
493
  'fastboot-js': {
505
- localPath: inAddonName,
506
- packageRoot: addon.root,
494
+ specifier: (0, reverse_exports_1.default)(addon.packageJSON, inAddonName),
495
+ fromFile: addonConfig.canResolveFromFile,
507
496
  fromPackageName: addon.name,
508
497
  },
509
498
  'app-js': prevEntry['app-js'],
@@ -525,7 +514,7 @@ class Resolver {
525
514
  return owningEngine;
526
515
  }
527
516
  handleRewrittenPackages(request) {
528
- if (request.isVirtual) {
517
+ if (isTerminal(request)) {
529
518
  return request;
530
519
  }
531
520
  let requestingPkg = this.packageCache.ownerOfFile(request.fromFile);
@@ -544,10 +533,6 @@ class Resolver {
544
533
  targetPkg = this.packageCache.resolve(packageName, requestingPkg);
545
534
  }
546
535
  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
536
  if (err.code !== 'MODULE_NOT_FOUND') {
552
537
  throw err;
553
538
  }
@@ -563,14 +548,26 @@ class Resolver {
563
548
  return logTransition('request targets a moved package', request, this.resolveWithinMovedPackage(request, targetPkg));
564
549
  }
565
550
  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')));
551
+ if (targetPkg) {
552
+ // in this case, the requesting package is moved but its destination is
553
+ // not, so we need to rehome the request back to the original location.
554
+ return logTransition('outbound request from moved package', request, request
555
+ // setting meta here because if this fails, we want the fallback
556
+ // logic to revert our rehome and continue from the *moved* package.
557
+ .withMeta({ originalFromFile: request.fromFile })
558
+ .rehome((0, path_1.resolve)(originalRequestingPkg.root, 'package.json')));
559
+ }
560
+ else {
561
+ // requesting package was moved and we failed to find its target. We
562
+ // can't let that accidentally succeed in the defaultResolve because we
563
+ // could escape the moved package system.
564
+ return logTransition('missing outbound request from moved package', request, request.notFound());
565
+ }
569
566
  }
570
567
  return request;
571
568
  }
572
569
  handleRenaming(request) {
573
- if (request.isVirtual) {
570
+ if (isTerminal(request)) {
574
571
  return request;
575
572
  }
576
573
  let packageName = (0, shared_internals_1.packageName)(request.specifier);
@@ -603,12 +600,34 @@ class Resolver {
603
600
  return logTransition(`renamePackages`, request, request.alias(request.specifier.replace(packageName, this.options.renamePackages[packageName])));
604
601
  }
605
602
  }
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')));
603
+ if (pkg.name === packageName) {
604
+ // we found a self-import
605
+ if (pkg.meta['auto-upgraded']) {
606
+ // auto-upgraded packages always get automatically adjusted. They never
607
+ // supported fancy package.json exports features so this direct mapping
608
+ // to the root is always right.
609
+ // "my-package/foo" -> "./foo"
610
+ // "my-package" -> "./" (this can't be just "." because node's require.resolve doesn't reliable support that)
611
+ let selfImportPath = request.specifier === pkg.name ? './' : request.specifier.replace(pkg.name, '.');
612
+ return logTransition(`v1 self-import`, request, request.alias(selfImportPath).rehome((0, path_1.resolve)(pkg.root, 'package.json')));
613
+ }
614
+ else {
615
+ // v2 packages are supposed to use package.json `exports` to enable
616
+ // self-imports, but not all build tools actually follow the spec. This
617
+ // is a workaround for badly behaved packagers.
618
+ //
619
+ // Known upstream bugs this works around:
620
+ // - https://github.com/vitejs/vite/issues/9731
621
+ if (pkg.packageJSON.exports) {
622
+ let found = (0, resolve_exports_1.exports)(pkg.packageJSON, request.specifier, {
623
+ browser: true,
624
+ conditions: ['default', 'imports'],
625
+ });
626
+ if (found === null || found === void 0 ? void 0 : found[0]) {
627
+ 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')));
628
+ }
629
+ }
630
+ }
612
631
  }
613
632
  return request;
614
633
  }
@@ -617,16 +636,17 @@ class Resolver {
617
636
  if (pkg.name.startsWith('@')) {
618
637
  levels.push('..');
619
638
  }
639
+ let originalFromFile = request.fromFile;
620
640
  let newRequest = request.rehome((0, path_1.resolve)(pkg.root, ...levels, 'moved-package-target.js'));
621
641
  if (newRequest === request) {
622
642
  return request;
623
643
  }
624
- return newRequest.withMeta({
625
- resolvedWithinPackage: pkg.root,
626
- });
644
+ // setting meta because if this fails, we want the fallback to pick up back
645
+ // in the original requesting package.
646
+ return newRequest.withMeta({ originalFromFile });
627
647
  }
628
648
  preHandleExternal(request) {
629
- if (request.isVirtual) {
649
+ if (isTerminal(request)) {
630
650
  return request;
631
651
  }
632
652
  let { specifier, fromFile } = request;
@@ -659,7 +679,15 @@ class Resolver {
659
679
  // engine
660
680
  let logicalLocation = this.reverseSearchAppTree(pkg, request.fromFile);
661
681
  if (logicalLocation) {
662
- return logTransition('beforeResolve: relative import in app-js', request, request.rehome((0, path_1.resolve)(logicalLocation.owningEngine.root, logicalLocation.inAppName)));
682
+ return logTransition('beforeResolve: relative import in app-js', request, request
683
+ .alias('./' + path_1.posix.join((0, path_1.dirname)(logicalLocation.inAppName), request.specifier))
684
+ // it's important that we're rehoming this to the root of the engine
685
+ // (which we know really exists), and not to a subdir like
686
+ // logicalLocation.inAppName (which might not physically exist),
687
+ // because some environments (including node's require.resolve) will
688
+ // refuse to do resolution from a notional path that doesn't
689
+ // physically exist.
690
+ .rehome((0, path_1.resolve)(logicalLocation.owningEngine.root, 'package.json')));
663
691
  }
664
692
  return request;
665
693
  }
@@ -674,11 +702,11 @@ class Resolver {
674
702
  if (shared_internals_1.emberVirtualPeerDeps.has(packageName) && !pkg.hasDependency(packageName)) {
675
703
  // addons (whether auto-upgraded or not) may use the app's
676
704
  // 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`);
705
+ let addon = this.locateActiveAddon(packageName);
706
+ if (!addon) {
707
+ throw new Error(`${pkg.name} is trying to import the emberVirtualPeerDep "${packageName}", but it seems to be missing`);
679
708
  }
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));
709
+ return logTransition(`emberVirtualPeerDeps`, request, request.rehome(addon.canResolveFromFile));
682
710
  }
683
711
  // if this file is part of an addon's app-js, it's really the logical
684
712
  // package to which it belongs (normally the app) that affects some policy
@@ -709,6 +737,22 @@ class Resolver {
709
737
  }
710
738
  return request;
711
739
  }
740
+ locateActiveAddon(packageName) {
741
+ if (packageName === this.options.modulePrefix) {
742
+ // the app itself is something that addon's can classically resolve if they know it's name.
743
+ return {
744
+ root: this.options.appRoot,
745
+ canResolveFromFile: (0, path_1.resolve)(this.packageCache.maybeMoved(this.packageCache.get(this.options.appRoot)).root, 'package.json'),
746
+ };
747
+ }
748
+ for (let engine of this.options.engines) {
749
+ for (let addon of engine.activeAddons) {
750
+ if (addon.name === packageName) {
751
+ return addon;
752
+ }
753
+ }
754
+ }
755
+ }
712
756
  external(label, request, specifier) {
713
757
  if (this.options.amdCompatibility === 'cjs') {
714
758
  let filename = (0, virtual_content_1.virtualExternalCJSModule)(specifier);
@@ -741,8 +785,11 @@ class Resolver {
741
785
  throw new Error(`Embroider's amdCompatibility option is disabled, but something tried to use it to access "${request.specifier}"`);
742
786
  }
743
787
  }
744
- fallbackResolve(request) {
788
+ async fallbackResolve(request) {
745
789
  var _a, _b, _c;
790
+ if (request.isVirtual) {
791
+ 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');
792
+ }
746
793
  if (request.specifier === '@embroider/macros') {
747
794
  // the macros package is always handled directly within babel (not
748
795
  // necessarily as a real resolvable package), so we should not mess with it.
@@ -750,8 +797,7 @@ class Resolver {
750
797
  // why we need to know about it.
751
798
  return logTransition('fallback early exit', request);
752
799
  }
753
- let { specifier, fromFile } = request;
754
- if (compatPattern.test(specifier)) {
800
+ if (compatPattern.test(request.specifier)) {
755
801
  // Some kinds of compat requests get rewritten into other things
756
802
  // deterministically. For example, "#embroider_compat/helpers/whatever"
757
803
  // means only "the-current-engine/helpers/whatever", and if that doesn't
@@ -767,39 +813,33 @@ class Resolver {
767
813
  // here.
768
814
  return request;
769
815
  }
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);
816
+ let pkg = this.packageCache.ownerOfFile(request.fromFile);
777
817
  if (!pkg) {
778
818
  return logTransition('no identifiable owningPackage', request);
779
819
  }
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()
820
+ // meta.originalFromFile gets set when we want to try to rehome a request
821
+ // but then come back to the original location here in the fallback when the
822
+ // rehomed request fails
784
823
  let movedPkg = this.packageCache.maybeMoved(pkg);
785
824
  if (movedPkg !== pkg) {
786
- if (!((_c = request.meta) === null || _c === void 0 ? void 0 : _c.wasMovedTo)) {
825
+ let originalFromFile = (_a = request.meta) === null || _a === void 0 ? void 0 : _a.originalFromFile;
826
+ if (typeof originalFromFile !== 'string') {
787
827
  throw new Error(`bug: embroider resolver's meta is not propagating`);
788
828
  }
789
- fromFile = request.meta.wasMovedTo;
829
+ request = request.rehome(originalFromFile);
790
830
  pkg = movedPkg;
791
831
  }
792
832
  if (!pkg.isV2Ember()) {
793
833
  return logTransition('fallbackResolve: not in an ember package', request);
794
834
  }
795
- let packageName = (0, shared_internals_1.packageName)(specifier);
835
+ let packageName = (0, shared_internals_1.packageName)(request.specifier);
796
836
  if (!packageName) {
797
837
  // this is a relative import
798
838
  let withinEngine = this.engineConfig(pkg.name);
799
839
  if (withinEngine) {
800
840
  // it's a relative import inside an engine (which also means app), which
801
841
  // 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)));
842
+ 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
843
  if (appJSMatch) {
804
844
  return logTransition('fallbackResolve: relative appJsMatch', request, appJSMatch);
805
845
  }
@@ -813,40 +853,49 @@ class Resolver {
813
853
  }
814
854
  }
815
855
  // 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);
856
+ if (pkg.meta['auto-upgraded']) {
857
+ let addon = this.locateActiveAddon(packageName);
858
+ if (addon) {
859
+ const rehomed = request.rehome(addon.canResolveFromFile);
860
+ if (rehomed !== request) {
861
+ return logTransition(`activeAddons`, request, rehomed);
862
+ }
820
863
  }
821
864
  }
822
- let logicalLocation = this.reverseSearchAppTree(pkg, fromFile);
865
+ let logicalLocation = this.reverseSearchAppTree(pkg, request.fromFile);
823
866
  if (logicalLocation) {
824
867
  // the requesting file is in an addon's appTree. We didn't succeed in
825
868
  // resolving this (non-relative) request from inside the actual addon, so
826
869
  // next try to resolve it from the corresponding logical location in the
827
870
  // 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)));
871
+ return logTransition('fallbackResolve: retry from logical home of app-js file', request,
872
+ // it might look more precise to rehome into logicalLocation.inAppName
873
+ // rather than package.json. But that logical location may not actually
874
+ // exist, and some systems (including node's require.resolve) will be
875
+ // mad about trying to resolve from notional paths that don't really
876
+ // exist.
877
+ request.rehome((0, path_1.resolve)(logicalLocation.owningEngine.root, 'package.json')));
829
878
  }
830
879
  let targetingEngine = this.engineConfig(packageName);
831
880
  if (targetingEngine) {
832
- let appJSMatch = this.searchAppTree(request, targetingEngine, specifier.replace(packageName, '.'));
881
+ let appJSMatch = await this.searchAppTree(request, targetingEngine, request.specifier.replace(packageName, '.'));
833
882
  if (appJSMatch) {
834
883
  return logTransition('fallbackResolve: non-relative appJsMatch', request, appJSMatch);
835
884
  }
836
885
  }
837
- if (pkg.meta['auto-upgraded']) {
886
+ if (pkg.meta['auto-upgraded'] && ((_c = (_b = request.meta) === null || _b === void 0 ? void 0 : _b.runtimeFallback) !== null && _c !== void 0 ? _c : true)) {
838
887
  // auto-upgraded packages can fall back to attempting to find dependencies at
839
888
  // runtime. Native v2 packages can only get this behavior in the
840
889
  // isExplicitlyExternal case above because they need to explicitly ask for
841
890
  // externals.
842
- return this.external('v1 catch-all fallback', request, specifier);
891
+ return this.external('v1 catch-all fallback', request, request.specifier);
843
892
  }
844
893
  else {
845
894
  // native v2 packages don't automatically externalize *everything* the way
846
895
  // auto-upgraded packages do, but they still externalize known and approved
847
896
  // ember virtual packages (like @ember/component)
848
897
  if (shared_internals_1.emberVirtualPackages.has(packageName)) {
849
- return this.external('emberVirtualPackages', request, specifier);
898
+ return this.external('emberVirtualPackages', request, request.specifier);
850
899
  }
851
900
  }
852
901
  // this is falling through with the original specifier which was
@@ -873,25 +922,23 @@ class Resolver {
873
922
  }
874
923
  }
875
924
  }
876
- searchAppTree(request, engine, inEngineSpecifier) {
925
+ async searchAppTree(request, engine, inEngineSpecifier) {
877
926
  let matched = this.getEntryFromMergeMap(inEngineSpecifier, engine.root);
878
927
  switch (matched === null || matched === void 0 ? void 0 : matched.entry.type) {
879
928
  case undefined:
880
929
  return undefined;
881
930
  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'));
931
+ return request.alias(matched.entry['app-js'].specifier).rehome(matched.entry['app-js'].fromFile);
885
932
  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'));
933
+ return request.alias(matched.entry['fastboot-js'].specifier).rehome(matched.entry['fastboot-js'].fromFile);
889
934
  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') {
935
+ let foundAppJS = await this.resolve(request.alias(matched.entry['app-js'].specifier).rehome(matched.entry['app-js'].fromFile).withMeta({
936
+ runtimeFallback: false,
937
+ }));
938
+ if (foundAppJS.type !== 'found') {
892
939
  throw new Error(`${matched.entry['app-js'].fromPackageName} declared ${inEngineSpecifier} in packageJSON.ember-addon.app-js, but that module does not exist`);
893
940
  }
894
- let { names } = (0, describe_exports_1.describeExports)((0, fs_1.readFileSync)(foundAppJS.filename, 'utf8'), {});
941
+ let { names } = (0, describe_exports_1.describeExports)((0, fs_1.readFileSync)(foundAppJS.filename, 'utf8'), { configFile: false });
895
942
  return request.virtualize((0, virtual_content_1.fastbootSwitch)(matched.matched, (0, path_1.resolve)(engine.root, 'package.json'), names));
896
943
  }
897
944
  }