@ynput/ayon-addon-dev 0.0.0-stage → 0.1.1
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/AGENTS.md +139 -0
- package/LICENSE +105 -0
- package/dist/browse.d.ts +59 -0
- package/dist/browse.js +156 -0
- package/dist/check.d.ts +30 -0
- package/dist/check.js +117 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +249 -0
- package/dist/eslint.d.ts +13 -0
- package/dist/eslint.js +47 -0
- package/dist/project.d.ts +28 -0
- package/dist/project.js +97 -0
- package/dist/rest.d.ts +47 -0
- package/dist/rest.js +181 -0
- package/dist/shell/index.js +1635 -0
- package/dist/shell/style.css +416 -0
- package/dist/standalone.d.ts +12 -0
- package/dist/standalone.js +176 -0
- package/dist/vite.d.ts +31 -0
- package/dist/vite.js +147 -0
- package/package.json +63 -4
- package/README.md +0 -3
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
html,
|
|
2
|
+
body,
|
|
3
|
+
#root {
|
|
4
|
+
height: 100%;
|
|
5
|
+
margin: 0;
|
|
6
|
+
}
|
|
7
|
+
body {
|
|
8
|
+
background: var(--md-sys-color-surface-container-lowest);
|
|
9
|
+
color: var(--md-sys-color-on-surface);
|
|
10
|
+
font-family: 'Nunito Sans', sans-serif;
|
|
11
|
+
font-size: 14px;
|
|
12
|
+
overflow: hidden;
|
|
13
|
+
}
|
|
14
|
+
main {
|
|
15
|
+
display: flex;
|
|
16
|
+
flex: 1;
|
|
17
|
+
height: 100%;
|
|
18
|
+
min-height: 0;
|
|
19
|
+
overflow: hidden;
|
|
20
|
+
}
|
|
21
|
+
/* AYON's own full-page layouts (the login page), as in its index.scss */
|
|
22
|
+
main.center {
|
|
23
|
+
flex-direction: column;
|
|
24
|
+
align-items: center;
|
|
25
|
+
justify-content: center;
|
|
26
|
+
}
|
|
27
|
+
.ayon-shell.p-splitter {
|
|
28
|
+
height: 100%;
|
|
29
|
+
border: none;
|
|
30
|
+
border-radius: 0;
|
|
31
|
+
background: none;
|
|
32
|
+
}
|
|
33
|
+
.ayon-shell-panel {
|
|
34
|
+
display: flex;
|
|
35
|
+
flex-direction: column;
|
|
36
|
+
width: 100%;
|
|
37
|
+
min-width: 0;
|
|
38
|
+
height: 100%;
|
|
39
|
+
background: var(--md-sys-color-surface-container-low);
|
|
40
|
+
}
|
|
41
|
+
.ayon-shell-panel-header {
|
|
42
|
+
display: flex;
|
|
43
|
+
align-items: center;
|
|
44
|
+
gap: 10px;
|
|
45
|
+
padding: 10px 12px;
|
|
46
|
+
flex-shrink: 0;
|
|
47
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
48
|
+
}
|
|
49
|
+
.ayon-shell-panel-header > .icon {
|
|
50
|
+
font-size: 22px;
|
|
51
|
+
color: var(--md-sys-color-primary);
|
|
52
|
+
}
|
|
53
|
+
.ayon-shell-panel-header > div {
|
|
54
|
+
display: flex;
|
|
55
|
+
flex-direction: column;
|
|
56
|
+
min-width: 0;
|
|
57
|
+
}
|
|
58
|
+
.ayon-shell-panel-header strong span {
|
|
59
|
+
font-weight: normal;
|
|
60
|
+
color: var(--md-sys-color-outline);
|
|
61
|
+
}
|
|
62
|
+
.ayon-shell-panel-header small {
|
|
63
|
+
overflow: hidden;
|
|
64
|
+
white-space: nowrap;
|
|
65
|
+
text-overflow: ellipsis;
|
|
66
|
+
color: var(--md-sys-color-outline);
|
|
67
|
+
}
|
|
68
|
+
/* AYON's gutters are transparent: the darker background behind them sets the halves apart */
|
|
69
|
+
.ayon-shell-panel-split.p-splitter {
|
|
70
|
+
flex: 1;
|
|
71
|
+
min-height: 0;
|
|
72
|
+
border: none;
|
|
73
|
+
border-radius: 0;
|
|
74
|
+
background: var(--md-sys-color-surface-container-lowest);
|
|
75
|
+
}
|
|
76
|
+
/* each half scrolls on its own; a long half scrolls instead of squeezing its parts
|
|
77
|
+
(.p-splitter: AYON's theme makes splitter panels transparent) */
|
|
78
|
+
.p-splitter > .ayon-shell-section {
|
|
79
|
+
background: var(--md-sys-color-surface-container-low);
|
|
80
|
+
}
|
|
81
|
+
.ayon-shell-section {
|
|
82
|
+
display: flex;
|
|
83
|
+
flex-direction: column;
|
|
84
|
+
gap: 8px;
|
|
85
|
+
min-width: 0;
|
|
86
|
+
padding: 12px;
|
|
87
|
+
overflow: auto;
|
|
88
|
+
}
|
|
89
|
+
.ayon-shell-section .ayon-addon-error {
|
|
90
|
+
overflow-wrap: anywhere;
|
|
91
|
+
}
|
|
92
|
+
.ayon-shell-section > * {
|
|
93
|
+
flex-shrink: 0;
|
|
94
|
+
}
|
|
95
|
+
.ayon-shell-panel h3 {
|
|
96
|
+
margin: 4px 0 0;
|
|
97
|
+
font-size: 12px;
|
|
98
|
+
text-transform: uppercase;
|
|
99
|
+
color: var(--md-sys-color-outline);
|
|
100
|
+
}
|
|
101
|
+
.ayon-shell-panel code {
|
|
102
|
+
font-size: 11px;
|
|
103
|
+
color: var(--md-sys-color-outline);
|
|
104
|
+
}
|
|
105
|
+
.ayon-shell-note {
|
|
106
|
+
color: var(--md-sys-color-outline);
|
|
107
|
+
}
|
|
108
|
+
/* contributions: an area of AYON, its slots (with the part of the area), one line each */
|
|
109
|
+
.ayon-shell-area {
|
|
110
|
+
display: flex;
|
|
111
|
+
flex-direction: column;
|
|
112
|
+
padding-bottom: 4px;
|
|
113
|
+
}
|
|
114
|
+
.ayon-shell-area + .ayon-shell-area {
|
|
115
|
+
padding-top: 4px;
|
|
116
|
+
border-top: 1px solid var(--md-sys-color-outline-variant);
|
|
117
|
+
}
|
|
118
|
+
.ayon-shell-area-header {
|
|
119
|
+
display: flex;
|
|
120
|
+
align-items: center;
|
|
121
|
+
gap: 8px;
|
|
122
|
+
height: 32px;
|
|
123
|
+
padding: 0 8px;
|
|
124
|
+
border: none;
|
|
125
|
+
border-radius: 4px;
|
|
126
|
+
background: none;
|
|
127
|
+
color: inherit;
|
|
128
|
+
font: inherit;
|
|
129
|
+
font-weight: 600;
|
|
130
|
+
cursor: pointer;
|
|
131
|
+
}
|
|
132
|
+
.ayon-shell-area-header:hover {
|
|
133
|
+
background: var(--md-sys-color-surface-container-high);
|
|
134
|
+
}
|
|
135
|
+
.ayon-shell-area-header .icon {
|
|
136
|
+
font-size: 18px;
|
|
137
|
+
color: var(--md-sys-color-outline);
|
|
138
|
+
}
|
|
139
|
+
.ayon-shell-area-header small {
|
|
140
|
+
margin-left: auto;
|
|
141
|
+
font-weight: normal;
|
|
142
|
+
color: var(--md-sys-color-outline);
|
|
143
|
+
}
|
|
144
|
+
.ayon-shell-slot {
|
|
145
|
+
display: flex;
|
|
146
|
+
flex-direction: column;
|
|
147
|
+
}
|
|
148
|
+
.ayon-shell-slot-label {
|
|
149
|
+
padding: 6px 8px 2px 34px;
|
|
150
|
+
font-size: 12px;
|
|
151
|
+
color: var(--md-sys-color-outline);
|
|
152
|
+
cursor: help;
|
|
153
|
+
}
|
|
154
|
+
.ayon-shell-slot-label span::after {
|
|
155
|
+
content: ' › ';
|
|
156
|
+
}
|
|
157
|
+
.ayon-shell-panel .contribution {
|
|
158
|
+
display: flex;
|
|
159
|
+
align-items: center;
|
|
160
|
+
gap: 8px;
|
|
161
|
+
height: 30px;
|
|
162
|
+
padding: 0 8px 0 34px;
|
|
163
|
+
border: none;
|
|
164
|
+
border-radius: 4px;
|
|
165
|
+
background: none;
|
|
166
|
+
color: inherit;
|
|
167
|
+
font: inherit;
|
|
168
|
+
text-align: left;
|
|
169
|
+
cursor: pointer;
|
|
170
|
+
}
|
|
171
|
+
.ayon-shell-panel .contribution span {
|
|
172
|
+
overflow: hidden;
|
|
173
|
+
white-space: nowrap;
|
|
174
|
+
text-overflow: ellipsis;
|
|
175
|
+
}
|
|
176
|
+
.ayon-shell-panel .contribution .icon {
|
|
177
|
+
font-size: 18px;
|
|
178
|
+
color: var(--md-sys-color-outline);
|
|
179
|
+
}
|
|
180
|
+
.ayon-shell-panel .contribution:hover {
|
|
181
|
+
background: var(--md-sys-color-surface-container-high);
|
|
182
|
+
}
|
|
183
|
+
.ayon-shell-panel .contribution.selected {
|
|
184
|
+
background: var(--md-sys-color-primary-container);
|
|
185
|
+
color: var(--md-sys-color-on-primary-container);
|
|
186
|
+
}
|
|
187
|
+
.ayon-shell-panel .contribution.selected .icon {
|
|
188
|
+
color: inherit;
|
|
189
|
+
}
|
|
190
|
+
/* the context pickers: a label, then the control */
|
|
191
|
+
.ayon-shell-fields {
|
|
192
|
+
display: flex;
|
|
193
|
+
flex-direction: column;
|
|
194
|
+
gap: 8px;
|
|
195
|
+
}
|
|
196
|
+
.ayon-shell-field {
|
|
197
|
+
display: grid;
|
|
198
|
+
grid-template-columns: 64px minmax(0, 1fr);
|
|
199
|
+
align-items: start;
|
|
200
|
+
gap: 8px;
|
|
201
|
+
}
|
|
202
|
+
.ayon-shell-field > label {
|
|
203
|
+
line-height: 32px;
|
|
204
|
+
color: var(--md-sys-color-outline);
|
|
205
|
+
}
|
|
206
|
+
.ayon-shell-field > div {
|
|
207
|
+
display: flex;
|
|
208
|
+
flex-direction: column;
|
|
209
|
+
gap: 4px;
|
|
210
|
+
min-width: 0;
|
|
211
|
+
}
|
|
212
|
+
.ayon-shell-entities {
|
|
213
|
+
display: flex;
|
|
214
|
+
gap: 4px;
|
|
215
|
+
}
|
|
216
|
+
.ayon-shell-entities > :first-child {
|
|
217
|
+
flex: 1;
|
|
218
|
+
min-width: 0;
|
|
219
|
+
}
|
|
220
|
+
.ayon-shell-entity {
|
|
221
|
+
display: flex;
|
|
222
|
+
align-items: center;
|
|
223
|
+
gap: 4px;
|
|
224
|
+
height: 28px;
|
|
225
|
+
padding-left: 8px;
|
|
226
|
+
border-radius: 4px;
|
|
227
|
+
background: var(--md-sys-color-surface-container);
|
|
228
|
+
}
|
|
229
|
+
.ayon-shell-entity span {
|
|
230
|
+
flex: 1;
|
|
231
|
+
overflow: hidden;
|
|
232
|
+
white-space: nowrap;
|
|
233
|
+
text-overflow: ellipsis;
|
|
234
|
+
}
|
|
235
|
+
.ayon-shell-subheader {
|
|
236
|
+
display: flex;
|
|
237
|
+
align-items: center;
|
|
238
|
+
gap: 4px;
|
|
239
|
+
min-height: 28px;
|
|
240
|
+
padding: 0;
|
|
241
|
+
border: none;
|
|
242
|
+
background: none;
|
|
243
|
+
color: var(--md-sys-color-outline);
|
|
244
|
+
font: inherit;
|
|
245
|
+
font-size: 12px;
|
|
246
|
+
}
|
|
247
|
+
.ayon-shell-subheader > .label {
|
|
248
|
+
flex: 1;
|
|
249
|
+
text-align: left;
|
|
250
|
+
}
|
|
251
|
+
button.ayon-shell-subheader {
|
|
252
|
+
cursor: pointer;
|
|
253
|
+
}
|
|
254
|
+
.ayon-shell-subheader .icon {
|
|
255
|
+
font-size: 18px;
|
|
256
|
+
}
|
|
257
|
+
.ayon-shell-passed {
|
|
258
|
+
display: flex;
|
|
259
|
+
flex-direction: column;
|
|
260
|
+
}
|
|
261
|
+
.ayon-shell-fixtures {
|
|
262
|
+
display: flex;
|
|
263
|
+
flex-direction: column;
|
|
264
|
+
gap: 6px;
|
|
265
|
+
margin-top: auto;
|
|
266
|
+
padding-top: 8px;
|
|
267
|
+
border-top: 1px solid var(--md-sys-color-outline-variant);
|
|
268
|
+
}
|
|
269
|
+
.ayon-shell-link {
|
|
270
|
+
display: flex;
|
|
271
|
+
align-items: center;
|
|
272
|
+
gap: 6px;
|
|
273
|
+
color: inherit;
|
|
274
|
+
text-decoration: none;
|
|
275
|
+
}
|
|
276
|
+
.ayon-shell-link:hover,
|
|
277
|
+
.ayon-shell-link.selected {
|
|
278
|
+
color: var(--md-sys-color-primary);
|
|
279
|
+
}
|
|
280
|
+
.ayon-shell-link .icon {
|
|
281
|
+
font-size: 16px;
|
|
282
|
+
}
|
|
283
|
+
.ayon-shell-code {
|
|
284
|
+
margin: 0;
|
|
285
|
+
padding: 8px;
|
|
286
|
+
font-size: 11px;
|
|
287
|
+
white-space: pre-wrap;
|
|
288
|
+
word-break: break-all;
|
|
289
|
+
background: var(--md-sys-color-surface-container-lowest);
|
|
290
|
+
border-radius: 4px;
|
|
291
|
+
}
|
|
292
|
+
/* a few lines of the context: the whole of it opens in a dialog */
|
|
293
|
+
.ayon-shell-context {
|
|
294
|
+
position: relative;
|
|
295
|
+
max-height: 64px;
|
|
296
|
+
padding: 0;
|
|
297
|
+
overflow: hidden;
|
|
298
|
+
border: 1px solid transparent;
|
|
299
|
+
border-radius: 4px;
|
|
300
|
+
background: none;
|
|
301
|
+
color: inherit;
|
|
302
|
+
text-align: left;
|
|
303
|
+
cursor: pointer;
|
|
304
|
+
}
|
|
305
|
+
.ayon-shell-context:not(:disabled):hover {
|
|
306
|
+
border-color: var(--md-sys-color-outline-variant);
|
|
307
|
+
}
|
|
308
|
+
.ayon-shell-context::after {
|
|
309
|
+
content: '';
|
|
310
|
+
position: absolute;
|
|
311
|
+
inset: auto 0 0;
|
|
312
|
+
height: 24px;
|
|
313
|
+
background: linear-gradient(transparent, var(--md-sys-color-surface-container-lowest));
|
|
314
|
+
}
|
|
315
|
+
.ayon-shell-stage {
|
|
316
|
+
display: flex;
|
|
317
|
+
flex-direction: column;
|
|
318
|
+
min-width: 0;
|
|
319
|
+
min-height: 0;
|
|
320
|
+
overflow: hidden;
|
|
321
|
+
}
|
|
322
|
+
main.ayon-shell-stage {
|
|
323
|
+
height: auto;
|
|
324
|
+
}
|
|
325
|
+
/* the shell's line above the stage: not part of AYON, so it looks like the developer panel */
|
|
326
|
+
.ayon-shell-caption {
|
|
327
|
+
display: flex;
|
|
328
|
+
align-items: center;
|
|
329
|
+
gap: 12px;
|
|
330
|
+
padding: 8px 16px;
|
|
331
|
+
flex-shrink: 0;
|
|
332
|
+
background: var(--md-sys-color-surface-container-low);
|
|
333
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
334
|
+
}
|
|
335
|
+
.ayon-shell-caption > .icon {
|
|
336
|
+
font-size: 22px;
|
|
337
|
+
color: var(--md-sys-color-outline);
|
|
338
|
+
}
|
|
339
|
+
.ayon-shell-caption-text {
|
|
340
|
+
display: flex;
|
|
341
|
+
flex: 1;
|
|
342
|
+
flex-direction: column;
|
|
343
|
+
min-width: 0;
|
|
344
|
+
}
|
|
345
|
+
.ayon-shell-caption-text > div {
|
|
346
|
+
display: flex;
|
|
347
|
+
align-items: baseline;
|
|
348
|
+
gap: 10px;
|
|
349
|
+
}
|
|
350
|
+
.ayon-shell-caption-text small {
|
|
351
|
+
overflow: hidden;
|
|
352
|
+
white-space: nowrap;
|
|
353
|
+
text-overflow: ellipsis;
|
|
354
|
+
color: var(--md-sys-color-outline);
|
|
355
|
+
}
|
|
356
|
+
.ayon-shell-caption-text code {
|
|
357
|
+
font-size: 11px;
|
|
358
|
+
}
|
|
359
|
+
.ayon-shell-caption-path {
|
|
360
|
+
color: var(--md-sys-color-outline);
|
|
361
|
+
}
|
|
362
|
+
.ayon-shell-caption-path span + span::before {
|
|
363
|
+
content: ' › ';
|
|
364
|
+
}
|
|
365
|
+
.ayon-shell-chip {
|
|
366
|
+
flex-shrink: 0;
|
|
367
|
+
padding: 2px 8px;
|
|
368
|
+
border-radius: 10px;
|
|
369
|
+
font-size: 12px;
|
|
370
|
+
background: var(--md-sys-color-surface-container-high);
|
|
371
|
+
color: var(--md-sys-color-outline);
|
|
372
|
+
}
|
|
373
|
+
.ayon-shell-chip.live {
|
|
374
|
+
background: var(--md-sys-color-primary-container);
|
|
375
|
+
color: var(--md-sys-color-on-primary-container);
|
|
376
|
+
}
|
|
377
|
+
.ayon-shell-bar {
|
|
378
|
+
display: flex;
|
|
379
|
+
align-items: center;
|
|
380
|
+
gap: 8px;
|
|
381
|
+
height: 48px;
|
|
382
|
+
padding: 0 8px;
|
|
383
|
+
flex-shrink: 0;
|
|
384
|
+
background: var(--md-sys-color-surface-container-low);
|
|
385
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
386
|
+
}
|
|
387
|
+
.ayon-shell-bar .spacer {
|
|
388
|
+
flex: 1;
|
|
389
|
+
}
|
|
390
|
+
.ayon-shell-tabs {
|
|
391
|
+
display: flex;
|
|
392
|
+
gap: 4px;
|
|
393
|
+
padding: 4px 8px;
|
|
394
|
+
flex-shrink: 0;
|
|
395
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
396
|
+
}
|
|
397
|
+
.ayon-shell-body {
|
|
398
|
+
display: flex;
|
|
399
|
+
flex: 1;
|
|
400
|
+
min-height: 0;
|
|
401
|
+
overflow: hidden;
|
|
402
|
+
}
|
|
403
|
+
.ayon-shell-table {
|
|
404
|
+
width: 100%;
|
|
405
|
+
border-collapse: collapse;
|
|
406
|
+
}
|
|
407
|
+
.ayon-shell-table th,
|
|
408
|
+
.ayon-shell-table td {
|
|
409
|
+
height: 34px;
|
|
410
|
+
padding: 0 8px;
|
|
411
|
+
text-align: left;
|
|
412
|
+
border-bottom: 1px solid var(--md-sys-color-outline-variant);
|
|
413
|
+
}
|
|
414
|
+
.ayon-shell-table tr.selected td {
|
|
415
|
+
background: var(--md-sys-color-primary-container);
|
|
416
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Plugin } from 'vite';
|
|
2
|
+
import type { AddonProject } from './project.js';
|
|
3
|
+
export declare const STANDALONE_ENV = "AYON_ADDON_STANDALONE";
|
|
4
|
+
interface StandaloneOptions {
|
|
5
|
+
root: string;
|
|
6
|
+
project: AddonProject;
|
|
7
|
+
remote: string;
|
|
8
|
+
manifest: string;
|
|
9
|
+
fixtures: string;
|
|
10
|
+
}
|
|
11
|
+
export declare const standalonePlugin: (options: StandaloneOptions) => Plugin;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
// `ayon-addon dev` (the default): the Vite dev server (hot reload) with the dev shell from
|
|
2
|
+
// @ynput/ayon-addon-dev/shell instead of the addon's own index.html. AYON's requests go through
|
|
3
|
+
// the dev server to the real server, signed in with the API key; preview fixtures are read and
|
|
4
|
+
// written under dev/fixtures.
|
|
5
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'fs';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { fileURLToPath } from 'url';
|
|
8
|
+
import { findConnection, findPackageDir } from './project.js';
|
|
9
|
+
export const STANDALONE_ENV = 'AYON_ADDON_STANDALONE';
|
|
10
|
+
const ENTRY = '/@ayon-addon/shell-entry';
|
|
11
|
+
const VIRTUAL_ENTRY = '\0ayon-addon-shell-entry';
|
|
12
|
+
const FIXTURES_URL = '/__ayon-addon/fixtures';
|
|
13
|
+
// what AYON serves itself: proxied to the server
|
|
14
|
+
const PROXIED = ['/api', '/graphql', '/ws', '/addons', '/static', '/AYON.svg'];
|
|
15
|
+
const SHARED = '@ynput/ayon-frontend-shared';
|
|
16
|
+
const DEDUPE = ['@tanstack/react-table'];
|
|
17
|
+
// served from their source: one registry for the addon, the shell and the shared package
|
|
18
|
+
const LINKED = ['@ynput/ayon-addon', '@ynput/ayon-addon-dev'];
|
|
19
|
+
// the built shell next to this file (dist/shell)
|
|
20
|
+
const SHELL_BUNDLE = fileURLToPath(new URL('./shell/index.js', import.meta.url));
|
|
21
|
+
const HTML = (title) => `<!doctype html>
|
|
22
|
+
<html lang="en">
|
|
23
|
+
<head>
|
|
24
|
+
<meta charset="UTF-8" />
|
|
25
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
26
|
+
<link rel="icon" href="/AYON.svg" />
|
|
27
|
+
<title>${title}</title>
|
|
28
|
+
</head>
|
|
29
|
+
<body>
|
|
30
|
+
<div id="root"></div>
|
|
31
|
+
<script type="module" src="${ENTRY}"></script>
|
|
32
|
+
</body>
|
|
33
|
+
</html>
|
|
34
|
+
`;
|
|
35
|
+
// the global styles AYON loads (src/index.tsx in ayon-frontend), from where the addon has them
|
|
36
|
+
const globalStyles = (root) => {
|
|
37
|
+
const sharedDir = findPackageDir(root, SHARED);
|
|
38
|
+
const candidates = [
|
|
39
|
+
['@ynput/ayon-react-components', 'dist/style.css'],
|
|
40
|
+
// the shared components' own styles (the context menu's)
|
|
41
|
+
[SHARED, 'dist/style.css'],
|
|
42
|
+
['react-toastify', 'dist/ReactToastify.css'],
|
|
43
|
+
['primereact', 'resources/primereact.min.css'],
|
|
44
|
+
['primeicons', 'primeicons.css'],
|
|
45
|
+
];
|
|
46
|
+
return candidates.flatMap(([pkg, file]) => {
|
|
47
|
+
const dir = findPackageDir(root, pkg) || (sharedDir && findPackageDir(sharedDir, pkg));
|
|
48
|
+
const css = dir && path.join(dir, file);
|
|
49
|
+
return css && existsSync(css) ? [css] : [];
|
|
50
|
+
});
|
|
51
|
+
};
|
|
52
|
+
const packageOf = (id) => id.split('/').slice(0, id.startsWith('@') ? 2 : 1).join('/');
|
|
53
|
+
// the packages the shell imports (its bundle keeps every package import as is)
|
|
54
|
+
const shellImports = (root) => {
|
|
55
|
+
if (!existsSync(SHELL_BUNDLE))
|
|
56
|
+
return [];
|
|
57
|
+
const bundle = readFileSync(SHELL_BUNDLE, 'utf8');
|
|
58
|
+
const ids = [...bundle.matchAll(/^(?:import|export)\b[^'"]*["']([^'"./][^'"]*)["']/gm)].map((m) => m[1]);
|
|
59
|
+
return [...new Set(ids)].filter((id) => !LINKED.includes(packageOf(id)) && !id.endsWith('.css') && findPackageDir(root, packageOf(id)));
|
|
60
|
+
};
|
|
61
|
+
const sendJson = (res, status, body) => {
|
|
62
|
+
res.statusCode = status;
|
|
63
|
+
res.setHeader('Content-Type', 'application/json');
|
|
64
|
+
res.end(JSON.stringify(body));
|
|
65
|
+
};
|
|
66
|
+
const readRequest = (req) => new Promise((resolve, reject) => {
|
|
67
|
+
let body = '';
|
|
68
|
+
req.on('data', (chunk) => (body += chunk));
|
|
69
|
+
req.on('end', () => resolve(body));
|
|
70
|
+
req.on('error', reject);
|
|
71
|
+
});
|
|
72
|
+
export const standalonePlugin = (options) => {
|
|
73
|
+
const { root, project, remote, fixtures } = options;
|
|
74
|
+
const fixturesDir = path.resolve(root, fixtures);
|
|
75
|
+
const connection = findConnection(root);
|
|
76
|
+
// relative to the root, as a path in the page and a glob for Vite's scan
|
|
77
|
+
const manifest = path.posix.normalize(options.manifest).replace(/^\.?\//, '');
|
|
78
|
+
return {
|
|
79
|
+
name: 'ayon-addon:standalone',
|
|
80
|
+
apply: (_config, { command }) => command === 'serve' && process.env[STANDALONE_ENV] === '1',
|
|
81
|
+
config() {
|
|
82
|
+
const headers = connection.apiKey ? { 'x-api-key': connection.apiKey } : {};
|
|
83
|
+
const proxy = Object.fromEntries(PROXIED.map((prefix) => [
|
|
84
|
+
prefix,
|
|
85
|
+
{ target: connection.serverUrl, changeOrigin: true, ws: prefix === '/ws', headers },
|
|
86
|
+
]));
|
|
87
|
+
const sharedDir = findPackageDir(root, SHARED);
|
|
88
|
+
const sharedEntries = sharedDir
|
|
89
|
+
? Object.entries(JSON.parse(readFileSync(path.join(sharedDir, 'package.json'), 'utf8')).exports)
|
|
90
|
+
// entries the package lists but doesn't ship
|
|
91
|
+
.filter(([, entry]) => entry.import && existsSync(path.join(sharedDir, entry.import)))
|
|
92
|
+
.map(([subpath]) => path.posix.join(SHARED, subpath))
|
|
93
|
+
: [];
|
|
94
|
+
return {
|
|
95
|
+
// the shell's page comes from the middleware below, not from index.html
|
|
96
|
+
appType: 'custom',
|
|
97
|
+
server: { proxy },
|
|
98
|
+
resolve: { dedupe: DEDUPE.filter((pkg) => findPackageDir(root, pkg)) },
|
|
99
|
+
optimizeDeps: {
|
|
100
|
+
exclude: LINKED,
|
|
101
|
+
// all known up front: a dependency found while the page loads re-optimizes and reloads
|
|
102
|
+
// it. The addon's are found by scanning from its manifest (pieces are imports of it).
|
|
103
|
+
include: [...new Set([...sharedEntries, ...shellImports(root)])],
|
|
104
|
+
entries: [manifest],
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
},
|
|
108
|
+
resolveId(id) {
|
|
109
|
+
if (id === ENTRY)
|
|
110
|
+
return VIRTUAL_ENTRY;
|
|
111
|
+
},
|
|
112
|
+
load(id) {
|
|
113
|
+
if (id !== VIRTUAL_ENTRY)
|
|
114
|
+
return;
|
|
115
|
+
const shellOptions = {
|
|
116
|
+
addon: { name: project.name, version: project.version, remote },
|
|
117
|
+
serverUrl: connection.serverUrl,
|
|
118
|
+
signedInWithKey: !!connection.apiKey,
|
|
119
|
+
};
|
|
120
|
+
return [
|
|
121
|
+
...globalStyles(root).map((css) => `import ${JSON.stringify(css)}`),
|
|
122
|
+
`import '@ynput/ayon-addon-dev/shell/style.css'`,
|
|
123
|
+
`import manifest from ${JSON.stringify(`/${manifest}`)}`,
|
|
124
|
+
`import { startShell } from '@ynput/ayon-addon-dev/shell'`,
|
|
125
|
+
`startShell({ ...${JSON.stringify(shellOptions)}, manifest })`,
|
|
126
|
+
].join('\n');
|
|
127
|
+
},
|
|
128
|
+
configureServer(server) {
|
|
129
|
+
server.middlewares.use(async (req, res, next) => {
|
|
130
|
+
const url = new URL(req.url || '/', 'http://localhost');
|
|
131
|
+
// preview fixtures: list, read, write (the developer panel's "Save")
|
|
132
|
+
if (url.pathname === FIXTURES_URL) {
|
|
133
|
+
const names = existsSync(fixturesDir)
|
|
134
|
+
? readdirSync(fixturesDir)
|
|
135
|
+
.filter((file) => file.endsWith('.json'))
|
|
136
|
+
.map((file) => file.replace(/\.json$/, ''))
|
|
137
|
+
: [];
|
|
138
|
+
return sendJson(res, 200, names);
|
|
139
|
+
}
|
|
140
|
+
if (url.pathname.startsWith(`${FIXTURES_URL}/`)) {
|
|
141
|
+
const name = decodeURIComponent(url.pathname.slice(FIXTURES_URL.length + 1));
|
|
142
|
+
if (!/^[\w-]+$/.test(name))
|
|
143
|
+
return sendJson(res, 400, { detail: 'fixture names: letters, digits, - and _' });
|
|
144
|
+
const file = path.join(fixturesDir, `${name}.json`);
|
|
145
|
+
if (req.method === 'POST') {
|
|
146
|
+
const body = await readRequest(req);
|
|
147
|
+
try {
|
|
148
|
+
JSON.parse(body);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return sendJson(res, 400, { detail: 'not JSON' });
|
|
152
|
+
}
|
|
153
|
+
mkdirSync(fixturesDir, { recursive: true });
|
|
154
|
+
writeFileSync(file, body.endsWith('\n') ? body : `${body}\n`);
|
|
155
|
+
return sendJson(res, 200, { saved: path.relative(root, file) });
|
|
156
|
+
}
|
|
157
|
+
if (!existsSync(file))
|
|
158
|
+
return sendJson(res, 404, { detail: `no ${path.relative(root, file)}` });
|
|
159
|
+
res.setHeader('Content-Type', 'application/json');
|
|
160
|
+
return res.end(readFileSync(file));
|
|
161
|
+
}
|
|
162
|
+
// the shell's page, for every navigation that isn't AYON's
|
|
163
|
+
const isPage = req.method === 'GET' &&
|
|
164
|
+
req.headers.accept?.includes('text/html') &&
|
|
165
|
+
!PROXIED.some((prefix) => url.pathname.startsWith(prefix)) &&
|
|
166
|
+
!url.pathname.startsWith('/@') &&
|
|
167
|
+
!url.pathname.startsWith('/__');
|
|
168
|
+
if (!isPage)
|
|
169
|
+
return next();
|
|
170
|
+
const html = await server.transformIndexHtml(req.url || '/', HTML(`${project.name} (AYON dev shell)`));
|
|
171
|
+
res.setHeader('Content-Type', 'text/html');
|
|
172
|
+
res.end(html);
|
|
173
|
+
});
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
};
|
package/dist/vite.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { PluginOption } from 'vite';
|
|
2
|
+
import type { RestOptions } from './rest.js';
|
|
3
|
+
export type { RestOptions } from './rest.js';
|
|
4
|
+
export interface AyonAddonOptions {
|
|
5
|
+
/** the federation remote name: the key in the server's `frontend_modules` */
|
|
6
|
+
name: string;
|
|
7
|
+
/** the manifest module, exposed as ./manifest (default ./src/manifest.ts) */
|
|
8
|
+
manifest?: string;
|
|
9
|
+
/** more modules to expose, e.g. keyword modules kept for AYON versions without slots */
|
|
10
|
+
exposes?: Record<string, string>;
|
|
11
|
+
/** more packages to share with AYON as singletons */
|
|
12
|
+
shared?: string[];
|
|
13
|
+
/** where `ayon-addon preview` reads fixtures (default dev/fixtures) */
|
|
14
|
+
fixtures?: string;
|
|
15
|
+
/** `ayon-addon generate-rest`: where the endpoints of the addon's server go */
|
|
16
|
+
rest?: RestOptions;
|
|
17
|
+
}
|
|
18
|
+
/** What `ayon-addon dev --ayon` serves next to the build, read by AYON's dev override. */
|
|
19
|
+
export interface AddonDevInfo {
|
|
20
|
+
name: string;
|
|
21
|
+
version: string;
|
|
22
|
+
remote: string;
|
|
23
|
+
modules: string[];
|
|
24
|
+
buildId: string;
|
|
25
|
+
}
|
|
26
|
+
export declare const HOST_PACKAGES: string[];
|
|
27
|
+
export declare const SHARED_PACKAGE = "@ynput/ayon-frontend-shared";
|
|
28
|
+
export declare const ADDON_API = "@ynput/ayon-addon";
|
|
29
|
+
/** The Vite plugins of an AYON addon frontend. Use with @vitejs/plugin-react. */
|
|
30
|
+
export declare const ayonAddon: (options: AyonAddonOptions) => Promise<PluginOption[]>;
|
|
31
|
+
export default ayonAddon;
|