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

fix vite watch mode dep optimizer #1876

Closed
Closed
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
2 changes: 1 addition & 1 deletion packages/core/src/virtual-entrypoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ export function renderEntrypoint(

return {
src: entryTemplate(params),
watches: [],
watches: [fromDir],
};
}

Expand Down
34 changes: 34 additions & 0 deletions packages/vite/src/build.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { fork } from 'child_process';
import type { Plugin } from 'vite';
import { resolve } from 'path';
import fs from 'fs-extra';
const { ensureSymlinkSync, existsSync, writeFileSync } = fs;
import { locateEmbroiderWorkingDir } from '@embroider/core';
import * as process from 'process';

export function emberBuild(command: string, mode: string, resolvableExtensions: string[] | undefined): Promise<void> {
let env: Record<string, string> = {
Expand Down Expand Up @@ -37,6 +42,34 @@ export function emberBuild(command: string, mode: string, resolvableExtensions:
});
}

// we need a bare specifier for rewritten packages that is resolvable from root
// we cannot use a bare specifier that starts with .embroider, that is not a bare import
// according to vite
// which is why we make this symlink
function createSymlinkToRewrittenPackages(appRoot: string = process.cwd()) {
const resolvableRewrittenPackages = resolve(
locateEmbroiderWorkingDir(appRoot),
'..',
'@embroider',
'rewritten-packages'
);
const embroiderDir = resolve(locateEmbroiderWorkingDir(appRoot), 'rewritten-packages');
if (existsSync(embroiderDir)) {
ensureSymlinkSync(embroiderDir, resolvableRewrittenPackages, 'dir');
writeFileSync(
resolve(resolvableRewrittenPackages, 'package.json'),
JSON.stringify(
{
name: '@embroider/rewritten-packages',
main: 'moved-package-target.js',
},
null,
2
)
);
}
}

export function compatPrebuild(): Plugin {
let viteCommand: string | undefined;
let viteMode: string | undefined;
Expand All @@ -58,6 +91,7 @@ export function compatPrebuild(): Plugin {
throw new Error(`bug: embroider compatPrebuild did not detect Vite's mode`);
}
await emberBuild(viteCommand, viteMode, resolvableExtensions);
createSymlinkToRewrittenPackages();
},
};
}
11 changes: 8 additions & 3 deletions packages/vite/src/esbuild-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { dirname } from 'path';

import type { PackageCache as _PackageCache, Resolution, ModuleRequest } from '@embroider/core';
import { externalName } from '@embroider/reverse-exports';
import { makeResolvable } from './request.js';

type PublicAPI<T> = { [K in keyof T]: T[K] };
type PackageCache = PublicAPI<_PackageCache>;
Expand Down Expand Up @@ -169,9 +170,13 @@ export class EsBuildModuleRequest implements ModuleRequest {

requestStatus(request.specifier);

let result = await this.context.resolve(request.specifier, {
importer: request.fromFile,
resolveDir: dirname(request.fromFile),
// we must make this also resolvable so that esbuild generates optimized deps for the same IDs
// that the vite resolver uses
let resolvable = makeResolvable(this.packageCache, this.fromFile, this.specifier);
let r = this.alias(resolvable.specifier).rehome(resolvable.fromFile);
let result = await this.context.resolve(r.specifier, {
importer: r.fromFile,
resolveDir: dirname(r.fromFile),
kind: this.kind,
pluginData: {
embroider: {
Expand Down
77 changes: 74 additions & 3 deletions packages/vite/src/request.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import type { ModuleRequest, Resolution } from '@embroider/core';
import type { ModuleRequest, Resolution, Package, PackageCache as _PackageCache } from '@embroider/core';
import core from '@embroider/core';
const { cleanUrl, getUrlQueryParams } = core;
const { cleanUrl, getUrlQueryParams, locateEmbroiderWorkingDir, packageName } = core;
import type { PluginContext, ResolveIdResult } from 'rollup';
import { resolve } from 'path';

type PublicAPI<T> = { [K in keyof T]: T[K] };
type PackageCache = PublicAPI<_PackageCache>;

export const virtualPrefix = 'embroider_virtual:';

export class RollupModuleRequest implements ModuleRequest {
static from(
packageCache: PackageCache,
context: PluginContext,
source: string,
importer: string | undefined,
Expand Down Expand Up @@ -34,6 +39,7 @@ export class RollupModuleRequest implements ModuleRequest {
let queryParams = getUrlQueryParams(source);

return new RollupModuleRequest(
packageCache,
context,
cleanSource,
fromFile,
Expand All @@ -47,6 +53,7 @@ export class RollupModuleRequest implements ModuleRequest {
}

private constructor(
public packageCache: PackageCache,
private context: PluginContext,
readonly specifier: string,
readonly fromFile: string,
Expand Down Expand Up @@ -75,6 +82,7 @@ export class RollupModuleRequest implements ModuleRequest {

alias(newSpecifier: string) {
return new RollupModuleRequest(
this.packageCache,
this.context,
newSpecifier,
this.fromFile,
Expand All @@ -90,6 +98,7 @@ export class RollupModuleRequest implements ModuleRequest {
return this;
} else {
return new RollupModuleRequest(
this.packageCache,
this.context,
this.specifier,
newFromFile,
Expand All @@ -103,6 +112,7 @@ export class RollupModuleRequest implements ModuleRequest {
}
virtualize(filename: string) {
return new RollupModuleRequest(
this.packageCache,
this.context,
virtualPrefix + filename,
this.fromFile,
Expand All @@ -115,6 +125,7 @@ export class RollupModuleRequest implements ModuleRequest {
}
withMeta(meta: Record<string, any> | undefined): this {
return new RollupModuleRequest(
this.packageCache,
this.context,
this.specifier,
this.fromFile,
Expand All @@ -127,6 +138,7 @@ export class RollupModuleRequest implements ModuleRequest {
}
notFound(): this {
return new RollupModuleRequest(
this.packageCache,
this.context,
this.specifier,
this.fromFile,
Expand All @@ -137,6 +149,7 @@ export class RollupModuleRequest implements ModuleRequest {
this.importerQueryParams
) as this;
}

async defaultResolve(): Promise<Resolution<ResolveIdResult>> {
if (this.isVirtual) {
return {
Expand All @@ -153,7 +166,9 @@ export class RollupModuleRequest implements ModuleRequest {
(err as any).code = 'MODULE_NOT_FOUND';
return { type: 'not_found', err };
}
let result = await this.context.resolve(this.specifierWithQueryParams, this.fromFileWithQueryParams, {
let resolvable = makeResolvable(this.packageCache, this.fromFile, this.specifier);
let r = this.alias(resolvable.specifier).rehome(resolvable.fromFile);
let result = await this.context.resolve(r.specifierWithQueryParams, r.fromFileWithQueryParams, {
skipSelf: true,
custom: {
embroider: {
Expand All @@ -172,6 +187,7 @@ export class RollupModuleRequest implements ModuleRequest {

resolveTo(resolution: Resolution<ResolveIdResult>): this {
return new RollupModuleRequest(
this.packageCache,
this.context,
this.specifier,
this.fromFile,
Expand All @@ -183,3 +199,58 @@ export class RollupModuleRequest implements ModuleRequest {
) as this;
}
}

/**
* For Vite to correctly detect and optimize dependencies the request must have the following conditions
* 1. specifier must be a bare import
* 2. specifier must be node resolvable without any plugins
* 3. importer must not be in node_modules
*
* this functions changes the request for rewritten addons such that they are resolvable from app root
*/
export function makeResolvable(
packageCache: PackageCache,
fromFile: string,
specifier: string
): { fromFile: string; specifier: string } {
if (fromFile.startsWith('@embroider/rewritten-packages')) {
let workingDir = locateEmbroiderWorkingDir(process.cwd());
const rewrittenRoot = resolve(workingDir, 'rewritten-packages');
fromFile = fromFile.replace('@embroider/rewritten-packages', rewrittenRoot);
}
if (fromFile && !fromFile.startsWith('./')) {
let fromPkg: Package;
try {
fromPkg = packageCache.ownerOfFile(fromFile) || packageCache.ownerOfFile(process.cwd())!;
} catch (e) {
fromPkg = packageCache.ownerOfFile(process.cwd())!;
}

if (!fromPkg.isV2App()) {
return { fromFile, specifier };
}

let pkgName = packageName(specifier);
try {
let pkg = pkgName ? packageCache.resolve(pkgName, fromPkg!) : fromPkg;
if (!pkg.isV2Addon() || !pkg.meta['auto-upgraded'] || !pkg.root.includes('rewritten-packages')) {
// some tests make addons be auto-upgraded, but are not actually in rewritten-packages
return { fromFile, specifier };
}
let levels = ['..'];
if (pkg.name.startsWith('@')) {
levels.push('..');
}
let resolvedRoot = resolve(pkg.root, ...levels, ...levels, '..');
if (specifier.startsWith(pkg.name)) {
specifier = resolve(pkg.root, ...levels, specifier);
}
specifier = specifier.replace(resolvedRoot, '@embroider/rewritten-packages').replace(/\\/g, '/');
return {
specifier,
fromFile: resolve(process.cwd(), 'package.json'),
};
} catch (e) {}
}
return { fromFile, specifier };
}
8 changes: 7 additions & 1 deletion packages/vite/src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,13 @@ export function resolver(): Plugin {
return await observeDepScan(this, source, importer, options);
}

let request = RollupModuleRequest.from(this, source, importer, options.custom);
let request = RollupModuleRequest.from(
resolverLoader.resolver.packageCache,
this,
source,
importer,
options.custom
);
if (!request) {
// fallthrough to other rollup plugins
return null;
Expand Down
Loading
Loading