Skip to content

Commit 9b7132e

Browse files
Brooooooklynclaude
andauthored
fix(vite): simplify linker transform filter for Vite/Rolldown compatibility (#193)
The complex regex in the transform filter (`[\\/]`, `(?:\?.*)?$`) was not reliably evaluated by Vite 8's Rolldown-backed filter mechanism, preventing the linker from processing excluded node_modules packages like PrimeNG. Without linking, Angular falls back to JIT which fails with NG0303 for non-standalone components. Split filtering into two stages: a simple `/node_modules/` static filter that any Vite version can evaluate, and precise extension + content checks inside the handler using JavaScript's native regex engine. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 8d11968 commit 9b7132e

File tree

2 files changed

+46
-31
lines changed

2 files changed

+46
-31
lines changed

napi/angular-compiler/test/linker.test.ts

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -139,61 +139,62 @@ describe('Angular linker - chunk file linking', () => {
139139
})
140140
})
141141

142-
describe('NODE_MODULES_JS_REGEX filter matching', () => {
143-
// This is the fixed regex from angular-linker-plugin.ts
144-
const NODE_MODULES_JS_REGEX = /node_modules[\\/].*\.[cm]?js(?:\?.*)?$/
142+
describe('Linker transform filter matching', () => {
143+
// These mirror the two-stage filter from angular-linker-plugin.ts:
144+
// 1. Broad static filter (NODE_MODULES_JS_REGEX) for Vite's filter mechanism
145+
// 2. Precise handler-level check (JS_EXT_REGEX) inside the transform handler
146+
const NODE_MODULES_JS_REGEX = /node_modules/
147+
const JS_EXT_REGEX = /\.[cm]?js(?:\?.*)?$/
148+
149+
function matches(id: string) {
150+
return NODE_MODULES_JS_REGEX.test(id) && JS_EXT_REGEX.test(id)
151+
}
145152

146153
it('should match standard Angular FESM files', () => {
147-
expect(NODE_MODULES_JS_REGEX.test('node_modules/@angular/common/fesm2022/common.mjs')).toBe(
148-
true,
149-
)
154+
expect(matches('node_modules/@angular/common/fesm2022/common.mjs')).toBe(true)
150155
})
151156

152157
it('should match chunk files', () => {
153-
expect(
154-
NODE_MODULES_JS_REGEX.test(
155-
'node_modules/@angular/common/fesm2022/_platform_location-chunk.mjs',
156-
),
157-
).toBe(true)
158+
expect(matches('node_modules/@angular/common/fesm2022/_platform_location-chunk.mjs')).toBe(true)
158159
})
159160

160161
it('should match absolute paths', () => {
161162
expect(
162-
NODE_MODULES_JS_REGEX.test(
163+
matches(
163164
'/Users/dev/project/node_modules/@angular/common/fesm2022/_platform_location-chunk.mjs',
164165
),
165166
).toBe(true)
166167
})
167168

168169
it('should match paths with Vite query strings', () => {
169-
expect(
170-
NODE_MODULES_JS_REGEX.test('node_modules/@angular/common/fesm2022/common.mjs?v=abc123'),
171-
).toBe(true)
170+
expect(matches('node_modules/@angular/common/fesm2022/common.mjs?v=abc123')).toBe(true)
172171
})
173172

174173
it('should match chunk files with Vite query strings', () => {
175174
expect(
176-
NODE_MODULES_JS_REGEX.test(
177-
'node_modules/@angular/common/fesm2022/_platform_location-chunk.mjs?v=df7b0864',
178-
),
175+
matches('node_modules/@angular/common/fesm2022/_platform_location-chunk.mjs?v=df7b0864'),
179176
).toBe(true)
180177
})
181178

182179
it('should match Windows-style backslash paths', () => {
183-
expect(NODE_MODULES_JS_REGEX.test('node_modules\\@angular\\common\\fesm2022\\common.mjs')).toBe(
184-
true,
185-
)
180+
expect(matches('node_modules\\@angular\\common\\fesm2022\\common.mjs')).toBe(true)
186181
})
187182

188183
it('should match .js and .cjs files', () => {
189-
expect(NODE_MODULES_JS_REGEX.test('node_modules/@ngrx/store/fesm2022/ngrx-store.js')).toBe(true)
190-
expect(NODE_MODULES_JS_REGEX.test('node_modules/some-lib/index.cjs')).toBe(true)
184+
expect(matches('node_modules/@ngrx/store/fesm2022/ngrx-store.js')).toBe(true)
185+
expect(matches('node_modules/some-lib/index.cjs')).toBe(true)
186+
})
187+
188+
it('should match PrimeNG files (excluded from optimizeDeps)', () => {
189+
expect(matches('node_modules/primeng/fesm2022/primeng-table.mjs')).toBe(true)
190+
expect(matches('node_modules/primeng/fesm2022/primeng-table.mjs?v=abc123')).toBe(true)
191191
})
192192

193193
it('should not match non-JS files', () => {
194-
expect(NODE_MODULES_JS_REGEX.test('node_modules/@angular/common/fesm2022/common.d.ts')).toBe(
195-
false,
196-
)
197-
expect(NODE_MODULES_JS_REGEX.test('src/app/app.component.ts')).toBe(false)
194+
expect(matches('node_modules/@angular/common/fesm2022/common.d.ts')).toBe(false)
195+
})
196+
197+
it('should not match application source files', () => {
198+
expect(matches('src/app/app.component.ts')).toBe(false)
198199
})
199200
})

napi/angular-compiler/vite-plugin/angular-linker-plugin.ts

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,14 @@ const LINKER_DECLARATION_PREFIX = '\u0275\u0275ngDeclare'
2424
// Skip these packages - they don't need linking
2525
const SKIP_REGEX = /[\\/]@angular[\\/](?:compiler|core)[\\/]/
2626

27-
// Match JS files in node_modules (Angular FESM bundles)
28-
// Allows optional query strings (?v=...) that Vite appends to module IDs
29-
const NODE_MODULES_JS_REGEX = /node_modules[\\/].*\.[cm]?js(?:\?.*)?$/
27+
// Broad filter for the transform hook — deliberately simple so that every
28+
// Vite/Rolldown version can evaluate it. Precise extension + query-string
29+
// checks are done inside the handler.
30+
const NODE_MODULES_JS_REGEX = /node_modules/
31+
32+
// Precise check run inside the handler: matches .js / .mjs / .cjs with an
33+
// optional Vite query string (?v=…) and works on both Unix and Windows paths.
34+
const JS_EXT_REGEX = /\.[cm]?js(?:\?.*)?$/
3035

3136
/**
3237
* Run the OXC Rust linker on the given code.
@@ -97,9 +102,18 @@ export function angularLinkerPlugin(): Plugin {
97102
transform: {
98103
filter: {
99104
id: NODE_MODULES_JS_REGEX,
100-
code: LINKER_DECLARATION_PREFIX,
101105
},
102106
async handler(code, id) {
107+
// Precise extension check (covers .js, .mjs, .cjs with optional ?v=… query)
108+
if (!JS_EXT_REGEX.test(id)) {
109+
return
110+
}
111+
112+
// Quick check: skip files without partial declarations
113+
if (!code.includes(LINKER_DECLARATION_PREFIX)) {
114+
return
115+
}
116+
103117
// Skip packages that don't need linking
104118
if (SKIP_REGEX.test(id)) {
105119
return

0 commit comments

Comments
 (0)