|
| 1 | +# Ignite UI for Angular — `ng update` Migrations |
| 2 | + |
| 3 | +This directory hosts the migration schematics that run when consumers execute |
| 4 | +`ng update igniteui-angular`. Each `update-*` folder is a versioned migration |
| 5 | +listed in [`migration-collection.json`](./migration-collection.json) and |
| 6 | +implemented in `update-*/index.ts`. Shared helpers live in |
| 7 | +[`common/`](./common). |
| 8 | + |
| 9 | +See [Wiki pages under Migrations & Schematics](https://github.com/IgniteUI/igniteui-angular/wiki#migrations--schematics) |
| 10 | +for authoring and testing guidance. |
| 11 | + |
| 12 | +## Encapsulation and external ESM like `@angular/compiler` |
| 13 | +The collection has `"encapsulation": true` so every |
| 14 | +migration is loaded in its own isolated VM context by the Angular |
| 15 | +schematics tooling. That also restricts some API access/functionality. |
| 16 | + |
| 17 | +See the description of [#13712 — fix(migrations,ng-add): turn on encapsulation for devkit/schematics deps](https://github.com/IgniteUI/igniteui-angular/pull/13712) when encapsulation was first enabled for mechanism details. |
| 18 | + |
| 19 | +### Dynamic `@angular/compiler` import via `nativeImport` |
| 20 | + |
| 21 | +`@angular/compiler` is an ES module. The migrations themselves are emitted and consumed |
| 22 | +(`require()`-d) by the Angular schematics as CommonJS, so they cannot statically `import` it; |
| 23 | +they have to use a runtime dynamic `import()`. |
| 24 | + |
| 25 | +- [`common/import-helper.cts`](./common/import-helper.cts) — authored as |
| 26 | + a TypeScript CJS module. The dynamic `import()` is preserved in the emitted. |
| 27 | +- Migrations call it via: |
| 28 | + ```ts |
| 29 | + import { nativeImport } from 'igniteui-angular/migrations/common/import-helper.cjs'; |
| 30 | + // ... |
| 31 | + const { HtmlParser } = await nativeImport('@angular/compiler'); |
| 32 | + ``` |
| 33 | + |
| 34 | +### Why the bare `igniteui-angular/...` specifier |
| 35 | + |
| 36 | +Schematics encapsulation being a VM execution is affected by the |
| 37 | +[Support of dynamic import() in compilation APIs](https://nodejs.org/api/vm.html#support-of-dynamic-import-in-compilation-apis) |
| 38 | +and at this time Angular schematics do not implement [importModuleDynamically](https://github.com/angular/angular-cli/blob/e5eb3e37c9756669b3b91b5b681a73cc9b616cb9/packages/angular/cli/src/command-builder/utilities/schematic-engine-host.ts#L50). |
| 39 | + |
| 40 | +A **bare package specifier** effectively passes through the schematics host encapsulation again and escapes [the relative wrapping ](https://github.com/angular/angular-cli/blob/e5eb3e37c9756669b3b91b5b681a73cc9b616cb9/packages/angular/cli/src/command-builder/utilities/schematic-engine-host.ts#L177-L178) out into [the original schematic require](https://github.com/angular/angular-cli/blob/e5eb3e37c9756669b3b91b5b681a73cc9b616cb9/packages/angular/cli/src/command-builder/utilities/schematic-engine-host.ts#L203-L204). |
| 41 | +This means just the `import-helper.cjs` script is ran outside of encapsulation and as such can perform dynamic imports. |
| 42 | + |
| 43 | +Every migration that needs `@angular/compiler` follows the same pattern. |
| 44 | + |
| 45 | +To keep TypeScript happy with the bare specifier at compile time, |
| 46 | +[`tsconfig.json`](./tsconfig.json) maps it back to the source: |
| 47 | + |
| 48 | +```jsonc |
| 49 | +"paths": { |
| 50 | + "igniteui-angular/*": ["../*"], |
| 51 | + "@infragistics/igniteui-angular/*": ["../*"] |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +At consumer install time the specifier resolves naturally through their |
| 56 | +`node_modules/igniteui-angular/migrations/common/import-helper.cjs`. |
| 57 | + |
| 58 | +### Tests setup for the bare specifier |
| 59 | + |
| 60 | +`npm run test:schematics` compiles the migrations with |
| 61 | +[`tsconfig.spec.json`](./tsconfig.spec.json) into `dist/igniteui-angular/...` |
| 62 | +and runs the compiled `*.spec.js` files through Jasmine. In this repo there is |
| 63 | +no `node_modules/igniteui-angular` (the library is the workspace itself), so |
| 64 | +the bare `igniteui-angular/migrations/common/import-helper.cjs` specifier |
| 65 | +would not resolve at runtime. |
| 66 | + |
| 67 | +[`tsconfig-paths-bootstrap.js`](./tsconfig-paths-bootstrap.js) fixes that. It |
| 68 | +is loaded with `node --require` before Jasmine starts and installs a runtime |
| 69 | +path map (via the `tsconfig-paths` package) that points |
| 70 | +`igniteui-angular/*` at `./dist/igniteui-angular/*`. |
| 71 | + |
| 72 | +The wiring in [`package.json`](../../../package.json) looks like: |
| 73 | + |
| 74 | +```text |
| 75 | +node -r ./projects/igniteui-angular/migrations/tsconfig-paths-bootstrap.js \ |
| 76 | + ./node_modules/jasmine/bin/jasmine.js \ |
| 77 | + "dist/igniteui-angular/migrations/**/*.spec.js" \ |
| 78 | + "dist/igniteui-angular/schematics/**/*.spec.js" |
| 79 | +``` |
0 commit comments