1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 | var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
10 | if (k2 === undefined) k2 = k;
|
11 | var desc = Object.getOwnPropertyDescriptor(m, k);
|
12 | if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
13 | desc = { enumerable: true, get: function() { return m[k]; } };
|
14 | }
|
15 | Object.defineProperty(o, k2, desc);
|
16 | }) : (function(o, m, k, k2) {
|
17 | if (k2 === undefined) k2 = k;
|
18 | o[k2] = m[k];
|
19 | }));
|
20 | var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
21 | Object.defineProperty(o, "default", { enumerable: true, value: v });
|
22 | }) : function(o, v) {
|
23 | o["default"] = v;
|
24 | });
|
25 | var __importStar = (this && this.__importStar) || function (mod) {
|
26 | if (mod && mod.__esModule) return mod;
|
27 | var result = {};
|
28 | if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
29 | __setModuleDefault(result, mod);
|
30 | return result;
|
31 | };
|
32 | var __importDefault = (this && this.__importDefault) || function (mod) {
|
33 | return (mod && mod.__esModule) ? mod : { "default": mod };
|
34 | };
|
35 | Object.defineProperty(exports, "__esModule", { value: true });
|
36 | exports.WebpackResourceLoader = void 0;
|
37 | const node_assert_1 = __importDefault(require("node:assert"));
|
38 | const node_buffer_1 = require("node:buffer");
|
39 | const path = __importStar(require("node:path"));
|
40 | const vm = __importStar(require("node:vm"));
|
41 | const diagnostics_1 = require("./ivy/diagnostics");
|
42 | const paths_1 = require("./ivy/paths");
|
43 | const inline_resource_1 = require("./loaders/inline-resource");
|
44 | const replace_resources_1 = require("./transformers/replace_resources");
|
45 | class WebpackResourceLoader {
|
46 | _parentCompilation;
|
47 | _fileDependencies = new Map();
|
48 | _reverseDependencies = new Map();
|
49 | fileCache;
|
50 | assetCache;
|
51 | modifiedResources = new Set();
|
52 | outputPathCounter = 1;
|
53 | inlineDataLoaderPath = inline_resource_1.InlineAngularResourceLoaderPath;
|
54 | constructor(shouldCache) {
|
55 | if (shouldCache) {
|
56 | this.fileCache = new Map();
|
57 | this.assetCache = new Map();
|
58 | }
|
59 | }
|
60 | update(parentCompilation, changedFiles) {
|
61 | this._parentCompilation = parentCompilation;
|
62 |
|
63 | this.modifiedResources.clear();
|
64 | if (changedFiles) {
|
65 | for (const changedFile of changedFiles) {
|
66 | const changedFileNormalized = (0, paths_1.normalizePath)(changedFile);
|
67 | this.assetCache?.delete(changedFileNormalized);
|
68 | for (const affectedResource of this.getAffectedResources(changedFile)) {
|
69 | const affectedResourceNormalized = (0, paths_1.normalizePath)(affectedResource);
|
70 | this.fileCache?.delete(affectedResourceNormalized);
|
71 | this.modifiedResources.add(affectedResource);
|
72 | for (const effectedDependencies of this.getResourceDependencies(affectedResourceNormalized)) {
|
73 | this.assetCache?.delete((0, paths_1.normalizePath)(effectedDependencies));
|
74 | }
|
75 | }
|
76 | }
|
77 | }
|
78 | else {
|
79 | this.fileCache?.clear();
|
80 | this.assetCache?.clear();
|
81 | }
|
82 |
|
83 | if (this.assetCache) {
|
84 | for (const [, { name, source, info }] of this.assetCache) {
|
85 | this._parentCompilation.emitAsset(name, source, info);
|
86 | }
|
87 | }
|
88 | }
|
89 | clearParentCompilation() {
|
90 | this._parentCompilation = undefined;
|
91 | }
|
92 | getModifiedResourceFiles() {
|
93 | return this.modifiedResources;
|
94 | }
|
95 | getResourceDependencies(filePath) {
|
96 | return this._fileDependencies.get(filePath) || [];
|
97 | }
|
98 | getAffectedResources(file) {
|
99 | return this._reverseDependencies.get(file) || [];
|
100 | }
|
101 | setAffectedResources(file, resources) {
|
102 | this._reverseDependencies.set(file, new Set(resources));
|
103 | }
|
104 |
|
105 | async _compile(filePath, data, fileExtension, resourceType, containingFile) {
|
106 | if (!this._parentCompilation) {
|
107 | throw new Error('WebpackResourceLoader cannot be used without parentCompilation');
|
108 | }
|
109 | const { context, webpack } = this._parentCompilation.compiler;
|
110 | const { EntryPlugin, NormalModule, library, node, sources, util: { createHash }, } = webpack;
|
111 | const getEntry = () => {
|
112 | if (filePath) {
|
113 | return `${filePath}?${replace_resources_1.NG_COMPONENT_RESOURCE_QUERY}`;
|
114 | }
|
115 | else if (resourceType) {
|
116 | return (
|
117 |
|
118 | `${containingFile}-${this.outputPathCounter}.${fileExtension}` +
|
119 | `?${replace_resources_1.NG_COMPONENT_RESOURCE_QUERY}!=!${this.inlineDataLoaderPath}!${containingFile}`);
|
120 | }
|
121 | else if (data) {
|
122 |
|
123 | return `angular-resource:${resourceType},${createHash('xxhash64')
|
124 | .update(data)
|
125 | .digest('hex')}`;
|
126 | }
|
127 | throw new Error(`"filePath", "resourceType" or "data" must be specified.`);
|
128 | };
|
129 | const entry = getEntry();
|
130 |
|
131 | if (filePath?.match(/\.[jt]s$/)) {
|
132 | throw new Error(`Cannot use a JavaScript or TypeScript file (${filePath}) in a component's styleUrls or templateUrl.`);
|
133 | }
|
134 | const outputFilePath = filePath ||
|
135 | `${containingFile}-angular-inline--${this.outputPathCounter++}.${resourceType === 'template' ? 'html' : 'css'}`;
|
136 | const outputOptions = {
|
137 | filename: outputFilePath,
|
138 | library: {
|
139 | type: 'var',
|
140 | name: 'resource',
|
141 | },
|
142 | };
|
143 | const childCompiler = this._parentCompilation.createChildCompiler('angular-compiler:resource', outputOptions, [
|
144 | new node.NodeTemplatePlugin(),
|
145 | new node.NodeTargetPlugin(),
|
146 | new EntryPlugin(context, entry, { name: 'resource' }),
|
147 | new library.EnableLibraryPlugin('var'),
|
148 | ]);
|
149 | childCompiler.hooks.thisCompilation.tap('angular-compiler', (compilation, { normalModuleFactory }) => {
|
150 |
|
151 | if (data !== undefined) {
|
152 | normalModuleFactory.hooks.resolveForScheme
|
153 | .for('angular-resource')
|
154 | .tap('angular-compiler', (resourceData) => {
|
155 | if (filePath) {
|
156 | resourceData.path = filePath;
|
157 | resourceData.resource = filePath;
|
158 | }
|
159 | return true;
|
160 | });
|
161 | NormalModule.getCompilationHooks(compilation)
|
162 | .readResourceForScheme.for('angular-resource')
|
163 | .tap('angular-compiler', () => data);
|
164 | compilation[inline_resource_1.InlineAngularResourceSymbol] = data;
|
165 | }
|
166 | compilation.hooks.additionalAssets.tap('angular-compiler', () => {
|
167 | const asset = compilation.assets[outputFilePath];
|
168 | if (!asset) {
|
169 | return;
|
170 | }
|
171 | try {
|
172 | const output = this._evaluate(outputFilePath, asset.source().toString());
|
173 | if (typeof output === 'string') {
|
174 | compilation.assets[outputFilePath] = new sources.RawSource(output);
|
175 | }
|
176 | }
|
177 | catch (error) {
|
178 | (0, node_assert_1.default)(error instanceof Error, 'catch clause variable is not an Error instance');
|
179 |
|
180 | (0, diagnostics_1.addError)(compilation, error.message);
|
181 | }
|
182 | });
|
183 | });
|
184 | let finalContent;
|
185 | childCompiler.hooks.compilation.tap('angular-compiler', (childCompilation) => {
|
186 | childCompilation.hooks.processAssets.tap({ name: 'angular-compiler', stage: webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT }, () => {
|
187 | finalContent = childCompilation.assets[outputFilePath]?.source().toString();
|
188 | for (const { files } of childCompilation.chunks) {
|
189 | for (const file of files) {
|
190 | childCompilation.deleteAsset(file);
|
191 | }
|
192 | }
|
193 | });
|
194 | });
|
195 | return new Promise((resolve, reject) => {
|
196 | childCompiler.runAsChild((error, _, childCompilation) => {
|
197 | if (error) {
|
198 | reject(error);
|
199 | return;
|
200 | }
|
201 | else if (!childCompilation) {
|
202 | reject(new Error('Unknown child compilation error'));
|
203 | return;
|
204 | }
|
205 |
|
206 |
|
207 |
|
208 | const parent = childCompiler.parentCompilation;
|
209 | if (parent) {
|
210 | parent.children = parent.children.filter((child) => child !== childCompilation);
|
211 | let fileDependencies;
|
212 | for (const dependency of childCompilation.fileDependencies) {
|
213 |
|
214 |
|
215 |
|
216 | if (!path.extname(dependency)) {
|
217 | continue;
|
218 | }
|
219 | if (data && containingFile && dependency.endsWith(entry)) {
|
220 |
|
221 | parent.fileDependencies.add(containingFile);
|
222 | }
|
223 | else {
|
224 | parent.fileDependencies.add(dependency);
|
225 | }
|
226 |
|
227 | if (filePath) {
|
228 | const resolvedFile = (0, paths_1.normalizePath)(dependency);
|
229 | const entry = this._reverseDependencies.get(resolvedFile);
|
230 | if (entry) {
|
231 | entry.add(filePath);
|
232 | }
|
233 | else {
|
234 | this._reverseDependencies.set(resolvedFile, new Set([filePath]));
|
235 | }
|
236 | if (fileDependencies) {
|
237 | fileDependencies.add(dependency);
|
238 | }
|
239 | else {
|
240 | fileDependencies = new Set([dependency]);
|
241 | this._fileDependencies.set(filePath, fileDependencies);
|
242 | }
|
243 | }
|
244 | }
|
245 | parent.contextDependencies.addAll(childCompilation.contextDependencies);
|
246 | parent.missingDependencies.addAll(childCompilation.missingDependencies);
|
247 | parent.buildDependencies.addAll(childCompilation.buildDependencies);
|
248 | parent.warnings.push(...childCompilation.warnings);
|
249 | parent.errors.push(...childCompilation.errors);
|
250 | if (this.assetCache) {
|
251 | for (const { info, name, source } of childCompilation.getAssets()) {
|
252 |
|
253 |
|
254 | const cacheKey = info.sourceFilename ?? `!![GENERATED]:${name}`;
|
255 | this.assetCache.set(cacheKey, { info, name, source });
|
256 | }
|
257 | }
|
258 | }
|
259 | resolve({
|
260 | content: finalContent ?? '',
|
261 | success: childCompilation.errors?.length === 0,
|
262 | });
|
263 | });
|
264 | });
|
265 | }
|
266 | _evaluate(filename, source) {
|
267 |
|
268 |
|
269 | const context = {
|
270 | btoa(input) {
|
271 | return node_buffer_1.Buffer.from(input).toString('base64');
|
272 | },
|
273 | };
|
274 | try {
|
275 | vm.runInNewContext(source, context, { filename });
|
276 | }
|
277 | catch {
|
278 |
|
279 | return null;
|
280 | }
|
281 | if (typeof context.resource === 'string') {
|
282 | return context.resource;
|
283 | }
|
284 | else if (typeof context.resource?.default === 'string') {
|
285 | return context.resource.default;
|
286 | }
|
287 | throw new Error(`The loader "${filename}" didn't return a string.`);
|
288 | }
|
289 | async get(filePath) {
|
290 | const normalizedFile = (0, paths_1.normalizePath)(filePath);
|
291 | let compilationResult = this.fileCache?.get(normalizedFile);
|
292 | if (compilationResult === undefined) {
|
293 |
|
294 | compilationResult = await this._compile(filePath);
|
295 |
|
296 | if (this.fileCache && compilationResult.success) {
|
297 | this.fileCache.set(normalizedFile, compilationResult);
|
298 | }
|
299 | }
|
300 | return compilationResult.content;
|
301 | }
|
302 | async process(data, fileExtension, resourceType, containingFile) {
|
303 | if (data.trim().length === 0) {
|
304 | return '';
|
305 | }
|
306 | const compilationResult = await this._compile(undefined, data, fileExtension, resourceType, containingFile);
|
307 | return compilationResult.content;
|
308 | }
|
309 | }
|
310 | exports.WebpackResourceLoader = WebpackResourceLoader;
|