UNPKG

7.37 kBJavaScriptView Raw
1"use strict";
2/**
3 * @license
4 * Copyright (c) 2016 The Polymer Project Authors. All rights reserved.
5 * This code may only be used under the BSD style license found at
6 * http://polymer.github.io/LICENSE.txt
7 * The complete set of authors may be found at
8 * http://polymer.github.io/AUTHORS.txt
9 * The complete set of contributors may be found at
10 * http://polymer.github.io/CONTRIBUTORS.txt
11 * Code distributed by Google as part of the polymer project is also
12 * subject to an additional IP rights grant found at
13 * http://polymer.github.io/PATENTS.txt
14 */
15Object.defineProperty(exports, "__esModule", { value: true });
16const utils_1 = require("../core/utils");
17const document_1 = require("./document");
18const source_range_1 = require("./source-range");
19// A regexp that matches paths to external code.
20// TODO(rictic): Make this part of the URL Resolver.
21// https://github.com/Polymer/polymer-analyzer/issues/803
22// Note that we will match any directory name prefixed by `bower_components` or
23// `node_modules` in order to ignore `polymer install`'s variants, which look
24// like bower_components-foo
25const MATCHES_EXTERNAL = /(^|\/)(bower_components|node_modules($|\/))/;
26/**
27 * Represents a queryable interface over all documents in a package/project.
28 *
29 * Results of queries will include results from all documents in the package, as
30 * well as from external dependencies that are transitively imported by
31 * documents in the package.
32 */
33class Analysis {
34 constructor(results, context) {
35 this.context = context;
36 workAroundDuplicateJsScriptsBecauseOfHtmlScriptTags(results);
37 this._results = results;
38 const documents = Array.from(results.values()).filter((r) => r instanceof document_1.Document);
39 const potentialRoots = new Set(documents);
40 // We trim down the set of documents as a performance optimization. We only
41 // need a set of documents such that all other documents we're interested in
42 // can be reached from them. That way we'll do less duplicate work when we
43 // query over all documents.
44 for (const doc of potentialRoots) {
45 for (const imprt of doc.getFeatures({ kind: 'import', imported: true })) {
46 // When there's cycles we can keep any element of the cycle, so why not
47 // this one.
48 if (imprt.document !== undefined && imprt.document !== doc) {
49 potentialRoots.delete(imprt.document);
50 }
51 }
52 }
53 this._searchRoots = potentialRoots;
54 }
55 static isExternal(path) {
56 return MATCHES_EXTERNAL.test(path);
57 }
58 getDocument(packageRelativeUrl) {
59 const url = this.context.resolver.resolve(packageRelativeUrl);
60 if (url === undefined) {
61 return { successful: false, error: undefined };
62 }
63 const result = this._results.get(url);
64 if (result != null) {
65 if (result instanceof document_1.Document) {
66 return { successful: true, value: result };
67 }
68 else {
69 return { successful: false, error: result };
70 }
71 }
72 const documents = Array
73 .from(this.getFeatures({ kind: 'document', id: url, externalPackages: true }))
74 .filter((d) => !d.isInline);
75 if (documents.length !== 1) {
76 return { successful: false, error: undefined };
77 }
78 return { successful: true, value: documents[0] };
79 }
80 getFeatures(query = {}) {
81 const result = new Set();
82 const docQuery = this._getDocumentQuery(query);
83 for (const doc of this._searchRoots) {
84 utils_1.addAll(result, doc.getFeatures(docQuery));
85 }
86 return result;
87 }
88 /**
89 * Get all warnings in the project.
90 */
91 getWarnings(options) {
92 const warnings = Array.from(this._results.values())
93 .filter((r) => !(r instanceof document_1.Document));
94 const result = new Set(warnings);
95 const docQuery = this._getDocumentQuery(options);
96 for (const doc of this._searchRoots) {
97 utils_1.addAll(result, new Set(doc.getWarnings(docQuery)));
98 }
99 return Array.from(result);
100 }
101 /**
102 * Potentially narrow down the document that contains the sourceRange.
103 * For example, if a source range is inside a inlineDocument, this function
104 * will narrow down the document to the most specific inline document.
105 *
106 * @param sourceRange Source range to search for in a document
107 */
108 getDocumentContaining(sourceRange) {
109 if (!sourceRange) {
110 return undefined;
111 }
112 let mostSpecificDocument = undefined;
113 const [outerDocument] = this.getFeatures({ kind: 'document', id: sourceRange.file });
114 if (!outerDocument) {
115 return undefined;
116 }
117 for (const doc of outerDocument.getFeatures({ kind: 'document' })) {
118 if (source_range_1.isPositionInsideRange(sourceRange.start, doc.sourceRange)) {
119 if (!mostSpecificDocument ||
120 source_range_1.isPositionInsideRange(doc.sourceRange.start, mostSpecificDocument.sourceRange)) {
121 mostSpecificDocument = doc;
122 }
123 }
124 }
125 return mostSpecificDocument;
126 }
127 _getDocumentQuery(query = {}) {
128 return {
129 kind: query.kind,
130 id: query.id,
131 externalPackages: query.externalPackages,
132 imported: true,
133 excludeBackreferences: query.excludeBackreferences,
134 noLazyImports: query.noLazyImports,
135 };
136 }
137}
138exports.Analysis = Analysis;
139/**
140 * So, we have this really terrible hack, whereby we generate a new Document for
141 * a js file when it is referenced in an external script tag in an HTML
142 * document. We do this so that we can inject an artificial import of the HTML
143 * document into the js document, so that the HTML document's dependencies are
144 * also dependencies of the js document.
145 *
146 * This works, but we want to eliminate these duplicate JS Documents from the
147 * Analysis before the user sees them.
148 *
149 * https://github.com/Polymer/polymer-analyzer/issues/615 tracks a better
150 * solution for this issue
151 */
152function workAroundDuplicateJsScriptsBecauseOfHtmlScriptTags(results) {
153 const documents = Array.from(results.values()).filter((r) => r instanceof document_1.Document);
154 // TODO(rictic): handle JS imported via script src from HTML better than
155 // this.
156 const potentialDuplicates = new Set(documents.filter((r) => r.kinds.has('js-document')));
157 const canonicalOverrides = new Set();
158 for (const doc of documents) {
159 if (potentialDuplicates.has(doc)) {
160 continue;
161 }
162 for (const potentialDupe of potentialDuplicates) {
163 const potentialCanonicalDocs = doc.getFeatures({ kind: 'js-document', id: potentialDupe.url, imported: true });
164 for (const potentialCanonicalDoc of potentialCanonicalDocs) {
165 if (!potentialCanonicalDoc.isInline) {
166 canonicalOverrides.add(potentialCanonicalDoc);
167 }
168 }
169 }
170 }
171 for (const canonicalDoc of canonicalOverrides) {
172 results.set(canonicalDoc.url, canonicalDoc);
173 }
174}
175//# sourceMappingURL=analysis.js.map
\No newline at end of file