Skip to content

Commit

Permalink
feat(process): modify processing order and dependent declarations inj…
Browse files Browse the repository at this point in the history
…ections to better handle dynami

This can change the output of variables that are being set by mixins, functions and with mixes of
default values. Previously they could be set to the wrong final value, while as they now will be the
same as in the resulting css.
  • Loading branch information
jgranstrom committed Nov 30, 2017
1 parent 257db5f commit aedd820
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 16 deletions.
15 changes: 11 additions & 4 deletions src/extract.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Promise from 'bluebird';
import sass from 'node-sass';
import { normalizePath, makeAbsolute } from './util';
import { loadCompiledFiles, loadCompiledFilesSync } from './load';
import { processFiles } from './process';
import { processFiles, parseFiles } from './process';
import { makeImporter, makeSyncImporter } from './importer';
import { Pluggable } from './pluggable';

Expand Down Expand Up @@ -62,7 +62,12 @@ function compileExtractionResult(orderedFiles, extractions) {
currentVariableDeclarations = variable.declarations;
}

variable = extractedVariables.global[variableKey] = Object.assign({}, extractedVariable.value);
const hasOnlyDefaults = currentVariableDeclarations.every(declaration => declaration.flags.default);
const currentIsDefault = extractedVariable.declaration.flags.default;

if(currentVariableDeclarations.length === 0 || !currentIsDefault || hasOnlyDefaults) {
variable = extractedVariables.global[variableKey] = Object.assign({}, extractedVariable.value);
}
variable.sources = currentVariableSources.indexOf(filename) < 0 ? [...currentVariableSources, filename] : currentVariableSources;
variable.declarations = [...currentVariableDeclarations, {
expression: extractedVariable.declaration.expression,
Expand All @@ -88,7 +93,8 @@ export function extract(rendered, { compileOptions = {}, extractOptions = {} } =

return loadCompiledFiles(includedFiles, entryFilename, compileOptions.data)
.then(({ compiledFiles, orderedFiles }) => {
const extractions = processFiles(compiledFiles, pluggable);
const parsedDeclarations = parseFiles(compiledFiles);
const extractions = processFiles(orderedFiles, compiledFiles, parsedDeclarations, pluggable);
const importer = makeImporter(extractions, includedFiles, includedPaths);
const extractionCompileOptions = makeExtractionCompileOptions(compileOptions, entryFilename, extractions, importer);

Expand All @@ -109,7 +115,8 @@ export function extractSync(rendered, { compileOptions = {}, extractOptions = {}
const { entryFilename, includedFiles, includedPaths } = getRenderedStats(rendered, compileOptions);

const { compiledFiles, orderedFiles } = loadCompiledFilesSync(includedFiles, entryFilename, compileOptions.data);
const extractions = processFiles(compiledFiles, pluggable);
const parsedDeclarations = parseFiles(compiledFiles);
const extractions = processFiles(orderedFiles, compiledFiles, parsedDeclarations, pluggable);
const importer = makeSyncImporter(extractions, includedFiles, includedPaths);
const extractionCompileOptions = makeExtractionCompileOptions(compileOptions, entryFilename, extractions, importer);

Expand Down
12 changes: 11 additions & 1 deletion src/inject.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { createStructuredValue } from './struct';
const FN_PREFIX = '___SV_INJECT';
const FN_PREFIX_IMPLICIT_GLOBAL = 'IG';
const FN_PREFIX_EXPLICIT_GLOBAL = 'EG';
const FN_PREFIX_DEPENDENT_GLOBAL = 'DG';
const FN_SUFFIX_VALUE = 'VALUE';

/**
Expand Down Expand Up @@ -30,7 +31,7 @@ function createInjection(fileId, categoryPrefix, declaration, idx, declarationRe
* Declaration result handlers will be called with the extracted value of each declaration
* Provided file id will be used to ensure unique function names per file
*/
export function injectExtractionFunctions(fileId, declarations, { globalDeclarationResultHandler }) {
export function injectExtractionFunctions(fileId, declarations, dependentDeclarations, { globalDeclarationResultHandler }) {
let injectedData = ``;
const injectedFunctions = {};

Expand All @@ -48,5 +49,14 @@ export function injectExtractionFunctions(fileId, declarations, { globalDeclarat
injectedData += injectedCode;
});

dependentDeclarations.forEach(({ declaration, decFileId }, idx) => {
// Do not add dependent injection if the declaration is in the current file
// It will already be added by explicits
if(decFileId === fileId) { return; }
const { fnName, injectedFunction, injectedCode } = createInjection(fileId, FN_PREFIX_DEPENDENT_GLOBAL, declaration, idx, globalDeclarationResultHandler);
injectedFunctions[fnName] = injectedFunction;
injectedData += injectedCode;
});

return { injectedData, injectedFunctions };
}
4 changes: 3 additions & 1 deletion src/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@ function includeRawDataFile(includedFiles, files, entryFilename, data) {

if(entryFilename === RAW_DATA_FILE && data) {
files[RAW_DATA_FILE] = data;
orderedFiles = [RAW_DATA_FILE, ...orderedFiles];
orderedFiles = [...orderedFiles, RAW_DATA_FILE];
} else if(orderedFiles.length > 0) {
orderedFiles = [...orderedFiles.slice(1), orderedFiles[0]];
}

return {
Expand Down
46 changes: 40 additions & 6 deletions src/process.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,30 @@ function getFileId(filename) {
return new Buffer(filename).toString('base64').replace(/=/g, '');
}

function parseFile(filename, data) {
return parseDeclarations(data);
}

function getDependentDeclarations(filename, declarations) {
const fileId = getFileId(filename);
const dependentDeclarations = [];

declarations.explicitGlobals.forEach(declaration => {
if(Object.keys(declaration.deps).length > 0) {
dependentDeclarations.push({ filename, declaration, decFileId: fileId });
}
});

return dependentDeclarations;
}

/**
* Process a single sass files to get declarations, injected source and functions
*/
function processFile(filename, data, pluggable) {
const declarations = parseDeclarations(data);
function processFile(idx, count, filename, data, parsedDeclarations, pluggable) {
const declarations = parsedDeclarations.files[filename];
// Inject dependent declaration extraction to last file
const dependentDeclarations = idx === count - 1 ? parsedDeclarations.dependentDeclarations : [];
const variables = { global: {} };

const globalDeclarationResultHandler = (declaration, value, sassValue) => {
Expand All @@ -25,7 +44,7 @@ function processFile(filename, data, pluggable) {
}

const fileId = getFileId(filename);
const injection = injectExtractionFunctions(fileId, declarations, { globalDeclarationResultHandler });
const injection = injectExtractionFunctions(fileId, declarations, dependentDeclarations, { globalDeclarationResultHandler });
const injectedData = `${data}\n\n${injection.injectedData}`;
const injectedFunctions = injection.injectedFunctions;

Expand All @@ -38,15 +57,30 @@ function processFile(filename, data, pluggable) {
};
}

export function parseFiles(files) {
const parsedDeclarations = {
files: {},
dependentDeclarations: [],
};

Object.keys(files).map(filename => {
const fileDeclarations = parseFile(filename, files[filename]);
parsedDeclarations.files[filename] = fileDeclarations;
parsedDeclarations.dependentDeclarations.push(...getDependentDeclarations(filename, fileDeclarations))
});

return parsedDeclarations;
}

/**
* Process a set of sass files to get declarations, injected source and functions
* Files are provided in a map of filename -> key entries
*/
export function processFiles(files, pluggable) {
export function processFiles(orderedFiles, files, parsedDeclarations, pluggable) {
const extractions = {};

Object.keys(files).map(filename => {
extractions[filename] = processFile(filename, files[filename], pluggable);
orderedFiles.forEach((filename, idx) => {
extractions[filename] = processFile(idx, orderedFiles.length, filename, files[filename], parsedDeclarations, pluggable);
});

return extractions;
Expand Down
15 changes: 14 additions & 1 deletion test/order.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ function verifyOrder(rendered, sourceFile, partialFile) {
expect(rendered.vars).to.exist;
expect(rendered.vars).to.have.property('global');
expect(rendered.vars.global).to.have.property('$var');
expect(rendered.vars.global).to.have.property('$var2');

expect(rendered.vars.global.$var.value).to.equal(2);
expect(rendered.vars.global.$var.sources[0]).to.equal(normalizePath(order1File));
Expand All @@ -20,14 +21,26 @@ function verifyOrder(rendered, sourceFile, partialFile) {
expect(rendered.vars.global.$var.declarations[1].in).to.equal(normalizePath(order2File));
expect(rendered.vars.global.$var.declarations[0].expression).to.equal('1');
expect(rendered.vars.global.$var.declarations[1].expression).to.equal('2');

expect(rendered.vars.global.$var2.value).to.equal(3);
expect(rendered.vars.global.$var2.sources[0]).to.equal(normalizePath(order1File));
expect(rendered.vars.global.$var2.sources[1]).to.equal(normalizePath(order2File));
expect(rendered.vars.global.$var2.sources[2]).to.equal(normalizePath(sourceFile));
expect(rendered.vars.global.$var2.declarations).to.have.length(3);
expect(rendered.vars.global.$var2.declarations[0].in).to.equal(normalizePath(order1File));
expect(rendered.vars.global.$var2.declarations[1].in).to.equal(normalizePath(order2File));
expect(rendered.vars.global.$var2.declarations[2].in).to.equal(normalizePath(sourceFile));
expect(rendered.vars.global.$var2.declarations[0].expression).to.equal('1');
expect(rendered.vars.global.$var2.declarations[1].expression).to.equal('2');
expect(rendered.vars.global.$var2.declarations[2].expression).to.equal('3');
}

describe('partial', () => {
describe('sync', () => {
it('should extract in the right order', () => {
for(let i = 0; i < 20; i++) {
const rendered = renderSync({ file: orderFile })
verifyOrder(rendered);
verifyOrder(rendered, orderFile);
}
});
});
Expand Down
3 changes: 2 additions & 1 deletion test/sass/order.scss
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
@import './order/2.scss';
@import './order/1.scss';
@import './order/1.scss';
$var2: 3;
3 changes: 2 additions & 1 deletion test/sass/order/1.scss
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
$var: 1;
$var: 1;
$var2: 1;
3 changes: 2 additions & 1 deletion test/sass/order/2.scss
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
$var: 2;
$var: 2;
$var2: 2;

0 comments on commit aedd820

Please sign in to comment.