Last active
August 22, 2017 22:25
-
-
Save vuhrmeister/775aa0183b26d66aa19632f03bbb1f8e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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