Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Esbuild fixes #2100

Merged
merged 8 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/compat/src/audit/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,11 @@ async function execute(
let stderrBuffer: string[] = [];
let stdoutBuffer: string[] = [];
let combinedBuffer: string[] = [];
child.stderr.on('data', data => {
child.stderr!.on('data', data => {
stderrBuffer.push(data);
combinedBuffer.push(data);
});
child.stdout.on('data', data => {
child.stdout!.on('data', data => {
stdoutBuffer.push(data);
combinedBuffer.push(data);
});
Expand Down
14 changes: 6 additions & 8 deletions packages/compat/src/compat-app-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,12 @@ export class CompatAppBuilder {
}

private resolvableExtensions(): string[] {
// webpack's default is ['.wasm', '.mjs', '.js', '.json']. Keeping that
// subset in that order is sensible, since many third-party libraries will
// expect it to work that way.
//
// For TS, we defer to ember-cli-babel, and the setting for
// "enableTypescriptTransform" can be set with and without
// ember-cli-typescript
return ['.wasm', '.mjs', '.js', '.json', '.ts', '.hbs', '.hbs.js', '.gjs', '.gts'];
let fromEnv = process.env.EMBROIDER_RESOLVABLE_EXTENSIONS;
if (fromEnv) {
return fromEnv.split(',');
} else {
return ['.mjs', '.gjs', '.js', '.mts', '.gts', '.ts', '.hbs', '.hbs.js', '.json'];
}
}

private modulePrefix(): string {
Expand Down
136 changes: 82 additions & 54 deletions packages/core/src/module-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -971,20 +971,13 @@ export class Resolver {
}

let pkg = this.packageCache.ownerOfFile(request.fromFile);
if (!pkg || !pkg.isV2Ember()) {
return request;
}

// real deps take precedence over renaming rules. That is, a package like
// ember-source might provide backburner via module renaming, but if you
// have an explicit dependency on backburner you should still get that real
// copy.

// if (pkg.root === this.options.engines[0].root && request.specifier === `${pkg.name}/environment/config`) {
// return logTransition('legacy config location', request, request.alias(`${pkg.name}/app/environment/config`));
// }

if (!pkg.hasDependency(packageName)) {
if (!pkg?.hasDependency(packageName)) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is where we're allowing a missing package to still match renamedModules.

for (let [candidate, replacement] of Object.entries(this.options.renameModules)) {
if (candidate === request.specifier) {
return logTransition(`renameModules`, request, request.alias(replacement));
Expand All @@ -1008,6 +1001,10 @@ export class Resolver {
}
}

if (!pkg || !pkg.isV2Ember()) {
return request;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This used to happen before renameModules, etc, now it happens after.

}

if (pkg.name === packageName) {
// we found a self-import
if (pkg.meta['auto-upgraded']) {
Expand Down Expand Up @@ -1285,63 +1282,42 @@ export class Resolver {
}

let pkg = this.packageCache.ownerOfFile(request.fromFile);
if (!pkg) {
return logTransition('no identifiable owningPackage', request);
}

// meta.originalFromFile gets set when we want to try to rehome a request
// but then come back to the original location here in the fallback when the
// rehomed request fails
let movedPkg = this.packageCache.maybeMoved(pkg);
if (movedPkg !== pkg) {
let originalFromFile = request.meta?.originalFromFile;
if (typeof originalFromFile !== 'string') {
throw new Error(`bug: embroider resolver's meta is not propagating`);
}
request = request.rehome(originalFromFile);
pkg = movedPkg;
if (pkg) {
({ pkg, request } = this.restoreRehomedRequest(pkg, request));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just an extraction because fallbackResolve was getting so long.

}

if (!pkg.isV2Ember()) {
return logTransition('fallbackResolve: not in an ember package', request);
if (!pkg?.isV2Ember()) {
// this request is coming from a file that appears to be owned by no ember
// package. We offer one fallback behavior for such files. They're allowed
// to resolve from the app's namespace.
//
// This makes it possible for integrations like vite to leave references
// to the app in their pre-bundled dependencies, which will end up in an
// arbitrary cache that is not inside any particular package.
let description = pkg ? 'non-ember package' : 'unowned module';

let packageName = getPackageName(request.specifier);
if (packageName === this.options.modulePrefix) {
return logTransition(
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the new support for app resolving from non-owned or non-ember packages.

`fallbackResolver: ${description} resolved app namespace`,
request,
request.rehome(this.packageCache.maybeMoved(this.packageCache.get(this.options.appRoot)).root)
);
}
return logTransition(`fallbackResolver: ${description}`, request);
}

let packageName = getPackageName(request.specifier);
if (!packageName) {
// this is a relative import

let withinEngine = this.engineConfig(pkg.name);
if (withinEngine) {
// it's a relative import inside an engine (which also means app), which
// means we may need to satisfy the request via app tree merging.

let logicalName = engineRelativeName(pkg, resolve(dirname(request.fromFile), request.specifier));
if (!logicalName) {
return logTransition(
'fallbackResolve: relative failure because this file is not externally accessible',
request
);
}
let appJSMatch = await this.searchAppTree(request, withinEngine, logicalName);
if (appJSMatch) {
return logTransition('fallbackResolve: relative appJsMatch', request, appJSMatch);
} else {
return logTransition('fallbackResolve: relative appJs search failure', request);
}
} else {
// nothing else to do for relative imports
return logTransition('fallbackResolve: relative failure', request);
}
return this.relativeFallbackResolve(pkg, request);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is another extraction to keep fallbackResolve shorter.

}

// auto-upgraded packages can fall back to the set of known active addons
if (pkg.needsLooseResolving()) {
let addon = this.locateActiveAddon(packageName);
if (addon) {
const rehomed = request.rehome(addon.canResolveFromFile);
if (rehomed !== request) {
return logTransition(`activeAddons`, request, rehomed);
}
let activeAddon = this.maybeFallbackToActiveAddon(request, packageName);
if (activeAddon) {
return activeAddon;
}
}

Expand Down Expand Up @@ -1391,6 +1367,58 @@ export class Resolver {
return logTransition('fallbackResolve final exit', request);
}

private restoreRehomedRequest<R extends ModuleRequest>(pkg: Package, request: R): { pkg: Package; request: R } {
// meta.originalFromFile gets set when we want to try to rehome a request
// but then come back to the original location here in the fallback when the
// rehomed request fails
let movedPkg = this.packageCache.maybeMoved(pkg);
if (movedPkg !== pkg) {
let originalFromFile = request.meta?.originalFromFile;
if (typeof originalFromFile !== 'string') {
throw new Error(`bug: embroider resolver's meta is not propagating`);
}
request = request.rehome(originalFromFile);
pkg = movedPkg;
}
return { pkg, request };
}

private async relativeFallbackResolve<R extends ModuleRequest>(pkg: Package, request: R): Promise<R> {
let withinEngine = this.engineConfig(pkg.name);
if (withinEngine) {
// it's a relative import inside an engine (which also means app), which
// means we may need to satisfy the request via app tree merging.

let logicalName = engineRelativeName(pkg, resolve(dirname(request.fromFile), request.specifier));
if (!logicalName) {
return logTransition(
'fallbackResolve: relative failure because this file is not externally accessible',
request
);
}
let appJSMatch = await this.searchAppTree(request, withinEngine, logicalName);
if (appJSMatch) {
return logTransition('fallbackResolve: relative appJsMatch', request, appJSMatch);
} else {
return logTransition('fallbackResolve: relative appJs search failure', request);
}
} else {
// nothing else to do for relative imports
return logTransition('fallbackResolve: relative failure', request);
}
}

private maybeFallbackToActiveAddon<R extends ModuleRequest>(request: R, requestedPackageName: string): R | undefined {
// auto-upgraded packages can fall back to the set of known active addons
let addon = this.locateActiveAddon(requestedPackageName);
if (addon) {
const rehomed = request.rehome(addon.canResolveFromFile);
if (rehomed !== request) {
return logTransition(`activeAddons`, request, rehomed);
}
}
}

private getEntryFromMergeMap(
inEngineSpecifier: string,
root: string
Expand Down
6 changes: 3 additions & 3 deletions packages/core/src/virtual-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,14 @@ function decodeVirtualExternalCJSModule(filename: string) {
}

const pairComponentMarker = '-embroider-pair-component';
const pairComponentPattern = /^(?<hbsModule>.*)\/(?<jsModule>[^\/]*)-embroider-pair-component$/;
const pairComponentPattern = /^(?<hbsModule>.*)__vpc__(?<jsModule>[^\/]*)-embroider-pair-component$/;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This change in naming means that our virtual pair component is sitting inside a real directory, not an imaginary one. That makes esbuild resolve outbound requests from it correctly.


export function virtualPairComponent(hbsModule: string, jsModule: string | undefined): string {
let relativeJSModule = '';
if (jsModule) {
relativeJSModule = explicitRelative(hbsModule, jsModule);
relativeJSModule = explicitRelative(dirname(hbsModule), jsModule);
}
return `${hbsModule}/${encodeURIComponent(relativeJSModule)}${pairComponentMarker}`;
return `${hbsModule}__vpc__${encodeURIComponent(relativeJSModule)}${pairComponentMarker}`;
}

function decodeVirtualPairComponent(
Expand Down
1 change: 1 addition & 0 deletions packages/vite/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"dependencies": {
"@babel/core": "^7.22.9",
"@embroider/macros": "workspace:*",
"@embroider/reverse-exports": "workspace:*",
"@rollup/pluginutils": "^4.1.1",
"assert-never": "^1.2.1",
"content-tag": "^2.0.1",
Expand Down
29 changes: 16 additions & 13 deletions packages/vite/src/build.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,26 @@
import { fork } from 'child_process';
import type { Plugin } from 'vite';

export function emberBuild(command: string, mode: string): Promise<void> {
export function emberBuild(command: string, mode: string, resolvableExtensions: string[] | undefined): Promise<void> {
let env: Record<string, string> = {
...process.env,
EMBROIDER_PREBUILD: 'true',
};

if (resolvableExtensions) {
env['EMBROIDER_RESOLVABLE_EXTENSIONS'] = resolvableExtensions?.join(',');
}

if (command === 'build') {
return new Promise((resolve, reject) => {
const child = fork('./node_modules/ember-cli/bin/ember', ['build', '--environment', mode], {
env: {
...process.env,
EMBROIDER_PREBUILD: 'true',
},
});
const child = fork('./node_modules/ember-cli/bin/ember', ['build', '--environment', mode], { env });
child.on('exit', code => (code === 0 ? resolve() : reject()));
});
}
return new Promise((resolve, reject) => {
const child = fork('./node_modules/ember-cli/bin/ember', ['build', '--watch', '--environment', mode], {
silent: true,
env: {
...process.env,
EMBROIDER_PREBUILD: 'true',
},
env,
});
child.on('exit', code => (code === 0 ? resolve() : reject(new Error('ember build --watch failed'))));
child.on('spawn', () => {
Expand All @@ -39,13 +40,15 @@ export function emberBuild(command: string, mode: string): Promise<void> {
export function compatPrebuild(): Plugin {
let viteCommand: string | undefined;
let viteMode: string | undefined;
let resolvableExtensions: string[] | undefined;

return {
name: 'embroider-builder',
enforce: 'pre',
config(_config, { mode, command }) {
config(config, { mode, command }) {
viteCommand = command;
viteMode = mode;
resolvableExtensions = config.resolve?.extensions;
},
async buildStart() {
if (!viteCommand) {
Expand All @@ -54,7 +57,7 @@ export function compatPrebuild(): Plugin {
if (!viteMode) {
throw new Error(`bug: embroider compatPrebuild did not detect Vite's mode`);
}
await emberBuild(viteCommand, viteMode);
await emberBuild(viteCommand, viteMode, resolvableExtensions);
},
};
}
Loading
Loading