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:
cnos set value dev.server.port 8800npm run devTypical result:
set value.dev.server.port in .cnos\values\dev.ymlCnos 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_enabledUse 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.*andsecret.*out of the browser bundle - uses no prefix by default for webpack public env unless you configure
public.frameworks.webpackor passprefix
Bundle and cache behavior
CNOS does not add a runtime fetch layer to the browser bundle.
@kitsy/cnos-webpackresolves public config at build time- browser-safe values are embedded into the existing webpack bundle through
DefinePluginandglobalThis.__CNOS_BROWSER_DATA__ @kitsy/cnos/browseris 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
CnosWebpackPluginearly in the plugin list. HtmlWebpackPlugin,MiniCssExtractPlugin,CopyPlugin, andESLintPlugincan follow it.- If you need CNOS values in HTML templates, read them with
createCnos()in the webpack config and pass them throughtemplateParameters.DefinePluginreplacements 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.mjsor a package with"type": "module"andimport ... - CommonJS:
webpack.config.cjsor CommonJSmodule.exports = async () => ..., using dynamicimport()for CNOS ESM packages
Do not mix top-level await plus require(...) in a config file that webpack treats as ESM.