@knighted/css 1.0.0-alpha.0 → 1.0.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +54 -9
- package/dist/cjs/css.cjs +7 -5
- package/dist/cjs/css.d.cts +5 -0
- package/dist/cjs/loader.cjs +29 -0
- package/dist/cjs/loader.d.cts +11 -0
- package/dist/css.d.ts +5 -0
- package/dist/css.js +6 -5
- package/dist/loader.d.ts +11 -0
- package/dist/loader.js +27 -0
- package/package.json +10 -2
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
[](https://codecov.io/gh/knightedcodemonkey/css)
|
|
5
5
|
[](https://www.npmjs.com/package/@knighted/css)
|
|
6
6
|
|
|
7
|
-
`@knighted/css` is a build-time helper that walks a JavaScript/TypeScript module graph, finds every CSS-like dependency (plain CSS, Sass/SCSS, Less, vanilla-extract), compiles them, and returns a single concatenated stylesheet string. It is designed
|
|
7
|
+
`@knighted/css` is a build-time helper that walks a JavaScript/TypeScript module graph, finds every CSS-like dependency (plain CSS, Sass/SCSS, Less, vanilla-extract), compiles them, and returns a single concatenated stylesheet string. It is designed for workflows where you want fully materialized styles ahead of time—feeding Lit components, server-rendered routes, static site builds, or any pipeline that needs all CSS for a specific entry point without running a full bundler.
|
|
8
8
|
|
|
9
9
|
## Features
|
|
10
10
|
|
|
@@ -23,12 +23,10 @@
|
|
|
23
23
|
## Installation
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
|
-
npm install @knighted/css
|
|
27
|
-
sass less \
|
|
28
|
-
@vanilla-extract/css @vanilla-extract/integration @vanilla-extract/recipes
|
|
26
|
+
npm install @knighted/css
|
|
29
27
|
```
|
|
30
28
|
|
|
31
|
-
|
|
29
|
+
Install the peers your project is using, for example `less`, or `sass`, etc.
|
|
32
30
|
|
|
33
31
|
## Quick Start
|
|
34
32
|
|
|
@@ -73,17 +71,18 @@ Typical customizations:
|
|
|
73
71
|
|
|
74
72
|
## Examples
|
|
75
73
|
|
|
76
|
-
###
|
|
74
|
+
### Generate standalone stylesheets
|
|
77
75
|
|
|
78
76
|
```ts
|
|
79
77
|
import { writeFile } from 'node:fs/promises'
|
|
80
78
|
import { css } from '@knighted/css'
|
|
81
79
|
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
// Build-time script that gathers all CSS imported by a React route
|
|
81
|
+
const sheet = await css('./src/routes/marketing-page.tsx', {
|
|
82
|
+
lightningcss: { minify: true, targets: { chrome: 120, safari: 17 } },
|
|
84
83
|
})
|
|
85
84
|
|
|
86
|
-
await writeFile('./dist/
|
|
85
|
+
await writeFile('./dist/marketing-page.css', sheet)
|
|
87
86
|
```
|
|
88
87
|
|
|
89
88
|
### Inline CSS during SSR
|
|
@@ -99,6 +98,52 @@ export async function render(url: string) {
|
|
|
99
98
|
}
|
|
100
99
|
```
|
|
101
100
|
|
|
101
|
+
### Bundler loader (`?knighted-css` query)
|
|
102
|
+
|
|
103
|
+
When using Webpack/Rspack, add the provided loader so importing a module with a specific query also returns the compiled stylesheet:
|
|
104
|
+
|
|
105
|
+
```js
|
|
106
|
+
// webpack.config.js
|
|
107
|
+
module.exports = {
|
|
108
|
+
module: {
|
|
109
|
+
rules: [
|
|
110
|
+
{
|
|
111
|
+
test: /\.[jt]sx?$/,
|
|
112
|
+
resourceQuery: /knighted-css/,
|
|
113
|
+
use: [
|
|
114
|
+
{
|
|
115
|
+
loader: '@knighted/css/loader',
|
|
116
|
+
options: {
|
|
117
|
+
exportName: 'reactStyles', // optional (default: "knightedCss")
|
|
118
|
+
lightningcss: { minify: true }, // all css() options supported
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
],
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
// lit wrapper
|
|
130
|
+
import { LitElement, html, unsafeCSS } from 'lit'
|
|
131
|
+
import { Button, reactStyles } from './button.tsx?knighted-css'
|
|
132
|
+
|
|
133
|
+
export class ButtonWrapper extends LitElement {
|
|
134
|
+
static styles = [unsafeCSS(reactStyles)]
|
|
135
|
+
render() {
|
|
136
|
+
return html`<${Button} />`
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// per-import override:
|
|
141
|
+
// import { Button, cardCss } from './button.tsx?knighted-css&exportName=cardCss'
|
|
142
|
+
// (exportName in the query wins over the loader option)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
The loader appends `export const reactStyles = "/* compiled css */"` to the module, so you can wire it directly into Lit’s `static styles` or any other runtime.
|
|
146
|
+
|
|
102
147
|
## Scripts
|
|
103
148
|
|
|
104
149
|
- `npm run build` – Produce CJS/ESM outputs via `@knighted/duel`.
|
package/dist/cjs/css.cjs
CHANGED
|
@@ -5,15 +5,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.DEFAULT_EXTENSIONS = void 0;
|
|
7
7
|
exports.css = css;
|
|
8
|
+
exports.cssWithMeta = cssWithMeta;
|
|
8
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
10
|
const node_fs_1 = require("node:fs");
|
|
10
11
|
const dependency_tree_1 = __importDefault(require("dependency-tree"));
|
|
11
12
|
const lightningcss_1 = require("lightningcss");
|
|
12
13
|
exports.DEFAULT_EXTENSIONS = ['.css', '.scss', '.sass', '.less', '.css.ts'];
|
|
13
|
-
/**
|
|
14
|
-
* Extract and compile all CSS-like dependencies for a given module.
|
|
15
|
-
*/
|
|
16
14
|
async function css(entry, options = {}) {
|
|
15
|
+
const { css: output } = await cssWithMeta(entry, options);
|
|
16
|
+
return output;
|
|
17
|
+
}
|
|
18
|
+
async function cssWithMeta(entry, options = {}) {
|
|
17
19
|
const cwd = options.cwd ? node_path_1.default.resolve(options.cwd) : process.cwd();
|
|
18
20
|
const entryPath = await resolveEntry(entry, cwd, options.resolver);
|
|
19
21
|
const extensions = (options.extensions ?? exports.DEFAULT_EXTENSIONS).map(ext => ext.toLowerCase());
|
|
@@ -24,7 +26,7 @@ async function css(entry, options = {}) {
|
|
|
24
26
|
dependencyTreeOptions: options.dependencyTree,
|
|
25
27
|
});
|
|
26
28
|
if (files.length === 0) {
|
|
27
|
-
return '';
|
|
29
|
+
return { css: '', files: [] };
|
|
28
30
|
}
|
|
29
31
|
const chunks = [];
|
|
30
32
|
for (const file of files) {
|
|
@@ -46,7 +48,7 @@ async function css(entry, options = {}) {
|
|
|
46
48
|
});
|
|
47
49
|
output = code.toString();
|
|
48
50
|
}
|
|
49
|
-
return output;
|
|
51
|
+
return { css: output, files: files.map(file => file.path) };
|
|
50
52
|
}
|
|
51
53
|
async function resolveEntry(entry, cwd, resolver) {
|
|
52
54
|
if (typeof resolver === 'function') {
|
package/dist/cjs/css.d.cts
CHANGED
|
@@ -18,5 +18,10 @@ export interface CssOptions {
|
|
|
18
18
|
/**
|
|
19
19
|
* Extract and compile all CSS-like dependencies for a given module.
|
|
20
20
|
*/
|
|
21
|
+
export interface CssResult {
|
|
22
|
+
css: string;
|
|
23
|
+
files: string[];
|
|
24
|
+
}
|
|
21
25
|
export declare function css(entry: string, options?: CssOptions): Promise<string>;
|
|
26
|
+
export declare function cssWithMeta(entry: string, options?: CssOptions): Promise<CssResult>;
|
|
22
27
|
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const css_js_1 = require("./css.cjs");
|
|
4
|
+
const DEFAULT_EXPORT_NAME = 'knightedCss';
|
|
5
|
+
const loader = async function loader(source) {
|
|
6
|
+
const rawOptions = (typeof this.getOptions === 'function' ? this.getOptions() : {});
|
|
7
|
+
const queryParams = typeof this.resourceQuery === 'string' && this.resourceQuery.startsWith('?')
|
|
8
|
+
? new URLSearchParams(this.resourceQuery.slice(1))
|
|
9
|
+
: undefined;
|
|
10
|
+
const queryExportName = queryParams?.get('exportName')?.trim();
|
|
11
|
+
const isValidIdentifier = typeof queryExportName === 'string' &&
|
|
12
|
+
/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(queryExportName);
|
|
13
|
+
const { exportName = DEFAULT_EXPORT_NAME, ...cssOptions } = rawOptions;
|
|
14
|
+
const resolvedExportName = isValidIdentifier ? queryExportName : exportName;
|
|
15
|
+
const normalizedOptions = {
|
|
16
|
+
...cssOptions,
|
|
17
|
+
cwd: cssOptions.cwd ?? this.rootContext ?? process.cwd(),
|
|
18
|
+
};
|
|
19
|
+
const { css, files } = await (0, css_js_1.cssWithMeta)(this.resourcePath, normalizedOptions);
|
|
20
|
+
const uniqueFiles = new Set([this.resourcePath, ...files]);
|
|
21
|
+
for (const file of uniqueFiles) {
|
|
22
|
+
this.addDependency(file);
|
|
23
|
+
}
|
|
24
|
+
const input = typeof source === 'string' ? source : source.toString('utf8');
|
|
25
|
+
const injection = `\n\nexport const ${resolvedExportName} = ${JSON.stringify(css)};\n`;
|
|
26
|
+
const output = `${input}${injection}`;
|
|
27
|
+
return output;
|
|
28
|
+
};
|
|
29
|
+
exports.default = loader;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LoaderDefinitionFunction } from 'webpack';
|
|
2
|
+
import { type CssOptions } from './css.cjs';
|
|
3
|
+
export interface KnightedCssLoaderOptions extends CssOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Named export that will contain the compiled CSS string.
|
|
6
|
+
* Defaults to "knightedCss".
|
|
7
|
+
*/
|
|
8
|
+
exportName?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const loader: LoaderDefinitionFunction<KnightedCssLoaderOptions>;
|
|
11
|
+
export default loader;
|
package/dist/css.d.ts
CHANGED
|
@@ -18,5 +18,10 @@ export interface CssOptions {
|
|
|
18
18
|
/**
|
|
19
19
|
* Extract and compile all CSS-like dependencies for a given module.
|
|
20
20
|
*/
|
|
21
|
+
export interface CssResult {
|
|
22
|
+
css: string;
|
|
23
|
+
files: string[];
|
|
24
|
+
}
|
|
21
25
|
export declare function css(entry: string, options?: CssOptions): Promise<string>;
|
|
26
|
+
export declare function cssWithMeta(entry: string, options?: CssOptions): Promise<CssResult>;
|
|
22
27
|
export {};
|
package/dist/css.js
CHANGED
|
@@ -3,10 +3,11 @@ import { promises as fs } from 'node:fs';
|
|
|
3
3
|
import dependencyTree from 'dependency-tree';
|
|
4
4
|
import { transform as lightningTransform, } from 'lightningcss';
|
|
5
5
|
export const DEFAULT_EXTENSIONS = ['.css', '.scss', '.sass', '.less', '.css.ts'];
|
|
6
|
-
/**
|
|
7
|
-
* Extract and compile all CSS-like dependencies for a given module.
|
|
8
|
-
*/
|
|
9
6
|
export async function css(entry, options = {}) {
|
|
7
|
+
const { css: output } = await cssWithMeta(entry, options);
|
|
8
|
+
return output;
|
|
9
|
+
}
|
|
10
|
+
export async function cssWithMeta(entry, options = {}) {
|
|
10
11
|
const cwd = options.cwd ? path.resolve(options.cwd) : process.cwd();
|
|
11
12
|
const entryPath = await resolveEntry(entry, cwd, options.resolver);
|
|
12
13
|
const extensions = (options.extensions ?? DEFAULT_EXTENSIONS).map(ext => ext.toLowerCase());
|
|
@@ -17,7 +18,7 @@ export async function css(entry, options = {}) {
|
|
|
17
18
|
dependencyTreeOptions: options.dependencyTree,
|
|
18
19
|
});
|
|
19
20
|
if (files.length === 0) {
|
|
20
|
-
return '';
|
|
21
|
+
return { css: '', files: [] };
|
|
21
22
|
}
|
|
22
23
|
const chunks = [];
|
|
23
24
|
for (const file of files) {
|
|
@@ -39,7 +40,7 @@ export async function css(entry, options = {}) {
|
|
|
39
40
|
});
|
|
40
41
|
output = code.toString();
|
|
41
42
|
}
|
|
42
|
-
return output;
|
|
43
|
+
return { css: output, files: files.map(file => file.path) };
|
|
43
44
|
}
|
|
44
45
|
async function resolveEntry(entry, cwd, resolver) {
|
|
45
46
|
if (typeof resolver === 'function') {
|
package/dist/loader.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { LoaderDefinitionFunction } from 'webpack';
|
|
2
|
+
import { type CssOptions } from './css.js';
|
|
3
|
+
export interface KnightedCssLoaderOptions extends CssOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Named export that will contain the compiled CSS string.
|
|
6
|
+
* Defaults to "knightedCss".
|
|
7
|
+
*/
|
|
8
|
+
exportName?: string;
|
|
9
|
+
}
|
|
10
|
+
declare const loader: LoaderDefinitionFunction<KnightedCssLoaderOptions>;
|
|
11
|
+
export default loader;
|
package/dist/loader.js
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { cssWithMeta } from './css.js';
|
|
2
|
+
const DEFAULT_EXPORT_NAME = 'knightedCss';
|
|
3
|
+
const loader = async function loader(source) {
|
|
4
|
+
const rawOptions = (typeof this.getOptions === 'function' ? this.getOptions() : {});
|
|
5
|
+
const queryParams = typeof this.resourceQuery === 'string' && this.resourceQuery.startsWith('?')
|
|
6
|
+
? new URLSearchParams(this.resourceQuery.slice(1))
|
|
7
|
+
: undefined;
|
|
8
|
+
const queryExportName = queryParams?.get('exportName')?.trim();
|
|
9
|
+
const isValidIdentifier = typeof queryExportName === 'string' &&
|
|
10
|
+
/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(queryExportName);
|
|
11
|
+
const { exportName = DEFAULT_EXPORT_NAME, ...cssOptions } = rawOptions;
|
|
12
|
+
const resolvedExportName = isValidIdentifier ? queryExportName : exportName;
|
|
13
|
+
const normalizedOptions = {
|
|
14
|
+
...cssOptions,
|
|
15
|
+
cwd: cssOptions.cwd ?? this.rootContext ?? process.cwd(),
|
|
16
|
+
};
|
|
17
|
+
const { css, files } = await cssWithMeta(this.resourcePath, normalizedOptions);
|
|
18
|
+
const uniqueFiles = new Set([this.resourcePath, ...files]);
|
|
19
|
+
for (const file of uniqueFiles) {
|
|
20
|
+
this.addDependency(file);
|
|
21
|
+
}
|
|
22
|
+
const input = typeof source === 'string' ? source : source.toString('utf8');
|
|
23
|
+
const injection = `\n\nexport const ${resolvedExportName} = ${JSON.stringify(css)};\n`;
|
|
24
|
+
const output = `${input}${injection}`;
|
|
25
|
+
return output;
|
|
26
|
+
};
|
|
27
|
+
export default loader;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@knighted/css",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.1",
|
|
4
4
|
"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.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/css.js",
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
"types": "./dist/css.d.ts",
|
|
11
11
|
"import": "./dist/css.js",
|
|
12
12
|
"require": "./dist/cjs/css.cjs"
|
|
13
|
+
},
|
|
14
|
+
"./loader": {
|
|
15
|
+
"types": "./dist/loader.d.ts",
|
|
16
|
+
"import": "./dist/loader.js",
|
|
17
|
+
"require": "./dist/cjs/loader.cjs"
|
|
13
18
|
}
|
|
14
19
|
},
|
|
15
20
|
"engines": {
|
|
@@ -37,7 +42,8 @@
|
|
|
37
42
|
],
|
|
38
43
|
"scripts": {
|
|
39
44
|
"build": "duel",
|
|
40
|
-
"
|
|
45
|
+
"check-types": "tsc --noEmit",
|
|
46
|
+
"test": "c8 --reporter=text --reporter=text-summary --reporter=lcov --include \"src/**/*.ts\" tsx --test test/**/*.test.ts",
|
|
41
47
|
"prettier": "prettier --write .",
|
|
42
48
|
"prettier:check": "prettier --check .",
|
|
43
49
|
"lint": "oxlint src test",
|
|
@@ -66,7 +72,9 @@
|
|
|
66
72
|
},
|
|
67
73
|
"devDependencies": {
|
|
68
74
|
"@knighted/duel": "^2.1.6",
|
|
75
|
+
"@rspack/core": "^1.0.0",
|
|
69
76
|
"@types/less": "^3.0.8",
|
|
77
|
+
"@types/webpack": "^5.28.5",
|
|
70
78
|
"@vanilla-extract/css": "^1.15.2",
|
|
71
79
|
"@vanilla-extract/integration": "^8.0.6",
|
|
72
80
|
"@vanilla-extract/recipes": "^0.5.7",
|