addTask: function (task, config) { // @todo Quote task name if needed. // @todo Do not insert comma to empty properties list. if ( !_.isString(task) || this.tasks[task] ) { return this; } if ( config === undefined ) { config = {}; } if ( !_.isString(config) ) { config = stringifyObject(config, { indent: this.indent, singleQuotes: true }); } var props = this._configObject.properties, lastProp = props[props.length - 1]; // Indent block var level = lastProp.endToken.loc.start.column, blockIndent = _str.repeat(this.indent, level); config = blockIndent + config.replace(/\n/g, '\n' + blockIndent); var configExpression = '({' + task + ': ' + config + '})', // @todo Simplify? configTree = rocambole.parse(configExpression, { loc: true }), newProp = configTree.body[0].expression.properties[0]; // Append new property var prefix = { value: ',\n\n' + blockIndent + task + ': ' }; tk.after(lastProp.endToken, prefix); tk.after(prefix, newProp); // Save results if ( this.autosave ) { this.save(); } this.reparse(); return this; },
node.properties.forEach(function(prop) { // convert comma-first to comma-last var comma = _tk.findNext(prop.value.endToken, [',', '}']); if (_tk.isComma(comma)) { var br = _tk.findInBetween(prop.value.endToken, comma, _tk.isBr); if (br) { _tk.remove(br); } _tk.remove(comma); _tk.after(prop.value.endToken, comma); } if (!shouldBeSingleLine) { _br.limitBefore(prop.key.startToken, 'PropertyName'); _br.limitAfter(prop.key.endToken, 'PropertyName'); _br.limitBefore(prop.value.startToken, 'PropertyValue'); _br.limitAfter(prop.value.endToken, 'PropertyValue'); } else if (prop.key.startToken.prev.value !== '{') { _ws.limitBefore(prop.key.startToken, 'Property'); } _ws.limitBefore(prop.key.startToken, 'PropertyName'); _ws.limitAfter(prop.key.endToken, 'PropertyName'); _ws.limitBefore(prop.value.startToken, 'PropertyValue'); _ws.limitAfter(prop.value.endToken, 'PropertyValue'); });
node.declarations.forEach(function(declarator, i) { var idStartToken = declarator.id.startToken; // need to swap comma-first line break var prevNonEmpty = _tk.findPrevNonEmpty(idStartToken); if (i && prevNonEmpty.value === ',') { if (_tk.isBr(prevNonEmpty.prev) || _tk.isBr(prevNonEmpty.prev.prev)) { var beforeComma = _tk.findPrev(prevNonEmpty, function(t) { return !_tk.isEmpty(t) && !_tk.isComment(t); }); _ws.limit(prevNonEmpty, 0); _tk.remove(prevNonEmpty); _tk.after(beforeComma, prevNonEmpty); } } if (!i && !_tk.isComment(_tk.findPrevNonEmpty(idStartToken))) { // XXX: we don't allow line breaks or multiple spaces after "var" // keyword for now (might change in the future) _tk.removeEmptyAdjacentBefore(idStartToken); } else if (!insideFor && declarator.init) { _br.limit(idStartToken, 'VariableName'); } _ws.limitBefore(idStartToken, 'VariableName'); if (declarator.init) { _ws.limitAfter(declarator.id.endToken, 'VariableName'); var equalSign = _tk.findNext(declarator.id.endToken, '='); var valueStart = _tk.findNextNonEmpty(equalSign); _br.limitBefore(valueStart, 'VariableValue'); _ws.limitBefore(valueStart, 'VariableValue'); _br.limitAfter(declarator.endToken, 'VariableValue'); _ws.limitAfter(declarator.endToken, 'VariableValue'); } });
exports.limitBeforeEndOfFile = function(ast, amount) { var typeOrValue = amount != null ? amount : 'EndOfFile'; var expected = getExpect('before', typeOrValue); if (expected < 0) return; // noop var lastNonEmpty = _tk.isEmpty(ast.endToken) ? _tk.findPrevNonEmpty(ast.endToken) : ast.endToken; if (lastNonEmpty) { limitInBetween('after', lastNonEmpty, null, expected, true); } else { do { var br = { type: 'LineBreak', value: _curOpts.value }; if (ast.startToken) { _tk.after(ast.startToken, br); } else { ast.startToken = ast.endToken = br; } } while (--expected); } };
node.properties.forEach(function(prop) { // we need to grab first/last "executable" token to avoid issues (see #191) var valueStart = _tk.findNextNonEmpty(_tk.findPrev(prop.value.startToken, ':')); var eol = _tk.findNext(prop.value.endToken, ['LineBreak', ',', '}']); var valueEnd = _tk.findPrev(eol, function(token) { return !_tk.isEmpty(token) && !_tk.isComment(token); }); // convert comma-first to comma-last var comma = _tk.findNext(prop.value.endToken, [',', '}']); if (_tk.isComma(comma)) { var br = _tk.findInBetween(prop.value.endToken, comma, _tk.isBr); if (br) { _tk.remove(br); } _tk.remove(comma); _tk.after(valueEnd, comma); } if (!shouldBeSingleLine) { _br.limitBefore(prop.key.startToken, 'PropertyName'); _br.limitAfter(prop.key.endToken, 'PropertyName'); _br.limitBefore(prop.value.startToken, 'PropertyValue'); _br.limitAfter(prop.value.endToken, 'PropertyValue'); } else if (prop.key.startToken.prev.value !== '{') { _ws.limitBefore(prop.key.startToken, 'Property'); } _ws.limitBefore(prop.key.startToken, 'PropertyName'); _ws.limitAfter(prop.key.endToken, 'PropertyName'); _ws.limitBefore(valueStart, 'PropertyValue'); _ws.limitAfter(valueEnd, 'PropertyValue'); });
function after(token, value) { value = !value ? _curOpts.value : value; var ws = { type: 'WhiteSpace', value: value }; _tk.after(token, ws); return ws; }
var wrapWithBraces = function (node, prop) { var old = node[prop]; var block = { type: 'BlockStatement', parent: old.parent, root: old.root, body: [old], startToken: tk.before(old.startToken, { type: 'Punctuator', value: '{' }), endToken: tk.after(old.endToken, { type: 'Punctuator', value: '}' }) }; old.parent = block; node[prop] = block; };
registerTask: function(name, tasks) { if ( !_.isArray(tasks) ) { tasks = [tasks]; } var expression; visit(this.tree, { visitMemberExpression: function(path) { var node = path.node; if ( node.object.name === 'grunt' && node.property.name === 'registerTask' && node.parent.arguments && node.parent.arguments[0].value === name ) { expression = node.parent; } this.traverse(path); } }); if (expression) { // Task group already exists var listExpression = expression.arguments[1]; if ( !listExpression ) { throw new Error('grunt.registerTask requires second argument'); } var list = listExpression.elements; var items, newToken; if ( list.length ) { // Filter already existent items var existentTasks = _.map(list, 'value'); tasks = _.filter(tasks, function(taskName) { return existentTasks.indexOf(taskName) === -1; }); if ( !tasks.length ) { // All tasks already existent return this; } // Append new items var quote = util.detectQuoteStyle(list[0]); items = _.map(tasks, util.quote.bind(util, quote)); newToken = { value: ', ' + items.join(', ') }; tk.after(list[list.length - 1].endToken, newToken); } else { // @todo Try to detect quotes somewhere else in a file. items = _.map(tasks, util.quote.bind(util, '\'')); newToken = { value: items.join(', ') }; tk.before(listExpression.endToken, newToken); } } else { // @todo } if ( this.autosave ) { this.save(); } this.reparse(); return this; },