UNPKG

14.6 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright Google LLC All Rights Reserved.
5 *
6 * Use of this source code is governed by an MIT-style license that can be
7 * found in the LICENSE file at https://angular.io/license
8 */
9var __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}));
20var __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});
25var __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};
32Object.defineProperty(exports, "__esModule", { value: true });
33exports.WebpackResourceLoader = void 0;
34const path = __importStar(require("path"));
35const vm = __importStar(require("vm"));
36const paths_1 = require("./ivy/paths");
37const inline_resource_1 = require("./loaders/inline-resource");
38const replace_resources_1 = require("./transformers/replace_resources");
39class WebpackResourceLoader {
40 constructor(shouldCache) {
41 this._fileDependencies = new Map();
42 this._reverseDependencies = new Map();
43 this.modifiedResources = new Set();
44 this.outputPathCounter = 1;
45 this.inlineDataLoaderPath = inline_resource_1.InlineAngularResourceLoaderPath;
46 if (shouldCache) {
47 this.fileCache = new Map();
48 this.assetCache = new Map();
49 }
50 }
51 update(parentCompilation, changedFiles) {
52 var _a, _b, _c, _d, _e;
53 this._parentCompilation = parentCompilation;
54 // Update resource cache and modified resources
55 this.modifiedResources.clear();
56 if (changedFiles) {
57 for (const changedFile of changedFiles) {
58 const changedFileNormalized = (0, paths_1.normalizePath)(changedFile);
59 (_a = this.assetCache) === null || _a === void 0 ? void 0 : _a.delete(changedFileNormalized);
60 for (const affectedResource of this.getAffectedResources(changedFile)) {
61 const affectedResourceNormalized = (0, paths_1.normalizePath)(affectedResource);
62 (_b = this.fileCache) === null || _b === void 0 ? void 0 : _b.delete(affectedResourceNormalized);
63 this.modifiedResources.add(affectedResource);
64 for (const effectedDependencies of this.getResourceDependencies(affectedResourceNormalized)) {
65 (_c = this.assetCache) === null || _c === void 0 ? void 0 : _c.delete((0, paths_1.normalizePath)(effectedDependencies));
66 }
67 }
68 }
69 }
70 else {
71 (_d = this.fileCache) === null || _d === void 0 ? void 0 : _d.clear();
72 (_e = this.assetCache) === null || _e === void 0 ? void 0 : _e.clear();
73 }
74 // Re-emit all assets for un-effected files
75 if (this.assetCache) {
76 for (const [, { name, source, info }] of this.assetCache) {
77 this._parentCompilation.emitAsset(name, source, info);
78 }
79 }
80 }
81 clearParentCompilation() {
82 this._parentCompilation = undefined;
83 }
84 getModifiedResourceFiles() {
85 return this.modifiedResources;
86 }
87 getResourceDependencies(filePath) {
88 return this._fileDependencies.get(filePath) || [];
89 }
90 getAffectedResources(file) {
91 return this._reverseDependencies.get(file) || [];
92 }
93 setAffectedResources(file, resources) {
94 this._reverseDependencies.set(file, new Set(resources));
95 }
96 // eslint-disable-next-line max-lines-per-function
97 async _compile(filePath, data, fileExtension, resourceType, containingFile) {
98 if (!this._parentCompilation) {
99 throw new Error('WebpackResourceLoader cannot be used without parentCompilation');
100 }
101 const { context, webpack } = this._parentCompilation.compiler;
102 const { EntryPlugin, NormalModule, library, node, sources, util: { createHash }, } = webpack;
103 const getEntry = () => {
104 if (filePath) {
105 return `${filePath}?${replace_resources_1.NG_COMPONENT_RESOURCE_QUERY}`;
106 }
107 else if (resourceType) {
108 return (
109 // app.component.ts-2.css?ngResource!=!@ngtools/webpack/src/loaders/inline-resource.js!app.component.ts
110 `${containingFile}-${this.outputPathCounter}.${fileExtension}` +
111 `?${replace_resources_1.NG_COMPONENT_RESOURCE_QUERY}!=!${this.inlineDataLoaderPath}!${containingFile}`);
112 }
113 else if (data) {
114 // Create a special URL for reading the resource from memory
115 return `angular-resource:${resourceType},${createHash('xxhash64')
116 .update(data)
117 .digest('hex')}`;
118 }
119 throw new Error(`"filePath", "resourceType" or "data" must be specified.`);
120 };
121 const entry = getEntry();
122 // Simple sanity check.
123 if (filePath === null || filePath === void 0 ? void 0 : filePath.match(/\.[jt]s$/)) {
124 throw new Error(`Cannot use a JavaScript or TypeScript file (${filePath}) in a component's styleUrls or templateUrl.`);
125 }
126 const outputFilePath = filePath ||
127 `${containingFile}-angular-inline--${this.outputPathCounter++}.${resourceType === 'template' ? 'html' : 'css'}`;
128 const outputOptions = {
129 filename: outputFilePath,
130 library: {
131 type: 'var',
132 name: 'resource',
133 },
134 };
135 const childCompiler = this._parentCompilation.createChildCompiler('angular-compiler:resource', outputOptions, [
136 new node.NodeTemplatePlugin(outputOptions),
137 new node.NodeTargetPlugin(),
138 new EntryPlugin(context, entry, { name: 'resource' }),
139 new library.EnableLibraryPlugin('var'),
140 ]);
141 childCompiler.hooks.thisCompilation.tap('angular-compiler', (compilation, { normalModuleFactory }) => {
142 // If no data is provided, the resource will be read from the filesystem
143 if (data !== undefined) {
144 normalModuleFactory.hooks.resolveForScheme
145 .for('angular-resource')
146 .tap('angular-compiler', (resourceData) => {
147 if (filePath) {
148 resourceData.path = filePath;
149 resourceData.resource = filePath;
150 }
151 return true;
152 });
153 NormalModule.getCompilationHooks(compilation)
154 .readResourceForScheme.for('angular-resource')
155 .tap('angular-compiler', () => data);
156 compilation[inline_resource_1.InlineAngularResourceSymbol] = data;
157 }
158 compilation.hooks.additionalAssets.tap('angular-compiler', () => {
159 const asset = compilation.assets[outputFilePath];
160 if (!asset) {
161 return;
162 }
163 try {
164 const output = this._evaluate(outputFilePath, asset.source().toString());
165 if (typeof output === 'string') {
166 compilation.assets[outputFilePath] = new sources.RawSource(output);
167 }
168 }
169 catch (error) {
170 // Use compilation errors, as otherwise webpack will choke
171 compilation.errors.push(error);
172 }
173 });
174 });
175 let finalContent;
176 childCompiler.hooks.compilation.tap('angular-compiler', (childCompilation) => {
177 childCompilation.hooks.processAssets.tap({ name: 'angular-compiler', stage: webpack.Compilation.PROCESS_ASSETS_STAGE_REPORT }, () => {
178 var _a;
179 finalContent = (_a = childCompilation.assets[outputFilePath]) === null || _a === void 0 ? void 0 : _a.source().toString();
180 for (const { files } of childCompilation.chunks) {
181 for (const file of files) {
182 childCompilation.deleteAsset(file);
183 }
184 }
185 });
186 });
187 return new Promise((resolve, reject) => {
188 childCompiler.runAsChild((error, _, childCompilation) => {
189 var _a, _b;
190 if (error) {
191 reject(error);
192 return;
193 }
194 else if (!childCompilation) {
195 reject(new Error('Unknown child compilation error'));
196 return;
197 }
198 // Workaround to attempt to reduce memory usage of child compilations.
199 // This removes the child compilation from the main compilation and manually propagates
200 // all dependencies, warnings, and errors.
201 const parent = childCompiler.parentCompilation;
202 if (parent) {
203 parent.children = parent.children.filter((child) => child !== childCompilation);
204 let fileDependencies;
205 for (const dependency of childCompilation.fileDependencies) {
206 // Skip paths that do not appear to be files (have no extension).
207 // `fileDependencies` can contain directories and not just files which can
208 // cause incorrect cache invalidation on rebuilds.
209 if (!path.extname(dependency)) {
210 continue;
211 }
212 if (data && containingFile && dependency.endsWith(entry)) {
213 // use containing file if the resource was inline
214 parent.fileDependencies.add(containingFile);
215 }
216 else {
217 parent.fileDependencies.add(dependency);
218 }
219 // Save the dependencies for this resource.
220 if (filePath) {
221 const resolvedFile = (0, paths_1.normalizePath)(dependency);
222 const entry = this._reverseDependencies.get(resolvedFile);
223 if (entry) {
224 entry.add(filePath);
225 }
226 else {
227 this._reverseDependencies.set(resolvedFile, new Set([filePath]));
228 }
229 if (fileDependencies) {
230 fileDependencies.add(dependency);
231 }
232 else {
233 fileDependencies = new Set([dependency]);
234 this._fileDependencies.set(filePath, fileDependencies);
235 }
236 }
237 }
238 parent.contextDependencies.addAll(childCompilation.contextDependencies);
239 parent.missingDependencies.addAll(childCompilation.missingDependencies);
240 parent.buildDependencies.addAll(childCompilation.buildDependencies);
241 parent.warnings.push(...childCompilation.warnings);
242 parent.errors.push(...childCompilation.errors);
243 if (this.assetCache) {
244 for (const { info, name, source } of childCompilation.getAssets()) {
245 // Use the originating file as the cache key if present
246 // Otherwise, generate a cache key based on the generated name
247 const cacheKey = (_a = info.sourceFilename) !== null && _a !== void 0 ? _a : `!![GENERATED]:${name}`;
248 this.assetCache.set(cacheKey, { info, name, source });
249 }
250 }
251 }
252 resolve({
253 content: finalContent !== null && finalContent !== void 0 ? finalContent : '',
254 success: ((_b = childCompilation.errors) === null || _b === void 0 ? void 0 : _b.length) === 0,
255 });
256 });
257 });
258 }
259 _evaluate(filename, source) {
260 var _a;
261 // Evaluate code
262 const context = {};
263 try {
264 vm.runInNewContext(source, context, { filename });
265 }
266 catch {
267 // Error are propagated through the child compilation.
268 return null;
269 }
270 if (typeof context.resource === 'string') {
271 return context.resource;
272 }
273 else if (typeof ((_a = context.resource) === null || _a === void 0 ? void 0 : _a.default) === 'string') {
274 return context.resource.default;
275 }
276 throw new Error(`The loader "${filename}" didn't return a string.`);
277 }
278 async get(filePath) {
279 var _a;
280 const normalizedFile = (0, paths_1.normalizePath)(filePath);
281 let compilationResult = (_a = this.fileCache) === null || _a === void 0 ? void 0 : _a.get(normalizedFile);
282 if (compilationResult === undefined) {
283 // cache miss so compile resource
284 compilationResult = await this._compile(filePath);
285 // Only cache if compilation was successful
286 if (this.fileCache && compilationResult.success) {
287 this.fileCache.set(normalizedFile, compilationResult);
288 }
289 }
290 return compilationResult.content;
291 }
292 async process(data, fileExtension, resourceType, containingFile) {
293 if (data.trim().length === 0) {
294 return '';
295 }
296 const compilationResult = await this._compile(undefined, data, fileExtension, resourceType, containingFile);
297 return compilationResult.content;
298 }
299}
300exports.WebpackResourceLoader = WebpackResourceLoader;