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 | Object.defineProperty(exports, "__esModule", { value: true });
|
24 | const cancel_token_1 = require("cancel-token");
|
25 | const path = require("path");
|
26 | const analyzer_1 = require("../core/analyzer");
|
27 | const css_custom_property_scanner_1 = require("../css/css-custom-property-scanner");
|
28 | const css_parser_1 = require("../css/css-parser");
|
29 | const html_element_reference_scanner_1 = require("../html/html-element-reference-scanner");
|
30 | const html_import_scanner_1 = require("../html/html-import-scanner");
|
31 | const html_parser_1 = require("../html/html-parser");
|
32 | const html_script_scanner_1 = require("../html/html-script-scanner");
|
33 | const html_style_scanner_1 = require("../html/html-style-scanner");
|
34 | const class_scanner_1 = require("../javascript/class-scanner");
|
35 | const function_scanner_1 = require("../javascript/function-scanner");
|
36 | const html_template_literal_scanner_1 = require("../javascript/html-template-literal-scanner");
|
37 | const javascript_export_scanner_1 = require("../javascript/javascript-export-scanner");
|
38 | const javascript_import_scanner_1 = require("../javascript/javascript-import-scanner");
|
39 | const javascript_parser_1 = require("../javascript/javascript-parser");
|
40 | const namespace_scanner_1 = require("../javascript/namespace-scanner");
|
41 | const json_parser_1 = require("../json/json-parser");
|
42 | const model_1 = require("../model/model");
|
43 | const document_1 = require("../parser/document");
|
44 | const behavior_scanner_1 = require("../polymer/behavior-scanner");
|
45 | const css_import_scanner_1 = require("../polymer/css-import-scanner");
|
46 | const dom_module_scanner_1 = require("../polymer/dom-module-scanner");
|
47 | const polymer_core_feature_scanner_1 = require("../polymer/polymer-core-feature-scanner");
|
48 | const polymer_element_scanner_1 = require("../polymer/polymer-element-scanner");
|
49 | const pseudo_element_scanner_1 = require("../polymer/pseudo-element-scanner");
|
50 | const scan_1 = require("../scanning/scan");
|
51 | const package_url_resolver_1 = require("../url-loader/package-url-resolver");
|
52 | const analysis_cache_1 = require("./analysis-cache");
|
53 | exports.analyzerVersion = require('../../package.json').version;
|
54 |
|
55 |
|
56 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 | class AnalysisContext {
|
68 | constructor(options, cache, generation) {
|
69 | this.parsers = new Map([
|
70 | ['html', new html_parser_1.HtmlParser()],
|
71 | ['js', new javascript_parser_1.JavaScriptParser()],
|
72 | ['mjs', new javascript_parser_1.JavaScriptParser()],
|
73 | ['css', new css_parser_1.CssParser()],
|
74 | ['json', new json_parser_1.JsonParser()],
|
75 | ]);
|
76 | this._languageAnalyzers = new Map([
|
77 |
|
78 |
|
79 | ]);
|
80 | this.loader = options.urlLoader;
|
81 | this.resolver = options.urlResolver || new package_url_resolver_1.PackageUrlResolver();
|
82 | this.parsers = options.parsers || this.parsers;
|
83 | this._lazyEdges = options.lazyEdges;
|
84 | this._scanners =
|
85 | options.scanners || AnalysisContext.getDefaultScanners(options);
|
86 | this._cache = cache || new analysis_cache_1.AnalysisCache();
|
87 | this._generation = generation || 0;
|
88 | this._analysisComplete = Promise.resolve();
|
89 | }
|
90 | static getDefaultScanners(options) {
|
91 | return new Map([
|
92 | [
|
93 | 'html',
|
94 | [
|
95 | new html_import_scanner_1.HtmlImportScanner(options.lazyEdges),
|
96 | new html_script_scanner_1.HtmlScriptScanner(),
|
97 | new html_style_scanner_1.HtmlStyleScanner(),
|
98 | new dom_module_scanner_1.DomModuleScanner(),
|
99 | new css_import_scanner_1.CssImportScanner(),
|
100 | new html_element_reference_scanner_1.HtmlCustomElementReferenceScanner(),
|
101 | new pseudo_element_scanner_1.PseudoElementScanner(),
|
102 | ]
|
103 | ],
|
104 | [
|
105 | 'js',
|
106 | [
|
107 | new polymer_element_scanner_1.PolymerElementScanner(),
|
108 | new polymer_core_feature_scanner_1.PolymerCoreFeatureScanner(),
|
109 | new behavior_scanner_1.BehaviorScanner(),
|
110 | new namespace_scanner_1.NamespaceScanner(),
|
111 | new function_scanner_1.FunctionScanner(),
|
112 | new class_scanner_1.ClassScanner(),
|
113 | new javascript_import_scanner_1.JavaScriptImportScanner({ moduleResolution: options.moduleResolution }),
|
114 | new javascript_export_scanner_1.JavaScriptExportScanner(),
|
115 | new html_template_literal_scanner_1.InlineHtmlDocumentScanner(),
|
116 | ]
|
117 | ],
|
118 | ['css', [new css_custom_property_scanner_1.CssCustomPropertyScanner()]]
|
119 | ]);
|
120 | }
|
121 | |
122 |
|
123 |
|
124 | filesChanged(urls) {
|
125 | const newCache = this._cache.invalidate(this.resolveUserInputUrls(urls));
|
126 | return this._fork(newCache);
|
127 | }
|
128 | |
129 |
|
130 |
|
131 | analyze(urls, cancelToken) {
|
132 | return __awaiter(this, void 0, void 0, function* () {
|
133 | const resolvedUrls = this.resolveUserInputUrls(urls);
|
134 |
|
135 |
|
136 | yield this._analysisComplete;
|
137 |
|
138 | const hasAllDocuments = resolvedUrls.every((url) => this._cache.analyzedDocuments.get(url) != null);
|
139 | if (hasAllDocuments) {
|
140 |
|
141 | return this;
|
142 | }
|
143 |
|
144 | const newCache = this._cache.invalidate([]);
|
145 | const newContext = this._fork(newCache);
|
146 | return newContext._analyze(resolvedUrls, cancelToken);
|
147 | });
|
148 | }
|
149 | |
150 |
|
151 |
|
152 | _analyze(resolvedUrls, cancelToken) {
|
153 | return __awaiter(this, void 0, void 0, function* () {
|
154 | const analysisComplete = (() => __awaiter(this, void 0, void 0, function* () {
|
155 |
|
156 | const maybeScannedDocuments = yield Promise.all(resolvedUrls.map((url) => __awaiter(this, void 0, void 0, function* () {
|
157 | try {
|
158 | const scannedResult = yield this.scan(url, cancelToken);
|
159 | if (scannedResult.successful === true) {
|
160 | this._cache.failedDocuments.delete(url);
|
161 | return scannedResult.value;
|
162 | }
|
163 | else {
|
164 | this._cache.failedDocuments.set(url, scannedResult.error);
|
165 | return undefined;
|
166 | }
|
167 | }
|
168 | catch (e) {
|
169 | if (cancel_token_1.isCancel(e)) {
|
170 | return;
|
171 | }
|
172 |
|
173 | throw e;
|
174 | }
|
175 | })));
|
176 | const scannedDocuments = maybeScannedDocuments.filter((d) => d !== undefined);
|
177 |
|
178 | const documents = scannedDocuments.map((d) => this.getDocument(d.url));
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 | return documents;
|
185 | }))();
|
186 | this._analysisComplete = analysisComplete.then((_) => { });
|
187 | yield this._analysisComplete;
|
188 | return this;
|
189 | });
|
190 | }
|
191 | |
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 |
|
198 |
|
199 | getDocument(resolvedUrl) {
|
200 | const cachedWarning = this._cache.failedDocuments.get(resolvedUrl);
|
201 | if (cachedWarning) {
|
202 | return cachedWarning;
|
203 | }
|
204 | const cachedResult = this._cache.analyzedDocuments.get(resolvedUrl);
|
205 | if (cachedResult) {
|
206 | return cachedResult;
|
207 | }
|
208 | const scannedDocument = this._cache.scannedDocuments.get(resolvedUrl);
|
209 | if (!scannedDocument) {
|
210 | return makeRequestedWithoutLoadingWarning(resolvedUrl);
|
211 | }
|
212 | const extension = path.extname(resolvedUrl).substring(1);
|
213 | const languageAnalyzer = this._languageAnalyzers.get(extension);
|
214 | let analysisResult;
|
215 | if (languageAnalyzer) {
|
216 | analysisResult = languageAnalyzer.analyze(scannedDocument.url);
|
217 | }
|
218 | const document = new model_1.Document(scannedDocument, this, analysisResult);
|
219 | this._cache.analyzedDocuments.set(resolvedUrl, document);
|
220 | this._cache.analyzedDocumentPromises.getOrCompute(resolvedUrl, () => __awaiter(this, void 0, void 0, function* () { return document; }));
|
221 | document.resolve();
|
222 | return document;
|
223 | }
|
224 | |
225 |
|
226 |
|
227 |
|
228 |
|
229 | _getScannedDocument(resolvedUrl) {
|
230 | return this._cache.scannedDocuments.get(resolvedUrl);
|
231 | }
|
232 | |
233 |
|
234 |
|
235 |
|
236 |
|
237 |
|
238 |
|
239 | clearCaches() {
|
240 | return this._fork(new analysis_cache_1.AnalysisCache());
|
241 | }
|
242 | |
243 |
|
244 |
|
245 |
|
246 |
|
247 |
|
248 | _fork(cache, options) {
|
249 | const contextOptions = {
|
250 | lazyEdges: this._lazyEdges,
|
251 | parsers: this.parsers,
|
252 | scanners: this._scanners,
|
253 | urlLoader: this.loader,
|
254 | urlResolver: this.resolver,
|
255 | };
|
256 | if (options && options.urlLoader) {
|
257 | contextOptions.urlLoader = options.urlLoader;
|
258 | }
|
259 | if (!cache) {
|
260 | cache = this._cache.invalidate([]);
|
261 | }
|
262 | const copy = new AnalysisContext(contextOptions, cache, this._generation + 1);
|
263 | return copy;
|
264 | }
|
265 | |
266 |
|
267 |
|
268 |
|
269 |
|
270 |
|
271 |
|
272 |
|
273 |
|
274 |
|
275 | _scanLocal(resolvedUrl, cancelToken) {
|
276 | return __awaiter(this, void 0, void 0, function* () {
|
277 | return this._cache.scannedDocumentPromises.getOrCompute(resolvedUrl, () => __awaiter(this, void 0, void 0, function* () {
|
278 | const parsedDocResult = yield this._parse(resolvedUrl, cancelToken);
|
279 | if (parsedDocResult.successful === false) {
|
280 | this._cache.dependencyGraph.rejectDocument(resolvedUrl, new model_1.WarningCarryingException(parsedDocResult.error));
|
281 | return parsedDocResult;
|
282 | }
|
283 | const parsedDoc = parsedDocResult.value;
|
284 | try {
|
285 | const scannedDocument = yield this._scanDocument(parsedDoc);
|
286 | const imports = scannedDocument.getNestedFeatures().filter((e) => e instanceof model_1.ScannedImport);
|
287 |
|
288 | const importUrls = filterOutUndefineds(imports.map((i) => i.url === undefined ?
|
289 | undefined :
|
290 | this.resolver.resolve(parsedDoc.baseUrl, i.url, i)));
|
291 | this._cache.dependencyGraph.addDocument(resolvedUrl, importUrls);
|
292 | return { successful: true, value: scannedDocument };
|
293 | }
|
294 | catch (e) {
|
295 | const message = (e && e.message) || `Unknown error during scan.`;
|
296 | const warning = new model_1.Warning({
|
297 | code: 'could-not-scan',
|
298 | message,
|
299 | parsedDocument: parsedDoc,
|
300 | severity: model_1.Severity.ERROR,
|
301 | sourceRange: {
|
302 | file: resolvedUrl,
|
303 | start: { column: 0, line: 0 },
|
304 | end: { column: 0, line: 0 }
|
305 | }
|
306 | });
|
307 | this._cache.dependencyGraph.rejectDocument(resolvedUrl, new model_1.WarningCarryingException(warning));
|
308 | return { successful: false, error: warning };
|
309 | }
|
310 | }), cancelToken);
|
311 | });
|
312 | }
|
313 | |
314 |
|
315 |
|
316 | scan(resolvedUrl, cancelToken) {
|
317 | return __awaiter(this, void 0, void 0, function* () {
|
318 | return this._cache.dependenciesScannedPromises.getOrCompute(resolvedUrl, () => __awaiter(this, void 0, void 0, function* () {
|
319 | const scannedDocumentResult = yield this._scanLocal(resolvedUrl, cancelToken);
|
320 | if (scannedDocumentResult.successful === false) {
|
321 | return scannedDocumentResult;
|
322 | }
|
323 | const scannedDocument = scannedDocumentResult.value;
|
324 | const imports = scannedDocument.getNestedFeatures().filter((e) => e instanceof model_1.ScannedImport);
|
325 |
|
326 | for (const scannedImport of imports) {
|
327 | if (scannedImport.url === undefined) {
|
328 | continue;
|
329 | }
|
330 | const importUrl = this.resolver.resolve(scannedDocument.document.baseUrl, scannedImport.url, scannedImport);
|
331 | if (importUrl === undefined) {
|
332 | continue;
|
333 | }
|
334 |
|
335 |
|
336 |
|
337 | this.scan(importUrl, cancelToken)
|
338 | .then((result) => {
|
339 | if (result.successful === true) {
|
340 | return;
|
341 | }
|
342 | scannedImport.error = result.error;
|
343 | })
|
344 | .catch((e) => {
|
345 | if (cancel_token_1.isCancel(e)) {
|
346 | return;
|
347 | }
|
348 | throw e;
|
349 | });
|
350 | }
|
351 | yield this._cache.dependencyGraph.whenReady(resolvedUrl);
|
352 | return scannedDocumentResult;
|
353 | }), cancelToken);
|
354 | });
|
355 | }
|
356 | |
357 |
|
358 |
|
359 | _scanDocument(document, maybeAttachedComment, maybeContainingDocument) {
|
360 | return __awaiter(this, void 0, void 0, function* () {
|
361 | const { features: scannedFeatures, warnings } = yield this._getScannedFeatures(document);
|
362 |
|
363 |
|
364 | const firstScannedFeature = scannedFeatures[0];
|
365 | if (firstScannedFeature && firstScannedFeature instanceof model_1.ScannedElement) {
|
366 | firstScannedFeature.applyHtmlComment(maybeAttachedComment, maybeContainingDocument);
|
367 | }
|
368 | const scannedDocument = new model_1.ScannedDocument(document, scannedFeatures, warnings);
|
369 | if (!scannedDocument.isInline) {
|
370 | if (this._cache.scannedDocuments.has(scannedDocument.url)) {
|
371 | throw new Error('Scanned document already in cache. This should never happen.');
|
372 | }
|
373 | this._cache.scannedDocuments.set(scannedDocument.url, scannedDocument);
|
374 | }
|
375 | yield this._scanInlineDocuments(scannedDocument);
|
376 | return scannedDocument;
|
377 | });
|
378 | }
|
379 | _getScannedFeatures(document) {
|
380 | return __awaiter(this, void 0, void 0, function* () {
|
381 | const scanners = this._scanners.get(document.type);
|
382 | if (scanners) {
|
383 | try {
|
384 | return yield scan_1.scan(document, scanners);
|
385 | }
|
386 | catch (e) {
|
387 | if (e instanceof model_1.WarningCarryingException) {
|
388 | throw e;
|
389 | }
|
390 | const message = e == null ? `Unknown error while scanning.` :
|
391 | `Error while scanning: ${String(e)}`;
|
392 | throw new model_1.WarningCarryingException(new model_1.Warning({
|
393 | code: 'internal-scanning-error',
|
394 | message,
|
395 | parsedDocument: document,
|
396 | severity: model_1.Severity.ERROR,
|
397 | sourceRange: {
|
398 | file: document.url,
|
399 | start: { column: 0, line: 0 },
|
400 | end: { column: 0, line: 0 },
|
401 | }
|
402 | }));
|
403 | }
|
404 | }
|
405 | return { features: [], warnings: [] };
|
406 | });
|
407 | }
|
408 | _scanInlineDocuments(containingDocument) {
|
409 | return __awaiter(this, void 0, void 0, function* () {
|
410 | for (const feature of containingDocument.features) {
|
411 | if (!(feature instanceof model_1.ScannedInlineDocument)) {
|
412 | continue;
|
413 | }
|
414 | const locationOffset = {
|
415 | line: feature.locationOffset.line,
|
416 | col: feature.locationOffset.col,
|
417 | filename: containingDocument.url
|
418 | };
|
419 | try {
|
420 | const parsedDoc = this._parseContents(feature.type, feature.contents, containingDocument.url, {
|
421 | locationOffset,
|
422 | astNode: feature.astNode,
|
423 | baseUrl: containingDocument.document.baseUrl
|
424 | });
|
425 | const scannedDoc = yield this._scanDocument(parsedDoc, feature.attachedComment, containingDocument.document);
|
426 | feature.scannedDocument = scannedDoc;
|
427 | }
|
428 | catch (err) {
|
429 | if (err instanceof model_1.WarningCarryingException) {
|
430 | containingDocument.warnings.push(err.warning);
|
431 | continue;
|
432 | }
|
433 | throw err;
|
434 | }
|
435 | }
|
436 | });
|
437 | }
|
438 | |
439 |
|
440 |
|
441 |
|
442 |
|
443 | canLoad(resolvedUrl) {
|
444 | return this.loader.canLoad(resolvedUrl);
|
445 | }
|
446 | |
447 |
|
448 |
|
449 |
|
450 |
|
451 |
|
452 |
|
453 |
|
454 |
|
455 | load(resolvedUrl) {
|
456 | return __awaiter(this, void 0, void 0, function* () {
|
457 | if (!this.canLoad(resolvedUrl)) {
|
458 | return {
|
459 | successful: false,
|
460 | error: `Configured URL Loader can not load URL ${resolvedUrl}`
|
461 | };
|
462 | }
|
463 | try {
|
464 | const value = yield this.loader.load(resolvedUrl);
|
465 | return { successful: true, value };
|
466 | }
|
467 | catch (e) {
|
468 | const message = (e && e.message) || `Unknown failure while loading.`;
|
469 | return { successful: false, error: message };
|
470 | }
|
471 | });
|
472 | }
|
473 | |
474 |
|
475 |
|
476 | _parse(resolvedUrl, cancelToken) {
|
477 | return __awaiter(this, void 0, void 0, function* () {
|
478 | return this._cache.parsedDocumentPromises.getOrCompute(resolvedUrl, () => __awaiter(this, void 0, void 0, function* () {
|
479 | const result = yield this.load(resolvedUrl);
|
480 | if (!result.successful) {
|
481 | return {
|
482 | successful: false,
|
483 | error: new model_1.Warning({
|
484 | code: 'could-not-load',
|
485 | parsedDocument: new document_1.UnparsableParsedDocument(resolvedUrl, ''),
|
486 | severity: model_1.Severity.ERROR,
|
487 | sourceRange: {
|
488 | file: resolvedUrl,
|
489 | start: { column: 0, line: 0 },
|
490 | end: { column: 0, line: 0 }
|
491 | },
|
492 | message: result.error
|
493 | })
|
494 | };
|
495 | }
|
496 | const extension = path.extname(resolvedUrl).substring(1);
|
497 | try {
|
498 | const parsedDoc = this._parseContents(extension, result.value, resolvedUrl);
|
499 | return { successful: true, value: parsedDoc };
|
500 | }
|
501 | catch (e) {
|
502 | if (e instanceof model_1.WarningCarryingException) {
|
503 | return { successful: false, error: e.warning };
|
504 | }
|
505 | const message = (e && e.message) || `Unknown error while parsing.`;
|
506 | return {
|
507 | successful: false,
|
508 | error: new model_1.Warning({
|
509 | code: 'could-not-parse',
|
510 | parsedDocument: new document_1.UnparsableParsedDocument(resolvedUrl, result.value),
|
511 | severity: model_1.Severity.ERROR,
|
512 | sourceRange: {
|
513 | file: resolvedUrl,
|
514 | start: { column: 0, line: 0 },
|
515 | end: { column: 0, line: 0 }
|
516 | },
|
517 | message
|
518 | })
|
519 | };
|
520 | }
|
521 | }), cancelToken);
|
522 | });
|
523 | }
|
524 | |
525 |
|
526 |
|
527 |
|
528 | _parseContents(type, contents, url, inlineInfo) {
|
529 | const parser = this.parsers.get(type);
|
530 | if (parser == null) {
|
531 | throw new analyzer_1.NoKnownParserError(`No parser for for file type ${type}`);
|
532 | }
|
533 | try {
|
534 | return parser.parse(contents, url, this.resolver, inlineInfo);
|
535 | }
|
536 | catch (error) {
|
537 | if (error instanceof model_1.WarningCarryingException) {
|
538 | throw error;
|
539 | }
|
540 | const parsedDocument = new document_1.UnparsableParsedDocument(url, contents);
|
541 | const message = error == null ? `Unable to parse as ${type}` :
|
542 | `Unable to parse as ${type}: ${error}`;
|
543 | throw new model_1.WarningCarryingException(new model_1.Warning({
|
544 | parsedDocument,
|
545 | code: 'parse-error',
|
546 | message,
|
547 | severity: model_1.Severity.ERROR,
|
548 | sourceRange: { file: url, start: { line: 0, column: 0 }, end: { line: 0, column: 0 } }
|
549 | }));
|
550 | }
|
551 | }
|
552 | |
553 |
|
554 |
|
555 | resolveUserInputUrls(urls) {
|
556 | return filterOutUndefineds(urls.map((u) => this.resolver.resolve(u)));
|
557 | }
|
558 | }
|
559 | exports.AnalysisContext = AnalysisContext;
|
560 | function filterOutUndefineds(arr) {
|
561 | return arr.filter((t) => t !== undefined);
|
562 | }
|
563 |
|
564 |
|
565 |
|
566 |
|
567 |
|
568 |
|
569 |
|
570 |
|
571 |
|
572 |
|
573 |
|
574 | function makeRequestedWithoutLoadingWarning(resolvedUrl) {
|
575 | const parsedDocument = new document_1.UnparsableParsedDocument(resolvedUrl, '');
|
576 | return new model_1.Warning({
|
577 | sourceRange: {
|
578 | file: resolvedUrl,
|
579 | start: { line: 0, column: 0 },
|
580 | end: { line: 0, column: 0 }
|
581 | },
|
582 | code: 'unable-to-analyze',
|
583 | message: `[Internal Error] Document was requested ` +
|
584 | `before loading and scanning finished. This usually indicates an ` +
|
585 | `anomalous error during loading or analysis. Please file a bug at ` +
|
586 | `https://github.com/Polymer/polymer-analyzer/issues/new with info ` +
|
587 | `on the source code that caused this. ` +
|
588 | `Polymer Analyzer version: ${exports.analyzerVersion}`,
|
589 | severity: model_1.Severity.ERROR,
|
590 | parsedDocument
|
591 | });
|
592 | }
|
593 |
|
\ | No newline at end of file |