npm package: @jiant/unify
Description: Interactive CLI to inline CSS and JS into an HTML file.
Latest version: 0.1.2

# `@jiant/unify`

`@jiant/unify` turns a small web project into one portable HTML file.

Point it at a directory with an HTML entry, choose the CSS and JavaScript or TypeScript files you want, and it will stitch everything together into a single document with inline `<style>` and `<script>` blocks. It is built for the boring but useful moment when you need to hand someone one file that just opens and works.

## What It Does

- Scans the current directory recursively for `.html`, `.css`, `.js`, and `.ts` files
- Lets you build interactively, or skip prompts with CLI flags
- Bundles selected JS and TS entry files with `esbuild`
- Inlines CSS before `</head>` when possible
- Inlines scripts before `</body>` when possible
- Falls back cleanly when `head` or `body` tags are missing
- Optionally removes matching external `<link>` and `<script src>` tags for the files you inlined
- Optionally minifies the final HTML output

## Why It Exists

Modern front-end projects scatter a simple page across multiple files. That is usually fine until you need one artifact:

- a throwaway prototype for review
- a standalone demo
- a shareable HTML deliverable
- an archive snapshot of a static page
- a single-file handoff for environments where asset hosting is annoying

`unify` is the compression step between “this project works locally” and “here, open this file.”

## Quick Start

Inside a project folder:

```bash
npx @jiant/unify
```

The interactive flow will:

1. ask you which HTML file should be the base document
2. let you add CSS files
3. let you add JS or TS entry files
4. ask for the output filename
5. ask whether to minify
6. ask whether to remove matching external references

The default output name is based on the selected HTML file:

- `index.html` -> `index-unify.html`
- `landing.html` -> `landing-unify.html`

## Non-Interactive Usage

```bash
npx @jiant/unify \
  --html index.html \
  --css reset.css,theme.css \
  --css components.css \
  --js vendor.js \
  --js app.ts,analytics.js \
  --ignore-path coverage,tmp/generated \
  --include-path dist \
  --output demo-bundle.html \
  --minify \
  --remove-inlined-references \
  --overwrite \
  --yes
```

### Flags

- `--html <file>`: base HTML file
- `--css <file[,file...]>`: CSS files to inline, repeatable
- `--js <file[,file...]>`: JS or TS entry files to bundle and inline, repeatable
- `--output <file>`: output HTML filename
- `--ignore-path <path[,path...]>`: ignore extra directories during recursive scan
- `--include-path <path[,path...]>`: include directories that would otherwise be ignored
- `--minify`: minify the generated file
- `--no-minify`: keep readable formatting
- `--remove-inlined-references`: remove matching external asset tags from the HTML
- `--keep-inlined-references`: keep original external tags
- `--overwrite`: replace an existing output file without asking
- `--yes`, `-y`: skip confirmation prompts
- `--help`, `-h`: print help

## How Injection Works

CSS is inserted in this order of preference:

1. before `</head>`
2. after `<head ...>`
3. after `<html ...>`
4. at the top of the document if none of the above exist

Scripts are inserted in this order of preference:

1. before `</body>`
2. before `</html>`
3. after `<body ...>`
4. at the end of the document if none of the above exist

Each inlined block keeps a source marker so you can still see where it came from:

```html
<style>
  /* Source: theme.css */
  body { color: red; }
</style>

<script>
  // Source: app.ts
  (() => {
    console.log("hello");
  })();
</script>
```

## JavaScript and TypeScript Handling

Selected `.js` and `.ts` files are treated as browser entry points and bundled with `esbuild` before injection.

Current behavior:

- bundle mode is enabled
- output format is `iife`
- platform is `browser`
- target is `es2018`

That makes the generated HTML easier to open directly without needing a module loader.

## File Discovery Rules

The scan is recursive and ignores common generated folders:

- `.git`
- `.next`
- `.nuxt`
- `.output`
- `.svelte-kit`
- `build`
- `coverage`
- `dist`
- `node_modules`
- `out`

To keep the prompt usable, the scan stops with an error if it finds more than `500` supported files total.

You can change that behavior from the CLI:

- `--ignore-path generated,coverage`: ignore additional directories by name or relative path
- `--ignore-path apps/site/tmp`: ignore one specific nested path
- `--include-path dist`: scan a directory that is ignored by default
- `--include-path packages/demo/dist`: scan one specific ignored path without changing the rest

## Reference Removal

If you pass `--remove-inlined-references`, `unify` will remove matching tags for the assets you selected, for example:

- `<link rel="stylesheet" href="./theme.css">`
- `<script src="./app.js"></script>`

It only removes tags that match files you actually chose to inline. Unrelated asset tags stay untouched.

## Development

```bash
npm install
npm run build
npm test
```

Run the CLI from source during development:

```bash
npm run dev -- --help
```

## Practical Notes

- Paths are resolved relative to the current working directory
- The tool expects the files you pass via flags to exist in the discovered project set
- Output names without an `.html` extension will automatically get one appended
- If no HTML files are found in the current directory, the CLI exits with an error