Skip to content

Commit

Permalink
[wip] sketching watch API
Browse files Browse the repository at this point in the history
This is just a toy example to motivate a watch api for virtual content.

Along the way we discovered that vite incorectly implements rollups `addWatchFile` API because (1) it's supposed to accept directories and doesn't and (2) it's supposed to resolve relative paths relative to the current working directory, not the current request.
  • Loading branch information
ef4 committed Jan 18, 2024
1 parent 289f83d commit 2488b9a
Show file tree
Hide file tree
Showing 5 changed files with 30 additions and 10 deletions.
4 changes: 4 additions & 0 deletions packages/core/src/module-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ export class Resolver {
constructor(readonly options: Options) {}

beforeResolve<R extends ModuleRequest>(request: R): R {
if (request.specifier === '#the-file-list') {
return request.virtualize('/the-file-list');
}

if (request.specifier === '@embroider/macros') {
// the macros package is always handled directly within babel (not
// necessarily as a real resolvable package), so we should not mess with it.
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/node-resolve.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export function nodeResolve(
type: 'found',
result: {
type: 'virtual' as 'virtual',
content: virtualContent(request.specifier, resolver),
content: virtualContent(request.specifier, resolver).src,
filename: request.specifier,
},
};
Expand Down
24 changes: 18 additions & 6 deletions packages/core/src/virtual-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { dirname, basename, resolve, posix, sep, join } from 'path';
import type { Resolver, AddonPackage, Package } from '.';
import { explicitRelative, extensionsPattern } from '.';
import { compile } from './js-handlebars';
import { readdirSync } from 'fs-extra';

const externalESPrefix = '/@embroider/ext-es/';
const externalCJSPrefix = '/@embroider/ext-cjs/';
Expand All @@ -10,29 +11,40 @@ const externalCJSPrefix = '/@embroider/ext-cjs/';
// this produces the corresponding contents. It's a static, stateless function
// because we recognize that that process that did resolution might not be the
// same one that loads the content.
export function virtualContent(filename: string, resolver: Resolver): string {
export function virtualContent(filename: string, resolver: Resolver): { src: string; watches: string[] } {
if (filename === '/the-file-list') {
let dir = join(resolver.options.appRoot, 'app');
let files = readdirSync(dir);
return {
src: `export default [
${files.map(f => `"${f}"`).join(',')}
]`,
watches: [join(resolver.options.appRoot, 'app', 'app.js')],
};
}

let cjsExtern = decodeVirtualExternalCJSModule(filename);
if (cjsExtern) {
return renderCJSExternalShim(cjsExtern);
return { src: renderCJSExternalShim(cjsExtern), watches: [] };
}

let extern = decodeVirtualExternalESModule(filename);
if (extern) {
return renderESExternalShim(extern);
return { src: renderESExternalShim(extern), watches: [] };
}
let match = decodeVirtualPairComponent(filename);
if (match) {
return pairedComponentShim(match);
return { src: pairedComponentShim(match), watches: [] };
}

let fb = decodeFastbootSwitch(filename);
if (fb) {
return fastbootSwitchTemplate(fb);
return { src: fastbootSwitchTemplate(fb), watches: [] };
}

let im = decodeImplicitModules(filename);
if (im) {
return renderImplicitModules(im, resolver);
return { src: renderImplicitModules(im, resolver), watches: [] };
}

throw new Error(`not an @embroider/core virtual file: ${filename}`);
Expand Down
4 changes: 2 additions & 2 deletions packages/vite/src/esbuild-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function esBuildResolver(root = process.cwd()): EsBuildPlugin {
});

build.onLoad({ namespace: 'embroider', filter: /./ }, ({ path }) => {
let src = virtualContent(path, resolverLoader.resolver);
let { src } = virtualContent(path, resolverLoader.resolver);
if (!macrosConfig) {
macrosConfig = readJSONSync(
resolve(locateEmbroiderWorkingDir(root), 'rewritten-app', 'macros-config.json')
Expand Down Expand Up @@ -82,7 +82,7 @@ export function esBuildResolver(root = process.cwd()): EsBuildPlugin {
build.onLoad({ filter: /\.[jt]s$/ }, ({ path, namespace }) => {
let src: string;
if (namespace === 'embroider') {
src = virtualContent(path, resolverLoader.resolver);
src = virtualContent(path, resolverLoader.resolver).src;
} else {
src = readFileSync(path, 'utf8');
}
Expand Down
6 changes: 5 additions & 1 deletion packages/vite/src/resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,11 @@ export function resolver(): Plugin {
},
load(id) {
if (id.startsWith(virtualPrefix)) {
return virtualContent(id.slice(virtualPrefix.length), resolverLoader.resolver);
let { src, watches } = virtualContent(id.slice(virtualPrefix.length), resolverLoader.resolver);
for (let watch of watches) {
this.addWatchFile(watch);
}
return src;
}
},
};
Expand Down

0 comments on commit 2488b9a

Please sign in to comment.