@embroider/core 3.4.8 → 3.4.9-unstable.ccbf41f

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,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
  }