@symbiote-native/css-parser 0.3.0 → 0.5.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/README.md +33 -31
- package/build/generate-dts/index.js +3 -8
- package/build/generate-dts-cli.js +5 -1
- package/build/golden-corpus/fixtures/ScopedGlobalDemo.svelte +25 -0
- package/build/golden-corpus/fixtures/ScopedGlobalDemo.svelte.d.ts +3 -0
- package/build/index.d.ts +8 -7
- package/build/index.js +4 -4
- package/build/lightning/declarations.d.ts +46 -0
- package/build/lightning/declarations.js +885 -0
- package/build/lightning/rules.d.ts +52 -0
- package/build/lightning/rules.js +157 -0
- package/build/lightning/selectors.d.ts +23 -0
- package/build/lightning/selectors.js +468 -0
- package/build/metro-css-module/index.d.ts +30 -2
- package/build/metro-css-module/index.js +142 -36
- package/build/preprocessors/index.js +4 -3
- package/build/properties.d.ts +5 -6
- package/build/properties.js +26 -14
- package/build/scoped-classes.d.ts +23 -0
- package/build/scoped-classes.js +35 -0
- package/build/values.d.ts +3 -7
- package/build/values.js +3 -60
- package/package.json +13 -3
- package/typescript-plugin.cjs +21 -14
- package/build/global-selectors.d.ts +0 -25
- package/build/global-selectors.js +0 -98
- package/build/parser/index.d.ts +0 -59
- package/build/parser/index.js +0 -338
package/README.md
CHANGED
|
@@ -16,9 +16,10 @@ CSS Modules all work identically regardless of source language.
|
|
|
16
16
|
|
|
17
17
|
## Install
|
|
18
18
|
|
|
19
|
-
Not installed directly by an app —
|
|
20
|
-
`@symbiote-native/vue`, `@symbiote-native/
|
|
21
|
-
|
|
19
|
+
Not installed directly by an app — every adapter package (`@symbiote-native/react`,
|
|
20
|
+
`@symbiote-native/vue`, `@symbiote-native/svelte`, `@symbiote-native/solid`,
|
|
21
|
+
`@symbiote-native/angular`) already depends on it and re-exports it via its own
|
|
22
|
+
`./metro-css-parser` subpath. Writing a Metro transformer for a new adapter yourself:
|
|
22
23
|
|
|
23
24
|
```bash
|
|
24
25
|
npm install @symbiote-native/css-parser
|
|
@@ -27,10 +28,11 @@ npm install @symbiote-native/css-parser
|
|
|
27
28
|
## Who calls this, and how
|
|
28
29
|
|
|
29
30
|
**An app never imports this package directly.** It runs only inside a Metro transformer, on the
|
|
30
|
-
Node build machine — never shipped in the app's native JS bundle.
|
|
31
|
-
(`@symbiote-native/react`, `@symbiote-native/vue`, `@symbiote-native/
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
Node build machine — never shipped in the app's native JS bundle. Every adapter package
|
|
32
|
+
(`@symbiote-native/react`, `@symbiote-native/vue`, `@symbiote-native/svelte`,
|
|
33
|
+
`@symbiote-native/solid`, `@symbiote-native/angular`) depends on `@symbiote-native/css-parser` as a
|
|
34
|
+
regular dependency and re-exports it via its own `./metro-css-parser` subpath, so a consuming app's
|
|
35
|
+
`metro.config.js` wires:
|
|
34
36
|
|
|
35
37
|
```js
|
|
36
38
|
// metro.config.js
|
|
@@ -70,20 +72,19 @@ import './theme.css'; // plain CSS — registers classes globally, no export
|
|
|
70
72
|
│ (build time, Metro) │ (runtime, all adapters)
|
|
71
73
|
▼ ▼
|
|
72
74
|
@symbiote-native/css-parser @symbiote-native/engine's style-registry
|
|
73
|
-
preprocessors
|
|
75
|
+
preprocessors → lightning (compileCssToRules) registerRules() / resolveClassName()
|
|
74
76
|
```
|
|
75
77
|
|
|
76
78
|
A preprocessor source is reduced to plain CSS text first (`compileScss`/`compileSass`/
|
|
77
|
-
`compileLess`/`compileStylus`); `
|
|
78
|
-
mechanism below runs identically regardless of source language.
|
|
79
|
+
`compileLess`/`compileStylus`); `compileCssToRules()` is the single downstream consumer either way,
|
|
80
|
+
so every mechanism below runs identically regardless of source language.
|
|
79
81
|
|
|
80
82
|
## API surface
|
|
81
83
|
|
|
82
84
|
```ts
|
|
83
85
|
import {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
kebabToCamel, // core compiler
|
|
86
|
+
compileCssToRules, // core compiler
|
|
87
|
+
compileScopedCss, // a scoped <style> block, rules + name map
|
|
87
88
|
compileCssFile,
|
|
88
89
|
isCssModuleFile, // standalone .css/.module.css files
|
|
89
90
|
createCssMetroTransformer, // Metro babelTransformerPath factory
|
|
@@ -96,29 +97,28 @@ import {
|
|
|
96
97
|
isStyleFile,
|
|
97
98
|
classNamesToDtsSource,
|
|
98
99
|
generateModuleDts, // .d.ts generation for CSS Modules typing
|
|
99
|
-
globalClassNamesIn,
|
|
100
|
-
globalClassTokensIn,
|
|
101
100
|
hashFilePath,
|
|
102
101
|
} from '@symbiote-native/css-parser';
|
|
103
102
|
```
|
|
104
103
|
|
|
105
|
-
- **`
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
104
|
+
- **`compileCssToRules(css, { filename, pattern?, remToPx? })`** — the compiler core: one
|
|
105
|
+
lightningcss pass over the typed AST, resolving `var()`/`calc()` and emitting a `rules` array.
|
|
106
|
+
Each rule carries the class TOKENS its selector was written with — as authored, never camelCased
|
|
107
|
+
or collapsed into a single key — plus specificity and source order, so the registry matches by
|
|
108
|
+
token subset instead of reversing a guess. With a CSS-Modules `pattern` it also returns the
|
|
109
|
+
authored→renamed `exports` map and `globals`, the tokens lightningcss did NOT rename, which is
|
|
110
|
+
exactly the set the author put inside `:global(...)`. A selector containing a pseudo-class
|
|
111
|
+
(`:hover`, …) is dropped whole — RN has no pseudo-class concept, so there is no
|
|
112
|
+
partial-application semantics to preserve.
|
|
113
|
+
- **`compileScopedCss(css, { filename, pattern })`** — the scoped-block form (a Svelte `<style>`, a
|
|
114
|
+
Vue `<style scoped>`): the same rules plus the authored→scoped name map its markup rewriter
|
|
115
|
+
resolves every class token through. Both halves come out of ONE compile, so the style side and
|
|
116
|
+
the markup side cannot disagree on a name.
|
|
110
117
|
- **`compileCssFile` / `isCssModuleFile`** — the standalone-file form: `Card.module.css`'s classes
|
|
111
118
|
are always scoped to a per-file hash and its default export is the name→scopedName map; a plain
|
|
112
119
|
`.css` file registers globally via a side-effect import.
|
|
113
120
|
- **`createCssMetroTransformer`** — wraps an upstream RN Babel transformer, detecting a stylesheet
|
|
114
121
|
extension and compiling it before delegating everything else unchanged.
|
|
115
|
-
- **`globalClassNamesIn` / `globalClassTokensIn`** — the two halves of `:global()`, and they answer
|
|
116
|
-
different questions. The first returns registered KEYS whose selector was global in full, so the
|
|
117
|
-
key itself skips scoping. The second returns MARKUP TOKENS that came out of a `:global(...)`
|
|
118
|
-
payload wherever it sat, including inside an otherwise-scoped selector — `.card :global(.reset)`
|
|
119
|
-
yields the key `cardReset` from neither, and the token `reset` from the second. A caller that
|
|
120
|
-
suffixes class names needs both: exempting only by key leaves a partial global's token
|
|
121
|
-
scope-mangled, exempting only by token leaves a fully global compound's rule dead.
|
|
122
122
|
- **Preprocessors** — `sass`/`less`/`stylus` are lazy, **optional** `devDependencies`: a project
|
|
123
123
|
that never authors `.scss`/`.less`/`.styl` never installs any of the three.
|
|
124
124
|
- **CSS Modules type safety** — `css-dts` (bin) walks a directory and writes a real `<file>.d.ts`
|
|
@@ -135,18 +135,20 @@ import {
|
|
|
135
135
|
- It does not implement Tailwind CSS — that needs whole-project class scanning and JIT utility
|
|
136
136
|
generation, a fundamentally different shape than "one source file reduces to CSS text", and is
|
|
137
137
|
being designed as a separate, future package.
|
|
138
|
-
- It supports `scoped` / `:global()` / CSS Modules and SCSS/Sass/Less/Stylus preprocessing
|
|
138
|
+
- It supports `scoped` / `:global()` / CSS Modules and SCSS/Sass/Less/Stylus preprocessing —
|
|
139
|
+
including Svelte's own `<style>` block (its preprocessor calls this package's
|
|
140
|
+
`compileScopedCss`, the same compile every Vue `<style scoped>` block goes through) — and it does
|
|
139
141
|
not yet generate a typed `.d.ts` for an **inline** Vue `<style module>` block (only standalone
|
|
140
142
|
`.module.css` files get the strict, no-index-signature type — Vue's own Volar plugin gives inline
|
|
141
|
-
blocks a looser, typo-tolerant type for free)
|
|
142
|
-
exists in SymbioteNative today).
|
|
143
|
+
blocks a looser, typo-tolerant type for free).
|
|
143
144
|
|
|
144
145
|
## Related packages
|
|
145
146
|
|
|
146
|
-
- [`@symbiote-native/engine`](../engine) — owns the runtime `style-registry` (`
|
|
147
|
+
- [`@symbiote-native/engine`](../engine) — owns the runtime `style-registry` (`registerRules` /
|
|
147
148
|
`resolveClassName`) this package's compiled output resolves against, and the class+style merge
|
|
148
149
|
used by every adapter.
|
|
149
150
|
- [`@symbiote-native/react`](../../adapters/react) / [`@symbiote-native/vue`](../../adapters/vue) /
|
|
151
|
+
[`@symbiote-native/svelte`](../../adapters/svelte) / [`@symbiote-native/solid`](../../adapters/solid) /
|
|
150
152
|
[`@symbiote-native/angular`](../../adapters/angular) — each depends on this package directly and
|
|
151
153
|
re-exports it via its own `./metro-css-parser` subpath, so a consuming app needs no extra install
|
|
152
154
|
step.
|
|
@@ -8,9 +8,7 @@
|
|
|
8
8
|
// appended rather than replaced — e.g. `Card.module.css.d.ts`) so TypeScript's own module
|
|
9
9
|
// resolution picks it up for a default import of `Card.module.css` without a separate
|
|
10
10
|
// registration step, the same convention typed-css-modules uses.
|
|
11
|
-
import {
|
|
12
|
-
import { compile, detectLanguage } from "../preprocessors/index.js";
|
|
13
|
-
import { isCssModuleFile } from "../metro-css-module/index.js";
|
|
11
|
+
import { isCssModuleFile, moduleClassNames, } from "../metro-css-module/index.js";
|
|
14
12
|
const IDENTIFIER_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
|
|
15
13
|
function formatKey(className) {
|
|
16
14
|
return IDENTIFIER_RE.test(className) ? className : JSON.stringify(className);
|
|
@@ -31,13 +29,10 @@ export function classNamesToDtsSource(classNames) {
|
|
|
31
29
|
].join('\n');
|
|
32
30
|
}
|
|
33
31
|
// Mirrors compileCssFile's own module/non-module branch: a plain (non-`.module.*`) style file
|
|
34
|
-
// has no default export to type (
|
|
32
|
+
// has no default export to type (registerRules() runs as a side effect only), so it gets no
|
|
35
33
|
// `.d.ts` at all.
|
|
36
34
|
export async function generateModuleDts(source, filename) {
|
|
37
35
|
if (!isCssModuleFile(filename))
|
|
38
36
|
return null;
|
|
39
|
-
|
|
40
|
-
const css = lang === 'css' ? source : await compile(source, lang, filename);
|
|
41
|
-
const parsed = parseCSS(css, { filename });
|
|
42
|
-
return classNamesToDtsSource(Object.keys(parsed));
|
|
37
|
+
return classNamesToDtsSource(await moduleClassNames(source, filename));
|
|
43
38
|
}
|
|
@@ -17,7 +17,11 @@ import * as path from 'node:path';
|
|
|
17
17
|
import { isStyleFile } from "./preprocessors/index.js";
|
|
18
18
|
import { isCssModuleFile } from "./metro-css-module/index.js";
|
|
19
19
|
import { generateModuleDts } from "./generate-dts/index.js";
|
|
20
|
-
const SKIPPED_DIR_NAMES = new Set([
|
|
20
|
+
const SKIPPED_DIR_NAMES = new Set([
|
|
21
|
+
'node_modules',
|
|
22
|
+
'build',
|
|
23
|
+
'.git',
|
|
24
|
+
]);
|
|
21
25
|
async function collectModuleStyleFiles(root) {
|
|
22
26
|
const stat = await fs.stat(root);
|
|
23
27
|
if (!stat.isDirectory()) {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
// Fixture, not a running component. examples/svelte has exactly one styled component and it
|
|
3
|
+
// carries no :global(), so the escape hatch had no golden coverage on this adapter either.
|
|
4
|
+
let isLoud = $state(false);
|
|
5
|
+
</script>
|
|
6
|
+
|
|
7
|
+
<View class="panel">
|
|
8
|
+
<View class="panel wide" />
|
|
9
|
+
<View class={['panel', isLoud && 'wide']} />
|
|
10
|
+
<View class="untouched" />
|
|
11
|
+
</View>
|
|
12
|
+
|
|
13
|
+
<style>
|
|
14
|
+
.panel {
|
|
15
|
+
padding: 9px;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.panel.wide {
|
|
19
|
+
padding: 19px;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
:global(.untouched) {
|
|
23
|
+
margin: 0;
|
|
24
|
+
}
|
|
25
|
+
</style>
|
package/build/index.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export type {
|
|
3
|
-
export { globalClassNamesIn, globalClassTokensIn } from './global-selectors.ts';
|
|
1
|
+
export { compileCssToRules } from './lightning/rules.ts';
|
|
2
|
+
export type { ICompiledCss, ICompileRulesOptions, IStyleRule, } from './lightning/rules.ts';
|
|
4
3
|
export { hashFilePath } from './file-scope-id.ts';
|
|
5
|
-
export {
|
|
6
|
-
export type {
|
|
7
|
-
export {
|
|
4
|
+
export { compileScopedCss } from './scoped-classes.ts';
|
|
5
|
+
export type { IScopedCss, IScopedCssOptions } from './scoped-classes.ts';
|
|
6
|
+
export { compileCssFile, compileCssModule, isCssModuleFile, } from './metro-css-module/index.ts';
|
|
7
|
+
export type { ICompiledCssFile, ICompiledCssModule, } from './metro-css-module/index.ts';
|
|
8
|
+
export { classNamesToDtsSource, generateModuleDts, } from './generate-dts/index.ts';
|
|
8
9
|
export { createCssMetroTransformer, resolveUpstreamTransformer, } from './metro-transformer/index.ts';
|
|
9
|
-
export type { IMetroTransformer, IMetroTransformParams } from './metro-transformer/index.ts';
|
|
10
|
+
export type { IMetroTransformer, IMetroTransformParams, } from './metro-transformer/index.ts';
|
|
10
11
|
export { compileScss, compileSass, compileLess, compileStylus, compile, detectLanguage, isStyleFile, } from './preprocessors/index.ts';
|
|
11
12
|
export type { IPreprocessorLanguage } from './preprocessors/index.ts';
|
package/build/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export {
|
|
2
|
-
export { globalClassNamesIn, globalClassTokensIn } from "./global-selectors.js";
|
|
1
|
+
export { compileCssToRules } from "./lightning/rules.js";
|
|
3
2
|
export { hashFilePath } from "./file-scope-id.js";
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
3
|
+
export { compileScopedCss } from "./scoped-classes.js";
|
|
4
|
+
export { compileCssFile, compileCssModule, isCssModuleFile, } from "./metro-css-module/index.js";
|
|
5
|
+
export { classNamesToDtsSource, generateModuleDts, } from "./generate-dts/index.js";
|
|
6
6
|
export { createCssMetroTransformer, resolveUpstreamTransformer, } from "./metro-transformer/index.js";
|
|
7
7
|
export { compileScss, compileSass, compileLess, compileStylus, compile, detectLanguage, isStyleFile, } from "./preprocessors/index.js";
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An OBJECT-shaped RN style value. `core/engine/src/styles.ts` has exactly one such field,
|
|
3
|
+
* `shadowOffset?: { width: number; height: number }`; RN's `textShadowOffset` has the same shape
|
|
4
|
+
* (it is missing from `ITextStyle` there, but `textShadowToStyle` below emits it and RN reads
|
|
5
|
+
* it). Both are all-number, so one record type covers the family.
|
|
6
|
+
*/
|
|
7
|
+
export type IStyleValueObject = Readonly<Record<string, number>>;
|
|
8
|
+
/**
|
|
9
|
+
* One React Native style value.
|
|
10
|
+
*
|
|
11
|
+
* Most are scalars. Two are objects ({@link IStyleValueObject}). Six are ARRAY-capable in RN —
|
|
12
|
+
* `transform: ITransformProp[]`, `boxShadow`, `filter`, `experimental_backgroundImage`,
|
|
13
|
+
* `transformOrigin`, `fontVariant` — but every one of them is `raw` in PROPERTY_TABLE, i.e. this
|
|
14
|
+
* pipeline hands the engine the CSS TEXT and lets its own processors (`process-transform`,
|
|
15
|
+
* `process-box-shadow`, …) build the array at commit time. So the array branch exists so the
|
|
16
|
+
* integration step cannot hit a second widening, not because a declaration produces one today.
|
|
17
|
+
*/
|
|
18
|
+
export type IStyleValue = string | number | IStyleValueObject | ReadonlyArray<string | number | IStyleValueObject>;
|
|
19
|
+
export type IStyleObject = Record<string, IStyleValue>;
|
|
20
|
+
export interface IDeclarationContext {
|
|
21
|
+
readonly filename: string;
|
|
22
|
+
/** Custom properties (`--x`) collected from the whole file, name -> raw value text. */
|
|
23
|
+
readonly variables: ReadonlyMap<string, string>;
|
|
24
|
+
/** Root font size for rem, default 16. */
|
|
25
|
+
readonly remToPx?: number;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* One lightningcss `Declaration` (the object handed to a visitor / found in
|
|
29
|
+
* `rule.value.declarations.declarations`) mapped to zero or more RN style entries.
|
|
30
|
+
*/
|
|
31
|
+
export declare function declarationToStyle(declaration: unknown, context: IDeclarationContext): IStyleObject;
|
|
32
|
+
/**
|
|
33
|
+
* Every custom property (`--x`) declared anywhere in the file, name -> its raw value text, in the
|
|
34
|
+
* shape {@link IDeclarationContext.variables} expects.
|
|
35
|
+
*
|
|
36
|
+
* A pass of its OWN, ahead of mapping declarations, because nothing orders `:root` first — a
|
|
37
|
+
* component stylesheet may declare a token in a class rule, inside `@media`, or below its first
|
|
38
|
+
* use, and a single forward walk would miss it. Later declaration of the same name wins, which is
|
|
39
|
+
* what the cascade does within one file.
|
|
40
|
+
*
|
|
41
|
+
* The value is stored VERBATIM: a token list that is itself a `var()` chain is printed back as
|
|
42
|
+
* `var(--other)`, and resolution stays in {@link declarationToStyle}, which is the only place that
|
|
43
|
+
* knows whether the chain terminates. Serialization goes through the same token printer the
|
|
44
|
+
* `unparsed` path uses — this package keeps ONE printer.
|
|
45
|
+
*/
|
|
46
|
+
export declare function variablesIn(css: string, filename: string): ReadonlyMap<string, string>;
|