diff --git a/lib/indent.js/VERSION b/lib/indent.js/VERSION index d15723f..42045ac 100644 --- a/lib/indent.js/VERSION +++ b/lib/indent.js/VERSION @@ -1 +1 @@ -0.3.2 +0.3.4 diff --git a/lib/indent.js/indent.js b/lib/indent.js/indent.js new file mode 100644 index 0000000..f2f7e80 --- /dev/null +++ b/lib/indent.js/indent.js @@ -0,0 +1,762 @@ +;(function(root, factory) { + if (typeof define === 'function' && define.amd) { + define([], factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.indent = factory(); + } +}(this, function() { +var indent = (function (root) { + var rulesCache = {}; + + function map(array, predicate) { + var i, results = []; + for (i=0; i/], + $ignoreRules: true, + $consumeEndMatch: true + }, + { + $languages: "html", + $name: "doctype", + $startPatterns: [/\<\!doctype html>/i], + $endPatterns: [NEW_LINE_REGEX], + $ignoreRules: true, + $consumeEndMatch: true + }, + { + $languages: "js html", + $name: "void-tags", + $startPatterns: [ + /\<(area|base|br|col|command|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)/i], + $endPatterns: [/>/], + $indent: true, + $consumeEndMatch: true + }, + { + $languages: "html", + $name: "mode switch js", + $startPatterns: [function (string) { + var start = /].*/i; + var end = /<\/script>/i; + var startMatch = start.exec(string); + var endMatch = end.exec(string); + + if (startMatch && (!endMatch || endMatch.index < startMatch.index)) { + return { + matchIndex: startMatch.index, + length: startMatch[0].length + }; + } + return null; + }], + $endPatterns: [/<\/script>/i], + $switchRules: "js", + $consumeEndMatch: true, + $indent: true, + $newScope: true + }, + { + $languages: "html", + $name: "mode switch css", + $startPatterns: [function (string) { + var start = /].*/i; + var end = /<\/style>/i; + var startMatch = start.exec(string); + var endMatch = end.exec(string); + + if (startMatch && (!endMatch || endMatch.index < startMatch.index)) { + return { + matchIndex: startMatch.index, + length: startMatch[0].length + }; + } + return null; + }], + $endPatterns: [/<\/style>/i], + $switchRules: "css", + $consumeEndMatch: true, + $indent: true, + $newScope: true + }, + { + $languages: "html", + $name: "html-tag", + $startPatterns: [//i], + $consumeEndMatch: true + }, + { + $languages: "js html", + $name: "tag", + $startPatterns: [function (string, rule, state) { + var re = /<([A-Za-z][A-Za-z0-9\-\.]*)/; + var match = string.match(re); + if (match) { + state.openingTag = match[1]; + return { + matchIndex: match.index, + length: match[0].length + } + } else { + return null; + } + }], + $endPatterns: [function (string, rule, state) { + var re = new RegExp("<\/" + state.openingTag + ">|\\s\/>", "i"); + var match = string.match(re); + if (match) { + return { + matchIndex: match.index, + length: match[0].length + } + } else { + return null; + } + }], + $indent: true, + $consumeEndMatch: true + }, + { + $languages: "js", + $name: "line-comment", + $startPatterns: [/\/\//], + $endPatterns: [NEW_LINE_REGEX], + $ignoreRules: true + }, + { + $languages: "js css", + $name: "block-comment", + $startPatterns: [/\/\*/], + $endPatterns: [/\*\//], + $ignoreRules: true, + $consumeEndMatch: true + }, + { + $languages: "js", + $name: "regex", + $startPatterns: [function (string, rule) { + var re = /[(,=:[!&|?{};][\s]*\/[^/]|^[\s]*\/[^/]/; + var startIndex = string.search(re); + if (startIndex != -1) { + startIndex = string.indexOf('/', startIndex); + var substr = string.substring(startIndex + 1); + var match = searchAny(substr, rule.$endPatterns, rule); + if (match.matchIndex != -1) { + substr = substr.substring(0, match.matchIndex); + try { + (new RegExp(substr)); + return { + matchIndex: startIndex, + length: 1 + }; + } + catch (e) { + return null; + } + } + } + return null; + }], + $endPatterns: [function (string) { + var fromIndex = 0; + var index = string.indexOf('/'); + while (index != -1) { + try { + (new RegExp(string.substring(0, index))); + break; + } + catch (e) { + index = string.indexOf('/', fromIndex); + fromIndex = index + 1; + } + } + return index === -1 ? null : { + matchIndex: index, + length: 1 + }; + }], + $ignoreRules: true, + $consumeEndMatch: true + }, + { + $languages: "js html", + $name: "quotes", + $excludeIf: HTML_TAG_RULES, + $startPatterns: [/"/], + $endPatterns: [/"/, NEW_LINE_REGEX], + $ignoreRules: true, + $consumeEndMatch: true + }, + { + $languages: "js html", + $name: "quotes", + $excludeIf: HTML_TAG_RULES, + $startPatterns: [/'/], + $endPatterns: [/'/, NEW_LINE_REGEX], + $ignoreRules: true, + $consumeEndMatch: true + }, + { + $languages: "js css", + $name: "string", + $startPatterns: [/(''|""|``)/], + $endPatterns: [/./, NEW_LINE_REGEX] + }, + { + $languages: "js css", + $name: "string", + $startPatterns: [/\"(?=[^"])/], + $endPatterns: [/[^\\]\"/, NEW_LINE_REGEX], + $ignoreRules: true, + $consumeEndMatch: true + }, + { + $languages: "js css", + $name: "string", + $startPatterns: [/\'(?=[^'])/], + $endPatterns: [/[^\\]\'/, NEW_LINE_REGEX], + $ignoreRules: true, + $consumeEndMatch: true + }, + { + $languages: "js css", + $name: "string", + $startPatterns: [/\`(?=[^`])/], + $endPatterns: [/[^\\]\`/], + $ignoreRules: true, + $consumeEndMatch: true + }, + { + $languages: "js", + $name: "if", + $startPatterns: [/^if\s*(?=\()/, /[\s]+if\s*(?=\()/], + $endPatterns: [/else[\s]+/, nonWhitespaceFollowByNewline, /[{;]/], + $indent: true + }, + { + $languages: "js", + $name: "for|while", + $startPatterns: [/^(for|while)\s*(?=\()/], + $endPatterns: [nonWhitespaceFollowByNewline, /[{;]/], + $indent: true + }, + { + $languages: "js", + $name: "else", + $startPatterns: [/else[\s]+/], + $endPatterns: [/if[^\w$]/, nonWhitespaceFollowByNewline, /[{;]/], + $indent: true + }, + { + $languages: "js css", + $name: "bracket", + $startPatterns: [/\(\s*(var|let|const)?\s*/], + $endPatterns: [/\)/], + $indent: true, + $consumeEndMatch: true, + $newScope: true + }, + { + $languages: "js", + $name: "dot-chain", + $startPatterns: [/^\.[A-Za-z$_]/], + $endPatterns: [/[\.;]/, NEW_LINE_REGEX], + $indent: true, + $matchBeginning: true, + $lineOffset: -1 + }, + { + $languages: "js", + $name: "dot-chain", + $startPatterns: [/\.\s*\r*\n/], + $endPatterns: [/[\.;})\]]/, /[^\s]\s*\r*\n/], + $indent: true + }, + { + $languages: "js css", + $name: "array", + $startPatterns: [/\[/], + $endPatterns: [/\]/], + $indent: true, + $consumeEndMatch: true, + $newScope: true + }, + { + $languages: "js css", + $name: "block", + $startPatterns: [/\{/], + $endPatterns: [/\}/], + $indent: true, + $consumeEndMatch: true, + $newScope: true + }, + { + $languages: "js", + $name: "var/let/const", + $startPatterns: [/(var|let|const)[\s]*\r*\n/], + $endPatterns: [nonWhitespaceFollowByNewline], + $indent: true, + $endPatternIndent: true + }, + { + $languages: "js", + $name: "var/let/const", + $startPatterns: [/(var|let|const)\s+(?=[\w$])/], + $endPatterns: [/[,;=]/, nonWhitespaceFollowByNewline], + $indent: true + }, + { + $languages: "js", + $name: "var/let/const", + $lastRule: ["var/let/const", "="], + $startPatterns: [/,[\s]*\r*\n/], + $endPatterns: [/[,;]/, nonWhitespaceFollowByNewline], + $indent: true, + callback: postIndentForCommaAfterEqual + }, + { + $languages: "js", + $name: "var/let/const", + $lastRule: ["var/let/const", "="], + $startPatterns: [/^,/], + $endPatterns: [/[,;]/, nonWhitespaceFollowByNewline], + $matchBeginning: true, + $indent: true, + $lineOffset: -1, + callback: postIndentForCommaAfterEqual + }, + { + $languages: "js", + $name: "equality", + $startPatterns: [/[=<>!]=(=)?/], + $endPatterns: [/./] + }, + { + $languages: "js", + $name: "=", + $excludeIf: HTML_TAG_RULES, + $startPatterns: [/=/], + $endPatterns: [/[,;\)\]}]/, NEW_LINE_REGEX] + }, + { + $languages: "js", + $name: "?:", + $startPatterns: [/\?/], + $endPatterns: [/[:;]/], + $endPatternIndent: true, + $indent: true + }, + { + $languages: "js", + $name: "case", + $startPatterns: [/^(case|default)[\s:]/], + $endPatterns: [/break[\s;\r\n]/, /^return[\s;\r\n]/, /^case[\s]+/, /^default[\s:]/, /}/], + $endPatternIndent: function (matchEnd) { + return matchEnd.endPatternIndex <= 1; + }, + $indent: true, + $newScope: true + }, + { + $languages: "js", + $name: "semicolon", + $startPatterns: [/;/], + $endPatterns: [/./] + } + ]; + + return { + css: function (code, options) { + return indent(code, filterRules('css', MASTER_RULES), options); + }, + js: function (code, options) { + return indent(code, filterRules('js', MASTER_RULES), options); + }, + ts: function (code, options) { + return indent(code, filterRules('js', MASTER_RULES), options); + }, + html: function (code, options) { + var rules = options && options.indentHtmlTag ? + filterRules('html', MASTER_RULES, 'html-tag') : filterRules('html', MASTER_RULES); + return indent(code, rules, options); + } + }; + + + function indent(code, baseRules, options) { + code = code || ''; + /** + * Algorithm assumptions + * + * indentDeltas - store the the deltas in tabString + * - can be manipulated directly to alter the tabString + * indentBuffer - used to keep tabs on the number of open indentations on each line + * dedentBuffer - each line in the buffer has an array storing open indent lines to be closed + * - an array of numbers is used to reference the opening line + * - a negative number is used to signify a soft dedent (see note about soft dedent) + * + * Each line can create at most 1 tabString. + * When a line is 'used up' for dedent, it cannot be used again, hence the indentBuffer. + */ + var tabString = options && options.tabString != null ? options.tabString : '\t'; + var lines = code.split(/[\r]?\n/gi); + var lineCount = lines.length; + var ignoreBuffer = intArray(lineCount); + var indentBuffer = intArray(lineCount); + var dedentBuffer = arrayOfArrays(lineCount); + var activeMatches = []; + var lastMatches= [null]; + var l = 0; + var pos = 0; + var matchEnd, matchStart; + var modeRules = null; + var line, lineToMatch, activeMatch; + + if (options) { + options.debug = { + buffers: { + ignore: ignoreBuffer, + indent: indentBuffer, + dedent: dedentBuffer, + active: activeMatches + } + }; + } + + while (l < lineCount) { + line = lines[l].trim(); + lineToMatch = cleanEscapedChars(line) + '\r\n'; + activeMatch = activeMatches[activeMatches.length-1]; + + matchStart = matchStartRule(lineToMatch, modeRules || baseRules, pos); + + if (activeMatches.length) { + matchEnd = matchEndRule(lineToMatch, activeMatch, pos, matchStart); + if (matchEnd.matchIndex === -1) { + if (activeMatch.rule.$ignoreRules) { + // last rule is still active, and it's telling us to ignore. + ignoreBuffer[l] = 1; + l++; pos = 0; + continue; + } + } + else if ( + activeMatch.rule.$ignoreRules || + matchStart.matchIndex === -1 || + matchEnd.matchIndex <= matchStart.matchIndex) { + removeCurrentRule(); + pos = matchEnd.cursor; + continue; // Repeat process for matching line start/end + } + } + + if (matchStart.matchIndex !== -1) { + implementRule(matchStart); + } + else { + // No new token match end, no new match start + l++; pos = 0; + } + } + + var + hardIndentCount, + dedentLines, dedentLine, dedents, + i, j, indents = 0, + hardIndents = copyIntArray(indentBuffer), + indentDeltas = intArray(lineCount), + newLines = []; + + for (i=0; i 0) { + hardIndents[dedentLine]--; + dedents += dedentLine !== i; + } + } + hardIndentCount = hardIndents[i]; + indentDeltas[i] = hardIndentCount > dedents ? 1 : + (hardIndentCount < dedents ? hardIndentCount - dedents : 0); + hardIndents[i] = hardIndentCount > 0 ? 1 : 0; + } + + for (i=0; i 0 ? repeatString(tabString, indents) : '') + lines[i].trim()); + } + } + + return newLines.join('\r\n'); + + + function implementRule(match) { + pos = match.cursor; + + var rule = match.rule; + var line = (l + 1) + (rule.$lineOffset || 0); + match.line = line; + activeMatches.push(match); + + if (rule.$indent) { + indentBuffer[line]++; + } + if (rule.$switchRules) { + modeRules = filterRules(rule.$switchRules, MASTER_RULES); + } + if (rule.$newScope) { + lastMatches.push(null); + } + if (rule.callback) { + rule.callback(match, indentBuffer, dedentBuffer); + } + } + + function removeCurrentRule() { + var match = activeMatches.pop(), + line = match.line, + rule = match.rule; + + if (rule.$indent) { + var endPatternIndent = typeof rule.$endPatternIndent === 'function' ? + rule.$endPatternIndent(matchEnd) : rule.$endPatternIndent; + var offset = !endPatternIndent && matchEnd.matchIndex === 0 ? 0 : 1; + if (dedentBuffer[l + offset]) dedentBuffer[l + offset].push(line); + } + if (rule.$switchRules) { + modeRules = null; + } + if (rule.$newScope) { + lastMatches.pop(); + } + lastMatches[lastMatches.length - 1] = match; + } + + function matchStartRule(string, rules, index) { + string = string.substring(index, string.length); + var result = null; + var minIndex = string.length; + var minMatch; + var match; + + var lastMatch = lastMatches[lastMatches.length - 1]; + var lastRuleInScope = lastMatch ? lastMatch.rule.$name : ''; + var activeRules = map(activeMatches, function (match) { + return match.rule.$name; + }).join('\n'); // Use \n as a special delimiter for rule names + + for (var rule, r = 0; r < rules.length; r++) { + rule = rules[r]; + if (rule.$excludeIf && some(rule.$excludeIf, function (excludeRule) { + return activeRules.indexOf(excludeRule) != -1; + })) { + } else if (!rule.$lastRule || + (lastRuleInScope && rule.$lastRule.indexOf(lastRuleInScope) !== -1) + ) { + match = searchAny(string, rule.$startPatterns, rule); + if (match.matchIndex != -1 && match.matchIndex < minIndex + && (!rule.$matchBeginning || index === 0)) { + minIndex = match.matchIndex; + minMatch = match; + result = rule; + } + } + } + return { + rule: result, + relativeIndex: result ? minIndex : -1, + matchIndex: result ? minIndex + index : -1, + cursor: result ? index + minMatch.cursor : -1, + state: minMatch ? minMatch.state : {}, + lastMatch: lastMatch + }; + } + + function matchEndRule(string, active, offset, matchStart) { + string = string.substr(offset, string.length); + var rule = active.rule; + var match = searchAny(string, rule.$endPatterns, rule, active.state, matchStart); + var cursor = rule.$consumeEndMatch ? match.cursor : match.matchIndex; + return { + endPatternIndex: match.endPatternIndex, + matchIndex: match.matchIndex === -1 ? -1 : match.matchIndex + offset, + cursor: cursor === -1 ? -1 : cursor + offset, + state: match.state + }; + } + } + + function arrayOfArrays(length) { + var array = new Array(length); + for (var i=0; i0&&(C[$]--,q+=$!==R);P=C[R],H[R]=P>q?1:P0?1:0}for(R=0;R0?s(p,M):"")+j[R].trim()));return L.join("\r\n")}function r(n){for(var e=new Array(n),t=0;t/],m:!0,l:!0},{a:"html",b:"doctype",i:[/\<\!doctype html>/i],k:[d],m:!0,l:!0},{a:"html",b:"void-tags",i:[/\<(area|base|br|col|command|embed|hr|img|input|keygen|link|menuitem|meta|param|source|track|wbr)/i],k:[/>/],d:!0,l:!0},{a:"html",b:"mode switch js",i:[function(n){var e=/].*/i,t=/<\/script>/i,r=e.exec(n),a=t.exec(n);return r&&(!a||a.index/i],e:"js",l:!0,d:!0,f:!0},{a:"html",b:"mode switch css",i:[function(n){var e=/].*/i,t=/<\/style>/i,r=e.exec(n),a=t.exec(n);return r&&(!a||a.index/i],e:"css",l:!0,d:!0,f:!0},{a:"html",b:"html-tag",i:[//i],l:!0},{a:"html",b:"tag",i:[function(n,e,t){var r=/<([A-Za-z0-9\-]+)/,a=n.match(r);return a?(t.openingTag=a[1],{matchIndex:a.index,length:a[0].length}):null}],k:[function(n,e,t){var r=new RegExp("","i"),a=n.match(r);return a?{matchIndex:a.index,length:a[0].length}:null}],d:!0,l:!0},{a:"js",b:"line-comment",i:[/\/\//],k:[d],m:!0},{a:"js css",b:"block-comment",i:[/\/\*/],k:[/\*\//],m:!0,l:!0},{a:"js",b:"regex",i:[function(n,e){var t=/[(,=:[!&|?{};][\s]*\/[^\/]|^[\s]*\/[^\/]/,r=n.search(t);if(r!=-1){r=n.indexOf("/",r);var a=n.substring(r+1),i=o(a,e.k,e);if(i.matchIndex!=-1){a=a.substring(0,i.matchIndex);try{return new RegExp(a),{matchIndex:r,length:1}}catch(s){return null}}}return null}],k:[function(n){for(var e=0,t=n.indexOf("/");t!=-1;)try{new RegExp(n.substring(0,t));break}catch(r){t=n.indexOf("/",e),e=t+1}return t===-1?null:{matchIndex:t,length:1}}],m:!0,l:!0},{a:"html",b:"quotes",i:[/"/],k:[/"/,d],m:!0,l:!0},{a:"html",b:"quotes",i:[/'/],k:[/'/,d],m:!0,l:!0},{a:"js css",b:"string",i:[/(''|""|``)/],k:[/./,d]},{a:"js css",b:"string",i:[/\"(?=[^"])/],k:[/[^\\]\"/,d],m:!0,l:!0},{a:"js css",b:"string",i:[/\'(?=[^'])/],k:[/[^\\]\'/,d],m:!0,l:!0},{a:"js css",b:"string",i:[/\`(?=[^`])/],k:[/[^\\]\`/],m:!0,l:!0},{a:"js",b:"if",i:[/^if\s*(?=\()/,/[\s]+if\s*(?=\()/],k:[/else[\s]+/,u,/[{;]/],d:!0},{a:"js",b:"for|while",i:[/^(for|while)\s*(?=\()/],k:[u,/[{;]/],d:!0},{a:"js",b:"else",i:[/else[\s]+/],k:[/if[^\w$]/,u,/[{;]/],d:!0},{a:"js css",b:"bracket",i:[/\(\s*(var|let|const)?\s*/],k:[/\)/],d:!0,l:!0,f:!0},{a:"js",b:"dot-chain",i:[/^\.[A-Za-z$_]/],k:[/[\.;]/,d],d:!0,j:!0,c:-1},{a:"js",b:"dot-chain",i:[/\.\s*\r*\n/],k:[/[\.;})\]]/,/[^\s]\s*\r*\n/],d:!0},{a:"js css",b:"array",i:[/\[/],k:[/\]/],d:!0,l:!0,f:!0},{a:"js css",b:"block",i:[/\{/],k:[/\}/],d:!0,l:!0,f:!0},{a:"js",b:"var/let/const",i:[/(var|let|const)[\s]*\r*\n/],k:[u],d:!0,g:!0},{a:"js",b:"var/let/const",i:[/(var|let|const)\s+(?=[\w$])/],k:[/[,;=]/,u],d:!0},{a:"js",b:"var/let/const",h:["var/let/const","="],i:[/,[\s]*\r*\n/],k:[/[,;]/,u],d:!0,callback:c},{a:"js",b:"var/let/const",h:["var/let/const","="],i:[/^,/],k:[/[,;]/,u],j:!0,d:!0,c:-1,callback:c},{a:"js",b:"equality",i:[/[=<>!]=(=)?/],k:[/./]},{a:"js",b:"=",i:[/=/],k:[/[,;\)\]}]/,d]},{a:"js",b:"?:",i:[/\?/],k:[/[:;]/],g:!0,d:!0},{a:"js",b:"case",i:[/^(case|default)[\s:]/],k:[/break[\s;\r\n]/,/^return[\s;\r\n]/,/^case[\s]+/,/^default[\s:]/,/}/],g:function(n){return n.endPatternIndex<=1},d:!0,f:!0},{a:"js",b:"semicolon",i:[/;/],k:[/./]}];return{css:function(n,r){return t(n,e("css",f),r)},js:function(n,r){return t(n,e("js",f),r)},ts:function(n,r){return t(n,e("js",f),r)},html:function(n,r){var a=r&&r.indentHtmlTag?e("html",f,"html-tag"):e("html",f);return t(n,a,r)}}}(this);return n}); \ No newline at end of file diff --git a/src/loading/validation-function-loader.js b/src/loading/validation-function-loader.js index e234e0d..3374b6d 100644 --- a/src/loading/validation-function-loader.js +++ b/src/loading/validation-function-loader.js @@ -9,7 +9,7 @@ exports.load = loadFromFile; const fs = require('fs'); const path = require('path'); -const indent = require('../../lib/indent.js/indent.min'); +const indent = require('../../lib/indent.js/indent'); const docDefinitionsLoader = require('./document-definitions-loader'); const fileFragmentLoader = require('./file-fragment-loader'); diff --git a/src/loading/validation-function-loader.spec.js b/src/loading/validation-function-loader.spec.js index ab0604d..4b56ebc 100644 --- a/src/loading/validation-function-loader.spec.js +++ b/src/loading/validation-function-loader.spec.js @@ -16,7 +16,7 @@ describe('Validation function loader', () => { mockRequire('fs', fsMock); indentMock = { js: simpleMock.stub() }; - mockRequire('../../lib/indent.js/indent.min.js', indentMock); + mockRequire('../../lib/indent.js/indent.js', indentMock); fileFragmentLoaderMock = { load: simpleMock.stub() }; mockRequire('./file-fragment-loader.js', fileFragmentLoaderMock);