Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: add additionalData option #248

Merged
merged 1 commit into from Sep 12, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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