-
Notifications
You must be signed in to change notification settings - Fork 13
Expand file tree
/
Copy pathangular-linker-plugin.ts
More file actions
135 lines (118 loc) · 3.9 KB
/
angular-linker-plugin.ts
File metadata and controls
135 lines (118 loc) · 3.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
import { linkAngularPackage } from '#binding'
import type { Plugin } from 'vite'
/**
* Angular Linker plugin for Vite.
*
* Processes pre-compiled Angular library code from node_modules that contains
* partial compilation declarations (ɵɵngDeclare*). These declarations need to
* be "linked" (converted to full ɵɵdefine* calls) at build time.
*
* Without this plugin, Angular falls back to JIT compilation which requires
* @angular/compiler at runtime.
*
* Uses OXC's native Rust-based linker for fast, zero-dependency linking of all
* declaration types including ɵɵngDeclareComponent (with full template compilation).
*
* This plugin works in two phases:
* 1. During dependency optimization (Rolldown pre-bundling) via a Rolldown load plugin
* 2. During Vite's transform pipeline for non-optimized node_modules files
*/
const LINKER_DECLARATION_PREFIX = '\u0275\u0275ngDeclare'
// Skip these packages - they don't need linking
const SKIP_REGEX = /[\\/]@angular[\\/](?:compiler|core)[\\/]/
// Broad filter for the transform hook — deliberately simple so that every
// Vite/Rolldown version can evaluate it. Precise extension + query-string
// checks are done inside the handler.
const NODE_MODULES_JS_REGEX = /node_modules/
// Precise check run inside the handler: matches .js / .mjs / .cjs with an
// optional Vite query string (?v=…) and works on both Unix and Windows paths.
const JS_EXT_REGEX = /\.[cm]?js(?:\?.*)?$/
/**
* Run the OXC Rust linker on the given code.
*/
async function linkCode(
code: string,
id: string,
): Promise<{ code: string; map: string | null; linked: boolean }> {
const result = await linkAngularPackage(code, id)
return {
code: result.linked ? result.code : code,
map: result.map ?? null,
linked: result.linked,
}
}
export function angularLinkerPlugin(): Plugin {
return {
name: '@oxc-angular/vite-linker',
config(_, { command }) {
return {
optimizeDeps: {
rolldownOptions: {
transform: {
define: {
ngJitMode: 'false',
ngI18nClosureMode: 'false',
...(command === 'serve' ? {} : { ngDevMode: 'false' }),
},
},
plugins: [
{
name: 'angular-linker',
load: {
filter: {
id: /\.[cm]?js$/,
},
async handler(id: string) {
// Skip @angular/compiler and @angular/core
if (SKIP_REGEX.test(id)) {
return
}
const code = await this.fs.readFile(id, {
encoding: 'utf8',
})
// Quick check: skip files without partial declarations
if (!code.includes(LINKER_DECLARATION_PREFIX)) {
return
}
const result = await linkCode(code, id)
if (!result.linked) {
return
}
return result.code
},
},
},
],
},
},
}
},
transform: {
filter: {
id: NODE_MODULES_JS_REGEX,
},
async handler(code, id) {
// Precise extension check (covers .js, .mjs, .cjs with optional ?v=… query)
if (!JS_EXT_REGEX.test(id)) {
return
}
// Quick check: skip files without partial declarations
if (!code.includes(LINKER_DECLARATION_PREFIX)) {
return
}
// Skip packages that don't need linking
if (SKIP_REGEX.test(id)) {
return
}
const result = await linkCode(code, id)
if (!result.linked) {
return
}
return {
code: result.code,
map: result.map,
}
},
},
}
}