From 72de88bb96b248a6e029c56e7612d40277bdb1a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 9 Apr 2018 20:58:20 +0200 Subject: [PATCH 1/6] Write scss vars as json during compilation --- package.json | 2 + scripts/compile-scss.js | 61 ++++++++++++++++++++----- src/components/key_pad_menu/_index.scss | 2 +- yarn.lock | 37 ++++++++++++++- 4 files changed, 88 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index 7f9fa27996f..107eef669f3 100644 --- a/package.json +++ b/package.json @@ -105,6 +105,8 @@ "redux": "^3.7.2", "redux-thunk": "^2.2.0", "rimraf": "^2.6.2", + "sass-extract": "^2.1.0", + "sass-extract-js": "^0.3.0", "sass-loader": "^6.0.6", "sass-vars-to-js-loader": "^2.0.2", "shelljs": "^0.8.1", diff --git a/scripts/compile-scss.js b/scripts/compile-scss.js index 73fe1dff894..0d4c01c3e36 100755 --- a/scripts/compile-scss.js +++ b/scripts/compile-scss.js @@ -1,16 +1,53 @@ -const { execSync } = require('child_process'); +const util = require('util'); +const writeFile = util.promisify(require('fs').writeFile); +const glob = util.promisify(require('glob')); + +const chalk = require('chalk'); +const postcss = require('postcss'); +const sassExtract = require('sass-extract'); const shell = require('shelljs'); -const glob = require('glob'); shell.mkdir('dist'); -glob('./src/theme_*.scss', undefined, (error, files) => { - files.forEach(file => { - const splitPath = file.split('/'); - const fileName = splitPath[splitPath.length - 1]; - const splitFileName = fileName.split('.'); - const baseFileName = splitFileName[0]; - execSync(`node-sass ${file} > "dist/eui_${baseFileName}.css"`); - execSync(`postcss --replace --config src-docs/postcss.config.js "dist/eui_${baseFileName}.css"`); - }); -}); +glob('./src/theme_*.scss', undefined).then(files => + Promise.all( + files.map(file => { + const splitPath = file.split('/'); + const fileName = splitPath[splitPath.length - 1]; + const splitFileName = fileName.split('.'); + const baseFileName = splitFileName[0]; + const cssFileName = `dist/eui_${baseFileName}.css`; + const varsFileName = `dist/eui_${baseFileName}.json`; + + console.log(`… Processing "${file}"`); + return sassExtract + .render( + { + file, + outFile: cssFileName, + }, + { + plugins: [{ plugin: 'sass-extract-js' }], + } + ) + .then(({ css, vars }) => + postcss(require('../src-docs/postcss.config.js')) + .process(css, { from: cssFileName, to: cssFileName }) + .then(processedCss => ({ + processedCss, + vars, + })) + ) + .then(({ processedCss, vars }) => { + console.log(`… Writing theme css to "${cssFileName}"`); + console.log(`… Writing theme variables to "${varsFileName}"`); + return Promise.all([ + writeFile(cssFileName, processedCss.css), + writeFile(varsFileName, JSON.stringify(vars, undefined, 2)), + ]).then(() => { + console.log(chalk.green(`✔ Finished processing "${file}"`)); + }); + }); + }) + ) +); diff --git a/src/components/key_pad_menu/_index.scss b/src/components/key_pad_menu/_index.scss index 4e5eeabf111..fa4d726dbce 100644 --- a/src/components/key_pad_menu/_index.scss +++ b/src/components/key_pad_menu/_index.scss @@ -2,4 +2,4 @@ $euiKeyPadMenuSize: $euiSize * 6; -@import 'key_pad_menu' +@import 'key_pad_menu'; diff --git a/yarn.lock b/yarn.lock index c3befe13e15..375bc03be51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1472,7 +1472,7 @@ callsites@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-2.0.0.tgz#06eb84f00eea413da86affefacbffb36093b3c50" -camel-case@3.0.x: +camel-case@3.0.x, camel-case@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" dependencies: @@ -1807,6 +1807,10 @@ color-convert@^1.3.0, color-convert@^1.9.0: dependencies: color-name "^1.1.1" +color-convert@~0.5.0: + version "0.5.3" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-0.5.3.tgz#bdb6c69ce660fadffe0b0007cc447e1b9f7282bd" + color-name@^1.0.0, color-name@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25" @@ -3680,6 +3684,12 @@ globule@^1.0.0: lodash "~4.17.4" minimatch "~3.0.2" +gonzales-pe@^4.2.2: + version "4.2.3" + resolved "https://registry.yarnpkg.com/gonzales-pe/-/gonzales-pe-4.2.3.tgz#41091703625433285e0aee3aa47829fc1fbeb6f2" + dependencies: + minimist "1.1.x" + got@^5.0.0: version "5.7.1" resolved "https://registry.yarnpkg.com/got/-/got-5.7.1.tgz#5f81635a61e4a6589f180569ea4e381680a51f35" @@ -5693,6 +5703,10 @@ minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" +minimist@1.1.x: + version "1.1.3" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.1.3.tgz#3bedfd91a92d39016fcfaa1c681e8faa1a1efda8" + minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" @@ -6401,6 +6415,12 @@ parse-asn1@^5.0.0: evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" +parse-color@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/parse-color/-/parse-color-1.0.0.tgz#7b748b95a83f03f16a94f535e52d7f3d94658619" + dependencies: + color-convert "~0.5.0" + parse-glob@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" @@ -7778,6 +7798,21 @@ sane@^2.0.0: optionalDependencies: fsevents "^1.1.1" +sass-extract-js@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/sass-extract-js/-/sass-extract-js-0.3.0.tgz#3fc5be20d84ce55c29a8b089a49254fbfb69a2a3" + dependencies: + camel-case "^3.0.0" + +sass-extract@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/sass-extract/-/sass-extract-2.1.0.tgz#c65e6ca3103cbcf2fca0dcd81b07e4e49a6cc583" + dependencies: + bluebird "^3.4.7" + gonzales-pe "^4.2.2" + parse-color "^1.0.0" + query-ast "^1.0.1" + sass-graph@^2.2.4: version "2.2.4" resolved "https://registry.yarnpkg.com/sass-graph/-/sass-graph-2.2.4.tgz#13fbd63cd1caf0908b9fd93476ad43a51d1e0b49" From 1e909466d670d0c69a84213c2ff61616b781a876 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Mon, 9 Apr 2018 21:09:34 +0200 Subject: [PATCH 2/6] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3058fd992ba..ec7de74f769 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ # [`master`](https://github.com/elastic/eui/tree/master) - Add disabled prop to `EuiComboBoxOption` ([#650](https://github.com/elastic/eui/pull/650)) +- Export sass theme variables in json format during compilation ([#642](https://github.com/elastic/eui/pull/642)) # [`0.0.40`](https://github.com/elastic/eui/tree/v0.0.40) From 4c5cd9f8f4c0bb7cae63277d531850575f7dfc9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Tue, 10 Apr 2018 12:24:05 +0200 Subject: [PATCH 3/6] Flatten Promise chains, improve error handling --- scripts/compile-scss.js | 116 ++++++++++++++++++++++++---------------- 1 file changed, 69 insertions(+), 47 deletions(-) diff --git a/scripts/compile-scss.js b/scripts/compile-scss.js index 0d4c01c3e36..d1a217ad1b8 100755 --- a/scripts/compile-scss.js +++ b/scripts/compile-scss.js @@ -1,53 +1,75 @@ +const path = require('path'); const util = require('util'); -const writeFile = util.promisify(require('fs').writeFile); -const glob = util.promisify(require('glob')); +const fs = require('fs'); +const globModule = require('glob'); const chalk = require('chalk'); const postcss = require('postcss'); const sassExtract = require('sass-extract'); -const shell = require('shelljs'); - -shell.mkdir('dist'); - -glob('./src/theme_*.scss', undefined).then(files => - Promise.all( - files.map(file => { - const splitPath = file.split('/'); - const fileName = splitPath[splitPath.length - 1]; - const splitFileName = fileName.split('.'); - const baseFileName = splitFileName[0]; - const cssFileName = `dist/eui_${baseFileName}.css`; - const varsFileName = `dist/eui_${baseFileName}.json`; - - console.log(`… Processing "${file}"`); - return sassExtract - .render( - { - file, - outFile: cssFileName, - }, - { - plugins: [{ plugin: 'sass-extract-js' }], - } - ) - .then(({ css, vars }) => - postcss(require('../src-docs/postcss.config.js')) - .process(css, { from: cssFileName, to: cssFileName }) - .then(processedCss => ({ - processedCss, - vars, - })) - ) - .then(({ processedCss, vars }) => { - console.log(`… Writing theme css to "${cssFileName}"`); - console.log(`… Writing theme variables to "${varsFileName}"`); - return Promise.all([ - writeFile(cssFileName, processedCss.css), - writeFile(varsFileName, JSON.stringify(vars, undefined, 2)), - ]).then(() => { - console.log(chalk.green(`✔ Finished processing "${file}"`)); - }); - }); + +const postcssConfiguration = require('../src-docs/postcss.config.js'); + +const writeFile = util.promisify(fs.writeFile); +const mkdir = util.promisify(fs.mkdir); +const glob = util.promisify(globModule); + +async function compileScssFiles(sourcePattern, destinationDirectory) { + try { + await mkdir(destinationDirectory); + } catch (err) { + if (err.code !== 'EEXIST') { + throw err; + } + } + + const inputFilenames = await glob(sourcePattern, undefined); + + await Promise.all( + inputFilenames.map(async inputFilename => { + console.log(chalk`{cyan …} Compiling {gray ${inputFilename}}`); + + try { + const { name } = path.parse(inputFilename); + const outputFilenames = await compileScssFile( + inputFilename, + path.join(destinationDirectory, `eui_${name}.css`), + path.join(destinationDirectory, `eui_${name}.json`) + ); + + console.log( + chalk`{green ✔} Finished compiling {gray ${inputFilename}} to ${outputFilenames + .map(filename => chalk.gray(filename)) + .join(', ')}` + ); + } catch (error) { + console.log(chalk`{red ✗} Failed to compile {gray ${inputFilename}} with ${error.stack}`); + } }) - ) -); + ); +} + +async function compileScssFile(inputFilename, outputCssFilename, outputVarsFilename) { + const { css: renderedCss, vars: extractedVars } = await sassExtract.render( + { + file: inputFilename, + outFile: outputCssFilename, + }, + { + plugins: [{ plugin: 'sass-extract-js' }], + } + ); + + const { css: postprocessedCss } = await postcss(postcssConfiguration).process(renderedCss, { + from: outputCssFilename, + to: outputCssFilename, + }); + + await Promise.all([ + writeFile(outputCssFilename, postprocessedCss), + writeFile(outputVarsFilename, JSON.stringify(extractedVars, undefined, 2)), + ]); + + return [outputCssFilename, outputVarsFilename]; +} + +compileScssFiles(path.join('src', 'theme_*.scss'), 'dist'); From 5d29f83b7403746ca201086eed82d6aed0e56f04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 11 Apr 2018 13:59:13 +0200 Subject: [PATCH 4/6] Add documentation for json consumption --- wiki/consuming.md | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/wiki/consuming.md b/wiki/consuming.md index 85d40d51899..d5b2e9d7472 100644 --- a/wiki/consuming.md +++ b/wiki/consuming.md @@ -56,6 +56,28 @@ If you want access to the Sass variables, functions, and mixins in EUI then you' @import '../node_modules/@elastic/eui/src/theme_light.scss'; ``` +### Reusing the variables in JavaScript + +The Sass variables are also made available for consumption as json files. This enables reuse of values in css-in-js systems like [styled-components](https://www.styled-components.com). As the following example shows, it can also make the downstream components theme-aware without much extra effort: + +```js +import * as React from 'react'; +import * as ReactDOM from 'react-dom'; +import styled, { ThemeProvider } from 'styled-components'; +import * as euiVars from '@elastic/eui/dist/eui_theme_k6_light.json'; + +const CustomComponent = styled.div` + color: ${props => props.theme.euiColorPrimary}; + border: ${props => props.theme.euiBorderThin}; +`; + +ReactDOM.render( + + content + +, document.querySelector('#renderTarget')); +``` + ### "Module build failed" or "Module parse failed: Unexpected token" error If you get an error when importing a React component, you might need to configure Webpack's `resolve.mainFields` to `['webpack', 'browser', 'main']` to import the components from `lib` intead of `src`. See the [Webpack docs](https://webpack.js.org/configuration/resolve/#resolve-mainfields) for more info. From 954b09338a4c57da6bb7f9fdc01c66f8a6096e74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 11 Apr 2018 15:18:07 +0200 Subject: [PATCH 5/6] Add basic typescript definitions for theme json files --- src/index.d.ts | 1 + src/themes/index.d.ts | 4 ++++ 2 files changed, 5 insertions(+) create mode 100644 src/themes/index.d.ts diff --git a/src/index.d.ts b/src/index.d.ts index 7acdb9091d4..4355a412ee7 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -1,2 +1,3 @@ /// /// +/// diff --git a/src/themes/index.d.ts b/src/themes/index.d.ts new file mode 100644 index 00000000000..dbebe469845 --- /dev/null +++ b/src/themes/index.d.ts @@ -0,0 +1,4 @@ +declare module "@elastic/eui/dist/eui_theme_*.json" { + const value: any; + export default value; +} From 785f657a34eefb515979a92ec2ff9cafdace2a2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20St=C3=BCrmer?= Date: Wed, 11 Apr 2018 15:58:10 +0200 Subject: [PATCH 6/6] Change changelog entry tense to past --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec7de74f769..16eccf6b8ff 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # [`master`](https://github.com/elastic/eui/tree/master) - Add disabled prop to `EuiComboBoxOption` ([#650](https://github.com/elastic/eui/pull/650)) -- Export sass theme variables in json format during compilation ([#642](https://github.com/elastic/eui/pull/642)) +- Added export of sass theme variables in json format during compilation ([#642](https://github.com/elastic/eui/pull/642)) # [`0.0.40`](https://github.com/elastic/eui/tree/v0.0.40)