@percy/config 1.0.0-beta.9 → 1.0.0

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 CHANGED
@@ -1,13 +1,14 @@
1
- ## @percy/config
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
- ## Usage
8
+ - [Loading config files](#loading-config-files)
9
+ - [Extending config options](#extending-config-options)
9
10
 
10
- ### Loading config files
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
- ### Extending config options
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({ propertyName: JSONSchema })
50
+ PercyConfig.addSchema({
51
+ propertyName: JSONSchema
52
+ })
47
53
  ```
package/package.json CHANGED
@@ -1,36 +1,45 @@
1
1
  {
2
2
  "name": "@percy/config",
3
- "version": "1.0.0-beta.9",
3
+ "version": "1.0.0",
4
4
  "license": "MIT",
5
- "main": "dist/index.js",
6
- "types": "types/index.d.ts",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "https://github.com/percy/cli",
8
+ "directory": "packages/config"
9
+ },
10
+ "publishConfig": {
11
+ "access": "public"
12
+ },
13
+ "engines": {
14
+ "node": ">=14"
15
+ },
7
16
  "files": [
8
- "dist",
9
- "types/index.d.ts"
17
+ "./dist",
18
+ "./types/index.d.ts"
10
19
  ],
20
+ "main": "./dist/index.js",
21
+ "types": "./types/index.d.ts",
22
+ "type": "module",
23
+ "exports": {
24
+ ".": "./dist/index.js",
25
+ "./utils": "./dist/utils/index.js",
26
+ "./test/helpers": "./test/helpers.js"
27
+ },
11
28
  "scripts": {
12
- "build": "babel --root-mode upward src --out-dir dist",
29
+ "build": "node ../../scripts/build",
13
30
  "lint": "eslint --ignore-path ../../.gitignore .",
14
- "test": "cross-env NODE_ENV=test mocha",
15
- "test:coverage": "nyc yarn test",
31
+ "test": "node ../../scripts/test",
32
+ "test:coverage": "yarn test --coverage",
16
33
  "test:types": "tsd"
17
34
  },
18
- "publishConfig": {
19
- "access": "public"
20
- },
21
- "mocha": {
22
- "require": "../../scripts/babel-register"
23
- },
24
35
  "dependencies": {
25
- "@percy/logger": "^1.0.0-beta.9",
26
- "ajv": "^6.12.0",
36
+ "@percy/logger": "1.0.0",
37
+ "ajv": "^8.6.2",
27
38
  "cosmiconfig": "^7.0.0",
28
- "deepmerge": "^4.2.2",
29
- "path-type": "^4.0.0",
30
- "yaml": "^1.8.0"
39
+ "yaml": "^1.10.0"
31
40
  },
32
41
  "devDependencies": {
33
42
  "json-schema-typed": "^7.0.3"
34
43
  },
35
- "gitHead": "57a2eeb90c7f5cdf8827c78be1e5c12df581f4b5"
44
+ "gitHead": "6df509421a60144e4f9f5d59dc57a5675372a0b2"
36
45
  }
package/dist/defaults.js DELETED
@@ -1,48 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = getDefaults;
7
-
8
- var _deepmerge = _interopRequireDefault(require("deepmerge"));
9
-
10
- var _validate = require("./validate");
11
-
12
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
13
-
14
- const {
15
- assign,
16
- entries,
17
- freeze
18
- } = Object; // Recursively walks a schema and collects defaults. When no schema is provided,
19
- // the default config schema is used. Returned defaults are frozen.
20
-
21
- function getDefaultFromSchema(schema) {
22
- if (!schema || typeof schema.$ref === 'string') {
23
- var _schema$$ref;
24
-
25
- // get the schema from ajv
26
- return getDefaultFromSchema((0, _validate.getSchema)((_schema$$ref = schema === null || schema === void 0 ? void 0 : schema.$ref) !== null && _schema$$ref !== void 0 ? _schema$$ref : 'config'));
27
- } else if (schema.default != null) {
28
- // return the frozen default for this schema
29
- return freeze(schema.default);
30
- } else if (schema.type === 'object' && schema.properties) {
31
- // return a frozen object of default properties
32
- return freeze(entries(schema.properties).reduce((acc, [prop, schema]) => {
33
- let def = getDefaultFromSchema(schema);
34
- return def != null ? assign(acc || {}, {
35
- [prop]: def
36
- }) : acc;
37
- }, undefined));
38
- } else {
39
- return undefined;
40
- }
41
- }
42
-
43
- function getDefaults(overrides = {}) {
44
- return _deepmerge.default.all([getDefaultFromSchema(), overrides], {
45
- // overwrite default arrays, do not merge
46
- arrayMerge: (_, arr) => arr
47
- });
48
- }
package/dist/index.js DELETED
@@ -1,33 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = void 0;
7
-
8
- var _load = _interopRequireWildcard(require("./load"));
9
-
10
- var _validate = _interopRequireWildcard(require("./validate"));
11
-
12
- var _defaults = _interopRequireDefault(require("./defaults"));
13
-
14
- var _stringify = _interopRequireDefault(require("./stringify"));
15
-
16
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
17
-
18
- function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
19
-
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; }
21
-
22
- // Export a single object that can be imported as PercyConfig
23
- var _default = {
24
- load: _load.default,
25
- cache: _load.cache,
26
- explorer: _load.explorer,
27
- validate: _validate.default,
28
- addSchema: _validate.addSchema,
29
- resetSchema: _validate.resetSchema,
30
- getDefaults: _defaults.default,
31
- stringify: _stringify.default
32
- };
33
- exports.default = _default;
package/dist/load.js DELETED
@@ -1,92 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = load;
7
- exports.explorer = exports.cache = void 0;
8
-
9
- var _path = require("path");
10
-
11
- var _cosmiconfig = require("cosmiconfig");
12
-
13
- var _pathType = require("path-type");
14
-
15
- var _deepmerge = _interopRequireDefault(require("deepmerge"));
16
-
17
- var _logger = _interopRequireDefault(require("@percy/logger"));
18
-
19
- var _defaults = _interopRequireDefault(require("./defaults"));
20
-
21
- var _normalize = _interopRequireDefault(require("./normalize"));
22
-
23
- var _validate = _interopRequireDefault(require("./validate"));
24
-
25
- var _stringify = require("./stringify");
26
-
27
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
28
-
29
- // Loaded configuration file cache
30
- const cache = new Map(); // The cosmiconfig explorer used to load config files
31
-
32
- exports.cache = cache;
33
- const explorer = (0, _cosmiconfig.cosmiconfigSync)('percy', {
34
- cache: false,
35
- searchPlaces: ['package.json', '.percyrc', '.percy.json', '.percy.yaml', '.percy.yml', '.percy.js', 'percy.config.js']
36
- }); // Finds and loads a config file using cosmiconfig, merges it with optional
37
- // inputs, validates the combined config according to the schema, and returns
38
- // the combined config. Loaded config files are cached and reused on next load,
39
- // unless `reload` is true in which the file will be reloaded and the cache
40
- // updated. Validation errors are logged as warnings and the config is returned
41
- // unless `bail` is true. Supports kebab-case and camelCase config options and
42
- // always returns camelCase options. Currently only supports version 2 config
43
- // files; missing versions or other versions are discarded.
44
-
45
- exports.explorer = explorer;
46
-
47
- function load({
48
- path,
49
- overrides = {},
50
- reload = false,
51
- bail = false
52
- } = {}) {
53
- var _Array$from;
54
-
55
- // 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]; // load config or reload cached config
57
-
58
- if (path !== false && (!config || reload)) {
59
- try {
60
- let result = !path || (0, _pathType.isDirectorySync)(path) ? explorer.search(path) : explorer.load(path);
61
-
62
- if (result && result.config) {
63
- _logger.default.debug(`Found config file: ${(0, _path.relative)('', result.filepath)}`);
64
-
65
- if (result.config.version !== 2) {
66
- _logger.default.warn('Ignoring config file - ' + (!result.config.version ? 'missing version' : 'unsupported version'));
67
- } else {
68
- // normalize to remove empty values and convert snake-case to camelCase
69
- config = (0, _normalize.default)(result.config);
70
- cache.set(path, config);
71
- }
72
- } else {
73
- _logger.default.debug('Config file not found');
74
- }
75
- } catch (error) {
76
- _logger.default.debug('Failed to load or parse config file');
77
-
78
- _logger.default.debug(error);
79
- }
80
- } // merge found config with overrides and validate
81
-
82
-
83
- config = (0, _deepmerge.default)(config || {}, overrides);
84
- if (!(0, _validate.default)(config, {
85
- scrub: true
86
- }) && bail) return; // normalize again to remove empty values from overrides and validation scrubbing
87
-
88
- config = (0, _normalize.default)(config);
89
- if (config) _logger.default.debug(`Using config:\n${(0, _stringify.inspect)(config)}`); // merge with defaults
90
-
91
- return (0, _defaults.default)(config);
92
- }
package/dist/normalize.js DELETED
@@ -1,37 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.default = normalize;
7
-
8
- // recursively reduces config objects and arrays to remove undefined and empty
9
- // values and rename kebab-case properties to camelCase.
10
- function normalize(subject) {
11
- if (typeof subject === 'object') {
12
- let isArray = Array.isArray(subject);
13
- return Object.entries(subject).reduce((result, [key, value]) => {
14
- value = normalize(value);
15
-
16
- if (typeof value !== 'undefined') {
17
- return isArray ? (result || []).concat(value) : Object.assign(result || {}, {
18
- [camelize(key)]: value
19
- });
20
- } else {
21
- return result;
22
- }
23
- }, undefined);
24
- } else {
25
- return subject;
26
- }
27
- } // Edge case camelizations
28
-
29
-
30
- const CAMELIZE_MAP = {
31
- css: 'CSS',
32
- javascript: 'JavaScript'
33
- }; // Converts a kebab-cased string to camelCase.
34
-
35
- function camelize(s) {
36
- return s.replace(/-([^-]+)/g, (_, w) => CAMELIZE_MAP[w] || w[0].toUpperCase() + w.slice(1));
37
- }
package/dist/stringify.js DELETED
@@ -1,42 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.inspect = inspect;
7
- exports.default = stringify;
8
-
9
- var _util = _interopRequireDefault(require("util"));
10
-
11
- var _yaml = _interopRequireDefault(require("yaml"));
12
-
13
- var _defaults = _interopRequireDefault(require("./defaults"));
14
-
15
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
-
17
- // Provides native util.inspect with common options for printing configs.
18
- function inspect(config) {
19
- return _util.default.inspect(config, {
20
- depth: null,
21
- compact: false
22
- });
23
- } // Converts a config to a yaml, json, or js string. When no config is provided,
24
- // falls back to schema defaults.
25
-
26
-
27
- function stringify(format, config = (0, _defaults.default)()) {
28
- switch (format) {
29
- case 'yml':
30
- case 'yaml':
31
- return _yaml.default.stringify(config);
32
-
33
- case 'json':
34
- return JSON.stringify(config, null, 2);
35
-
36
- case 'js':
37
- return `module.exports = ${inspect(config)}`;
38
-
39
- default:
40
- throw new Error(`Unsupported format: ${format}`);
41
- }
42
- }
package/dist/validate.js DELETED
@@ -1,121 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.getSchema = getSchema;
7
- exports.addSchema = addSchema;
8
- exports.resetSchema = resetSchema;
9
- exports.default = validate;
10
-
11
- var _ajv = _interopRequireDefault(require("ajv"));
12
-
13
- var _logger = _interopRequireDefault(require("@percy/logger"));
14
-
15
- function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
-
17
- const {
18
- assign,
19
- entries
20
- } = Object; // Ajv manages and validates schemas.
21
-
22
- const ajv = new _ajv.default({
23
- verbose: true,
24
- allErrors: true,
25
- schemas: {
26
- config: getDefaultSchema()
27
- }
28
- }); // Returns a new default schema.
29
-
30
- function getDefaultSchema() {
31
- return {
32
- type: 'object',
33
- additionalProperties: false,
34
- properties: {
35
- version: {
36
- type: 'integer',
37
- default: 2
38
- }
39
- }
40
- };
41
- } // Gets the schema object from the AJV schema.
42
-
43
-
44
- function getSchema(name) {
45
- return ajv.getSchema(name).schema;
46
- } // Adds schemas to the config schema's properties. The config schema is removed,
47
- // modified, and replaced after the new schemas are added to clear any compiled
48
- // caches. Existing schemas are removed and replaced as well.
49
-
50
-
51
- function addSchema(schemas) {
52
- let config = getSchema('config');
53
- ajv.removeSchema('config');
54
-
55
- for (let [$id, schema] of entries(schemas)) {
56
- if (ajv.getSchema($id)) ajv.removeSchema($id);
57
- assign(config.properties, {
58
- [$id]: {
59
- $ref: $id
60
- }
61
- });
62
- ajv.addSchema(schema, $id);
63
- }
64
-
65
- ajv.addSchema(config, 'config');
66
- } // Resets the schema by removing all schemas and inserting a new default schema.
67
-
68
-
69
- function resetSchema() {
70
- ajv.removeSchema();
71
- ajv.addSchema(getDefaultSchema(), 'config');
72
- } // Validates config data according to the config schema and logs warnings to the
73
- // console. Optionallly scrubs invalid values from the provided config. Returns
74
- // true when the validation success, false otherwise.
75
-
76
-
77
- function validate(config, {
78
- scrub
79
- } = {}) {
80
- let result = ajv.validate('config', config);
81
-
82
- if (!result) {
83
- _logger.default.warn('Invalid config:');
84
-
85
- for (let error of ajv.errors) {
86
- let {
87
- dataPath,
88
- keyword,
89
- params,
90
- message,
91
- data
92
- } = error;
93
- let pre = dataPath ? `'${dataPath.substr(1)}' ` : '';
94
-
95
- if (keyword === 'required') {
96
- message = `is missing required property '${params.missingProperty}'`;
97
- } else if (keyword === 'additionalProperties') {
98
- pre = pre ? `${pre}has ` : '';
99
- message = `unknown property '${params.additionalProperty}'`;
100
- if (scrub) delete data[params.additionalProperty];
101
- } else if (keyword === 'type') {
102
- let dataType = Array.isArray(data) ? 'array' : typeof data;
103
- message = `should be ${a(params.type)}, received ${a(dataType)}`;
104
-
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
-
111
- _logger.default.warn(`- ${pre}${message}`);
112
- }
113
- }
114
-
115
- return result;
116
- } // Adds "a" or "an" to a word for readability.
117
-
118
-
119
- function a(word) {
120
- return `${'aeiou'.includes(word[0]) ? 'an' : 'a'} ${word}`;
121
- }
package/types/index.d.ts DELETED
@@ -1,33 +0,0 @@
1
- import { JSONSchema } from 'json-schema-typed';
2
-
3
- interface Pojo { [x: string]: any; }
4
- export interface PercyConfigObject extends Pojo { version: number; }
5
-
6
- declare namespace PercyConfig {
7
- function load(options?: {
8
- path?: undefined | string | false,
9
- overrides?: Pojo,
10
- reload?: boolean,
11
- bail?: boolean
12
- }): PercyConfigObject;
13
-
14
- function validate(
15
- config: PercyConfigObject,
16
- options?: { scrub?: boolean }
17
- ): boolean;
18
-
19
- function addSchema(schemas: {
20
- [x: string]: JSONSchema
21
- }): void;
22
-
23
- function getDefaults(
24
- overrides?: Pojo
25
- ): PercyConfigObject;
26
-
27
- function stringify(
28
- format: 'yaml' | 'json' | 'js',
29
- config?: PercyConfigObject
30
- ): string;
31
- }
32
-
33
- export default PercyConfig