Home Documentation Templates Examples Showcase GitHub ↗
Theme

Build output · Final web artifacts

Minification.

Nift can minify supported generated outputs automatically, or minify arbitrary files directly from the CLI. The feature is intentionally aimed at final web artifacts rather than source languages that already have their own compiler/toolchain.

Minification happens after Nift renders successfully, but before output is committed.

If the selected minifier rejects the rendered text, the build fails without overwriting the last successful output or its page metadata.

Supported formats

ExtensionsModeNotes
.html, .htmHTMLConservative whitespace/comment reduction; raw text such as <pre>, <textarea>, <script> and <style> is protected.
.cssCSSRemoves ordinary comments and redundant whitespace while preserving strings, required operator spacing and /*! ... */ comments. It does not whitelist today's CSS syntax, so unknown/future at-rules and functions are treated conservatively.
.js, .mjs, .cjsJavaScriptConservative lexical minification with tests around regex/division ambiguity, ASI-sensitive newlines, template literals and token boundaries.
.jsxJSX-awareMinifies ordinary JavaScript around JSX and JavaScript inside {...} expressions while preserving JSX text and tag spelling conservatively.
.jsonJSONValidates with the minifier subproject's self-contained JSON parser first, then removes insignificant whitespace. Invalid JSON fails instead of being rewritten.
.xmlXMLCompacts markup/comments conservatively while preserving text-node whitespace and CDATA.
.svgSVG/XMLUses the conservative XML rules; visible whitespace in <text>/<tspan> is not discarded.

Project-wide minify-exts

Add a minify-exts array to .nift/config.json. Any tracked output whose final extension appears in the array is minified automatically:

{
  "config": {
    "content-dir": "content/",
    "content-ext": ".html",
    "output-dir": "public/",
    "output-ext": ".html",
    "default-template": "templates/template.html",
    "build-threads": -1,
    "incremental-mode": "modified",
    "minify-exts": [".html", ".css", ".js", ".json", ".svg"]
  }
}

Extensions must begin with . and must name a format Nift currently supports. Duplicate entries are harmless. Extension matching is case-insensitive.

Override one tracked item

A tracked entry may explicitly opt in or out with a boolean minify field:

{
  "name": "assets/app",
  "title": "App",
  "template": "templates/app.js",
  "content-ext": ".js",
  "output-ext": ".js",
  "minify": true
}

true forces minification even if .js is absent from minify-exts. false disables it even if the extension is globally enabled. Omitting the field means “follow minify-exts.”

Incremental builds understand minification state

Nift persists the effective minification decision in each page's internal build metadata. Changing minify-exts or a tracked minify override is therefore a rebuild reason even when the template/content did not change.

Minified page metadata also stores an internal minifier-format version. If a future Nift release changes minifier semantics for safety or correctness, that version can be bumped so already-built minified pages are regenerated instead of silently retaining output from an older algorithm.

Standalone nift minify

The minifier is also exposed as a project-independent file utility. Its default is deliberately non-destructive:

nift minify public/index.html
# writes public/index.min.html

nift minify public/assets/site.css /srv/site/feed.json
# writes public/assets/site.min.css and /srv/site/feed.min.json

The original files are left unchanged. The output name is formed by inserting .min before the existing extension: app.js → app.min.js, icon.svg → icon.min.svg, and so on.

Explicit in-place mode

nift minify --in-place public/index.html
nift minify -i public/assets/site.css

-i / --in-place explicitly asks Nift to overwrite the source. This makes accidental destructive minification harder while retaining a concise form for generated files or build scripts where replacement is exactly what you want.

The files do not need to belong to a Nift project and may be anywhere the running process has permission to read and write. Multiple files may be supplied at once; Nift processes supported files and returns a failure status if any requested file is missing, unsupported, malformed for a validating minifier such as JSON, or cannot be written.

Failure is non-destructive.

The transformed text is produced and validated in memory before the destination is written. Malformed JSON, for example, leaves the source untouched and does not create a partial .min.json file.

The minifier is an embedded standalone subproject

The implementation now lives behind its own source-tree boundary rather than inside Nift's project/build classes:

minifier/
  include/minify/Minify.h
  src/
  cli/
  tests/

Nift build engine
  └── calls the public minifier library API

The subtree builds independently as nift-minify and carries its own test entry points. It does not depend on Nift's tracked-file model, template parser, project configuration or build metadata. Nift embeds it because final-output minification is useful during a build; the dependency direction is intentionally one-way so the minifier can later become a separate project without dragging Nift internals with it.

Why TypeScript, TSX, SCSS and Sass are not here

The minifier is deliberately about final artifacts. TypeScript, TSX, SCSS and Sass are normally inputs to TypeScript/esbuild/Vite/Sass or another specialist compiler, and those tools already understand their source languages much more deeply than Nift should.

TypeScript / TSX / SCSS
          │
          ▼
specialist compiler/bundler
          │
          ▼
       JS / CSS
          │
          ▼
optional Nift minification

This keeps Nift from accumulating a second frontend compiler stack merely to shorten source files that are normally compiled anyway.

This boundary is part of Nift's explicit architectural rules: optional final-output optimisation may be embedded, while source-language compilation remains an external toolchain responsibility.

Reliability

The minifier is continuously stress-tested with executable before/after JavaScript cases, while the public behavior remains intentionally simple.

The minifier is intentionally conservative where whitespace or token boundaries can affect behavior. JSX keeps visible text conservative while compacting JavaScript expressions; XML/SVG preserve text whitespace; JavaScript keeps semantics-sensitive boundaries rather than chasing every possible byte.

Minification is continuously checked against executable before/after JavaScript behavior as well as direct format-specific regression cases, so optimising output size does not take priority over preserving meaning.

The implementation deliberately favors conservative output over aggressive compression when JavaScript syntax is ambiguous.

JSX is tested separately from ordinary JavaScript as well: generated valid TSX examples are parsed before and after minification and minified twice to check idempotence. The JSX corpus deliberately includes nested expressions, generic component tags and regex-heavy JavaScript rather than only simple markup.

The same conservative approach is exercised against modern CSS features and mixed-content HTML/XML/SVG so newer syntax is preserved rather than rejected by hard-coded feature lists.

The generated JavaScript and JSX corpora are intentionally grown as parser ambiguities are discovered; function-type generic JSX tags, regex literals and modern class/function syntax are all included in the current stress set.

Its test corpus continues to grow around new JavaScript syntax and ambiguity boundaries even when a checkpoint finds no new bug.

The minifier format remains 1.0 while this feature is being hardened. Nift keeps the transformation engine independently testable even though it is integrated into normal builds, so fixes can be stress-tested directly without changing the public configuration.