Commit 9596055e authored by Robert Knight's avatar Robert Knight

Change SASS processing pipeline to match h, lms projects

Change the SASS processing pipeline to match the h and lms projects. The
purpose of this is cross-project consistency for easier maintenance,
consistent processing of SASS files from the upcoming pattern library
and to enable us to use the new SASS module system [1]

The main changes are:

 - Use Dart Sass (the canonical SASS implementation) instead of node-sass.

   This removes a native dependency which occassionally causes build
   headaches (eg. when the installed Node version changes) and will also enable
   us to make use of the new SASS module system.

 - Use a small utility module, `create-style-bundle.js`, that calls the SASS
   compiler directly instead of using gulp plugins. This simplifies
   understanding the build process by removing dependencies and is
   consistent with the h and lms projects.

   This module was copied from the hypothesis/lms repo and modified to
   add the `postcss-url` URL rewriting plugin that the previous
   gulp-sass based pipeline had.

[1] http://sass.logdown.com/posts/7858341-the-module-system-is-launched
parent bc30c556
...@@ -2,23 +2,20 @@ ...@@ -2,23 +2,20 @@
'use strict'; 'use strict';
const { mkdirSync } = require('fs');
const path = require('path'); const path = require('path');
const changed = require('gulp-changed'); const changed = require('gulp-changed');
const commander = require('commander'); const commander = require('commander');
const debounce = require('lodash.debounce'); const debounce = require('lodash.debounce');
const gulp = require('gulp'); const gulp = require('gulp');
const gulpIf = require('gulp-if');
const gulpUtil = require('gulp-util'); const gulpUtil = require('gulp-util');
const postcss = require('gulp-postcss');
const postcssURL = require('postcss-url');
const replace = require('gulp-replace'); const replace = require('gulp-replace');
const rename = require('gulp-rename'); const rename = require('gulp-rename');
const sass = require('gulp-sass');
const sourcemaps = require('gulp-sourcemaps');
const through = require('through2'); const through = require('through2');
const createBundle = require('./scripts/gulp/create-bundle'); const createBundle = require('./scripts/gulp/create-bundle');
const createStyleBundle = require('./scripts/gulp/create-style-bundle');
const manifest = require('./scripts/gulp/manifest'); const manifest = require('./scripts/gulp/manifest');
const servePackage = require('./scripts/gulp/serve-package'); const servePackage = require('./scripts/gulp/serve-package');
const vendorBundles = require('./scripts/gulp/vendor-bundles'); const vendorBundles = require('./scripts/gulp/vendor-bundles');
...@@ -56,10 +53,6 @@ function parseCommandLine() { ...@@ -56,10 +53,6 @@ function parseCommandLine() {
const taskArgs = parseCommandLine(); const taskArgs = parseCommandLine();
function isSASSFile(file) {
return file.path.match(/\.scss$/);
}
function getEnv(key) { function getEnv(key) {
if (!process.env.hasOwnProperty(key)) { if (!process.env.hasOwnProperty(key)) {
throw new Error(`Environment variable ${key} is not set`); throw new Error(`Environment variable ${key} is not set`);
...@@ -175,7 +168,7 @@ gulp.task( ...@@ -175,7 +168,7 @@ gulp.task(
}) })
); );
const styleFiles = [ const cssBundles = [
// H // H
'./src/styles/annotator/annotator.scss', './src/styles/annotator/annotator.scss',
'./src/styles/annotator/pdfjs-overrides.scss', './src/styles/annotator/pdfjs-overrides.scss',
...@@ -189,35 +182,21 @@ const styleFiles = [ ...@@ -189,35 +182,21 @@ const styleFiles = [
]; ];
gulp.task('build-css', function() { gulp.task('build-css', function() {
// Rewrite font URLs to look for fonts in 'build/fonts' instead of mkdirSync(STYLE_DIR, { recursive: true });
// 'build/styles/fonts' const bundles = cssBundles.map(entry =>
function rewriteCSSURL(asset) { createStyleBundle({
return asset.url.replace(/^fonts\//, '../fonts/'); input: entry,
} output: `${STYLE_DIR}/${path.basename(entry, path.extname(entry))}.css`,
minify: IS_PRODUCTION_BUILD,
const sassOpts = { })
outputStyle: IS_PRODUCTION_BUILD ? 'compressed' : 'nested', );
}; return Promise.all(bundles);
const cssURLRewriter = postcssURL({
url: rewriteCSSURL,
});
return gulp
.src(styleFiles)
.pipe(sourcemaps.init())
.pipe(gulpIf(isSASSFile, sass(sassOpts).on('error', sass.logError)))
.pipe(postcss([require('autoprefixer'), cssURLRewriter]))
.pipe(sourcemaps.write('.'))
.pipe(gulp.dest(STYLE_DIR));
}); });
gulp.task( gulp.task(
'watch-css', 'watch-css',
gulp.series('build-css', function watchCSS() { gulp.series('build-css', function watchCSS() {
const vendorCSS = styleFiles.filter(function(path) { const vendorCSS = cssBundles.filter(path => path.endsWith('.css'));
return path.endsWith('.css');
});
const styleFileGlobs = vendorCSS.concat('./src/styles/**/*.scss'); const styleFileGlobs = vendorCSS.concat('./src/styles/**/*.scss');
gulp.watch(styleFileGlobs, gulp.task('build-css')); gulp.watch(styleFileGlobs, gulp.task('build-css'));
......
...@@ -64,7 +64,6 @@ ...@@ -64,7 +64,6 @@
"gulp-postcss": "^8.0.0", "gulp-postcss": "^8.0.0",
"gulp-rename": "^1.2.2", "gulp-rename": "^1.2.2",
"gulp-replace": "^1.0.0", "gulp-replace": "^1.0.0",
"gulp-sass": "^4.0.2",
"gulp-sourcemaps": "^2.6.4", "gulp-sourcemaps": "^2.6.4",
"gulp-util": "^3.0.7", "gulp-util": "^3.0.7",
"hammerjs": "^2.0.4", "hammerjs": "^2.0.4",
...@@ -99,6 +98,7 @@ ...@@ -99,6 +98,7 @@
"request": "^2.76.0", "request": "^2.76.0",
"reselect": "^4.0.0", "reselect": "^4.0.0",
"retry": "^0.12.0", "retry": "^0.12.0",
"sass": "^1.23.0",
"scroll-into-view": "^1.8.2", "scroll-into-view": "^1.8.2",
"seamless-immutable": "^7.1.4", "seamless-immutable": "^7.1.4",
"shallowequal": "^1.1.0", "shallowequal": "^1.1.0",
......
'use strict';
const fs = require('fs');
const path = require('path');
const autoprefixer = require('autoprefixer');
const postcss = require('postcss');
const postcssURL = require('postcss-url');
const sass = require('sass');
/**
* @typedef Options
* @prop {string} input - Input file path
* @prop {boolean} minify - Whether to minify the generated bundle
* @prop {string} output - Output file path
*/
/**
* Compile a SASS file and postprocess the result.
*
* @param {Options} options - Object specifying the input and output paths and
* whether to minify the result.
* @return {Promise} Promise for completion of the build.
*/
async function compileSass({ input, minify, output }) {
const sourcemapPath = output + '.map';
let result = sass.renderSync({
file: input,
includePaths: [path.dirname(input), 'node_modules'],
outputStyle: minify ? 'compressed' : 'expanded',
sourceMap: sourcemapPath,
});
// Rewrite font URLs to look for fonts in 'build/fonts' instead of
// 'build/styles/fonts'
function rewriteCSSURL(asset) {
return asset.url.replace(/^fonts\//, '../fonts/');
}
const cssURLRewriter = postcssURL({
url: rewriteCSSURL,
});
const postcssPlugins = [autoprefixer, cssURLRewriter];
result = await postcss(postcssPlugins).process(result.css, {
from: output,
to: output,
map: {
inline: false,
prev: result.map.toString(),
},
});
fs.writeFileSync(output, result.css);
fs.writeFileSync(sourcemapPath, result.map.toString());
}
module.exports = compileSass;
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment