Skip to content

Commit 2cedd41

Browse files
committed
[INTERNAL] Extract compile from index.js
1 parent 2613b11 commit 2cedd41

File tree

2 files changed

+205
-181
lines changed

2 files changed

+205
-181
lines changed

lib/Compiler.js

Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
const path = require("path");
2+
const createParser = require("./less/parser");
3+
const clone = require("clone");
4+
5+
// Plugins
6+
const ImportCollectorPlugin = require("./plugin/import-collector");
7+
const VariableCollectorPlugin = require("./plugin/variable-collector");
8+
const UrlCollector = require("./plugin/url-collector");
9+
10+
class Compiler {
11+
constructor({options, fileUtils, customFs}) {
12+
this.options = options;
13+
this.fileUtils = fileUtils;
14+
15+
// Stores mapping between "virtual" paths (used within LESS) and real filesystem paths.
16+
// Only relevant when using the "rootPaths" option.
17+
this.mFileMappings = {};
18+
19+
// Only use custom file handler when "rootPaths" or custom "fs" are used
20+
if ((this.options.rootPaths && this.options.rootPaths.length > 0) || customFs) {
21+
this.fnFileHandler = this.createFileHandler();
22+
} else {
23+
this.fnFileHandler = undefined;
24+
}
25+
}
26+
createFileHandler() {
27+
return async (file, currentFileInfo, handleDataAndCallCallback, callback) => {
28+
try {
29+
let pathname;
30+
31+
// support absolute paths such as "/resources/my/base.less"
32+
if (path.posix.isAbsolute(file)) {
33+
pathname = path.posix.normalize(file);
34+
} else {
35+
pathname = path.posix.join(currentFileInfo.currentDirectory, file);
36+
}
37+
38+
const result = await this.fileUtils.readFile(pathname, this.options.rootPaths);
39+
if (!result) {
40+
// eslint-disable-next-line no-console
41+
console.log("File not found: " + pathname);
42+
callback({type: "File", message: "Could not find file at path '" + pathname + "'"});
43+
} else {
44+
try {
45+
// Save import mapping to calculate full import paths later on
46+
this.mFileMappings[currentFileInfo.rootFilename] =
47+
this.mFileMappings[currentFileInfo.rootFilename] || {};
48+
this.mFileMappings[currentFileInfo.rootFilename][pathname] = result.path;
49+
50+
handleDataAndCallCallback(pathname, result.content);
51+
} catch (e) {
52+
// eslint-disable-next-line no-console
53+
console.log(e);
54+
callback(e);
55+
}
56+
}
57+
} catch (err) {
58+
// eslint-disable-next-line no-console
59+
console.log(err);
60+
callback(err);
61+
}
62+
};
63+
}
64+
async compile(config) {
65+
const parserOptions = clone(this.options.parser);
66+
let rootpath;
67+
68+
if (config.path) {
69+
// Keep rootpath for later usage in the ImportCollectorPlugin
70+
rootpath = config.path;
71+
parserOptions.filename = config.localPath;
72+
}
73+
74+
// inject the library name as prefix (e.g. "sap.m" as "_sap_m")
75+
if (this.options.library && typeof this.options.library.name === "string") {
76+
const libName = config.libName = this.options.library.name;
77+
config.libPath = libName.replace(/\./g, "/");
78+
config.prefix = "_" + libName.replace(/\./g, "_") + "_";
79+
} else {
80+
config.prefix = ""; // no library, no prefix
81+
}
82+
83+
// Keep filename for later usage (see ImportCollectorPlugin) as less modifies the parserOptions.filename
84+
const filename = parserOptions.filename;
85+
86+
const parser = createParser(parserOptions, this.fnFileHandler);
87+
88+
function parseContent(content) {
89+
return new Promise(function(resolve, reject) {
90+
parser.parse(content, function(err, tree) {
91+
if (err) {
92+
reject(err);
93+
} else {
94+
resolve(tree);
95+
}
96+
});
97+
});
98+
}
99+
100+
const tree = await parseContent(config.content);
101+
const result = {tree};
102+
103+
// plugins to collect imported files and variable values
104+
const oImportCollector = new ImportCollectorPlugin({
105+
importMappings: this.mFileMappings[filename]
106+
});
107+
const oVariableCollector = new VariableCollectorPlugin(this.options.compiler);
108+
const oUrlCollector = new UrlCollector();
109+
110+
// render to css
111+
result.css = tree.toCSS(Object.assign({}, this.options.compiler, {
112+
plugins: [oImportCollector, oVariableCollector, oUrlCollector]
113+
}));
114+
115+
// retrieve imported files
116+
result.imports = oImportCollector.getImports();
117+
118+
// retrieve reduced set of variables
119+
result.variables = oVariableCollector.getVariables(Object.keys(this.mFileMappings[filename] || {}));
120+
121+
// retrieve all variables
122+
result.allVariables = oVariableCollector.getAllVariables();
123+
124+
// also compile rtl-version if requested
125+
let oRTL;
126+
if (this.options.rtl) {
127+
const RTLPlugin = require("./plugin/rtl");
128+
oRTL = new RTLPlugin();
129+
130+
const urls = oUrlCollector.getUrls();
131+
132+
const existingImgRtlUrls = (await Promise.all(
133+
urls.map(async ({currentDirectory, relativeUrl}) => {
134+
const relativeImgRtlUrl = RTLPlugin.getRtlImgUrl(relativeUrl);
135+
if (relativeImgRtlUrl) {
136+
const resolvedImgRtlUrl = path.posix.join(currentDirectory, relativeImgRtlUrl);
137+
if (await this.fileUtils.findFile(resolvedImgRtlUrl, this.options.rootPaths)) {
138+
return resolvedImgRtlUrl;
139+
}
140+
}
141+
})
142+
)).filter(Boolean);
143+
144+
oRTL.setExistingImgRtlPaths(existingImgRtlUrls);
145+
}
146+
147+
if (oRTL) {
148+
result.cssRtl = tree.toCSS(Object.assign({}, this.options.compiler, {
149+
plugins: [oRTL]
150+
}));
151+
}
152+
153+
if (rootpath) {
154+
result.imports.unshift(rootpath);
155+
}
156+
157+
// also compile css-variables version if requested
158+
if (this.options.cssVariables) {
159+
// parse the content again to have a clean tree
160+
const cssVariablesSkeletonTree = await parseContent(config.content);
161+
162+
// generate the skeleton-css and the less-variables
163+
const CSSVariablesCollectorPlugin = require("./plugin/css-variables-collector");
164+
const oCSSVariablesCollector = new CSSVariablesCollectorPlugin(config);
165+
const oVariableCollector = new VariableCollectorPlugin(this.options.compiler);
166+
result.cssSkeleton = cssVariablesSkeletonTree.toCSS(Object.assign({}, this.options.compiler, {
167+
plugins: [oCSSVariablesCollector, oVariableCollector]
168+
}));
169+
const varsOverride = oVariableCollector.getAllVariables();
170+
result.cssVariablesSource = oCSSVariablesCollector.toLessVariables(varsOverride);
171+
172+
if (oRTL) {
173+
const oCSSVariablesCollectorRTL = new CSSVariablesCollectorPlugin(config);
174+
result.cssSkeletonRtl = cssVariablesSkeletonTree.toCSS(Object.assign({}, this.options.compiler, {
175+
plugins: [oCSSVariablesCollectorRTL, oRTL]
176+
}));
177+
}
178+
179+
// generate the css-variables content out of the less-variables
180+
const cssVariablesTree = await parseContent(result.cssVariablesSource);
181+
const CSSVariablesPointerPlugin = require("./plugin/css-variables-pointer");
182+
result.cssVariables = cssVariablesTree.toCSS(Object.assign({}, this.options.compiler, {
183+
plugins: [new CSSVariablesPointerPlugin()]
184+
}));
185+
}
186+
187+
return result;
188+
}
189+
}
190+
191+
module.exports = Compiler;

0 commit comments

Comments
 (0)