@cioky/vike-pandacss 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/README.md ADDED
@@ -0,0 +1,76 @@
1
+ # @cioky/vike-pandacss
2
+
3
+ > ⚠️ **HIGHLY EXPERIMENTAL** — This package is in early development. APIs may change without notice, parts may not work, and documentation may be incomplete. Use at your own risk.
4
+
5
+ [Panda CSS](https://panda-css.com) integration for [Ripple TS](https://ripple-ts.com) — transforms `.tsrx` files for Panda extraction and enables `@apply` in `<style>` blocks.
6
+
7
+ Part of the [vike-ripple monorepo](https://github.com/Opaius/vike-ripple).
8
+
9
+ ## Quick Start
10
+
11
+ ```bash
12
+ npx @cioky/vike-create my-app --style pandacss
13
+ cd my-app && npm run dev
14
+ ```
15
+
16
+ ## Manual Install
17
+
18
+ ```sh
19
+ npm install @cioky/vike-pandacss
20
+ ```
21
+
22
+ ## Setup
23
+
24
+ Two setup scripts must run **in order**:
25
+
26
+ ```bash
27
+ npx @cioky/vike-core setup # core patches (.tsrx, server isolation, etc.)
28
+ npx @cioky/vike-pandacss setup # replaces tailwind @apply with Panda @layer
29
+ ```
30
+
31
+ ## Usage
32
+
33
+ ### Vite plugin
34
+
35
+ ```ts
36
+ // vite.config.ts
37
+ import vikeRipplePandacss from '@cioky/vike-pandacss'
38
+
39
+ export default defineConfig({
40
+ css: { postcss: './postcss.config.js' },
41
+ plugins: [
42
+ vikeRipplePandacss(),
43
+ ],
44
+ })
45
+ ```
46
+
47
+ ### PostCSS config
48
+
49
+ ```js
50
+ // postcss.config.js
51
+ export default { plugins: { '@pandacss/dev/postcss': {} } }
52
+ ```
53
+
54
+ ### Panda CSS plugin
55
+
56
+ ```ts
57
+ // panda.config.ts
58
+ import { pluginRipple } from '@cioky/vike-pandacss/panda-plugin'
59
+
60
+ export default defineConfig({
61
+ plugins: [pluginRipple()],
62
+ })
63
+ ```
64
+
65
+ The `pluginRipple()` implements Panda's `parser:before` hook to transform `.tsrx` content into valid TSX before Panda's ts-morph extracts `css()`/`cva()`/`sva()` calls.
66
+
67
+ ## How it works
68
+
69
+ - **`parser:before` hook**: Strips Ripple's `@if`/`@for`/`@each` directives, `@{}` markers, and `<style>` blocks from `.tsrx` files so Panda's extraction engine can parse the remaining TSX.
70
+ - **`@apply` patch**: Prepends `@layer reset, base, tokens, recipes, utilities;` to extracted CSS from `<style>` blocks so Panda CSS `@apply` directives resolve at build time.
71
+
72
+ ## Related Packages
73
+
74
+ - [`@cioky/vike-core`](https://github.com/Opaius/vike-ripple/tree/main/vike-ripple) — Core Vike + Ripple integration
75
+ - [`@cioky/vike-tailwindcss`](https://github.com/Opaius/vike-ripple/tree/main/vike-ripple-tailwindcss) — Tailwind CSS alternative
76
+ - [`@cioky/vike-create`](https://github.com/Opaius/vike-ripple/tree/main/create-vike-ripple) — Project scaffold
package/index.d.ts ADDED
@@ -0,0 +1,27 @@
1
+ declare module '@cioky/vike-pandacss' {
2
+ import type { Plugin } from 'vite';
3
+ const plugin: () => Plugin;
4
+ export default plugin;
5
+ }
6
+
7
+ declare module '@cioky/vike-pandacss/panda-plugin' {
8
+ import type { PandaPlugin } from '@pandacss/types';
9
+
10
+ /**
11
+ * Panda CSS plugin for Ripple TS (.tsrx files).
12
+ * Transforms .tsrx content into valid TSX before Panda extracts
13
+ * css()/cva()/sva() calls via the `parser:before` hook.
14
+ */
15
+ export function pluginRipple(): PandaPlugin;
16
+
17
+ /**
18
+ * Core transform: converts Ripple .tsrx syntax to parseable TSX.
19
+ * Strips <style>, converts @{} → {}, @if/for → {} blocks,
20
+ * &[var] → var declarations.
21
+ */
22
+ export function tsrxToTsx(code: string): string;
23
+ }
24
+
25
+ declare module '@cioky/vike-pandacss/setup' {
26
+ export {};
27
+ }
package/package.json ADDED
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@cioky/vike-pandacss",
3
+ "version": "0.1.0",
4
+ "description": "Panda CSS integration for Ripple TS — transforms .tsrx for Panda extraction, @apply in <style> blocks",
5
+ "type": "module",
6
+ "main": "./src/index.js",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./index.d.ts",
10
+ "default": "./src/index.js"
11
+ },
12
+ "./panda-plugin": {
13
+ "types": "./index.d.ts",
14
+ "default": "./src/panda-plugin.js"
15
+ }
16
+ },
17
+ "bin": {
18
+ "@cioky/vike-pandacss": "src/setup.js"
19
+ },
20
+ "files": [
21
+ "src",
22
+ "index.d.ts"
23
+ ],
24
+ "repository": {
25
+ "type": "git",
26
+ "url": "https://github.com/Opaius/vike-ripple.git"
27
+ },
28
+ "homepage": "https://github.com/Opaius/vike-ripple",
29
+ "bugs": {
30
+ "url": "https://github.com/Opaius/vike-ripple/issues"
31
+ },
32
+ "keywords": [
33
+ "pandacss",
34
+ "ripple",
35
+ "ripplets",
36
+ "vike"
37
+ ],
38
+ "license": "MIT",
39
+ "peerDependencies": {
40
+ "@pandacss/dev": ">=0.50.0",
41
+ "@ripple-ts/vite-plugin": ">=0.3.0"
42
+ }
43
+ }
package/src/index.js ADDED
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Vike Ripple Panda CSS — Vite plugin entry.
3
+ *
4
+ * The main export provides a Vite plugin that configures Panda CSS PostCSS
5
+ * integration for Vike + Ripple projects. The Panda CSS plugin for .tsrx
6
+ * transformation lives at `@cioky/vike-pandacss/panda-plugin`.
7
+ *
8
+ * ## Usage in vite.config.ts
9
+ * import vikeRipplePandacss from '@cioky/vike-pandacss'
10
+ * import { pluginRipple } from '@cioky/vike-pandacss/panda-plugin'
11
+ *
12
+ * export default defineConfig({
13
+ * css: {
14
+ * postcss: {
15
+ * plugins: [require('@pandacss/dev/postcss')()],
16
+ * },
17
+ * },
18
+ * plugins: [
19
+ * vikeRipplePandacss(), // ordering marker
20
+ * // ...
21
+ * ],
22
+ * })
23
+ *
24
+ * // panda.config.ts
25
+ * export default defineConfig({
26
+ * plugins: [pluginRipple()],
27
+ * include: ['./pages/**\/*.{tsrx,tsx}', './src/**\/*.{ts,tsx}'],
28
+ * })
29
+ */
30
+
31
+ export { pluginRipple } from './panda-plugin.js';
32
+
33
+ export default function vikeRipplePandacss() {
34
+ return {
35
+ name: '@cioky/vike-pandacss',
36
+ enforce: 'pre'
37
+ };
38
+ }
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Panda CSS plugin for Ripple TS (.tsrx files).
3
+ *
4
+ * Implements the `parser:before` hook to transform .tsrx content into valid TSX
5
+ * before Panda's ts-morph parser extracts css()/cva()/sva() calls.
6
+ *
7
+ * Usage in panda.config.ts:
8
+ * import { pluginRipple } from '@cioky/vike-pandacss/panda-plugin'
9
+ *
10
+ * export default defineConfig({
11
+ * plugins: [pluginRipple()],
12
+ * })
13
+ *
14
+ * @module @cioky/vike-pandacss/panda-plugin
15
+ */
16
+
17
+ import { tsrxToTsx } from './tsrx-to-tsx.js';
18
+
19
+ /** @type {import('@pandacss/types').PandaPlugin} */
20
+ export function pluginRipple() {
21
+ return {
22
+ name: '@pandacss/plugin-ripple',
23
+ hooks: {
24
+ 'parser:before': ({ filePath, content }) => {
25
+ if (filePath.endsWith('.tsrx')) {
26
+ return tsrxToTsx(content);
27
+ }
28
+ }
29
+ }
30
+ };
31
+ }
package/src/setup.js ADDED
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @cioky/vike-pandacss setup — enables Panda CSS @apply in Ripple <style> blocks.
4
+ *
5
+ * Run once: npx @cioky/vike-pandacss setup
6
+ * Or add to project's package.json: "postinstall": "@cioky/vike-pandacss setup"
7
+ */
8
+ import { existsSync, readFileSync, writeFileSync } from 'fs';
9
+ import { createRequire } from 'module';
10
+ import { join } from 'path';
11
+
12
+ const projectRoot = process.cwd();
13
+ let exitCode = 0;
14
+
15
+ function log(msg) {
16
+ console.log('[@cioky/vike-pandacss]', msg);
17
+ }
18
+ function warn(msg) {
19
+ console.warn('[@cioky/vike-pandacss]', msg);
20
+ }
21
+
22
+ function patchRippleApply() {
23
+ const target = resolveModule('@ripple-ts/vite-plugin/src/index.js');
24
+ if (!target) {
25
+ warn('@ripple-ts/vite-plugin not found — skipping. Run npm install first.');
26
+ return;
27
+ }
28
+
29
+ let src = readFileSync(target, 'utf-8');
30
+ if (src.includes('PANDA_PATCH_APPLY')) {
31
+ log('@apply patch already applied');
32
+ return;
33
+ }
34
+
35
+ // Case 1: @cioky/vike-core's tailwind patch is already present — replace it
36
+ if (src.includes('TW_PATCH_APPLY') || src.includes('@import "tailwindcss"')) {
37
+ const twPatched =
38
+ '\t\t\t\t\t\t// TW_PATCH_APPLY: @apply support\n' +
39
+ '\t\t\t\t\t\tcss = \'@import "tailwindcss" layer(reference);\\n\' + css;\n' +
40
+ "\t\t\t\t\t\tconst cssId = createVirtualImportId(filename, root, 'style');\n" +
41
+ '\t\t\t\t\t\tcssCache.set(cssId, css);';
42
+ const pandaPatched =
43
+ '\t\t\t\t\t\t// PANDA_PATCH_APPLY: bring panda CSS layer into scope for @apply\n' +
44
+ "\t\t\t\t\t\tcss = '@layer reset, base, tokens, recipes, utilities;\\n' + css;\n" +
45
+ "\t\t\t\t\t\tconst cssId = createVirtualImportId(filename, root, 'style');\n" +
46
+ '\t\t\t\t\t\tcssCache.set(cssId, css);';
47
+ const result = src.replace(twPatched, pandaPatched);
48
+ if (result === src) {
49
+ warn('Could not replace tailwind patch with Panda patch');
50
+ exitCode = 1;
51
+ return;
52
+ }
53
+ writeFileSync(target, result, 'utf-8');
54
+ log('Replaced tailwind @apply patch with Panda CSS @apply patch');
55
+ return;
56
+ }
57
+
58
+ // Case 2: fresh install, no tailwind patch — apply panda directly
59
+ const orig =
60
+ '\t\t\t\t\tif (css) {\n' +
61
+ "\t\t\t\t\t\tconst cssId = createVirtualImportId(filename, root, 'style');\n" +
62
+ '\t\t\t\t\t\tcssCache.set(cssId, css);';
63
+ const patched =
64
+ '\t\t\t\t\tif (css) {\n' +
65
+ '\t\t\t\t\t\t// PANDA_PATCH_APPLY: bring panda CSS layer into scope for @apply\n' +
66
+ "\t\t\t\t\t\tcss = '@layer reset, base, tokens, recipes, utilities;\\n' + css;\n" +
67
+ "\t\t\t\t\t\tconst cssId = createVirtualImportId(filename, root, 'style');\n" +
68
+ '\t\t\t\t\t\tcssCache.set(cssId, css);';
69
+
70
+ const result = src.replace(orig, patched);
71
+ if (result === src) {
72
+ warn('Could not find target in Ripple plugin');
73
+ exitCode = 1;
74
+ return;
75
+ }
76
+
77
+ writeFileSync(target, result, 'utf-8');
78
+ log('Patched Ripple plugin for Panda CSS @apply support in <style> blocks');
79
+ }
80
+
81
+ function resolveModule(rel) {
82
+ const p = join(projectRoot, 'node_modules', rel);
83
+ if (existsSync(p)) return p;
84
+ try {
85
+ const r = createRequire(join(projectRoot, 'package.json'));
86
+ return r.resolve(rel);
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
91
+
92
+ log('Applying Panda CSS @apply patch...');
93
+ patchRippleApply();
94
+ log('Done');
95
+ process.exit(exitCode);
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Transform Ripple .tsrx content into valid TSX for Panda CSS extraction.
3
+ *
4
+ * Ripple uses @-prefixed template syntax inside TSX. This transform:
5
+ * - Strips <style> blocks (CSS is noise for extraction)
6
+ * - Replaces `@{}` function body markers with `{}`
7
+ * - Strips `@if/for/else/empty` directive wrappers, keeping brace-delimited bodies
8
+ * as valid JSX expression blocks so ts-morph can traverse them
9
+ * - Converts `&[varname]` reactive declarations to plain identifiers
10
+ *
11
+ * The result is parseable TSX — not semantically correct, but ts-morph can walk it
12
+ * to find css()/cva()/sva() calls.
13
+ */
14
+
15
+ // Match @if/for/else-if directives with their args up to the opening brace.
16
+ // Handles one level of nested parentheses in conditions.
17
+ const directiveOpenRe =
18
+ /@(?:(if|for|else\s+if)\s*\((?:[^()]|\([^()]*\))*\)|(else|empty))\s*(\{)/g;
19
+ // Match @{} function body markers
20
+ const atBlockOpenRe = /@\{/g;
21
+ // Match <style> blocks (including attributes like scoped)
22
+ const styleBlockRe = /<style[^>]*>[\s\S]*?<\/style>/gi;
23
+ // Match reactive declarations
24
+ const reactiveDeclRe = /(let|const|var)\s+&\[(\w+)\]/g;
25
+
26
+ export function tsrxToTsx(code) {
27
+ // 1. Strip <style> blocks
28
+ let result = code.replace(styleBlockRe, '');
29
+
30
+ // 2. Replace function body @{} markers
31
+ result = result.replace(atBlockOpenRe, '{');
32
+
33
+ // 3. Convert reactive declarations: let &[name] → let name
34
+ result = result.replace(reactiveDeclRe, '$1 $2');
35
+
36
+ // 4. Strip @-directive wrappers, keeping the brace
37
+ // @if (expr) { → {
38
+ // } @else if (expr) { → }{
39
+ // } @else { → }{
40
+ // } @empty { → }{
41
+ // @for (decl; key) { → {
42
+ result = result.replace(
43
+ directiveOpenRe,
44
+ (match, directiveWithArgs, elseEmpty, brace) => {
45
+ // The directive name and parenthesized args are dropped.
46
+ // All we keep is the brace (which opens the body block).
47
+ return brace;
48
+ }
49
+ );
50
+
51
+ return result;
52
+ }