Skip to content

Commit

Permalink
Merge pull request #1763 from embroider-build/watch-api
Browse files Browse the repository at this point in the history
Add new watches API
  • Loading branch information
mansona authored Feb 1, 2024
2 parents c5f8d55 + 0c756cc commit f361b71
Show file tree
Hide file tree
Showing 6 changed files with 95 additions and 22 deletions.
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
74 changes: 60 additions & 14 deletions packages/core/src/virtual-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,16 @@ import { compile } from './js-handlebars';
const externalESPrefix = '/@embroider/ext-es/';
const externalCJSPrefix = '/@embroider/ext-cjs/';

export interface VirtualContentResult {
src: string;
watches: string[];
}

// Given a filename that was passed to your ModuleRequest's `virtualize()`,
// 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): VirtualContentResult {
let cjsExtern = decodeVirtualExternalCJSModule(filename);
if (cjsExtern) {
return renderCJSExternalShim(cjsExtern);
Expand All @@ -27,7 +32,7 @@ export function virtualContent(filename: string, resolver: Resolver): string {

let fb = decodeFastbootSwitch(filename);
if (fb) {
return fastbootSwitchTemplate(fb);
return renderFastbootSwitchTemplate(fb);
}

let im = decodeImplicitModules(filename);
Expand Down Expand Up @@ -56,15 +61,37 @@ export { {{#each names as |name|}}{{name}}, {{/each}} }
{{/if}}
`) as (params: { moduleName: string; default: boolean; names: string[] }) => string;

function renderESExternalShim({ moduleName, exports }: { moduleName: string; exports: string[] }): string {
return externalESShim({
moduleName,
default: exports.includes('default'),
names: exports.filter(n => n !== 'default'),
});
function renderESExternalShim({
moduleName,
exports,
}: {
moduleName: string;
exports: string[];
}): VirtualContentResult {
return {
src: externalESShim({
moduleName,
default: exports.includes('default'),
names: exports.filter(n => n !== 'default'),
}),
watches: [],
};
}

interface PairedComponentShimParams {
relativeHBSModule: string;
relativeJSModule: string | null;
debugName: string;
}

function pairedComponentShim(params: PairedComponentShimParams): VirtualContentResult {
return {
src: pairedComponentShimTemplate(params),
watches: [],
};
}

const pairedComponentShim = compile(`
const pairedComponentShimTemplate = compile(`
import { setComponentTemplate } from "@ember/component";
import template from "{{{js-string-escape relativeHBSModule}}}";
{{#if relativeJSModule}}
Expand All @@ -74,7 +101,7 @@ export default setComponentTemplate(template, component);
import templateOnlyComponent from "@ember/component/template-only";
export default setComponentTemplate(template, templateOnlyComponent(undefined, "{{{js-string-escape debugName}}}"));
{{/if}}
`) as (params: { relativeHBSModule: string; relativeJSModule: string | null; debugName: string }) => string;
`) as (params: PairedComponentShimParams) => string;

export function virtualExternalESModule(specifier: string, exports: string[] | undefined): string {
if (exports) {
Expand Down Expand Up @@ -166,6 +193,18 @@ export function decodeFastbootSwitch(filename: string) {
}
}

interface FastbootSwitchParams {
names: string[];
hasDefaultExport: boolean;
}

function renderFastbootSwitchTemplate(params: FastbootSwitchParams): VirtualContentResult {
return {
src: fastbootSwitchTemplate(params),
watches: [],
};
}

const fastbootSwitchTemplate = compile(`
import { macroCondition, getGlobalConfig, importSync } from '@embroider/macros';
let mod;
Expand All @@ -180,7 +219,7 @@ export default mod.default;
{{#each names as |name|}}
export const {{name}} = mod.{{name}};
{{/each}}
`) as (params: { names: string[]; hasDefaultExport: boolean }) => string;
`) as (params: FastbootSwitchParams) => string;

const implicitModulesPattern = /(?<filename>.*)[\\/]-embroider-implicit-(?<test>test-)?modules\.js$/;

Expand Down Expand Up @@ -209,7 +248,7 @@ function renderImplicitModules(
fromFile: string;
},
resolver: Resolver
): string {
): VirtualContentResult {
let resolvableExtensionsPattern = extensionsPattern(resolver.options.resolvableExtensions);

const pkg = resolver.packageCache.ownerOfFile(fromFile);
Expand Down Expand Up @@ -269,7 +308,7 @@ function renderImplicitModules(
dependencyModules.push(posix.join(dep.name, `-embroider-${type}.js`));
}
}
return implicitModulesTemplate({ ownModules, dependencyModules });
return { src: implicitModulesTemplate({ ownModules, dependencyModules }), watches: [] };
}

const implicitModulesTemplate = compile(`
Expand Down Expand Up @@ -323,7 +362,14 @@ function orderAddons(depA: Package, depB: Package): number {
return depAIdx - depBIdx;
}

const renderCJSExternalShim = compile(`
function renderCJSExternalShim(params: { moduleName: string }): VirtualContentResult {
return {
src: renderCJSExternalShimTemplate(params),
watches: [],
};
}

const renderCJSExternalShimTemplate = compile(`
module.exports = new Proxy({}, {
get(target, prop) {
Expand Down
2 changes: 1 addition & 1 deletion packages/hbs-loader/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export interface Options {
};
}

export default function hbsLoader(this: LoaderContext<Options>, templateContent: string) {
export default function hbsLoader(this: LoaderContext<Options>, templateContent: string): string | undefined {
let { compatModuleNaming } = this.getOptions();
try {
return hbsToJS(templateContent, { filename: this.resourcePath, compatModuleNaming });
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
31 changes: 29 additions & 2 deletions packages/vite/src/resolver.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,40 @@
import type { PluginContext, ResolveIdResult } from 'rollup';
import type { Plugin } from 'vite';
import type { Plugin, ViteDevServer } from 'vite';
import type { Resolution, ResolverFunction } from '@embroider/core';
import { virtualContent, ResolverLoader } from '@embroider/core';
import { RollupModuleRequest, virtualPrefix } from './request';
import assertNever from 'assert-never';
import makeDebug from 'debug';

const debug = makeDebug('embroider:vite');

export function resolver(): Plugin {
let resolverLoader = new ResolverLoader(process.cwd());
let server: ViteDevServer;
let virtualDeps: Map<string, string[]> = new Map();

return {
name: 'embroider-resolver',
enforce: 'pre',

configureServer(s) {
server = s;
server.watcher.on('all', (_eventName, path) => {
for (let [id, watches] of virtualDeps) {
for (let watch of watches) {
if (path.startsWith(watch)) {
debug('Invalidate %s because %s', id, path);
server.moduleGraph.onFileChange(id);
let m = server.moduleGraph.getModuleById(id);
if (m) {
server.reloadModule(m);
}
}
}
}
});
},

async resolveId(source, importer, options) {
let request = RollupModuleRequest.from(source, importer, options.custom);
if (!request) {
Expand All @@ -29,7 +53,10 @@ 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);
virtualDeps.set(id, watches);
server.watcher.add(watches);
return src;
}
},
};
Expand Down
4 changes: 2 additions & 2 deletions packages/webpack/src/virtual-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ function setup(appRoot: string): ResolverLoader {
return resolverLoader;
}

export default function virtualLoader(this: LoaderContext<unknown>) {
export default function virtualLoader(this: LoaderContext<unknown>): string | undefined {
if (typeof this.query === 'string' && this.query[0] === '?') {
let params = new URLSearchParams(this.query);
let filename = params.get('f');
Expand All @@ -20,7 +20,7 @@ export default function virtualLoader(this: LoaderContext<unknown>) {
}
let { resolver } = setup(appRoot);
this.resourcePath = filename;
return virtualContent(filename, resolver);
return virtualContent(filename, resolver).src;
}
throw new Error(`@embroider/webpack/src/virtual-loader received unexpected request: ${this.query}`);
}

0 comments on commit f361b71

Please sign in to comment.