grunt-pjs
Advanced tools
Comparing version 0.1.1 to 0.1.2
{ | ||
"name": "grunt-pjs", | ||
"description": "Preprocess JS build task for Grunt", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"homepage": "https://github.com/ashi009/grunt-pjs", | ||
@@ -6,0 +6,0 @@ "author": "Xiaoyi Shi <ashi009@gmail.com>", |
166
tasks/pjs.js
@@ -9,9 +9,2 @@ var async = require('async'); | ||
var kEmptyLinePattern = /^\s*$/; | ||
var kLineMarkerPattern = /^# (\d+) "(.+)"([\d\s]*)$/; | ||
var kLexPattern = /(['"])|\/([\/*])|(\*\/)|([$a-z_][$\w]*)|(\.?\d[x\da-f\+\-]*)|\{/ig; | ||
var kInvalidQuotePattern = /^[^\/]?\/$/; | ||
var kKeywords = arrayToObject('break case catch continue debugger default delete do else finally for function if in instanceof new return switch this throw try typeof var void while with'.split(' ')); | ||
function relativePath(from, to) { | ||
@@ -34,92 +27,89 @@ var fromDir = pth.dirname(from); | ||
var kKeywords = arrayToObject('break case catch continue debugger default delete do else finally for function if in instanceof new return switch this throw try typeof var void while with'.split(' ')); | ||
var kMappingInfoPattern = /\{(P:[^{}]+)\}/g; | ||
var kIndentPattern = /^[^{]*/; | ||
var kSymbolPattern = /^[$_a-z][$\w]*/i; | ||
function parseMappingInfo(str) { | ||
var info = {}; | ||
str.split(';').forEach(function(kv) { | ||
var index = kv.indexOf(':'); | ||
var key = kv.substr(0, index); | ||
var value = kv.substr(index + 1); | ||
switch (key) { | ||
case 'P': | ||
info.file = value; | ||
break; | ||
case 'L': | ||
info.line = parseInt(value, 10); | ||
break; | ||
case 'C': | ||
info.column = parseInt(value, 10) - 1; | ||
break; | ||
} | ||
}); | ||
return info; | ||
} | ||
function extractSourceMap(source, map, destFilePath, mapFilePath) { | ||
var sourcePathCache = {}; | ||
var lines = []; | ||
var sourceStack = []; | ||
var currentSource = null; | ||
var inComment = false; | ||
var inQuote = false; | ||
source.split(/\n/).forEach(function(line) { | ||
source.split('\n').forEach(function(rawLine) { | ||
var match = kLineMarkerPattern.exec(line); | ||
if (match) { | ||
var linenum = parseInt(match[1], 10); | ||
var filename = match[2]; | ||
var flags = arrayToObject(match[3].split(/\s+/)); | ||
if (flags[1]) { | ||
sourceStack.push(currentSource = { | ||
filename: filename | ||
}); | ||
} else if (flags[2]) { | ||
sourceStack.pop(); | ||
currentSource = sourceStack[sourceStack.length - 1]; | ||
} | ||
if (currentSource) { | ||
// console.assert(currentSource.filename == filename); | ||
// console.assert(currentSource.linenum == linenum, lines.length, currentSource, linenum); | ||
currentSource.linenum = linenum; | ||
} | ||
var line = kIndentPattern.exec(rawLine)[0]; | ||
var mappings = []; | ||
kMappingInfoPattern.lastIndex = line.length; | ||
for (var match; match = kMappingInfoPattern.exec(rawLine); ) { | ||
mappings.push({ | ||
startPos: match.index, | ||
endPos: match.index + match[0].length, | ||
info: parseMappingInfo(match[1]) | ||
}); | ||
} | ||
if (mappings.length == 0) { | ||
lines.push(rawLine); | ||
return; | ||
} | ||
if (!kEmptyLinePattern.test(line)) { | ||
mappings.push({ | ||
startPos: rawLine.length | ||
}); | ||
lines.push(line); | ||
for (var i = 0; i < mappings.length - 1; i++) { | ||
// Sadly learned that even the generated line is identical to the source, | ||
// source map requires a mapping before each symbol, so this is a simple | ||
// parser to find out all necessary tokens. | ||
// Note: Expect errors! This parser is not fully context aware. Eg. | ||
// keyword as object key will not be outputted, etc. | ||
for (kLexPattern.lastIndex = 0; match = kLexPattern.exec(line); ) { | ||
if (inComment) { | ||
if (match[3]) | ||
inComment = false; | ||
continue; | ||
var token = rawLine.substring(mappings[i].endPos, mappings[i + 1].startPos); | ||
var mappingInfo = mappings[i].info; | ||
var symbol = kSymbolPattern.exec(token); | ||
if (symbol) { | ||
symbol = symbol[0]; | ||
if (kKeywords[symbol]) { | ||
symbol = null; | ||
} | ||
if (inQuote) { | ||
if (match[1] == inQuote && | ||
!kInvalidQuotePattern.test(line.substring(match.index - 2, match.index))) | ||
inQuote = false; | ||
continue; | ||
} | ||
// Not in a string nor a comment | ||
// Check if starts a string | ||
if (match[1]) { | ||
if (!kInvalidQuotePattern.test(line.substring(match.index - 2, match.index))) | ||
inQuote = match[1]; | ||
// Check if starts a comment | ||
} else if (match[2]) { | ||
if (match[2] == '\/') | ||
break; | ||
inComment = true; | ||
continue; | ||
} | ||
var name = null; | ||
if (match[4]) { | ||
name = match[4]; | ||
if (kKeywords[name]) | ||
name = null; | ||
} | ||
if (name !== false) { | ||
map.addMapping({ | ||
generated: { | ||
line: lines.length, | ||
column: match.index | ||
}, | ||
source: relativePath(mapFilePath, currentSource.filename), | ||
original: { | ||
line: currentSource.linenum, | ||
column: match.index | ||
}, | ||
name: name | ||
}); | ||
} | ||
} | ||
map.addMapping({ | ||
generated: { | ||
line: lines.length + 1, | ||
column: line.length | ||
}, | ||
source: sourcePath(mappingInfo.file), | ||
original: mappingInfo, | ||
name: symbol | ||
}); | ||
line += token; | ||
} | ||
if (currentSource) | ||
currentSource.linenum++; | ||
lines.push(line); | ||
@@ -131,2 +121,9 @@ }); | ||
return lines.join('\n'); | ||
function sourcePath(to) { | ||
if (!sourcePathCache[to]) | ||
sourcePathCache[to] = relativePath(mapFilePath, to); | ||
return sourcePathCache[to]; | ||
} | ||
} | ||
@@ -154,2 +151,3 @@ | ||
var baseArgs = [ | ||
'-P', | ||
'-C', | ||
@@ -160,4 +158,4 @@ '-w', | ||
if (!options.sourceMap) | ||
baseArgs.push('-P'); | ||
if (options.sourceMap) | ||
baseArgs.push('-fdebug-cpp', '-E'); | ||
@@ -164,0 +162,0 @@ options.includePaths.forEach(function(path) { |
8152
181