From 4b6494033f7f9d9de1de1d652be5a4e5badfe0d7 Mon Sep 17 00:00:00 2001 From: Alex LaFroscia Date: Sat, 10 Apr 2021 23:24:10 -0400 Subject: [PATCH 1/5] refactor: move packager utils into core --- packages/{webpack => core}/src/html-entrypoint.ts | 2 +- packages/{webpack => core}/src/html-placeholder.ts | 0 packages/core/src/index.ts | 2 ++ packages/{webpack => core}/src/stat-summary.ts | 2 +- packages/webpack/src/ember-webpack.ts | 4 +--- 5 files changed, 5 insertions(+), 5 deletions(-) rename packages/{webpack => core}/src/html-entrypoint.ts (98%) rename packages/{webpack => core}/src/html-placeholder.ts (100%) rename packages/{webpack => core}/src/stat-summary.ts (88%) diff --git a/packages/webpack/src/html-entrypoint.ts b/packages/core/src/html-entrypoint.ts similarity index 98% rename from packages/webpack/src/html-entrypoint.ts rename to packages/core/src/html-entrypoint.ts index 17880fc79..533638f3f 100644 --- a/packages/webpack/src/html-entrypoint.ts +++ b/packages/core/src/html-entrypoint.ts @@ -1,4 +1,4 @@ -import { getOrCreate } from '@embroider/core'; +import { getOrCreate } from '@embroider/shared-internals'; import { readFileSync } from 'fs-extra'; import { join } from 'path'; import { JSDOM } from 'jsdom'; diff --git a/packages/webpack/src/html-placeholder.ts b/packages/core/src/html-placeholder.ts similarity index 100% rename from packages/webpack/src/html-placeholder.ts rename to packages/core/src/html-placeholder.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index f9069d61f..52f7e6171 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -5,6 +5,8 @@ export { applyVariantToBabelConfig, applyVariantToTemplateCompiler, } from './packager'; +export { HTMLEntrypoint } from './html-entrypoint'; +export { StatSummary } from './stat-summary'; export { Resolver } from './resolver'; export { default as Stage } from './stage'; export { NodeTemplateCompiler, NodeTemplateCompilerParams } from './template-compiler-node'; diff --git a/packages/webpack/src/stat-summary.ts b/packages/core/src/stat-summary.ts similarity index 88% rename from packages/webpack/src/stat-summary.ts rename to packages/core/src/stat-summary.ts index 1ca146b90..a359c0406 100644 --- a/packages/webpack/src/stat-summary.ts +++ b/packages/core/src/stat-summary.ts @@ -1,4 +1,4 @@ -import { Variant } from '@embroider/core'; +import { Variant } from './packager'; export interface StatSummary { // entrypoints.get(inputAsset).get(variantIndex) === outputAssets diff --git a/packages/webpack/src/ember-webpack.ts b/packages/webpack/src/ember-webpack.ts index 9f16e89ce..b1272e462 100644 --- a/packages/webpack/src/ember-webpack.ts +++ b/packages/webpack/src/ember-webpack.ts @@ -9,7 +9,7 @@ getting script vs module context correct). */ -import { getOrCreate, Variant, applyVariantToBabelConfig } from '@embroider/core'; +import { getOrCreate, Variant, applyVariantToBabelConfig, HTMLEntrypoint, StatSummary } from '@embroider/core'; import { PackagerInstance, AppMeta, Packager } from '@embroider/core'; import webpack, { Configuration } from 'webpack'; import { readFileSync, outputFileSync, copySync, realpathSync, Stats, statSync, readJsonSync } from 'fs-extra'; @@ -22,8 +22,6 @@ import makeDebug from 'debug'; import { format } from 'util'; import { tmpdir } from 'os'; import { warmup as threadLoaderWarmup } from 'thread-loader'; -import { HTMLEntrypoint } from './html-entrypoint'; -import { StatSummary } from './stat-summary'; import crypto from 'crypto'; import type { HbsLoaderConfig } from '@embroider/hbs-loader'; From 0314a0edda3e3e2e73201e43f9982fa93b44d62b Mon Sep 17 00:00:00 2001 From: Alex LaFroscia Date: Mon, 19 Apr 2021 15:16:04 -0400 Subject: [PATCH 2/5] chore(core): rename Packager interfaces --- packages/compat/src/default-pipeline.ts | 4 ++-- packages/core/src/index.ts | 2 +- packages/core/src/packager.ts | 6 +++--- packages/core/src/to-broccoli-plugin.ts | 8 +++++--- packages/test-setup/src/index.ts | 4 ++-- packages/webpack/src/ember-webpack.ts | 4 ++-- 6 files changed, 15 insertions(+), 13 deletions(-) diff --git a/packages/compat/src/default-pipeline.ts b/packages/compat/src/default-pipeline.ts index 68ee1b673..17ce84c89 100644 --- a/packages/compat/src/default-pipeline.ts +++ b/packages/compat/src/default-pipeline.ts @@ -1,5 +1,5 @@ import { App, Addons as CompatAddons, Options, PrebuiltAddons } from '.'; -import { toBroccoliPlugin, Packager, Variant } from '@embroider/core'; +import { toBroccoliPlugin, PackagerConstructor, Variant } from '@embroider/core'; import { Node } from 'broccoli-node-api'; import writeFile from 'broccoli-file-creator'; import mergeTrees from 'broccoli-merge-trees'; @@ -12,7 +12,7 @@ export interface PipelineOptions extends Options { export default function defaultPipeline( emberApp: object, - packager?: Packager, + packager?: PackagerConstructor, options?: PipelineOptions ): Node { let outputPath: string; diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 52f7e6171..00938f83e 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,6 +1,6 @@ export { Packager, - PackagerInstance, + PackagerConstructor, Variant, applyVariantToBabelConfig, applyVariantToTemplateCompiler, diff --git a/packages/core/src/packager.ts b/packages/core/src/packager.ts index b1208c1cd..b969049c4 100644 --- a/packages/core/src/packager.ts +++ b/packages/core/src/packager.ts @@ -23,7 +23,7 @@ export interface Variant { optimizeForProduction: boolean; } -export interface Packager { +export interface PackagerConstructor { new ( // where on disk the packager will find the app it's supposed to build. The // app and its addons will necessarily already be in v2 format, which is @@ -54,13 +54,13 @@ export interface Packager { // packager is based on a third-party tool, this is where that tool's // configuration can go. options?: Options - ): PackagerInstance; + ): Packager; // a description for this packager that aids debugging & profiling annotation: string; } -export interface PackagerInstance { +export interface Packager { build(): Promise; } diff --git a/packages/core/src/to-broccoli-plugin.ts b/packages/core/src/to-broccoli-plugin.ts index 7bdb0b66d..53e5ab306 100644 --- a/packages/core/src/to-broccoli-plugin.ts +++ b/packages/core/src/to-broccoli-plugin.ts @@ -1,14 +1,16 @@ import Plugin from 'broccoli-plugin'; -import { Packager, PackagerInstance, Variant } from './packager'; +import { Packager, PackagerConstructor, Variant } from './packager'; import Stage from './stage'; interface BroccoliPackager { new (stage: Stage, variants: Variant[], options?: Options): Plugin; } -export default function toBroccoliPlugin(packagerClass: Packager): BroccoliPackager { +export default function toBroccoliPlugin( + packagerClass: PackagerConstructor +): BroccoliPackager { class PackagerRunner extends Plugin { - private packager: PackagerInstance | undefined; + private packager: Packager | undefined; constructor(private stage: Stage, private variants: Variant[], private options?: Options) { super([stage.tree], { persistentOutput: true, diff --git a/packages/test-setup/src/index.ts b/packages/test-setup/src/index.ts index 0f797038f..fc933f9f3 100644 --- a/packages/test-setup/src/index.ts +++ b/packages/test-setup/src/index.ts @@ -1,8 +1,8 @@ import type { PipelineOptions } from '@embroider/compat'; -import type { Packager } from '@embroider/core'; +import type { PackagerConstructor } from '@embroider/core'; import type { Webpack } from '@embroider/webpack'; -type EmberWebpackOptions = typeof Webpack extends Packager ? Options : never; +type EmberWebpackOptions = typeof Webpack extends PackagerConstructor ? Options : never; // eslint-disable-next-line @typescript-eslint/no-require-imports const currentEmbroiderVersion = require('../package.json').version; diff --git a/packages/webpack/src/ember-webpack.ts b/packages/webpack/src/ember-webpack.ts index b1272e462..c57a6bba4 100644 --- a/packages/webpack/src/ember-webpack.ts +++ b/packages/webpack/src/ember-webpack.ts @@ -10,7 +10,7 @@ */ import { getOrCreate, Variant, applyVariantToBabelConfig, HTMLEntrypoint, StatSummary } from '@embroider/core'; -import { PackagerInstance, AppMeta, Packager } from '@embroider/core'; +import { AppMeta, Packager, PackagerConstructor } from '@embroider/core'; import webpack, { Configuration } from 'webpack'; import { readFileSync, outputFileSync, copySync, realpathSync, Stats, statSync, readJsonSync } from 'fs-extra'; import { join, dirname, relative, sep } from 'path'; @@ -66,7 +66,7 @@ interface Options { // PackagerInstance, but our constructor conforms to Packager. So instead of // just exporting our class directly, we export a const constructor of the // correct type. -const Webpack: Packager = class Webpack implements PackagerInstance { +const Webpack: PackagerConstructor = class Webpack implements Packager { static annotation = '@embroider/webpack'; pathToVanillaApp: string; From e0514408c7c07d3ffa8b2307d90c5460e9b975a2 Mon Sep 17 00:00:00 2001 From: Alex LaFroscia Date: Mon, 19 Apr 2021 16:14:46 -0400 Subject: [PATCH 3/5] chore(core): move `getAppMeta` util to core --- packages/core/src/index.ts | 1 + packages/core/src/packager.ts | 11 +++++++++ packages/core/tests/packager.test.ts | 35 +++++++++++++++++++++++++++ packages/webpack/src/ember-webpack.ts | 15 +++++++++--- 4 files changed, 59 insertions(+), 3 deletions(-) create mode 100644 packages/core/tests/packager.test.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 00938f83e..4c115ed1a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -4,6 +4,7 @@ export { Variant, applyVariantToBabelConfig, applyVariantToTemplateCompiler, + getAppMeta, } from './packager'; export { HTMLEntrypoint } from './html-entrypoint'; export { StatSummary } from './stat-summary'; diff --git a/packages/core/src/packager.ts b/packages/core/src/packager.ts index b969049c4..d92ea34e6 100644 --- a/packages/core/src/packager.ts +++ b/packages/core/src/packager.ts @@ -1,3 +1,7 @@ +import { AppMeta } from '@embroider/shared-internals'; +import { readFileSync } from 'fs-extra'; +import { join } from 'path'; + // This is a collection of flags that convey what kind of build you want. They // are intended to be generic across Packagers, and it's up to Packager authors // to support each option (or not). @@ -87,3 +91,10 @@ export function applyVariantToTemplateCompiler(_variant: Variant, templateCompil // Packagers must call this function anyway because we will. return templateCompiler; } + +/** + * Get the app meta-data for a package + */ +export function getAppMeta(pathToVanillaApp: string): AppMeta { + return JSON.parse(readFileSync(join(pathToVanillaApp, 'package.json'), 'utf8'))['ember-addon'] as AppMeta; +} diff --git a/packages/core/tests/packager.test.ts b/packages/core/tests/packager.test.ts new file mode 100644 index 000000000..b21dcc577 --- /dev/null +++ b/packages/core/tests/packager.test.ts @@ -0,0 +1,35 @@ +import { AppMeta, getAppMeta } from '../src'; +import { writeJSONSync } from 'fs-extra'; +import { join } from 'path'; +import * as tmp from 'tmp'; + +tmp.setGracefulCleanup(); + +describe('getAppMeta', () => { + let name: string, removeCallback: tmp.DirResult['removeCallback']; + + beforeEach(() => { + ({ name, removeCallback } = tmp.dirSync()); + + writeJSONSync(join(name, 'package.json'), { + 'ember-addon': { + version: 2, + type: 'app', + 'auto-upgraded': true, + }, + }); + }); + + afterEach(() => { + removeCallback(); + }); + + test('reading the app metadata from a package', () => { + const meta: AppMeta = getAppMeta(name); + expect(meta).toMatchObject({ + version: 2, + type: 'app', + 'auto-upgraded': true, + }); + }); +}); diff --git a/packages/webpack/src/ember-webpack.ts b/packages/webpack/src/ember-webpack.ts index c57a6bba4..65d34c80b 100644 --- a/packages/webpack/src/ember-webpack.ts +++ b/packages/webpack/src/ember-webpack.ts @@ -9,8 +9,17 @@ getting script vs module context correct). */ -import { getOrCreate, Variant, applyVariantToBabelConfig, HTMLEntrypoint, StatSummary } from '@embroider/core'; -import { AppMeta, Packager, PackagerConstructor } from '@embroider/core'; +import { + AppMeta, + HTMLEntrypoint, + Packager, + PackagerConstructor, + StatSummary, + Variant, + applyVariantToBabelConfig, + getAppMeta, + getOrCreate, +} from '@embroider/core'; import webpack, { Configuration } from 'webpack'; import { readFileSync, outputFileSync, copySync, realpathSync, Stats, statSync, readJsonSync } from 'fs-extra'; import { join, dirname, relative, sep } from 'path'; @@ -95,7 +104,7 @@ const Webpack: PackagerConstructor = class Webpack implements Packager } private examineApp(): AppInfo { - let meta = JSON.parse(readFileSync(join(this.pathToVanillaApp, 'package.json'), 'utf8'))['ember-addon'] as AppMeta; + let meta = getAppMeta(this.pathToVanillaApp); let templateCompiler = meta['template-compiler']; let rootURL = meta['root-url']; let babel = meta['babel']; From e4627b445f50275c17a334d33ee1e365910b1361 Mon Sep 17 00:00:00 2001 From: Alex LaFroscia Date: Tue, 20 Apr 2021 08:36:45 -0400 Subject: [PATCH 4/5] chore(core): extract util for packager cache location --- packages/core/src/index.ts | 1 + packages/core/src/packager.ts | 10 ++++++++++ packages/core/tests/packager.test.ts | 10 +++++++++- packages/webpack/src/ember-webpack.ts | 7 ++----- 4 files changed, 22 insertions(+), 6 deletions(-) diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 4c115ed1a..2a4f32732 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -5,6 +5,7 @@ export { applyVariantToBabelConfig, applyVariantToTemplateCompiler, getAppMeta, + getPackagerCacheDir, } from './packager'; export { HTMLEntrypoint } from './html-entrypoint'; export { StatSummary } from './stat-summary'; diff --git a/packages/core/src/packager.ts b/packages/core/src/packager.ts index d92ea34e6..3fe9d5cd2 100644 --- a/packages/core/src/packager.ts +++ b/packages/core/src/packager.ts @@ -1,6 +1,7 @@ import { AppMeta } from '@embroider/shared-internals'; import { readFileSync } from 'fs-extra'; import { join } from 'path'; +import { tmpdir } from 'os'; // This is a collection of flags that convey what kind of build you want. They // are intended to be generic across Packagers, and it's up to Packager authors @@ -98,3 +99,12 @@ export function applyVariantToTemplateCompiler(_variant: Variant, templateCompil export function getAppMeta(pathToVanillaApp: string): AppMeta { return JSON.parse(readFileSync(join(pathToVanillaApp, 'package.json'), 'utf8'))['ember-addon'] as AppMeta; } + +/** + * Get the path to a cache directory in the recommended location + * + * This ensures they have exactly the same lifetime as some of embroider's own caches. + */ +export function getPackagerCacheDir(name: string): string { + return join(tmpdir(), 'embroider', name); +} diff --git a/packages/core/tests/packager.test.ts b/packages/core/tests/packager.test.ts index b21dcc577..0331b6462 100644 --- a/packages/core/tests/packager.test.ts +++ b/packages/core/tests/packager.test.ts @@ -1,6 +1,7 @@ -import { AppMeta, getAppMeta } from '../src'; +import { AppMeta, getAppMeta, getPackagerCacheDir } from '../src'; import { writeJSONSync } from 'fs-extra'; import { join } from 'path'; +import { tmpdir } from 'os'; import * as tmp from 'tmp'; tmp.setGracefulCleanup(); @@ -33,3 +34,10 @@ describe('getAppMeta', () => { }); }); }); + +describe('getPackagerCacheDir', () => { + test('getting the path to a cache directory', () => { + const cacheDir = getPackagerCacheDir('foo'); + expect(cacheDir).toBe(join(tmpdir(), 'embroider', 'foo')); + }); +}); diff --git a/packages/webpack/src/ember-webpack.ts b/packages/webpack/src/ember-webpack.ts index 65d34c80b..2bf0cfbdd 100644 --- a/packages/webpack/src/ember-webpack.ts +++ b/packages/webpack/src/ember-webpack.ts @@ -18,6 +18,7 @@ import { Variant, applyVariantToBabelConfig, getAppMeta, + getPackagerCacheDir, getOrCreate, } from '@embroider/core'; import webpack, { Configuration } from 'webpack'; @@ -550,11 +551,7 @@ function nonNullArray(array: T[]): NonNullable[] { function babelLoaderOptions(majorVersion: 6 | 7, variant: Variant, appBabelConfigPath: string) { // eslint-disable-next-line @typescript-eslint/no-require-imports let options = Object.assign({}, applyVariantToBabelConfig(variant, require(appBabelConfigPath)), { - // all stage3 packagers should keep persistent caches under - // `join(tmpdir(), 'embroider')`. An important reason is that - // they should have exactly the same lifetime as some of - // embroider's own caches. - cacheDirectory: join(tmpdir(), 'embroider', 'webpack-babel-loader'), + cacheDirectory: getPackagerCacheDir('webpack-babel-loader'), }); if (majorVersion === 7) { if (options.plugins) { From 852f8da7eafcdc26d81e53c0e9124aebf8568cf0 Mon Sep 17 00:00:00 2001 From: Edward Faulkner Date: Wed, 19 May 2021 11:29:53 -0400 Subject: [PATCH 5/5] adjusting StatSummary "Stat" is a webpack-specific concept, so I'd prefer to deemphasize it in the naming and organization and keep this as an implementation detail of HTMLEntrypoint. --- packages/core/src/html-entrypoint.ts | 15 +++++++++++++-- packages/core/src/index.ts | 3 +-- packages/core/src/stat-summary.ts | 12 ------------ packages/webpack/src/ember-webpack.ts | 8 ++++---- 4 files changed, 18 insertions(+), 20 deletions(-) delete mode 100644 packages/core/src/stat-summary.ts diff --git a/packages/core/src/html-entrypoint.ts b/packages/core/src/html-entrypoint.ts index 533638f3f..dfba87266 100644 --- a/packages/core/src/html-entrypoint.ts +++ b/packages/core/src/html-entrypoint.ts @@ -5,7 +5,7 @@ import { JSDOM } from 'jsdom'; import partition from 'lodash/partition'; import zip from 'lodash/zip'; import Placeholder from './html-placeholder'; -import { StatSummary } from './stat-summary'; +import { Variant } from './packager'; export class HTMLEntrypoint { private dom: JSDOM; @@ -77,7 +77,7 @@ export class HTMLEntrypoint { } // bundles maps from input asset to a per-variant map of output assets - render(stats: StatSummary): string { + render(stats: BundleSummary): string { let insertedLazy = false; let fastbootVariant = stats.variants.findIndex(v => Boolean(v.runtime === 'fastboot')); let supportsFastboot = stats.variants.some(v => v.runtime === 'fastboot' || v.runtime === 'all'); @@ -129,6 +129,17 @@ export class HTMLEntrypoint { } } +export interface BundleSummary { + // entrypoints.get(inputAsset).get(variantIndex) === outputAssets + entrypoints: Map>; + + // lazyBundles are tracked specifically for fastboot, so these always come + // from the fastboot variant, if any + lazyBundles: Set; + + variants: Variant[]; +} + function isAbsoluteURL(url: string) { return /^(?:[a-z]+:)?\/\//i.test(url); } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 2a4f32732..968e5461a 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -7,8 +7,7 @@ export { getAppMeta, getPackagerCacheDir, } from './packager'; -export { HTMLEntrypoint } from './html-entrypoint'; -export { StatSummary } from './stat-summary'; +export { HTMLEntrypoint, BundleSummary } from './html-entrypoint'; export { Resolver } from './resolver'; export { default as Stage } from './stage'; export { NodeTemplateCompiler, NodeTemplateCompilerParams } from './template-compiler-node'; diff --git a/packages/core/src/stat-summary.ts b/packages/core/src/stat-summary.ts deleted file mode 100644 index a359c0406..000000000 --- a/packages/core/src/stat-summary.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { Variant } from './packager'; - -export interface StatSummary { - // entrypoints.get(inputAsset).get(variantIndex) === outputAssets - entrypoints: Map>; - - // lazyBundles are tracked specifically for fastboot, so these always come - // from the fastboot variant, if any - lazyBundles: Set; - - variants: Variant[]; -} diff --git a/packages/webpack/src/ember-webpack.ts b/packages/webpack/src/ember-webpack.ts index 2bf0cfbdd..fa6289de4 100644 --- a/packages/webpack/src/ember-webpack.ts +++ b/packages/webpack/src/ember-webpack.ts @@ -12,9 +12,9 @@ import { AppMeta, HTMLEntrypoint, + BundleSummary, Packager, PackagerConstructor, - StatSummary, Variant, applyVariantToBabelConfig, getAppMeta, @@ -293,7 +293,7 @@ const Webpack: PackagerConstructor = class Webpack implements Packager } } - private async writeFiles(stats: StatSummary, { entrypoints, otherAssets }: AppInfo) { + private async writeFiles(stats: BundleSummary, { entrypoints, otherAssets }: AppInfo) { // we're doing this ourselves because I haven't seen a webpack 4 HTML plugin // that handles multiple HTML entrypoints correctly. @@ -386,8 +386,8 @@ const Webpack: PackagerConstructor = class Webpack implements Packager return fileParts.join('.'); } - private summarizeStats(multiStats: webpack.StatsCompilation): StatSummary { - let output: StatSummary = { + private summarizeStats(multiStats: webpack.StatsCompilation): BundleSummary { + let output: BundleSummary = { entrypoints: new Map(), lazyBundles: new Set(), variants: this.variants,