taglify 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,120 @@
1
+ # Taglify — keep generated content in sync, everywhere
2
+
3
+ [![npm version](https://img.shields.io/npm/v/taglify)](https://www.npmjs.com/package/taglify)
4
+ [![license](https://img.shields.io/npm/l/taglify)](./LICENSE)
5
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](./tsconfig.json)
6
+ [![CI](https://github.com/jayf0x/taglify/actions/workflows/ci.yml/badge.svg)](https://github.com/jayf0x/taglify/actions/workflows/ci.yml)
7
+
8
+ ![Preview](./assets/preview.png)
9
+
10
+ > ⭐ **Star this [repository](https://github.com/jayf0x/taglify) if you’d like to support its growth.**
11
+
12
+ I was writing bash functions to have my e2e tests output live stats in the
13
+ README of [compress-shader-literals](https://github.com/jayf0x/compress-shader-literals).
14
+ Then I started reaching for the same trick in other tools and thought "why not ship this".
15
+ Enjoy 🤗
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ bun add taglify
21
+ # OR npm / pnpm / yarn ...
22
+ ```
23
+
24
+ ## Example
25
+
26
+ In your README:
27
+
28
+ ```md
29
+ <!-- STATS:START -->
30
+ <!-- STATS:END -->
31
+ ```
32
+
33
+ You build or release flow:
34
+
35
+ ```js
36
+ // scripts/sync-readme.js
37
+ import { taglifyFile } from 'taglify';
38
+
39
+ const coverageSummary = '...';
40
+ taglifyFile('./README.md', { STATS: coverageSummary });
41
+ ```
42
+
43
+ ```json
44
+ // package.json
45
+ {
46
+ "scripts": {
47
+ "prepublishOnly": "bun run scripts/sync-readme.js"
48
+ }
49
+ }
50
+ ```
51
+
52
+ result:
53
+
54
+ ```md
55
+ | Browser | Passed | Failed |
56
+ | ------- | ------ | ------ |
57
+ | Brave | 127 | 0 |
58
+ | Firefox | 127 | 0 |
59
+ | Edge | 0 | 127 |
60
+ ```
61
+
62
+ ---
63
+
64
+ Same idea works in a CI step, a git pre-commit hook, or a cron job.
65
+
66
+ ## API
67
+
68
+ ### `taglifyText(text, tags)`
69
+
70
+ | Param | Type | Description |
71
+ | ------ | ------------------------ | ------------------------------ |
72
+ | `text` | `string` | Source text |
73
+ | `tags` | `Record<string, string>` | Tag name → replacement content |
74
+
75
+ Returns `{ text: string, changed: boolean }`.
76
+
77
+ ### `taglifyFile(filePath, tags)`
78
+
79
+ Reads `filePath`, applies `taglifyText`, and writes back only if the content
80
+ changed.
81
+
82
+ - Returns `boolean` — whether the file was modified.
83
+ - Throws a friendly error (`File not found: <path>`) if the file doesn't
84
+ exist.
85
+
86
+ ---
87
+
88
+ ## Marker format
89
+
90
+ ```html
91
+ <!-- TAG:START -->
92
+ content
93
+ <!-- TAG:END -->
94
+ ```
95
+
96
+ | Rule | Behavior |
97
+ | -------------------------- | ----------------------------------------- |
98
+ | Tag name casing | Case-insensitive, normalized to uppercase |
99
+ | Content between markers | Fully replaced |
100
+ | Markers themselves | Always preserved |
101
+ | Multiple blocks, same tag | All replaced |
102
+ | Tag with no matching block | Skipped |
103
+ | Replacement value type | `string` only |
104
+
105
+ ---
106
+
107
+ ## Roadmap
108
+
109
+ Taglify is a small, deliberately minimal util today. These are ideas for
110
+ where it could go — see [BACKLOG.md](./BACKLOG.md) for the full list.
111
+
112
+ - [ ] Configurable marker syntax and comment styles (`//`, `#`, `/* */`)
113
+ - [ ] Create missing blocks instead of skipping
114
+ - [ ] CLI with dry-run and diff output
115
+ - [ ] Glob / directory processing
116
+ - [ ] Support non-string values, serialized as formatted JSON
117
+
118
+ ## License
119
+
120
+ [MIT](./LICENSE) © [jayF0x](https://github.com/jayf0x)
package/dist/index.cjs ADDED
@@ -0,0 +1,81 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropNames = Object.getOwnPropertyNames;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ function __accessProp(key) {
6
+ return this[key];
7
+ }
8
+ var __toCommonJS = (from) => {
9
+ var entry = (__moduleCache ??= new WeakMap).get(from), desc;
10
+ if (entry)
11
+ return entry;
12
+ entry = __defProp({}, "__esModule", { value: true });
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (var key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(entry, key))
16
+ __defProp(entry, key, {
17
+ get: __accessProp.bind(from, key),
18
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
19
+ });
20
+ }
21
+ __moduleCache.set(from, entry);
22
+ return entry;
23
+ };
24
+ var __moduleCache;
25
+ var __returnValue = (v) => v;
26
+ function __exportSetter(name, newValue) {
27
+ this[name] = __returnValue.bind(null, newValue);
28
+ }
29
+ var __export = (target, all) => {
30
+ for (var name in all)
31
+ __defProp(target, name, {
32
+ get: all[name],
33
+ enumerable: true,
34
+ configurable: true,
35
+ set: __exportSetter.bind(all, name)
36
+ });
37
+ };
38
+
39
+ // src/index.ts
40
+ var exports_src = {};
41
+ __export(exports_src, {
42
+ taglifyText: () => taglifyText,
43
+ taglifyFile: () => taglifyFile
44
+ });
45
+ module.exports = __toCommonJS(exports_src);
46
+ var import_node_fs = require("node:fs");
47
+ var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
48
+ var taglifyText = (text, tags) => {
49
+ let output = text;
50
+ for (const [tagName, replacement] of Object.entries(tags)) {
51
+ const tag = escapeRegExp(tagName.toUpperCase());
52
+ const blockRegex = new RegExp(`<!-- ${tag}:START -->(.*?)<!-- ${tag}:END -->`, "gis");
53
+ output = output.replace(blockRegex, (_match, block) => {
54
+ const lineEnding = block.includes(`\r
55
+ `) ? `\r
56
+ ` : `
57
+ `;
58
+ return `<!-- ${tag}:START -->${lineEnding}${replacement}${lineEnding}<!-- ${tag}:END -->`;
59
+ });
60
+ }
61
+ return {
62
+ text: output,
63
+ changed: output !== text
64
+ };
65
+ };
66
+ var taglifyFile = (filePath, tags) => {
67
+ let text;
68
+ try {
69
+ text = import_node_fs.readFileSync(filePath, "utf8");
70
+ } catch (error) {
71
+ if (error.code === "ENOENT") {
72
+ throw new Error(`File not found: ${filePath}`);
73
+ }
74
+ throw error;
75
+ }
76
+ const result = taglifyText(text, tags);
77
+ if (result.changed) {
78
+ import_node_fs.writeFileSync(filePath, result.text, "utf8");
79
+ }
80
+ return result.changed;
81
+ };
@@ -0,0 +1,20 @@
1
+ export interface TaglifyResult {
2
+ text: string;
3
+ changed: boolean;
4
+ }
5
+ /**
6
+ * Replaces content between HTML comment markers.
7
+ *
8
+ * Example:
9
+ * <!-- BADGES:START -->
10
+ * old content
11
+ * <!-- BADGES:END -->
12
+ */
13
+ export declare const taglifyText: (text: string, tags: Record<string, string>) => TaglifyResult;
14
+ /**
15
+ * Applies tag replacements to a file.
16
+ *
17
+ * Returns true if the file was modified.
18
+ * Throws a friendly error if the file does not exist.
19
+ */
20
+ export declare const taglifyFile: (filePath: string, tags: Record<string, string>) => boolean;
package/dist/index.js ADDED
@@ -0,0 +1,38 @@
1
+ // src/index.ts
2
+ import { readFileSync, writeFileSync } from "node:fs";
3
+ var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4
+ var taglifyText = (text, tags) => {
5
+ let output = text;
6
+ for (const [tagName, replacement] of Object.entries(tags)) {
7
+ const tag = escapeRegExp(tagName.toUpperCase());
8
+ const blockRegex = new RegExp(`<!-- ${tag}:START -->(.*?)<!-- ${tag}:END -->`, "gis");
9
+ output = output.replace(blockRegex, (_match, block) => {
10
+ const lineEnding = block.includes("\r\n") ? "\r\n" : "\n";
11
+ return `<!-- ${tag}:START -->${lineEnding}${replacement}${lineEnding}<!-- ${tag}:END -->`;
12
+ });
13
+ }
14
+ return {
15
+ text: output,
16
+ changed: output !== text
17
+ };
18
+ };
19
+ var taglifyFile = (filePath, tags) => {
20
+ let text;
21
+ try {
22
+ text = readFileSync(filePath, "utf8");
23
+ } catch (error) {
24
+ if (error.code === "ENOENT") {
25
+ throw new Error(`File not found: ${filePath}`);
26
+ }
27
+ throw error;
28
+ }
29
+ const result = taglifyText(text, tags);
30
+ if (result.changed) {
31
+ writeFileSync(filePath, result.text, "utf8");
32
+ }
33
+ return result.changed;
34
+ };
35
+ export {
36
+ taglifyFile,
37
+ taglifyText
38
+ };
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "taglify",
3
+ "version": "1.0.1",
4
+ "description": "Synchronize generated content inside marker blocks in any text file.",
5
+ "keywords": [
6
+ "markers",
7
+ "codegen",
8
+ "sync",
9
+ "readme",
10
+ "text"
11
+ ],
12
+ "type": "module",
13
+ "private": false,
14
+ "sideEffects": false,
15
+ "main": "./dist/index.cjs",
16
+ "module": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "files": [
19
+ "dist"
20
+ ],
21
+ "exports": {
22
+ ".": {
23
+ "types": "./dist/index.d.ts",
24
+ "import": "./dist/index.js",
25
+ "require": "./dist/index.cjs"
26
+ }
27
+ },
28
+ "devDependencies": {
29
+ "@size-limit/file": "^11.0.0",
30
+ "@trivago/prettier-plugin-sort-imports": "^5.0.0",
31
+ "@types/bun": "latest",
32
+ "@types/node": "^22.20.1",
33
+ "esbuild": "^0.24.0",
34
+ "prettier": "^3.0.0",
35
+ "size-limit": "^11.0.0",
36
+ "typescript": "^5.6.0"
37
+ },
38
+ "license": "MIT",
39
+ "author": "jayF0x",
40
+ "engines": {
41
+ "node": ">=18"
42
+ },
43
+ "scripts": {
44
+ "build": "rm -rf dist && esbuild src/index.ts --bundle --outfile=dist/index.js --platform=node --format=esm && bun build src/index.ts --outdir dist --entry-naming index.cjs --target node --format cjs && tsc",
45
+ "typecheck": "tsc -p tsconfig.json --noEmit",
46
+ "test": "bun test",
47
+ "test:run": "bun test",
48
+ "format": "prettier --write .",
49
+ "format:check": "prettier --check .",
50
+ "npm:deploy": "bash ./scripts/publish-npm.sh",
51
+ "rm:stalepr": "git branch -vv | grep '\\[gone\\]' | awk '{print $1}' | xargs git branch -D",
52
+ "size": "size-limit",
53
+ "rkit:pull": "bunx repokit pull",
54
+ "rkit:push": "bunx repokit push"
55
+ },
56
+ "repository": {
57
+ "type": "git",
58
+ "url": "git+https://github.com/jayf0x/taglify.git"
59
+ },
60
+ "homepage": "https://github.com/jayf0x/taglify#readme",
61
+ "bugs": {
62
+ "url": "https://github.com/jayf0x/taglify/issues"
63
+ }
64
+ }