Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ The built-in walker already leans on [`oxc-resolver`](https://github.com/oxc-pro
> [!TIP]
> Hash-prefixed specifiers defined in `package.json#imports` resolve automatically—no extra loader or `css()` options required. Reach for a custom resolver only when you need behavior beyond what `oxc-resolver` already mirrors.

> [!NOTE]
> Sass-specific prefixes such as `pkg:#button` live outside Node’s resolver and still need a shim. See [docs/sass-import-aliases.md](./docs/sass-import-aliases.md) for a drop-in helper that strips those markers before `@knighted/css` walks the graph.

```ts
import { ResolverFactory } from 'enhanced-resolve'
import { css } from '@knighted/css'
Expand Down
3 changes: 3 additions & 0 deletions docs/loader.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ export default {
> [!NOTE]
> The loader shares the same auto-configured `oxc-resolver` as the standalone `css()` API, so hash-prefixed specifiers declared under `package.json#imports` (for example, `#ui/button`) resolve without additional options.

> [!TIP]
> Sass-only aliases such as `pkg:#button` never hit Node resolution. Add a small shim resolver (see [docs/sass-import-aliases.md](./sass-import-aliases.md)) when you need to rewrite those specifiers before the loader runs.

### Combined imports

Need the component exports **and** the compiled CSS from a single import? Use `?knighted-css&combined` and narrow the result with `KnightedCssCombinedModule` to keep TypeScript happy:
Expand Down
56 changes: 56 additions & 0 deletions docs/sass-import-aliases.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Sass import aliases

Some Sass codebases rely on custom load-path prefixes such as `pkg:#ui/button` or `pkg:@scope/app/components/button`. Those specifiers are resolved by the Sass compiler itself—they never travel through Node.js resolution, `package.json#imports`, or tsconfig `paths`. Because `@knighted/css` reuses Node-style resolution rules underneath (`oxc-resolver`), it cannot interpret Sass-only prefixes automatically.

The fix is to provide a custom resolver that rewrites those specifiers into absolute paths before the loader (or the standalone `css()` function) tries to walk the dependency graph.

## When you need a resolver

Add a resolver whenever you see either of the following:

- An `@use`/`@import` statement that starts with a nonstandard scheme such as `pkg:` or `sass:`.
- A project-level shorthand that never appears in `package.json#imports` or `tsconfig.json` (for example, `@scope/app` pointing at a workspace directory only Sass knows about).

Without a resolver, those imports throw “Cannot resolve specifier” errors as soon as `@knighted/css` tries to crawl the module graph.

## Example: strip `pkg:#` aliases

```ts
import path from 'node:path'
import { css } from '@knighted/css'

const pkgAppSrcDir = path.resolve(process.cwd(), 'packages/app/src')

function resolvePkgAlias(specifier: string): string | undefined {
if (!specifier.startsWith('pkg:')) return undefined
const remainder = specifier
.slice('pkg:'.length)
.replace(/^#/, '')
.replace(/^@scope\/app\/?/, '')
.replace(/^\/+/, '')
return path.resolve(pkgAppSrcDir, remainder)
}

await css('./src/entry.ts', {
resolver: (specifier, { cwd }) => resolvePkgAlias(specifier) ?? undefined,
})
```

The same helper works inside bundler rules:

```js
{
test: /\.[jt]sx?$/,
resourceQuery: /knighted-css/,
use: [
{
loader: '@knighted/css/loader',
options: {
resolver: specifier => resolvePkgAlias(specifier),
},
},
],
}
```

Customize the rewrite logic to match your project’s prefixes or directory layout. Once the resolver returns an absolute file path, `@knighted/css` will process the Sass dependency chain normally and still honor every other built-in resolution feature (tsconfig `paths`, package `imports`, extension aliases, etc.).
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/css/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@knighted/css",
"version": "1.0.0-rc.15",
"version": "1.0.0",
"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.",
"type": "module",
"main": "./dist/css.js",
Expand Down
2 changes: 1 addition & 1 deletion packages/playwright/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
"pretest": "npm run build"
},
"dependencies": {
"@knighted/css": "1.0.0-rc.15",
"@knighted/css": "1.0.0",
"@knighted/jsx": "^1.4.1",
"lit": "^3.2.1",
"react": "^19.0.0",
Expand Down