Skip to content

Commit

Permalink
feat: add additionalData option (#248)
Browse files Browse the repository at this point in the history
  • Loading branch information
cap-Bernardito committed Sep 12, 2020
1 parent eba6f1f commit 9f781b7
Show file tree
Hide file tree
Showing 8 changed files with 247 additions and 14 deletions.
81 changes: 76 additions & 5 deletions README.md
Expand Up @@ -45,11 +45,12 @@ And run `webpack` via your preferred method.

## Options

| Name | Type | Default | Description |
| :---------------------------------------: | :------------------: | :----------------: | :--------------------------------------------- |
| **[`stylusOptions`](#stylusOptions)** | `{Object\|Function}` | `{}` | Options for Stylus. |
| **[`sourceMap`](#sourcemap)** | `{Boolean}` | `compiler.devtool` | Enables/Disables generation of source maps. |
| **[`webpackImporter`](#webpackimporter)** | `{Boolean}` | `true` | Enables/Disables the default Webpack importer. |
| Name | Type | Default | Description |
| :---------------------------------------: | :------------------: | :----------------: | :------------------------------------------------------- |
| **[`stylusOptions`](#stylusOptions)** | `{Object\|Function}` | `{}` | Options for Stylus. |
| **[`sourceMap`](#sourcemap)** | `{Boolean}` | `compiler.devtool` | Enables/Disables generation of source maps. |
| **[`webpackImporter`](#webpackimporter)** | `{Boolean}` | `true` | Enables/Disables the default Webpack importer. |
| **[`additionalData`](#additionalData)** | `{String\|Function}` | `undefined` | Prepends/Appends `Stylus` code to the actual entry file. |

### `stylusOptions`

Expand Down Expand Up @@ -210,6 +211,76 @@ module.exports = {
};
```

### `additionalData`

Type: `String|Function`
Default: `undefined`

Prepends `Stylus` code before the actual entry file.
In this case, the `stylus-loader` will not override the source but just **prepend** the entry's content.

This is especially useful when some of your Stylus variables depend on the environment:

> ℹ Since you're injecting code, this will break the source mappings in your entry file. Often there's a simpler solution than this, like multiple Stylus entry files.
#### `String`

```js
module.exports = {
module: {
rules: [
{
test: /\.styl/,
use: [
'style-loader',
'css-loader',
{
loader: 'stylus-loader',
options: {
additionalData: `@env: ${process.env.NODE_ENV};`,
},
},
],
},
],
},
};
```

#### `Function`

```js
module.exports = {
module: {
rules: [
{
test: /\.styl/,
use: [
'style-loader',
'css-loader',
{
loader: 'stylus-loader',
options: {
additionalData: (content, loaderContext) => {
// More information about available properties https://webpack.js.org/api/loaders/
const { resourcePath, rootContext } = loaderContext;
const relativePath = path.relative(rootContext, resourcePath);

if (relativePath === 'styles/foo.styl') {
return 'value = 100px' + content;
}

return 'value 200px' + content;
},
},
},
],
},
],
},
};
```

## Examples

### Normal usage
Expand Down
11 changes: 10 additions & 1 deletion src/index.js
Expand Up @@ -33,7 +33,16 @@ export default async function stylusLoader(source) {
};
}

const styl = stylus(source, stylusOptions);
let data = source;

if (typeof options.additionalData !== 'undefined') {
data =
typeof options.additionalData === 'function'
? `${options.additionalData(data, this)}`
: `${options.additionalData}\n${data}`;
}

const styl = stylus(data, stylusOptions);

if (typeof stylusOptions.include !== 'undefined') {
for (const included of stylusOptions.include) {
Expand Down
11 changes: 11 additions & 0 deletions src/options.json
Expand Up @@ -20,6 +20,17 @@
"webpackImporter": {
"description": "Enables/Disables default `webpack` importer (https://github.com/webpack-contrib/stylus-loader#webpackimporter).",
"type": "boolean"
},
"additionalData": {
"description": "Prepends/Appends `Stylus` code to the actual entry file (https://github.com/webpack-contrib/stylus-loader#additionalData).",
"anyOf": [
{
"type": "string"
},
{
"instanceof": "Function"
}
]
}
},
"additionalProperties": false
Expand Down
27 changes: 27 additions & 0 deletions test/__snapshots__/additionalData-option.test.js.snap
@@ -0,0 +1,27 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`"additionalData" option should work additionalData data as function: css 1`] = `
"/* RelativePath: additional-data.styl; */
body {
color: #ff7f50;
}
.custom-class {
background: #808080;
}
"
`;

exports[`"additionalData" option should work additionalData data as function: errors 1`] = `Array []`;

exports[`"additionalData" option should work additionalData data as function: warnings 1`] = `Array []`;

exports[`"additionalData" option should work additionalData data as string: css 1`] = `
"body {
color: #ff7f50;
}
"
`;

exports[`"additionalData" option should work additionalData data as string: errors 1`] = `Array []`;

exports[`"additionalData" option should work additionalData data as string: warnings 1`] = `Array []`;
76 changes: 68 additions & 8 deletions test/__snapshots__/validate-options.test.js.snap
@@ -1,5 +1,65 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`validate options should throw an error on the "additionalData" option with "/test/" value 1`] = `
"Invalid options object. Stylus Loader has been initialized using an options object that does not match the API schema.
- options.additionalData should be one of these:
string | function
-> Prepends/Appends \`Stylus\` code to the actual entry file (https://github.com/webpack-contrib/stylus-loader#additionalData).
Details:
* options.additionalData should be a string.
* options.additionalData should be an instance of function."
`;

exports[`validate options should throw an error on the "additionalData" option with "[]" value 1`] = `
"Invalid options object. Stylus Loader has been initialized using an options object that does not match the API schema.
- options.additionalData should be one of these:
string | function
-> Prepends/Appends \`Stylus\` code to the actual entry file (https://github.com/webpack-contrib/stylus-loader#additionalData).
Details:
* options.additionalData should be a string.
* options.additionalData should be an instance of function."
`;

exports[`validate options should throw an error on the "additionalData" option with "{}" value 1`] = `
"Invalid options object. Stylus Loader has been initialized using an options object that does not match the API schema.
- options.additionalData should be one of these:
string | function
-> Prepends/Appends \`Stylus\` code to the actual entry file (https://github.com/webpack-contrib/stylus-loader#additionalData).
Details:
* options.additionalData should be a string.
* options.additionalData should be an instance of function."
`;

exports[`validate options should throw an error on the "additionalData" option with "1" value 1`] = `
"Invalid options object. Stylus Loader has been initialized using an options object that does not match the API schema.
- options.additionalData should be one of these:
string | function
-> Prepends/Appends \`Stylus\` code to the actual entry file (https://github.com/webpack-contrib/stylus-loader#additionalData).
Details:
* options.additionalData should be a string.
* options.additionalData should be an instance of function."
`;

exports[`validate options should throw an error on the "additionalData" option with "false" value 1`] = `
"Invalid options object. Stylus Loader has been initialized using an options object that does not match the API schema.
- options.additionalData should be one of these:
string | function
-> Prepends/Appends \`Stylus\` code to the actual entry file (https://github.com/webpack-contrib/stylus-loader#additionalData).
Details:
* options.additionalData should be a string.
* options.additionalData should be an instance of function."
`;

exports[`validate options should throw an error on the "additionalData" option with "true" value 1`] = `
"Invalid options object. Stylus Loader has been initialized using an options object that does not match the API schema.
- options.additionalData should be one of these:
string | function
-> Prepends/Appends \`Stylus\` code to the actual entry file (https://github.com/webpack-contrib/stylus-loader#additionalData).
Details:
* options.additionalData should be a string.
* options.additionalData should be an instance of function."
`;

exports[`validate options should throw an error on the "sourceMap" option with "string" value 1`] = `
"Invalid options object. Stylus Loader has been initialized using an options object that does not match the API schema.
- options.sourceMap should be a boolean.
Expand Down Expand Up @@ -64,49 +124,49 @@ exports[`validate options should throw an error on the "stylusOptions" option wi
exports[`validate options should throw an error on the "unknown" option with "/test/" value 1`] = `
"Invalid options object. Stylus Loader 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 { stylusOptions?, sourceMap?, webpackImporter? }"
object { stylusOptions?, sourceMap?, webpackImporter?, additionalData? }"
`;
exports[`validate options should throw an error on the "unknown" option with "[]" value 1`] = `
"Invalid options object. Stylus Loader 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 { stylusOptions?, sourceMap?, webpackImporter? }"
object { stylusOptions?, sourceMap?, webpackImporter?, additionalData? }"
`;
exports[`validate options should throw an error on the "unknown" option with "{"foo":"bar"}" value 1`] = `
"Invalid options object. Stylus Loader 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 { stylusOptions?, sourceMap?, webpackImporter? }"
object { stylusOptions?, sourceMap?, webpackImporter?, additionalData? }"
`;
exports[`validate options should throw an error on the "unknown" option with "{}" value 1`] = `
"Invalid options object. Stylus Loader 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 { stylusOptions?, sourceMap?, webpackImporter? }"
object { stylusOptions?, sourceMap?, webpackImporter?, additionalData? }"
`;
exports[`validate options should throw an error on the "unknown" option with "1" value 1`] = `
"Invalid options object. Stylus Loader 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 { stylusOptions?, sourceMap?, webpackImporter? }"
object { stylusOptions?, sourceMap?, webpackImporter?, additionalData? }"
`;
exports[`validate options should throw an error on the "unknown" option with "false" value 1`] = `
"Invalid options object. Stylus Loader 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 { stylusOptions?, sourceMap?, webpackImporter? }"
object { stylusOptions?, sourceMap?, webpackImporter?, additionalData? }"
`;
exports[`validate options should throw an error on the "unknown" option with "test" value 1`] = `
"Invalid options object. Stylus Loader 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 { stylusOptions?, sourceMap?, webpackImporter? }"
object { stylusOptions?, sourceMap?, webpackImporter?, additionalData? }"
`;
exports[`validate options should throw an error on the "unknown" option with "true" value 1`] = `
"Invalid options object. Stylus Loader 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 { stylusOptions?, sourceMap?, webpackImporter? }"
object { stylusOptions?, sourceMap?, webpackImporter?, additionalData? }"
`;
exports[`validate options should throw an error on the "webpackImporter" option with "string" value 1`] = `
Expand Down
49 changes: 49 additions & 0 deletions test/additionalData-option.test.js
@@ -0,0 +1,49 @@
import {
compile,
getCodeFromBundle,
getCompiler,
getErrors,
getWarnings,
} from './helpers';

describe('"additionalData" option', () => {
it('should work additionalData data as string', async () => {
const testId = './additional-data.styl';
const additionalData = 'color = coral';
const compiler = getCompiler(testId, { additionalData });
const stats = await compile(compiler);
const codeFromBundle = getCodeFromBundle(stats, compiler);

expect(codeFromBundle.css).toMatchSnapshot('css');
expect(getWarnings(stats)).toMatchSnapshot('warnings');
expect(getErrors(stats)).toMatchSnapshot('errors');
});

it('should work additionalData data as function', async () => {
const testId = './additional-data.styl';
const additionalData = (content, loaderContext) => {
const { resourcePath, rootContext } = loaderContext;
// eslint-disable-next-line global-require
const relativePath = require('path').relative(rootContext, resourcePath);

return `
/* RelativePath: ${relativePath}; */
color = coral;
bg = gray;
${content}
.custom-class
background: bg
`;
};
const compiler = getCompiler(testId, { additionalData });
const stats = await compile(compiler);
const codeFromBundle = getCodeFromBundle(stats, compiler);

expect(codeFromBundle.css).toMatchSnapshot('css');
expect(getWarnings(stats)).toMatchSnapshot('warnings');
expect(getErrors(stats)).toMatchSnapshot('errors');
});
});
2 changes: 2 additions & 0 deletions test/fixtures/additional-data.styl
@@ -0,0 +1,2 @@
body
color color
4 changes: 4 additions & 0 deletions test/validate-options.test.js
Expand Up @@ -34,6 +34,10 @@ describe('validate options', () => {
success: [true, false],
failure: ['string'],
},
additionalData: {
success: ['color = coral', () => 'bg = coral'],
failure: [1, true, false, /test/, [], {}],
},
unknown: {
success: [],
failure: [1, true, false, 'test', /test/, [], {}, { foo: 'bar' }],
Expand Down

0 comments on commit 9f781b7

Please sign in to comment.