diff --git a/app/javascript/packages/build-sass/CHANGELOG.md b/app/javascript/packages/build-sass/CHANGELOG.md index 0beb96c1c32..8f0ff3b2261 100644 --- a/app/javascript/packages/build-sass/CHANGELOG.md +++ b/app/javascript/packages/build-sass/CHANGELOG.md @@ -1,3 +1,13 @@ +## Unreleased + +### New Features + +- Add support for verbose CLI output using `--verbose` flag (`-v` shorthand), which currently outputs files being built. + +### Bug Fixes + +- Fix rebuild after error when using `--watch` mode. + ## 3.0.0 ### Breaking Changes diff --git a/app/javascript/packages/build-sass/README.md b/app/javascript/packages/build-sass/README.md index 31e1003cff3..4afdecc86a1 100644 --- a/app/javascript/packages/build-sass/README.md +++ b/app/javascript/packages/build-sass/README.md @@ -31,6 +31,7 @@ Flags: - `--out-dir`: The output directory - `--watch`: Run in watch mode, recompiling files on change - `--load-path`: Include additional path in Sass path resolution +- `--verbose` (`-v`): Output verbose debugging details ### API diff --git a/app/javascript/packages/build-sass/cli.js b/app/javascript/packages/build-sass/cli.js index c16e3871df0..79e29d4fd0b 100755 --- a/app/javascript/packages/build-sass/cli.js +++ b/app/javascript/packages/build-sass/cli.js @@ -14,6 +14,8 @@ import getErrorSassStackPaths from './get-error-sass-stack-paths.js'; /** @typedef {import('sass-embedded').Options<'sync'>} SyncSassOptions */ /** @typedef {import('sass-embedded').Exception} SassException */ /** @typedef {import('./').BuildOptions} BuildOptions */ +/** @typedef {import('node:child_process').ChildProcess} ChildProcess */ +/** @typedef {import('sass-embedded').AsyncCompiler & { process: ChildProcess}} SassAsyncCompiler */ const env = process.env.NODE_ENV || process.env.RAILS_ENV || 'development'; const isProduction = env === 'production'; @@ -24,10 +26,11 @@ const { values: flags, positionals: fileArgs } = parseArgs({ watch: { type: 'boolean' }, 'out-dir': { type: 'string' }, 'load-path': { type: 'string', multiple: true, default: [] }, + verbose: { type: 'boolean', short: 'v' }, }, }); -const { watch: isWatching, 'out-dir': outDir, 'load-path': loadPaths = [] } = flags; +const { watch: isWatching, 'out-dir': outDir, 'load-path': loadPaths = [], verbose } = flags; loadPaths.push(...getDefaultLoadPaths()); const sassCompiler = await initAsyncSassCompiler(); @@ -62,6 +65,10 @@ const isSassException = (error) => 'span' in /** @type {SassException} */ (error * @return {Promise} */ function build(files) { + if (verbose) { + console.log('Building files', files); + } + return Promise.all( files.map(async (file) => { const { loadedUrls } = await buildFile(file, options); @@ -75,7 +82,8 @@ function build(files) { console.error(error); if (isWatching && isSassException(error)) { - watchOnce(getErrorSassStackPaths(error.sassStack), () => build(files)); + const { spawnfile } = /** @type {SassAsyncCompiler} */ (sassCompiler).process; + watchOnce(getErrorSassStackPaths(error.sassStack, spawnfile), () => build(files)); } else { throw error; } diff --git a/app/javascript/packages/build-sass/get-error-sass-stack-paths.js b/app/javascript/packages/build-sass/get-error-sass-stack-paths.js index e7044fcb776..ca2aa1e2dcb 100644 --- a/app/javascript/packages/build-sass/get-error-sass-stack-paths.js +++ b/app/javascript/packages/build-sass/get-error-sass-stack-paths.js @@ -1,32 +1,30 @@ +import { dirname, relative, resolve } from 'path'; + /** * Returns all file paths contained in the given Sass stack trace. * * @example * ``` * getErrorSassStackPaths( - * 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/_functions.scss 35:8 divide()\n' + - * 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/mixins/_icon.scss 77:12 add-color-icon()\n' + - * 'app/assets/stylesheets/components/_alert.scss 13:5 @import\n' + - * 'app/assets/stylesheets/components/all.scss 3:9 @import\n' + - * 'app/assets/stylesheets/application.css.scss 7:9 root stylesheet\n', + * '../../../../app/assets/stylesheets/design-system-waiting-room.scss 31:2 @forward\n' + + * '../../../../app/assets/stylesheets/application.css.scss 4:1 root stylesheet\n', + * 'node_modules/sass-embedded-darwin-arm64/dart-sass/src/dart', * ); * // [ - * // 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/_functions.scss', - * // 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/mixins/_icon.scss', - * // 'app/assets/stylesheets/components/_alert.scss', - * // 'app/assets/stylesheets/components/all.scss', + * // 'app/assets/stylesheets/design-system-waiting-room.scss', * // 'app/assets/stylesheets/application.css.scss', * // ] * ``` * * @param {string} sassStack Sass stack trace (see example). + * @param {string} relativeFrom File from which to resolve relative paths from Sass stack trace. * * @return {string[]} Array of file paths. */ -const getErrorSassStackPaths = (sassStack) => +const getErrorSassStackPaths = (sassStack, relativeFrom) => sassStack .split(/\.scss \d+:\d+\s+.+?\n/) .filter(Boolean) - .map((basename) => `${basename}.scss`); + .map((basename) => relative('.', resolve(dirname(relativeFrom), `${basename}.scss`))); export default getErrorSassStackPaths; diff --git a/app/javascript/packages/build-sass/get-error-sass-stack-paths.spec.js b/app/javascript/packages/build-sass/get-error-sass-stack-paths.spec.js index c27615993ac..e2e040004af 100644 --- a/app/javascript/packages/build-sass/get-error-sass-stack-paths.spec.js +++ b/app/javascript/packages/build-sass/get-error-sass-stack-paths.spec.js @@ -1,39 +1,29 @@ import getErrorSassStackPaths from './get-error-sass-stack-paths.js'; describe('getErrorSassStackPaths', () => { - it('returns an array of paths from a sass stack message', () => { + it('returns an array of paths from a sass stack message resolved from relative file', () => { const stackPaths = getErrorSassStackPaths( - 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/_functions.scss 35:8 divide()\n' + - 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/mixins/_icon.scss 77:12 add-color-icon()\n' + - 'app/assets/stylesheets/components/_alert.scss 13:5 @import\n' + - 'app/assets/stylesheets/components/all.scss 3:9 @import\n' + - 'app/assets/stylesheets/application.css.scss 7:9 root stylesheet\n', + '../../../../app/assets/stylesheets/design-system-waiting-room.scss 31:2 @forward\n' + + '../../../../app/assets/stylesheets/application.css.scss 4:1 root stylesheet\n', + 'node_modules/sass-embedded-darwin-arm64/dart-sass/src/dart', ); expect(stackPaths).to.deep.equal([ - 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/_functions.scss', - 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/mixins/_icon.scss', - 'app/assets/stylesheets/components/_alert.scss', - 'app/assets/stylesheets/components/all.scss', + 'app/assets/stylesheets/design-system-waiting-room.scss', 'app/assets/stylesheets/application.css.scss', ]); }); context('with a stack path containing a space', () => { - it('returns an array of paths from a sass stack message', () => { + it('returns an array of paths from a sass stack message resolved from relative file', () => { const stackPaths = getErrorSassStackPaths( - 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/_functions.scss 35:8 divide()\n' + - 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/mixins/_icon.scss 77:12 add-color-icon()\n' + - 'app/assets/stylesheets/components/_alert example.scss 13:5 @import\n' + - 'app/assets/stylesheets/components/all.scss 3:9 @import\n' + - 'app/assets/stylesheets/application.css.scss 7:9 root stylesheet\n', + '../../../../app/assets/stylesheets/design-system waiting-room.scss 31:2 @forward\n' + + '../../../../app/assets/stylesheets/application.css.scss 4:1 root stylesheet\n', + 'node_modules/sass-embedded-darwin-arm64/dart-sass/src/dart', ); expect(stackPaths).to.deep.equal([ - 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/_functions.scss', - 'node_modules/@18f/identity-design-system/dist/assets/scss/uswds/core/mixins/_icon.scss', - 'app/assets/stylesheets/components/_alert example.scss', - 'app/assets/stylesheets/components/all.scss', + 'app/assets/stylesheets/design-system waiting-room.scss', 'app/assets/stylesheets/application.css.scss', ]); });