UNPKG

7.59 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3exports.ConfigurationManager = void 0;
4const tslib_1 = require("tslib");
5const inversify_1 = require("inversify");
6const ymir_1 = require("@fimbul/ymir");
7const path = require("path");
8const utils_1 = require("../utils");
9const minimatch_1 = require("minimatch");
10const isNegated = require("is-negated-glob");
11const cached_file_system_1 = require("./cached-file-system");
12let ConfigurationManager = class ConfigurationManager {
13 constructor(directories, configProvider, fs, cache) {
14 this.directories = directories;
15 this.configProvider = configProvider;
16 this.fs = fs;
17 this.configCache = cache.create();
18 }
19 /** Look up the location of the configuration file for the specified file name. */
20 findPath(file) {
21 file = path.resolve(this.directories.getCurrentDirectory(), file);
22 try {
23 return this.configProvider.find(file);
24 }
25 catch (e) {
26 throw new ymir_1.ConfigurationError(`Error finding configuration for '${file}': ${e && e.message}`);
27 }
28 }
29 /** Load the configuration for the specified file. */
30 find(file) {
31 const config = this.findPath(file);
32 return config === undefined ? undefined : this.load(config);
33 }
34 /** Load the given config from a local file if it exists or from the resolved path otherwise */
35 loadLocalOrResolved(pathOrName, basedir = this.directories.getCurrentDirectory()) {
36 const absolute = path.resolve(basedir, pathOrName);
37 return this.load(this.fs.isFile(absolute) ? absolute : this.resolve(pathOrName, basedir));
38 }
39 /**
40 * Resolve a configuration name to it's absolute path.
41 *
42 * @param name
43 * - name of a builtin config
44 * - package name in `node_modules` or a submodule thereof
45 * - absolute path
46 * - relative path starting with `./` or `../`
47 */
48 resolve(name, basedir) {
49 try {
50 return this.configProvider.resolve(name, path.resolve(this.directories.getCurrentDirectory(), basedir));
51 }
52 catch (e) {
53 throw new ymir_1.ConfigurationError(`${e && e.message}`);
54 }
55 }
56 /**
57 * Collects all matching configuration options for the given file. Flattens all base configs and matches overrides.
58 * Returns `undefined` if the file is excluded in one of the configuraton files.
59 */
60 reduce(config, file) {
61 return reduceConfig(config, path.resolve(this.directories.getCurrentDirectory(), file), { rules: new Map(), settings: new Map(), processor: undefined });
62 }
63 /** Get the processor configuration for a given file. */
64 getProcessor(config, fileName) {
65 return findProcessorInConfig(config, path.resolve(this.directories.getCurrentDirectory(), fileName)) || undefined;
66 }
67 /** Get the settings for a given file. */
68 getSettings(config, fileName) {
69 return reduceSettings(config, path.resolve(this.directories.getCurrentDirectory(), fileName), new Map());
70 }
71 /** Load a configuration from a resolved path using the ConfigurationProvider, recursively resolving and loading base configs. */
72 load(fileName) {
73 const stack = [];
74 const loadResolved = (file) => {
75 const circular = stack.includes(file);
76 stack.push(file);
77 if (circular)
78 throw new Error(`Circular configuration dependency.`);
79 const config = this.configProvider.load(file, {
80 stack,
81 load: (name) => utils_1.resolveCachedResult(this.configCache, this.configProvider.resolve(name, path.dirname(file)), loadResolved),
82 });
83 stack.pop();
84 return config;
85 };
86 try {
87 return utils_1.resolveCachedResult(this.configCache, path.resolve(this.directories.getCurrentDirectory(), fileName), loadResolved);
88 }
89 catch (e) {
90 throw new ymir_1.ConfigurationError(`Error loading ${stack.join(' => ')}: ${e && e.message}`);
91 }
92 }
93};
94ConfigurationManager = tslib_1.__decorate([
95 inversify_1.injectable(),
96 tslib_1.__metadata("design:paramtypes", [ymir_1.DirectoryService,
97 ymir_1.ConfigurationProvider,
98 cached_file_system_1.CachedFileSystem,
99 ymir_1.CacheFactory])
100], ConfigurationManager);
101exports.ConfigurationManager = ConfigurationManager;
102function reduceConfig(config, filename, receiver) {
103 const relativeFilename = path.relative(path.dirname(config.filename), filename);
104 if (config.exclude !== undefined && matchesGlobs(relativeFilename, config.exclude))
105 return;
106 for (const base of config.extends)
107 if (reduceConfig(base, filename, receiver) === undefined)
108 return;
109 extendConfig(receiver, config);
110 if (config.overrides !== undefined)
111 for (const override of config.overrides)
112 if (matchesGlobs(relativeFilename, override.files))
113 extendConfig(receiver, override);
114 return receiver;
115}
116function extendConfig(receiver, config) {
117 if (config.processor !== undefined)
118 receiver.processor = config.processor || undefined;
119 if (config.settings !== undefined)
120 extendSettings(receiver.settings, config.settings);
121 if (config.rules !== undefined)
122 extendRules(receiver.rules, config.rules);
123}
124function matchesGlobs(file, patterns) {
125 for (let i = patterns.length - 1; i >= 0; --i) {
126 const glob = isNegated(patterns[i]);
127 const local = glob.pattern.startsWith('./');
128 if (local)
129 glob.pattern = glob.pattern.substr(2);
130 if (new minimatch_1.Minimatch(glob.pattern, { matchBase: !local, dot: true }).match(file))
131 return !glob.negated;
132 }
133 return false;
134}
135function extendRules(receiver, rules) {
136 for (const [ruleName, config] of rules) {
137 const prev = receiver.get(ruleName);
138 receiver.set(ruleName, {
139 severity: 'error',
140 options: undefined,
141 ...prev,
142 ...config,
143 });
144 }
145}
146function extendSettings(receiver, settings) {
147 for (const [key, value] of settings)
148 receiver.set(key, value);
149}
150function findProcessorInConfig(config, fileName) {
151 if (config.overrides !== undefined) {
152 const relative = path.relative(path.dirname(config.filename), fileName);
153 for (let i = config.overrides.length - 1; i >= 0; --i) {
154 const override = config.overrides[i];
155 if (override.processor !== undefined && matchesGlobs(relative, override.files))
156 return override.processor;
157 }
158 }
159 if (config.processor !== undefined)
160 return config.processor;
161 for (let i = config.extends.length - 1; i >= 0; --i) {
162 const processor = findProcessorInConfig(config.extends[i], fileName);
163 if (processor !== undefined)
164 return processor;
165 }
166 return;
167}
168function reduceSettings(config, fileName, receiver) {
169 for (const base of config.extends)
170 reduceSettings(base, fileName, receiver);
171 if (config.settings !== undefined)
172 extendSettings(receiver, config.settings);
173 if (config.overrides !== undefined) {
174 const relative = path.relative(path.dirname(config.filename), fileName);
175 for (const override of config.overrides)
176 if (override.settings && matchesGlobs(relative, override.files))
177 extendSettings(receiver, override.settings);
178 }
179 return receiver;
180}
181//# sourceMappingURL=configuration-manager.js.map
\No newline at end of file