Start Here
Configuration
Create a bascik.config.js file in your project root to override any default settings.
Full Example
// bascik.config.js
export const bascikConfig = {
directory: {
pages: 'src/pages',
components: 'src/components',
watch: ['scripts/', 'data/'],
},
scopeScriptBlocks: true,
inheritAttributes: true,
scopeAttribute: {
class: true,
id: true,
name: true,
},
skipTranspilingElementContents: ['code'],
deduplicateCss: true,
minifyStyles: true,
inlineStyles: ['src/pages/css/styles.css'],
minifyScripts: true,
obfuscateAttributeNames: true,
cacheHttp: false, // true by default in --serve mode
siteUrl: 'https://example.com',
generate: {
sitemap: true,
robots: true,
},
useWorkers: false,
devServer: {
logging: {
level: 'info', // silent | error | warn | info | debug
requests: true,
copies: true,
deletes: true,
transpiles: true,
},
},
};
export const buildOverrideConfig = {
obfuscateAttributeNames: true,
minifyStyles: true,
};Options Reference
directory
Paths to your pages and components directories. Relative to the project root.
directory: {
pages: 'src/pages', // default
components: 'src/components', // default
}scopeScriptBlocks
Wrap component <script> tags in an IIFE and rewrite scoped attribute references. Set to false if you want raw unmodified script output.
scopeScriptBlocks: true // defaultinheritAttributes
Control whether non-data-bascik-* attributes on a component usage tag are merged onto the component root element. Defaults to true.
inheritAttributes: true // defaultscopeAttribute
Control which HTML attribute types are scoped independently. Useful if you're using Tailwind (class: false) or don't need name scoping.
scopeAttribute: {
class: true, // default
id: true, // default
name: true, // default
}deduplicateCss
When true (default), all instances of the same component share the same scoped class names so the compiled <style> block is emitted only once per component type, regardless of how many times the component appears on the page.
When false, every instance gets its own unique per-instance class names (the same scheme used for id scoping). This means a querySelector('.myClass') inside a component script will naturally target only elements inside that specific instance, but each instance emits its own <style> block.
deduplicateCss: true // defaultChoosing false: Use per-instance class scoping when a component's JavaScript needs to use class selectors to locate its own root element and you have multiple instances of that component on the same page. For most components, using an id attribute to anchor the script (which is always per-instance) is a simpler alternative.
skipTranspilingElementContents
An array of HTML element names whose inner content is left untouched by the scoping pipeline. Attribute values, element-selector class injection, and JS selector rewriting are all skipped for any HTML found inside these elements. The elements' own opening-tag attributes (e.g. class="cblock-body" on <code> itself) are still scoped normally.
Defaults to ["code"]: the typical element used to display literal code examples. Add "pre" only if your templates contain raw text inside <pre> blocks that aren't wrapped in <code> and whose content has attribute patterns you don't want scoped. Note: when "pre" is in the list, attributes on elements inside<pre> (such as class="cblock-body" on a <code> child) are also excluded from scoping.
skipTranspilingElementContents: ['code'] // defaultSet to an empty array to disable the protection entirely, or extend the list for other elements whose contents should be preserved as-is.
minifyStyles
Collapse whitespace and newlines in the injected <style> block. Defaults to true.
minifyScripts
Minify inline <script> content and .js static files in the build output. Accepts three forms:
true(default), built-in minifier: strips block and line comments, collapses whitespace. String and template literals are preserved verbatim.false: no minification.(fn): a custom async-capable function called for each script body. Use this to plug in esbuild, terser, or any other tool:
// bascik.config.js
import { transform } from 'esbuild';
export const buildOverrideConfig = {
minifyScripts: async (js) => {
const result = await transform(js, { minify: true, loader: 'js' });
return result.code;
},
};Only applies to inline scripts (those without a src attribute) and to .js files copied into dist/. Non-JS script types such as application/ld+json are always left untouched.
obfuscateAttributeNames
Hash the generated class and id names to short hex strings instead of the verbose bascik__component__id__name format. Recommended for production.
obfuscateAttributeNames: true // production default
// bascik__my-nav__ab12cd34__navigation
// becomes: bab12cd34cacheHttp
Controls HTTP caching on server responses. Defaults to false in dev mode and true in --serve (production) mode, you rarely need to set this explicitly.
When true, HTML pages receive an ETag header and the server returns 304 Not Modified for unchanged content. Static assets get Cache-Control: public, max-age=3600. When false, responses include Cache-Control: no-store so browsers always fetch fresh content.
Set cacheHttp: false explicitly when running --serve behind a CDN that manages its own caching.
Build logs
Bascik can write a copy of the build output to a file when you enable it from the CLI. This is meant for debugging and CI diagnosis, not as a normal project artifact.
bascik --build --log
bascik --build --log ./logs/build.logThe default path is .bascik/build.log. If you omit --log, Bascik does not create a file automatically. Terminal output remains the primary source of build diagnostics.
devServer
Control the noise level of the development server's status output. The level field applies to all dev-server status events; individual toggles let you silence only the logs you do not want.
devServer: {
logging: {
level: 'info', // silent | error | warn | info | debug
requests: true, // log each page request as 'serving: ...'
copies: true, // log each static file copied into dist/
deletes: true, // log each dist/ file or dir deleted
transpiles: true, // log each page transpile
},
}Use level: 'warn' or level: 'silent' to suppress the high-volume status lines when you are focused on application logic rather than build output.
serve
Configure the HTTP/2 server started by bascik --serve and bascik (dev mode). port, hostname, keyFile, and certFile are read in both modes. bascik --build does not start a server and ignores this block.
serve: {
port: 8443, // default
hostname: 'localhost', // default; set '0.0.0.0' to bind all interfaces
keyFile: 'bascik-privkey.pem', // path to TLS private key
certFile: 'bascik-cert.pem', // path to TLS certificate
logging: {
level: 'info', // silent | error | warn | info | debug
requests: true, // log each request as 'GET / ... 200 17ms'
},
}See Production Server for the full guide.
siteUrl
The canonical base URL of your deployed site (e.g. 'https://example.com'). Required for sitemap generation. Trailing slashes are trimmed automatically.
siteUrl: 'https://example.com'generate
Control which files are written to dist/ during bascik --build. Both default to true. Requires siteUrl to be set.
generate: {
sitemap: true, // write dist/sitemap.xml
robots: true, // write dist/robots.txt
}See Sitemap & robots.txt for a full walkthrough.
directory.watch
An array of directories or files that, when changed in dev mode, trigger a full re-transpile of all pages. Useful for utility scripts, data files, or image directories that pages depend on at build time.
directory: {
pages: 'src/pages',
components: 'src/components',
watch: ['scripts/', 'data/'],
}Has no effect during bascik --build.
inlineStyles
Controls which global stylesheets Bascik reads and injects as <style> tags into every page's <head> during transpilation. When minifyStyles is true the content is minified before injection. Global styles are placed before component styles so component rules take precedence.
false: inline nothingtrue: inline every.cssfile underdirectory.pagesstring[]: inline only the listed stylesheet paths
inlineStyles: false // default
inlineStyles: true
inlineStyles: ['src/pages/css/styles.css']This eliminates the render-blocking <link rel="stylesheet"> request, the CSS arrives in the same HTTP response as the HTML. It pairs naturally with buildOverrideConfig to minify only in production:
export const bascikConfig = {
inlineStyles: ['src/pages/css/styles.css'],
minifyStyles: false,
};
export const buildOverrideConfig = {
minifyStyles: true,
};When to inline. Stylesheets under ~15 KB (gzipped) are good candidates. Larger stylesheets are better loaded asynchronously or split into critical and non-critical parts.
useWorkers
Transpile pages across a pool of CPU-core worker threads instead of sequentially on the main thread.
useWorkers: false // default
useWorkers: trueSpinning up the worker pool has a fixed cost, each worker loads the transpiler's module graph independently, which takes roughly 15-250ms in total depending on machine and cache state (all workers load in parallel, so this is a one-time fixed delay, not a per-page cost). For small sites, or sites without slow per-page work, this fixed cost outweighs the benefit of spreading pages across cores, and sequential transpilation on the main thread finishes first.
When to enable. Turn this on for larger sites (dozens of pages or more) doing CPU-heavy work per page, complex CSS/JS scoping across many components. It will not help much on sites whose slow parts are I/O-bound (e.g. <script data-bascik-build> blocks that fetch data or spawn subprocesses), since that work is already asynchronous regardless of which thread initiates it.
buildOverrideConfig
Exporting a second buildOverrideConfig object lets you set options that only apply during bascik --build, overriding the values in bascikConfig. A common pattern is to enable obfuscation and minification only in production:
export const buildOverrideConfig = {
obfuscateAttributeNames: true,
minifyStyles: true,
minifyScripts: true, // or a custom function for esbuild/terser
};