KnightedCssResolverPlugin is the resolver companion for declaration mode. It teaches your
bundler to rewrite module imports to ?knighted-css (and &combined when applicable) when a
matching declaration sidecar exists, so runtime exports stay aligned with the generated types.
Without the plugin, TypeScript may compile, but the bundler will load the original module
without the loader query. That means knightedCss, stableSelectors, or other injected exports
will be missing at runtime.
- Scans resolved JS/TS module requests.
- Looks for a declaration sidecar (
.d.ts) generated byknighted-css-generate-types --mode declaration. - If a sidecar is found, rewrites the request to append
?knighted-css. - Optionally enforces strict sidecar matches using a manifest to avoid accidental rewrites.
- Optionally marks “combined” entries (via
combinedPaths) so the query includes&combined.
The idiomatic usage is to instantiate the class:
import { KnightedCssResolverPlugin } from '@knighted/css/plugin'
new KnightedCssResolverPlugin(options)If you prefer a factory function, the package also exports knightedCssResolverPlugin which
returns the same plugin instance:
import { knightedCssResolverPlugin } from '@knighted/css/plugin'
knightedCssResolverPlugin(options)type KnightedCssResolverPluginOptions = {
rootDir?: string
tsconfig?: string | Record<string, unknown>
conditions?: string[]
extensions?: string[]
debug?: boolean
combinedPaths?: Array<string | RegExp>
strictSidecar?: boolean
manifestPath?: string
}rootDir(optional): Base directory used for resolver scoping. Defaults toprocess.cwd().tsconfig(optional): Path to a tsconfig or an in-memory tsconfig object for path resolution.conditions(optional): Custompackage.jsonexport conditions to honor during resolution.extensions(optional): File extensions considered as script modules. Defaults to.ts,.tsx,.js,.jsx,.mts,.cts,.mjs,.cjs.debug(optional): Logs rewrite decisions and a summary of cache hits/misses.combinedPaths(optional): List of strings or regexes. Any resolved path that matches will receive&combinedalongside?knighted-css. Use this when the request is handled by@knighted/css/loader-bridge(hashed CSS Modules) or whenever you need a wrapper module that re-exports the original JS/TS exports plusknightedCss.strictSidecar(optional): When true, only modules present in the manifest are rewritten. Defaults to true whenmanifestPathis provided.manifestPath(optional): Path to the sidecar manifest generated byknighted-css-generate-types --manifest. Used for strict matching.
// rspack.config.js
import { KnightedCssResolverPlugin } from '@knighted/css/plugin'
export default {
resolve: {
plugins: [new KnightedCssResolverPlugin()],
},
}// Type generation
knighted-css-generate-types --root . --include src --mode declarationThis lets you write clean imports while still receiving knightedCss at runtime:
import { Button, knightedCss } from './button.js'Use strict sidecars to avoid rewriting modules that merely happen to have a .d.ts next to
them. This ensures only declaration mode sidecars generated by the CLI trigger rewrites.
knighted-css-generate-types --root . --include src --mode declaration --manifest .knighted-css/knighted-manifest.json// rspack.config.js
import path from 'node:path'
import { KnightedCssResolverPlugin } from '@knighted/css/plugin'
export default {
resolve: {
plugins: [
new KnightedCssResolverPlugin({
strictSidecar: true,
manifestPath: path.resolve('.knighted-css/knighted-manifest.json'),
}),
],
},
}If you have modules that are consumed with combined exports (?knighted-css&combined),
set combinedPaths to ensure the resolver appends &combined during rewrites.
&combined is required when the underlying loader chain does not preserve the module’s
original exports. The common case is declaration mode + --hashed, where requests flow through
@knighted/css/loader-bridge and would otherwise only expose knightedCss/knightedCssModules.
Appending &combined tells the loader to generate a small wrapper module that re-exports the
original module and then appends the knightedCss exports.
You typically do not need combinedPaths when requests are handled by @knighted/css/loader
(non-hashed declaration mode), because that loader appends exports directly onto the original
module.
Outside of loader-bridge, &combined is still useful when you are not running
knighted-css-generate-types but want a single runtime import that includes both the original
JS/TS exports and knightedCss (for example, in runtime-only builds, tests, or tooling that
cannot resolve the generated .knighted-css proxy modules).
Example of an import that relies on &combined at runtime:
import { Card, knightedCss } from './combined-card.js'And how you would use combinedPaths to support that:
import path from 'node:path'
import { KnightedCssResolverPlugin } from '@knighted/css/plugin'
export default {
resolve: {
plugins: [
new KnightedCssResolverPlugin({
combinedPaths: [
path.resolve('src/components/combined-card.tsx'),
/src\/views\/.*\.tsx$/,
],
}),
],
},
}Enable debug: true to log each decision and a final summary that includes counts for
rewrites, cache hits, marker misses, and manifest misses.
import { KnightedCssResolverPlugin } from '@knighted/css/plugin'
export default {
resolve: {
plugins: [new KnightedCssResolverPlugin({ debug: true })],
},
}