@analogjs/vite-plugin-angular 3.0.0-alpha.41 → 3.0.0-alpha.43
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 +1 -1
- package/src/lib/angular-jit-plugin.js +1 -1
- package/src/lib/angular-vite-plugin.d.ts +3 -77
- package/src/lib/angular-vite-plugin.js +77 -1012
- package/src/lib/angular-vite-plugin.js.map +1 -1
- package/src/lib/compilation-api/compilation-api-plugin.d.ts +29 -0
- package/src/lib/compilation-api/compilation-api-plugin.js +468 -0
- package/src/lib/compilation-api/compilation-api-plugin.js.map +1 -0
- package/src/lib/compilation-api/index.d.ts +1 -0
- package/src/lib/compilation-api/index.js +1 -0
- package/src/lib/compiler/compile.js +2 -2
- package/src/lib/compiler/defer.js +1 -1
- package/src/lib/compiler/js-emitter.js +1 -1
- package/src/lib/encapsulation-plugin.d.ts +13 -0
- package/src/lib/encapsulation-plugin.js +48 -0
- package/src/lib/encapsulation-plugin.js.map +1 -0
- package/src/lib/fast-compile-plugin.js +2 -28
- package/src/lib/fast-compile-plugin.js.map +1 -1
- package/src/lib/host.js +1 -1
- package/src/lib/stylesheet-registry.js +1 -1
- package/src/lib/tailwind-plugin.d.ts +34 -0
- package/src/lib/tailwind-plugin.js +116 -0
- package/src/lib/tailwind-plugin.js.map +1 -0
- package/src/lib/template-class-binding-guard-plugin.d.ts +30 -0
- package/src/lib/template-class-binding-guard-plugin.js +237 -0
- package/src/lib/template-class-binding-guard-plugin.js.map +1 -0
- package/src/lib/utils/compilation-shared.d.ts +58 -0
- package/src/lib/utils/compilation-shared.js +133 -0
- package/src/lib/utils/compilation-shared.js.map +1 -0
- package/src/lib/utils/tsconfig-resolver.d.ts +28 -0
- package/src/lib/utils/tsconfig-resolver.js +191 -0
- package/src/lib/utils/tsconfig-resolver.js.map +1 -0
- package/src/lib/utils/virtual-resources.js +4 -31
- package/src/lib/utils/virtual-resources.js.map +1 -1
- package/src/lib/virtual-modules-plugin.d.ts +5 -0
- package/src/lib/virtual-modules-plugin.js +50 -0
- package/src/lib/virtual-modules-plugin.js.map +1 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { debugStylesV } from "./utils/debug.js";
|
|
2
|
+
import * as ngCompiler from "@angular/compiler";
|
|
3
|
+
//#region packages/vite-plugin-angular/src/lib/encapsulation-plugin.ts
|
|
4
|
+
function isComponentStyleSheet(id) {
|
|
5
|
+
return id.includes("ngcomp=");
|
|
6
|
+
}
|
|
7
|
+
function getComponentStyleSheetMeta(id) {
|
|
8
|
+
const params = new URL(id, "http://localhost").searchParams;
|
|
9
|
+
return {
|
|
10
|
+
componentId: params.get("ngcomp"),
|
|
11
|
+
encapsulation: {
|
|
12
|
+
"0": "emulated",
|
|
13
|
+
"2": "none",
|
|
14
|
+
"3": "shadow"
|
|
15
|
+
}[params.get("e")]
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Encapsulation runs in enforce: 'post' so that @tailwindcss/vite
|
|
20
|
+
* (enforce: 'pre') fully resolves @apply directives — including those
|
|
21
|
+
* inside :host {} — before Angular's ShadowCss rewrites selectors.
|
|
22
|
+
* (#2293)
|
|
23
|
+
*/
|
|
24
|
+
function encapsulationPlugin(shouldExternalizeStyles) {
|
|
25
|
+
return {
|
|
26
|
+
name: "@analogjs/vite-plugin-angular:encapsulation",
|
|
27
|
+
enforce: "post",
|
|
28
|
+
transform(code, id) {
|
|
29
|
+
if (shouldExternalizeStyles() && isComponentStyleSheet(id)) {
|
|
30
|
+
const { encapsulation, componentId } = getComponentStyleSheetMeta(id);
|
|
31
|
+
if (encapsulation === "emulated" && componentId) {
|
|
32
|
+
debugStylesV("applying emulated view encapsulation (post)", {
|
|
33
|
+
stylesheet: id.split("?")[0],
|
|
34
|
+
componentId
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
code: ngCompiler.encapsulateStyle(code, componentId),
|
|
38
|
+
map: null
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
export { encapsulationPlugin, getComponentStyleSheetMeta, isComponentStyleSheet };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=encapsulation-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encapsulation-plugin.js","names":[],"sources":["../../../src/lib/encapsulation-plugin.ts"],"sourcesContent":["import * as ngCompiler from '@angular/compiler';\nimport { Plugin } from 'vite';\nimport { debugStylesV } from './utils/debug.js';\n\nexport function isComponentStyleSheet(id: string): boolean {\n return id.includes('ngcomp=');\n}\n\nexport function getComponentStyleSheetMeta(id: string): {\n componentId: string;\n encapsulation: 'emulated' | 'shadow' | 'none';\n} {\n const params = new URL(id, 'http://localhost').searchParams;\n const encapsulationMapping = {\n '0': 'emulated',\n '2': 'none',\n '3': 'shadow',\n };\n return {\n componentId: params.get('ngcomp')!,\n encapsulation: encapsulationMapping[\n params.get('e') as keyof typeof encapsulationMapping\n ] as 'emulated' | 'shadow' | 'none',\n };\n}\n\n/**\n * Encapsulation runs in enforce: 'post' so that @tailwindcss/vite\n * (enforce: 'pre') fully resolves @apply directives — including those\n * inside :host {} — before Angular's ShadowCss rewrites selectors.\n * (#2293)\n */\nexport function encapsulationPlugin(\n shouldExternalizeStyles: () => boolean,\n): Plugin {\n return {\n name: '@analogjs/vite-plugin-angular:encapsulation',\n enforce: 'post',\n transform(code: string, id: string) {\n if (shouldExternalizeStyles() && isComponentStyleSheet(id)) {\n const { encapsulation, componentId } = getComponentStyleSheetMeta(id);\n if (encapsulation === 'emulated' && componentId) {\n debugStylesV('applying emulated view encapsulation (post)', {\n stylesheet: id.split('?')[0],\n componentId,\n });\n const encapsulated = ngCompiler.encapsulateStyle(code, componentId);\n return {\n code: encapsulated,\n map: null,\n };\n }\n }\n },\n };\n}\n"],"mappings":";;;AAIA,SAAgB,sBAAsB,IAAqB;AACzD,QAAO,GAAG,SAAS,UAAU;;AAG/B,SAAgB,2BAA2B,IAGzC;CACA,MAAM,SAAS,IAAI,IAAI,IAAI,mBAAmB,CAAC;AAM/C,QAAO;EACL,aAAa,OAAO,IAAI,SAAS;EACjC,eAP2B;GAC3B,KAAK;GACL,KAAK;GACL,KAAK;GACN,CAIG,OAAO,IAAI,IAAI;EAElB;;;;;;;;AASH,SAAgB,oBACd,yBACQ;AACR,QAAO;EACL,MAAM;EACN,SAAS;EACT,UAAU,MAAc,IAAY;AAClC,OAAI,yBAAyB,IAAI,sBAAsB,GAAG,EAAE;IAC1D,MAAM,EAAE,eAAe,gBAAgB,2BAA2B,GAAG;AACrE,QAAI,kBAAkB,cAAc,aAAa;AAC/C,kBAAa,+CAA+C;MAC1D,YAAY,GAAG,MAAM,IAAI,CAAC;MAC1B;MACD,CAAC;AAEF,YAAO;MACL,MAFmB,WAAW,iBAAiB,MAAM,YAAY;MAGjE,KAAK;MACN;;;;EAIR"}
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { loadVirtualRawModule, loadVirtualStyleModule, rewriteHtmlRawImport, rewriteInlineStyleImport, shouldPreprocessTestCss } from "./utils/virtual-resources.js";
|
|
1
|
+
import { TS_EXT_REGEX, createDepOptimizerConfig, getTsConfigPath } from "./utils/plugin-config.js";
|
|
3
2
|
import { debugCompile, debugRegistry } from "./compiler/debug.js";
|
|
4
3
|
import { compile } from "./compiler/compile.js";
|
|
5
4
|
import { scanFile } from "./compiler/registry.js";
|
|
@@ -8,12 +7,11 @@ import { jitTransform } from "./compiler/jit-transform.js";
|
|
|
8
7
|
import { generateHmrCode } from "./compiler/hmr.js";
|
|
9
8
|
import { extractInlineStyles, inlineResourceUrls } from "./compiler/resource-inliner.js";
|
|
10
9
|
import "./compiler/index.js";
|
|
11
|
-
import { TS_EXT_REGEX, createDepOptimizerConfig, getTsConfigPath } from "./utils/plugin-config.js";
|
|
12
10
|
import { promises } from "node:fs";
|
|
13
11
|
import { dirname, resolve } from "node:path";
|
|
14
12
|
import * as compilerCli from "@angular/compiler-cli";
|
|
15
13
|
import * as vite from "vite";
|
|
16
|
-
import { defaultClientConditions,
|
|
14
|
+
import { defaultClientConditions, preprocessCSS } from "vite";
|
|
17
15
|
//#region packages/vite-plugin-angular/src/lib/fast-compile-plugin.ts
|
|
18
16
|
function fastCompilePlugin(pluginOptions) {
|
|
19
17
|
let resolvedConfig;
|
|
@@ -223,30 +221,6 @@ function fastCompilePlugin(pluginOptions) {
|
|
|
223
221
|
}
|
|
224
222
|
return ctx.modules;
|
|
225
223
|
},
|
|
226
|
-
resolveId(id, importer) {
|
|
227
|
-
if (id.startsWith("virtual:@analogjs/vite-plugin-angular:inline-style:") || id.startsWith("virtual:@analogjs/vite-plugin-angular:raw:")) return `\0${id}`;
|
|
228
|
-
if (pluginOptions.jit && id.startsWith("angular:jit:")) {
|
|
229
|
-
const filePath = normalizePath(resolve(dirname(importer), id.split(";")[1]));
|
|
230
|
-
return id.includes(":style") ? toVirtualStyleId(filePath) : toVirtualRawId(filePath);
|
|
231
|
-
}
|
|
232
|
-
const rawRewrite = rewriteHtmlRawImport(id, importer);
|
|
233
|
-
if (rawRewrite) return rawRewrite;
|
|
234
|
-
const inlineRewrite = rewriteInlineStyleImport(id, importer);
|
|
235
|
-
if (inlineRewrite) return inlineRewrite;
|
|
236
|
-
},
|
|
237
|
-
async load(id) {
|
|
238
|
-
const styleModule = await loadVirtualStyleModule(this, id, resolvedConfig);
|
|
239
|
-
if (styleModule !== void 0) return styleModule;
|
|
240
|
-
const rawModule = await loadVirtualRawModule(this, id);
|
|
241
|
-
if (rawModule !== void 0) return rawModule;
|
|
242
|
-
if (/\.(css|scss|sass|less)\?inline$/.test(id)) {
|
|
243
|
-
const filePath = id.split("?")[0];
|
|
244
|
-
const code = await promises.readFile(filePath, "utf-8");
|
|
245
|
-
if (!shouldPreprocessTestCss(resolvedConfig, filePath)) return `export default ${JSON.stringify(code)}`;
|
|
246
|
-
const result = await preprocessCSS(code, filePath, resolvedConfig);
|
|
247
|
-
return `export default ${JSON.stringify(result.code)}`;
|
|
248
|
-
}
|
|
249
|
-
},
|
|
250
224
|
transform: {
|
|
251
225
|
filter: { id: {
|
|
252
226
|
include: [TS_EXT_REGEX],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fast-compile-plugin.js","names":[],"sources":["../../../src/lib/fast-compile-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 './compiler/index.js';\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 shouldPreprocessTestCss,\n} from './utils/virtual-resources.js';\n\nexport interface FastCompilePluginOptions {\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 fastCompileMode?: 'full' | 'partial';\n}\n\nexport function fastCompilePlugin(\n pluginOptions: FastCompilePluginOptions,\n): Plugin {\n let resolvedConfig: ResolvedConfig;\n let tsConfigResolutionContext: TsConfigResolutionContext | null = null;\n let watchMode = false;\n\n // fast-compile plugin state\n const registry: ComponentRegistry = new Map();\n const resourceToSource = new Map<string, string>();\n const scannedDtsPackages = new Set<string>();\n let projectRoot = '';\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 || !registry.has(entry.className)) {\n registry.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 initFastCompile() {\n if (pluginOptions.jit) return; // JIT: no registry scan needed\n\n // Scan all source files to build the registry\n registry.clear();\n scannedDtsPackages.clear();\n const resolvedTsConfigPath = resolveTsConfigPath();\n projectRoot = 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 ?? projectRoot) 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 'initFastCompile: 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 registry.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 'initFastCompile done: %d entries from %d candidate files',\n registry.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, projectRoot);\n for (const entry of dtsEntries) {\n if (!registry.has(entry.className)) {\n registry.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 handleFastCompileTransform(\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 // In tests, mirror Vitest's `test.css` rules — defaults to no\n // preprocessing (matches Vite's CSS pipeline behavior). (#2297)\n if (!shouldPreprocessTestCss(resolvedConfig, fakePath)) {\n resolvedInlineStyles.set(i, styleStrings[i]);\n continue;\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,\n resolvedStyles,\n resolvedInlineStyles,\n useDefineForClassFields,\n compilationMode: pluginOptions.fastCompileMode,\n });\n\n // Track resource dependencies for HMR\n for (const dep of result.resourceDependencies) {\n resourceToSource.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 = [...registry.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-fast-compile',\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 initFastCompile();\n },\n async handleHotUpdate(ctx) {\n // Resource file changes → invalidate parent .ts module\n if (resourceToSource.has(ctx.file)) {\n const parentSource = resourceToSource.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 = [...registry.entries()]\n .filter(([_, v]) => v.fileName === fileId)\n .map(([k]) => k);\n for (const key of oldEntries) {\n registry.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 // In tests, mirror Vitest's `test.css` rules — defaults to no\n // preprocessing (matches Vite's CSS pipeline behavior). (#2297)\n if (!shouldPreprocessTestCss(resolvedConfig, filePath)) {\n return `export default ${JSON.stringify(code)}`;\n }\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 handleFastCompileTransform(code, id);\n },\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"fast-compile-plugin.js","names":[],"sources":["../../../src/lib/fast-compile-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 './compiler/index.js';\n\nimport {\n TS_EXT_REGEX,\n getTsConfigPath,\n createDepOptimizerConfig,\n type TsConfigResolutionContext,\n} from './utils/plugin-config.js';\n\nexport interface FastCompilePluginOptions {\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 fastCompileMode?: 'full' | 'partial';\n}\n\nexport function fastCompilePlugin(\n pluginOptions: FastCompilePluginOptions,\n): Plugin {\n let resolvedConfig: ResolvedConfig;\n let tsConfigResolutionContext: TsConfigResolutionContext | null = null;\n let watchMode = false;\n\n // fast-compile plugin state\n const registry: ComponentRegistry = new Map();\n const resourceToSource = new Map<string, string>();\n const scannedDtsPackages = new Set<string>();\n let projectRoot = '';\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 || !registry.has(entry.className)) {\n registry.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 initFastCompile() {\n if (pluginOptions.jit) return; // JIT: no registry scan needed\n\n // Scan all source files to build the registry\n registry.clear();\n scannedDtsPackages.clear();\n const resolvedTsConfigPath = resolveTsConfigPath();\n projectRoot = 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 ?? projectRoot) 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 'initFastCompile: 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 registry.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 'initFastCompile done: %d entries from %d candidate files',\n registry.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, projectRoot);\n for (const entry of dtsEntries) {\n if (!registry.has(entry.className)) {\n registry.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 handleFastCompileTransform(\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 // In tests, mirror Vitest's `test.css` rules — defaults to no\n // preprocessing (matches Vite's CSS pipeline behavior). (#2297)\n if (!shouldPreprocessTestCss(resolvedConfig, fakePath)) {\n resolvedInlineStyles.set(i, styleStrings[i]);\n continue;\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,\n resolvedStyles,\n resolvedInlineStyles,\n useDefineForClassFields,\n compilationMode: pluginOptions.fastCompileMode,\n });\n\n // Track resource dependencies for HMR\n for (const dep of result.resourceDependencies) {\n resourceToSource.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 = [...registry.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-fast-compile',\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 initFastCompile();\n },\n async handleHotUpdate(ctx) {\n // Resource file changes → invalidate parent .ts module\n if (resourceToSource.has(ctx.file)) {\n const parentSource = resourceToSource.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 = [...registry.entries()]\n .filter(([_, v]) => v.fileName === fileId)\n .map(([k]) => k);\n for (const key of oldEntries) {\n registry.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 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 handleFastCompileTransform(code, id);\n },\n },\n };\n}\n"],"mappings":"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"}
|
package/src/lib/host.js
CHANGED
|
@@ -2,9 +2,9 @@ import { debugStyles } from "./utils/debug.js";
|
|
|
2
2
|
import { isTailwindReferenceError } from "./utils/tailwind-reference.js";
|
|
3
3
|
import { normalizeStylesheetDependencies } from "./style-preprocessor.js";
|
|
4
4
|
import { preprocessStylesheetResult, registerStylesheetContent } from "./stylesheet-registry.js";
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
5
|
import path from "node:path";
|
|
7
6
|
import { normalizePath } from "vite";
|
|
7
|
+
import { createHash } from "node:crypto";
|
|
8
8
|
//#region packages/vite-plugin-angular/src/lib/host.ts
|
|
9
9
|
function augmentHostWithResources(host, transform, options) {
|
|
10
10
|
const resourceHost = host;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizeStylesheetTransformResult } from "./style-preprocessor.js";
|
|
2
|
-
import { createHash } from "node:crypto";
|
|
3
2
|
import { dirname, normalize, resolve } from "node:path";
|
|
4
3
|
import { normalizePath } from "vite";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
5
|
//#region packages/vite-plugin-angular/src/lib/stylesheet-registry.ts
|
|
6
6
|
var AnalogStylesheetRegistry = class {
|
|
7
7
|
servedById = /* @__PURE__ */ new Map();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Plugin, ResolvedConfig } from "vite";
|
|
2
|
+
import type { StylePreprocessor } from "./style-preprocessor.js";
|
|
3
|
+
import type { AngularStylePipelineOptions } from "./style-pipeline.js";
|
|
4
|
+
export interface TailwindCssOptions {
|
|
5
|
+
rootStylesheet: string;
|
|
6
|
+
prefixes?: string[];
|
|
7
|
+
}
|
|
8
|
+
export interface TailwindReferencePluginOptions {
|
|
9
|
+
tailwindCss?: TailwindCssOptions;
|
|
10
|
+
}
|
|
11
|
+
export declare function tailwindReferencePlugin(options: TailwindReferencePluginOptions): Plugin;
|
|
12
|
+
/**
|
|
13
|
+
* Builds a resolved stylePreprocessor function from plugin options.
|
|
14
|
+
*
|
|
15
|
+
* When `tailwindCss` is configured, creates an injector that prepends
|
|
16
|
+
* `@reference "<rootStylesheet>"` into component CSS that uses Tailwind
|
|
17
|
+
* utilities. Uses absolute paths because Angular's externalRuntimeStyles
|
|
18
|
+
* serves component CSS as virtual modules (hash-based IDs) with no
|
|
19
|
+
* meaningful directory — relative paths can't resolve from a hash.
|
|
20
|
+
*
|
|
21
|
+
* If both `tailwindCss` and `stylePreprocessor` are provided, they are
|
|
22
|
+
* chained: Tailwind reference injection runs first, then the user's
|
|
23
|
+
* custom preprocessor.
|
|
24
|
+
*/
|
|
25
|
+
export declare function buildStylePreprocessor(options?: {
|
|
26
|
+
stylePreprocessor?: StylePreprocessor;
|
|
27
|
+
stylePipeline?: AngularStylePipelineOptions;
|
|
28
|
+
tailwindCss?: TailwindCssOptions;
|
|
29
|
+
}): StylePreprocessor | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Validates the Tailwind CSS integration configuration and emits actionable
|
|
32
|
+
* warnings for common misconfigurations that cause silent failures.
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateTailwindConfig(tailwindCss: TailwindCssOptions | undefined, config: ResolvedConfig, isWatchMode: boolean): void;
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { debugTailwind, debugTailwindV } from "./utils/debug.js";
|
|
2
|
+
import { inspectCssTailwindDirectives, throwTailwindReferenceTextError } from "./utils/tailwind-reference.js";
|
|
3
|
+
import { composeStylePreprocessors } from "./style-preprocessor.js";
|
|
4
|
+
import { stylePipelinePreprocessorFromPlugins } from "./style-pipeline.js";
|
|
5
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
6
|
+
import { basename, dirname, isAbsolute } from "node:path";
|
|
7
|
+
import { normalizePath } from "vite";
|
|
8
|
+
//#region packages/vite-plugin-angular/src/lib/tailwind-plugin.ts
|
|
9
|
+
/**
|
|
10
|
+
* Core injection logic shared by both the Vite pre-transform plugin and the
|
|
11
|
+
* Angular style preprocessor. Returns the `@reference`-prepended CSS when
|
|
12
|
+
* injection is needed, or `undefined` when the file should be left alone.
|
|
13
|
+
*/
|
|
14
|
+
function injectTailwindReference(code, opts) {
|
|
15
|
+
const directiveState = inspectCssTailwindDirectives(code);
|
|
16
|
+
if (directiveState.hasReferenceDirective || directiveState.hasTailwindImportDirective) return;
|
|
17
|
+
if (!(opts.prefixes ? opts.prefixes.some((p) => directiveState.commentlessCode.includes(p)) : directiveState.commentlessCode.includes("@apply"))) return;
|
|
18
|
+
if (directiveState.hasReferenceText) throwTailwindReferenceTextError(opts.fileId, opts.rootStylesheet);
|
|
19
|
+
return `@reference "${opts.rootStylesheet.replace(/\\/g, "/")}";\n${code}`;
|
|
20
|
+
}
|
|
21
|
+
function tailwindReferencePlugin(options) {
|
|
22
|
+
return {
|
|
23
|
+
name: "@analogjs/vite-plugin-angular:tailwind-reference",
|
|
24
|
+
enforce: "pre",
|
|
25
|
+
transform(code, id) {
|
|
26
|
+
const tw = options.tailwindCss;
|
|
27
|
+
if (!tw || !id.includes(".css")) return;
|
|
28
|
+
if (id.split("?")[0] === tw.rootStylesheet) return;
|
|
29
|
+
const rootBasename = basename(tw.rootStylesheet);
|
|
30
|
+
if (inspectCssTailwindDirectives(code).commentlessCode.includes(rootBasename)) return;
|
|
31
|
+
const result = injectTailwindReference(code, {
|
|
32
|
+
rootStylesheet: tw.rootStylesheet,
|
|
33
|
+
prefixes: tw.prefixes,
|
|
34
|
+
fileId: id
|
|
35
|
+
});
|
|
36
|
+
if (result) debugTailwind("injected @reference via pre-transform", { id: id.split("/").slice(-2).join("/") });
|
|
37
|
+
return result;
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Builds a resolved stylePreprocessor function from plugin options.
|
|
43
|
+
*
|
|
44
|
+
* When `tailwindCss` is configured, creates an injector that prepends
|
|
45
|
+
* `@reference "<rootStylesheet>"` into component CSS that uses Tailwind
|
|
46
|
+
* utilities. Uses absolute paths because Angular's externalRuntimeStyles
|
|
47
|
+
* serves component CSS as virtual modules (hash-based IDs) with no
|
|
48
|
+
* meaningful directory — relative paths can't resolve from a hash.
|
|
49
|
+
*
|
|
50
|
+
* If both `tailwindCss` and `stylePreprocessor` are provided, they are
|
|
51
|
+
* chained: Tailwind reference injection runs first, then the user's
|
|
52
|
+
* custom preprocessor.
|
|
53
|
+
*/
|
|
54
|
+
function buildStylePreprocessor(options) {
|
|
55
|
+
const userPreprocessor = options?.stylePreprocessor;
|
|
56
|
+
const stylePipelinePreprocessor = stylePipelinePreprocessorFromPlugins(options?.stylePipeline);
|
|
57
|
+
const tw = options?.tailwindCss;
|
|
58
|
+
if (!tw && !userPreprocessor && !stylePipelinePreprocessor) return;
|
|
59
|
+
let tailwindPreprocessor;
|
|
60
|
+
if (tw) {
|
|
61
|
+
const rootStylesheet = tw.rootStylesheet;
|
|
62
|
+
const prefixes = tw.prefixes;
|
|
63
|
+
debugTailwind("configured", {
|
|
64
|
+
rootStylesheet,
|
|
65
|
+
prefixes
|
|
66
|
+
});
|
|
67
|
+
if (!existsSync(rootStylesheet)) console.warn(`[@analogjs/vite-plugin-angular] tailwindCss.rootStylesheet not found at "${rootStylesheet}". @reference directives will point to a non-existent file, which will cause Tailwind CSS errors. Ensure the path is absolute and the file exists.`);
|
|
68
|
+
tailwindPreprocessor = (code, filename) => {
|
|
69
|
+
const result = injectTailwindReference(code, {
|
|
70
|
+
rootStylesheet,
|
|
71
|
+
prefixes,
|
|
72
|
+
fileId: filename
|
|
73
|
+
});
|
|
74
|
+
if (result) {
|
|
75
|
+
debugTailwind("injected @reference via preprocessor", { filename });
|
|
76
|
+
return result;
|
|
77
|
+
}
|
|
78
|
+
debugTailwindV("skip (no injection needed)", { filename });
|
|
79
|
+
return code;
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
if (tailwindPreprocessor && (stylePipelinePreprocessor || userPreprocessor)) debugTailwind("chained with style pipeline or user stylePreprocessor");
|
|
83
|
+
return composeStylePreprocessors([
|
|
84
|
+
tailwindPreprocessor,
|
|
85
|
+
stylePipelinePreprocessor,
|
|
86
|
+
userPreprocessor
|
|
87
|
+
]);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Validates the Tailwind CSS integration configuration and emits actionable
|
|
91
|
+
* warnings for common misconfigurations that cause silent failures.
|
|
92
|
+
*/
|
|
93
|
+
function validateTailwindConfig(tailwindCss, config, isWatchMode) {
|
|
94
|
+
const PREFIX = "[@analogjs/vite-plugin-angular]";
|
|
95
|
+
const tw = tailwindCss;
|
|
96
|
+
if (!tw) return;
|
|
97
|
+
if (!isAbsolute(tw.rootStylesheet)) console.warn(`${PREFIX} tailwindCss.rootStylesheet must be an absolute path. Got: "${tw.rootStylesheet}". Use path.resolve(__dirname, '...') in your vite.config to convert it.`);
|
|
98
|
+
const hasTailwindPlugin = config.plugins.some((p) => p.name.startsWith("@tailwindcss/vite") || p.name.startsWith("tailwindcss"));
|
|
99
|
+
if (isWatchMode && !hasTailwindPlugin) throw new Error(`${PREFIX} tailwindCss is configured but no @tailwindcss/vite plugin was found. Component CSS with @apply directives will not be processed.\n\n Fix: npm install @tailwindcss/vite --save-dev\n Then add tailwindcss() to your vite.config plugins array.\n`);
|
|
100
|
+
if (isWatchMode && tw.rootStylesheet) {
|
|
101
|
+
const projectRoot = normalizePath(config.root);
|
|
102
|
+
const normalizedRootStylesheet = normalizePath(tw.rootStylesheet);
|
|
103
|
+
if (!normalizedRootStylesheet.startsWith(projectRoot)) {
|
|
104
|
+
if (!(config.server?.fs?.allow ?? []).some((allowed) => normalizedRootStylesheet.startsWith(normalizePath(allowed)))) console.warn(`${PREFIX} tailwindCss.rootStylesheet is outside the Vite project root. The dev server may reject it with 403.\n\n Root: ${projectRoot}\n Stylesheet: ${tw.rootStylesheet}\n\n Fix: server.fs.allow: ['${dirname(tw.rootStylesheet)}']\n`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (tw.prefixes !== void 0 && tw.prefixes.length === 0) console.warn(`${PREFIX} tailwindCss.prefixes is an empty array. No component stylesheets will receive @reference injection. Either remove the prefixes option (to use @apply detection) or specify your prefixes: ['tw:']\n`);
|
|
108
|
+
if (existsSync(tw.rootStylesheet)) try {
|
|
109
|
+
const rootContent = readFileSync(tw.rootStylesheet, "utf-8");
|
|
110
|
+
if (!rootContent.includes("@import \"tailwindcss\"") && !rootContent.includes("@import 'tailwindcss'")) console.warn(`${PREFIX} tailwindCss.rootStylesheet does not contain @import "tailwindcss". The @reference directive will point to a file without Tailwind configuration.\n\n File: ${tw.rootStylesheet}\n`);
|
|
111
|
+
} catch {}
|
|
112
|
+
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { buildStylePreprocessor, tailwindReferencePlugin, validateTailwindConfig };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=tailwind-plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tailwind-plugin.js","names":[],"sources":["../../../src/lib/tailwind-plugin.ts"],"sourcesContent":["import { existsSync, readFileSync } from 'node:fs';\nimport { basename, dirname, isAbsolute } from 'node:path';\nimport { normalizePath, Plugin, ResolvedConfig } from 'vite';\nimport {\n inspectCssTailwindDirectives,\n throwTailwindReferenceTextError,\n} from './utils/tailwind-reference.js';\nimport { debugTailwind, debugTailwindV } from './utils/debug.js';\nimport { composeStylePreprocessors } from './style-preprocessor.js';\nimport type { StylePreprocessor } from './style-preprocessor.js';\nimport { stylePipelinePreprocessorFromPlugins } from './style-pipeline.js';\nimport type { AngularStylePipelineOptions } from './style-pipeline.js';\n\nexport interface TailwindCssOptions {\n rootStylesheet: string;\n prefixes?: string[];\n}\n\nexport interface TailwindReferencePluginOptions {\n tailwindCss?: TailwindCssOptions;\n}\n\n/**\n * Core injection logic shared by both the Vite pre-transform plugin and the\n * Angular style preprocessor. Returns the `@reference`-prepended CSS when\n * injection is needed, or `undefined` when the file should be left alone.\n */\nfunction injectTailwindReference(\n code: string,\n opts: {\n rootStylesheet: string;\n prefixes?: string[];\n fileId: string;\n },\n): string | undefined {\n const directiveState = inspectCssTailwindDirectives(code);\n\n if (\n directiveState.hasReferenceDirective ||\n directiveState.hasTailwindImportDirective\n ) {\n return undefined;\n }\n\n const needsRef = opts.prefixes\n ? opts.prefixes.some((p) => directiveState.commentlessCode.includes(p))\n : directiveState.commentlessCode.includes('@apply');\n\n if (!needsRef) {\n return undefined;\n }\n\n if (directiveState.hasReferenceText) {\n throwTailwindReferenceTextError(opts.fileId, opts.rootStylesheet);\n }\n\n return `@reference \"${opts.rootStylesheet.replace(/\\\\/g, '/')}\";\\n${code}`;\n}\n\nexport function tailwindReferencePlugin(\n options: TailwindReferencePluginOptions,\n): Plugin {\n return {\n name: '@analogjs/vite-plugin-angular:tailwind-reference',\n enforce: 'pre',\n transform(code: string, id: string) {\n const tw = options.tailwindCss;\n if (!tw || !id.includes('.css')) return;\n\n const cleanId = id.split('?')[0];\n if (cleanId === tw.rootStylesheet) return;\n\n // Skip entry stylesheets that @import the root config\n const rootBasename = basename(tw.rootStylesheet);\n const directiveState = inspectCssTailwindDirectives(code);\n if (directiveState.commentlessCode.includes(rootBasename)) return;\n\n const result = injectTailwindReference(code, {\n rootStylesheet: tw.rootStylesheet,\n prefixes: tw.prefixes,\n fileId: id,\n });\n\n if (result) {\n debugTailwind('injected @reference via pre-transform', {\n id: id.split('/').slice(-2).join('/'),\n });\n }\n\n return result;\n },\n };\n}\n\n/**\n * Builds a resolved stylePreprocessor function from plugin options.\n *\n * When `tailwindCss` is configured, creates an injector that prepends\n * `@reference \"<rootStylesheet>\"` into component CSS that uses Tailwind\n * utilities. Uses absolute paths because Angular's externalRuntimeStyles\n * serves component CSS as virtual modules (hash-based IDs) with no\n * meaningful directory — relative paths can't resolve from a hash.\n *\n * If both `tailwindCss` and `stylePreprocessor` are provided, they are\n * chained: Tailwind reference injection runs first, then the user's\n * custom preprocessor.\n */\nexport function buildStylePreprocessor(options?: {\n stylePreprocessor?: StylePreprocessor;\n stylePipeline?: AngularStylePipelineOptions;\n tailwindCss?: TailwindCssOptions;\n}): StylePreprocessor | undefined {\n const userPreprocessor = options?.stylePreprocessor;\n const stylePipelinePreprocessor = stylePipelinePreprocessorFromPlugins(\n options?.stylePipeline,\n );\n const tw = options?.tailwindCss;\n\n if (!tw && !userPreprocessor && !stylePipelinePreprocessor) {\n return undefined;\n }\n\n let tailwindPreprocessor:\n | ((code: string, filename: string) => string)\n | undefined;\n\n if (tw) {\n const rootStylesheet = tw.rootStylesheet;\n const prefixes = tw.prefixes;\n debugTailwind('configured', { rootStylesheet, prefixes });\n\n if (!existsSync(rootStylesheet)) {\n console.warn(\n `[@analogjs/vite-plugin-angular] tailwindCss.rootStylesheet not found ` +\n `at \"${rootStylesheet}\". @reference directives will point to a ` +\n `non-existent file, which will cause Tailwind CSS errors. ` +\n `Ensure the path is absolute and the file exists.`,\n );\n }\n\n tailwindPreprocessor = (code: string, filename: string): string => {\n const result = injectTailwindReference(code, {\n rootStylesheet,\n prefixes,\n fileId: filename,\n });\n\n if (result) {\n debugTailwind('injected @reference via preprocessor', { filename });\n return result;\n }\n\n debugTailwindV('skip (no injection needed)', { filename });\n return code;\n };\n }\n\n if (tailwindPreprocessor && (stylePipelinePreprocessor || userPreprocessor)) {\n debugTailwind('chained with style pipeline or user stylePreprocessor');\n }\n\n return composeStylePreprocessors([\n tailwindPreprocessor,\n stylePipelinePreprocessor,\n userPreprocessor,\n ]);\n}\n\n/**\n * Validates the Tailwind CSS integration configuration and emits actionable\n * warnings for common misconfigurations that cause silent failures.\n */\nexport function validateTailwindConfig(\n tailwindCss: TailwindCssOptions | undefined,\n config: ResolvedConfig,\n isWatchMode: boolean,\n): void {\n const PREFIX = '[@analogjs/vite-plugin-angular]';\n const tw = tailwindCss;\n\n if (!tw) return;\n\n if (!isAbsolute(tw.rootStylesheet)) {\n console.warn(\n `${PREFIX} tailwindCss.rootStylesheet must be an absolute path. ` +\n `Got: \"${tw.rootStylesheet}\". Use path.resolve(__dirname, '...') ` +\n `in your vite.config to convert it.`,\n );\n }\n\n const resolvedPlugins = config.plugins;\n const hasTailwindPlugin = resolvedPlugins.some(\n (p) =>\n p.name.startsWith('@tailwindcss/vite') ||\n p.name.startsWith('tailwindcss'),\n );\n\n if (isWatchMode && !hasTailwindPlugin) {\n throw new Error(\n `${PREFIX} tailwindCss is configured but no @tailwindcss/vite ` +\n `plugin was found. Component CSS with @apply directives will ` +\n `not be processed.\\n\\n` +\n ` Fix: npm install @tailwindcss/vite --save-dev\\n` +\n ` Then add tailwindcss() to your vite.config plugins array.\\n`,\n );\n }\n\n if (isWatchMode && tw.rootStylesheet) {\n const projectRoot = normalizePath(config.root);\n const normalizedRootStylesheet = normalizePath(tw.rootStylesheet);\n if (!normalizedRootStylesheet.startsWith(projectRoot)) {\n const fsAllow = config.server?.fs?.allow ?? [];\n const isAllowed = fsAllow.some((allowed) =>\n normalizedRootStylesheet.startsWith(normalizePath(allowed)),\n );\n if (!isAllowed) {\n console.warn(\n `${PREFIX} tailwindCss.rootStylesheet is outside the Vite ` +\n `project root. The dev server may reject it with 403.\\n\\n` +\n ` Root: ${projectRoot}\\n` +\n ` Stylesheet: ${tw.rootStylesheet}\\n\\n` +\n ` Fix: server.fs.allow: ['${dirname(tw.rootStylesheet)}']\\n`,\n );\n }\n }\n }\n\n if (tw.prefixes !== undefined && tw.prefixes.length === 0) {\n console.warn(\n `${PREFIX} tailwindCss.prefixes is an empty array. No component ` +\n `stylesheets will receive @reference injection. Either remove ` +\n `the prefixes option (to use @apply detection) or specify your ` +\n `prefixes: ['tw:']\\n`,\n );\n }\n\n if (existsSync(tw.rootStylesheet)) {\n try {\n const rootContent = readFileSync(tw.rootStylesheet, 'utf-8');\n if (\n !rootContent.includes('@import \"tailwindcss\"') &&\n !rootContent.includes(\"@import 'tailwindcss'\")\n ) {\n console.warn(\n `${PREFIX} tailwindCss.rootStylesheet does not contain ` +\n `@import \"tailwindcss\". The @reference directive will ` +\n `point to a file without Tailwind configuration.\\n\\n` +\n ` File: ${tw.rootStylesheet}\\n`,\n );\n }\n } catch {\n // Silently skip — existence check already warned in buildStylePreprocessor.\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
import { StyleUrlsResolver } from "./component-resolvers.js";
|
|
3
|
+
export interface ActiveGraphComponentRecord {
|
|
4
|
+
file: string;
|
|
5
|
+
className: string;
|
|
6
|
+
selector?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface StyleOwnerRecord {
|
|
9
|
+
sourcePath: string;
|
|
10
|
+
ownerFile: string;
|
|
11
|
+
}
|
|
12
|
+
interface TemplateClassBindingIssue {
|
|
13
|
+
line: number;
|
|
14
|
+
column: number;
|
|
15
|
+
snippet: string;
|
|
16
|
+
}
|
|
17
|
+
export interface TemplateClassBindingGuardContext {
|
|
18
|
+
styleUrlsResolver: StyleUrlsResolver;
|
|
19
|
+
activeGraphComponentMetadata: Map<string, ActiveGraphComponentRecord[]>;
|
|
20
|
+
selectorOwners: Map<string, Set<string>>;
|
|
21
|
+
classNameOwners: Map<string, Set<string>>;
|
|
22
|
+
transformedStyleOwnerMetadata: Map<string, StyleOwnerRecord[]>;
|
|
23
|
+
styleSourceOwners: Map<string, Set<string>>;
|
|
24
|
+
}
|
|
25
|
+
export declare function templateClassBindingGuardPlugin(ctx: TemplateClassBindingGuardContext): Plugin;
|
|
26
|
+
export declare function removeActiveGraphMetadata(ctx: TemplateClassBindingGuardContext, file: string): void;
|
|
27
|
+
export declare function removeStyleOwnerMetadata(ctx: TemplateClassBindingGuardContext, file: string): void;
|
|
28
|
+
export declare function findStaticClassAndBoundClassConflicts(template: string): TemplateClassBindingIssue[];
|
|
29
|
+
export declare function findBoundClassAndNgClassConflicts(template: string): TemplateClassBindingIssue[];
|
|
30
|
+
export {};
|