| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237 | /*! * 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;
 |