diff --git a/change/just-scripts-2020-10-01-10-54-45-node-sass.json b/change/just-scripts-2020-10-01-10-54-45-node-sass.json new file mode 100644 index 00000000..81860445 --- /dev/null +++ b/change/just-scripts-2020-10-01-10-54-45-node-sass.json @@ -0,0 +1,8 @@ +{ + "type": "patch", + "comment": "adds some safe-guard against stalling with node-sass in sassTask", + "packageName": "just-scripts", + "email": "kchau@microsoft.com", + "dependentChangeType": "patch", + "date": "2020-10-01T17:54:45.328Z" +} diff --git a/packages/just-scripts/src/tasks/sassTask.ts b/packages/just-scripts/src/tasks/sassTask.ts index 9a57e8d2..6a2b1c46 100644 --- a/packages/just-scripts/src/tasks/sassTask.ts +++ b/packages/just-scripts/src/tasks/sassTask.ts @@ -10,6 +10,8 @@ export interface SassTaskOptions { postcssPlugins?: any[]; } +let processHandlerSet = false; + export function sassTask(options: SassTaskOptions): TaskFunction; /** @deprecated Use object param version */ export function sassTask(createSourceModule: (fileName: string, css: string) => string, postcssPlugins?: any[]): TaskFunction; @@ -17,6 +19,9 @@ export function sassTask( optionsOrCreateSourceModule: SassTaskOptions | ((fileName: string, css: string) => string), postcssPlugins?: any[] ): TaskFunction { + // node-sass causes strange behavior when it fails. It sometimes hangs the whole process + setUncaughtExceptionHandler(); + let createSourceModule: (fileName: string, css: string) => string; if (typeof optionsOrCreateSourceModule === 'function') { createSourceModule = optionsOrCreateSourceModule; @@ -35,7 +40,7 @@ export function sassTask( if (!nodeSass || !postcss || !autoprefixer) { logger.warn('One of these [node-sass, postcss, autoprefixer] is not installed, so this task has no effect'); - done(); + cleanUpAndDone(done); return; } @@ -84,9 +89,9 @@ export function sassTask( } ); - parallelLimit(tasks, 5, done); + parallelLimit(tasks, 5, err => cleanUpAndDone(done, err)); } else { - done(); + cleanUpAndDone(done); } }; } @@ -107,3 +112,43 @@ function patchSassUrl(url: string, _prev: string, _done: any) { return { file: newUrl }; } + +/** + * sets up an uncaughtException handler for node-sass + */ +function setUncaughtExceptionHandler() { + if (!processHandlerSet) { + processHandlerSet = true; + process.on('uncaughtException', uncaughtExceptionHandler); + } +} + +/** + * unsets the uncaughtException handler for node-sass + */ +function unsetUncaughtExceptionHandler() { + if (processHandlerSet) { + processHandlerSet = false; + process.off('uncaughtException', uncaughtExceptionHandler); + } +} + +/** + * The uncaughtExceptionHandler is a work around for node-sass hanging + * See: https://github.com/sass/node-sass/issues/1048 + * @param e + */ +function uncaughtExceptionHandler(e: Error) { + console.error(e.stack); + process.kill(process.pid, 'SIGINT'); +} + +/** + * In order for us to clean up the exception handler, we attach the clean up to the "done" callback + * @param done + * @param e + */ +function cleanUpAndDone(done: (e?: Error) => void, e?: Error) { + unsetUncaughtExceptionHandler(); + return done(e); +}