chore(angular): test schematics and code-splitting - #31401
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
| const baselineStats = readStatsJson(); | ||
| const baselineChunks = findChunksForComponents(baselineStats, components); | ||
|
|
||
| execSync(`node ${migrateImportsScript}`, { cwd: projectDir, stdio: 'inherit' }); |
ShaneK
left a comment
There was a problem hiding this comment.
Glad to see this, it's the test I was after on the exports PR. Two big things I noticed though, the export type move breaks IonicSafeString at runtime, and neither new test fails yet on what it's protecting. There's some other things worth looking at too though, and some nits.
| getTimeGivenProgression, | ||
| // TYPES | ||
| } from '@ionic/core/components'; | ||
| export type { |
There was a problem hiding this comment.
Moving IonicSafeString into this block drops it at runtime. It's a class, and @ionic/core/components exports it as a value, so export type erases the re-export. A consumer doing new IonicSafeString('<b>hi</b>') now gets error TS1362: 'IonicSafeString' cannot be used as a value because it was exported using 'export type', where the same code compiles clean on main.
This seems like a big issue since it's how you pass HTML to ion-alert and ion-toast, and the lazy entry point still exports it as a value, so the two disagree now. I think it wants to move back up next to IonicSlides. That old // TYPES comment was hand-maintained, so I'd guess it just had this one on the wrong side.
| if (baselineChunks.size != 1) { | ||
| throw new Error("Components should have all been included in the same chunk before migrating."); | ||
| } | ||
| if (migratedChunks.size != components.length) { |
There was a problem hiding this comment.
I don't think this catches the case it's meant to. The set unions both components together, and main.js counts as a chunk, so the count can be 2 while a component sits in the initial bundle.
I tried it by importing IonToggle eagerly into AppComponent. The toggle ends up in main.js, so every landing page visitor downloads it, and this still passes. Checking that the chunk holding ion-toggle isn't one the landing page pulls in would be harder to fool.
The baseline check has the opposite problem, since requiring exactly 1 asserts the broken state is still broken. If esbuild ever splits the barrel on its own, this goes red on an improvement.
| providers: [ | ||
| { provide: RouteReuseStrategy, useClass: IonicRouteStrategy }, | ||
| provideIonicAngular(), | ||
| provideRouter(routes, withPreloading(PreloadAllModules), withComponentInputBinding()), |
There was a problem hiding this comment.
Using PreloadAllModules here undoes what the fixture is meant to show. The router fetches every lazy route after the first navigation, so sitting on the landing page pulls down the home page chunk, toggle included.
Drop withPreloading and rebuild the same source, and the landing page loads its own chunk and leaves the home one alone. So the bundler splits it correctly and the router downloads it all anyway. Could this come out?
| "test": "npm run test.schematics && npm run test.code-split", | ||
| "test.code-split": "node ./scripts/test-code-split.js", | ||
| "test.schematics": "node ./scripts/verify-schematics.js", | ||
| "validate": "npm i && npm run lint && npm run test && npm run build", |
There was a problem hiding this comment.
Now that test isn't an echo any more, this ordering bites. Both new tests pack the package before build has run, so on a fresh checkout they get a tarball with no dist and ng add can't resolve the collection, same as the schematics path thread on the exports PR.
CI is ordered correctly, but prerelease goes through validate. Could you swap it to build before test?
| execSync(`npx ng new ${testName} --style css --ssr false --ai-config none`, {cwd: packageRootDir}); | ||
|
|
||
| // Install ionic-angular package | ||
| execSync(`npx ng add --skip-confirmation ../ionic-angular-*`, {cwd: testDir}); |
There was a problem hiding this comment.
The glob doesn't clear old tarballs first the way sync.sh and sync-and-pack.sh both do, and never deletes the one it just made. With 8.8.2, a nightly 9.0.0-dev and 9.0.0 all sitting there the shell hands over 8.8.2 first, and since the CLI takes one collection argument with strict(false), the extras go by silently. A leftover tarball from main means this passes having checked the wrong version.
Exiting 0 is also the only assertion here, and most rules in schematics/add/index.ts no-op rather than throw. With no app.config.ts, addProvideIonicAngular skips quietly, and addIonicStyles writes eleven hardcoded css paths nothing verifies. FW-7692 wants this to fail when schematics files are incorrect. Only missing ones get caught, and running npm run build in the generated project would pick up the css list for free.
|
|
||
| function readStatsJson() { | ||
| if (!fs.existsSync(statsFile)) { | ||
| throw new Error(`${path.relative(projectDir, statsFile)} was not produced by the ${label} build.`); |
There was a problem hiding this comment.
Reproduced this one: label belongs to build(), not to this function, so the missing stats path throws a ReferenceError instead of the message. Taking it as an argument and passing 'baseline' and 'migrated' at the two call sites would sort it.
| "./config": "./dist/common/providers/config.js", | ||
| "./platform": "./dist/common/providers/platform.js", | ||
| "./nav-params": "./dist/common/directives/navigation/nav-params.js", | ||
| "./ion-modal-token": "./dist/common/providers/angular-delegate.js", |
There was a problem hiding this comment.
The six subpaths above this are bare kebab-case and needed no exclusion. This one takes the ion- prefix but it's a DI token rather than a component, and that's the only reason KNOWN_NON_CORE_ION_COMPONENTS had to be added to the verifier I asked for on the exports PR.
It also points at angular-delegate.js, which exports more than the token. Running the codemod with --print-map sends AngularDelegate and attachView to @ionic/angular/ion-modal-token, so someone's AngularDelegate import gets rewritten to a path named after a modal token.
Calling it ./modal-token matches the siblings and lets the exclusion list go away. Free to change now, breaking once it's released.
| name: ionic-angular | ||
| output: ./packages/angular/AngularBuild.zip | ||
| paths: ./packages/angular/dist ./packages/angular/css | ||
| - name: 📐 Run Angular Package Tests |
There was a problem hiding this comment.
Every other package in build.yml keeps the build action to building and archiving, with the suites in their own job. This adds an ng new, two npm installs and two production Angular builds to it.
Since test-angular-e2e needs build-angular, a flaky ng new now skips all five e2e apps and shows up as build-angular failing, and stencil-nightly.yml uses the same action, so an Angular CLI or registry problem would surface as a Stencil nightly failure. It also sits after Check Diff, and both scripts write into the tree.
Would a test-angular-package job with needs: [build-angular] work instead? Tiny nit: trailing space in the step name.
| @@ -0,0 +1,18 @@ | |||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||
There was a problem hiding this comment.
Nothing runs these. No workflow touches the app's test target, and test-code-split.js only does npm i, sync.sh and npm run build, so the spec files, test-setup.ts and the vitest and jsdom devDeps are all unreachable. They only assert toBeTruthy() as well, so even wired up they wouldn't cover what this PR protects.
Looks like ng new scaffolding that came along for the ride, so worth either wiring up or dropping.
While you're in there, test-setup.ts explains its matchMedia polyfill with ion-menu and ion-split-pane, and this app uses neither.
| 'ion-select-popover', | ||
| 'ion-slides', | ||
| ]; | ||
| const KNOWN_NON_CORE_ION_COMPONENTS = [ |
There was a problem hiding this comment.
I think this is the FW-7695 part of the PR, but I can't find the rest of it. That ticket asks for the standalone e2e pages to move to per-component imports and for this script to check every export subpath resolves from the barrel too, and the standalone pages are all still on the barrel.
The code-split app does typecheck the entry points for the seven components it uses, which is great, but it's not quite the same coverage. Is FW-7695 meant to be a follow-up?
Nit: double space before the =, and the array's missing a semicolon and trailing comma compared to the one above it. Nothing will catch those since scripts is ignored by eslint and prettier.
Issue number: resolves #
What is the current behavior?
What is the new behavior?
package.jsonDoes this introduce a breaking change?
Other information