website hosted
This commit is contained in:
45
node_modules/stream-replace-string/.github/workflows/checks.yml
generated
vendored
Normal file
45
node_modules/stream-replace-string/.github/workflows/checks.yml
generated
vendored
Normal file
@@ -0,0 +1,45 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: Checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [master]
|
||||
pull_request:
|
||||
branches: [master]
|
||||
|
||||
jobs:
|
||||
Lint:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 18
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
- name: Check Lint
|
||||
run: npm run lint
|
||||
|
||||
Test:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [14, 16, 18, 19]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
- name: Run Tests
|
||||
run: npm test
|
||||
8
node_modules/stream-replace-string/.vscode/settings.json
generated
vendored
Normal file
8
node_modules/stream-replace-string/.vscode/settings.json
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"papaper"
|
||||
],
|
||||
"javascript.format.semicolons": "remove",
|
||||
"javascript.format.insertSpaceAfterConstructor": true,
|
||||
"javascript.format.insertSpaceBeforeFunctionParenthesis": true
|
||||
}
|
||||
21
node_modules/stream-replace-string/LICENSE
generated
vendored
Normal file
21
node_modules/stream-replace-string/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 ChocolateLoverRaj
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
55
node_modules/stream-replace-string/README.md
generated
vendored
Normal file
55
node_modules/stream-replace-string/README.md
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
# stream-replace-string
|
||||
Replaces strings in a stream.
|
||||
|
||||
## Install
|
||||
```shell
|
||||
npm i stream-replace-string
|
||||
```
|
||||
|
||||
## Using
|
||||
```javascript
|
||||
import replace from 'stream-replace-string'
|
||||
import { createReadStream, createWriteStream } from 'fs'
|
||||
|
||||
createReadStream("./input.txt")
|
||||
.pipe(replace("{fruit}", "apple"))
|
||||
.pipe(createWriteStream("./output.txt"))
|
||||
```
|
||||
You can also replace multiple different words by piping your data through multiple replace streams
|
||||
```javascript
|
||||
createReadStream("./input.txt")
|
||||
.pipe(replace("good", "great"))
|
||||
.pipe(replace("bad", "horrible"))
|
||||
.pipe(replace("grey", "gray"))
|
||||
.pipe(createWriteStream("./output.txt"))
|
||||
```
|
||||
|
||||
### The `replace` function
|
||||
```javascript
|
||||
replace(searchStr, replaceWith, options)
|
||||
```
|
||||
- Parameters
|
||||
- `searchStr` - The string to search for. This must be a string, and cannot be a regex.
|
||||
- `replaceWith` - There are a couple of different things you can use:
|
||||
- String - Inserts the string.
|
||||
- Promise resolving a string - Once the promise is resolved, the given string is used.
|
||||
- Replacer function - This is a custom replacer function. The function can take a parameter, `matches`, which is the number of matches so far (for the first match, this will be `0`). This function should return a string or a promise resolving a string.
|
||||
- Readable stream - A readable stream can be given, which can make more chunks available sooner. See `options.bufferReplaceStream` for options if you are using a readable stream.
|
||||
- `options` (optional) - An object of options.
|
||||
- `limit` (optional, default `Infinity`) - The maximum number of strings to replace. This can be useful if you know there is only 1 occurrence of `searchStr`. Once this limit is reached, the transform stream will stop transforming anything.
|
||||
- `bufferReplaceStream` (optional, default `true`) - This is for when you use a readable stream for `replaceWith`. If this is true, the `replaceWith` stream will be read right away, and be kept in memory to be used once a match is found. For fastest performance, keep this to be `true`. If your `replaceWith` stream is very large and you have a limit of 1, you can set this to `false` to save memory.
|
||||
- Returns
|
||||
- Returns a transform stream. See the [How It Works](#how-it-works) section for more information.
|
||||
|
||||
## How It Works
|
||||
|
||||
### The transform stream
|
||||
The [`replace`](#the-`replace`-function) function returns a Node.js transform stream. Transform streams take in chunks and can also be read. In this particular transform stream, it takes a string and outputs the same string, except that it replaces the replace.
|
||||
|
||||
### Efficient memory use.
|
||||
The tricky part with this transform stream is knowing when to hold chunks, and when to pass them on. Let's say we were looking for `'paper'` in our text. If our first chunk was: `'perfect pot'`, this module knows that there is no way the string `'perfect pot'` will fit into the search string, `'paper'`. We can then pass the chunks onto the output of the transform stream, available right away for the stream consumer. However, if our first chunk was: `'p'`, we can't pass that on, because there is a change that the next chunk could start with `'aper'`. Since we aren't sure if the `'p'` will be replaced or not, we hold on to this text, and check it once we get the next chunk. If we get `'aper'` in the next chunk, we replace the text. If we get something else, like `'ear'`, we can attach it to the `'p'` and output `'pear'`. It gets even more complicated when multiple potential matches are possible. Let's say we get `'pap'` in our first chunk. We need to be watching the first `'p'` and the third `'p'`, because it could end up being `'paper'`, if the next chunk was `'er'`, or it could end of being `'papaper'`. This package is smart and it will efficiently find matches spanning multiple chunks, and get rid of text it knows won't have a match.
|
||||
|
||||
#### Importing with ESModules
|
||||
```javascript
|
||||
import replace from 'stream-replace-string'
|
||||
```
|
||||
13
node_modules/stream-replace-string/index.d.ts
generated
vendored
Normal file
13
node_modules/stream-replace-string/index.d.ts
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Transform, Readable } from 'stream';
|
||||
|
||||
interface Options {
|
||||
limit?: number;
|
||||
bufferReplaceStream?: boolean;
|
||||
}
|
||||
|
||||
type ReplaceStr = string | Promise<string>;
|
||||
type ReplacerFunc = (matches?: number) => ReplaceStr;
|
||||
|
||||
declare function replace(searchStr: string, replaceWith: ReplaceStr | ReplacerFunc | Readable, options?: Options): Transform;
|
||||
|
||||
export default replace;
|
||||
238
node_modules/stream-replace-string/index.js
generated
vendored
Normal file
238
node_modules/stream-replace-string/index.js
generated
vendored
Normal file
@@ -0,0 +1,238 @@
|
||||
import { Transform, Readable } from 'stream'
|
||||
import { StringDecoder } from 'string_decoder'
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} searchStr
|
||||
* @param {string} replaceWith
|
||||
* @param {number} limit
|
||||
*/
|
||||
const replace = (searchStr, replaceWith, options = {}) => {
|
||||
// Defaulting
|
||||
if (!Object.prototype.hasOwnProperty.call(options, 'limit')) {
|
||||
options.limit = Infinity
|
||||
}
|
||||
if (!Object.prototype.hasOwnProperty.call(options, 'bufferReplaceStream')) {
|
||||
options.bufferReplaceStream = true
|
||||
}
|
||||
|
||||
// Type checking
|
||||
if (typeof searchStr !== 'string') {
|
||||
throw new TypeError('searchStr must be a string.')
|
||||
}
|
||||
if (!(
|
||||
typeof replaceWith === 'string' ||
|
||||
replaceWith instanceof Promise ||
|
||||
typeof replaceWith === 'function' ||
|
||||
replaceWith instanceof Readable
|
||||
)) {
|
||||
throw new TypeError('replaceWith must be either a string, a promise resolving a string, a function returning string, a function returning a promise resolving a string, or a readable stream.')
|
||||
}
|
||||
if (typeof options !== 'object') {
|
||||
throw new TypeError('options must be an object.')
|
||||
}
|
||||
if (!(
|
||||
(Number.isInteger(options.limit) && options.limit > 0) ||
|
||||
(options.limit === Infinity)
|
||||
)) {
|
||||
throw new TypeError('options.limit must be a positive integer or infinity.')
|
||||
}
|
||||
if (typeof options.bufferReplaceStream !== 'boolean') {
|
||||
throw new TypeError('options.bufferReplaceStream must be a boolean.')
|
||||
}
|
||||
const limit = options.limit
|
||||
|
||||
// This stuff is for if replaceWith is a readable stream
|
||||
let replaceWithBuffer = ''
|
||||
let replaceWithNewChunk
|
||||
let isDecodingReplaceWithStream = false
|
||||
const startDecodingReplaceWithStream = () => {
|
||||
isDecodingReplaceWithStream = true
|
||||
const stringDecoder = new StringDecoder('utf-8')
|
||||
const dataHandler = data => {
|
||||
replaceWithNewChunk = stringDecoder.write(data)
|
||||
if (options.bufferReplaceStream) {
|
||||
replaceWithBuffer += replaceWithNewChunk
|
||||
}
|
||||
}
|
||||
|
||||
const endHandler = () => {
|
||||
replaceWithNewChunk = stringDecoder.end()
|
||||
if (options.bufferReplaceStream) {
|
||||
replaceWithBuffer += replaceWithNewChunk
|
||||
}
|
||||
}
|
||||
|
||||
replaceWith
|
||||
.on('data', dataHandler)
|
||||
.on('end', endHandler)
|
||||
}
|
||||
if (replaceWith instanceof Readable) {
|
||||
if (options.bufferReplaceStream) {
|
||||
startDecodingReplaceWithStream()
|
||||
} else {
|
||||
replaceWith.pause()
|
||||
}
|
||||
}
|
||||
|
||||
// Create a new string decoder to turn buffers into strings
|
||||
const stringDecoder = new StringDecoder('utf-8')
|
||||
|
||||
// Whether the limit has been reached
|
||||
let doneReplacing = false
|
||||
// Then number of matches
|
||||
let matches = 0
|
||||
|
||||
// The in progress matches waiting for next chunk to be continued
|
||||
/**
|
||||
* An array of numbers, each number is an index which marks the start of the string `unsureBuffer`
|
||||
*/
|
||||
let partialMatchesFromPrevChunk = []
|
||||
|
||||
// The string data that we aren't yet sure it's part of the search string or not
|
||||
// We have to hold on to this until we are sure.
|
||||
let unsureBuffer = ''
|
||||
|
||||
// Get the replace string
|
||||
let replaceStr = typeof replaceWith === 'string' ? replaceWith : undefined
|
||||
const pushReplaceStr = async () => {
|
||||
switch (typeof replaceWith) {
|
||||
case 'string': {
|
||||
transform.push(replaceStr)
|
||||
break
|
||||
}
|
||||
case 'function': {
|
||||
const returnedStr = await replaceWith(matches)
|
||||
if (typeof returnedStr !== 'string') {
|
||||
throw new TypeError('Replace function did not return a string or a promise resolving a string.')
|
||||
}
|
||||
transform.push(returnedStr)
|
||||
break
|
||||
}
|
||||
case 'object': {
|
||||
if (replaceWith instanceof Promise) {
|
||||
replaceStr = await replaceWith
|
||||
if (typeof replaceStr !== 'string') {
|
||||
throw new TypeError('Replace promise did not resolve to a string.')
|
||||
}
|
||||
transform.push(replaceStr)
|
||||
} else if (replaceWith instanceof Readable) {
|
||||
await new Promise((resolve, reject) => {
|
||||
if (!isDecodingReplaceWithStream) {
|
||||
startDecodingReplaceWithStream()
|
||||
}
|
||||
// Push the buffer so far
|
||||
transform.push(replaceWithBuffer)
|
||||
if (!replaceWith.readableEnded) {
|
||||
replaceWith
|
||||
.on('data', () => {
|
||||
transform.push(replaceWithNewChunk)
|
||||
})
|
||||
.once('end', () => {
|
||||
transform.push(replaceWithNewChunk)
|
||||
resolve()
|
||||
})
|
||||
replaceWith.resume()
|
||||
} else {
|
||||
resolve()
|
||||
}
|
||||
})
|
||||
}
|
||||
break
|
||||
}
|
||||
default: {
|
||||
throw new Error("This shouldn't happen.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const foundMatch = async () => {
|
||||
await pushReplaceStr()
|
||||
matches++
|
||||
if (matches === limit) {
|
||||
doneReplacing = true
|
||||
}
|
||||
}
|
||||
|
||||
const transform = new Transform({
|
||||
async transform (chunk, encoding, callback) {
|
||||
if (doneReplacing) {
|
||||
callback(undefined, chunk)
|
||||
} else {
|
||||
// Convert to utf-8
|
||||
let chunkStr = stringDecoder.write(chunk)
|
||||
|
||||
/**
|
||||
* This is the end index of the string that we might replace later
|
||||
*/
|
||||
let keepEndIndex
|
||||
// Continue / complete / discard partial matches from previous chunks
|
||||
for (let i = 0; i < partialMatchesFromPrevChunk.length;) {
|
||||
const pastChunkMatchIndex = partialMatchesFromPrevChunk[i]
|
||||
const chunkStrEndIndex = searchStr.length - (unsureBuffer.length - pastChunkMatchIndex)
|
||||
const strToCheck = unsureBuffer.slice(pastChunkMatchIndex) + chunkStr.slice(0, chunkStrEndIndex)
|
||||
if (searchStr.startsWith(strToCheck)) {
|
||||
if (strToCheck.length >= searchStr.length) {
|
||||
// Match completed
|
||||
// Push the previous part of the string that was saved
|
||||
transform.push(unsureBuffer.slice(0, pastChunkMatchIndex))
|
||||
await foundMatch()
|
||||
unsureBuffer = ''
|
||||
partialMatchesFromPrevChunk = []
|
||||
chunkStr = chunkStr.slice(chunkStrEndIndex)
|
||||
} else {
|
||||
// Match continued
|
||||
keepEndIndex = keepEndIndex ?? 0
|
||||
i++
|
||||
}
|
||||
} else {
|
||||
// Match discarded
|
||||
partialMatchesFromPrevChunk.splice(i, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// Check for completed / partial matches in the current chunk
|
||||
let chunkStrIndex = 0
|
||||
while (chunkStrIndex < chunkStr.length) {
|
||||
const strToCheck = chunkStr.slice(chunkStrIndex, chunkStrIndex + searchStr.length)
|
||||
if (searchStr.startsWith(strToCheck)) {
|
||||
if (strToCheck.length === searchStr.length) {
|
||||
// Match completed
|
||||
transform.push(chunkStr.slice(0, chunkStrIndex))
|
||||
await foundMatch()
|
||||
chunkStr = chunkStr.slice(chunkStrIndex + strToCheck.length)
|
||||
chunkStrIndex = 0
|
||||
} else {
|
||||
// Partial match
|
||||
partialMatchesFromPrevChunk.push(chunkStrIndex)
|
||||
keepEndIndex = keepEndIndex ?? chunkStrIndex
|
||||
chunkStrIndex++
|
||||
}
|
||||
} else {
|
||||
// No match
|
||||
if (keepEndIndex === undefined) {
|
||||
// Push the string out right away
|
||||
transform.push(chunkStr.slice(chunkStrIndex, chunkStrIndex + 1))
|
||||
chunkStr = chunkStr.slice(chunkStrIndex + 1)
|
||||
} else {
|
||||
// We are keeping the string
|
||||
chunkStrIndex++
|
||||
}
|
||||
}
|
||||
}
|
||||
// Save the part of the chunk that might be part of a match later
|
||||
unsureBuffer += chunkStr.slice(keepEndIndex)
|
||||
// This is needed
|
||||
callback()
|
||||
}
|
||||
},
|
||||
flush (callback) {
|
||||
// Release the unsureBuffer
|
||||
callback(undefined, unsureBuffer)
|
||||
}
|
||||
})
|
||||
|
||||
return transform
|
||||
}
|
||||
|
||||
export default replace
|
||||
35
node_modules/stream-replace-string/package.json
generated
vendored
Normal file
35
node_modules/stream-replace-string/package.json
generated
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"name": "stream-replace-string",
|
||||
"version": "2.0.0",
|
||||
"description": "Replaces strings in a stream.",
|
||||
"main": "./index.js",
|
||||
"types": "./index.d.ts",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"lint": "standard",
|
||||
"test": "node test.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/ChocolateLoverRaj/stream-replace-string.git"
|
||||
},
|
||||
"keywords": [
|
||||
"string",
|
||||
"replace",
|
||||
"find",
|
||||
"find-and-replace",
|
||||
"stream",
|
||||
"transform"
|
||||
],
|
||||
"author": "Rajas Paranjpe",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/ChocolateLoverRaj/stream-replace-string/issues"
|
||||
},
|
||||
"homepage": "https://github.com/ChocolateLoverRaj/stream-replace-string#readme",
|
||||
"devDependencies": {
|
||||
"baretest": "^2.0.0",
|
||||
"standard": "^17.0.0",
|
||||
"stream-to-string": "^1.2.0"
|
||||
}
|
||||
}
|
||||
64
node_modules/stream-replace-string/test.js
generated
vendored
Normal file
64
node_modules/stream-replace-string/test.js
generated
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
import baretest from 'baretest'
|
||||
import { strictEqual } from 'assert'
|
||||
import replace from './index.js'
|
||||
import { Readable } from 'stream'
|
||||
import streamToString from 'stream-to-string'
|
||||
|
||||
const test = baretest('Tests')
|
||||
|
||||
test('no change', async () => {
|
||||
strictEqual(await streamToString(Readable.from([
|
||||
'paper\n',
|
||||
'pajamas\n',
|
||||
'socks\n'
|
||||
]).pipe(replace('pinapple', 'apple'))), 'paper\npajamas\nsocks\n')
|
||||
})
|
||||
|
||||
test('chunk boundary', async () => {
|
||||
strictEqual(await streamToString(Readable.from([
|
||||
'pap',
|
||||
'aper',
|
||||
'socks'
|
||||
]).pipe(replace('paper', 'stuff that we write on'))), 'pastuff that we write onsocks')
|
||||
})
|
||||
|
||||
test('within chunk boundary', async () => {
|
||||
strictEqual(await streamToString(Readable.from([
|
||||
'red\n',
|
||||
'grey\n',
|
||||
'orange\n'
|
||||
]).pipe(replace('grey', 'gray'))), 'red\ngray\norange\n')
|
||||
})
|
||||
|
||||
test('split over multiple chunks', async () => {
|
||||
strictEqual(await streamToString(Readable.from([
|
||||
'|a',
|
||||
'pp',
|
||||
'le|'
|
||||
]).pipe(replace('apple', 'mela'))), '|mela|')
|
||||
})
|
||||
|
||||
test('split over multiple chunks 2', async () => {
|
||||
strictEqual(await streamToString(Readable.from([
|
||||
'|apple a',
|
||||
'pp',
|
||||
'le|'
|
||||
]).pipe(replace('apple', 'mela'))), '|mela mela|')
|
||||
})
|
||||
|
||||
test('mystery bug #6', async () => {
|
||||
strictEqual(await streamToString(Readable.from([
|
||||
'One two one\n',
|
||||
'One two one\n',
|
||||
'One two one two\n',
|
||||
'One two one two\n'
|
||||
]).pipe(replace('two', 'three'))), [
|
||||
'One three one\n',
|
||||
'One three one\n',
|
||||
'One three one three\n',
|
||||
'One three one three\n'
|
||||
].join(''))
|
||||
})
|
||||
|
||||
const allTestsPassed = await test.run()
|
||||
if (!allTestsPassed) process.exitCode = 1
|
||||
Reference in New Issue
Block a user