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 assert = require("assert");
|
25 | const fs = require("fs");
|
26 | const jsonschema = require("jsonschema");
|
27 | const path = require("path");
|
28 | const logging = require("plylog");
|
29 | const builds_1 = require("./builds");
|
30 | const minimatchAll = require("minimatch-all");
|
31 | const polymer_analyzer_1 = require("polymer-analyzer");
|
32 | var builds_2 = require("./builds");
|
33 | exports.applyBuildPreset = builds_2.applyBuildPreset;
|
34 | const logger = logging.getLogger('polymer-project-config');
|
35 |
|
36 |
|
37 |
|
38 | exports.defaultSourceGlobs = ['src/**/*'];
|
39 | const moduleResolutionStrategies = new Set(['none', 'node']);
|
40 |
|
41 |
|
42 |
|
43 |
|
44 | function globResolve(fromPath, glob) {
|
45 | if (glob.startsWith('!')) {
|
46 | const includeGlob = glob.substring(1);
|
47 | return '!' + path.resolve(fromPath, includeGlob);
|
48 | }
|
49 | else {
|
50 | return path.resolve(fromPath, glob);
|
51 | }
|
52 | }
|
53 |
|
54 |
|
55 |
|
56 |
|
57 | function globRelative(fromPath, glob) {
|
58 | if (glob.startsWith('!')) {
|
59 | return '!' + path.relative(fromPath, glob.substr(1));
|
60 | }
|
61 | return path.relative(fromPath, glob);
|
62 | }
|
63 |
|
64 |
|
65 |
|
66 | function getPositiveGlob(glob) {
|
67 | if (glob.startsWith('!')) {
|
68 | return glob.substring(1);
|
69 | }
|
70 | else {
|
71 | return glob;
|
72 | }
|
73 | }
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 | function fixDeprecatedOptions(options) {
|
80 | if (typeof options.sourceGlobs !== 'undefined') {
|
81 | logger.warn('"sourceGlobs" config option has been renamed to "sources" and will no longer be supported in future versions');
|
82 | options.sources = options.sources || options.sourceGlobs;
|
83 | }
|
84 | if (typeof options.includeDependencies !== 'undefined') {
|
85 | logger.warn('"includeDependencies" config option has been renamed to "extraDependencies" and will no longer be supported in future versions');
|
86 | options.extraDependencies =
|
87 | options.extraDependencies || options.includeDependencies;
|
88 | }
|
89 |
|
90 |
|
91 |
|
92 | if (options.lint && options.lint.warningsToIgnore) {
|
93 | options.lint.ignoreWarnings = options.lint.warningsToIgnore;
|
94 | }
|
95 | else if (options.lint && options.lint.ignoreWarnings) {
|
96 | options.lint.warningsToIgnore = options.lint.ignoreWarnings;
|
97 | }
|
98 | return options;
|
99 | }
|
100 | class ProjectConfig {
|
101 | |
102 |
|
103 |
|
104 |
|
105 |
|
106 |
|
107 | constructor(options) {
|
108 | this.lint = undefined;
|
109 | options = (options) ? fixDeprecatedOptions(options) : {};
|
110 | |
111 |
|
112 |
|
113 | this.npm = options.npm;
|
114 |
|
115 | if (this.npm) {
|
116 | this.componentDir = 'node_modules/';
|
117 | }
|
118 | |
119 |
|
120 |
|
121 | this.moduleResolution = options.moduleResolution || 'node';
|
122 | |
123 |
|
124 |
|
125 | if (options.root) {
|
126 | this.root = path.resolve(options.root);
|
127 | }
|
128 | else {
|
129 | this.root = process.cwd();
|
130 | }
|
131 | |
132 |
|
133 |
|
134 | if (options.entrypoint) {
|
135 | this.entrypoint = path.resolve(this.root, options.entrypoint);
|
136 | }
|
137 | else {
|
138 | this.entrypoint = path.resolve(this.root, 'index.html');
|
139 | }
|
140 | |
141 |
|
142 |
|
143 | if (options.shell) {
|
144 | this.shell = path.resolve(this.root, options.shell);
|
145 | }
|
146 | |
147 |
|
148 |
|
149 | if (options.fragments) {
|
150 | this.fragments = options.fragments.map((e) => path.resolve(this.root, e));
|
151 | }
|
152 | else {
|
153 | this.fragments = [];
|
154 | }
|
155 | |
156 |
|
157 |
|
158 | this.extraDependencies = (options.extraDependencies ||
|
159 | []).map((glob) => globResolve(this.root, glob));
|
160 | |
161 |
|
162 |
|
163 | this.sources = (options.sources || exports.defaultSourceGlobs)
|
164 | .map((glob) => globResolve(this.root, glob));
|
165 | this.sources.push(this.entrypoint);
|
166 | if (this.shell) {
|
167 | this.sources.push(this.shell);
|
168 | }
|
169 | if (this.fragments) {
|
170 | this.sources = this.sources.concat(this.fragments);
|
171 | }
|
172 | |
173 |
|
174 |
|
175 | this.allFragments = [];
|
176 |
|
177 | if (this.shell) {
|
178 | this.allFragments.push(this.shell);
|
179 | }
|
180 | if (this.fragments) {
|
181 | this.allFragments = this.allFragments.concat(this.fragments);
|
182 | }
|
183 | if (this.allFragments.length === 0) {
|
184 | this.allFragments.push(this.entrypoint);
|
185 | }
|
186 | if (options.lint) {
|
187 | this.lint = options.lint;
|
188 | }
|
189 | |
190 |
|
191 |
|
192 | if (options.builds) {
|
193 | this.builds = options.builds;
|
194 | if (Array.isArray(this.builds)) {
|
195 | this.builds = this.builds.map(builds_1.applyBuildPreset);
|
196 | }
|
197 | }
|
198 | |
199 |
|
200 |
|
201 | if (options.autoBasePath) {
|
202 | this.autoBasePath = options.autoBasePath;
|
203 | for (const build of this.builds || []) {
|
204 | build.basePath = true;
|
205 | }
|
206 | }
|
207 | |
208 |
|
209 |
|
210 | if (options.componentDir) {
|
211 | this.componentDir = options.componentDir;
|
212 | }
|
213 | }
|
214 | |
215 |
|
216 |
|
217 |
|
218 |
|
219 |
|
220 |
|
221 |
|
222 | static loadOptionsFromFile(filepath) {
|
223 | try {
|
224 | const configContent = fs.readFileSync(filepath, 'utf-8');
|
225 | const contents = JSON.parse(configContent);
|
226 | return this.validateOptions(contents);
|
227 | }
|
228 | catch (error) {
|
229 |
|
230 | if (error && error.code === 'ENOENT') {
|
231 | logger.debug('no polymer config file found', { file: filepath });
|
232 | return null;
|
233 | }
|
234 |
|
235 | throw error;
|
236 | }
|
237 | }
|
238 | |
239 |
|
240 |
|
241 |
|
242 | static loadConfigFromFile(filepath) {
|
243 | const configParsed = ProjectConfig.loadOptionsFromFile(filepath);
|
244 | if (!configParsed) {
|
245 | return null;
|
246 | }
|
247 | return new ProjectConfig(configParsed);
|
248 | }
|
249 | |
250 |
|
251 |
|
252 |
|
253 | static validateOptions(configJsonObject) {
|
254 | const validator = new jsonschema.Validator();
|
255 | const result = validator.validate(configJsonObject, getSchema());
|
256 | if (result.errors.length > 0) {
|
257 | const error = result.errors[0];
|
258 | if (!error.property && !error.message) {
|
259 | throw error;
|
260 | }
|
261 | let propertyName = error.property;
|
262 | if (propertyName.startsWith('instance.')) {
|
263 | propertyName = propertyName.slice(9);
|
264 | }
|
265 | throw new Error(`Property '${propertyName}' ${error.message}`);
|
266 | }
|
267 | return configJsonObject;
|
268 | }
|
269 | |
270 |
|
271 |
|
272 |
|
273 |
|
274 |
|
275 |
|
276 |
|
277 |
|
278 | static validateAndCreate(configJsonObject) {
|
279 | const options = this.validateOptions(configJsonObject);
|
280 | return new this(options);
|
281 | }
|
282 | |
283 |
|
284 |
|
285 |
|
286 |
|
287 | static initializeAnalyzerFromDirectory(dirname) {
|
288 | return __awaiter(this, void 0, void 0, function* () {
|
289 | const config = this.loadConfigFromFile(path.join(dirname, 'polymer.json')) ||
|
290 | new ProjectConfig({ root: dirname });
|
291 | return config.initializeAnalyzer();
|
292 | });
|
293 | }
|
294 | |
295 |
|
296 |
|
297 |
|
298 | initializeAnalyzer() {
|
299 | return __awaiter(this, void 0, void 0, function* () {
|
300 | const urlLoader = new polymer_analyzer_1.FsUrlLoader(this.root);
|
301 | const urlResolver = new polymer_analyzer_1.PackageUrlResolver({ packageDir: this.root, componentDir: this.componentDir });
|
302 | const analyzer = new polymer_analyzer_1.Analyzer({
|
303 | urlLoader,
|
304 | urlResolver,
|
305 | moduleResolution: convertModuleResolution(this.moduleResolution)
|
306 | });
|
307 | const lintConfig = this.lint || {};
|
308 | const warningFilter = new polymer_analyzer_1.WarningFilter({
|
309 | minimumSeverity: polymer_analyzer_1.Severity.WARNING,
|
310 | warningCodesToIgnore: new Set(lintConfig.warningsToIgnore || []),
|
311 | filesToIgnore: lintConfig.filesToIgnore || []
|
312 | });
|
313 | return { urlLoader, urlResolver, analyzer, warningFilter };
|
314 | });
|
315 | }
|
316 | isFragment(filepath) {
|
317 | return this.allFragments.indexOf(filepath) !== -1;
|
318 | }
|
319 | isShell(filepath) {
|
320 | return (!!this.shell && (this.shell === filepath));
|
321 | }
|
322 | isSource(filepath) {
|
323 | return minimatchAll(filepath, this.sources);
|
324 | }
|
325 | |
326 |
|
327 |
|
328 |
|
329 | validate() {
|
330 | const validateErrorPrefix = `Polymer Config Error`;
|
331 | if (this.entrypoint) {
|
332 | assert(this.entrypoint.startsWith(this.root), `${validateErrorPrefix}: entrypoint (${this.entrypoint}) ` +
|
333 | `does not resolve within root (${this.root})`);
|
334 | }
|
335 | if (this.shell) {
|
336 | assert(this.shell.startsWith(this.root), `${validateErrorPrefix}: shell (${this.shell}) ` +
|
337 | `does not resolve within root (${this.root})`);
|
338 | }
|
339 | this.fragments.forEach((f) => {
|
340 | assert(f.startsWith(this.root), `${validateErrorPrefix}: a "fragments" path (${f}) ` +
|
341 | `does not resolve within root (${this.root})`);
|
342 | });
|
343 | this.sources.forEach((s) => {
|
344 | assert(getPositiveGlob(s).startsWith(this.root), `${validateErrorPrefix}: a "sources" path (${s}) ` +
|
345 | `does not resolve within root (${this.root})`);
|
346 | });
|
347 | this.extraDependencies.forEach((d) => {
|
348 | assert(getPositiveGlob(d).startsWith(this.root), `${validateErrorPrefix}: an "extraDependencies" path (${d}) ` +
|
349 | `does not resolve within root (${this.root})`);
|
350 | });
|
351 | assert(moduleResolutionStrategies.has(this.moduleResolution), `${validateErrorPrefix}: "moduleResolution" must be one of: ` +
|
352 | `${[...moduleResolutionStrategies].join(', ')}.`);
|
353 |
|
354 |
|
355 | if (this.builds) {
|
356 | assert(Array.isArray(this.builds), `${validateErrorPrefix}: "builds" (${this.builds}) ` +
|
357 | `expected an array of build configurations.`);
|
358 | if (this.builds.length > 1) {
|
359 | const buildNames = new Set();
|
360 | for (const build of this.builds) {
|
361 | const buildName = build.name;
|
362 | const buildPreset = build.preset;
|
363 | assert(!buildPreset || builds_1.isValidPreset(buildPreset), `${validateErrorPrefix}: "${buildPreset}" is not a valid ` +
|
364 | ` "builds" preset.`);
|
365 | assert(buildName, `${validateErrorPrefix}: all "builds" require ` +
|
366 | `a "name" property when there are multiple builds defined.`);
|
367 | assert(!buildNames.has(buildName), `${validateErrorPrefix}: "builds" duplicate build name ` +
|
368 | `"${buildName}" found. Build names must be unique.`);
|
369 | buildNames.add(buildName);
|
370 | }
|
371 | }
|
372 | }
|
373 | return true;
|
374 | }
|
375 | |
376 |
|
377 |
|
378 |
|
379 | toJSON() {
|
380 | let lintObj = undefined;
|
381 | if (this.lint) {
|
382 | lintObj = Object.assign({}, this.lint);
|
383 | delete lintObj.ignoreWarnings;
|
384 | }
|
385 | const isWindows = process.platform === 'win32';
|
386 | const normalizePath = isWindows ?
|
387 | (path) => path.replace(/\\/g, '/') :
|
388 | (path) => path;
|
389 | const obj = {
|
390 | entrypoint: globRelative(this.root, this.entrypoint),
|
391 | shell: this.shell ? globRelative(this.root, this.shell) : undefined,
|
392 | fragments: this.fragments.map((absolutePath) => {
|
393 | return normalizePath(globRelative(this.root, absolutePath));
|
394 | }),
|
395 | sources: this.sources.map((absolutePath) => {
|
396 | return normalizePath(globRelative(this.root, absolutePath));
|
397 | }),
|
398 | extraDependencies: this.extraDependencies.map((absolutePath) => {
|
399 | return normalizePath(globRelative(this.root, absolutePath));
|
400 | }),
|
401 | builds: this.builds,
|
402 | autoBasePath: this.autoBasePath,
|
403 | lint: lintObj,
|
404 | npm: this.npm,
|
405 | componentDir: this.componentDir,
|
406 | moduleResolution: this.moduleResolution,
|
407 | };
|
408 | return JSON.stringify(obj, null, 2);
|
409 | }
|
410 | }
|
411 | exports.ProjectConfig = ProjectConfig;
|
412 |
|
413 |
|
414 |
|
415 | const getSchema = (() => {
|
416 | let schema;
|
417 | return () => {
|
418 | if (schema === undefined) {
|
419 | schema = JSON.parse(fs.readFileSync(path.join(__dirname, 'schema.json'), 'utf-8'));
|
420 | }
|
421 | return schema;
|
422 | };
|
423 | })();
|
424 |
|
425 |
|
426 |
|
427 |
|
428 | function convertModuleResolution(moduleResolution) {
|
429 | switch (moduleResolution) {
|
430 | case 'node':
|
431 | return 'node';
|
432 | case 'none':
|
433 | return undefined;
|
434 | default:
|
435 | const never = moduleResolution;
|
436 | throw new Error(`Unknown module resolution parameter: ${never}`);
|
437 | }
|
438 | }
|
439 |
|
\ | No newline at end of file |