update-file-content
Version:
Utility for executing RegEx replacement on files, powered by stream.
391 lines (364 loc) • 11.6 kB
TypeScript
/// <reference types="node" />
import { Writable, Readable } from "stream";
type GlobalLimit = number;
type LocalLimit = number;
interface BasicReplaceOption {
/**
* Correspondence: String.prototype.replace's 2nd argument.
*
* Replaces the according text for a given match, a string or
* a function that returns the replacement text can be passed.
*
* Special replacement patterns (parenthesized capture group placeholders)
* are well supported.
*
* For a partial replacement, $& (also the 1st supplied value to replace
* function) and $1 (the 2nd param passed) always have the same value,
* supplying the matched substring in the parenthesized capture group
* you specified.
*/
replacement?: string | ((wholeMatch: string, ...args: string[]) => string);
/**
* Perform a full replacement or not.
*
* A RegExp search without capture groups or a search in string will be
* treated as a full replacement silently.
*/
isFullReplacement?: Boolean;
/**
* Only valid for a string replacement.
*
* Disable placeholders in replacement or not. Processed result shall be
* exactly the same as the string replacement if set to true.
*
* Default: false
*/
disablePlaceholders?: Boolean;
/**
* Apply restriction on certain search's maximum executed times.
*
* Upon reaching the limit, if option `truncate` is falsy (false by default),
* underlying transform stream will become a transparent passThrough stream.
*
* Default: Infinity. 0 is considered as Infinity for this option.
*/
limit?: LocalLimit;
/**
* Observe a certain search's executed times, remove that search right
* after upper limit reached.
*
* Default: Infinity. 0 is considered as Infinity for this option.
*/
maxTimes?: number;
}
interface SearchAndReplaceOption extends BasicReplaceOption {
/**
* Correspondence: `String.prototype.replaceAll`'s 1st argument.
*
* Accepts a literal string or a RegExp object.
*
* Will replace all occurrences by converting input into a global RegExp
* object, which means that the according replacement might be invoked
* multiple times for each full match to be replaced.
*
* Every `search` and `replacement` not arranged in pairs is silently
* discarded in `options`, while in `options.replace` that will result in
* an error thrown.
*/
search?: string | RegExp;
}
interface MatchAndReplaceOption extends BasicReplaceOption {
/**
* Alias for options.search.
*
* Correspondence: `String.prototype.replaceAll`'s 1st argument.
*
* Accepts a literal string or a RegExp object.
*
* Will replace all occurrences by converting input into a global RegExp
* object, which means that the according replacement might be invoked
* multiple times for each full match to be replaced.
*
* Every `match` and `replacement` not arranged in pairs is silently
* discarded in `options`, while in `options.replace` that will result in
* an error thrown.
*/
match?: string | RegExp;
}
interface MultipleReplacementOption {
/**
* Apply restriction on the maximum count of every search's executed times.
*
* Upon reaching the limit, if option `truncate` is falsy (false by default),
* underlying transform stream will become a transparent passThrough stream.
*
* Default: Infinity. 0 is considered as Infinity for this option.
*/
limit?: GlobalLimit;
/**
* Should be an array of { [ "match" | "search" ], "replacement" } pairs.
*
* Possible `search|match` and `replacement` pair in `options` scope will be
* prepended to `options.replace` array, if both exist.
*/
replace?: Array<SearchAndReplaceOption | MatchAndReplaceOption>;
}
// An interface can only extend an object type or intersection of object types with statically known members.
type ReplaceOptions = MultipleReplacementOption & MatchAndReplaceOption & SearchAndReplaceOption;
interface BasicOptions extends ReplaceOptions {
/**
* Correspondence: String.prototype.split's 1nd argument.
*
* Accepts a literal string or a RegExp object.
*
* Used by underlying transform stream to split upstream data into separate
* to-be-processed parts.
*
* String.prototype.split will implicitly call `toString` on non-string &
* non-regex & non-void values.
*
* Specify `null` or `undefined` to process upstream data as a whole.
*
* Default: /(?<=\r?\n)/. Line endings following lines.
*/
separator?: string | RegExp | null;
/**
* Correspondence: String.prototype.join's 1nd argument, though a function
* is also acceptable.
*
* You can specify a literal string or a function that returns the post-processed
* part.
*
* Example function for appending a CRLF: part => part.concat("\r\n");
*
* Default: part => part
*/
join?: string | ((part: string) => string) | null;
/**
* Correspondence: encoding of Node.js Buffer.
*
* If specified, then processed and joined strings will be encoded to buffers
* with that encoding.
*
* Node.js currently supportes following options:
* "ascii" | "utf8" | "utf-8" | "utf16le" | "ucs2" | "ucs-2" | "base64" | "latin1" | "binary" | "hex"
* Default: "utf8".
*/
encoding?: BufferEncoding;
/**
* Correspondence: encodings of WHATWG Encoding Standard TextDecoder.
*
* Accept a specific character encoding, like utf-8, iso-8859-2, koi8, cp1261,
* gbk, etc for decoding the input raw buffer.
*
* This option only makes sense when no encoding is assigned and stream data are
* passed as Buffer objects (that is, haven't done something like
* readable.setEncoding('utf8'));
*
* Example: updateFileContent({
* from: createReadStream("gbk.txt"),
* to: createWriteStream("utf8.txt"),
* decodeBuffers: "gbk"
* });
*
* Some encodings are only available for Node embedded the entire ICU (full-icu).
* See https://nodejs.org/api/util.html#util_class_util_textdecoder.
*
* Default: "utf8".
*/
decodeBuffers?: string;
/**
* Truncating the rest or not when limits reached.
*
* Default: false.
*/
truncate?: Boolean;
/**
* The maximum size of the line buffer.
*
* A line buffer is the buffer used for buffering the last incomplete substring
* when dividing chunks (typically 64 KiB) by options.separator.
*
* Default: Infinity.
*/
maxLength?: number;
/**
* Correspondence: readableObjectMode option of Node.js stream.Transform
*
* Options writableObjectMode and objectMode are not supported.
*
* Default: Infinity.
*/
readableObjectMode?: boolean;
/**
* A post-processing function that consumes transformed strings and returns a
* string or a Buffer. This option has higher priority over option `join`.
*
* If readableObjectMode is enabled, any object accepted by Node.js objectMode
* streams can be returned.
*/
postProcessing: (part: string, isLastPart: boolean) => any
}
type WritableOrVoid = Writable | void;
// updateFileContent - file
interface UpdateFileOptions extends BasicOptions {
/**
* Path to the file.
*/
file: string;
/**
* Correspondence: fs.read's 4th argument - position
*
* The location where to begin reading data from the file.
*
* Default: 0
*/
readStart?: number;
/**
* Correspondence: fs.write's 4th argument - position
*
* The offset from the beginning of the file where the substituted data should be written.
*
* writeStart should be smaller or equal to readStart.
*
* Default: 0
*/
writeStart?: number;
}
// updateFileContent - TransformReadable
interface TransformReadableOptions<T> extends BasicOptions {
/**
* A Readable stream.
*/
from: Readable;
/**
* A Writable stream.
*/
to: T;
}
interface TransformReadableOptionsAlias<T> extends BasicOptions {
/**
* Alias of `from`.
*/
readableStream: Readable;
/**
* Alias of `to`.
*/
writableStream: T;
}
/**
* update file content, or transform/transcode/truncate streams
*
* P.S. TS doesn't support overloading functions with same
* number of parameters, so a huge union is there 😀
*/
export declare function updateFileContent<T extends WritableOrVoid>(
options: UpdateFileOptions | TransformReadableOptions<T> | TransformReadableOptionsAlias<T>
): Promise<T>;
// updateFiles - files
interface UpdateFilesOptions extends BasicOptions {
/**
* A array of filepaths.
*/
files: string[];
/**
* Correspondence: fs.read's 4th argument - position
*
* The location where to begin reading data from the file.
*
* Applies to all files.
*
* Default: 0
*/
readStart?: number;
/**
* Correspondence: fs.write's 4th argument - position
*
* The offset from the beginning of the file where the substituted data should be written.
*
* Applies to all files.
*
* writeStart should be smaller or equal to readStart.
*
* Default: 0
*/
writeStart?: number;
}
// updateFiles - readables -> writable
interface MultipleReadablesToWritableOptions<T> extends BasicOptions {
/**
* An array of Readable streams.
*/
from: Array<Readable>;
/**
* A Writable stream.
*/
to: T;
/**
* Concatenate results of transformed Readables with the input value.
*
* Accepts a literal string or a Buffer.
*
* Default: ""
*/
contentJoin: string | Buffer;
}
interface MultipleReadablesToWritableOptionsAlias<T> extends BasicOptions {
/**
* Alias of `from`.
* An array of Readable streams
*/
readableStreams: Array<Readable>;
/**
* Alias of `to`.
* A Writable stream.
*/
writableStream: T;
/**
* Concatenate results of transformed Readables with the input value.
*
* Accepts a literal string or a Buffer.
*
* option.encoding will be passed along with contentJoin to Writable.write
*
* Default: ""
*/
contentJoin: string | Buffer;
}
// updateFiles - readable -> writables
interface ReadableToMultipleWritablesOptions<T> extends BasicOptions {
/**
* A Readable stream source.
*/
from: Readable;
/**
* An array of Writable streams, preferably being the same type.
*/
to: Array<T>;
}
interface ReadableToMultipleWritablesOptionsAlias<T> extends BasicOptions {
/**
* Alias of `from`.
* A Readable stream source.
*/
readableStream: Readable;
/**
* Alias of `to`.
* An array of Writable streams, preferably being the same type.
*/
writableStreams: Array<T>;
}
/**
* update files, or transform streams and pipe/tee/combine them here and there.
*
* `from` `to` cannot be arrays at the same time.
*
* P.S. TS doesn't support overloading functions with same
* number of parameters, so a huge union is there 😀
*/
export declare function updateFiles<T extends WritableOrVoid>(
options:
UpdateFilesOptions |
MultipleReadablesToWritableOptionsAlias<T> | MultipleReadablesToWritableOptions<T> |
ReadableToMultipleWritablesOptions<T> | ReadableToMultipleWritablesOptionsAlias<T>
): Promise< T[] | T >;