From 08e33e7e1fccffc291ecbcc20b2b7b7514dce998 Mon Sep 17 00:00:00 2001 From: Marine Dunstetter Date: Fri, 7 Jun 2024 16:03:42 +0200 Subject: [PATCH] feat(new app-boot): implement an error message to help moving custom app-boot --- packages/compat/src/compat-app-builder.ts | 41 ++++++++++++++++++- packages/compat/src/options.ts | 7 ++++ .../macro-sample-addon/ember-cli-build.js | 1 + tests/fixtures/macro-test/ember-cli-build.js | 4 +- tests/scenarios/macro-test.ts | 36 ++++++++++++++++ 5 files changed, 86 insertions(+), 3 deletions(-) diff --git a/packages/compat/src/compat-app-builder.ts b/packages/compat/src/compat-app-builder.ts index 2f813e8a27..9784113147 100644 --- a/packages/compat/src/compat-app-builder.ts +++ b/packages/compat/src/compat-app-builder.ts @@ -482,10 +482,14 @@ export class CompatAppBuilder { writeFileSync(join(this.root, 'package.json'), JSON.stringify(pkg, null, 2), 'utf8'); let resolverConfig = this.resolverConfig(appFiles); + let config = this.configTree.readConfig(); + let contentForConfig = this.contentForTree.readContents(); + this.addResolverConfig(resolverConfig); - this.addContentForConfig(this.contentForTree.readContents()); - this.addEmberEnvConfig(this.configTree.readConfig().EmberENV); + this.addContentForConfig(contentForConfig); + this.addEmberEnvConfig(config.EmberENV); this.addAppBoot(this.compatApp.appBoot.readAppBoot()); + this.outputAppBootError(config.modulePrefix, config.APP, contentForConfig); let babelConfig = await this.babelConfig(resolverConfig); this.addBabelConfig(babelConfig); this.addMacrosConfig(this.compatApp.macrosConfig.babelPluginConfig()[0]); @@ -601,6 +605,39 @@ export class CompatAppBuilder { private addAppBoot(appBoot?: string) { writeFileSync(join(locateEmbroiderWorkingDir(this.compatApp.root), 'ember-app-boot.js'), appBoot ?? ''); } + + // Classic addons providing custom content-for "app-boot" is no longer supported. + // The purpose of this error message is to help developers to move the classic addons code. + // Developers can deactivate it with useAddonAppBoot build option. + private outputAppBootError(modulePrefix: string, appConfig: any, contentForConfig: any) { + if (!this.compatApp.options.useAddonAppBoot) { + return; + } + + // This is the default script provided by + // https://github.com/ember-cli/ember-cli/blob/master/lib/utilities/ember-app-utils.js#L103 + const defaultAppBoot = ` + if (!runningTests) { + require("${modulePrefix}/app")["default"].create(${JSON.stringify(appConfig || {})}); + } + `.replace(/\s/g, ''); + + const appBoot = contentForConfig['/index.html']['app-boot']; + const diff = appBoot.replace(/\s/g, '').replace(defaultAppBoot, ''); + + if (diff.length) { + throw new Error(` + Your app uses at least one classic addon that provides content-for 'app-boot'. This is no longer supported. + With Embroider, you have full control over the app-boot script, so classic addons no longer need to modify it under the hood. + The following code is used for your app boot: + + ${appBoot} + + 1. If you want to keep the same behavior, copy and paste it to your /app/app-boot.js: + 2. Once /app/app-boot.js has the content you need, remove the present error by setting "useAddonAppBoot" to false in the build options. + `); + } + } } function defaultAddonPackageRules(): PackageRules[] { diff --git a/packages/compat/src/options.ts b/packages/compat/src/options.ts index b5962f8810..a4584fa09d 100644 --- a/packages/compat/src/options.ts +++ b/packages/compat/src/options.ts @@ -102,6 +102,12 @@ export default interface Options extends CoreOptions { // 'body-footer', 'test-body-footer'. You need to use this config only to extend // this list. availableContentForTypes?: string[]; + + // Allows you to cancel the warning that at least one classic addon provides + // content-for 'app-boot'. This warning brings awareness for developers + // switching to Embroider, but is no longer necessary once content-for + // 'app-boot' code has been properly moved to the app-side. + useAddonAppBoot?: boolean; } const defaults = Object.assign(coreWithDefaults(), { @@ -114,6 +120,7 @@ const defaults = Object.assign(coreWithDefaults(), { packageRules: [], allowUnsafeDynamicComponents: false, availableContentForTypes: [], + useAddonAppBoot: true, }); export function optionsWithDefaults(options?: Options): Required { diff --git a/tests/fixtures/macro-sample-addon/ember-cli-build.js b/tests/fixtures/macro-sample-addon/ember-cli-build.js index 34c584bf32..90518ee15b 100644 --- a/tests/fixtures/macro-sample-addon/ember-cli-build.js +++ b/tests/fixtures/macro-sample-addon/ember-cli-build.js @@ -16,6 +16,7 @@ module.exports = function(defaults) { */ return maybeEmbroider(app, { + useAddonAppBoot: false, skipBabel: [ { package: 'qunit', diff --git a/tests/fixtures/macro-test/ember-cli-build.js b/tests/fixtures/macro-test/ember-cli-build.js index aa04607199..eb531608f5 100644 --- a/tests/fixtures/macro-test/ember-cli-build.js +++ b/tests/fixtures/macro-test/ember-cli-build.js @@ -42,5 +42,7 @@ module.exports = function (defaults) { app.import('vendor/prepend/four.js', { prepend: true }); app.import('vendor/prepend/order.js', { prepend: true }); - return maybeEmbroider(app); + return maybeEmbroider(app, { + useAddonAppBoot: false, + }); }; diff --git a/tests/scenarios/macro-test.ts b/tests/scenarios/macro-test.ts index 7637b7c56d..aab5a77862 100644 --- a/tests/scenarios/macro-test.ts +++ b/tests/scenarios/macro-test.ts @@ -285,3 +285,39 @@ dummyAppScenarios }); }); }); + +dummyAppScenarios + .map('macro-sample-addon-useAddonAppBoot', project => { + dummyAppScenarioSetup(project); + project.mergeFiles({ + 'ember-cli-build.js': ` + 'use strict'; + const EmberAddon = require('ember-cli/lib/broccoli/ember-addon'); + const { maybeEmbroider } = require('@embroider/test-setup'); + module.exports = function(defaults) { + let app = new EmberAddon(defaults, {}); + return maybeEmbroider(app, { + useAddonAppBoot: true, + }); + }; + `, + }); + }) + .forEachScenario(scenario => { + Qmodule(scenario.name, function (hooks) { + let addon: PreparedApp; + + hooks.before(async () => { + addon = await scenario.prepare(); + }); + + test(`pnpm test`, async function (assert) { + let result = await addon.execute('pnpm test'); + assert.equal(result.exitCode, 1, 'tests exit with errors'); + assert.true( + result.output.includes(`Your app uses at least one classic addon that provides content-for 'app-boot'.`), + 'the output contains the error message about migrating custom app-boot code' + ); + }); + }); + });