2017-02-02 19:56:41 +08:00
/* globals describe, it */
2017-01-07 10:36:33 +08:00
"use strict" ;
2017-02-02 19:56:41 +08:00
require ( "should" ) ;
2017-11-03 06:28:34 +08:00
2017-01-07 10:36:33 +08:00
const webpack = require ( "../lib/webpack" ) ;
2016-09-19 06:54:35 +08:00
2017-01-18 23:29:47 +08:00
describe ( "Validation" , ( ) => {
2018-02-25 09:00:20 +08:00
const testCases = [
{
name : "undefined configuration" ,
config : undefined ,
message : [ " - configuration should be an object." ]
2016-09-19 06:54:35 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "null configuration" ,
config : null ,
message : [ " - configuration should be an object." ]
2017-01-04 13:23:57 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "empty entry string" ,
config : {
entry : ""
} ,
message : [
" - configuration.entry should be one of these:" ,
" object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string] | function" ,
" -> The entry point(s) of the compilation." ,
" Details:" ,
" * configuration.entry should be an object." ,
" -> Multiple entry bundles are created. The key is the chunk name. The value can be a string or an array." ,
" * configuration.entry should not be empty." ,
" -> An entry point without name. The string is resolved to a module which is loaded upon startup." ,
" * configuration.entry should be an array:" ,
" [non-empty string]" ,
" * configuration.entry should be an instance of function" ,
" -> A Function returning an entry object, an entry string, an entry array or a promise to these things."
]
2016-11-04 06:52:59 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "empty entry bundle array" ,
config : {
entry : {
bundle : [ ]
}
} ,
message : [
" - configuration.entry should be one of these:" ,
" object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string] | function" ,
" -> The entry point(s) of the compilation." ,
" Details:" ,
" * configuration.entry['bundle'] should be a string." ,
" -> The string is resolved to a module which is loaded upon startup." ,
" * configuration.entry['bundle'] should not be empty." ,
" * configuration.entry should be a string." ,
" -> An entry point without name. The string is resolved to a module which is loaded upon startup." ,
" * configuration.entry should be an array:" ,
" [non-empty string]" ,
" * configuration.entry should be an instance of function" ,
" -> A Function returning an entry object, an entry string, an entry array or a promise to these things."
]
2017-11-16 15:59:22 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "invalid instanceof" ,
config : {
entry : "a" ,
module : {
wrappedContextRegExp : 1337
}
} ,
message : [
" - configuration.module.wrappedContextRegExp should be an instance of RegExp" ,
" -> Set the inner regular expression for partial dynamic dependencies"
]
2017-11-16 15:59:22 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "invalid minimum" ,
config : {
entry : "a" ,
parallelism : 0
} ,
message : [
" - configuration.parallelism should be >= 1." ,
" -> The number of parallel processed modules in the compilation."
]
2016-09-19 06:54:35 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "repeated value" ,
config : {
entry : [ "abc" , "def" , "abc" ]
} ,
message : [
" - configuration.entry should be one of these:" ,
" object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string] | function" ,
" -> The entry point(s) of the compilation." ,
" Details:" ,
" * configuration.entry should be an object." ,
" -> Multiple entry bundles are created. The key is the chunk name. The value can be a string or an array." ,
" * configuration.entry should be a string." ,
" -> An entry point without name. The string is resolved to a module which is loaded upon startup." ,
" * configuration.entry should not contain the item 'abc' twice." ,
" * configuration.entry should be an instance of function" ,
" -> A Function returning an entry object, an entry string, an entry array or a promise to these things."
]
} ,
{
name : "multiple errors" ,
config : {
entry : [ /a/ ] ,
output : {
filename : /a/
}
} ,
message : [
" - configuration.entry should be one of these:" ,
" object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string] | function" ,
" -> The entry point(s) of the compilation." ,
" Details:" ,
" * configuration.entry should be an object." ,
" -> Multiple entry bundles are created. The key is the chunk name. The value can be a string or an array." ,
" * configuration.entry should be a string." ,
" -> An entry point without name. The string is resolved to a module which is loaded upon startup." ,
" * configuration.entry[0] should be a string." ,
" -> A non-empty string" ,
" * configuration.entry should be an instance of function" ,
" -> A Function returning an entry object, an entry string, an entry array or a promise to these things." ,
" - configuration.output.filename should be one of these:" ,
" string | function" ,
" -> Specifies the name of each output file on disk. You must **not** specify an absolute path here! The `output.path` option determines the location on disk the files are written to, filename is used solely for naming the individual files." ,
" Details:" ,
" * configuration.output.filename should be a string." ,
" * configuration.output.filename should be an instance of function"
]
} ,
{
name : "multiple configurations" ,
config : [
{
entry : [ /a/ ]
} ,
{
entry : "a" ,
output : {
filename : /a/
}
}
] ,
message : [
" - configuration[0].entry should be one of these:" ,
" object { <key>: non-empty string | [non-empty string] } | non-empty string | [non-empty string] | function" ,
" -> The entry point(s) of the compilation." ,
" Details:" ,
" * configuration[0].entry should be an object." ,
" -> Multiple entry bundles are created. The key is the chunk name. The value can be a string or an array." ,
" * configuration[0].entry should be a string." ,
" -> An entry point without name. The string is resolved to a module which is loaded upon startup." ,
" * configuration[0].entry[0] should be a string." ,
" -> A non-empty string" ,
" * configuration[0].entry should be an instance of function" ,
" -> A Function returning an entry object, an entry string, an entry array or a promise to these things." ,
" - configuration[1].output.filename should be one of these:" ,
" string | function" ,
" -> Specifies the name of each output file on disk. You must **not** specify an absolute path here! The `output.path` option determines the location on disk the files are written to, filename is used solely for naming the individual files." ,
" Details:" ,
" * configuration[1].output.filename should be a string." ,
" * configuration[1].output.filename should be an instance of function"
]
} ,
{
name : "deep error" ,
config : {
entry : "a" ,
module : {
rules : [
{
oneOf : [
{
test : "/a" ,
2018-02-26 10:39:15 +08:00
passer : {
2018-02-25 09:00:20 +08:00
amd : false
}
}
]
2016-09-19 06:54:35 +08:00
}
2018-02-25 09:00:20 +08:00
]
}
} ,
message : [
2018-02-26 10:39:15 +08:00
" - configuration.module.rules[0].oneOf[0] has an unknown property 'passer'. These properties are valid:" ,
2018-02-25 09:00:20 +08:00
" object { enforce?, exclude?, include?, issuer?, loader?, loaders?, oneOf?, options?, parser?, resolve?, sideEffects?, query?, type?, resource?, resourceQuery?, compiler?, rules?, test?, use? }" ,
" -> A rule"
]
2016-09-19 06:54:35 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "additional key on root" ,
config : {
entry : "a" ,
postcss : ( ) => { }
} ,
message : [
" - configuration has an unknown property 'postcss'. These properties are valid:" ,
" object { mode?, amd?, bail?, cache?, context?, dependencies?, devServer?, devtool?, entry?, externals?, " +
"loader?, module?, name?, node?, output?, optimization?, parallelism?, performance?, plugins?, profile?, recordsInputPath?, " +
"recordsOutputPath?, recordsPath?, resolve?, resolveLoader?, stats?, target?, watch?, watchOptions? }" ,
" For typos: please correct them." ,
2018-02-28 04:41:31 +08:00
" For loader options: webpack >= v2.0.0 no longer allows custom properties in configuration." ,
2018-02-25 09:00:20 +08:00
" Loaders should be updated to allow passing options via loader options in module.rules." ,
" Until loaders are updated one can use the LoaderOptionsPlugin to pass these options to the loader:" ,
" plugins: [" ,
" new webpack.LoaderOptionsPlugin({" ,
" // test: /\\.xxx$/, // may apply this only for some modules" ,
" options: {" ,
" postcss: ..." ,
" }" ,
" })" ,
" ]"
]
2016-09-21 02:18:52 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "enum" ,
config : {
entry : "a" ,
devtool : true
} ,
message : [
" - configuration.devtool should be one of these:" ,
" string | false" ,
" -> A developer tool to enhance debugging." ,
" Details:" ,
" * configuration.devtool should be a string." ,
" * configuration.devtool should be false"
]
2016-12-14 19:03:24 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "relative path" ,
config : {
entry : "foo.js" ,
output : {
filename : "/bar"
}
} ,
message : [
' - configuration.output.filename: A relative path is expected. However the provided value "/bar" is an absolute path!' ,
" -> Specifies the name of each output file on disk. You must **not** specify an absolute path here! The `output.path` option determines the location on disk the files are written to, filename is used solely for naming the individual files." ,
" Please use output.path to specify absolute path and output.filename for the file name."
]
2017-02-03 21:49:38 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "absolute path" ,
config : {
entry : "foo.js" ,
output : {
filename : "bar"
} ,
context : "baz"
2017-02-03 21:49:38 +08:00
} ,
2018-02-25 09:00:20 +08:00
message : [
' - configuration.context: The provided value "baz" is not an absolute path!' ,
" -> The base directory (absolute path!) for resolving the `entry` option. If `output.pathinfo` is set, the included pathinfo is shortened to this directory."
]
2017-02-03 21:49:38 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "missing stats option" ,
config : {
entry : "foo.js" ,
stats : {
foobar : true
}
} ,
test ( err ) {
err . message . should . startWith ( "Invalid configuration object." ) ;
err . message
. split ( "\n" )
. slice ( 1 ) [ 0 ]
. should . be . eql ( " - configuration.stats should be one of these:" ) ;
2017-10-14 06:12:24 +08:00
}
} ,
2018-02-25 09:00:20 +08:00
{
name : "Invalid plugin provided: bool" ,
config : {
entry : "foo.js" ,
plugins : [ false ]
} ,
message : [
" - configuration.plugins[0] should be one of these:" ,
" object { apply, ... } | function" ,
" -> Plugin of type object or instanceof Function" ,
" Details:" ,
" * configuration.plugins[0] should be an object." ,
" -> Plugin instance" ,
" * configuration.plugins[0] should be an instance of function" ,
" -> Function acting as plugin"
2017-11-25 00:54:48 +08:00
]
} ,
2018-02-25 09:00:20 +08:00
{
name : "Invalid plugin provided: array" ,
config : {
entry : "foo.js" ,
plugins : [ [ ] ]
} ,
message : [
" - configuration.plugins[0] should be one of these:" ,
" object { apply, ... } | function" ,
" -> Plugin of type object or instanceof Function" ,
" Details:" ,
" * configuration.plugins[0] should be an object." ,
" -> Plugin instance" ,
" * configuration.plugins[0] should be an instance of function" ,
" -> Function acting as plugin"
2017-11-25 00:54:48 +08:00
]
} ,
2018-02-25 09:00:20 +08:00
{
name : "Invalid plugin provided: string" ,
config : {
entry : "foo.js" ,
plugins : [ "abc123" ]
} ,
message : [
" - configuration.plugins[0] should be one of these:" ,
" object { apply, ... } | function" ,
" -> Plugin of type object or instanceof Function" ,
" Details:" ,
" * configuration.plugins[0] should be an object." ,
" -> Plugin instance" ,
" * configuration.plugins[0] should be an instance of function" ,
" -> Function acting as plugin"
2017-11-25 00:54:48 +08:00
]
} ,
2018-02-25 09:00:20 +08:00
{
name : "Invalid plugin provided: int" ,
config : {
entry : "foo.js" ,
plugins : [ 12 ]
} ,
message : [
" - configuration.plugins[0] should be one of these:" ,
" object { apply, ... } | function" ,
" -> Plugin of type object or instanceof Function" ,
" Details:" ,
" * configuration.plugins[0] should be an object." ,
" -> Plugin instance" ,
" * configuration.plugins[0] should be an instance of function" ,
" -> Function acting as plugin"
]
2017-11-25 00:54:48 +08:00
} ,
2018-02-25 09:00:20 +08:00
{
name : "Invalid plugin provided: object without apply function" ,
config : {
entry : "foo.js" ,
plugins : [ { } ]
} ,
message : [
" - configuration.plugins[0] should be one of these:" ,
" object { apply, ... } | function" ,
" -> Plugin of type object or instanceof Function" ,
" Details:" ,
" * configuration.plugins[0] misses the property 'apply'." ,
" function" ,
" -> The run point of the plugin, required method." ,
" * configuration.plugins[0] misses the property 'apply'." ,
" function" ,
" -> The run point of the plugin, required method." ,
" * configuration.plugins[0] should be an instance of function" ,
" -> Function acting as plugin"
]
}
] ;
2017-11-03 06:28:34 +08:00
2018-02-25 09:00:20 +08:00
testCases . forEach ( testCase => {
2017-01-18 23:29:47 +08:00
it ( "should fail validation for " + testCase . name , ( ) => {
2016-09-19 06:54:35 +08:00
try {
webpack ( testCase . config ) ;
2018-02-25 09:00:20 +08:00
} catch ( err ) {
if ( err . name !== "WebpackOptionsValidationError" ) throw err ;
2017-10-14 06:12:24 +08:00
2018-02-25 09:00:20 +08:00
if ( testCase . test ) {
2017-11-03 06:28:34 +08:00
testCase . test ( err ) ;
2017-10-14 06:12:24 +08:00
return ;
}
2017-11-03 06:28:34 +08:00
err . message . should . startWith ( "Invalid configuration object." ) ;
2018-02-25 09:00:20 +08:00
err . message
. split ( "\n" )
. slice ( 1 )
. should . be . eql ( testCase . message ) ;
2017-11-03 06:28:34 +08:00
2016-09-19 06:54:35 +08:00
return ;
}
2017-11-03 06:28:34 +08:00
2016-09-19 06:54:35 +08:00
throw new Error ( "Validation didn't fail" ) ;
2017-02-02 19:56:41 +08:00
} ) ;
2016-09-19 06:54:35 +08:00
} ) ;
} ) ;