feat: Vite support, template coverage, and ember-cli decoupling - #432
Open
aklkv wants to merge 6 commits into
Open
feat: Vite support, template coverage, and ember-cli decoupling#432aklkv wants to merge 6 commits into
aklkv wants to merge 6 commits into
Conversation
aklkv
marked this pull request as ready for review
September 11, 2026 08:59
Continues the modernization branch: fixes classic and Embroider
regressions introduced by the rewrite, finishes Vite support, adds
Glimmer template branch coverage, and brings dependencies current.
Classic/Embroider fixes:
- Restore serverMiddleware, --server handling, and custom
coverageEnvVar support in addon-main.cjs, lost in the rewrite
- Fix Embroider coverage silently dropping every app file: the
default exclude switched from a shallow to a depth-independent
glob, which now also matches Embroider's rewritten-app tree;
added a negated re-include for it
- Fix this addon's own linked source leaking into every fixture's
coverage report, which could escape a report's output directory
entirely when relative paths crossed workspace boundaries;
excluded via the installed package root instead of a name-based
glob, since the latter also matches this repo's own checkout name
- Restore the in-repo-addon, custom-path, in-repo-engine, v1-addon,
and scoped-v1-addon fixtures deleted during modernization, bumped
to ember-auto-import 2.x (v2 addons require it)
Vite support:
- Move instrumentation into a post-enforce Vite transform instead of
documenting it as a @rollup/plugin-babel concern, so .gjs/.gts see
plain JS regardless of plugin ordering
- Add a build-time coverage baseline so files no test imports still
report at 0% instead of silently vanishing from Vite builds
- Add viteTestemMiddleware for `ember test --path dist`, where the
Vite dev server (and its own /write-coverage handler) isn't running
Template coverage:
- New Glimmer AST plugin instrumenting {{#if}}/{{#unless}},
{{else if}} chains, {{#each}}/{{#each-in}} with {{else}}, and
inline conditionals, reporting branches through the existing
Istanbul pipeline
- Browser runtime and app-tree re-exports for the coverage helpers;
a parallel-babel-safe wrapper so template coverage doesn't disable
Babel's `throwUnlessParallelizable`
- Scoped to .hbs; .gjs/.gts strict-mode templates are not yet covered
Also:
- Bump dependencies across root, the addon, and every fixture to
current safe versions; hold TypeScript at 6.0.3 (typescript-eslint
ceiling) and Babel at 7.x (Ember toolchain isn't Babel 8 ready)
- Serialize integration tests (fileParallelism: false): running them
in parallel let real Ember builds starve each other for CPU,
degrading coverage collection under load and making snapshots flap
- Consolidate lint into one `pnpm lint` at the root, chaining the
addon's own js/types/publish checks via `pnpm -r --filter
'!./test-packages/**'`, plus root formatting
- Modernize CI: bump action versions, add permissions/timeouts/
workflow_dispatch, add Node 26 to the test matrix
- Drop the root vite devDependency (only vitest/config was needed),
the stale root .prettierrc.js and .tool-versions, and the
untracked tsconfig.tsbuildinfo build cache
Closes the three gaps between this branch and what issue ember-cli-code-coverage#431 and PRs ember-cli-code-coverage#433/ember-cli-code-coverage#434 set out to do. Strict-mode template coverage (.gjs/.gts): - createTemplateCoveragePlugin now takes `strict: true`, emitting camelCase helper references (coverageMark) instead of dash-cased ones. The two can't be shared: loose mode resolves helpers by name through the classic resolver, which current ember-resolver versions require to be dash-cased — they removed the camelCase fallback as a deliberate bug fix. Strict mode has no resolver at all, so every reference must be a JS binding, and a dash isn't a valid identifier character. - New templateCoverageImportPlugin supplies those bindings, injecting the three helper imports from a Babel pre() hook so they exist before babel-plugin-ember-template-compilation validates template scope. - Skip templates outside the project and anything under node_modules: Embroider rewrites classic v1 addon dependencies into v2-compatible template() calls through the host app's own `transforms`, and those never went through the import plugin, so instrumenting them broke the build outright. - Derive the coverage key from `env.filename` when the preprocessor registry didn't set `env.meta.moduleName` (it never does on the strict-mode path), so entries no longer collapse onto a shared placeholder key. - Suffix that key so it doesn't resolve to a real file. A .gjs file's own JS coverage has a real source map, and applying it to template positions read off the pre-compilation AST produced invalid columns and crashed reporting for the whole payload. Fix adjustCoverage discarding coverage on key collision: Two raw keys resolving to the same file overwrote rather than merged, so one silently won. Merges via istanbul-lib-coverage now — the template and JS halves of a .gjs file depend on it, and it was already wrong for any other collision. Standalone CLI: `coverage-merge` bin runs the merge without ember-cli, for projects with no ember-cli command layer. Takes optional --root and --config. New /core subpath export, exposing the config, path-mapping and report utilities without pulling in babel, vite, or testem code.
The template coverage AST plugin skipped files outside the project root, which silently excluded every template on classic and Embroider builds: those compile out of broccoli temp directories, so an app's own templates routinely have paths nowhere near the project. Loose mode escaped it only because `env.filename` isn't set on that path. Deciding this on `node_modules` alone is both sufficient and correct. The dependencies that motivated the filter — a v1 addon Embroider rewrites through the host app's own `transforms` — are under `node_modules`, so containment never did the work; it only broke the legitimate case. The `root` option is dropped rather than kept as a footgun. Also documents, with a test rather than an assumption, that strict-mode `.gjs`/`.gts` templates get no coverage on classic/Embroider at all: ember-template-imports converts them through its own preprocessor and the AST plugin registered via setupPreprocessorRegistry never reaches the result, with no `transforms` hook on that path to register against. They are skipped silently rather than breaking the build; their JavaScript is still covered. The README said this combination would work with the same babel config a Vite app uses, which isn't true — a classic app has no equivalent file to put it in. Consolidates the two diverged CHANGELOG.md files. The nested one uniquely held v2.0.0-beta.1, v2.0.0-beta.2 and v1.0.0-beta.9, which root skipped between beta.8 and v1.0.0; those are merged into root at their chronological positions and the nested file removed. Every version in the nested file is verified present in root, and nothing was dropped from it.
aklkv
force-pushed
the
feat/modernize
branch
from
September 11, 2026 09:22
0227496 to
15d18d7
Compare
The README told Embroider users to pass `buildBabelPlugin({ embroider:
true })`, but that option no longer exists — it was how earlier versions
repointed Istanbul's cwd at the rewritten app, which the re-include
pattern now handles automatically. Passing it did nothing.
`my-embroider-app` already builds without it and reports coverage
correctly, which is what proves the flag is unnecessary; the
template-imports fixture was still carrying it as dead config.
The root lint task skipped everything under test-packages, so the fixture apps' own lint scripts had never run in this workspace. Turning them on surfaced five real failures, fixed here: - add the missing `type="button"` that ember-template-lint requires in the gjs/gts fixtures - reformat the vite app's components and tests per its own prettier config - import `SetupTestOptions` as a type, which verbatim module syntax requires - silence `node/no-missing-require` in the two classic fixtures, since eslint-plugin-node is unmaintained and cannot resolve package `exports` subpaths such as `ember-cli-code-coverage/testem` Also ignore `.eslintcache` unanchored so it covers every workspace package rather than only the root.
aklkv
force-pushed
the
feat/modernize
branch
from
September 11, 2026 09:48
de6bab8 to
f393729
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Explorations for #431 — continues the v2 addon migration and takes it to a working state across classic, Embroider, and Vite builds.
What #431 asked for
The issue is really about decoupling from ember-cli, not just "add Vite". Status against the specific asks in that thread:
coverageMiddlewareandviteTestemMiddlewareare plain exports fromember-cli-code-coverage/testem, wired intotestem.cjswith no ember-cli involvement.coverage-mergebin runs the merge standalone, with optional--root/--config./istanbul,/vite,/testem,/babel) — done, as/core,/vite,/testem,/babel, plus/glimmerand/merge. Named/coreto match the internal directory, consistent with every other subpath here.ember-vitest— untouched, still experimental upstream.Regressions fixed from the original modernize commit
These were broken on the branch before this work, not by it:
*/node_modules/**/*to**/node_modules/**/*, and Embroider ≥3.1 rewrites the app intonode_modules/.embroider/rewritten-app/, so the app's own files were all excluded. The committed snapshot proves it — it contained one entry, this addon's own file, and none of the app's.ember serveandember test --serverlost their fresh-map-per-request behavior, and a project-definedcoverageEnvVarwas ignored in favour of a hardcodedCOVERAGE.addon/andaddon-test-support/trees.Vite
Incorporates the intent of #434, with fixes beyond it:
.gjs/.gtsare seen as plain JS regardless of plugin ordering.viteTestemMiddlewareforember test --path dist, where the Vite dev server isn't running.Template coverage
Related to #433 but a different implementation, because that PR's central assumption no longer holds: it relies on camelCase helper names resolving through the classic resolver, and current
ember-resolverremoved that fallback deliberately — the runtime warning says so outright. Loose mode needs dash-case, strict mode needs camelCase, so they can't share one set..hbs(loose mode) works with no setup beyondtemplateCoverage: trueinconfig/coverage.js..gjs/.gts(strict mode, including apps onember-strict-application-resolver) works on Vite, viacreateTemplateCoveragePlugin({ strict: true })plustemplateCoverageImportPlugin, which supplies the JS bindings strict mode requires. On classic/Embroider those files get no template coverage — see below.{{#if}}/{{#unless}},{{else if}}chains,{{#each}}/{{#each-in}}with{{else}}, and inline conditionals including attribute position.throwUnlessParallelizable, which feature: template coverage #433 doesn't address.Also fixes
adjustCoverageoverwriting rather than merging when two raw keys resolve to the same file — the template and JS halves of a.gjsfile depend on that merge, and it was already wrong for any other collision.Tooling
pnpm lintat the root chains formatting plus every workspace package's own lint, fixtures included (pnpm -r --if-present run lint), in about 6 seconds. Five fixtures had lint failures blocking this — missingtype="button", a prettier mismatch, averbatimModuleSyntaxtype-only import, and anode/no-missing-requirefalse positive whereeslint-plugin-nodepredatesexportsmaps and can't resolveember-cli-code-coverage/testemeven though node resolves it fine. All fixed.permissions, job timeouts,workflow_dispatch, Node 26 added to the matrix.CHANGELOG.mdfiles. The nested one uniquely heldv2.0.0-beta.1,v2.0.0-beta.2andv1.0.0-beta.9(root skipped straight from beta.8 to v1.0.0); those are merged into root at their chronological positions and the nested file removed. Nothing was dropped from root — every version in the nested file is verified present.Testing
58 tests across 18 files. Restores the in-repo-addon, custom-path, in-repo-engine, v1-addon and scoped-v1-addon fixtures deleted during modernization (they cover namespace mapping and
modifyAssetLocation, the most breakage-prone code here), and adds fixtures for Vite and for template coverage.Not verified / open
ember-template-importsconverts.gjsthrough its own preprocessor, and the AST plugin registered viasetupPreprocessorRegistrynever reaches the result; there's notransformshook on that path to register against, which is why the Vite setup needs hand-wiring inbabel.config.mjsand a classic app has no equivalent file for it. Those templates are silently skipped rather than breaking the build — confirmed by compiling a real{{#if}}in a.gjsfile. Their JavaScript is still covered.