@analogjs/vite-plugin-angular 3.0.0-alpha.3 → 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/migrations/migrate-setup-vitest/migrate-setup-vitest.d.ts +2 -0
- package/migrations/migrate-setup-vitest/migrate-setup-vitest.js +49 -0
- package/migrations/migrate-setup-vitest/migrate-setup-vitest.js.map +1 -0
- package/migrations/migration.json +7 -1
- package/migrations/migrations.json +9 -0
- package/migrations/update-3-0-0/migrate-setup-vitest.d.ts +2 -0
- package/migrations/update-3-0-0/migrate-setup-vitest.js +36 -0
- package/migrations/update-3-0-0/migrate-setup-vitest.js.map +1 -0
- package/package.json +27 -13
- package/src/index.d.ts +3 -2
- package/src/index.js +6 -2
- package/src/index.js.map +1 -1
- package/src/lib/analog-compiler-plugin.d.ts +14 -0
- package/src/lib/analog-compiler-plugin.js +257 -0
- package/src/lib/analog-compiler-plugin.js.map +1 -0
- package/src/lib/angular-build-optimizer-plugin.d.ts +4 -4
- package/src/lib/angular-build-optimizer-plugin.js +48 -62
- package/src/lib/angular-build-optimizer-plugin.js.map +1 -1
- package/src/lib/angular-jit-plugin.d.ts +3 -3
- package/src/lib/angular-jit-plugin.js +37 -37
- package/src/lib/angular-jit-plugin.js.map +1 -1
- package/src/lib/angular-pending-tasks.plugin.d.ts +7 -7
- package/src/lib/angular-pending-tasks.plugin.js +17 -18
- package/src/lib/angular-pending-tasks.plugin.js.map +1 -1
- package/src/lib/angular-vite-plugin.d.ts +231 -40
- package/src/lib/angular-vite-plugin.js +1948 -964
- package/src/lib/angular-vite-plugin.js.map +1 -1
- package/src/lib/angular-vitest-plugin.d.ts +19 -15
- package/src/lib/angular-vitest-plugin.js +99 -114
- package/src/lib/angular-vitest-plugin.js.map +1 -1
- package/src/lib/compiler-plugin.d.ts +11 -11
- package/src/lib/compiler-plugin.js +43 -44
- package/src/lib/compiler-plugin.js.map +1 -1
- package/src/lib/component-resolvers.d.ts +23 -5
- package/src/lib/component-resolvers.js +153 -63
- package/src/lib/component-resolvers.js.map +1 -1
- package/src/lib/host.d.ts +10 -8
- package/src/lib/host.js +109 -101
- package/src/lib/host.js.map +1 -1
- package/src/lib/live-reload-plugin.d.ts +5 -5
- package/src/lib/live-reload-plugin.js +57 -62
- package/src/lib/live-reload-plugin.js.map +1 -1
- package/src/lib/models.d.ts +9 -9
- package/src/lib/nx-folder-plugin.d.ts +5 -5
- package/src/lib/nx-folder-plugin.js +18 -16
- package/src/lib/nx-folder-plugin.js.map +1 -1
- package/src/lib/plugins/file-replacements.plugin.d.ts +4 -4
- package/src/lib/plugins/file-replacements.plugin.js +40 -62
- package/src/lib/plugins/file-replacements.plugin.js.map +1 -1
- package/src/lib/router-plugin.d.ts +1 -1
- package/src/lib/router-plugin.js +23 -23
- package/src/lib/router-plugin.js.map +1 -1
- package/src/lib/style-pipeline.d.ts +15 -0
- package/src/lib/style-pipeline.js +31 -0
- package/src/lib/style-pipeline.js.map +1 -0
- package/src/lib/style-preprocessor.d.ts +35 -0
- package/src/lib/style-preprocessor.js +35 -0
- package/src/lib/style-preprocessor.js.map +1 -0
- package/src/lib/stylesheet-registry.d.ts +73 -0
- package/src/lib/stylesheet-registry.js +168 -0
- package/src/lib/stylesheet-registry.js.map +1 -0
- package/src/lib/tools/package.json +2 -7
- package/src/lib/tools/src/builders/vite/vite-build.impl.js +31 -38
- package/src/lib/tools/src/builders/vite/vite-build.impl.js.map +1 -1
- package/src/lib/tools/src/builders/vite-dev-server/dev-server.impl.js +51 -62
- package/src/lib/tools/src/builders/vite-dev-server/dev-server.impl.js.map +1 -1
- package/src/lib/tools/src/index.js +0 -2
- package/src/lib/utils/compiler-plugin-options.d.ts +11 -11
- package/src/lib/utils/debug-harness.d.ts +23 -0
- package/src/lib/utils/debug-harness.js +88 -0
- package/src/lib/utils/debug-harness.js.map +1 -0
- package/src/lib/utils/debug-log-file.d.ts +5 -0
- package/src/lib/utils/debug-log-file.js +56 -0
- package/src/lib/utils/debug-log-file.js.map +1 -0
- package/src/lib/utils/debug.d.ts +26 -0
- package/src/lib/utils/debug.js +35 -0
- package/src/lib/utils/debug.js.map +1 -0
- package/src/lib/utils/devkit.d.ts +6 -6
- package/src/lib/utils/devkit.js +34 -38
- package/src/lib/utils/devkit.js.map +1 -1
- package/src/lib/utils/hmr-candidates.d.ts +28 -28
- package/src/lib/utils/plugin-config.d.ts +30 -0
- package/src/lib/utils/plugin-config.js +64 -0
- package/src/lib/utils/plugin-config.js.map +1 -0
- package/src/lib/utils/rolldown.d.ts +2 -0
- package/src/lib/utils/rolldown.js +12 -0
- package/src/lib/utils/rolldown.js.map +1 -0
- package/src/lib/utils/source-file-cache.d.ts +8 -15
- package/src/lib/utils/source-file-cache.js +35 -37
- package/src/lib/utils/source-file-cache.js.map +1 -1
- package/src/lib/utils/virtual-ids.d.ts +8 -0
- package/src/lib/utils/virtual-ids.js +35 -0
- package/src/lib/utils/virtual-ids.js.map +1 -0
- package/src/lib/utils/virtual-resources.d.ts +31 -0
- package/src/lib/utils/virtual-resources.js +60 -0
- package/src/lib/utils/virtual-resources.js.map +1 -0
- package/src/test-setup.d.ts +2 -0
- package/setup-vitest.d.ts +0 -4
- package/setup-vitest.js +0 -215
- package/setup-vitest.js.map +0 -1
- package/src/lib/models.js +0 -1
- package/src/lib/models.js.map +0 -1
- package/src/lib/tools/README.md +0 -3
- package/src/lib/tools/src/index.d.ts +0 -0
- package/src/lib/tools/src/index.js.map +0 -1
- package/src/lib/utils/compiler-plugin-options.js +0 -1
- package/src/lib/utils/compiler-plugin-options.js.map +0 -1
- package/src/lib/utils/hmr-candidates.js +0 -272
- package/src/lib/utils/hmr-candidates.js.map +0 -1
|
@@ -1,69 +1,159 @@
|
|
|
1
|
-
import { dirname, resolve } from
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
// resolved style URLs.
|
|
24
|
-
if (entry && entry.matchedStyleUrls === matchedStyleUrls) {
|
|
25
|
-
return entry.styleUrls;
|
|
26
|
-
}
|
|
27
|
-
const styleUrls = matchedStyleUrls.map((styleUrlPath) => {
|
|
28
|
-
return `${styleUrlPath}|${normalizePath(resolve(dirname(id), styleUrlPath))}`;
|
|
29
|
-
});
|
|
30
|
-
this.styleUrlsCache.set(id, { styleUrls, matchedStyleUrls });
|
|
31
|
-
return styleUrls;
|
|
32
|
-
}
|
|
1
|
+
import { dirname, resolve } from "node:path";
|
|
2
|
+
import { normalizePath } from "vite";
|
|
3
|
+
import { parseSync } from "oxc-parser";
|
|
4
|
+
import { Visitor } from "rolldown/utils";
|
|
5
|
+
//#region packages/vite-plugin-angular/src/lib/component-resolvers.ts
|
|
6
|
+
/**
|
|
7
|
+
* Extracts a string value from an ESTree AST node.
|
|
8
|
+
*
|
|
9
|
+
* Handles three forms that Angular decorators may use:
|
|
10
|
+
* - `Literal` with a string value → `'./foo.css'` / `"./foo.css"`
|
|
11
|
+
* - `StringLiteral` (OXC-specific) → same representation
|
|
12
|
+
* - `TemplateLiteral` with zero expressions → `` `./foo.css` ``
|
|
13
|
+
*
|
|
14
|
+
* Uses `any` because OXC's AST mixes standard ESTree nodes with
|
|
15
|
+
* OXC-specific variants (e.g. `StringLiteral`), and the project's
|
|
16
|
+
* tsconfig enforces `noPropertyAccessFromIndexSignature`.
|
|
17
|
+
*/
|
|
18
|
+
function getStringValue(node) {
|
|
19
|
+
if (!node) return void 0;
|
|
20
|
+
if (node.type === "Literal" && typeof node.value === "string") return node.value;
|
|
21
|
+
if (node.type === "StringLiteral") return node.value;
|
|
22
|
+
if (node.type === "TemplateLiteral" && node.expressions.length === 0 && node.quasis.length === 1) return node.quasis[0].value.cooked ?? node.quasis[0].value.raw;
|
|
33
23
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
24
|
+
/**
|
|
25
|
+
* Parses TypeScript/JS source with OXC and collects `styleUrl`, `styleUrls`,
|
|
26
|
+
* and `templateUrl` property values from Angular `@Component()` decorators
|
|
27
|
+
* in a single AST pass.
|
|
28
|
+
*
|
|
29
|
+
* This replaces the previous ts-morph implementation — OXC parses natively
|
|
30
|
+
* via Rust NAPI bindings, avoiding the overhead of spinning up a full
|
|
31
|
+
* TypeScript `Project` for each file.
|
|
32
|
+
*/
|
|
33
|
+
function collectComponentUrls(code) {
|
|
34
|
+
const { program } = parseSync("cmp.ts", code);
|
|
35
|
+
const styleUrls = [];
|
|
36
|
+
const templateUrls = [];
|
|
37
|
+
const inlineTemplates = [];
|
|
38
|
+
new Visitor({ ClassDeclaration(node) {
|
|
39
|
+
const decorators = node.decorators ?? [];
|
|
40
|
+
for (const decorator of decorators) {
|
|
41
|
+
const expression = decorator.expression;
|
|
42
|
+
if (expression?.type !== "CallExpression" || expression.callee?.type !== "Identifier" || expression.callee.name !== "Component") continue;
|
|
43
|
+
const componentArg = expression.arguments?.[0];
|
|
44
|
+
if (componentArg?.type !== "ObjectExpression") continue;
|
|
45
|
+
for (const property of componentArg.properties ?? []) {
|
|
46
|
+
if (property?.type !== "Property" || property.key?.type !== "Identifier") continue;
|
|
47
|
+
const name = property.key.name;
|
|
48
|
+
if (name === "styleUrls" && property.value?.type === "ArrayExpression") for (const el of property.value.elements) {
|
|
49
|
+
const val = getStringValue(el);
|
|
50
|
+
if (val !== void 0) styleUrls.push(val);
|
|
51
|
+
}
|
|
52
|
+
if (name === "styleUrl") {
|
|
53
|
+
const val = getStringValue(property.value);
|
|
54
|
+
if (val !== void 0) styleUrls.push(val);
|
|
55
|
+
}
|
|
56
|
+
if (name === "templateUrl") {
|
|
57
|
+
const val = getStringValue(property.value);
|
|
58
|
+
if (val !== void 0) templateUrls.push(val);
|
|
59
|
+
}
|
|
60
|
+
if (name === "template") {
|
|
61
|
+
const val = getStringValue(property.value);
|
|
62
|
+
if (val !== void 0) inlineTemplates.push(val);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
} }).visit(program);
|
|
67
|
+
return {
|
|
68
|
+
styleUrls,
|
|
69
|
+
templateUrls,
|
|
70
|
+
inlineTemplates
|
|
71
|
+
};
|
|
39
72
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Extract Angular component identities from raw source code before Angular's
|
|
75
|
+
* compilation pipeline strips decorators. This is used for dev-time
|
|
76
|
+
* diagnostics such as duplicate selectors, duplicate component class names,
|
|
77
|
+
* selectorless shared components, and inline-template validation.
|
|
78
|
+
*/
|
|
79
|
+
function getAngularComponentMetadata(code) {
|
|
80
|
+
const { program } = parseSync("cmp.ts", code);
|
|
81
|
+
const components = [];
|
|
82
|
+
new Visitor({ ClassDeclaration(node) {
|
|
83
|
+
const decorators = node.decorators ?? [];
|
|
84
|
+
for (const decorator of decorators) {
|
|
85
|
+
const expression = decorator.expression;
|
|
86
|
+
if (expression?.type !== "CallExpression" || expression.callee?.type !== "Identifier" || expression.callee.name !== "Component") continue;
|
|
87
|
+
const componentArg = expression.arguments?.[0];
|
|
88
|
+
if (componentArg?.type !== "ObjectExpression") continue;
|
|
89
|
+
const metadata = {
|
|
90
|
+
className: node.id?.name ?? "(anonymous)",
|
|
91
|
+
styleUrls: [],
|
|
92
|
+
templateUrls: [],
|
|
93
|
+
inlineTemplates: []
|
|
94
|
+
};
|
|
95
|
+
for (const property of componentArg.properties ?? []) {
|
|
96
|
+
if (property?.type !== "Property" || property.key?.type !== "Identifier") continue;
|
|
97
|
+
const name = property.key.name;
|
|
98
|
+
if (name === "selector") metadata.selector = getStringValue(property.value);
|
|
99
|
+
else if (name === "styleUrl") {
|
|
100
|
+
const val = getStringValue(property.value);
|
|
101
|
+
if (val !== void 0) metadata.styleUrls.push(val);
|
|
102
|
+
} else if (name === "styleUrls" && property.value?.type === "ArrayExpression") for (const el of property.value.elements ?? []) {
|
|
103
|
+
const val = getStringValue(el);
|
|
104
|
+
if (val !== void 0) metadata.styleUrls.push(val);
|
|
105
|
+
}
|
|
106
|
+
else if (name === "templateUrl") {
|
|
107
|
+
const val = getStringValue(property.value);
|
|
108
|
+
if (val !== void 0) metadata.templateUrls.push(val);
|
|
109
|
+
} else if (name === "template") {
|
|
110
|
+
const val = getStringValue(property.value);
|
|
111
|
+
if (val !== void 0) metadata.inlineTemplates.push(val);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
components.push(metadata);
|
|
115
|
+
}
|
|
116
|
+
} }).visit(program);
|
|
117
|
+
return components;
|
|
50
118
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const properties = sourceFile.getDescendantsOfKind(SyntaxKind.PropertyAssignment);
|
|
55
|
-
return getTextByProperty('templateUrl', properties);
|
|
119
|
+
/** Extract all `styleUrl` / `styleUrls` values from Angular component source. */
|
|
120
|
+
function getStyleUrls(code) {
|
|
121
|
+
return collectComponentUrls(code).styleUrls;
|
|
56
122
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const entry = this.templateUrlsCache.get(id);
|
|
61
|
-
if (entry?.code === code) {
|
|
62
|
-
return entry.templateUrlPaths;
|
|
63
|
-
}
|
|
64
|
-
const templateUrlPaths = getTemplateUrls(code).map((url) => `${url}|${normalizePath(resolve(dirname(id), url))}`);
|
|
65
|
-
this.templateUrlsCache.set(id, { code, templateUrlPaths });
|
|
66
|
-
return templateUrlPaths;
|
|
67
|
-
}
|
|
123
|
+
/** Extract all `templateUrl` values from Angular component source. */
|
|
124
|
+
function getTemplateUrls(code) {
|
|
125
|
+
return collectComponentUrls(code).templateUrls;
|
|
68
126
|
}
|
|
127
|
+
var StyleUrlsResolver = class {
|
|
128
|
+
styleUrlsCache = /* @__PURE__ */ new Map();
|
|
129
|
+
resolve(code, id) {
|
|
130
|
+
const matchedStyleUrls = getStyleUrls(code);
|
|
131
|
+
const entry = this.styleUrlsCache.get(id);
|
|
132
|
+
if (entry && entry.matchedStyleUrls === matchedStyleUrls) return entry.styleUrls;
|
|
133
|
+
const styleUrls = matchedStyleUrls.map((styleUrlPath) => {
|
|
134
|
+
return `${styleUrlPath}|${normalizePath(resolve(dirname(id), styleUrlPath))}`;
|
|
135
|
+
});
|
|
136
|
+
this.styleUrlsCache.set(id, {
|
|
137
|
+
styleUrls,
|
|
138
|
+
matchedStyleUrls
|
|
139
|
+
});
|
|
140
|
+
return styleUrls;
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var TemplateUrlsResolver = class {
|
|
144
|
+
templateUrlsCache = /* @__PURE__ */ new Map();
|
|
145
|
+
resolve(code, id) {
|
|
146
|
+
const entry = this.templateUrlsCache.get(id);
|
|
147
|
+
if (entry?.code === code) return entry.templateUrlPaths;
|
|
148
|
+
const templateUrlPaths = getTemplateUrls(code).map((url) => `${url}|${normalizePath(resolve(dirname(id), url))}`);
|
|
149
|
+
this.templateUrlsCache.set(id, {
|
|
150
|
+
code,
|
|
151
|
+
templateUrlPaths
|
|
152
|
+
});
|
|
153
|
+
return templateUrlPaths;
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
//#endregion
|
|
157
|
+
export { StyleUrlsResolver, TemplateUrlsResolver, getAngularComponentMetadata };
|
|
158
|
+
|
|
69
159
|
//# sourceMappingURL=component-resolvers.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"component-resolvers.js","sourceRoot":"","sources":["../../../../../packages/vite-plugin-angular/src/lib/component-resolvers.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"component-resolvers.js","names":[],"sources":["../../../src/lib/component-resolvers.ts"],"sourcesContent":["import { dirname, resolve } from 'node:path';\n// OXC parser (native Rust, NAPI-RS) replaces ts-morph for AST extraction.\n// It is ~10-50x faster for the narrow task of pulling property values from\n// Angular component decorators. The Visitor helper from Rolldown walks the\n// ESTree-compatible AST that OXC produces.\nimport { parseSync } from 'oxc-parser';\nimport { Visitor } from 'rolldown/utils';\nimport { normalizePath } from 'vite';\n\n// ---------------------------------------------------------------------------\n// AST helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Extracts a string value from an ESTree AST node.\n *\n * Handles three forms that Angular decorators may use:\n * - `Literal` with a string value → `'./foo.css'` / `\"./foo.css\"`\n * - `StringLiteral` (OXC-specific) → same representation\n * - `TemplateLiteral` with zero expressions → `` `./foo.css` ``\n *\n * Uses `any` because OXC's AST mixes standard ESTree nodes with\n * OXC-specific variants (e.g. `StringLiteral`), and the project's\n * tsconfig enforces `noPropertyAccessFromIndexSignature`.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nfunction getStringValue(node: any): string | undefined {\n if (!node) return undefined;\n // Standard ESTree Literal (string value)\n if (node.type === 'Literal' && typeof node.value === 'string') {\n return node.value;\n }\n // OXC-specific StringLiteral node\n if (node.type === 'StringLiteral') {\n return node.value;\n }\n // Template literal with no interpolation (e.g., `./foo.css`)\n if (\n node.type === 'TemplateLiteral' &&\n node.expressions.length === 0 &&\n node.quasis.length === 1\n ) {\n return node.quasis[0].value.cooked ?? node.quasis[0].value.raw;\n }\n return undefined;\n}\n\n/**\n * Parses TypeScript/JS source with OXC and collects `styleUrl`, `styleUrls`,\n * and `templateUrl` property values from Angular `@Component()` decorators\n * in a single AST pass.\n *\n * This replaces the previous ts-morph implementation — OXC parses natively\n * via Rust NAPI bindings, avoiding the overhead of spinning up a full\n * TypeScript `Project` for each file.\n */\nfunction collectComponentUrls(code: string): {\n styleUrls: string[];\n templateUrls: string[];\n inlineTemplates: string[];\n} {\n const { program } = parseSync('cmp.ts', code);\n const styleUrls: string[] = [];\n const templateUrls: string[] = [];\n const inlineTemplates: string[] = [];\n\n const visitor = new Visitor({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ClassDeclaration(node: any) {\n const decorators = node.decorators ?? [];\n for (const decorator of decorators) {\n const expression = decorator.expression;\n if (\n expression?.type !== 'CallExpression' ||\n expression.callee?.type !== 'Identifier' ||\n expression.callee.name !== 'Component'\n ) {\n continue;\n }\n\n const componentArg = expression.arguments?.[0];\n if (componentArg?.type !== 'ObjectExpression') {\n continue;\n }\n\n for (const property of componentArg.properties ?? []) {\n if (\n property?.type !== 'Property' ||\n property.key?.type !== 'Identifier'\n ) {\n continue;\n }\n\n const name = property.key.name;\n\n if (\n name === 'styleUrls' &&\n property.value?.type === 'ArrayExpression'\n ) {\n for (const el of property.value.elements) {\n const val = getStringValue(el);\n if (val !== undefined) styleUrls.push(val);\n }\n }\n\n if (name === 'styleUrl') {\n const val = getStringValue(property.value);\n if (val !== undefined) styleUrls.push(val);\n }\n\n if (name === 'templateUrl') {\n const val = getStringValue(property.value);\n if (val !== undefined) templateUrls.push(val);\n }\n\n if (name === 'template') {\n const val = getStringValue(property.value);\n if (val !== undefined) inlineTemplates.push(val);\n }\n }\n }\n },\n });\n visitor.visit(program);\n\n return { styleUrls, templateUrls, inlineTemplates };\n}\n\nexport interface AngularComponentMetadata {\n className: string;\n selector?: string;\n styleUrls: string[];\n templateUrls: string[];\n inlineTemplates: string[];\n}\n\n/**\n * Extract Angular component identities from raw source code before Angular's\n * compilation pipeline strips decorators. This is used for dev-time\n * diagnostics such as duplicate selectors, duplicate component class names,\n * selectorless shared components, and inline-template validation.\n */\nexport function getAngularComponentMetadata(\n code: string,\n): AngularComponentMetadata[] {\n const { program } = parseSync('cmp.ts', code);\n const components: AngularComponentMetadata[] = [];\n\n const visitor = new Visitor({\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n ClassDeclaration(node: any) {\n const decorators = node.decorators ?? [];\n for (const decorator of decorators) {\n const expression = decorator.expression;\n if (\n expression?.type !== 'CallExpression' ||\n expression.callee?.type !== 'Identifier' ||\n expression.callee.name !== 'Component'\n ) {\n continue;\n }\n\n const componentArg = expression.arguments?.[0];\n if (componentArg?.type !== 'ObjectExpression') {\n continue;\n }\n\n const metadata: AngularComponentMetadata = {\n className: node.id?.name ?? '(anonymous)',\n styleUrls: [],\n templateUrls: [],\n inlineTemplates: [],\n };\n\n for (const property of componentArg.properties ?? []) {\n if (\n property?.type !== 'Property' ||\n property.key?.type !== 'Identifier'\n ) {\n continue;\n }\n\n const name = property.key.name;\n if (name === 'selector') {\n metadata.selector = getStringValue(property.value);\n } else if (name === 'styleUrl') {\n const val = getStringValue(property.value);\n if (val !== undefined) {\n metadata.styleUrls.push(val);\n }\n } else if (\n name === 'styleUrls' &&\n property.value?.type === 'ArrayExpression'\n ) {\n for (const el of property.value.elements ?? []) {\n const val = getStringValue(el);\n if (val !== undefined) {\n metadata.styleUrls.push(val);\n }\n }\n } else if (name === 'templateUrl') {\n const val = getStringValue(property.value);\n if (val !== undefined) {\n metadata.templateUrls.push(val);\n }\n } else if (name === 'template') {\n const val = getStringValue(property.value);\n if (val !== undefined) {\n metadata.inlineTemplates.push(val);\n }\n }\n }\n\n components.push(metadata);\n }\n },\n });\n visitor.visit(program);\n\n return components;\n}\n\n/** Extract all `styleUrl` / `styleUrls` values from Angular component source. */\nexport function getStyleUrls(code: string): string[] {\n return collectComponentUrls(code).styleUrls;\n}\n\n/** Extract all `templateUrl` values from Angular component source. */\nexport function getTemplateUrls(code: string): string[] {\n return collectComponentUrls(code).templateUrls;\n}\n\n/** Extract inline `template` strings from Angular component source. */\nexport function getInlineTemplates(code: string): string[] {\n return collectComponentUrls(code).inlineTemplates;\n}\n\n// ---------------------------------------------------------------------------\n// Resolver caches\n// ---------------------------------------------------------------------------\n\ninterface StyleUrlsCacheEntry {\n matchedStyleUrls: string[];\n styleUrls: string[];\n}\n\nexport class StyleUrlsResolver {\n // These resolvers may be called multiple times during the same\n // compilation for the same files. Caching is required because these\n // resolvers use synchronous system calls to the filesystem, which can\n // degrade performance when running compilations for multiple files.\n private readonly styleUrlsCache = new Map<string, StyleUrlsCacheEntry>();\n\n resolve(code: string, id: string): string[] {\n const matchedStyleUrls = getStyleUrls(code);\n const entry = this.styleUrlsCache.get(id);\n // We're using `matchedStyleUrls` as a key because the code may be changing continuously,\n // resulting in the resolver being called multiple times. While the code changes, the\n // `styleUrls` may remain constant, which means we should always return the previously\n // resolved style URLs.\n if (entry && entry.matchedStyleUrls === matchedStyleUrls) {\n return entry.styleUrls;\n }\n\n const styleUrls = matchedStyleUrls.map((styleUrlPath) => {\n return `${styleUrlPath}|${normalizePath(\n resolve(dirname(id), styleUrlPath),\n )}`;\n });\n\n this.styleUrlsCache.set(id, { styleUrls, matchedStyleUrls });\n return styleUrls;\n }\n}\n\ninterface TemplateUrlsCacheEntry {\n code: string;\n templateUrlPaths: string[];\n}\n\nexport class TemplateUrlsResolver {\n private readonly templateUrlsCache = new Map<\n string,\n TemplateUrlsCacheEntry\n >();\n\n resolve(code: string, id: string): string[] {\n const entry = this.templateUrlsCache.get(id);\n if (entry?.code === code) {\n return entry.templateUrlPaths;\n }\n\n const templateUrlPaths = getTemplateUrls(code).map(\n (url) => `${url}|${normalizePath(resolve(dirname(id), url))}`,\n );\n\n this.templateUrlsCache.set(id, { code, templateUrlPaths });\n return templateUrlPaths;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AA0BA,SAAS,eAAe,MAA+B;AACrD,KAAI,CAAC,KAAM,QAAO,KAAA;AAElB,KAAI,KAAK,SAAS,aAAa,OAAO,KAAK,UAAU,SACnD,QAAO,KAAK;AAGd,KAAI,KAAK,SAAS,gBAChB,QAAO,KAAK;AAGd,KACE,KAAK,SAAS,qBACd,KAAK,YAAY,WAAW,KAC5B,KAAK,OAAO,WAAW,EAEvB,QAAO,KAAK,OAAO,GAAG,MAAM,UAAU,KAAK,OAAO,GAAG,MAAM;;;;;;;;;;;AAc/D,SAAS,qBAAqB,MAI5B;CACA,MAAM,EAAE,YAAY,UAAU,UAAU,KAAK;CAC7C,MAAM,YAAsB,EAAE;CAC9B,MAAM,eAAyB,EAAE;CACjC,MAAM,kBAA4B,EAAE;AAEpB,KAAI,QAAQ,EAE1B,iBAAiB,MAAW;EAC1B,MAAM,aAAa,KAAK,cAAc,EAAE;AACxC,OAAK,MAAM,aAAa,YAAY;GAClC,MAAM,aAAa,UAAU;AAC7B,OACE,YAAY,SAAS,oBACrB,WAAW,QAAQ,SAAS,gBAC5B,WAAW,OAAO,SAAS,YAE3B;GAGF,MAAM,eAAe,WAAW,YAAY;AAC5C,OAAI,cAAc,SAAS,mBACzB;AAGF,QAAK,MAAM,YAAY,aAAa,cAAc,EAAE,EAAE;AACpD,QACE,UAAU,SAAS,cACnB,SAAS,KAAK,SAAS,aAEvB;IAGF,MAAM,OAAO,SAAS,IAAI;AAE1B,QACE,SAAS,eACT,SAAS,OAAO,SAAS,kBAEzB,MAAK,MAAM,MAAM,SAAS,MAAM,UAAU;KACxC,MAAM,MAAM,eAAe,GAAG;AAC9B,SAAI,QAAQ,KAAA,EAAW,WAAU,KAAK,IAAI;;AAI9C,QAAI,SAAS,YAAY;KACvB,MAAM,MAAM,eAAe,SAAS,MAAM;AAC1C,SAAI,QAAQ,KAAA,EAAW,WAAU,KAAK,IAAI;;AAG5C,QAAI,SAAS,eAAe;KAC1B,MAAM,MAAM,eAAe,SAAS,MAAM;AAC1C,SAAI,QAAQ,KAAA,EAAW,cAAa,KAAK,IAAI;;AAG/C,QAAI,SAAS,YAAY;KACvB,MAAM,MAAM,eAAe,SAAS,MAAM;AAC1C,SAAI,QAAQ,KAAA,EAAW,iBAAgB,KAAK,IAAI;;;;IAKzD,CAAC,CACM,MAAM,QAAQ;AAEtB,QAAO;EAAE;EAAW;EAAc;EAAiB;;;;;;;;AAiBrD,SAAgB,4BACd,MAC4B;CAC5B,MAAM,EAAE,YAAY,UAAU,UAAU,KAAK;CAC7C,MAAM,aAAyC,EAAE;AAEjC,KAAI,QAAQ,EAE1B,iBAAiB,MAAW;EAC1B,MAAM,aAAa,KAAK,cAAc,EAAE;AACxC,OAAK,MAAM,aAAa,YAAY;GAClC,MAAM,aAAa,UAAU;AAC7B,OACE,YAAY,SAAS,oBACrB,WAAW,QAAQ,SAAS,gBAC5B,WAAW,OAAO,SAAS,YAE3B;GAGF,MAAM,eAAe,WAAW,YAAY;AAC5C,OAAI,cAAc,SAAS,mBACzB;GAGF,MAAM,WAAqC;IACzC,WAAW,KAAK,IAAI,QAAQ;IAC5B,WAAW,EAAE;IACb,cAAc,EAAE;IAChB,iBAAiB,EAAE;IACpB;AAED,QAAK,MAAM,YAAY,aAAa,cAAc,EAAE,EAAE;AACpD,QACE,UAAU,SAAS,cACnB,SAAS,KAAK,SAAS,aAEvB;IAGF,MAAM,OAAO,SAAS,IAAI;AAC1B,QAAI,SAAS,WACX,UAAS,WAAW,eAAe,SAAS,MAAM;aACzC,SAAS,YAAY;KAC9B,MAAM,MAAM,eAAe,SAAS,MAAM;AAC1C,SAAI,QAAQ,KAAA,EACV,UAAS,UAAU,KAAK,IAAI;eAG9B,SAAS,eACT,SAAS,OAAO,SAAS,kBAEzB,MAAK,MAAM,MAAM,SAAS,MAAM,YAAY,EAAE,EAAE;KAC9C,MAAM,MAAM,eAAe,GAAG;AAC9B,SAAI,QAAQ,KAAA,EACV,UAAS,UAAU,KAAK,IAAI;;aAGvB,SAAS,eAAe;KACjC,MAAM,MAAM,eAAe,SAAS,MAAM;AAC1C,SAAI,QAAQ,KAAA,EACV,UAAS,aAAa,KAAK,IAAI;eAExB,SAAS,YAAY;KAC9B,MAAM,MAAM,eAAe,SAAS,MAAM;AAC1C,SAAI,QAAQ,KAAA,EACV,UAAS,gBAAgB,KAAK,IAAI;;;AAKxC,cAAW,KAAK,SAAS;;IAG9B,CAAC,CACM,MAAM,QAAQ;AAEtB,QAAO;;;AAIT,SAAgB,aAAa,MAAwB;AACnD,QAAO,qBAAqB,KAAK,CAAC;;;AAIpC,SAAgB,gBAAgB,MAAwB;AACtD,QAAO,qBAAqB,KAAK,CAAC;;AAiBpC,IAAa,oBAAb,MAA+B;CAK7B,iCAAkC,IAAI,KAAkC;CAExE,QAAQ,MAAc,IAAsB;EAC1C,MAAM,mBAAmB,aAAa,KAAK;EAC3C,MAAM,QAAQ,KAAK,eAAe,IAAI,GAAG;AAKzC,MAAI,SAAS,MAAM,qBAAqB,iBACtC,QAAO,MAAM;EAGf,MAAM,YAAY,iBAAiB,KAAK,iBAAiB;AACvD,UAAO,GAAG,aAAa,GAAG,cACxB,QAAQ,QAAQ,GAAG,EAAE,aAAa,CACnC;IACD;AAEF,OAAK,eAAe,IAAI,IAAI;GAAE;GAAW;GAAkB,CAAC;AAC5D,SAAO;;;AASX,IAAa,uBAAb,MAAkC;CAChC,oCAAqC,IAAI,KAGtC;CAEH,QAAQ,MAAc,IAAsB;EAC1C,MAAM,QAAQ,KAAK,kBAAkB,IAAI,GAAG;AAC5C,MAAI,OAAO,SAAS,KAClB,QAAO,MAAM;EAGf,MAAM,mBAAmB,gBAAgB,KAAK,CAAC,KAC5C,QAAQ,GAAG,IAAI,GAAG,cAAc,QAAQ,QAAQ,GAAG,EAAE,IAAI,CAAC,GAC5D;AAED,OAAK,kBAAkB,IAAI,IAAI;GAAE;GAAM;GAAkB,CAAC;AAC1D,SAAO"}
|
package/src/lib/host.d.ts
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import * as ts from
|
|
2
|
-
import type
|
|
1
|
+
import * as ts from "typescript";
|
|
2
|
+
import { type StylePreprocessor } from "./style-preprocessor.js";
|
|
3
|
+
import { AnalogStylesheetRegistry } from "./stylesheet-registry.js";
|
|
4
|
+
import type { SourceFileCache } from "./utils/source-file-cache.js";
|
|
3
5
|
export declare function augmentHostWithResources(host: ts.CompilerHost, transform: (code: string, id: string, options?: {
|
|
4
|
-
|
|
6
|
+
ssr?: boolean;
|
|
5
7
|
}) => ReturnType<any> | null, options: {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
inlineStylesExtension: string;
|
|
9
|
+
isProd?: boolean;
|
|
10
|
+
stylesheetRegistry?: AnalogStylesheetRegistry;
|
|
11
|
+
sourceFileCache?: SourceFileCache;
|
|
12
|
+
stylePreprocessor?: StylePreprocessor;
|
|
11
13
|
}): void;
|
|
12
14
|
export declare function augmentProgramWithVersioning(program: ts.Program): void;
|
|
13
15
|
export declare function augmentHostWithCaching(host: ts.CompilerHost, cache: Map<string, ts.SourceFile>): void;
|
package/src/lib/host.js
CHANGED
|
@@ -1,108 +1,116 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
1
|
+
import { debugStyles } from "./utils/debug.js";
|
|
2
|
+
import { normalizeStylesheetDependencies } from "./style-preprocessor.js";
|
|
3
|
+
import { preprocessStylesheetResult, registerStylesheetContent } from "./stylesheet-registry.js";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { normalizePath } from "vite";
|
|
7
|
+
//#region packages/vite-plugin-angular/src/lib/host.ts
|
|
8
|
+
function augmentHostWithResources(host, transform, options) {
|
|
9
|
+
const resourceHost = host;
|
|
10
|
+
resourceHost.readResource = async function(fileName) {
|
|
11
|
+
const filePath = normalizePath(fileName);
|
|
12
|
+
const content = this.readFile(filePath);
|
|
13
|
+
if (content === void 0) throw new Error("Unable to locate component resource: " + fileName);
|
|
14
|
+
return content;
|
|
15
|
+
};
|
|
16
|
+
resourceHost.getModifiedResourceFiles = function() {
|
|
17
|
+
return options?.sourceFileCache?.modifiedFiles;
|
|
18
|
+
};
|
|
19
|
+
resourceHost.transformResource = async function(data, context) {
|
|
20
|
+
if (context.type !== "style") return null;
|
|
21
|
+
const filename = context.resourceFile ?? context.containingFile.replace(".ts", `.${options?.inlineStylesExtension}`);
|
|
22
|
+
const preprocessed = preprocessStylesheetResult(data, filename, options.stylePreprocessor, {
|
|
23
|
+
filename,
|
|
24
|
+
containingFile: context.containingFile,
|
|
25
|
+
resourceFile: context.resourceFile ?? void 0,
|
|
26
|
+
className: context.className,
|
|
27
|
+
order: context.order,
|
|
28
|
+
inline: !context.resourceFile
|
|
29
|
+
});
|
|
30
|
+
if (options.stylesheetRegistry) {
|
|
31
|
+
const stylesheetId = registerStylesheetContent(options.stylesheetRegistry, {
|
|
32
|
+
code: preprocessed.code,
|
|
33
|
+
dependencies: normalizeStylesheetDependencies(preprocessed.dependencies),
|
|
34
|
+
diagnostics: preprocessed.diagnostics,
|
|
35
|
+
tags: preprocessed.tags,
|
|
36
|
+
containingFile: context.containingFile,
|
|
37
|
+
className: context.className,
|
|
38
|
+
order: context.order,
|
|
39
|
+
inlineStylesExtension: options.inlineStylesExtension,
|
|
40
|
+
resourceFile: context.resourceFile ?? void 0
|
|
41
|
+
});
|
|
42
|
+
debugStyles("NgtscProgram: stylesheet deferred to Vite pipeline", {
|
|
43
|
+
stylesheetId,
|
|
44
|
+
resourceFile: context.resourceFile ?? "(inline)",
|
|
45
|
+
dependencies: preprocessed.dependencies,
|
|
46
|
+
diagnostics: preprocessed.diagnostics,
|
|
47
|
+
tags: preprocessed.tags
|
|
48
|
+
});
|
|
49
|
+
return { content: stylesheetId };
|
|
50
|
+
}
|
|
51
|
+
debugStyles("NgtscProgram: stylesheet processed inline via transform", {
|
|
52
|
+
filename,
|
|
53
|
+
resourceFile: context.resourceFile ?? "(inline)",
|
|
54
|
+
dataLength: preprocessed.code.length
|
|
55
|
+
});
|
|
56
|
+
let stylesheetResult;
|
|
57
|
+
try {
|
|
58
|
+
stylesheetResult = await transform(preprocessed.code, `${filename}?direct`);
|
|
59
|
+
} catch (e) {
|
|
60
|
+
debugStyles("NgtscProgram: stylesheet transform error", {
|
|
61
|
+
filename,
|
|
62
|
+
resourceFile: context.resourceFile ?? "(inline)",
|
|
63
|
+
error: String(e)
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
if (!stylesheetResult?.code) return null;
|
|
67
|
+
return { content: stylesheetResult.code };
|
|
68
|
+
};
|
|
69
|
+
resourceHost.resourceNameToFileName = function(resourceName, containingFile, fallbackResolve) {
|
|
70
|
+
const resolvedPath = normalizePath(fallbackResolve ? fallbackResolve(path.dirname(containingFile), resourceName) : path.join(path.dirname(containingFile), resourceName));
|
|
71
|
+
if (!options.stylesheetRegistry || !hasStyleExtension(resolvedPath)) return resolvedPath;
|
|
72
|
+
const filename = createHash("sha256").update(resolvedPath).digest("hex") + path.extname(resolvedPath);
|
|
73
|
+
options.stylesheetRegistry.registerExternalRequest(filename, resolvedPath);
|
|
74
|
+
debugStyles("NgtscProgram: external stylesheet ID mapped for resolveId", {
|
|
75
|
+
resourceName,
|
|
76
|
+
resolvedPath,
|
|
77
|
+
filename
|
|
78
|
+
});
|
|
79
|
+
return filename;
|
|
80
|
+
};
|
|
58
81
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return files;
|
|
67
|
-
};
|
|
82
|
+
function augmentProgramWithVersioning(program) {
|
|
83
|
+
const baseGetSourceFiles = program.getSourceFiles;
|
|
84
|
+
program.getSourceFiles = function(...parameters) {
|
|
85
|
+
const files = baseGetSourceFiles(...parameters);
|
|
86
|
+
for (const file of files) file.version ??= createHash("sha256").update(file.text).digest("hex");
|
|
87
|
+
return files;
|
|
88
|
+
};
|
|
68
89
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
cache.set(fileName, file);
|
|
78
|
-
}
|
|
79
|
-
return file;
|
|
80
|
-
};
|
|
90
|
+
function augmentHostWithCaching(host, cache) {
|
|
91
|
+
const baseGetSourceFile = host.getSourceFile;
|
|
92
|
+
host.getSourceFile = function(fileName, languageVersion, onError, shouldCreateNewSourceFile, ...parameters) {
|
|
93
|
+
if (!shouldCreateNewSourceFile && cache.has(fileName)) return cache.get(fileName);
|
|
94
|
+
const file = baseGetSourceFile.call(host, fileName, languageVersion, onError, true, ...parameters);
|
|
95
|
+
if (file) cache.set(fileName, file);
|
|
96
|
+
return file;
|
|
97
|
+
};
|
|
81
98
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
result.after = [...(first.after || []), ...(second.after || [])];
|
|
89
|
-
}
|
|
90
|
-
if (first.afterDeclarations || second.afterDeclarations) {
|
|
91
|
-
result.afterDeclarations = [
|
|
92
|
-
...(first.afterDeclarations || []),
|
|
93
|
-
...(second.afterDeclarations || []),
|
|
94
|
-
];
|
|
95
|
-
}
|
|
96
|
-
return result;
|
|
99
|
+
function mergeTransformers(first, second) {
|
|
100
|
+
const result = {};
|
|
101
|
+
if (first.before || second.before) result.before = [...first.before || [], ...second.before || []];
|
|
102
|
+
if (first.after || second.after) result.after = [...first.after || [], ...second.after || []];
|
|
103
|
+
if (first.afterDeclarations || second.afterDeclarations) result.afterDeclarations = [...first.afterDeclarations || [], ...second.afterDeclarations || []];
|
|
104
|
+
return result;
|
|
97
105
|
}
|
|
98
106
|
function hasStyleExtension(file) {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
default:
|
|
105
|
-
return false;
|
|
106
|
-
}
|
|
107
|
+
switch (path.extname(file).toLowerCase()) {
|
|
108
|
+
case ".css":
|
|
109
|
+
case ".scss": return true;
|
|
110
|
+
default: return false;
|
|
111
|
+
}
|
|
107
112
|
}
|
|
113
|
+
//#endregion
|
|
114
|
+
export { augmentHostWithCaching, augmentHostWithResources, augmentProgramWithVersioning, mergeTransformers };
|
|
115
|
+
|
|
108
116
|
//# sourceMappingURL=host.js.map
|
package/src/lib/host.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"host.js","
|
|
1
|
+
{"version":3,"file":"host.js","names":[],"sources":["../../../src/lib/host.ts"],"sourcesContent":["import type { CompilerHost } from '@angular/compiler-cli';\nimport { normalizePath } from 'vite';\n\nimport * as ts from 'typescript';\n\nimport { createHash } from 'node:crypto';\nimport path from 'node:path';\nimport {\n normalizeStylesheetDependencies,\n type StylePreprocessor,\n} from './style-preprocessor.js';\nimport {\n AnalogStylesheetRegistry,\n preprocessStylesheetResult,\n registerStylesheetContent,\n} from './stylesheet-registry.js';\nimport { debugStyles } from './utils/debug.js';\nimport type { SourceFileCache } from './utils/source-file-cache.js';\n\nexport function augmentHostWithResources(\n host: ts.CompilerHost,\n transform: (\n code: string,\n id: string,\n options?: { ssr?: boolean },\n ) => ReturnType<any> | null,\n options: {\n inlineStylesExtension: string;\n isProd?: boolean;\n stylesheetRegistry?: AnalogStylesheetRegistry;\n sourceFileCache?: SourceFileCache;\n stylePreprocessor?: StylePreprocessor;\n },\n): void {\n const resourceHost = host as CompilerHost;\n\n resourceHost.readResource = async function (fileName: string) {\n const filePath = normalizePath(fileName);\n\n const content = (this as any).readFile(filePath);\n\n if (content === undefined) {\n throw new Error('Unable to locate component resource: ' + fileName);\n }\n\n return content;\n };\n\n resourceHost.getModifiedResourceFiles = function () {\n return options?.sourceFileCache?.modifiedFiles;\n };\n\n resourceHost.transformResource = async function (data, context) {\n // Only style resources are supported currently\n if (context.type !== 'style') {\n return null;\n }\n\n const filename =\n context.resourceFile ??\n context.containingFile.replace(\n '.ts',\n `.${options?.inlineStylesExtension}`,\n );\n const preprocessed = preprocessStylesheetResult(\n data,\n filename,\n options.stylePreprocessor,\n {\n filename,\n containingFile: context.containingFile,\n resourceFile: context.resourceFile ?? undefined,\n className: context.className,\n order: context.order,\n inline: !context.resourceFile,\n },\n );\n\n // Externalized path: store preprocessed CSS for Vite's serve-time pipeline.\n // CSS must NOT be transformed here — the load hook returns it into\n // Vite's transform pipeline where PostCSS / Tailwind process it once.\n if (options.stylesheetRegistry) {\n const stylesheetId = registerStylesheetContent(\n options.stylesheetRegistry,\n {\n code: preprocessed.code,\n dependencies: normalizeStylesheetDependencies(\n preprocessed.dependencies,\n ),\n diagnostics: preprocessed.diagnostics,\n tags: preprocessed.tags,\n containingFile: context.containingFile,\n className: context.className,\n order: context.order,\n inlineStylesExtension: options.inlineStylesExtension,\n resourceFile: context.resourceFile ?? undefined,\n },\n );\n debugStyles('NgtscProgram: stylesheet deferred to Vite pipeline', {\n stylesheetId,\n resourceFile: context.resourceFile ?? '(inline)',\n dependencies: preprocessed.dependencies,\n diagnostics: preprocessed.diagnostics,\n tags: preprocessed.tags,\n });\n return { content: stylesheetId };\n }\n\n // Non-externalized: CSS is returned directly to the Angular compiler\n // and never re-enters Vite's pipeline, so transform eagerly.\n debugStyles('NgtscProgram: stylesheet processed inline via transform', {\n filename,\n resourceFile: context.resourceFile ?? '(inline)',\n dataLength: preprocessed.code.length,\n });\n let stylesheetResult;\n\n try {\n stylesheetResult = await transform(\n preprocessed.code,\n `${filename}?direct`,\n );\n } catch (e) {\n debugStyles('NgtscProgram: stylesheet transform error', {\n filename,\n resourceFile: context.resourceFile ?? '(inline)',\n error: String(e),\n });\n }\n\n if (!stylesheetResult?.code) {\n return null;\n }\n\n return { content: stylesheetResult.code };\n };\n\n resourceHost.resourceNameToFileName = function (\n resourceName,\n containingFile,\n fallbackResolve,\n ) {\n const resolvedPath = normalizePath(\n fallbackResolve\n ? fallbackResolve(path.dirname(containingFile), resourceName)\n : path.join(path.dirname(containingFile), resourceName),\n );\n\n // All resource names that have template file extensions are assumed to be templates\n if (!options.stylesheetRegistry || !hasStyleExtension(resolvedPath)) {\n return resolvedPath;\n }\n\n // For external stylesheets, create a unique identifier and store the mapping\n const externalId = createHash('sha256').update(resolvedPath).digest('hex');\n const filename = externalId + path.extname(resolvedPath);\n\n options.stylesheetRegistry.registerExternalRequest(filename, resolvedPath);\n debugStyles('NgtscProgram: external stylesheet ID mapped for resolveId', {\n resourceName,\n resolvedPath,\n filename,\n });\n\n return filename;\n };\n}\n\nexport function augmentProgramWithVersioning(program: ts.Program): void {\n const baseGetSourceFiles = program.getSourceFiles;\n program.getSourceFiles = function (...parameters) {\n const files: readonly (ts.SourceFile & { version?: string })[] =\n baseGetSourceFiles(...parameters);\n\n for (const file of files) {\n file.version ??= createHash('sha256').update(file.text).digest('hex');\n }\n\n return files;\n };\n}\n\nexport function augmentHostWithCaching(\n host: ts.CompilerHost,\n cache: Map<string, ts.SourceFile>,\n): void {\n const baseGetSourceFile = host.getSourceFile;\n host.getSourceFile = function (\n fileName,\n languageVersion,\n onError,\n shouldCreateNewSourceFile,\n ...parameters\n ) {\n if (!shouldCreateNewSourceFile && cache.has(fileName)) {\n return cache.get(fileName);\n }\n\n const file = baseGetSourceFile.call(\n host,\n fileName,\n languageVersion,\n onError,\n true,\n ...parameters,\n );\n\n if (file) {\n cache.set(fileName, file);\n }\n\n return file;\n };\n}\n\nexport function mergeTransformers(\n first: ts.CustomTransformers,\n second: ts.CustomTransformers,\n): ts.CustomTransformers {\n const result: ts.CustomTransformers = {};\n\n if (first.before || second.before) {\n result.before = [...(first.before || []), ...(second.before || [])];\n }\n\n if (first.after || second.after) {\n result.after = [...(first.after || []), ...(second.after || [])];\n }\n\n if (first.afterDeclarations || second.afterDeclarations) {\n result.afterDeclarations = [\n ...(first.afterDeclarations || []),\n ...(second.afterDeclarations || []),\n ];\n }\n\n return result;\n}\n\nfunction hasStyleExtension(file: string): boolean {\n const extension = path.extname(file).toLowerCase();\n\n switch (extension) {\n case '.css':\n case '.scss':\n return true;\n default:\n return false;\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Plugin } from
|
|
2
|
-
import { EmitFileResult } from
|
|
3
|
-
export declare function liveReloadPlugin({ classNames, fileEmitter
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
import { EmitFileResult } from "./models.js";
|
|
3
|
+
export declare function liveReloadPlugin({ classNames, fileEmitter }: {
|
|
4
|
+
classNames: Map<string, string>;
|
|
5
|
+
fileEmitter: (file: string) => EmitFileResult | undefined;
|
|
6
6
|
}): Plugin;
|