Skip to content

Commit

Permalink
fix(@angular/build): watch all related files during a Sass error
Browse files Browse the repository at this point in the history
When `dart-sass` returns an error, the provided location span may not be
the exact location of the error but can instead be the location of the
`@error` call within a Sass function. The generated stack trace string
does contain all files related to the error including the usage site in
the case of the function `@error` case. To ensure all related files for
the error are watched and allow for a rebuild that may correct the error,
the stack trace is parsed and found files are added to the watch list.
Unfortunately, `dart-sass` does not provide the list directly. If that
changes in the future, the stack trace parsing could be removed.
  • Loading branch information
clydin authored and dgp1130 committed Jun 3, 2024
1 parent 22e05dc commit a5fc020
Showing 1 changed file with 32 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,11 @@ async function compileString(
if (isSassException(error)) {
const fileWithError = error.span.url ? fileURLToPath(error.span.url) : undefined;

const watchFiles = [filePath, ...extractFilesFromStack(error.sassStack)];
if (fileWithError) {
watchFiles.push(fileWithError);
}

return {
loader: 'css',
errors: [
Expand All @@ -182,7 +187,7 @@ async function compileString(
},
],
warnings,
watchFiles: fileWithError ? [filePath, fileWithError] : [filePath],
watchFiles,
};
}

Expand All @@ -202,3 +207,29 @@ function sourceMapToUrlComment(

return `/*# sourceMappingURL=data:application/json;charset=utf-8;base64,${urlSourceMap} */`;
}

function* extractFilesFromStack(stack: string): Iterable<string> {
const lines = stack.split('\n');
const cwd = process.cwd();

// Stack line has format of "<file> <location> <identifier>"
for (const line of lines) {
const segments = line.split(' ');
if (segments.length < 3) {
break;
}

// Extract path from stack line.
// Paths may contain spaces. All segments before location are part of the file path.
let path = '';
let index = 0;
while (!segments[index].match(/\d+:\d+/)) {
path += segments[index++];
}

if (path) {
// Stack paths from dart-sass are relative to the current working directory (not input file or workspace root)
yield join(cwd, path);
}
}
}

0 comments on commit a5fc020

Please sign in to comment.