Skip to content

Commit

Permalink
Refactor to add async API variant
Browse files Browse the repository at this point in the history
  • Loading branch information
jgranstrom committed Jan 23, 2017
1 parent f545999 commit 90c5797
Show file tree
Hide file tree
Showing 5 changed files with 153 additions and 52 deletions.
17 changes: 14 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
const { renderSync } = require('./lib/render');
const { extractSync } = require('./lib/extract');
const { render, renderSync } = require('./lib/render');
const { extract, extractSync } = require('./lib/extract');

exports.render = render;
exports.renderSync = renderSync;
exports.extract = extract;
exports.extractSync = extractSync;

const res = exports.renderSync({
file: require('path').join(__dirname, 'test.scss'),
});

console.log('vars', res.vars);

console.log('vars sync', res.vars);

exports.render({
file: require('path').join(__dirname, 'test.scss'),
})
.then(res => {
console.log('vars async', res.vars);
}).done();


setInterval(() => {

Expand Down
163 changes: 116 additions & 47 deletions lib/extract.js
Original file line number Diff line number Diff line change
@@ -1,46 +1,61 @@
const Promise = require('bluebird');
const sass = require('node-sass');
const path = require('path');
const { parseDeclarations } = require('./parse');
const { loadSync } = require('./load');
const { load, loadSync } = require('./load');
const { injectFunctions } = require('./inject');

exports.extractSync = (rendered, { compileOptions = {} } = {}) => {
const extractionMap = {};
let completeExtractionFunctions = {};
const entryFilename = rendered.stats.entry;

Promise.promisifyAll(sass);

rendered.stats.includedFiles.forEach(filename => {
const includedData = loadSync(filename);
const declarations = parseDeclarations(includedData);
const variableResults = {};
function compileResult(extractionMap) {
const extractedVariables = {};

const declarationResultHandler = (context, declaration, value) => {
if(!variableResults[context]) {
variableResults[context] = {};
}
Object.keys(extractionMap).map(filename => {
Object.keys(extractionMap[filename].variableResults).map(variableContext => {
Object.keys(extractionMap[filename].variableResults[variableContext]).map(variableKey => {
if(!extractedVariables[variableContext]) {
extractedVariables[variableContext] = {};
}

variableResults[context][declaration.declaration] = { value, expression: declaration.expression };
}
if(!extractedVariables[variableContext][variableKey]) {
extractedVariables[variableContext][variableKey] = Object.assign({}, extractionMap[filename].variableResults[variableContext][variableKey].value);

const injection = injectFunctions(filename, includedData, declarations, declarationResultHandler);
completeExtractionFunctions = Object.assign({}, completeExtractionFunctions, injection.injectedFunctions);
extractedVariables[variableContext][variableKey].sources = [];
extractedVariables[variableContext][variableKey].expressions = [];
}

extractionMap[filename] = {
includedData,
declarations,
variableResults,
injectedData: injection.injectedData,
injectedFunctions: injection.injectedFunctions,
}
extractedVariables[variableContext][variableKey].sources.push(filename);
extractedVariables[variableContext][variableKey].expressions.push(extractionMap[filename].variableResults[variableContext][variableKey].expression);
});
});
});

const extractionCompileOptions = Object.assign({}, compileOptions);
return extractedVariables;
}

extractionCompileOptions.functions = completeExtractionFunctions;
extractionCompileOptions.filename = entryFilename;
extractionCompileOptions.data = extractionMap[entryFilename].injectedData;
extractionCompileOptions.importer = function(url, prev) {
function makeImporter(extractionMap) {
return function(url, prev, done) {
// TOOD: Add async variant of importer
try {
let absolutePath = path.join(path.dirname(prev), url);
let extension = path.extname(prev);

// Ensure import directive path include extension
if(path.extname(absolutePath) !== extension) {
absolutePath += extension;
}

done({ file: absolutePath, contents: extractionMap[absolutePath].injectedData });
} catch(err) {
// note: importer has to return any error
done(err);
}
}
}

function makeSyncImporter(extractionMap) {
return function(url, prev) {
// TOOD: Add async variant of importer
try {
let absolutePath = path.join(path.dirname(prev), url);
let extension = path.extname(prev);
Expand All @@ -56,30 +71,84 @@ exports.extractSync = (rendered, { compileOptions = {} } = {}) => {
return err;
}
}
}

sass.renderSync(extractionCompileOptions);
function augmentExtractionCompilerOption(compileOptions, completeExtractionFunctions, entryFilename, extractionMap) {
const extractionCompileOptions = Object.assign({}, compileOptions);

const extractedVariables = {};
extractionCompileOptions.functions = completeExtractionFunctions;
extractionCompileOptions.filename = entryFilename;
extractionCompileOptions.data = extractionMap[entryFilename].injectedData;

Object.keys(extractionMap).map(filename => {
Object.keys(extractionMap[filename].variableResults).map(variableContext => {
Object.keys(extractionMap[filename].variableResults[variableContext]).map(variableKey => {
if(!extractedVariables[variableContext]) {
extractedVariables[variableContext] = {};
}
return extractionCompileOptions;
}

if(!extractedVariables[variableContext][variableKey]) {
extractedVariables[variableContext][variableKey] = Object.assign({}, extractionMap[filename].variableResults[variableContext][variableKey].value);
function processFile(filename, includedData, extractionMap, completeExtractionFunctions) {
const declarations = parseDeclarations(includedData);
const variableResults = {};

extractedVariables[variableContext][variableKey].sources = [];
extractedVariables[variableContext][variableKey].expressions = [];
}
const declarationResultHandler = (context, declaration, value) => {
if(!variableResults[context]) {
variableResults[context] = {};
}

extractedVariables[variableContext][variableKey].sources.push(filename);
extractedVariables[variableContext][variableKey].expressions.push(extractionMap[filename].variableResults[variableContext][variableKey].expression);
});
});
variableResults[context][declaration.declaration] = { value, expression: declaration.expression };
}

const injection = injectFunctions(filename, includedData, declarations, declarationResultHandler);
completeExtractionFunctions = Object.assign(completeExtractionFunctions, injection.injectedFunctions);

extractionMap[filename] = {
includedData,
declarations,
variableResults,
injectedData: injection.injectedData,
injectedFunctions: injection.injectedFunctions,
};
}

exports.extract = (rendered, { compileOptions = {} } = {}) => {
const extractionMap = {};
let completeExtractionFunctions = {};
const entryFilename = rendered.stats.entry;

return Promise.resolve()
.then(() => {
return Promise.all(rendered.stats.includedFiles.map(filename => {
return load(filename)
.then(includedData => {
processFile(filename, includedData, extractionMap, completeExtractionFunctions);
})
}));
})
.then(() => {
const extractionCompileOptions = augmentExtractionCompilerOption(compileOptions, completeExtractionFunctions, entryFilename, extractionMap);
extractionCompileOptions.importer = makeImporter(extractionMap);
return sass.renderAsync(extractionCompileOptions);
})
.then(() => {
const extractedVariables = compileResult(extractionMap);
return extractedVariables;
});
}

exports.extractSync = (rendered, { compileOptions = {} } = {}) => {
const extractionMap = {};
let completeExtractionFunctions = {};
const entryFilename = rendered.stats.entry;


rendered.stats.includedFiles.forEach(filename => {
const includedData = loadSync(filename);
processFile(filename, includedData, extractionMap, completeExtractionFunctions);
});

const extractionCompileOptions = augmentExtractionCompilerOption(compileOptions, completeExtractionFunctions, entryFilename, extractionMap);
extractionCompileOptions.importer = makeSyncImporter(extractionMap);

sass.renderSync(extractionCompileOptions);

const extractedVariables = compileResult(extractionMap);

return extractedVariables;
}
6 changes: 6 additions & 0 deletions lib/load.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
const Promise = require('bluebird');
const fs = require('fs');
Promise.promisifyAll(fs);

exports.load = (filename, encoding = 'utf8') => {
return fs.readFileAsync(filename, encoding);
}

exports.loadSync = (filename, encoding = 'utf8') => {
return fs.readFileSync(filename, encoding);
Expand Down
18 changes: 16 additions & 2 deletions lib/render.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,22 @@
const Promise = require('bluebird');
const sass = require('node-sass');
const { extractSync } = require('./extract');
const { extract, extractSync } = require('./extract');

Promise.promisifyAll(sass);

exports.render = (compileOptions = {}) => {
return sass.renderAsync(compileOptions)
.then(rendered => {
return extract(rendered, { compileOptions} )
.then(vars => {
rendered.vars = vars;
return rendered;
})
});
}

exports.renderSync = (compileOptions = {}) => {
const rendered = sass.renderSync(compileOptions);
rendered.vars = extractSync(rendered, { compileOptions })
return rendered;
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"author": "",
"license": "ISC",
"dependencies": {
"bluebird": "^3.4.7",
"node-sass": "^4.3.0"
}
}

0 comments on commit 90c5797

Please sign in to comment.