From ddeef79f6fd4b79ada7083ab0ba502ff7d2053fc Mon Sep 17 00:00:00 2001 From: Drew Powers Date: Wed, 14 Oct 2020 09:39:30 -0600 Subject: [PATCH 1/5] Pass sass options to plugin --- plugins/plugin-sass/README.md | 13 +++-- plugins/plugin-sass/plugin.js | 20 +++++++- .../plugin-sass/test/plugin-mocked.test.js | 47 +++++++++++++++++++ 3 files changed, 76 insertions(+), 4 deletions(-) create mode 100644 plugins/plugin-sass/test/plugin-mocked.test.js diff --git a/plugins/plugin-sass/README.md b/plugins/plugin-sass/README.md index 2ebb338176..07f62d20f4 100644 --- a/plugins/plugin-sass/README.md +++ b/plugins/plugin-sass/README.md @@ -30,6 +30,13 @@ 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 set up, but the result can be [up to 9x faster.](https://stackoverflow.com/a/56422541) Defaults to false. | +| `style` | `'expanded'` (default) \| `'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. (defaul: `true`). | +| `update` | `boolean` | Only compile out-of-date stylesheets (default: `false`). | diff --git a/plugins/plugin-sass/plugin.js b/plugins/plugin-sass/plugin.js index b4b19458f1..3a3c17498a 100644 --- a/plugins/plugin-sass/plugin.js +++ b/plugins/plugin-sass/plugin.js @@ -27,7 +27,7 @@ function scanSassImports(fileContents, filePath, fileExt) { }); } -module.exports = function postcssPlugin(_, {native}) { +module.exports = function sassPlugin(_, {native, ...sassOptions} = {}) { /** A map of partially resolved imports to the files that imported them. */ const importedByMap = new Map(); @@ -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(sassOptions || {}).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, diff --git a/plugins/plugin-sass/test/plugin-mocked.test.js b/plugins/plugin-sass/test/plugin-mocked.test.js new file mode 100644 index 0000000000..e50ee36bb5 --- /dev/null +++ b/plugins/plugin-sass/test/plugin-mocked.test.js @@ -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: undefined, expect: []}, + { + name: 'string option', + given: {style: 'compressed'}, + expect: [`--style=compressed`], + }, + { + name: 'boolean option', + given: {sourceMaps: false}, + expect: [`--no-source-maps`], + }, + { + name: 'combination', + given: {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); + }); + }); + }); +}); From 5a9460a6fcebc774a14be2671644e25837d1f853 Mon Sep 17 00:00:00 2001 From: Drew Powers Date: Wed, 14 Oct 2020 14:17:34 -0600 Subject: [PATCH 2/5] Rename Sass options -> compilerOptions --- plugins/plugin-sass/README.md | 26 ++++++++++++------- plugins/plugin-sass/plugin.js | 4 +-- .../plugin-sass/test/plugin-mocked.test.js | 8 +++--- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/plugins/plugin-sass/README.md b/plugins/plugin-sass/README.md index 07f62d20f4..89b8abde7f 100644 --- a/plugins/plugin-sass/README.md +++ b/plugins/plugin-sass/README.md @@ -30,13 +30,19 @@ 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. | -| `style` | `'expanded'` (default) \| `'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. (defaul: `true`). | -| `update` | `boolean` | Only compile out-of-date stylesheets (default: `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 set up, but the result can be [up to 9x faster.](https://stackoverflow.com/a/56422541) (default: `false`). | +| `compilerOptions.*` | `object` | Pass [Sass options](https://sass-lang.com/documentation/cli/dart-sass#options) directly to the Sass compiler (see `compilerOptions`). | + +### `compilerOptions` + +| 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. (defaul: `true`). | +| `update` | `boolean` | Only compile out-of-date stylesheets (default: `false`). | diff --git a/plugins/plugin-sass/plugin.js b/plugins/plugin-sass/plugin.js index 3a3c17498a..886fc511d8 100644 --- a/plugins/plugin-sass/plugin.js +++ b/plugins/plugin-sass/plugin.js @@ -27,7 +27,7 @@ function scanSassImports(fileContents, filePath, fileExt) { }); } -module.exports = function sassPlugin(_, {native, ...sassOptions} = {}) { +module.exports = function sassPlugin(_, {native, compilerOptions = {}} = {}) { /** A map of partially resolved imports to the files that imported them. */ const importedByMap = new Map(); @@ -97,7 +97,7 @@ module.exports = function sassPlugin(_, {native, ...sassOptions} = {}) { } // Pass in user-defined options - Object.entries(sassOptions || {}).forEach(([flag, value]) => { + 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': { diff --git a/plugins/plugin-sass/test/plugin-mocked.test.js b/plugins/plugin-sass/test/plugin-mocked.test.js index e50ee36bb5..2a82eab6b4 100644 --- a/plugins/plugin-sass/test/plugin-mocked.test.js +++ b/plugins/plugin-sass/test/plugin-mocked.test.js @@ -10,20 +10,20 @@ const MOCK_CONFIG = null; const MOCK_LOAD = {filePath: path.join(__dirname, 'fixtures', 'scss', 'App.scss'), isDev: false}; const tests = [ - {name: 'no options', given: undefined, expect: []}, + {name: 'no options', given: {}, expect: []}, { name: 'string option', - given: {style: 'compressed'}, + given: {compilerOptions: {style: 'compressed'}}, expect: [`--style=compressed`], }, { name: 'boolean option', - given: {sourceMaps: false}, + given: {compilerOptions: {sourceMaps: false}}, expect: [`--no-source-maps`], }, { name: 'combination', - given: {style: 'compressed', sourceMaps: true}, + given: {compilerOptions: {style: 'compressed', sourceMaps: true}}, expect: [`--style=compressed`, `--source-maps`], }, ]; From e8096f1db56d121897ea900edf4b21e6b7da3640 Mon Sep 17 00:00:00 2001 From: Drew Powers Date: Wed, 14 Oct 2020 14:22:46 -0600 Subject: [PATCH 3/5] Typo --- plugins/plugin-sass/README.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/plugin-sass/README.md b/plugins/plugin-sass/README.md index 89b8abde7f..268e4c7326 100644 --- a/plugins/plugin-sass/README.md +++ b/plugins/plugin-sass/README.md @@ -30,10 +30,10 @@ 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) (default: `false`). | -| `compilerOptions.*` | `object` | Pass [Sass options](https://sass-lang.com/documentation/cli/dart-sass#options) directly to the Sass compiler (see `compilerOptions`). | +| 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](https://sass-lang.com/documentation/cli/dart-sass#options) directly to the Sass compiler (see `compilerOptions`). | ### `compilerOptions` From 943312bf362272e8bbf63d53625ff3f2eff724cb Mon Sep 17 00:00:00 2001 From: Drew Powers Date: Wed, 14 Oct 2020 14:27:14 -0600 Subject: [PATCH 4/5] Fix string --- plugins/plugin-sass/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/plugin-sass/README.md b/plugins/plugin-sass/README.md index 268e4c7326..31332e3a01 100644 --- a/plugins/plugin-sass/README.md +++ b/plugins/plugin-sass/README.md @@ -41,7 +41,7 @@ module.exports = { | :--------------- | :----------------------------: | :------------------------------------------------------------------------------------------------------ | | `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'`). | +| `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. (defaul: `true`). | From 6b8006bf419df7a5a76e0b0aaf8ac3ba3fecf1e9 Mon Sep 17 00:00:00 2001 From: Drew Powers Date: Thu, 15 Oct 2020 12:15:21 -0600 Subject: [PATCH 5/5] Update README --- plugins/plugin-sass/README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/plugins/plugin-sass/README.md b/plugins/plugin-sass/README.md index 31332e3a01..d7e3c862b6 100644 --- a/plugins/plugin-sass/README.md +++ b/plugins/plugin-sass/README.md @@ -33,10 +33,12 @@ module.exports = { | 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](https://sass-lang.com/documentation/cli/dart-sass#options) directly to the Sass compiler (see `compilerOptions`). | +| `compilerOptions.*` | `object` | Pass [Sass options][sass-options] directly to the Sass compiler (see `compilerOptions`). | ### `compilerOptions` +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'`). | @@ -44,5 +46,7 @@ module.exports = { | `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. (defaul: `true`). | +| `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