var contexts = require("../contexts"), Visitor = require("./visitor"), ImportSequencer = require("./import-sequencer"); var ImportVisitor = function(importer, finish) { this._visitor = new Visitor(this); this._importer = importer; this._finish = finish; this.context = new contexts.Eval(); this.importCount = 0; this.onceFileDetectionMap = {}; this.recursionDetector = {}; this._sequencer = new ImportSequencer(this._onSequencerEmpty.bind(this)); }; ImportVisitor.prototype = { isReplacing: false, run: function (root) { try { // process the contents this._visitor.visit(root); } catch(e) { this.error = e; } this.isFinished = true; this._sequencer.tryRun(); }, _onSequencerEmpty: function() { if (!this.isFinished) { return; } this._finish(this.error); }, visitImport: function (importNode, visitArgs) { var inlineCSS = importNode.options.inline; if (!importNode.css || inlineCSS) { var context = new contexts.Eval(this.context, this.context.frames.slice(0)); var importParent = context.frames[0]; this.importCount++; if (importNode.isVariableImport()) { this._sequencer.addVariableImport(this.processImportNode.bind(this, importNode, context, importParent)); } else { this.processImportNode(importNode, context, importParent); } } visitArgs.visitDeeper = false; }, processImportNode: function(importNode, context, importParent) { var evaldImportNode, inlineCSS = importNode.options.inline; try { evaldImportNode = importNode.evalForImport(context); } catch(e) { if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } // attempt to eval properly and treat as css importNode.css = true; // if that fails, this error will be thrown importNode.error = e; } if (evaldImportNode && (!evaldImportNode.css || inlineCSS)) { if (evaldImportNode.options.multiple) { context.importMultiple = true; } // try appending if we haven't determined if it is css or not var tryAppendLessExtension = evaldImportNode.css === undefined; for (var i = 0; i < importParent.rules.length; i++) { if (importParent.rules[i] === importNode) { importParent.rules[i] = evaldImportNode; break; } } var onImported = this.onImported.bind(this, evaldImportNode, context), sequencedOnImported = this._sequencer.addImport(onImported); this._importer.push(evaldImportNode.getPath(), tryAppendLessExtension, evaldImportNode.currentFileInfo, evaldImportNode.options, sequencedOnImported); } else { this.importCount--; if (this.isFinished) { this._sequencer.tryRun(); } } }, onImported: function (importNode, context, e, root, importedAtRoot, fullPath) { if (e) { if (!e.filename) { e.index = importNode.index; e.filename = importNode.currentFileInfo.filename; } this.error = e; } var importVisitor = this, inlineCSS = importNode.options.inline, isPlugin = importNode.options.plugin, isOptional = importNode.options.optional, duplicateImport = importedAtRoot || fullPath in importVisitor.recursionDetector; if (!context.importMultiple) { if (duplicateImport) { importNode.skip = true; } else { importNode.skip = function() { if (fullPath in importVisitor.onceFileDetectionMap) { return true; } importVisitor.onceFileDetectionMap[fullPath] = true; return false; }; } } if (!fullPath && isOptional) { importNode.skip = true; } if (root) { importNode.root = root; importNode.importedFilename = fullPath; if (!inlineCSS && !isPlugin && (context.importMultiple || !duplicateImport)) { importVisitor.recursionDetector[fullPath] = true; var oldContext = this.context; this.context = context; try { this._visitor.visit(root); } catch (e) { this.error = e; } this.context = oldContext; } } importVisitor.importCount--; if (importVisitor.isFinished) { importVisitor._sequencer.tryRun(); } }, visitRule: function (ruleNode, visitArgs) { if (ruleNode.value.type === "DetachedRuleset") { this.context.frames.unshift(ruleNode); } else { visitArgs.visitDeeper = false; } }, visitRuleOut : function(ruleNode) { if (ruleNode.value.type === "DetachedRuleset") { this.context.frames.shift(); } }, visitDirective: function (directiveNode, visitArgs) { this.context.frames.unshift(directiveNode); }, visitDirectiveOut: function (directiveNode) { this.context.frames.shift(); }, visitMixinDefinition: function (mixinDefinitionNode, visitArgs) { this.context.frames.unshift(mixinDefinitionNode); }, visitMixinDefinitionOut: function (mixinDefinitionNode) { this.context.frames.shift(); }, visitRuleset: function (rulesetNode, visitArgs) { this.context.frames.unshift(rulesetNode); }, visitRulesetOut: function (rulesetNode) { this.context.frames.shift(); }, visitMedia: function (mediaNode, visitArgs) { this.context.frames.unshift(mediaNode.rules[0]); }, visitMediaOut: function (mediaNode) { this.context.frames.shift(); } }; module.exports = ImportVisitor;