1 | "use strict";
|
2 |
|
3 | Object.defineProperty(exports, "__esModule", { value: true });
|
4 | exports.validatePluginConfiguration = exports.validateAutoRc = exports.validateIoConfiguration = exports.validatePlugins = exports.formatError = void 0;
|
5 | const tslib_1 = require("tslib");
|
6 | const t = tslib_1.__importStar(require("io-ts"));
|
7 | const Either_1 = require("fp-ts/lib/Either");
|
8 | const types_1 = require("./types");
|
9 | const omit_1 = require("./utils/omit");
|
10 | const chalk_1 = tslib_1.__importDefault(require("chalk"));
|
11 | const endent_1 = tslib_1.__importDefault(require("endent"));
|
12 | const ignoreTypes = ["PartialType", "IntersectionType", "ExactType"];
|
13 | const unexpectedValue = chalk_1.default.redBright.bold;
|
14 | const errorPath = chalk_1.default.underline.bold;
|
15 |
|
16 | function formatError(error) {
|
17 | if (typeof error === "string") {
|
18 | return error;
|
19 | }
|
20 | const { path, expectedType, value } = error;
|
21 | const formattedValue = (Array.isArray(value) &&
|
22 | endent_1.default `
|
23 | [
|
24 | ${value.join(",\n")}
|
25 | ]
|
26 | `) ||
|
27 | (typeof value === "object" && JSON.stringify(value, null, 2)) ||
|
28 | value;
|
29 | return `${errorPath(`"${path}"`)}\n\nExpected ${chalk_1.default.greenBright.bold(expectedType)} but got: ${unexpectedValue(formattedValue)}\n`;
|
30 | }
|
31 | exports.formatError = formatError;
|
32 |
|
33 | function reporter(validation) {
|
34 | if (validation._tag !== "Left") {
|
35 | return false;
|
36 | }
|
37 | const errors = validation.left.map((error) => {
|
38 | let parentType = "";
|
39 | const path = error.context
|
40 | .filter((c) => {
|
41 | const tag = c.type._tag;
|
42 | const include = parentType === "ArrayType" ||
|
43 | (!ignoreTypes.includes(tag) &&
|
44 | parentType !== "UnionType" &&
|
45 | parentType !== "IntersectionType");
|
46 | parentType = tag;
|
47 | return c.key && include;
|
48 | })
|
49 | .map((c) => c.key)
|
50 | .join(".");
|
51 | return {
|
52 | path,
|
53 | expectedType: error.context[error.context.length - 1].type.name,
|
54 | value: error.value,
|
55 | };
|
56 | });
|
57 | const otherErrors = [];
|
58 | const grouped = errors.reduce((acc, item) => {
|
59 | if (typeof item === "string") {
|
60 | otherErrors.push(item);
|
61 | return acc;
|
62 | }
|
63 | if (!acc[item.path]) {
|
64 | acc[item.path] = [];
|
65 | }
|
66 | acc[item.path].push(item);
|
67 | return acc;
|
68 | }, {});
|
69 | const paths = Object.keys(grouped);
|
70 | return [
|
71 | ...otherErrors,
|
72 | ...Object.entries(grouped)
|
73 | .filter(([path]) => {
|
74 | return !paths.some((p) => p.includes(path) && p !== path);
|
75 | })
|
76 | .map(([path, group]) => {
|
77 | const expectedType = group
|
78 | .map((g) => g.expectedType)
|
79 | .map((t) => `"${t}"`)
|
80 | .join(" or ");
|
81 | const value = group[0].value;
|
82 | return {
|
83 | expectedType,
|
84 | path,
|
85 | value,
|
86 | };
|
87 | }),
|
88 | ];
|
89 | }
|
90 |
|
91 | function flatKeys(obj) {
|
92 | return Object.keys(obj)
|
93 | .map((key) => {
|
94 | if (typeof obj[key] === "object") {
|
95 | return flatKeys(obj[key]).map((sub) => `${key}.${sub}`);
|
96 | }
|
97 | return [key];
|
98 | })
|
99 | .reduce((acc, item) => acc.concat(item), []);
|
100 | }
|
101 |
|
102 | async function validatePlugins(validatePlugin, rc) {
|
103 | const errors = [];
|
104 | if (!Array.isArray(rc.plugins)) {
|
105 | return [];
|
106 | }
|
107 | await Promise.all(rc.plugins.map(async (plugin) => {
|
108 | if (!Array.isArray(plugin)) {
|
109 | return;
|
110 | }
|
111 | const pluginErrors = await validatePlugin.promise(...plugin);
|
112 | if (pluginErrors) {
|
113 | errors.push(...pluginErrors);
|
114 | }
|
115 | }));
|
116 | return errors;
|
117 | }
|
118 | exports.validatePlugins = validatePlugins;
|
119 | const shouldRecurse = [
|
120 | "PartialType",
|
121 | "IntersectionType",
|
122 | "ArrayType",
|
123 | "InterfaceType",
|
124 | ];
|
125 |
|
126 |
|
127 |
|
128 |
|
129 | function makeExactType(configDeceleration) {
|
130 | let strictConfigDeclaration = configDeceleration;
|
131 | if ("props" in configDeceleration &&
|
132 | configDeceleration._tag !== "StrictType") {
|
133 | const props = {};
|
134 | Object.entries(configDeceleration.props).forEach(([propName, propType]) => {
|
135 | props[propName] = shouldRecurse.includes(propType._tag)
|
136 | ? makeExactType(propType)
|
137 | : propType;
|
138 | });
|
139 | strictConfigDeclaration = t.exact(configDeceleration._tag === "InterfaceType"
|
140 | ? t.interface(Object.assign({}, props))
|
141 | : t.partial(Object.assign({}, props)));
|
142 | }
|
143 | else if ("types" in configDeceleration) {
|
144 | const exactInterfaces = configDeceleration.types.map((propType) => shouldRecurse.includes(propType._tag) ? makeExactType(propType) : propType);
|
145 | strictConfigDeclaration =
|
146 | configDeceleration._tag === "IntersectionType"
|
147 | ? t.intersection(exactInterfaces)
|
148 | : t.union(exactInterfaces);
|
149 | }
|
150 | else if ("type" in configDeceleration) {
|
151 | strictConfigDeclaration = t.array(makeExactType(configDeceleration.type));
|
152 | }
|
153 | return strictConfigDeclaration;
|
154 | }
|
155 |
|
156 | exports.validateIoConfiguration = (name, configDeceleration) =>
|
157 |
|
158 | async (rc) => {
|
159 | const looseRc = configDeceleration.decode(rc);
|
160 | const errors = reporter(looseRc);
|
161 | if (errors) {
|
162 | return errors;
|
163 | }
|
164 | const exactRc = makeExactType(configDeceleration).decode(rc);
|
165 | if (!Either_1.isRight(looseRc) || !Either_1.isRight(exactRc)) {
|
166 | return [];
|
167 | }
|
168 | const correctKeys = flatKeys(exactRc.right);
|
169 | const unknownTopKeys = Object.keys(looseRc.right).filter((k) => !(k in exactRc.right));
|
170 | const unknownDeepKeys = flatKeys(omit_1.omit(looseRc.right, unknownTopKeys)).filter((k) => !correctKeys.includes(k));
|
171 | const unknownKeys = [...unknownTopKeys, ...unknownDeepKeys];
|
172 | if (unknownKeys.length === 0) {
|
173 | return [];
|
174 | }
|
175 | return [
|
176 | `${errorPath(`"${name}"`)}\n\nFound unknown configuration keys: ${unexpectedValue(unknownKeys.join(", "))}\n`,
|
177 | ];
|
178 | };
|
179 | exports.validateAutoRc = exports.validateIoConfiguration(".autorc", types_1.autoRc);
|
180 |
|
181 | async function validatePluginConfiguration(name, pluginDefinition, providedOptions) {
|
182 | const validateConfig = exports.validateIoConfiguration(name, pluginDefinition);
|
183 | const errors = await validateConfig(providedOptions);
|
184 | return errors.map((error) => {
|
185 | if (typeof error === "string") {
|
186 | return error;
|
187 | }
|
188 | return Object.assign(Object.assign({}, error), { path: error.path ? `${name}.${error.path}` : name });
|
189 | });
|
190 | }
|
191 | exports.validatePluginConfiguration = validatePluginConfiguration;
|
192 |
|
\ | No newline at end of file |