next-style 1.2.0 → 2.0.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.
@@ -0,0 +1,95 @@
1
+ /**
2
+ * The compiled representation of a single `css({})` call.
3
+ * Produced by `StyleCollector` and consumed by the PostCSS plugin.
4
+ */
5
+ export interface CompiledStyle {
6
+ /** Scoped class name, e.g. `"ns-1x2y3z"`. */
7
+ className: string;
8
+ /** Base CSS rule block for this class. */
9
+ css: string;
10
+ /** Map of normalized media query string → CSS rule block. */
11
+ mediaQueries: Record<string, string>;
12
+ /** Map of pseudo selector (e.g. `":hover"`) → CSS rule block. */
13
+ pseudoClasses: Record<string, string>;
14
+ /** Concatenated `@keyframes` blocks referenced by this style. */
15
+ keyframes: string;
16
+ /** Map of `@supports` condition → CSS rule block. */
17
+ supports: Record<string, string>;
18
+ /** Map of `@layer` name → CSS rule block. */
19
+ layers: Record<string, string>;
20
+ }
21
+ /**
22
+ * Collects, compiles, and deduplicates styles registered via `css()` and `global()`.
23
+ *
24
+ * A single shared instance is held by the runtime module. The PostCSS plugin
25
+ * calls `getAllStyles()` at build time to drain the collected CSS.
26
+ *
27
+ * @example
28
+ * const collector = new StyleCollector()
29
+ * const className = collector.addStyle({ color: 'red' }) // "ns-abc123"
30
+ * const css = collector.getAllStyles() // ".ns-abc123 { color: red; }"
31
+ */
32
+ export declare class StyleCollector {
33
+ private styles;
34
+ /**
35
+ * Compiles a style object and registers it in the collector.
36
+ * Returns the same class name for identical style objects (deduplication).
37
+ *
38
+ * @param styleObj - Raw style object from a `css({})` call.
39
+ * @returns Scoped class name string.
40
+ */
41
+ addStyle(styleObj: any): string;
42
+ /**
43
+ * Registers a global style rule (no scoped class).
44
+ * Subsequent calls with the same selector are ignored (idempotent).
45
+ *
46
+ * @param selector - CSS selector string, e.g. `"body"` or `"h1, h2"`.
47
+ * @param styleObj - Style properties to apply to the selector.
48
+ */
49
+ addGlobalStyle(selector: string, styleObj: any): void;
50
+ private compileStyle;
51
+ private parseStyles;
52
+ private buildDeclarations;
53
+ /**
54
+ * Serialises all collected styles into a single CSS string.
55
+ *
56
+ * Output order:
57
+ * 1. `@keyframes` blocks
58
+ * 2. Base class rules
59
+ * 3. Pseudo-class/element rules
60
+ * 4. `@layer` blocks
61
+ * 5. `@supports` blocks
62
+ * 6. Media queries (ascending `min-width`, mobile-first)
63
+ *
64
+ * @returns Full CSS string ready to be injected or written to a file.
65
+ */
66
+ getAllStyles(): string;
67
+ private extractMinWidth;
68
+ /**
69
+ * Returns a snapshot of the internal style map.
70
+ * Intended for inspection and testing — not for direct mutation.
71
+ */
72
+ getStyleMap(): Map<string, CompiledStyle>;
73
+ }
74
+ /**
75
+ * Creates an isolated `StyleCollector` paired with a transform helper.
76
+ *
77
+ * Primarily used by SWC/Babel transforms and test harnesses that need
78
+ * a fresh collector per file or per test, independent of the shared
79
+ * runtime instance.
80
+ *
81
+ * @example
82
+ * const { collector, transformCssCall } = createTransformer()
83
+ * const className = transformCssCall({ color: 'red' })
84
+ * const css = collector.getAllStyles()
85
+ */
86
+ export declare function createTransformer(): {
87
+ collector: StyleCollector;
88
+ /**
89
+ * Equivalent to calling `css()` against this transformer's isolated collector.
90
+ * @param styleObj - Raw style object.
91
+ * @returns Scoped class name string.
92
+ */
93
+ transformCssCall(styleObj: any): string;
94
+ };
95
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compiler/index.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC7B,6CAA6C;IAC7C,SAAS,EAAE,MAAM,CAAA;IACjB,0CAA0C;IAC1C,GAAG,EAAE,MAAM,CAAA;IACX,6DAA6D;IAC7D,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACpC,iEAAiE;IACjE,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACrC,iEAAiE;IACjE,SAAS,EAAE,MAAM,CAAA;IACjB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC9B;AAED;;;;;;;;;;GAUG;AACH,qBAAa,cAAc;IAC1B,OAAO,CAAC,MAAM,CAAmC;IAEjD;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,EAAE,GAAG,GAAG,MAAM;IAW/B;;;;;;OAMG;IACH,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,GAAG,IAAI;IAgBrD,OAAO,CAAC,YAAY;IA+DpB,OAAO,CAAC,WAAW;IAwCnB,OAAO,CAAC,iBAAiB;IAUzB;;;;;;;;;;;;OAYG;IACH,YAAY,IAAI,MAAM;IAwBtB,OAAO,CAAC,eAAe;IAKvB;;;OAGG;IACH,WAAW;CAGX;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB;;IAI/B;;;;OAIG;+BACwB,GAAG,GAAG,MAAM;EAIxC"}
@@ -0,0 +1,229 @@
1
+ import { BREAKPOINTS, camelToKebab, generateClassHash, normalizeMediaQuery } from '../utils';
2
+ /**
3
+ * Collects, compiles, and deduplicates styles registered via `css()` and `global()`.
4
+ *
5
+ * A single shared instance is held by the runtime module. The PostCSS plugin
6
+ * calls `getAllStyles()` at build time to drain the collected CSS.
7
+ *
8
+ * @example
9
+ * const collector = new StyleCollector()
10
+ * const className = collector.addStyle({ color: 'red' }) // "ns-abc123"
11
+ * const css = collector.getAllStyles() // ".ns-abc123 { color: red; }"
12
+ */
13
+ export class StyleCollector {
14
+ constructor() {
15
+ this.styles = new Map();
16
+ }
17
+ /**
18
+ * Compiles a style object and registers it in the collector.
19
+ * Returns the same class name for identical style objects (deduplication).
20
+ *
21
+ * @param styleObj - Raw style object from a `css({})` call.
22
+ * @returns Scoped class name string.
23
+ */
24
+ addStyle(styleObj) {
25
+ const hash = generateClassHash(styleObj);
26
+ const className = `ns-${hash}`;
27
+ if (this.styles.has(className)) {
28
+ return className;
29
+ }
30
+ const compiled = this.compileStyle(styleObj);
31
+ this.styles.set(className, compiled);
32
+ return className;
33
+ }
34
+ /**
35
+ * Registers a global style rule (no scoped class).
36
+ * Subsequent calls with the same selector are ignored (idempotent).
37
+ *
38
+ * @param selector - CSS selector string, e.g. `"body"` or `"h1, h2"`.
39
+ * @param styleObj - Style properties to apply to the selector.
40
+ */
41
+ addGlobalStyle(selector, styleObj) {
42
+ const key = `global:${selector}`;
43
+ if (this.styles.has(key))
44
+ return;
45
+ const props = this.buildDeclarations(styleObj);
46
+ if (!props)
47
+ return;
48
+ this.styles.set(key, {
49
+ className: key,
50
+ css: `${selector} {\n${props}}`,
51
+ mediaQueries: {},
52
+ pseudoClasses: {},
53
+ keyframes: '',
54
+ supports: {},
55
+ layers: {}
56
+ });
57
+ }
58
+ compileStyle(styleObj) {
59
+ const { mediaQueries, pseudoClasses, normalStyles, keyframes, supports, container, layer } = this.parseStyles(styleObj);
60
+ const hash = generateClassHash(styleObj);
61
+ const className = `ns-${hash}`;
62
+ const declarations = this.buildDeclarations(normalStyles);
63
+ const css = `.${className} {\n${declarations}}`;
64
+ const compiledMedia = {};
65
+ Object.entries(mediaQueries).forEach(([query, styles]) => {
66
+ const normalized = normalizeMediaQuery(query);
67
+ const inner = this.buildDeclarations(styles, ' ');
68
+ const existing = compiledMedia[normalized];
69
+ compiledMedia[normalized] = existing
70
+ ? `${normalized} {\n .${className} {\n${existing.split('\n').slice(2, -1).join('\n')}\n${inner} }\n}`
71
+ : `${normalized} {\n .${className} {\n${inner} }\n}`;
72
+ });
73
+ Object.entries(container).forEach(([query, styles]) => {
74
+ const inner = this.buildDeclarations(styles, ' ');
75
+ compiledMedia[query] = `${query} {\n .${className} {\n${inner} }\n}`;
76
+ });
77
+ const compiledPseudos = {};
78
+ Object.entries(pseudoClasses).forEach(([pseudo, styles]) => {
79
+ const inner = this.buildDeclarations(styles);
80
+ compiledPseudos[pseudo] = `.${className}${pseudo} {\n${inner}}`;
81
+ });
82
+ let keyframesCss = '';
83
+ Object.entries(keyframes).forEach(([name, frames]) => {
84
+ keyframesCss += `@keyframes ${name} {\n`;
85
+ Object.entries(frames).forEach(([stop, props]) => {
86
+ const inner = this.buildDeclarations(props, ' ');
87
+ keyframesCss += ` ${stop} {\n${inner} }\n`;
88
+ });
89
+ keyframesCss += '}\n';
90
+ });
91
+ const compiledSupports = {};
92
+ Object.entries(supports).forEach(([condition, styles]) => {
93
+ const inner = this.buildDeclarations(styles, ' ');
94
+ compiledSupports[condition] = `@supports ${condition} {\n .${className} {\n${inner} }\n}`;
95
+ });
96
+ const compiledLayers = {};
97
+ Object.entries(layer).forEach(([name, styles]) => {
98
+ const inner = this.buildDeclarations(styles, ' ');
99
+ compiledLayers[name] = `@layer ${name} {\n${inner}}`;
100
+ });
101
+ return {
102
+ className,
103
+ css,
104
+ mediaQueries: compiledMedia,
105
+ pseudoClasses: compiledPseudos,
106
+ keyframes: keyframesCss,
107
+ supports: compiledSupports,
108
+ layers: compiledLayers
109
+ };
110
+ }
111
+ parseStyles(styleObj) {
112
+ const normalStyles = {};
113
+ const mediaQueries = {};
114
+ const pseudoClasses = {};
115
+ const keyframes = {};
116
+ const supports = {};
117
+ const container = {};
118
+ const layer = {};
119
+ Object.entries(styleObj).forEach(([key, value]) => {
120
+ if (key.startsWith('@keyframes ')) {
121
+ keyframes[key.slice('@keyframes '.length)] = value;
122
+ }
123
+ else if (key === '@keyframes' && typeof value === 'object') {
124
+ Object.assign(keyframes, value);
125
+ }
126
+ else if (key.startsWith('@supports')) {
127
+ supports[key.slice('@supports '.length)] = value;
128
+ }
129
+ else if (key.startsWith('@container')) {
130
+ container[key] = value;
131
+ }
132
+ else if (key.startsWith('@layer')) {
133
+ layer[key.slice('@layer '.length) || 'default'] = value;
134
+ }
135
+ else if (key in BREAKPOINTS || key.startsWith('@media')) {
136
+ mediaQueries[key] = value;
137
+ }
138
+ else if (key.startsWith(':') || key.startsWith('::')) {
139
+ pseudoClasses[key] = value;
140
+ }
141
+ else {
142
+ normalStyles[key] = value;
143
+ }
144
+ });
145
+ return { normalStyles, mediaQueries, pseudoClasses, keyframes, supports, container, layer };
146
+ }
147
+ buildDeclarations(styles, indent = ' ') {
148
+ let css = '';
149
+ Object.entries(styles).forEach(([key, value]) => {
150
+ if (typeof value === 'string' || typeof value === 'number') {
151
+ css += `${indent}${camelToKebab(key)}: ${value};\n`;
152
+ }
153
+ });
154
+ return css;
155
+ }
156
+ /**
157
+ * Serialises all collected styles into a single CSS string.
158
+ *
159
+ * Output order:
160
+ * 1. `@keyframes` blocks
161
+ * 2. Base class rules
162
+ * 3. Pseudo-class/element rules
163
+ * 4. `@layer` blocks
164
+ * 5. `@supports` blocks
165
+ * 6. Media queries (ascending `min-width`, mobile-first)
166
+ *
167
+ * @returns Full CSS string ready to be injected or written to a file.
168
+ */
169
+ getAllStyles() {
170
+ let allCss = '';
171
+ this.styles.forEach(style => {
172
+ if (style.keyframes)
173
+ allCss += `${style.keyframes}\n`;
174
+ if (style.css)
175
+ allCss += `${style.css}\n`;
176
+ Object.values(style.pseudoClasses).forEach(css => {
177
+ allCss += `${css}\n`;
178
+ });
179
+ Object.values(style.layers).forEach(css => {
180
+ allCss += `${css}\n`;
181
+ });
182
+ Object.values(style.supports).forEach(css => {
183
+ allCss += `${css}\n`;
184
+ });
185
+ const mediaEntries = Object.entries(style.mediaQueries).sort(([a], [b]) => this.extractMinWidth(a) - this.extractMinWidth(b));
186
+ mediaEntries.forEach(([, css]) => {
187
+ allCss += `${css}\n`;
188
+ });
189
+ });
190
+ return allCss;
191
+ }
192
+ extractMinWidth(query) {
193
+ const match = query.match(/min-width:\s*(\d+)px/);
194
+ return match ? parseInt(match[1], 10) : 0;
195
+ }
196
+ /**
197
+ * Returns a snapshot of the internal style map.
198
+ * Intended for inspection and testing — not for direct mutation.
199
+ */
200
+ getStyleMap() {
201
+ return new Map(this.styles);
202
+ }
203
+ }
204
+ /**
205
+ * Creates an isolated `StyleCollector` paired with a transform helper.
206
+ *
207
+ * Primarily used by SWC/Babel transforms and test harnesses that need
208
+ * a fresh collector per file or per test, independent of the shared
209
+ * runtime instance.
210
+ *
211
+ * @example
212
+ * const { collector, transformCssCall } = createTransformer()
213
+ * const className = transformCssCall({ color: 'red' })
214
+ * const css = collector.getAllStyles()
215
+ */
216
+ export function createTransformer() {
217
+ const collector = new StyleCollector();
218
+ return {
219
+ collector,
220
+ /**
221
+ * Equivalent to calling `css()` against this transformer's isolated collector.
222
+ * @param styleObj - Raw style object.
223
+ * @returns Scoped class name string.
224
+ */
225
+ transformCssCall(styleObj) {
226
+ return collector.addStyle(styleObj);
227
+ }
228
+ };
229
+ }
package/dist/index.d.ts CHANGED
@@ -1,297 +1,5 @@
1
- import type { Properties } from "csstype";
2
- import type { DetailedReactHTMLElement } from "react";
3
- /**
4
- * Style object used by NextStyle.
5
- *
6
- * Supports:
7
- * - Standard CSS properties (camelCase)
8
- * - Pseudo states via `_hover`, `_focus`, `_active`
9
- * - Responsive media queries via `_sm` ~ `_xxl`
10
- *
11
- * Example:
12
- * ```ts
13
- * const button = css({
14
- * padding: "8px 16px",
15
- * backgroundColor: "black",
16
- * color: "white",
17
- * _hover: { opacity: 0.8 },
18
- * _md: { padding: "12px 20px" }
19
- * })
20
- * ```
21
- */
22
- export type NextStyleProperties = {
23
- [K in keyof Properties<string | number>]?: Properties<string | number>[K];
24
- } & {
25
- _hover?: NextStyleObject;
26
- _focus?: NextStyleObject;
27
- _active?: NextStyleObject;
28
- _sm?: NextStyleObject;
29
- _md?: NextStyleObject;
30
- _lg?: NextStyleObject;
31
- _xl?: NextStyleObject;
32
- _xxl?: NextStyleObject;
33
- };
34
- type NextStyleObject = Omit<NextStyleProperties, "_sm" | "_md" | "_lg" | "_xl" | "_xxl">;
35
- type KeyframesObject = {
36
- [step: string]: Properties<string | number>;
37
- };
38
- type FontFaceObject = {
39
- fontFamily: string;
40
- src: string;
41
- fontWeight?: string | number;
42
- fontStyle?: string;
43
- fontDisplay?: string;
44
- unicodeRange?: string;
45
- };
46
- type PseudoState = "hover" | "focus" | "active";
47
- /**
48
- * Builder for defining relations starting from a generated class name.
49
- *
50
- * This builder should only be created via `NextStyle.when(...)`.
51
- */
52
- declare class RelationBuilder {
53
- private ns;
54
- private source;
55
- private pseudo?;
56
- /**
57
- * @internal
58
- * @param ns NextStyle instance
59
- * @param source Source class name generated by `css()`
60
- * @param pseudo Optional pseudo state applied to the source
61
- */
62
- constructor(ns: NextStyle, source: string, pseudo?: PseudoState | undefined);
63
- /**
64
- * Apply `:hover` pseudo state to the source selector.
65
- *
66
- * All subsequent relations will be scoped under `:hover`.
67
- *
68
- * @returns RelationBuilder
69
- *
70
- * Example:
71
- * ```ts
72
- * when(card)
73
- * .hover()
74
- * .child(icon, { opacity: 1 })
75
- * ```
76
- */
77
- hover(): RelationBuilder;
78
- /**
79
- * Apply `:focus` pseudo state to the source selector.
80
- *
81
- * @returns RelationBuilder
82
- */
83
- focus(): RelationBuilder;
84
- /**
85
- * Apply `:active` pseudo state to the source selector.
86
- *
87
- * @returns RelationBuilder
88
- */
89
- active(): RelationBuilder;
90
- /**
91
- * Define styles for an adjacent sibling selector.
92
- *
93
- * CSS equivalent:
94
- * `.source + .target`
95
- *
96
- * @param target Target class name generated by `css()`
97
- * @param style Style applied to the target element
98
- *
99
- * Example:
100
- * ```ts
101
- * when(input)
102
- * .focus()
103
- * .adjacent(label, { color: "red" })
104
- * ```
105
- */
106
- adjacent(target: string, style: NextStyleProperties): void;
107
- /**
108
- * Define styles for a direct child selector.
109
- *
110
- * CSS equivalent:
111
- * `.source > .target`
112
- *
113
- * @param target Target class name generated by `css()`
114
- * @param style Style applied to the target element
115
- */
116
- child(target: string, style: NextStyleProperties): void;
117
- /**
118
- * Define styles for a general sibling selector.
119
- *
120
- * CSS equivalent:
121
- * `.source ~ .target`
122
- *
123
- * @param target Target class name generated by `css()`
124
- * @param style Style applied to the target element
125
- */
126
- sibling(target: string, style: NextStyleProperties): void;
127
- /**
128
- * Define styles for a descendant selector.
129
- *
130
- * CSS equivalent:
131
- * `.source .target`
132
- *
133
- * @param target Target class name generated by `css()`
134
- * @param style Style applied to the target element
135
- */
136
- descendant(target: string, style: NextStyleProperties): void;
137
- /**
138
- * @internal
139
- * Emit a global relational rule.
140
- */
141
- private emit;
142
- }
143
- /**
144
- * NextStyle
145
- *
146
- * Lightweight runtime CSS-in-JS engine with deterministic class names.
147
- *
148
- * Features:
149
- * - Scoped class generation
150
- * - Nested pseudo selectors
151
- * - Responsive media queries
152
- * - Global styles
153
- * - Keyframes and font-face support
154
- *
155
- * Example:
156
- * ```ts
157
- * const ns = new NextStyle("app")
158
- * ```
159
- */
160
- export declare class NextStyle {
161
- private prefix;
162
- private rules;
163
- private globalStore;
164
- constructor(prefix?: string);
165
- /**
166
- * Generate a scoped class name from a style object.
167
- *
168
- * The returned value is a branded class name and is intended
169
- * to be used with `when(...)` and relational APIs.
170
- *
171
- * @param style Style definition object
172
- * @returns Scoped class name
173
- *
174
- * Example:
175
- * ```ts
176
- * const button = css({ padding: 8 })
177
- * ```
178
- */
179
- css: (style: NextStyleProperties) => string;
180
- /**
181
- * Define a global CSS selector.
182
- *
183
- * Styles are merged at property level instead of overwritten.
184
- *
185
- * @param selector CSS selector
186
- * @param style Style definition
187
- *
188
- * Example:
189
- * ```ts
190
- * global("body", {
191
- * fontFamily: "Kanit, sans-serif"
192
- * })
193
- * ```
194
- */
195
- global: (selector: string, style: NextStyleProperties) => void;
196
- /**
197
- * Apply default browser reset styles
198
- * ```css
199
- * html, body {
200
- * max-width: 100vw;
201
- * overflow-x: hidden;
202
- * }
203
- * body {
204
- * color: "black";
205
- * background: "white";
206
- * -moz-osx-font-smoothing: grayscale;
207
- * -webkit-font-smoothing: antialiased;
208
- * font-family: "Arial, Helvetica, sans-serif"
209
- * }
210
- * *,
211
- * *::before,
212
- * *::after {
213
- * margin: 0;
214
- * padding: 0;
215
- * box-sizing: border-box;
216
- * }
217
- * a {
218
- * color: inherit;
219
- * text-decoration: none
220
- * }
221
- * ```
222
- */
223
- resetStyle: () => this;
224
- /**
225
- * Create relational selectors based on a class generated by `css(...)`.
226
- *
227
- * ⚠️ This method only accepts class names returned from `css`.
228
- *
229
- * @param source Class name generated by `css`
230
- *
231
- * Example:
232
- * ```ts
233
- * const card = css({ ... })
234
- *
235
- * when(card)
236
- * .hover()
237
- * .child("icon", { opacity: 1 })
238
- * ```
239
- */
240
- when: (source: string) => RelationBuilder;
241
- /**
242
- * Register keyframes animation.
243
- *
244
- * @param frames Keyframes definition
245
- * @returns Generated animation name
246
- *
247
- * Example:
248
- * ```ts
249
- * const fadeIn = keyframes({
250
- * from: { opacity: 0 },
251
- * to: { opacity: 1 }
252
- * })
253
- * ```
254
- */
255
- keyframes: (frames: KeyframesObject) => string;
256
- /**
257
- * Register a `@font-face` rule.
258
- *
259
- * Each unique definition is injected only once.
260
- *
261
- * @param font Font-face definition
262
- *
263
- * Example:
264
- * ```ts
265
- * fontFace({
266
- * fontFamily: "Kanit",
267
- * src: "url(/kanit.woff2) format('woff2')",
268
- * fontWeight: 400
269
- * })
270
- * ```
271
- */
272
- fontFace: (font: FontFaceObject) => void;
273
- /**
274
- * Serialize all generated CSS into a single string.
275
- *
276
- * Intended for:
277
- * - Manual `<style>` injection
278
- * - SSR environments
279
- *
280
- * @returns CSS text or `null` if no styles exist
281
- */
282
- toTextCss: () => string | null;
283
- /**
284
- * React component that injects generated CSS into a `<style>` tag.
285
- *
286
- * Returns `null` if no styles are registered.
287
- *
288
- * Example:
289
- * ```tsx
290
- * <StyleProvider />
291
- * ```
292
- */
293
- StyleProvider: () => DetailedReactHTMLElement<{
294
- children: string;
295
- }, HTMLStyleElement> | null;
296
- }
297
- export {};
1
+ export { type CompiledStyle, createTransformer, StyleCollector } from './compiler';
2
+ export { default as postcssPlugin, plugin } from './postcss-plugin';
3
+ export { type CSSObject, type CSSProperties, css, global, styleCollector } from './runtime';
4
+ export { BREAKPOINTS, camelToKebab, deduplicateStyles, generateClassHash, normalizeMediaQuery, validateCSSProperty } from './utils';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,aAAa,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,YAAY,CAAA;AAClF,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AACnE,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,aAAa,EAAE,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAC3F,OAAO,EACN,WAAW,EACX,YAAY,EACZ,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,mBAAmB,EACnB,MAAM,SAAS,CAAA"}
package/dist/index.js CHANGED
@@ -1,3 +1,4 @@
1
- import postcss from"postcss";import{createElement}from"react";import autoprefixer from"autoprefixer";var processor=postcss([autoprefixer({overrideBrowserslist:[">0.2%","not dead","not op_mini all"]})]),postcssCache=new Map;function postcssTransform(cssText){let cached=postcssCache.get(cssText);if(cached)return cached;let result=processor.process(cssText,{from:void 0}).css;return postcssCache.set(cssText,result),result}function stableStringify(value){if(value==null||typeof value!=="object")return JSON.stringify(value);if(Array.isArray(value))return`[${value.map(stableStringify).join(",")}]`;return`{${Object.keys(value).sort().map((k)=>`"${k}":${stableStringify(value[k])}`).join(",")}}`}function createHashName(seed){let hash=BigInt("0xcbf29ce484222325"),prime=BigInt("0x100000001b3");for(let i=0;i<seed.length;i++)hash^=BigInt(seed.charCodeAt(i)),hash*=prime,hash&=BigInt("0xffffffffffffffff");return hash.toString(36).slice(0,9)}function toKebabCase(prop){return prop.replace(/[A-Z]/g,(m)=>`-${m.toLowerCase()}`)}var MEDIA_MAP={_sm:"(min-width:640px)",_md:"(min-width:768px)",_lg:"(min-width:1024px)",_xl:"(min-width:1280px)",_xxl:"(min-width:1536px)"};function mergeMedia(parent,current){if(!parent)return current;if(!current)return parent;return`${parent} and ${current}`}function serializeNested(style,ctx){let css="",declarations=[];for(let key in style){let value=style[key];if(value==null||typeof value==="object"||key.startsWith("_"))continue;declarations.push(`${toKebabCase(key)}:${value}`)}if(declarations.length){let rule=`${ctx.selector}{${declarations.join(";")}}`;css+=ctx.media?`@media ${ctx.media}{${rule}}`:rule}for(let pseudo of["_hover","_focus","_active"]){let value=style[pseudo];if(!value)continue;css+=serializeNested(value,{selector:`${ctx.selector}:${pseudo.slice(1)}`,media:ctx.media})}for(let key in MEDIA_MAP){let mediaKey=key,value=style[mediaKey];if(!value)continue;css+=serializeNested(value,{selector:ctx.selector,media:mergeMedia(ctx.media,MEDIA_MAP[mediaKey])})}return css}class RelationBuilder{ns;source;pseudo;constructor(ns,source,pseudo){this.ns=ns;this.source=source;this.pseudo=pseudo}hover(){return new RelationBuilder(this.ns,this.source,"hover")}focus(){return new RelationBuilder(this.ns,this.source,"focus")}active(){return new RelationBuilder(this.ns,this.source,"active")}adjacent(target,style){this.emit("+",target,style)}child(target,style){this.emit(">",target,style)}sibling(target,style){this.emit("~",target,style)}descendant(target,style){this.emit(" ",target,style)}emit(combinator,target,style){let pseudo=this.pseudo?`:${this.pseudo}`:"",selector=`.${this.source}${pseudo}${combinator}.${target}`;this.ns.global(selector,style)}}class NextStyle{prefix;rules=new Map;globalStore=new Map;constructor(prefix="next"){this.prefix=prefix}css=(style)=>{let seed=stableStringify(style),hash=createHashName(seed),className=`${this.prefix}_${hash}`,key=`class:${className}`;if(!this.rules.has(key)){let raw=serializeNested(style,{selector:`.${className}`}),cssText=postcssTransform(raw);this.rules.set(key,cssText)}return className};global=(selector,style)=>{let key=`global:${selector}`,merged={...this.globalStore.get(key)??{},...style};this.globalStore.set(key,merged);let raw=serializeNested(merged,{selector}),cssText=postcssTransform(raw);if(this.rules.has(key))this.rules.delete(key);this.rules.set(key,cssText)};resetStyle=()=>{return this.global("html,body",{maxWidth:"100vw",overflowX:"hidden"}),this.global("body",{color:"black",background:"white",MozOsxFontSmoothing:"grayscale",WebkitFontSmoothing:"antialiased",fontFamily:"Arial, Helvetica, sans-serif"}),this.global("*,*::before,*::after",{margin:0,padding:0,boxSizing:"border-box"}),this.global("a",{color:"inherit",textDecoration:"none"}),this};when=(source)=>new RelationBuilder(this,source);keyframes=(frames)=>{let seed=stableStringify(frames),hash=createHashName(seed),name=`${this.prefix}_${hash}`,key=`@keyframes:${name}`;if(!this.rules.has(key)){let body="";for(let step in frames){let declarations=[],frame=frames[step];for(let prop in frame)declarations.push(`${toKebabCase(prop)}:${frame[prop]}`);body+=`${step}{ ${declarations.join(";")} }`}let cssText=postcssTransform(`@keyframes ${name}{ ${body} }`);this.rules.set(key,cssText)}return name};fontFace=(font)=>{let seed=stableStringify(font),key=`@font-face:${createHashName(seed)}`;if(!this.rules.has(key)){let declarations=[];for(let prop in font)declarations.push(`${toKebabCase(prop)}:${font[prop]}`);let cssText=postcssTransform(`@font-face{ ${declarations.join(";")} }`);this.rules.set(key,cssText)}};toTextCss=()=>{if(this.rules.size===0)return null;let cssText="";for(let rule of this.rules.values())cssText+=rule+`
2
- `;return cssText};StyleProvider=()=>{if(this.rules.size===0)return null;let cssText="";for(let rule of this.rules.values())cssText+=rule+`
3
- `;return createElement("style",{children:cssText})}}export{NextStyle};
1
+ export { createTransformer, StyleCollector } from './compiler';
2
+ export { default as postcssPlugin, plugin } from './postcss-plugin';
3
+ export { css, global, styleCollector } from './runtime';
4
+ export { BREAKPOINTS, camelToKebab, deduplicateStyles, generateClassHash, normalizeMediaQuery, validateCSSProperty } from './utils';
@@ -0,0 +1,28 @@
1
+ import postcss from 'postcss';
2
+ import type { StyleCollector } from '../compiler';
3
+ /**
4
+ * PostCSS plugin for next-style.
5
+ *
6
+ * Replaces `@import "next-style";` (or `@import 'next-style';`) in CSS files
7
+ * with the compiled CSS collected by the StyleCollector.
8
+ *
9
+ * The collector is populated by the runtime `css()` / `global()` calls that
10
+ * run during the build-time module evaluation (SWC / tsc transform pass).
11
+ * At that point all `css({})` calls have been executed and their styles are
12
+ * sitting in the collector — the PostCSS plugin just needs to drain it.
13
+ *
14
+ * Usage in postcss.config.js:
15
+ * import nextStylePlugin from 'next-style/plugin'
16
+ * export default { plugins: { 'next-style/plugin': {} } }
17
+ */
18
+ interface PluginOptions {
19
+ /** Provide a custom collector (useful for testing / server integration). */
20
+ collector?: StyleCollector;
21
+ }
22
+ declare function nextStylePlugin(opts?: PluginOptions): postcss.Plugin;
23
+ declare namespace nextStylePlugin {
24
+ var postcss: boolean;
25
+ }
26
+ export default nextStylePlugin;
27
+ export declare const plugin: typeof nextStylePlugin;
28
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/postcss-plugin/index.ts"],"names":[],"mappings":"AAAA,OAAO,OAAO,MAAM,SAAS,CAAA;AAC7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAIjD;;;;;;;;;;;;;;GAcG;AAEH,UAAU,aAAa;IACtB,4EAA4E;IAC5E,SAAS,CAAC,EAAE,cAAc,CAAA;CAC1B;AAED,iBAAS,eAAe,CAAC,IAAI,GAAE,aAAkB,kBA6ChD;kBA7CQ,eAAe;;;AAiDxB,eAAe,eAAe,CAAA;AAC9B,eAAO,MAAM,MAAM,wBAAkB,CAAA"}