1 | "use strict";
|
2 | Object.defineProperty(exports, "__esModule", { value: true });
|
3 | exports.ConfigurationManager = void 0;
|
4 | const tslib_1 = require("tslib");
|
5 | const inversify_1 = require("inversify");
|
6 | const ymir_1 = require("@fimbul/ymir");
|
7 | const path = require("path");
|
8 | const utils_1 = require("../utils");
|
9 | const minimatch_1 = require("minimatch");
|
10 | const isNegated = require("is-negated-glob");
|
11 | const cached_file_system_1 = require("./cached-file-system");
|
12 | let 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 |
|
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 |
|
30 | find(file) {
|
31 | const config = this.findPath(file);
|
32 | return config === undefined ? undefined : this.load(config);
|
33 | }
|
34 |
|
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 |
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 |
|
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 |
|
58 |
|
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 |
|
64 | getProcessor(config, fileName) {
|
65 | return findProcessorInConfig(config, path.resolve(this.directories.getCurrentDirectory(), fileName)) || undefined;
|
66 | }
|
67 |
|
68 | getSettings(config, fileName) {
|
69 | return reduceSettings(config, path.resolve(this.directories.getCurrentDirectory(), fileName), new Map());
|
70 | }
|
71 |
|
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 | };
|
94 | ConfigurationManager = 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);
|
101 | exports.ConfigurationManager = ConfigurationManager;
|
102 | function 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 | }
|
116 | function 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 | }
|
124 | function 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 | }
|
135 | function 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 | }
|
146 | function extendSettings(receiver, settings) {
|
147 | for (const [key, value] of settings)
|
148 | receiver.set(key, value);
|
149 | }
|
150 | function 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 | }
|
168 | function 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 |
|
\ | No newline at end of file |