@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 +76 -0
- package/index.d.ts +27 -0
- package/package.json +43 -0
- package/src/index.js +38 -0
- package/src/panda-plugin.js +31 -0
- package/src/setup.js +95 -0
- package/src/tsrx-to-tsx.js +52 -0
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
|
+
}
|