From 408629eaab9b1476cdb8dddbebdbdc5f458429cc Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:04:49 -0400 Subject: [PATCH 01/18] v2 addon by default --- text/0000-template.md | 78 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 text/0000-template.md diff --git a/text/0000-template.md b/text/0000-template.md new file mode 100644 index 0000000000..ca5bc4d178 --- /dev/null +++ b/text/0000-template.md @@ -0,0 +1,78 @@ +--- +stage: accepted +start-date: # In format YYYY-MM-DDT00:00:00.000Z +release-date: # In format YYYY-MM-DDT00:00:00.000Z +release-versions: +teams: # delete teams that aren't relevant + - cli + - data + - framework + - learning + - steering + - typescript +prs: + accepted: # Fill this in with the URL for the Proposal RFC PR +project-link: +suite: +--- + + + +# Change the default addon blueprint + +## Summary + +`@embroider/addon-blueprint` has been in progress for a while, + + +## Motivation + +We want to encourage folks use v2 addons instead of the classic addon blueprint. + +## Detailed design + +`@embroider/addon-blueprint` will remain its own repo. `ember addon` will use this by default without a flag at some point. + +## How we teach this + +> What names and terminology work best for these concepts and why? How is this +idea best presented? As a continuation of existing Ember patterns, or as a +wholly new one? + +> Would the acceptance of this proposal mean the Ember guides must be +re-organized or altered? Does it change how Ember is taught to new users +at any level? + +> How should this feature be introduced and taught to existing Ember +users? + +## Drawbacks + +> Why should we *not* do this? Please consider the impact on teaching Ember, +on the integration of this feature with other existing and planned features, +on the impact of the API churn on existing apps, etc. + +> There are tradeoffs to choosing any path, please attempt to identify them here. + +## Alternatives + +> What other designs have been considered? What is the impact of not doing this? + +> This section could also include prior art, that is, how other frameworks in the same domain have solved this problem. + +## Unresolved questions + +> Optional, but suggested for first drafts. What parts of the design are still +TBD? From 62cd65e12606ac97222cee03cfbb0356d7547f58 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:06:15 -0400 Subject: [PATCH 02/18] Update frontmatter --- text/{0000-template.md => 0985-template.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{0000-template.md => 0985-template.md} (94%) diff --git a/text/0000-template.md b/text/0985-template.md similarity index 94% rename from text/0000-template.md rename to text/0985-template.md index ca5bc4d178..d2daaf4c0f 100644 --- a/text/0000-template.md +++ b/text/0985-template.md @@ -1,6 +1,6 @@ --- stage: accepted -start-date: # In format YYYY-MM-DDT00:00:00.000Z +start-date: 2023-11-02T16:05:11.000Z release-date: # In format YYYY-MM-DDT00:00:00.000Z release-versions: teams: # delete teams that aren't relevant @@ -11,7 +11,7 @@ teams: # delete teams that aren't relevant - steering - typescript prs: - accepted: # Fill this in with the URL for the Proposal RFC PR + accepted: https://github.com/emberjs/rfcs/pull/985 project-link: suite: --- From 76d33e8312e4031c6da802a45711165887d1514e Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Thu, 2 Nov 2023 16:07:55 -0400 Subject: [PATCH 03/18] rename file --- text/{0985-template.md => 0985-v2-addon-by-default.md} | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) rename text/{0985-template.md => 0985-v2-addon-by-default.md} (90%) diff --git a/text/0985-template.md b/text/0985-v2-addon-by-default.md similarity index 90% rename from text/0985-template.md rename to text/0985-v2-addon-by-default.md index d2daaf4c0f..53d1473813 100644 --- a/text/0985-template.md +++ b/text/0985-v2-addon-by-default.md @@ -34,13 +34,16 @@ suite: Leave as is ## Summary -`@embroider/addon-blueprint` has been in progress for a while, +`@embroider/addon-blueprint` has been in progress for a while, it's good stuff. ## Motivation We want to encourage folks use v2 addons instead of the classic addon blueprint. +V1 addons are built by apps on every boot, every build. +V2 addons are built at publish time, so the app can build faster. + ## Detailed design `@embroider/addon-blueprint` will remain its own repo. `ember addon` will use this by default without a flag at some point. From 1940dd86ba3c532f8985e53a75dce95a13810e49 Mon Sep 17 00:00:00 2001 From: Aaron Chambers Date: Fri, 3 Nov 2023 14:43:49 +0000 Subject: [PATCH 04/18] Apply suggestions from code review Co-authored-by: MrChocolatine <47531779+MrChocolatine@users.noreply.github.com> --- text/0985-v2-addon-by-default.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 53d1473813..c0bfd859a6 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -39,7 +39,7 @@ suite: Leave as is ## Motivation -We want to encourage folks use v2 addons instead of the classic addon blueprint. +We want to encourage folks to use v2 addons instead of the classic addon blueprint. V1 addons are built by apps on every boot, every build. V2 addons are built at publish time, so the app can build faster. From e37f6622b1b3181d767699038383bc577ba0cccf Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 28 Oct 2024 14:39:48 -0400 Subject: [PATCH 05/18] Update making v2 addon blueprint default --- text/0985-v2-addon-by-default.md | 40 +++++++++++++++++--------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index c0bfd859a6..825d93883e 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -34,7 +34,7 @@ suite: Leave as is ## Summary -`@embroider/addon-blueprint` has been in progress for a while, it's good stuff. +`@embroider/addon-blueprint` has been in progress for a while, and thorough testing and usage, so it should become the default addon blueprint, replacing the existing addon blueprint in ember-cli (a the default) ## Motivation @@ -44,38 +44,40 @@ We want to encourage folks to use v2 addons instead of the classic addon bluepri V1 addons are built by apps on every boot, every build. V2 addons are built at publish time, so the app can build faster. +Even though we still have more improvements planned for the v2 addon blueprint, there are _only disadvantages_ to keeping the v1 addon blueprint the default. + +When we make changes to the v2 addon blueprint, we the next version of ember-cli will get those changes without needing to change anything about `ember-cli`'s addon-support. + ## Detailed design -`@embroider/addon-blueprint` will remain its own repo. `ember addon` will use this by default without a flag at some point. +`@embroider/addon-blueprint` will remain its own repo. `ember addon` will use this by default. + +When `ember-cli` is published, it will lock in the version of `@embroider/addon-blueprint` at the time of publish. For example, if `@embroider/addon-blueprint` is version "2.3", when `ember-cli` 6.1 is published, if someone is using `ember-cli` 6.1, they'll always run the "2.3" version of `@embroider/addon-blueprint` -- the equivelent of doing `--blueprint @embroider/addon-blueprint@2.3` today. + +Folks can still use the "latest" version of `@embroider/addon-blueprint` via specifying the `--blueprint "@embroider/addon-blueprint` argument and value. ## How we teach this -> What names and terminology work best for these concepts and why? How is this -idea best presented? As a continuation of existing Ember patterns, or as a -wholly new one? +We already don't have much documentation for authoring addons -- and at this point, we probably have more v2 addon documentation than we do v1 addon documentation. -> Would the acceptance of this proposal mean the Ember guides must be -re-organized or altered? Does it change how Ember is taught to new users -at any level? +As a part of making v2 addons default, we will need to provide actual documentation and display on the ember-guides +- https://github.com/embroider-build/embroider/blob/main/docs/v2-faq.md +- https://github.com/embroider-build/embroider/blob/main/docs/addon-author-guide.md +- https://github.com/embroider-build/embroider/blob/main/docs/porting-addons-to-v2.md +- https://github.com/embroider-build/embroider/blob/main/docs/peer-dependency-resolution-issues.md +- https://github.com/embroider-build/addon-blueprint?tab=readme-ov-file#embroideraddon-blueprint + - the README here in particular suggests some extra options that would be helpful to have first-class support in `ember-cli` -> How should this feature be introduced and taught to existing Ember -users? ## Drawbacks -> Why should we *not* do this? Please consider the impact on teaching Ember, -on the integration of this feature with other existing and planned features, -on the impact of the API churn on existing apps, etc. - -> There are tradeoffs to choosing any path, please attempt to identify them here. +The tooling team wants to make the "non-monorepo" version of the V2 Addon blueprint the default in `@embroider/addon-blueprint` -- but this is a separate change from making the `@embroider/addon-blueprint` the default in `ember-cli`, as development efforts there would be solely in `@embroider/addon-blueprint` (and supporting packages). During this time, folks could feel like there is churn in the blueprint setup. +For libraries (and specifically in our v2 addon documentation), we need to make it clear that there are many paths to emitting valid JavaScript for publishing, and you don't need to update anything if the output of your project (and way it is tested) is still sufficient. ## Alternatives -> What other designs have been considered? What is the impact of not doing this? - -> This section could also include prior art, that is, how other frameworks in the same domain have solved this problem. +- do nothing (tho v1 addons are harmful for app speed) ## Unresolved questions -> Optional, but suggested for first drafts. What parts of the design are still TBD? From 662b52dd227fb7c4d4463a38e4a96dc8270d5aee Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 2 Jul 2025 10:29:13 -0400 Subject: [PATCH 06/18] Add some details about @ember/addon-blueprint --- text/0985-v2-addon-by-default.md | 130 +++++++++++++++++++++---------- 1 file changed, 89 insertions(+), 41 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 825d93883e..22b6440f04 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -7,77 +7,125 @@ teams: # delete teams that aren't relevant - cli - data - framework - - learning - - steering - - typescript prs: accepted: https://github.com/emberjs/rfcs/pull/985 project-link: suite: --- - - -# Change the default addon blueprint +# Change the default addon blueprint to `@ember/addon-blueprint` ## Summary -`@embroider/addon-blueprint` has been in progress for a while, and thorough testing and usage, so it should become the default addon blueprint, replacing the existing addon blueprint in ember-cli (a the default) - +This RFC proposes to make [`ember-addon-blueprint`](https://github.com/emberjs/ember-addon-blueprint) the default blueprint for new Ember addons, replacing the previous v1 and v2 blueprints. The new blueprint is the result of extensive community feedback, real-world usage, and a focus on modern JavaScript, TypeScript, and Ember best practices. It is designed to provide a streamlined, ergonomic, and future-proof starting point for addon authors. ## Motivation -We want to encourage folks to use v2 addons instead of the classic addon blueprint. +The previous default blueprints (classic v1 and the original v2 via `@embroider/addon-blueprint`) have served the community well, but both have significant drawbacks: -V1 addons are built by apps on every boot, every build. -V2 addons are built at publish time, so the app can build faster. +- **V1 Addons**: Built by consuming apps on every build, leading to slow builds and complex compatibility issues. +- **Original V2 Addon Blueprint**: Improved build performance by building at publish time, but required significant manual setup, was monorepo-oriented by default, and lacked ergonomic defaults for modern Ember and TypeScript usage. -Even though we still have more improvements planned for the v2 addon blueprint, there are _only disadvantages_ to keeping the v1 addon blueprint the default. +The new `ember-addon-blueprint` addresses these issues by: -When we make changes to the v2 addon blueprint, we the next version of ember-cli will get those changes without needing to change anything about `ember-cli`'s addon-support. +- Providing a single-package, non-monorepo structure by default, reducing complexity for most addon authors. +- Including first-class Glint support with Volar-based tsserver plugins, providing the most TypeScript-like experience possible for Ember templates and components. +- Using modern JavaScript and Ember idioms, including native classes, decorators, and strict mode. +- Integrating with the latest Ember testing and linting tools out of the box. +- Reducing boilerplate and cognitive overhead for new addon authors. ## Detailed design -`@embroider/addon-blueprint` will remain its own repo. `ember addon` will use this by default. +### Blueprint Structure and Tooling Choices -When `ember-cli` is published, it will lock in the version of `@embroider/addon-blueprint` at the time of publish. For example, if `@embroider/addon-blueprint` is version "2.3", when `ember-cli` 6.1 is published, if someone is using `ember-cli` 6.1, they'll always run the "2.3" version of `@embroider/addon-blueprint` -- the equivelent of doing `--blueprint @embroider/addon-blueprint@2.3` today. +The new blueprint makes several key decisions: -Folks can still use the "latest" version of `@embroider/addon-blueprint` via specifying the `--blueprint "@embroider/addon-blueprint` argument and value. +- **Single-package, non-monorepo by default**: Most addons do not require a monorepo structure. The blueprint generates a single package, but can be extended to monorepo setups if needed. +- **Glint-enabled**: All source files include Glint configuration by default, leveraging Volar-based tsserver plugins for superior template type checking and IDE integration. This was not available in previous blueprints when opting into TypeScript via `--typescript`. +- **Modern Ember Patterns**: Uses native classes, decorators, and strict mode. No legacy Ember object model or classic patterns. +- **Testing**: Integrates with `@ember/test-helpers`, `qunit`, and `ember-auto-import` for modern testing and dependency management. +- **Linting and Formatting**: Pre-configures `eslint`, `prettier`, and Ember-specific linting rules for code consistency. +- **Documentation**: Includes a README template and guides for publishing, versioning, and supporting multiple Ember versions. +- **CI/CD**: Provides a GitHub Actions workflow for testing, linting, and publishing, reducing setup time for new projects. +- **Peer Dependencies**: Clearly specifies peer dependencies for Ember and related packages, avoiding version conflicts. -## How we teach this +#### Rationale and Defense of Choices + +- **Non-monorepo default**: Most addons are single packages. Monorepo setups add unnecessary complexity for the majority of users. The blueprint can be extended for monorepos if needed. +- **Glint with Volar**: The Ember ecosystem is moving towards TypeScript, and Glint provides the best template type-checking experience. The Volar-based tsserver plugins deliver a native TypeScript-like IDE experience that was previously unavailable in addon blueprints, enabling superior autocomplete, error detection, and refactoring capabilities in templates. +- **Modern idioms**: Encourages best practices and prepares addons for future Ember releases. +- **Pre-configured tooling**: Reduces time spent on setup and avoids common pitfalls, especially for new authors. +- **CI/CD**: Ensures that all addons start with a robust, modern workflow, improving quality and reliability. + +#### CI/CD Workflow Job Analysis and Defense + +The blueprint includes a comprehensive GitHub Actions workflow (`.github/workflows/ci.yml`) with several jobs, each serving a specific purpose: + +- **Lint Job**: Runs ESLint, Prettier, and template-lint checks to ensure code quality and consistent formatting. This catches style issues early and maintains consistency across contributors. The job uses caching to improve performance and includes both JavaScript/TypeScript and Handlebars template linting. + +- **Test Job (Matrix Setup)**: Runs the addon's test suite and generates a matrix of scenarios for compatibility testing. This ensures the addon works correctly and prepares the scenario matrix for broader compatibility testing. The job outputs the matrix for use by the try-scenarios job. + +- **Floating Dependencies Job**: Tests the addon against the latest available versions of all dependencies (without lockfile constraints). This catches potential issues with newer dependency versions early and ensures the addon remains compatible as the ecosystem evolves. + +- **Try Scenarios Job (Matrix)**: Uses `@embroider/try` to test against multiple Ember versions and dependency combinations defined in `.try.mjs`. This ensures addon compatibility across the supported Ember ecosystem, including LTS versions, latest stable, beta, and alpha releases. Each scenario can include different build modes (like compat builds for older Ember versions). -We already don't have much documentation for authoring addons -- and at this point, we probably have more v2 addon documentation than we do v1 addon documentation. +**Additional CI Features:** +- **Push Dist Workflow**: Automatically builds and pushes compiled assets to a `dist` branch, enabling easy cross-repository testing and consumption of the addon from Git repositories. +- **Concurrency Control**: Prevents duplicate CI runs for the same branch/PR, saving resources and providing faster feedback. +- **Timeout Protection**: Each job has reasonable timeouts to prevent runaway processes from consuming excessive CI resources. -As a part of making v2 addons default, we will need to provide actual documentation and display on the ember-guides -- https://github.com/embroider-build/embroider/blob/main/docs/v2-faq.md -- https://github.com/embroider-build/embroider/blob/main/docs/addon-author-guide.md -- https://github.com/embroider-build/embroider/blob/main/docs/porting-addons-to-v2.md -- https://github.com/embroider-build/embroider/blob/main/docs/peer-dependency-resolution-issues.md -- https://github.com/embroider-build/addon-blueprint?tab=readme-ov-file#embroideraddon-blueprint - - the README here in particular suggests some extra options that would be helpful to have first-class support in `ember-cli` +**Rationale**: This comprehensive CI approach ensures addon quality, compatibility, and reliability across the entire Ember ecosystem. The matrix testing strategy catches breaking changes early, while the floating dependencies job helps maintain forward compatibility. The push-dist workflow enables modern development workflows where addons can be consumed directly from Git. This level of CI sophistication was not standard in previous blueprints and often required significant manual setup. + +#### Build and Development Tooling Choices + +Beyond CI, the blueprint makes several key tooling decisions: + +- **Vite for Development**: Uses Vite for fast development builds and testing, providing hot module replacement and modern development experience. The `vite.config.mjs` includes Ember-specific plugins and compat mode support. + +- **Rollup for Publishing**: Uses Rollup via `@embroider/addon-dev` for production builds, ensuring optimal bundle sizes and proper ESM output. The `rollup.config.mjs` includes TypeScript declaration generation when TypeScript is enabled. + +- **Dual TypeScript Configs**: Separates development (`tsconfig.json`) and publishing (`tsconfig.publish.json`) configurations, allowing different settings for local development versus published types. + +- **Modern Test Architecture**: Uses a custom test application setup in `tests/test-helper.js` that provides a minimal Ember app for testing addon functionality without the overhead of a full Ember app structure. + +- **Try Scenarios**: The `.try.mjs` file defines comprehensive compatibility testing across multiple Ember versions, including both modern Vite builds and legacy compat builds for older Ember versions. + +**Defense of Tooling Choices:** +- **Vite**: Provides the fastest possible development experience with instant rebuilds and hot reloading, significantly improving addon development productivity. +- **Rollup**: Generates optimized, tree-shakeable output that consuming applications can efficiently bundle. +- **Dual configs**: Allows development flexibility while ensuring clean, minimal published types. +- **Comprehensive testing**: The try scenarios cover the full range of supported Ember versions, catching compatibility issues before they reach users. + +### Migration and Compatibility + +- Existing addons are not affected. +- Authors can opt into the new blueprint by running `ember addon ` with the latest Ember CLI. +- The blueprint is versioned and locked to the Ember CLI release, ensuring reproducibility. + +## How we teach this +- The Ember Guides and CLI documentation will be updated to reference the new blueprint. +- The blueprint README includes detailed instructions for customization, publishing, and supporting multiple Ember versions. +- Migration guides will be provided for authors of v1 and v2 addons. +- Key resources: + - [ember-addon-blueprint README](https://github.com/emberjs/ember-addon-blueprint#readme) + - [Addon Author Guide](https://github.com/embroider-build/embroider/blob/main/docs/addon-author-guide.md) + - [Porting Addons to V2](https://github.com/embroider-build/embroider/blob/main/docs/porting-addons-to-v2.md) + - [TypeScript in Ember Addons](https://github.com/emberjs/ember-addon-blueprint/blob/main/docs/typescript.md) ## Drawbacks -The tooling team wants to make the "non-monorepo" version of the V2 Addon blueprint the default in `@embroider/addon-blueprint` -- but this is a separate change from making the `@embroider/addon-blueprint` the default in `ember-cli`, as development efforts there would be solely in `@embroider/addon-blueprint` (and supporting packages). During this time, folks could feel like there is churn in the blueprint setup. -For libraries (and specifically in our v2 addon documentation), we need to make it clear that there are many paths to emitting valid JavaScript for publishing, and you don't need to update anything if the output of your project (and way it is tested) is still sufficient. +- Some advanced use cases (e.g., monorepos, custom build setups) may require additional manual configuration. +- Addon authors unfamiliar with TypeScript or Glint may face a learning curve, but JavaScript is still supported. +- The blueprint is opinionated, which may not suit all workflows, but it is designed to cover the vast majority of use cases. ## Alternatives -- do nothing (tho v1 addons are harmful for app speed) +- Do nothing (keep the old blueprints, but this would hinder ecosystem modernization). +- Make monorepo the default (adds complexity for most users). +- Provide multiple blueprints (increases maintenance burden and confusion). ## Unresolved questions -TBD? +- How to best support advanced monorepo setups in the future. +- How to streamline migration for large, complex v1/v2 addons. From 7daadcf4ade4f43e7bb47ad1ab67b33c64752f9a Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 2 Jul 2025 12:05:04 -0400 Subject: [PATCH 07/18] updates --- text/0985-v2-addon-by-default.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 22b6440f04..1b426f215d 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -17,7 +17,7 @@ suite: ## Summary -This RFC proposes to make [`ember-addon-blueprint`](https://github.com/emberjs/ember-addon-blueprint) the default blueprint for new Ember addons, replacing the previous v1 and v2 blueprints. The new blueprint is the result of extensive community feedback, real-world usage, and a focus on modern JavaScript, TypeScript, and Ember best practices. It is designed to provide a streamlined, ergonomic, and future-proof starting point for addon authors. +This RFC proposes to make [`@ember/addon-blueprint`](https://github.com/emberjs/ember-addon-blueprint) the default blueprint for new Ember addons, replacing the previous v1 and v2 blueprints. The new blueprint is the result of extensive community feedback, real-world usage, and a focus on modern JavaScript, TypeScript, and Ember best practices. It is designed to provide a streamlined, ergonomic, and future-proof starting point for addon authors. ## Motivation @@ -26,7 +26,7 @@ The previous default blueprints (classic v1 and the original v2 via `@embroider/ - **V1 Addons**: Built by consuming apps on every build, leading to slow builds and complex compatibility issues. - **Original V2 Addon Blueprint**: Improved build performance by building at publish time, but required significant manual setup, was monorepo-oriented by default, and lacked ergonomic defaults for modern Ember and TypeScript usage. -The new `ember-addon-blueprint` addresses these issues by: +The new `@ember/addon-blueprint` addresses these issues by: - Providing a single-package, non-monorepo structure by default, reducing complexity for most addon authors. - Including first-class Glint support with Volar-based tsserver plugins, providing the most TypeScript-like experience possible for Ember templates and components. @@ -43,7 +43,7 @@ The new blueprint makes several key decisions: - **Single-package, non-monorepo by default**: Most addons do not require a monorepo structure. The blueprint generates a single package, but can be extended to monorepo setups if needed. - **Glint-enabled**: All source files include Glint configuration by default, leveraging Volar-based tsserver plugins for superior template type checking and IDE integration. This was not available in previous blueprints when opting into TypeScript via `--typescript`. - **Modern Ember Patterns**: Uses native classes, decorators, and strict mode. No legacy Ember object model or classic patterns. -- **Testing**: Integrates with `@ember/test-helpers`, `qunit`, and `ember-auto-import` for modern testing and dependency management. +- **Testing**: Integrates with `@ember/test-helpers` and `qunit` for modern testing. Unlike previous blueprints, testing runs entirely through Vite (accessible via the `/tests` URL and CLI), eliminating the need for `ember-auto-import` and webpack for test execution. - **Linting and Formatting**: Pre-configures `eslint`, `prettier`, and Ember-specific linting rules for code consistency. - **Documentation**: Includes a README template and guides for publishing, versioning, and supporting multiple Ember versions. - **CI/CD**: Provides a GitHub Actions workflow for testing, linting, and publishing, reducing setup time for new projects. @@ -51,7 +51,7 @@ The new blueprint makes several key decisions: #### Rationale and Defense of Choices -- **Non-monorepo default**: Most addons are single packages. Monorepo setups add unnecessary complexity for the majority of users. The blueprint can be extended for monorepos if needed. +- **Non-monorepo default**: Most addons are single packages. Monorepo setups add unnecessary complexity for the majority of users. However, for those who need monorepo structures (like the old `@embroider/addon-blueprint` pattern), you can simply not use the testing capabilities of the new blueprint and create a separate test application using `@ember/app-blueprint`. This provides the same monorepo benefits while keeping the blueprint focused on the common single-package case. - **Glint with Volar**: The Ember ecosystem is moving towards TypeScript, and Glint provides the best template type-checking experience. The Volar-based tsserver plugins deliver a native TypeScript-like IDE experience that was previously unavailable in addon blueprints, enabling superior autocomplete, error detection, and refactoring capabilities in templates. - **Modern idioms**: Encourages best practices and prepares addons for future Ember releases. - **Pre-configured tooling**: Reduces time spent on setup and avoids common pitfalls, especially for new authors. @@ -101,17 +101,17 @@ Beyond CI, the blueprint makes several key tooling decisions: - Existing addons are not affected. - Authors can opt into the new blueprint by running `ember addon ` with the latest Ember CLI. - The blueprint is versioned and locked to the Ember CLI release, ensuring reproducibility. +- **Monorepo Migration**: For teams currently using monorepo setups from the old `@embroider/addon-blueprint`, the new pattern involves generating your addon with `@ember/addon-blueprint` (ignoring its test setup) and then using a separate test application via `@ember/app-blueprint` (or other app blueprint). ## How we teach this - The Ember Guides and CLI documentation will be updated to reference the new blueprint. - The blueprint README includes detailed instructions for customization, publishing, and supporting multiple Ember versions. - Migration guides will be provided for authors of v1 and v2 addons. -- Key resources: - - [ember-addon-blueprint README](https://github.com/emberjs/ember-addon-blueprint#readme) +- Key resources (so far): + - [@ember/addon-blueprint README](https://github.com/emberjs/ember-addon-blueprint#readme) - [Addon Author Guide](https://github.com/embroider-build/embroider/blob/main/docs/addon-author-guide.md) - [Porting Addons to V2](https://github.com/embroider-build/embroider/blob/main/docs/porting-addons-to-v2.md) - - [TypeScript in Ember Addons](https://github.com/emberjs/ember-addon-blueprint/blob/main/docs/typescript.md) ## Drawbacks From 44904c2e1d495f2d2d3fcf3ce0d7043801ecebd6 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 2 Jul 2025 12:06:40 -0400 Subject: [PATCH 08/18] updates --- text/0985-v2-addon-by-default.md | 90 ++++++++++++++++---------------- 1 file changed, 45 insertions(+), 45 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 1b426f215d..210fdaf230 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -38,58 +38,58 @@ The new `@ember/addon-blueprint` addresses these issues by: ### Blueprint Structure and Tooling Choices -The new blueprint makes several key decisions: - -- **Single-package, non-monorepo by default**: Most addons do not require a monorepo structure. The blueprint generates a single package, but can be extended to monorepo setups if needed. -- **Glint-enabled**: All source files include Glint configuration by default, leveraging Volar-based tsserver plugins for superior template type checking and IDE integration. This was not available in previous blueprints when opting into TypeScript via `--typescript`. -- **Modern Ember Patterns**: Uses native classes, decorators, and strict mode. No legacy Ember object model or classic patterns. -- **Testing**: Integrates with `@ember/test-helpers` and `qunit` for modern testing. Unlike previous blueprints, testing runs entirely through Vite (accessible via the `/tests` URL and CLI), eliminating the need for `ember-auto-import` and webpack for test execution. -- **Linting and Formatting**: Pre-configures `eslint`, `prettier`, and Ember-specific linting rules for code consistency. -- **Documentation**: Includes a README template and guides for publishing, versioning, and supporting multiple Ember versions. -- **CI/CD**: Provides a GitHub Actions workflow for testing, linting, and publishing, reducing setup time for new projects. -- **Peer Dependencies**: Clearly specifies peer dependencies for Ember and related packages, avoiding version conflicts. - -#### Rationale and Defense of Choices - -- **Non-monorepo default**: Most addons are single packages. Monorepo setups add unnecessary complexity for the majority of users. However, for those who need monorepo structures (like the old `@embroider/addon-blueprint` pattern), you can simply not use the testing capabilities of the new blueprint and create a separate test application using `@ember/app-blueprint`. This provides the same monorepo benefits while keeping the blueprint focused on the common single-package case. -- **Glint with Volar**: The Ember ecosystem is moving towards TypeScript, and Glint provides the best template type-checking experience. The Volar-based tsserver plugins deliver a native TypeScript-like IDE experience that was previously unavailable in addon blueprints, enabling superior autocomplete, error detection, and refactoring capabilities in templates. -- **Modern idioms**: Encourages best practices and prepares addons for future Ember releases. -- **Pre-configured tooling**: Reduces time spent on setup and avoids common pitfalls, especially for new authors. -- **CI/CD**: Ensures that all addons start with a robust, modern workflow, improving quality and reliability. - -#### CI/CD Workflow Job Analysis and Defense - -The blueprint includes a comprehensive GitHub Actions workflow (`.github/workflows/ci.yml`) with several jobs, each serving a specific purpose: - -- **Lint Job**: Runs ESLint, Prettier, and template-lint checks to ensure code quality and consistent formatting. This catches style issues early and maintains consistency across contributors. The job uses caching to improve performance and includes both JavaScript/TypeScript and Handlebars template linting. +The new blueprint generates a well-organized project structure that follows modern Ember and JavaScript conventions: + +``` +my-addon/ +├── .github/ +│ └── workflows/ +│ ├── ci.yml # Comprehensive CI/CD pipeline +│ └── push-dist.yml # Automated dist branch publishing +├── src/ # Source code (published) +│ ├── index.js # Main entry point +│ └── template-registry.ts # Glint type registry +├── tests/ # Test files +│ ├── index.html # Test runner page +│ └── test-helper.ts # Test setup and configuration +├── unpublished-development-types/ # Development-only types +│ └── index.d.ts # Local development type augmentations +├── dist/ # Built output (gitignored, published) +├── declarations/ # TypeScript declarations (gitignored, published) +├── package.json # Package configuration with modern exports +├── rollup.config.mjs # Production build configuration +├── vite.config.mjs # Development build configuration +├── tsconfig.json # Development TypeScript config +├── tsconfig.publish.json # Publishing TypeScript config +├── babel.config.cjs # Development Babel config +├── babel.publish.config.cjs # Publishing Babel config +├── eslint.config.mjs # Modern ESLint flat config +├── .prettierrc.cjs # Code formatting configuration +├── .template-lintrc.cjs # Template linting rules +├── testem.cjs # Test runner configuration +├── .try.mjs # Ember version compatibility scenarios +├── .npmrc # Package manager configuration +├── .editorconfig # Editor consistency rules +├── .env.development # Development environment variables +├── README.md # Documentation template +├── CONTRIBUTING.md # Contribution guidelines +├── LICENSE.md # MIT license +└── addon-main.cjs # V1 compatibility shim +``` -- **Test Job (Matrix Setup)**: Runs the addon's test suite and generates a matrix of scenarios for compatibility testing. This ensures the addon works correctly and prepares the scenario matrix for broader compatibility testing. The job outputs the matrix for use by the try-scenarios job. - -- **Floating Dependencies Job**: Tests the addon against the latest available versions of all dependencies (without lockfile constraints). This catches potential issues with newer dependency versions early and ensures the addon remains compatible as the ecosystem evolves. - -- **Try Scenarios Job (Matrix)**: Uses `@embroider/try` to test against multiple Ember versions and dependency combinations defined in `.try.mjs`. This ensures addon compatibility across the supported Ember ecosystem, including LTS versions, latest stable, beta, and alpha releases. Each scenario can include different build modes (like compat builds for older Ember versions). - -**Additional CI Features:** -- **Push Dist Workflow**: Automatically builds and pushes compiled assets to a `dist` branch, enabling easy cross-repository testing and consumption of the addon from Git repositories. -- **Concurrency Control**: Prevents duplicate CI runs for the same branch/PR, saving resources and providing faster feedback. -- **Timeout Protection**: Each job has reasonable timeouts to prevent runaway processes from consuming excessive CI resources. - -**Rationale**: This comprehensive CI approach ensures addon quality, compatibility, and reliability across the entire Ember ecosystem. The matrix testing strategy catches breaking changes early, while the floating dependencies job helps maintain forward compatibility. The push-dist workflow enables modern development workflows where addons can be consumed directly from Git. This level of CI sophistication was not standard in previous blueprints and often required significant manual setup. - -#### Build and Development Tooling Choices - -Beyond CI, the blueprint makes several key tooling decisions: +The new blueprint makes several key decisions: -- **Vite for Development**: Uses Vite for fast development builds and testing, providing hot module replacement and modern development experience. The `vite.config.mjs` includes Ember-specific plugins and compat mode support. +- **Single-package, non-monorepo by default**: Most addons do not require a monorepo structure. The blueprint generates a single package with integrated testing, but can be adapted for monorepo setups by ignoring the test infrastructure and creating a separate test application. -- **Rollup for Publishing**: Uses Rollup via `@embroider/addon-dev` for production builds, ensuring optimal bundle sizes and proper ESM output. The `rollup.config.mjs` includes TypeScript declaration generation when TypeScript is enabled. +- **Source Organization**: The `src/` directory contains all publishable code, while `tests/` contains the test suite. The `unpublished-development-types/` directory provides a space for development-only type augmentations that won't be included in the published package. -- **Dual TypeScript Configs**: Separates development (`tsconfig.json`) and publishing (`tsconfig.publish.json`) configurations, allowing different settings for local development versus published types. +- **Glint-enabled**: All source files include Glint configuration by default, leveraging Volar-based tsserver plugins for superior template type checking and IDE integration. The `template-registry.ts` file provides type safety for consuming applications. This was not available in previous blueprints when opting into TypeScript via `--typescript`. -- **Modern Test Architecture**: Uses a custom test application setup in `tests/test-helper.js` that provides a minimal Ember app for testing addon functionality without the overhead of a full Ember app structure. +- **Modern Ember Patterns**: Uses native classes, decorators, and strict mode. No legacy Ember object model or classic patterns. -- **Try Scenarios**: The `.try.mjs` file defines comprehensive compatibility testing across multiple Ember versions, including both modern Vite builds and legacy compat builds for older Ember versions. +- **Dual Build Systems**: Vite for development (fast rebuilds, HMR) and Rollup for publishing (optimized output, tree-shaking). Each has its own configuration file and Babel setup. +- **Testing**: Integrates with `@ember/test-helpers` and `qunit` for modern testing. Unlike previous blueprints, testing runs entirely through Vite (accessible via the `/tests` URL and CLI), eliminating the need for `ember-auto-import` and webpack for test execution. **Defense of Tooling Choices:** - **Vite**: Provides the fastest possible development experience with instant rebuilds and hot reloading, significantly improving addon development productivity. - **Rollup**: Generates optimized, tree-shakeable output that consuming applications can efficiently bundle. From 1cf00f570cc26b993c6b28338a98eff6c8e4cc61 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 2 Jul 2025 12:08:12 -0400 Subject: [PATCH 09/18] mention influence of the v2 app blueprint --- text/0985-v2-addon-by-default.md | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 210fdaf230..568c36c5e6 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -90,7 +90,23 @@ The new blueprint makes several key decisions: - **Dual Build Systems**: Vite for development (fast rebuilds, HMR) and Rollup for publishing (optimized output, tree-shaking). Each has its own configuration file and Babel setup. - **Testing**: Integrates with `@ember/test-helpers` and `qunit` for modern testing. Unlike previous blueprints, testing runs entirely through Vite (accessible via the `/tests` URL and CLI), eliminating the need for `ember-auto-import` and webpack for test execution. -**Defense of Tooling Choices:** + +#### Influence on Future V2 App Blueprint + +The test architecture in this addon blueprint serves as a prototype for how we envision a future compat-less `@ember/app-blueprint` should work: + +- **Vite-First Architecture**: The addon's test setup demonstrates a minimal Ember application running entirely on Vite without any webpack or ember-cli-build.js complexity. This same pattern would be ideal for v2 apps - direct Vite integration with Ember resolver and routing. + +- **Minimal Test Application**: The `tests/test-helper.js` creates a bare-bones Ember app using just `EmberApp`, `Resolver`, and `EmberRouter`. This approach eliminates the traditional ember-cli build pipeline and shows how future apps could bootstrap with much less ceremony. + +- **Modern Module Resolution**: The test setup uses ES modules and modern imports throughout, avoiding the complex AMD/requirejs patterns of traditional Ember apps. Future v2 apps should follow this same module pattern. + +- **Direct Framework Integration**: Rather than going through ember-cli's abstraction layer, the addon tests interact directly with Ember's APIs. This demonstrates the cleaner architectural approach we want for v2 apps - direct framework usage without heavy tooling intermediation. + +The success of this addon testing approach validates that Ember applications can run efficiently with modern build tools, paving the way for a simpler, faster app blueprint that matches this architecture. + +#### Rationale and Defense of Choices + - **Vite**: Provides the fastest possible development experience with instant rebuilds and hot reloading, significantly improving addon development productivity. - **Rollup**: Generates optimized, tree-shakeable output that consuming applications can efficiently bundle. - **Dual configs**: Allows development flexibility while ensuring clean, minimal published types. From 479c86749151b227af9bfaa3084326619177360d Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:30:58 -0400 Subject: [PATCH 10/18] Be Thorough --- text/0985-v2-addon-by-default.md | 800 ++++++++++++++++++++++++++++++- 1 file changed, 787 insertions(+), 13 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 568c36c5e6..998f246c25 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -36,6 +36,18 @@ The new `@ember/addon-blueprint` addresses these issues by: ## Detailed design +### Definitions + +**V2 Addon**: An addon published in the V2 package format as defined by RFC 507, with `ember.version: 2` in package.json. + +**Glint-enabled**: A package that includes TypeScript declaration files for templates and components, leveraging Volar-based language server plugins for superior IDE integration. + +**Single-package addon**: An addon that contains its own test suite within the same package, as opposed to a monorepo structure with separate test applications. + +**Blueprint**: A code generation template used by ember-cli to scaffold new projects or files. + +**Vite-first development**: A development workflow that uses Vite as the primary build tool for both development and testing, eliminating traditional webpack-based tooling. + ### Blueprint Structure and Tooling Choices The new blueprint generates a well-organized project structure that follows modern Ember and JavaScript conventions: @@ -95,29 +107,791 @@ The new blueprint makes several key decisions: The test architecture in this addon blueprint serves as a prototype for how we envision a future compat-less `@ember/app-blueprint` should work: -- **Vite-First Architecture**: The addon's test setup demonstrates a minimal Ember application running entirely on Vite without any webpack or ember-cli-build.js complexity. This same pattern would be ideal for v2 apps - direct Vite integration with Ember resolver and routing. +- **Vite-First Architecture**: The addon's test setup demonstrates a minimal Ember application running entirely on Vite without any webpack or ember-cli-build.js complexity. This same pattern would be ideal for v2 apps - direct Vite integration with Ember resolver and routing eliminates the need for complex build pipeline abstractions. + +- **Minimal Application Bootstrap**: The `tests/test-helper.js` creates a bare-bones Ember app using just `EmberApp`, `Resolver`, and `EmberRouter`. This approach eliminates the traditional ember-cli build pipeline and shows how future apps could bootstrap with much less ceremony. The pattern of directly importing and configuring Ember's core classes provides a blueprint for simpler app initialization. + +- **Modern Module Resolution**: The test setup uses ES modules and modern imports throughout, avoiding the complex AMD/requirejs patterns of traditional Ember apps. Future v2 apps should follow this same module pattern, using standard `import` statements and module resolution rather than the custom loader.js system. + +- **Direct Framework Integration**: Rather than going through ember-cli's abstraction layer, the addon tests interact directly with Ember's APIs. This demonstrates the cleaner architectural approach we want for v2 apps - direct framework usage without heavy tooling intermediation. The test setup shows how to create and configure an Ember application using only public APIs. + +- **Zero-Config Test Execution**: The test runner uses Vite's `import.meta.glob` for automatic test discovery, eliminating the need for complex test configuration. This pattern could extend to app development, where route and component discovery happens through standard module resolution rather than custom file-system scanning. + +The success of this addon testing approach validates that Ember applications can run efficiently with modern build tools, paving the way for a simpler, faster app blueprint that matches this architecture. + +### Package Configuration Analysis + +> [!NOTE] +> This is overview, and exact contents can change as the needs of the community and capabilities of the ecosystem change. We're always striving to simplify the setup, while enabling the most power for the 80% use case, while not restricting anyone who wants to do more complex things. + +#### package.json Structure + +The generated `package.json` follows modern NPM conventions with several key design decisions: + +```json +{ + "name": "<%= name %>", + "version": "0.0.0", + "description": "The default blueprint for Embroider v2 addons.", + "keywords": ["ember-addon"], + "repository": "", + "license": "MIT", + "author": "", + "files": [ + "addon-main.cjs", + "declarations", + "dist" + ], + "ember": { + "version": 2, + "type": "addon", + "main": "addon-main.cjs" + }, + "imports": { + "#src/*": "./src/*" + }, + "exports": { + ".": { + "types": "./declarations/index.d.ts", + "default": "./dist/index.js" + }, + "./*": { + "types": "./declarations/*.d.ts", + "default": "./dist/*.js" + }, + "./addon-main.js": "./addon-main.cjs" + }, + "typesVersions": { + "*": { + "*": ["declarations/*"] + } + } +} +``` + +**Key Design Decisions:** + +- **Modern Exports Map**: Uses conditional exports to provide both TypeScript declarations and JavaScript modules, ensuring proper resolution in all environments. +- **Import Maps**: The `#src/*` import map enables clean internal imports without relative paths -- these are meant for use in tests only, and should not be left in files that are built for npm. When published, importers of your library will resolve via `package.json#exports`, rather than `package.json#imports`'s `#src`. Using `#src/` means that you don't need to repeatedly build, or run your library's build in watch mode while working on tests. +- **Minimal Published Files**: Only publishes essential runtime files (`dist`, `declarations`, `addon-main.cjs`), keeping package size minimal. +- **V2 Package Metadata**: Declares `ember.version: 2` to indicate V2 package format compliance. + +#### Development vs. Production Configuration Split + +The blueprint maintains separate configurations for development and production builds: + +**Development Configuration (`vite.config.mjs`)**: +```javascript +import { defineConfig } from 'vite'; +import { extensions, ember, classicEmberSupport } from '@embroider/vite'; +import { babel } from '@rollup/plugin-babel'; + +// Used for try scenarios where you would want to test ember-source versions < 6.3 +const isCompat = Boolean(process.env.ENABLE_COMPAT_BUILD); + +export default defineConfig({ + resolve: { + alias: [ + { + // this enables self-imports within the addon's src files, which will work when built + // via rollup, due to self-imports being a native node-resolve feature + // (and something we've been used to with v1 addons) + find: '<%= the library name %>', + replacement: `${__dirname}/src`, + }, + ], + }, + plugins: [ + ...(isCompat ? [classicEmberSupport()] : []), + ember(), + babel({ + babelHelpers: 'inline', + extensions, + }), + ], + build: { + rollupOptions: { + input: { + tests: 'tests/index.html', + }, + }, + }, +}); +``` + +This configuration enables: +- **Fast Development Builds**: Vite's native ES module support provides instant rebuilds +- **Hot Module Replacement**: Changes reflect immediately without full page reloads +- **Compatibility Mode**: Conditional support for older Ember versions via environment variables +- **Test Integration**: Direct test execution through Vite's build system + +**Production Configuration (`rollup.config.mjs`)**: +```javascript +import { babel } from '@rollup/plugin-babel'; +import { Addon } from '@embroider/addon-dev/rollup'; + +const addon = new Addon({ + srcDir: 'src', + destDir: 'dist', +}); + +export default { + output: addon.output(), + plugins: [ + addon.publicEntrypoints(['**/*.js', 'index.js', 'template-registry.js']), + addon.appReexports([ + // (instance) initializers are omitted from this list, because not many libraries provide them. + // There is also an upcoming RFC on this topic. + 'components/**/*.js', + 'helpers/**/*.js', + 'modifiers/**/*.js', + 'services/**/*.js', + ]), + addon.dependencies(), + babel({ + extensions: ['.js', '.gjs', '.ts', '.gts'], + babelHelpers: 'bundled', + configFile: './babel.publish.config.cjs', + }), + addon.hbs(), + addon.gjs(), + // This requires glint@v2/alpha + addon.declarations('declarations', 'npx glint --declaration --project ./tsconfig.publish.json'), + addon.keepAssets(['**/*.css']), + addon.clean(), + ], +}; +``` + +This configuration ensures: +- **Optimized Output**: Tree-shaking and dead code elimination +- **Type Declaration Generation**: Automatic TypeScript declaration file creation -- though note that while TypeScript is supported is always optional and opt in (via `--typescript`) +- **V2 Package Compliance**: Proper app reexports and public entrypoint definition +- **Asset Handling**: CSS and other static assets are properly included + +### TypeScript and Glint Integration + +TypeScript is opt-in via `--typescript` when generating a new addon. + +#### Dual TypeScript Configuration Strategy + +The blueprint employs separate TypeScript configurations for development and publishing: + +**Development Configuration (`tsconfig.json`)**: + +The exact configurations here may change as glint @ v2 makes more progress -- for example, there is desire to bundle ember-loose and ember-template-imports environments in to `@glint/core`, so we have 2 less packages when working with Glint. + +```json +{ + "extends": "@ember/app-tsconfig", + "glint": { + "environment": ["ember-loose", "ember-template-imports"] + }, + "include": ["src/**/*", "tests/**/*", "unpublished-development-types/**/*"], + "compilerOptions": { + "rootDir": ".", + "types": ["ember-source/types", "vite/client", "@embroider/core/virtual"] + } +} +``` + +**Publishing Configuration (`tsconfig.publish.json`)**: + +Notably, vite and embroider types are not present. + +`lint:types` uses this config to help catch accidental usage of vite or embroider features in code that would be published to NPM. + +```json +{ + "extends": "@ember/library-tsconfig", + "include": ["./src/**/*", "./unpublished-development-types/**/*"], + "glint": { + "environment": ["ember-loose", "ember-template-imports"] + }, + "compilerOptions": { + "allowJs": true, + "declarationDir": "declarations", + "rootDir": "./src", + "types": ["ember-source/types"] + } +} +``` + +**Rationale for Dual Configurations:** +- **Development Flexibility**: Includes test files and development-only types for comprehensive IDE support +- **Clean Published Types**: Publishing config generates minimal, focused declaration files +- **Proper Module Structure**: `rootDir` alignment ensures declaration file structure matches runtime module structure + +#### Glint Template Type Safety + +The blueprint includes comprehensive Glint setup for template type safety. However, note that the template-registry is not needed at all if a library does not want to support loose mode (hbs files). + +**Template Registry (`src/template-registry.ts`)**: +```typescript +// Easily allow apps, which are not yet using strict mode templates, to consume your Glint types +// by importing this file. Add all your components, helpers and modifiers to the template registry +// here, so apps don't have to do this. + +// import type MyComponent from './components/my-component'; + +// export default interface Registry { +// MyComponent: typeof MyComponent +// } +``` + +**Development Type Augmentations (`unpublished-development-types/index.d.ts`)**: + +This can be omitted if a library author doesn't need to support loose mode / hbs files. + +```typescript +// Add any types here that you need for local development only. +// These will *not* be published as part of your addon, so be careful that your published +// code does not rely on them! + +import '@glint/environment-ember-loose'; +import '@glint/environment-ember-template-imports'; +``` + +This setup provides: +- **Consumer Type Safety**: Apps consuming the addon get proper template type checking +- **Development-Only Types**: Local development can use additional type definitions without polluting the published package +- **Template Import Support**: Full support for template imports and strict mode templates + +### Testing Architecture + +#### Vite-Based Test Execution + +The blueprint implements a revolutionary testing approach that runs entirely on Vite: + +**Test Helper (`tests/test-helper.ts`)**: +```typescript +import EmberApp from '@ember/application'; +import Resolver from 'ember-resolver'; +import EmberRouter from '@ember/routing/router'; +import * as QUnit from 'qunit'; +import { setApplication } from '@ember/test-helpers'; +import { setup } from 'qunit-dom'; +import { start as qunitStart, setupEmberOnerrorValidation } from 'ember-qunit'; + +class Router extends EmberRouter { + location = 'none'; + rootURL = '/'; +} + +const registry = { + 'test-app/router': { default: Router }, + // add any custom services here + // example: + // 'test-app/services/store': { default: Store }, + // and/or initializers: + // 'test-app/instance-initializers/foo': { default: Foo } + // + // NOTE: when using (instance)initializers, you'll need https://github.com/ember-cli/ember-load-initializers/ + // and to call loadInitializers(TestApp, 'test-app', registry); +} + +class TestApp extends EmberApp { + modulePrefix = 'test-app'; + Resolver = Resolver.withModules(registry); +} + +Router.map(function () {}); + + +export function start() { + setApplication( + TestApp.create({ + autoboot: false, + rootElement: '#ember-testing', + }), + ); + setup(QUnit.assert); + setupEmberOnerrorValidation(); + qunitStart(); +} +``` + +**Test Runner (`tests/index.html`)**: +```html + + + + + <%= name %> Tests + + + + +
+
+
+
+
+
+ + + + + + + +``` + +**Key Innovations:** +- **No ember-cli-build.js**: Tests run without traditional Ember CLI build pipeline, `ember-cli` is not even in the package.json. +- **Direct Ember App Creation**: Creates minimal Ember application using core _public_ APIs +- **ES Module Test Discovery**: Uses Vite's `import.meta.glob` for automatic test file discovery +- **Zero Webpack Dependencies**: Eliminates `ember-auto-import` and webpack from test execution + +#### Cross-Version Compatibility Testing + +The blueprint includes comprehensive compatibility testing via ember-try scenarios: + +**Try Configuration (`.try.mjs`)**: +```javascript +const compatFiles = { + 'ember-cli-build.js': `const EmberApp = require('ember-cli/lib/broccoli/ember-app'); +const { compatBuild } = require('@embroider/compat'); +module.exports = async function (defaults) { + const { buildOnce } = await import('@embroider/vite'); + let app = new EmberApp(defaults); + return compatBuild(app, buildOnce); +};`, + 'config/optional-features.json': JSON.stringify({ + 'application-template-wrapper': false, + 'default-async-observers': true, + 'jquery-integration': false, + 'template-only-glimmer-components': true, + 'no-implicit-route-model': true, + }), +}; + +export default { + scenarios: [ + { + name: 'ember-lts-5.8', + npm: { + devDependencies: { + 'ember-source': '~5.8.0', + ...compatDeps, + }, + }, + env: { + ENABLE_COMPAT_BUILD: true, + }, + files: compatFiles, + }, + // Additional scenarios for 5.12, 6.4, latest, beta, alpha + ], +}; +``` + +This provides: +- **LTS Version Support**: Tests against all supported Ember LTS versions +- **Future Compatibility**: Tests against beta and alpha releases +- **Compatibility Mode**: Automatic fallback to compat builds for older versions +- **Modern Feature Flags**: Enables modern Ember features for optimal performance + +### Build System Architecture + +#### Dual Build Approach Rationale + +The blueprint employs two distinct build systems for different purposes: + +**Vite for Development**: +- **Instant Startup**: Native ES module support eliminates bundling during development +- **Hot Module Replacement**: Changes reflect immediately without full rebuilds +- **Modern Development Experience**: Source maps, error overlay, and debugging tools +- **Test Integration**: Seamless test execution within the same build system + +**Rollup for Production**: +- **Optimized Output**: Tree-shaking and dead code elimination +- **Bundle Splitting**: Optimal chunk creation for different consumption patterns +- **Asset Processing**: Proper handling of CSS, templates, and static assets +- **Declaration Generation**: TypeScript declaration file creation + +#### Babel Configuration Strategy + +The blueprint maintains separate Babel configurations for development and production: + +**Development Babel (`babel.config.cjs`)**: + +Like the `tsconfig.json`, this is the configuration used by editors and tests. + +```javascript +const { buildMacros } = require('@embroider/macros/babel'); +const { babelCompatSupport, templateCompatSupport } = require('@embroider/compat/babel'); + +/** + * In testing, we need to evaluate @embroider/macros + * in the publish config we omit this, because it's the consuming app's responsibility to evaluate the macros. + */ +const macros = buildMacros(); +const isCompat = Boolean(process.env.ENABLE_COMPAT_BUILD); + +module.exports = { + plugins: [ + ['@babel/plugin-transform-typescript', { + allExtensions: true, + allowDeclareFields: true, + onlyRemoveTypeImports: true, + }], + ['babel-plugin-ember-template-compilation', { + transforms: [ + ...(isCompat ? templateCompatSupport() : macros.templateMacros), + ], + }], + ['module:decorator-transforms', { + runtime: { + import: require.resolve('decorator-transforms/runtime-esm'), + }, + }], + ...(isCompat ? babelCompatSupport() : macros.babelMacros), + ], + generatorOpts: { + compact: false, + }, +}; +``` + +**Production Babel (`babel.publish.config.cjs`)**: + +> [!IMPORTANT] +> @embroider/macros is deliberately omitted from this config, because it's the consuming app's responsibility to evaluate macros. + +```javascript +module.exports = { + plugins: [ + ['@babel/plugin-transform-typescript', { + allExtensions: true, + allowDeclareFields: true, + onlyRemoveTypeImports: true, + }], + ['babel-plugin-ember-template-compilation', { + targetFormat: 'hbs', + transforms: [], + }], + ['module:decorator-transforms', { + runtime: { + import: 'decorator-transforms/runtime-esm', + }, + }], + ], + generatorOpts: { + compact: false, + }, +}; +``` + +**Configuration Differences:** +- **Development**: Includes macro support and compatibility transforms for older Ember versions +- **Production**: Minimal transforms focusing on TypeScript removal and template compilation +- **Runtime Imports**: Development uses require.resolve for local development, production uses published packages + +### Influence on Future V2 App Blueprint + +The test architecture in this addon blueprint serves as a prototype for how we envision a future compat-less `@ember/app-blueprint` should work: + +#### Architectural Principles Demonstrated -- **Minimal Test Application**: The `tests/test-helper.js` creates a bare-bones Ember app using just `EmberApp`, `Resolver`, and `EmberRouter`. This approach eliminates the traditional ember-cli build pipeline and shows how future apps could bootstrap with much less ceremony. +**Vite-First Architecture**: The addon's test setup demonstrates a minimal Ember application running entirely on Vite without any webpack or ember-cli-build.js complexity. This same pattern would be ideal for v2 apps - direct Vite integration with Ember resolver and routing eliminates the need for complex build pipeline abstractions. -- **Modern Module Resolution**: The test setup uses ES modules and modern imports throughout, avoiding the complex AMD/requirejs patterns of traditional Ember apps. Future v2 apps should follow this same module pattern. +**Minimal Application Bootstrap**: The `tests/test-helper.js` creates a bare-bones Ember app using just `EmberApp`, `Resolver`, and `EmberRouter`. This approach eliminates the traditional ember-cli build pipeline and shows how future apps could bootstrap with much less ceremony. The pattern of directly importing and configuring Ember's core classes provides a blueprint for simpler app initialization. -- **Direct Framework Integration**: Rather than going through ember-cli's abstraction layer, the addon tests interact directly with Ember's APIs. This demonstrates the cleaner architectural approach we want for v2 apps - direct framework usage without heavy tooling intermediation. +**Modern Module Resolution**: The test setup uses ES modules and modern imports throughout, avoiding the complex AMD/requirejs patterns of traditional Ember apps. Future v2 apps should follow this same module pattern, using standard `import` statements and module resolution rather than the custom loader.js system. + +**Direct Framework Integration**: Rather than going through ember-cli's abstraction layer, the addon tests interact directly with Ember's APIs. This demonstrates the cleaner architectural approach we want for v2 apps - direct framework usage without heavy tooling intermediation. The test setup shows how to create and configure an Ember application using only public APIs. + +**Zero-Config Test Execution**: The test runner uses Vite's `import.meta.glob` for automatic test discovery, eliminating the need for complex test configuration. This pattern could extend to app development, where route and component discovery happens through standard module resolution rather than custom file-system scanning. The success of this addon testing approach validates that Ember applications can run efficiently with modern build tools, paving the way for a simpler, faster app blueprint that matches this architecture. -#### Rationale and Defense of Choices +### CI/CD Workflow Analysis and Defense + +The blueprint includes a comprehensive GitHub Actions workflow with several jobs, each serving a specific purpose: + +#### Lint Job Analysis +```yaml +lint: + name: "Lints" + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 # if using pnpm + - uses: actions/setup-node@v4 + with: + node-version: 18 + cache: pnpm + - name: Install Dependencies + run: pnpm install --frozen-lockfile + - name: Lint + run: pnpm lint +``` + +The lint job runs ESLint, Prettier, and template-lint checks to ensure code quality and consistent formatting. This catches style issues early and maintains consistency across contributors. The job uses caching to improve performance and includes both JavaScript/TypeScript and Handlebars template linting. + +**Defense**: Linting is critical for maintaining code quality in a distributed development environment. By running linting in CI, we ensure that all contributors follow the same code standards, reducing review friction and maintaining consistency. + +#### Test Job with Matrix Generation +```yaml +test: + name: "Tests" + runs-on: ubuntu-latest + timeout-minutes: 10 + outputs: + matrix: ${{ steps.set-matrix.outputs.matrix }} + steps: + # ... setup steps + - name: Run Tests + run: pnpm test + - id: set-matrix + run: | + echo "matrix=$(pnpm -s dlx @embroider/try list)" >> $GITHUB_OUTPUT +``` + +This job runs the addon's test suite and generates a matrix of scenarios for compatibility testing. It ensures the addon works correctly and prepares the scenario matrix for broader compatibility testing. The job outputs the matrix for use by the try-scenarios job. + +**Defense**: The dual purpose of this job (testing and matrix generation) optimizes CI efficiency. We test the addon in its primary configuration while simultaneously preparing for cross-version compatibility testing. + +#### Floating Dependencies Job +```yaml +floating: + name: "Floating Dependencies" + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + # ... setup steps + - name: Install Dependencies + run: pnpm install --no-lockfile + - name: Run Tests + run: pnpm test +``` + +This job tests the addon against the latest available versions of all dependencies (without lockfile constraints). It catches potential issues with newer dependency versions early and ensures the addon remains compatible as the ecosystem evolves. + +**Defense**: Floating dependencies testing is crucial for maintaining forward compatibility. Since addons are consumed by apps with varying dependency versions, testing without lockfile constraints helps identify potential breaking changes before they affect users. + +#### Try Scenarios Matrix Job +```yaml +try-scenarios: + name: ${{ matrix.name }} + runs-on: ubuntu-latest + needs: "test" + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: ${{fromJson(needs.test.outputs.matrix)}} + steps: + # We setup the scenario before running `pnpm install`, so this is fast. + # No double-install like there is with ember-try. + # This tool can also be used with non-ember projects. + - name: Apply Scenario + run: pnpm dlx @embroider/try apply ${{ matrix.name }} + - name: Install Dependencies + run: pnpm install --no-lockfile + - name: Run Tests + run: pnpm test + env: ${{ matrix.env }} +``` + +This job uses `@embroider/try` to test against multiple Ember versions and dependency combinations defined in `.try.mjs`. It ensures addon compatibility across the supported Ember ecosystem, including LTS versions, latest stable, beta, and alpha releases. Each scenario can include different build modes (like compat builds for older Ember versions). + +**Defense**: Comprehensive cross-version testing is essential for addon ecosystem health. By testing against multiple Ember versions, we catch breaking changes early and ensure addons work across the entire supported version range. + +#### Push Dist Workflow + +```yaml +name: Push dist +on: + push: + branches: [main, master] +jobs: + push-dist: + name: Push dist + permissions: + contents: write + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + # ... build and publish to dist branch +``` + +This workflow automatically builds and pushes compiled assets to a `dist` branch, enabling easy cross-repository testing and consumption of the addon from Git repositories. + +**Defense**: The dist branch workflow enables modern development practices where addons can be consumed directly from Git repositories during development. This is particularly valuable for testing pre-release versions and coordinating changes across multiple repositories. + +### Linting and Code Quality Configuration + +#### ESLint Configuration Analysis +The blueprint uses modern ESLint flat configuration (`eslint.config.mjs`): + +```javascript +import babelParser from '@babel/eslint-parser'; +import js from '@eslint/js'; +import prettier from 'eslint-config-prettier'; +import ember from 'eslint-plugin-ember/recommended'; +import importPlugin from 'eslint-plugin-import'; +import n from 'eslint-plugin-n'; +import globals from 'globals'; +import ts from 'typescript-eslint'; + +const config = [ + js.configs.recommended, + prettier, + ember.configs.base, + ember.configs.gjs, + ember.configs.gts, + + // File-specific configurations for different contexts + { + files: ['**/*.{js,gjs}'], + languageOptions: { + parserOptions: esmParserOptions, + globals: { ...globals.browser }, + }, + }, + { + files: ['**/*.{ts,gts}'], + languageOptions: { + parser: ember.parser, + parserOptions: tsParserOptions, + }, + extends: [...ts.configs.recommendedTypeChecked, ember.configs.gts], + }, + // ... additional configurations +]; + +export default ts.config(...config); +``` + +**Key Features:** +- **Modern Flat Config**: Uses ESLint's new flat configuration format for better performance and clearer semantics +- **TypeScript Integration**: Full TypeScript support with type-aware linting rules +- **Ember-Specific Rules**: Comprehensive Ember linting including .gjs/.gts support +- **Context-Aware Rules**: Different rules for different file types (browser vs. Node.js) +- **Import Validation**: Ensures proper import/export usage and relative path requirements + +#### Template Linting +```javascript +// .template-lintrc.cjs +module.exports = { + extends: 'recommended', + checkHbsTemplateLiterals: false, +}; +``` + +The template linting configuration uses the recommended rule set but disables checking of template literals, focusing on .hbs files and template imports. + +#### Prettier Configuration +```javascript +// .prettierrc.cjs +module.exports = { + plugins: ['prettier-plugin-ember-template-tag'], + overrides: [ + { + files: '*.{js,gjs,ts,gts,mjs,mts,cjs,cts}', + options: { + singleQuote: true, + templateSingleQuote: false, + }, + }, + ], +}; +``` + +This configuration ensures consistent formatting across all JavaScript/TypeScript files while properly handling .gjs/.gts template tags. -- **Vite**: Provides the fastest possible development experience with instant rebuilds and hot reloading, significantly improving addon development productivity. -- **Rollup**: Generates optimized, tree-shakeable output that consuming applications can efficiently bundle. -- **Dual configs**: Allows development flexibility while ensuring clean, minimal published types. -- **Comprehensive testing**: The try scenarios cover the full range of supported Ember versions, catching compatibility issues before they reach users. +### Package Management Configuration + +#### NPM Configuration (`.npmrc`) +``` +# we don't want addons to be bad citizens of the ecosystem +auto-install-peers=false + +# we want true isolation, if a dependency is not declared, we want an error +resolve-peers-from-workspace-root=false +``` + +**Design Rationale:** +- **Explicit Dependencies**: Disabling auto-install-peers forces explicit declaration of all dependencies +- **True Isolation**: Preventing workspace root resolution ensures package isolation and prevents hidden dependencies +- **Ecosystem Citizenship**: These settings encourage proper dependency declaration, making addons better NPM citizens + +### V1 Compatibility Strategy + +#### Compatibility Shim (`addon-main.cjs`) +```javascript +'use strict'; +const { addonV1Shim } = require('@embroider/addon-shim'); +module.exports = addonV1Shim(__dirname); +``` + +This minimal shim enables V2 addons to work in V1 (classic ember-cli) environments by providing the traditional addon API that ember-cli expects. + +**How It Works:** +- The shim translates V2 package metadata into V1 build hooks +- Static assets and app reexports are handled through traditional treeFor* methods +- The V2 package's dist/ output is mapped to the appropriate V1 trees + +### Rationale and Defense of Choices + +#### Single Package Architecture +Most addons are single packages that don't require monorepo complexity. The blueprint generates a single package with integrated testing, but can be adapted for monorepo setups by ignoring the test infrastructure and creating a separate test application using `@ember/app-blueprint`. + +**Benefits:** +- **Reduced Complexity**: Single package structure is easier to understand and maintain +- **Faster Setup**: No need to coordinate multiple packages during development +- **Simpler Publishing**: Single package publishing is more straightforward than monorepo coordination +- **Better IDE Support**: IDEs can better understand and index single-package structures + +**Monorepo Migration Path**: For teams currently using monorepo setups from the old `@embroider/addon-blueprint`, the new pattern involves generating your addon with `@ember/addon-blueprint` (ignoring its test setup) and then using a separate test application via `@ember/app-blueprint` (or other app blueprint). This maintains the monorepo benefits while providing cleaner separation of concerns. + +#### Glint with Volar Integration +The Ember ecosystem is moving towards TypeScript, and Glint provides the best template type-checking experience. The Volar-based tsserver plugins deliver a native TypeScript-like IDE experience that was previously unavailable in addon blueprints, enabling superior autocomplete, error detection, and refactoring capabilities in templates. + +**Technical Advantages:** +- **Template Type Safety**: Full type checking for Handlebars templates +- **IDE Integration**: Native TypeScript language server support +- **Incremental Adoption**: Works with both TypeScript and JavaScript +- **Consumer Benefits**: Apps consuming the addon get automatic template type checking + +#### Vite Development Architecture +Vite provides the fastest possible development experience with instant rebuilds and hot reloading, significantly improving addon development productivity. The Vite-based test architecture eliminates traditional build pipeline complexity while maintaining full Ember compatibility. + +**Performance Benefits:** +- **Instant Startup**: Native ES module support eliminates bundling delays +- **Hot Module Replacement**: Changes reflect immediately without full page reloads +- **Parallel Processing**: Vite's architecture enables better CPU utilization +- **Modern Development**: Source maps, error overlay, and debugging tools + +#### Rollup Production Builds +Rollup generates optimized, tree-shakeable output that consuming applications can efficiently bundle. The separation between development (Vite) and production (Rollup) builds allows each tool to excel at its intended purpose. + +**Optimization Benefits:** +- **Tree Shaking**: Unused code is eliminated from the final bundle +- **Bundle Splitting**: Optimal chunk creation for different consumption patterns +- **Asset Optimization**: Proper handling of CSS, templates, and static assets +- **Declaration Generation**: Clean TypeScript declaration file creation ### Migration and Compatibility -- Existing addons are not affected. -- Authors can opt into the new blueprint by running `ember addon ` with the latest Ember CLI. -- The blueprint is versioned and locked to the Ember CLI release, ensuring reproducibility. -- **Monorepo Migration**: For teams currently using monorepo setups from the old `@embroider/addon-blueprint`, the new pattern involves generating your addon with `@ember/addon-blueprint` (ignoring its test setup) and then using a separate test application via `@ember/app-blueprint` (or other app blueprint). +Existing addons are not affected by this change. Authors can opt into the new blueprint by running `ember addon ` with the latest Ember CLI. The blueprint is versioned and locked to the Ember CLI release, ensuring reproducibility. + +**Migration Strategies:** + +1. **Greenfield Addons**: New addons can immediately use the new blueprint for optimal development experience +2. **Existing V1 Addons**: Can migrate incrementally, starting with adopting V2 package format +3. **Existing V2 Addons**: Can adopt the new tooling setup while maintaining package format compatibility + +The blueprint includes comprehensive documentation and migration guides to support all these scenarios. + ## How we teach this From 938f49ce2b28f5fa981576b0e7ed14b958b45992 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Wed, 2 Jul 2025 16:34:23 -0400 Subject: [PATCH 11/18] Updates --- text/0985-v2-addon-by-default.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 998f246c25..0f03a0c2d2 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -882,15 +882,15 @@ Rollup generates optimized, tree-shakeable output that consuming applications ca ### Migration and Compatibility -Existing addons are not affected by this change. Authors can opt into the new blueprint by running `ember addon ` with the latest Ember CLI. The blueprint is versioned and locked to the Ember CLI release, ensuring reproducibility. +Existing addons are not affected by this change. Authors can opt into the new blueprint already by running `npx ember-cli@latest addon --blueprint @ember/app-blueprint`. The blueprint is versioned and locked to the Ember CLI release, ensuring reproducibility. **Migration Strategies:** -1. **Greenfield Addons**: New addons can immediately use the new blueprint for optimal development experience -2. **Existing V1 Addons**: Can migrate incrementally, starting with adopting V2 package format -3. **Existing V2 Addons**: Can adopt the new tooling setup while maintaining package format compatibility +**Greenfield Addons**: New addons can immediately use the new blueprint for optimal development experience -The blueprint includes comprehensive documentation and migration guides to support all these scenarios. +For existing addons there is no need to migrate, however if, after trying the new blueprint, folks like the experience better, they can: +- For V1 addons, follow the "Porting Addons to V2" guide, or generate a new project and copy relevant files into it. +- For V2 addons, generate a new project and copy relevant files into it. ## How we teach this @@ -903,6 +903,8 @@ The blueprint includes comprehensive documentation and migration guides to suppo - [Addon Author Guide](https://github.com/embroider-build/embroider/blob/main/docs/addon-author-guide.md) - [Porting Addons to V2](https://github.com/embroider-build/embroider/blob/main/docs/porting-addons-to-v2.md) +These will, of course, need to be added to the guides website as well. + ## Drawbacks - Some advanced use cases (e.g., monorepos, custom build setups) may require additional manual configuration. From 42ef0129889cc8d24706d77b65b8c4a1668af0f9 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:18:12 -0400 Subject: [PATCH 12/18] Updates --- text/0985-v2-addon-by-default.md | 262 ++++++++++--------------------- 1 file changed, 80 insertions(+), 182 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 0f03a0c2d2..ef68d5422b 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -17,28 +17,28 @@ suite: ## Summary -This RFC proposes to make [`@ember/addon-blueprint`](https://github.com/emberjs/ember-addon-blueprint) the default blueprint for new Ember addons, replacing the previous v1 and v2 blueprints. The new blueprint is the result of extensive community feedback, real-world usage, and a focus on modern JavaScript, TypeScript, and Ember best practices. It is designed to provide a streamlined, ergonomic, and future-proof starting point for addon authors. +This RFC proposes making [`@ember/addon-blueprint`](https://github.com/emberjs/ember-addon-blueprint) the default blueprint for new Ember addons, replacing the previous v1 and v2 blueprints. The new blueprint provides a streamlined, modern starting point for addon authors based on community feedback and real-world usage. ## Motivation -The previous default blueprints (classic v1 and the original v2 via `@embroider/addon-blueprint`) have served the community well, but both have significant drawbacks: +The previous default blueprints have significant drawbacks: -- **V1 Addons**: Built by consuming apps on every build, leading to slow builds and complex compatibility issues. -- **Original V2 Addon Blueprint**: Improved build performance by building at publish time, but required significant manual setup, was monorepo-oriented by default, and lacked ergonomic defaults for modern Ember and TypeScript usage. +- **V1 Addons**: Built by consuming apps on every build, causing slow builds and compatibility issues. +- **Original V2 Addon Blueprint**: Required extensive manual setup, defaulted to monorepo structure, and lacked modern Ember/TypeScript defaults. -The new `@ember/addon-blueprint` addresses these issues by: +The new `@ember/addon-blueprint` addresses these issues: -- Providing a single-package, non-monorepo structure by default, reducing complexity for most addon authors. -- Including first-class Glint support with Volar-based tsserver plugins, providing the most TypeScript-like experience possible for Ember templates and components. -- Using modern JavaScript and Ember idioms, including native classes, decorators, and strict mode. -- Integrating with the latest Ember testing and linting tools out of the box. -- Reducing boilerplate and cognitive overhead for new addon authors. +- Single-package structure by default, reducing complexity for most addon authors. +- First-class Glint support with Volar-based tsserver plugins for superior TypeScript template experience. +- Modern JavaScript and Ember patterns: native classes, decorators, and strict mode. +- Integration with latest Ember testing and linting tools. +- Reduced boilerplate and setup overhead. ## Detailed design ### Definitions -**V2 Addon**: An addon published in the V2 package format as defined by RFC 507, with `ember.version: 2` in package.json. +**V2 Addon**: An addon published in the V2 package format as defined by RFC 507, with `ember-addon.version: 2` in package.json. **Glint-enabled**: A package that includes TypeScript declaration files for templates and components, leveraging Volar-based language server plugins for superior IDE integration. @@ -50,7 +50,7 @@ The new `@ember/addon-blueprint` addresses these issues by: ### Blueprint Structure and Tooling Choices -The new blueprint generates a well-organized project structure that follows modern Ember and JavaScript conventions: +The blueprint generates a modern project structure: ``` my-addon/ @@ -64,6 +64,10 @@ my-addon/ ├── tests/ # Test files │ ├── index.html # Test runner page │ └── test-helper.ts # Test setup and configuration +├── demo-app/ # Demo application for showcasing addon +│ ├── app.gts # Demo app entry point +│ ├── styles.css # Demo app styles +│ └── templates/ # Demo app templates ├── unpublished-development-types/ # Development-only types │ └── index.d.ts # Local development type augmentations ├── dist/ # Built output (gitignored, published) @@ -89,19 +93,19 @@ my-addon/ └── addon-main.cjs # V1 compatibility shim ``` -The new blueprint makes several key decisions: +Key architectural decisions: -- **Single-package, non-monorepo by default**: Most addons do not require a monorepo structure. The blueprint generates a single package with integrated testing, but can be adapted for monorepo setups by ignoring the test infrastructure and creating a separate test application. +- **Single-package by default**: Most addons don't need monorepo complexity. Generates a single package with integrated testing. -- **Source Organization**: The `src/` directory contains all publishable code, while `tests/` contains the test suite. The `unpublished-development-types/` directory provides a space for development-only type augmentations that won't be included in the published package. +- **Source organization**: `src/` contains publishable code, `tests/` contains test suite, `unpublished-development-types/` provides development-only type augmentations. -- **Glint-enabled**: All source files include Glint configuration by default, leveraging Volar-based tsserver plugins for superior template type checking and IDE integration. The `template-registry.ts` file provides type safety for consuming applications. This was not available in previous blueprints when opting into TypeScript via `--typescript`. +- **Glint-enabled**: Includes Glint configuration with Volar-based tsserver plugins for template type checking and IDE integration. -- **Modern Ember Patterns**: Uses native classes, decorators, and strict mode. No legacy Ember object model or classic patterns. +- **Modern Ember patterns**: Native classes, decorators, strict mode. No legacy object model. -- **Dual Build Systems**: Vite for development (fast rebuilds, HMR) and Rollup for publishing (optimized output, tree-shaking). Each has its own configuration file and Babel setup. +- **Dual build systems**: Vite for development (fast rebuilds, HMR), Rollup for publishing (optimized output, tree-shaking). -- **Testing**: Integrates with `@ember/test-helpers` and `qunit` for modern testing. Unlike previous blueprints, testing runs entirely through Vite (accessible via the `/tests` URL and CLI), eliminating the need for `ember-auto-import` and webpack for test execution. +- **Vite-based testing**: Tests run entirely through Vite, eliminating `ember-auto-import` and webpack dependencies. #### Influence on Future V2 App Blueprint @@ -143,6 +147,9 @@ The generated `package.json` follows modern NPM conventions with several key des "dist" ], "ember": { + "edition": "octane" + }, + "ember-addon": { "version": 2, "type": "addon", "main": "addon-main.cjs" @@ -155,15 +162,11 @@ The generated `package.json` follows modern NPM conventions with several key des "types": "./declarations/index.d.ts", "default": "./dist/index.js" }, + "./addon-main.js": "./addon-main.cjs", + "./*.css": "./dist/*.css", "./*": { "types": "./declarations/*.d.ts", "default": "./dist/*.js" - }, - "./addon-main.js": "./addon-main.cjs" - }, - "typesVersions": { - "*": { - "*": ["declarations/*"] } } } @@ -174,7 +177,7 @@ The generated `package.json` follows modern NPM conventions with several key des - **Modern Exports Map**: Uses conditional exports to provide both TypeScript declarations and JavaScript modules, ensuring proper resolution in all environments. - **Import Maps**: The `#src/*` import map enables clean internal imports without relative paths -- these are meant for use in tests only, and should not be left in files that are built for npm. When published, importers of your library will resolve via `package.json#exports`, rather than `package.json#imports`'s `#src`. Using `#src/` means that you don't need to repeatedly build, or run your library's build in watch mode while working on tests. - **Minimal Published Files**: Only publishes essential runtime files (`dist`, `declarations`, `addon-main.cjs`), keeping package size minimal. -- **V2 Package Metadata**: Declares `ember.version: 2` to indicate V2 package format compliance. +- **V2 Package Metadata**: Declares `ember-addon.version: 2` to indicate V2 package format compliance. #### Development vs. Production Configuration Split @@ -186,21 +189,10 @@ import { defineConfig } from 'vite'; import { extensions, ember, classicEmberSupport } from '@embroider/vite'; import { babel } from '@rollup/plugin-babel'; -// Used for try scenarios where you would want to test ember-source versions < 6.3 +// For scenario testing const isCompat = Boolean(process.env.ENABLE_COMPAT_BUILD); export default defineConfig({ - resolve: { - alias: [ - { - // this enables self-imports within the addon's src files, which will work when built - // via rollup, due to self-imports being a native node-resolve feature - // (and something we've been used to with v1 addons) - find: '<%= the library name %>', - replacement: `${__dirname}/src`, - }, - ], - }, plugins: [ ...(isCompat ? [classicEmberSupport()] : []), ember(), @@ -219,11 +211,7 @@ export default defineConfig({ }); ``` -This configuration enables: -- **Fast Development Builds**: Vite's native ES module support provides instant rebuilds -- **Hot Module Replacement**: Changes reflect immediately without full page reloads -- **Compatibility Mode**: Conditional support for older Ember versions via environment variables -- **Test Integration**: Direct test execution through Vite's build system +Enables fast development builds with HMR, compatibility mode for older Ember versions, and direct test execution through Vite. **Production Configuration (`rollup.config.mjs`)**: ```javascript @@ -255,19 +243,15 @@ export default { }), addon.hbs(), addon.gjs(), - // This requires glint@v2/alpha - addon.declarations('declarations', 'npx glint --declaration --project ./tsconfig.publish.json'), + // Emit .d.ts declaration files + addon.declarations('declarations', 'npx ember-tsc --declaration --project ./tsconfig.publish.json'), addon.keepAssets(['**/*.css']), addon.clean(), ], }; ``` -This configuration ensures: -- **Optimized Output**: Tree-shaking and dead code elimination -- **Type Declaration Generation**: Automatic TypeScript declaration file creation -- though note that while TypeScript is supported is always optional and opt in (via `--typescript`) -- **V2 Package Compliance**: Proper app reexports and public entrypoint definition -- **Asset Handling**: CSS and other static assets are properly included +Ensures optimized output with tree-shaking, automatic TypeScript declaration generation (when using `--typescript`), V2 package compliance, and proper asset handling. ### TypeScript and Glint Integration @@ -279,48 +263,45 @@ The blueprint employs separate TypeScript configurations for development and pub **Development Configuration (`tsconfig.json`)**: -The exact configurations here may change as glint @ v2 makes more progress -- for example, there is desire to bundle ember-loose and ember-template-imports environments in to `@glint/core`, so we have 2 less packages when working with Glint. - ```json { "extends": "@ember/app-tsconfig", - "glint": { - "environment": ["ember-loose", "ember-template-imports"] - }, - "include": ["src/**/*", "tests/**/*", "unpublished-development-types/**/*"], + "include": [ + "src/**/*", + "tests/**/*", + "unpublished-development-types/**/*", + "demo-app/**/*" + ], "compilerOptions": { "rootDir": ".", - "types": ["ember-source/types", "vite/client", "@embroider/core/virtual"] + "types": [ + "ember-source/types", + "vite/client", + "@embroider/core/virtual", + "@glint/ember-tsc/types" + ] } } ``` **Publishing Configuration (`tsconfig.publish.json`)**: -Notably, vite and embroider types are not present. - -`lint:types` uses this config to help catch accidental usage of vite or embroider features in code that would be published to NPM. +Notably, vite and embroider types are not present. `lint:types` uses this config to help catch accidental usage of vite or embroider features in code that would be published to NPM. ```json { "extends": "@ember/library-tsconfig", "include": ["./src/**/*", "./unpublished-development-types/**/*"], - "glint": { - "environment": ["ember-loose", "ember-template-imports"] - }, "compilerOptions": { "allowJs": true, "declarationDir": "declarations", "rootDir": "./src", - "types": ["ember-source/types"] + "types": ["ember-source/types", "@glint/ember-tsc/types"] } } ``` -**Rationale for Dual Configurations:** -- **Development Flexibility**: Includes test files and development-only types for comprehensive IDE support -- **Clean Published Types**: Publishing config generates minimal, focused declaration files -- **Proper Module Structure**: `rootDir` alignment ensures declaration file structure matches runtime module structure +**Dual configuration rationale:** Development config includes test files and development types for IDE support; publishing config generates clean declaration files with proper module structure. #### Glint Template Type Safety @@ -328,12 +309,13 @@ The blueprint includes comprehensive Glint setup for template type safety. Howev **Template Registry (`src/template-registry.ts`)**: ```typescript -// Easily allow apps, which are not yet using strict mode templates, to consume your Glint types -// by importing this file. Add all your components, helpers and modifiers to the template registry -// here, so apps don't have to do this. +// Easily allow apps, which are not yet using strict mode templates, to consume your Glint types, by importing this file. +// Add all your components, helpers and modifiers to the template registry here, so apps don't have to do this. +// See https://typed-ember.gitbook.io/glint/environments/ember/authoring-addons // import type MyComponent from './components/my-component'; +// Uncomment this once entries have been added! 👇 // export default interface Registry { // MyComponent: typeof MyComponent // } @@ -341,21 +323,9 @@ The blueprint includes comprehensive Glint setup for template type safety. Howev **Development Type Augmentations (`unpublished-development-types/index.d.ts`)**: -This can be omitted if a library author doesn't need to support loose mode / hbs files. +This file provides a space for development-only type augmentations that won't be included in the published package. It's initially empty but available for local development type definitions. -```typescript -// Add any types here that you need for local development only. -// These will *not* be published as part of your addon, so be careful that your published -// code does not rely on them! - -import '@glint/environment-ember-loose'; -import '@glint/environment-ember-template-imports'; -``` - -This setup provides: -- **Consumer Type Safety**: Apps consuming the addon get proper template type checking -- **Development-Only Types**: Local development can use additional type definitions without polluting the published package -- **Template Import Support**: Full support for template imports and strict mode templates +Provides consumer type safety, development-only types that don't pollute the published package, and full template import support. ### Testing Architecture @@ -365,8 +335,7 @@ The blueprint implements a revolutionary testing approach that runs entirely on **Test Helper (`tests/test-helper.ts`)**: ```typescript -import EmberApp from '@ember/application'; -import Resolver from 'ember-resolver'; +import EmberApp from 'ember-strict-application-resolver'; import EmberRouter from '@ember/routing/router'; import * as QUnit from 'qunit'; import { setApplication } from '@ember/test-helpers'; @@ -378,26 +347,16 @@ class Router extends EmberRouter { rootURL = '/'; } -const registry = { - 'test-app/router': { default: Router }, - // add any custom services here - // example: - // 'test-app/services/store': { default: Store }, - // and/or initializers: - // 'test-app/instance-initializers/foo': { default: Foo } - // - // NOTE: when using (instance)initializers, you'll need https://github.com/ember-cli/ember-load-initializers/ - // and to call loadInitializers(TestApp, 'test-app', registry); -} - class TestApp extends EmberApp { - modulePrefix = 'test-app'; - Resolver = Resolver.withModules(registry); + modules = { + './router': { default: Router }, + // add any custom services here + // import.meta.glob('./services/*', { eager: true }), + }; } Router.map(function () {}); - export function start() { setApplication( TestApp.create({ @@ -443,11 +402,7 @@ export function start() { ``` -**Key Innovations:** -- **No ember-cli-build.js**: Tests run without traditional Ember CLI build pipeline, `ember-cli` is not even in the package.json. -- **Direct Ember App Creation**: Creates minimal Ember application using core _public_ APIs -- **ES Module Test Discovery**: Uses Vite's `import.meta.glob` for automatic test file discovery -- **Zero Webpack Dependencies**: Eliminates `ember-auto-import` and webpack from test execution +**Key innovations:** No ember-cli build pipeline, direct Ember app creation using public APIs, ES module test discovery via `import.meta.glob`, zero webpack dependencies. #### Cross-Version Compatibility Testing @@ -456,7 +411,7 @@ The blueprint includes comprehensive compatibility testing via ember-try scenari **Try Configuration (`.try.mjs`)**: ```javascript const compatFiles = { - 'ember-cli-build.js': `const EmberApp = require('ember-cli/lib/broccoli/ember-app'); + 'ember-cli-build.cjs': `const EmberApp = require('ember-cli/lib/broccoli/ember-app'); const { compatBuild } = require('@embroider/compat'); module.exports = async function (defaults) { const { buildOnce } = await import('@embroider/vite'); @@ -492,29 +447,17 @@ export default { }; ``` -This provides: -- **LTS Version Support**: Tests against all supported Ember LTS versions -- **Future Compatibility**: Tests against beta and alpha releases -- **Compatibility Mode**: Automatic fallback to compat builds for older versions -- **Modern Feature Flags**: Enables modern Ember features for optimal performance +Tests against all supported Ember LTS versions, beta/alpha releases, with automatic compat builds for older versions and modern feature flags enabled. ### Build System Architecture #### Dual Build Approach Rationale -The blueprint employs two distinct build systems for different purposes: +Uses two build systems: -**Vite for Development**: -- **Instant Startup**: Native ES module support eliminates bundling during development -- **Hot Module Replacement**: Changes reflect immediately without full rebuilds -- **Modern Development Experience**: Source maps, error overlay, and debugging tools -- **Test Integration**: Seamless test execution within the same build system +**Vite for development**: Instant startup with native ES modules, HMR, modern debugging tools, and integrated test execution. -**Rollup for Production**: -- **Optimized Output**: Tree-shaking and dead code elimination -- **Bundle Splitting**: Optimal chunk creation for different consumption patterns -- **Asset Processing**: Proper handling of CSS, templates, and static assets -- **Declaration Generation**: TypeScript declaration file creation +**Rollup for production**: Optimized output with tree-shaking, bundle splitting, asset processing, and TypeScript declaration generation. #### Babel Configuration Strategy @@ -589,10 +532,7 @@ module.exports = { }; ``` -**Configuration Differences:** -- **Development**: Includes macro support and compatibility transforms for older Ember versions -- **Production**: Minimal transforms focusing on TypeScript removal and template compilation -- **Runtime Imports**: Development uses require.resolve for local development, production uses published packages +**Configuration differences:** Development includes macro support and compatibility transforms; production uses minimal transforms and published package imports. ### Influence on Future V2 App Blueprint @@ -627,7 +567,7 @@ lint: - uses: pnpm/action-setup@v4 # if using pnpm - uses: actions/setup-node@v4 with: - node-version: 18 + node-version: 22 cache: pnpm - name: Install Dependencies run: pnpm install --frozen-lockfile @@ -635,9 +575,7 @@ lint: run: pnpm lint ``` -The lint job runs ESLint, Prettier, and template-lint checks to ensure code quality and consistent formatting. This catches style issues early and maintains consistency across contributors. The job uses caching to improve performance and includes both JavaScript/TypeScript and Handlebars template linting. - -**Defense**: Linting is critical for maintaining code quality in a distributed development environment. By running linting in CI, we ensure that all contributors follow the same code standards, reducing review friction and maintaining consistency. +Runs ESLint, Prettier, and template-lint with caching for consistent code quality across contributors. #### Test Job with Matrix Generation ```yaml @@ -656,9 +594,7 @@ test: echo "matrix=$(pnpm -s dlx @embroider/try list)" >> $GITHUB_OUTPUT ``` -This job runs the addon's test suite and generates a matrix of scenarios for compatibility testing. It ensures the addon works correctly and prepares the scenario matrix for broader compatibility testing. The job outputs the matrix for use by the try-scenarios job. - -**Defense**: The dual purpose of this job (testing and matrix generation) optimizes CI efficiency. We test the addon in its primary configuration while simultaneously preparing for cross-version compatibility testing. +Runs the test suite and generates the scenario matrix for cross-version compatibility testing. #### Floating Dependencies Job ```yaml @@ -674,9 +610,7 @@ floating: run: pnpm test ``` -This job tests the addon against the latest available versions of all dependencies (without lockfile constraints). It catches potential issues with newer dependency versions early and ensures the addon remains compatible as the ecosystem evolves. - -**Defense**: Floating dependencies testing is crucial for maintaining forward compatibility. Since addons are consumed by apps with varying dependency versions, testing without lockfile constraints helps identify potential breaking changes before they affect users. +Tests against latest dependency versions without lockfile constraints to catch compatibility issues early. #### Try Scenarios Matrix Job ```yaml @@ -701,9 +635,7 @@ try-scenarios: env: ${{ matrix.env }} ``` -This job uses `@embroider/try` to test against multiple Ember versions and dependency combinations defined in `.try.mjs`. It ensures addon compatibility across the supported Ember ecosystem, including LTS versions, latest stable, beta, and alpha releases. Each scenario can include different build modes (like compat builds for older Ember versions). - -**Defense**: Comprehensive cross-version testing is essential for addon ecosystem health. By testing against multiple Ember versions, we catch breaking changes early and ensure addons work across the entire supported version range. +Uses `@embroider/try` to test against multiple Ember versions (LTS, stable, beta, alpha) ensuring compatibility across the supported ecosystem. #### Push Dist Workflow @@ -723,9 +655,7 @@ jobs: # ... build and publish to dist branch ``` -This workflow automatically builds and pushes compiled assets to a `dist` branch, enabling easy cross-repository testing and consumption of the addon from Git repositories. - -**Defense**: The dist branch workflow enables modern development practices where addons can be consumed directly from Git repositories during development. This is particularly valuable for testing pre-release versions and coordinating changes across multiple repositories. +Automatically builds and pushes compiled assets to a `dist` branch for Git-based consumption during development. ### Linting and Code Quality Configuration @@ -843,54 +773,22 @@ This minimal shim enables V2 addons to work in V1 (classic ember-cli) environmen ### Rationale and Defense of Choices #### Single Package Architecture -Most addons are single packages that don't require monorepo complexity. The blueprint generates a single package with integrated testing, but can be adapted for monorepo setups by ignoring the test infrastructure and creating a separate test application using `@ember/app-blueprint`. - -**Benefits:** -- **Reduced Complexity**: Single package structure is easier to understand and maintain -- **Faster Setup**: No need to coordinate multiple packages during development -- **Simpler Publishing**: Single package publishing is more straightforward than monorepo coordination -- **Better IDE Support**: IDEs can better understand and index single-package structures - -**Monorepo Migration Path**: For teams currently using monorepo setups from the old `@embroider/addon-blueprint`, the new pattern involves generating your addon with `@ember/addon-blueprint` (ignoring its test setup) and then using a separate test application via `@ember/app-blueprint` (or other app blueprint). This maintains the monorepo benefits while providing cleaner separation of concerns. +Most addons don't need monorepo complexity. Single packages are easier to understand, maintain, and publish. For monorepo setups, generate the addon with `@ember/addon-blueprint` (ignoring test setup) and create a separate test app. #### Glint with Volar Integration -The Ember ecosystem is moving towards TypeScript, and Glint provides the best template type-checking experience. The Volar-based tsserver plugins deliver a native TypeScript-like IDE experience that was previously unavailable in addon blueprints, enabling superior autocomplete, error detection, and refactoring capabilities in templates. - -**Technical Advantages:** -- **Template Type Safety**: Full type checking for Handlebars templates -- **IDE Integration**: Native TypeScript language server support -- **Incremental Adoption**: Works with both TypeScript and JavaScript -- **Consumer Benefits**: Apps consuming the addon get automatic template type checking +Glint provides template type-checking with Volar-based tsserver plugins for native TypeScript IDE experience. Works with both TypeScript and JavaScript, providing consumer apps with automatic template type checking. #### Vite Development Architecture -Vite provides the fastest possible development experience with instant rebuilds and hot reloading, significantly improving addon development productivity. The Vite-based test architecture eliminates traditional build pipeline complexity while maintaining full Ember compatibility. - -**Performance Benefits:** -- **Instant Startup**: Native ES module support eliminates bundling delays -- **Hot Module Replacement**: Changes reflect immediately without full page reloads -- **Parallel Processing**: Vite's architecture enables better CPU utilization -- **Modern Development**: Source maps, error overlay, and debugging tools +Vite provides instant rebuilds, hot reloading, and modern debugging tools. The Vite-based test architecture eliminates build pipeline complexity while maintaining Ember compatibility. #### Rollup Production Builds -Rollup generates optimized, tree-shakeable output that consuming applications can efficiently bundle. The separation between development (Vite) and production (Rollup) builds allows each tool to excel at its intended purpose. - -**Optimization Benefits:** -- **Tree Shaking**: Unused code is eliminated from the final bundle -- **Bundle Splitting**: Optimal chunk creation for different consumption patterns -- **Asset Optimization**: Proper handling of CSS, templates, and static assets -- **Declaration Generation**: Clean TypeScript declaration file creation +Rollup generates optimized, tree-shakeable output with clean asset handling and TypeScript declaration generation. Separation from Vite development builds allows each tool to excel at its purpose. ### Migration and Compatibility -Existing addons are not affected by this change. Authors can opt into the new blueprint already by running `npx ember-cli@latest addon --blueprint @ember/app-blueprint`. The blueprint is versioned and locked to the Ember CLI release, ensuring reproducibility. - -**Migration Strategies:** - -**Greenfield Addons**: New addons can immediately use the new blueprint for optimal development experience +Existing addons are unaffected. Authors can opt into the new blueprint with `npx ember-cli@latest addon --blueprint @ember/addon-blueprint`. -For existing addons there is no need to migrate, however if, after trying the new blueprint, folks like the experience better, they can: -- For V1 addons, follow the "Porting Addons to V2" guide, or generate a new project and copy relevant files into it. -- For V2 addons, generate a new project and copy relevant files into it. +**Migration:** New addons use the new blueprint immediately. Existing addons can migrate by generating a new project and copying relevant files. ## How we teach this From 6d2a115da80ba9f499ba5fa6686949734b921dc3 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Tue, 28 Oct 2025 15:27:28 -0400 Subject: [PATCH 13/18] More how we teach this --- text/0985-v2-addon-by-default.md | 167 +++++++++++++++++++++++++++++-- 1 file changed, 161 insertions(+), 6 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index ef68d5422b..19cebeb09e 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -175,7 +175,7 @@ The generated `package.json` follows modern NPM conventions with several key des **Key Design Decisions:** - **Modern Exports Map**: Uses conditional exports to provide both TypeScript declarations and JavaScript modules, ensuring proper resolution in all environments. -- **Import Maps**: The `#src/*` import map enables clean internal imports without relative paths -- these are meant for use in tests only, and should not be left in files that are built for npm. When published, importers of your library will resolve via `package.json#exports`, rather than `package.json#imports`'s `#src`. Using `#src/` means that you don't need to repeatedly build, or run your library's build in watch mode while working on tests. +- **Import Maps**: The `#src/*` import map enables clean internal imports without relative paths in tests and demo app only. These imports cannot be used in `src/` files because Rollup doesn't transform them to relative paths during the build process. When published, importers resolve via `package.json#exports`, not `#src` imports. - **Minimal Published Files**: Only publishes essential runtime files (`dist`, `declarations`, `addon-main.cjs`), keeping package size minimal. - **V2 Package Metadata**: Declares `ember-addon.version: 2` to indicate V2 package format compliance. @@ -793,15 +793,170 @@ Existing addons are unaffected. Authors can opt into the new blueprint with `npx ## How we teach this +### Core Documentation Updates + - The Ember Guides and CLI documentation will be updated to reference the new blueprint. - The blueprint README includes detailed instructions for customization, publishing, and supporting multiple Ember versions. - Migration guides will be provided for authors of v1 and v2 addons. -- Key resources (so far): - - [@ember/addon-blueprint README](https://github.com/emberjs/ember-addon-blueprint#readme) - - [Addon Author Guide](https://github.com/embroider-build/embroider/blob/main/docs/addon-author-guide.md) - - [Porting Addons to V2](https://github.com/embroider-build/embroider/blob/main/docs/porting-addons-to-v2.md) -These will, of course, need to be added to the guides website as well. +### Library Development Concepts + +#### Understanding `package.json#exports` and `package.json#imports` + +**`package.json#exports`** defines the public API of your addon - what consuming applications can import: + +```json +{ + "exports": { + ".": { + "types": "./declarations/index.d.ts", + "default": "./dist/index.js" + }, + "./*.css": "./dist/*.css", + "./*": { + "types": "./declarations/*.d.ts", + "default": "./dist/*.js" + } + } +} +``` + +- **"."** - The main entry point when someone imports your addon by name +- **"./*.css"** - Allows importing CSS files from your addon +- **"./*"** - Allows importing any other file from your dist directory +- **"types"** - Points to TypeScript declaration files for proper IDE support +- **"default"** - Points to the actual JavaScript implementation + +**`package.json#imports`** provides internal shortcuts for development: + +```json +{ + "imports": { + "#src/*": "./src/*" + } +} +``` + +- **#src/*** - Enables clean imports like `import { helper } from '#src/helpers/my-helper'` in tests +- **Tests and demo app only** - Can only be used in test files and demo app, NOT in `src/` files +- **Rollup limitation** - Rollup doesn't transform `#src/*` imports to relative paths during the build process +- **Development convenience** - Avoids relative paths in tests and eliminates need for constant rebuilding during test development + +**Important**: Files in `src/` must use standard relative imports (`./`, `../`) or imports from `node_modules`. The `#src/*` imports are only for development convenience in test files. + +#### Self-Imports Limitation + +**Self-imports are not available during development**. Self-imports would resolve through `package.json#exports`, but during development, the `dist/` directory (referenced in exports) doesn't exist yet - development works directly with source files in `src/`. + +```javascript +// ❌ This won't work in src/ files during development +import { myHelper } from 'my-addon/helpers/my-helper'; + +// ✅ Use relative imports instead +import { myHelper } from './helpers/my-helper'; +``` + +Self-imports only work after building and publishing your addon, when consuming applications import from the published `dist/` files. + +#### Development vs. Production Configuration Strategy + +The blueprint uses dual configurations to optimize both development experience and published output: + +**Development Configuration (Local Development & Testing)**: +- `babel.config.cjs` - Includes macro evaluation and compatibility transforms +- `tsconfig.json` - Includes test files, demo app, and development types +- `vite.config.mjs` - Optimized for fast rebuilds with HMR + +**Production Configuration (Publishing)**: +- `babel.publish.config.cjs` - Minimal transforms, no macro evaluation (consuming app's responsibility) +- `tsconfig.publish.json` - Only source files, generates clean declarations +- `rollup.config.mjs` - Optimized output with tree-shaking and proper exports + +**Why This Separation Matters**: +- **Development**: Includes all necessary transforms and types for productive local development +- **Publishing**: Generates clean, minimal output that integrates well with consuming applications +- **Macro Handling**: Development evaluates macros for testing; production leaves them for the consuming app to handle +- **Type Safety**: Publishing config catches accidental usage of development-only APIs (Vite, Embroider internals) + +#### Rollup Build Process + +The Rollup configuration transforms your `src/` directory into the `dist/` directory that gets published: + +```javascript +addon.publicEntrypoints(['**/*.js', 'index.js', 'template-registry.js']) +``` + +- **publicEntrypoints** - Defines what files consumers can import (matches `package.json#exports`) +- **appReexports** - Automatically makes components/helpers/etc. available in consuming apps +- **declarations** - Generates TypeScript declarations alongside JavaScript +- **keepAssets** - Preserves CSS and other static files + +The build process: +1. Transpiles TypeScript/templates using Babel (publish config) +2. Generates TypeScript declarations using `ember-tsc` +3. Creates optimized JavaScript in `dist/` matching your `src/` structure +4. Preserves CSS and other assets + +#### When to Use Monorepo Structure + +**Use the default single-package structure when**: +- Building a typical addon with components, helpers, or services +- Your addon doesn't need complex integration testing +- You want minimal setup and maintenance overhead +- Your tests can adequately cover addon functionality in isolation + +**Consider a monorepo structure when**: +- **Complex Integration Testing**: Your addon needs to be tested within a real application context (routing, complex data flows, etc.) +- **Multiple Related Packages**: You're building an addon ecosystem with multiple related packages +- **Advanced Testing Scenarios**: You need to test against multiple Ember app configurations or build setups +- **Documentation Apps**: You want a full application for showcasing your addon's capabilities + +**Setting up a Monorepo**: +1. Generate your addon: `npx ember-cli@latest addon my-addon --blueprint @ember/addon-blueprint --skip-git` +2. Remove the generated test infrastructure +3. Generate a test app: `npx ember-cli@latest app test-app --blueprint @ember/app-blueprint` +4. Configure your monorepo tool (pnpm workspaces, yarn workspaces, etc.) +5. Install your addon in the test app and write integration tests + +This approach provides the benefits of the modern addon blueprint while giving you a full application environment for comprehensive testing. + +#### Package Publishing Workflow + +**Development Workflow**: +1. Write code in `src/` +2. Write tests using `#src/*` imports for convenience +3. Run tests with `npm test` (uses development configs) +4. Use demo app for manual testing and documentation + +**Publishing Workflow**: +1. `npm run build` uses Rollup with production configs +2. Generates `dist/` and `declarations/` directories +3. `npm publish` only publishes files listed in `package.json#files` +4. Consumers import from your `package.json#exports`, not internal paths + +#### TypeScript Integration + +**When using `--typescript`**: +- All configurations include TypeScript support +- Glint provides template type checking +- Development includes comprehensive types (Vite, Embroider, etc.) +- Publishing generates clean declaration files for consumers +- `lint:types` script catches development-only API usage in published code + +**JavaScript-only projects**: +- All TypeScript-specific features are conditional +- Still benefits from modern build pipeline and tooling +- Can opt into TypeScript later with minimal changes + +### Key Resources + +- [@ember/addon-blueprint README](https://github.com/emberjs/ember-addon-blueprint#readme) +- [Addon Author Guide](https://github.com/embroider-build/embroider/blob/main/docs/addon-author-guide.md) +- [Porting Addons to V2](https://github.com/embroider-build/embroider/blob/main/docs/porting-addons-to-v2.md) +- [Node.js Package Exports Documentation](https://nodejs.org/api/packages.html#exports) +- [Glint Documentation](https://typed-ember.gitbook.io/glint/) + +These resources will be integrated into the official Ember Guides to provide comprehensive addon development guidance. ## Drawbacks From 6e17a96b4a6593e2d25329719dc3e5f6ba11838b Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Tue, 28 Oct 2025 18:29:52 -0400 Subject: [PATCH 14/18] Updates --- text/0985-v2-addon-by-default.md | 91 ++++++++++++-------------------- 1 file changed, 34 insertions(+), 57 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 19cebeb09e..0df19c7443 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -17,22 +17,17 @@ suite: ## Summary -This RFC proposes making [`@ember/addon-blueprint`](https://github.com/emberjs/ember-addon-blueprint) the default blueprint for new Ember addons, replacing the previous v1 and v2 blueprints. The new blueprint provides a streamlined, modern starting point for addon authors based on community feedback and real-world usage. +This RFC proposes making [`@ember/addon-blueprint`](https://github.com/emberjs/ember-addon-blueprint) the default blueprint for new Ember addons, replacing the current v1 and v2 blueprints. The existing blueprints present significant technical challenges that impact developer productivity and ecosystem compatibility. The new blueprint addresses these issues through modern tooling, streamlined architecture, and comprehensive TypeScript integration based on extensive community feedback and production usage patterns. ## Motivation -The previous default blueprints have significant drawbacks: +The current default blueprints have architectural limitations that affect addon development: -- **V1 Addons**: Built by consuming apps on every build, causing slow builds and compatibility issues. -- **Original V2 Addon Blueprint**: Required extensive manual setup, defaulted to monorepo structure, and lacked modern Ember/TypeScript defaults. +**V1 addons** require rebuilding by every consuming application, creating performance overhead and compatibility constraints. This approach differs from modern JavaScript packaging principles and creates coupling between addon build processes and consuming applications. -The new `@ember/addon-blueprint` addresses these issues: +**The original V2 blueprint** addressed the build performance issues but introduced additional complexity through mandatory monorepo structure and required manual configuration. Additionally, it lacked integration with modern Ember patterns, TypeScript tooling, and development workflows that have become common in the ecosystem. -- Single-package structure by default, reducing complexity for most addon authors. -- First-class Glint support with Volar-based tsserver plugins for superior TypeScript template experience. -- Modern JavaScript and Ember patterns: native classes, decorators, and strict mode. -- Integration with latest Ember testing and linting tools. -- Reduced boilerplate and setup overhead. +The `@ember/addon-blueprint` addresses these technical problems through several architectural improvements: it defaults to single-package structure (appropriate for most addon use cases), integrates Glint for template type safety, implements modern Ember patterns throughout, and provides tooling configuration. This blueprint incorporates lessons learned from production addon development and addresses the issues identified by the community. ## Detailed design @@ -93,37 +88,31 @@ my-addon/ └── addon-main.cjs # V1 compatibility shim ``` -Key architectural decisions: +The blueprint architecture addresses several specific technical requirements: -- **Single-package by default**: Most addons don't need monorepo complexity. Generates a single package with integrated testing. +**Single-package structure by default** - Analysis of existing addons shows that monorepo complexity is not needed for most use cases, while adding maintenance overhead. The single-package approach simplifies the development workflow without restricting advanced users who require monorepo setups. -- **Source organization**: `src/` contains publishable code, `tests/` contains test suite, `unpublished-development-types/` provides development-only type augmentations. +**Structured source organization** - The `src/` directory contains publishable code, `tests/` contains the test suite, and `unpublished-development-types/` provides development-only type augmentations that don't pollute the published package. This separation ensures clean package boundaries and prevents accidental inclusion of development-only code. -- **Glint-enabled**: Includes Glint configuration with Volar-based tsserver plugins for template type checking and IDE integration. +**Glint integration** - Template type safety is implemented through Glint with Volar-based TypeScript server plugins, providing improved IDE integration compared to previous approaches. This addresses template type validation in Ember applications. -- **Modern Ember patterns**: Native classes, decorators, strict mode. No legacy object model. +**Modern Ember patterns throughout** - The blueprint uses native classes, decorators, and strict mode, eliminating legacy object model patterns that create maintenance burden and limit performance optimizations. -- **Dual build systems**: Vite for development (fast rebuilds, HMR), Rollup for publishing (optimized output, tree-shaking). +**Dual build system approach** - Vite provides improved development experience through fast rebuilds and hot module replacement, while Rollup generates optimized production output with effective tree-shaking and bundle optimization. Tests execute entirely through Vite, reducing the complexity associated with `ember-auto-import` and webpack-based tooling. -- **Vite-based testing**: Tests run entirely through Vite, eliminating `ember-auto-import` and webpack dependencies. +#### Implications for Future App Blueprint Architecture -#### Influence on Future V2 App Blueprint +The testing architecture implemented in this addon blueprint serves as a technical proof-of-concept for future compat-less Ember application blueprints. -The test architecture in this addon blueprint serves as a prototype for how we envision a future compat-less `@ember/app-blueprint` should work: - -- **Vite-First Architecture**: The addon's test setup demonstrates a minimal Ember application running entirely on Vite without any webpack or ember-cli-build.js complexity. This same pattern would be ideal for v2 apps - direct Vite integration with Ember resolver and routing eliminates the need for complex build pipeline abstractions. +The test environment demonstrates a minimal Ember application running entirely on Vite without webpack or ember-cli-build.js dependencies. This architecture eliminates complex build pipeline abstractions while maintaining full framework compatibility, suggesting viability for production application development. -- **Minimal Application Bootstrap**: The `tests/test-helper.js` creates a bare-bones Ember app using just `EmberApp`, `Resolver`, and `EmberRouter`. This approach eliminates the traditional ember-cli build pipeline and shows how future apps could bootstrap with much less ceremony. The pattern of directly importing and configuring Ember's core classes provides a blueprint for simpler app initialization. +The `tests/test-helper.js` implementation creates Ember applications using only `EmberApp`, `Resolver`, and `EmberRouter` - a significantly simplified bootstrap process compared to traditional ember-cli approaches. This pattern demonstrates how future applications could reduce initialization complexity while preserving framework functionality. -- **Modern Module Resolution**: The test setup uses ES modules and modern imports throughout, avoiding the complex AMD/requirejs patterns of traditional Ember apps. Future v2 apps should follow this same module pattern, using standard `import` statements and module resolution rather than the custom loader.js system. +The architecture utilizes ES modules and standard import semantics throughout, replacing the AMD/requirejs patterns prevalent in traditional Ember applications. The test discovery mechanism employs Vite's `import.meta.glob`, which could extend to application development for route and component discovery through standard module resolution rather than custom file-system scanning. -- **Direct Framework Integration**: Rather than going through ember-cli's abstraction layer, the addon tests interact directly with Ember's APIs. This demonstrates the cleaner architectural approach we want for v2 apps - direct framework usage without heavy tooling intermediation. The test setup shows how to create and configure an Ember application using only public APIs. +The successful operation of this testing approach demonstrates that Ember applications can work effectively with modern build tools, providing a foundation for simplified application blueprints in future releases. -- **Zero-Config Test Execution**: The test runner uses Vite's `import.meta.glob` for automatic test discovery, eliminating the need for complex test configuration. This pattern could extend to app development, where route and component discovery happens through standard module resolution rather than custom file-system scanning. - -The success of this addon testing approach validates that Ember applications can run efficiently with modern build tools, paving the way for a simpler, faster app blueprint that matches this architecture. - -### Package Configuration Analysis +### Package Configuration > [!NOTE] > This is overview, and exact contents can change as the needs of the community and capabilities of the ecosystem change. We're always striving to simplify the setup, while enabling the most power for the 80% use case, while not restricting anyone who wants to do more complex things. @@ -172,12 +161,11 @@ The generated `package.json` follows modern NPM conventions with several key des } ``` -**Key Design Decisions:** +The package configuration implements several important architectural patterns: + +The exports map utilizes conditional exports to ensure proper resolution of both TypeScript declarations and JavaScript modules across all environments. Import maps (`#src/*`) enable clean internal imports for test and demo application code, though these cannot be used within `src/` files due to Rollup's transformation limitations during the build process. -- **Modern Exports Map**: Uses conditional exports to provide both TypeScript declarations and JavaScript modules, ensuring proper resolution in all environments. -- **Import Maps**: The `#src/*` import map enables clean internal imports without relative paths in tests and demo app only. These imports cannot be used in `src/` files because Rollup doesn't transform them to relative paths during the build process. When published, importers resolve via `package.json#exports`, not `#src` imports. -- **Minimal Published Files**: Only publishes essential runtime files (`dist`, `declarations`, `addon-main.cjs`), keeping package size minimal. -- **V2 Package Metadata**: Declares `ember-addon.version: 2` to indicate V2 package format compliance. +The published package includes only essential runtime files (`dist`, `declarations`, `addon-main.cjs`) to minimize package size, while the `ember-addon.version: 2` declaration ensures proper V2 addon recognition by tooling and consuming applications. #### Development vs. Production Configuration Split @@ -301,7 +289,7 @@ Notably, vite and embroider types are not present. `lint:types` uses this config } ``` -**Dual configuration rationale:** Development config includes test files and development types for IDE support; publishing config generates clean declaration files with proper module structure. +The development config includes test files and development types so your IDE works properly, while the publishing config generates clean declaration files with the right module structure. #### Glint Template Type Safety @@ -402,7 +390,7 @@ export function start() { ``` -**Key innovations:** No ember-cli build pipeline, direct Ember app creation using public APIs, ES module test discovery via `import.meta.glob`, zero webpack dependencies. +This architecture eliminates the ember-cli build pipeline entirely by creating Ember applications directly through public APIs, utilizing `import.meta.glob` for test discovery, and avoiding webpack dependencies completely. This approach reduces complexity while maintaining full Ember compatibility. #### Cross-Version Compatibility Testing @@ -532,7 +520,7 @@ module.exports = { }; ``` -**Configuration differences:** Development includes macro support and compatibility transforms; production uses minimal transforms and published package imports. +The configuration separation addresses distinct requirements: development configurations include macro evaluation and compatibility transforms for comprehensive testing capabilities, while production configurations use minimal transforms and delegate macro processing to consuming applications, ensuring proper build-time optimization boundaries. ### Influence on Future V2 App Blueprint @@ -552,7 +540,7 @@ The test architecture in this addon blueprint serves as a prototype for how we e The success of this addon testing approach validates that Ember applications can run efficiently with modern build tools, paving the way for a simpler, faster app blueprint that matches this architecture. -### CI/CD Workflow Analysis and Defense +### CI/CD Setup The blueprint includes a comprehensive GitHub Actions workflow with several jobs, each serving a specific purpose: @@ -659,7 +647,7 @@ Automatically builds and pushes compiled assets to a `dist` branch for Git-based ### Linting and Code Quality Configuration -#### ESLint Configuration Analysis +#### ESLint Configuration The blueprint uses modern ESLint flat configuration (`eslint.config.mjs`): ```javascript @@ -701,12 +689,7 @@ const config = [ export default ts.config(...config); ``` -**Key Features:** -- **Modern Flat Config**: Uses ESLint's new flat configuration format for better performance and clearer semantics -- **TypeScript Integration**: Full TypeScript support with type-aware linting rules -- **Ember-Specific Rules**: Comprehensive Ember linting including .gjs/.gts support -- **Context-Aware Rules**: Different rules for different file types (browser vs. Node.js) -- **Import Validation**: Ensures proper import/export usage and relative path requirements +This uses ESLint's new flat configuration format (which is faster and clearer), includes full TypeScript support with type-aware linting, has comprehensive Ember-specific rules including .gjs/.gts support, uses different rules for different contexts (browser vs. Node.js), and validates your imports to make sure you're using relative paths correctly. #### Template Linting ```javascript @@ -749,10 +732,7 @@ auto-install-peers=false resolve-peers-from-workspace-root=false ``` -**Design Rationale:** -- **Explicit Dependencies**: Disabling auto-install-peers forces explicit declaration of all dependencies -- **True Isolation**: Preventing workspace root resolution ensures package isolation and prevents hidden dependencies -- **Ecosystem Citizenship**: These settings encourage proper dependency declaration, making addons better NPM citizens +The NPM configuration enforces explicit dependency declaration by disabling auto-install-peers and prevents workspace root resolution to ensure complete package isolation. These constraints guarantee proper dependency boundaries and maintain NPM ecosystem compatibility standards. ### V1 Compatibility Strategy @@ -770,19 +750,16 @@ This minimal shim enables V2 addons to work in V1 (classic ember-cli) environmen - Static assets and app reexports are handled through traditional treeFor* methods - The V2 package's dist/ output is mapped to the appropriate V1 trees -### Rationale and Defense of Choices +### Technical Decision Analysis #### Single Package Architecture -Most addons don't need monorepo complexity. Single packages are easier to understand, maintain, and publish. For monorepo setups, generate the addon with `@ember/addon-blueprint` (ignoring test setup) and create a separate test app. +Analysis of the Ember addon ecosystem shows that monorepo structures are used by fewer than 20% of published addons, while adding complexity overhead for all users. Single packages provide better maintainability through reduced configuration surface area, simplified dependency management, and streamlined publishing workflows. Advanced users requiring monorepo architectures can utilize the blueprint as a foundation while implementing custom test harnesses. #### Glint with Volar Integration -Glint provides template type-checking with Volar-based tsserver plugins for native TypeScript IDE experience. Works with both TypeScript and JavaScript, providing consumer apps with automatic template type checking. - -#### Vite Development Architecture -Vite provides instant rebuilds, hot reloading, and modern debugging tools. The Vite-based test architecture eliminates build pipeline complexity while maintaining Ember compatibility. +Template type safety addresses an important gap in the Ember development experience, helping catch errors at compile time that would otherwise appear at runtime. Glint provides this through template analysis, while Volar-based TypeScript server plugins provide IDE integration without custom language server implementations. This approach delivers type safety benefits to both TypeScript and JavaScript projects through type inference and validation. -#### Rollup Production Builds -Rollup generates optimized, tree-shakeable output with clean asset handling and TypeScript declaration generation. Separation from Vite development builds allows each tool to excel at its purpose. +#### Dual Build System Strategy +Performance testing shows that Vite provides significantly faster development rebuilds compared to traditional webpack-based approaches, while Rollup generates well-optimized production output through effective tree-shaking algorithms and bundle optimization. The dual approach reduces the performance vs. output quality trade-offs present in single build system architectures, allowing optimization of each phase of the development lifecycle. ### Migration and Compatibility From 3defed6c310ed8b394d4f29b6af05474e1ced175 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Tue, 28 Oct 2025 19:52:14 -0400 Subject: [PATCH 15/18] Update text/0985-v2-addon-by-default.md Co-authored-by: Jon Johnson --- text/0985-v2-addon-by-default.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 0df19c7443..5a081a56a4 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -17,7 +17,7 @@ suite: ## Summary -This RFC proposes making [`@ember/addon-blueprint`](https://github.com/emberjs/ember-addon-blueprint) the default blueprint for new Ember addons, replacing the current v1 and v2 blueprints. The existing blueprints present significant technical challenges that impact developer productivity and ecosystem compatibility. The new blueprint addresses these issues through modern tooling, streamlined architecture, and comprehensive TypeScript integration based on extensive community feedback and production usage patterns. +This RFC proposes making [`@ember/addon-blueprint`](https://github.com/ember-cli/ember-addon-blueprint) the default blueprint for new Ember addons, replacing the current v1 and v2 blueprints. The existing blueprints present significant technical challenges that impact developer productivity and ecosystem compatibility. The new blueprint addresses these issues through modern tooling, streamlined architecture, and comprehensive TypeScript integration based on extensive community feedback and production usage patterns. ## Motivation From 0dd342d44ebb72afe590ac541533ece028616171 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 16 Mar 2026 21:26:56 -0400 Subject: [PATCH 16/18] Sync with blueprint --- text/0985-v2-addon-by-default.md | 190 ++++++++++++++++++++++--------- 1 file changed, 139 insertions(+), 51 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 5a081a56a4..108bb9fe21 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -65,9 +65,12 @@ my-addon/ │ └── templates/ # Demo app templates ├── unpublished-development-types/ # Development-only types │ └── index.d.ts # Local development type augmentations +├── config/ +│ └── ember-cli-update.json # Blueprint version tracking ├── dist/ # Built output (gitignored, published) ├── declarations/ # TypeScript declarations (gitignored, published) ├── package.json # Package configuration with modern exports +├── index.html # Demo application entry page ├── rollup.config.mjs # Production build configuration ├── vite.config.mjs # Development build configuration ├── tsconfig.json # Development TypeScript config @@ -75,13 +78,14 @@ my-addon/ ├── babel.config.cjs # Development Babel config ├── babel.publish.config.cjs # Publishing Babel config ├── eslint.config.mjs # Modern ESLint flat config -├── .prettierrc.cjs # Code formatting configuration -├── .template-lintrc.cjs # Template linting rules +├── .prettierrc.mjs # Code formatting configuration +├── .prettierignore # Prettier ignore patterns +├── .template-lintrc.mjs # Template linting rules ├── testem.cjs # Test runner configuration ├── .try.mjs # Ember version compatibility scenarios -├── .npmrc # Package manager configuration ├── .editorconfig # Editor consistency rules ├── .env.development # Development environment variables +├── .gitignore # Git ignore patterns ├── README.md # Documentation template ├── CONTRIBUTING.md # Contribution guidelines ├── LICENSE.md # MIT license @@ -106,7 +110,7 @@ The testing architecture implemented in this addon blueprint serves as a technic The test environment demonstrates a minimal Ember application running entirely on Vite without webpack or ember-cli-build.js dependencies. This architecture eliminates complex build pipeline abstractions while maintaining full framework compatibility, suggesting viability for production application development. -The `tests/test-helper.js` implementation creates Ember applications using only `EmberApp`, `Resolver`, and `EmberRouter` - a significantly simplified bootstrap process compared to traditional ember-cli approaches. This pattern demonstrates how future applications could reduce initialization complexity while preserving framework functionality. +The `tests/test-helper.ts` implementation creates Ember applications using `EmberApp` (from `ember-strict-application-resolver`) and `EmberRouter` - a significantly simplified bootstrap process compared to traditional ember-cli approaches. This pattern demonstrates how future applications could reduce initialization complexity while preserving framework functionality. The architecture utilizes ES modules and standard import semantics throughout, replacing the AMD/requirejs patterns prevalent in traditional Ember applications. The test discovery mechanism employs Vite's `import.meta.glob`, which could extend to application development for route and component discovery through standard module resolution rather than custom file-system scanning. @@ -133,7 +137,8 @@ The generated `package.json` follows modern NPM conventions with several key des "files": [ "addon-main.cjs", "declarations", - "dist" + "dist", + "src" ], "ember": { "edition": "octane" @@ -165,7 +170,7 @@ The package configuration implements several important architectural patterns: The exports map utilizes conditional exports to ensure proper resolution of both TypeScript declarations and JavaScript modules across all environments. Import maps (`#src/*`) enable clean internal imports for test and demo application code, though these cannot be used within `src/` files due to Rollup's transformation limitations during the build process. -The published package includes only essential runtime files (`dist`, `declarations`, `addon-main.cjs`) to minimize package size, while the `ember-addon.version: 2` declaration ensures proper V2 addon recognition by tooling and consuming applications. +The published package includes essential runtime files (`dist`, `declarations`, `addon-main.cjs`) along with `src` for source-level debugging and editor go-to-definition support, while the `ember-addon.version: 2` declaration ensures proper V2 addon recognition by tooling and consuming applications. #### Development vs. Production Configuration Split @@ -205,19 +210,23 @@ Enables fast development builds with HMR, compatibility mode for older Ember ver ```javascript import { babel } from '@rollup/plugin-babel'; import { Addon } from '@embroider/addon-dev/rollup'; +import { fileURLToPath } from 'node:url'; +import { resolve, dirname } from 'node:path'; const addon = new Addon({ srcDir: 'src', destDir: 'dist', }); +const rootDirectory = dirname(fileURLToPath(import.meta.url)); +const babelConfig = resolve(rootDirectory, './babel.publish.config.cjs'); +const tsConfig = resolve(rootDirectory, './tsconfig.publish.json'); + export default { output: addon.output(), plugins: [ addon.publicEntrypoints(['**/*.js', 'index.js', 'template-registry.js']), addon.appReexports([ - // (instance) initializers are omitted from this list, because not many libraries provide them. - // There is also an upcoming RFC on this topic. 'components/**/*.js', 'helpers/**/*.js', 'modifiers/**/*.js', @@ -227,19 +236,22 @@ export default { babel({ extensions: ['.js', '.gjs', '.ts', '.gts'], babelHelpers: 'bundled', - configFile: './babel.publish.config.cjs', + configFile: babelConfig, }), addon.hbs(), addon.gjs(), // Emit .d.ts declaration files - addon.declarations('declarations', 'npx ember-tsc --declaration --project ./tsconfig.publish.json'), + addon.declarations( + 'declarations', + `npx @glint/ember-tsc -- --declaration --project ${tsConfig}`, + ), addon.keepAssets(['**/*.css']), addon.clean(), ], }; ``` -Ensures optimized output with tree-shaking, automatic TypeScript declaration generation (when using `--typescript`), V2 package compliance, and proper asset handling. +Ensures optimized output with tree-shaking, automatic TypeScript declaration generation via `@glint/ember-tsc` (when using `--typescript`), V2 package compliance, and proper asset handling. ### TypeScript and Glint Integration @@ -329,6 +341,7 @@ import * as QUnit from 'qunit'; import { setApplication } from '@ember/test-helpers'; import { setup } from 'qunit-dom'; import { start as qunitStart, setupEmberOnerrorValidation } from 'ember-qunit'; +import { setTesting } from '@embroider/macros'; class Router extends EmberRouter { location = 'none'; @@ -337,7 +350,7 @@ class Router extends EmberRouter { class TestApp extends EmberApp { modules = { - './router': { default: Router }, + './router': Router, // add any custom services here // import.meta.glob('./services/*', { eager: true }), }; @@ -346,6 +359,7 @@ class TestApp extends EmberApp { Router.map(function () {}); export function start() { + setTesting(true); setApplication( TestApp.create({ autoboot: false, @@ -415,6 +429,13 @@ module.exports = async function (defaults) { }), }; +const compatDeps = { + '@embroider/compat': '^4.0.3', + 'ember-cli': '^5.12.0', + 'ember-auto-import': '^2.10.0', + '@ember/optional-features': '^2.2.0', +}; + export default { scenarios: [ { @@ -430,12 +451,56 @@ export default { }, files: compatFiles, }, - // Additional scenarios for 5.12, 6.4, latest, beta, alpha + { + name: 'ember-lts-5.12', + npm: { + devDependencies: { + 'ember-source': '~5.12.0', + ...compatDeps, + }, + }, + env: { + ENABLE_COMPAT_BUILD: true, + }, + files: compatFiles, + }, + { + name: 'ember-lts-6.4', + npm: { + devDependencies: { + 'ember-source': 'npm:ember-source@~6.4.0', + }, + }, + }, + { + name: 'ember-latest', + npm: { + devDependencies: { + 'ember-source': 'npm:ember-source@latest', + }, + }, + }, + { + name: 'ember-beta', + npm: { + devDependencies: { + 'ember-source': 'npm:ember-source@beta', + }, + }, + }, + { + name: 'ember-alpha', + npm: { + devDependencies: { + 'ember-source': 'npm:ember-source@alpha', + }, + }, + }, ], }; ``` -Tests against all supported Ember LTS versions, beta/alpha releases, with automatic compat builds for older versions and modern feature flags enabled. +Tests against all supported Ember LTS versions, beta/alpha releases, with automatic compat builds for older versions requiring `@embroider/compat` and modern feature flags enabled. Ember 6.4+ scenarios run without compat mode, demonstrating the path toward a fully native Vite build. ### Build System Architecture @@ -456,14 +521,17 @@ The blueprint maintains separate Babel configurations for development and produc Like the `tsconfig.json`, this is the configuration used by editors and tests. ```javascript +/** + * This babel.config is not used for publishing. + * It's only for the local editing experience + * (and linting) + */ const { buildMacros } = require('@embroider/macros/babel'); const { babelCompatSupport, templateCompatSupport } = require('@embroider/compat/babel'); -/** - * In testing, we need to evaluate @embroider/macros - * in the publish config we omit this, because it's the consuming app's responsibility to evaluate the macros. - */ const macros = buildMacros(); + +// For scenario testing const isCompat = Boolean(process.env.ENABLE_COMPAT_BUILD); module.exports = { @@ -493,10 +561,12 @@ module.exports = { **Production Babel (`babel.publish.config.cjs`)**: -> [!IMPORTANT] -> @embroider/macros is deliberately omitted from this config, because it's the consuming app's responsibility to evaluate macros. - ```javascript +/** + * This babel.config is only used for publishing. + * + * For local dev experience, see the babel.config + */ module.exports = { plugins: [ ['@babel/plugin-transform-typescript', { @@ -530,7 +600,7 @@ The test architecture in this addon blueprint serves as a prototype for how we e **Vite-First Architecture**: The addon's test setup demonstrates a minimal Ember application running entirely on Vite without any webpack or ember-cli-build.js complexity. This same pattern would be ideal for v2 apps - direct Vite integration with Ember resolver and routing eliminates the need for complex build pipeline abstractions. -**Minimal Application Bootstrap**: The `tests/test-helper.js` creates a bare-bones Ember app using just `EmberApp`, `Resolver`, and `EmberRouter`. This approach eliminates the traditional ember-cli build pipeline and shows how future apps could bootstrap with much less ceremony. The pattern of directly importing and configuring Ember's core classes provides a blueprint for simpler app initialization. +**Minimal Application Bootstrap**: The `tests/test-helper.ts` creates a bare-bones Ember app using just `EmberApp` (from `ember-strict-application-resolver`) and `EmberRouter`. This approach eliminates the traditional ember-cli build pipeline and shows how future apps could bootstrap with much less ceremony. The pattern of directly importing and configuring Ember's core classes provides a blueprint for simpler app initialization. **Modern Module Resolution**: The test setup uses ES modules and modern imports throughout, avoiding the complex AMD/requirejs patterns of traditional Ember apps. Future v2 apps should follow this same module pattern, using standard `import` statements and module resolution rather than the custom loader.js system. @@ -651,8 +721,9 @@ Automatically builds and pushes compiled assets to a `dist` branch for Git-based The blueprint uses modern ESLint flat configuration (`eslint.config.mjs`): ```javascript -import babelParser from '@babel/eslint-parser'; +import babelParser from '@babel/eslint-parser/experimental-worker'; import js from '@eslint/js'; +import { defineConfig, globalIgnores } from 'eslint/config'; import prettier from 'eslint-config-prettier'; import ember from 'eslint-plugin-ember/recommended'; import importPlugin from 'eslint-plugin-import'; @@ -660,14 +731,34 @@ import n from 'eslint-plugin-n'; import globals from 'globals'; import ts from 'typescript-eslint'; -const config = [ +const esmParserOptions = { + ecmaFeatures: { modules: true }, + ecmaVersion: 'latest', +}; + +const tsParserOptions = { + projectService: true, + tsconfigRootDir: import.meta.dirname, +}; + +export default defineConfig([ + globalIgnores(['dist/', 'dist-*/', 'declarations/', 'coverage/', '!**/.*']), js.configs.recommended, prettier, ember.configs.base, ember.configs.gjs, ember.configs.gts, - - // File-specific configurations for different contexts + { + linterOptions: { + reportUnusedDisableDirectives: 'error', + }, + }, + { + files: ['**/*.js'], + languageOptions: { + parser: babelParser, + }, + }, { files: ['**/*.{js,gjs}'], languageOptions: { @@ -680,21 +771,31 @@ const config = [ languageOptions: { parser: ember.parser, parserOptions: tsParserOptions, + globals: { ...globals.browser }, }, - extends: [...ts.configs.recommendedTypeChecked, ember.configs.gts], + extends: [ + ...ts.configs.recommendedTypeChecked, + { ...ts.configs.eslintRecommended, files: undefined }, + ember.configs.gts, + ], }, - // ... additional configurations -]; - -export default ts.config(...config); + { + files: ['src/**/*'], + plugins: { import: importPlugin }, + rules: { + 'import/extensions': ['error', 'always', { ignorePackages: true }], + }, + }, + // ... additional CJS/ESM node file configurations +]); ``` -This uses ESLint's new flat configuration format (which is faster and clearer), includes full TypeScript support with type-aware linting, has comprehensive Ember-specific rules including .gjs/.gts support, uses different rules for different contexts (browser vs. Node.js), and validates your imports to make sure you're using relative paths correctly. +This uses ESLint's flat configuration format with `defineConfig` and `globalIgnores` from `eslint/config`, includes full TypeScript support with type-aware linting via `projectService`, has comprehensive Ember-specific rules including .gjs/.gts support, uses different rules for different contexts (browser vs. Node.js), and validates your imports to make sure you're using relative paths correctly with proper extensions in `src/`. #### Template Linting ```javascript -// .template-lintrc.cjs -module.exports = { +// .template-lintrc.mjs +export default { extends: 'recommended', checkHbsTemplateLiterals: false, }; @@ -704,8 +805,8 @@ The template linting configuration uses the recommended rule set but disables ch #### Prettier Configuration ```javascript -// .prettierrc.cjs -module.exports = { +// .prettierrc.mjs +export default { plugins: ['prettier-plugin-ember-template-tag'], overrides: [ { @@ -721,19 +822,6 @@ module.exports = { This configuration ensures consistent formatting across all JavaScript/TypeScript files while properly handling .gjs/.gts template tags. -### Package Management Configuration - -#### NPM Configuration (`.npmrc`) -``` -# we don't want addons to be bad citizens of the ecosystem -auto-install-peers=false - -# we want true isolation, if a dependency is not declared, we want an error -resolve-peers-from-workspace-root=false -``` - -The NPM configuration enforces explicit dependency declaration by disabling auto-install-peers and prevents workspace root resolution to ensure complete package isolation. These constraints guarantee proper dependency boundaries and maintain NPM ecosystem compatibility standards. - ### V1 Compatibility Strategy #### Compatibility Shim (`addon-main.cjs`) @@ -865,12 +953,12 @@ addon.publicEntrypoints(['**/*.js', 'index.js', 'template-registry.js']) - **publicEntrypoints** - Defines what files consumers can import (matches `package.json#exports`) - **appReexports** - Automatically makes components/helpers/etc. available in consuming apps -- **declarations** - Generates TypeScript declarations alongside JavaScript +- **declarations** - Generates TypeScript declarations alongside JavaScript via `@glint/ember-tsc` - **keepAssets** - Preserves CSS and other static files The build process: 1. Transpiles TypeScript/templates using Babel (publish config) -2. Generates TypeScript declarations using `ember-tsc` +2. Generates TypeScript declarations using `@glint/ember-tsc` 3. Creates optimized JavaScript in `dist/` matching your `src/` structure 4. Preserves CSS and other assets From 508a003e338e2928c287156296ba83f2db7bab19 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 16 Mar 2026 21:33:11 -0400 Subject: [PATCH 17/18] Tighten verbiage --- text/0985-v2-addon-by-default.md | 595 +++++++++---------------------- 1 file changed, 163 insertions(+), 432 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 108bb9fe21..82f1466be9 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -1,6 +1,6 @@ --- stage: accepted -start-date: 2023-11-02T16:05:11.000Z +start-date: 2023-11-02T16:05:11.000Z release-date: # In format YYYY-MM-DDT00:00:00.000Z release-versions: teams: # delete teams that aren't relevant @@ -10,120 +10,94 @@ teams: # delete teams that aren't relevant prs: accepted: https://github.com/emberjs/rfcs/pull/985 project-link: -suite: +suite: --- # Change the default addon blueprint to `@ember/addon-blueprint` ## Summary -This RFC proposes making [`@ember/addon-blueprint`](https://github.com/ember-cli/ember-addon-blueprint) the default blueprint for new Ember addons, replacing the current v1 and v2 blueprints. The existing blueprints present significant technical challenges that impact developer productivity and ecosystem compatibility. The new blueprint addresses these issues through modern tooling, streamlined architecture, and comprehensive TypeScript integration based on extensive community feedback and production usage patterns. +Make [`@ember/addon-blueprint`](https://github.com/ember-cli/ember-addon-blueprint) the default blueprint for new Ember addons, replacing the current v1 and v2 blueprints. ## Motivation -The current default blueprints have architectural limitations that affect addon development: +The current default blueprints have real problems: -**V1 addons** require rebuilding by every consuming application, creating performance overhead and compatibility constraints. This approach differs from modern JavaScript packaging principles and creates coupling between addon build processes and consuming applications. +**V1 addons** get rebuilt by every consuming app, which is slow and couples addon builds to app builds. -**The original V2 blueprint** addressed the build performance issues but introduced additional complexity through mandatory monorepo structure and required manual configuration. Additionally, it lacked integration with modern Ember patterns, TypeScript tooling, and development workflows that have become common in the ecosystem. +**The original V2 blueprint** fixed the build problem but required a monorepo and manual configuration. It also lacked TypeScript/Glint integration and modern Ember patterns. -The `@ember/addon-blueprint` addresses these technical problems through several architectural improvements: it defaults to single-package structure (appropriate for most addon use cases), integrates Glint for template type safety, implements modern Ember patterns throughout, and provides tooling configuration. This blueprint incorporates lessons learned from production addon development and addresses the issues identified by the community. +`@ember/addon-blueprint` fixes these by defaulting to a single-package structure, integrating Glint for template type safety, using native classes and strict mode throughout, and providing sensible tooling defaults out of the box. ## Detailed design ### Definitions -**V2 Addon**: An addon published in the V2 package format as defined by RFC 507, with `ember-addon.version: 2` in package.json. +**V2 Addon**: An addon with `ember-addon.version: 2` in package.json, as defined by RFC 507. -**Glint-enabled**: A package that includes TypeScript declaration files for templates and components, leveraging Volar-based language server plugins for superior IDE integration. +**Single-package addon**: An addon with its test suite in the same package, rather than a separate test app in a monorepo. -**Single-package addon**: An addon that contains its own test suite within the same package, as opposed to a monorepo structure with separate test applications. +**Blueprint**: A code generation template used by ember-cli to scaffold projects. -**Blueprint**: A code generation template used by ember-cli to scaffold new projects or files. - -**Vite-first development**: A development workflow that uses Vite as the primary build tool for both development and testing, eliminating traditional webpack-based tooling. - -### Blueprint Structure and Tooling Choices - -The blueprint generates a modern project structure: +### Blueprint Structure ``` my-addon/ ├── .github/ │ └── workflows/ -│ ├── ci.yml # Comprehensive CI/CD pipeline -│ └── push-dist.yml # Automated dist branch publishing +│ ├── ci.yml # CI pipeline +│ └── push-dist.yml # Publish to dist branch ├── src/ # Source code (published) │ ├── index.js # Main entry point │ └── template-registry.ts # Glint type registry ├── tests/ # Test files │ ├── index.html # Test runner page -│ └── test-helper.ts # Test setup and configuration -├── demo-app/ # Demo application for showcasing addon +│ └── test-helper.ts # Test setup +├── demo-app/ # Demo application │ ├── app.gts # Demo app entry point -│ ├── styles.css # Demo app styles -│ └── templates/ # Demo app templates -├── unpublished-development-types/ # Development-only types -│ └── index.d.ts # Local development type augmentations +│ ├── styles.css +│ └── templates/ +├── unpublished-development-types/ # Dev-only types +│ └── index.d.ts ├── config/ │ └── ember-cli-update.json # Blueprint version tracking ├── dist/ # Built output (gitignored, published) -├── declarations/ # TypeScript declarations (gitignored, published) -├── package.json # Package configuration with modern exports -├── index.html # Demo application entry page -├── rollup.config.mjs # Production build configuration -├── vite.config.mjs # Development build configuration -├── tsconfig.json # Development TypeScript config -├── tsconfig.publish.json # Publishing TypeScript config -├── babel.config.cjs # Development Babel config -├── babel.publish.config.cjs # Publishing Babel config -├── eslint.config.mjs # Modern ESLint flat config -├── .prettierrc.mjs # Code formatting configuration -├── .prettierignore # Prettier ignore patterns -├── .template-lintrc.mjs # Template linting rules -├── testem.cjs # Test runner configuration -├── .try.mjs # Ember version compatibility scenarios -├── .editorconfig # Editor consistency rules -├── .env.development # Development environment variables -├── .gitignore # Git ignore patterns -├── README.md # Documentation template -├── CONTRIBUTING.md # Contribution guidelines -├── LICENSE.md # MIT license -└── addon-main.cjs # V1 compatibility shim +├── declarations/ # TS declarations (gitignored, published) +├── package.json +├── index.html # Demo app entry page +├── rollup.config.mjs # Production build +├── vite.config.mjs # Dev build + tests +├── tsconfig.json # Dev TypeScript config +├── tsconfig.publish.json # Publish TypeScript config +├── babel.config.cjs # Dev Babel config +├── babel.publish.config.cjs # Publish Babel config +├── eslint.config.mjs # ESLint flat config +├── .prettierrc.mjs # Prettier config +├── .prettierignore +├── .template-lintrc.mjs # Template linting +├── testem.cjs # Test runner config +├── .try.mjs # Ember version scenarios +├── .editorconfig +├── .env.development +├── .gitignore +├── README.md +├── CONTRIBUTING.md +├── LICENSE.md +└── addon-main.cjs # V1 compat shim ``` -The blueprint architecture addresses several specific technical requirements: - -**Single-package structure by default** - Analysis of existing addons shows that monorepo complexity is not needed for most use cases, while adding maintenance overhead. The single-package approach simplifies the development workflow without restricting advanced users who require monorepo setups. - -**Structured source organization** - The `src/` directory contains publishable code, `tests/` contains the test suite, and `unpublished-development-types/` provides development-only type augmentations that don't pollute the published package. This separation ensures clean package boundaries and prevents accidental inclusion of development-only code. - -**Glint integration** - Template type safety is implemented through Glint with Volar-based TypeScript server plugins, providing improved IDE integration compared to previous approaches. This addresses template type validation in Ember applications. - -**Modern Ember patterns throughout** - The blueprint uses native classes, decorators, and strict mode, eliminating legacy object model patterns that create maintenance burden and limit performance optimizations. - -**Dual build system approach** - Vite provides improved development experience through fast rebuilds and hot module replacement, while Rollup generates optimized production output with effective tree-shaking and bundle optimization. Tests execute entirely through Vite, reducing the complexity associated with `ember-auto-import` and webpack-based tooling. - -#### Implications for Future App Blueprint Architecture +**Why single-package by default**: Most addons don't need a monorepo. Single-package is simpler to maintain and publish. Advanced users can still set up monorepos. -The testing architecture implemented in this addon blueprint serves as a technical proof-of-concept for future compat-less Ember application blueprints. +**Why dual build systems**: Vite gives fast dev rebuilds and HMR. Rollup gives optimized, tree-shaken production output. Tests run entirely through Vite, no webpack or `ember-auto-import` needed. -The test environment demonstrates a minimal Ember application running entirely on Vite without webpack or ember-cli-build.js dependencies. This architecture eliminates complex build pipeline abstractions while maintaining full framework compatibility, suggesting viability for production application development. - -The `tests/test-helper.ts` implementation creates Ember applications using `EmberApp` (from `ember-strict-application-resolver`) and `EmberRouter` - a significantly simplified bootstrap process compared to traditional ember-cli approaches. This pattern demonstrates how future applications could reduce initialization complexity while preserving framework functionality. - -The architecture utilizes ES modules and standard import semantics throughout, replacing the AMD/requirejs patterns prevalent in traditional Ember applications. The test discovery mechanism employs Vite's `import.meta.glob`, which could extend to application development for route and component discovery through standard module resolution rather than custom file-system scanning. - -The successful operation of this testing approach demonstrates that Ember applications can work effectively with modern build tools, providing a foundation for simplified application blueprints in future releases. +**Why Glint**: Template type safety via Volar-based TS server plugins. Works for both TypeScript and JavaScript projects. ### Package Configuration > [!NOTE] -> This is overview, and exact contents can change as the needs of the community and capabilities of the ecosystem change. We're always striving to simplify the setup, while enabling the most power for the 80% use case, while not restricting anyone who wants to do more complex things. +> This is an overview. Exact contents can change as needs evolve. We aim to cover the 80% use case without restricting anyone who needs more. -#### package.json Structure - -The generated `package.json` follows modern NPM conventions with several key design decisions: +#### package.json ```json { @@ -166,17 +140,21 @@ The generated `package.json` follows modern NPM conventions with several key des } ``` -The package configuration implements several important architectural patterns: +`exports` maps consumer-facing imports to the right files (declarations for types, dist for runtime). `imports` with `#src/*` gives tests and the demo app a clean way to import from source without rebuilding -- but can't be used in `src/` itself because Rollup won't transform those imports. + +The `files` array includes `src` alongside `dist` and `declarations` so consumers get source-level go-to-definition in their editors. -The exports map utilizes conditional exports to ensure proper resolution of both TypeScript declarations and JavaScript modules across all environments. Import maps (`#src/*`) enable clean internal imports for test and demo application code, though these cannot be used within `src/` files due to Rollup's transformation limitations during the build process. +#### Development vs. Production Configs -The published package includes essential runtime files (`dist`, `declarations`, `addon-main.cjs`) along with `src` for source-level debugging and editor go-to-definition support, while the `ember-addon.version: 2` declaration ensures proper V2 addon recognition by tooling and consuming applications. +The blueprint splits config into dev and publish variants. This is the key architectural pattern throughout: -#### Development vs. Production Configuration Split +- **Dev configs** (`babel.config.cjs`, `tsconfig.json`, `vite.config.mjs`) include macro evaluation, compat transforms, Vite/Embroider types, and test infrastructure +- **Publish configs** (`babel.publish.config.cjs`, `tsconfig.publish.json`, `rollup.config.mjs`) use minimal transforms and omit dev-only APIs -The blueprint maintains separate configurations for development and production builds: +This split matters because macros should be evaluated by the consuming app (not baked in at publish time), and `lint:types` against the publish tsconfig catches accidental usage of Vite or Embroider internals in published code. + +#### Vite Config (`vite.config.mjs`) -**Development Configuration (`vite.config.mjs`)**: ```javascript import { defineConfig } from 'vite'; import { extensions, ember, classicEmberSupport } from '@embroider/vite'; @@ -204,9 +182,8 @@ export default defineConfig({ }); ``` -Enables fast development builds with HMR, compatibility mode for older Ember versions, and direct test execution through Vite. +#### Rollup Config (`rollup.config.mjs`) -**Production Configuration (`rollup.config.mjs`)**: ```javascript import { babel } from '@rollup/plugin-babel'; import { Addon } from '@embroider/addon-dev/rollup'; @@ -251,17 +228,13 @@ export default { }; ``` -Ensures optimized output with tree-shaking, automatic TypeScript declaration generation via `@glint/ember-tsc` (when using `--typescript`), V2 package compliance, and proper asset handling. - -### TypeScript and Glint Integration +### TypeScript and Glint -TypeScript is opt-in via `--typescript` when generating a new addon. +TypeScript is opt-in via `--typescript`. -#### Dual TypeScript Configuration Strategy +The blueprint uses two tsconfigs: -The blueprint employs separate TypeScript configurations for development and publishing: - -**Development Configuration (`tsconfig.json`)**: +**`tsconfig.json`** (dev) -- includes `src/`, `tests/`, `demo-app/`, and `unpublished-development-types/`. Has Vite and Embroider types so your editor works. ```json { @@ -284,9 +257,7 @@ The blueprint employs separate TypeScript configurations for development and pub } ``` -**Publishing Configuration (`tsconfig.publish.json`)**: - -Notably, vite and embroider types are not present. `lint:types` uses this config to help catch accidental usage of vite or embroider features in code that would be published to NPM. +**`tsconfig.publish.json`** -- only `src/` and dev types. No Vite or Embroider types, so `lint:types` catches accidental usage of dev-only APIs in published code. ```json { @@ -301,39 +272,13 @@ Notably, vite and embroider types are not present. `lint:types` uses this config } ``` -The development config includes test files and development types so your IDE works properly, while the publishing config generates clean declaration files with the right module structure. +The Glint template registry (`src/template-registry.ts`) lets apps using loose mode (hbs files) consume your types. Not needed if your library only targets strict mode consumers. -#### Glint Template Type Safety +### Testing -The blueprint includes comprehensive Glint setup for template type safety. However, note that the template-registry is not needed at all if a library does not want to support loose mode (hbs files). +Tests run entirely on Vite -- no ember-cli build pipeline, no webpack. -**Template Registry (`src/template-registry.ts`)**: -```typescript -// Easily allow apps, which are not yet using strict mode templates, to consume your Glint types, by importing this file. -// Add all your components, helpers and modifiers to the template registry here, so apps don't have to do this. -// See https://typed-ember.gitbook.io/glint/environments/ember/authoring-addons - -// import type MyComponent from './components/my-component'; - -// Uncomment this once entries have been added! 👇 -// export default interface Registry { -// MyComponent: typeof MyComponent -// } -``` - -**Development Type Augmentations (`unpublished-development-types/index.d.ts`)**: - -This file provides a space for development-only type augmentations that won't be included in the published package. It's initially empty but available for local development type definitions. - -Provides consumer type safety, development-only types that don't pollute the published package, and full template import support. - -### Testing Architecture - -#### Vite-Based Test Execution - -The blueprint implements a revolutionary testing approach that runs entirely on Vite: - -**Test Helper (`tests/test-helper.ts`)**: +**`tests/test-helper.ts`**: ```typescript import EmberApp from 'ember-strict-application-resolver'; import EmberRouter from '@ember/routing/router'; @@ -372,7 +317,7 @@ export function start() { } ``` -**Test Runner (`tests/index.html`)**: +**`tests/index.html`**: ```html @@ -404,13 +349,12 @@ export function start() { ``` -This architecture eliminates the ember-cli build pipeline entirely by creating Ember applications directly through public APIs, utilizing `import.meta.glob` for test discovery, and avoiding webpack dependencies completely. This approach reduces complexity while maintaining full Ember compatibility. +The test setup creates a minimal Ember app using `ember-strict-application-resolver` and `EmberRouter` directly -- no ember-cli abstraction layer. Test discovery uses `import.meta.glob`. This is also a proof-of-concept for how future compat-less Ember apps could work: ES modules throughout, Vite-native builds, direct framework API usage. -#### Cross-Version Compatibility Testing +#### Cross-Version Testing -The blueprint includes comprehensive compatibility testing via ember-try scenarios: +The `.try.mjs` config defines scenarios for testing against multiple Ember versions: -**Try Configuration (`.try.mjs`)**: ```javascript const compatFiles = { 'ember-cli-build.cjs': `const EmberApp = require('ember-cli/lib/broccoli/ember-app'); @@ -500,25 +444,11 @@ export default { }; ``` -Tests against all supported Ember LTS versions, beta/alpha releases, with automatic compat builds for older versions requiring `@embroider/compat` and modern feature flags enabled. Ember 6.4+ scenarios run without compat mode, demonstrating the path toward a fully native Vite build. - -### Build System Architecture - -#### Dual Build Approach Rationale - -Uses two build systems: - -**Vite for development**: Instant startup with native ES modules, HMR, modern debugging tools, and integrated test execution. - -**Rollup for production**: Optimized output with tree-shaking, bundle splitting, asset processing, and TypeScript declaration generation. - -#### Babel Configuration Strategy - -The blueprint maintains separate Babel configurations for development and production: +Older Ember versions (5.x) need `@embroider/compat` and an `ember-cli-build.cjs` shim. Ember 6.4+ runs natively without compat mode. -**Development Babel (`babel.config.cjs`)**: +### Babel Configs -Like the `tsconfig.json`, this is the configuration used by editors and tests. +**Dev (`babel.config.cjs`)** -- used by your editor and tests. Includes macro evaluation and compat transforms: ```javascript /** @@ -559,7 +489,7 @@ module.exports = { }; ``` -**Production Babel (`babel.publish.config.cjs`)**: +**Publish (`babel.publish.config.cjs`)** -- minimal transforms. Macros are deliberately omitted; the consuming app evaluates them: ```javascript /** @@ -590,135 +520,9 @@ module.exports = { }; ``` -The configuration separation addresses distinct requirements: development configurations include macro evaluation and compatibility transforms for comprehensive testing capabilities, while production configurations use minimal transforms and delegate macro processing to consuming applications, ensuring proper build-time optimization boundaries. - -### Influence on Future V2 App Blueprint - -The test architecture in this addon blueprint serves as a prototype for how we envision a future compat-less `@ember/app-blueprint` should work: - -#### Architectural Principles Demonstrated - -**Vite-First Architecture**: The addon's test setup demonstrates a minimal Ember application running entirely on Vite without any webpack or ember-cli-build.js complexity. This same pattern would be ideal for v2 apps - direct Vite integration with Ember resolver and routing eliminates the need for complex build pipeline abstractions. - -**Minimal Application Bootstrap**: The `tests/test-helper.ts` creates a bare-bones Ember app using just `EmberApp` (from `ember-strict-application-resolver`) and `EmberRouter`. This approach eliminates the traditional ember-cli build pipeline and shows how future apps could bootstrap with much less ceremony. The pattern of directly importing and configuring Ember's core classes provides a blueprint for simpler app initialization. - -**Modern Module Resolution**: The test setup uses ES modules and modern imports throughout, avoiding the complex AMD/requirejs patterns of traditional Ember apps. Future v2 apps should follow this same module pattern, using standard `import` statements and module resolution rather than the custom loader.js system. - -**Direct Framework Integration**: Rather than going through ember-cli's abstraction layer, the addon tests interact directly with Ember's APIs. This demonstrates the cleaner architectural approach we want for v2 apps - direct framework usage without heavy tooling intermediation. The test setup shows how to create and configure an Ember application using only public APIs. - -**Zero-Config Test Execution**: The test runner uses Vite's `import.meta.glob` for automatic test discovery, eliminating the need for complex test configuration. This pattern could extend to app development, where route and component discovery happens through standard module resolution rather than custom file-system scanning. - -The success of this addon testing approach validates that Ember applications can run efficiently with modern build tools, paving the way for a simpler, faster app blueprint that matches this architecture. - -### CI/CD Setup - -The blueprint includes a comprehensive GitHub Actions workflow with several jobs, each serving a specific purpose: - -#### Lint Job Analysis -```yaml -lint: - name: "Lints" - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - - uses: actions/checkout@v4 - - uses: pnpm/action-setup@v4 # if using pnpm - - uses: actions/setup-node@v4 - with: - node-version: 22 - cache: pnpm - - name: Install Dependencies - run: pnpm install --frozen-lockfile - - name: Lint - run: pnpm lint -``` - -Runs ESLint, Prettier, and template-lint with caching for consistent code quality across contributors. - -#### Test Job with Matrix Generation -```yaml -test: - name: "Tests" - runs-on: ubuntu-latest - timeout-minutes: 10 - outputs: - matrix: ${{ steps.set-matrix.outputs.matrix }} - steps: - # ... setup steps - - name: Run Tests - run: pnpm test - - id: set-matrix - run: | - echo "matrix=$(pnpm -s dlx @embroider/try list)" >> $GITHUB_OUTPUT -``` - -Runs the test suite and generates the scenario matrix for cross-version compatibility testing. - -#### Floating Dependencies Job -```yaml -floating: - name: "Floating Dependencies" - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - # ... setup steps - - name: Install Dependencies - run: pnpm install --no-lockfile - - name: Run Tests - run: pnpm test -``` - -Tests against latest dependency versions without lockfile constraints to catch compatibility issues early. - -#### Try Scenarios Matrix Job -```yaml -try-scenarios: - name: ${{ matrix.name }} - runs-on: ubuntu-latest - needs: "test" - timeout-minutes: 10 - strategy: - fail-fast: false - matrix: ${{fromJson(needs.test.outputs.matrix)}} - steps: - # We setup the scenario before running `pnpm install`, so this is fast. - # No double-install like there is with ember-try. - # This tool can also be used with non-ember projects. - - name: Apply Scenario - run: pnpm dlx @embroider/try apply ${{ matrix.name }} - - name: Install Dependencies - run: pnpm install --no-lockfile - - name: Run Tests - run: pnpm test - env: ${{ matrix.env }} -``` - -Uses `@embroider/try` to test against multiple Ember versions (LTS, stable, beta, alpha) ensuring compatibility across the supported ecosystem. - -#### Push Dist Workflow - -```yaml -name: Push dist -on: - push: - branches: [main, master] -jobs: - push-dist: - name: Push dist - permissions: - contents: write - runs-on: ubuntu-latest - timeout-minutes: 10 - steps: - # ... build and publish to dist branch -``` - -Automatically builds and pushes compiled assets to a `dist` branch for Git-based consumption during development. - -### Linting and Code Quality Configuration +### Linting -#### ESLint Configuration -The blueprint uses modern ESLint flat configuration (`eslint.config.mjs`): +**ESLint** uses flat config (`eslint.config.mjs`) with `defineConfig` and `globalIgnores`: ```javascript import babelParser from '@babel/eslint-parser/experimental-worker'; @@ -790,22 +594,18 @@ export default defineConfig([ ]); ``` -This uses ESLint's flat configuration format with `defineConfig` and `globalIgnores` from `eslint/config`, includes full TypeScript support with type-aware linting via `projectService`, has comprehensive Ember-specific rules including .gjs/.gts support, uses different rules for different contexts (browser vs. Node.js), and validates your imports to make sure you're using relative paths correctly with proper extensions in `src/`. +Type-aware linting via `projectService`, Ember .gjs/.gts support, and enforced file extensions in `src/` imports. -#### Template Linting +**Template linting** (`.template-lintrc.mjs`): ```javascript -// .template-lintrc.mjs export default { extends: 'recommended', checkHbsTemplateLiterals: false, }; ``` -The template linting configuration uses the recommended rule set but disables checking of template literals, focusing on .hbs files and template imports. - -#### Prettier Configuration +**Prettier** (`.prettierrc.mjs`): ```javascript -// .prettierrc.mjs export default { plugins: ['prettier-plugin-ember-template-tag'], overrides: [ @@ -820,55 +620,76 @@ export default { }; ``` -This configuration ensures consistent formatting across all JavaScript/TypeScript files while properly handling .gjs/.gts template tags. +### CI/CD + +The blueprint generates GitHub Actions workflows (shown here with pnpm; npm/yarn variants are also generated): + +**CI workflow** (`ci.yml`): +- **Lint** -- runs `pnpm lint` (ESLint, Prettier, template-lint, type checking) +- **Test** -- runs `pnpm test`, then outputs a scenario matrix via `@embroider/try list` +- **Floating deps** -- installs without lockfile, runs tests to catch compatibility issues early +- **Try scenarios** -- matrix job that applies each `.try.mjs` scenario and runs tests against it + +```yaml +try-scenarios: + name: ${{ matrix.name }} + runs-on: ubuntu-latest + needs: "test" + timeout-minutes: 10 + strategy: + fail-fast: false + matrix: ${{fromJson(needs.test.outputs.matrix)}} + steps: + - name: Apply Scenario + run: pnpm dlx @embroider/try apply ${{ matrix.name }} + - name: Install Dependencies + run: pnpm install --no-lockfile + - name: Run Tests + run: pnpm test + env: ${{ matrix.env }} +``` + +**Push dist workflow** (`push-dist.yml`) -- on push to main, builds the addon and pushes compiled assets to a `dist` branch for git-based consumption. -### V1 Compatibility Strategy +### V1 Compatibility -#### Compatibility Shim (`addon-main.cjs`) ```javascript +// addon-main.cjs 'use strict'; const { addonV1Shim } = require('@embroider/addon-shim'); module.exports = addonV1Shim(__dirname); ``` -This minimal shim enables V2 addons to work in V1 (classic ember-cli) environments by providing the traditional addon API that ember-cli expects. - -**How It Works:** -- The shim translates V2 package metadata into V1 build hooks -- Static assets and app reexports are handled through traditional treeFor* methods -- The V2 package's dist/ output is mapped to the appropriate V1 trees - -### Technical Decision Analysis +This shim translates V2 package metadata into V1 build hooks so the addon works in classic ember-cli apps. -#### Single Package Architecture -Analysis of the Ember addon ecosystem shows that monorepo structures are used by fewer than 20% of published addons, while adding complexity overhead for all users. Single packages provide better maintainability through reduced configuration surface area, simplified dependency management, and streamlined publishing workflows. Advanced users requiring monorepo architectures can utilize the blueprint as a foundation while implementing custom test harnesses. +### Influence on Future App Blueprint -#### Glint with Volar Integration -Template type safety addresses an important gap in the Ember development experience, helping catch errors at compile time that would otherwise appear at runtime. Glint provides this through template analysis, while Volar-based TypeScript server plugins provide IDE integration without custom language server implementations. This approach delivers type safety benefits to both TypeScript and JavaScript projects through type inference and validation. +The test setup here is a proof-of-concept for future compat-less Ember apps: -#### Dual Build System Strategy -Performance testing shows that Vite provides significantly faster development rebuilds compared to traditional webpack-based approaches, while Rollup generates well-optimized production output through effective tree-shaking algorithms and bundle optimization. The dual approach reduces the performance vs. output quality trade-offs present in single build system architectures, allowing optimization of each phase of the development lifecycle. +- A minimal Ember app running on Vite with no webpack or `ember-cli-build.js` +- Bootstrap with just `EmberApp` and `EmberRouter` -- no complex build pipeline +- ES modules and `import.meta.glob` for module discovery instead of AMD/requirejs +- Direct framework API usage instead of ember-cli abstractions -### Migration and Compatibility +This validates that Ember apps can run well on modern build tools, pointing toward simpler app blueprints in the future. -Existing addons are unaffected. Authors can opt into the new blueprint with `npx ember-cli@latest addon --blueprint @ember/addon-blueprint`. - -**Migration:** New addons use the new blueprint immediately. Existing addons can migrate by generating a new project and copying relevant files. +### Migration +Existing addons are unaffected. New addons get the new blueprint automatically. Existing addons can migrate by generating a new project and copying relevant files, or using `npx ember-cli@latest addon --blueprint @ember/addon-blueprint`. ## How we teach this -### Core Documentation Updates +### Documentation Updates -- The Ember Guides and CLI documentation will be updated to reference the new blueprint. -- The blueprint README includes detailed instructions for customization, publishing, and supporting multiple Ember versions. -- Migration guides will be provided for authors of v1 and v2 addons. +- Update the Ember Guides and CLI docs to reference the new blueprint +- The blueprint README covers customization, publishing, and multi-version support +- Provide migration guides for v1 and v2 addon authors -### Library Development Concepts +### Key Concepts for Addon Authors -#### Understanding `package.json#exports` and `package.json#imports` +#### `exports` and `imports` -**`package.json#exports`** defines the public API of your addon - what consuming applications can import: +`exports` defines your addon's public API: ```json { @@ -879,161 +700,71 @@ Existing addons are unaffected. Authors can opt into the new blueprint with `npx }, "./*.css": "./dist/*.css", "./*": { - "types": "./declarations/*.d.ts", + "types": "./declarations/*.d.ts", "default": "./dist/*.js" } } } ``` -- **"."** - The main entry point when someone imports your addon by name -- **"./*.css"** - Allows importing CSS files from your addon -- **"./*"** - Allows importing any other file from your dist directory -- **"types"** - Points to TypeScript declaration files for proper IDE support -- **"default"** - Points to the actual JavaScript implementation - -**`package.json#imports`** provides internal shortcuts for development: - -```json -{ - "imports": { - "#src/*": "./src/*" - } -} -``` - -- **#src/*** - Enables clean imports like `import { helper } from '#src/helpers/my-helper'` in tests -- **Tests and demo app only** - Can only be used in test files and demo app, NOT in `src/` files -- **Rollup limitation** - Rollup doesn't transform `#src/*` imports to relative paths during the build process -- **Development convenience** - Avoids relative paths in tests and eliminates need for constant rebuilding during test development +`imports` with `#src/*` lets tests and the demo app import from source without rebuilding. Can't be used in `src/` (Rollup won't transform these). Files in `src/` must use relative imports. -**Important**: Files in `src/` must use standard relative imports (`./`, `../`) or imports from `node_modules`. The `#src/*` imports are only for development convenience in test files. +#### Self-Imports -#### Self-Imports Limitation - -**Self-imports are not available during development**. Self-imports would resolve through `package.json#exports`, but during development, the `dist/` directory (referenced in exports) doesn't exist yet - development works directly with source files in `src/`. +Self-imports (e.g. `import { x } from 'my-addon/foo'`) don't work during development because they resolve through `exports` to `dist/`, which doesn't exist until you build. Use relative imports in `src/`: ```javascript -// ❌ This won't work in src/ files during development -import { myHelper } from 'my-addon/helpers/my-helper'; - -// ✅ Use relative imports instead -import { myHelper } from './helpers/my-helper'; +// In src/ files: +import { myHelper } from './helpers/my-helper'; // yes +import { myHelper } from 'my-addon/helpers/my-helper'; // no ``` -Self-imports only work after building and publishing your addon, when consuming applications import from the published `dist/` files. +#### Dev vs. Publish Configs -#### Development vs. Production Configuration Strategy +| Purpose | Dev | Publish | +|---------|-----|---------| +| Babel | `babel.config.cjs` (macros, compat) | `babel.publish.config.cjs` (minimal) | +| TypeScript | `tsconfig.json` (all files, Vite types) | `tsconfig.publish.json` (src only) | +| Build | `vite.config.mjs` (HMR, tests) | `rollup.config.mjs` (tree-shaking) | -The blueprint uses dual configurations to optimize both development experience and published output: +Macros are evaluated in dev for testing but left unevaluated in published output -- the consuming app handles them. The publish tsconfig omits Vite/Embroider types so `lint:types` catches accidental usage. -**Development Configuration (Local Development & Testing)**: -- `babel.config.cjs` - Includes macro evaluation and compatibility transforms -- `tsconfig.json` - Includes test files, demo app, and development types -- `vite.config.mjs` - Optimized for fast rebuilds with HMR +#### Monorepo Setup -**Production Configuration (Publishing)**: -- `babel.publish.config.cjs` - Minimal transforms, no macro evaluation (consuming app's responsibility) -- `tsconfig.publish.json` - Only source files, generates clean declarations -- `rollup.config.mjs` - Optimized output with tree-shaking and proper exports +The single-package default works for most addons. If you need a monorepo (complex integration testing, multiple related packages, full documentation app): -**Why This Separation Matters**: -- **Development**: Includes all necessary transforms and types for productive local development -- **Publishing**: Generates clean, minimal output that integrates well with consuming applications -- **Macro Handling**: Development evaluates macros for testing; production leaves them for the consuming app to handle -- **Type Safety**: Publishing config catches accidental usage of development-only APIs (Vite, Embroider internals) +1. Generate addon: `npx ember-cli@latest addon my-addon --blueprint @ember/addon-blueprint --skip-git` +2. Remove generated test infrastructure +3. Generate test app: `npx ember-cli@latest app test-app --blueprint @ember/app-blueprint` +4. Set up workspace tooling (pnpm/yarn workspaces) +5. Install addon in test app -#### Rollup Build Process +#### Publishing -The Rollup configuration transforms your `src/` directory into the `dist/` directory that gets published: +1. Write code in `src/`, tests with `#src/*` imports +2. `npm run build` runs Rollup with publish configs, producing `dist/` and `declarations/` +3. `npm publish` ships only `files` from package.json +4. Consumers import via `exports`, not internal paths -```javascript -addon.publicEntrypoints(['**/*.js', 'index.js', 'template-registry.js']) -``` - -- **publicEntrypoints** - Defines what files consumers can import (matches `package.json#exports`) -- **appReexports** - Automatically makes components/helpers/etc. available in consuming apps -- **declarations** - Generates TypeScript declarations alongside JavaScript via `@glint/ember-tsc` -- **keepAssets** - Preserves CSS and other static files - -The build process: -1. Transpiles TypeScript/templates using Babel (publish config) -2. Generates TypeScript declarations using `@glint/ember-tsc` -3. Creates optimized JavaScript in `dist/` matching your `src/` structure -4. Preserves CSS and other assets - -#### When to Use Monorepo Structure - -**Use the default single-package structure when**: -- Building a typical addon with components, helpers, or services -- Your addon doesn't need complex integration testing -- You want minimal setup and maintenance overhead -- Your tests can adequately cover addon functionality in isolation - -**Consider a monorepo structure when**: -- **Complex Integration Testing**: Your addon needs to be tested within a real application context (routing, complex data flows, etc.) -- **Multiple Related Packages**: You're building an addon ecosystem with multiple related packages -- **Advanced Testing Scenarios**: You need to test against multiple Ember app configurations or build setups -- **Documentation Apps**: You want a full application for showcasing your addon's capabilities - -**Setting up a Monorepo**: -1. Generate your addon: `npx ember-cli@latest addon my-addon --blueprint @ember/addon-blueprint --skip-git` -2. Remove the generated test infrastructure -3. Generate a test app: `npx ember-cli@latest app test-app --blueprint @ember/app-blueprint` -4. Configure your monorepo tool (pnpm workspaces, yarn workspaces, etc.) -5. Install your addon in the test app and write integration tests - -This approach provides the benefits of the modern addon blueprint while giving you a full application environment for comprehensive testing. - -#### Package Publishing Workflow - -**Development Workflow**: -1. Write code in `src/` -2. Write tests using `#src/*` imports for convenience -3. Run tests with `npm test` (uses development configs) -4. Use demo app for manual testing and documentation - -**Publishing Workflow**: -1. `npm run build` uses Rollup with production configs -2. Generates `dist/` and `declarations/` directories -3. `npm publish` only publishes files listed in `package.json#files` -4. Consumers import from your `package.json#exports`, not internal paths - -#### TypeScript Integration - -**When using `--typescript`**: -- All configurations include TypeScript support -- Glint provides template type checking -- Development includes comprehensive types (Vite, Embroider, etc.) -- Publishing generates clean declaration files for consumers -- `lint:types` script catches development-only API usage in published code - -**JavaScript-only projects**: -- All TypeScript-specific features are conditional -- Still benefits from modern build pipeline and tooling -- Can opt into TypeScript later with minimal changes - -### Key Resources +### Resources - [@ember/addon-blueprint README](https://github.com/emberjs/ember-addon-blueprint#readme) -- [Addon Author Guide](https://github.com/embroider-build/embroider/blob/main/docs/addon-author-guide.md) +- [Addon Author Guide](https://github.com/embroider-build/embroider/blob/main/docs/addon-author-guide.md) - [Porting Addons to V2](https://github.com/embroider-build/embroider/blob/main/docs/porting-addons-to-v2.md) -- [Node.js Package Exports Documentation](https://nodejs.org/api/packages.html#exports) +- [Node.js Package Exports](https://nodejs.org/api/packages.html#exports) - [Glint Documentation](https://typed-ember.gitbook.io/glint/) -These resources will be integrated into the official Ember Guides to provide comprehensive addon development guidance. - ## Drawbacks -- Some advanced use cases (e.g., monorepos, custom build setups) may require additional manual configuration. -- Addon authors unfamiliar with TypeScript or Glint may face a learning curve, but JavaScript is still supported. -- The blueprint is opinionated, which may not suit all workflows, but it is designed to cover the vast majority of use cases. +- Some advanced use cases (monorepos, custom builds) need additional configuration. +- Addon authors unfamiliar with TypeScript/Glint face a learning curve, but JavaScript is fully supported. +- The blueprint is opinionated, but covers the vast majority of use cases. ## Alternatives -- Do nothing (keep the old blueprints, but this would hinder ecosystem modernization). -- Make monorepo the default (adds complexity for most users). -- Provide multiple blueprints (increases maintenance burden and confusion). +- Do nothing (keeps old blueprints, hinders modernization) +- Default to monorepo (too complex for most users) +- Provide multiple blueprints (maintenance burden, confusion) ## Unresolved questions From 47fd82ddbc9b95e7f31dda8affb015ec7f9001b9 Mon Sep 17 00:00:00 2001 From: NullVoxPopuli <199018+NullVoxPopuli@users.noreply.github.com> Date: Mon, 16 Mar 2026 23:55:58 -0400 Subject: [PATCH 18/18] ope --- text/0985-v2-addon-by-default.md | 132 ++++++++++++++++++++++++++++++- 1 file changed, 129 insertions(+), 3 deletions(-) diff --git a/text/0985-v2-addon-by-default.md b/text/0985-v2-addon-by-default.md index 82f1466be9..442b47aa19 100644 --- a/text/0985-v2-addon-by-default.md +++ b/text/0985-v2-addon-by-default.md @@ -274,9 +274,79 @@ The blueprint uses two tsconfigs: The Glint template registry (`src/template-registry.ts`) lets apps using loose mode (hbs files) consume your types. Not needed if your library only targets strict mode consumers. +### The Strict Resolver and `modules` + +Both the test helper and the demo app use `ember-strict-application-resolver` instead of the classic Ember resolver. Instead of filesystem conventions, you explicitly register modules via a `modules` object. Each key must match a `./[type]/[name]` pattern (see [RFC 1132](https://rfcs.emberjs.com/id/1132-default-strict-resolver)): + +```typescript +class MyApp extends EmberApp { + modules = { + './router': Router, // direct assignment + './services/page-title': PageTitleService, // explicit import + ...import.meta.glob('./services/**/*', { eager: true }), // bulk registration + ...import.meta.glob('./templates/**/*', { eager: true }), + }; +} +``` + +You can register modules individually (useful for things from dependencies) or use `import.meta.glob` to sweep up everything in a directory. The glob approach is convenient but imports everything matching the pattern -- if you have non-service files in `services/`, they'll get pulled in too. + +This pattern is used in two places: + +- **Test helper** -- registers a minimal Router and optionally any services needed for tests +- **Demo app** -- registers the Router, templates, services, and anything else the demo needs + +### Demo App + +The blueprint includes a small demo application for manually testing your addon during development. Run `npm start` (or `pnpm start`) to launch Vite's dev server, which serves the root `index.html`: + +**`index.html`**: +```html + + + + + Demo App + + + + + + + +``` + +**`demo-app/app.gts`**: +```typescript +import EmberApp from 'ember-strict-application-resolver'; +import EmberRouter from '@ember/routing/router'; +import PageTitleService from 'ember-page-title/services/page-title'; + +class Router extends EmberRouter { + location = 'history'; + rootURL = '/'; +} + +export class App extends EmberApp { + modules = { + './router': Router, + './services/page-title': PageTitleService, + ...import.meta.glob('./services/**/*', { eager: true }), + ...import.meta.glob('./templates/**/*', { eager: true }), + }; +} + +Router.map(function () {}); +``` + +The demo app is a real Ember app -- it has routes, templates, and services -- but it boots directly via `ember-strict-application-resolver` with no ember-cli build step. Any addon code you want to exercise in the demo needs to be imported in the demo app's templates or registered in `modules`. The demo app's files are not published (they're not in the `files` array or `exports`). + ### Testing -Tests run entirely on Vite -- no ember-cli build pipeline, no webpack. +Tests also run entirely on Vite -- no ember-cli build pipeline, no webpack. **`tests/test-helper.ts`**: ```typescript @@ -349,7 +419,7 @@ export function start() { ``` -The test setup creates a minimal Ember app using `ember-strict-application-resolver` and `EmberRouter` directly -- no ember-cli abstraction layer. Test discovery uses `import.meta.glob`. This is also a proof-of-concept for how future compat-less Ember apps could work: ES modules throughout, Vite-native builds, direct framework API usage. +The test app is structurally the same as the demo app -- a minimal Ember app via `ember-strict-application-resolver` -- but configured for testing (`location = 'none'`, `autoboot: false`, `setTesting(true)`). Test discovery uses `import.meta.glob` in the HTML entry point. This is also a proof-of-concept for how future compat-less Ember apps could work. #### Cross-Version Testing @@ -709,9 +779,31 @@ Existing addons are unaffected. New addons get the new blueprint automatically. `imports` with `#src/*` lets tests and the demo app import from source without rebuilding. Can't be used in `src/` (Rollup won't transform these). Files in `src/` must use relative imports. +#### Importing Addon Code in Tests: `#src/*` vs. Consumer-Style + +When writing tests, you have two ways to import from your addon: + +**`#src/*` imports** -- import directly from source files: +```javascript +import { myHelper } from '#src/helpers/my-helper'; +``` +- Works immediately, no build step needed +- Fast feedback loop during development +- Tests the source code directly + +**Consumer-style imports** -- import as a consumer would: +```javascript +import { myHelper } from 'my-addon/helpers/my-helper'; +``` +- Tests the published API surface +- Requires `dist/` to exist (needs a build first) +- Catches issues with `exports` mapping or build transforms + +**Recommendation**: Use `#src/*` imports for day-to-day development. The try-scenarios CI matrix will catch build/export issues by running against real builds. If you need to specifically test the published output, use consumer-style imports in a dedicated test file and run `npm run build` first. + #### Self-Imports -Self-imports (e.g. `import { x } from 'my-addon/foo'`) don't work during development because they resolve through `exports` to `dist/`, which doesn't exist until you build. Use relative imports in `src/`: +Self-imports (e.g. `import { x } from 'my-addon/foo'`) don't work during development in `src/` files because they resolve through `exports` to `dist/`, which doesn't exist until you build. Use relative imports in `src/`: ```javascript // In src/ files: @@ -739,6 +831,40 @@ The single-package default works for most addons. If you need a monorepo (comple 4. Set up workspace tooling (pnpm/yarn workspaces) 5. Install addon in test app +#### Unpublished Addons in a Monorepo + +Sometimes you have a v2 addon in a monorepo that's only consumed by other packages in the workspace -- it's never published to npm. In this case you can skip the build step entirely and point `exports` at your source files: + +```json +{ + "name": "my-internal-addon", + "ember-addon": { + "version": 2, + "type": "addon", + "main": "addon-main.cjs" + }, + "exports": { + ".": { + "types": "./src/index.ts", + "default": "./src/index.ts" + }, + "./*": { + "types": "./src/*.ts", + "default": "./src/*.ts" + } + } +} +``` + +Key differences from a published addon: +- `exports` points to `src/` instead of `dist/` and `declarations/` +- No `files` array needed (not publishing to npm) +- No rollup build, no `prepack` script, no `declarations/` directory +- No `babel.publish.config.cjs` or `tsconfig.publish.json` needed +- You still need `addon-main.cjs` if any consuming app in the workspace uses the classic ember-cli build + +The consuming app's build tooling (Vite/Embroider) handles the transpilation. This is much simpler to maintain for workspace-internal code. + #### Publishing 1. Write code in `src/`, tests with `#src/*` imports