Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7b543fc

Browse files
authoredAug 20, 2019
feat: validate loader options (#737)
1 parent 9c5028b commit 7b543fc

8 files changed

+190
-20
lines changed
 

‎README.md

+4-2
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ module.exports = {
203203

204204
Type: `Object|Function`
205205

206-
Setups options for [Node Sass](https://github.com/sass/node-sass) or [Dart Sass](http://sass-lang.com/dart-sass).
206+
Options for [Node Sass](https://github.com/sass/node-sass) or [Dart Sass](http://sass-lang.com/dart-sass) implementation.
207207

208208
> ℹ️ The `indentedSyntax` option has `true` value for the `sass` extension.
209209
@@ -403,7 +403,7 @@ module.exports = {
403403
Type: `Boolean`
404404
Default: `true`
405405

406-
Allows to disable default `webpack` importer.
406+
Enables/Disables default `webpack` importer.
407407

408408
This can improve performance in some cases. Use it with caution because aliases and `@import` at-rules starts with `~` will not work, but you can pass own `importer` to solve this (see [`importer docs`](https://github.com/sass/node-sass#importer--v200---experimental)).
409409

@@ -476,6 +476,8 @@ module.exports = {
476476

477477
### Source maps
478478

479+
Enables/Disables generation of source maps.
480+
479481
To enable CSS source maps, you'll need to pass the `sourceMap` option to the sass-loader _and_ the css-loader.
480482

481483
**webpack.config.js**

‎package-lock.json

+52-17
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+1
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
"clone-deep": "^4.0.1",
4242
"loader-utils": "^1.2.3",
4343
"neo-async": "^2.6.1",
44+
"schema-utils": "^2.1.0",
4445
"semver": "^6.3.0"
4546
},
4647
"devDependencies": {

‎src/getSassOptions.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ function getSassOptions(loaderContext, loaderOptions, content) {
2525
const options = cloneDeep(
2626
loaderOptions.sassOptions
2727
? typeof loaderOptions.sassOptions === 'function'
28-
? loaderOptions.sassOptions(loaderContext)
28+
? loaderOptions.sassOptions(loaderContext) || {}
2929
: loaderOptions.sassOptions
3030
: {}
3131
);

‎src/index.js

+7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import path from 'path';
22

3+
import validateOptions from 'schema-utils';
34
import async from 'neo-async';
45
import semver from 'semver';
56
import { getOptions } from 'loader-utils';
67

8+
import schema from './options.json';
79
import formatSassError from './formatSassError';
810
import webpackImporter from './webpackImporter';
911
import getSassOptions from './getSassOptions';
@@ -19,6 +21,11 @@ let nodeSassJobQueue = null;
1921
function loader(content) {
2022
const options = getOptions(this) || {};
2123

24+
validateOptions(schema, options, {
25+
name: 'Sass Loader',
26+
baseDataPath: 'options',
27+
});
28+
2229
const callback = this.async();
2330
const addNormalizedDependency = (file) => {
2431
// node-sass returns POSIX paths

‎src/options.json

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{
2+
"type": "object",
3+
"properties": {
4+
"implementation": {
5+
"description": "The implementation of the sass to be used (https://github.com/webpack-contrib/sass-loader#implementation)."
6+
},
7+
"sassOptions": {
8+
"description": "Options for `node-sass` or `sass` (`Dart Sass`) implementation. (https://github.com/webpack-contrib/sass-loader#implementation).",
9+
"anyOf": [
10+
{
11+
"type": "object",
12+
"additionalProperties": true
13+
},
14+
{
15+
"instanceof": "Function"
16+
}
17+
]
18+
},
19+
"prependData": {
20+
"description": "Prepends `Sass`/`SCSS` code before the actual entry file (https://github.com/webpack-contrib/sass-loader#prependdata).",
21+
"anyOf": [
22+
{
23+
"type": "string"
24+
},
25+
{
26+
"instanceof": "Function"
27+
}
28+
]
29+
},
30+
"sourceMap": {
31+
"description": "Enables/Disables generation of source maps (https://github.com/webpack-contrib/sass-loader#sourcemap).",
32+
"type": "boolean"
33+
},
34+
"webpackImporter": {
35+
"description": "Enables/Disables default `webpack` importer (https://github.com/webpack-contrib/sass-loader#webpackimporter).",
36+
"type": "boolean"
37+
}
38+
},
39+
"additionalProperties": false
40+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`validate options 1`] = `
4+
"Invalid options object. Sass Loader has been initialised using an options object that does not match the API schema.
5+
- options.sassOptions should be one of these:
6+
object {} | function
7+
-> Options for \`node-sass\` or \`sass\` (\`Dart Sass\`) implementation. (https://github.com/webpack-contrib/sass-loader#implementation).
8+
Details:
9+
* options.sassOptions should be an object:
10+
object {}
11+
* options.sassOptions should be an instance of function."
12+
`;
13+
14+
exports[`validate options 2`] = `
15+
"Invalid options object. Sass Loader has been initialised using an options object that does not match the API schema.
16+
- options.prependData should be one of these:
17+
string | function
18+
-> Prepends \`Sass\`/\`SCSS\` code before the actual entry file (https://github.com/webpack-contrib/sass-loader#prependdata).
19+
Details:
20+
* options.prependData should be a string.
21+
* options.prependData should be an instance of function."
22+
`;
23+
24+
exports[`validate options 3`] = `
25+
"Invalid options object. Sass Loader has been initialised using an options object that does not match the API schema.
26+
- options.webpackImporter should be a boolean.
27+
-> Enables/Disables default \`webpack\` importer (https://github.com/webpack-contrib/sass-loader#webpackimporter)."
28+
`;
29+
30+
exports[`validate options 4`] = `
31+
"Invalid options object. Sass Loader has been initialised using an options object that does not match the API schema.
32+
- options has an unknown property 'unknown'. These properties are valid:
33+
object { implementation?, sassOptions?, prependData?, sourceMap?, webpackImporter? }"
34+
`;

‎test/validate-options.test.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import loader from '../src/cjs';
2+
3+
it('validate options', () => {
4+
const validate = (options) =>
5+
loader.call(
6+
Object.assign(
7+
{},
8+
{
9+
query: options,
10+
loaders: [],
11+
resourcePath: 'file.scss',
12+
getResolve: () => () => {},
13+
async: () => (error) => {
14+
if (error) {
15+
throw error;
16+
}
17+
},
18+
}
19+
),
20+
'a { color: red; }'
21+
);
22+
23+
// eslint-disable-next-line global-require
24+
// expect(() => validate({ implementation: require('node-sass') })).not.toThrow();
25+
// eslint-disable-next-line global-require
26+
// expect(() => validate({ implementation: require('sass') })).not.toThrow();
27+
// expect(() => validate({ implementation: true })).not.toThrow();
28+
29+
expect(() => validate({ sassOptions: {} })).not.toThrow();
30+
expect(() =>
31+
validate({
32+
sassOptions: () => {
33+
return {};
34+
},
35+
})
36+
).not.toThrow();
37+
expect(() => validate({ sassOptions: () => {} })).not.toThrow();
38+
expect(() => validate({ sassOptions: true })).toThrowErrorMatchingSnapshot();
39+
40+
expect(() => validate({ prependData: '$color: red;' })).not.toThrow();
41+
expect(() => validate({ prependData: () => '$color: red;' })).not.toThrow();
42+
expect(() => validate({ prependData: true })).toThrowErrorMatchingSnapshot();
43+
44+
expect(() => validate({ webpackImporter: true })).not.toThrow();
45+
expect(() => validate({ webpackImporter: false })).not.toThrow();
46+
expect(() =>
47+
validate({ webpackImporter: 'unknown' })
48+
).toThrowErrorMatchingSnapshot();
49+
50+
expect(() => validate({ unknown: 'unknown' })).toThrowErrorMatchingSnapshot();
51+
});

0 commit comments

Comments
 (0)
Please sign in to comment.