1 | "use strict";
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
16 | return new (P || (P = Promise))(function (resolve, reject) {
|
17 | function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
18 | function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
19 | function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
|
20 | step((generator = generator.apply(thisArg, _arguments || [])).next());
|
21 | });
|
22 | };
|
23 | var __asyncValues = (this && this.__asyncValues) || function (o) {
|
24 | if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
25 | var m = o[Symbol.asyncIterator], i;
|
26 | return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
27 | function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
28 | function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
29 | };
|
30 | var __await = (this && this.__await) || function (v) { return this instanceof __await ? (this.v = v, this) : new __await(v); }
|
31 | var __asyncGenerator = (this && this.__asyncGenerator) || function (thisArg, _arguments, generator) {
|
32 | if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
33 | var g = generator.apply(thisArg, _arguments || []), i, q = [];
|
34 | return i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i;
|
35 | function verb(n) { if (g[n]) i[n] = function (v) { return new Promise(function (a, b) { q.push([n, v, a, b]) > 1 || resume(n, v); }); }; }
|
36 | function resume(n, v) { try { step(g[n](v)); } catch (e) { settle(q[0][3], e); } }
|
37 | function step(r) { r.value instanceof __await ? Promise.resolve(r.value.v).then(fulfill, reject) : settle(q[0][2], r); }
|
38 | function fulfill(value) { resume("next", value); }
|
39 | function reject(value) { resume("throw", value); }
|
40 | function settle(f, v) { if (f(v), q.shift(), q.length) resume(q[0][0], q[0][1]); }
|
41 | };
|
42 | Object.defineProperty(exports, "__esModule", { value: true });
|
43 | const path = require("path");
|
44 | const logging = require("plylog");
|
45 | const polymer_analyzer_1 = require("polymer-analyzer");
|
46 | const stream_1 = require("stream");
|
47 | const vinyl_fs_1 = require("vinyl-fs");
|
48 | const path_transformers_1 = require("./path-transformers");
|
49 | const streams_1 = require("./streams");
|
50 | const logger = logging.getLogger('cli.build.analyzer');
|
51 |
|
52 |
|
53 |
|
54 |
|
55 |
|
56 | class ResolveTransform extends streams_1.AsyncTransformStream {
|
57 | constructor(buildAnalyzer) {
|
58 | super({ objectMode: true });
|
59 | this._buildAnalyzer = buildAnalyzer;
|
60 | }
|
61 | _transformIter(files) {
|
62 | return __asyncGenerator(this, arguments, function* _transformIter_1() {
|
63 | var e_1, _a;
|
64 | try {
|
65 | for (var files_1 = __asyncValues(files), files_1_1; files_1_1 = yield __await(files_1.next()), !files_1_1.done;) {
|
66 | const file = files_1_1.value;
|
67 | this._buildAnalyzer.resolveFile(file);
|
68 | yield yield __await(file);
|
69 | }
|
70 | }
|
71 | catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
72 | finally {
|
73 | try {
|
74 | if (files_1_1 && !files_1_1.done && (_a = files_1.return)) yield __await(_a.call(files_1));
|
75 | }
|
76 | finally { if (e_1) throw e_1.error; }
|
77 | }
|
78 | });
|
79 | }
|
80 | }
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 |
|
87 |
|
88 |
|
89 |
|
90 |
|
91 |
|
92 |
|
93 | class AnalyzeTransform extends streams_1.AsyncTransformStream {
|
94 | constructor(buildAnalyzer) {
|
95 |
|
96 |
|
97 |
|
98 |
|
99 | super({ objectMode: true, highWaterMark: 10000 });
|
100 | this._buildAnalyzer = buildAnalyzer;
|
101 | }
|
102 | _transformIter(files) {
|
103 | return __asyncGenerator(this, arguments, function* _transformIter_2() {
|
104 | var e_2, _a;
|
105 | try {
|
106 | for (var files_2 = __asyncValues(files), files_2_1; files_2_1 = yield __await(files_2.next()), !files_2_1.done;) {
|
107 | const file = files_2_1.value;
|
108 | yield __await(this._buildAnalyzer.analyzeFile(file));
|
109 | yield yield __await(file);
|
110 | }
|
111 | }
|
112 | catch (e_2_1) { e_2 = { error: e_2_1 }; }
|
113 | finally {
|
114 | try {
|
115 | if (files_2_1 && !files_2_1.done && (_a = files_2.return)) yield __await(_a.call(files_2));
|
116 | }
|
117 | finally { if (e_2) throw e_2.error; }
|
118 | }
|
119 | });
|
120 | }
|
121 | }
|
122 | class BuildAnalyzer {
|
123 | constructor(config,
|
124 | /** If null is given, we do not log warnings. */
|
125 | streamToWarnTo = process.stdout) {
|
126 | this.streamToWarnTo = streamToWarnTo;
|
127 | this.started = false;
|
128 | this.sourceFilesLoaded = false;
|
129 | this.files = new Map();
|
130 | this.warnings = new Set();
|
131 | this._dependencyAnalysis = {
|
132 | depsToFragments: new Map(),
|
133 | fragmentToDeps: new Map(),
|
134 | fragmentToFullDeps: new Map()
|
135 | };
|
136 | this.config = config;
|
137 | this.loader = new StreamLoader(this);
|
138 | this.analyzer = new polymer_analyzer_1.Analyzer({
|
139 | urlLoader: this.loader,
|
140 |
|
141 |
|
142 | urlResolver: new polymer_analyzer_1.FsUrlResolver(config.root),
|
143 | moduleResolution: config.moduleResolution === 'none' ?
|
144 | undefined :
|
145 | config.moduleResolution,
|
146 | });
|
147 | this.allFragmentsToAnalyze =
|
148 | new Set(this.config.allFragments.map((f) => f));
|
149 | this.analyzeDependencies = new Promise((resolve, _reject) => {
|
150 | this._resolveDependencyAnalysis = resolve;
|
151 | });
|
152 | const lintOptions = (this.config.lint || {});
|
153 | const warningCodesToIgnore = new Set(lintOptions.ignoreWarnings || []);
|
154 |
|
155 |
|
156 | warningCodesToIgnore.add('not-loadable');
|
157 | this._warningsFilter = new polymer_analyzer_1.WarningFilter({ warningCodesToIgnore, minimumSeverity: polymer_analyzer_1.Severity.WARNING });
|
158 | }
|
159 | |
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 | startAnalysis() {
|
166 | if (this.started) {
|
167 | return;
|
168 | }
|
169 | this.started = true;
|
170 |
|
171 | this._dependenciesStream = new stream_1.PassThrough({ objectMode: true });
|
172 | this._sourcesStream = vinyl_fs_1.src(this.config.sources, {
|
173 | cwdbase: true,
|
174 | nodir: true,
|
175 | });
|
176 |
|
177 |
|
178 |
|
179 | this._sourcesProcessingStream =
|
180 | this._sourcesStream
|
181 | .on('error', (err) => this._sourcesProcessingStream.emit('error', err))
|
182 | .pipe(new ResolveTransform(this))
|
183 | .on('error', (err) => this._sourcesProcessingStream.emit('error', err))
|
184 | .on('end', this.onSourcesStreamComplete.bind(this))
|
185 | .pipe(new AnalyzeTransform(this));
|
186 |
|
187 |
|
188 |
|
189 | this._dependenciesProcessingStream =
|
190 | this._dependenciesStream
|
191 | .on('error', (err) => this._dependenciesProcessingStream.emit('error', err))
|
192 | .pipe(new streams_1.VinylReaderTransform())
|
193 | .on('error', (err) => this._dependenciesProcessingStream.emit('error', err))
|
194 | .pipe(new ResolveTransform(this));
|
195 | }
|
196 | |
197 |
|
198 |
|
199 |
|
200 | dependencies() {
|
201 | this.startAnalysis();
|
202 | return this._dependenciesProcessingStream;
|
203 | }
|
204 | |
205 |
|
206 |
|
207 |
|
208 | sources() {
|
209 | this.startAnalysis();
|
210 | return this._sourcesProcessingStream;
|
211 | }
|
212 | |
213 |
|
214 |
|
215 | resolveFile(file) {
|
216 | const filePath = file.path;
|
217 | this.addFile(file);
|
218 |
|
219 | if (this.loader.hasDeferredFile(filePath)) {
|
220 | this.loader.resolveDeferredFile(filePath, file);
|
221 | }
|
222 | }
|
223 | |
224 |
|
225 |
|
226 |
|
227 |
|
228 | analyzeFile(file) {
|
229 | return __awaiter(this, void 0, void 0, function* () {
|
230 | const filePath = file.path;
|
231 |
|
232 | if (this.config.isFragment(filePath)) {
|
233 | const deps = yield this._getDependencies(this.analyzer.resolveUrl(path_transformers_1.urlFromPath(this.config.root, filePath)));
|
234 | this._addDependencies(filePath, deps);
|
235 | this.allFragmentsToAnalyze.delete(filePath);
|
236 |
|
237 | if (this.allFragmentsToAnalyze.size === 0) {
|
238 | this._done();
|
239 | }
|
240 | }
|
241 | });
|
242 | }
|
243 | |
244 |
|
245 |
|
246 | onSourcesStreamComplete() {
|
247 |
|
248 |
|
249 | for (const filePath of this.loader.deferredFiles.keys()) {
|
250 | if (this.config.isSource(filePath)) {
|
251 | const err = new Error(`Not found: ${filePath}`);
|
252 | this.loader.rejectDeferredFile(filePath, err);
|
253 | }
|
254 | }
|
255 |
|
256 | this.sourceFilesLoaded = true;
|
257 | }
|
258 | |
259 |
|
260 |
|
261 |
|
262 | emitAnalysisError(err) {
|
263 | this._sourcesProcessingStream.emit('error', err);
|
264 | this._dependenciesProcessingStream.emit('error', err);
|
265 | }
|
266 | |
267 |
|
268 |
|
269 |
|
270 |
|
271 | _done() {
|
272 | this.printWarnings();
|
273 | const allWarningCount = this.countWarningsByType();
|
274 | const errorWarningCount = allWarningCount.get(polymer_analyzer_1.Severity.ERROR);
|
275 |
|
276 | if (errorWarningCount > 0) {
|
277 | this.emitAnalysisError(new Error(`${errorWarningCount} error(s) occurred during build.`));
|
278 | return;
|
279 | }
|
280 |
|
281 |
|
282 | for (const filePath of this.loader.deferredFiles.keys()) {
|
283 | const err = new Error(`Not found: ${filePath}`);
|
284 | this.loader.rejectDeferredFile(filePath, err);
|
285 | return;
|
286 | }
|
287 |
|
288 | this._dependenciesStream.end();
|
289 | this._resolveDependencyAnalysis(this._dependencyAnalysis);
|
290 | }
|
291 | getFile(filepath) {
|
292 | const url = path_transformers_1.urlFromPath(this.config.root, filepath);
|
293 | return this.getFileByUrl(url);
|
294 | }
|
295 | getFileByUrl(url) {
|
296 |
|
297 | if (url.startsWith('/')) {
|
298 | url = url.substring(1);
|
299 | }
|
300 | return this.files.get(url);
|
301 | }
|
302 | |
303 |
|
304 |
|
305 |
|
306 |
|
307 |
|
308 | addFile(file) {
|
309 | logger.debug(`addFile: ${file.path}`);
|
310 |
|
311 |
|
312 | const filepath = path.normalize(file.path);
|
313 |
|
314 | this.files.set(path_transformers_1.urlFromPath(this.config.root, filepath), file);
|
315 | }
|
316 | printWarnings() {
|
317 | if (this.streamToWarnTo === null) {
|
318 | return;
|
319 | }
|
320 | const warningPrinter = new polymer_analyzer_1.WarningPrinter(this.streamToWarnTo);
|
321 | warningPrinter.printWarnings(this.warnings);
|
322 | }
|
323 | countWarningsByType() {
|
324 | const errorCountMap = new Map();
|
325 | errorCountMap.set(polymer_analyzer_1.Severity.INFO, 0);
|
326 | errorCountMap.set(polymer_analyzer_1.Severity.WARNING, 0);
|
327 | errorCountMap.set(polymer_analyzer_1.Severity.ERROR, 0);
|
328 | for (const warning of this.warnings) {
|
329 | errorCountMap.set(warning.severity, errorCountMap.get(warning.severity) + 1);
|
330 | }
|
331 | return errorCountMap;
|
332 | }
|
333 | |
334 |
|
335 |
|
336 | _getDependencies(url) {
|
337 | return __awaiter(this, void 0, void 0, function* () {
|
338 | const analysis = yield this.analyzer.analyze([url]);
|
339 | const result = analysis.getDocument(url);
|
340 | if (result.successful === false) {
|
341 | const message = result.error && result.error.message || 'unknown';
|
342 | throw new Error(`Unable to get document ${url}: ${message}`);
|
343 | }
|
344 | const doc = result.value;
|
345 | doc.getWarnings({ imported: true })
|
346 | .filter((w) => !this._warningsFilter.shouldIgnore(w))
|
347 | .forEach((w) => this.warnings.add(w));
|
348 | const scripts = new Set();
|
349 | const styles = new Set();
|
350 | const imports = new Set();
|
351 | const importFeatures = doc.getFeatures({ kind: 'import', externalPackages: true, imported: true });
|
352 | for (const importFeature of importFeatures) {
|
353 | const importUrl = importFeature.url;
|
354 | if (!this.analyzer.canLoad(importUrl)) {
|
355 | logger.debug(`ignoring external dependency: ${importUrl}`);
|
356 | }
|
357 | else if (importFeature.type === 'html-script') {
|
358 | scripts.add(this.analyzer.urlResolver.relative(importUrl));
|
359 | }
|
360 | else if (importFeature.type === 'html-style') {
|
361 | styles.add(this.analyzer.urlResolver.relative(importUrl));
|
362 | }
|
363 | else if (importFeature.type === 'html-import') {
|
364 | imports.add(this.analyzer.urlResolver.relative(importUrl));
|
365 | }
|
366 | else {
|
367 | logger.debug(`unexpected import type encountered: ${importFeature.type}`);
|
368 | }
|
369 | }
|
370 | const deps = {
|
371 | scripts: [...scripts],
|
372 | styles: [...styles],
|
373 | imports: [...imports],
|
374 | };
|
375 | logger.debug(`dependencies analyzed for: ${url}`, deps);
|
376 | return deps;
|
377 | });
|
378 | }
|
379 | _addDependencies(filePath, deps) {
|
380 |
|
381 | if (!this.allFragmentsToAnalyze.has(filePath)) {
|
382 | throw new Error(`Dependency analysis incorrectly called for ${filePath}`);
|
383 | }
|
384 | const relativeUrl = path_transformers_1.urlFromPath(this.config.root, filePath);
|
385 |
|
386 |
|
387 | this._dependencyAnalysis.fragmentToFullDeps.set(relativeUrl, deps);
|
388 | this._dependencyAnalysis.fragmentToDeps.set(relativeUrl, deps.imports);
|
389 | deps.imports.forEach((url) => {
|
390 | const entrypointList = this._dependencyAnalysis.depsToFragments.get(url);
|
391 | if (entrypointList) {
|
392 | entrypointList.push(relativeUrl);
|
393 | }
|
394 | else {
|
395 | this._dependencyAnalysis.depsToFragments.set(url, [relativeUrl]);
|
396 | }
|
397 | });
|
398 | }
|
399 | |
400 |
|
401 |
|
402 |
|
403 |
|
404 | sourcePathAnalyzed(filePath) {
|
405 |
|
406 |
|
407 |
|
408 | if (this.sourceFilesLoaded) {
|
409 | throw new Error(`Not found: "${filePath}"`);
|
410 | }
|
411 |
|
412 |
|
413 |
|
414 | logger.debug('dependency is a source file, ignoring...', { dep: filePath });
|
415 | }
|
416 | |
417 |
|
418 |
|
419 |
|
420 | dependencyPathAnalyzed(filePath) {
|
421 | if (this.getFile(filePath)) {
|
422 | logger.debug('dependency has already been pushed, ignoring...', { dep: filePath });
|
423 | return;
|
424 | }
|
425 | logger.debug('new dependency analyzed, pushing into dependency stream...', filePath);
|
426 | this._dependenciesStream.push(filePath);
|
427 | }
|
428 | }
|
429 | exports.BuildAnalyzer = BuildAnalyzer;
|
430 | class StreamLoader {
|
431 | constructor(buildAnalyzer) {
|
432 |
|
433 |
|
434 |
|
435 | this.deferredFiles = new Map();
|
436 | this._buildAnalyzer = buildAnalyzer;
|
437 | this.config = this._buildAnalyzer.config;
|
438 | }
|
439 | hasDeferredFile(filePath) {
|
440 | return this.deferredFiles.has(filePath);
|
441 | }
|
442 | hasDeferredFiles() {
|
443 | return this.deferredFiles.size > 0;
|
444 | }
|
445 | resolveDeferredFile(filePath, file) {
|
446 | const deferredCallbacks = this.deferredFiles.get(filePath);
|
447 | if (deferredCallbacks == null) {
|
448 | throw new Error(`Internal error: could not get deferredCallbacks for ${filePath}`);
|
449 | }
|
450 | deferredCallbacks.resolve(file.contents.toString());
|
451 | this.deferredFiles.delete(filePath);
|
452 | }
|
453 | rejectDeferredFile(filePath, err) {
|
454 | const deferredCallbacks = this.deferredFiles.get(filePath);
|
455 | if (deferredCallbacks == null) {
|
456 | throw new Error(`Internal error: could not get deferredCallbacks for ${filePath}`);
|
457 | }
|
458 | deferredCallbacks.reject(err);
|
459 | this.deferredFiles.delete(filePath);
|
460 | }
|
461 |
|
462 | canLoad(url) {
|
463 | return url.startsWith('file:///');
|
464 | }
|
465 | load(url) {
|
466 | return __awaiter(this, void 0, void 0, function* () {
|
467 | logger.debug(`loading: ${url}`);
|
468 | if (!this.canLoad(url)) {
|
469 | throw new Error('Unable to load ${url}.');
|
470 | }
|
471 | const urlPath = this._buildAnalyzer.analyzer.urlResolver.relative(url);
|
472 | const filePath = path_transformers_1.pathFromUrl(this.config.root, urlPath);
|
473 | const file = this._buildAnalyzer.getFile(filePath);
|
474 | if (file) {
|
475 | return file.contents.toString();
|
476 | }
|
477 | return new Promise((resolve, reject) => {
|
478 | this.deferredFiles.set(filePath, { resolve, reject });
|
479 | try {
|
480 | if (this.config.isSource(filePath)) {
|
481 | this._buildAnalyzer.sourcePathAnalyzed(filePath);
|
482 | }
|
483 | else {
|
484 | this._buildAnalyzer.dependencyPathAnalyzed(filePath);
|
485 | }
|
486 | }
|
487 | catch (err) {
|
488 | this.rejectDeferredFile(filePath, err);
|
489 | }
|
490 | });
|
491 | });
|
492 | }
|
493 | }
|
494 | exports.StreamLoader = StreamLoader;
|
495 |
|
\ | No newline at end of file |