UNPKG

@extjs/sencha-cmd-linux-32

Version:

Productivity and performance optimization tool for building applications with Sencha Ext JS and Sencha Touch.

557 lines (493 loc) 16.8 kB
"use strict"; var Fashion = require('./export/Base.js'), Base = Fashion.Base; var Visitor = require('./Visitor.js'); var regexes = { replaces: /^\s*?\/\/# fashion replaces\s+?(.*?)$/, }; class SassVariableAttributes { } Fashion.apply(SassVariableAttributes.prototype, { global: false, "default": false, dynamic: false }); class SassVariable extends Base { constructor(cfg) { super(cfg); this.references = this.references || []; } elevateDynamics(variables, elevator) { var me = this, dynamicWas = me.dynamic; me.dynamic = true; if (!dynamicWas) { if (this.enableElevationWarning) { Fashion.warn("Elevating variable '" + me.name + "' to dynamic", me.node); Fashion.warn("\tcaused by", elevator.node); } me.elevationCause = elevator; variables.push(me); } me.references.forEach((ref) => { var variable = me.map[ref]; if (variable && !variable.dynamic) { variable.elevateDynamics(variables, me); } }); } verify() { if (this.dynamic) { if (this.previous) { if (!this.previous.dynamic) { Fashion.error([ 'Cannot redefine ', this.name, ' as dynamic' ].join('')); Fashion.error('\tfrom ', this.previous.getNode()); Fashion.error('\t at ', this.getNode()); this.preprocessor.errors += 1; return false; } } } return true; } elevated() { return this.dynamic && !this.attributes.dynamic; } getNode() { var node = this.node; return node; } } Fashion.apply(SassVariable.prototype, { name: null, previous: null, node: null, dynamic: false, references: null, attributes: null, isGlobal: false, map: null, elevationCause: null, processed: false, preprocessor: null, enableElevationWarning: false, scope: undefined }); class Preprocessor extends Visitor { constructor(cfg) { super(cfg); this.reset(); } reset() { this.replacedVariable = null; this.variables = {}; this.functions = []; this.currentVariable = null; this.functionDeclarations = {}; this.mixinDeclarations = {}; this.registeredDeclarations = null; this.replacesMap = {}; this.currentScope = { variables: {}, isGlobal: true }; } handleFunc(node, collection) { var func = node.func || node.name, name = Fashion.getJsName(func.id || func.value), parameters = Preprocessor.getFunctionCallArgs(func); collection[name] = { parameters: parameters, ast: node, scope: this.currentScope }; for (var i = 0; i < parameters.length; i++) { var param = parameters[i]; name = Fashion.getJsName(param.name); this.currentScope.variables[name] = new SassVariable({ name: name, node: param.arg, isGlobal: false, dynamic: false, map: this.currentScope.variables, preprocessor: this, enableElevationWarning: this.enableElevationWarning, scope: this.currentScope }) } } Mixin(node) { var scopeWas = this.currentScope; this.currentScope = { prev: scopeWas, variables: {} }; this.handleFunc(node, this.mixinDeclarations); node.descend(this); this.currentScope = scopeWas; } Function(node) { var isGlobal = this.nodeStack.length == 1; var scopeWas = this.currentScope; if (isGlobal) { this.functions.push(node); } this.currentScope = { prev: scopeWas, variables: {} }; this.handleFunc(node, this.functionDeclarations); node.descend(this); this.currentScope = scopeWas; } getVariable (name) { var scope = this.currentScope, variable; while (scope) { variable = scope.variables[name]; if (variable) { break; } scope = scope.prev; } return variable; } Variable(node) { if (this.currentVariable) { var refName = Fashion.getJsName(node.name), variable = this.getVariable(refName); if (!variable || variable.scope.isGlobal) { this.currentVariable.references.push(refName); } } } FunctionCall (node) { if (this.currentVariable) { node.descend(this); var id = Fashion.getJsName(node.id), entry = this.functionDeclarations[id]; if (entry) { // re-visit the function body for this node, as that call will // possibly contain references to other global variables var scopeWas = this.currentScope; this.currentScope = entry.scope; this.visit(entry.ast.statements); this.currentScope = scopeWas; } } } Comment(comment) { var replaces = regexes.replaces, match = replaces.exec(comment); if (comment === '//# fashion warn -elevation') { this.enableElevationWarning = false; } else if (comment === '//# fashion warn +elevation') { this.enableElevationWarning = true; } else if (match) { this.replacedVariable = match[1]; } } VariableAssignment(node) { var replaces = this.replacedVariable, replacesEntry = this.replacesMap[Fashion.getJsName(node.name)], origName = node.name, nodeName = (replacesEntry && replacesEntry.name) || origName, name = Fashion.getJsName(nodeName), currVariable = this.variables[name], varWas = this.currentVariable, bangGlobal = !!node.global, bangDynamic = !!node.dynamic, bangDefault = !!node.default, isGlobalVar = this.nodeStack.length === 1, variable, value, funcName; if (replacesEntry) { Fashion.warn("Using new variable " + replacesEntry.name + " for deprecated variable " + node.name, node); } node.name = nodeName; value = node.value; if (value.type === 'FunctionCall') { funcName = value.id || value.value; if (funcName === 'dynamic') { bangDynamic = true; value.visitTarget = value.args; if (value.args.items && value.args.items.length === 1) { value.visitTarget = value.args.items[0]; } } } variable = new SassVariable({ name: name, nodeName: origName, node: node, previous: currVariable, attributes: { global: bangGlobal, "default": bangDefault, dynamic: bangDynamic }, isGlobal: isGlobalVar, dynamic: (currVariable && currVariable.dynamic) || bangDynamic, map: this.variables, preprocessor: this, enableElevationWarning: this.enableElevationWarning, scope: this.currentScope, replaces: replaces || (currVariable && currVariable.replaces) }); if (variable.replaces) { var replacesName = Fashion.getJsName(variable.replaces); var current = this.replacesMap[replacesName]; if (current && variable.name != current.name) { Fashion.warn("Variable " + current.name + " already replaces variable " + variable.replaces, node); } this.replacesMap[replacesName] = variable; } this.replacedVariable = null; this.currentScope.variables[name] = variable; if (isGlobalVar || bangGlobal) { if (!!node.dynamic) { Fashion.warn("Use of !dynamic has been deprecated", node); Fashion.warn("Use dynamic() function instead."); } variable.verify(); this.variables[name] = variable; } else { node.descend(this); return false; } this.currentVariable = variable; node.descend(this); this.currentVariable = varWas; } getRuntime() { return this.runtime; } getRegisteredFunctions() { if (!this.registeredFunctions) { this.registeredFunctions = (this.runtime && this.runtime.getRegisteredFunctions()) || {}; } return this.registeredFunctions; } loadRegisteredFunctionArgs() { if (!this.registeredDeclarations) { var registered = this.getRegisteredFunctions(), funcArgsRx = this.funcArgsRx, paramsMap = {}, name, func, src, params, match, args, i, argName; for (name in registered) { func = registered[name]; if (Fashion.isFunction(func)) { src = func + ''; params = []; match = funcArgsRx.exec(src); if (match && match[2]) { args = (match[2] && match[2].split(/,/g)) || []; for (i = 0; i < args.length; i++) { argName = args[i].trim(); params.push({ name: argName, position: i }); } } paramsMap[name] = params; } } this.registeredDeclarations = paramsMap; } } preprocess(node, skipRegistrations) { this.reset(); this.visit(node); if (!skipRegistrations) { this.loadRegisteredFunctionArgs(); } if (this.errors) { Fashion.raise([ 'Encountered ', this.errors, ' error(s) during preprocessing.' ].join('')); } } getVariables() { return this.variables; } generateCycleError(stack, variables) { var referenceTrace = [], r, trace; for (r = 0; r < stack.length; r++) { trace = [ stack[r], " => ", variables[stack[r]].node.file, ":", variables[stack[r]].node.lineNumber ].join(''); referenceTrace.push(trace); } var msg = [ "Variable Cycle detected in variable : ", referenceTrace.join('\n') ].join('\n'); Fashion.error(msg); Fashion.raise(msg); } topoSort(variable, variables, sorted, processed, stack) { processed = processed || {}; sorted = sorted || []; stack = stack || []; var me = this, name = variable.name, refs, ref, r, refVariable; function processRef (ref) { var refVariable = variables[ref]; if (refVariable) { if (refVariable.dynamic || me.sortAllVariables) { me.topoSort(refVariable, variables, sorted, processed, stack); return true; } } return false; } if (processed[name] !== true) { stack.push(name); if (processed[name] === 'processing') { this.generateCycleError(stack, variables); } processed[name] = 'processing'; refs = variable.references; for (r = 0; r < refs.length; r++) { ref = refs[r]; if (!processRef(ref)) { if (this.replacesMap[ref]) { var entry = this.replacesMap[ref], ref = Fashion.getJsName(entry.name); processRef(ref); } } } sorted.push(variable); processed[name] = true; stack.pop(); } } getDynamics() { var variables = this.sortAllVariables ? this.currentScope.variables : this.variables, variableNames = Object.keys(variables), dynamics = [], sorted = [], variable, name, dynamic, d, n; // push the dynamic flag to all variables referenced for (n = 0; n < variableNames.length; n++) { name = variableNames[n]; variable = variables[name]; if (variable.dynamic) { variable.elevateDynamics(dynamics); dynamics.push(variable); } } for (d = 0; d < dynamics.length; d++) { dynamic = dynamics[d]; this.topoSort(dynamic, variables, sorted); } return sorted; } getDynamicsMap() { var dynamicVariables = this.getDynamics(), map = {}, i, variable; for (i = 0; i < dynamicVariables.length; i++) { variable = dynamicVariables[i]; map[variable.name] = variable; } return map; } getSortedDynamicAstNodes() { var sortedVariables = this.getDynamics(), sortedAst = [], i; for (i = 0; i < sortedVariables.length; i++) { sortedAst.push(sortedVariables[i].getNode()); } return sortedAst; } loadPreprocessorCache(preprocessor) { this.functionDeclarations = preprocessor.functionDeclarations; this.mixinDeclarations = preprocessor.mixinDeclarations; this.registeredDeclarations = preprocessor.registeredDeclarations; this.registeredFunctions = preprocessor.registeredFunctions; this.currentScope = preprocessor.currentScope; } static loadArgsArray(args) { if (args && (args.type === 'SelectorList' || args.type === 'List')) { args = args.items; } if (!Array.isArray(args)) { args = [args]; } return args; } static getFunctionCallArgs(func) { var args = this.loadArgsArray(func.args), parameters = [], arg, a, param; for (a = 0; a < args.length; a++) { arg = args[a]; if (arg) { param = { name: arg.variable || arg.name, value: arg, position: a, varArgs: arg.varArgs }; parameters.push(param); if (arg.variable) { if (arg.token) { var scanner = arg.token.scanner, style = scanner && scanner.style, start = arg.token.startIdx, end = arg.token.idx; param.default = style.substring(start, end).trim(); } else if (arg.type == 'List') { var first = arg.items[0], last = arg.items[arg.items.length - 1], startTok = first && first.token, endTok = last && last.token; if (startTok && endTok) { var style = startTok.scanner.style, start = startTok.startIdx, end = endTok.idx; param.default = style.substring(start, end).trim(); } } } } } return parameters; } } Fashion.apply(Preprocessor.prototype, { functionDeclarations: null, mixinDeclarations: null, registeredDeclarations: null, registeredFunctions: null, runtime: null, currentVariable: null, functions: null, variables: null, errors: 0, enableElevationWarning: true, funcArgsRx: /function\s*?(.*?)\((.*?)\)\s*?\{/, sortAllVariables: false }); module.exports = Preprocessor;