UNPKG

imba

Version:

Intuitive and powerful language for building webapps that fly

301 lines (252 loc) 7.69 kB
import * as path from 'path'; import * as util from './helpers.mjs'; function iter$(a){ return a ? (a.toArray ? a.toArray() : a) : []; }; // imba$inlineHelpers=1 // imba$v2=0 // externs; var VLQ_SHIFT = 5; var VLQ_CONTINUATION_BIT = 1 << VLQ_SHIFT; var VLQ_VALUE_MASK = VLQ_CONTINUATION_BIT - 1; var BASE64_CHARS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; function SourceMap(script,options){ this._script = script; this._options = options || {}; this._sourcePath = this._options.sourcePath; this._sourceRoot = this._options.sourceRoot; this._targetPath = this._options.targetPath; this._maps = []; this._map = ""; this._js = ""; }; SourceMap.prototype.result = function(v){ return this._result; } SourceMap.prototype.setResult = function(v){ this._result = v; return this; }; SourceMap.prototype.source = function (){ return this._source; }; SourceMap.prototype.options = function (){ return this._options; }; SourceMap.prototype.sourceCode = function (){ return this._script.sourceCode; }; SourceMap.prototype.sourceName = function (){ return path.basename(this._sourcePath); }; SourceMap.prototype.targetName = function (){ return path.basename(this._targetPath); }; SourceMap.prototype.sourceFiles = function (){ return [this.sourceName()]; }; SourceMap.prototype.parse = function (){ // var matcher = /\/\*\%\$(\d*)\$\%/ var self = this; var matcher = /\/\*\%([\w\|]*)?\$\*\//; var replacer = /^(.*?)\/\*\%([\w\|]*)\$\*\//; var prejs = self._script.js; var lines = self._script.js.split(/\n/g); // what about js? var verbose = self._options.debug; // return self var sourceCode = self.sourceCode(); var locmap = util.locationToLineColMap(sourceCode); var append = ""; self._locs = []; self._maps = []; self._names = []; var pairs = []; var groups = {}; var uniqueGroups = {}; var match; // split the code in lines. go through each line // go through the code looking for LOC markers // remove markers along the way and keep track of // console.log source:js var jsloc = 0; for (let i = 0, items = iter$(lines), len = items.length, line; i < len; i++) { // console.log 'parse line',line // could split on these? line = items[i]; var col = 0; var caret = -1; self._maps[i] = []; while (line.match(matcher)){ line = line.replace(replacer,function(m,pre,meta) { var grp; if (meta == '') { return pre }; let pars = meta.split('|'); let loc = parseInt(pars[0]); let gid = pars[1] && parseInt(pars[1]); var lc = locmap[loc]; if (!lc) { return pre; }; let srcline = lc[0] + 1; let srccol = lc[1] + 1; if (caret != pre.length) { caret = pre.length; var mapping = [[srcline,srccol],[i + 1,caret + 1]]; // source and output self._maps[i].push(mapping); }; let locpair = [jsloc + caret,loc]; self._locs.push(locpair); if (gid) { if (grp = groups[gid]) { // groups[gid].push(locpair[0],locpair[1]) grp[1] = locpair[0]; grp[3] = locpair[1]; // grp.START = locpair let gstr = grp.join('|'); if (uniqueGroups[gstr]) { groups[gid] = []; } else { uniqueGroups[gstr] = true; let name = sourceCode.slice(grp[2],grp[3]); if (grp.START) { grp.START[2] = name; if (self._names.indexOf(name) < 0) { self._names.push(name); }; }; }; // grp[4] = locpair[1] } else { groups[gid] = [locpair[0],null,locpair[1],null]; }; // pairs.push([jsloc + caret,parseInt(pars[2]),loc,parseInt(pars[1]) - loc ]) }; return pre; }); }; jsloc += line.length + 1; lines[i] = line; }; self._script.js = lines.join('\n'); self._script.locs = { // map: locmap // generated: @locs spans: Object.values(groups) }; if (verbose) { for (let i = 0, items = iter$(self._script.locs.spans), len = items.length, pair; i < len; i++) { pair = items[i]; if (pair[1] != null) { let jsstr = self._script.js.slice(pair[0],pair[1]).split("\n"); let imbastr = sourceCode.slice(pair[2],pair[3]).split("\n"); pair.push(jsstr[0]); pair.push(imbastr[0]); }; }; let superMap = { 0: '\u2080', 1: '\u2081', 2: '\u2082', 3: '\u2083', 4: '\u2084', 5: '\u2085', 6: '\u2086', 7: '\u2087', 8: '\u2088', 9: '\u2089', '|': '\u208C' }; let repSuper = function(m,str) { return ("[" + str + "]"); let o = ''; let l = str.length; let i = 0; while (i < l){ o += superMap[str[i++]]; }; return '\u208D' + o + '\u208E'; }; self._script.js = self._script.js + '\n/*\n' + prejs.replace(/\/\*\%([\w\|]*)?\$\*\//g,repSuper).replace(/\/\*/g,'**').replace(/\*\//g,'**') + '\n*/'; }; return self; }; SourceMap.prototype.generate = function (){ this.parse(); var lastColumn = 1; var lastSourceLine = 1; var lastSourceColumn = 1; var buffer = ""; for (let lineNumber = 0, items = iter$(this._maps), len = items.length, line; lineNumber < len; lineNumber++) { line = items[lineNumber]; lastColumn = 1; for (let nr = 0, ary = iter$(line), len = ary.length, map; nr < len; nr++) { map = ary[nr]; if (nr != 0) { buffer += ',' }; var src = map[0]; var dest = map[1]; buffer += this.encodeVlq(dest[1] - lastColumn); lastColumn = dest[1]; // add index buffer += this.encodeVlq(0); // The starting line in the original source, relative to the previous source line. buffer += this.encodeVlq(src[0] - lastSourceLine); lastSourceLine = src[0]; // The starting column in the original source, relative to the previous column. buffer += this.encodeVlq(src[1] - lastSourceColumn); lastSourceColumn = src[1]; }; buffer += ";"; }; var rel = this._targetPath && path.relative(path.dirname(this._targetPath),this._sourcePath); var map = { version: 3, file: this.sourceName().replace(/\.imba/,'.js') || '', sourceRoot: this._sourceRoot || '', sources: [rel || this._sourcePath], sourcesContent: [this.sourceCode()], names: [], mappings: buffer // maps: @maps }; if (this._options.sourcemap == 'inline') { map.file = this.sourceName(); map.sources = [this.sourceName()]; }; this._result = map; return this; }; SourceMap.prototype.inlined = function (){ // maybe drop the sourcesContent try { var str = JSON.stringify(this._result); if (globalThis.Buffer) { str = Buffer.from(str,'utf-8').toString("base64"); } else if (typeof btoa == 'function') { str = btoa(str); } else { return; }; return ("\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + str); } catch (e) { }; console.warn("base64 encoding not supported - skipping inline sourceMapping"); return ""; }; // borrowed from CoffeeScript SourceMap.prototype.encodeVlq = function (value){ var answer = ''; // Least significant bit represents the sign. var signBit = (value < 0) ? 1 : 0; var nextChunk; // The next bits are the actual value. var valueToEncode = (Math.abs(value) << 1) + signBit; // Make sure we encode at least one character, even if valueToEncode is 0. while (valueToEncode || !answer){ nextChunk = valueToEncode & VLQ_VALUE_MASK; valueToEncode = valueToEncode >> VLQ_SHIFT; if (valueToEncode) { nextChunk |= VLQ_CONTINUATION_BIT; }; answer += this.encodeBase64(nextChunk); }; return answer; }; SourceMap.prototype.toJSON = function (){ return this._result; }; SourceMap.prototype.encodeBase64 = function (value){ return BASE64_CHARS[value]; // or throw Error.new("Cannot Base64 encode value: {value}") }; export { SourceMap };