Skip to content
Kitsy Docs Open CNOS

Frontend with Webpack

Frontend with Webpack

CNOS integrates with webpack through @kitsy/cnos-webpack. This covers the common static-bundle case where webpack produces a browser build that is then deployed as static assets.

Highlight: change webpack dev-server config from CNOS

Once webpack reads build-time config through createCnos(), changing a setting such as the dev-server port is just a config write:

Terminal window
cnos set value dev.server.port 8800
npm run dev

Typical result:

set value.dev.server.port in .cnos\values\dev.yml
Cnos value.dev.server.port: 8800
[webpack-dev-server] Project is running at:
[webpack-dev-server] Loopback: http://localhost:8800/

This is the main benefit of the CNOS webpack path: you change config in .cnos, not scattered build scripts or ad hoc env files.

Promote browser-safe values:

public:
promote:
- value.app.apiUrl
- flags.upi_enabled

Use an async webpack config. If your repo already uses ESM webpack config, this is the cleanest shape:

import { createCnos } from '@kitsy/cnos/configure';
import { CnosWebpackPlugin } from '@kitsy/cnos-webpack';
export default async () => {
const cnos = await createCnos({
profile: process.env.NODE_ENV === 'production' ? 'prod' : 'local',
});
return {
devServer: {
port: Number(cnos.readOr('value.devServer.port', 3000)),
},
plugins: [
new CnosWebpackPlugin({
profile: process.env.NODE_ENV === 'production' ? 'prod' : 'local',
}),
],
};
};

If your project uses CommonJS-style webpack config, keep module.exports = async () => ... and load CNOS with dynamic import() inside the async factory:

const ESLintPlugin = require('eslint-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = async () => {
const { createCnos } = await import('@kitsy/cnos/configure');
const { CnosWebpackPlugin } = await import('@kitsy/cnos-webpack');
const profile = process.env.NODE_ENV === 'production' ? 'prod' : 'local';
const cnos = await createCnos({ profile });
return {
devServer: {
port: Number(cnos.readOr('value.devServer.port', 3000)),
},
plugins: [
new CnosWebpackPlugin({ profile }),
new HtmlWebpackPlugin({
templateParameters: {
appName: cnos.read('public.app.name'),
},
}),
new MiniCssExtractPlugin(),
new ESLintPlugin(),
],
};
};

Read in browser code:

import cnos from '@kitsy/cnos/browser';
console.log(cnos('public.app.apiUrl'));
console.log(cnos('public.flags.upi_enabled'));
console.log(process.env.APP_API_URL);

The webpack plugin:

  • injects process.env.* define replacements for promoted public values
  • embeds globalThis.__CNOS_BROWSER_DATA__ for @kitsy/cnos/browser
  • keeps server-only value.* and secret.* out of the browser bundle
  • uses no prefix by default for webpack public env unless you configure public.frameworks.webpack or pass prefix

Bundle and cache behavior

CNOS does not add a runtime fetch layer to the browser bundle.

  • @kitsy/cnos-webpack resolves public config at build time
  • browser-safe values are embedded into the existing webpack bundle through DefinePlugin and globalThis.__CNOS_BROWSER_DATA__
  • @kitsy/cnos/browser is a small reader over that embedded payload
  • if your app does not import @kitsy/cnos/browser, webpack can leave it out of the browser bundle
  • if your app does import @kitsy/cnos/browser, it is bundled like any other app dependency and follows your existing webpack and service-worker caching strategy

So if your site already uses sw.js for offline behavior, CNOS does not disable or bypass that. CNOS becomes part of the same built assets your service worker already caches.

If you need build-time server config such as devServer.port, read it in the webpack config through createCnos() so dev and production builds use the same CNOS source of truth.

Plugin order

Recommended order:

plugins: [
new CnosWebpackPlugin({ profile }),
new HtmlWebpackPlugin(...),
new MiniCssExtractPlugin(...),
new CopyPlugin(...),
new ESLintPlugin(...),
]

Guidance:

  • Put CnosWebpackPlugin early in the plugin list.
  • HtmlWebpackPlugin, MiniCssExtractPlugin, CopyPlugin, and ESLintPlugin can follow it.
  • If you need CNOS values in HTML templates, read them with createCnos() in the webpack config and pass them through templateParameters. DefinePlugin replacements do not automatically populate HTML templates.

Why require is not defined happens

If webpack-cli logs a path like file:///.../webpack.config.js, it loaded your config as ESM. In that mode, require(...) is not available.

Choose one style and stay consistent:

  • ESM: webpack.config.mjs or a package with "type": "module" and import ...
  • CommonJS: webpack.config.cjs or CommonJS module.exports = async () => ..., using dynamic import() for CNOS ESM packages

Do not mix top-level await plus require(...) in a config file that webpack treats as ESM.