UNPKG

@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
"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;