@analogjs/vite-plugin-angular 3.0.0-alpha.30 → 3.0.0-alpha.31

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analogjs/vite-plugin-angular",
3
- "version": "3.0.0-alpha.30",
3
+ "version": "3.0.0-alpha.31",
4
4
  "description": "Vite Plugin for Angular",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -36,6 +36,7 @@
36
36
  }
37
37
  },
38
38
  "dependencies": {
39
+ "@analogjs/angular-compiler": "2.5.0-beta.40",
39
40
  "es-toolkit": "^1.45.1",
40
41
  "obug": "^2.1.1",
41
42
  "oxc-parser": "^0.124.0",
@@ -64,7 +65,8 @@
64
65
  "@analogjs/storybook-angular",
65
66
  "@analogjs/vite-plugin-angular",
66
67
  "@analogjs/vite-plugin-nitro",
67
- "@analogjs/vitest-angular"
68
+ "@analogjs/vitest-angular",
69
+ "@analogjs/angular-compiler"
68
70
  ],
69
71
  "migrations": "./migrations/migration.json"
70
72
  },
@@ -0,0 +1,14 @@
1
+ import { Plugin } from "vite";
2
+ export interface AnalogCompilerPluginOptions {
3
+ tsconfigGetter: () => string;
4
+ workspaceRoot: string;
5
+ inlineStylesExtension: string;
6
+ jit: boolean;
7
+ liveReload: boolean;
8
+ supportedBrowsers: string[];
9
+ transformFilter?: (code: string, id: string) => boolean;
10
+ isTest: boolean;
11
+ isAstroIntegration: boolean;
12
+ analogCompilationMode?: "full" | "partial";
13
+ }
14
+ export declare function analogCompilerPlugin(pluginOptions: AnalogCompilerPluginOptions): Plugin;
@@ -0,0 +1,257 @@
1
+ import { TS_EXT_REGEX, createDepOptimizerConfig, getTsConfigPath } from "./utils/plugin-config.js";
2
+ import { toVirtualRawId, toVirtualStyleId } from "./utils/virtual-ids.js";
3
+ import { loadVirtualRawModule, loadVirtualStyleModule, rewriteHtmlRawImport, rewriteInlineStyleImport } from "./utils/virtual-resources.js";
4
+ import { promises } from "node:fs";
5
+ import { dirname, resolve } from "node:path";
6
+ import * as compilerCli from "@angular/compiler-cli";
7
+ import * as vite from "vite";
8
+ import { defaultClientConditions, normalizePath, preprocessCSS } from "vite";
9
+ import { collectImportedPackages, collectRelativeReExports, compile, debugCompile, debugRegistry, extractInlineStyles, generateHmrCode, inlineResourceUrls, jitTransform, scanFile, scanPackageDts } from "@analogjs/angular-compiler";
10
+ //#region packages/vite-plugin-angular/src/lib/analog-compiler-plugin.ts
11
+ function analogCompilerPlugin(pluginOptions) {
12
+ let resolvedConfig;
13
+ let tsConfigResolutionContext = null;
14
+ let watchMode = false;
15
+ const analogRegistry = /* @__PURE__ */ new Map();
16
+ const analogResourceToSource = /* @__PURE__ */ new Map();
17
+ const scannedDtsPackages = /* @__PURE__ */ new Set();
18
+ let analogProjectRoot = "";
19
+ let useDefineForClassFields = true;
20
+ /**
21
+ * Scan a file into the registry, then recursively walk its relative
22
+ * `export *` / `export { … } from './x'` chain so any underlying
23
+ * directive classes also land in the registry. Used both at
24
+ * `buildStart` (for tsconfig path entries) and at dev time (file
25
+ * `add` and `handleHotUpdate`) so newly added barrels stay in sync
26
+ * without requiring a server restart.
27
+ *
28
+ * The `visited` set prevents infinite recursion within a single
29
+ * top-level call. Each fresh scan should pass an empty set (so HMR
30
+ * re-scans aren't blocked by buildStart's earlier visits).
31
+ */
32
+ async function scanBarrelExports(file, visited = /* @__PURE__ */ new Set(), overwrite = false) {
33
+ if (visited.has(file)) return;
34
+ visited.add(file);
35
+ let code;
36
+ try {
37
+ code = await promises.readFile(file, "utf-8");
38
+ } catch (e) {
39
+ if (debugRegistry.enabled) debugRegistry("scanBarrelExports: failed to read %s: %s", file, e?.message);
40
+ return;
41
+ }
42
+ const entries = scanFile(code, file);
43
+ for (const entry of entries) if (overwrite || !analogRegistry.has(entry.className)) analogRegistry.set(entry.className, entry);
44
+ const dir = dirname(file);
45
+ for (const rel of collectRelativeReExports(code, file)) {
46
+ const normalizedRel = rel.replace(/\.(?:js|mjs)$/u, "");
47
+ const reExportCandidates = [resolve(dir, normalizedRel + ".ts"), resolve(dir, normalizedRel, "index.ts")];
48
+ let resolved = false;
49
+ for (const candidate of reExportCandidates) try {
50
+ await promises.access(candidate);
51
+ await scanBarrelExports(candidate, visited, overwrite);
52
+ resolved = true;
53
+ break;
54
+ } catch {}
55
+ if (!resolved && debugRegistry.enabled) debugRegistry("scanBarrelExports: %s re-export %s did not resolve to %o", file, rel, reExportCandidates);
56
+ }
57
+ }
58
+ async function initAnalogCompiler() {
59
+ if (pluginOptions.jit) return;
60
+ analogRegistry.clear();
61
+ scannedDtsPackages.clear();
62
+ const resolvedTsConfigPath = resolveTsConfigPath();
63
+ analogProjectRoot = dirname(resolvedTsConfigPath);
64
+ const config = compilerCli.readConfiguration(resolvedTsConfigPath);
65
+ useDefineForClassFields = config.options?.useDefineForClassFields ?? true;
66
+ const candidates = new Set(config.rootNames);
67
+ const tsPaths = config.options?.paths;
68
+ const baseUrl = config.options?.baseUrl ?? analogProjectRoot;
69
+ if (tsPaths) for (const targets of Object.values(tsPaths)) for (const target of targets) {
70
+ if (target.includes("*")) continue;
71
+ candidates.add(resolve(baseUrl, target));
72
+ }
73
+ const results = await Promise.all(Array.from(candidates).map(async (file) => {
74
+ try {
75
+ return scanFile(await promises.readFile(file, "utf-8"), file);
76
+ } catch (e) {
77
+ if (debugRegistry.enabled) debugRegistry("initAnalogCompiler: skipping unreadable %s: %s", file, e?.message);
78
+ return [];
79
+ }
80
+ }));
81
+ for (const entries of results) for (const entry of entries) analogRegistry.set(entry.className, entry);
82
+ const buildStartVisited = /* @__PURE__ */ new Set();
83
+ if (tsPaths) {
84
+ const barrelCandidates = [];
85
+ for (const targets of Object.values(tsPaths)) for (const target of targets) {
86
+ if (target.includes("*")) continue;
87
+ barrelCandidates.push(resolve(baseUrl, target));
88
+ }
89
+ await Promise.all(barrelCandidates.map((c) => scanBarrelExports(c, buildStartVisited)));
90
+ }
91
+ debugRegistry("initAnalogCompiler done: %d entries from %d candidate files", analogRegistry.size, candidates.size);
92
+ }
93
+ function ensureDtsRegistryForSource(code, id) {
94
+ for (const pkg of collectImportedPackages(code, id)) {
95
+ if (scannedDtsPackages.has(pkg)) continue;
96
+ scannedDtsPackages.add(pkg);
97
+ try {
98
+ const dtsEntries = scanPackageDts(pkg, analogProjectRoot);
99
+ for (const entry of dtsEntries) if (!analogRegistry.has(entry.className)) analogRegistry.set(entry.className, entry);
100
+ } catch {}
101
+ }
102
+ }
103
+ async function handleAnalogCompilerTransform(code, id) {
104
+ if (!/(Component|Directive|Pipe|Injectable|NgModule)\(/.test(code)) return;
105
+ if (pluginOptions.jit) {
106
+ const result = jitTransform(code, id);
107
+ return {
108
+ code: result.code,
109
+ map: result.map
110
+ };
111
+ }
112
+ code = inlineResourceUrls(code, id);
113
+ let resolvedStyles;
114
+ let resolvedInlineStyles;
115
+ if (pluginOptions.inlineStylesExtension !== "css") {
116
+ const styleStrings = extractInlineStyles(code, id);
117
+ if (styleStrings.length > 0) {
118
+ resolvedInlineStyles = /* @__PURE__ */ new Map();
119
+ for (let i = 0; i < styleStrings.length; i++) try {
120
+ const fakePath = id.replace(/\.ts$/, `.inline-${i}.${pluginOptions.inlineStylesExtension}`);
121
+ const processed = await preprocessCSS(styleStrings[i], fakePath, resolvedConfig);
122
+ resolvedInlineStyles.set(i, processed.code);
123
+ } catch (e) {
124
+ if (debugCompile.enabled) debugCompile("inline style #%d preprocessing failed in %s: %s", i, id, e?.message);
125
+ }
126
+ if (resolvedInlineStyles.size === 0) resolvedInlineStyles = void 0;
127
+ }
128
+ }
129
+ ensureDtsRegistryForSource(code, id);
130
+ const result = compile(code, id, {
131
+ registry: analogRegistry,
132
+ resolvedStyles,
133
+ resolvedInlineStyles,
134
+ useDefineForClassFields,
135
+ compilationMode: pluginOptions.analogCompilationMode
136
+ });
137
+ for (const dep of result.resourceDependencies) analogResourceToSource.set(dep, id);
138
+ let outputCode = (vite.transformWithOxc ? await vite.transformWithOxc(result.code, id, {
139
+ lang: "ts",
140
+ sourcemap: false,
141
+ decorator: {
142
+ legacy: false,
143
+ emitDecoratorMetadata: false
144
+ }
145
+ }) : await vite.transformWithEsbuild(result.code, id, {
146
+ loader: "ts",
147
+ sourcemap: false
148
+ })).code;
149
+ if (watchMode && pluginOptions.liveReload) {
150
+ const fileDeclarations = [...analogRegistry.values()].filter((e) => e.fileName === id);
151
+ if (fileDeclarations.length > 0) {
152
+ const localDepClassNames = fileDeclarations.map((e) => e.className);
153
+ outputCode += generateHmrCode(fileDeclarations, localDepClassNames);
154
+ }
155
+ }
156
+ return {
157
+ code: outputCode,
158
+ map: result.map
159
+ };
160
+ }
161
+ function resolveTsConfigPath() {
162
+ const { root, isProd, isLib } = tsConfigResolutionContext;
163
+ return getTsConfigPath(root, pluginOptions.tsconfigGetter(), isProd, pluginOptions.isTest, isLib);
164
+ }
165
+ return {
166
+ name: "@analogjs/vite-plugin-angular-compiler",
167
+ enforce: "pre",
168
+ async config(config, { command }) {
169
+ watchMode = command === "serve";
170
+ const isProd = config.mode === "production" || process.env.NODE_ENV === "production";
171
+ tsConfigResolutionContext = {
172
+ root: config.root || ".",
173
+ isProd,
174
+ isLib: !!config?.build?.lib
175
+ };
176
+ const depOptimizer = createDepOptimizerConfig({
177
+ tsconfig: resolveTsConfigPath(),
178
+ isProd,
179
+ jit: pluginOptions.jit,
180
+ watchMode,
181
+ isTest: pluginOptions.isTest,
182
+ isAstroIntegration: pluginOptions.isAstroIntegration
183
+ });
184
+ return {
185
+ ...vite.rolldownVersion ? { oxc: {} } : { esbuild: false },
186
+ ...depOptimizer,
187
+ resolve: { conditions: [...depOptimizer.resolve.conditions, ...config.resolve?.conditions || defaultClientConditions] }
188
+ };
189
+ },
190
+ configResolved(config) {
191
+ resolvedConfig = config;
192
+ },
193
+ configureServer(server) {
194
+ server.watcher.on("add", async (filePath) => {
195
+ if (filePath.endsWith(".ts") && !filePath.endsWith(".spec.ts") && !filePath.endsWith(".d.ts")) await scanBarrelExports(filePath, /* @__PURE__ */ new Set(), true);
196
+ });
197
+ },
198
+ async buildStart() {
199
+ await initAnalogCompiler();
200
+ },
201
+ async handleHotUpdate(ctx) {
202
+ if (analogResourceToSource.has(ctx.file)) {
203
+ const parentSource = analogResourceToSource.get(ctx.file);
204
+ const parentModule = ctx.server.moduleGraph.getModuleById(parentSource);
205
+ if (parentModule) return [parentModule];
206
+ }
207
+ if (TS_EXT_REGEX.test(ctx.file)) {
208
+ const [fileId] = ctx.file.split("?");
209
+ const oldEntries = [...analogRegistry.entries()].filter(([_, v]) => v.fileName === fileId).map(([k]) => k);
210
+ for (const key of oldEntries) analogRegistry.delete(key);
211
+ await scanBarrelExports(fileId, /* @__PURE__ */ new Set(), true);
212
+ }
213
+ return ctx.modules;
214
+ },
215
+ resolveId(id, importer) {
216
+ if (id.startsWith("virtual:@analogjs/vite-plugin-angular:inline-style:") || id.startsWith("virtual:@analogjs/vite-plugin-angular:raw:")) return `\0${id}`;
217
+ if (pluginOptions.jit && id.startsWith("angular:jit:")) {
218
+ const filePath = normalizePath(resolve(dirname(importer), id.split(";")[1]));
219
+ return id.includes(":style") ? toVirtualStyleId(filePath) : toVirtualRawId(filePath);
220
+ }
221
+ const rawRewrite = rewriteHtmlRawImport(id, importer);
222
+ if (rawRewrite) return rawRewrite;
223
+ const inlineRewrite = rewriteInlineStyleImport(id, importer);
224
+ if (inlineRewrite) return inlineRewrite;
225
+ },
226
+ async load(id) {
227
+ const styleModule = await loadVirtualStyleModule(this, id, resolvedConfig);
228
+ if (styleModule !== void 0) return styleModule;
229
+ const rawModule = await loadVirtualRawModule(this, id);
230
+ if (rawModule !== void 0) return rawModule;
231
+ if (/\.(css|scss|sass|less)\?inline$/.test(id)) {
232
+ const filePath = id.split("?")[0];
233
+ const result = await preprocessCSS(await promises.readFile(filePath, "utf-8"), filePath, resolvedConfig);
234
+ return `export default ${JSON.stringify(result.code)}`;
235
+ }
236
+ },
237
+ transform: {
238
+ filter: { id: {
239
+ include: [TS_EXT_REGEX],
240
+ exclude: [
241
+ /node_modules/,
242
+ "type=script",
243
+ "@ng/component"
244
+ ]
245
+ } },
246
+ async handler(code, id) {
247
+ if (pluginOptions.transformFilter && !(pluginOptions.transformFilter(code, id) ?? true)) return;
248
+ if (id.includes(".ts?")) id = id.replace(/\?(.*)/, "");
249
+ return handleAnalogCompilerTransform(code, id);
250
+ }
251
+ }
252
+ };
253
+ }
254
+ //#endregion
255
+ export { analogCompilerPlugin };
256
+
257
+ //# sourceMappingURL=analog-compiler-plugin.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"analog-compiler-plugin.js","names":[],"sources":["../../../src/lib/analog-compiler-plugin.ts"],"sourcesContent":["import { promises as fsPromises } from 'node:fs';\nimport { dirname, resolve } from 'node:path';\nimport * as vite from 'vite';\n\nimport * as compilerCli from '@angular/compiler-cli';\nimport {\n defaultClientConditions,\n normalizePath,\n Plugin,\n preprocessCSS,\n ResolvedConfig,\n} from 'vite';\n\nimport {\n compile,\n scanFile,\n scanPackageDts,\n collectImportedPackages,\n collectRelativeReExports,\n jitTransform,\n inlineResourceUrls,\n extractInlineStyles,\n generateHmrCode,\n debugCompile,\n debugRegistry,\n type ComponentRegistry,\n} from '@analogjs/angular-compiler';\n\nimport {\n TS_EXT_REGEX,\n getTsConfigPath,\n createDepOptimizerConfig,\n type TsConfigResolutionContext,\n} from './utils/plugin-config.js';\nimport {\n VIRTUAL_RAW_PREFIX,\n VIRTUAL_STYLE_PREFIX,\n toVirtualRawId,\n toVirtualStyleId,\n} from './utils/virtual-ids.js';\nimport {\n loadVirtualRawModule,\n loadVirtualStyleModule,\n rewriteHtmlRawImport,\n rewriteInlineStyleImport,\n} from './utils/virtual-resources.js';\n\nexport interface AnalogCompilerPluginOptions {\n tsconfigGetter: () => string;\n workspaceRoot: string;\n inlineStylesExtension: string;\n jit: boolean;\n liveReload: boolean;\n supportedBrowsers: string[];\n transformFilter?: (code: string, id: string) => boolean;\n isTest: boolean;\n isAstroIntegration: boolean;\n analogCompilationMode?: 'full' | 'partial';\n}\n\nexport function analogCompilerPlugin(\n pluginOptions: AnalogCompilerPluginOptions,\n): Plugin {\n let resolvedConfig: ResolvedConfig;\n let tsConfigResolutionContext: TsConfigResolutionContext | null = null;\n let watchMode = false;\n\n // Analog compiler state\n const analogRegistry: ComponentRegistry = new Map();\n const analogResourceToSource = new Map<string, string>();\n const scannedDtsPackages = new Set<string>();\n let analogProjectRoot = '';\n let useDefineForClassFields = true;\n\n /**\n * Scan a file into the registry, then recursively walk its relative\n * `export *` / `export { … } from './x'` chain so any underlying\n * directive classes also land in the registry. Used both at\n * `buildStart` (for tsconfig path entries) and at dev time (file\n * `add` and `handleHotUpdate`) so newly added barrels stay in sync\n * without requiring a server restart.\n *\n * The `visited` set prevents infinite recursion within a single\n * top-level call. Each fresh scan should pass an empty set (so HMR\n * re-scans aren't blocked by buildStart's earlier visits).\n */\n async function scanBarrelExports(\n file: string,\n visited: Set<string> = new Set(),\n overwrite = false,\n ): Promise<void> {\n if (visited.has(file)) return;\n visited.add(file);\n let code: string;\n try {\n code = await fsPromises.readFile(file, 'utf-8');\n } catch (e) {\n if (debugRegistry.enabled) {\n debugRegistry(\n 'scanBarrelExports: failed to read %s: %s',\n file,\n (e as Error)?.message,\n );\n }\n return;\n }\n const entries = scanFile(code, file);\n for (const entry of entries) {\n // At buildStart we want stable registry entries (don't overwrite\n // an earlier scan with a barrel re-scan); HMR explicitly asks\n // for overwrite so updated metadata replaces stale entries.\n if (overwrite || !analogRegistry.has(entry.className)) {\n analogRegistry.set(entry.className, entry);\n }\n }\n // Collect every relative re-export specifier via OXC AST so\n // recursive scans can't trip over each other (a shared `/g` regex\n // would have its `lastIndex` reset by each recursive call and\n // silently skip half of an outer barrel's re-exports, which\n // previously left directives like `HlmRadioGroup` unregistered).\n const dir = dirname(file);\n for (const rel of collectRelativeReExports(code, file)) {\n // NodeNext-style libraries write `export * from './foo.js'`\n // even though the source is `./foo.ts`. Strip the ESM\n // extension before probing or the candidates would be\n // `foo.js.ts` / `foo.js/index.ts`, which never exist.\n const normalizedRel = rel.replace(/\\.(?:js|mjs)$/u, '');\n const reExportCandidates = [\n resolve(dir, normalizedRel + '.ts'),\n resolve(dir, normalizedRel, 'index.ts'),\n ];\n let resolved = false;\n for (const candidate of reExportCandidates) {\n try {\n await fsPromises.access(candidate);\n await scanBarrelExports(candidate, visited, overwrite);\n resolved = true;\n break;\n } catch {\n // try next candidate\n }\n }\n if (!resolved && debugRegistry.enabled) {\n debugRegistry(\n 'scanBarrelExports: %s re-export %s did not resolve to %o',\n file,\n rel,\n reExportCandidates,\n );\n }\n }\n }\n\n async function initAnalogCompiler() {\n if (pluginOptions.jit) return; // JIT: no registry scan needed\n\n // Scan all source files to build the registry\n analogRegistry.clear();\n scannedDtsPackages.clear();\n const resolvedTsConfigPath = resolveTsConfigPath();\n analogProjectRoot = dirname(resolvedTsConfigPath);\n const config = compilerCli.readConfiguration(resolvedTsConfigPath);\n useDefineForClassFields = config.options?.useDefineForClassFields ?? true;\n\n // Collect candidate files: tsconfig rootNames PLUS the entry points\n // named in `compilerOptions.paths`. App tsconfigs typically only\n // include the app's own sources, so workspace library entry barrels\n // (e.g. `HlmSelectImports = [HlmSelect, HlmSelectContent, ...] as\n // const`) live outside `rootNames` and would otherwise miss the\n // initial scan. The compiler then can't see that `HlmSelectImports`\n // is a tuple barrel and emits the bare identifier into the parent\n // component's `dependencies()` list, where Angular's runtime\n // silently drops it because arrays don't have a directive def.\n const candidates = new Set<string>(config.rootNames);\n const tsPaths = config.options?.paths;\n const baseUrl = (config.options?.baseUrl ?? analogProjectRoot) as string;\n if (tsPaths) {\n for (const targets of Object.values(tsPaths)) {\n for (const target of targets as string[]) {\n // Skip wildcard patterns — entry barrels are normally exact\n // file paths like \"libs/helm/select/src/index.ts\".\n if (target.includes('*')) continue;\n candidates.add(resolve(baseUrl, target));\n }\n }\n }\n const results = await Promise.all(\n Array.from(candidates).map(async (file) => {\n try {\n const code = await fsPromises.readFile(file, 'utf-8');\n return scanFile(code, file);\n } catch (e) {\n if (debugRegistry.enabled) {\n debugRegistry(\n 'initAnalogCompiler: skipping unreadable %s: %s',\n file,\n (e as Error)?.message,\n );\n }\n return []; // Skip unreadable files\n }\n }),\n );\n\n for (const entries of results) {\n for (const entry of entries) {\n analogRegistry.set(entry.className, entry);\n }\n }\n\n // Library barrels typically `export * from './lib/...'` rather than\n // declaring directives directly, so the entry file alone gives us\n // the tuple consts but not the directive classes they reference.\n // Walk the relative `export *` chain so the underlying classes also\n // land in the registry. Use a SHARED visited set across all\n // barrels so recursive walks don't double-scan a file that's\n // re-exported from multiple entry points.\n const buildStartVisited = new Set<string>();\n if (tsPaths) {\n const barrelCandidates: string[] = [];\n for (const targets of Object.values(tsPaths)) {\n for (const target of targets as string[]) {\n if (target.includes('*')) continue;\n barrelCandidates.push(resolve(baseUrl, target));\n }\n }\n await Promise.all(\n barrelCandidates.map((c) => scanBarrelExports(c, buildStartVisited)),\n );\n }\n debugRegistry(\n 'initAnalogCompiler done: %d entries from %d candidate files',\n analogRegistry.size,\n candidates.size,\n );\n }\n\n function ensureDtsRegistryForSource(code: string, id: string) {\n for (const pkg of collectImportedPackages(code, id)) {\n if (scannedDtsPackages.has(pkg)) continue;\n scannedDtsPackages.add(pkg);\n\n try {\n const dtsEntries = scanPackageDts(pkg, analogProjectRoot);\n for (const entry of dtsEntries) {\n if (!analogRegistry.has(entry.className)) {\n analogRegistry.set(entry.className, entry);\n }\n }\n } catch {\n // Package may not have .d.ts files or may not be Angular\n }\n }\n }\n\n async function handleAnalogCompilerTransform(\n code: string,\n id: string,\n ): Promise<{ code: string; map: any } | undefined> {\n if (!/(Component|Directive|Pipe|Injectable|NgModule)\\(/.test(code)) {\n // Non-Angular file — leave it alone so a downstream plugin (or\n // Vite's built-in TS handler) can process it.\n return undefined;\n }\n\n // JIT mode\n if (pluginOptions.jit) {\n const result = jitTransform(code, id);\n return { code: result.code, map: result.map };\n }\n\n // Inline external templateUrl/styleUrl(s) into the source before compilation\n code = inlineResourceUrls(code, id);\n\n // Pre-resolve inline styles that need preprocessing (SCSS/Sass/Less)\n let resolvedStyles: Map<string, string> | undefined;\n let resolvedInlineStyles: Map<number, string> | undefined;\n\n if (pluginOptions.inlineStylesExtension !== 'css') {\n const styleStrings = extractInlineStyles(code, id);\n\n if (styleStrings.length > 0) {\n resolvedInlineStyles = new Map();\n for (let i = 0; i < styleStrings.length; i++) {\n try {\n const fakePath = id.replace(\n /\\.ts$/,\n `.inline-${i}.${pluginOptions.inlineStylesExtension}`,\n );\n const processed = await preprocessCSS(\n styleStrings[i],\n fakePath,\n resolvedConfig,\n );\n resolvedInlineStyles.set(i, processed.code);\n } catch (e) {\n if (debugCompile.enabled) {\n debugCompile(\n 'inline style #%d preprocessing failed in %s: %s',\n i,\n id,\n (e as Error)?.message,\n );\n }\n // Skip styles that can't be preprocessed\n }\n }\n if (resolvedInlineStyles.size === 0) resolvedInlineStyles = undefined;\n }\n }\n\n ensureDtsRegistryForSource(code, id);\n\n const result = compile(code, id, {\n registry: analogRegistry,\n resolvedStyles,\n resolvedInlineStyles,\n useDefineForClassFields,\n compilationMode: pluginOptions.analogCompilationMode,\n });\n\n // Track resource dependencies for HMR\n for (const dep of result.resourceDependencies) {\n analogResourceToSource.set(dep, id);\n }\n\n // Strip TypeScript-only syntax\n const stripped = vite.transformWithOxc\n ? await vite.transformWithOxc(result.code, id, {\n lang: 'ts',\n sourcemap: false,\n decorator: { legacy: false, emitDecoratorMetadata: false },\n })\n : await vite.transformWithEsbuild(result.code, id, {\n loader: 'ts',\n sourcemap: false,\n });\n let outputCode = stripped.code;\n\n // Append HMR code in dev mode\n if (watchMode && pluginOptions.liveReload) {\n const fileDeclarations = [...analogRegistry.values()].filter(\n (e) => e.fileName === id,\n );\n if (fileDeclarations.length > 0) {\n const localDepClassNames = fileDeclarations.map((e) => e.className);\n outputCode += generateHmrCode(fileDeclarations, localDepClassNames);\n }\n }\n\n return { code: outputCode, map: result.map };\n }\n\n function resolveTsConfigPath() {\n const { root, isProd, isLib } = tsConfigResolutionContext!;\n return getTsConfigPath(\n root,\n pluginOptions.tsconfigGetter(),\n isProd,\n pluginOptions.isTest,\n isLib,\n );\n }\n\n return {\n name: '@analogjs/vite-plugin-angular-compiler',\n enforce: 'pre' as const,\n async config(config, { command }) {\n watchMode = command === 'serve';\n const isProd =\n config.mode === 'production' ||\n process.env['NODE_ENV'] === 'production';\n\n tsConfigResolutionContext = {\n root: config.root || '.',\n isProd,\n isLib: !!config?.build?.lib,\n };\n\n const preliminaryTsConfigPath = resolveTsConfigPath();\n\n const depOptimizer = createDepOptimizerConfig({\n tsconfig: preliminaryTsConfigPath,\n isProd,\n jit: pluginOptions.jit,\n watchMode,\n isTest: pluginOptions.isTest,\n isAstroIntegration: pluginOptions.isAstroIntegration,\n });\n\n return {\n ...(vite.rolldownVersion ? { oxc: {} as any } : { esbuild: false }),\n ...depOptimizer,\n resolve: {\n conditions: [\n ...depOptimizer.resolve.conditions,\n ...(config.resolve?.conditions || defaultClientConditions),\n ],\n },\n };\n },\n configResolved(config) {\n resolvedConfig = config;\n },\n configureServer(server) {\n // Watch for new .ts files and scan them into the registry. Use\n // the barrel-aware scanner so a newly added re-export entry\n // (`export * from './x'`) also expands its underlying directive\n // classes — otherwise the registry stays stale until restart.\n server.watcher.on('add', async (filePath) => {\n if (\n filePath.endsWith('.ts') &&\n !filePath.endsWith('.spec.ts') &&\n !filePath.endsWith('.d.ts')\n ) {\n await scanBarrelExports(filePath, new Set(), true);\n }\n });\n },\n async buildStart() {\n await initAnalogCompiler();\n },\n async handleHotUpdate(ctx) {\n // Resource file changes → invalidate parent .ts module\n if (analogResourceToSource.has(ctx.file)) {\n const parentSource = analogResourceToSource.get(ctx.file)!;\n const parentModule = ctx.server.moduleGraph.getModuleById(parentSource);\n if (parentModule) {\n return [parentModule];\n }\n }\n\n if (TS_EXT_REGEX.test(ctx.file)) {\n const [fileId] = ctx.file.split('?');\n\n // Remove old entries from this file\n const oldEntries = [...analogRegistry.entries()]\n .filter(([_, v]) => v.fileName === fileId)\n .map(([k]) => k);\n for (const key of oldEntries) {\n analogRegistry.delete(key);\n }\n\n // Rescan the changed file via the barrel-aware scanner so an\n // edited barrel re-export picks up newly-referenced files.\n // Pass overwrite=true so updated metadata replaces stale\n // entries from the previous scan.\n await scanBarrelExports(fileId, new Set(), true);\n }\n\n // Let Vite handle the rest — the transform hook will recompile\n return ctx.modules;\n },\n resolveId(id, importer) {\n if (\n id.startsWith(VIRTUAL_STYLE_PREFIX) ||\n id.startsWith(VIRTUAL_RAW_PREFIX)\n ) {\n return `\\0${id}`;\n }\n\n if (pluginOptions.jit && id.startsWith('angular:jit:')) {\n const filePath = normalizePath(\n resolve(dirname(importer as string), id.split(';')[1]),\n );\n return id.includes(':style')\n ? toVirtualStyleId(filePath)\n : toVirtualRawId(filePath);\n }\n\n const rawRewrite = rewriteHtmlRawImport(id, importer);\n if (rawRewrite) return rawRewrite;\n\n const inlineRewrite = rewriteInlineStyleImport(id, importer);\n if (inlineRewrite) return inlineRewrite;\n\n return undefined;\n },\n async load(id) {\n const styleModule = await loadVirtualStyleModule(\n this,\n id,\n resolvedConfig,\n );\n if (styleModule !== undefined) return styleModule;\n\n const rawModule = await loadVirtualRawModule(this, id);\n if (rawModule !== undefined) return rawModule;\n\n // Vitest bypass: module-runner skips resolveId, so the bare `?inline`\n // query reaches load unchanged.\n if (/\\.(css|scss|sass|less)\\?inline$/.test(id)) {\n const filePath = id.split('?')[0];\n const code = await fsPromises.readFile(filePath, 'utf-8');\n const result = await preprocessCSS(code, filePath, resolvedConfig);\n return `export default ${JSON.stringify(result.code)}`;\n }\n\n return;\n },\n transform: {\n filter: {\n id: {\n include: [TS_EXT_REGEX],\n exclude: [/node_modules/, 'type=script', '@ng/component'],\n },\n },\n async handler(code, id) {\n if (\n pluginOptions.transformFilter &&\n !(pluginOptions.transformFilter(code, id) ?? true)\n ) {\n return;\n }\n\n if (id.includes('.ts?')) {\n id = id.replace(/\\?(.*)/, '');\n }\n return handleAnalogCompilerTransform(code, id);\n },\n },\n };\n}\n"],"mappings":"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"}
@@ -46,6 +46,13 @@ export interface PluginOptions {
46
46
  fileReplacements?: FileReplacement[];
47
47
  experimental?: {
48
48
  useAngularCompilationAPI?: boolean;
49
+ useAnalogCompiler?: boolean;
50
+ /**
51
+ * Compilation output mode for the Analog compiler.
52
+ * - `'full'` (default): Emit final Ivy definitions for application builds.
53
+ * - `'partial'`: Emit partial declarations for library publishing.
54
+ */
55
+ analogCompilationMode?: "full" | "partial";
49
56
  };
50
57
  /**
51
58
  * Enable debug logging for specific scopes.
@@ -8,6 +8,10 @@ import { StyleUrlsResolver, TemplateUrlsResolver, getAngularComponentMetadata }
8
8
  import { composeStylePreprocessors, normalizeStylesheetDependencies } from "./style-preprocessor.js";
9
9
  import { AnalogStylesheetRegistry, preprocessStylesheet, preprocessStylesheetResult, registerStylesheetContent, rewriteRelativeCssImports } from "./stylesheet-registry.js";
10
10
  import { augmentHostWithCaching, augmentHostWithResources, augmentProgramWithVersioning, mergeTransformers } from "./host.js";
11
+ import { TS_EXT_REGEX, createTsConfigGetter, getTsConfigPath } from "./utils/plugin-config.js";
12
+ import { toVirtualRawId, toVirtualStyleId } from "./utils/virtual-ids.js";
13
+ import { loadVirtualRawModule, loadVirtualStyleModule } from "./utils/virtual-resources.js";
14
+ import { analogCompilerPlugin } from "./analog-compiler-plugin.js";
11
15
  import { angularVitestPlugins } from "./angular-vitest-plugin.js";
12
16
  import { pendingTasksPlugin } from "./angular-pending-tasks.plugin.js";
13
17
  import { liveReloadPlugin } from "./live-reload-plugin.js";
@@ -17,7 +21,7 @@ import { routerPlugin } from "./router-plugin.js";
17
21
  import { configureStylePipelineRegistry, stylePipelinePreprocessorFromPlugins } from "./style-pipeline.js";
18
22
  import { union } from "es-toolkit";
19
23
  import { createHash } from "node:crypto";
20
- import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
24
+ import { existsSync, mkdirSync, promises, readFileSync, statSync, writeFileSync } from "node:fs";
21
25
  import { basename, dirname, isAbsolute, join, relative, resolve } from "node:path";
22
26
  import * as compilerCli from "@angular/compiler-cli";
23
27
  import { createRequire } from "node:module";
@@ -41,12 +45,6 @@ function normalizeIncludeGlob(workspaceRoot, glob) {
41
45
  if (normalizedGlob.startsWith("/")) return `${normalizedWorkspaceRoot}${normalizedGlob}`;
42
46
  return normalizePath(resolve(normalizedWorkspaceRoot, normalizedGlob));
43
47
  }
44
- /**
45
- * TypeScript file extension regex
46
- * Match .(c or m)ts, .ts extensions with an optional ? for query params
47
- * Ignore .tsx extensions
48
- */
49
- var TS_EXT_REGEX = /\.[cm]?(ts)[^x]?\??/;
50
48
  var classNames = /* @__PURE__ */ new Map();
51
49
  function evictDeletedFileMetadata(file, { removeActiveGraphMetadata, removeStyleOwnerMetadata, classNamesMap, fileTransformMap }) {
52
50
  const normalizedFile = normalizePath(file.split("?")[0]);
@@ -712,9 +710,22 @@ function angular(options) {
712
710
  return ctx.modules;
713
711
  },
714
712
  resolveId(id, importer) {
713
+ if (id.startsWith("virtual:@analogjs/vite-plugin-angular:inline-style:") || id.startsWith("virtual:@analogjs/vite-plugin-angular:raw:")) return `\0${id}`;
715
714
  if (jit && id.startsWith("angular:jit:")) {
716
715
  const path = id.split(";")[1];
717
- return `${normalizePath(resolve(dirname(importer), path))}?${id.includes(":style") ? "inline" : "raw"}`;
716
+ const resolved = normalizePath(resolve(dirname(importer), path));
717
+ if (id.includes(":style")) return toVirtualStyleId(resolved);
718
+ return toVirtualRawId(resolved);
719
+ }
720
+ if (id.includes(".html?raw")) {
721
+ const filePath = id.split("?")[0];
722
+ const resolved = isAbsolute(filePath) ? normalizePath(filePath) : importer ? normalizePath(resolve(dirname(importer), filePath)) : void 0;
723
+ if (resolved) return toVirtualRawId(resolved);
724
+ }
725
+ if (/\.(css|scss|sass|less)\?inline$/.test(id)) {
726
+ const filePath = id.split("?")[0];
727
+ const resolved = isAbsolute(filePath) ? normalizePath(filePath) : importer ? normalizePath(resolve(dirname(importer), filePath)) : void 0;
728
+ if (resolved) return toVirtualStyleId(resolved);
718
729
  }
719
730
  if (isComponentStyleSheet(id)) {
720
731
  const filename = getFilenameFromPath(id);
@@ -738,6 +749,15 @@ function angular(options) {
738
749
  }
739
750
  },
740
751
  async load(id) {
752
+ const styleModule = await loadVirtualStyleModule(this, id, resolvedConfig);
753
+ if (styleModule !== void 0) return styleModule;
754
+ const rawModule = await loadVirtualRawModule(this, id);
755
+ if (rawModule !== void 0) return rawModule;
756
+ if (/\.(css|scss|sass|less)\?inline$/.test(id)) {
757
+ const filePath = id.split("?")[0];
758
+ const result = await preprocessCSS(await promises.readFile(filePath, "utf-8"), filePath, resolvedConfig);
759
+ return `export default ${JSON.stringify(result.code)}`;
760
+ }
741
761
  if (isComponentStyleSheet(id)) {
742
762
  const filename = getFilenameFromPath(id);
743
763
  const componentStyles = stylesheetRegistry?.getServedContent(filename);
@@ -836,18 +856,22 @@ function angular(options) {
836
856
  pendingCompilation = null;
837
857
  }
838
858
  const typescriptResult = fileEmitter(id);
839
- if (typescriptResult?.warnings && typescriptResult?.warnings.length > 0) this.warn(`${typescriptResult.warnings.join("\n")}`);
840
- if (typescriptResult?.errors && typescriptResult?.errors.length > 0) this.error(`${typescriptResult.errors.join("\n")}`);
841
- let data = typescriptResult?.content ?? "";
859
+ if (!typescriptResult) {
860
+ if (!id.includes("@ng/component") && /(Component|Directive|Pipe|Injectable|NgModule)\(/.test(code)) this.warn(`[@analogjs/vite-plugin-angular]: "${id}" contains Angular decorators but is not in the TypeScript program. Ensure it is included in your tsconfig.`);
861
+ return;
862
+ }
863
+ if (typescriptResult.warnings && typescriptResult.warnings.length > 0) this.warn(`${typescriptResult.warnings.join("\n")}`);
864
+ if (typescriptResult.errors && typescriptResult.errors.length > 0) this.error(`${typescriptResult.errors.join("\n")}`);
865
+ let data = typescriptResult.content ?? "";
842
866
  if (jit && data.includes("angular:jit:")) {
843
867
  data = data.replace(/angular:jit:style:inline;/g, "virtual:angular:jit:style:inline;");
844
868
  templateUrls.forEach((templateUrlSet) => {
845
869
  const [templateFile, resolvedTemplateUrl] = templateUrlSet.split("|");
846
- data = data.replace(`angular:jit:template:file;${templateFile}`, `${resolvedTemplateUrl}?raw`);
870
+ data = data.replace(`angular:jit:template:file;${templateFile}`, toVirtualRawId(resolvedTemplateUrl));
847
871
  });
848
872
  styleUrls.forEach((styleUrlSet) => {
849
873
  const [styleFile, resolvedStyleUrl] = styleUrlSet.split("|");
850
- data = data.replace(`angular:jit:style:file;${styleFile}`, `${resolvedStyleUrl}?inline`);
874
+ data = data.replace(`angular:jit:style:file;${styleFile}`, toVirtualStyleId(resolvedStyleUrl));
851
875
  });
852
876
  }
853
877
  if (data.includes("HmrLoad")) {
@@ -879,6 +903,18 @@ function angular(options) {
879
903
  }
880
904
  };
881
905
  }
906
+ pluginOptions.useAnalogCompiler ? analogCompilerPlugin({
907
+ tsconfigGetter: pluginOptions.tsconfigGetter,
908
+ workspaceRoot: pluginOptions.workspaceRoot,
909
+ inlineStylesExtension: pluginOptions.inlineStylesExtension,
910
+ jit,
911
+ liveReload: pluginOptions.liveReload,
912
+ supportedBrowsers: pluginOptions.supportedBrowsers,
913
+ transformFilter: options?.transformFilter,
914
+ isTest,
915
+ isAstroIntegration,
916
+ analogCompilationMode: pluginOptions.analogCompilationMode
917
+ }) : angularPlugin();
882
918
  return [
883
919
  replaceFiles(pluginOptions.fileReplacements, pluginOptions.workspaceRoot),
884
920
  {
@@ -997,23 +1033,6 @@ function angular(options) {
997
1033
  absolute: true
998
1034
  });
999
1035
  }
1000
- function createTsConfigGetter(tsconfigOrGetter) {
1001
- if (typeof tsconfigOrGetter === "function") return tsconfigOrGetter;
1002
- return () => tsconfigOrGetter || "";
1003
- }
1004
- function getTsConfigPath(root, tsconfig, isProd, isTest, isLib) {
1005
- if (tsconfig && isAbsolute(tsconfig)) {
1006
- if (!existsSync(tsconfig)) console.error(`[@analogjs/vite-plugin-angular]: Unable to resolve tsconfig at ${tsconfig}. This causes compilation issues. Check the path or set the "tsconfig" property with an absolute path.`);
1007
- return tsconfig;
1008
- }
1009
- let tsconfigFilePath = "./tsconfig.app.json";
1010
- if (isLib) tsconfigFilePath = isProd ? "./tsconfig.lib.prod.json" : "./tsconfig.lib.json";
1011
- if (isTest) tsconfigFilePath = "./tsconfig.spec.json";
1012
- if (tsconfig) tsconfigFilePath = tsconfig;
1013
- const resolvedPath = resolve(root, tsconfigFilePath);
1014
- if (!existsSync(resolvedPath)) console.error(`[@analogjs/vite-plugin-angular]: Unable to resolve tsconfig at ${resolvedPath}. This causes compilation issues. Check the path or set the "tsconfig" property with an absolute path.`);
1015
- return resolvedPath;
1016
- }
1017
1036
  function resolveTsConfigPath() {
1018
1037
  const tsconfigValue = pluginOptions.tsconfigGetter();
1019
1038
  return getTsConfigPath(tsConfigResolutionContext.root, tsconfigValue, tsConfigResolutionContext.isProd, isTest, tsConfigResolutionContext.isLib);