i want concatenate bunch of different files of single type 1 large file. example, many javascript files 1 large file, many css files down 1 etc. want create sourcemap of files pre concatenation, not know start. working in node, open solutions in other environments.
i know there tools can this, seem on language language basis (uglifyjs, cssmin or whatever called these days), want tool not language specific.
also, define how files bound. example, in javascript want give each file own closure iife. such as:
(function () { // file }());
i can think of other wrappers implement different files.
here options see right now. however, don't know best or how start of them.
- find module (i'm working in node.js environment)
- create algorithm mozilla's source-map module. see couple options.
- only map each line new line location
- map every single character new location
- map every word new location (this options seems way out of scope)
- don't worry source maps
what guys think these options. i've tried options 2.1 , 2.2, solution seemed way complicated concatenation algorithm , did not perform in google chrome browser tools.
i implemented code without dependencies this:
export interface sourcemap { version: number; // 3 file?: string; sourceroot?: string; sources: string[]; sourcescontent?: string[]; names?: string[]; mappings: string | buffer; } const emptysourcemap: sourcemap = { version: 3, sources: [], mappings: new buffer(0) } var chartointeger = new buffer(256); var integertochar = new buffer(64); chartointeger.fill(255); 'abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyz0123456789+/='.split('').foreach((char, i) => { chartointeger[char.charcodeat(0)] = i; integertochar[i] = char.charcodeat(0); }); class dynamicbuffer { buffer: buffer; size: number; constructor() { this.buffer = new buffer(512); this.size = 0; } ensurecapacity(capacity: number) { if (this.buffer.length >= capacity) return; let oldbuffer = this.buffer; this.buffer = new buffer(math.max(oldbuffer.length * 2, capacity)); oldbuffer.copy(this.buffer); } addbyte(b: number) { this.ensurecapacity(this.size + 1); this.buffer[this.size++] = b; } addvlq(num: number) { var clamped: number; if (num < 0) { num = (-num << 1) | 1; } else { num <<= 1; } { clamped = num & 31; num >>= 5; if (num > 0) { clamped |= 32; } this.addbyte(integertochar[clamped]); } while (num > 0); } addstring(s: string) { let l = buffer.bytelength(s); this.ensurecapacity(this.size + l); this.buffer.write(s, this.size); this.size += l; } addbuffer(b: buffer) { this.ensurecapacity(this.size + b.length); b.copy(this.buffer, this.size); this.size += b.length; } tobuffer(): buffer { return this.buffer.slice(0, this.size); } } function countnl(b: buffer): number { let res = 0; (let = 0; < b.length; i++) { if (b[i] === 10) res++; } return res; } export class sourcemapbuilder { outputbuffer: dynamicbuffer; sources: string[]; mappings: dynamicbuffer; lastsourceindex = 0; lastsourceline = 0; lastsourcecol = 0; constructor() { this.outputbuffer = new dynamicbuffer(); this.mappings = new dynamicbuffer(); this.sources = []; } addline(text: string) { this.outputbuffer.addstring(text); this.outputbuffer.addbyte(10); this.mappings.addbyte(59); // ; } addsource(content: buffer, sourcemap?: sourcemap) { if (sourcemap == null) sourcemap = emptysourcemap; this.outputbuffer.addbuffer(content); let sourcelines = countnl(content); if (content.length > 0 && content[content.length - 1] !== 10) { sourcelines++; this.outputbuffer.addbyte(10); } let sourceremap = []; sourcemap.sources.foreach((v) => { let pos = this.sources.indexof(v); if (pos < 0) { pos = this.sources.length; this.sources.push(v); } sourceremap.push(pos); }); let lastoutputcol = 0; let inputmappings = (typeof sourcemap.mappings === "string") ? new buffer(<string>sourcemap.mappings) : <buffer>sourcemap.mappings; let outputline = 0; let ip = 0; let inoutputcol = 0; let insourceindex = 0; let insourceline = 0; let insourcecol = 0; let shift = 0; let value = 0; let valpos = 0; const commit = () => { if (valpos === 0) return; this.mappings.addvlq(inoutputcol - lastoutputcol); lastoutputcol = inoutputcol; if (valpos === 1) { valpos = 0; return; } let outsourceindex = sourceremap[insourceindex]; this.mappings.addvlq(outsourceindex - this.lastsourceindex); this.lastsourceindex = outsourceindex; this.mappings.addvlq(insourceline - this.lastsourceline); this.lastsourceline = insourceline; this.mappings.addvlq(insourcecol - this.lastsourcecol); this.lastsourcecol = insourcecol; valpos = 0; } while (ip < inputmappings.length) { let b = inputmappings[ip++]; if (b === 59) { // ; commit(); this.mappings.addbyte(59); inoutputcol = 0; lastoutputcol = 0; outputline++; } else if (b === 44) { // , commit(); this.mappings.addbyte(44); } else { b = chartointeger[b]; if (b === 255) throw new error("invalid sourcemap"); value += (b & 31) << shift; if (b & 32) { shift += 5; } else { let shouldnegate = value & 1; value >>= 1; if (shouldnegate) value = -value; switch (valpos) { case 0: inoutputcol += value; break; case 1: insourceindex += value; break; case 2: insourceline += value; break; case 3: insourcecol += value; break; } valpos++; value = shift = 0; } } } commit(); while (outputline < sourcelines) { this.mappings.addbyte(59); outputline++; } } tocontent(): buffer { return this.outputbuffer.tobuffer(); } tosourcemap(sourceroot?: string): buffer { return new buffer(json.stringify({ version: 3, sourceroot, sources: this.sources, mappings: this.mappings.tobuffer().tostring() })); } }
i, @ first, implemented "index map" spec, find out not supported browser.
another project useful @ magic string.