Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

broccoli-eyeglass

Package Overview
Dependencies
Maintainers
4
Versions
84
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

broccoli-eyeglass - npm Package Compare versions

Comparing version 3.0.1 to 4.0.0

.eslintrc.js

9

Gruntfile.js

@@ -0,4 +1,7 @@

"use strict";
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
pkg: grunt.file.readJSON("package.json"),
release: {

@@ -9,5 +12,5 @@ options: {

});
grunt.loadNpmTasks('grunt-release');
grunt.registerTask('default', []);
grunt.loadNpmTasks("grunt-release");
grunt.registerTask("default", []);
};
"use strict";
var path = require("path");
var crypto = require("crypto");
var debugGenerator = require("debug");
var debug = debugGenerator("broccoli-eyeglass");
var hotCacheDebug = debugGenerator("broccoli-eyeglass:hot-cache");
var persistentCacheDebug = debugGenerator("broccoli-eyeglass:persistent-cache");
var fs = require("fs");
var RSVP = require("rsvp");
var mkdirp = require("mkdirp");
const path = require("path");
const crypto = require("crypto");
const debugGenerator = require("debug");
const debug = debugGenerator("broccoli-eyeglass");
const hotCacheDebug = debugGenerator("broccoli-eyeglass:hot-cache");
const persistentCacheDebug = debugGenerator("broccoli-eyeglass:persistent-cache");
const fs = require("fs");
const RSVP = require("rsvp");
const mkdirp = require("mkdirp");
mkdirp.promise = mkdirp.promise || RSVP.denodeify(mkdirp);
var BroccoliPlugin = require("broccoli-plugin");
var sass = require("node-sass");
var glob = require("glob");
var FSTree = require("fs-tree-diff");
var FSTreeFromEntries = FSTree.fromEntries;
var walkSync = require("walk-sync");
var os = require("os");
var queue = require("async-promise-queue");
const BroccoliPlugin = require("broccoli-plugin");
const sass = require("node-sass");
const glob = require("glob");
const FSTree = require("fs-tree-diff");
const FSTreeFromEntries = FSTree.fromEntries;
const walkSync = require("walk-sync");
const os = require("os");
const queue = require("async-promise-queue");
require("string.prototype.startswith");
const READ_AS_UTF_8 = {encoding: "utf8"};
const readFile = RSVP.denodeify(fs.readFile);
const writeFile = RSVP.denodeify(fs.writeFile);
function absolutizeEntries(entries) {
// We make everything absolute because relative path comparisons don't work for us.
entries.forEach(entry => {
// TODO support windows paths
entry.relativePath = path.join(entry.basePath, entry.relativePath);
entry.basePath = "/";
});
}
class Entry {
constructor(path) {
let stats = fs.statSync(path);
this.relativePath = path;
this.basePath = "/";
this.mode = stats.mode;
this.size = stats.size;
this.mtime = stats.mtime;
}
isDirectory() {
return false;
}
}
function unique(array) {
var o = {};
var rv = [];
for (var i = 0; i < array.length; i++) {
let o = {};
let rv = [];
for (let i = 0; i < array.length; i++) {
o[array[i]] = array[i];
}
for (var k in o) {
for (let k in o) {
if (o.hasOwnProperty(k)) {

@@ -39,9 +68,7 @@ rv.push(o[k]);

// Standard async rendering for node-sass.
var renderSass = RSVP.denodeify(sass.render);
const renderSass = RSVP.denodeify(sass.render);
// This promise runs sass synchronously
function renderSassSync(options) {
return new RSVP.Promise(function(resolve) {
resolve(sass.renderSync(options));
});
return new RSVP.Promise(resolve => resolve(sass.renderSync(options)));
}

@@ -51,4 +78,2 @@

function copyObject(obj) {
var newObj, key;
if (typeof obj !== "object") {

@@ -58,10 +83,8 @@ return obj;

if (obj.forEach) {
newObj = [];
obj.forEach(function(o) {
newObj.push(copyObject(o));
});
let newObj = [];
obj.forEach(o => newObj.push(copyObject(o)));
return newObj;
} else {
newObj = {};
for (key in obj) {
let newObj = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {

@@ -77,4 +100,4 @@ newObj[key] = copyObject(obj[key]);

if (arguments.length > 3) {
var returnValues = [];
for (var i = 2; i < arguments.length; i++) {
let returnValues = [];
for (let i = 2; i < arguments.length; i++) {
returnValues[i - 2] = moveOption(source, destination, arguments[i]);

@@ -95,4 +118,4 @@ }

}
var newFileNames = [];
for (var i = 0; i < fileNames.length; i++) {
let newFileNames = [];
for (let i = 0; i < fileNames.length; i++) {
if (fileNames[i].indexOf(prefix) === 0) {

@@ -121,3 +144,3 @@ newFileNames[i] = fileNames[i].substring(prefix.length);

} else {
var parsed = {
let parsed = {
dir: path.dirname(pathname),

@@ -188,604 +211,592 @@ base: path.basename(pathname),

//
function BroccoliSassCompiler(inputTree, options) {
if (Array.isArray(inputTree)) {
if (inputTree.length > 1) {
console.warn("Support for passing several trees to BroccoliSassCompiler has been removed.\n" +
"Passing the trees to broccoli-merge-trees with the overwrite option set,\n" +
"but you should do this yourself if you need to compile CSS files from them\n" +
"or use the node-sass includePaths option if you need to import from them.");
var MergeTrees = require("broccoli-merge-trees");
inputTree = new MergeTrees(inputTree, {overwrite: true, annotation: "Sass Trees"});
} else {
inputTree = inputTree[0];
module.exports = class BroccoliSassCompiler extends BroccoliPlugin {
constructor(inputTree, options) {
if (Array.isArray(inputTree)) {
if (inputTree.length > 1) {
console.warn(
"Support for passing several trees to BroccoliSassCompiler has been removed.\n" +
"Passing the trees to broccoli-merge-trees with the overwrite option set,\n" +
"but you should do this yourself if you need to compile CSS files from them\n" +
"or use the node-sass includePaths option if you need to import from them.");
let MergeTrees = require("broccoli-merge-trees");
inputTree = new MergeTrees(inputTree, {overwrite: true, annotation: "Sass Trees"});
} else {
inputTree = inputTree[0];
}
}
}
options = options || {};
options.persistentOutput = true;
BroccoliPlugin.call(this, [inputTree], options);
options = options || {};
options.persistentOutput = true;
super([inputTree], options);
this.buildCount = 0;
this.treeName = options.annotation;
this.sass = sass;
this.options = copyObject(options);
var EventEmitter = require("chained-emitter").EventEmitter;
this.events = new EventEmitter();
this.buildCount = 0;
this.treeName = options.annotation;
this.sass = sass;
this.options = copyObject(options);
let EventEmitter = require("chained-emitter").EventEmitter;
this.events = new EventEmitter();
this.currentTree = null;
this.dependencies = {};
this.outputs = {};
if (options.persistentCache) {
var AsyncDiskCache = require("async-disk-cache");
this.persistentCache = new AsyncDiskCache(options.persistentCache);
}
this.currentTree = null;
this.dependencies = {};
this.outputs = {};
if (options.persistentCache) {
let AsyncDiskCache = require("async-disk-cache");
this.persistentCache = new AsyncDiskCache(options.persistentCache);
}
moveOption(this.options, this, "cssDir", "sassDir",
"optionsGenerator", "fullException",
"verbose", "renderSync",
"discover", "sourceFiles",
"maxListeners");
if (!this.cssDir) {
throw new Error("Expected cssDir option.");
}
if (!this.optionsGenerator) {
this.optionsGenerator = defaultOptionsGenerator;
}
if (!this.sourceFiles) {
this.sourceFiles = [];
if (this.discover === false) {
throw new Error("sourceFiles are required when discovery is disabled.");
} else {
// Default to discovery mode if no sourcefiles are provided.
this.discover = true;
moveOption(this.options, this, "cssDir", "sassDir",
"optionsGenerator", "fullException",
"verbose", "renderSync",
"discover", "sourceFiles",
"maxListeners");
if (!this.cssDir) {
throw new Error("Expected cssDir option.");
}
if (!this.optionsGenerator) {
this.optionsGenerator = defaultOptionsGenerator;
}
if (!this.sourceFiles) {
this.sourceFiles = [];
if (this.discover === false) {
throw new Error("sourceFiles are required when discovery is disabled.");
} else {
// Default to discovery mode if no sourcefiles are provided.
this.discover = true;
}
}
this.maxListeners = this.maxListeners || 10;
forbidNodeSassOption(this.options, "file");
forbidNodeSassOption(this.options, "data");
forbidNodeSassOption(this.options, "outFile");
if (this.verbose) {
this.colors = require("colors/safe");
this.events.on("compiled", this.logCompilationSuccess.bind(this));
this.events.on("failed", this.logCompilationFailure.bind(this));
}
this.events.addListener("compiled", (details, result) => {
this.addOutput(details.fullSassFilename, details.fullCssFilename);
let depFiles = result.stats.includedFiles;
this.addDependency(details.fullSassFilename, details.fullSassFilename);
for (let i = 0; i < depFiles.length; i++) {
this.addDependency(details.fullSassFilename, depFiles[i]);
}
});
}
this.maxListeners = this.maxListeners || 10;
forbidNodeSassOption(this.options, "file");
forbidNodeSassOption(this.options, "data");
forbidNodeSassOption(this.options, "outFile");
if (this.verbose) {
this.colors = require("colors/safe");
this.events.on("compiled", this.logCompilationSuccess.bind(this));
this.events.on("failed", this.logCompilationFailure.bind(this));
logCompilationSuccess(details, result) {
let timeInSeconds = result.stats.duration / 1000.0;
if (timeInSeconds === 0) {
timeInSeconds = "0.001"; // nothing takes zero seconds.
}
let action = this.colors.inverse.green("compile (" + timeInSeconds + "s)");
let message = this.scopedFileName(details.sassFilename) + " => " + details.cssFilename;
console.log(action + " " + message);
}
this.events.addListener("compiled", function(details, result) {
this.addOutput(details.fullSassFilename, details.fullCssFilename);
var depFiles = result.stats.includedFiles;
this.addDependency(details.fullSassFilename, details.fullSassFilename);
for (var i = 0; i < depFiles.length; i++) {
this.addDependency(details.fullSassFilename, depFiles[i]);
logCompilationFailure(details, error) {
let sassFilename = details.sassFilename;
let action = this.colors.bgRed.white("error");
let message = this.colors.red(error.message);
let location = "Line " + error.line + ", Column " + error.column;
if (error.file.substring(error.file.length - sassFilename.length) !== sassFilename) {
location = location + " of " + error.file;
}
}.bind(this));
}
BroccoliSassCompiler.prototype = Object.create(BroccoliPlugin.prototype);
BroccoliSassCompiler.prototype.constructor = BroccoliSassCompiler;
BroccoliSassCompiler.prototype.logCompilationSuccess = function(details, result) {
var timeInSeconds = result.stats.duration / 1000.0;
if (timeInSeconds === 0) {
timeInSeconds = "0.001"; // nothing takes zero seconds.
console.log(action + " " + sassFilename + " (" + location + "): " + message);
}
var action = this.colors.inverse.green("compile (" + timeInSeconds + "s)");
var message = this.scopedFileName(details.sassFilename) + " => " + details.cssFilename;
console.log(action + " " + message);
};
BroccoliSassCompiler.prototype.logCompilationFailure = function(details, error) {
var sassFilename = details.sassFilename;
var action = this.colors.bgRed.white("error");
var message = this.colors.red(error.message);
var location = "Line " + error.line + ", Column " + error.column;
if (error.file.substring(error.file.length - sassFilename.length) !== sassFilename) {
location = location + " of " + error.file;
compileTree(srcPath, files, destDir) {
let numConcurrentCalls = Number(process.env.JOBS) || os.cpus().length;
let worker = queue.async.asyncify(file => {
return RSVP.all(this.compileSassFile(srcPath, file, destDir));
});
return RSVP.resolve(queue(worker, files, numConcurrentCalls));
}
console.log(action + " " + sassFilename + " (" + location + "): " + message);
};
BroccoliSassCompiler.prototype.compileTree = function(srcPath, files, destDir) {
var self = this;
var numConcurrentCalls = Number(process.env.JOBS) || os.cpus().length;
compileSassFile(srcPath, sassFilename, destDir) {
let sassOptions = copyObject(this.options);
sassOptions.file = path.join(srcPath, sassFilename);
let parsedName = parsePath(sassFilename);
var worker = queue.async.asyncify(function(file) {
return RSVP.all(self.compileSassFile(srcPath, file, destDir));
});
return RSVP.resolve(queue(worker, files, numConcurrentCalls)
.catch(function(reason) {
throw reason;
if (this.sassDir && parsedName.dir.slice(0, this.sassDir.length) === this.sassDir) {
parsedName.dir = parsedName.dir.slice(this.sassDir.length + 1);
}
));
};
BroccoliSassCompiler.prototype.compileSassFile = function(srcPath, sassFilename, destDir) {
var self = this;
var sassOptions = copyObject(this.options);
sassOptions.file = path.join(srcPath, sassFilename);
var parsedName = parsePath(sassFilename);
if (this.sassDir && parsedName.dir.slice(0, this.sassDir.length) === this.sassDir) {
parsedName.dir = parsedName.dir.slice(this.sassDir.length + 1);
parsedName.ext = ".css";
parsedName.base = parsedName.name + ".css";
let cssFileName = path.join(this.cssDir, formatPath(parsedName));
let promises = [];
this.optionsGenerator(
sassFilename, cssFileName, sassOptions,
(resolvedCssFileName, resolvedOptions) => {
let details = {
srcPath: srcPath,
sassFilename: sassFilename,
fullSassFilename: resolvedOptions.file,
destDir: destDir,
cssFilename: resolvedCssFileName,
fullCssFilename: path.join(destDir, resolvedCssFileName),
options: copyObject(resolvedOptions)
};
details.options.outFile = details.cssFilename;
promises.push(this.compileCssFileMaybe(details));
});
return promises;
}
parsedName.ext = ".css";
parsedName.base = parsedName.name + ".css";
var cssFileName = path.join(this.cssDir, formatPath(parsedName));
var promises = [];
this.optionsGenerator(
sassFilename, cssFileName, sassOptions,
function(resolvedCssFileName, resolvedOptions) {
var details = {
srcPath: srcPath,
sassFilename: sassFilename,
fullSassFilename: resolvedOptions.file,
destDir: destDir,
cssFilename: resolvedCssFileName,
fullCssFilename: path.join(destDir, resolvedCssFileName),
options: copyObject(resolvedOptions)
};
details.options.outFile = details.cssFilename;
promises.push(self.compileCssFileMaybe(details));
});
return promises;
};
BroccoliSassCompiler.prototype.renderer = function() {
if (this.renderSync) {
return renderSassSync;
} else {
return renderSass;
renderer() {
if (this.renderSync) {
return renderSassSync;
} else {
return renderSass;
}
}
};
var READ_AS_UTF_8 = {encoding: "utf8"};
var readFile = RSVP.denodeify(fs.readFile);
var writeFile = RSVP.denodeify(fs.writeFile);
/* Check if a dependency's hash has changed.
*
* @argument srcDir The directory in which to resolve relative paths against.
* @argument dep An array of two elements, the first is the file and
* the second is the last known hash of that file.
*
* Returns a promise that resolves as true when the file hasn't changed from the specified hash.
**/
checkDependency(srcDir, dep) {
let file = path.isAbsolute(dep[0]) ? dep[0] : path.join(srcDir, dep[0]);
let hexDigest = dep[1];
/* Check if a dependency's hash has changed.
*
* @argument srcDir The directory in which to resolve relative paths against.
* @argument dep An array of two elements, the first is the file and
* the second is the last known hash of that file.
*
* Returns a promise that resolves as true when the file hasn't changed from the specified hash.
**/
BroccoliSassCompiler.prototype.checkDependency = function(srcDir, dep) {
var self = this;
var file = path.isAbsolute(dep[0]) ? dep[0] : path.join(srcDir, dep[0]);
var hexDigest = dep[1];
return new RSVP.Promise(function(resolve, reject) {
self.hashForFile(file).then(function(hash) {
var hd = hash.digest("hex");
return this.hashForFile(file).then(hash => {
let hd = hash.digest("hex");
if (hd === hexDigest) {
resolve(true);
return true;
} else {
reject(new Error("dependency changed: " + dep[0]));
throw new Error("dependency changed: " + dep[0]);
}
}, reject);
});
};
});
}
/* get the cached output for a source file, or receive a cache miss.
*
* @argument srcDir The directory in which to resolve relative paths against.
* @argument relativeFilename The filename relative to the srcDir that is being compiled.
* @argument options The compilation options.
*
* @return Promise that resolves to the cached output of the compiled file or rejects with an
* error explaining why the cache wasn't available. In most cases, the cache error will have
* a property named `key` that can be used to populate the cache if it's missing. Some error
* conditions can't set the key property -- for example when a file is removed or the cache
* can't be created.
**/
BroccoliSassCompiler.prototype.cachedOutput = function(srcDir, relativeFilename, options) {
var self = this;
return new RSVP.Promise(function(resolve, reject) {
self.keyForSourceFile(srcDir, relativeFilename, options).then(function(key) {
persistentCacheDebug("key for " + self.scopedFileName(relativeFilename) + ": " + key);
self.persistentCache.get(self.dependenciesKey(key)).then(function(cacheEntry) {
/* get the cached output for a source file, or receive a cache miss.
*
* @argument srcDir The directory in which to resolve relative paths against.
* @argument relativeFilename The filename relative to the srcDir that is being compiled.
* @argument options The compilation options.
*
* @return Promise that resolves to the cached output of the compiled file or rejects with an
* error explaining why the cache wasn't available. In most cases, the cache error will have
* a property named `key` that can be used to populate the cache if it's missing. Some error
* conditions can't set the key property -- for example when a file is removed or the cache
* can't be created.
**/
cachedOutput(srcDir, relativeFilename, options) {
debugger;
return this.keyForSourceFile(srcDir, relativeFilename, options).then(key => {
persistentCacheDebug("key for " + this.scopedFileName(relativeFilename) + ": " + key);
return this.persistentCache.get(this.dependenciesKey(key)).then(cacheEntry => {
if (cacheEntry.isCached) {
var dependencies = JSON.parse(cacheEntry.value);
var depChecks = dependencies.map(function(dep) {
return self.checkDependency(srcDir, dep);
});
RSVP.all(depChecks).
then(function(depResults) {
return self.persistentCache.get(self.outputKey(key));
}).
then(function(cacheEntry) {
let dependencies = JSON.parse(cacheEntry.value);
let depChecks = dependencies.map(dep => this.checkDependency(srcDir, dep));
return RSVP.all(depChecks).
then(depResults => this.persistentCache.get(this.outputKey(key))).
then(cacheEntry => {
if (cacheEntry.isCached) {
var depFiles = dependencies.map(function(depAndHash) {
return depAndHash[0];
});
resolve([depFiles, JSON.parse(cacheEntry.value)]);
let depFiles = dependencies.map(depAndHash => depAndHash[0]);
return [
depFiles,
JSON.parse(cacheEntry.value)
];
} else {
var error = new Error("no output data for " + relativeFilename);
error.key = key;
reject(error);
throw new Error("no output data for " + relativeFilename);
}
}).
catch(function(error) {
catch(error => {
error.key = key;
reject(error);
throw error;
});
} else {
var error = new Error("no dependency data for " + relativeFilename);
let error = new Error("no dependency data for " + relativeFilename);
error.key = key;
reject(error);
throw error;
}
});
}, reject);
});
};
});
}
/* compute the hash for a file.
*
* @argument absolutePath The absolute path to the file.
* @return Promise that resolves to a hash object. rejects if it can't read the file.
**/
BroccoliSassCompiler.prototype.hashForFile = function(absolutePath) {
return readFile(absolutePath, READ_AS_UTF_8).then(function(data) {
return crypto.createHash("md5").update(data);
});
};
/* compute the hash for a file.
*
* @argument absolutePath The absolute path to the file.
* @return Promise that resolves to a hash object. rejects if it can't read the file.
**/
hashForFile(absolutePath) {
return readFile(absolutePath, READ_AS_UTF_8).then(data => {
return crypto.createHash("md5").update(data);
});
}
/* construct a base cache key for a file to be compiled.
*
* @argument srcDir The directory in which to resolve relative paths against.
* @argument relativeFilename The filename relative to the srcDir that is being compiled.
* @argument options The compilation options.
*
* @return Promise that resolves to the cache key for the file or rejects if it can't read the file.
**/
BroccoliSassCompiler.prototype.keyForSourceFile = function(srcDir, relativeFilename, options) {
var absolutePath = path.join(srcDir, relativeFilename);
return this.hashForFile(absolutePath).then(function(hash) {
return relativeFilename + "@" + hash.digest("hex");
});
};
/* construct a base cache key for a file to be compiled.
*
* @argument srcDir The directory in which to resolve relative paths against.
* @argument relativeFilename The filename relative to the srcDir that is being compiled.
* @argument options The compilation options.
*
* @return Promise that resolves to the cache key for the file or rejects if
* it can't read the file.
**/
keyForSourceFile(srcDir, relativeFilename, options) {
let absolutePath = path.join(srcDir, relativeFilename);
return this.hashForFile(absolutePath).then(hash => {
return relativeFilename + "@" + hash.digest("hex");
});
}
/* construct a cache key for storing dependency hashes.
*
* @argument key The base cache key
* @return String
*/
BroccoliSassCompiler.prototype.dependenciesKey = function(key) {
return "[[[dependencies of " + key + "]]]";
};
/* construct a cache key for storing dependency hashes.
*
* @argument key The base cache key
* @return String
*/
dependenciesKey(key) {
return "[[[dependencies of " + key + "]]]";
}
/* construct a cache key for storing output.
*
* @argument key The base cache key
* @return String
*/
BroccoliSassCompiler.prototype.outputKey = function(key) {
return "[[[output of " + key + "]]]";
};
/* construct a cache key for storing output.
*
* @argument key The base cache key
* @return String
*/
outputKey(key) {
return "[[[output of " + key + "]]]";
}
/* retrieve the files from cache, write them, and populate the hot cache information
* for rebuilds.
*/
BroccoliSassCompiler.prototype.handleCacheHit = function(details, inputAndOutputFiles) {
var self = this;
var inputFiles = inputAndOutputFiles[0];
var outputFiles = inputAndOutputFiles[1];
persistentCacheDebug("%s is cached. Writing to %s.",
details.sassFilename,
details.fullCssFilename);
/* retrieve the files from cache, write them, and populate the hot cache information
* for rebuilds.
*/
handleCacheHit(details, inputAndOutputFiles) {
let inputFiles = inputAndOutputFiles[0];
let outputFiles = inputAndOutputFiles[1];
if (self.verbose) {
var action = self.colors.inverse.green("cached");
var message = self.scopedFileName(details.sassFilename) + " => " + details.cssFilename;
console.log(action + " " + message);
persistentCacheDebug("%s is cached. Writing to %s.",
details.sassFilename,
details.fullCssFilename);
if (this.verbose) {
let action = this.colors.inverse.green("cached");
let message = this.scopedFileName(details.sassFilename) + " => " + details.cssFilename;
console.log(action + " " + message);
}
let results = [];
inputFiles.forEach(dep => {
// populate the dependencies cache for rebuilds
this.addDependency(details.fullSassFilename, path.resolve(details.srcPath, dep));
});
let files = Object.keys(outputFiles);
persistentCacheDebug("cached output files for %s are: %s",
details.sassFilename, files.join(", "));
files.forEach(f => {
let data = outputFiles[f];
let outputFile = path.join(details.destDir, f);
// populate the output cache for rebuilds
this.addOutput(details.fullSassFilename, outputFile);
let writePromise = mkdirp.promise(path.dirname(outputFile)).
then(() => writeFile(outputFile, new Buffer(data, "base64")));
results.push(writePromise);
});
return RSVP.all(results);
}
var results = [];
inputFiles.forEach(function(dep) {
// populate the dependencies cache for rebuilds
self.addDependency(details.fullSassFilename, path.resolve(details.srcPath, dep));
});
var files = Object.keys(outputFiles);
persistentCacheDebug("cached output files for %s are: %s",
details.sassFilename, files.join(", "));
files.forEach(function(f) {
var data = outputFiles[f];
var outputFile = path.join(details.destDir, f);
// populate the output cache for rebuilds
self.addOutput(details.fullSassFilename, outputFile);
var writePromise = mkdirp.promise(path.dirname(outputFile)).
then(function() {
return writeFile(outputFile, new Buffer(data, "base64"));
});
results.push(writePromise);
});
return RSVP.all(results);
};
BroccoliSassCompiler.prototype.scopedFileName = function(file) {
file = this.relativize(file);
if (this.treeName) {
return this.treeName + "/" + file;
} else {
return file;
scopedFileName(file) {
file = this.relativize(file);
if (this.treeName) {
return this.treeName + "/" + file;
} else {
return file;
}
}
};
BroccoliSassCompiler.prototype.relativize = function(file) {
return removePathPrefix(this.inputPaths[0], [file])[0];
};
relativize(file) {
return removePathPrefix(this.inputPaths[0], [file])[0];
}
BroccoliSassCompiler.prototype.relativizeAll = function(files) {
return removePathPrefix(this.inputPaths[0], files);
};
relativizeAll(files) {
return removePathPrefix(this.inputPaths[0], files);
}
BroccoliSassCompiler.prototype.hasDependenciesSet = function(file) {
return this.dependencies[this.relativize(file)] !== undefined;
};
hasDependenciesSet(file) {
return this.dependencies[this.relativize(file)] !== undefined;
}
BroccoliSassCompiler.prototype.dependenciesOf = function(file) {
return this.dependencies[this.relativize(file)] || new Set();
};
dependenciesOf(file) {
return this.dependencies[this.relativize(file)] || new Set();
}
BroccoliSassCompiler.prototype.outputsFrom = function(file) {
return this.outputs[this.relativize(file)] || new Set();
};
outputsFrom(file) {
return this.outputs[this.relativize(file)] || new Set();
}
/* hash all dependencies asynchronously and return the files that exist
* as an array of pairs (filename, hash).
*/
BroccoliSassCompiler.prototype.hashDependencies = function(details) {
var self = this;
var dependencyPromises = [];
self.dependenciesOf(details.fullSassFilename).forEach(function(f) {
dependencyPromises.push(self.hashForFile(f).then(function(h) {
if (f.startsWith(details.srcPath)) {
f = f.substring(details.srcPath.length + 1);
}
return [f, h.digest("hex")];
}).catch(function(e) {
if (e.code === "ENOENT") {
persistentCacheDebug("Ignoring non-existent file: %s", f);
return [];
} else {
throw e;
}
}));
});
return RSVP.all(dependencyPromises).then(function(depsWithHashes) {
return depsWithHashes.filter(function(dwh) {
return dwh.length > 0; // prune out the dependencies that weren't files.
/* hash all dependencies asynchronously and return the files that exist
* as an array of pairs (filename, hash).
*/
hashDependencies(details) {
let dependencyPromises = [];
this.dependenciesOf(details.fullSassFilename).forEach(f => {
dependencyPromises.push(this.hashForFile(f).then(h =>{
if (f.startsWith(details.srcPath)) {
f = f.substring(details.srcPath.length + 1);
}
return [
f,
h.digest("hex")
];
}).catch(e => {
if (typeof e === "object" && e !== null && e.code === "ENOENT") {
persistentCacheDebug("Ignoring non-existent file: %s", f);
return [];
} else {
throw e;
}
}));
});
});
};
/* read all output files asynchronously and return the contents
* as a hash of relative filenames to base64 encoded strings.
*/
BroccoliSassCompiler.prototype.readOutputs = function(details) {
var readPromises = [];
var outputs = this.outputsFrom(details.fullSassFilename);
outputs.forEach(function(o) {
readPromises.push(readFile(o).then(function(buffer) {
return [o, buffer.toString("base64")];
}));
});
return RSVP.all(readPromises).then(function(outputs) {
var outputContents = {};
outputs.forEach(function(output) {
var fileName = output[0];
var contents = output[1];
if (fileName.startsWith(details.destDir)) {
outputContents[fileName.substring(details.destDir.length + 1)] = contents;
} else {
persistentCacheDebug("refusing to cache output file found outside the output tree: %s", fileName);
}
return RSVP.all(dependencyPromises).then(depsWithHashes => {
// prune out the dependencies that weren't files.
return depsWithHashes.filter(dwh => dwh.length > 0);
});
return outputContents;
});
};
}
/* Writes the dependencies and output contents to the persistent cache */
BroccoliSassCompiler.prototype.populateCache = function(key, details, result) {
var self = this;
var cache = this.persistentCache;
persistentCacheDebug("Populating cache for " + key);
var setDeps = self.hashDependencies(details).then(function(depsWithHashes) {
return cache.set(self.dependenciesKey(key), JSON.stringify(depsWithHashes));
});
/* read all output files asynchronously and return the contents
* as a hash of relative filenames to base64 encoded strings.
*/
readOutputs(details) {
let readPromises = [];
let outputs = this.outputsFrom(details.fullSassFilename);
var setOutput = self.readOutputs(details).then(function(outputContents) {
return cache.set(self.outputKey(key), JSON.stringify(outputContents));
});
outputs.forEach(output => {
readPromises.push(readFile(output).then(buffer => {
return [output, buffer.toString("base64")];
}));
});
return RSVP.all([setDeps, setOutput]);
};
return RSVP.all(readPromises).then(outputs => {
let outputContents = {};
/* When the cache misses, we need to compile the file and then populate the cache if we can. */
BroccoliSassCompiler.prototype.handleCacheMiss = function(details, reason) {
var self = this;
var key = reason.key;
if (key) {
persistentCacheDebug("Persistent cache miss for %s. Reason: %s",
details.sassFilename,
reason.message);
} else {
persistentCacheDebug("Cannot cache %s. Reason: %s",
details.sassFilename,
reason.message);
persistentCacheDebug("Stacktrace:", reason.stack);
outputs.forEach(output => {
let fileName = output[0];
let contents = output[1];
if (fileName.startsWith(details.destDir)) {
outputContents[fileName.substring(details.destDir.length + 1)] = contents;
} else {
persistentCacheDebug("refusing to cache output file found outside the output tree: %s",
fileName);
}
});
return outputContents;
});
}
return self.compileCssFile(details).
then(function(result) {
if (key) {
return self.populateCache(key, details, result);
}
/* Writes the dependencies and output contents to the persistent cache */
populateCache(key, details, result) {
let cache = this.persistentCache;
persistentCacheDebug("Populating cache for " + key);
let setDeps = this.hashDependencies(details).then(depsWithHashes => {
return cache.set(this.dependenciesKey(key), JSON.stringify(depsWithHashes));
});
};
/* Compile the file if it's not in the cache.
* Reuse cached output if it is.
*
* @argument details The compilation details object.
*
* @return A promise that resolves when the output files are written
* either from cache or by compiling. Rejects on error.
*/
BroccoliSassCompiler.prototype.compileCssFileMaybe = function(details) {
var self = this;
if (this.persistentCache) {
return this.cachedOutput(details.srcPath, details.sassFilename, details.options).
then(self.handleCacheHit.bind(self, details),
self.handleCacheMiss.bind(self, details));
} else {
return self.compileCssFile(details);
let setOutput = this.readOutputs(details).then(outputContents => {
return cache.set(this.outputKey(key), JSON.stringify(outputContents));
});
return RSVP.all([
setDeps,
setOutput
]);
}
};
BroccoliSassCompiler.prototype.compileCssFile = function(details) {
var sass = this.renderer();
var success = this.handleSuccess.bind(this, details);
var failure = this.handleFailure.bind(this, details);
var self = this;
/* When the cache misses, we need to compile the file and then populate the cache if we can. */
handleCacheMiss(details, reason) {
let key = reason.key;
return this.events.emit("compiling", details)
.then(function() {
var dependencyListener = function(absolutePath) {
self.addDependency(details.fullSassFilename, absolutePath);
};
if (key) {
persistentCacheDebug("Persistent cache miss for %s. Reason: %s",
details.sassFilename,
reason.message);
} else {
persistentCacheDebug("Cannot cache %s. Reason: %s",
details.sassFilename,
reason.message);
persistentCacheDebug("Stacktrace:", reason.stack);
}
return this.compileCssFile(details).
then(result => {
if (key) {
return this.populateCache(key, details, result);
}
});
}
var additionalOutputListener = function(filename) {
self.addOutput(details.fullSassFilename, filename);
};
/* Compile the file if it's not in the cache.
* Reuse cached output if it is.
*
* @argument details The compilation details object.
*
* @return A promise that resolves when the output files are written
* either from cache or by compiling. Rejects on error.
*/
compileCssFileMaybe(details) {
if (this.persistentCache) {
return this.cachedOutput(details.srcPath, details.sassFilename, details.options).
then(this.handleCacheHit.bind(this, details),
this.handleCacheMiss.bind(this, details));
} else {
return this.compileCssFile(details);
}
}
self.events.addListener("additional-output", additionalOutputListener);
self.events.addListener("dependency", dependencyListener);
compileCssFile(details) {
let sass = this.renderer();
return sass(details.options)
.then(function(result) {
self.events.removeListener("dependency", dependencyListener);
self.events.removeListener("additional-output", additionalOutputListener);
return success(result).then(function() {
return result;
});
}, failure);
});
};
let success = this.handleSuccess.bind(this, details);
let failure = this.handleFailure.bind(this, details);
BroccoliSassCompiler.prototype.handleSuccess = function(details, result) {
mkdirp.sync(path.dirname(details.fullCssFilename));
fs.writeFileSync(details.fullCssFilename, result.css);
return this.events.emit("compiled", details, result);
};
return this.events.emit("compiling", details)
.then(() => {
let dependencyListener = absolutePath => {
this.addDependency(details.fullSassFilename, absolutePath);
};
BroccoliSassCompiler.prototype.handleFailure = function(details, error) {
var failed = this.events.emit("failed", details, error);
var rethrow = failed.finally(function() {
var message = error.message;
var location = " at " + error.file + ":" + error.line + ":" + error.column;
// TODO: implement fullException
throw new Error(message + "\n" + location);
});
return rethrow;
};
let additionalOutputListener = filename => {
this.addOutput(details.fullSassFilename, filename);
};
BroccoliSassCompiler.prototype.filesInTree = function(srcPath) {
var sassDir = this.sassDir || "";
var files = [];
if (this.discover) {
var pattern = path.join(srcPath, sassDir, "**", "[^_]*.scss");
files = glob.sync(pattern);
this.events.addListener("additional-output", additionalOutputListener);
this.events.addListener("dependency", dependencyListener);
return sass(details.options)
.then(result => {
this.events.removeListener("dependency", dependencyListener);
this.events.removeListener("additional-output", additionalOutputListener);
return success(result).then(() => result);
}, failure);
});
}
this.sourceFiles.forEach(function(sourceFile) {
var pattern = path.join(srcPath, sassDir, sourceFile);
files = files.concat(glob.sync(pattern));
});
return unique(files);
};
function Entry(path) {
var stats = fs.statSync(path);
this.relativePath = path;
this.basePath = "/";
this.mode = stats.mode;
this.size = stats.size;
this.mtime = stats.mtime;
}
handleSuccess(details, result) {
mkdirp.sync(path.dirname(details.fullCssFilename));
fs.writeFileSync(details.fullCssFilename, result.css);
Entry.prototype.isDirectory = function() {
return false;
};
return this.events.emit("compiled", details, result);
}
BroccoliSassCompiler.prototype.addOutput = function(sassFilename, outputFilename) {
sassFilename = this.relativize(sassFilename);
this.outputs[sassFilename] =
this.outputs[sassFilename] || new Set();
this.outputs[sassFilename].add(outputFilename);
};
handleFailure(details, error) {
let failed = this.events.emit("failed", details, error);
let rethrow = failed.finally(() => {
let message = error.message;
let location = " at " + error.file + ":" + error.line + ":" + error.column;
// TODO: implement fullException
throw new Error(message + "\n" + location);
});
BroccoliSassCompiler.prototype.clearOutputs = function(files) {
var self = this;
this.relativizeAll(files).forEach(function(f) {
if (self.outputs[f]) {
delete self.outputs[f];
return rethrow;
}
filesInTree(srcPath) {
let sassDir = this.sassDir || "";
let files = [];
if (this.discover) {
let pattern = path.join(srcPath, sassDir, "**", "[^_]*.scss");
files = glob.sync(pattern);
}
});
};
/* This method computes the output files that are only output for at least one given inputs
* and never for an input that isn't provided.
*
* This is important because the same assets might be output from compiling several
* different inputs for tools like eyeglass assets.
*
* @return Set<String> The full paths output files.
*/
BroccoliSassCompiler.prototype.outputsFromOnly = function(inputs) {
inputs = this.relativizeAll(inputs);
var otherOutputs = new Set();
var onlyOutputs = new Set();
var allInputs = Object.keys(this.outputs);
for (var i = 0; i < allInputs.length; i++) {
var outputs = this.outputs[allInputs[i]];
if (inputs.indexOf(allInputs[i]) < 0) {
outputs.forEach(function(output) {
otherOutputs.add(output);
});
} else {
outputs.forEach(function(output) {
onlyOutputs.add(output);
});
}
this.sourceFiles.forEach(sourceFile => {
let pattern = path.join(srcPath, sassDir, sourceFile);
files = files.concat(glob.sync(pattern));
});
return unique(files);
}
onlyOutputs.forEach(function(only) {
if (otherOutputs.has(only)) {
onlyOutputs.delete(only);
}
});
return onlyOutputs;
};
BroccoliSassCompiler.prototype.addDependency = function(sassFilename, dependencyFilename) {
sassFilename = this.relativize(sassFilename);
this.dependencies[sassFilename] =
this.dependencies[sassFilename] || new Set();
this.dependencies[sassFilename].add(dependencyFilename);
};
addOutput(sassFilename, outputFilename) {
sassFilename = this.relativize(sassFilename);
BroccoliSassCompiler.prototype.clearDependencies = function(files) {
var self = this;
this.relativizeAll(files).forEach(function(f) {
if (self.dependencies[f]) {
delete self.dependencies[f];
this.outputs[sassFilename] =
this.outputs[sassFilename] || new Set();
this.outputs[sassFilename].add(outputFilename);
}
clearOutputs(files) {
this.relativizeAll(files).forEach(f => {
if (this.outputs[f]) {
delete this.outputs[f];
}
});
}
/* This method computes the output files that are only output for at least one given inputs
* and never for an input that isn't provided.
*
* This is important because the same assets might be output from compiling several
* different inputs for tools like eyeglass assets.
*
* @return Set<String> The full paths output files.
*/
outputsFromOnly(inputs) {
inputs = this.relativizeAll(inputs);
let otherOutputs = new Set();
let onlyOutputs = new Set();
let allInputs = Object.keys(this.outputs);
for (let i = 0; i < allInputs.length; i++) {
let outputs = this.outputs[allInputs[i]];
if (inputs.indexOf(allInputs[i]) < 0) {
outputs.forEach(output => otherOutputs.add(output));
} else {
outputs.forEach(output => onlyOutputs.add(output));
}
}
});
};
onlyOutputs.forEach(only => {
if (otherOutputs.has(only)) {
onlyOutputs.delete(only);
}
});
return onlyOutputs;
}
BroccoliSassCompiler.prototype.knownDependencies = function() {
var deps = new Set();
var sassFiles = Object.keys(this.dependencies);
for (var i = 0; i < sassFiles.length; i++) {
var sassFile = sassFiles[i];
deps.add(sassFile);
this.dependencies[sassFile].forEach(function (dep) {
deps.add(dep);
addDependency(sassFilename, dependencyFilename) {
sassFilename = this.relativize(sassFilename);
this.dependencies[sassFilename] =
this.dependencies[sassFilename] || new Set();
this.dependencies[sassFilename].add(dependencyFilename);
}
clearDependencies(files) {
this.relativizeAll(files).forEach(f => {
delete this.dependencies[f];
});
}
var entries = [];
deps.forEach(function(d) {
knownDependencies() {
let deps = new Set();
let sassFiles = Object.keys(this.dependencies);
for (let i = 0; i < sassFiles.length; i++) {
let sassFile = sassFiles[i];
deps.add(sassFile);
this.dependencies[sassFile].forEach(dep => deps.add(dep));
}
let entries = [];
deps.forEach(d => {
try {

@@ -796,41 +807,30 @@ entries.push(new Entry(d));

}
});
return entries;
};
});
BroccoliSassCompiler.prototype.hasKnownDependencies = function() {
return Object.keys(this.dependencies).length > 0;
};
return entries;
}
function absolutizeEntries(entries) {
// We make everything absolute because relative path comparisons don't work for us.
entries.forEach(function(entry) {
// TODO support windows paths
entry.relativePath = path.join(entry.basePath, entry.relativePath);
entry.basePath = "/";
});
}
hasKnownDependencies() {
return Object.keys(this.dependencies).length > 0;
}
BroccoliSassCompiler.prototype.knownDependenciesTree = function(inputPath) {
var entries = walkSync.entries(inputPath);
absolutizeEntries(entries);
var tree = new FSTreeFromEntries(entries);
tree.addEntries(this.knownDependencies(), {sortAndExpand: true});
return tree;
};
knownDependenciesTree(inputPath) {
let entries = walkSync.entries(inputPath);
absolutizeEntries(entries);
let tree = new FSTreeFromEntries(entries);
tree.addEntries(this.knownDependencies(), {sortAndExpand: true});
return tree;
}
BroccoliSassCompiler.prototype.build = function() {
this.buildCount = this.buildCount + 1;
var self = this;
var nextTree = null;
var patches = [];
var inputPath = this.inputPaths[0];
var outputPath = this.outputPath;
var currentTree = this.currentTree;
_build() {
let inputPath = this.inputPaths[0];
let outputPath = this.outputPath;
let currentTree = this.currentTree;
function doBuild() {
if (self.hasKnownDependencies()) {
let nextTree = null;
let patches = [];
if (this.hasKnownDependencies()) {
hotCacheDebug("Has known dependencies");
nextTree = self.knownDependenciesTree(inputPath);
self.currentTree = nextTree;
nextTree = this.knownDependenciesTree(inputPath);
this.currentTree = nextTree;
currentTree = currentTree || new FSTree();

@@ -846,13 +846,16 @@ patches = currentTree.calculatePatch(nextTree);

// TODO: handle indented syntax files.
var treeFiles = removePathPrefix(inputPath, self.filesInTree(inputPath));
treeFiles = treeFiles.filter(function(f, i) {
let treeFiles = removePathPrefix(inputPath, this.filesInTree(inputPath));
treeFiles = treeFiles.filter(f => {
f = path.join(inputPath, f);
if (!self.hasDependenciesSet(f)) {
hotCacheDebug("no deps for", self.scopedFileName(f));
if (!this.hasDependenciesSet(f)) {
hotCacheDebug("no deps for", this.scopedFileName(f));
return true;
}
var deps = self.dependenciesOf(f);
let deps = this.dependenciesOf(f);
hotCacheDebug("dependencies are", deps);
for (var p = 0; p < patches.length; p++) {
var entry = patches[p][2];
let entry = patches[p][2];
hotCacheDebug("looking for", entry.relativePath);

@@ -864,6 +867,6 @@ if (deps.has(entry.relativePath)) {

}
if (self.verbose) {
var action = self.colors.inverse.green("unchanged");
var message = f;
var message = self.scopedFileName(f);
if (this.verbose) {
let action = this.colors.inverse.green("unchanged");
let message = this.scopedFileName(f);
console.log(action + " " + message);

@@ -874,6 +877,6 @@ }

// Cleanup any unneeded output files
var removed = [];
let removed = [];
for (var p = 0; p < patches.length; p++) {
if (patches[p][0] === "unlink") {
var entry = patches[p][2];
let entry = patches[p][2];
if (entry.relativePath.indexOf(inputPath) === 0) {

@@ -884,5 +887,6 @@ removed.push(entry.relativePath);

}
if (removed.length > 0) {
var outputs = self.outputsFromOnly(removed);
outputs.forEach(function(output) {
let outputs = this.outputsFromOnly(removed);
outputs.forEach(output => {
if (output.indexOf(outputPath) === 0) {

@@ -894,3 +898,3 @@ fs.unlinkSync(output);

});
self.clearOutputs(removed);
this.clearOutputs(removed);
}

@@ -900,21 +904,19 @@

var absoluteTreeFiles = treeFiles.map(function(f) {
return path.join(inputPath, f);
});
let absoluteTreeFiles = treeFiles.map(f => path.join(inputPath, f));
self.clearDependencies(absoluteTreeFiles);
self.clearOutputs(absoluteTreeFiles);
this.clearDependencies(absoluteTreeFiles);
this.clearOutputs(absoluteTreeFiles);
var internalListeners = absoluteTreeFiles.length * 2 + // 1 dep & 1 output listeners each
1 + // one compilation listener
(self.verbose ? 2 : 0); // 2 logging listeners if in verbose mode
let internalListeners = absoluteTreeFiles.length * 2 + // 1 dep & 1 output listeners each
1 + // one compilation listener
(this.verbose ? 2 : 0); // 2 logging listeners if in verbose mode
debug("There are %d internal event listeners.", internalListeners);
debug("Setting max external listeners to %d via the maxListeners option (default: 10).",
self.maxListeners);
self.events.setMaxListeners(internalListeners + self.maxListeners);
this.maxListeners);
this.events.setMaxListeners(internalListeners + this.maxListeners);
return self.compileTree(inputPath, treeFiles, outputPath).finally(function() {
if (!self.currentTree) {
self.currentTree = self.knownDependenciesTree(inputPath);
return this.compileTree(inputPath, treeFiles, outputPath).finally(() => {
if (!this.currentTree) {
this.currentTree = this.knownDependenciesTree(inputPath);
}

@@ -924,10 +926,13 @@ });

if (self.buildCount === 1 && process.env["BROCCOLI_EYEGLASS"] === "forceInvalidateCache") {
persistentCacheDebug("clearing cache because forceInvalidateCache was set.");
return this.persistentCache.clear().then(doBuild);
} else {
return doBuild();
build() {
this.buildCount = this.buildCount + 1;
if (this.buildCount === 1 && process.env.BROCCOLI_EYEGLASS === "forceInvalidateCache") {
persistentCacheDebug("clearing cache because forceInvalidateCache was set.");
debugger;
return this.persistentCache.clear().then(this._build.bind(this));
} else {
return this._build();
}
}
};
module.exports = BroccoliSassCompiler;
"use strict";
var BroccoliSassCompiler = require("./broccoli_sass_compiler");
var RSVP = require("rsvp");
var crypto = require("crypto");
var merge = require("lodash.merge");
var path = require("path");
var sortby = require("lodash.sortby");
var stringify = require("json-stable-stringify");
var debugGenerator = require("debug");
var persistentCacheDebug = debugGenerator("broccoli-eyeglass:persistent-cache");
const BroccoliSassCompiler = require("./broccoli_sass_compiler");
const RSVP = require("rsvp");
const crypto = require("crypto");
const merge = require("lodash.merge");
const path = require("path");
const sortby = require("lodash.sortby");
const stringify = require("json-stable-stringify");
const debugGenerator = require("debug");
const persistentCacheDebug = debugGenerator("broccoli-eyeglass:persistent-cache");
function httpJoin() {
var joined = [];
for (var i = 0; i < arguments.length; i++) {
let joined = [];
for (let i = 0; i < arguments.length; i++) {
if (arguments[i]) {
var segment = arguments[i];
let segment = arguments[i];
if (path.sep !== "/") {

@@ -24,3 +24,3 @@ segment = segment.replace(path.sep, "/");

}
var result = joined.join("/");
let result = joined.join("/");
result = result.replace("///", "/");

@@ -31,147 +31,159 @@ result = result.replace("//", "/");

function EyeglassCompiler(inputTrees, options) {
options = merge({}, options);
this.pristineOptions = merge({}, options);
this.Eyeglass = require("eyeglass");
if (!Array.isArray(inputTrees)) {
inputTrees = [inputTrees];
}
if (options.configureEyeglass) {
this.configureEyeglass = options.configureEyeglass;
delete options.configureEyeglass;
}
module.exports = class EyeglassCompiler extends BroccoliSassCompiler {
constructor(inputTrees, options) {
options = merge({}, options);
let pristineOptions = merge({}, options);
if (!Array.isArray(inputTrees)) {
inputTrees = [inputTrees];
}
let configureEyeglass, assetDirectories, assetsHttpPrefix;
this.relativeAssets = options.relativeAssets;
delete options.relativeAssets;
if (options.configureEyeglass) {
configureEyeglass = options.configureEyeglass;
delete options.configureEyeglass;
}
if (options.assets) {
this.assetDirectories = options.assets;
if (typeof this.assetDirectories === "string") {
this.assetDirectories = [this.assetDirectories];
let relativeAssets = options.relativeAssets;
delete options.relativeAssets;
if (options.assets) {
assetDirectories = options.assets;
if (typeof assetDirectories === "string") {
assetDirectories = [assetDirectories];
}
delete options.assets;
}
delete options.assets;
}
if (options.assetsHttpPrefix) {
this.assetsHttpPrefix = options.assetsHttpPrefix;
delete options.assetsHttpPrefix;
}
if (options.assetsHttpPrefix) {
assetsHttpPrefix = options.assetsHttpPrefix;
delete options.assetsHttpPrefix;
}
// TODO: this should not be accessed before super (ES6 Aligment);
BroccoliSassCompiler.call(this, inputTrees, options);
this.events.on("compiling", this.handleNewFile.bind(this));
}
super(inputTrees, options);
EyeglassCompiler.prototype = Object.create(BroccoliSassCompiler.prototype);
EyeglassCompiler.prototype.constructor = EyeglassCompiler;
EyeglassCompiler.prototype.handleNewFile = function(details) {
if (!details.options.eyeglass) {
details.options.eyeglass = {};
this.pristineOptions = pristineOptions;
this.Eyeglass = require("eyeglass");
this.configureEyeglass = configureEyeglass;
this.relativeAssets = relativeAssets;
this.assetDirectories = assetDirectories;
this.assetsHttpPrefix = assetsHttpPrefix;
this.events.on("compiling", this.handleNewFile.bind(this));
}
if ((this.assetsHttpPrefix || this.assetsRelativeTo) && !details.options.eyeglass.assets) {
details.options.eyeglass.assets = {};
}
if (this.assetsHttpPrefix) {
details.options.eyeglass.assets.httpPrefix = this.assetsHttpPrefix;
}
if (this.relativeAssets) {
details.options.eyeglass.assets.relativeTo =
httpJoin(details.options.eyeglass.httpRoot || "/", path.dirname(details.cssFilename));
}
handleNewFile(details) {
if (!details.options.eyeglass) {
details.options.eyeglass = {};
}
if ((this.assetsHttpPrefix || this.assetsRelativeTo) && !details.options.eyeglass.assets) {
details.options.eyeglass.assets = {};
}
if (this.assetsHttpPrefix) {
details.options.eyeglass.assets.httpPrefix = this.assetsHttpPrefix;
}
details.options.eyeglass.buildDir = details.destDir;
details.options.eyeglass.engines = details.options.eyeglass.engines || {};
details.options.eyeglass.engines.sass = details.options.eyeglass.engines.sass || this.sass;
if (this.relativeAssets) {
details.options.eyeglass.assets.relativeTo =
httpJoin(details.options.eyeglass.httpRoot || "/", path.dirname(details.cssFilename));
}
var eyeglass = new this.Eyeglass(details.options);
details.options.eyeglass.buildDir = details.destDir;
details.options.eyeglass.engines = details.options.eyeglass.engines || {};
details.options.eyeglass.engines.sass = details.options.eyeglass.engines.sass || this.sass;
// set up asset dependency tracking
var self = this;
var realResolve = eyeglass.assets.resolve;
eyeglass.assets.resolve = function(filepath, fullUri, cb) {
self.events.emit("dependency", filepath);
realResolve.call(eyeglass.assets, filepath, fullUri, cb);
};
var realInstall = eyeglass.assets.install;
eyeglass.assets.install = function(file, uri, cb) {
realInstall.call(eyeglass.assets, file, uri, function(error, file) {
if (!error) {
self.events.emit("additional-output", file);
let eyeglass = new this.Eyeglass(details.options);
// set up asset dependency tracking
let self = this;
let realResolve = eyeglass.assets.resolve;
eyeglass.assets.resolve = function(filepath, fullUri, cb) {
self.events.emit("dependency", filepath);
realResolve.call(eyeglass.assets, filepath, fullUri, cb);
};
let realInstall = eyeglass.assets.install;
eyeglass.assets.install = function(file, uri, cb) {
realInstall.call(eyeglass.assets, file, uri, (error, file) => {
if (!error) {
self.events.emit("additional-output", file);
}
cb(error, file);
});
};
if (this.assetDirectories) {
for (var i = 0; i < this.assetDirectories.length; i++) {
eyeglass.assets.addSource(path.resolve(eyeglass.options.eyeglass.root,
this.assetDirectories[i]),
{
globOpts: {
ignore: ["**/*.js", "**/*.s[ac]ss"]
}
});
}
cb(error, file);
});
};
}
if (this.assetDirectories) {
for (var i = 0; i < this.assetDirectories.length; i++) {
eyeglass.assets.addSource(path.resolve(eyeglass.options.eyeglass.root,
this.assetDirectories[i]),
{
globOpts: {
ignore: ["**/*.js", "**/*.s[ac]ss"]
}
});
if (this.configureEyeglass) {
this.configureEyeglass(eyeglass, this.sass, details);
}
details.options = eyeglass.options;
details.options.eyeglass.engines.eyeglass = eyeglass;
}
if (this.configureEyeglass) {
this.configureEyeglass(eyeglass, this.sass, details);
cachableOptions(rawOptions) {
rawOptions = merge({}, rawOptions);
delete rawOptions.file;
if (rawOptions.eyeglass) {
delete rawOptions.eyeglass.engines;
delete rawOptions.eyeglass.modules;
}
return rawOptions;
}
details.options = eyeglass.options;
details.options.eyeglass.engines.eyeglass = eyeglass;
};
EyeglassCompiler.prototype.cachableOptions = function(rawOptions) {
rawOptions = merge({}, rawOptions);
delete rawOptions.file;
if (rawOptions.eyeglass) {
delete rawOptions.eyeglass.engines;
delete rawOptions.eyeglass.modules;
static currentVersion() {
let selfDir = path.resolve(path.join(__dirname, ".."));
let selfPkg = require(path.join(selfDir, "package.json"));
return selfPkg.version;
}
return rawOptions;
};
EyeglassCompiler.currentVersion = function() {
var selfDir = path.resolve(path.join(__dirname, ".."));
var selfPkg = require(path.join(selfDir, "package.json"));
return selfPkg.version;
};
dependenciesHash(srcDir, relativeFilename, options) {
return new RSVP.Promise(resolve => {
if (!this._dependenciesHash) {
let hashForDep = require("hash-for-dep");
let eyeglass = new this.Eyeglass(options);
let hash = crypto.createHash("sha1");
let cachableOptions = stringify(this.cachableOptions(options));
EyeglassCompiler.prototype.dependenciesHash = function(srcDir, relativeFilename, options) {
var self = this;
return new RSVP.Promise(function(resolve, reject) {
if (!self._dependenciesHash) {
var hashForDep = require("hash-for-dep");
var eyeglass = new self.Eyeglass(options);
var hash = crypto.createHash("sha1");
persistentCacheDebug("cachableOptions are %s", cachableOptions);
hash.update(cachableOptions);
hash.update("broccoli-eyeglass@" + EyeglassCompiler.currentVersion());
var cachableOptions = stringify(self.cachableOptions(options));
persistentCacheDebug("cachableOptions are %s", cachableOptions);
hash.update(cachableOptions);
hash.update("broccoli-eyeglass@" + EyeglassCompiler.currentVersion());
var egModules = sortby(eyeglass.modules.list, function(m) {
return m.name;
});
egModules.forEach(function(mod) {
if (mod.inDevelopment || mod.eyeglass.inDevelopment) {
hash.update(mod.name+"@"+hashForDep(mod.path));
} else {
hash.update(mod.name+"@"+mod.version);
}
});
self._dependenciesHash = hash.digest("hex");
}
resolve(self._dependenciesHash);
});
};
let egModules = sortby(eyeglass.modules.list, m => m.name);
EyeglassCompiler.prototype.keyForSourceFile = function(srcDir, relativeFilename, options) {
var keyPromise = BroccoliSassCompiler.prototype.keyForSourceFile.call(this,
srcDir, relativeFilename, options);
var dependenciesPromise = this.dependenciesHash(srcDir, relativeFilename, options);
return RSVP.all([keyPromise, dependenciesPromise]).then(function(results) {
var mungedKey = results[0] + "+" + results[1];
return mungedKey;
});
egModules.forEach(mod => {
if (mod.inDevelopment || mod.eyeglass.inDevelopment) {
hash.update(mod.name+"@"+hashForDep(mod.path));
} else {
hash.update(mod.name+"@"+mod.version);
}
});
this._dependenciesHash = hash.digest("hex");
}
resolve(this._dependenciesHash);
});
}
keyForSourceFile(srcDir, relativeFilename, options) {
let keyPromise = super.keyForSourceFile(srcDir, relativeFilename, options);
let dependenciesPromise = this.dependenciesHash(srcDir, relativeFilename, options);
return RSVP.all([
keyPromise,
dependenciesPromise
]).then(results => {
let mungedKey = results[0] + "+" + results[1];
return mungedKey;
});
}
};
module.exports = EyeglassCompiler;
{
"name": "broccoli-eyeglass",
"description": "Sass compiler for Broccoli with Eyeglass Integration",
"version": "3.0.1",
"version": "4.0.0",
"author": "Chris Eppstein <chris@eppsteins.net>",

@@ -13,3 +13,4 @@ "main": "lib/index.js",

"scripts": {
"test": "gulp test"
"test": "mocha test/test_*.js",
"test:debug": "mocha debug test/test_*.js"
},

@@ -26,3 +27,3 @@ "keywords": [

"async-promise-queue": "^1.0.1",
"broccoli-merge-trees": "^1.1.4",
"broccoli-merge-trees": "^2.0.0",
"broccoli-plugin": "^1.2.2",

@@ -34,4 +35,4 @@ "chained-emitter": "^0.1.2",

"fs-tree-diff": "^0.5.3",
"glob": "^5.0.3",
"hash-for-dep": "^1.0.3",
"glob": "^7.1.2",
"hash-for-dep": "^1.2.0",
"json-stable-stringify": "^1.0.1",

@@ -42,21 +43,19 @@ "lodash.merge": "^4.6.0",

"node-sass": "^4.0.0 || ^3.10.1",
"rsvp": "^3.0.21",
"rsvp": "^4.0.1",
"string.prototype.startswith": "^0.2.0",
"walk-sync": "^0.3.1"
},
"engines": {
"node": "6.* || >= 7.*"
},
"devDependencies": {
"broccoli": "^0.16.5",
"eslint": "^0.22.0",
"eyeglass-dev-eslint": "*",
"fixturify": "^0.3.0",
"grunt": "^0.4.5",
"grunt": "^1.0.1",
"grunt-release": "^0.12.0",
"gulp": "^3.9.0",
"gulp-eslint": "1.1.1",
"gulp-jshint": "^2.0.0",
"gulp-mocha": "^2.2.0",
"jshint": "^2.8.0",
"mocha": "^3.0.2",
"mocha": "^3.5.0",
"mocha-eslint": "^4.1.0",
"rimraf": "^2.5.4"
}
}

@@ -1,5 +0,5 @@

var debug = false;
var EyeglassCompiler = require("..");
const debug = false;
const EyeglassCompiler = require("..");
var tree = new EyeglassCompiler(["sass"], {
const tree = new EyeglassCompiler(["sass"], {
cssDir: "css",

@@ -13,5 +13,5 @@ verbose: true,

if (debug) {
var instrument = require("broccoli-debug").instrument;
let instrument = require("broccoli-debug").instrument;
tree = instrument.print(tree);
}
module.exports = tree;
/* Copyright 2016 LinkedIn Corp. Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
 You may obtain a copy of
* you may not use this file except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0

@@ -12,12 +12,12 @@ *

var assert = require("assert");
var path = require("path");
var fs = require("fs");
var rimraf = require("rimraf");
var fixturify = require("fixturify");
var broccoli = require("broccoli");
var RSVP = require("rsvp");
var glob = require("glob");
var EyeglassCompiler = require("../lib/index");
var AsyncDiskCache = require("async-disk-cache");
const assert = require("assert");
const path = require("path");
const fs = require("fs");
const rimraf = require("rimraf");
const fixturify = require("fixturify");
const broccoli = require("broccoli");
const RSVP = require("rsvp");
const glob = require("glob");
const EyeglassCompiler = require("../lib/index");
const AsyncDiskCache = require("async-disk-cache");

@@ -36,6 +36,7 @@ function fixtureDir(name) {

var fixtureDirCount = 0;
let fixtureDirCount = 0;
function makeFixtures(name, files) {
fixtureDirCount = fixtureDirCount + 1;
var dirname = fixtureDir(name + fixtureDirCount + ".tmp");
let dirname = fixtureDir(name + fixtureDirCount + ".tmp");
fs.mkdirSync(dirname);

@@ -49,20 +50,17 @@ fixturify.writeSync(dirname, files);

return RSVP.Promise.resolve()
.then(function() {
return builder.build();
})
.then(function(hash) {
return builder.tree.outputPath;
});
.then(() => builder.build())
.then(() => builder.tree.outputPath);
}
function assertEqualDirs(actualDir, expectedDir) {
var actualFiles = glob.sync("**/*", {cwd: actualDir}).sort();
var expectedFiles = glob.sync("**/*", {cwd: expectedDir}).sort();
let actualFiles = glob.sync("**/*", {cwd: actualDir}).sort();
let expectedFiles = glob.sync("**/*", {cwd: expectedDir}).sort();
assert.deepEqual(actualFiles, expectedFiles);
actualFiles.forEach(function(file) {
var actualPath = path.join(actualDir, file);
var expectedPath = path.join(expectedDir, file);
var stats = fs.statSync(actualPath);
actualFiles.forEach(file => {
let actualPath = path.join(actualDir, file);
let expectedPath = path.join(expectedDir, file);
let stats = fs.statSync(actualPath);
if (stats.isFile()) {

@@ -82,3 +80,3 @@ assert.equal(fs.readFileSync(actualPath).toString(),

var rectangleSVG = svg(
const rectangleSVG = svg(
' <rect x="10" y="10" height="100" width="100"\n' +

@@ -88,3 +86,3 @@ ' style="stroke:#ff0000; fill: #0000ff"/>\n'

var circleSVG = svg(
const circleSVG = svg(
'<circle cx="40" cy="40" r="24" style="stroke:#006600; fill:#00cc00"/>'

@@ -95,3 +93,3 @@ );

it("can be instantiated", function () {
var optimizer = new EyeglassCompiler(fixtureSourceDir("basicProject"), {
let optimizer = new EyeglassCompiler(fixtureSourceDir("basicProject"), {
cssDir: "."

@@ -103,10 +101,10 @@ });

it("compiles sass files", function () {
var optimizer = new EyeglassCompiler(fixtureSourceDir("basicProject"), {
let optimizer = new EyeglassCompiler(fixtureSourceDir("basicProject"), {
cssDir: "."
});
var builder = new broccoli.Builder(optimizer);
let builder = new broccoli.Builder(optimizer);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, fixtureOutputDir("basicProject"));

@@ -117,3 +115,3 @@ });

it("passes unknown options to eyeglass", function() {
var optimizer = new EyeglassCompiler(fixtureSourceDir("basicProject"), {
let optimizer = new EyeglassCompiler(fixtureSourceDir("basicProject"), {
cssDir: ".",

@@ -129,3 +127,3 @@ foo: true

assert.throws(
function() {
() => {
new EyeglassCompiler(fixtureSourceDir("basicProject"), {

@@ -142,3 +140,3 @@ cssDir: ".",

assert.throws(
function() {
() => {
new EyeglassCompiler(fixtureSourceDir("basicProject"), {

@@ -155,3 +153,3 @@ cssDir: ".",

assert.throws(
function() {
() => {
new EyeglassCompiler(fixtureSourceDir("basicProject"), {

@@ -167,3 +165,3 @@ cssDir: ".",

it("outputs exceptions when the fullException option is set", function() {
var optimizer = new EyeglassCompiler(fixtureSourceDir("errorProject"), {
let optimizer = new EyeglassCompiler(fixtureSourceDir("errorProject"), {
cssDir: ".",

@@ -173,8 +171,8 @@ fullException: true

var builder = new broccoli.Builder(optimizer);
let builder = new broccoli.Builder(optimizer);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, fixtureOutputDir("basicProject"));
}, function(error) {
}, error => {
assert.equal("property \"asdf\" must be followed by a ':'", error.message.split("\n")[0]);

@@ -185,3 +183,3 @@ });

it("supports manual modules", function() {
var optimizer = new EyeglassCompiler(fixtureSourceDir("usesManualModule"), {
let optimizer = new EyeglassCompiler(fixtureSourceDir("usesManualModule"), {
cssDir: ".",

@@ -196,6 +194,6 @@ fullException: true,

var builder = new broccoli.Builder(optimizer);
let builder = new broccoli.Builder(optimizer);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, fixtureOutputDir("usesManualModule"));

@@ -206,6 +204,5 @@ });

function cleanupTempDirs() {
var tmpDirs = glob.sync(path.join(path.resolve(__dirname, "fixtures"),"**", "*.tmp"));
tmpDirs.forEach(function(tmpDir) {
rimraf.sync(tmpDir);
});
let tmpDirs = glob.sync(path.join(path.resolve(__dirname, "fixtures"),"**", "*.tmp"));
tmpDirs.forEach(tmpDir => rimraf.sync(tmpDir));
}

@@ -217,8 +214,8 @@

it("caches when an unrelated file changes", function() {
var sourceDir = fixtureSourceDir("basicProject");
var unusedSourceFile = path.join(sourceDir, "styles", "_unused.scss");
var compiledFiles = [];
var compiler = new EyeglassCompiler(sourceDir, {
let sourceDir = fixtureSourceDir("basicProject");
let unusedSourceFile = path.join(sourceDir, "styles", "_unused.scss");
let compiledFiles = [];
let compiler = new EyeglassCompiler(sourceDir, {
cssDir: ".",
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
compiledFiles.push(sassFile);

@@ -231,6 +228,6 @@ cb(cssFile, options);

var builder = new broccoli.Builder(compiler);
let builder = new broccoli.Builder(compiler);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, fixtureOutputDir("basicProject"));

@@ -240,3 +237,3 @@ assert.equal(1, compiledFiles.length);

fs.writeFileSync(unusedSourceFile, "// changed but still not used.");
return build(builder).then(function(outputDir2) {
return build(builder).then(outputDir2 => {
assert.equal(outputDir, outputDir2);

@@ -249,7 +246,7 @@ assert.equal(1, compiledFiles.length);

it("doesn't cache when there's a change", function() {
var sourceDir = fixtureSourceDir("basicProject");
var compiledFiles = [];
var compiler = new EyeglassCompiler(sourceDir, {
let sourceDir = fixtureSourceDir("basicProject");
let compiledFiles = [];
let compiler = new EyeglassCompiler(sourceDir, {
cssDir: ".",
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
compiledFiles.push(sassFile);

@@ -260,12 +257,12 @@ cb(cssFile, options);

var builder = new broccoli.Builder(compiler);
let builder = new broccoli.Builder(compiler);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, fixtureOutputDir("basicProject"));
assert.equal(1, compiledFiles.length);
var sourceFile = path.join(sourceDir, "styles", "foo.scss");
var originalSource = fs.readFileSync(sourceFile);
var newSource = "@import \"used\";\n" +
let sourceFile = path.join(sourceDir, "styles", "foo.scss");
let originalSource = fs.readFileSync(sourceFile);
let newSource = "@import \"used\";\n" +
"$color: blue;\n" +

@@ -276,14 +273,15 @@ ".foo {\n" +

var newExpectedOutput = ".foo {\n" +
let newExpectedOutput = ".foo {\n" +
" color: blue; }\n";
fs.writeFileSync(sourceFile, newSource);
return build(builder)
.then(function(outputDir2) {
.then(outputDir2 => {
assert.equal(outputDir, outputDir2);
var outputFile = path.join(outputDir2, "styles", "foo.css");
let outputFile = path.join(outputDir2, "styles", "foo.css");
assert.equal(newExpectedOutput, fs.readFileSync(outputFile));
assert.equal(2, compiledFiles.length);
})
.finally(function() {
.finally(() => {
fs.writeFileSync(sourceFile, originalSource);

@@ -295,18 +293,18 @@ });

it("caches on the 3rd build", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": '@import "external";',
"_unrelated.scss": "/* This is unrelated to anything. */"
});
var includeDir = makeFixtures("includeDir", {
let includeDir = makeFixtures("includeDir", {
"external.scss": ".external { float: left; }"
});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": ".external {\n float: left; }\n"
});
var compiledFiles = [];
var compiler = new EyeglassCompiler(projectDir, {
let compiledFiles = [];
let compiler = new EyeglassCompiler(projectDir, {
cssDir: ".",
includePaths: [includeDir],
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
compiledFiles.push(sassFile);

@@ -317,6 +315,6 @@ cb(cssFile, options);

var builder = new broccoli.Builder(compiler);
let builder = new broccoli.Builder(compiler);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -331,3 +329,3 @@ assert.equal(1, compiledFiles.length);

return build(builder)
.then(function(outputDir2) {
.then(outputDir2 => {
assert.equal(outputDir, outputDir2);

@@ -342,3 +340,3 @@ assert.equal(compiledFiles.length, 0);

return build(builder)
.then(function(outputDir2) {
.then(outputDir2 => {
assert.equal(outputDir, outputDir2);

@@ -353,17 +351,17 @@ assert.equal(compiledFiles.length, 0);

it("busts cache when file reached via includePaths changes", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": '@import "external";'
});
var includeDir = makeFixtures("includeDir", {
let includeDir = makeFixtures("includeDir", {
"external.scss": ".external { float: left; }"
});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": ".external {\n float: left; }\n"
});
var compiledFiles = [];
var compiler = new EyeglassCompiler(projectDir, {
let compiledFiles = [];
let compiler = new EyeglassCompiler(projectDir, {
cssDir: ".",
includePaths: [includeDir],
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
compiledFiles.push(sassFile);

@@ -374,6 +372,6 @@ cb(cssFile, options);

var builder = new broccoli.Builder(compiler);
let builder = new broccoli.Builder(compiler);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -392,3 +390,3 @@ assert.equal(1, compiledFiles.length);

return build(builder)
.then(function(outputDir2) {
.then(outputDir2 => {
assert.equal(outputDir, outputDir2);

@@ -402,17 +400,17 @@ assert.equal(compiledFiles.length, 1);

it("busts cache when file mode changes", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": '@import "external";'
});
var includeDir = makeFixtures("includeDir", {
let includeDir = makeFixtures("includeDir", {
"external.scss": ".external { float: left; }"
});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": ".external {\n float: left; }\n"
});
var compiledFiles = [];
var compiler = new EyeglassCompiler(projectDir, {
let compiledFiles = [];
let compiler = new EyeglassCompiler(projectDir, {
cssDir: ".",
includePaths: [includeDir],
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
compiledFiles.push(sassFile);

@@ -423,6 +421,6 @@ cb(cssFile, options);

var builder = new broccoli.Builder(compiler);
let builder = new broccoli.Builder(compiler);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -435,3 +433,3 @@ assert.equal(1, compiledFiles.length);

return build(builder)
.then(function(outputDir2) {
.then(outputDir2 => {
assert.equal(outputDir, outputDir2);

@@ -445,6 +443,6 @@ assert.equal(compiledFiles.length, 1);

it("busts cache when an eyeglass module is upgraded", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": '@import "eyeglass-module";'
});
var eyeglassModDir = makeFixtures("eyeglassmod", {
let eyeglassModDir = makeFixtures("eyeglassmod", {
"package.json": "{\n" +

@@ -465,10 +463,10 @@ ' "name": "is_a_module",\n' +

});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": ".eyeglass-mod {\n content: eyeglass; }\n"
});
var compiledFiles = [];
var compiler = new EyeglassCompiler(projectDir, {
let compiledFiles = [];
let compiler = new EyeglassCompiler(projectDir, {
cssDir: ".",
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
compiledFiles.push(sassFile);

@@ -484,6 +482,6 @@ cb(cssFile, options);

var builder = new broccoli.Builder(compiler);
let builder = new broccoli.Builder(compiler);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -504,3 +502,3 @@ assert.equal(1, compiledFiles.length);

return build(builder)
.then(function(outputDir2) {
.then(outputDir2 => {
assert.equal(outputDir, outputDir2);

@@ -514,3 +512,3 @@ assert.equal(compiledFiles.length, 1);

it("busts cache when an eyeglass asset changes", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss":

@@ -520,3 +518,3 @@ '@import "eyeglass-module/assets";\n' +

});
var eyeglassModDir = makeFixtures("eyeglassmod2", {
let eyeglassModDir = makeFixtures("eyeglassmod2", {
"package.json": "{\n" +

@@ -548,3 +546,3 @@ ' "name": "is_a_module",\n' +

var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"eyeglass-module": {

@@ -556,6 +554,6 @@ "shape.svg": rectangleSVG

var compiledFiles = [];
var compiler = new EyeglassCompiler(projectDir, {
let compiledFiles = [];
let compiler = new EyeglassCompiler(projectDir, {
cssDir: ".",
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
compiledFiles.push(sassFile);

@@ -571,6 +569,6 @@ cb(cssFile, options);

var builder = new broccoli.Builder(compiler);
let builder = new broccoli.Builder(compiler);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -595,3 +593,3 @@ assert.equal(1, compiledFiles.length);

return build(builder)
.then(function(outputDir2) {
.then(outputDir2 => {
assert.equal(outputDir, outputDir2);

@@ -605,20 +603,20 @@ assert.equal(compiledFiles.length, 1);

it("busts cache when file reached via ../ outside the load path changes", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": '@import "external";'
});
var relativeIncludeDir = makeFixtures("relativeIncludeDir", {
let relativeIncludeDir = makeFixtures("relativeIncludeDir", {
"relative.scss": ".external { float: left; }"
});
var includeDir = makeFixtures("includeDir", {
let includeDir = makeFixtures("includeDir", {
"external.scss": '@import "../relativeIncludeDir' + fixtureDirCount + '.tmp/relative";'
});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": ".external {\n float: left; }\n"
});
var compiledFiles = [];
var compiler = new EyeglassCompiler(projectDir, {
let compiledFiles = [];
let compiler = new EyeglassCompiler(projectDir, {
cssDir: ".",
includePaths: [includeDir],
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
compiledFiles.push(sassFile);

@@ -629,6 +627,6 @@ cb(cssFile, options);

var builder = new broccoli.Builder(compiler);
let builder = new broccoli.Builder(compiler);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -647,3 +645,3 @@ assert.equal(1, compiledFiles.length);

return build(builder)
.then(function(outputDir2) {
.then(outputDir2 => {
assert.equal(outputDir, outputDir2);

@@ -657,13 +655,13 @@ assert.equal(compiledFiles.length, 1);

it("removes a css file when the corresponding sass file is removed", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": "/* project */"
});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": "/* project */\n"
});
var compiledFiles = [];
var compiler = new EyeglassCompiler(projectDir, {
let compiledFiles = [];
let compiler = new EyeglassCompiler(projectDir, {
cssDir: ".",
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
compiledFiles.push(sassFile);

@@ -674,6 +672,6 @@ cb(cssFile, options);

var builder = new broccoli.Builder(compiler);
let builder = new broccoli.Builder(compiler);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -692,3 +690,3 @@ assert.equal(1, compiledFiles.length);

return build(builder)
.then(function(outputDir2) {
.then(outputDir2 => {
assert.equal(outputDir, outputDir2);

@@ -702,3 +700,3 @@ assert.equal(compiledFiles.length, 0);

it("removes an asset file when the corresponding sass file is removed", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss":

@@ -708,3 +706,3 @@ '@import "eyeglass-module/assets";\n' +

});
var eyeglassModDir = makeFixtures("eyeglassmod", {
let eyeglassModDir = makeFixtures("eyeglassmod", {
"package.json": "{\n" +

@@ -736,3 +734,3 @@ ' "name": "is_a_module",\n' +

var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"eyeglass-module": {

@@ -744,6 +742,6 @@ "shape.svg": rectangleSVG

var compiledFiles = [];
var compiler = new EyeglassCompiler(projectDir, {
let compiledFiles = [];
let compiler = new EyeglassCompiler(projectDir, {
cssDir: ".",
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
compiledFiles.push(sassFile);

@@ -759,6 +757,6 @@ cb(cssFile, options);

var builder = new broccoli.Builder(compiler);
let builder = new broccoli.Builder(compiler);
return build(builder)
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -780,3 +778,3 @@ assert.equal(1, compiledFiles.length);

return build(builder)
.then(function(outputDir2) {
.then(outputDir2 => {
assert.equal(outputDir, outputDir2);

@@ -795,3 +793,3 @@ assert.equal(compiledFiles.length, 0);

afterEach(function() {
var cache = new AsyncDiskCache("test");
let cache = new AsyncDiskCache("test");
return cache.clear();

@@ -801,7 +799,8 @@ });

function warmBuilders(count, dir, options, compilationListener) {
var builders = [];
let builders = [];
for (var i = 0; i < count; i++) {
var compiler = new EyeglassCompiler(dir, options);
let compiler = new EyeglassCompiler(dir, options);
compiler.events.on("compiled", compilationListener);
var builder = new broccoli.Builder(compiler);
let builder = new broccoli.Builder(compiler);
builder.compiler = compiler;

@@ -816,15 +815,15 @@ builders.push(builder);

it("preserves cache across builder instances", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": '@import "related";',
"_related.scss": "/* This is related to something. */"
});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": "/* This is related to something. */\n"
});
var compiledFiles = [];
var builders = warmBuilders(2, projectDir, {
let compiledFiles = [];
let builders = warmBuilders(2, projectDir, {
cssDir: ".",
persistentCache: "test"
}, function(details) {
}, details => {
compiledFiles.push(details.fullSassFilename);

@@ -834,3 +833,3 @@ });

return build(builders[0])
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -841,3 +840,3 @@ assert.equal(1, compiledFiles.length);

return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -851,15 +850,15 @@ assert.equal(compiledFiles.length, 0);

it("invalidates when a dependent file changes.", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": '@import "related";',
"_related.scss": "/* This is related to something. */"
});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": "/* This is related to something. */\n"
});
var compiledFiles = [];
var builders = warmBuilders(2, projectDir, {
let compiledFiles = [];
let builders = warmBuilders(2, projectDir, {
cssDir: ".",
persistentCache: "test"
}, function(details) {
}, details => {
compiledFiles.push(details.fullSassFilename);

@@ -869,3 +868,3 @@ });

return build(builders[0])
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -884,3 +883,3 @@ assert.equal(1, compiledFiles.length);

return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -894,3 +893,3 @@ assert.equal(compiledFiles.length, 1);

it("restored side-effect outputs when cached.", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"sass": {

@@ -904,3 +903,3 @@ "project.scss": '@import "assets";\n' +

});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": '.shape {\n content: url("/shape.svg"); }\n',

@@ -910,4 +909,4 @@ "shape.svg": rectangleSVG

var compiledFiles = [];
var builders = warmBuilders(2, projectDir, {
let compiledFiles = [];
let builders = warmBuilders(2, projectDir, {
sassDir: "sass",

@@ -920,3 +919,3 @@ assets: "assets",

}
}, function(details) {
}, details => {
compiledFiles.push(details.fullSassFilename);

@@ -926,3 +925,3 @@ });

return build(builders[0])
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -933,3 +932,3 @@ assert.equal(1, compiledFiles.length);

return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -943,3 +942,3 @@ assert.equal(compiledFiles.length, 0);

it("invalidates when non-sass file dependencies change.", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"sass": {

@@ -954,3 +953,3 @@ "project.scss": '@import "assets";\n' +

var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": '.shape {\n content: url("/shape.svg"); }\n',

@@ -960,4 +959,4 @@ "shape.svg": rectangleSVG

var compiledFiles = [];
var builders = warmBuilders(2, projectDir, {
let compiledFiles = [];
let builders = warmBuilders(2, projectDir, {
sassDir: "sass",

@@ -970,3 +969,3 @@ assets: "assets",

}
}, function(details) {
}, details => {
compiledFiles.push(details.fullSassFilename);

@@ -976,3 +975,3 @@ });

return build(builders[0])
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -993,3 +992,3 @@ assert.equal(1, compiledFiles.length);

return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -1003,7 +1002,7 @@ assert.equal(compiledFiles.length, 1);

it("invalidates when eyeglass modules javascript files changes.", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss":
".foo { content: foo(); }\n"
});
var eyeglassModDir = makeFixtures("eyeglassmod", {
let eyeglassModDir = makeFixtures("eyeglassmod", {
"package.json": "{\n" +

@@ -1044,9 +1043,9 @@ ' "name": "is_a_module",\n' +

var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": ".foo {\n content: foo; }\n"
});
var compiledFiles = [];
let compiledFiles = [];
var builders = warmBuilders(2, projectDir, {
let builders = warmBuilders(2, projectDir, {
cssDir: ".",

@@ -1059,3 +1058,3 @@ persistentCache: "test",

}
}, function(details) {
}, details => {
compiledFiles.push(details.fullSassFilename);

@@ -1066,3 +1065,3 @@ });

return build(builders[0])
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -1087,3 +1086,3 @@ assert.equal(1, compiledFiles.length);

return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -1097,7 +1096,7 @@ assert.equal(compiledFiles.length, 1);

it("invalidates when eyeglass modules javascript files changes (package.json).", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss":
".foo { content: foo(); }\n"
});
var eyeglassModDir = makeFixtures("eyeglassmod3", {
let eyeglassModDir = makeFixtures("eyeglassmod3", {
"package.json": "{\n" +

@@ -1138,9 +1137,9 @@ ' "name": "is_a_module",\n' +

var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": ".foo {\n content: foo; }\n"
});
var compiledFiles = [];
let compiledFiles = [];
var builders = warmBuilders(2, projectDir, {
let builders = warmBuilders(2, projectDir, {
cssDir: ".",

@@ -1153,3 +1152,3 @@ persistentCache: "test",

}
}, function(details) {
}, details => {
compiledFiles.push(details.fullSassFilename);

@@ -1160,3 +1159,3 @@ });

return build(builders[0])
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -1181,3 +1180,3 @@ assert.equal(1, compiledFiles.length);

return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -1191,15 +1190,15 @@ assert.equal(compiledFiles.length, 1);

it("can force invalidate the persistent cache", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": '@import "related";',
"_related.scss": "/* This is related to something. */"
});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": "/* This is related to something. */\n"
});
var compiledFiles = [];
var builders = warmBuilders(2, projectDir, {
let compiledFiles = [];
let builders = warmBuilders(2, projectDir, {
cssDir: ".",
persistentCache: "test"
}, function(details) {
}, details => {
compiledFiles.push(details.fullSassFilename);

@@ -1209,3 +1208,3 @@ });

return build(builders[0])
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -1218,3 +1217,3 @@ assert.equal(1, compiledFiles.length);

return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -1224,3 +1223,3 @@ assert.equal(1, compiledFiles.length);

})
.finally(function() {
.finally(() => {
delete process.env["BROCCOLI_EYEGLASS"];

@@ -1232,15 +1231,15 @@ });

it("busts cache when options used for compilation are different", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": '@import "related";',
"_related.scss": "/* This is related to something. */"
});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": "/* This is related to something. */\n"
});
var compiledFiles = [];
var builders = warmBuilders(2, projectDir, {
let compiledFiles = [];
let builders = warmBuilders(2, projectDir, {
cssDir: ".",
persistentCache: "test"
}, function(details) {
}, details => {
compiledFiles.push(details.fullSassFilename);

@@ -1250,3 +1249,3 @@ });

return build(builders[0])
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -1259,3 +1258,3 @@ assert.equal(1, compiledFiles.length);

return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -1269,20 +1268,20 @@ assert.equal(compiledFiles.length, 1);

it("can use the rebuild cache after restoring from the persistent cache.", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": '@import "related";',
"_related.scss": "/* This is related to something. */"
});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": "/* This is related to something. */\n"
});
var compiledFiles = [];
var hotCompiledFiles = [];
var builders = warmBuilders(2, projectDir, {
let compiledFiles = [];
let hotCompiledFiles = [];
let builders = warmBuilders(2, projectDir, {
cssDir: ".",
persistentCache: "test",
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
hotCompiledFiles.push(sassFile);
cb(cssFile, options);
}
}, function(details) {
}, details => {
compiledFiles.push(details.fullSassFilename);

@@ -1292,3 +1291,3 @@ });

return build(builders[0])
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -1301,3 +1300,3 @@ assert.equal(1, compiledFiles.length);

return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -1312,3 +1311,3 @@ assert.equal(compiledFiles.length, 0);

return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -1324,20 +1323,20 @@ assert.equal(compiledFiles.length, 0);

it("busts the rebuild cache after restoring from the persistent cache.", function() {
var projectDir = makeFixtures("projectDir", {
let projectDir = makeFixtures("projectDir", {
"project.scss": '@import "related";',
"_related.scss": "/* This is related to something. */"
});
var expectedOutputDir = makeFixtures("expectedOutputDir", {
let expectedOutputDir = makeFixtures("expectedOutputDir", {
"project.css": "/* This is related to something. */\n"
});
var compiledFiles = [];
var hotCompiledFiles = [];
var builders = warmBuilders(2, projectDir, {
let compiledFiles = [];
let hotCompiledFiles = [];
let builders = warmBuilders(2, projectDir, {
cssDir: ".",
persistentCache: "test",
optionsGenerator: function(sassFile, cssFile, options, cb) {
optionsGenerator(sassFile, cssFile, options, cb) {
hotCompiledFiles.push(sassFile);
cb(cssFile, options);
}
}, function(details) {
}, details => {
compiledFiles.push(details.fullSassFilename);

@@ -1347,3 +1346,3 @@ });

return build(builders[0])
.then(function(outputDir) {
.then(outputDir => {
assertEqualDirs(outputDir, expectedOutputDir);

@@ -1356,3 +1355,3 @@ assert.equal(1, compiledFiles.length);

return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -1371,7 +1370,8 @@ assert.equal(compiledFiles.length, 0);

fixturify.writeSync(expectedOutputDir, {
"project.css": "/* This is related to something. */\n.something {\n color: red; }\n"
"project.css": "/* This is related to something. */\n" +
".something {\n color: red; }\n"
});
return build(builders[1])
.then(function(outputDir2) {
.then(outputDir2 => {
assert.notEqual(outputDir, outputDir2);

@@ -1378,0 +1378,0 @@ assert.equal(compiledFiles.length, 1);

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc