diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..efd389d --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Ignore all differences in line endings +* -crlf + diff --git a/README.md b/README.md index d573026..d3fba30 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ [![Develop Build Status](https://travis-ci.org/signicode/scramjet.svg?branch=develop)](https://travis-ci.org/signicode/scramjet) [![Dependencies](https://david-dm.org/signicode/scramjet/status.svg)](https://david-dm.org/signicode/scramjet) [![Dev Dependencies](https://david-dm.org/signicode/scramjet/dev-status.svg)](https://david-dm.org/signicode/scramjet?type=dev) +[![Known Vulnerabilities](https://snyk.io/test/github/signicode/scramjet/badge.svg)](https://snyk.io/test/github/signicode/scramjet) ## What is scramjet diff --git a/docs/plugins.md b/docs/plugins.md index 4ee7b7a..c873fda 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -11,3 +11,40 @@ objects containing prototype extensions. One special extension is "constructor" that will be called in the stream constructor. A quick example can be the [scramjet-fini](https://www.npmjs.com/package/scramjet-fini) module on npm. + +A sample plugin +----------------- + +Here's how a minimal plugin looks like: + +```javascript +module.exports = { + DataStream = { + constructor(options) { + if (options.someValue) { + this.someData = true; + } + }, + addId(func, prefix) { + const fin = fini(prefix || defaultPrefix.next().value); + return this.pipe(new this.constructor({ + parallelTransform: (chunk) => func(chunk, fin.next().value) + })); + } + } +}; +``` + +All methods, getters/setters are copied upon scramjet stream prototype of the same name. The `constructor` method is +called at the end of the constructor. + +If a scramjet stream of a specific name does not exist, it will be added as a new class. + +Testing plugins +----------------- + +You can test your plugins as you like but it would be wise to check if your plugin doesn't break any scramjet-core +features. In order to do that set `SCRAMJET_TEST_HOME` to your designated plugin location and include +`path.resolve(require.resolve("scramjet-core"), "../test/v1/*.js")` from this dependency to your plugin as nodeunit. + +This is until we come up with a better solution. diff --git a/gulpfile.js b/gulpfile.js index 10e642c..9fb84cb 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -1,4 +1,5 @@ const gulp = require("gulp"); +const env = require('gulp-env'); const path = require("path"); const gutil = require("gulp-util"); const rename = require("gulp-rename"); @@ -10,6 +11,7 @@ const cache = require('gulp-cached'); const remember = require('gulp-remember'); const {Transform} = require('stream'); +const corepath = path.dirname(require.resolve("scramjet-core")); gulp.task('lint', function() { return gulp.src('./lib/*.js') @@ -22,7 +24,13 @@ gulp.task('lint', function() { }); gulp.task("test_legacy", function () { - return gulp.src(path.resolve(require.resolve("scramjet-core"), "../../test/v1/*.js")) + + return gulp.src(path.resolve(corepath, "../test/v1/*.js")) + .pipe(env({ + vars: { + SCRAMJET_TEST_HOME: __dirname + } + })) .pipe(nodeunit_runner({reporter: "verbose"})) ; }); @@ -68,8 +76,12 @@ gulp.task("readme", function() { ); }); -gulp.task("docs", ["readme"], function() { - const corepath = path.dirname(require.resolve("scramjet-core")); +gulp.task("copy_docs", function() { + return gulp.src(path.resolve(corepath, "../docs/*")) + .pipe(gulp.dest("docs/")); +}); + +gulp.task("docs", ["copy_docs", "readme"], function() { const jsdoc2md = require('jsdoc-to-markdown'); return gulp.src(["lib/*.js"]) diff --git a/jsdoc2md/partial/main.hbs b/jsdoc2md/partial/main.hbs index b0b3264..0c523a2 100644 --- a/jsdoc2md/partial/main.hbs +++ b/jsdoc2md/partial/main.hbs @@ -6,6 +6,7 @@ [![Develop Build Status](https://travis-ci.org/signicode/scramjet.svg?branch=develop)](https://travis-ci.org/signicode/scramjet) [![Dependencies](https://david-dm.org/signicode/scramjet/status.svg)](https://david-dm.org/signicode/scramjet) [![Dev Dependencies](https://david-dm.org/signicode/scramjet/dev-status.svg)](https://david-dm.org/signicode/scramjet?type=dev) +[![Known Vulnerabilities](https://snyk.io/test/github/signicode/scramjet/badge.svg)](https://snyk.io/test/github/signicode/scramjet) ## What is scramjet diff --git a/package-lock.json b/package-lock.json index ea965f5..0833e34 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3615,9 +3615,9 @@ "dev": true }, "scramjet-core": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/scramjet-core/-/scramjet-core-3.1.0.tgz", - "integrity": "sha1-vEY+4pMOEPpNiVaFle09p3bmKZs=" + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/scramjet-core/-/scramjet-core-3.1.4.tgz", + "integrity": "sha1-Ph/G2EN3GLJXxTbTWnRN6sxARMU=" }, "semver": { "version": "4.3.6", diff --git a/package.json b/package.json index 6a9abb4..cd7cb23 100644 --- a/package.json +++ b/package.json @@ -43,12 +43,15 @@ "devDependencies": { "freeport": "^1.0.5", "fs-promise": "^2.0.0", + "fs-then-native": "^2.0.0", "gulp": "^3.9.1", + "gulp-cached": "^1.1.1", "gulp-concat": "^2.6.0", "gulp-env": "^0.4.0", "gulp-exec": "^2.1.3", "gulp-jshint": "^2.0.4", "gulp-nodeunit-runner": "^0.2.2", + "gulp-remember": "^0.3.1", "gulp-rename": "^1.2.2", "gulp-util": "^3.0.7", "jsdoc-to-markdown": "^3.0.0", @@ -58,12 +61,9 @@ "nodeunit": "^0.11.0", "request": "^2.79.0", "request-promise": "^4.1.1", - "through2": "^2.0.3", - "fs-then-native": "^2.0.0", - "gulp-cached": "^1.1.1", - "gulp-remember": "^0.3.1" + "through2": "^2.0.3" }, "dependencies": { - "scramjet-core": "^3.1.0" + "scramjet-core": "^3.1.4" } } diff --git a/samples/data-stream-filter.js b/samples/data-stream-filter.js index 886759e..54a7e53 100644 --- a/samples/data-stream-filter.js +++ b/samples/data-stream-filter.js @@ -1,45 +1,45 @@ -#!/usr/bin/env node -// module: data-stream, method: filter - -const DataStream = require('../').DataStream; - -exports.stream = () => DataStream.fromArray([1,2,3,4,5,6,7,8,9,10]) - .filter((num) => num % 2 === 0) // return true for even numbers - ; - -exports.test = { - normal: (test) => { - - test.expect(2); - - const returned = exports.stream(); - - returned - .once("data", (num) => { - test.equals(num, 2, "First item must be even"); - }) - .toArray() - .then( - (ret) => { - test.equals(ret.toString(), '2,4,6,8,10', "Odd items need to be filtered out"); - test.done(); - } - ); - }, - filterAll: (test) => { - - test.expect(1); - - DataStream.fromArray([1,2,3,4,5]) - .filter(() => 0) - .map(() => test.ok(false, "No data should be outputted even on merged transforms")) - .on("data", () => test.ok(false, "No data should be emitted")) - .on("end", () => { - test.ok(true, "Stream should end"); - test.done(); - }); - - } -}; - -exports.log = console.log.bind(console); +#!/usr/bin/env node +// module: data-stream, method: filter + +const DataStream = require('../').DataStream; + +exports.stream = () => DataStream.fromArray([1,2,3,4,5,6,7,8,9,10]) + .filter((num) => num % 2 === 0) // return true for even numbers + ; + +exports.test = { + normal: (test) => { + + test.expect(2); + + const returned = exports.stream(); + + returned + .once("data", (num) => { + test.equals(num, 2, "First item must be even"); + }) + .toArray() + .then( + (ret) => { + test.equals(ret.toString(), '2,4,6,8,10', "Odd items need to be filtered out"); + test.done(); + } + ); + }, + filterAll: (test) => { + + test.expect(1); + debugger; + DataStream.fromArray([1,2,3,4,5]) + .filter(() => 0) + .map(() => test.ok(false, "No data should be outputted even on merged transforms")) + .on("data", () => test.ok(false, "No data should be emitted")) + .on("end", () => { + test.ok(true, "Stream should end"); + test.done(); + }); + + } +}; + +exports.log = console.log.bind(console); diff --git a/test/samples/test-samples.js b/test/samples/test-samples.js index d24ef09..f823f0c 100644 --- a/test/samples/test-samples.js +++ b/test/samples/test-samples.js @@ -1,115 +1,123 @@ -#!/usr/bin/env node - -const scramjet = require("../../"); -const fs = require('fs'); -const path = require('path'); -const nodeunit = require('../lib/reporter'); -const {unhandledRejectionHandler} = require("./handlers"); - -const matrix = [ - ["buffer", scramjet.BufferStream], - ["string", scramjet.StrinStream], - ["multi", scramjet.MultiStream], - ["data", scramjet.DataStream], - ["scramjet", "index.js"] -]; - -console.log("Samples testing"); - -process.on("unhandledRejection", unhandledRejectionHandler); - -module.exports = scramjet.fromArray( - matrix.map( - (item) => ({ - cls: item[0], - srcpath: - path.resolve(__dirname, "../../lib/", typeof item[1] === "string" ? item[1] : item[0] + '-stream.js') - }) - ) -).remap( - (emit, item) => { - return fs.createReadStream(item.srcpath) - .pipe(new scramjet.StringStream()) - .split(scramjet.StringStream.SPLIT_LINE) - .match(/@example.*\{@link ([^\}]+)(?:\|[^\}]*)?\}/gi) - .map((match) => new Promise((s) => { - const file = path.resolve(__dirname, "../", match); - fs.access( - file, - (err) => s(Object.assign({ - err: err, - sample: file - }, item)) - ); - })) - .reduce((nutn, item) => { - let out = null; - const fname = item.sample.match(/([^-]+).js/)[1]; - if (item.err) - return emit( - Object.assign({method: fname, test: (test) => { - test.ok(false, "Sample for "+fname+" is missing!"); - test.done(); - }}, item) - ); - try { - out = require(item.sample); - out.log = () => 1; - } catch(e) { - return emit( - Object.assign({method: fname, test: (test) => { - test.ok(false, "Test is failed to load: \n" + e && e.stack); - test.done(); - }}, item) - ); - } - - return emit( - Object.assign({method: fname, test: out.test || ((test) => { - test.ok(true, "Sample exists but there's no test."); - test.done(); - })}, item) - ); - }); - } -).reduce( - (acc, item) => { - const base = item.cls + ":" + item.method; - acc[base] = item.test; - return acc; - }, - {} -).then( - (tests) => new Promise((s, j) => { - console.log("Running "+Object.keys(tests).length+" tests"); - tests = Object.keys(tests).sort().reduce( - (acc, key) => (acc[key] = tests[key], acc), - {} - ); - nodeunit.run("samples_test", tests, null, (err, outcome) => { - if (err) { - return j(err); - } else { - err = outcome.filter( - (item) => item.error - ); - if (err.length) { - return j(err); - } else { - return s(); - } - } - }); - }) -).then( - () => console.log("Tests succeeded!"), - (e) => { - console.error("Some tests failed", e); - process.exit(101); - } -).catch( - (error) => { - console.log("Error", error); - process.exit(100); - } -); +#!/usr/bin/env node + +const scramjet = require("../../"); +const fs = require('fs'); +const path = require('path'); +const nodeunit = require('../lib/reporter'); +const {unhandledRejectionHandler} = require("./handlers"); +const corepath = path.dirname(require.resolve("scramjet-core")); + +const matrix = [ + ["buffer", scramjet.BufferStream], + ["string", scramjet.StrinStream], + ["multi", scramjet.MultiStream], + ["data", scramjet.DataStream], + ["scramjet", "index.js"] +]; + +console.log("Samples testing"); + +process.on("unhandledRejection", unhandledRejectionHandler); +module.exports = scramjet.fromArray( + matrix.map( + (item) => ({ + cls: item[0], + srcpath: + path.resolve(__dirname, "../../lib/", typeof item[1] === "string" ? item[1] : item[0] + '-stream.js') + }) + ).concat( + matrix.map( + (item) => ({ + cls: item[0], + srcpath: + path.resolve(corepath, typeof item[1] === "string" ? item[1] : item[0] + '-stream.js') + }) + ) + ) +).remap( + (emit, item) => { + return fs.createReadStream(item.srcpath) + .pipe(new scramjet.StringStream()) + .split(scramjet.StringStream.SPLIT_LINE) + .match(/@example.*\{@link ([^\}]+)(?:\|[^\}]*)?\}/gi) + .map((match) => new Promise((s) => { + const file = path.resolve(__dirname, "../", match); + fs.access( + file, + (err) => s(Object.assign({ + err: err, + sample: file + }, item)) + ); + })) + .reduce((nutn, item) => { + let out = null; + const fname = item.sample.match(/([^-]+).js/)[1]; + if (item.err) + return emit( + Object.assign({method: fname, test: (test) => { + test.ok(false, "Sample for "+fname+" is missing!"); + test.done(); + }}, item) + ); + try { + out = require(item.sample); + out.log = () => 1; + } catch(e) { + return emit( + Object.assign({method: fname, test: (test) => { + test.ok(false, "Test is failed to load: \n" + e && e.stack); + test.done(); + }}, item) + ); + } + + return emit( + Object.assign({method: fname, test: out.test || ((test) => { + test.ok(true, "Sample exists but there's no test."); + test.done(); + })}, item) + ); + }); + } +).reduce( + (acc, item) => { + const base = item.cls + ":" + item.method; + acc[base] = item.test; + return acc; + }, + {} +).then( + (tests) => new Promise((s, j) => { + console.log("Running "+Object.keys(tests).length+" tests"); + tests = Object.keys(tests).sort().reduce( + (acc, key) => (acc[key] = tests[key], acc), + {} + ); + nodeunit.run("samples_test", tests, null, (err, outcome) => { + if (err) { + return j(err); + } else { + err = outcome.filter( + (item) => item.error + ); + if (err.length) { + return j(err); + } else { + return s(); + } + } + }); + }) +).then( + () => console.log("Tests succeeded!"), + (e) => { + console.error("Some tests failed", e); + process.exit(101); + } +).catch( + (error) => { + console.log("Error", error); + process.exit(100); + } +);