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

Pass sass options to plugin #1308

Merged
merged 5 commits into from
Oct 15, 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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 20 additions & 3 deletions plugins/plugin-sass/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,23 @@ module.exports = {

## Plugin Options

| Name | Type | Description |
| :------- | :-------: | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `native` | `boolean` | If true, the plugin will ignore the npm version of sass installed locally for the native Sass CLI [installed separately](https://sass-lang.com/install). This involves extra set up, but the result can be [up to 9x faster.](https://stackoverflow.com/a/56422541) Defaults to false. |
| Name | Type | Description |
| :------------------ | :-------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `native` | `boolean` | If `true`, the plugin will ignore the npm version of sass installed locally for the native Sass CLI [installed separately](https://sass-lang.com/install). This involves extra setup, but the result can be [up to 9× faster.](https://stackoverflow.com/a/56422541) (default: `false`). |
| `compilerOptions.*` | `object` | Pass [Sass options][sass-options] directly to the Sass compiler (see `compilerOptions`). |

### `compilerOptions`
drwpow marked this conversation as resolved.
Show resolved Hide resolved

These options are camelCased equivalents of the [Sass CLI Options][sass-options]. The options listed here are safe for use. The other flags not listed here may cause issues or conflicts with Snowpack and/or other plugins; use at your discretion.

| Name | Type | Description |
| :--------------- | :----------------------------: | :------------------------------------------------------------------------------------------------------ |
| `style` | `'expanded'` \| `'compressed'` | The output style. Specify `'compressed'` to enable Sass’ built-in minification (default: `'expanded'`). |
| `sourceMap` | `boolean` | Enable / disable source maps (default: `true`). |
| `sourceMapUrls` | `'relative'` \| `'absolute'` | How to link from source maps to source files (default: `'relative'`). |
| `embedSources` | `boolean` | Embed source file contents in source maps (default: `false`). |
| `embedSourceMap` | `boolean` | Embed source map contents in CSS (default: `false`). |
| `charset` | `boolean` | Emit a `@charset` or BOM for CSS with non-ASCII characters. (default: `true`). |
| `update` | `boolean` | Only compile out-of-date stylesheets (default: `false`). |

[sass-options]: https://sass-lang.com/documentation/cli/dart-sass#options
20 changes: 19 additions & 1 deletion plugins/plugin-sass/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ function scanSassImports(fileContents, filePath, fileExt) {
});
}

module.exports = function postcssPlugin(_, {native}) {
module.exports = function sassPlugin(_, {native, compilerOptions = {}} = {}) {
/** A map of partially resolved imports to the files that imported them. */
const importedByMap = new Map();

Expand Down Expand Up @@ -89,11 +89,29 @@ module.exports = function postcssPlugin(_, {native}) {
const sassImports = scanSassImports(contents, filePath, fileExt);
sassImports.forEach((imp) => addImportsToMap(filePath, imp));
}

// If file is `.sass`, use YAML-style. Otherwise, use default.
const args = ['--stdin', '--load-path', path.dirname(filePath)];
if (fileExt === '.sass') {
args.push('--indented');
}

// Pass in user-defined options
Object.entries(compilerOptions).forEach(([flag, value]) => {
let flagName = flag.replace(/[A-Z]/g, (c) => `-${c.toLowerCase()}`); // convert camelCase to kebab-case
switch (typeof value) {
case 'boolean': {
args.push(`--${value === false ? 'no-' : ''}${flagName}`);
break;
}
case 'string':
case 'number': {
args.push(`--${flagName}=${value}`);
break;
}
}
});

// Build the file.
const {stdout, stderr} = await execa('sass', args, {
input: contents,
Expand Down
47 changes: 47 additions & 0 deletions plugins/plugin-sass/test/plugin-mocked.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/**
* This test requires mocks which could disrupt other tests
*/
const path = require('path');
const mockExeca = jest.fn().mockImplementation(() => Promise.resolve({stdout: '', stderr: ''}));
jest.mock('execa', () => (cmd, args) => mockExeca(cmd, args));
const plugin = require('../plugin');

const MOCK_CONFIG = null;
const MOCK_LOAD = {filePath: path.join(__dirname, 'fixtures', 'scss', 'App.scss'), isDev: false};

const tests = [
{name: 'no options', given: {}, expect: []},
{
name: 'string option',
given: {compilerOptions: {style: 'compressed'}},
expect: [`--style=compressed`],
},
{
name: 'boolean option',
given: {compilerOptions: {sourceMaps: false}},
expect: [`--no-source-maps`],
},
{
name: 'combination',
given: {compilerOptions: {style: 'compressed', sourceMaps: true}},
expect: [`--style=compressed`, `--source-maps`],
},
];

describe('plugin-sass', () => {
afterEach(() => {
mockExeca.mockClear(); // clear calls between each test
});

tests.forEach((t) => {
it(t.name, async () => {
const {load} = plugin(MOCK_CONFIG, t.given);
await load(MOCK_LOAD);
// Note: this test assumes execa is only used once in the entire plugin.
// If execa needs to be used for another purpose, you can filter calls by 'sass' 1st param here
t.expect.forEach((arg) => {
expect(mockExeca.mock.calls[0][1]).toContain(arg);
});
});
});
});