Skip to content

Commit 8af76d6

Browse files
committed
Minifier: Introduce @herb-tools/minifier package
1 parent aece0f8 commit 8af76d6

File tree

16 files changed

+1248
-12
lines changed

16 files changed

+1248
-12
lines changed

javascript/packages/minifier/README.md

Lines changed: 133 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
# Herb Minifier <Badge type="info" text="coming soon" />
1+
# Herb Minifier
22

33
**Package:** [`@herb-tools/minifier`](https://www.npmjs.com/package/@herb-tools/minifier)
44

55
---
66

7-
HTML+ERB template minification.
7+
HTML+ERB template minification. Removes non-significant whitespace while preserving whitespace in `<pre>` and `<code>` tags.
88

99
## Installation
1010

@@ -26,14 +26,139 @@ bun add @herb-tools/minifier
2626
```
2727
:::
2828

29-
<!-- ### Usage -->
29+
## Usage
3030

31-
<!-- TODO -->
31+
### Basic Usage
3232

33-
<!-- #### Configuration Options -->
33+
```typescript
34+
import { Herb } from '@herb-tools/node-wasm'
35+
import { Minifier } from '@herb-tools/minifier'
3436

35-
<!-- TODO -->
37+
// Create and initialize minifier
38+
const minifier = new Minifier(Herb)
39+
await minifier.initialize()
3640

37-
<!-- #### CLI Usage -->
41+
const template = `
42+
<div class="container">
43+
<h1>Hello World</h1>
44+
<p>This is a test</p>
45+
</div>
46+
`
3847

39-
<!-- TODO -->
48+
const minified = minifier.minifyString(template)
49+
// Result: '<div class="container"><h1>Hello World</h1><p>This is a test</p></div>'
50+
```
51+
52+
### Minifying AST Nodes
53+
54+
You can also minify AST nodes directly:
55+
56+
```typescript
57+
import { Herb } from '@herb-tools/node-wasm'
58+
import { Minifier } from '@herb-tools/minifier'
59+
60+
const minifier = new Minifier(Herb)
61+
await minifier.initialize()
62+
63+
const parseResult = Herb.parse(template, { track_whitespace: true })
64+
const minifiedNode = minifier.minify(parseResult.value)
65+
```
66+
67+
### Whitespace Preservation
68+
69+
The minifier preserves whitespace in `<pre>` and `<code>` tags:
70+
71+
```typescript
72+
const template = `
73+
<div>
74+
<pre>
75+
Line 1
76+
Line 2
77+
Line 3
78+
</pre>
79+
</div>
80+
`
81+
82+
const minified = minifier.minifyString(template)
83+
// Result: '<div><pre>\n Line 1\n Line 2\n Line 3\n </pre></div>'
84+
```
85+
86+
### ERB Support
87+
88+
The minifier works seamlessly with ERB templates:
89+
90+
```typescript
91+
const template = `
92+
<div>
93+
<% if admin? %>
94+
<span>Admin</span>
95+
<% else %>
96+
<span>User</span>
97+
<% end %>
98+
</div>
99+
`
100+
101+
const minified = minifier.minifyString(template)
102+
// Result: '<div><%if admin?%><span>Admin</span><%else%><span>User</span><%end%></div>'
103+
```
104+
105+
## API Reference
106+
107+
### `Minifier`
108+
109+
The main minifier class.
110+
111+
#### Constructor
112+
113+
```typescript
114+
new Minifier(herb: HerbBackend)
115+
```
116+
117+
**Parameters:**
118+
- `herb`: The Herb backend instance
119+
120+
#### Methods
121+
122+
##### `initialize()`
123+
124+
```typescript
125+
async initialize(): Promise<void>
126+
```
127+
128+
Initializes the minifier by loading the Herb backend. Must be called before using minification methods.
129+
130+
##### `minifyString()`
131+
132+
```typescript
133+
minifyString(template: string): string
134+
```
135+
136+
Minifies an HTML+ERB template string.
137+
138+
**Parameters:**
139+
- `template`: The template string to minify
140+
141+
**Returns:** The minified template string
142+
143+
##### `minify()`
144+
145+
```typescript
146+
minify<T extends Node>(node: T): T
147+
```
148+
149+
Minifies an HTML+ERB AST node.
150+
151+
**Parameters:**
152+
- `node`: The AST node to minify
153+
154+
**Returns:** The minified AST node
155+
156+
## Features
157+
158+
- ✅ Removes non-significant whitespace
159+
- ✅ Preserves whitespace in `<pre>` and `<code>` tags
160+
- ✅ Supports nested preserve-whitespace tags
161+
- ✅ Works with ERB templates
162+
- ✅ Preserves HTML attributes
163+
- ✅ Handles self-closing tags
164+
- ✅ Gracefully handles parse errors (returns original template)
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
{
2+
"name": "@herb-tools/minifier",
3+
"version": "0.7.5",
4+
"description": "HTML+ERB template minification",
5+
"license": "MIT",
6+
"homepage": "https://herb-tools.dev",
7+
"bugs": "https://github.com/marcoroth/herb/issues/new?title=Package%20%60@herb-tools/minifier%60:%20",
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/marcoroth/herb.git",
11+
"directory": "javascript/packages/minifier"
12+
},
13+
"main": "./dist/index.cjs",
14+
"module": "./dist/index.esm.js",
15+
"require": "./dist/index.cjs",
16+
"types": "./dist/types/index.d.ts",
17+
"scripts": {
18+
"build": "yarn clean && rollup -c rollup.config.mjs",
19+
"dev": "rollup -c rollup.config.mjs -w",
20+
"clean": "rimraf dist",
21+
"test": "vitest run",
22+
"test:watch": "vitest --watch",
23+
"prepublishOnly": "yarn clean && yarn build && yarn test"
24+
},
25+
"exports": {
26+
"./package.json": "./package.json",
27+
".": {
28+
"types": "./dist/types/index.d.ts",
29+
"import": "./dist/index.esm.js",
30+
"require": "./dist/index.cjs",
31+
"default": "./dist/index.esm.js"
32+
}
33+
},
34+
"dependencies": {
35+
"@herb-tools/core": "0.7.5",
36+
"@herb-tools/printer": "0.7.5",
37+
"@herb-tools/rewriter": "0.7.5"
38+
},
39+
"files": [
40+
"package.json",
41+
"README.md",
42+
"dist/",
43+
"src/"
44+
]
45+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "@herb-tools/minifier",
3+
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
4+
"sourceRoot": "javascript/packages/minifier/src",
5+
"projectType": "library",
6+
"targets": {
7+
"build": {
8+
"executor": "nx:run-script",
9+
"options": {
10+
"script": "build"
11+
},
12+
"dependsOn": [
13+
"@herb-tools/core:build",
14+
"@herb-tools/printer:build",
15+
"@herb-tools/rewriter:build"
16+
]
17+
},
18+
"test": {
19+
"executor": "nx:run-script",
20+
"options": {
21+
"script": "test"
22+
}
23+
},
24+
"clean": {
25+
"executor": "nx:run-script",
26+
"options": {
27+
"script": "clean"
28+
}
29+
}
30+
},
31+
"tags": []
32+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import typescript from "@rollup/plugin-typescript"
2+
import { nodeResolve } from "@rollup/plugin-node-resolve"
3+
import commonjs from "@rollup/plugin-commonjs"
4+
import json from "@rollup/plugin-json"
5+
6+
const external = [
7+
"path",
8+
"url",
9+
"fs",
10+
"module",
11+
"@herb-tools/tailwind-class-sorter"
12+
]
13+
14+
function isExternal(id) {
15+
return (
16+
external.includes(id) ||
17+
external.some((pkg) => id === pkg || id.startsWith(pkg + "/"))
18+
)
19+
}
20+
21+
export default [
22+
// Browser-compatible entry point (core APIs only)
23+
{
24+
input: "src/index.ts",
25+
output: {
26+
file: "dist/index.esm.js",
27+
format: "esm",
28+
sourcemap: true,
29+
},
30+
external: [],
31+
plugins: [
32+
nodeResolve({ preferBuiltins: true }),
33+
commonjs(),
34+
json(),
35+
typescript({
36+
tsconfig: "./tsconfig.json",
37+
declaration: true,
38+
declarationDir: "./dist/types",
39+
rootDir: "src/",
40+
}),
41+
],
42+
},
43+
{
44+
input: "src/index.ts",
45+
output: {
46+
file: "dist/index.cjs",
47+
format: "cjs",
48+
sourcemap: true,
49+
},
50+
external: [],
51+
plugins: [
52+
nodeResolve({ preferBuiltins: true }),
53+
commonjs(),
54+
json(),
55+
typescript({
56+
tsconfig: "./tsconfig.json",
57+
rootDir: "src/",
58+
}),
59+
],
60+
}
61+
]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export { Minifier, minify } from "./minifier.js"
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { ASTRewriter } from "@herb-tools/rewriter"
2+
import { MinifierVisitor } from "./minifier-visitor.js"
3+
4+
import type { RewriteContext } from "@herb-tools/rewriter"
5+
import type { Node } from "@herb-tools/core"
6+
7+
export class MinifyRewriter extends ASTRewriter {
8+
get name() {
9+
return "Minifier"
10+
}
11+
12+
get description() {
13+
return "Minifies HTML+ERB documents by removing non-significant whitespace."
14+
}
15+
16+
rewrite<T extends Node>(node: T, _context?: RewriteContext): T {
17+
const visitor = new MinifierVisitor()
18+
19+
node.accept(visitor)
20+
21+
return node
22+
}
23+
}

0 commit comments

Comments
 (0)