@rozenite/vite-plugin 1.12.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.
- package/README.md +12 -0
- package/dist/bundle-dts.d.ts.map +1 -1
- package/dist/client-plugin.d.ts.map +1 -1
- package/dist/dev-config-module.d.ts.map +1 -1
- package/dist/dev-host/assets/index-DU3zXL2c.css +1 -0
- package/dist/dev-host/assets/index-Xsb6uYhI.js +13 -0
- package/dist/dev-host/index.html +3 -3
- package/dist/dev-host/manifest.json +2 -2
- package/dist/index.cjs +24 -8
- package/dist/index.d.ts +9 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +24 -8
- package/dist/react-native-plugin.d.ts.map +1 -1
- package/dist/sdk-plugin.d.ts.map +1 -1
- package/dist/server-plugin.d.ts.map +1 -1
- package/dist/tsconfig.lib.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/bundle-dts.ts +126 -0
- package/src/client-plugin.ts +394 -0
- package/src/dev-config-module.ts +32 -0
- package/src/dev-host/App.tsx +373 -0
- package/src/dev-host/components/DispatchForm.tsx +155 -0
- package/src/dev-host/components/FlowList.tsx +122 -0
- package/src/dev-host/components/MessageDetailsPane.tsx +82 -0
- package/src/dev-host/components/MessageLogPane.tsx +102 -0
- package/src/dev-host/components/PanelTabs.tsx +41 -0
- package/src/dev-host/config.ts +111 -0
- package/src/dev-host/constants.ts +12 -0
- package/src/dev-host/flow-runtime.ts +318 -0
- package/src/dev-host/index.html +12 -0
- package/src/dev-host/main.tsx +22 -0
- package/src/dev-host/server.ts +61 -0
- package/src/dev-host/styles.css +301 -0
- package/src/dev-host/types.ts +56 -0
- package/src/dev-host/utils.ts +99 -0
- package/src/dev-host/vite.config.mts +22 -0
- package/src/index.ts +124 -0
- package/src/load-config.ts +117 -0
- package/src/package-json.ts +16 -0
- package/src/react-native-plugin.ts +52 -0
- package/src/require-plugin.ts +221 -0
- package/src/sdk-plugin.ts +47 -0
- package/src/server-plugin.ts +39 -0
- package/src/utils.ts +31 -0
- package/src/virtual-modules.d.ts +7 -0
- package/dist/dev-host/assets/index-CkxvBMpp.css +0 -1
- package/dist/dev-host/assets/index-cNKHRwej.js +0 -11
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
type DevHostManifestEntry = {
|
|
5
|
+
file?: string;
|
|
6
|
+
css?: string[];
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
type DevHostBuildManifest = Record<string, DevHostManifestEntry>;
|
|
10
|
+
|
|
11
|
+
export type DevHostBuiltAssets = {
|
|
12
|
+
script: string;
|
|
13
|
+
styles: string[];
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const DEV_HOST_BUILD_ENTRY_KEY = 'index.html';
|
|
17
|
+
|
|
18
|
+
export const getDevHostHtmlTemplate = () => {
|
|
19
|
+
return `<!DOCTYPE html>
|
|
20
|
+
<html lang="en">
|
|
21
|
+
<head>
|
|
22
|
+
<meta charset="UTF-8" />
|
|
23
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
24
|
+
<title>Rozenite Dev Host</title>
|
|
25
|
+
</head>
|
|
26
|
+
<body>
|
|
27
|
+
<div id="root"></div>
|
|
28
|
+
</body>
|
|
29
|
+
</html>`;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const getDevHostSourceEntryFile = (packageDir: string) => {
|
|
33
|
+
return path.join(packageDir, 'src', 'dev-host', 'main.tsx');
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const getBuiltDevHostAssets = (
|
|
37
|
+
packageDir: string,
|
|
38
|
+
): DevHostBuiltAssets | null => {
|
|
39
|
+
const devHostDistDir = path.join(packageDir, 'dist', 'dev-host');
|
|
40
|
+
const manifestPath = path.join(devHostDistDir, 'manifest.json');
|
|
41
|
+
|
|
42
|
+
if (!fs.existsSync(manifestPath)) {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const manifest = JSON.parse(
|
|
47
|
+
fs.readFileSync(manifestPath, 'utf8'),
|
|
48
|
+
) as DevHostBuildManifest;
|
|
49
|
+
const entry = manifest[DEV_HOST_BUILD_ENTRY_KEY];
|
|
50
|
+
|
|
51
|
+
if (!entry?.file) {
|
|
52
|
+
throw new Error(
|
|
53
|
+
`Missing ${DEV_HOST_BUILD_ENTRY_KEY} entry in dev host manifest.`,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
script: path.join(devHostDistDir, entry.file),
|
|
59
|
+
styles: (entry.css ?? []).map((file) => path.join(devHostDistDir, file)),
|
|
60
|
+
};
|
|
61
|
+
};
|
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
@import '@rozenite/ui/styles.css';
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* The dev host is built from the vite-plugin package, while the shared
|
|
5
|
+
* stylesheet is owned by @rozenite/ui. Include its source explicitly so
|
|
6
|
+
* Tailwind emits the utilities used inside shared components (PluginShell,
|
|
7
|
+
* PluginHeader, and Split) as well as the ones used by the host itself.
|
|
8
|
+
*/
|
|
9
|
+
@source '../../../ui/src';
|
|
10
|
+
|
|
11
|
+
@layer base {
|
|
12
|
+
html,
|
|
13
|
+
body,
|
|
14
|
+
#root {
|
|
15
|
+
height: 100%;
|
|
16
|
+
margin: 0;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
body {
|
|
20
|
+
overflow: hidden;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.dev-host {
|
|
25
|
+
height: 100dvh;
|
|
26
|
+
overflow: hidden;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.dev-host-body {
|
|
30
|
+
min-height: 0;
|
|
31
|
+
overflow: hidden !important;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.dev-host-header {
|
|
35
|
+
min-height: 3.25rem;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
.dev-host-header-title {
|
|
39
|
+
display: flex;
|
|
40
|
+
min-width: 0;
|
|
41
|
+
flex-direction: column;
|
|
42
|
+
gap: 0.125rem;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
.dev-host-header-subtitle {
|
|
46
|
+
max-width: 30rem;
|
|
47
|
+
overflow: hidden;
|
|
48
|
+
text-overflow: ellipsis;
|
|
49
|
+
white-space: nowrap;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
.dev-host-panel-select {
|
|
53
|
+
width: min(16rem, 32vw);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.dev-host-main-split {
|
|
57
|
+
min-height: 0;
|
|
58
|
+
flex: 1;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.dev-host-preview {
|
|
62
|
+
height: 100%;
|
|
63
|
+
position: relative;
|
|
64
|
+
min-height: 0;
|
|
65
|
+
overflow: hidden;
|
|
66
|
+
background: var(--background);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.dev-host-iframe {
|
|
70
|
+
display: block;
|
|
71
|
+
height: 100%;
|
|
72
|
+
width: 100%;
|
|
73
|
+
border: 0;
|
|
74
|
+
background: var(--background);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.dev-host-devtools {
|
|
78
|
+
height: 100%;
|
|
79
|
+
min-height: 0;
|
|
80
|
+
overflow: hidden;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.dev-host-pane {
|
|
84
|
+
display: flex;
|
|
85
|
+
height: 100%;
|
|
86
|
+
min-height: 0;
|
|
87
|
+
flex: 1;
|
|
88
|
+
flex-direction: column;
|
|
89
|
+
overflow: hidden;
|
|
90
|
+
background: var(--background);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.dev-host-pane-header {
|
|
94
|
+
display: flex;
|
|
95
|
+
min-height: 2.75rem;
|
|
96
|
+
flex-shrink: 0;
|
|
97
|
+
align-items: center;
|
|
98
|
+
justify-content: space-between;
|
|
99
|
+
gap: 0.75rem;
|
|
100
|
+
border-bottom: 1px solid var(--border);
|
|
101
|
+
background: var(--card);
|
|
102
|
+
padding: 0.625rem 0.875rem;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.dev-host-pane-title {
|
|
106
|
+
color: var(--foreground);
|
|
107
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
108
|
+
font-size: 0.6875rem;
|
|
109
|
+
font-weight: 600;
|
|
110
|
+
letter-spacing: 0.06em;
|
|
111
|
+
text-transform: uppercase;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
.dev-host-pane-actions {
|
|
115
|
+
display: flex;
|
|
116
|
+
align-items: center;
|
|
117
|
+
gap: 0.25rem;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
.dev-host-scroll {
|
|
121
|
+
min-height: 0;
|
|
122
|
+
flex: 1;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.dev-host-message-table {
|
|
126
|
+
min-width: 42rem;
|
|
127
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.dev-host-direction-in {
|
|
131
|
+
color: #22c55e;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
.dev-host-direction-out {
|
|
135
|
+
color: #f59e0b;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.dev-host-form,
|
|
139
|
+
.dev-host-flow-list,
|
|
140
|
+
.dev-host-details {
|
|
141
|
+
display: flex;
|
|
142
|
+
min-height: 0;
|
|
143
|
+
flex-direction: column;
|
|
144
|
+
gap: 1rem;
|
|
145
|
+
padding: 1rem;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
.dev-host-form {
|
|
149
|
+
flex: 1;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.dev-host-field {
|
|
153
|
+
display: grid;
|
|
154
|
+
gap: 0.375rem;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.dev-host-label {
|
|
158
|
+
color: var(--muted-foreground);
|
|
159
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
160
|
+
font-size: 0.75rem;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.dev-host-actions {
|
|
164
|
+
display: flex;
|
|
165
|
+
justify-content: flex-end;
|
|
166
|
+
gap: 0.5rem;
|
|
167
|
+
margin-top: auto;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.dev-host-tabs-header {
|
|
171
|
+
display: flex;
|
|
172
|
+
align-items: center;
|
|
173
|
+
justify-content: space-between;
|
|
174
|
+
gap: 0.75rem;
|
|
175
|
+
padding: 0.75rem 1rem 0;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
.dev-host-tab-panel {
|
|
179
|
+
min-height: 0;
|
|
180
|
+
flex: 1;
|
|
181
|
+
overflow: auto;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.dev-host-flow-list {
|
|
185
|
+
padding-top: 0.75rem;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
.dev-host-flow-button {
|
|
189
|
+
display: flex;
|
|
190
|
+
width: 100%;
|
|
191
|
+
align-items: center;
|
|
192
|
+
justify-content: space-between;
|
|
193
|
+
gap: 0.75rem;
|
|
194
|
+
text-align: left;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.dev-host-flow-name,
|
|
198
|
+
.dev-host-flow-title {
|
|
199
|
+
display: inline-flex;
|
|
200
|
+
align-items: center;
|
|
201
|
+
gap: 0.375rem;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
.dev-host-flow-runs {
|
|
205
|
+
display: grid;
|
|
206
|
+
gap: 0.75rem;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
.dev-host-flow-state {
|
|
210
|
+
display: grid;
|
|
211
|
+
gap: 0.5rem;
|
|
212
|
+
border: 1px solid var(--border);
|
|
213
|
+
border-radius: var(--radius-md);
|
|
214
|
+
padding: 0.75rem;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
.dev-host-flow-state[data-status='running'] {
|
|
218
|
+
border-color: var(--primary);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
.dev-host-flow-state[data-status='failed'] {
|
|
222
|
+
border-color: var(--destructive);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.dev-host-flow-state-header {
|
|
226
|
+
display: flex;
|
|
227
|
+
align-items: center;
|
|
228
|
+
justify-content: space-between;
|
|
229
|
+
gap: 0.5rem;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
.dev-host-flow-state-result,
|
|
233
|
+
.dev-host-flow-state-error,
|
|
234
|
+
.dev-host-detail-value,
|
|
235
|
+
.dev-host-detail-payload {
|
|
236
|
+
overflow: auto;
|
|
237
|
+
border: 1px solid var(--border);
|
|
238
|
+
border-radius: var(--radius-sm);
|
|
239
|
+
background: var(--muted);
|
|
240
|
+
padding: 0.625rem 0.75rem;
|
|
241
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
242
|
+
font-size: 0.75rem;
|
|
243
|
+
line-height: 1.5;
|
|
244
|
+
white-space: pre-wrap;
|
|
245
|
+
word-break: break-word;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
.dev-host-flow-state-error {
|
|
249
|
+
color: var(--destructive);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
.dev-host-detail-section {
|
|
253
|
+
display: grid;
|
|
254
|
+
gap: 0.375rem;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.dev-host-detail-payload {
|
|
258
|
+
min-height: 4rem;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
.dev-host-mobile-tabs {
|
|
262
|
+
height: 100%;
|
|
263
|
+
min-height: 0;
|
|
264
|
+
display: flex;
|
|
265
|
+
flex: 1;
|
|
266
|
+
flex-direction: column;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
.dev-host-mobile-tab-list {
|
|
270
|
+
flex-shrink: 0;
|
|
271
|
+
padding: 0.5rem 0.75rem 0;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.dev-host-mobile-tab-panel {
|
|
275
|
+
min-height: 0;
|
|
276
|
+
display: flex;
|
|
277
|
+
flex: 1;
|
|
278
|
+
flex-direction: column;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
.dev-host-mobile-tab-panel > .dev-host-pane {
|
|
282
|
+
display: flex;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
@media (max-width: 960px) {
|
|
286
|
+
.dev-host-header {
|
|
287
|
+
padding-inline: 0.75rem;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.dev-host-header-subtitle {
|
|
291
|
+
display: none;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
.dev-host-panel-select {
|
|
295
|
+
width: min(12rem, 42vw);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
.dev-host-message-table {
|
|
299
|
+
min-width: 36rem;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { DevFlowEntry } from '../load-config.js';
|
|
2
|
+
|
|
3
|
+
export type DevHostPanelEntry = {
|
|
4
|
+
label: string;
|
|
5
|
+
source: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type DevHostPresetEntry = {
|
|
9
|
+
name: string;
|
|
10
|
+
displayName: string;
|
|
11
|
+
type: string;
|
|
12
|
+
payload: unknown;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type DevHostFlowEntry = {
|
|
16
|
+
name: string;
|
|
17
|
+
displayName: string;
|
|
18
|
+
autoRun: boolean;
|
|
19
|
+
run: DevFlowEntry['run'];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export type DevHostState = {
|
|
23
|
+
packageName: string;
|
|
24
|
+
packageDescription: string;
|
|
25
|
+
panels: DevHostPanelEntry[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type MessageEntry = {
|
|
29
|
+
id: string;
|
|
30
|
+
direction: 'in' | 'out';
|
|
31
|
+
date: string;
|
|
32
|
+
type: string;
|
|
33
|
+
payload: unknown;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type DevHostFlowRunStatus =
|
|
37
|
+
| 'running'
|
|
38
|
+
| 'succeeded'
|
|
39
|
+
| 'failed'
|
|
40
|
+
| 'aborted';
|
|
41
|
+
|
|
42
|
+
export type DevHostFlowRunState = {
|
|
43
|
+
id: string;
|
|
44
|
+
flowName: string;
|
|
45
|
+
flowDisplayName: string;
|
|
46
|
+
status: DevHostFlowRunStatus;
|
|
47
|
+
result: unknown;
|
|
48
|
+
error: string | null;
|
|
49
|
+
autoRun: boolean;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export type PluginMessage = {
|
|
53
|
+
pluginId: string;
|
|
54
|
+
type: string;
|
|
55
|
+
payload: unknown;
|
|
56
|
+
};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { DEV_HOST_STATE_ELEMENT_ID } from './constants.js';
|
|
2
|
+
import type {
|
|
3
|
+
DevHostPanelEntry,
|
|
4
|
+
DevHostState,
|
|
5
|
+
MessageEntry,
|
|
6
|
+
PluginMessage,
|
|
7
|
+
} from './types.js';
|
|
8
|
+
|
|
9
|
+
export const cn = (...parts: Array<string | false | null | undefined>) => {
|
|
10
|
+
return parts.filter(Boolean).join(' ');
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export const formatPayloadPreview = (payload: unknown) => {
|
|
14
|
+
if (payload == null) {
|
|
15
|
+
return 'null';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if (typeof payload === 'string') {
|
|
19
|
+
return payload;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
return JSON.stringify(payload);
|
|
24
|
+
} catch {
|
|
25
|
+
return String(payload);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const formatMessageDate = (date: string) => {
|
|
30
|
+
return new Date(date).toLocaleString();
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const formatMessageTableDate = (date: string) => {
|
|
34
|
+
return new Date(date).toLocaleString([], {
|
|
35
|
+
month: '2-digit',
|
|
36
|
+
day: '2-digit',
|
|
37
|
+
hour: '2-digit',
|
|
38
|
+
minute: '2-digit',
|
|
39
|
+
second: '2-digit',
|
|
40
|
+
hour12: false,
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
export const formatPayloadForCommandInput = (payload: unknown) => {
|
|
45
|
+
try {
|
|
46
|
+
return JSON.stringify(payload, null, 2);
|
|
47
|
+
} catch {
|
|
48
|
+
return String(payload);
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const isPluginMessage = (value: unknown): value is PluginMessage => {
|
|
53
|
+
return (
|
|
54
|
+
typeof value === 'object' &&
|
|
55
|
+
value !== null &&
|
|
56
|
+
'pluginId' in value &&
|
|
57
|
+
'type' in value &&
|
|
58
|
+
'payload' in value
|
|
59
|
+
);
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const getInitialPanel = (panels: DevHostPanelEntry[]) => {
|
|
63
|
+
const requestedPanel = new URLSearchParams(window.location.search).get(
|
|
64
|
+
'panel',
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
if (requestedPanel) {
|
|
68
|
+
const matchedPanel = panels.find((panel) => panel.label === requestedPanel);
|
|
69
|
+
if (matchedPanel) {
|
|
70
|
+
return matchedPanel;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return panels[0] ?? null;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
export const createMessageEntry = (
|
|
78
|
+
input: Omit<MessageEntry, 'id' | 'date'>,
|
|
79
|
+
): MessageEntry => {
|
|
80
|
+
return {
|
|
81
|
+
id: crypto.randomUUID(),
|
|
82
|
+
date: new Date().toISOString(),
|
|
83
|
+
...input,
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export const clamp = (value: number, min: number, max: number) => {
|
|
88
|
+
return Math.min(Math.max(value, min), max);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export const readDevHostState = (): DevHostState => {
|
|
92
|
+
const stateElement = document.getElementById(DEV_HOST_STATE_ELEMENT_ID);
|
|
93
|
+
|
|
94
|
+
if (!stateElement?.textContent) {
|
|
95
|
+
throw new Error('Rozenite dev host failed to initialize.');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return JSON.parse(stateElement.textContent) as DevHostState;
|
|
99
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
4
|
+
import { defineConfig } from 'vite';
|
|
5
|
+
|
|
6
|
+
const HOST_APP_ROOT = fileURLToPath(new URL('./', import.meta.url));
|
|
7
|
+
const PACKAGE_ROOT = fileURLToPath(new URL('../../', import.meta.url));
|
|
8
|
+
|
|
9
|
+
export default defineConfig({
|
|
10
|
+
root: HOST_APP_ROOT,
|
|
11
|
+
base: './',
|
|
12
|
+
publicDir: false,
|
|
13
|
+
plugins: [tailwindcss()],
|
|
14
|
+
build: {
|
|
15
|
+
outDir: path.join(PACKAGE_ROOT, 'dist', 'dev-host'),
|
|
16
|
+
emptyOutDir: true,
|
|
17
|
+
manifest: 'manifest.json',
|
|
18
|
+
// The dev host is served locally during plugin development, so the
|
|
19
|
+
// default production-oriented chunk warning is just noise here.
|
|
20
|
+
chunkSizeWarningLimit: 1000,
|
|
21
|
+
},
|
|
22
|
+
});
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { PluginOption } from 'vite';
|
|
2
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
3
|
+
import react from '@vitejs/plugin-react';
|
|
4
|
+
import reactNativeWeb from 'vite-plugin-react-native-web';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import { rozeniteServerPlugin } from './server-plugin.js';
|
|
7
|
+
import { rozeniteClientPlugin } from './client-plugin.js';
|
|
8
|
+
import { rozeniteReactNativePlugin } from './react-native-plugin.js';
|
|
9
|
+
import { rozeniteSdkPlugin } from './sdk-plugin.js';
|
|
10
|
+
import maybeDtsPlugin from 'vite-plugin-dts';
|
|
11
|
+
import requirePlugin from './require-plugin.js';
|
|
12
|
+
import { bundleTargetDeclarations } from './bundle-dts.js';
|
|
13
|
+
|
|
14
|
+
// vite-plugin-dts exports differently in CJS and ESM
|
|
15
|
+
const dtsPlugin =
|
|
16
|
+
'default' in maybeDtsPlugin
|
|
17
|
+
? (maybeDtsPlugin.default as typeof maybeDtsPlugin)
|
|
18
|
+
: maybeDtsPlugin;
|
|
19
|
+
|
|
20
|
+
const getDtsPlugin = (
|
|
21
|
+
target: 'react-native' | 'metro' | 'sdk',
|
|
22
|
+
): PluginOption => {
|
|
23
|
+
const projectRoot = process.cwd();
|
|
24
|
+
const entryRoot =
|
|
25
|
+
target === 'react-native'
|
|
26
|
+
? 'react-native.ts'
|
|
27
|
+
: target === 'metro'
|
|
28
|
+
? 'metro.ts'
|
|
29
|
+
: 'sdk.ts';
|
|
30
|
+
const distRoot = path.join(projectRoot, 'dist');
|
|
31
|
+
const targetRoot = path.join(distRoot, target);
|
|
32
|
+
const publicEntryPath = path.join(projectRoot, 'dist', target, 'index.d.ts');
|
|
33
|
+
const sdkBundleEntryPath = path.join(targetRoot, `${target}.d.ts`);
|
|
34
|
+
const rawSdkEntryPath = path.join(distRoot, `${target}.d.ts`);
|
|
35
|
+
|
|
36
|
+
return dtsPlugin({
|
|
37
|
+
entryRoot,
|
|
38
|
+
include: [entryRoot, 'src/**/*.ts', 'src/**/*.tsx', 'src/**/*.d.ts'],
|
|
39
|
+
outDir: `dist/${target}`,
|
|
40
|
+
strictOutput: false,
|
|
41
|
+
insertTypesEntry: false,
|
|
42
|
+
// Preserve package specifiers in published declarations instead of
|
|
43
|
+
// rewriting workspace imports to source file paths.
|
|
44
|
+
pathsToAliases: false,
|
|
45
|
+
tsconfigPath: path.join(projectRoot, 'tsconfig.json'),
|
|
46
|
+
beforeWriteFile: (filePath, content) => {
|
|
47
|
+
if (!filePath.endsWith('.d.ts')) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (
|
|
52
|
+
filePath === publicEntryPath ||
|
|
53
|
+
(target !== 'sdk' && filePath.endsWith(`/${target}.d.ts`))
|
|
54
|
+
) {
|
|
55
|
+
return {
|
|
56
|
+
filePath: publicEntryPath,
|
|
57
|
+
content,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (target === 'sdk' && filePath === rawSdkEntryPath) {
|
|
62
|
+
return {
|
|
63
|
+
// vite-plugin-dts emits the SDK root declaration at dist/sdk.d.ts.
|
|
64
|
+
// Move it under dist/sdk/ before API Extractor rolls it up into the
|
|
65
|
+
// published dist/sdk/index.d.ts.
|
|
66
|
+
filePath: sdkBundleEntryPath,
|
|
67
|
+
content,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const relativeToDist = path.relative(distRoot, filePath);
|
|
72
|
+
|
|
73
|
+
if (!relativeToDist.startsWith('src/')) {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
filePath: path.join(targetRoot, relativeToDist),
|
|
79
|
+
content,
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
afterBuild: async () => {
|
|
83
|
+
await bundleTargetDeclarations(projectRoot, target);
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export type RozenitePluginOptions = {
|
|
89
|
+
/**
|
|
90
|
+
* Configure Tailwind CSS v4 for client panels.
|
|
91
|
+
*
|
|
92
|
+
* @default true
|
|
93
|
+
*/
|
|
94
|
+
tailwind?: boolean;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
export const rozenitePlugin = ({
|
|
98
|
+
tailwind = true,
|
|
99
|
+
}: RozenitePluginOptions = {}): PluginOption[] => {
|
|
100
|
+
const isServer = process.env.VITE_ROZENITE_TARGET === 'server';
|
|
101
|
+
const isReactNative = process.env.VITE_ROZENITE_TARGET === 'react-native';
|
|
102
|
+
const isSdk = process.env.VITE_ROZENITE_TARGET === 'sdk';
|
|
103
|
+
|
|
104
|
+
if (isServer) {
|
|
105
|
+
return [rozeniteServerPlugin(), getDtsPlugin('metro')] as PluginOption[];
|
|
106
|
+
} else if (isReactNative) {
|
|
107
|
+
return [
|
|
108
|
+
react(),
|
|
109
|
+
requirePlugin(),
|
|
110
|
+
rozeniteReactNativePlugin(),
|
|
111
|
+
getDtsPlugin('react-native'),
|
|
112
|
+
] as PluginOption[];
|
|
113
|
+
} else if (isSdk) {
|
|
114
|
+
return [rozeniteSdkPlugin(), getDtsPlugin('sdk')] as PluginOption[];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return [
|
|
118
|
+
...(tailwind ? [tailwindcss()] : []),
|
|
119
|
+
react(),
|
|
120
|
+
// @ts-expect-error: TypeScript gets confused by the dual export
|
|
121
|
+
reactNativeWeb(),
|
|
122
|
+
rozeniteClientPlugin(),
|
|
123
|
+
];
|
|
124
|
+
};
|