Skip to content

Commit

Permalink
feat!: Support streaming contents (#33)
Browse files Browse the repository at this point in the history
  • Loading branch information
phated committed Oct 15, 2022
1 parent 34b2282 commit e989297
Show file tree
Hide file tree
Showing 5 changed files with 918 additions and 76 deletions.
35 changes: 17 additions & 18 deletions index.js
@@ -1,6 +1,7 @@
'use strict';

var File = require('vinyl');
var vinylContents = require('vinyl-contents');

var helpers = require('./lib/helpers');

Expand All @@ -12,25 +13,28 @@ function add(file, callback) {
return callback(new Error(PLUGIN_NAME + '-add: Not a vinyl file'));
}

// Bail early with an error if file has streaming contents
if (file.isStream()) {
return callback(new Error(PLUGIN_NAME + '-add: Streaming not supported'));
}

// Bail early successfully if file is null or already has a sourcemap
if (file.isNull() || file.sourceMap) {
return callback(null, file);
}

var state = {
path: '', // Root path for the sources in the map
map: null,
content: file.contents.toString(),
// TODO: handle this?
preExistingComment: null,
};
vinylContents(file, onContents);

function onContents(err, contents) {
if (err) {
return callback(err);
}

var state = {
path: '', // Root path for the sources in the map
map: null,
content: contents.toString(),
// TODO: handle this?
preExistingComment: null,
};

helpers.addSourceMaps(file, state, callback);
helpers.addSourceMaps(file, state, callback);
}
}

function write(file, destPath, callback) {
Expand All @@ -45,11 +49,6 @@ function write(file, destPath, callback) {
return callback(new Error(PLUGIN_NAME + '-write: Not a vinyl file'));
}

// Bail early with an error if file has streaming contents
if (file.isStream()) {
return callback(new Error(PLUGIN_NAME + '-write: Streaming not supported'));
}

// Bail early successfully if file is null or doesn't have sourcemap
if (file.isNull() || !file.sourceMap) {
return callback(null, file);
Expand Down
104 changes: 96 additions & 8 deletions lib/helpers.js
Expand Up @@ -5,14 +5,17 @@ var os = require('os');
var TextDecoder = require('util').TextDecoder;

var fs = require('graceful-fs');
var Readable = require('streamx').Readable;
var Transform = require('streamx').Transform;
var nal = require('now-and-later');
var File = require('vinyl');
var convert = require('convert-source-map');

var urlRegex = /^(https?|webpack(-[^:]+)?):\/\//;

var bufCR = Buffer.from('\r\n');
var bufNL = Buffer.from('\n');
var bufCR = Buffer.from('\r');
var bufCRLF = Buffer.from('\r\n');
var bufLF = Buffer.from('\n');
var bufEOL = Buffer.from(os.EOL);

var decoder = new TextDecoder('utf-8', { ignoreBOM: false });
Expand All @@ -39,7 +42,11 @@ function loadSourceMap(file, state, callback) {
state.path = file.dirname;
state.content = convert.removeComments(state.content);
// Remove source map comment from source
file.contents = Buffer.from(state.content, 'utf8');
if (file.isStream()) {
file.contents = Readable.from(state.content);
} else {
file.contents = Buffer.from(state.content, 'utf8');
}
return callback();
}

Expand All @@ -51,7 +58,11 @@ function loadSourceMap(file, state, callback) {
mapFile = path.resolve(file.dirname, mapComment[1] || mapComment[2]);
state.content = convert.removeMapFileComments(state.content);
// Remove source map comment from source
file.contents = Buffer.from(state.content, 'utf8');
if (file.isStream()) {
file.contents = Readable.from(state.content);
} else {
file.contents = Buffer.from(state.content, 'utf8');
}
} else {
// If no comment try map file with same name as source file
mapFile = file.path + '.map';
Expand Down Expand Up @@ -232,23 +243,100 @@ function writeSourceMaps(file, destPath, callback) {
}

// Append source map comment
file.contents = appendBuffer(file.contents, Buffer.from(comment));
file = append(file, Buffer.from(comment));

callback(null, file, sourceMapFile);
}

function append(file, comment) {
if (file.isBuffer()) {
file.contents = appendBuffer(file.contents, comment);
}

if (file.isStream()) {
file.contents = file.contents.pipe(appendStream(comment));
}

return file;
}

function appendBuffer(buf1, buf2) {
var eol;
if (buf1.slice(-2).equals(bufCR)) {
if (buf1.slice(-2).equals(bufCRLF)) {
eol = bufCRLF;
} else if (buf1.slice(-1).equals(bufLF)) {
eol = bufLF;
} else if (buf1.slice(-1).equals(bufCR)) {
eol = bufCR;
} else if (buf1.slice(-1).equals(bufNL)) {
eol = bufNL;
} else {
eol = bufEOL;
}
return Buffer.concat([buf1, buf2, eol]);
}

// Implementation heavily inspired by https://github.com/maxogden/eol-stream
function appendStream(comment) {
var crAtEnd = false;
var eol = bufEOL;

return new Transform({
transform: detect,
flush: flush,
});

function detect(chunk, callback) {
var isString = typeof chunk === 'string';

var cr = isString ? bufCR.toString() : bufCR[0];
var lf = isString ? bufLF.toString() : bufLF[0];

if (crAtEnd) {
if (chunk[0] === lf) {
eol = bufCRLF;
} else {
eol = bufCR;
}
// Reset the flag because we search for the _last_ line ending
crAtEnd = false;
return callback(null, chunk);
}

var i = 0;
while (i < chunk.length) {
if (chunk[i] === cr) {
if (i === chunk.length - 1) {
// Need to read the next chunk to see if it is a CRLF
crAtEnd = true;
i += 1;
} else if (chunk[i + 1] === lf) {
eol = bufCRLF;
// We skip two because we saw the CRLF
i += 2;
} else {
eol = bufCR;
i += 1;
}
continue;
}

if (chunk[i] === lf) {
eol = bufLF;
}

i += 1;
}

callback(null, chunk);
}

function flush(cb) {
this.push(comment);
this.push(eol);

cb();
}
}

function normalizeRelpath(fp) {
// Since we only ever operate on relative paths,
// this utility shouldn't need to handle path roots
Expand Down
6 changes: 4 additions & 2 deletions package.json
Expand Up @@ -27,7 +27,9 @@
"convert-source-map": "^1.8.0",
"graceful-fs": "^4.2.10",
"now-and-later": "^3.0.0",
"vinyl": "^3.0.0"
"streamx": "^2.12.5",
"vinyl": "^3.0.0",
"vinyl-contents": "^2.0.0"
},
"devDependencies": {
"eslint": "^7.32.0",
Expand All @@ -36,7 +38,7 @@
"expect": "^27.5.1",
"mocha": "^8.4.0",
"nyc": "^15.1.0",
"streamx": "^2.12.5"
"readable-stream": "^3.6.0"
},
"nyc": {
"extension": [
Expand Down

0 comments on commit e989297

Please sign in to comment.