diff --git a/.eslintignore b/.eslintignore index a693875c8a..a1587677ba 100644 --- a/.eslintignore +++ b/.eslintignore @@ -4,6 +4,5 @@ node_modules/ www/ icons/ dependent-usage-analyzer/ -build-scss.js component-generator/ example/ diff --git a/build-scss.js b/build-scss.js index 915df45fae..a49d591158 100755 --- a/build-scss.js +++ b/build-scss.js @@ -10,6 +10,41 @@ const { pathToFileURL } = require('url'); const path = require('path'); const { program, Option } = require('commander'); +const paragonThemeOutputFilename = 'paragon-theme.json'; + +/** + * Updates `paragonThemeOutput` object with appropriate name and URLs. + * + * @param {object} args + * @param {object} args.paragonThemeOutput Object containing the `themeUrls` pointing + * to the core and theme variant CSS files. + * @param {string} args.name Name of the theme variant. + * @param {boolean} args.isThemeVariant Indicates whether the stylesheet is a theme variant. + * + * @returns Updated paragonThemeOutput object. + */ +const updateParagonThemeOutput = ({ + paragonThemeOutput, + name, + isThemeVariant, +}) => { + if (isThemeVariant) { + paragonThemeOutput.themeUrls.variants = { + ...paragonThemeOutput.themeUrls.variants, + [name]: { + default: `./${name}.css`, + minified: `./${name}.min.css`, + }, + }; + } else { + paragonThemeOutput.themeUrls[name] = { + default: `./${name}.css`, + minified: `./${name}.min.css`, + }; + } + return paragonThemeOutput; +}; + /** * Compiles SCSS file with sass and transforms resulting file with PostCSS: * 1. Resulting CSS file @@ -17,40 +52,68 @@ const { program, Option } = require('commander'); * 3. Minified version of resulting CSS file * * @param {string} name - base name of the resulting files - * @param {string} path - path to the SCSS stylesheet + * @param {string} stylesPath - path to the stylesheet to be compiled * @param {string} outDir - indicates where to output compiled files + * @param {boolean} isThemeVariant - indicates whether the stylesheet is a theme variant */ -const compileAndWriteStyleSheets = (name, path, outDir) => { - const compiledStyleSheet = sass.compile(path, { +const compileAndWriteStyleSheets = ({ + name, + stylesPath, + outDir, + isThemeVariant = false, +}) => { + const compiledStyleSheet = sass.compile(stylesPath, { importers: [{ // An importer that redirects relative URLs starting with '~' to 'node_modules'. findFileUrl(url) { if (!url.startsWith('~')) { return null; } - return new URL(url.substring(1), `${pathToFileURL('node_modules')}/node_modules`) - } - }] + return new URL(url.substring(1), `${pathToFileURL('node_modules')}/node_modules`); + }, + }], }); + const commonPostCssPlugins = [ + postCSSCustomMedia({ preserve: true }), + postCSSImport(), + combineSelectors({ removeDuplicatedProperties: true }), + ]; - postCSS([ - postCSSCustomMedia({ preserve: true }), - postCSSImport(), - combineSelectors({ removeDuplicatedProperties: true })]) - .process(compiledStyleSheet.css, { from: path, map: { inline: false } }) + postCSS(commonPostCssPlugins) + .process(compiledStyleSheet.css, { from: stylesPath, map: { inline: false } }) .then(result => { fs.writeFileSync(`${outDir}/${name}.css`, result.css); fs.writeFileSync(`${outDir}/${name}.css.map`, result.map.toString()); + + const hasExistingParagonThemeOutput = fs.existsSync(`${outDir}/${paragonThemeOutputFilename}`); + let paragonThemeOutput; + if (!hasExistingParagonThemeOutput) { + const initialConfigOutput = { themeUrls: {} }; + paragonThemeOutput = updateParagonThemeOutput({ + paragonThemeOutput: initialConfigOutput, + name, + isThemeVariant, + }); + } else { + const existingParagonThemeOutput = JSON.parse(fs.readFileSync(`${outDir}/${paragonThemeOutputFilename}`, 'utf8')); + paragonThemeOutput = updateParagonThemeOutput({ + paragonThemeOutput: existingParagonThemeOutput, + name, + isThemeVariant, + }); + } + fs.writeFileSync(`${outDir}/${paragonThemeOutputFilename}`, `${JSON.stringify(paragonThemeOutput, null, 2)}\n`); }); postCSS([ - postCSSCustomMedia({ preserve: true }), - postCSSImport(), - postCSSMinify(), - combineSelectors({ removeDuplicatedProperties: true })]) - .process(compiledStyleSheet.css, { from: path }) - .then(result => fs.writeFileSync(`${outDir}/${name}.min.css`, result.css)); -} + ...commonPostCssPlugins, + postCSSMinify(), + ]) + .process(compiledStyleSheet.css, { from: stylesPath, map: { inline: false } }) + .then(result => { + fs.writeFileSync(`${outDir}/${name}.min.css`, result.css); + }); +}; program .version('0.0.1') @@ -58,7 +121,8 @@ program .addOption( new Option( '--corePath ', - 'Path to the theme\'s core SCSS file, defaults to Paragon\'s core.scss.') + 'Path to the theme\'s core SCSS file, defaults to Paragon\'s core.scss.', + ), ) .addOption( new Option( @@ -79,24 +143,36 @@ program where index.css has imported all other CSS files in the theme's subdirectory. The script will output light.css, dark.css and some_other_custom_theme.css files (together with maps and minified versions). You can provide any amount of themes. Default to paragon's themes. - ` - )) + `, + ), + ) .addOption( new Option( '--outDir ', - 'Specifies directory where to out resulting CSS files.' - ) + 'Specifies directory where to out resulting CSS files.', + ), ) .action(async (options) => { const { corePath = path.resolve(__dirname, 'styles/scss/core/core.scss'), themesPath = path.resolve(__dirname, 'styles/css/themes'), - outDir = './dist' + outDir = './dist', } = options; - compileAndWriteStyleSheets('core', corePath, outDir); + compileAndWriteStyleSheets({ + name: 'core', + stylesPath: corePath, + outDir, + }); fs.readdirSync(themesPath, { withFileTypes: true }) .filter((item) => item.isDirectory()) - .forEach((themeDir) => compileAndWriteStyleSheets(themeDir.name, `${themesPath}/${themeDir.name}/index.css`, outDir)) + .forEach((themeDir) => { + compileAndWriteStyleSheets({ + name: themeDir.name, + stylesPath: `${themesPath}/${themeDir.name}/index.css`, + outDir, + isThemeVariant: true, + }); + }); }); program.parse(process.argv);