/bower_components/bootstrap/grunt/bs-lessdoc-parser.js |
@@ -0,0 +1,237 @@ |
/*! |
* Bootstrap Grunt task for parsing Less docstrings |
* http://getbootstrap.com |
* Copyright 2014-2015 Twitter, Inc. |
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) |
*/ |
|
'use strict'; |
|
var Markdown = require('markdown-it'); |
|
function markdown2html(markdownString) { |
var md = new Markdown(); |
|
// the slice removes the <p>...</p> wrapper output by Markdown processor |
return md.render(markdownString.trim()).slice(3, -5); |
} |
|
|
/* |
Mini-language: |
//== This is a normal heading, which starts a section. Sections group variables together. |
//## Optional description for the heading |
|
//=== This is a subheading. |
|
//** Optional description for the following variable. You **can** use Markdown in descriptions to discuss `<html>` stuff. |
@foo: #fff; |
|
//-- This is a heading for a section whose variables shouldn't be customizable |
|
All other lines are ignored completely. |
*/ |
|
|
var CUSTOMIZABLE_HEADING = /^[/]{2}={2}(.*)$/; |
var UNCUSTOMIZABLE_HEADING = /^[/]{2}-{2}(.*)$/; |
var SUBSECTION_HEADING = /^[/]{2}={3}(.*)$/; |
var SECTION_DOCSTRING = /^[/]{2}#{2}(.+)$/; |
var VAR_ASSIGNMENT = /^(@[a-zA-Z0-9_-]+):[ ]*([^ ;][^;]*);[ ]*$/; |
var VAR_DOCSTRING = /^[/]{2}[*]{2}(.+)$/; |
|
function Section(heading, customizable) { |
this.heading = heading.trim(); |
this.id = this.heading.replace(/\s+/g, '-').toLowerCase(); |
this.customizable = customizable; |
this.docstring = null; |
this.subsections = []; |
} |
|
Section.prototype.addSubSection = function (subsection) { |
this.subsections.push(subsection); |
}; |
|
function SubSection(heading) { |
this.heading = heading.trim(); |
this.id = this.heading.replace(/\s+/g, '-').toLowerCase(); |
this.variables = []; |
} |
|
SubSection.prototype.addVar = function (variable) { |
this.variables.push(variable); |
}; |
|
function VarDocstring(markdownString) { |
this.html = markdown2html(markdownString); |
} |
|
function SectionDocstring(markdownString) { |
this.html = markdown2html(markdownString); |
} |
|
function Variable(name, defaultValue) { |
this.name = name; |
this.defaultValue = defaultValue; |
this.docstring = null; |
} |
|
function Tokenizer(fileContent) { |
this._lines = fileContent.split('\n'); |
this._next = undefined; |
} |
|
Tokenizer.prototype.unshift = function (token) { |
if (this._next !== undefined) { |
throw new Error('Attempted to unshift twice!'); |
} |
this._next = token; |
}; |
|
Tokenizer.prototype._shift = function () { |
// returning null signals EOF |
// returning undefined means the line was ignored |
if (this._next !== undefined) { |
var result = this._next; |
this._next = undefined; |
return result; |
} |
if (this._lines.length <= 0) { |
return null; |
} |
var line = this._lines.shift(); |
var match = null; |
match = SUBSECTION_HEADING.exec(line); |
if (match !== null) { |
return new SubSection(match[1]); |
} |
match = CUSTOMIZABLE_HEADING.exec(line); |
if (match !== null) { |
return new Section(match[1], true); |
} |
match = UNCUSTOMIZABLE_HEADING.exec(line); |
if (match !== null) { |
return new Section(match[1], false); |
} |
match = SECTION_DOCSTRING.exec(line); |
if (match !== null) { |
return new SectionDocstring(match[1]); |
} |
match = VAR_DOCSTRING.exec(line); |
if (match !== null) { |
return new VarDocstring(match[1]); |
} |
var commentStart = line.lastIndexOf('//'); |
var varLine = commentStart === -1 ? line : line.slice(0, commentStart); |
match = VAR_ASSIGNMENT.exec(varLine); |
if (match !== null) { |
return new Variable(match[1], match[2]); |
} |
return undefined; |
}; |
|
Tokenizer.prototype.shift = function () { |
while (true) { |
var result = this._shift(); |
if (result === undefined) { |
continue; |
} |
return result; |
} |
}; |
|
function Parser(fileContent) { |
this._tokenizer = new Tokenizer(fileContent); |
} |
|
Parser.prototype.parseFile = function () { |
var sections = []; |
while (true) { |
var section = this.parseSection(); |
if (section === null) { |
if (this._tokenizer.shift() !== null) { |
throw new Error('Unexpected unparsed section of file remains!'); |
} |
return sections; |
} |
sections.push(section); |
} |
}; |
|
Parser.prototype.parseSection = function () { |
var section = this._tokenizer.shift(); |
if (section === null) { |
return null; |
} |
if (!(section instanceof Section)) { |
throw new Error('Expected section heading; got: ' + JSON.stringify(section)); |
} |
var docstring = this._tokenizer.shift(); |
if (docstring instanceof SectionDocstring) { |
section.docstring = docstring; |
} else { |
this._tokenizer.unshift(docstring); |
} |
this.parseSubSections(section); |
|
return section; |
}; |
|
Parser.prototype.parseSubSections = function (section) { |
while (true) { |
var subsection = this.parseSubSection(); |
if (subsection === null) { |
if (section.subsections.length === 0) { |
// Presume an implicit initial subsection |
subsection = new SubSection(''); |
this.parseVars(subsection); |
} else { |
break; |
} |
} |
section.addSubSection(subsection); |
} |
|
if (section.subsections.length === 1 && !section.subsections[0].heading && section.subsections[0].variables.length === 0) { |
// Ignore lone empty implicit subsection |
section.subsections = []; |
} |
}; |
|
Parser.prototype.parseSubSection = function () { |
var subsection = this._tokenizer.shift(); |
if (subsection instanceof SubSection) { |
this.parseVars(subsection); |
return subsection; |
} |
this._tokenizer.unshift(subsection); |
return null; |
}; |
|
Parser.prototype.parseVars = function (subsection) { |
while (true) { |
var variable = this.parseVar(); |
if (variable === null) { |
return; |
} |
subsection.addVar(variable); |
} |
}; |
|
Parser.prototype.parseVar = function () { |
var docstring = this._tokenizer.shift(); |
if (!(docstring instanceof VarDocstring)) { |
this._tokenizer.unshift(docstring); |
docstring = null; |
} |
var variable = this._tokenizer.shift(); |
if (variable instanceof Variable) { |
variable.docstring = docstring; |
return variable; |
} |
this._tokenizer.unshift(variable); |
return null; |
}; |
|
|
module.exports = Parser; |