@cssints/css-grammar 0.0.0-stage → 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Alexei Accio
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,179 @@
1
- # Temporary Holding Version
1
+ # CSS-in-TS
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ <img src="https://cssints.dev/logo.svg" alt="" width="96" height="96">
4
+
5
+ cssints is compile-time CSS-in-TypeScript. You write calls such as `css.flex().alignItems("center")`. A Vite plugin runs them at build time and replaces each one with atomic, content-hashed class names in `@layer`s. Nothing of the styling code ships to the browser except `cx` and the few lines behind `cv`, which join class names.
6
+
7
+ - Values are checked against the spec grammars (mdn-data) in the types and again at build time. A typo is an error in the editor and in the build.
8
+ - Conditions (`media`, `container`, `supports`, `hover`), variants (`cv`), design tokens and themes (`createTokens`, `createGlobalTheme`, `check.contrast`) are all build-time, and all typed.
9
+ - LightningCSS optimizes the output. Browser support (`@mdn/browser-compat-data` against your browserslist) is reported as warnings, except for what LightningCSS lowers for those targets.
10
+
11
+ Status: 0.1 is not published yet. Decisions and findings are in [docs/wayfinder.md](docs/wayfinder.md); the API reference is [docs/API.md](docs/API.md). Both are also a site with search and type hovers: [apps/docs](apps/docs) (`cd apps/docs && bun run dev`); deployed to Cloudflare with Alchemy: <https://cssints.dev>.
12
+
13
+ ## Install
14
+
15
+ Not on npm yet. In this repo `cssints` is a workspace package (`apps/demo` depends on `"cssints": "workspace:*"`). It needs Vite 8 (Vite+ works too) and, for the plugin, Node with type stripping (22.18 or later).
16
+
17
+ ```ts
18
+ // vite.config.ts
19
+ import cssints, { cssTarget, targets } from "cssints/vite";
20
+
21
+ export default {
22
+ plugins: [cssints()],
23
+ css: { transformer: "lightningcss", lightningcss: { targets } },
24
+ build: { cssMinify: "lightningcss", cssTarget },
25
+ };
26
+ ```
27
+
28
+ ```ts
29
+ // the entry, once
30
+ import "virtual:cssints.css";
31
+ ```
32
+
33
+ `targets` and `cssTarget` come from the browserslist query of the support warnings: the `BROWSERSLIST` variable, else the standard config (`"browserslist"` in `package.json` or `.browserslistrc`, with the `BROWSERSLIST_ENV`, `NODE_ENV` or `production` section), else `defaults`. The exports are computed when `vite.config.ts` is imported, before Vite's root is known, so they look the config up from `process.cwd()` and upward; the support warnings look it up from Vite's `root` (2026-10-09, cssints-p3d2). They are the same when Vite runs from the app's directory; with `vite <dir>` from elsewhere, give `targets` and `cssTarget` the app's config yourself. Vite's CSS minify reads `cssTarget`, not `targets`: without it the minifier merges the declarations of a `firstThatWorks` into the last one. Add `"types": ["vite/client"]` to `tsconfig.json` for the `virtual:` import.
34
+
35
+ ### Frameworks
36
+
37
+ Any framework that builds JSX through a Vite plugin works; the engine evaluates your modules itself, so it does not care what the plugin makes of JSX, nor in which order the plugins are listed. `apps/sky` is the Solid 2 app (`solid-js` and `@solidjs/web` 2.0.0-rc.13, `@solidjs/vite-plugin` 3.0.0-next.47):
38
+
39
+ ```ts skip
40
+ // vite.config.ts
41
+ import solid from "@solidjs/vite-plugin";
42
+ import cssints, { cssTarget, targets } from "cssints/vite";
43
+
44
+ export default {
45
+ plugins: [cssints(), solid()],
46
+ css: { transformer: "lightningcss", lightningcss: { targets } },
47
+ build: { cssMinify: "lightningcss", cssTarget },
48
+ };
49
+ ```
50
+
51
+ `tsconfig.json` is Solid's: `"jsx": "preserve"`, `"jsxImportSource": "@solidjs/web"`, `"types": ["vite/client"]`. Solid's `class` takes a string, an object or an array, so `cx` is not needed: `class={[card, on() && p(4)]}` and `class={badge({ tone: tone() })}` work as in React. A site's arguments are still build-time values, so a signal goes in the condition or in a `cv` variant, not in `p(n())`. Under `vite dev` an edit of a component replaces it and resets its state below it (Solid's refresh); an edit of a style changes the class strings and the sheet with no reload.
52
+
53
+ ```tsx
54
+ import { cx } from "cssints";
55
+ import { bg, color, cn, cv, flex, hover, media, p, style, token } from "cssints" with { type: "cssints" };
56
+
57
+ const md = media("(width >= 40rem)");
58
+ const accent = token.color("#2563eb", "accent");
59
+
60
+ const button = cv({
61
+ base: cn(p(2), color("white"), bg(accent), hover(bg("#1d4ed8"))),
62
+ variants: { wide: { true: md(p(4)) } },
63
+ });
64
+
65
+ export const Row = ({ wide, on }: { wide?: boolean; on?: boolean }) => (
66
+ <div className={cx(flex().alignItems("center").gap(2), on && p(1))} style={style(token.set(accent, "#dc2626"))}>
67
+ <button className={button({ wide })}>Save</button>
68
+ </div>
69
+ );
70
+ ```
71
+
72
+ `with { type: "cssints" }` marks what runs at build time. The plugin replaces each call with a class string, so the browser gets `cx("_1s47490 _1ob5z5m _g2iyum", on && "_lqk467")` and `button` is a call of the runtime with `{ base: "_1t8gqy5 ...", ... }`, so it never sees `flex()`. `virtual:cssints.css` is one sheet with every rule once, sorted by layer, then condition, then shorthand before longhand, then class name:
73
+
74
+ ```css
75
+ @layer _.k, _.a, _.b, _.c, _.d, _.e;
76
+ @property --accent{syntax:"<color>";inherits:true;initial-value:#2563eb}
77
+ @layer _.a {
78
+ ._1t8gqy5{padding:0.5rem}
79
+ ._lqk467{padding:0.25rem}
80
+ ._g2iyum{gap:0.5rem}
81
+ ._17xzvrh{background-color:var(--accent)}
82
+ ._1binn6n{color:white}
83
+ ._1ob5z5m{align-items:center}
84
+ ._1s47490{display:flex}
85
+ }
86
+ @layer _.b {
87
+ ._1tsjws2:hover{background-color:#1d4ed8}
88
+ @media (width >= 40rem) {
89
+ ._tdcza3{padding:1rem}
90
+ }
91
+ }
92
+ ```
93
+
94
+ ### In a terminal
95
+
96
+ `import * as css from "cssints/tui" with { type: "cssints" }` gives the members a terminal has (cells, colours, borders, flex, states, `cursor`), and the plugin compiles them to prop objects for [OpenTUI](https://opentui.com) renderables instead of classes; `@cssints/tui` applies their states and themes: [packages/tui](packages/tui/README.md), docs/API.md "TUI".
97
+
98
+ More in [docs/API.md](docs/API.md): property names and short names, `flex()` and `grid()` chains, conditions and layers, tokens and themes, `cx`, `cn` and `cv`, and the errors you can get.
99
+
100
+ ## Debugging
101
+
102
+ `CSSINTS_TRACE=otlp bun run build` (or `dev`) sends a trace of every Vite hook, with the file of each module, to a local OpenTelemetry backend; unset, it costs nothing. How to start the backend in one `docker run`, and the spans: [docs/API.md](docs/API.md#debugging-traces-of-the-engine-cssints_trace-cssints-r2qq).
103
+
104
+ ## For agents
105
+
106
+ `cssints` ships two [agent skills](https://agentskills.io) in its package, `skills/cssints` (writing styles: setup,
107
+ `cn`/`cx`/`cv`, conditions, tokens and themes, the plugin packages, build errors) and `skills/cssints-plugin` (writing
108
+ a plugin on `scope()`, with its tests). Install them into your agent's skills folder:
109
+
110
+ ```bash
111
+ npx skills-npm # links the skills of every installed package that ships some
112
+ npx skills add ./node_modules/cssints/skills # or the skills CLI, for opencode, Claude Code, Codex, Cursor, ...
113
+ ```
114
+
115
+ Until 0.1 is on npm, point `npx skills add` at `packages/cssints/skills` of this repository. Agents working on this
116
+ repository get `.agents/skills/cssints-contributor` (also `.claude/skills`) with no install.
117
+
118
+ `@cssints/mcp` is an MCP server with the build's own checks (`check_declaration`, `check_support`, `check_contrast`,
119
+ `compile`, `search_docs`, `list_plugins`), in [packages/mcp](packages/mcp) with the config for opencode, Claude Code
120
+ and Cursor. The skills use it when it is configured and work without it.
121
+
122
+ ## Layout
123
+
124
+ | Path | What |
125
+ |---|---|
126
+ | `packages/cssints` | Vite plugin (`cssints/vite`) on the Vite Module Runner, and the `cssints` library |
127
+ | `packages/css-grammar` | Type-level recognizer and its runtime twin over mdn-data grammars, Typed OM values, browser support and prose rules |
128
+ | `packages/icons` | `@cssints/icons`: icons from iconify JSON collections or SVG folders, a mask style with no runtime |
129
+ | `packages/mise-en-mode` | `@cssints/mise-en-mode`: looks as themes in the layers app < author < viewer, on `data-mode` scopes, checked against the tokens and by `check.contrast` |
130
+ | `apps/demo` | React demo of every 0.1 feature |
131
+ | `apps/docs` | The documentation site: this file and `docs/API.md` as pages, with search and type hovers (`cd apps/docs && bun run dev`) |
132
+ | `docs/research`, `docs/archive` | Comparisons with other libraries; outdated plans |
133
+
134
+ ## Development
135
+
136
+ ```bash
137
+ bun install
138
+ bun run test # all packages (vp run -r test, cached where possible); includes the type-check of the code in docs/API.md and this file
139
+ bun run bench # type instantiation budgets (tsc and tsgo)
140
+ bun run check # format, lint, type-check (vp check), then the Effect diagnostics (bun run typecheck)
141
+ bun run diff:check # the added lines against master: anti-slop rules, key-file changes (tools/ai-automation)
142
+ cd apps/demo && bun run dev
143
+ cd apps/docs && bun run dev # the documentation site, on the pages of README.md and docs/API.md
144
+ ```
145
+
146
+ See [AGENTS.md](AGENTS.md) for the layout, commands and code style.
147
+
148
+ ## Credits
149
+
150
+ cssints is MIT. It stands on other people's work; this is what we took, from whom, and under which license. A package that copies data or code carries the upstream notice (a Credits or Licenses section of its README, or `THIRD_PARTY_NOTICES.md`).
151
+
152
+ ### Data and code that ships
153
+
154
+ | Where | What | From | License |
155
+ |---|---|---|---|
156
+ | [@cssints/tokens](packages/tokens/README.md) | the Open Props, Radix Colors, Flexoki and Reasonable Colors sets | [Open Props](https://github.com/argyleink/open-props) (Adam Argyle), [Radix Colors](https://github.com/radix-ui/colors), [Flexoki](https://github.com/kepano/flexoki) (Steph Ango), [Reasonable Colors](https://github.com/matthewhowell/reasonable-colors) (Matthew Howell) | MIT, notices in its README |
157
+ | [@cssints/css-grammar](packages/css-grammar/THIRD_PARTY_NOTICES.md) | the CSS syntax strings (`src/mdn.ts`, `keywords.ts`, `functions.ts`), also in the types of `cssints` | [mdn-data](https://github.com/mdn/data); the numeric ranges it omits from [@webref/css](https://github.com/w3c/webref) | CC0-1.0; MIT (W3C) |
158
+ | @cssints/css-grammar | browser support warnings, read from the package (not copied) | [@mdn/browser-compat-data](https://github.com/mdn/browser-compat-data) | CC0-1.0 |
159
+ | [@cssints/fonts](packages/fonts/README.md) | the latin character weights and the metrics of Arial, Times New Roman and Courier New | [Capsize](https://github.com/seek-oss/capsize) (SEEK) | MIT, notice in its README |
160
+ | [@cssints/icons](packages/icons/README.md) | nothing: the collections are your dependency | [Iconify](https://iconify.design) `@iconify-json/*` | each collection's own |
161
+ | `tools/ai-automation/vendor` | lint rules, not published | [dmmulroy/anti-slop](https://github.com/dmmulroy/anti-slop) (Dillon Mulroy) | MIT, `LICENSE` and `UPSTREAM.md` |
162
+ | [apps/sky](apps/sky/NOTICE.md) | layout and token values; Inter; Phosphor icons | Mu and `@bsky.app/alf` (Bluesky Social PBC), [Inter](https://github.com/rsms/inter), [Phosphor](https://phosphoricons.com) | MIT; OFL-1.1; MIT |
163
+ | apps/demo | Material Design Icons (`@iconify-json/mdi`) | Pictogrammers | Apache-2.0 |
164
+
165
+ ### Ideas and designs
166
+
167
+ No code is copied from these; each package README says which part follows which.
168
+
169
+ - **Plugin packages.** [Every Layout](https://every-layout.dev) and [smolcss](https://smolcss.dev) (layout), [nytimes/text-balancer](https://github.com/nytimes/text-balancer) and [react-wrap-balancer](https://github.com/shuding/react-wrap-balancer) (balance), [fluid-tailwind](https://github.com/barvian/fluid-tailwind) (fluid), [postcss-easing-gradients](https://github.com/larsenwork/postcss-easing-gradients) (gradient), [tailwindcss-motion](https://github.com/romboHQ/tailwindcss-motion) and [tailwindcss-animate](https://github.com/jamiebuilds/tailwindcss-animate) (motion), [the supersonic plugin](https://github.com/the-illarionov/the-supersonic-plugin-for-scroll-based-animation) and [lax.js](https://github.com/alexfoxy/lax.js) (reveal), [sync-skeleton](https://github.com/crutchcorn/sync-skeleton) (skeleton), [bramus/sticky-observer](https://github.com/bramus/sticky-observer) (stuck), [unpic-placeholder](https://github.com/ascorbic/unpic-placeholder) and [ThumbHash](https://github.com/evanw/thumbhash) (placeholder), [nutelch](https://github.com/meodai/nutelch) and [Leonardo](https://github.com/adobe/leonardo) (ramp), the resets of Josh Comeau, Jake Lazaroff, Vale and Andy Bell (reset), [Mise en Mode](https://standard.mode.place) and its Mode Contract (mise-en-mode), [brendonovich/vite-plugin-opencode](https://github.com/Brendonovich/vite-plugin-opencode) (the prompt box of devtools).
170
+ - **The engine.** The comparisons in [docs/research](docs/research) record what we took from [StyleX](https://stylexjs.com), [vanilla-extract](https://vanilla-extract.style), [Tailwind CSS](https://tailwindcss.com), [Tokenami](https://tokenami.com), [SugarCube](https://sugarcube.sh) and the [React Spectrum](https://react-spectrum.adobe.com) style macros; the first spark was [Haz's post](https://bsky.app/profile/haz.dev/post/3mebm2mx6jk27) on a CSS framework in TypeScript. Tokens follow the [Design Tokens Community Group](https://www.designtokens.org) format.
171
+ - **apps/sky** follows the look of Mu and the hue slider of Witchsky.
172
+
173
+ ### Built on
174
+
175
+ [Effect](https://effect.website), [Vite](https://vite.dev) and Vite+, [OXC](https://oxc.rs), [LightningCSS](https://lightningcss.dev) (MPL-2.0; its wasm build runs in the playground), [culori](https://culorijs.org), [TypeScript](https://www.typescriptlang.org) and TypeScript Go, [Bun](https://bun.sh), [Solid](https://www.solidjs.com), [OpenTUI](https://opentui.com), [Shiki](https://shiki.style), [Monaco Editor](https://microsoft.github.io/monaco-editor/), [Playwright](https://playwright.dev), [fast-check](https://fast-check.dev), [Quint](https://quint-lang.org), [atcute](https://github.com/mary-ext/atcute), and [Beans](https://github.com/hmans/beans) for the tickets. Phosphor icons (MIT) are on this site through `@iconify-json/ph`. Every dependency keeps its own license; none is copied into the repository.
176
+
177
+ ## License
178
+
179
+ MIT
@@ -0,0 +1,33 @@
1
+ # Third-party notices
2
+
3
+ The code of `@cssints/css-grammar` is MIT (`LICENSE`). The generated data in `src/` (and `dist/`) keeps the license of its
4
+ source.
5
+
6
+ ## Data
7
+
8
+ - **mdn-data** (`src/mdn.ts`, `src/keywords.ts`, `src/functions.ts`): the CSS syntax strings, copied verbatim by
9
+ `bun run extract`. CC0 1.0 Universal, the mdn-data contributors. <https://github.com/mdn/data>
10
+ - **@webref/css** (`src/mdn.ts`): the numeric ranges that mdn-data leaves out, copied by `bun run extract`. MIT,
11
+ Copyright (c) 2020 World Wide Web Consortium. <https://github.com/w3c/webref>
12
+ - **@mdn/browser-compat-data**: read at build time for the support warnings, not copied. CC0 1.0 Universal.
13
+ <https://github.com/mdn/browser-compat-data>
14
+ - `src/longhands.ts` and `scripts/webref-properties.json` are written by `bun run longhands` and `bun run webref`, which
15
+ ask Chrome how it expands a shorthand and what it accepts. They hold facts, no text of a source.
16
+
17
+ The MIT notice of @webref/css, Copyright (c) 2020 World Wide Web Consortium:
18
+
19
+ > Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
20
+ > documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
21
+ > rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit
22
+ > persons to whom the Software is furnished to do so, subject to the following conditions:
23
+ >
24
+ > The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
25
+ > Software.
26
+ >
27
+ > THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
28
+ > WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
29
+ > COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
30
+ > OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31
+
32
+ CC0 1.0 Universal needs no notice. The full text is in the `LICENSE` of the `mdn-data` and `@mdn/browser-compat-data`
33
+ packages.
@@ -0,0 +1,6 @@
1
+ import { Condition } from "./query.mjs";
2
+ //#region src/api-query.d.ts
3
+ export declare const media: Condition<"media", string>;
4
+ export declare const container: Condition<"container", string>;
5
+ export declare const supports: Condition<"supports", string>;
6
+ //#endregion
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,18 @@
1
+ import { properties } from "./mdn.mjs";
2
+ import { Sentinel, Valid } from "./css.mjs";
3
+ import { CSSStyleValue } from "./values.mjs";
4
+ //#region src/api-typed.d.ts
5
+ type Part = string | CSSStyleValue;
6
+ type Text<P> = P extends string ? P : P extends CSSStyleValue<infer T> ? Sentinel<T> : never;
7
+ export type Join<A extends readonly Part[]> = A extends readonly [infer H, ...infer R extends readonly Part[]] ? R extends readonly [] ? Text<H> : `${Text<H>} ${Join<R>}` : "";
8
+ type Check<P extends keyof typeof properties, A extends readonly Part[]> = Valid<P, Join<A>> extends (infer V) ? (V extends Join<A> ? A : { [K in keyof A]: V; }) : never;
9
+ export type TypedPropertyFn<P extends keyof typeof properties> = <const A extends readonly Part[]>(...parts: Check<P, A>) => string;
10
+ export declare const padding: TypedPropertyFn<"padding">;
11
+ export declare const margin: TypedPropertyFn<"margin">;
12
+ export declare const width: TypedPropertyFn<"width">;
13
+ export declare const gap: TypedPropertyFn<"gap">;
14
+ export declare const border: TypedPropertyFn<"border">;
15
+ export declare const color: TypedPropertyFn<"color">;
16
+ export declare const opacity: TypedPropertyFn<"opacity">;
17
+ export declare const fontSize: TypedPropertyFn<"font-size">;
18
+ //#endregion
@@ -0,0 +1 @@
1
+ export {};
package/dist/api.d.mts ADDED
@@ -0,0 +1,13 @@
1
+ import { PropertyFn } from "./css.mjs";
2
+ //#region src/api.d.ts
3
+ export declare const padding: PropertyFn<"padding">;
4
+ export declare const margin: PropertyFn<"margin">;
5
+ export declare const width: PropertyFn<"width">;
6
+ export declare const gap: PropertyFn<"gap">;
7
+ export declare const border: PropertyFn<"border">;
8
+ export declare const color: PropertyFn<"color">;
9
+ export declare const alignItems: PropertyFn<"align-items">;
10
+ export declare const display: PropertyFn<"display">;
11
+ export declare const borderRadius: PropertyFn<"border-radius">;
12
+ export declare const fontSize: PropertyFn<"font-size">;
13
+ //#endregion
package/dist/api.mjs ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { BrowsersError } from "./errors.mjs";
2
+ import { Effect } from "effect";
3
+ //#region src/browsers.d.ts
4
+ export declare const browsers: (query?: string | undefined, path?: string | undefined) => Effect.Effect<string[], BrowsersError, never>;
5
+ //#endregion
@@ -0,0 +1,15 @@
1
+ import { BrowsersError } from "./errors.mjs";
2
+ import { Effect } from "effect";
3
+ //#region src/browsers.ts
4
+ const browserslist = process.getBuiltinModule("node:module").createRequire(import.meta.url)("browserslist");
5
+ const browsers = Effect.fn("browsers")(function* (query, path) {
6
+ return yield* Effect.try({
7
+ try: () => browserslist(query, path === void 0 ? void 0 : { path }),
8
+ catch: (e) => new BrowsersError({
9
+ message: e instanceof Error ? e.message : String(e),
10
+ query
11
+ })
12
+ });
13
+ });
14
+ //#endregion
15
+ export { browsers };
@@ -0,0 +1,17 @@
1
+ import { QueryKind } from "./query.mjs";
2
+ import { RuleError, TwinError } from "./errors.mjs";
3
+ import { Warning } from "./support.mjs";
4
+ import { Effect } from "effect";
5
+ //#region src/check.d.ts
6
+ export type DeclarationError = TwinError | RuleError;
7
+ export declare const isDeclarationError: (e: unknown) => e is DeclarationError;
8
+ export declare function checkDeclarationNow(p: string, v: string, warnings: boolean): Warning[];
9
+ export declare const checkValueNow: (label: string, grammar: string, v: string) => void;
10
+ /** The support warnings of `p: v` (none unless `warnings`), or why the declaration is invalid. */
11
+ export declare const checkDeclaration: (p: string, v: string, warnings: boolean) => Effect.Effect<Warning[], DeclarationError, never>;
12
+ export declare function checkQueryNow(kind: QueryKind, q: string): void;
13
+ /** Twin of ValidQuery<K, S> (query.ts): the query of a media(), container() or supports() call is valid, or why not. */
14
+ export declare const checkQuery: (kind: "media" | "container" | "supports", q: string) => Effect.Effect<void, TwinError, never>;
15
+ /** The selector of a `css.global()` call is a <complex-selector-list>, or why not (selector.ts). */
16
+ export declare const checkSelector: (selector: string) => Effect.Effect<void, TwinError, never>;
17
+ //#endregion
package/dist/check.mjs ADDED
@@ -0,0 +1,85 @@
1
+ import { InvalidValueError, RuleError, isTwinError } from "./errors.mjs";
2
+ import { syntaxes } from "./mdn.mjs";
3
+ import { ENTRY } from "./query.mjs";
4
+ import { consume, parseGrammar, spaced, valid } from "./twin-core.mjs";
5
+ import { violation } from "./rules.mjs";
6
+ import { checkSelectorNow } from "./selector.mjs";
7
+ import { supportOf } from "./support.mjs";
8
+ import { Effect } from "effect";
9
+ //#region src/check.ts
10
+ const isDeclarationError = (e) => isTwinError(e) || e instanceof RuleError;
11
+ const passed = /* @__PURE__ */ new Set();
12
+ function checkDeclarationNow(p, v, warnings) {
13
+ if (p.startsWith("--")) return [];
14
+ const key = `${p}\0${v}`;
15
+ if (!passed.has(key)) {
16
+ valid(p, v);
17
+ const error = violation(p, v);
18
+ if (error) throw error;
19
+ passed.add(key);
20
+ }
21
+ return warnings ? supportOf(p, v) : [];
22
+ }
23
+ const checkValueNow = (label, grammar, v) => {
24
+ valid(label, v, grammar);
25
+ };
26
+ /** The support warnings of `p: v` (none unless `warnings`), or why the declaration is invalid. */
27
+ const checkDeclaration = Effect.fnUntraced(function* (p, v, warnings) {
28
+ return yield* Effect.try({
29
+ try: () => checkDeclarationNow(p, v, warnings),
30
+ catch: (e) => {
31
+ if (isDeclarationError(e)) return e;
32
+ throw e;
33
+ }
34
+ });
35
+ });
36
+ function checkQueryNow(kind, q) {
37
+ const syntax = syntaxes;
38
+ const v = spaced(q).trimStart();
39
+ const ctx = {
40
+ len: v.length,
41
+ far: -1,
42
+ expected: /* @__PURE__ */ new Set()
43
+ };
44
+ const rs = consume(parseGrammar(syntax[ENTRY[kind]]), v, ctx);
45
+ if (rs.has("")) return;
46
+ for (const r of rs) {
47
+ const at = v.length - r.length;
48
+ if (at > ctx.far) Object.assign(ctx, {
49
+ far: at,
50
+ expected: /* @__PURE__ */ new Set()
51
+ });
52
+ if (at === ctx.far) ctx.expected.add("end of query");
53
+ }
54
+ const token = /^[^ ,)]*/.exec(v.slice(ctx.far))[0];
55
+ const expected = [...ctx.expected];
56
+ throw new InvalidValueError({
57
+ message: `@${kind}: ${token ? `unexpected "${token}"` : "unexpected end"} in "${q}", expected ${expected.join(" | ")}`,
58
+ property: `@${kind}`,
59
+ value: q,
60
+ position: ctx.far,
61
+ expected
62
+ });
63
+ }
64
+ /** Twin of ValidQuery<K, S> (query.ts): the query of a media(), container() or supports() call is valid, or why not. */
65
+ const checkQuery = Effect.fnUntraced(function* (kind, q) {
66
+ return yield* Effect.try({
67
+ try: () => checkQueryNow(kind, q),
68
+ catch: (e) => {
69
+ if (isTwinError(e)) return e;
70
+ throw e;
71
+ }
72
+ });
73
+ });
74
+ /** The selector of a `css.global()` call is a <complex-selector-list>, or why not (selector.ts). */
75
+ const checkSelector = Effect.fnUntraced(function* (selector) {
76
+ return yield* Effect.try({
77
+ try: () => checkSelectorNow(selector),
78
+ catch: (e) => {
79
+ if (isTwinError(e)) return e;
80
+ throw e;
81
+ }
82
+ });
83
+ });
84
+ //#endregion
85
+ export { checkDeclaration, checkDeclarationNow, checkQuery, checkQueryNow, checkSelector, checkValueNow, isDeclarationError };