-
Notifications
You must be signed in to change notification settings - Fork 39
/
extract.js
147 lines (120 loc) · 5.79 KB
/
extract.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import { normalizePath, makeAbsolute, getSassImplementation, promisifySass, isDartSass } from './util';
import { loadCompiledFiles, loadCompiledFilesSync } from './load';
import { processFiles, parseFiles } from './process';
import { makeImporter, makeSyncImporter } from './importer';
import { Pluggable } from './pluggable';
import { patchReadFile, unpatchReadFile } from './fs-patcher';
/**
* Get rendered stats required for extraction
*/
function getRenderedStats(rendered, compileOptions) {
return {
entryFilename: normalizePath(rendered.stats.entry),
includedFiles: rendered.stats.includedFiles.map(f => normalizePath(makeAbsolute(f))),
includedPaths: (compileOptions.includePaths || []).map(normalizePath),
};
}
/**
* Make the compilation option for the extraction rendering
* Set the data to be rendered to the injected source
* Add compilation functions and custom importer for injected sources
*/
function makeExtractionCompileOptions(compileOptions, entryFilename, extractions, importer) {
const extractionCompileOptions = Object.assign({}, compileOptions);
const extractionFunctions = {};
// Copy all extraction function for each file into one object for compilation
Object.keys(extractions).forEach(extractionKey => {
Object.assign(extractionFunctions, extractions[extractionKey].injectedFunctions);
});
extractionCompileOptions.functions = Object.assign(extractionFunctions, compileOptions.functions);
extractionCompileOptions.data = extractions[entryFilename].injectedData;
if(!makeExtractionCompileOptions.imported) {
extractionCompileOptions.importer = importer;
}
return extractionCompileOptions;
}
/**
* Compile extracted variables per file into a complete result object
*/
function compileExtractionResult(orderedFiles, extractions) {
const extractedVariables = { global: {} };
orderedFiles.map(filename => {
const globalFileVariables = extractions[filename].variables.global;
Object.keys(globalFileVariables).map(variableKey => {
globalFileVariables[variableKey].forEach(extractedVariable => {
let variable = extractedVariables.global[variableKey];
let currentVariableSources = [];
let currentVariableDeclarations = [];
if(variable) {
currentVariableSources = variable.sources;
currentVariableDeclarations = variable.declarations;
}
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,
flags: extractedVariable.declaration.flags,
in: filename,
position: extractedVariable.declaration.position,
}];
});
});
});
return extractedVariables;
}
/**
* Extract the variables from already rendered sass file(s)
* Returns the extracted variables
*/
export function extract(rendered, { compileOptions = {}, extractOptions = {} } = {}) {
const pluggable = new Pluggable(extractOptions.plugins).init();
const sass = promisifySass(getSassImplementation(extractOptions));
const { entryFilename, includedFiles, includedPaths } = getRenderedStats(rendered, compileOptions);
return loadCompiledFiles(includedFiles, entryFilename, compileOptions.data)
.then(({ compiledFiles, orderedFiles }) => {
const parsedDeclarations = parseFiles(compiledFiles);
const extractions = processFiles(orderedFiles, compiledFiles, parsedDeclarations, pluggable, sass);
const importer = makeImporter(extractions, includedFiles, includedPaths, compileOptions.importer);
const extractionCompileOptions = makeExtractionCompileOptions(compileOptions, entryFilename, extractions, importer);
const isDart = isDartSass(sass);
if (isDart) {
extractionCompileOptions.importer = compileOptions.importer;
patchReadFile(extractions, entryFilename);
}
return sass.renderAsync(extractionCompileOptions)
.then(() => {
if (isDart) {
unpatchReadFile();
}
return pluggable.run(Pluggable.POST_EXTRACT, compileExtractionResult(orderedFiles, extractions));
});
});
}
/**
* Synchronously extract the variables from already rendered sass file(s)
* Returns the extracted variables
*/
export function extractSync(rendered, { compileOptions = {}, extractOptions = {} } = {}) {
const pluggable = new Pluggable(extractOptions.plugins).init();
const sass = getSassImplementation(extractOptions);
const { entryFilename, includedFiles, includedPaths } = getRenderedStats(rendered, compileOptions);
const { compiledFiles, orderedFiles } = loadCompiledFilesSync(includedFiles, entryFilename, compileOptions.data);
const parsedDeclarations = parseFiles(compiledFiles);
const extractions = processFiles(orderedFiles, compiledFiles, parsedDeclarations, pluggable, sass);
const importer = makeSyncImporter(extractions, includedFiles, includedPaths, compileOptions.importer);
const extractionCompileOptions = makeExtractionCompileOptions(compileOptions, entryFilename, extractions, importer);
const isDart = isDartSass(sass);
if (isDart) {
extractionCompileOptions.importer = compileOptions.importer;
patchReadFile(extractions, entryFilename);
}
sass.renderSync(extractionCompileOptions);
if (isDart) {
unpatchReadFile();
}
return pluggable.run(Pluggable.POST_EXTRACT, compileExtractionResult(orderedFiles, extractions));
}