Skip to content

Instantly share code, notes, and snippets.

@vuhrmeister
Last active August 22, 2017 22:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vuhrmeister/775aa0183b26d66aa19632f03bbb1f8e to your computer and use it in GitHub Desktop.
Save vuhrmeister/775aa0183b26d66aa19632f03bbb1f8e to your computer and use it in GitHub Desktop.
class SassCompiler extends MultiFileCachingCompiler {
// That's a method to override from Meteor
// see: https://github.com/meteor/meteor/blob/devel/packages/caching-compiler/multi-file-caching-compiler.js#L57
// Meteor has a build system with caching files.
// Meteor will find all relevant files with given extension and call this method on each of them
// passing the processed file and all other files that matches the extension.
compileOneFile(inputFile, allFiles) {
let matchHistory = [];
let prevMatch = '';
const importer = (url, prev, done) => {
let tempMatchHistory = [...matchHistory];
let foundMatch = false;
for (let i = matchHistory.length -1; i > -1; i--) {
if (matchHistory[i][0] === prev) {
matchHistory = tempMatchHistory;
foundMatch = true;
break;
}
tempMatchHistory.pop();
}
if (!foundMatch) {
matchHistory.push([prev, prevMatch]);
}
// Some magic function to get a real existing path
const matchedPath = this.matchRelativePath(url, matchHistory[matchHistory.length -1][1]);
if (matchedPath) {
prevMatch = matchedPath;
// Variant A – does not work, throws Exception with "Undefined variable"
// Since Meteor passes all found / cached files we have the content already so
// the should not need to read every single file.
// done({ contents: allFiles.get(matchedPath).getContentsAsString() });
// Variant B
const appdir = process.env.PWD || process.cwd();
const fileName = path.join(appdir, matchedPath.replace('{}', ''));
done({ file: fileName });
} else {
const error = new Error(`File to import: ${url} not found in file: ${matchHistory[matchHistory.length -1][0]}`);
done(error);
}
}
const future = new Future;
const options = {
// Although sourceMap is turned on, I omitted the code in this example
sourceMap: true,
sourceMapContents: true,
sourceMapEmbed: false,
sourceComments: false,
omitSourceMapUrl: true,
sourceMapRoot: '.',
includePaths: [],
importer,
indentedSyntax: inputFile.getExtension() === 'sass',
outFile: '.' + inputFile.getBasename(),
// see: https://github.com/meteor/meteor/blob/devel/packages/caching-compiler/multi-file-caching-compiler.js#L74-L80
file: this.getAbsoluteImportPath(inputFile),
data: inputFile.getContentsAsBuffer().toString('utf8'),
};
// If the file is empty, options.data is an empty string
// In that case options.file will be used by node-sass,
// which it can not read since it will contain a meteor package or app reference '{}'
// This is one workaround, another one would be to not set options.file, in which case the importer 'prev' will be 'stdin'
// However, this would result in problems if a file named stdín.scss would exist.
// Not the most elegant of solutions, but it works.
if (!options.data.trim()) {
options.data = "$fakevariable : blue;"
}
let output;
try {
sass.render(options, future.resolver());
output = future.wait();
} catch (e) {
inputFile.error({
message: `Scss compiler error: ${e.formatted}\n`,
sourcePath: inputFile.getDisplayPath()
});
return null;
}
if (!output) {
return;
}
return {
referencedImportPaths: referencedImportPaths.filter((item, index, array) => array.indexOf(item) === index),
compileResult: {
css: output.css.toString('utf-8')
}
};
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment