@jogak/core 0.1.0-alpha.7.1 → 0.1.0-alpha.9

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.
@@ -0,0 +1,29 @@
1
+ /**
2
+ * 알파.9: jogak chrome ↔ preview iframe 간 postMessage 통신 프로토콜.
3
+ *
4
+ * 모든 어댑터(vite/next/webpack/standalone)가 동일 프로토콜 사용. iframe 안 entry source는
5
+ * cross-origin 환경에서도 작동하도록 `targetOrigin: '*'`. preview는 사용자 본인 컴포넌트만
6
+ * mount하는 신뢰 환경이므로 origin 검증은 README 명시 후 생략.
7
+ */
8
+ /**
9
+ * 부모(jogak SPA) → iframe (preview entry).
10
+ */
11
+ export type JogakMessageToFrame = {
12
+ readonly type: 'jogak:setProps';
13
+ readonly entryId: string;
14
+ readonly args: Readonly<Record<string, unknown>>;
15
+ } | {
16
+ readonly type: 'jogak:unmount';
17
+ };
18
+ /**
19
+ * iframe (preview entry) → 부모(jogak SPA).
20
+ */
21
+ export type JogakMessageFromFrame = {
22
+ readonly type: 'jogak:ready';
23
+ } | {
24
+ readonly type: 'jogak:rendered';
25
+ readonly entryId: string;
26
+ } | {
27
+ readonly type: 'jogak:error';
28
+ readonly message: string;
29
+ };
@@ -0,0 +1,30 @@
1
+ /**
2
+ * 알파.9: 모든 빌더 어댑터가 공통으로 사용하는 preview entry source.
3
+ *
4
+ * 빌더에 무관하게 다음 책임:
5
+ * 1. `defaultRegistry.requestEntry(entryId)`로 사용자 컴포넌트 dynamic import
6
+ * 2. `reactAdapter.render`로 mount
7
+ * 3. postMessage 프로토콜 (`@jogak/core/preview-entry/protocol`)로 부모와 통신
8
+ *
9
+ * 어댑터별 차이는 `extraImports` 배열뿐:
10
+ * - vite-adapter: `['virtual:jogak/preview-global-css']`
11
+ * - next-adapter: 사용자 globalCss 절대 경로 직접 inject
12
+ * - webpack-adapter: 사용자 globalCss 절대 경로
13
+ * - standalone-adapter: 사용자 사전 빌드 css 절대 경로
14
+ */
15
+ export interface RenderPreviewEntryOptions {
16
+ /**
17
+ * 어댑터별 추가 import 라인. 라인 순서대로 emit되어 cascade order 결정.
18
+ *
19
+ * @example vite-adapter
20
+ * ['virtual:jogak/preview-global-css']
21
+ *
22
+ * @example next-adapter
23
+ * ['/abs/path/src/index.css']
24
+ */
25
+ readonly extraImports?: readonly string[];
26
+ }
27
+ /**
28
+ * 어댑터가 호출 — 자기 scope에 맞는 추가 import 라인 주입.
29
+ */
30
+ export declare function renderPreviewEntrySource(opts?: RenderPreviewEntryOptions): string;
@@ -0,0 +1 @@
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o=require("node:fs"),l=require("node:path"),u=require("./detect-global-css-CDcuUAYE.cjs"),d=["ts","js","mjs","cjs"],b=["ts","js","mjs","cjs"],j=["ts","mts","js","mjs","cjs"];function h(s){const e=C(s),n={...(e==null?void 0:e.dependencies)??{},...(e==null?void 0:e.devDependencies)??{}},t=a(s,d,"next.config"),f="next"in n,p="react-scripts"in n,i=a(s,b,"webpack.config"),g="webpack"in n,r=a(s,j,"vite.config");if(t||f){const c=[];return r&&c.push("vite.config.* (마이그레이션 중?)"),{name:"next",signal:t?"next.config.*":"package.json: next dep",...c.length>0?{ambiguous:c}:{}}}return p?{name:"webpack",signal:"package.json: react-scripts dep (CRA)"}:i||g?{name:"webpack",signal:i?"webpack.config.*":"package.json: webpack dep"}:r?{name:"vite",signal:"vite.config.*"}:{name:"standalone",signal:"(no builder signals)"}}function a(s,e,n){for(const t of e)if(o.existsSync(l.resolve(s,`${n}.${t}`)))return!0;return!1}function C(s){const e=l.resolve(s,"package.json");if(o.existsSync(e))try{const n=o.readFileSync(e,"utf-8");return JSON.parse(n)}catch{return}}exports.detectUserGlobalCss=u.detectUserGlobalCss;exports.resolveGlobalCssPaths=u.resolveGlobalCssPaths;exports.detectBuilder=h;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * 알파.9: server-only 유틸리티 (Node.js only).
3
+ *
4
+ * 본 subpath의 함수들은 `node:fs`/`node:path`를 사용하므로 브라우저 client 번들에
5
+ * 포함되어서는 안 된다. CLI / 어댑터 / Vite plugin 등 Node 환경에서만 import.
6
+ *
7
+ * 사용처:
8
+ * - `@jogak/cli`: detectBuilder
9
+ * - `@jogak/vite-adapter` / `@jogak/next-adapter` / `@jogak/webpack-adapter`: resolveGlobalCssPaths
10
+ */
11
+ export { detectBuilder, type DetectBuilderResult, } from './builder-detect.js';
12
+ export { detectUserGlobalCss, resolveGlobalCssPaths, } from './vite/detect-global-css.js';
@@ -0,0 +1,45 @@
1
+ import { existsSync as r, readFileSync as u } from "node:fs";
2
+ import { resolve as f } from "node:path";
3
+ import { d as N, r as S } from "./detect-global-css-CIVMmgzy.js";
4
+ const m = ["ts", "js", "mjs", "cjs"], d = ["ts", "js", "mjs", "cjs"], j = ["ts", "mts", "js", "mjs", "cjs"];
5
+ function k(n) {
6
+ const e = b(n), s = {
7
+ ...(e == null ? void 0 : e.dependencies) ?? {},
8
+ ...(e == null ? void 0 : e.devDependencies) ?? {}
9
+ }, t = c(n, m, "next.config"), p = "next" in s, g = "react-scripts" in s, o = c(n, d, "webpack.config"), l = "webpack" in s, i = c(n, j, "vite.config");
10
+ if (t || p) {
11
+ const a = [];
12
+ return i && a.push("vite.config.* (마이그레이션 중?)"), {
13
+ name: "next",
14
+ signal: t ? "next.config.*" : "package.json: next dep",
15
+ ...a.length > 0 ? { ambiguous: a } : {}
16
+ };
17
+ }
18
+ return g ? {
19
+ name: "webpack",
20
+ signal: "package.json: react-scripts dep (CRA)"
21
+ } : o || l ? {
22
+ name: "webpack",
23
+ signal: o ? "webpack.config.*" : "package.json: webpack dep"
24
+ } : i ? { name: "vite", signal: "vite.config.*" } : { name: "standalone", signal: "(no builder signals)" };
25
+ }
26
+ function c(n, e, s) {
27
+ for (const t of e)
28
+ if (r(f(n, `${s}.${t}`))) return !0;
29
+ return !1;
30
+ }
31
+ function b(n) {
32
+ const e = f(n, "package.json");
33
+ if (r(e))
34
+ try {
35
+ const s = u(e, "utf-8");
36
+ return JSON.parse(s);
37
+ } catch {
38
+ return;
39
+ }
40
+ }
41
+ export {
42
+ k as detectBuilder,
43
+ N as detectUserGlobalCss,
44
+ S as resolveGlobalCssPaths
45
+ };
package/dist/types.d.ts CHANGED
@@ -209,38 +209,95 @@ export interface JogakPluginOptions {
209
209
  */
210
210
  readonly globalCss?: boolean | string | readonly string[];
211
211
  /**
212
- * Preview 영역의 격리 모드 (알파.7 도입, 알파.7.1에서 default 변경).
212
+ * Preview 영역의 격리 모드 (알파.7 도입, 알파.8에서 default `'iframe'`로 변경).
213
213
  *
214
- * 사용자 globalCss와 jogak chrome의 양방향 격리를 제공한다. 알파.7까지는
215
- * 사용자 globalCssouter document에 inject하면서 jogak chrome utility를
216
- * 무력화하는 결함이 있었으나 알파.7.1에서 main.tsx + ShadowMount가 모드별
217
- * scope을 분리하도록 정정됨.
214
+ * 알파.8의 default `'iframe'`은 사용자 vite 인스턴스를 spawn하여 vite의 정상 client에
215
+ * iframe document마운트한다. 사용자 plugins(@tailwindcss/vite 등)이 그대로 작동하므로
216
+ * **사용자 컴포넌트가 사용자 디자인 시스템 그대로 보인다**. 동시에 outer document의
217
+ * jogak chrome은 iframe 외부라 사용자 css 영향 zero (양방향 격리).
218
218
  *
219
219
  * 모드:
220
- * - `'shadow'` (default, 알파.7.1): Preview의 `[data-jogak-content]` 영역만
221
- * ShadowRoot에 마운트하고 사용자 globalCss ShadowRoot scope에만 inject.
222
- * outer document의 jogak chrome은 사용자 reset/preflight 침범을 받지 않는다.
223
- * **한계**: Radix UI(shadcn dialog/popover/tooltip 등)는 default Portal target이
224
- * `document.body` (shadow 외부) portal 내용은 사용자 css 미적용. 회피:
225
- * 사용자가 명시적으로 `<Portal container={shadowRootEl}>` 전달.
226
- * - `'iframe'`: Preview를 별도 `<iframe>`에 로드. 완벽 격리. **한계**: jogak의
227
- * "single Vite, no iframe" 차별점과 상충하므로 명시적 opt-in 한정. HMR은
228
- * iframe도 Vite dev server module이라 작동하지만, args/이벤트 전달이
229
- * `contentWindow` 직접 접근 한 단계 추가됨.
230
- * - `'none'` (back-compat opt-in): Preview는 jogak SPA와 동일 document에 마운트하고
231
- * 사용자 globalCss를 outer document에 inject. 사용자 reset/preflight가 jogak
232
- * chrome에 침범 가능. 사용자가 침범을 의도적으로 허용하는 경우만 사용.
233
- *
234
- * @default 'shadow'
235
- *
236
- * @example 양방향 격리 (default — 미지정 시 적용)
237
- * jogak({ globalCss: true }) // previewIsolation 'shadow' 자동 적용
238
- *
239
- * @example 사용자 reset이 chrome에도 영향을 주길 원하는 경우
240
- * jogak({ globalCss: true, previewIsolation: 'none' })
220
+ * - `'iframe'` (default, 알파.8): Preview를 사용자 vite정상 client(iframe)에 마운트.
221
+ * 사용자 utility 정상 컴파일 + 사용자 globalCss 적용 + Radix Portal 정상 (iframe document.body).
222
+ * chrome 침범 zero. cross-origin postMessage로 entry/args 전달.
223
+ * `userVite` 옵션으로 spawn 동작 제어.
224
+ * - `'shadow'` (deprecated, 알파.9 부활 검토): Preview를 ShadowRoot에 마운트. 알파.8 v1에서는
225
+ * 사용자 vite 산출물을 shadow에 inject하는 통로가 미구현 — 사용자 utility 미적용 한계.
226
+ * chrome 침범은 zero지만 사용자 컴포넌트 시각이 raw에 가까움.
227
+ * - `'none'` (deprecated, 알파.10 제거 검토): 사용자 globalCss를 outer document에 inject.
228
+ * jogak chrome이 사용자 reset/preflight 영향을 받음 (back-compat).
229
+ *
230
+ * @default 'iframe'
241
231
  *
242
- * @example Radix portal까지 완벽 격리
243
- * jogak({ globalCss: true, previewIsolation: 'iframe' })
232
+ * @example 사용자 vite 자동 spawn + iframe 모드 (default — 미지정 시 적용)
233
+ * jogak({ globalCss: true }) // previewIsolation 'iframe', 사용자 vite 자동 탐지
234
+ *
235
+ * @example 사용자 vite 스폰 비활성 + 사용자 reset이 chrome에도 영향 (back-compat)
236
+ * jogak({ globalCss: true, previewIsolation: 'none' })
244
237
  */
245
238
  readonly previewIsolation?: 'none' | 'shadow' | 'iframe';
239
+ /**
240
+ * 알파.8 internal: 본 jogak() plugin이 사용자 vite scope의 preview-frame entry용으로
241
+ * 동작하는지. CLI의 spawnUserVite가 사용자 vite에 jogak()을 mergeConfig로 inject할 때
242
+ * `previewFrame: true`를 함께 설정한다.
243
+ *
244
+ * `previewFrame: true`일 때:
245
+ * - jogak SPA chrome 가상 모듈(`_jogakCodeTheme`/`_jogakPreviewIsolation`/`_jogakUserViteUrl`/
246
+ * `_jogakMetas`) emit 비활성화
247
+ * - entry 가상 모듈(`virtual:jogak/entry/<slug>`)은 그대로 emit (preview-entry가 사용)
248
+ * - 사용자 측 vite plugins(@tailwindcss/vite 등)와 공존
249
+ *
250
+ * 사용자가 직접 설정하는 옵션이 아니다.
251
+ */
252
+ readonly previewFrame?: boolean;
253
+ /**
254
+ * @deprecated 알파.10 제거 예정. `userPreviewUrl` 사용.
255
+ *
256
+ * 알파.8 internal: jogak SPA가 iframe src로 사용할 사용자 vite의 base URL.
257
+ */
258
+ readonly userViteUrl?: string;
259
+ /**
260
+ * 알파.9 internal: jogak SPA가 iframe src로 사용할 어댑터 dev URL
261
+ * (예: `http://localhost:5174`). CLI의 어댑터 dispatch 결과가 host 통해 plugin에 전달.
262
+ *
263
+ * 빈 문자열 시 fallback (jogak SPA Vite scope의 preview-frame).
264
+ *
265
+ * 사용자가 직접 설정하는 옵션이 아니다.
266
+ */
267
+ readonly userPreviewUrl?: string;
268
+ /**
269
+ * 알파.9 internal: iframe src의 path (예: `/__jogak_preview__/index.html`).
270
+ * `BuilderAdapter.previewEntryMeta.devEntryPath` 값. 어댑터별 routing.
271
+ *
272
+ * 사용자가 직접 설정하는 옵션이 아니다.
273
+ */
274
+ readonly previewEntryPath?: string;
275
+ }
276
+ /**
277
+ * 알파.8: 사용자 vite 인스턴스 spawn 옵션.
278
+ *
279
+ * jogak CLI는 사용자 cwd의 `vite.config.{ts,mts,js,mjs,cjs}`를 자동 탐지해 별도
280
+ * vite dev server를 spawn한다. iframe 모드의 src로 사용되어 사용자 컴포넌트가
281
+ * 사용자 vite plugins(@tailwindcss/vite, custom alias 등)의 정상 client에서
282
+ * 평가된다.
283
+ */
284
+ export interface UserViteOptions {
285
+ /**
286
+ * 사용자 vite.config.ts 절대/상대 경로. 미지정 시 cwd에서 자동 탐지
287
+ * (`vite.config.ts` > `vite.config.mts` > `vite.config.js` > `vite.config.mjs` > `vite.config.cjs`).
288
+ */
289
+ readonly configFile?: string;
290
+ /**
291
+ * 사용자 vite dev server 포트. 미지정 시 0(free port).
292
+ */
293
+ readonly port?: number;
294
+ /**
295
+ * 사용자 vite dev server host. 미지정 시 'localhost'.
296
+ */
297
+ readonly host?: string | boolean;
298
+ /**
299
+ * 사용자 vite spawn을 비활성화. 알파.7.1 동등 fallback 동작
300
+ * (사용자 utility 미컴파일).
301
+ */
302
+ readonly disabled?: boolean;
246
303
  }
@@ -0,0 +1,118 @@
1
+ "use strict";var Z=Object.create;var U=Object.defineProperty;var ee=Object.getOwnPropertyDescriptor;var te=Object.getOwnPropertyNames;var re=Object.getPrototypeOf,oe=Object.prototype.hasOwnProperty;var ne=(e,t,o,n)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of te(t))!oe.call(e,a)&&a!==o&&U(e,a,{get:()=>t[a],enumerable:!(n=ee(t,a))||n.enumerable});return e};var ae=(e,t,o)=>(o=e!=null?Z(re(e)):{},ne(t||!e||!e.__esModule?U(o,"default",{value:e,enumerable:!0}):o,e));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const v=require("node:path"),S=require("node:fs"),E=require("node:fs/promises"),B=require("../detect-global-css-CDcuUAYE.cjs"),$=require("../extractor-client-CiWszHel.cjs"),se=["@jogak/core","@jogak/react","@jogak/web-components","@jogak/next"];async function W(e){try{const n=await E.stat(e);if(!n.isDirectory())return n.mtimeMs}catch{return 0}let t=0,o;try{o=await E.readdir(e)}catch{return 0}for(const n of o){const a=v.join(e,n);try{const l=await E.stat(a);if(l.isDirectory()){const g=await W(a);g>t&&(t=g)}else l.mtimeMs>t&&(t=l.mtimeMs)}catch{continue}}return t}async function ie(e){const t=v.resolve(e.root,"node_modules/.vite/deps");if(!S.existsSync(t))return{purged:!1};const o=v.join(t,"_metadata.json");if(!S.existsSync(o))return{purged:!1};let n;try{n=(await E.stat(o)).mtimeMs}catch(l){return e.logger.warn(`[jogak] cache validation: failed to stat _metadata.json (${l.message})`),{purged:!1}}const a=e.packages??se;for(const l of a){const g=v.resolve(e.root,"node_modules",l,"dist");if(!S.existsSync(g))continue;let y;try{y=await W(g)}catch(h){e.logger.warn(`[jogak] cache validation: failed to walk ${l}/dist (${h.message})`);continue}if(y>n+1e3)try{return await E.rm(t,{recursive:!0,force:!0}),e.logger.info(`[jogak] vite deps cache invalidated (stale): ${l} dist newer than cache`),{purged:!0,reason:l}}catch(h){return e.logger.warn(`[jogak] cache validation: failed to purge ${t} (${h.message})`),{purged:!1}}}return{purged:!1}}function ce(e){return e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"")}function le(e){if(S.existsSync(e))try{const t=S.readFileSync(e,"utf8");return JSON.parse(ce(t))}catch{return}}function ue(e,t,o){if(!e.endsWith("/*")||!t.endsWith("/*"))return;const n=e.slice(0,-2),a=v.resolve(o,t.slice(0,-2));return[n,a]}function de(e,t){var a,l;const o={},n=new Set([e,v.resolve(t,"tsconfig.app.json")]);for(const g of n){const y=le(g);if(y===void 0)continue;const h=(a=y.compilerOptions)==null?void 0:a.paths;if(h===void 0)continue;const T=((l=y.compilerOptions)==null?void 0:l.baseUrl)??".",R=v.resolve(v.dirname(g),T);for(const[b,d]of Object.entries(h)){const f=d[0];if(f===void 0)continue;const _=ue(b,f,R);if(_===void 0)continue;const[k,C]=_;o[k]===void 0&&(o[k]=C)}}return o}const K="virtual:jogak",O="\0"+K,H="virtual:jogak/entry/",L="\0"+H,Y="virtual:jogak/global-css",D="\0"+Y,M="virtual:jogak/preview-entry",F="\0"+M,q="virtual:jogak/preview-global-css",G="\0"+q;function ge(e){return Buffer.from(e,"utf8").toString("base64url")}function fe(e){return Buffer.from(e,"base64url").toString("utf8")}const me="/__jogak_preview__/index.html";function pe(e){return{name:"vite-plugin-jogak-preview-frame",enforce:"pre",configureServer(t){t.middlewares.use((o,n,a)=>{if(o.url===void 0||o.url.split("?")[0]!==me||o.method!=="GET")return a();const g=ve();t.transformIndexHtml(o.url,g).then(y=>{n.statusCode=200,n.setHeader("Content-Type","text/html; charset=utf-8"),n.end(y)}).catch(a)})},resolveId(t){if(t===M)return F;if(t===q)return G},load(t){if(t===G){const o=B.resolveGlobalCssPaths(e.globalCss,e.userRoot);return o.length===0?`// [jogak] preview-global-css: no candidates
2
+ export {}
3
+ `:o.map(n=>`import ${JSON.stringify(n)}`).join(`
4
+ `)+`
5
+ export {}
6
+ `}if(t===F)return ye}}}function ve(){return`<!doctype html>
7
+ <html lang="en">
8
+ <head>
9
+ <meta charset="UTF-8" />
10
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
11
+ <title>jogak preview</title>
12
+ <style>
13
+ html, body { margin: 0; padding: 0; }
14
+ #jogak-preview-root { display: block; }
15
+ </style>
16
+ </head>
17
+ <body>
18
+ <div id="jogak-preview-root"></div>
19
+ <script type="module" src="/@id/${M}"><\/script>
20
+ </body>
21
+ </html>
22
+ `}const ye=`
23
+ import { reactAdapter } from '@jogak/react'
24
+ import { defaultRegistry } from '@jogak/core'
25
+ import 'virtual:jogak'
26
+ import 'virtual:jogak/preview-global-css'
27
+
28
+ const rootEl = document.getElementById('jogak-preview-root')
29
+ if (rootEl === null) throw new Error('[jogak] #jogak-preview-root not found')
30
+
31
+ let currentContainer = null
32
+ let currentArgs = {}
33
+ let currentEntryId = null
34
+
35
+ async function renderEntry(entryId, args) {
36
+ currentEntryId = entryId
37
+ currentArgs = args
38
+ const entry = await defaultRegistry.requestEntry(entryId)
39
+ if (currentContainer === null) {
40
+ currentContainer = document.createElement('div')
41
+ rootEl.replaceChildren(currentContainer)
42
+ }
43
+ reactAdapter.render(entry, args, currentContainer)
44
+ }
45
+
46
+ function unmount() {
47
+ if (currentContainer !== null) {
48
+ reactAdapter.unmount(currentContainer)
49
+ currentContainer = null
50
+ currentEntryId = null
51
+ }
52
+ }
53
+
54
+ window.addEventListener('message', (event) => {
55
+ const data = event.data
56
+ if (data == null || typeof data !== 'object') return
57
+ if (data.type === 'jogak:setProps') {
58
+ void renderEntry(data.entryId, data.args ?? {}).then(() => {
59
+ window.parent.postMessage({ type: 'jogak:rendered', entryId: data.entryId }, '*')
60
+ }).catch((err) => {
61
+ window.parent.postMessage({ type: 'jogak:error', message: String(err?.message ?? err) }, '*')
62
+ })
63
+ } else if (data.type === 'jogak:unmount') {
64
+ unmount()
65
+ }
66
+ })
67
+
68
+ window.parent.postMessage({ type: 'jogak:ready' }, '*')
69
+ `;function J(e){return{title:e.title,jogakNamesKey:[...e.jogakNames].sort().join("|")}}function we(e,t){return e!==void 0&&e.title===t.title&&e.jogakNamesKey===t.jogakNamesKey}function he(e={}){const{patterns:t=["src/**/*.jogak.ts","src/**/*.jogak.tsx"],codeTheme:o="vsDark"}=e,n=e.cwd,a=e.tsConfigFilePath,l=e.disableCacheValidation===!0,g=e.resolveAlias,y=e.globalCss,h=e.previewIsolation??"iframe",T=e.previewFrame===!0,R=e.userPreviewUrl??e.userViteUrl??"",b=e.previewEntryPath??"/__jogak_preview__/index.html";let d,f,_;const k=new Map,C=new Map,x=new Map;async function N(){const{glob:r}=await import("glob"),m=_??process.cwd(),s=(await r(t,{cwd:m,absolute:!0})).sort(),u=[];k.clear(),C.clear();for(const i of s){let w="";try{w=await E.readFile(i,"utf8")}catch{continue}let j={},c=null;if(f!==void 0){try{j=await f.extract(i)}catch{j={}}try{c=await f.extractMeta(i)}catch{c=null}}if(c===null)continue;const p=c.title;k.set(p,i),C.set(i,p);const I={id:p,title:c.title,jogakNames:c.jogakNames,autoArgTypes:j,userArgTypes:c.userArgTypes,source:w,filePath:i,metaExtras:c.metaExtras};x.set(i,J(I)),u.push({id:p,filePath:i,meta:I})}return u}return{name:"vite-plugin-jogak",config(){const r=n??process.cwd(),m=a??v.resolve(r,"tsconfig.json"),s=de(m,r),u={};if(g!==void 0)for(const[w,j]of Object.entries(g))u[w]=v.resolve(r,j);const i={...s,...u};if(Object.keys(i).length!==0)return{resolve:{alias:i}}},async configResolved(r){_=n??r.root,r.command==="serve"&&!l&&await ie({root:r.root,logger:{info:s=>r.logger.info(s),warn:s=>r.logger.warn(s)}});const m=a??v.resolve(_,"tsconfig.json");f=S.existsSync(m)?$.createPropsExtractor({tsConfigFilePath:m}):$.createPropsExtractor()},configureServer(r){d=r},buildEnd(){f==null||f.releaseCache()},resolveId(r){if(r===K)return O;if(r===Y)return D;if(r.startsWith(H))return"\0"+r},async load(r){if(r===D){const m=_??process.cwd(),s=B.resolveGlobalCssPaths(y,m);return s.length===0?`// [jogak] globalCss not configured or no candidates found.
70
+ export {}
71
+ `:`${s.map(i=>`import ${JSON.stringify(i)}`).join(`
72
+ `)}
73
+ export {}
74
+ `}if(r===O){const s=(await N()).map(i=>i.meta),u=T?"":`
75
+ export const _jogakCodeTheme = ${JSON.stringify(o)}
76
+ export const _jogakPreviewIsolation = ${JSON.stringify(h)}
77
+ export const _jogakUserPreviewUrl = ${JSON.stringify(R)}
78
+ export const _jogakPreviewEntryPath = ${JSON.stringify(b)}
79
+ export const _jogakUserViteUrl = ${JSON.stringify(R)}
80
+ export const _jogakMetas = _metas
81
+ `;return`import { defaultRegistry } from '@jogak/core'
82
+
83
+ const _entryLoader = (slug) =>
84
+ import(/* @vite-ignore */ '/@id/__x00__virtual:jogak/entry/' + slug)
85
+ defaultRegistry.setEntryLoader((id) => {
86
+ const slug = ${je()}
87
+ return _entryLoader(slug(id))
88
+ })
89
+
90
+ const _metas = ${JSON.stringify(s)}
91
+
92
+ for (const m of _metas) defaultRegistry.registerMeta(m)
93
+ ${u}`}if(r.startsWith(L)){const m=r.slice(L.length),s=fe(m);let u=k.get(s);return u===void 0&&(await N(),u=k.get(s)),u===void 0?`// [jogak] unknown entry id: ${JSON.stringify(s)}
94
+ export {}
95
+ `:`import * as _user from ${JSON.stringify(u)}
96
+ import { defaultRegistry } from '@jogak/core'
97
+
98
+ const _meta = _user.default
99
+ const _named = { ..._user }
100
+ delete _named.default
101
+ const _jogaks = Object.values(_named).filter(
102
+ (v) => v !== null && typeof v === 'object' && typeof v.name === 'string'
103
+ )
104
+ defaultRegistry.hydrateEntry(${JSON.stringify(s)}, _jogaks, _meta?.component)
105
+
106
+ if (import.meta.hot) {
107
+ import.meta.hot.accept()
108
+ }
109
+
110
+ export {}
111
+ `}},async handleHotUpdate({file:r,modules:m}){const s=/\.jogak\.(tsx?|jsx?)$/.test(r),u=/\.(tsx?|jsx?)$/.test(r)&&!s;if(!s&&!u||d===void 0||!s)return;const i=d.moduleGraph.getModuleById(O),w=C.get(r),j=w!==void 0?L+ge(w):void 0,c=j!==void 0?d.moduleGraph.getModuleById(j):void 0;let p=null,I={},P="";if(f!==void 0){try{p=await f.extractMeta(r)}catch{p=null}try{I=await f.extract(r)}catch{I={}}try{P=await E.readFile(r,"utf8")}catch{P=""}}if(p===null){i!==void 0&&d.moduleGraph.invalidateModule(i),c!==void 0&&d.moduleGraph.invalidateModule(c),d.ws.send({type:"full-reload"});return}const V=J(p),X=x.get(r),z=we(X,V);if(x.set(r,V),!z||w===void 0){i!==void 0&&d.moduleGraph.invalidateModule(i),c!==void 0&&d.moduleGraph.invalidateModule(c),d.ws.send({type:"full-reload"});return}const Q={id:w,title:p.title,jogakNames:p.jogakNames,autoArgTypes:I,userArgTypes:p.userArgTypes,source:P,filePath:r,metaExtras:p.metaExtras};c!==void 0&&d.moduleGraph.invalidateModule(c),d.ws.send({type:"custom",event:"jogak:meta-update",data:{id:w,meta:Q}});const A=[...m];return c!==void 0&&!A.includes(c)&&A.push(c),A}}}function je(){return`(rawId) => {
112
+ if (typeof Buffer !== 'undefined') return Buffer.from(rawId, 'utf8').toString('base64url')
113
+ // 브라우저 폴백: btoa는 binary string 기준이라 UTF-8을 한번 인코딩해야 한다.
114
+ const enc = new TextEncoder().encode(rawId)
115
+ let bin = ''
116
+ for (let i = 0; i < enc.length; i++) bin += String.fromCharCode(enc[i])
117
+ return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')
118
+ }`}exports.jogak=he;exports.jogakPreviewFramePlugin=pe;