@percy/config 1.0.0-beta.7 → 1.0.0-beta.73
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -11
- package/dist/defaults.js +23 -19
- package/dist/index.js +90 -3
- package/dist/load.js +68 -33
- package/dist/migrate.js +98 -0
- package/dist/normalize.js +50 -32
- package/dist/stringify.js +8 -4
- package/dist/utils.js +190 -0
- package/dist/validate.js +188 -53
- package/package.json +19 -16
package/README.md
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
# @percy/config
|
|
2
2
|
|
|
3
3
|
Handles loading and adding options to Percy configuration files. Uses
|
|
4
4
|
[cosmiconfig](https://github.com/davidtheclark/cosmiconfig) to load configuration files and [JSON
|
|
5
5
|
schema](https://json-schema.org/) with [AJV](https://github.com/epoberezkin/ajv) to validate those
|
|
6
6
|
configuration files.
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
- [Loading config files](#loading-config-files)
|
|
9
|
+
- [Extending config options](#extending-config-options)
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
## Loading config files
|
|
11
12
|
|
|
12
13
|
The `.load()` method will load and validate a configuation file, optionally merging it with any
|
|
13
14
|
provided `overrides`. If no `path` is provided, will search for the first supported config found
|
|
@@ -18,14 +19,17 @@ from the current directory up to the home directoy. Configuration files are cach
|
|
|
18
19
|
import PercyConfig from '@percy/config'
|
|
19
20
|
|
|
20
21
|
// loading is done synchronously
|
|
21
|
-
const config = PercyConfig.load(
|
|
22
|
-
path, // config file path or directory path containing a config file
|
|
23
|
-
overrides = {}, // configuration option overrides
|
|
24
|
-
reload = false, // reload file and update cache
|
|
25
|
-
bail = false // return undefined on validation warnings
|
|
26
|
-
})
|
|
22
|
+
const config = PercyConfig.load(options)
|
|
27
23
|
```
|
|
28
24
|
|
|
25
|
+
#### Options
|
|
26
|
+
|
|
27
|
+
- `path` — Config file path or directory containing a config file
|
|
28
|
+
- `overrides` — Config option overrides
|
|
29
|
+
- `reload` — Do not use cached config (**default** `false`)
|
|
30
|
+
- `bail` — Return undefined when failing validation (**default** `false`)
|
|
31
|
+
- `print` — Print info and error logs (**default** `false`)
|
|
32
|
+
|
|
29
33
|
#### Supported files
|
|
30
34
|
|
|
31
35
|
- `"percy"` entry in `package.json`
|
|
@@ -34,7 +38,7 @@ const config = PercyConfig.load({
|
|
|
34
38
|
- `.percy.yaml` or `.percy.yml` YAML file
|
|
35
39
|
- `.percy.js` or `percy.config.js` file that exports an object
|
|
36
40
|
|
|
37
|
-
|
|
41
|
+
## Extending config options
|
|
38
42
|
|
|
39
43
|
The `.addSchema()` function will add a sub-schema to the Percy configuration file which will be
|
|
40
44
|
parsed and validated when `PercyConfig.load()` is called. See [JSON
|
|
@@ -43,5 +47,7 @@ schema](https://json-schema.org/) for possible schema options.
|
|
|
43
47
|
```js
|
|
44
48
|
import PercyConfig from '@percy/config'
|
|
45
49
|
|
|
46
|
-
PercyConfig.addSchema({
|
|
50
|
+
PercyConfig.addSchema({
|
|
51
|
+
propertyName: JSONSchema
|
|
52
|
+
})
|
|
47
53
|
```
|
package/dist/defaults.js
CHANGED
|
@@ -3,46 +3,50 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default =
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
exports.getDefaults = getDefaults;
|
|
7
8
|
|
|
8
|
-
var
|
|
9
|
+
var _utils = require("./utils");
|
|
9
10
|
|
|
10
11
|
var _validate = require("./validate");
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
const {
|
|
14
|
+
isArray
|
|
15
|
+
} = Array;
|
|
14
16
|
const {
|
|
15
17
|
assign,
|
|
16
|
-
entries
|
|
17
|
-
freeze
|
|
18
|
+
entries
|
|
18
19
|
} = Object; // Recursively walks a schema and collects defaults. When no schema is provided,
|
|
19
|
-
// the default config schema is used.
|
|
20
|
+
// the default config schema is used.
|
|
20
21
|
|
|
21
|
-
function
|
|
22
|
+
function getDefaultsFromSchema(schema) {
|
|
22
23
|
if (!schema || typeof schema.$ref === 'string') {
|
|
23
24
|
var _schema$$ref;
|
|
24
25
|
|
|
25
26
|
// get the schema from ajv
|
|
26
|
-
return
|
|
27
|
+
return getDefaultsFromSchema((0, _validate.getSchema)((_schema$$ref = schema === null || schema === void 0 ? void 0 : schema.$ref) !== null && _schema$$ref !== void 0 ? _schema$$ref : '/config'));
|
|
27
28
|
} else if (schema.default != null) {
|
|
28
|
-
// return the
|
|
29
|
-
return
|
|
29
|
+
// return the default for this schema
|
|
30
|
+
return schema.default;
|
|
30
31
|
} else if (schema.type === 'object' && schema.properties) {
|
|
31
|
-
// return
|
|
32
|
-
return
|
|
33
|
-
let def =
|
|
32
|
+
// return an object of default properties
|
|
33
|
+
return entries(schema.properties).reduce((acc, [prop, schema]) => {
|
|
34
|
+
let def = getDefaultsFromSchema(schema);
|
|
34
35
|
return def != null ? assign(acc || {}, {
|
|
35
36
|
[prop]: def
|
|
36
37
|
}) : acc;
|
|
37
|
-
}, undefined)
|
|
38
|
+
}, undefined);
|
|
38
39
|
} else {
|
|
39
40
|
return undefined;
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
function getDefaults(overrides = {}) {
|
|
44
|
-
return
|
|
45
|
-
//
|
|
46
|
-
|
|
45
|
+
return (0, _utils.merge)([getDefaultsFromSchema(), overrides], (path, prev, next) => {
|
|
46
|
+
// override default array instead of merging
|
|
47
|
+
return isArray(next) && [path, next];
|
|
47
48
|
});
|
|
48
|
-
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
var _default = getDefaults;
|
|
52
|
+
exports.default = _default;
|
package/dist/index.js
CHANGED
|
@@ -3,31 +3,118 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
Object.defineProperty(exports, "addMigration", {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
get: function () {
|
|
9
|
+
return _migrate.addMigration;
|
|
10
|
+
}
|
|
11
|
+
});
|
|
12
|
+
Object.defineProperty(exports, "addSchema", {
|
|
13
|
+
enumerable: true,
|
|
14
|
+
get: function () {
|
|
15
|
+
return _validate.addSchema;
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
Object.defineProperty(exports, "cache", {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () {
|
|
21
|
+
return _load.cache;
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
Object.defineProperty(exports, "clearMigrations", {
|
|
25
|
+
enumerable: true,
|
|
26
|
+
get: function () {
|
|
27
|
+
return _migrate.clearMigrations;
|
|
28
|
+
}
|
|
29
|
+
});
|
|
6
30
|
exports.default = void 0;
|
|
31
|
+
Object.defineProperty(exports, "explorer", {
|
|
32
|
+
enumerable: true,
|
|
33
|
+
get: function () {
|
|
34
|
+
return _load.explorer;
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
Object.defineProperty(exports, "getDefaults", {
|
|
38
|
+
enumerable: true,
|
|
39
|
+
get: function () {
|
|
40
|
+
return _defaults.default;
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
Object.defineProperty(exports, "load", {
|
|
44
|
+
enumerable: true,
|
|
45
|
+
get: function () {
|
|
46
|
+
return _load.default;
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
Object.defineProperty(exports, "migrate", {
|
|
50
|
+
enumerable: true,
|
|
51
|
+
get: function () {
|
|
52
|
+
return _migrate.default;
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
Object.defineProperty(exports, "normalize", {
|
|
56
|
+
enumerable: true,
|
|
57
|
+
get: function () {
|
|
58
|
+
return _normalize.default;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
Object.defineProperty(exports, "resetSchema", {
|
|
62
|
+
enumerable: true,
|
|
63
|
+
get: function () {
|
|
64
|
+
return _validate.resetSchema;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
Object.defineProperty(exports, "search", {
|
|
68
|
+
enumerable: true,
|
|
69
|
+
get: function () {
|
|
70
|
+
return _load.search;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
Object.defineProperty(exports, "stringify", {
|
|
74
|
+
enumerable: true,
|
|
75
|
+
get: function () {
|
|
76
|
+
return _stringify.default;
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
Object.defineProperty(exports, "validate", {
|
|
80
|
+
enumerable: true,
|
|
81
|
+
get: function () {
|
|
82
|
+
return _validate.default;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
7
85
|
|
|
8
86
|
var _load = _interopRequireWildcard(require("./load"));
|
|
9
87
|
|
|
10
88
|
var _validate = _interopRequireWildcard(require("./validate"));
|
|
11
89
|
|
|
90
|
+
var _migrate = _interopRequireWildcard(require("./migrate"));
|
|
91
|
+
|
|
12
92
|
var _defaults = _interopRequireDefault(require("./defaults"));
|
|
13
93
|
|
|
94
|
+
var _normalize = _interopRequireDefault(require("./normalize"));
|
|
95
|
+
|
|
14
96
|
var _stringify = _interopRequireDefault(require("./stringify"));
|
|
15
97
|
|
|
16
98
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
17
99
|
|
|
18
|
-
function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var
|
|
100
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
19
101
|
|
|
20
|
-
function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
102
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
21
103
|
|
|
22
|
-
//
|
|
104
|
+
// mirror the namespace as the default export
|
|
23
105
|
var _default = {
|
|
24
106
|
load: _load.default,
|
|
107
|
+
search: _load.search,
|
|
25
108
|
cache: _load.cache,
|
|
26
109
|
explorer: _load.explorer,
|
|
27
110
|
validate: _validate.default,
|
|
28
111
|
addSchema: _validate.addSchema,
|
|
29
112
|
resetSchema: _validate.resetSchema,
|
|
113
|
+
migrate: _migrate.default,
|
|
114
|
+
addMigration: _migrate.addMigration,
|
|
115
|
+
clearMigrations: _migrate.clearMigrations,
|
|
30
116
|
getDefaults: _defaults.default,
|
|
117
|
+
normalize: _normalize.default,
|
|
31
118
|
stringify: _stringify.default
|
|
32
119
|
};
|
|
33
120
|
exports.default = _default;
|
package/dist/load.js
CHANGED
|
@@ -3,27 +3,28 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.default =
|
|
7
|
-
exports.
|
|
6
|
+
exports.explorer = exports.default = exports.cache = void 0;
|
|
7
|
+
exports.load = load;
|
|
8
|
+
exports.search = search;
|
|
8
9
|
|
|
9
10
|
var _path = require("path");
|
|
10
11
|
|
|
11
|
-
var
|
|
12
|
-
|
|
13
|
-
var _pathType = require("path-type");
|
|
12
|
+
var _fs = require("fs");
|
|
14
13
|
|
|
15
|
-
var
|
|
14
|
+
var _cosmiconfig = require("cosmiconfig");
|
|
16
15
|
|
|
17
16
|
var _logger = _interopRequireDefault(require("@percy/logger"));
|
|
18
17
|
|
|
19
18
|
var _defaults = _interopRequireDefault(require("./defaults"));
|
|
20
19
|
|
|
21
|
-
var
|
|
20
|
+
var _migrate = _interopRequireDefault(require("./migrate"));
|
|
22
21
|
|
|
23
|
-
var
|
|
22
|
+
var _normalize = _interopRequireDefault(require("./normalize"));
|
|
24
23
|
|
|
25
24
|
var _stringify = require("./stringify");
|
|
26
25
|
|
|
26
|
+
var _validate = _interopRequireDefault(require("./validate"));
|
|
27
|
+
|
|
27
28
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
28
29
|
|
|
29
30
|
// Loaded configuration file cache
|
|
@@ -33,60 +34,94 @@ exports.cache = cache;
|
|
|
33
34
|
const explorer = (0, _cosmiconfig.cosmiconfigSync)('percy', {
|
|
34
35
|
cache: false,
|
|
35
36
|
searchPlaces: ['package.json', '.percyrc', '.percy.json', '.percy.yaml', '.percy.yml', '.percy.js', 'percy.config.js']
|
|
36
|
-
}); //
|
|
37
|
+
}); // Searches within a provided directory, or loads the provided config path
|
|
38
|
+
|
|
39
|
+
exports.explorer = explorer;
|
|
40
|
+
|
|
41
|
+
function search(path) {
|
|
42
|
+
try {
|
|
43
|
+
let result = path && !(0, _fs.statSync)(path).isDirectory() ? explorer.load(path) : explorer.search(path);
|
|
44
|
+
return result || {};
|
|
45
|
+
} catch (error) {
|
|
46
|
+
if (error.code === 'ENOENT') return {};else throw error;
|
|
47
|
+
}
|
|
48
|
+
} // Finds and loads a config file using cosmiconfig, merges it with optional
|
|
37
49
|
// inputs, validates the combined config according to the schema, and returns
|
|
38
50
|
// the combined config. Loaded config files are cached and reused on next load,
|
|
39
51
|
// unless `reload` is true in which the file will be reloaded and the cache
|
|
40
52
|
// updated. Validation errors are logged as warnings and the config is returned
|
|
41
53
|
// unless `bail` is true. Supports kebab-case and camelCase config options and
|
|
42
|
-
// always returns camelCase options.
|
|
43
|
-
//
|
|
54
|
+
// always returns camelCase options. Will automatically convert older config
|
|
55
|
+
// versions to the latest version while printing a warning.
|
|
44
56
|
|
|
45
|
-
exports.explorer = explorer;
|
|
46
57
|
|
|
47
58
|
function load({
|
|
48
59
|
path,
|
|
49
60
|
overrides = {},
|
|
50
61
|
reload = false,
|
|
51
|
-
bail = false
|
|
62
|
+
bail = false,
|
|
63
|
+
print = false
|
|
52
64
|
} = {}) {
|
|
53
65
|
var _Array$from;
|
|
54
66
|
|
|
55
67
|
// load cached config; when no path is specified, get the last config cached
|
|
56
|
-
let config = path ? cache.get(path) : (_Array$from = Array.from(cache)[cache.size - 1]) === null || _Array$from === void 0 ? void 0 : _Array$from[1];
|
|
68
|
+
let config = path ? cache.get(path) : (_Array$from = Array.from(cache)[cache.size - 1]) === null || _Array$from === void 0 ? void 0 : _Array$from[1];
|
|
69
|
+
let infoDebug = print ? 'info' : 'debug';
|
|
70
|
+
let errorDebug = print ? 'error' : 'debug';
|
|
71
|
+
let log = (0, _logger.default)('config'); // load config or reload cached config
|
|
57
72
|
|
|
58
73
|
if (path !== false && (!config || reload)) {
|
|
59
74
|
try {
|
|
60
|
-
let result =
|
|
75
|
+
let result = search(path);
|
|
61
76
|
|
|
62
|
-
if (result && result.config) {
|
|
63
|
-
|
|
77
|
+
if (result !== null && result !== void 0 && result.config) {
|
|
78
|
+
log[infoDebug](`Found config file: ${(0, _path.relative)('', result.filepath)}`);
|
|
79
|
+
let version = parseInt(result.config.version, 10);
|
|
64
80
|
|
|
65
|
-
if (
|
|
66
|
-
|
|
81
|
+
if (Number.isNaN(version)) {
|
|
82
|
+
log.warn('Ignoring config file - missing or invalid version');
|
|
83
|
+
} else if (version > 2) {
|
|
84
|
+
log.warn(`Ignoring config file - unsupported version "${version}"`);
|
|
67
85
|
} else {
|
|
68
|
-
|
|
69
|
-
|
|
86
|
+
if (version < 2) {
|
|
87
|
+
log.warn('Found older config file version, please run ' + '`percy config:migrate` to update to the latest version');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
config = (0, _migrate.default)(result.config);
|
|
70
91
|
cache.set(path, config);
|
|
71
92
|
}
|
|
72
93
|
} else {
|
|
73
|
-
|
|
94
|
+
log[infoDebug]('Config file not found');
|
|
95
|
+
if (bail) return;
|
|
74
96
|
}
|
|
75
97
|
} catch (error) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
_logger.default.debug(error.toString());
|
|
98
|
+
log[errorDebug](error);
|
|
99
|
+
if (bail) return;
|
|
79
100
|
}
|
|
80
|
-
} //
|
|
101
|
+
} // normalize and merge with overrides then validate
|
|
102
|
+
|
|
81
103
|
|
|
104
|
+
config = (0, _normalize.default)(config, {
|
|
105
|
+
overrides,
|
|
106
|
+
schema: '/config'
|
|
107
|
+
});
|
|
108
|
+
let errors = config && (0, _validate.default)(config);
|
|
82
109
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
110
|
+
if (errors) {
|
|
111
|
+
log.warn('Invalid config:');
|
|
112
|
+
|
|
113
|
+
for (let e of errors) log.warn(`- ${e.path}: ${e.message}`);
|
|
114
|
+
|
|
115
|
+
if (bail) return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
if (path !== false && config) {
|
|
119
|
+
log[infoDebug](`Using config:\n${(0, _stringify.inspect)(config)}`);
|
|
120
|
+
} // merge with defaults
|
|
87
121
|
|
|
88
|
-
config = (0, _normalize.default)(config);
|
|
89
|
-
if (config) _logger.default.debug(`Using config:\n${(0, _stringify.inspect)(config)}`); // merge with defaults
|
|
90
122
|
|
|
91
123
|
return (0, _defaults.default)(config);
|
|
92
|
-
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
var _default = load;
|
|
127
|
+
exports.default = _default;
|
package/dist/migrate.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.addMigration = addMigration;
|
|
7
|
+
exports.clearMigrations = clearMigrations;
|
|
8
|
+
exports.default = void 0;
|
|
9
|
+
exports.migrate = migrate;
|
|
10
|
+
|
|
11
|
+
var _logger = _interopRequireDefault(require("@percy/logger"));
|
|
12
|
+
|
|
13
|
+
var _normalize2 = _interopRequireDefault(require("./normalize"));
|
|
14
|
+
|
|
15
|
+
var _utils = require("./utils");
|
|
16
|
+
|
|
17
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
18
|
+
|
|
19
|
+
// Global set of registered migrations
|
|
20
|
+
const migrations = new Map(); // Register a migration function for the main config schema by default
|
|
21
|
+
|
|
22
|
+
function addMigration(migration, schema = '/config') {
|
|
23
|
+
if (Array.isArray(migration)) {
|
|
24
|
+
// accept schema as the first item in a tuple
|
|
25
|
+
if (typeof migration[0] === 'string') [schema, ...migration] = migration;
|
|
26
|
+
return migration.map(m => addMigration(m, schema));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!migrations.has(schema)) migrations.set(schema, []);
|
|
30
|
+
migrations.get(schema).unshift(migration);
|
|
31
|
+
return migration;
|
|
32
|
+
} // Clear all migration functions
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
function clearMigrations() {
|
|
36
|
+
migrations.clear();
|
|
37
|
+
addMigration(defaultMigration);
|
|
38
|
+
} // The default config migration
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
addMigration(defaultMigration);
|
|
42
|
+
|
|
43
|
+
function defaultMigration(config, {
|
|
44
|
+
set
|
|
45
|
+
}) {
|
|
46
|
+
if (config.version !== 2) set('version', 2);
|
|
47
|
+
} // Migrate util for deprecated options
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
function deprecate(config, log, path, options) {
|
|
51
|
+
if ((0, _utils.get)(config, path) == null) return;
|
|
52
|
+
let {
|
|
53
|
+
type,
|
|
54
|
+
until: ver,
|
|
55
|
+
map: to,
|
|
56
|
+
alt,
|
|
57
|
+
warn
|
|
58
|
+
} = options;
|
|
59
|
+
let name = (0, _utils.joinPropertyPath)(path);
|
|
60
|
+
let message = 'The ' + [type ? `${type} option \`${name}\`` : `\`${name}\` option`, `will be removed in ${ver || 'a future release'}.`, to ? `Use \`${to}\` instead.` : alt || ''].join(' ').trim();
|
|
61
|
+
if (warn) log.warn(`Warning: ${message}`);else log.deprecated(message);
|
|
62
|
+
return to ? (0, _utils.map)(config, path, to) : config;
|
|
63
|
+
} // Calls each registered migration function with a normalize provided config
|
|
64
|
+
// and util functions for working with the config object
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
function migrate(config, schema = '/config') {
|
|
68
|
+
var _normalize;
|
|
69
|
+
|
|
70
|
+
config = (_normalize = (0, _normalize2.default)(config, {
|
|
71
|
+
schema
|
|
72
|
+
})) !== null && _normalize !== void 0 ? _normalize : {};
|
|
73
|
+
|
|
74
|
+
if (migrations.has(schema)) {
|
|
75
|
+
let log = (0, _logger.default)('config');
|
|
76
|
+
let util = {
|
|
77
|
+
deprecate: deprecate.bind(null, config, log),
|
|
78
|
+
set: _utils.set.bind(null, config),
|
|
79
|
+
map: _utils.map.bind(null, config),
|
|
80
|
+
del: _utils.del.bind(null, config),
|
|
81
|
+
log
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
for (let migration of migrations.get(schema)) {
|
|
85
|
+
migration(config, util);
|
|
86
|
+
} // normalize again to adjust for migrations
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
config = (0, _normalize2.default)(config, {
|
|
90
|
+
schema
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return config;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
var _default = migrate;
|
|
98
|
+
exports.default = _default;
|
package/dist/normalize.js
CHANGED
|
@@ -3,35 +3,53 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
6
|
+
exports.camelcase = camelcase;
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
exports.kebabcase = kebabcase;
|
|
9
|
+
exports.normalize = normalize;
|
|
10
|
+
|
|
11
|
+
var _validate = require("./validate");
|
|
12
|
+
|
|
13
|
+
var _utils = require("./utils");
|
|
14
|
+
|
|
15
|
+
// Edge case camelizations
|
|
16
|
+
const CAMELCASE_MAP = new Map([['css', 'CSS'], ['javascript', 'JavaScript']]); // Converts kebab-cased and snake_cased strings to camelCase.
|
|
17
|
+
|
|
18
|
+
const KEBAB_SNAKE_REG = /[-_]([^-_]+)/g;
|
|
19
|
+
|
|
20
|
+
function camelcase(str) {
|
|
21
|
+
if (typeof str !== 'string') return str;
|
|
22
|
+
return str.replace(KEBAB_SNAKE_REG, (match, word) => CAMELCASE_MAP.get(word) || word[0].toUpperCase() + word.slice(1));
|
|
23
|
+
} // Coverts camelCased and snake_cased strings to kebab-case.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
const CAMEL_SNAKE_REG = /([a-z])([A-Z]+)|_([^_]+)/g;
|
|
27
|
+
|
|
28
|
+
function kebabcase(str) {
|
|
29
|
+
if (typeof str !== 'string') return str;
|
|
30
|
+
return Array.from(CAMELCASE_MAP).reduce((str, [word, camel]) => str.replace(camel, `-${word}`), str).replace(CAMEL_SNAKE_REG, (match, p, n, w) => `${p || ''}-${(n || w).toLowerCase()}`);
|
|
31
|
+
} // Removes undefined empty values and renames kebab-case properties to camelCase. Optionally
|
|
32
|
+
// allows deep merging with options.overrides, converting keys to kebab-case with options.kebab,
|
|
33
|
+
// and normalizing against a schema with options.schema.
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
function normalize(object, options) {
|
|
37
|
+
let keycase = options !== null && options !== void 0 && options.kebab ? kebabcase : camelcase;
|
|
38
|
+
return (0, _utils.merge)([object, options === null || options === void 0 ? void 0 : options.overrides], path => {
|
|
39
|
+
var _schemas$shift;
|
|
40
|
+
|
|
41
|
+
let schemas = (0, _validate.getSchema)(options === null || options === void 0 ? void 0 : options.schema, path.map(camelcase));
|
|
42
|
+
let skip = ((_schemas$shift = schemas.shift()) === null || _schemas$shift === void 0 ? void 0 : _schemas$shift.normalize) === false;
|
|
43
|
+
path = path.map((k, i) => {
|
|
44
|
+
var _schemas$i;
|
|
45
|
+
|
|
46
|
+
if (skip) return k;
|
|
47
|
+
skip || (skip = ((_schemas$i = schemas[i]) === null || _schemas$i === void 0 ? void 0 : _schemas$i.normalize) === false);
|
|
48
|
+
return keycase(k);
|
|
49
|
+
});
|
|
50
|
+
return [path];
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
var _default = normalize;
|
|
55
|
+
exports.default = _default;
|
package/dist/stringify.js
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
+
exports.default = void 0;
|
|
6
7
|
exports.inspect = inspect;
|
|
7
|
-
exports.
|
|
8
|
+
exports.stringify = stringify;
|
|
8
9
|
|
|
9
10
|
var _util = _interopRequireDefault(require("util"));
|
|
10
11
|
|
|
@@ -31,12 +32,15 @@ function stringify(format, config = (0, _defaults.default)()) {
|
|
|
31
32
|
return _yaml.default.stringify(config);
|
|
32
33
|
|
|
33
34
|
case 'json':
|
|
34
|
-
return JSON.stringify(config, null, 2);
|
|
35
|
+
return JSON.stringify(config, null, 2) + '\n';
|
|
35
36
|
|
|
36
37
|
case 'js':
|
|
37
|
-
return `module.exports = ${inspect(config)}`;
|
|
38
|
+
return `module.exports = ${inspect(config)}\n`;
|
|
38
39
|
|
|
39
40
|
default:
|
|
40
41
|
throw new Error(`Unsupported format: ${format}`);
|
|
41
42
|
}
|
|
42
|
-
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
var _default = stringify;
|
|
46
|
+
exports.default = _default;
|
package/dist/utils.js
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.del = del;
|
|
7
|
+
exports.filterEmpty = filterEmpty;
|
|
8
|
+
exports.get = get;
|
|
9
|
+
exports.isArrayKey = isArrayKey;
|
|
10
|
+
exports.joinPropertyPath = joinPropertyPath;
|
|
11
|
+
exports.map = map;
|
|
12
|
+
exports.merge = merge;
|
|
13
|
+
exports.parsePropertyPath = parsePropertyPath;
|
|
14
|
+
exports.set = set;
|
|
15
|
+
const {
|
|
16
|
+
isArray
|
|
17
|
+
} = Array;
|
|
18
|
+
const {
|
|
19
|
+
isInteger
|
|
20
|
+
} = Number;
|
|
21
|
+
const {
|
|
22
|
+
entries
|
|
23
|
+
} = Object; // Creates an empty object or array
|
|
24
|
+
|
|
25
|
+
function create(array) {
|
|
26
|
+
return array ? [] : {};
|
|
27
|
+
} // Returns true or false if a subject has iterable keys or not
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
function hasKeys(subj) {
|
|
31
|
+
return isArray(subj) || Object.getPrototypeOf(subj) === Object.prototype;
|
|
32
|
+
} // Returns true if the provided key looks like an array key
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
const ARRAY_PATH_KEY_REG = /^(\[\d+]|0|[1-9]\d*)$/;
|
|
36
|
+
|
|
37
|
+
function isArrayKey(key) {
|
|
38
|
+
return isInteger(key) || ARRAY_PATH_KEY_REG.test(key);
|
|
39
|
+
} // Split a property path string by dot or array notation
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
function parsePropertyPath(path) {
|
|
43
|
+
return isArray(path) ? path : path.split('.').reduce((full, part) => {
|
|
44
|
+
return full.concat(part.split('[').reduce((f, p) => {
|
|
45
|
+
if (p.endsWith(']')) p = p.slice(0, -1);
|
|
46
|
+
return f.concat(isArrayKey(p) ? parseInt(p, 10) : p);
|
|
47
|
+
}, []));
|
|
48
|
+
}, []);
|
|
49
|
+
} // Join an array of path parts into a single path string
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
function joinPropertyPath(path) {
|
|
53
|
+
if (typeof path === 'string') return path;
|
|
54
|
+
let joined = path.map(k => isArrayKey(k) ? `[${k}]` : `.${k}`).join('');
|
|
55
|
+
if (joined.startsWith('.')) return joined.substr(1);
|
|
56
|
+
return joined;
|
|
57
|
+
} // Gets a value in the object at the path
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
function get(object, path, find) {
|
|
61
|
+
return parsePropertyPath(path).reduce((target, key) => target === null || target === void 0 ? void 0 : target[key], object);
|
|
62
|
+
} // Sets a value to the object at the path creating any necessary nested
|
|
63
|
+
// objects or arrays along the way
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
function set(object, path, value) {
|
|
67
|
+
return parsePropertyPath(path).reduce((target, key, index, path) => {
|
|
68
|
+
if (index < path.length - 1) {
|
|
69
|
+
var _target$key;
|
|
70
|
+
|
|
71
|
+
(_target$key = target[key]) !== null && _target$key !== void 0 ? _target$key : target[key] = create(isArrayKey(path[index + 1]));
|
|
72
|
+
return target[key];
|
|
73
|
+
} else {
|
|
74
|
+
target[key] = value;
|
|
75
|
+
return object;
|
|
76
|
+
}
|
|
77
|
+
}, object);
|
|
78
|
+
} // Deletes properties from an object at the paths
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
function del(object, ...paths) {
|
|
82
|
+
return paths.reduce((object, path) => {
|
|
83
|
+
return parsePropertyPath(path).reduce((target, key, index, path) => {
|
|
84
|
+
if (index < path.length - 1) {
|
|
85
|
+
return target === null || target === void 0 ? void 0 : target[key];
|
|
86
|
+
} else {
|
|
87
|
+
target === null || target === void 0 ? true : delete target[key];
|
|
88
|
+
return object;
|
|
89
|
+
}
|
|
90
|
+
}, object);
|
|
91
|
+
}, object);
|
|
92
|
+
} // Maps a value from one path to another, deleting the first path
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
function map(object, from, to, transform = v => v) {
|
|
96
|
+
return set(object, to, transform(parsePropertyPath(from).reduce((target, key, index, path) => {
|
|
97
|
+
let value = target === null || target === void 0 ? void 0 : target[key];
|
|
98
|
+
|
|
99
|
+
if (index === path.length - 1) {
|
|
100
|
+
target === null || target === void 0 ? true : delete target[key];
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return value;
|
|
104
|
+
}, object)));
|
|
105
|
+
} // Steps through an object's properties calling the function with the path and value of each
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
function walk(object, fn, path = []) {
|
|
109
|
+
if (path.length && fn([...path], object) === false) return;
|
|
110
|
+
|
|
111
|
+
if (object != null && typeof object === 'object') {
|
|
112
|
+
let isArrayObject = isArray(object);
|
|
113
|
+
|
|
114
|
+
for (let [key, value] of entries(object)) {
|
|
115
|
+
if (isArrayObject) key = parseInt(key, 10);
|
|
116
|
+
walk(value, fn, [...path, key]);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
} // Merges source values and returns a new merged value. The map function will be called with a
|
|
120
|
+
// property's path, previous value, and next value; it should return an array containing any
|
|
121
|
+
// replacement path and value; when a replacement value not defined, values will be merged.
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
function merge(sources, map) {
|
|
125
|
+
return sources.reduce((target, source, i) => {
|
|
126
|
+
let isSourceArray = isArray(source);
|
|
127
|
+
walk(source, (path, value) => {
|
|
128
|
+
var _ctx;
|
|
129
|
+
|
|
130
|
+
let ctx = get(target, path.slice(0, -1));
|
|
131
|
+
let key = path[path.length - 1];
|
|
132
|
+
let prev = (_ctx = ctx) === null || _ctx === void 0 ? void 0 : _ctx[key]; // maybe map the property path and/or value
|
|
133
|
+
|
|
134
|
+
let [mapped, next] = (map === null || map === void 0 ? void 0 : map(path, prev, value)) || []; // update the context and path if changed
|
|
135
|
+
|
|
136
|
+
if (mapped !== null && mapped !== void 0 && mapped.some((m, i) => m !== path[i])) {
|
|
137
|
+
ctx = get(target, mapped.slice(0, -1));
|
|
138
|
+
path = [...mapped];
|
|
139
|
+
} // adjust path to concat array values when necessary
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
if (next !== null && (isArray(ctx) || isInteger(key))) {
|
|
143
|
+
var _ctx$length, _ctx2;
|
|
144
|
+
|
|
145
|
+
path.splice(-1, 1, (_ctx$length = (_ctx2 = ctx) === null || _ctx2 === void 0 ? void 0 : _ctx2.length) !== null && _ctx$length !== void 0 ? _ctx$length : 0);
|
|
146
|
+
} // delete prev values
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
if (next === null || next == null && value === null) {
|
|
150
|
+
del(target, path);
|
|
151
|
+
} // set the next or default value if there is one
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
if (next != null || next !== null && value != null && !hasKeys(value)) {
|
|
155
|
+
var _target;
|
|
156
|
+
|
|
157
|
+
set((_target = target) !== null && _target !== void 0 ? _target : target = create(isSourceArray), path, next !== null && next !== void 0 ? next : value);
|
|
158
|
+
} // do not recurse mapped objects
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
return next === undefined;
|
|
162
|
+
});
|
|
163
|
+
return target;
|
|
164
|
+
}, undefined);
|
|
165
|
+
} // Recursively mutate and filter empty values from arrays and objects
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
function filterEmpty(subject) {
|
|
169
|
+
if (typeof subject === 'object') {
|
|
170
|
+
if (isArray(subject)) {
|
|
171
|
+
for (let i = 0; i < subject.length; i++) {
|
|
172
|
+
if (!filterEmpty(subject[i])) {
|
|
173
|
+
subject.splice(i--, 1);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return subject.length > 0;
|
|
178
|
+
} else {
|
|
179
|
+
for (let k in subject) {
|
|
180
|
+
if (!filterEmpty(subject[k])) {
|
|
181
|
+
delete subject[k];
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return entries(subject).length > 0;
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
return subject != null;
|
|
189
|
+
}
|
|
190
|
+
}
|
package/dist/validate.js
CHANGED
|
@@ -3,32 +3,77 @@
|
|
|
3
3
|
Object.defineProperty(exports, "__esModule", {
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
|
-
exports.getSchema = getSchema;
|
|
7
6
|
exports.addSchema = addSchema;
|
|
7
|
+
exports.default = void 0;
|
|
8
|
+
exports.getSchema = getSchema;
|
|
8
9
|
exports.resetSchema = resetSchema;
|
|
9
|
-
exports.
|
|
10
|
+
exports.validate = validate;
|
|
10
11
|
|
|
11
12
|
var _ajv = _interopRequireDefault(require("ajv"));
|
|
12
13
|
|
|
13
|
-
var
|
|
14
|
+
var _utils = require("./utils");
|
|
14
15
|
|
|
15
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
16
17
|
|
|
18
|
+
const {
|
|
19
|
+
isArray
|
|
20
|
+
} = Array;
|
|
17
21
|
const {
|
|
18
22
|
assign,
|
|
19
23
|
entries
|
|
20
|
-
} = Object; //
|
|
24
|
+
} = Object; // AJV manages and validates schemas.
|
|
21
25
|
|
|
22
26
|
const ajv = new _ajv.default({
|
|
27
|
+
strict: false,
|
|
23
28
|
verbose: true,
|
|
24
29
|
allErrors: true,
|
|
25
|
-
schemas:
|
|
26
|
-
|
|
27
|
-
|
|
30
|
+
schemas: [getDefaultSchema()],
|
|
31
|
+
keywords: [{
|
|
32
|
+
// custom instanceof schema validation
|
|
33
|
+
keyword: 'instanceof',
|
|
34
|
+
metaSchema: {
|
|
35
|
+
enum: ['Function', 'RegExp']
|
|
36
|
+
},
|
|
37
|
+
error: {
|
|
38
|
+
message: cxt => _ajv.default.str`must be an instanceof ${cxt.schemaCode}`,
|
|
39
|
+
params: cxt => _ajv.default._`{ instanceof: ${cxt.schemaCode} }`
|
|
40
|
+
},
|
|
41
|
+
code: cxt => cxt.fail(_ajv.default._`!(${cxt.data} instanceof ${_ajv.default._([cxt.schema])})`)
|
|
42
|
+
}, {
|
|
43
|
+
// disallowed validation based on required
|
|
44
|
+
keyword: 'disallowed',
|
|
45
|
+
metaSchema: {
|
|
46
|
+
type: 'array',
|
|
47
|
+
items: {
|
|
48
|
+
type: 'string'
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
error: {
|
|
52
|
+
message: 'disallowed property',
|
|
53
|
+
params: cxt => _ajv.default._`{ disallowedProperty: ${cxt.params.disallowedProperty} }`
|
|
54
|
+
},
|
|
55
|
+
code: cxt => {
|
|
56
|
+
let {
|
|
57
|
+
data,
|
|
58
|
+
gen,
|
|
59
|
+
schema
|
|
60
|
+
} = cxt;
|
|
61
|
+
|
|
62
|
+
for (let prop of schema) {
|
|
63
|
+
gen.if(_ajv.default._`${data}.${_ajv.default._([prop])} !== undefined`, () => {
|
|
64
|
+
cxt.setParams({
|
|
65
|
+
disallowedProperty: _ajv.default._`${prop}`
|
|
66
|
+
}, true);
|
|
67
|
+
cxt.error();
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}]
|
|
28
72
|
}); // Returns a new default schema.
|
|
29
73
|
|
|
30
74
|
function getDefaultSchema() {
|
|
31
75
|
return {
|
|
76
|
+
$id: '/config',
|
|
32
77
|
type: 'object',
|
|
33
78
|
additionalProperties: false,
|
|
34
79
|
properties: {
|
|
@@ -38,84 +83,174 @@ function getDefaultSchema() {
|
|
|
38
83
|
}
|
|
39
84
|
}
|
|
40
85
|
};
|
|
41
|
-
} // Gets the schema object from the AJV schema.
|
|
86
|
+
} // Gets the schema object from the AJV schema. If a path is provided, an array of schemas is
|
|
87
|
+
// returned, with each index representing the schema of each part of the path (index zero is root).
|
|
88
|
+
|
|
42
89
|
|
|
90
|
+
function getSchema(name, path, root) {
|
|
91
|
+
var _ajv$getSchema, _schema, _schema2, _schema3, _schema4;
|
|
92
|
+
|
|
93
|
+
// get the root schema if necessary, resolve it, and return it when there is no path
|
|
94
|
+
let schema = typeof name === 'string' ? (_ajv$getSchema = ajv.getSchema(name)) === null || _ajv$getSchema === void 0 ? void 0 : _ajv$getSchema.schema : name;
|
|
95
|
+
|
|
96
|
+
if ((_schema = schema) !== null && _schema !== void 0 && _schema.$ref) {
|
|
97
|
+
let [name, ref = ''] = schema.$ref.split('#');
|
|
98
|
+
schema = (0, _utils.get)(getSchema(name || root), ref.split('/').slice(1));
|
|
99
|
+
}
|
|
43
100
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
101
|
+
if (!path) return schema; // destructure the path and set the default root schema for future refs
|
|
102
|
+
|
|
103
|
+
let [key, ...rest] = path = (0, _utils.parsePropertyPath)(path);
|
|
104
|
+
root || (root = schema); // if the desired schema is one of many, we need to find one that best matches
|
|
105
|
+
|
|
106
|
+
let many = isArray(schema) ? schema : (_schema2 = schema) === null || _schema2 === void 0 ? void 0 : _schema2[['anyOf', 'oneOf', 'allOf'].find(p => schema[p])];
|
|
107
|
+
|
|
108
|
+
if (many) {
|
|
109
|
+
let isLooseMatch = s => (s === null || s === void 0 ? void 0 : s.type) === 'object' && s.additionalProperties !== false; // the best possible match will match most of the path or loosely match
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
return many.map(p => getSchema(p, path, root)).sort((a, b) => b.length - a.length || (isLooseMatch(a[0]) ? -1 : 1))[0];
|
|
113
|
+
} else if ((0, _utils.isArrayKey)(key) && ((_schema3 = schema) === null || _schema3 === void 0 ? void 0 : _schema3.type) === 'array') {
|
|
114
|
+
// find the remaining paths in the items schema
|
|
115
|
+
return [schema].concat(getSchema(schema.items, rest, root));
|
|
116
|
+
} else if (path.length && ((_schema4 = schema) === null || _schema4 === void 0 ? void 0 : _schema4.type) === 'object') {
|
|
117
|
+
var _schema$properties;
|
|
118
|
+
|
|
119
|
+
// find the remaining paths nested in the property schema
|
|
120
|
+
return [schema].concat(getSchema((_schema$properties = schema.properties) === null || _schema$properties === void 0 ? void 0 : _schema$properties[key], rest, root));
|
|
121
|
+
} else if (!path.length && schema) {
|
|
122
|
+
// end of path matching
|
|
123
|
+
return [schema];
|
|
124
|
+
} else {
|
|
125
|
+
// no match
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
} // Adds schemas to the config schema's properties. The config schema is removed, modified, and
|
|
129
|
+
// replaced after the new schemas are added to clear any compiled caches. Existing schemas are
|
|
130
|
+
// removed and replaced as well. If a schema has an existing $id, the schema will not be added
|
|
131
|
+
// as config schema properties.
|
|
49
132
|
|
|
50
133
|
|
|
51
134
|
function addSchema(schemas) {
|
|
52
|
-
|
|
53
|
-
|
|
135
|
+
if (isArray(schemas)) {
|
|
136
|
+
return schemas.map(addSchema);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (schemas.$id) {
|
|
140
|
+
let {
|
|
141
|
+
$id
|
|
142
|
+
} = schemas;
|
|
143
|
+
if (ajv.getSchema($id)) ajv.removeSchema($id);
|
|
144
|
+
return ajv.addSchema(schemas);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
let config = getSchema('/config');
|
|
148
|
+
ajv.removeSchema('/config');
|
|
54
149
|
|
|
55
|
-
for (let [
|
|
150
|
+
for (let [key, schema] of entries(schemas)) {
|
|
151
|
+
let $id = `/config/${key}`;
|
|
56
152
|
if (ajv.getSchema($id)) ajv.removeSchema($id);
|
|
57
153
|
assign(config.properties, {
|
|
58
|
-
[
|
|
154
|
+
[key]: {
|
|
59
155
|
$ref: $id
|
|
60
156
|
}
|
|
61
157
|
});
|
|
62
158
|
ajv.addSchema(schema, $id);
|
|
63
159
|
}
|
|
64
160
|
|
|
65
|
-
ajv.addSchema(config, 'config');
|
|
161
|
+
return ajv.addSchema(config, '/config');
|
|
66
162
|
} // Resets the schema by removing all schemas and inserting a new default schema.
|
|
67
163
|
|
|
68
164
|
|
|
69
165
|
function resetSchema() {
|
|
70
166
|
ajv.removeSchema();
|
|
71
|
-
ajv.addSchema(getDefaultSchema(), 'config');
|
|
72
|
-
} //
|
|
73
|
-
|
|
74
|
-
|
|
167
|
+
ajv.addSchema(getDefaultSchema(), '/config');
|
|
168
|
+
} // Adds "a" or "an" to a word for readability.
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
function a(word) {
|
|
172
|
+
if (word === 'undefined' || word === 'null') return word;
|
|
173
|
+
return `${'aeiou'.includes(word[0]) ? 'an' : 'a'} ${word}`;
|
|
174
|
+
} // Default errors anywhere within these keywords can be confusing
|
|
175
|
+
|
|
75
176
|
|
|
177
|
+
const HIDE_NESTED_KEYWORDS = ['oneOf', 'anyOf', 'allOf', 'not'];
|
|
76
178
|
|
|
77
|
-
function
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
179
|
+
function shouldHideError({
|
|
180
|
+
parentSchema,
|
|
181
|
+
keyword,
|
|
182
|
+
schemaPath
|
|
183
|
+
}) {
|
|
184
|
+
var _parentSchema$errors;
|
|
81
185
|
|
|
82
|
-
|
|
83
|
-
|
|
186
|
+
return !(parentSchema.error || (_parentSchema$errors = parentSchema.errors) !== null && _parentSchema$errors !== void 0 && _parentSchema$errors[keyword]) && HIDE_NESTED_KEYWORDS.some(k => schemaPath.includes(`/${k}`));
|
|
187
|
+
} // Validates data according to the associated schema and returns a list of errors, if any.
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
function validate(data, key = '/config') {
|
|
191
|
+
if (!ajv.validate(key, data)) {
|
|
192
|
+
let errors = new Map();
|
|
84
193
|
|
|
85
194
|
for (let error of ajv.errors) {
|
|
195
|
+
var _parentSchema$errors2;
|
|
196
|
+
|
|
197
|
+
if (shouldHideError(error)) continue;
|
|
86
198
|
let {
|
|
87
|
-
|
|
199
|
+
instancePath,
|
|
200
|
+
parentSchema,
|
|
88
201
|
keyword,
|
|
89
|
-
params,
|
|
90
202
|
message,
|
|
91
|
-
|
|
203
|
+
params
|
|
92
204
|
} = error;
|
|
93
|
-
let
|
|
205
|
+
let path = instancePath ? instancePath.substr(1).split('/') : []; // generate a custom error message
|
|
94
206
|
|
|
95
|
-
if (
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
pre = pre ? `${pre}has ` : '';
|
|
99
|
-
message = `unknown property '${params.additionalProperty}'`;
|
|
100
|
-
if (scrub) delete data[params.additionalProperty];
|
|
207
|
+
if (parentSchema.error || (_parentSchema$errors2 = parentSchema.errors) !== null && _parentSchema$errors2 !== void 0 && _parentSchema$errors2[keyword]) {
|
|
208
|
+
let custom = parentSchema.error || parentSchema.errors[keyword];
|
|
209
|
+
message = typeof custom === 'function' ? custom(error) : custom;
|
|
101
210
|
} else if (keyword === 'type') {
|
|
102
|
-
let dataType =
|
|
103
|
-
message = `
|
|
211
|
+
let dataType = error.data === null ? 'null' : isArray(error.data) ? 'array' : typeof error.data;
|
|
212
|
+
message = `must be ${a(params.type)}, received ${a(dataType)}`;
|
|
213
|
+
} else if (keyword === 'required') {
|
|
214
|
+
message = 'missing required property';
|
|
215
|
+
} else if (keyword === 'additionalProperties') {
|
|
216
|
+
message = 'unknown property';
|
|
217
|
+
} // fix paths
|
|
104
218
|
|
|
105
|
-
if (scrub) {
|
|
106
|
-
let [key, ...path] = dataPath.substr(1).split('.').reverse();
|
|
107
|
-
delete path.reduceRight((d, k) => d[k], config)[key];
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
219
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
220
|
+
if (params.missingProperty) {
|
|
221
|
+
path.push(params.missingProperty);
|
|
222
|
+
} else if (params.additionalProperty) {
|
|
223
|
+
path.push(params.additionalProperty);
|
|
224
|
+
} else if (params.disallowedProperty) {
|
|
225
|
+
path.push(params.disallowedProperty);
|
|
226
|
+
} // fix invalid data
|
|
114
227
|
|
|
115
|
-
return result;
|
|
116
|
-
} // Adds "a" or "an" to a word for readability.
|
|
117
228
|
|
|
229
|
+
if (keyword === 'minimum') {
|
|
230
|
+
(0, _utils.set)(data, path, Math.max(error.data, error.schema));
|
|
231
|
+
} else if (keyword === 'maximum') {
|
|
232
|
+
(0, _utils.set)(data, path, Math.min(error.data, error.schema));
|
|
233
|
+
} else if (keyword === 'required') {
|
|
234
|
+
(0, _utils.del)(data, path.slice(0, -1));
|
|
235
|
+
} else {
|
|
236
|
+
(0, _utils.del)(data, path);
|
|
237
|
+
} // joined for error messages
|
|
118
238
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
239
|
+
|
|
240
|
+
path = (0, _utils.joinPropertyPath)(path); // map one error per path
|
|
241
|
+
|
|
242
|
+
errors.set(path, {
|
|
243
|
+
path,
|
|
244
|
+
message
|
|
245
|
+
});
|
|
246
|
+
} // filter empty values as a result of scrubbing
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
(0, _utils.filterEmpty)(data); // return an array of errors
|
|
250
|
+
|
|
251
|
+
return Array.from(errors.values());
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
var _default = validate;
|
|
256
|
+
exports.default = _default;
|
package/package.json
CHANGED
|
@@ -1,36 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@percy/config",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.73",
|
|
4
4
|
"license": "MIT",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/percy/cli",
|
|
8
|
+
"directory": "packages/config"
|
|
9
|
+
},
|
|
10
|
+
"publishConfig": {
|
|
11
|
+
"access": "public"
|
|
12
|
+
},
|
|
5
13
|
"main": "dist/index.js",
|
|
6
14
|
"types": "types/index.d.ts",
|
|
7
15
|
"files": [
|
|
8
16
|
"dist",
|
|
9
17
|
"types/index.d.ts"
|
|
10
18
|
],
|
|
19
|
+
"engines": {
|
|
20
|
+
"node": ">=12"
|
|
21
|
+
},
|
|
11
22
|
"scripts": {
|
|
12
|
-
"build": "
|
|
23
|
+
"build": "node ../../scripts/build",
|
|
13
24
|
"lint": "eslint --ignore-path ../../.gitignore .",
|
|
14
|
-
"test": "
|
|
15
|
-
"test:coverage": "
|
|
25
|
+
"test": "node ../../scripts/test",
|
|
26
|
+
"test:coverage": "yarn test --coverage",
|
|
16
27
|
"test:types": "tsd"
|
|
17
28
|
},
|
|
18
|
-
"publishConfig": {
|
|
19
|
-
"access": "public"
|
|
20
|
-
},
|
|
21
|
-
"mocha": {
|
|
22
|
-
"require": "../../scripts/babel-register"
|
|
23
|
-
},
|
|
24
29
|
"dependencies": {
|
|
25
|
-
"@percy/logger": "
|
|
26
|
-
"ajv": "^6.
|
|
30
|
+
"@percy/logger": "1.0.0-beta.73",
|
|
31
|
+
"ajv": "^8.6.2",
|
|
27
32
|
"cosmiconfig": "^7.0.0",
|
|
28
|
-
"
|
|
29
|
-
"path-type": "^4.0.0",
|
|
30
|
-
"yaml": "^1.8.0"
|
|
33
|
+
"yaml": "^1.10.0"
|
|
31
34
|
},
|
|
32
35
|
"devDependencies": {
|
|
33
36
|
"json-schema-typed": "^7.0.3"
|
|
34
37
|
},
|
|
35
|
-
"gitHead": "
|
|
38
|
+
"gitHead": "aa8160e02bea3e04ab1d3605762f89fbe79605d4"
|
|
36
39
|
}
|