From 1c714fb998b24cb70a3c228260e0147a0d7ea0ee Mon Sep 17 00:00:00 2001 From: cap-Bernardito Date: Sat, 25 Jul 2020 20:30:59 +0300 Subject: [PATCH] refactor: add schema --- src/index.js | 16 ++- src/options.json | 78 +++++++++- .../validate-options.test.js.snap | 116 +++++++++++++++ test/validate-options.test.js | 135 ++++++++++++++++++ 4 files changed, 339 insertions(+), 6 deletions(-) create mode 100644 test/__snapshots__/validate-options.test.js.snap create mode 100644 test/validate-options.test.js diff --git a/src/index.js b/src/index.js index 713c80d..0e67c0b 100644 --- a/src/index.js +++ b/src/index.js @@ -1,9 +1,17 @@ -const cssnano = require('cssnano'); -const { ModuleFilenameHelpers } = require('webpack'); -const { SourceMapSource, RawSource } = require('webpack-sources'); +import cssnano from 'cssnano'; +import { ModuleFilenameHelpers } from 'webpack'; +import { SourceMapSource, RawSource } from 'webpack-sources'; +import validateOptions from 'schema-utils'; + +import schema from './options.json'; class CssnanoPlugin { - constructor(options) { + constructor(options = {}) { + validateOptions(schema, options, { + name: 'Cssnano webpack plugin', + baseDataPath: 'options', + }); + this.options = Object.assign( { test: /\.css(\?.*)?$/i, diff --git a/src/options.json b/src/options.json index 066dc78..ff042ec 100644 --- a/src/options.json +++ b/src/options.json @@ -1,8 +1,82 @@ { + "definitions": { + "Rule": { + "description": "Filtering rule as regex or string.", + "anyOf": [ + { + "instanceof": "RegExp", + "tsType": "RegExp" + }, + { + "type": "string", + "minLength": 1 + } + ] + }, + "Rules": { + "description": "Filtering rules.", + "anyOf": [ + { + "type": "array", + "items": { + "description": "A rule condition.", + "oneOf": [ + { + "$ref": "#/definitions/Rule" + } + ] + } + }, + { + "$ref": "#/definitions/Rule" + } + ] + } + }, + "title": "CssnanoWebpackPluginOptions", "type": "object", "properties": { - "name": { - "type": "boolean" + "test": { + "description": "Include all modules that pass test assertion.", + "oneOf": [ + { + "$ref": "#/definitions/Rules" + } + ] + }, + "include": { + "description": "Include all modules matching any of these conditions.", + "oneOf": [ + { + "$ref": "#/definitions/Rules" + } + ] + }, + "exclude": { + "description": "Exclude all modules matching any of these conditions.", + "oneOf": [ + { + "$ref": "#/definitions/Rules" + } + ] + }, + "sourceMap": { + "description": "Enables/Disables generation of source maps.", + "anyOf": [ + { + "description": "Options for source map.", + "additionalProperties": true, + "type": "object" + }, + { + "type": "boolean" + } + ] + }, + "cssnanoOptions": { + "description": "Options for `cssnanoOptions`.", + "additionalProperties": true, + "type": "object" } }, "additionalProperties": false diff --git a/test/__snapshots__/validate-options.test.js.snap b/test/__snapshots__/validate-options.test.js.snap new file mode 100644 index 0000000..7089c5a --- /dev/null +++ b/test/__snapshots__/validate-options.test.js.snap @@ -0,0 +1,116 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`validation 1`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.test should be one of these: + [RegExp | non-empty string, ...] | RegExp | non-empty string + -> Filtering rules. + Details: + * options.test should be an array: + [RegExp | non-empty string, ...] + * options.test should be one of these: + RegExp | non-empty string + -> Filtering rule as regex or string. + Details: + * options.test should be an instance of RegExp. + * options.test should be a non-empty string." +`; + +exports[`validation 2`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.test should be one of these: + [RegExp | non-empty string, ...] | RegExp | non-empty string + -> Filtering rules. + Details: + * options.test[0] should be one of these: + RegExp | non-empty string + -> Filtering rule as regex or string. + Details: + * options.test[0] should be an instance of RegExp. + * options.test[0] should be a non-empty string." +`; + +exports[`validation 3`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.include should be one of these: + [RegExp | non-empty string, ...] | RegExp | non-empty string + -> Filtering rules. + Details: + * options.include should be an array: + [RegExp | non-empty string, ...] + * options.include should be one of these: + RegExp | non-empty string + -> Filtering rule as regex or string. + Details: + * options.include should be an instance of RegExp. + * options.include should be a non-empty string." +`; + +exports[`validation 4`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.include should be one of these: + [RegExp | non-empty string, ...] | RegExp | non-empty string + -> Filtering rules. + Details: + * options.include[0] should be one of these: + RegExp | non-empty string + -> Filtering rule as regex or string. + Details: + * options.include[0] should be an instance of RegExp. + * options.include[0] should be a non-empty string." +`; + +exports[`validation 5`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.exclude should be one of these: + [RegExp | non-empty string, ...] | RegExp | non-empty string + -> Filtering rules. + Details: + * options.exclude should be an array: + [RegExp | non-empty string, ...] + * options.exclude should be one of these: + RegExp | non-empty string + -> Filtering rule as regex or string. + Details: + * options.exclude should be an instance of RegExp. + * options.exclude should be a non-empty string." +`; + +exports[`validation 6`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.exclude should be one of these: + [RegExp | non-empty string, ...] | RegExp | non-empty string + -> Filtering rules. + Details: + * options.exclude[0] should be one of these: + RegExp | non-empty string + -> Filtering rule as regex or string. + Details: + * options.exclude[0] should be an instance of RegExp. + * options.exclude[0] should be a non-empty string." +`; + +exports[`validation 7`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.sourceMap should be one of these: + object { … } | boolean + -> Enables/Disables generation of source maps. + Details: + * options.sourceMap should be an object: + object { … } + -> Options for source map. + * options.sourceMap should be a boolean." +`; + +exports[`validation 8`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options.cssnanoOptions should be an object: + object { … } + -> Options for \`cssnanoOptions\`." +`; + +exports[`validation 9`] = ` +"Invalid options object. Cssnano webpack plugin has been initialized using an options object that does not match the API schema. + - options has an unknown property 'unknown'. These properties are valid: + object { test?, include?, exclude?, sourceMap?, cssnanoOptions? }" +`; diff --git a/test/validate-options.test.js b/test/validate-options.test.js new file mode 100644 index 0000000..4e8718c --- /dev/null +++ b/test/validate-options.test.js @@ -0,0 +1,135 @@ +import CssnanoWebpackPlugin from '../src'; + +it('validation', () => { + /* eslint-disable no-new */ + expect(() => { + new CssnanoWebpackPlugin({ test: /foo/ }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ test: 'foo' }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ test: [/foo/] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ test: [/foo/, /bar/] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ test: ['foo', 'bar'] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ test: [/foo/, 'bar'] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ test: true }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new CssnanoWebpackPlugin({ test: [true] }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new CssnanoWebpackPlugin({ include: /foo/ }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ include: 'foo' }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ include: [/foo/] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ include: [/foo/, /bar/] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ include: ['foo', 'bar'] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ include: [/foo/, 'bar'] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ include: true }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new CssnanoWebpackPlugin({ include: [true] }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new CssnanoWebpackPlugin({ exclude: /foo/ }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ exclude: 'foo' }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ exclude: [/foo/] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ exclude: [/foo/, /bar/] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ exclude: ['foo', 'bar'] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ exclude: [/foo/, 'bar'] }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ exclude: true }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new CssnanoWebpackPlugin({ exclude: [true] }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new CssnanoWebpackPlugin({ sourceMap: true }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ sourceMap: false }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ sourceMap: { inline: true } }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ sourceMap: 'true' }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new CssnanoWebpackPlugin({ cssnanoOptions: {} }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ cssnanoOptions: null }); + }).toThrowErrorMatchingSnapshot(); + + expect(() => { + new CssnanoWebpackPlugin({ + cssnanoOptions: { colormin: true }, + }); + }).not.toThrow(); + + expect(() => { + new CssnanoWebpackPlugin({ unknown: true }); + }).toThrowErrorMatchingSnapshot(); + /* eslint-enable no-new */ +});