Skip to content

Commit 060b408

Browse files
refactor: generate types with double-extension support. (#30)
1 parent 54bdf89 commit 060b408

7 files changed

Lines changed: 371 additions & 578 deletions

File tree

README.md

Lines changed: 26 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -200,15 +200,25 @@ CSS Modules hash class names after the loader extracts selectors, so the stylesh
200200
<div className={`${styles['css-modules-badge']} css-modules-badge`}>
201201
```
202202
203-
### Stable selector type generation
203+
### Stable selector modules (`*.knighted-css.ts`)
204204
205-
Run `npx knighted-css-generate-types --root .` to scan your project for `?knighted-css&types` imports. The CLI:
205+
Import the generated selector module anywhere you want literal tokens:
206206
207-
- extracts selectors via the loader, then writes literal module declarations whose specifiers resolve to the real stylesheet paths (TypeScript picks up `import './foo.scss?knighted-css&types'` directly—no registries or casting helpers required). The default output still lands in `node_modules/@knighted/css/node_modules/.knighted-css`, but every module declaration points back to your source tree.
208-
- updates the packaged stub at `node_modules/@knighted/css/types-stub/index.d.ts`
209-
- exposes the declarations automatically because `types.d.ts` references the stub, so no `tsconfig` wiring is required
207+
```ts
208+
import { stableSelectors } from './styles.css.knighted-css.js'
209+
210+
stableSelectors.demo // "knighted-demo"
211+
type StableSelectors = typeof stableSelectors
212+
```
213+
214+
Run `npx knighted-css-generate-types --root .` to scan for `.knighted-css` specifiers and keep those modules current. The CLI:
210215
211-
Re-run the command whenever imports change (add it to a `types:css` npm script or your build). If you need a different destination, pass `--out-dir` and/or `--types-root` to override the defaults.
216+
- extracts selectors via the loader, applies your `stableNamespace`, and sorts the tokens deterministically
217+
- writes sibling `*.knighted-css.ts` files next to each stylesheet so editors resolve them immediately
218+
- records everything inside `<root>/.knighted-css/selector-modules.json` (or your custom `--out-dir`) and removes stale modules when imports disappear
219+
- warns whenever a specifier cannot be resolved or escapes the configured project root
220+
221+
Because these are regular modules—not `.d.ts` shims—you can import the default export or the named `stableSelectors`, and helper types (`KnightedCssStableSelectors`, `KnightedCssStableSelectorToken`) ship alongside the literal map. Pass `--stable-namespace` so the CLI and loader agree on prefixes, and use `--include` / `--out-dir` to expand the scan or relocate the manifest cache (the selector modules themselves always live beside the source stylesheet). Re-run the command whenever you touch `.knighted-css` imports (wire it into a `knighted:types` script or a watch task).
212222
213223
Sass/Less projects can import the shared mixins directly:
214224
@@ -252,7 +262,7 @@ Override the namespace via `:root { --knighted-stable-namespace: 'acme'; }` if y
252262
253263
#### Type-safe selector maps (`?knighted-css&types`)
254264
255-
Append `&types` to any loader import to receive a literal map of the discovered stable selectors alongside the raw CSS:
265+
Use `?knighted-css&types` when you need the `stableSelectors` map at runtime (Lit styles, SSR, tests, etc.). TypeScript already sees the literal tokens via the generated `.knighted-css` modules, but the loader surfaces the same data for application code:
256266
257267
```ts
258268
import { knightedCss, stableSelectors } from './styles.css?knighted-css&types'
@@ -274,7 +284,7 @@ const { knightedCss } = combined as KnightedCssCombinedModule<
274284
stableSelectors.demo // "knighted-demo"
275285
```
276286
277-
Namespaces default to `knighted`, but you can configure a global fallback via the loader’s `stableNamespace` option:
287+
Namespaces default to `knighted`, but you can configure a global fallback via the loader’s `stableNamespace` option (match it with the CLI’s `--stable-namespace` flag so runtime + generated modules agree):
278288
279289
```js
280290
{
@@ -289,24 +299,20 @@ All imports share the namespace resolved by the loader (or the `knighted-css-gen
289299
290300
#### TypeScript support for loader queries
291301
292-
Loader query types ship directly with `@knighted/css`. Reference them once in your project—either by adding `"types": ["@knighted/css/loader-queries"]` to `tsconfig.json` or dropping `/// <reference types="@knighted/css/loader-queries" />` into a global `.d.ts`—and the following ambient modules become available everywhere:
302+
Loader query types still ship directly with `@knighted/css`. Reference them once in your project—either by adding `"types": ["@knighted/css/loader-queries"]` to `tsconfig.json` or dropping `/// <reference types="@knighted/css/loader-queries" />` into a global `.d.ts`—and the following ambient modules become available everywhere:
293303

294304
- `*?knighted-css` imports expose a `knightedCss: string` export.
295305
- `*?knighted-css&types` exposes both `knightedCss` and `stableSelectors`, the readonly selector map.
296306
- `*?knighted-css&combined` (plus `&named-only` / `&no-default`) mirror the source module exports while adding `knightedCss`, which you can narrow with `KnightedCssCombinedModule` before destructuring named members.
297307
- `*?knighted-css&combined&types` variants add the same `stableSelectors` map on top of the combined behavior so a single import can surface everything.
298308

299-
No vendor copies are necessarythe declarations live inside `@knighted/css`, you just need to point your TypeScript config at the shipped `loader-queries` subpath once.
309+
No vendor copies are necessarythe declarations live inside `@knighted/css`, you just need to point your TypeScript config at the shipped `loader-queries` subpath once. Use them for runtime loader imports and lean on the `.knighted-css` modules for editor-time selector literals.
300310

301-
#### Generate literal selector types
311+
#### Keeping selector modules up to date
302312

303-
The runtime `stableSelectors` export is always a literal `as const` map, but TypeScript can only see those exact tokens if your project emits matching `.d.ts` files. Run the bundled CLI whenever you change a module that imports `?knighted-css&types` (or any `&combined&types` variants):
313+
The CLI walks every file you include (defaults to the project root, skipping `node_modules`, `dist`, etc.), finds specifiers ending in `.knighted-css`, reuses the loader to extract CSS, and writes deterministic `*.knighted-css.ts` siblings next to the real stylesheets. Each module exports the literal selector map plus helper types, and the manifest stored in `<root>/.knighted-css/selector-modules.json` keeps the cache tidy by removing stale files. Because the generator writes actual TypeScript modules, editors pick them up immediately through normal resolutionno registries or `typeRoots` wiring required.
304314

305-
```bash
306-
npx knighted-css-generate-types --root .
307-
```
308-
309-
or wire it into `package.json` for local workflows:
315+
Wire the CLI into `package.json` so local workflows stay fresh:
310316

311317
```json
312318
{
@@ -316,17 +322,14 @@ or wire it into `package.json` for local workflows:
316322
}
317323
```
318324

319-
The CLI scans every file you include (by default the project root, skipping `node_modules`, `dist`, etc.), finds imports containing `?knighted-css&types`, reuses the loader to extract CSS, and writes deterministic `.d.ts` files into `node_modules/.knighted-css/knt-*.d.ts`. Each declaration uses a specifier that resolves back to the original stylesheet path, so TypeScript sees the literal `import './foo.scss?knighted-css&types'` as soon as the CLI runs. It also maintains `node_modules/@knighted/css/types-stub/index.d.ts`, so TypeScript picks up the generated declarations automaticallyno extra `typeRoots` or registry scripts are required.
320-
321325
Key flags:
322326

323327
- `--root` / `-r`project root (defaults to `process.cwd()`).
324328
- `--include` / `-i`additional directories or files to scan (repeatable).
325-
- `--out-dir`custom output folder for the generated `knt-*` declarations.
326-
- `--types-root`override the `@types` directory used for the aggregator.
327-
- `--stable-namespace`namespace prefix for the generated selector map.
329+
- `--out-dir`directory for the selector module manifest cache (defaults to `<root>/.knighted-css`).
330+
- `--stable-namespace`namespace prefix shared by the generated selector maps and loader runtime.
328331

329-
Re-run the CLI (or add it to a pre-build hook) whenever selectors change so new tokens land in the literal declaration files.
332+
Re-run the CLI (or hook it into a watcher) whenever you touch `.knighted-css` imports so new tokens land in the literal selector modules.
330333

331334
#### Combined module + CSS import
332335

docs/combined-queries.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
This document summarizes how `?knighted-css&combined` behaves for different module export shapes and how to structure your imports accordingly. Use it as guidance when filing documentation feedback for `@knighted/css`.
44

5+
> [!NOTE]
6+
> TypeScript now reads literal selector tokens from the generated `.knighted-css.ts` modules (emitted by `knighted-css-generate-types`). Append `&types` to combined imports only when you also need `stableSelectors` at runtime—the loader still exports the map, while the double-extension modules keep your editors in sync.
7+
58
## Decision Matrix
69

710
| Source module exports | Recommended query | TypeScript import pattern | Notes |

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/css/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@knighted/css",
3-
"version": "1.0.0-rc.11",
3+
"version": "1.0.0-rc.12",
44
"description": "A build-time utility that traverses JavaScript/TypeScript module dependency graphs to extract, compile, and optimize all imported CSS into a single, in-memory string.",
55
"type": "module",
66
"main": "./dist/css.js",

0 commit comments

Comments
 (0)