Skip to content

Commit ae9b47b

Browse files
committed
feat(validations): add validateOptions module
1 parent 8593f31 commit ae9b47b

File tree

3 files changed

+149
-0
lines changed

3 files changed

+149
-0
lines changed

src/index.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import { validateOptions } from './lib/validate-options';
2+
3+
export default validateOptions;

src/lib/validate-options.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import Ajv from 'ajv';
2+
3+
const validateOptions = (schema, data) => {
4+
// This mutates the original data with defaults!
5+
const ajv = new Ajv({
6+
useDefaults: true,
7+
errorDataPath: 'property',
8+
});
9+
const isValid = ajv.validate(schema, data);
10+
11+
return {
12+
isValid,
13+
error: ajv.errors && ajv.errorsText(),
14+
};
15+
};
16+
17+
export default validateOptions;

src/lib/validate-options.test.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import assert from 'assert';
2+
import validateOptions from './validate-options';
3+
4+
const generateProperties = (entry) => {
5+
const ret = {};
6+
7+
Object.keys(entry).forEach((e) => {
8+
ret[e] = {
9+
type: ['array', 'string'],
10+
items: {
11+
type: 'string',
12+
},
13+
};
14+
});
15+
16+
return ret;
17+
};
18+
19+
const parsePaths = (entry) => {
20+
const ret = {
21+
type: ['array', 'object'],
22+
};
23+
24+
if (entry instanceof Object) {
25+
ret.additionalProperties = false;
26+
ret.properties = generateProperties(entry);
27+
} else {
28+
ret.items = {
29+
type: 'string',
30+
};
31+
}
32+
33+
return ret;
34+
};
35+
36+
const testSchema = ({ entry } = {}) => {
37+
return {
38+
$schema: 'http://json-schema.org/draft-04/schema#',
39+
additionalProperties: false,
40+
type: 'object',
41+
properties: {
42+
styleExtensions: {
43+
type: 'array',
44+
items: {
45+
type: 'string',
46+
},
47+
default: ['.css'],
48+
},
49+
minimize: {
50+
type: 'boolean',
51+
},
52+
moduleExtensions: {
53+
type: 'array',
54+
items: {
55+
type: 'string',
56+
},
57+
},
58+
paths: parsePaths(entry),
59+
purifyOptions: {
60+
type: 'object',
61+
properties: {},
62+
},
63+
verbose: {
64+
type: 'boolean',
65+
},
66+
},
67+
required: [
68+
'paths',
69+
],
70+
};
71+
};
72+
73+
describe('Validate options', () => {
74+
it('fails without a schema and data', () => {
75+
assert.throws(
76+
() => {
77+
validateOptions();
78+
},
79+
Error,
80+
);
81+
});
82+
83+
it('fails with empty data', () => {
84+
const result = validateOptions(testSchema());
85+
86+
assert.ok(!result.isValid);
87+
assert.ok(result.error);
88+
});
89+
90+
it('does not fail if paths are provided', () => {
91+
const result = validateOptions(testSchema(), { paths: ['./foo'] });
92+
93+
assert.ok(result.isValid);
94+
assert.ok(!result.error);
95+
});
96+
97+
it('does not allow arbitrary properties', () => {
98+
const result = validateOptions(testSchema(), { paths: ['./foo'], foobar: ['./foo'] });
99+
100+
assert.ok(!result.isValid);
101+
assert.ok(result.error);
102+
});
103+
104+
it('styleExtensions have defaults', () => {
105+
const paths = ['./foo'];
106+
const data = { paths };
107+
108+
const result = validateOptions(testSchema(), data);
109+
110+
assert.deepEqual(data, { paths, styleExtensions: ['.css'] });
111+
assert.ok(!result.error);
112+
});
113+
114+
it('fails without matching path keys', () => {
115+
const data = {
116+
paths: {
117+
a: './foo',
118+
},
119+
};
120+
121+
const result = validateOptions(testSchema({
122+
entry: {
123+
b: './bar',
124+
},
125+
}), data);
126+
127+
assert.ok(result.error);
128+
});
129+
});

0 commit comments

Comments
 (0)