Skip to content

Commit

Permalink
Merge pull request #1805 from BlueCutOfficial/virtual-vendor-style
Browse files Browse the repository at this point in the history
Module resolver: virtualize vendor.css
  • Loading branch information
mansona authored Apr 10, 2024
2 parents ef8eacd + 9795beb commit cb8efec
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 19 deletions.
19 changes: 2 additions & 17 deletions packages/compat/src/compat-app-builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,10 +516,8 @@ export class CompatAppBuilder {
}
}

let implicitStyles = this.implicitStylesAsset(prepared, parentEngine);
if (implicitStyles) {
html.insertStyleLink(html.implicitStyles, implicitStyles.relativePath);
}
// virtual vendor.css entrypoint
html.insertStyleLink(html.implicitStyles, '@embroider/core/vendor.css');

if (!asset.fileAsset.includeTests) {
return;
Expand Down Expand Up @@ -555,19 +553,6 @@ export class CompatAppBuilder {
return asset;
}

private implicitStylesAsset(prepared: Map<string, InternalAsset>, application: AppFiles): InternalAsset | undefined {
let asset = prepared.get('assets/vendor.css');
if (!asset) {
let implicitStyles = this.impliedAssets('implicit-styles', application);
if (implicitStyles.length > 0) {
// we reverse because we want the synthetic vendor style at the top
asset = new ConcatenatedAsset('assets/vendor.css', implicitStyles.reverse(), this.resolvableExtensionsPattern);
prepared.set(asset.relativePath, asset);
}
}
return asset;
}

private implicitTestStylesAsset(
prepared: Map<string, InternalAsset>,
application: AppFiles
Expand Down
23 changes: 23 additions & 0 deletions packages/core/src/module-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ export class Resolver {
request = await this.handleGlobalsCompat(request);
request = this.handleImplicitModules(request);
request = this.handleImplicitTestScripts(request);
request = this.handleVendorStyles(request);
request = this.handleRenaming(request);
// we expect the specifier to be app relative at this point - must be after handleRenaming
request = this.generateFastbootSwitch(request);
Expand Down Expand Up @@ -473,6 +474,28 @@ export class Resolver {
}
}

private handleVendorStyles<R extends ModuleRequest>(request: R): R {
//TODO move the extra forwardslash handling out into the vite plugin
const candidates = ['@embroider/core/vendor.css', '/@embroider/core/vendor.css', './@embroider/core/vendor.css'];

if (!candidates.includes(request.specifier)) {
return request;
}

let pkg = this.packageCache.ownerOfFile(request.fromFile);
if (pkg?.root !== this.options.engines[0].root) {
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.`
);
}

return logTransition(
'vendor-styles',
request,
request.virtualize(resolve(pkg.root, '-embroider-vendor-styles.css'))
);
}

private resolveHelper<R extends ModuleRequest>(path: string, inEngine: EngineConfig, request: R): R {
let target = this.parseGlobalPath(path, inEngine);
return logTransition(
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/virtual-content.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { explicitRelative, extensionsPattern } from '.';
import { compile } from './js-handlebars';
import { decodeImplicitTestScripts, renderImplicitTestScripts } from './virtual-test-support';

import { decodeVirtualVendorStyles, renderVendorStyles } from './virtual-vendor-styles';

const externalESPrefix = '/@embroider/ext-es/';
const externalCJSPrefix = '/@embroider/ext-cjs/';

Expand Down Expand Up @@ -46,6 +48,11 @@ export function virtualContent(filename: string, resolver: Resolver): VirtualCon
return renderImplicitTestScripts(filename, resolver);
}

let isVendorStyles = decodeVirtualVendorStyles(filename);
if (isVendorStyles) {
return renderVendorStyles(filename, resolver);
}

throw new Error(`not an @embroider/core virtual file: ${filename}`);
}

Expand Down
77 changes: 77 additions & 0 deletions packages/core/src/virtual-vendor-styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import type { Package } from '@embroider/shared-internals';
import type { V2AddonPackage } from '@embroider/shared-internals/src/package';
import { readFileSync } from 'fs';
import { sortBy } from 'lodash';
import resolve from 'resolve';
import type { Resolver } from './module-resolver';
import type { VirtualContentResult } from './virtual-content';
import type { Engine } from './app-files';

export function decodeVirtualVendorStyles(filename: string): boolean {
return filename.endsWith('-embroider-vendor-styles.css');
}

export function renderVendorStyles(filename: string, resolver: Resolver): VirtualContentResult {
const owner = resolver.packageCache.ownerOfFile(filename);
if (!owner) {
throw new Error(`Failed to find a valid owner for ${filename}`);
}
return { src: getVendorStyles(owner, resolver), watches: [] };
}

function getVendorStyles(owner: Package, resolver: Resolver): string {
let engineConfig = resolver.owningEngine(owner);
let engine: Engine = {
package: owner,
addons: new Map(
engineConfig.activeAddons.map(addon => [
resolver.packageCache.get(addon.root) as V2AddonPackage,
addon.canResolveFromFile,
])
),
isApp: true,
modulePrefix: resolver.options.modulePrefix,
appRelativePath: 'NOT_USED_DELETE_ME',
};

return generateVendorStyles(engine);
}

function generateVendorStyles(engine: Engine): string {
let result: string[] = impliedAddonVendorStyles(engine).map((sourcePath: string): string => {
let source = readFileSync(sourcePath);
return `${source}`;
});

return result.join('') as string;
}

function impliedAddonVendorStyles(engine: Engine): string[] {
let result: Array<string> = [];
for (let addon of sortBy(Array.from(engine.addons.keys()), pkg => {
switch (pkg.name) {
case 'loader.js':
return 0;
case 'ember-source':
return 10;
default:
return 1000;
}
})) {
let implicitStyles = addon.meta['implicit-styles'];
if (implicitStyles) {
let styles = [];
let options = { basedir: addon.root };
for (let mod of implicitStyles) {
// exclude engines because they will handle their own css importation
if (!addon.isLazyEngine()) {
styles.push(resolve.sync(mod, options));
}
}
if (styles.length) {
result = [...styles, ...result];
}
}
}
return result;
}
4 changes: 4 additions & 0 deletions packages/vite/src/esbuild-resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ export function esBuildResolver(root = process.cwd()): EsBuildPlugin {
});

build.onLoad({ namespace: 'embroider', filter: /./ }, ({ path }) => {
// We don't want esbuild to try loading virtual CSS files
if (path.endsWith('.css')) {
return { contents: '' };
}
let { src } = virtualContent(path, resolverLoader.resolver);
if (!macrosConfig) {
macrosConfig = readJSONSync(
Expand Down
4 changes: 2 additions & 2 deletions tests/scenarios/compat-app-html-attributes-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ appScenarios
expectFile('./index.html').doesNotMatch('rel="stylesheet"', 'does not have rel=stylesheet');
expectFile('./index.html').matches('<script defer', 'has script defer');
expectFile('./index.html').doesNotMatch('<script src', 'does not have script src');
expectFile('./index.html').doesNotMatch(
expectFile('./index.html').matches(
'data-original-filename="vendor.css">',
'does not have data-original-filename vendor.css'
'has data-original-filename vendor.css'
);
expectFile('./index.html').matches(
'" data-original-filename="app-template.css">',
Expand Down

0 comments on commit cb8efec

Please sign in to comment.