UNPKG

restream

Version:

Regular Expression Detection & Replacement streams.

233 lines (219 loc) 11.2 kB
const { _Replaceable, _SyncReplaceable, _SerialAsyncReplaceable, _replace, _restream, _makeMarkers, _makeCutRule, _makePasteRule } = require('./restream') /** * Create a _Transform_ stream which will maintain a buffer with data received from a _Readable_ stream and write data when the buffer can be matched against the regex. It will push the whole match object (or objects when the g flag is used) returned by `/regex/.exec(buffer)`. * @param {!RegExp} regex The regular expression to execute. * @return {!Transform} */ function $restream(regex) { return _restream(regex) } /** * An interface for the context accessible via this in replacer functions. */ class Replaceable extends _Replaceable { /** * _Replaceable_ class extends _Transform_ and pushes data when it's done replacing each incoming chunk. Replacement functions (that can also be async) are supplied via rules and work in the same way as replacers for `string.replace` method (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace): functions will take the `match` as the first argument, and matched `p1`, `p2`, _etc_ parameters as following arguments. * @param {!(Rule|Array<!Rule>)} rules An array with rules, or a single rule. * @param {!TransformOptions=} [options] Options for the transform stream. * @example * ```js * import { Replaceable, replace } from 'restream' * * // markdown __ to html <em> implementation * const stream = new Replaceable({ * re: /__(\S+)__/g, * replacement(match, p1) { * return `<em>${p1}</em>` * }, * }) * const res = await replace(stream, '__HelloWorld__') * console.log(res) // <em>HelloWorld</em> * ``` */ constructor(rules, options) { super(rules, options) } /** * After calling this method, any of the following rules and matches within the same rule won't be able to make any more changes. */ brake() { return super.brake() } /** * Creates a new replaceable to replace the given string, buffer or stream using the rules of the current stream. Calling `brake` will also set `_broke` on the parent stream. The new _Replaceable_ will copy the rules, and be assigned the context to it before replacing data. The `this` won't be shared by parent and child rules, but the context will be updated: `const context = { test: this.test }; content = await this.replace(content, context); this.test = context.test`. * @param {!(string|Buffer|Stream)} data The input data to replace via forked _Replaceable_. * @param {!Object<string, *>=} [context] The context to assign to the new _Replaceable_. * @return {!Promise<string>} */ async replace(data, context) { return super.replace(data, context) } } /** * An interface for the context accessible via this in replacer functions. */ class __SerialAsyncReplaceable extends _SerialAsyncReplaceable { /** * _Replaceable_ class extends _Transform_ and pushes data when it's done replacing each incoming chunk. Replacement functions (that can also be async) are supplied via rules and work in the same way as replacers for `string.replace` method (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace): functions will take the `match` as the first argument, and matched `p1`, `p2`, _etc_ parameters as following arguments. * @param {!(Rule|Array<!Rule>)} rules An array with rules, or a single rule. * @param {!TransformOptions=} [options] Options for the transform stream. * @example * ```js * import { Replaceable, replace } from 'restream' * * // markdown __ to html <em> implementation * const stream = new Replaceable({ * re: /__(\S+)__/g, * replacement(match, p1) { * return `<em>${p1}</em>` * }, * }) * const res = await replace(stream, '__HelloWorld__') * console.log(res) // <em>HelloWorld</em> * ``` */ constructor(rules, options) { super(rules, options) } /** * After calling this method, any of the following rules and matches within the same rule won't be able to make any more changes. */ brake() { return super.brake() } /** * Creates a new replaceable to replace the given string, buffer or stream using the rules of the current stream. Calling `brake` will also set `_broke` on the parent stream. The new _Replaceable_ will copy the rules, and be assigned the context to it before replacing data. The `this` won't be shared by parent and child rules, but the context will be updated: `const context = { test: this.test }; content = await this.replace(content, context); this.test = context.test`. * @param {!(string|Buffer|Stream)} data The input data to replace via forked _Replaceable_. * @param {!Object<string, *>=} [context] The context to assign to the new _Replaceable_. * @return {!Promise<string>} */ async replace(data, context) { return super.replace(data, context) } } /** * A class for when serial execution of asynchronous replacements within the same rule are needed. */ class SerialAsyncReplaceable extends __SerialAsyncReplaceable { /** * @param {() => !Promise<string>} link An async replacer function to be executed when all previous links in the chain have resolved. * @return {!Promise<string>} */ async addItem(link) { return super.addItem(link) } } /** * Make markers from a configuration object. Returns an object with markers for each requested type. * @param {!Object<string, !RegExp>} matchers An object with types of markers to create as keys and their detection regexes as values. * @param {!MakeMarkersConfig} [config] Additional configuration. * @param {(name: string, index: number) => string} [config.getReplacement] The function used to create a replacement when some text needs to be cut. * @param {(name: string) => !RegExp} [config.getRegex] The function used to create a RegExp to detect replaced chunks. * @return {!Object<string, !Marker>} */ function $makeMarkers(matchers, config) { return _makeMarkers(matchers, config) } /** * Make a rule for initial replacement of markers. * @param {!Marker} marker A marker is used to cut and paste portions of text to exclude them from processing by other rules. Markers should be created using the `makeMarker` factory method that will assign their properties. * @return {!Rule} A rule to cut matched chunks. */ function $makeCutRule(marker) { return _makeCutRule(marker) } /** * Make a rule for pasting markers back. * @param {!Marker} marker A marker is used to cut and paste portions of text to exclude them from processing by other rules. Markers should be created using the `makeMarker` factory method that will assign their properties. * @param {!(Rule|Array<!Rule>)=} [pipeRules] Any additional rules to replace the value of the marker before pasting it. Must be synchronous. * @return {!Rule} A rule to paste previously replaced chunks. */ function $makePasteRule(marker, pipeRules) { return _makePasteRule(marker, pipeRules) } /** * _SyncReplaceable_ function receives the whole string and returns the result of transform rules which are either sync function replacers or string replacements. This is not a class and just a function, but `this.brake` is still available. * @param {(string|!Buffer)} input The string or buffer to transform synchronously using the replacements. Does not support asynchronous replacements. * @param {!(Rule|Array<!Rule>)=} [rules] An array with rules with syncrhonous replacers. * @example * ```js * import { SyncReplaceable } from 'restream' * * const string = SyncReplaceable('_Sync Replacer_', [{ * re: /_(.+?)_/g, * replacement(match, p1) { * return `<em>${p1}</em>` * }, * }]) * console.log(string) // <em>Sync Replacer</em> * ``` * @return {string} A string with replacements according to sync rules. */ function $SyncReplaceable(input, rules) { return _SyncReplaceable(input, rules) } /** * The method to end the stream with given data, and collect the result. * @param {!Replaceable} replaceable An instance of _Replaceable_ to feed the data to. * @param {(string|!Buffer|!Stream)} input The data or stream to end the _Replaceable_ instance with. * @example * ```js * import { Replaceable, replace } from 'restream' * * const replaceable = new Replaceable([{ * re: /__(\S+?)__/g, * replacement(match, p1) { * return `<em>${p1}</em>` * }, * }]) * const res = await replace(replaceable, '__hello__') * console.log(res) // <em>hello</em> * ``` * @return {!Promise<string>} The replaced string */ function $replace(replaceable, input) { return _replace(replaceable, input) } module.exports = exports = $restream module.exports.Replaceable = Replaceable module.exports.SerialAsyncReplaceable = SerialAsyncReplaceable module.exports.makeMarkers = $makeMarkers module.exports.makeCutRule = $makeCutRule module.exports.makePasteRule = $makePasteRule module.exports.SyncReplaceable = $SyncReplaceable module.exports.replace = $replace /** * @typedef {$restream} restream * @typedef {$makeMarkers} makeMarkers * @typedef {$makeCutRule} makeCutRule * @typedef {$makePasteRule} makePasteRule * @typedef {$SyncReplaceable} SyncReplaceable * @typedef {$replace} replace */ /* typal types/markers.xml */ /** * @typedef {Object} Marker A marker is used to cut and paste portions of text to exclude them from processing by other rules. Markers should be created using the `makeMarker` factory method that will assign their properties. * @prop {string} name The name of the marker for annotation purposes. * @prop {!RegExp} re The regular expression used for detection of the match. * @prop {!RegExp} regExp The generated regular expression to replace the marker back to its original value. * @prop {!Object<number, string>} map The map which holds detected matches at their indexes. * @prop {number} lastIndex The index of the last inserted element. Starts with 0. * @prop {(name: string, index: number) => string} getReplacement The function to generate marker placeholders which can be then found, e.g., for (name: `marker`, index: `10`) by default _Restream_ will generate `%%_RESTREAM_MARKER_REPLACEMENT_10_%%`, but can be overridden with this method. * @typedef {Object} MakeMarkersConfig `@record` Additional configuration. * @prop {(name: string, index: number) => string} [getReplacement] The function used to create a replacement when some text needs to be cut. * @prop {(name: string) => !RegExp} [getRegex] The function used to create a RegExp to detect replaced chunks. */ /* typal types/index.xml */ /** * @typedef {(this: Replaceable, match: string, ...params: string[]) => (string|!Promise<string>)} Replacer A synchronous replacer function. * @typedef {Object} Rule `@record` A replacement rule. * @prop {!RegExp} re The regular expression to match input against. * @prop {(string|!Replacer)} replacement The replacement string, or the replacer function. */ /** * @typedef {import('stream').TransformOptions} TransformOptions * @typedef {import('stream').Stream} Stream * @typedef {import('stream').Transform} Transform */