Created
July 28, 2013 21:24
-
-
Save andreypopp/6100311 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env coffee | |
fs = require 'fs' | |
{Parser, tree} = require 'less' | |
{extend} = require 'underscore' | |
filename = process.argv[2] | |
parser = new Parser(filename: filename) | |
str = fs.readFileSync(filename).toString() | |
defineVisitor = (base, props) -> | |
extend Object.create(base), props | |
renderValue = (node) -> | |
impl = Object.create(expressionVisitor) | |
visitor = new tree.visitor impl | |
visitor.visit node | |
value = impl.value.trim() | |
value | |
renderTree = (node, indent = '') -> | |
impl = Object.create(treeVisitor) | |
impl.indent = indent | |
new tree.visitor(impl).visit(node) | |
renderMixinParam = (node) -> | |
param = node.name.slice(1) | |
if node.value | |
param = "#{param}=#{renderValue(node.value)}" | |
param | |
renderMixinArg = (node) -> | |
param = renderValue(node.value) | |
if node.name | |
param = "#{node.name.slice(1)}=#{param}" | |
param | |
baseVisitor = | |
visitAlpha: (node) -> | |
throw new Error('not implemented') | |
visitAnonymous: (node) -> | |
throw new Error('not implemented') | |
visitAssigment: (node) -> | |
throw new Error('not implemented') | |
visitCall: (node) -> | |
throw new Error('not implemented') | |
visitColor: (node) -> | |
throw new Error('not implemented') | |
visitComment: (node) -> | |
throw new Error('not implemented') | |
visitCondition: (node) -> | |
throw new Error('not implemented') | |
visitDimension: (node) -> | |
throw new Error('not implemented') | |
visitDirective: (node) -> | |
throw new Error('not implemented') | |
visitElement: (node) -> | |
throw new Error('not implemented') | |
visitExpression: (node) -> | |
throw new Error('not implemented') | |
visitExtend: (node) -> | |
throw new Error('not implemented') | |
visitImport: (node) -> | |
throw new Error('not implemented') | |
visitJavaScript: (node) -> | |
throw new Error('not implemented') | |
visitKeyword: (node) -> | |
throw new Error('not implemented') | |
visitMedia: (node) -> | |
throw new Error('not implemented') | |
visitMixin: (node) -> | |
throw new Error('not implemented') | |
visitMixinCall: (node) -> | |
throw new Error('not implemented') | |
visitMixinDefinition: (node) -> | |
throw new Error('not implemented') | |
visitNegative: (node) -> | |
throw new Error('not implemented') | |
visitOperation: (node) -> | |
throw new Error('not implemented') | |
visitParen: (node) -> | |
throw new Error('not implemented') | |
visitQuoted: (node) -> | |
throw new Error('not implemented') | |
visitRule: (node, options) -> | |
throw new Error('not implemented') | |
visitRuleset: (node, options) -> | |
throw new Error('not implemented') | |
visitSelector: (node, options) -> | |
throw new Error('not implemented') | |
visitValue: (node) -> | |
throw new Error('not implemented') | |
visitVariable: (node) -> | |
throw new Error('not implemented') | |
visitURL: (node) -> | |
throw new Error('not implemented') | |
visitUnicodeDescriptor: (node) -> | |
throw new Error('not implemented') | |
expressionVisitor = defineVisitor baseVisitor, | |
acc: (v) -> | |
@value = '' unless @value | |
@value += ' ' + v | |
visitAnonymous: (node) -> | |
@acc node.value | |
visitDimension: (node) -> | |
@acc "#{node.value}#{node.unit.numerator.join('')}" | |
visitVariable: (node) -> | |
@acc node.name.slice(1) | |
visitCall: (node, options) -> | |
options.visitDeeper = false | |
args = node.args.map(renderValue).join(', ') | |
@acc "#{node.name}(#{args})" | |
visitSelector: (node, options) -> | |
options.visitDeeper = false | |
@acc node.elements.map(renderValue).join(' ') | |
visitElement: (node) -> | |
@acc "#{node.combinator.value} #{node.value}" | |
visitKeyword: (node) -> | |
@acc node.value | |
visitQuoted: (node) -> | |
@acc "#{node.quote}#{node.value}#{node.quote}" | |
visitParen: (node, options) -> | |
options.visitDeeper = false | |
@acc renderValue(node.value) | |
visitRule: (node, options) -> | |
options.visitDeeper = false | |
@acc "#{node.name} #{renderValue(node.value)}" | |
visitOperation: (node, options) -> | |
options.visitDeeper = false | |
if node.operands.length != 2 | |
throw new Error('assertion') | |
[left, right] = node.operands | |
@acc "#{renderValue(left)}#{node.op}#{renderValue(right)}" | |
visitValue: (node, options) -> | |
options.visitDeeper = false | |
@acc node.value.map(renderValue).join(', ') | |
visitExpression: (node, options) -> | |
options.visitDeeper = false | |
@acc node.value.map(renderValue).join(' ') | |
visitColor: (node) -> | |
if node.rgb | |
c = "rgb(#{node.rgb.join(', ')}" | |
if node.alpha | |
c += ", #{node.alpha}" | |
c += ")" | |
@acc c | |
else | |
throw new Error("unknow color #{node}") | |
treeVisitor = defineVisitor baseVisitor, | |
indent: '' | |
increaseIndent: -> | |
@indent + ' ' | |
decreaseIndent: -> | |
@indent.slice(0, -2) | |
p: (m) -> | |
console.log "#{@indent}#{m.trim()}" | |
visitRuleset: (node, options) -> | |
unless node.root | |
options.visitDeeper = false | |
@p node.selectors.map(renderValue).join(', ') | |
for rule in node.rules | |
renderTree(rule, @increaseIndent()) | |
visitRulesetOut: (node) -> | |
unless node.root | |
@decreaseIndent() | |
visitRule: (node, options) -> | |
options.visitDeeper = false | |
name = node.name | |
if name[0] == '@' | |
@p "#{name.slice(1)} = #{renderValue(node.value)}" | |
else | |
@p "#{name} #{renderValue(node.value)}" | |
visitComment: (node) -> | |
@p node.value | |
visitMedia: (node, options) -> | |
options.visitDeeper = false | |
@p "@media #{renderValue(node.features)}" | |
renderTree(node.ruleset, @increaseIndent()) | |
visitSelector: (node, options) -> | |
options.visitDeeper = false | |
@p node.elements.map(renderValue).join('') | |
visitMixinDefinition: (node, options) -> | |
options.visitDeeper = false | |
@p "#{node.name.slice(1)}(#{node.params.map(renderMixinParam).join(', ')})" | |
for rule in node.rules | |
renderTree(rule, @increaseIndent()) | |
visitMixinCall: (node, options) -> | |
options.visitDeeper = false | |
v = "#{renderValue(node.selector).slice(1)}" | |
if node.arguments.length > 0 | |
v += "(#{node.arguments.map(renderMixinArg).join(', ')})" | |
@p v | |
parser.parse str, (err, node) -> | |
process.exit(1) if err | |
renderTree node |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment