@extjs/sencha-cmd-linux-32
Version:
Productivity and performance optimization tool for building applications with Sencha Ext JS and Sencha Touch.
453 lines (379 loc) • 13.5 kB
JavaScript
"use strict";
var Fashion = require('./export/Base.js'),
Base = Fashion.Base;
var Context = require('./Context.js');
var Preprocessor = require('./Preprocessor.js');
var Transpiler = require('./Transpiler.js');
var Env = require('./Env.js');
var SassFile = require('./SassFile.js');
var Parser = require('./parse/Parser.js');
var Scanner = require('./parse/Scanner.js');
class Builder extends Base {
constructor(config) {
super(config);
this.scripts = {};
Fashion.lastBuilder = this;
this.context = new Context(this.context);
this.context.libraries = this.context.libraries || {
compass: "../lib/compass/stylesheets/",
blueprint: "../lib/blueprint/stylesheets/"
};
}
getContext() {
return this.context;
}
compile(scss, file) {
var context = this.getContext(),
jsCode = context.convert(scss, file),
fn = context.compile(jsCode),
css = fn(context.runtime, {}, context.preprocessor.getDynamicsMap());
return css;
}
createSetters(vars) {
var setters = '',
varName;
if (typeof vars === 'string') {
setters = vars;
} else {
for (varName in vars) {
setters += varName + ": dynamic(" + vars[varName] + ");\n";
}
}
return setters;
}
createVarsScope(vars) {
var context = this.context,
parser = new Parser(),
preprocessor = new Preprocessor({
runtime: context.runtime
}),
newContext = new Context({
runtime: context.runtime,
preprocessor: preprocessor
}),
setters = this.createSetters(vars),
allVariables = Fashion.chain(context.getVariables()),
setterAst, setterVariables, sortedAst, settersCode, settersFunc,
newGlobals;
setterAst = parser.parse(setters);
preprocessor.preprocess(setterAst, true);
preprocessor.loadPreprocessorCache(context.preprocessor);
setterVariables = preprocessor.getVariables();
Fashion.apply(allVariables, setterVariables);
preprocessor.variables = allVariables;
context.dynamicsMap = preprocessor.getDynamicsMap();
sortedAst = preprocessor.getSortedDynamicAstNodes();
settersCode = newContext.convert(sortedAst);
newContext.runtime.setCaches(context.transpiler);
settersFunc = newContext.compile(settersCode);
// execute the generated fn to setup a global scope that
// has all the parsed values;
settersFunc(context.runtime, null, context.dynamicsMap);
newGlobals = context.runtime.getGlobalScope();
return newGlobals;
}
rebuildCss(vars) {
var context = this.context,
func = context.getFunc(),
css;
// now, re-executed the cached fn using the provided setters
// as initial state
css = func(context.runtime, vars, context.dynamicsMap);
return css;
}
/**
File can be of type string or object with following parameters:
{
path: '', // the path to the scss file to build
split: 4095, // the split threshold
compress: false, // whether to copress output
variables: { // global variable overrides
'$base-color': ''
}
}
*
*/
build(file, callback) {
var me = this,
context, sassFile, split, vars;
if (typeof file !== 'string') {
split = file.split;
me.compressed = file.compress;
vars = file.variables;
file = file.path;
}
// reset some calculated path variables
this._sassFileUiPath = null;
this._sassFileSavePath = null;
function onError(error) {
Fashion.log("Build error for " + file);
var message = (error.message || error) + "";
callback && callback(["/* " + message + " */"], error);
}
context = me.getContext();
try {
sassFile = me.getSassFile(file);
sassFile.invalidate();
sassFile.onReady(function () {
Fashion.log("Fashion build starting for " + file);
try {
var ast = sassFile.getExpandedAst(),
converted = context.convert(ast),
func = context.compile(converted),
css = func(),
dynamics = context.dynamicsMap || context.preprocessor.getDynamicsMap();
if (vars) {
var scope = me.createVarsScope(vars);
context.dynamicsMap = dynamics;
css = me.rebuildCss(scope);
}
css.getText(function (cssContent, exportFn) {
Fashion.log("Fashion build complete for " + file);
callback && callback(cssContent, null, exportFn);
},
me.compressed,
me.indent,
me.skipComments,
split);
} catch (error) {
onError(error);
}
});
}
catch (error) {
onError(error);
}
}
getSassFile(path, relPath, origSource, importer) {
var scripts = this.scripts,
isSaveFile = false,
isUiFile = false,
sassFileSavePath, sassFileUiPath, script, className;
path = this.getSassFilePath(path, relPath);
script = scripts[path];
if (this.varSavePath) {
sassFileSavePath = this._sassFileSavePath || this.getSassFilePath(this.varSavePath);
this._sassFileSavePath = sassFileSavePath;
isSaveFile = (path === sassFileSavePath);
}
if (this.srcSavePath) {
sassFileUiPath = this._sassFileUiPath || this.getSassFilePath(this.srcSavePath);
this._sassFileUiPath = sassFileUiPath;
isUiFile = (path.indexOf(sassFileUiPath) === 0);
}
if (isUiFile) {
className = path.replace(sassFileUiPath, '')
.replace(/^\//g, '')
.replace(/\//g, ".")
.replace(/\.scss$/g, '');
}
if (!script) {
script = new SassFile({
path: path,
builder: this,
originalSource: origSource,
importer: importer,
isSaveFile: isSaveFile,
isUiFile: isUiFile,
jsClassName: className
});
scripts[path] = script;
}
return script;
}
getFsMap() {
var map = this.fsMap,
scripts = this.scripts,
keys = Object.keys(scripts),
len = keys.length,
key, k, path, parts, p, plen, part, level, next;
if (!map) {
map = {
files: {},
root: true
};
this.fsMap = map;
}
for (k = 0; k < len; k++) {
key = keys[k];
path = key.replace(/\\/g, '/');
parts = path.split('/');
plen = parts.length;
level = map;
for (p = 0; p < plen - 1; p++) {
part = parts[p];
next = level.files[part];
if (!next) {
next = {
fullName: level.fullName ? [level.fullName, part].join('/') : part,
files: {},
isDir: true
};
level.files[part] = next;
}
level = next;
}
part = parts[plen - 1];
next = level.files[part];
if (!next) {
next = {
fullName: level.fullName ? [level.fullName, part].join('/') : part,
key: key,
isFile: true,
sassFile: scripts[key]
};
level.files[part] = next;
}
level.hasFile = true;
}
return map;
}
getSassFilePath(path, relPath) {
if (relPath) {
path = this.getPath(path, relPath);
}
path = path.replace(/\\/g, "/");
path = this.resolveUrl(path);
return path;
}
getParser() {
return new Parser();
}
getPath(baseFile, relPath) {
if (relPath) {
var separatorIndex = relPath.indexOf('/'),
libraryPaths = this.context.libraries,
root, libpath;
if (separatorIndex !== 0) { // not an absolute path
if (separatorIndex === -1) {
// no path separator found e.g. "@import 'compass';"
root = relPath;
} else {
// path separator found e.g. "@import 'compass/css3"
root = relPath.substring(0,
separatorIndex !== -1
? separatorIndex
: relPath.length);
}
libpath = libraryPaths[root];
if (libpath) {
return this.calcPath(libpath, relPath);
}
}
}
return this.calcPath(baseFile, relPath);
}
calcPath(baseFile, relPath) {
var sep = baseFile.lastIndexOf("/"),
path;
if (sep > -1) {
path = baseFile.substring(0, sep + 1) + relPath;
} else {
path = baseFile + "/" + relPath;
}
if (path.indexOf(".scss") === -1 && path.indexOf(".js") === -1) {
path = path + ".scss";
}
return path;
}
getResolverEl() {
if (!this.resolverEl) {
this.resolverEl = document.createElement("a");
}
return this.resolverEl;
}
getCanonicalPath(path) {
var parts = path.split('/'),
out = [],
part, p;
for (p = 0; p < parts.length; p++) {
part = parts[p];
if (part == '.') {
continue;
} else if (part == '..') {
if (out.length === 0) {
Fashion.raise("bad path for getCanonicalPath : " + path);
}
out.pop();
} else {
out.push(part);
}
}
return out.join('/');
}
resolveUrl(path) {
// firefox won't automatically convert \ chars to / chars
// so need to do that here
path = path.replace(/\\/g, "/");
if (Env.isBrowser) {
var resolverEl = this.getResolverEl();
resolverEl.href = path;
return resolverEl.href;
} else {
path = this.getCanonicalPath(path);
}
return path;
}
createStyleEl(href, content, before) {
var head = document.getElementsByTagName('head')[0],
base = document.createElement('base'),
styleEl, ieMode;
Fashion.log("Using base href : " + href);
base.href = href;
if (head.firstChild) {
head.insertBefore(base, head.firstChild);
} else {
head.appendChild(base);
}
// IE hack to force re-processing of the href
base.href = base.href;
styleEl = document.createElement("style");
styleEl.type = 'text/css';
ieMode = ('styleSheet' in styleEl);
if (ieMode) {
if (before) {
head.insertBefore(styleEl, before);
} else {
head.appendChild(styleEl);
}
styleEl.styleSheet.cssText = content;
} else {
styleEl.textContent = content;
if (before) {
head.insertBefore(styleEl, before);
} else {
head.appendChild(styleEl);
}
}
head.removeChild(base);
return styleEl
}
injectCss(cssPath, cssContent) {
this.lastCssPath = cssPath;
if (!Array.isArray(cssContent)) {
cssContent = [cssContent];
}
cssPath = this.resolveUrl(cssPath);
var me = this,
currEls = me.styleEls || [],
href = cssPath.substring(0, cssPath.lastIndexOf("/") + 1);
me.styleEls = [];
cssContent.forEach((content, idx) => {
content += "\n/*# sourceURL=" + cssPath + "_" + idx + " */";
var before = (currEls.length && currEls[0]) || null,
styleEl = this.createStyleEl(href, content, before);
me.styleEls.push(styleEl);
});
var head = document.getElementsByTagName('head')[0];
currEls.forEach((el) => {
head.removeChild(el);
});
}
}
Fashion.apply(Builder.prototype, {
context: undefined,
varSavePath: undefined,
srcSavePath: undefined,
sassNamespace: undefined
});
module.exports = Builder;