@analogjs/angular-compiler 2.5.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/COMPILER.md +474 -0
- package/README.md +5 -0
- package/package.json +51 -0
- package/src/index.d.ts +5 -0
- package/src/index.js +6 -0
- package/src/index.js.map +1 -0
- package/src/lib/ast-translator.d.ts +40 -0
- package/src/lib/ast-translator.js +243 -0
- package/src/lib/ast-translator.js.map +1 -0
- package/src/lib/compile.d.ts +24 -0
- package/src/lib/compile.js +630 -0
- package/src/lib/compile.js.map +1 -0
- package/src/lib/defer.d.ts +23 -0
- package/src/lib/defer.js +158 -0
- package/src/lib/defer.js.map +1 -0
- package/src/lib/dts-reader.d.ts +25 -0
- package/src/lib/dts-reader.js +326 -0
- package/src/lib/dts-reader.js.map +1 -0
- package/src/lib/hmr.d.ts +12 -0
- package/src/lib/hmr.js +49 -0
- package/src/lib/hmr.js.map +1 -0
- package/src/lib/index.d.ts +5 -0
- package/src/lib/index.js +6 -0
- package/src/lib/index.js.map +1 -0
- package/src/lib/jit-metadata.d.ts +11 -0
- package/src/lib/jit-metadata.js +201 -0
- package/src/lib/jit-metadata.js.map +1 -0
- package/src/lib/jit-transform.d.ts +21 -0
- package/src/lib/jit-transform.js +207 -0
- package/src/lib/jit-transform.js.map +1 -0
- package/src/lib/js-emitter.d.ts +8 -0
- package/src/lib/js-emitter.js +317 -0
- package/src/lib/js-emitter.js.map +1 -0
- package/src/lib/metadata.d.ts +36 -0
- package/src/lib/metadata.js +465 -0
- package/src/lib/metadata.js.map +1 -0
- package/src/lib/registry.d.ts +32 -0
- package/src/lib/registry.js +177 -0
- package/src/lib/registry.js.map +1 -0
- package/src/lib/resource-inliner.d.ts +17 -0
- package/src/lib/resource-inliner.js +109 -0
- package/src/lib/resource-inliner.js.map +1 -0
- package/src/lib/style-ast.d.ts +8 -0
- package/src/lib/style-ast.js +110 -0
- package/src/lib/style-ast.js.map +1 -0
- package/src/lib/styles.d.ts +13 -0
- package/src/lib/styles.js +60 -0
- package/src/lib/styles.js.map +1 -0
- package/src/lib/utils.d.ts +8 -0
- package/src/lib/utils.js +71 -0
- package/src/lib/utils.js.map +1 -0
- package/vite.config.d.ts +2 -0
- package/vite.config.js +19 -0
- package/vite.config.js.map +1 -0
package/COMPILER.md
ADDED
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
# Analog Angular Compiler
|
|
2
|
+
|
|
3
|
+
A lightweight Angular compiler that transforms decorators and signal-based reactive APIs into Ivy static definitions. Designed for fast dev server compilation via Vite, without requiring a full TypeScript program.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @analogjs/angular-compiler
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Peer dependencies: `@angular/compiler` >=19, `@angular/compiler-cli` >=19, `@angular/build` >=19, `vite` >=6.
|
|
12
|
+
|
|
13
|
+
## Entry Point
|
|
14
|
+
|
|
15
|
+
| Import | Exports | Use case |
|
|
16
|
+
| ---------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------- |
|
|
17
|
+
| `@analogjs/angular-compiler` | `compile()`, `scanFile()`, `scanDtsFile()`, `scanPackageDts()`, `collectImportedPackages()`, `jitTransform()`, `inlineResourceUrls()`, `extractInlineStyles()` | Programmatic compiler API |
|
|
18
|
+
|
|
19
|
+
The compiler is integrated into `@analogjs/vite-plugin-angular` via the `experimental.useAnalogCompiler` flag — there is no separate Vite plugin entry point.
|
|
20
|
+
|
|
21
|
+
## Usage
|
|
22
|
+
|
|
23
|
+
### Via Analog Platform (recommended)
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import analog from '@analogjs/platform';
|
|
27
|
+
|
|
28
|
+
export default defineConfig({
|
|
29
|
+
plugins: [
|
|
30
|
+
analog({
|
|
31
|
+
vite: {
|
|
32
|
+
experimental: {
|
|
33
|
+
useAnalogCompiler: true,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
}),
|
|
37
|
+
],
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Programmatic API
|
|
42
|
+
|
|
43
|
+
```ts
|
|
44
|
+
import {
|
|
45
|
+
compile,
|
|
46
|
+
scanFile,
|
|
47
|
+
scanDtsFile,
|
|
48
|
+
scanPackageDts,
|
|
49
|
+
collectImportedPackages,
|
|
50
|
+
jitTransform,
|
|
51
|
+
inlineResourceUrls,
|
|
52
|
+
extractInlineStyles,
|
|
53
|
+
} from '@analogjs/angular-compiler';
|
|
54
|
+
|
|
55
|
+
// Compile a single file
|
|
56
|
+
const result = compile(sourceCode, fileName, { registry });
|
|
57
|
+
// result.code — compiled JavaScript
|
|
58
|
+
// result.map — V3 source map
|
|
59
|
+
// result.resourceDependencies — external template/style paths read
|
|
60
|
+
|
|
61
|
+
// Scan a source file for Angular metadata (uses OXC Rust parser)
|
|
62
|
+
const entries = scanFile(code, fileName);
|
|
63
|
+
// entries: [{ selector, kind, className, fileName, inputs, outputs, ... }]
|
|
64
|
+
|
|
65
|
+
// Scan a .d.ts file for pre-compiled Angular declarations
|
|
66
|
+
const dtsEntries = scanDtsFile(dtsCode, fileName);
|
|
67
|
+
// Reads ɵɵDirectiveDeclaration/ComponentDeclaration/PipeDeclaration type params
|
|
68
|
+
|
|
69
|
+
// Scan all .d.ts files in an npm package
|
|
70
|
+
const pkgEntries = scanPackageDts('@angular/router', projectRoot);
|
|
71
|
+
|
|
72
|
+
// Collect bare-specifier package names from imports
|
|
73
|
+
const packages = collectImportedPackages(code, fileName);
|
|
74
|
+
// Set { '@angular/router', 'rxjs', ... }
|
|
75
|
+
|
|
76
|
+
// Inline templateUrl/styleUrl(s) in source code (OXC AST rewriting)
|
|
77
|
+
const inlined = inlineResourceUrls(sourceCode, fileName);
|
|
78
|
+
|
|
79
|
+
// Extract inline style strings for preprocessing (OXC parser)
|
|
80
|
+
const styles = extractInlineStyles(sourceCode, fileName);
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## Architecture
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
Source file (.ts)
|
|
87
|
+
│
|
|
88
|
+
├─ vite-plugin-angular (buildStart, when useAnalogCompiler is true)
|
|
89
|
+
│ @angular/compiler-cli readConfiguration() → rootNames
|
|
90
|
+
│ OXC parser ──▶ scanFile() ──▶ ComponentRegistry
|
|
91
|
+
│ (selectors, inputs, outputs, pipes, NgModule exports)
|
|
92
|
+
│
|
|
93
|
+
└─ vite-plugin-angular (transform, per request)
|
|
94
|
+
collectImportedPackages() ──▶ scanPackageDts() ──▶ registry (lazy, once per package)
|
|
95
|
+
inlineResourceUrls() ──▶ OXC AST rewrite (templateUrl → template, styleUrl → styles)
|
|
96
|
+
│
|
|
97
|
+
▼
|
|
98
|
+
compile(code, fileName, { registry })
|
|
99
|
+
ts.createSourceFile ──▶ extractMetadata / detectSignals / detectFieldDecorators
|
|
100
|
+
│
|
|
101
|
+
▼
|
|
102
|
+
@angular/compiler
|
|
103
|
+
parseTemplate()
|
|
104
|
+
compileComponentFromMetadata()
|
|
105
|
+
compileDirectiveFromMetadata()
|
|
106
|
+
compilePipeFromMetadata()
|
|
107
|
+
compileNgModule() / compileInjector()
|
|
108
|
+
compileFactoryFunction()
|
|
109
|
+
compileClassMetadata()
|
|
110
|
+
│
|
|
111
|
+
▼
|
|
112
|
+
JSEmitter (Angular output AST → JavaScript strings, ~4x faster than ts.Printer)
|
|
113
|
+
│
|
|
114
|
+
▼
|
|
115
|
+
MagicString (surgical edits on original source → JS + source map)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The compiler is integrated into `@analogjs/vite-plugin-angular` as an alternative compilation path alongside ngtsc and the Angular Compilation API. When `experimental.useAnalogCompiler` is enabled, the vite plugin:
|
|
119
|
+
|
|
120
|
+
1. Scans all source files with `scanFile()` at build start to populate the `ComponentRegistry`
|
|
121
|
+
2. Inlines external resources (`templateUrl`, `styleUrl`) via OXC AST rewriting before compilation
|
|
122
|
+
3. Compiles each file on-demand with `compile()` during Vite's transform phase (runs with `enforce: 'pre'`)
|
|
123
|
+
4. Strips TypeScript syntax from the output via `vite.transformWithOxc()` (handles `import type`, generics, interfaces, `implements`, etc.)
|
|
124
|
+
5. Appends HMR code for leaf components in dev mode
|
|
125
|
+
6. Injects synthetic imports for NgModule-exported classes
|
|
126
|
+
|
|
127
|
+
The existing vite-plugin-angular plugins (build optimizer, router, vitest, etc.) continue to work alongside the analog compiler.
|
|
128
|
+
|
|
129
|
+
## Source Files
|
|
130
|
+
|
|
131
|
+
| File | Purpose |
|
|
132
|
+
| --------------------- | ---------------------------------------------------------------------------------------------- |
|
|
133
|
+
| `compile.ts` | Single-file AOT compiler: Ivy codegen via `@angular/compiler` APIs |
|
|
134
|
+
| `registry.ts` | OXC-based file scanner, extracts selectors, inputs, outputs |
|
|
135
|
+
| `metadata.ts` | Decorator metadata extraction (`extractMetadata`, `detectSignals`, `detectFieldDecorators`) |
|
|
136
|
+
| `js-emitter.ts` | Angular output AST → JavaScript string emitter (~4x faster than `ts.Printer`) |
|
|
137
|
+
| `ast-translator.ts` | Angular output AST → TypeScript AST translator (for complex expressions) |
|
|
138
|
+
| `resource-inliner.ts` | OXC-based `templateUrl`/`styleUrl` inlining and inline style extraction |
|
|
139
|
+
| `dts-reader.ts` | OXC-based `.d.ts` scanner for pre-compiled Angular packages (selectors, inputs, outputs) |
|
|
140
|
+
| `jit-transform.ts` | JIT mode: decorator metadata arrays + constructor DI + signal downleveling + decorator removal |
|
|
141
|
+
| `jit-metadata.ts` | JIT metadata helpers (constructor parameters, property decorators) |
|
|
142
|
+
| `defer.ts` | `@defer` dependency map builder |
|
|
143
|
+
| `hmr.ts` | HMR code generation using `ɵɵreplaceMetadata` |
|
|
144
|
+
| `styles.ts` | Style preprocessing via Vite's `preprocessCSS` (OXC-based extraction) |
|
|
145
|
+
| `utils.ts` | Type-only import detection, class finder, `forwardRef` unwrapper |
|
|
146
|
+
|
|
147
|
+
## What's Supported
|
|
148
|
+
|
|
149
|
+
### Decorators
|
|
150
|
+
|
|
151
|
+
| Decorator | Static Fields | Notes |
|
|
152
|
+
| ------------- | ------------------------------------------ | ----------------------------------------------- |
|
|
153
|
+
| `@Component` | `ɵcmp`, `ɵfac`, `setClassMetadata` | Full template compilation with Ivy instructions |
|
|
154
|
+
| `@Directive` | `ɵdir`, `ɵfac`, `setClassMetadata` | Host bindings, listeners, inputs/outputs |
|
|
155
|
+
| `@Pipe` | `ɵpipe`, `ɵfac`, `setClassMetadata` | Pure and impure |
|
|
156
|
+
| `@Injectable` | `ɵprov`, `ɵfac`, `setClassMetadata` | `providedIn` variants |
|
|
157
|
+
| `@NgModule` | `ɵmod`, `ɵinj`, `ɵfac`, `setClassMetadata` | Declarations, exports, providers, bootstrap |
|
|
158
|
+
|
|
159
|
+
### Field Decorators
|
|
160
|
+
|
|
161
|
+
| Decorator | Supported |
|
|
162
|
+
| ------------------------------------------------------------------ | --------- |
|
|
163
|
+
| `@Input()` / `@Input('alias')` / `@Input({ required, transform })` | Yes |
|
|
164
|
+
| `@Output()` / `@Output('alias')` | Yes |
|
|
165
|
+
| `@ViewChild(pred, opts)` / `@ViewChildren(pred, opts)` | Yes |
|
|
166
|
+
| `@ContentChild(pred, opts)` / `@ContentChildren(pred, opts)` | Yes |
|
|
167
|
+
| `@HostBinding('prop')` | Yes |
|
|
168
|
+
| `@HostListener('event', ['$event'])` | Yes |
|
|
169
|
+
|
|
170
|
+
### Dependency Injection
|
|
171
|
+
|
|
172
|
+
| Feature | Supported |
|
|
173
|
+
| ------------------------------------- | ------------------------------------ |
|
|
174
|
+
| Constructor parameter injection | Yes (type annotations as tokens) |
|
|
175
|
+
| `@Inject(TOKEN)` | Yes |
|
|
176
|
+
| `@Optional()` | Yes |
|
|
177
|
+
| `@Self()` / `@SkipSelf()` / `@Host()` | Yes |
|
|
178
|
+
| `@Attribute('name')` | Yes |
|
|
179
|
+
| Type-only imports (`import type`) | Detected → `ɵɵinvalidFactory` |
|
|
180
|
+
| Class inheritance without constructor | `ɵɵgetInheritedFactory` |
|
|
181
|
+
| `forwardRef(() => X)` unwrapping | Yes (in imports, providers, queries) |
|
|
182
|
+
|
|
183
|
+
### @Component API Coverage
|
|
184
|
+
|
|
185
|
+
| Property | Status |
|
|
186
|
+
| -------------------------- | ------------------------------------------------------------- |
|
|
187
|
+
| `selector` | Supported (auto-generated for selectorless routed components) |
|
|
188
|
+
| `template` | Supported |
|
|
189
|
+
| `templateUrl` | Supported (inlined at compile time) |
|
|
190
|
+
| `styles` (array or string) | Supported (ShadowCss emulated encapsulation) |
|
|
191
|
+
| `styleUrl` / `styleUrls` | Supported (inlined at compile time) |
|
|
192
|
+
| `standalone` | Supported (defaults `true` for Angular 19+) |
|
|
193
|
+
| `changeDetection` | Supported (OnPush / Default) |
|
|
194
|
+
| `encapsulation` | Supported (Emulated / None / ShadowDom) |
|
|
195
|
+
| `imports` | Supported (resolved via registry) |
|
|
196
|
+
| `providers` | Supported |
|
|
197
|
+
| `viewProviders` | Supported |
|
|
198
|
+
| `animations` | Supported (passed through) |
|
|
199
|
+
| `exportAs` | Supported |
|
|
200
|
+
| `preserveWhitespaces` | Supported |
|
|
201
|
+
| `host` | Supported (listeners, properties, attributes) |
|
|
202
|
+
| `schemas` | Passed through |
|
|
203
|
+
|
|
204
|
+
### Signal APIs
|
|
205
|
+
|
|
206
|
+
| API | Supported |
|
|
207
|
+
| -------------------------------------------- | ----------------------------------------------------------------------- |
|
|
208
|
+
| `signal()` | Yes (preserved as-is) |
|
|
209
|
+
| `computed()` | Yes (preserved as-is) |
|
|
210
|
+
| `input()` / `input.required()` | Yes (signal input descriptors with required flag, transform extraction) |
|
|
211
|
+
| `model()` / `model.required()` | Yes (generates input + `Change` output) |
|
|
212
|
+
| `output()` | Yes |
|
|
213
|
+
| `viewChild()` / `viewChild.required()` | Yes (signal queries) |
|
|
214
|
+
| `viewChildren()` | Yes (signal queries) |
|
|
215
|
+
| `contentChild()` / `contentChild.required()` | Yes (signal queries) |
|
|
216
|
+
| `contentChildren()` | Yes (signal queries) |
|
|
217
|
+
| `inject()` | Yes (preserved as-is) |
|
|
218
|
+
|
|
219
|
+
### Template Features
|
|
220
|
+
|
|
221
|
+
| Feature | Supported |
|
|
222
|
+
| ------------------------------------------------------------------------------------------------------- | --------- |
|
|
223
|
+
| `@if` / `@else if` / `@else` | Yes |
|
|
224
|
+
| `@for` with `track`, `@empty` | Yes |
|
|
225
|
+
| `@for` implicit variables (`$index`, `$first`, `$last`, `$even`, `$odd`, `$count`) | Yes |
|
|
226
|
+
| `@switch` / `@case` / `@default` | Yes |
|
|
227
|
+
| `@defer` with all triggers (`on viewport`, `on idle`, `on timer`, `on hover`, `on interaction`, `when`) | Yes |
|
|
228
|
+
| `@defer` sub-blocks (`@loading`, `@placeholder`, `@error`) with `minimum` | Yes |
|
|
229
|
+
| `@defer` lazy dependency loading via `import()` | Yes |
|
|
230
|
+
| Nested `@defer` inside control flow | Yes |
|
|
231
|
+
| `@let` declarations | Yes |
|
|
232
|
+
| `{{ interpolation }}` | Yes |
|
|
233
|
+
| `[property]` binding | Yes |
|
|
234
|
+
| `(event)` binding | Yes |
|
|
235
|
+
| `[(two-way)]` binding | Yes |
|
|
236
|
+
| `[class.name]` / `[style.prop]` | Yes |
|
|
237
|
+
| `<ng-content>` (multi-slot projection) | Yes |
|
|
238
|
+
| Pipes in templates (with args, chained) | Yes |
|
|
239
|
+
|
|
240
|
+
### Cross-file Resolution
|
|
241
|
+
|
|
242
|
+
| Scenario | Supported |
|
|
243
|
+
| ---------------------------------------------- | ------------------------------------------------------------------------- |
|
|
244
|
+
| Component imports component | Yes (via registry, with input/output binding resolution) |
|
|
245
|
+
| Component imports directive | Yes (via registry) |
|
|
246
|
+
| Component imports pipe | Yes (via registry) |
|
|
247
|
+
| Component imports NgModule | Yes (recursively expanded, handles nested NgModules with cycle detection) |
|
|
248
|
+
| Library imports (e.g. `RouterOutlet`) | Yes (lazily scanned from package `.d.ts` via `scanPackageDts()`) |
|
|
249
|
+
| Library NgModule exports | Yes (`.d.ts` scanner reads `ɵɵNgModuleDeclaration` type params) |
|
|
250
|
+
| Same-file imports | Yes (file-local selector fallback) |
|
|
251
|
+
| Self-referencing (recursive) components | Yes (component added to its own declarations) |
|
|
252
|
+
| Signal input bindings (`[prop]="value"`) | Yes (registry extracts `input()`, `input.required()`, `@Input()`) |
|
|
253
|
+
| Signal output bindings (`(event)="handler()"`) | Yes (registry extracts `output()`, `model()`, `@Output()`) |
|
|
254
|
+
| Two-way binding (`[(prop)]="signal"`) | Yes (via `model()` input + output registration) |
|
|
255
|
+
|
|
256
|
+
## What's Not Supported
|
|
257
|
+
|
|
258
|
+
| Feature | Reason |
|
|
259
|
+
| ---------------------------- | --------------------------------------------------------------- |
|
|
260
|
+
| Template type checking | Requires full `ts.Program`; use Angular Language Service in IDE |
|
|
261
|
+
| i18n / localization | Out of scope (future consideration) |
|
|
262
|
+
| Partial / linker compilation | Handled by separate plugin |
|
|
263
|
+
| Template source maps | Angular compiler doesn't propagate sourceSpan to output AST |
|
|
264
|
+
| Signal debug names | Not implemented |
|
|
265
|
+
| `setClassDebugInfo` | Not implemented |
|
|
266
|
+
| Spread imports (`...Module`) | Not implemented |
|
|
267
|
+
|
|
268
|
+
## HMR (Hot Module Replacement)
|
|
269
|
+
|
|
270
|
+
Leaf components support true HMR via Angular's `ɵɵreplaceMetadata`. When a component file changes in dev mode:
|
|
271
|
+
|
|
272
|
+
1. Vite hot-replaces the module
|
|
273
|
+
2. The `import.meta.hot.accept` callback calls `ɵɵreplaceMetadata` with the new component definition
|
|
274
|
+
3. Angular merges old/new definitions and recreates matching LViews without page reload
|
|
275
|
+
|
|
276
|
+
Root components (e.g. `App`) fall back to page reload since they can't be hot-replaced without re-bootstrapping the application. Non-Angular files use Vite's default HMR.
|
|
277
|
+
|
|
278
|
+
External template and style changes invalidate the parent `.ts` module, triggering re-compilation and HMR. Preprocessed styles are cached by mtime for fast re-compilation.
|
|
279
|
+
|
|
280
|
+
## Source Maps
|
|
281
|
+
|
|
282
|
+
The compiler generates V3 source maps via `magic-string` using surgical edits on the original source. Class bodies, methods, and expressions stay at their original character positions — only removed decorators and inserted Ivy fields are new content. The source map is passed through Vite's transform pipeline which composes it with other transforms for end-to-end mapping in browser devtools.
|
|
283
|
+
|
|
284
|
+
## Style Preprocessing
|
|
285
|
+
|
|
286
|
+
Both external and inline styles are preprocessed via Vite's `preprocessCSS` API:
|
|
287
|
+
|
|
288
|
+
- **External styles** (`.scss`, `.sass`, `.less`, `.styl` via `styleUrl`/`styleUrls`): extracted via OXC parser, read, preprocessed, and passed to the compiler via `resolvedStyles`
|
|
289
|
+
- **Inline styles** (`styles: [...]` or `styles: \`...\``): extracted via OXC parser (`extractInlineStyles`), preprocessed, and passed via `resolvedInlineStyles`
|
|
290
|
+
- **Template `<style>` tags**: Angular's `parseTemplate()` extracts `<style>` blocks from the HTML template. These are merged with the decorator-level `styles` array so both sources contribute to the compiled output.
|
|
291
|
+
|
|
292
|
+
The `inlineStyleLanguage` option (default: `'scss'`) controls the file extension used for inline style preprocessing. Set to `'css'` to disable inline preprocessing.
|
|
293
|
+
|
|
294
|
+
## Comparison with Angular's Compilers
|
|
295
|
+
|
|
296
|
+
### vs ngtsc (Angular's native compiler)
|
|
297
|
+
|
|
298
|
+
Both produce identical Ivy output because both call the same `@angular/compiler` APIs (`compileComponentFromMetadata`, `parseTemplate`, `compileFactoryFunction`, etc.). The template instructions are byte-for-byte equivalent.
|
|
299
|
+
|
|
300
|
+
| | ngtsc | This compiler |
|
|
301
|
+
| ------------------------------- | ---------------------------------------- | ------------------------------------------------------------ |
|
|
302
|
+
| Size | ~200,000+ lines | ~2,000 lines |
|
|
303
|
+
| Requires `ts.Program` | Yes (reads all files, resolves modules) | No |
|
|
304
|
+
| Type checking | Full TS + template type checking | None (use Angular Language Service) |
|
|
305
|
+
| Template compilation | Full Ivy instructions | Full Ivy instructions (same APIs) |
|
|
306
|
+
| Output format | `ɵɵdefineComponent` (final) | `ɵɵdefineComponent` (final) |
|
|
307
|
+
| Global analysis | Via type checker (full scope resolution) | Via tsconfig + OXC registry scan |
|
|
308
|
+
| Constructor DI | Full (via type checker) | Full (via AST parameter analysis) |
|
|
309
|
+
| `setClassMetadata` | Yes | Yes |
|
|
310
|
+
| Source maps | Yes (via TS emitter) | Yes (via MagicString surgical edits + inline string emitter) |
|
|
311
|
+
| HMR | Full (with tracking metadata) | Leaf components (root falls back to reload) |
|
|
312
|
+
| `@defer` lazy loading | Yes | Yes |
|
|
313
|
+
| SCSS preprocessing | Via `@angular/build` | Via Vite `preprocessCSS` + LmdbCacheStore |
|
|
314
|
+
| i18n | Full ICU extraction + localization | Not supported |
|
|
315
|
+
| Template type checking | Full (`strictTemplates`) | Not supported |
|
|
316
|
+
| Incremental compilation | `ts.Program` reuse | Per-file (Vite handles caching) |
|
|
317
|
+
| Diagnostic messages | Hundreds of template/binding errors | Unresolved selector warnings only |
|
|
318
|
+
| Partial compilation (libraries) | `ɵɵngDeclareComponent` | Not in scope |
|
|
319
|
+
| Declaration files (`.d.ts`) | Yes | Not in scope |
|
|
320
|
+
|
|
321
|
+
#### Performance
|
|
322
|
+
|
|
323
|
+
| Metric | ngtsc | This compiler |
|
|
324
|
+
| ---------------------------- | ------------------ | --------------- |
|
|
325
|
+
| Cold build (500 components) | 5-15s | <1s (on-demand) |
|
|
326
|
+
| Hot rebuild (1 file changed) | 200-500ms | 2-5ms |
|
|
327
|
+
| Dev server start | 3-10s | <1s |
|
|
328
|
+
| Registry scan (1000 files) | N/A (type checker) | ~37ms (OXC) |
|
|
329
|
+
|
|
330
|
+
#### The Tradeoff
|
|
331
|
+
|
|
332
|
+
ngtsc gives **compile-time safety** — wrong template bindings, missing inputs, and type mismatches are caught before the browser runs. This compiler gives **speed** — identical Ivy output, identical runtime behavior, but no compile-time template validation. With Angular Language Service running in the IDE, the developer experience is nearly identical — errors show as red squiggles in the editor instead of terminal output.
|
|
333
|
+
|
|
334
|
+
### vs Angular Local Compilation
|
|
335
|
+
|
|
336
|
+
| | Local Compilation | This compiler |
|
|
337
|
+
| --------------------------- | -------------------------------- | ------------------------------- |
|
|
338
|
+
| Output format | `ɵɵngDeclareComponent` (partial) | `ɵɵdefineComponent` (final) |
|
|
339
|
+
| Linker required | Yes | No |
|
|
340
|
+
| Template compiled at | Link time | Compile time |
|
|
341
|
+
| Selector matching | Deferred to linker | Done via global analysis plugin |
|
|
342
|
+
| Cross-version compatibility | Yes (stable declaration format) | No (tied to Angular version) |
|
|
343
|
+
| Use case | Library publishing (npm) | Application dev server |
|
|
344
|
+
| Requires `ts.Program` | No | No |
|
|
345
|
+
|
|
346
|
+
### vs esbuild/SWC (type stripping)
|
|
347
|
+
|
|
348
|
+
| | esbuild/SWC | This compiler |
|
|
349
|
+
| -------------------- | ------------------------------ | -------------------------------------- |
|
|
350
|
+
| TypeScript handling | Strip types only | Strip types + transform decorators |
|
|
351
|
+
| Angular awareness | None | Full (templates, signals, Ivy codegen) |
|
|
352
|
+
| Template compilation | N/A | Full Ivy instructions |
|
|
353
|
+
| Speed | ~0.01ms/file | ~0.5-2ms/file |
|
|
354
|
+
| Output | Valid JS (no Angular metadata) | Valid JS + Ivy static fields |
|
|
355
|
+
|
|
356
|
+
## Performance
|
|
357
|
+
|
|
358
|
+
### Per-file Compilation
|
|
359
|
+
|
|
360
|
+
| Component Complexity | Time |
|
|
361
|
+
| -------------------------------------------- | ------ |
|
|
362
|
+
| Simple (1 element, no signals) | ~0.5ms |
|
|
363
|
+
| Medium (control flow, signals, styles) | ~1.8ms |
|
|
364
|
+
| Complex (nested control flow, many bindings) | ~3ms |
|
|
365
|
+
|
|
366
|
+
In Vite dev mode, only requested files are compiled on demand. A typical page load compiles 10-20 files (~20-40ms total).
|
|
367
|
+
|
|
368
|
+
### Compilation Breakdown (medium component)
|
|
369
|
+
|
|
370
|
+
| Phase | % of time | Tool |
|
|
371
|
+
| ------------------ | --------- | ----------------------------------- |
|
|
372
|
+
| Template parsing | ~46% | `@angular/compiler` (JS) |
|
|
373
|
+
| TypeScript parsing | ~29% | `ts.createSourceFile` (JS) |
|
|
374
|
+
| Code emission | ~25% | Inline string emitter + MagicString |
|
|
375
|
+
|
|
376
|
+
The dominant cost is Angular's template parser — JavaScript that can't be replaced with Rust without reimplementing the Angular template compiler.
|
|
377
|
+
|
|
378
|
+
### Registry Cold Scan
|
|
379
|
+
|
|
380
|
+
| Files | OXC (parse + walk) | TypeScript (parse only) |
|
|
381
|
+
| ----- | ------------------ | ----------------------- |
|
|
382
|
+
| 200 | ~8ms | ~23ms |
|
|
383
|
+
| 500 | ~19ms | ~55ms |
|
|
384
|
+
| 1000 | ~37ms | ~55ms |
|
|
385
|
+
|
|
386
|
+
The registry scan uses OXC's native Rust parser for ~1.5x faster file scanning at build start.
|
|
387
|
+
|
|
388
|
+
## Angular Version Compatibility
|
|
389
|
+
|
|
390
|
+
The compiler detects the installed `@angular/compiler` version at startup and adapts:
|
|
391
|
+
|
|
392
|
+
| Feature | Angular 19 | Angular 20+ | Angular 21+ |
|
|
393
|
+
| -------------------------- | ---------- | ------------------------ | -------------------- |
|
|
394
|
+
| `hasDirectiveDependencies` | Omitted | Set when imports present | Same |
|
|
395
|
+
| `externalStyles` | Omitted | Same | Available (not used) |
|
|
396
|
+
| All other APIs | Compatible | Compatible | Compatible |
|
|
397
|
+
|
|
398
|
+
Supported range: **Angular 19+**. Conformance tested against **Angular 17-21**.
|
|
399
|
+
|
|
400
|
+
## Future Architecture (tsgo)
|
|
401
|
+
|
|
402
|
+
When TypeScript moves to `tsgo` (Go-based compiler), Angular will need to separate type checking from decorator transformation — the same split this compiler already makes:
|
|
403
|
+
|
|
404
|
+
```
|
|
405
|
+
tsgo (Go) Angular Transform (JS)
|
|
406
|
+
├─ Type stripping ├─ compile() per file
|
|
407
|
+
├─ Module resolution ├─ @angular/compiler APIs
|
|
408
|
+
└─ Type checking └─ angular() plugin
|
|
409
|
+
└─ Registry scan (OXC/Rust)
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
This compiler's architecture — single-file transforms using `@angular/compiler` with global analysis as a separate registry — is the likely direction for Angular's compiler when `tsgo` replaces `tsc`.
|
|
413
|
+
|
|
414
|
+
## Test Suite
|
|
415
|
+
|
|
416
|
+
| File | Tests | Coverage |
|
|
417
|
+
| ------------------------------- | ----- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
418
|
+
| `jit-transform.spec.ts` | 32 | Decorator conversion, member decorator removal (@Input, @Output, @ViewChild, @ContentChild), constructor parameter decorator removal (@Inject, @Optional), constructor DI, signal API downleveling, external resources, edge cases, ReflectionCapabilities integration |
|
|
419
|
+
| `integration.spec.ts` | 50 | Registry input/output extraction, cross-component binding, constant pool ordering, assignment precedence, templateUrl inlining, content projection, pipe chaining, template refs, two-way binding, computed signals, safe navigation, @defer triggers, @if alias, multi-component files, duplicate i0 prevention, OXC resource inlining, arrow object literal wrapping, .d.ts extraction, signal field preservation, decorator stripping, collectImportedPackages, non-Angular passthrough, template `<style>` merging, NgModule export expansion, member decorator removal, self-referencing components, .d.ts NgModule scanning, TS syntax preservation, lazy dependency arrays, directive compilation, recursive NgModule expansion, circular NgModule handling |
|
|
420
|
+
| `dts-reader.spec.ts` | 7 | Directive/component/pipe extraction from `.d.ts`, signal inputs, aliased inputs, multiple classes |
|
|
421
|
+
| `component.spec.ts` | 48 | All @Component features, signals (including required variants), control flow, defer, pipes, content projection, external resources, resource dependencies, providers, source maps |
|
|
422
|
+
| `ast-translator.spec.ts` | 43 | Every AST visitor method (expressions + statements), ngDevMode global |
|
|
423
|
+
| `decorator-fields.spec.ts` | 15 | @Input, @Output, @ViewChild, @ContentChild, @HostBinding, @HostListener field decorators |
|
|
424
|
+
| `constructor-di.spec.ts` | 8 | Constructor DI: @Inject, @Optional, inheritance, union types, multiple params |
|
|
425
|
+
| `error-handling.spec.ts` | 7 | Unknown decorators, undecorated classes, selectorless components, forwardRef, invalid templates |
|
|
426
|
+
| `registry.spec.ts` | 6 | All decorator types, multi-declaration, NgModule exports |
|
|
427
|
+
| `cross-file-resolution.spec.ts` | 5 | Cross-file component, pipe, directive resolution |
|
|
428
|
+
| `ngmodule.spec.ts` | 3 | Compilation, providers, export resolution |
|
|
429
|
+
| `injectable.spec.ts` | 3 | `providedIn` variants |
|
|
430
|
+
| `directive.spec.ts` | 2 | Host bindings, exportAs |
|
|
431
|
+
| `pipe.spec.ts` | 2 | Pure and impure |
|
|
432
|
+
| `compile.spec.ts` | 2 | Original smoke tests |
|
|
433
|
+
| `conformance.spec.ts` | 161 | Angular compliance test suite (v17-v21, 87%+ Ivy instruction match) |
|
|
434
|
+
|
|
435
|
+
### Conformance Testing
|
|
436
|
+
|
|
437
|
+
The compiler is validated against Angular's official compliance test suite. A conformance test runner compares compiled Ivy instruction output against Angular's expected patterns with instruction normalization (`ɵɵtemplate`↔`ɵɵdomTemplate`, named↔anonymous functions).
|
|
438
|
+
|
|
439
|
+
#### Pass Rates by Angular Version
|
|
440
|
+
|
|
441
|
+
| Angular | Pass Rate | Tests |
|
|
442
|
+
| ------------------ | --------- | ----- |
|
|
443
|
+
| v17 (latest patch) | 93.2% | 120 |
|
|
444
|
+
| v18 (latest patch) | 82.9% | 143 |
|
|
445
|
+
| v19 (latest patch) | 83.5% | 151 |
|
|
446
|
+
| v20 (latest patch) | 91.5% | 155 |
|
|
447
|
+
| v21 (latest patch) | 91.8% | 160 |
|
|
448
|
+
| latest | 91.8% | 160 |
|
|
449
|
+
| next (v22.0.0) | 91.8% | 160 |
|
|
450
|
+
|
|
451
|
+
Remaining soft-failures are output formatting differences (`@defer` multi-file deps, named function patterns), not functional issues. All versions produce 0 hard test failures.
|
|
452
|
+
|
|
453
|
+
#### Running Conformance Tests
|
|
454
|
+
|
|
455
|
+
```bash
|
|
456
|
+
# Specific major version (resolves latest patch)
|
|
457
|
+
bash packages/angular-compiler/scripts/setup-conformance.sh 19
|
|
458
|
+
ANGULAR_SOURCE_DIR=.angular-conformance npx vitest run packages/angular-compiler/src/lib/conformance.spec.ts
|
|
459
|
+
|
|
460
|
+
# Exact version
|
|
461
|
+
bash packages/angular-compiler/scripts/setup-conformance.sh 21.0.0
|
|
462
|
+
|
|
463
|
+
# Latest stable release (auto-detected via GitHub API)
|
|
464
|
+
bash packages/angular-compiler/scripts/setup-conformance.sh
|
|
465
|
+
bash packages/angular-compiler/scripts/setup-conformance.sh latest
|
|
466
|
+
|
|
467
|
+
# Latest prerelease (-next.N)
|
|
468
|
+
bash packages/angular-compiler/scripts/setup-conformance.sh next
|
|
469
|
+
|
|
470
|
+
# Local (auto-detects ~/projects/angular/angular if present)
|
|
471
|
+
npx vitest run packages/angular-compiler/src/lib/conformance.spec.ts
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
CI runs a matrix of Angular 17, 18, 19, 20, 21, latest, and next on every push to `feat/angular-compiler` via `.github/workflows/conformance.yml`.
|
package/README.md
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@analogjs/angular-compiler",
|
|
3
|
+
"version": "2.5.0-beta.1",
|
|
4
|
+
"description": "Lightweight compiler for Angular",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"keywords": [
|
|
8
|
+
"angular",
|
|
9
|
+
"compiler",
|
|
10
|
+
"vite",
|
|
11
|
+
"ivy",
|
|
12
|
+
"signals",
|
|
13
|
+
"analogjs"
|
|
14
|
+
],
|
|
15
|
+
"author": "Brandon Roberts <robertsbt@gmail.com>",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/analogjs/analog/issues"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/analogjs/analog#readme",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/analogjs/analog.git"
|
|
23
|
+
},
|
|
24
|
+
"funding": {
|
|
25
|
+
"type": "github",
|
|
26
|
+
"url": "https://github.com/sponsors/brandonroberts"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"@angular/compiler": ">=19.0.0",
|
|
30
|
+
"@angular/compiler-cli": ">=19.0.0"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"magic-string": "^0.30.0",
|
|
34
|
+
"oxc-parser": "^0.121.0"
|
|
35
|
+
},
|
|
36
|
+
"exports": {
|
|
37
|
+
"./package.json": "./package.json",
|
|
38
|
+
".": {
|
|
39
|
+
"types": "./src/index.d.ts",
|
|
40
|
+
"import": "./src/index.js",
|
|
41
|
+
"default": "./src/index.js"
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
"publishConfig": {
|
|
45
|
+
"access": "public",
|
|
46
|
+
"provenance": false
|
|
47
|
+
},
|
|
48
|
+
"types": "./src/index.d.ts",
|
|
49
|
+
"module": "./src/index.js",
|
|
50
|
+
"main": "./src/index.js"
|
|
51
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { compile, type CompileResult, type CompileOptions, } from './lib/compile.js';
|
|
2
|
+
export { scanFile, type RegistryEntry, type ComponentRegistry, } from './lib/registry.js';
|
|
3
|
+
export { scanDtsFile, scanPackageDts, collectImportedPackages, } from './lib/dts-reader.js';
|
|
4
|
+
export { jitTransform, type JitTransformResult } from './lib/jit-transform.js';
|
|
5
|
+
export { inlineResourceUrls, extractInlineStyles, } from './lib/resource-inliner.js';
|
package/src/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { compile, } from './lib/compile.js';
|
|
2
|
+
export { scanFile, } from './lib/registry.js';
|
|
3
|
+
export { scanDtsFile, scanPackageDts, collectImportedPackages, } from './lib/dts-reader.js';
|
|
4
|
+
export { jitTransform } from './lib/jit-transform.js';
|
|
5
|
+
export { inlineResourceUrls, extractInlineStyles, } from './lib/resource-inliner.js';
|
|
6
|
+
//# sourceMappingURL=index.js.map
|
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../packages/angular-compiler/src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,OAAO,GAGR,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EACL,QAAQ,GAGT,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EACL,WAAW,EACX,cAAc,EACd,uBAAuB,GACxB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,YAAY,EAA2B,MAAM,wBAAwB,CAAC;AAC/E,OAAO,EACL,kBAAkB,EAClB,mBAAmB,GACpB,MAAM,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import * as o from '@angular/compiler';
|
|
2
|
+
import * as ts from 'typescript';
|
|
3
|
+
export declare class AstTranslator implements o.ExpressionVisitor, o.StatementVisitor {
|
|
4
|
+
visitWritePropExpr(ast: any, context: any): ts.BinaryExpression;
|
|
5
|
+
visitInvokeMethodExpr(ast: any, context: any): ts.CallExpression;
|
|
6
|
+
visitWriteVarExpr(ast: any, context: any): ts.BinaryExpression;
|
|
7
|
+
visitWriteKeyExpr(ast: any, context: any): ts.BinaryExpression;
|
|
8
|
+
visitTaggedTemplateLiteralExpr(ast: any, context: any): ts.TaggedTemplateExpression;
|
|
9
|
+
visitReadVarExpr(ast: o.ReadVarExpr, context: any): ts.Identifier | ts.ThisExpression | ts.SuperExpression;
|
|
10
|
+
visitReadPropExpr(ast: o.ReadPropExpr, context: any): ts.PropertyAccessExpression;
|
|
11
|
+
visitReadKeyExpr(ast: o.ReadKeyExpr, context: any): ts.ElementAccessExpression;
|
|
12
|
+
visitLiteralExpr(ast: o.LiteralExpr, context: any): ts.StringLiteral | ts.NumericLiteral | ts.PrefixUnaryExpression | ts.TrueLiteral | ts.FalseLiteral | ts.VoidExpression | ts.NullLiteral;
|
|
13
|
+
visitLiteralArrayExpr(ast: o.LiteralArrayExpr, context: any): ts.ArrayLiteralExpression;
|
|
14
|
+
visitSpreadElementExpr(ast: o.SpreadElementExpr, context: any): ts.SpreadElement;
|
|
15
|
+
visitLiteralMapExpr(ast: o.LiteralMapExpr, context: any): ts.ObjectLiteralExpression;
|
|
16
|
+
visitInvokeFunctionExpr(ast: o.InvokeFunctionExpr, context: any): ts.CallExpression;
|
|
17
|
+
visitInstantiateExpr(ast: o.InstantiateExpr, context: any): ts.NewExpression;
|
|
18
|
+
visitTemplateLiteralExpr(ast: o.TemplateLiteralExpr, context: any): ts.TemplateExpression;
|
|
19
|
+
visitBinaryOperatorExpr(ast: o.BinaryOperatorExpr, context: any): ts.BinaryExpression;
|
|
20
|
+
visitConditionalExpr(ast: o.ConditionalExpr, context: any): ts.ConditionalExpression;
|
|
21
|
+
visitNotExpr(ast: o.NotExpr, context: any): ts.PrefixUnaryExpression;
|
|
22
|
+
visitTypeofExpr(ast: o.TypeofExpr, context: any): ts.TypeOfExpression;
|
|
23
|
+
visitUnaryOperatorExpr(ast: o.UnaryOperatorExpr, context: any): ts.PrefixUnaryExpression;
|
|
24
|
+
visitFunctionExpr(ast: o.FunctionExpr, context: any): ts.ArrowFunction;
|
|
25
|
+
visitArrowFunctionExpr(ast: o.ArrowFunctionExpr, context: any): ts.ArrowFunction;
|
|
26
|
+
visitDynamicImportExpr(ast: o.DynamicImportExpr, context: any): ts.CallExpression;
|
|
27
|
+
visitParenthesizedExpr(ast: o.ParenthesizedExpr, context: any): ts.ParenthesizedExpression;
|
|
28
|
+
visitCommaExpr(ast: o.CommaExpr, context: any): any;
|
|
29
|
+
visitVoidExpr(ast: o.VoidExpr, context: any): ts.VoidExpression;
|
|
30
|
+
visitLocalizedString(ast: o.LocalizedString, context: any): void;
|
|
31
|
+
visitRegularExpressionLiteral(ast: o.RegularExpressionLiteralExpr, context: any): ts.RegularExpressionLiteral;
|
|
32
|
+
visitTemplateLiteralElementExpr(ast: o.TemplateLiteralElementExpr, context: any): ts.StringLiteral;
|
|
33
|
+
visitWrappedNodeExpr(ast: o.WrappedNodeExpr<any>, context: any): ts.Expression;
|
|
34
|
+
visitExternalExpr(ast: o.ExternalExpr, context: any): ts.Identifier | ts.PropertyAccessExpression;
|
|
35
|
+
visitDeclareVarStmt(stmt: o.DeclareVarStmt, context: any): ts.VariableStatement;
|
|
36
|
+
visitDeclareFunctionStmt(stmt: o.DeclareFunctionStmt, context: any): ts.FunctionDeclaration;
|
|
37
|
+
visitExpressionStmt(stmt: o.ExpressionStatement, context: any): ts.ExpressionStatement;
|
|
38
|
+
visitReturnStmt(stmt: o.ReturnStatement, context: any): ts.ReturnStatement;
|
|
39
|
+
visitIfStmt(stmt: o.IfStmt, context: any): ts.IfStatement;
|
|
40
|
+
}
|