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

[BD-46] feat: Paragon theme CSS is built and published independently of consuming applications #2102

Merged
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
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ Usage for Open edX and others:

```
// ... Any custom SCSS variables should be defined here
@import '~@edx/paragon/scss/core/core.scss';
@import '~@edx/paragon/styles/scss/core/core.scss';
```

Usage on with `@edx/brand`:
Expand All @@ -49,7 +49,7 @@ Usage on with `@edx/brand`:
```
@import '~@edx/brand/paragon/fonts.scss';
@import '~@edx/brand/paragon/variables.scss';
@import '~@edx/paragon/scss/core/core.scss';
@import '~@edx/paragon/styles/scss/core/core.scss';
@import '~@edx/brand/paragon/overrides.scss';
```

Expand Down Expand Up @@ -138,7 +138,7 @@ module.exports = {
dist: The sub-directory of the source code where it puts its build artifact. Often "dist".
*/
localModules: [
{ moduleName: '@edx/paragon/scss/core', dir: '../src/paragon', dist: 'scss/core' },
{ moduleName: '@edx/paragon/styles/scss/core', dir: '../src/paragon', dist: 'styles/scss/core' },
{ moduleName: '@edx/paragon/icons', dir: '../src/paragon', dist: 'icons' },
// Note that using dist: 'dist' will require you to run 'npm build' in Paragon
// to add local changes to the 'dist' directory, so that they can be picked up by the MFE.
Expand All @@ -148,7 +148,7 @@ module.exports = {
};
```

Then, when importing Paragon's core SCSS in your MFE the import needs to begin with a tilde `~` so that path to your local Paragon repository gets resolved correctly: `@import "~@edx/paragon/scss/core";`
Then, when importing Paragon's core SCSS in your MFE the import needs to begin with a tilde `~` so that path to your local Paragon repository gets resolved correctly: `@import "~@edx/paragon/styles/scss/core";`

#### Internationalization

Expand Down
115 changes: 97 additions & 18 deletions build-scss.js
100644 → 100755
Original file line number Diff line number Diff line change
@@ -1,23 +1,102 @@
var path = require('path');
var sass = require('sass');
var fs = require('fs');
#!/usr/bin/env node
const fs = require('fs');
const sass = require('sass');
const postCSS = require('postcss');
const postCSSCustomMedia = require('postcss-custom-media');
const postCSSImport = require('postcss-import');
const postCSSMinify = require('postcss-minify');
const combineSelectors = require('postcss-combine-duplicated-selectors');
const { pathToFileURL } = require('url');
const path = require('path');
const { program, Option } = require('commander');

/**
* Compiles SCSS file with sass and transforms resulting file with PostCSS:
* 1. Resulting CSS file
* 2. Map file
* 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} outDir - indicates where to output compiled files
*/
const compileAndWriteStyleSheets = (name, path, outDir) => {
const compiledStyleSheet = sass.compile(path, {
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`)
}
}]
});

// Resolve tildas the way webpack does
var tildaImporter = function(url, prev, done) {
if (url[0] === '~') {
url = path.resolve('node_modules', url.substr(1));
}
postCSS([
postCSSCustomMedia({ preserve: true }),
postCSSImport(),
combineSelectors({ removeDuplicatedProperties: true })])
.process(compiledStyleSheet.css, { from: path, map: { inline: false } })
.then(result => {
fs.writeFileSync(`${outDir}/${name}.css`, result.css);
fs.writeFileSync(`${outDir}/${name}.css.map`, result.map.toString());
});

return { file: url };
};
postCSS([
postCSSCustomMedia({ preserve: true }),
postCSSImport(),
postCSSMinify(),
combineSelectors({ removeDuplicatedProperties: true })])
.process(compiledStyleSheet.css, { from: path })
.then(result => fs.writeFileSync(`${outDir}/${name}.min.css`, result.css));
}

program
.version('0.0.1')
.description('CLI to compile Paragon\'s core and themes\' SCSS into CSS.')
.addOption(
new Option(
'--corePath <corePath>',
'Path to the theme\'s core SCSS file, defaults to Paragon\'s core.scss.')
)
.addOption(
new Option(
'--themesPath <themesPath>',
`Path to the directory that contains themes' files. Expects directory to have following structure:
themes/
light/
│ ├─ index.css
│ ├─ other_css_files
dark/
│ ├─ index.css
│ ├─ other_css_files
some_other_custom_theme/
│ ├─ index.css
│ ├─ other_css_files
...

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 <outDir>',
'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'
} = options;
compileAndWriteStyleSheets('core', corePath, outDir);
fs.readdirSync(themesPath, { withFileTypes: true })
.filter((item) => item.isDirectory())
.forEach((themeDir) => compileAndWriteStyleSheets(themeDir.name, `${themesPath}/${themeDir.name}/index.css`, outDir))
});

// Core paragon style
var coreResult = sass.renderSync({
file: './scss/core/core.scss',
outputStyle: 'compressed',
importer: tildaImporter,
});

fs.writeFileSync('./dist/paragon.css', coreResult.css);
program.parse(process.argv);
2 changes: 1 addition & 1 deletion example/module.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
localModules: [
{ moduleName: '@edx/paragon/scss/core', dir: '..', dist: 'scss/core' },
{ moduleName: '@edx/paragon/scss/core', dir: '..', dist: 'styles/scss/core' },
{ moduleName: '@edx/paragon/icons', dir: '..', dist: 'icons' },
{ moduleName: '@edx/paragon', dir: '..', dist: 'src' },
],
Expand Down
Loading