next-single-file 1.0.0 โ†’ 1.0.2

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.
Files changed (3) hide show
  1. package/README.md +80 -38
  2. package/dist/cli.js +9 -6
  3. package/package.json +9 -3
package/README.md CHANGED
@@ -1,39 +1,50 @@
1
- # Next.js Single HTML CLI ๐Ÿš€
1
+ # next-single-file
2
2
 
3
- A powerful CLI tool that transforms a Next.js static export into a **completely self-contained, single HTML file** with hash-based routing. Regex based, zero deps.
4
- ## ๐Ÿ“– How it Works
3
+ [![npm version](https://img.shields.io/npm/v/next-single-file?color=black)](https://www.npmjs.com/package/next-single-file)
4
+ [![npm downloads](https://img.shields.io/npm/dm/next-single-file?color=black)](https://www.npmjs.com/package/next-single-file)
5
+ [![Next.js](https://img.shields.io/badge/Next.js-16-black?logo=next.js)](https://nextjs.org)
6
+ [![Bun](https://img.shields.io/badge/runtime-bun-black?logo=bun)](https://bun.sh)
5
7
 
6
- The tool crawls your `out/` directory, extracts all routes, and bundles them into a single file. It inlines all assets (JS, CSS, Fonts, Images) as Data URIs (base64) and injects a robust hash-based router.
8
+ A CLI tool that transforms a Next.js static export into a **single, self-contained HTML file** with hash-based routing. Regex-based, zero runtime dependencies.
7
9
 
8
- ### ๐Ÿ— Architecture
10
+ ## How it Works
11
+
12
+ The tool parses your `out/` directory, extracts all routes, and bundles everything into one file. All assets (JS, CSS, fonts, images) are inlined as base64 data URIs, and a hash-based router is injected for client-side navigation.
9
13
 
10
14
  ```mermaid
11
15
  graph TD
12
16
  A[Next.js App] -->|next build| B[out/ Directory]
13
- B -->|Parser| C[Asset Map \u0026 Routes]
14
- C -->|Inliner| D[Data URIs \u0026 CSS/JS Bundles]
17
+ B -->|Parser| C[Asset Map & Routes]
18
+ C -->|Inliner| D[Data URIs & Bundles]
15
19
  D -->|Bundler| E[Single index.html]
16
- F[Hash Router Shim] -->|Injected| E
17
-
18
- subgraph "Single HTML File"
19
- E
20
- end
21
-
22
- E -->|Browser| G[Hash-based Navigation]
23
- G -->|#/about| H[DOM Swap via ROUTE_MAP]
20
+ F[Hash Router] -->|Injected| E
21
+ E -->|Browser| G[Hash Navigation]
24
22
  ```
25
23
 
26
- ## ๐Ÿ›  Features
24
+ ## Features
25
+
26
+ - **Self-Contained** โ€” Zero external dependencies. Fonts, images, and scripts are all inlined.
27
+ - **Hash Routing** โ€” Automatically converts path navigation to `#/hash` navigation.
28
+ - **Next.js Compatible** โ€” Supports Geist fonts, Turbopack, and modern Next.js features.
29
+ - **Robust Encoding** โ€” Uses Base64 for the internal route map to prevent minification issues.
30
+ - **Browser Shims** โ€” Polyfills `document.currentScript` and other APIs Next.js expects.
27
31
 
28
- - **Self-Contained**: Zero external dependencies. Fonts, images, and scripts are all inlined.
29
- - **Hash Routing**: Automatically converts path navigation to `#/hash` navigation.
30
- - **Next.js Compatibility**: Supports latest Next.js features like Geist fonts and Turbopack.
31
- - **Robust Escaping**: Uses Base64 encoding for the internal route map to prevent minified JS syntax errors.
32
- - **Shims**: Automatically shims `document.currentScript` and other browser APIs that Next.js expects.
32
+ ## Installation
33
+
34
+ ```bash
35
+ bunx next-single-file --input out --output dist/index.html
36
+ ```
33
37
 
34
- ## ๐Ÿš€ Usage
38
+ Or with npm (requires Bun to be installed):
39
+
40
+ ```bash
41
+ npx next-single-file --input out --output dist/index.html
42
+ ```
43
+
44
+ ## Usage
45
+
46
+ ### 1. Configure Next.js for Static Export
35
47
 
36
- ### 1. Generate Static Export
37
48
  Ensure your `next.config.js` has `output: 'export'`:
38
49
 
39
50
  ```javascript
@@ -41,35 +52,66 @@ Ensure your `next.config.js` has `output: 'export'`:
41
52
  const nextConfig = {
42
53
  output: 'export',
43
54
  };
55
+
44
56
  module.exports = nextConfig;
45
57
  ```
46
58
 
47
- Then build:
59
+ > [!WARNING]
60
+ > **Purely Client-Side Runtime**
61
+ > This tool generates a standalone bundle with no backend.
62
+ > - **Server Logic:** Features like Server Actions, `cookies()`, and Middleware are not supported.
63
+ > - **Dynamic Routes:** You must use `generateStaticParams` for any dynamic paths (e.g., `[id].tsx`) to ensure they are pre-rendered into the `out/` directory before bundling.
64
+ > - **RSC:** React Server Components are supported only insofar as they can be statically rendered to HTML at build time.
65
+
66
+ ### 2. Build Your Next.js App
67
+
48
68
  ```bash
49
- npm run build
50
- # or
69
+ # Any pkg manager is fine
51
70
  bun run build
52
71
  ```
53
72
 
54
- ### 2. Run the Inliner
73
+ ### 3. Generate Single HTML File
74
+
55
75
  ```bash
56
- # you need bun installed (can be run by npm tho)
76
+ # npx works too, you need bun installed on your system though
57
77
  bunx next-single-file --input out --output dist/index.html
58
- # or npm, needs bun installed
59
- npx next-single-file --input out --output dist/index.html
60
-
61
78
  ```
62
79
 
63
- ## ๐ŸŽฏ Use Cases
80
+ ## Use Cases
81
+
82
+ | Use Case | Description |
83
+ |----------|-------------|
84
+ | **Portable Demos** | Send a fully functional web app as a single email attachment |
85
+ | **Offline Documentation** | Create interactive docs that work without internet |
86
+ | **Embedded UIs** | Embed Next.js interfaces into desktop apps or dashboards |
87
+ | **Simple Hosting** | Host multi-page apps on GitHub Gists or basic file servers |
88
+
89
+ ## Benchmark
90
+
91
+ Performance on the included test Next.js app (averaged over 3 runs):
92
+
93
+ | Metric | Value |
94
+ |--------|-------|
95
+ | **Duration** | ~392 ms |
96
+ | **Output Size** | ~13.9 MB |
97
+ | **Memory Usage** | ~77 MB |
64
98
 
65
- - **Portable Demos**: Send a fully functional web app as a single email attachment.
66
- - **Offline Documentation**: Create rich, interactive docs that work without an internet connection.
67
- - **Embedded UIs**: Embed a Next.js interface into desktop applications or hardware dashboards.
68
- - **Simple Hosting**: Host a multi-page app on GitHub Gists or any basic file server.
99
+ Run your own benchmark:
69
100
 
70
- ## ๐Ÿงช Development
101
+ ```bash
102
+ bun run benchmark
103
+ ```
104
+
105
+ > Results may vary based on your app size and system. The test app includes 4 routes with images, fonts, and interactivity.
106
+
107
+ ## Development
71
108
 
72
- To run the tests:
73
109
  ```bash
110
+ bun install
74
111
  bun test
112
+ bun run build
75
113
  ```
114
+
115
+ ## License
116
+
117
+ MIT
package/dist/cli.js CHANGED
@@ -2,8 +2,8 @@
2
2
  // @bun
3
3
 
4
4
  // src/parser.ts
5
- import { readdir, readFile } from "node:fs/promises";
6
- import { join, relative, extname } from "node:path";
5
+ import { readdir, readFile } from "fs/promises";
6
+ import { join, relative, extname } from "path";
7
7
  async function walk(dir) {
8
8
  const files = [];
9
9
  const entries = await readdir(dir, { withFileTypes: true });
@@ -412,14 +412,14 @@ function extractMeta(html) {
412
412
  function escapeScriptTag(js) {
413
413
  return js.replace(/<\/script>/gi, "<\\/script>");
414
414
  }
415
+ function minifyHtml(html) {
416
+ return html.replace(/<!--[\s\S]*?-->/g, "").replace(/>\s+</g, "><").replace(/\s{2,}/g, " ").trim();
417
+ }
415
418
  function bundleToSingleHtml(inlined, routerShim) {
416
419
  const indexRoute = inlined.routes.find((r) => r.path === "/");
417
420
  if (!indexRoute) {
418
421
  throw new Error("No index route found");
419
422
  }
420
- const anyRoute = inlined.routes[0];
421
- if (!anyRoute)
422
- throw new Error("No routes found");
423
423
  const htmlAttrs = extractHtmlAttrs(indexRoute.htmlContent);
424
424
  const bodyAttrs = extractBodyAttrs(indexRoute.htmlContent);
425
425
  const title = extractTitle(indexRoute.headContent);
@@ -445,7 +445,7 @@ function bundleToSingleHtml(inlined, routerShim) {
445
445
  };
446
446
  }
447
447
  `;
448
- return `<!DOCTYPE html><!--${inlined.buildId}-->
448
+ const finalHtml = `<!DOCTYPE html><!--${inlined.buildId}-->
449
449
  <html${htmlAttrs}>
450
450
  <head>
451
451
  <meta charset="utf-8">
@@ -473,6 +473,7 @@ ${escapeScriptTag(routerShim)}
473
473
  </script>
474
474
  </body>
475
475
  </html>`;
476
+ return minifyHtml(finalHtml);
476
477
  }
477
478
 
478
479
  // index.ts
@@ -511,3 +512,5 @@ await $`mkdir -p ${outputFile.split("/").slice(0, -1).join("/") || "."}`.quiet()
511
512
  await Bun.write(outputFile, html);
512
513
  console.log(`\u2705 Done! Output: ${outputFile}`);
513
514
  console.log(` Size: ${(html.length / 1024).toFixed(1)} KB`);
515
+ console.log("Star us: https://github.com/simples-tools/next-single-file");
516
+ console.log("Report bugs: https://github.com/simples-tools/next-single-file/issues");
package/package.json CHANGED
@@ -1,16 +1,22 @@
1
1
  {
2
2
  "name": "next-single-file",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Convert Next.js static export to a single HTML file with hash routing",
5
5
  "bin": {
6
- "next-single-file": "./dist/cli.js"
6
+ "next-single-file": "dist/cli.js"
7
7
  },
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/simples-tools/next-single-file.git"
11
+ },
12
+ "author": "@brrock <github.com/brrock>",
8
13
  "files": [
9
14
  "dist/cli.js"
10
15
  ],
11
16
  "scripts": {
12
- "build": "bun build ./index.ts --target node --outfile ./dist/cli.js && chmod +x ./dist/cli.js",
17
+ "build": "bun build ./index.ts --target bun --outfile ./dist/cli.js && chmod +x ./dist/cli.js",
13
18
  "test": "bun test",
19
+ "benchmark": "bun run src/benchmark.ts",
14
20
  "convert": "bun run ./index.ts"
15
21
  },
16
22
  "type": "module",