UNPKG

replacer-util

Version:

Find and replace strings or template outputs in text files (CLI tool designed for use in npm package.json scripts)

302 lines (250 loc) 16.9 kB
# replacer-util <img src=https://centerkey.com/graphics/center-key-logo.svg align=right width=200 alt=logo> _Find and replace strings, regex patterns, or template outputs in text files (CLI tool designed for use in npm package.json scripts)_ [![License:MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://github.com/center-key/replacer-util/blob/main/LICENSE.txt) [![npm](https://img.shields.io/npm/v/replacer-util.svg)](https://www.npmjs.com/package/replacer-util) [![Build](https://github.com/center-key/replacer-util/actions/workflows/run-spec-on-push.yaml/badge.svg)](https://github.com/center-key/replacer-util/actions/workflows/run-spec-on-push.yaml) **replacer-util** searches for text to substitute with a replacement string or with values from your project's **package.json** file, such as the project version number.&nbsp; It can also insert path metadata and concatenate output to generate content such as an HTML file of `<a>` links.&nbsp; **LiquidJS** powers the template outputs and enables **replacer-util** to act as a static site generator complete with filter formatters and `render` tags for including partials.&nbsp; <img src=https://raw.githubusercontent.com/center-key/replacer-util/main/screenshot.png width=800 alt=screenshot> ## A) Setup Install package for node: ```shell $ npm install --save-dev replacer-util ``` ## B) Usage ### 1. Synopsis ``` replacer [SOURCE] [TARGET] ``` Parameters: * The **first** parameter is the *source* folder or file. * The **second** parameter is the *target* folder. ### 2. npm package.json scripts Run `replacer` from the `"scripts"` section of your **package.json** file. Example **package.json** scripts: ```json "scripts": { "build-web": "replacer src/web --ext=.html dist/website", "poetry": "replacer poems --find=human --replacement=robot dystopian-poems" }, ``` In addition to the `--find` and `--replacement` CLI flags, template outputs in the source files will be replaced with their corresponding template variable values.&nbsp; The template variable `package` points to the **package.json** object, enabling `{{package.version}}` in the source file to be replaced with the project's version number. ### 3. Command-line npx Example terminal commands: ```shell $ npm install --save-dev replacer-util $ npx replacer src/web ext=.html docs/api-manual ``` You can also install **replacer-util** globally (`--global`) and then run it anywhere directly from the terminal. ### 4. CLI flags Command-line flags: | Flag | Description | Value | | ----------------- | ----------------------------------------------------- | ---------- | | `--cd` | Change working directory before starting search. | **string** | | `--concat` | Merge all files into one file in the target folder. | **string** | | `--content` | String to be used instead of the input file contents. | **string** | | `--exclude` | Skip files containing the string in their path. | **string** | | `--ext` | Filter files by file extension, such as `.js`.<br>Use a comma to specify multiple extensions. | **string** | | `--find` | Text to search for in the source input files. | **string** | | `--header` | Prepend a line of text to each file. | **string** | | `--no-liquid` | Turn off LiquidJS templating. | N/A | | `--no-source-map` | Remove any `sourceMappingURL` comment directives. | N/A | | `--non-recursive` | Only read files in the source folder not subfolders. | N/A | | `--note` | Place to add a comment only for humans. | **string** | | `--quiet` | Suppress informational messages. | N/A | | `--regex` | Pattern to search for in the source input files. | **string** | | `--rename` | New output filename. | **string** | | `--replacement` | Text to insert into the target output files. | **string** | | `--summary` | Only print out the single line summary message. | N/A | | `--title-sort` | Ignore leading articles in `--concat` filenames. | N/A | | `--virtual-input` | Do not read any files, use `--content` instead. | N/A | To avoid issues on the command line, problematic characters can be _"escaped"_ with safe strings as listed below. Escape characters: | Character | Safe stand-in string | | --------- | -------------------- | | `'` | `{{apos}}` | | `!` | `{{bang}}` | | `}` | `{{close-curly}}` | | `=` | `{{equals}}` | | `>` | `{{gt}}` | | `#` | `{{hash}}` | | `<` | `{{lt}}` | | `{` | `{{open-curly}}` | | `\|` | `{{pipe}}` | | `"` | `{{quote}}` | | `;` | `{{semi}}` | | ` ` | `{{space}}` | Alternatively, escaping for the command line can be avoided with [macros](#9-macros) you define in your project's **package.json** file (see documentation below). ### 5. Examples - `replacer src build`<br> Recursively copies all the files in the **src** folder to the **build** folder using the data in **package.json** to update the template outputs. - `replacer src/docs --ext=.md --find=Referer --replacement=Referrer output/fixed`<br> Fixes spelling error in markdown files. - `replacer src/docs --ext=.md --find=Referer --replacement=Referrer --no-liquid output/fixed`<br> Same as previous example but disables LiquidJS templating (useful in case source files contain characters inadvertently interpreted at templating commands). - `replacer web '--find=cat dog' '--replacement= cat{{pipe}}dog ' target`<br> `replacer web --find=cat\ dog --replacement=\ cat{{pipe}}dog\ target`<br> `replacer web --find=cat{{space}}dog --replacement={{space}}cat{{pipe}}dog{{space}} target`<br> Replaces all occurances of the string `'cat dog'` with `' cat|dog '` (note the _3 different_ ways to _"escape"_ a space character). - `replacer src --ext=.js --no-liquid --concat=bundle.js build`<br> Merges all JS files into **build/bundle.js**. - `replacer app/widgets --ext=.less --content=@import{{space}}{{quote}}{{file.dir}}/{{file.name}}{{quote}}{{semi}} --concat=widgets.less app/style`<br> Creates a single LESS file that imports the LESS files of every widget component. - `replacer app/widgets --ext=.less --content={{macro:less-import}} --concat=widgets.less app/style`<br> Identical to the previous command assuming the `less-import` macro is properly defined in the **package.json** file. - `replacer src --summary build`<br> Displays the summary informaion but not informaion about individual files copied. - `replacer src --regex=/^--/gm --replacement=🥕🥕🥕 build`<br> Finds double dashes at the start of lines and replace them with 3 carrots.&nbsp; Note the `g` and `m` [regex options](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#advanced_searching_with_flags). - `replacer build/my-app.js --rename=my-app.browser.js build`<br> Copies **my-app.js** to **my-app.browser.js** without making and changes. - `replacer src/web --ext=.html --rename=index.html dist/website`<br> Renames all HTML files, such as **src/web/about/about.html**, to **index.html** while preserving the folder structure. - `replacer --cd=spec fixtures/web --find=insect --replacement=A.I. target/web`<br> Removes all insects.&nbsp; See: [fixtures/web/mock1.html](spec/fixtures/web/mock1.html) and [target/web/mock1.html](spec/target/exclude/mock1.html) - `replacer node_modules/chart.js/dist/chart.umd.js --no-source-map build/1-pre/libs`<br> Removes the `//# sourceMappingURL=chart.umd.js.map` line at the bottom of the **Chart.js** distribution file. - `replacer . docs --rename=robots.txt --virtual-input --content={{hash}}{{space}}Allow{{space}}bots{{bang}}`<br> Creates a **robots.txt** file with the line `# Allow bots!` in the **docs** folder. - `replacer --virtual-input . --rename=CNAME docs --content=example.com`<br> Creates a **CNAME** file with the line `example.com` in the **docs** folder. For examples of using `replacer` as part of front-end build process, check out the `"runScriptsConfig"` section of:<br> https://github.com/dna-dom/data-dashboard/blob/main/package.json > [!NOTE] > _Single quotes in commands are normalized so they work cross-platform and avoid the errors often encountered on Microsoft Windows._ ### 6. Template outputs and filter formatters The source files are processed by LiquidJS, so you can use [template outputs](https://liquidjs.com/tutorials/intro-to-liquid.html#Outputs) and [filter formatters](https://liquidjs.com/filters/overview.html).&nbsp; Custom variables are created with the [assign](ttps://liquidjs.com/tags/assign.html) tag. Three special variables are available by default: * `file` ([path](https://nodejs.org/api/path.html#pathparsepath) and date information about the source file) * `package` (values from your project's **package.json** file) * `webRoot` (relative path to root folder: `.`, `..`, `../..`, `../../..`, etc.) For example, a TypeScript file with the lines: ```typescript const msg1: string = 'The current release of {{package.name | upcase}} is v{{package.version}}.'; const msg2: string = 'This file is: {{file.base}}'; ``` will be transformed into something like: ```typescript const msg1: string = 'The current release of MY-COOL-NPM-PACKAGE is v1.2.3.'; const msg2: string = 'This file is: my-app.ts'; ``` Example outputs and formatters: | Source file text | Example output value | Note | | ------------------------------ | -------------------------- | ---------------------------------------------- | | `{{package.name}}` | `my-project` | Value from `name` field in **package.json** | | `{{package.version}}` | `3.1.4` | Value from `version` field in **package.json** | | `{{package.version\|size}}` | `5` | Length of the version number string | | `{{file.path}}` | `src/web/sign-in.html` | Full path to source file | | `{{file.folder}}` | `web` | Name of parent folder of the source file | | `{{file.base}}` | `sign-in.html` | Source filename with the file extension | | `{{file.name}}` | `sign-in` | Source filename without the file extension | | `{{file.ext}}` | `.html` | File extension of the source file | | `{{file.modified}}` | `April 7, 2030` | Formatted date of when file was last modifiled | | `{{file.date\|date:"%A"}}` | `Sunday` | Date object for when file was last modifiled | | `{{file.timestamp}}` | `2030-04-07T07:01:36.037Z` | Value for the `datetime` attribute of `<time>` | | `<a href={{webRoot}}>Home</a>` | `<a href=../..>Home</a>` | Link is relative to the source folder | | `{{"now"\|date:"%Y-%m-%d"}}` | `2024-01-21` | Build date timestamp | | `{{myVariable\|upcase}}` | `DARK MODE` | Custom variable set with: `{% assign myVariable = 'dark mode' %}` | > [!NOTE] > _Use the `--no-liquid` flag if characters in your source files are inadvertently being interpreted as templating commands and causing errors._ ### 7. SemVer Your project's dependancies declared in **package.json** can be used to automatically keep your CDN links up-to-date. Three special filter formatters are available to support Semantic Versioning (SemVer): * `version` * `major-version` * `minor-version` For example, if your project declares a dependency of `^3.1.4` for **fetch-json**, the line: ```html <script src=https://cdn.jsdelivr.net/npm/fetch-json@{{package.dependencies.fetch-json|minor-version}}/dist/fetch-json.min.js></script> ``` will be transformed into: ```html <script src=https://cdn.jsdelivr.net/npm/fetch-json@3.5/dist/fetch-json.min.js></script> ``` > [!WARNING] > _Some package names contain one or more of the characters `@`, `/`, and `.`, and these 3 characters are not supported for replacement.&nbsp; Use `-` in the package name instead._ For example, CDN links for the packages `"@fortawesome/fontawesome-free"` and `"highlight.js"` can be created with: ```html <link rel=stylesheet href=https://cdn.jsdelivr.net/npm/@fortawesome/fontawesome-free@{{package.devDependencies.-fortawesome-fontawesome-free|version}}/css/all.min.css> <script src=https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@{{package.devDependencies.highlight-js|version}}/build/highlight.min.js></script> ``` ### 8. Last Updated The special `file` varaible can be leveraged to create a "Last Updated" field that is automatically populated with the date the source file was most recently modified. For example, an HTML file with following lines: ```html <header> <h1>My Blog</h1> <h2>🚀 How to Watch a Rocket Launch 🚀</h2> <time datetime={{file.timestamp}}>{{file.modified}}</time> </header> ``` will be transformed into something similar to: ```html <header> <h1>My Blog</h1> <h2>🚀 How to Watch a Rocket Launch 🚀</h2> <time datetime=2030-04-07T07:01:36.037Z>April 7, 2030</time> </header> ``` **Note:**<br> Be aware that `git checkout` deliberately resets file modification dates (`mtime`).&nbsp; If you use GitHub Actions to publish your website, you'll need to restore file modification dates with a script or tool.&nbsp; One tool that does this is [git-restore-mtime](https://github.com/marketplace/actions/git-restore-mtime): ```yaml steps: - uses: actions/checkout@v6 with: ref: main #fetch the full git history of the "main" fetch-depth: 0 #branch for git-restore-mtime-action below - uses: chetan/git-restore-mtime-action@v2 - uses: actions/setup-node@v6 - run: npm install - run: npm run publish ``` For a working example, see: [publish-website.yaml](https://github.com/center-key/think-metric/blob/main/.github/workflows/publish-website.yaml) ### 9. Macros Define macros in your project's **package.json** file and use them to make commands more compact and readable. Example: ```json "cliConfig": { "macros": { "less-import": "@import {{quote}}{{file.dir}}/{{file.name}}{{quote}};" } }, "scripts": { "less-imports": "replacer app/widgets --ext=.less --content={{macro:less-import}} --concat=widgets.less app/style" }, ``` ## C) Application Code Even though **replacer-util** is primarily intended for build scripts, the package can be used programmatically in ESM and TypeScript projects. Example: ``` typescript import { replacer } from 'replacer-util'; const options = { extensions: ['.html', '.js'] }; const results = replacer.transform('src/web', 'docs/api-manual', options); console.info('Number of files copied:', results.count); ``` See the **TypeScript Declarations** at the top of [replacer.ts](src/replacer.ts) for documentation. <br> --- **CLI Build Tools for package.json** - 🎋 [add-dist-header](https://github.com/center-key/add-dist-header):&nbsp; _Prepend a one-line banner comment (with license notice) to distribution files_ - 📄 [copy-file-util](https://github.com/center-key/copy-file-util):&nbsp; _Copy or rename a file with optional package version number_ - 📂 [copy-folder-util](https://github.com/center-key/copy-folder-util):&nbsp; _Recursively copy files from one folder to another folder_ - 🪺 [recursive-exec](https://github.com/center-key/recursive-exec):&nbsp; _Run a command on each file in a folder and its subfolders_ - 🔍 [replacer-util](https://github.com/center-key/replacer-util):&nbsp; _Find and replace strings or template outputs in text files_ - 🔢 [rev-web-assets](https://github.com/center-key/rev-web-assets):&nbsp; _Revision web asset filenames with cache busting content hash fingerprints_ - 🚆 [run-scripts-util](https://github.com/center-key/run-scripts-util):&nbsp; _Organize npm package.json scripts into groups of easy to manage commands_ - 🚦 [w3c-html-validator](https://github.com/center-key/w3c-html-validator):&nbsp; _Check the markup validity of HTML files using the W3C validator_ Feel free to submit questions at:<br> [github.com/center-key/replacer-util/issues](https://github.com/center-key/replacer-util/issues) [MIT License](LICENSE.txt)