Internals
Minification
Bascik features zero-dependency minifiers for HTML, CSS, and JavaScript, deterministic identifier hashing for scoped selectors, and custom minifier extensibility.
Overview
Minification reduces payload sizes without introducing heavy external bundlers or AST parsers. Bascik includes three specialized minification passes:
html-minifier.ts: Shields raw-text content, strips comments, consolidates eligible scripts, and collapses whitespace while protecting<pre>and<textarea>content.css-minifier.ts: Removes comments and structural whitespace while shielding string literals andurl()definitions.js-minifier.ts: Strips comments and unnecessary spaces while preserving string literals, template literals, and regex literals verbatim.- Identifier Hashing (
names.ts): Hashes scoped class names and element IDs using SHA-256 and Base62 encoding whenminify.identifiers: trueis configured. - BYO Minifier: Supports custom third-party minifier integrations (
esbuild,SWC,PostCSS,Lightning CSS) configured inbascik.config.ts.
Zero-AST Lexical Context Preservation
Traditional build tools rely on heavy Abstract Syntax Tree (AST) parsers to minify code safely. Bascik achieves equivalent safety and higher throughput using zero-dependency lexical context preservation:
- HTML Null-Byte Token Shielding: Whitespace-sensitive elements (
<pre>,<textarea>, and<style>) and script bodies are replaced with namespaced null-byte placeholders before comments are stripped or whitespace is collapsed. This prevents code blocks, formatted text, CSS CDO/CDC tokens, and comment-like strings inside scripts from being parsed as document structure. - CSS String and Resource Shielding: Quoted string literals and
url(...)declarations in CSS can contain colons, semicolons, or multiple spaces (such as data URIs or content strings).shieldCssStringsextracts these values into temporary tokens before structural whitespace stripping, restoring them unchanged afterward. - JS Lexical Context and Regex Disambiguation: JavaScript code is segmented into literal regions (quoted strings, template literals, regexes) and minifiable code regions. To disambiguate the forward slash
/character (which can represent either a division operator or a regex literal),js-minifier.tstracks preceding keyword context (such asreturn,case,typeof,yield,await). Forward slashes following expression keywords are preserved as regex literals.
HTML Minification (html-minifier.ts)
minifyHtml optimizes HTML documents through structural transformations and whitespace rules:
The order is deliberate:
- Shield complete
<pre>,<textarea>, and<style>elements plus script bodies. - Strip ordinary HTML comments from the remaining document structure.
- Extract eligible client scripts while leaving scripts inside shielded containers in place.
- Collapse structural whitespace.
- Restore every shielded region and append only the scripts selected for consolidation.
Key HTML Minification Behaviors
- Comment Stripping: Ordinary HTML comments (
<!-- ... -->) are removed after raw-text regions are shielded. Comment text inside<pre>,<textarea>,<style>, and scripts survives unchanged. In particular, CSS CDO/CDC tokens (<!--and-->) inside a<style>element are not outer HTML comments and are left in place so the authored stylesheet is preserved at the HTML stage. - Whitespace-Sensitive Shielding: Complete
<pre>,<textarea>, and<style>elements are stored before structural processing. Scripts nested inside those containers remain in their original location. Storing<style>as a whole element means structural whitespace inside it is preserved; CSS minimization is owned by the CSS minifier, never by HTML comment removal. - Smart Inline Tag Spacing &
O(1)Tag-Boundary Scanning: Whitespace between block-level tags (</div> <div>) is collapsed completely ("></div><div>"). For inline tags (a,span,b,strong,code), a single space is preserved between adjacent elements ("> <"). Tag boundary matching usesO(1)backwards scanning (lastIndexOf('<', offset)) and bounded slices rather than full-stringslice(0, offset)allocations, avoidingO(N^2)memory churn and V8 garbage collection overhead on large HTML pages. - Script Consolidation: Eligible classic and module client scripts may be extracted and re-appended at the end of the document. Build, routes, server, data, and scripts nested inside shielded containers remain in place.
JavaScript minification applies to scripts with no type and to text/javascript, module, application/javascript, text/ecmascript, and application/ecmascript. External src scripts and non-JavaScript data scripts are not minified.
CSS Minification (css-minifier.ts)
minifyCss reduces stylesheet sizes by stripping comments and collapsing whitespace around CSS syntax delimiters ({, }, :, ;, ,).
export const minifyCss = (css: string): string => {
const { css: shielded, restore } = shieldCssStrings(removeCommentsFromCss(css));
const minified = shielded
.replace(/\n/g, " ")
.replace(/\s\s+/g, " ")
.replace(/\s*([{}:;,])\s*/g, "$1")
.trim();
return restore(minified);
}; String and URL Shielding (shieldCssStrings)
CSS property values can contain strings, data URIs, or custom properties containing colons, semicolons, or multiple spaces:
.badge::after {
content: "Status: Active; Version 1.0";
}
.hero {
background-image: url("data:image/svg+xml;charset=utf-8,...");
} shieldCssStrings extracts quoted string literals and url(...) declarations before minification runs, replacing them with temporary tokens. After structural whitespace is stripped, restore() re-injects the original string content unchanged.
JavaScript Minification (js-minifier.ts)
minifyJs performs single-pass lexical scanning to strip comments and collapse whitespace in client-side scripts.
Lexical Segmenting
JavaScript source code is divided into literal segments (which must be preserved verbatim) and minifiable code segments:
type Segment = { literal: boolean; text: string }; Quoted strings ("...", '...'), template literals (`...`), and regex literals (/.../) are placed in literal segments.
Regex Disambiguation
A forward slash / can denote either a division operator or the start of a regular expression literal. To disambiguate, js-minifier.ts tracks preceding keyword context:
const REGEX_PRECEDING_KEYWORDS = new Set([
"return",
"case",
"throw",
"yield",
"await",
"delete",
"typeof",
"void",
"default",
"in",
"of",
"instanceof",
"new",
"do",
]); When / follows an operator or expression keyword, it is parsed as a regular expression literal and preserved intact.
Identifier Hashing (names.ts)
When production identifier minification is enabled (minify.identifiers: true), Bascik replaces long scoped class names and element IDs with compressed alphanumeric hashes.
Base62 Hash Encoding
names.ts computes SHA-256 digests of scoped attribute names and encodes the first 64 bits into a Base62 string:
const BASE62_ALPHABET =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
export const toBase62 = (num: bigint, length = 11): string => {
if (num === 0n) return "0".repeat(length);
let str = "";
let current = num;
while (current > 0n) {
const remainder = Number(current % 62n);
str = BASE62_ALPHABET[remainder] + str;
current = current / 62n;
}
return str.padStart(length, "0");
};
export const getAttributeNameHash = (attributeName: string): string => {
const digest = createHash("sha256").update(attributeName).digest();
const num = typeof digest === "string" ? Buffer.from(digest).readBigUInt64BE(0) : digest.readBigUInt64BE(0);
return `b${toBase62(num, 11)}`;
}; The output is prefixed with a b character to ensure class and ID names always begin with a valid CSS letter identifier rather than a digit (for example, b2Y4G9eD1K8b).
O(1) Hash Memoization (hashCache)
To eliminate redundant crypto and string allocations during builds, getAttributeNameHash caches computed Base62 hashes in an in-memory Map<string, string> (hashCache). Subsequent encounters of identical attribute names across pages or components return the cached hash instantly with 0 SHA-256 digests, BigInt conversions, or string creation overhead.
Bring Your Own Minifier (BYO Minifier)
For projects with specialized optimization needs, Bascik allows overriding the default minifiers in bascik.config.ts. Custom minifiers can be synchronous or asynchronous.
Integration Examples
JavaScript with esbuild
import { transform } from 'esbuild';
export default {
minify: {
js: async (code: string) => {
const result = await transform(code, { loader: 'js', minify: true });
return result.code.trim();
},
},
}; CSS with Lightning CSS or PostCSS
import postcss from 'postcss';
import autoprefixer from 'autoprefixer';
export default {
minify: {
css: async (code: string) => {
const result = await postcss([autoprefixer]).process(code, { from: undefined });
return result.css;
},
},
}; When custom minifier functions are provided, Bascik routes compiled assets through the custom handlers during the final build phase.