From 242b731c86c2a5066445340f163547b52c07de09 Mon Sep 17 00:00:00 2001 From: Chris Manson Date: Mon, 16 Dec 2024 22:11:14 +0000 Subject: [PATCH] use staticInvoakables instead of staticHelpers, staticModifiers, and staticComponents --- README.md | 9 ++-- packages/compat/src/compat-app-builder.ts | 56 ++++++++++++++----- packages/compat/src/options.ts | 21 ++++++-- packages/compat/src/template-tag-codemod.ts | 4 +- packages/core/src/options.ts | 60 ++++++++++++--------- 5 files changed, 101 insertions(+), 49 deletions(-) diff --git a/README.md b/README.md index a3f404a64..06db49eb6 100644 --- a/README.md +++ b/README.md @@ -82,9 +82,7 @@ You can pass options into Embroider by passing them into the `compatBuild` funct return require('@embroider/compat').compatBuild(app, Webpack, { // staticAddonTestSupportTrees: true, // staticAddonTrees: true, - // staticHelpers: true, - // staticModifiers: true, - // staticComponents: true, + // staticInvokables: true, // staticEmberSource: true, // splitAtRoutes: ['route.name'], // can also be a RegExp // packagerOptions: { @@ -99,9 +97,8 @@ The recommended steps when introducing Embroider into an existing app are: 1. First make it work with no options. This is the mode that supports maximum backward compatibility. If you're hitting errors, first look at the "Compatibility with Classic Builds" section below. 2. Enable `staticAddonTestSupportTrees` and `staticAddonTrees` and test your application. This is usually safe, because most code in these trees gets consumed via `import` statements that we can analyze. But you might find exceptional cases where some code is doing a more dynamic thing. -3. Enable `staticHelpers` and `staticModifiers` and test. This is usually safe because addon helpers and modifiers get invoked declaratively in templates and we can see all invocations. -4. Enable `staticComponents`, and work to eliminate any resulting build warnings about dynamic component invocation. You may need to add `packageRules` that declare where invocations like `{{component someComponent}}` are getting `someComponent` from. -5. Once your app is working with all of the above, you can enable `splitAtRoutes` and add the `@embroider/router` and code splitting should work. See the packages/router/README.md for details and limitations. +3. Enable `staticInvokables` and work to eliminate any resulting build warnings about dynamic component invocation. You may need to add `packageRules` that declare where invocations like `{{component someComponent}}` are getting `someComponent` from. +4. Once your app is working with all of the above, you can enable `splitAtRoutes` and add the `@embroider/router` and code splitting should work. See the packages/router/README.md for details and limitations. ## Configuring asset URLs diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index 7589c4628..becb5cd98 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -67,6 +67,9 @@ import { SyncDir } from './sync-dir'; export class CompatAppBuilder { // for each relativePath, an Asset we have already emitted private assets: Map = new Map(); + private staticComponents: boolean; + private staticHelpers: boolean; + private staticModifiers: boolean; constructor( private root: string, @@ -77,7 +80,39 @@ export class CompatAppBuilder { private configTree: V1Config, private synthVendor: Package, private synthStyles: Package - ) {} + ) { + // staticInvokables always wins when configured + if (typeof options.staticInvokables !== 'undefined') { + if ( + typeof options.staticComponents !== 'undefined' || + typeof options.staticHelpers !== 'undefined' || + typeof options.staticModifiers !== 'undefined' + ) { + throw new Error( + 'You cannot set `staticHelpers`, `staticComponents`, or `staticModifiers` if you have set `staticInvokables`. Delete these configs to continue.' + ); + } + this.staticComponents = this.staticHelpers = this.staticModifiers = options.staticInvokables; + return; + } + + if (typeof options.staticComponents !== 'undefined') { + // TODO it doesn't seem like we have any real deprecation functionality in this package yet. + // do we need it? + console.error(`Setting 'staticComponents' is deprecated. Use 'staticInvokables' instead`); + this.staticComponents = options.staticComponents; + } + + if (typeof options.staticHelpers !== 'undefined') { + console.error(`Setting 'staticHelpers' is deprecated. Use 'staticInvokables' instead`); + this.staticHelpers = options.staticHelpers; + } + + if (typeof options.staticModifiers !== 'undefined') { + console.error(`Setting 'staticModifiers' is deprecated. Use 'staticInvokables' instead`); + this.staticModifiers = options.staticModifiers; + } + } @Memoize() private fastbootJSSrcDir() { @@ -269,9 +304,9 @@ export class CompatAppBuilder { } let options: CompatResolverOptions['options'] = { - staticHelpers: this.options.staticHelpers, - staticModifiers: this.options.staticModifiers, - staticComponents: this.options.staticComponents, + staticHelpers: this.staticHelpers, + staticModifiers: this.staticModifiers, + staticComponents: this.staticComponents, allowUnsafeDynamicComponents: this.options.allowUnsafeDynamicComponents, }; @@ -1021,12 +1056,7 @@ export class CompatAppBuilder { transforms.push(macroPlugin as any); } - if ( - this.options.staticComponents || - this.options.staticHelpers || - this.options.staticModifiers || - (globalThis as any).embroider_audit - ) { + if (this.staticComponents || this.staticHelpers || this.staticModifiers || (globalThis as any).embroider_audit) { let opts: ResolverTransformOptions = { appRoot: resolverConfig.appRoot, emberVersion: this.emberVersion(), @@ -1211,13 +1241,13 @@ export class CompatAppBuilder { let eagerModules: string[] = []; let requiredAppFiles = [this.requiredOtherFiles(appFiles)]; - if (!this.options.staticComponents) { + if (!this.staticComponents) { requiredAppFiles.push(appFiles.components); } - if (!this.options.staticHelpers) { + if (!this.staticHelpers) { requiredAppFiles.push(appFiles.helpers); } - if (!this.options.staticModifiers) { + if (!this.staticModifiers) { requiredAppFiles.push(appFiles.modifiers); } diff --git a/packages/compat/src/options.ts b/packages/compat/src/options.ts index dabc89786..67517db9e 100644 --- a/packages/compat/src/options.ts +++ b/packages/compat/src/options.ts @@ -95,6 +95,22 @@ export default interface Options extends CoreOptions { // it on in production. But it can be helpful when testing how much of your // app is able to work with staticComponents enabled. allowUnsafeDynamicComponents?: boolean; + + /** + * When true, we statically resolve all components, modifiers, and helpers (collectively + * knows as Invokables) at build time. This causes any unused Invokables to be left out + * of the build if they are unused i.e. "tree shaking". + * + * Defaults to false which gives you greater compatibility with classic Ember apps at the + * cost of bigger builds. + * + * This setting takes over from `staticHelpers`, `staticModifiers`, and `staticComponents` + * because the Developer Experience was less than ideal if any of these settings did not + * agree i.e. they all needed to be true or they all needed to be false. + * + * Enabling this is a prerequisite for route splitting. + */ + staticInvokables?: boolean; } const defaults = Object.assign(coreWithDefaults(), { @@ -106,6 +122,7 @@ const defaults = Object.assign(coreWithDefaults(), { workspaceDir: null, packageRules: [], allowUnsafeDynamicComponents: false, + staticInvokables: false, }); export function optionsWithDefaults(options?: Options): Required { @@ -121,9 +138,7 @@ export const recommendedOptions: { [name: string]: Options } = Object.freeze({ optimized: Object.freeze({ staticAddonTrees: true, staticAddonTestSupportTrees: true, - staticHelpers: true, - staticModifiers: true, - staticComponents: true, + staticInvokables: true, staticEmberSource: true, allowUnsafeDynamicComponents: false, }), diff --git a/packages/compat/src/template-tag-codemod.ts b/packages/compat/src/template-tag-codemod.ts index 8305a258f..4fad95d10 100644 --- a/packages/compat/src/template-tag-codemod.ts +++ b/packages/compat/src/template-tag-codemod.ts @@ -42,9 +42,7 @@ export default function templateTagCodemod( compatBuild(emberApp, undefined, { staticAddonTrees: true, staticAddonTestSupportTrees: true, - staticComponents: true, - staticHelpers: true, - staticModifiers: true, + staticInvokables: true, staticEmberSource: true, amdCompatibility: { es: [], diff --git a/packages/core/src/options.ts b/packages/core/src/options.ts index b89b5672f..6fbcb003a 100644 --- a/packages/core/src/options.ts +++ b/packages/core/src/options.ts @@ -1,32 +1,44 @@ export default interface Options { - // When true, we statically resolve all template helpers at build time. This - // causes unused helpers to be left out of the build ("tree shaking" of - // helpers). - // - // Defaults to false, which gives you greater compatibility with classic Ember - // apps at the cost of bigger builds. - // - // Enabling this is a prerequisite for route splitting. + /** + * When true, we statically resolve all template helpers at build time. This + * causes unused helpers to be left out of the build ("tree shaking" of + * helpers). + * + * Defaults to false, which gives you greater compatibility with classic Ember + * apps at the cost of bigger builds. + * + * Enabling this is a prerequisite for route splitting. + * + * @deprecated use staticInvokables instead + */ staticHelpers?: boolean; - // When true, we statically resolve all modifiers at build time. This - // causes unused modifiers to be left out of the build ("tree shaking" of - // modifiers). - // - // Defaults to false, which gives you greater compatibility with classic Ember - // apps at the cost of bigger builds. - // - // Enabling this is a prerequisite for route splitting. + /** + * When true, we statically resolve all modifiers at build time. This + * causes unused modifiers to be left out of the build ("tree shaking" of + * modifiers). + * + * Defaults to false, which gives you greater compatibility with classic Ember + * apps at the cost of bigger builds. + * + * Enabling this is a prerequisite for route splitting. + * + * @deprecated use staticInvokables instead + */ staticModifiers?: boolean; - // When true, we statically resolve all components at build time. This causes - // unused components to be left out of the build ("tree shaking" of - // components). - // - // Defaults to false, which gives you greater compatibility with classic Ember - // apps at the cost of bigger builds. - // - // Enabling this is a prerequisite for route splitting. + /** + * When true, we statically resolve all components at build time. This causes + * unused components to be left out of the build ("tree shaking" of + * components). + * + * Defaults to false, which gives you greater compatibility with classic Ember + * apps at the cost of bigger builds. + * + * Enabling this is a prerequisite for route splitting. + * + * @deprecated use staticInvokables instead + */ staticComponents?: boolean; // Enables per-route code splitting. Any route names that match these patterns