Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

restream

Package Overview
Dependencies
Maintainers
1
Versions
26
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

restream - npm Package Compare versions

Comparing version 2.1.0 to 3.0.0

build/index.js.map

47

build/index.js

@@ -7,3 +7,2 @@ "use strict";

exports.default = createRegexTransformStream;
exports.replaceStream = replaceStream;
exports.Replaceable = void 0;

@@ -46,22 +45,3 @@

}
/**
* Create a replacement stream, which will push data when it's done replacing each incoming chunk. The return is a transform stream which writes strings. If the replacement is passed as a function, it will work in the same way as the replacer for `string.replace` method (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace), taking the `match` as the first argument, and matched `p1`, `p2`, _etc_ parameters as following arguments.
* @param {Rule|Rule[]} rules A single replacement rule, or multiple rules.
* @example
*
* // markdown __ to html emphasise implementation
* const stream = replaceStream({
* re: /__(\S+)__/g,
* replacement(match, p1) {
* return `<em>${p1}</em>`
* },
* })
*/
function replaceStream(rules) {
const ts = new Replaceable(rules);
return ts;
}
class Replaceable extends _stream.Transform {

@@ -89,3 +69,4 @@ /**

async _transform(chunk, _, next) {
async reduce(chunk) {
/** @type {string} */
const s = await this.rules.reduce(async (acc, {

@@ -101,5 +82,7 @@ re,

} else {
/** @type {function} */
const R = replacement.bind(this);
const promises = [];
const t = string.replace(re, (match, ...args) => {
const p = replacement(match, ...args);
const p = R(match, ...args);

@@ -123,6 +106,15 @@ if (p instanceof Promise) {

}, `${chunk}`);
this.push(s);
next();
return s;
}
async _transform(chunk, _, next) {
try {
const s = await this.reduce(chunk);
this.push(s);
next();
} catch (err) {
next(err);
}
}
}

@@ -134,7 +126,8 @@ /**

* @typedef {Object} Rule
* @property {RegExp} re Regular expression to match against
* @property {string|Replacer|AsyncReplacer} replacement A replacement string, or a replacement string function.
* @prop {RegExp} re Regular expression to match against
* @prop {string|Replacer|AsyncReplacer} replacement A replacement string, or a replacement string function.
*/
exports.Replaceable = Replaceable;
exports.Replaceable = Replaceable;
//# sourceMappingURL=index.js.map

@@ -20,2 +20,3 @@ "use strict";

return hasRe && type;
}
}
//# sourceMappingURL=index.js.map

@@ -0,1 +1,38 @@

## 21 July 2018
### 3.0.0
**Breaking**: pass context to replaces as _Replaceable_ instance. It is possible that someone relied on the `this` keyword inside of a rule or scope.
```js
const rule = {
abc: 'def',
f() {
console.log(this.abc)
},
}
rule.f() // def
```
```js
function test() {
this.abc = 'def'
const rule = {
f: () => {
console.log(this.abc)
},
}
rule.f() // def
}
test()
```
- [feature] Pass context to the `replacer` functions.
- [feature] Catch errors in replacers and emit them as stream errors.
- [deprecate] Remove `replaceStream` function to construct to what is now a `Replaceable` stream.
- [doc] Document with `%TYPE%`, give examples of `sync` and `async` replacers.
## 20 June 2018

@@ -2,0 +39,0 @@

{
"name": "restream",
"version": "2.1.0",
"version": "3.0.0",
"description": "Regular Expression Detection & Replacement streams",

@@ -11,9 +11,12 @@ "main": "build",

"test-examples/": "ZOROASTER_TIMEOUT=5000 zoroaster test-examples/spec -b",
"build": "babel src --out-dir build",
"e": "node examples/run",
"doc": "doc README-source.md -o README.md",
"examples/replacer.js": "yarn e examples/replacer",
"examples/replace-stream.js": "yarn e examples/replace-stream",
"examples/Replaceable.js": "yarn e examples/Replaceable",
"examples/restream.js": "yarn e examples/restream"
"b": "yarn-s doc build",
"build": "b --source-maps",
"e": "node example",
"doc": "NODE_DEBUG=doc doc documentary -o README.md",
"example/replacer.js": "yarn e example/replacer",
"example/replace-stream.js": "yarn e example/replace-stream",
"example/Replaceable.js": "yarn e example/Replaceable",
"example/restream.js": "yarn e example/restream",
"example/context.js": "yarn e example/context",
"example/errors.js": "yarn e example/errors"
},

@@ -45,12 +48,8 @@ "files": [

"devDependencies": {
"@babel/cli": "7.0.0-beta.51",
"@babel/core": "7.0.0-beta.51",
"@babel/plugin-syntax-object-rest-spread": "7.0.0-beta.51",
"@babel/plugin-transform-modules-commonjs": "7.0.0-beta.51",
"@babel/register": "7.0.0-beta.51",
"babel-plugin-transform-rename-import": "2.2.0",
"catchment": "2.0.1",
"documentary": "1.3.1",
"catchment": "3.0.0",
"documentary": "1.10.0",
"eslint-config-artdeco": "1.0.1",
"snapshot-context": "2.0.1",
"spawncommand": "2.0.1",
"yarn-s": "1.1.0",
"zoroaster": "2.1.0"

@@ -57,0 +56,0 @@ },

@@ -11,3 +11,2 @@ # restream

## Table of Contents

@@ -17,7 +16,13 @@

- [API](#api)
* [`restream(regex: RegExp): Transform`](#restreamregex-regexp-transform)
* [`Replaceable`](#replaceable)
* [`Rule` Type](#rule-type)
* [`constructor(rule: Rule|Rules[]): Replaceable`](#constructorrule-rulerules-replaceable)
* [`DEPRECATED_replaceStream(rule: Rule|Rule[]): Transform`](#deprecated_replacestreamrule-rulerule-transform)
- [`restream(regex: RegExp): Transform`](#restreamregex-regexp-transform)
- [`Replaceable` Class](#replaceable-class)
* [`Rule` Type](#rule-type)
* [<strong><code>re*</code></strong>](#re)
* [<strong><code>replacement*</code></strong>](#replacement)
* [`String` Replacement](#string-replacement)
* [`Function` Replacer](#function-replacer)
* [`Async Function` Replacer](#async-function-replacer)
* [`constructor(rule: Rule|Rules[]): Replaceable`](#constructorrule-rulerules-replaceable)
* [`Replacer` Context](#replacer-context)
* [`Replacer` Errors](#replacer-errors)

@@ -32,8 +37,8 @@ ## API

### `restream(`<br/>&nbsp;&nbsp;`regex: RegExp,`<br/>`): Transform`
## `restream(`<br/>&nbsp;&nbsp;`regex: RegExp,`<br/>`): Transform`
Create a `Transform` stream which will buffer incoming data and push regex results when matches can be made, i.e. when `regex.exec` returns non-null value. When the `g` flag is added to the regex, multiple matches will be detected.
```js
/** yarn examples/restream.js **/
```javascript
/** yarn example/restream.js */
import restream from 'restream'

@@ -72,19 +77,98 @@ import { createReadable, createWritable } from './lib'

### `Replaceable`
## `Replaceable` Class
A `Replaceable` transform stream can be used to transform data according to a single or multiple rules.
A _Replaceable_ transform stream can be used to transform data according to a single or multiple rules.
#### `Rule` Type
### `Rule` Type
`Replaceable` uses rules to determine how to transform data. Below is the description of the `Rule` type.
_Replaceable_ uses rules to determine how to transform data. Below is the description of the `Rule` type.
| Property | Type | Description |
| -------- | ---- | ----------- |
| re | `RegExp` | A regular expression. |
| replacement (1) | `string` | Replacement as a string. Here and below, it will be passed to the `string.replace(re, replacement)` native JavaScript method. |
| replacement (2) | `function` | Replacement as a sync function. See [MDN][2] for more documentation on how the replacer function should be implemented. |
| replacement (3) | `async function` | An asynchronous function to get replacements. The stream won't push any data until the replacer's promise is resolved. Due to implementation details, the regex will have to be run against incoming chunks twice, therefore it might be not ideal for heavy-load applications with many matches. |
<table>
<thead>
<tr>
<th>Property</th>
<th>Type</th>
<th>Description</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td><a name="re"><strong><code>re*</code></strong></a></td>
<td><em>RegExp</em></td>
<td>A regular expression.</td>
<td>Detect inline code blocks in markdown: <code>/`(.+?)`/</code>.</td>
</tr>
<tr>
<td><a name="replacement"><strong><code>replacement*</code></strong></a></td>
<td><em>string | function | async function</em></td>
<td>A replacer either as a <a href="#string-replacement">string</a>, <a href="#function-replacer">function</a>, or <a href="#async-function-replacer">async function</a>. It will be passed to the <code>string.replace(re, replacement)</code> native JavaScript method.</td>
<td>As a string: <code>INLINE_CODE</code>.</td>
</tr>
</tbody>
</table>
#### `constructor(`<br/>&nbsp;&nbsp;`rule: Rule|Rules[],`<br/>`): Replaceable`
##### `String` Replacement
Replacement as a string. Given a simple string, it will replace a match detected by the rule's regular expression, without consideration for the capturing groups.
##### `Function` Replacer
Replacement as a function. See [MDN](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter) for more documentation on how the replacer function should be implemented.
The example below allows to replace strings like `%NPM: documentary%` and `%NPM: @rqt/aqt%` into a markdown badge (used in [`documentary`](https://www.npmjs.com/package/documentary)).
<table>
<tr></tr>
<tr><td>
```js
const syncRule = {
re: /^%NPM: ((?:[@\w\d-_]+\/)?[\w\d-_]+)%$/gm,
replacement(match, name) {
const n = encodeURIComponent(name)
const svg = `https://badge.fury.io/js/${n}.svg`
const link = `https://npmjs.org/package/${name}`
return `[![npm version](${svg})](${link})`
},
}
```
</td></tr>
</table>
##### `Async Function` Replacer
An asynchronous function to get replacements. The stream won't push any data until the replacer's promise is resolved. Due to implementation details, the regex will have to be run against incoming chunks twice, therefore it might be not ideal for heavy-load applications with many matches.
This example will replace strings like `%FORK-js: example example/Replaceable.js%` into the output of a forked JavaScript program (used in [`documentary`](https://www.npmjs.com/package/documentary)).
<table>
<tr></tr>
<tr><td>
```js
import { fork } from 'spawncommand'
const codeSurround = (m, lang = '') =>
`\`\`\`${lang}\n${m.trim()}\n\`\`\``
const forkRule = {
re: /%FORK(?:-(\w+))? (.+)%/mg,
async replacement(match, lang, m) {
const [mod, ...args] = m.split(' ')
const { promise } = fork(mod, args, {
execArgv: [],
stdio: 'pipe',
})
const { stdout } = await promise
return codeSurround(stdout, lang)
},
}
```
</td></tr>
</table>
### `constructor(`<br/>&nbsp;&nbsp;`rule: Rule|Rules[],`<br/>`): Replaceable`
Create a `Transform` stream which will make data available when an incoming chunk has been updated according to the specified rule or rules.

@@ -94,4 +178,4 @@

```js
/** yarn examples/Replaceable.js */
```javascript
/** yarn example/Replaceable.js */
import Catchment from 'catchment'

@@ -101,21 +185,36 @@ import { Replaceable } from 'restream'

(async () => {
const dateRule = {
re: /%DATE%/g,
replacement: new Date().toLocaleString(),
}
const emRule = {
re: /__(.+?)__/g,
replacement(match, p1) {
return `<em>${p1}</em>`
},
}
const authorRule = {
re: /^%AUTHOR_ID: (.+?)%$/mg,
async replacement(match, id) {
const name = await new Promise(resolve => {
// pretend to lookup author name from the database
const authors = { 5: 'John' }
resolve(authors[id])
})
return `Author: <strong>${name}</strong>`
},
}
const STRING = `
Hello __Fred__, your username is __fred__.
You have __5__ stars.
%AUTHOR_ID: 5%
on __%DATE%__
`
;(async () => {
try {
const dateRule = {
re: /%DATE%/g,
replacement: new Date().toLocaleString(),
}
const emRule = {
re: /__(.+?)__/g,
replacement(match, p1) {
return `<em>${p1}</em>`
},
}
const authorRule = {
re: /^%AUTHOR: (.+?)%$/mg,
async replacement(match, p1) {
await new Promise(r => setTimeout(r,100))
return `Author: <strong>${p1}</strong>`
},
}
const replaceable = new Replaceable([

@@ -126,7 +225,3 @@ dateRule,

])
const rs = createReadable(`
Hello __Fred__, your username is __fred__ and you have __5__ stars.
%AUTHOR: John%
on __%DATE%__
`)
const rs = createReadable(STRING)
rs.pipe(replaceable)

@@ -148,37 +243,77 @@

```fs
Hello <em>Fred</em>, your username is <em>fred</em> and you have <em>5</em> stars.
```html
Hello <em>Fred</em>, your username is <em>fred</em>.
You have <em>5</em> stars.
Author: <strong>John</strong>
on <em>2018-6-20 19:59:18</em>
on <em>2018-7-21 02:09:39</em>
```
### `Replacer` Context
#### `DEPRECATED_replaceStream(`<br/>&nbsp;&nbsp;`rule: Rule|Rule[],`<br/>`): Transform`
Replacer functions will be executed with their context set to a _Replaceable_ instance to which they belong. Both `sync` and `async` replacers can use the `this` keyword to access their _Replaceable_ instance and modify its properties and/or emit events. This is done so that there's a mechanism by which replacers can share data between themselves.
Used to create a `Replaceable` stream. Deprecated in favour of the class constructor (see above).
For example, we might want to read and parse an external file first, but remember its data for use in following replacers.
```js
/** yarn examples/replace-stream.js */
Given an external file `example/types.json`:
```json
{
"TypeA": "A new type with certain properties.",
"TypeB": "A type to represent the state of the world."
}
```
_Replaceable_ can read it in the first `typesRule` rule, and reference its data in the second `paramRule` rule:
```javascript
/** yarn example/context.js */
import Catchment from 'catchment'
import { replaceStream } from 'restream'
import { createReadStream } from 'fs'
import { Replaceable } from 'restream'
import { createReadable } from './lib'
(async () => {
const typesRule = {
re: /^%types: (.+?)%$/mg,
async replacement(match, location) {
const rs = createReadStream(location)
const { promise } = new Catchment({ rs })
const d = await promise
const j = JSON.parse(d)
this.types = j // remember types for access in following rules
return match
},
}
const paramRule = {
re: /^ \* @typedef {(.+?)} (.+)(?: .*)?/mg,
replacement(match, type, typeName) {
const description = this.types[typeName]
if (!description) return match
return ` * @typedef {${type}} ${typeName} ${description}`
},
}
const STRING = `
%types: example/types.json%
/**
* @typedef {Object} TypeA
*/
`
;(async () => {
try {
const stream = replaceStream([{
re: /{{ user }}/,
replacement: 'fred',
}, {
re: /{{ name }}/g,
replacement: 'Fred',
}, {
re: /{{ stars }}/,
replacement: '5',
}])
const replaceable = new Replaceable([
typesRule,
paramRule,
])
const rs = createReadable(STRING)
rs.pipe(replaceable)
const rs = createReadable('Hello {{ name }}, your username is {{ user }} and you have {{ stars }} stars')
rs.pipe(stream)
const { promise } = new Catchment({
rs: stream,
rs: replaceable,
})
const res = await promise
console.log(res)

@@ -191,13 +326,57 @@ } catch (err) {

Output:
```js
%types: example/types.json%
```fs
Hello Fred, your username is fred and you have 5 stars
/**
* @typedef {Object} TypeA A new type with certain properties.
*/
```
As can be seen above, the description of the type was automatically updated based on the data read from the file.
### `Replacer` Errors
If an error happens in a `sync` or `async` replacer function, the `Replaceable` will emit it and close.
```javascript
/** yarn example/errors.js */
import { Replaceable } from 'restream'
import { createReadable } from './lib'
const STRING = 'test string'
;(async () => {
try {
const rs = createReadable(STRING)
const replaceable = new Replaceable([
{
re: /.*/,
replacement() {
throw new Error('An error occurred during a replacement.')
},
},
])
await new Promise((resolve, reject) => {
replaceable
.on('close', resolve)
.on('error', reject)
rs.pipe(replaceable)
})
} catch ({ message }) {
console.log(message)
}
})()
```
```js
An error occurred during a replacement.
```
---
(c) [Art Deco Code][1] 2018
(c) [Art Deco][1] 2018
[1]: https://artdeco.bz
[2]: https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/String/replace#Specifying_a_function_as_a_parameter
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc