@jogak/ui 0.1.0-alpha.7 → 0.1.0-alpha.8
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/CHANGELOG.md +53 -0
- package/README.md +47 -24
- package/dist/app/App.d.ts +10 -5
- package/dist/components/Preview/IframeMount.d.ts +17 -24
- package/dist/components/Preview/ShadowMount.d.ts +13 -14
- package/dist/components/Preview/index.d.ts +10 -5
- package/dist/host/index.d.ts +12 -4
- package/dist/host/index.js +1 -1
- package/dist/host/index.mjs +30 -30
- package/dist/index.js +1 -2
- package/dist/index.mjs +328 -324
- package/package.json +3 -3
- package/src/app/App.tsx +12 -5
- package/src/app/main.tsx +15 -7
- package/src/components/Preview/IframeMount.tsx +55 -42
- package/src/components/Preview/ShadowMount.tsx +15 -59
- package/src/components/Preview/index.tsx +35 -14
- package/src/vite-env.d.ts +7 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,59 @@ All notable changes to Jogak packages are documented here. The repository follow
|
|
|
5
5
|
|
|
6
6
|
Version numbers apply to all packages in the workspace (synchronized release).
|
|
7
7
|
|
|
8
|
+
## [0.1.0-alpha.8] — 2026-05-09
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **`JogakHostOptions.userViteUrl`** — 사용자 vite spawn URL을 jogak SPA host에 전달.
|
|
13
|
+
CLI의 `spawnUserVite` 결과가 `runHost`에 자동 전달되며 jogak() plugin의
|
|
14
|
+
`_jogakUserViteUrl`로 emit됨 → `IframeMount`가 iframe `src` base로 사용.
|
|
15
|
+
- **`IframeMount` postMessage 통신 재작성** — cross-origin 환경(사용자 vite ≠ jogak
|
|
16
|
+
SPA)에서 `entry.id`를 메시지로 전달, iframe 안에서 `defaultRegistry.requestEntry(id)`로
|
|
17
|
+
dynamic import.
|
|
18
|
+
|
|
19
|
+
### Changed (의도된 default 변경)
|
|
20
|
+
|
|
21
|
+
- **`previewIsolation` default `'shadow'` → `'iframe'`** — 사용자 vite scope에서
|
|
22
|
+
사용자 컴포넌트가 사용자 디자인 그대로 보이는 것이 default 동작.
|
|
23
|
+
- **`main.tsx` 단순화** — 사용자 globalCss는 사용자 vite scope에서만 처리되므로 jogak
|
|
24
|
+
SPA outer document inject는 `'none'` 모드(deprecated)에서만.
|
|
25
|
+
|
|
26
|
+
### Notes
|
|
27
|
+
|
|
28
|
+
- jogak-test-app(React + Vite + Tailwind v4 + shadcn) 검증: Badge가 사용자 디자인 그대로
|
|
29
|
+
표시 + chrome 침범 zero 입증.
|
|
30
|
+
|
|
31
|
+
## [0.1.0-alpha.7.1] — 2026-05-09
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
|
|
35
|
+
- **`previewIsolation` 격리 통로 정정** — 알파.7은 `main.tsx`가 isolation 모드와
|
|
36
|
+
무관하게 사용자 globalCss를 outer document에 무조건 inject해서 jogak chrome
|
|
37
|
+
utility를 사용자 reset/preflight가 무력화하던 결함이 있었음. 알파.7.1:
|
|
38
|
+
- `main.tsx`: `_jogakPreviewIsolation === 'none'`일 때만 사용자 globalCss를
|
|
39
|
+
dynamic import. 다른 모드에서는 outer document inject 차단 → chrome 침범 zero.
|
|
40
|
+
- `ShadowMount`: `adoptedStyleSheets` 흡수 + MutationObserver HMR sync 로직 제거.
|
|
41
|
+
ShadowMount는 양방향 격리만 책임 (사용자 globalCss는 shadow scope에 inject 안 됨).
|
|
42
|
+
- 사용자 컴포넌트 styling 통로(사용자 디자인 토큰/Tailwind utility 적용)는 알파.8
|
|
43
|
+
사이클에서 사용자 vite 통합으로 별도 도입 예정.
|
|
44
|
+
- **`Preview` cleanup race condition** — `NoneAdapterContent` / `ShadowAdapterContent`
|
|
45
|
+
/ `IframeMount`의 unmount race(`Attempted to synchronously unmount...`)를
|
|
46
|
+
`queueMicrotask`로 defer해 회피.
|
|
47
|
+
|
|
48
|
+
### Changed (의도된 default 변경)
|
|
49
|
+
|
|
50
|
+
- **`JogakHostOptionsBase.previewIsolation` default `'none'` → `'shadow'`** —
|
|
51
|
+
양방향 격리가 default. back-compat은 `previewIsolation: 'none'` 명시.
|
|
52
|
+
- **`JogakApp` / `Preview` `previewIsolation` prop default `'none'` → `'shadow'`**.
|
|
53
|
+
|
|
54
|
+
### README 업데이트
|
|
55
|
+
|
|
56
|
+
- "previewIsolation 사용 가이드" 섹션 재작성 — 3 모드 비교표 + `'shadow'` default
|
|
57
|
+
동작/한계 + `'iframe'` 주의 + `'none'` back-compat 사용법.
|
|
58
|
+
- "격리 보장" 섹션 — default 표시 갱신 (`'none'` → `'shadow'`).
|
|
59
|
+
- 로드맵 표 — alpha.7.1 entry 추가.
|
|
60
|
+
|
|
8
61
|
## [0.1.0-alpha.7] — 2026-05-09
|
|
9
62
|
|
|
10
63
|
### Added
|
package/README.md
CHANGED
|
@@ -22,8 +22,9 @@ pnpm add @jogak/ui @jogak/core @jogak/react react react-dom
|
|
|
22
22
|
import { defineJogakConfig } from '@jogak/core'
|
|
23
23
|
|
|
24
24
|
export default defineJogakConfig({
|
|
25
|
-
globalCss: true,
|
|
26
|
-
previewIsolation: '
|
|
25
|
+
globalCss: true, // 사용자 globalCss 자동 감지 + import
|
|
26
|
+
// previewIsolation: 'iframe', // 'iframe' (default, 알파.8) | 'shadow' (deprecated) | 'none' (deprecated)
|
|
27
|
+
// userVite: { port: 5174 }, // 사용자 vite spawn 옵션 (자동 탐지 시 미명시)
|
|
27
28
|
codeTheme: 'vsDark',
|
|
28
29
|
port: 5173, // dev server (CLI --port로 override)
|
|
29
30
|
})
|
|
@@ -84,8 +85,10 @@ See the [main README](https://github.com/devclib/jogak#readme) for the full host
|
|
|
84
85
|
| alpha.4 | ✅ 완료 | jogak UI 빌드에 Tailwind v4 + `jogak:` prefix 도입 |
|
|
85
86
|
| alpha.5 | ✅ 완료 | jogak UI 컴포넌트를 Tailwind class로 마이그레이션 (4 PR) |
|
|
86
87
|
| alpha.6 | ✅ 완료 | `JogakPluginOptions.globalCss` 옵션 + chrome 보호 rule (단, 통로 부재로 사용자 환경에서 무효 — 알파.7에서 정정) |
|
|
87
|
-
|
|
|
88
|
-
| alpha.
|
|
88
|
+
| alpha.7 | ✅ 완료 | `jogak.config.ts` 통로 + `previewIsolation` 옵션 + `JogakHostOptionsBase` 확장 (단, isolation 통로 결함으로 격리 무효 — 알파.7.1에서 정정) |
|
|
89
|
+
| alpha.7.1 | ✅ 완료 | isolation 통로 hotfix + default `previewIsolation: 'shadow'` (단, 사용자 utility 미컴파일 한계 — 알파.8에서 사용자 vite 통합으로 해결) |
|
|
90
|
+
| **alpha.8** | ✅ **본 릴리즈** | **사용자 vite 자동 spawn + 사용자 Tailwind utility 정상 컴파일 + default `previewIsolation: 'iframe'`** |
|
|
91
|
+
| alpha.9+ | 예정 | shadow 모드 사용자 utility inject 부활, iframe sandbox 옵션, multi-baseline VR |
|
|
89
92
|
|
|
90
93
|
### 사용자 globalCss 적용
|
|
91
94
|
|
|
@@ -123,43 +126,63 @@ defineJogakConfig({ globalCss: './src/index.css' })
|
|
|
123
126
|
defineJogakConfig({ globalCss: ['./src/tokens.css', './src/reset.css'] })
|
|
124
127
|
```
|
|
125
128
|
|
|
126
|
-
#### 격리 보장 (default `previewIsolation: '
|
|
129
|
+
#### 격리 보장 (default `previewIsolation: 'iframe'`, 알파.8)
|
|
127
130
|
|
|
128
131
|
- **Tailwind utility class**: jogak UI는 `prefix=jogak`로 빌드되어 사용자 utility와 충돌 zero (예: 사용자 `bg-primary` ≠ jogak `jogak:bg-...`).
|
|
129
132
|
- **CSS variable**: jogak은 `--jogak-*` prefix로 namespace 격리 → 사용자 `:root { --primary }` 같은 디자인 토큰은 영향 없음.
|
|
130
133
|
- **Form element 보호**: `[data-jogak-shell]` 안의 button/input/select/textarea는 사용자 reset의 `border` / `background` / `color` 침범을 받지 않도록 `:where()` 보호 rule 적용. specificity 0이라 사용자가 명시적으로 `[data-jogak-shell] button { ... }`를 작성하면 정상 override됩니다.
|
|
131
134
|
|
|
132
|
-
### previewIsolation 사용 가이드 (알파.
|
|
135
|
+
### previewIsolation 사용 가이드 (알파.8)
|
|
133
136
|
|
|
134
|
-
|
|
137
|
+
jogak chrome ↔ 사용자 영역의 **양방향 격리** + 사용자 컴포넌트는 사용자 디자인 시스템 그대로 표시. 알파.8부터 default가 `'iframe'`로 변경되어 사용자 vite 인스턴스가 자동 spawn되고, iframe document가 사용자 vite의 정상 client로 작동합니다.
|
|
135
138
|
|
|
136
139
|
#### 모드 비교
|
|
137
140
|
|
|
138
|
-
| 모드 | mount |
|
|
139
|
-
|
|
140
|
-
| `'
|
|
141
|
-
| `'shadow'` | ShadowRoot |
|
|
142
|
-
| `'
|
|
141
|
+
| 모드 | mount | 사용자 utility 컴파일 | chrome 침범 | Radix portal | cold start |
|
|
142
|
+
|------|-------|---------------------|-----------|--------------|-----------|
|
|
143
|
+
| `'iframe'` (default) | iframe document (사용자 vite scope) | **정상** ✅ | zero | iframe document (정상) | ★★ |
|
|
144
|
+
| `'shadow'` (deprecated) | ShadowRoot | **미컴파일** | zero | document.body (shadow 외부) | ★★★ |
|
|
145
|
+
| `'none'` (deprecated) | 같은 document | 미컴파일 | **있음** | document.body | ★★★ |
|
|
143
146
|
|
|
144
|
-
#### `'
|
|
147
|
+
#### `'iframe'` 모드 (default, 알파.8)
|
|
145
148
|
|
|
146
|
-
|
|
149
|
+
jogak CLI가 사용자 cwd의 `vite.config.{ts,mts,js,mjs,cjs}`를 자동 탐지해 별도 vite dev server를 spawn합니다. iframe `src`가 그 사용자 vite를 가리키므로 사용자 plugins(@tailwindcss/vite, custom alias 등)이 정상 작동 → **사용자 컴포넌트가 사용자 디자인 그대로 표시**.
|
|
147
150
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
</Dialog.Portal>
|
|
152
|
-
```
|
|
151
|
+
```ts
|
|
152
|
+
// jogak.config.ts (사용자 프로젝트 root)
|
|
153
|
+
import { defineJogakConfig } from '@jogak/core'
|
|
153
154
|
|
|
154
|
-
|
|
155
|
+
export default defineJogakConfig({
|
|
156
|
+
globalCss: true,
|
|
157
|
+
// previewIsolation: 'iframe', // default
|
|
158
|
+
// userVite: { port: 5174 }, // 명시 시
|
|
159
|
+
})
|
|
160
|
+
```
|
|
155
161
|
|
|
156
|
-
|
|
162
|
+
**자동 동작:**
|
|
163
|
+
- 사용자 `vite.config.ts` 자동 탐지 → `loadConfigFromFile` + `mergeConfig`로 jogak plugins 자동 inject (사용자 액션 zero)
|
|
164
|
+
- 사용자 vite default port 5174 (jogak SPA가 default 5173 차지). 사용자 명시 가능
|
|
165
|
+
- cross-origin postMessage로 entry/args 전달 (`entry.id`만, iframe 안에서 `defaultRegistry.requestEntry(id)` dynamic import)
|
|
166
|
+
- HMR: 사용자 vite의 React Fast Refresh + Tailwind utility 재생성 정상 작동
|
|
157
167
|
|
|
158
|
-
|
|
168
|
+
**Fallback:**
|
|
169
|
+
- `vite.config.ts` 미발견 또는 평가 실패 → spawn skip + warning. jogak SPA만 시작 (`'none'` 모드 동등 동작).
|
|
159
170
|
|
|
160
|
-
|
|
171
|
+
**주의:**
|
|
172
|
+
- jogak ↔ 사용자 vite 두 인스턴스 (vs 알파.7.1까지의 single vite). dev cold start +100ms, RSS +50~80MB.
|
|
173
|
+
- iframe sandbox 미적용 (사용자 컴포넌트의 fetch/clipboard/storage 자유 사용).
|
|
161
174
|
|
|
162
|
-
|
|
175
|
+
#### `'shadow'` 모드 (deprecated)
|
|
176
|
+
|
|
177
|
+
ShadowRoot 격리는 정상 작동하나 사용자 utility가 shadow scope에 inject되지 않아 사용자 컴포넌트가 raw 형태로 표시됩니다. 알파.9에서 사용자 vite의 `transformRequest` API로 shadow inject 부활 검토.
|
|
178
|
+
|
|
179
|
+
#### `'none'` 모드 (deprecated)
|
|
180
|
+
|
|
181
|
+
사용자 globalCss를 outer document에 inject. 사용자 reset이 jogak chrome 침범. back-compat 시나리오만 사용:
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
defineJogakConfig({ globalCss: true, previewIsolation: 'none' })
|
|
185
|
+
```
|
|
163
186
|
|
|
164
187
|
### scope 가이드 — 알려진 영향 영역 (`'none'` 모드 기준)
|
|
165
188
|
|
package/dist/app/App.d.ts
CHANGED
|
@@ -15,14 +15,19 @@ export interface JogakAppProps {
|
|
|
15
15
|
readonly metas?: readonly RegistryEntryMeta[];
|
|
16
16
|
readonly codeTheme?: string;
|
|
17
17
|
/**
|
|
18
|
-
* 알파.
|
|
18
|
+
* 알파.8: Preview 영역 격리 모드. default `'iframe'`.
|
|
19
19
|
*
|
|
20
|
-
* - `'
|
|
21
|
-
* - `'shadow'` — ShadowRoot 안에 마운트. 사용자
|
|
22
|
-
* - `'
|
|
20
|
+
* - `'iframe'` (default) — 사용자 vite 정상 client(iframe)에 마운트. 사용자 utility 정상 컴파일.
|
|
21
|
+
* - `'shadow'` (deprecated) — ShadowRoot 안에 마운트. 사용자 utility 미적용.
|
|
22
|
+
* - `'none'` (deprecated) — chrome 같은 document에 렌더. 알파.6까지의 동작.
|
|
23
23
|
*
|
|
24
24
|
* 자세한 트레이드오프는 `@jogak/ui` README의 "previewIsolation 사용 가이드" 참조.
|
|
25
25
|
*/
|
|
26
26
|
readonly previewIsolation?: 'none' | 'shadow' | 'iframe';
|
|
27
|
+
/**
|
|
28
|
+
* 알파.8: 사용자 vite spawn URL. iframe `src` base로 사용.
|
|
29
|
+
* 빈 문자열 시 fallback (jogak SPA Vite scope의 preview-frame.tsx).
|
|
30
|
+
*/
|
|
31
|
+
readonly userViteUrl?: string;
|
|
27
32
|
}
|
|
28
|
-
export declare function JogakApp({ entries, metas, codeTheme, previewIsolation, }?: JogakAppProps): ReactElement;
|
|
33
|
+
export declare function JogakApp({ entries, metas, codeTheme, previewIsolation, userViteUrl, }?: JogakAppProps): ReactElement;
|
|
@@ -3,35 +3,28 @@ import { RegistryEntry } from '@jogak/core';
|
|
|
3
3
|
export interface IframeMountProps {
|
|
4
4
|
readonly entry: RegistryEntry;
|
|
5
5
|
readonly args: Readonly<Record<string, unknown>>;
|
|
6
|
+
/**
|
|
7
|
+
* 알파.8: 사용자 vite spawn URL (예: `http://localhost:5174`).
|
|
8
|
+
* 빈 문자열 시 fallback (jogak SPA Vite scope의 `/preview-frame.html`).
|
|
9
|
+
*/
|
|
10
|
+
readonly userViteUrl: string;
|
|
6
11
|
readonly className?: string;
|
|
7
12
|
readonly 'data-testid'?: string;
|
|
8
13
|
}
|
|
9
|
-
interface SetPropsArgs {
|
|
10
|
-
readonly entry: RegistryEntry;
|
|
11
|
-
readonly args: Readonly<Record<string, unknown>>;
|
|
12
|
-
}
|
|
13
|
-
declare global {
|
|
14
|
-
interface Window {
|
|
15
|
-
__jogak_setProps__?: (args: SetPropsArgs) => void;
|
|
16
|
-
__jogak_unmount__?: () => void;
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
14
|
/**
|
|
20
|
-
* 알파.
|
|
15
|
+
* 알파.8: previewIsolation='iframe' 모드의 mount 컴포넌트.
|
|
21
16
|
*
|
|
22
|
-
*
|
|
23
|
-
* -
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* - entry/args 변경 시 setProps 재호출 (load 완료 이후).
|
|
17
|
+
* 통신:
|
|
18
|
+
* - 사용자 vite spawn URL이 주어지면(`userViteUrl !== ''`) iframe src를
|
|
19
|
+
* `${userViteUrl}/__jogak_preview__/index.html` (cross-origin)로 설정.
|
|
20
|
+
* - 동일 origin fallback 시 `/preview-frame.html` (jogak SPA Vite scope).
|
|
27
21
|
*
|
|
28
|
-
*
|
|
29
|
-
* - iframe
|
|
30
|
-
*
|
|
31
|
-
* - previewIsolation 모드 자체 변경은 가상 모듈 invalidate → full reload.
|
|
22
|
+
* 양쪽 모두 postMessage로 통신:
|
|
23
|
+
* - 부모 → iframe: `{ type: 'jogak:setProps', entryId, args }` | `{ type: 'jogak:unmount' }`
|
|
24
|
+
* - iframe → 부모: `{ type: 'jogak:ready' }` | `{ type: 'jogak:rendered', entryId }`
|
|
32
25
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
26
|
+
* `entry`는 객체가 아닌 **id만 전달** — iframe 안에서 `defaultRegistry.requestEntry(id)`로
|
|
27
|
+
* dynamic import. 사용자 vite scope의 entry 가상 모듈이 사용자 컴포넌트를 fetch하므로
|
|
28
|
+
* 사용자 plugins(@tailwindcss/vite, custom alias 등)이 정상 작동.
|
|
35
29
|
*/
|
|
36
|
-
export declare function IframeMount({ entry, args, className, 'data-testid': dataTestId, }: IframeMountProps): ReactElement;
|
|
37
|
-
export {};
|
|
30
|
+
export declare function IframeMount({ entry, args, userViteUrl, className, 'data-testid': dataTestId, }: IframeMountProps): ReactElement;
|
|
@@ -3,24 +3,23 @@ export interface ShadowMountProps {
|
|
|
3
3
|
readonly children: ReactNode;
|
|
4
4
|
readonly className?: string;
|
|
5
5
|
readonly style?: CSSProperties;
|
|
6
|
-
/** 외부 테스트 hook (호스트 div에 부여). */
|
|
7
6
|
readonly 'data-testid'?: string;
|
|
8
7
|
}
|
|
9
8
|
/**
|
|
10
|
-
* 알파.7: previewIsolation='shadow' 모드의 mount 컴포넌트.
|
|
9
|
+
* 알파.7.1: previewIsolation='shadow' 모드의 mount 컴포넌트.
|
|
11
10
|
*
|
|
12
|
-
*
|
|
13
|
-
* -
|
|
14
|
-
* -
|
|
15
|
-
* -
|
|
16
|
-
* `adoptedStyleSheets`로 ShadowRoot에 share (jogak.css + virtual:jogak/global-css
|
|
17
|
-
* 둘 다 자동 포함).
|
|
18
|
-
* - Vite dev에서 `<style>` HMR 시 MutationObserver로 ShadowRoot도 갱신.
|
|
11
|
+
* 책임: 양방향 격리만 제공 (Preview ↔ outer document 양방향 cascade 차단).
|
|
12
|
+
* - 사용자 globalCss는 main.tsx 가드로 outer document에 inject되지 않음.
|
|
13
|
+
* - shadow root 안에는 jogak chrome css도 사용자 css도 없음 (둘 다 외부에서 격리).
|
|
14
|
+
* - 사용자 컴포넌트의 utility class 컴파일은 결함 B (알파.8 사이클).
|
|
19
15
|
*
|
|
20
|
-
*
|
|
21
|
-
* -
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
16
|
+
* 알파.7 결함 정정:
|
|
17
|
+
* - `syncStyleSheets`/`MutationObserver`/`adoptedStyleSheets` 흡수 로직 제거.
|
|
18
|
+
* 알파.7은 outer document에 사용자 css가 있는 한 chrome을 침범했고, shadow
|
|
19
|
+
* 안의 흡수 로직은 의미가 없었음. 알파.7.1: outer에 사용자 css 자체가 없음.
|
|
20
|
+
*
|
|
21
|
+
* Radix portal 한계 (사용자 인지 필요, README 명시):
|
|
22
|
+
* - default Portal target = document.body (shadow 외부). 사용자가 명시적으로
|
|
23
|
+
* `<Portal container={shadowRootEl}>`을 전달해야 portal 내용도 격리됨.
|
|
25
24
|
*/
|
|
26
25
|
export declare function ShadowMount({ children, className, style, 'data-testid': dataTestId, }: ShadowMountProps): ReactElement;
|
|
@@ -13,13 +13,18 @@ export interface PreviewProps {
|
|
|
13
13
|
*/
|
|
14
14
|
readonly onResolveJogak?: (entryId: string, jogakName: string) => void;
|
|
15
15
|
/**
|
|
16
|
-
* 알파.
|
|
16
|
+
* 알파.8: Preview 영역 격리 모드. default `'iframe'`.
|
|
17
17
|
*
|
|
18
|
-
* - `'
|
|
19
|
-
* - `'shadow'` — ShadowRoot에 마운트. 사용자
|
|
20
|
-
* - `'
|
|
18
|
+
* - `'iframe'` (default) — 사용자 vite scope에 마운트. 사용자 utility 정상 컴파일.
|
|
19
|
+
* - `'shadow'` (deprecated) — ShadowRoot에 마운트. 사용자 utility 미적용.
|
|
20
|
+
* - `'none'` (deprecated) — chrome과 같은 document에 렌더.
|
|
21
21
|
*/
|
|
22
22
|
readonly previewIsolation?: 'none' | 'shadow' | 'iframe';
|
|
23
|
+
/**
|
|
24
|
+
* 알파.8: 사용자 vite spawn URL. iframe `src` base.
|
|
25
|
+
* 빈 문자열 시 fallback (jogak SPA Vite scope의 `/preview-frame.html`).
|
|
26
|
+
*/
|
|
27
|
+
readonly userViteUrl?: string;
|
|
23
28
|
}
|
|
24
29
|
/**
|
|
25
30
|
* Preview — `useEntry(entryId)`의 status에 따라 분기 (계약 §5.4).
|
|
@@ -31,5 +36,5 @@ export interface PreviewProps {
|
|
|
31
36
|
*
|
|
32
37
|
* Layout shift 방지를 위해 캔버스 영역 minHeight 유지.
|
|
33
38
|
*/
|
|
34
|
-
export declare function Preview({ entryId, jogakName, overrideArgs, onArgChange, onReset, codeTheme, onResolveJogak, previewIsolation, }: PreviewProps): ReactElement;
|
|
39
|
+
export declare function Preview({ entryId, jogakName, overrideArgs, onArgChange, onReset, codeTheme, onResolveJogak, previewIsolation, userViteUrl, }: PreviewProps): ReactElement;
|
|
35
40
|
export type { UseEntryState };
|
package/dist/host/index.d.ts
CHANGED
|
@@ -49,15 +49,23 @@ export interface JogakHostOptionsBase {
|
|
|
49
49
|
*/
|
|
50
50
|
readonly globalCss?: boolean | string | readonly string[];
|
|
51
51
|
/**
|
|
52
|
-
* 알파.
|
|
52
|
+
* 알파.8: Preview 영역 격리 모드.
|
|
53
53
|
*
|
|
54
|
-
* `'
|
|
55
|
-
* jogak() Vite plugin의 `previewIsolation` 옵션으로 그대로
|
|
56
|
-
* Preview 컴포넌트가 해당 모드별 분기로 렌더한다.
|
|
54
|
+
* `'iframe'` (default, 알파.8), `'shadow'` (deprecated), `'none'` (deprecated) 중 하나.
|
|
55
|
+
* jogak() Vite plugin의 `previewIsolation` 옵션으로 그대로 전달된다.
|
|
57
56
|
*
|
|
58
57
|
* 자세한 모드 설명은 `JogakPluginOptions.previewIsolation` JSDoc 참조.
|
|
59
58
|
*/
|
|
60
59
|
readonly previewIsolation?: 'none' | 'shadow' | 'iframe';
|
|
60
|
+
/**
|
|
61
|
+
* 알파.8: 사용자 vite 인스턴스의 dev server URL (예: `http://localhost:5174`).
|
|
62
|
+
* jogak SPA가 iframe `src` base로 사용한다 (`<iframe src="${userViteUrl}/__jogak_preview__/index.html">`).
|
|
63
|
+
*
|
|
64
|
+
* 미지정/빈 문자열 시 fallback (jogak SPA Vite scope의 preview-frame.tsx).
|
|
65
|
+
*
|
|
66
|
+
* jogak CLI가 spawnUserVite 결과를 자동 전달 — 사용자가 직접 설정하지 않는다.
|
|
67
|
+
*/
|
|
68
|
+
readonly userViteUrl?: string;
|
|
61
69
|
}
|
|
62
70
|
export interface JogakDevOptions extends JogakHostOptionsBase {
|
|
63
71
|
readonly mode: 'dev';
|
package/dist/host/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
"use strict";var S=Object.create;var m=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var B=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty;var N=(t,e,o,
|
|
1
|
+
"use strict";var S=Object.create;var m=Object.defineProperty;var k=Object.getOwnPropertyDescriptor;var A=Object.getOwnPropertyNames;var B=Object.getPrototypeOf,H=Object.prototype.hasOwnProperty;var N=(t,e,o,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let r of A(e))!H.call(t,r)&&r!==o&&m(t,r,{get:()=>e[r],enumerable:!(n=k(e,r))||n.enumerable});return t};var a=(t,e,o)=>(o=t!=null?S(B(t)):{},N(e||!t||!t.__esModule?m(o,"default",{value:t,enumerable:!0}):o,t));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const x=require("node:url"),s=require("node:path");var f=typeof document<"u"?document.currentScript:null;const F=x.fileURLToPath(typeof document>"u"?require("url").pathToFileURL(__filename).href:f&&f.tagName.toUpperCase()==="SCRIPT"&&f.src||new URL("host/index.js",document.baseURI).href),i=s.resolve(s.dirname(F),"..",".."),E=s.resolve(i,"index.html"),V=s.resolve(i,"src/app/main.tsx");async function Y(t){const e=await import("vite"),o=await import("@vitejs/plugin-react"),n=await import("@jogak/core/vite"),r=await import("@tailwindcss/vite"),{createServer:v,build:h}=e,b=o.default,w=r.default,{jogak:_}=n,C=t.codeTheme??"vsDark",u={patterns:t.patterns,codeTheme:C,cwd:t.userRoot};t.tsConfigFilePath!==void 0&&(u.tsConfigFilePath=t.tsConfigFilePath),t.globalCss!==void 0&&(u.globalCss=t.globalCss),t.previewIsolation!==void 0&&(u.previewIsolation=t.previewIsolation),t.userViteUrl!==void 0&&(u.userViteUrl=t.userViteUrl);const U=t.extraPlugins??[],d={root:i,configFile:!1,plugins:[b(),w(),_(u),...U],optimizeDeps:{include:["react","react-dom/client","@jogak/core","@jogak/react"]}};if(t.mode==="dev"){const D={port:t.port??5173,host:t.host??"localhost",open:t.open??!1,fs:{allow:[i,t.userRoot]}},L={...d,server:D},l=await v(L);await l.listen();const p=l.config.server.port??t.port??5173,c=t.host??"localhost",M=`http://${typeof c=="boolean"?c?"0.0.0.0":"localhost":c}:${p.toString()}/`;let g=!1;return{url:M,port:p,close:async()=>{g||(g=!0,await l.close())},printUrls:()=>{l.printUrls()}}}const R={...d,base:t.base??"./",build:{outDir:t.outDir,emptyOutDir:!0,sourcemap:t.sourcemap??!1,minify:t.minify??"esbuild",rollupOptions:{input:{main:s.resolve(i,"index.html"),preview:s.resolve(i,"preview-frame.html")}}}},I=Date.now(),P=await h(R),T=Date.now()-I,{assetCount:O,totalBytes:j}=q(P);return{outDir:t.outDir,elapsedMs:T,assetCount:O,totalBytes:j}}function q(t){const e=z(t);if(e===void 0)return{assetCount:0,totalBytes:0};let o=0,n=0;for(const r of e)o+=1,n+=$(r);return{assetCount:o,totalBytes:n}}function y(t){return typeof t=="object"&&t!==null&&Array.isArray(t.output)}function z(t){if(Array.isArray(t)){const e=[];for(const o of t)y(o)&&e.push(...o.output);return e}if(y(t))return t.output}function $(t){if(typeof t!="object"||t===null)return 0;const e=t;if(e.type==="chunk"&&typeof e.code=="string")return Buffer.byteLength(e.code,"utf8");if(e.type==="asset"){const o=e.source;if(typeof o=="string")return Buffer.byteLength(o,"utf8");if(o instanceof Uint8Array)return o.byteLength}return 0}exports.UI_HTML_ENTRY=E;exports.UI_MAIN_ENTRY=V;exports.runHost=Y;
|
package/dist/host/index.mjs
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
|
-
import { fileURLToPath as
|
|
2
|
-
import { resolve as
|
|
3
|
-
const
|
|
4
|
-
async function
|
|
5
|
-
const e = await import("vite"), o = await import("@vitejs/plugin-react"),
|
|
1
|
+
import { fileURLToPath as k } from "node:url";
|
|
2
|
+
import { resolve as i, dirname as B } from "node:path";
|
|
3
|
+
const T = k(import.meta.url), r = i(B(T), "..", ".."), z = i(r, "index.html"), N = i(r, "src/app/main.tsx");
|
|
4
|
+
async function S(t) {
|
|
5
|
+
const e = await import("vite"), o = await import("@vitejs/plugin-react"), s = await import("@jogak/core/vite"), l = await import("@tailwindcss/vite"), { createServer: m, build: g } = e, y = o.default, h = l.default, { jogak: v } = s, w = t.codeTheme ?? "vsDark", n = {
|
|
6
6
|
patterns: t.patterns,
|
|
7
7
|
codeTheme: w,
|
|
8
8
|
cwd: t.userRoot
|
|
9
9
|
};
|
|
10
|
-
t.tsConfigFilePath !== void 0 && (
|
|
11
|
-
const b = t.extraPlugins ?? [],
|
|
12
|
-
root:
|
|
10
|
+
t.tsConfigFilePath !== void 0 && (n.tsConfigFilePath = t.tsConfigFilePath), t.globalCss !== void 0 && (n.globalCss = t.globalCss), t.previewIsolation !== void 0 && (n.previewIsolation = t.previewIsolation), t.userViteUrl !== void 0 && (n.userViteUrl = t.userViteUrl);
|
|
11
|
+
const b = t.extraPlugins ?? [], c = {
|
|
12
|
+
root: r,
|
|
13
13
|
configFile: !1,
|
|
14
14
|
// ui/vite.config.ts 무시
|
|
15
|
-
plugins: [y(), h(), v(
|
|
15
|
+
plugins: [y(), h(), v(n), ...b],
|
|
16
16
|
optimizeDeps: {
|
|
17
17
|
include: ["react", "react-dom/client", "@jogak/core", "@jogak/react"]
|
|
18
18
|
}
|
|
19
19
|
};
|
|
20
20
|
if (t.mode === "dev") {
|
|
21
|
-
const
|
|
21
|
+
const I = {
|
|
22
22
|
port: t.port ?? 5173,
|
|
23
23
|
host: t.host ?? "localhost",
|
|
24
24
|
open: t.open ?? !1,
|
|
25
|
-
fs: { allow: [
|
|
26
|
-
},
|
|
27
|
-
...
|
|
28
|
-
server:
|
|
29
|
-
}, a = await m(
|
|
25
|
+
fs: { allow: [r, t.userRoot] }
|
|
26
|
+
}, _ = {
|
|
27
|
+
...c,
|
|
28
|
+
server: I
|
|
29
|
+
}, a = await m(_);
|
|
30
30
|
await a.listen();
|
|
31
|
-
const f = a.config.server.port ?? t.port ?? 5173,
|
|
31
|
+
const f = a.config.server.port ?? t.port ?? 5173, u = t.host ?? "localhost", j = `http://${typeof u == "boolean" ? u ? "0.0.0.0" : "localhost" : u}:${f.toString()}/`;
|
|
32
32
|
let d = !1;
|
|
33
33
|
return {
|
|
34
|
-
url:
|
|
34
|
+
url: j,
|
|
35
35
|
port: f,
|
|
36
36
|
close: async () => {
|
|
37
37
|
d || (d = !0, await a.close());
|
|
@@ -42,7 +42,7 @@ async function $(t) {
|
|
|
42
42
|
};
|
|
43
43
|
}
|
|
44
44
|
const C = {
|
|
45
|
-
...
|
|
45
|
+
...c,
|
|
46
46
|
base: t.base ?? "./",
|
|
47
47
|
build: {
|
|
48
48
|
outDir: t.outDir,
|
|
@@ -51,27 +51,27 @@ async function $(t) {
|
|
|
51
51
|
minify: t.minify ?? "esbuild",
|
|
52
52
|
rollupOptions: {
|
|
53
53
|
input: {
|
|
54
|
-
main: r
|
|
55
|
-
preview: r
|
|
54
|
+
main: i(r, "index.html"),
|
|
55
|
+
preview: i(r, "preview-frame.html")
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
|
-
}, P = Date.now(), O = await g(C),
|
|
59
|
+
}, P = Date.now(), O = await g(C), U = Date.now() - P, { assetCount: D, totalBytes: R } = x(O);
|
|
60
60
|
return {
|
|
61
61
|
outDir: t.outDir,
|
|
62
|
-
elapsedMs:
|
|
63
|
-
assetCount:
|
|
64
|
-
totalBytes:
|
|
62
|
+
elapsedMs: U,
|
|
63
|
+
assetCount: D,
|
|
64
|
+
totalBytes: R
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
67
|
function x(t) {
|
|
68
68
|
const e = A(t);
|
|
69
69
|
if (e === void 0)
|
|
70
70
|
return { assetCount: 0, totalBytes: 0 };
|
|
71
|
-
let o = 0,
|
|
71
|
+
let o = 0, s = 0;
|
|
72
72
|
for (const l of e)
|
|
73
|
-
o += 1,
|
|
74
|
-
return { assetCount: o, totalBytes:
|
|
73
|
+
o += 1, s += L(l);
|
|
74
|
+
return { assetCount: o, totalBytes: s };
|
|
75
75
|
}
|
|
76
76
|
function p(t) {
|
|
77
77
|
return typeof t == "object" && t !== null && Array.isArray(t.output);
|
|
@@ -99,7 +99,7 @@ function L(t) {
|
|
|
99
99
|
return 0;
|
|
100
100
|
}
|
|
101
101
|
export {
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
z as UI_HTML_ENTRY,
|
|
103
|
+
N as UI_MAIN_ENTRY,
|
|
104
|
+
S as runHost
|
|
105
105
|
};
|