@jogak/core 0.1.0-alpha.7.1 → 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/dist/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { JogakPluginOptions } from './types.js';
1
+ import { JogakPluginOptions, UserViteOptions } from './types.js';
2
2
  /**
3
3
  * `jogak.config.{ts,mts,mjs,js,json}`의 default export 타입.
4
4
  *
@@ -27,6 +27,19 @@ export interface JogakConfig extends JogakPluginOptions {
27
27
  readonly minify?: boolean | 'esbuild' | 'terser';
28
28
  /** 빌드 소스맵. 기본 false. CLI `--sourcemap`로 override. */
29
29
  readonly sourcemap?: boolean;
30
+ /**
31
+ * 사용자 vite 인스턴스 spawn 옵션 (알파.8). 미지정 시 cwd의 `vite.config.{ts,mts,js,mjs,cjs}`를
32
+ * 자동 탐지해 별도 vite dev server를 띄워 iframe src로 사용한다.
33
+ *
34
+ * `previewIsolation: 'iframe'` (default)에서만 사용된다.
35
+ *
36
+ * @example 사용자 vite 명시 비활성화 (알파.7.1 동등 fallback)
37
+ * defineJogakConfig({ userVite: { disabled: true } })
38
+ *
39
+ * @example 사용자 vite 포트 명시
40
+ * defineJogakConfig({ userVite: { port: 5174 } })
41
+ */
42
+ readonly userVite?: UserViteOptions;
30
43
  }
31
44
  /**
32
45
  * `jogak.config.ts`에서 사용자가 호출하는 identity helper.
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type { ArgType, JogakMeta, Jogak, RegistryEntry, RegistryEntryMeta, CategoryTree, CategoryMetaTree, JogakAdapter, JogakPluginOptions, } from './types.js';
1
+ export type { ArgType, JogakMeta, Jogak, RegistryEntry, RegistryEntryMeta, CategoryTree, CategoryMetaTree, JogakAdapter, JogakPluginOptions, UserViteOptions, } from './types.js';
2
2
  export { ComponentRegistry, defaultRegistry, UnknownEntryError } from './registry.js';
3
3
  export { ActionChannel, defaultActionChannel, action, injectActions, type ActionLog, type ActionListener, } from './actions.js';
4
4
  export { defineJogakConfig, type JogakConfig } from './config.js';
package/dist/types.d.ts CHANGED
@@ -209,38 +209,83 @@ 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'
231
+ *
232
+ * @example 사용자 vite 자동 spawn + iframe 모드 (default — 미지정 시 적용)
233
+ * jogak({ globalCss: true }) // previewIsolation 'iframe', 사용자 vite 자동 탐지
241
234
  *
242
- * @example Radix portal까지 완벽 격리
243
- * jogak({ globalCss: true, previewIsolation: 'iframe' })
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
+ * 알파.8 internal: jogak SPA가 iframe src로 사용할 사용자 vite의 base URL
255
+ * (예: `http://localhost:5174`). CLI가 spawnUserVite 결과를 `runHost` 통해
256
+ * jogak() plugin에 전달한다.
257
+ *
258
+ * 빈 문자열 또는 미지정 시 기존 fallback (jogak SPA Vite scope의 preview-frame.tsx).
259
+ *
260
+ * 사용자가 직접 설정하는 옵션이 아니다.
261
+ */
262
+ readonly userViteUrl?: string;
263
+ }
264
+ /**
265
+ * 알파.8: 사용자 vite 인스턴스 spawn 옵션.
266
+ *
267
+ * jogak CLI는 사용자 cwd의 `vite.config.{ts,mts,js,mjs,cjs}`를 자동 탐지해 별도
268
+ * vite dev server를 spawn한다. iframe 모드의 src로 사용되어 사용자 컴포넌트가
269
+ * 사용자 vite plugins(@tailwindcss/vite, custom alias 등)의 정상 client에서
270
+ * 평가된다.
271
+ */
272
+ export interface UserViteOptions {
273
+ /**
274
+ * 사용자 vite.config.ts 절대/상대 경로. 미지정 시 cwd에서 자동 탐지
275
+ * (`vite.config.ts` > `vite.config.mts` > `vite.config.js` > `vite.config.mjs` > `vite.config.cjs`).
276
+ */
277
+ readonly configFile?: string;
278
+ /**
279
+ * 사용자 vite dev server 포트. 미지정 시 0(free port).
280
+ */
281
+ readonly port?: number;
282
+ /**
283
+ * 사용자 vite dev server host. 미지정 시 'localhost'.
284
+ */
285
+ readonly host?: string | boolean;
286
+ /**
287
+ * 사용자 vite spawn을 비활성화. 알파.7.1 동등 fallback 동작
288
+ * (사용자 utility 미컴파일).
289
+ */
290
+ readonly disabled?: boolean;
246
291
  }
@@ -1,25 +1,94 @@
1
- "use strict";var K=Object.create;var L=Object.defineProperty;var q=Object.getOwnPropertyDescriptor;var W=Object.getOwnPropertyNames;var X=Object.getPrototypeOf,Y=Object.prototype.hasOwnProperty;var H=(e,t,s,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of W(t))!Y.call(e,a)&&a!==s&&L(e,a,{get:()=>t[a],enumerable:!(o=q(t,a))||o.enumerable});return e};var z=(e,t,s)=>(s=e!=null?K(X(e)):{},H(t||!e||!e.__esModule?L(s,"default",{value:e,enumerable:!0}):s,e));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const f=require("node:path"),_=require("node:fs"),k=require("node:fs/promises"),R=require("../extractor-client-CiWszHel.cjs"),Q=["@jogak/core","@jogak/react","@jogak/web-components","@jogak/next"];async function P(e){try{const o=await k.stat(e);if(!o.isDirectory())return o.mtimeMs}catch{return 0}let t=0,s;try{s=await k.readdir(e)}catch{return 0}for(const o of s){const a=f.join(e,o);try{const l=await k.stat(a);if(l.isDirectory()){const y=await P(a);y>t&&(t=y)}else l.mtimeMs>t&&(t=l.mtimeMs)}catch{continue}}return t}async function Z(e){const t=f.resolve(e.root,"node_modules/.vite/deps");if(!_.existsSync(t))return{purged:!1};const s=f.join(t,"_metadata.json");if(!_.existsSync(s))return{purged:!1};let o;try{o=(await k.stat(s)).mtimeMs}catch(l){return e.logger.warn(`[jogak] cache validation: failed to stat _metadata.json (${l.message})`),{purged:!1}}const a=e.packages??Q;for(const l of a){const y=f.resolve(e.root,"node_modules",l,"dist");if(!_.existsSync(y))continue;let h;try{h=await P(y)}catch(w){e.logger.warn(`[jogak] cache validation: failed to walk ${l}/dist (${w.message})`);continue}if(h>o+1e3)try{return await k.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(w){return e.logger.warn(`[jogak] cache validation: failed to purge ${t} (${w.message})`),{purged:!1}}}return{purged:!1}}const ee=["src/index.css","src/main.css","src/styles.css","src/styles/globals.css","src/styles/index.css","src/app/globals.css","src/global.css","src/app.css"];function te(e){for(const t of ee){const s=f.resolve(e,t);if(_.existsSync(s))return[s]}return[]}function re(e,t){if(e===void 0||e===!1)return[];if(e===!0)return te(t);if(typeof e=="string")return e.length>0?[f.resolve(t,e)]:[];const s=[];for(const o of e)typeof o=="string"&&o.length>0&&s.push(f.resolve(t,o));return s}function se(e){return e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"")}function oe(e){if(_.existsSync(e))try{const t=_.readFileSync(e,"utf8");return JSON.parse(se(t))}catch{return}}function ne(e,t,s){if(!e.endsWith("/*")||!t.endsWith("/*"))return;const o=e.slice(0,-2),a=f.resolve(s,t.slice(0,-2));return[o,a]}function ae(e,t){var a,l;const s={},o=new Set([e,f.resolve(t,"tsconfig.app.json")]);for(const y of o){const h=oe(y);if(h===void 0)continue;const w=(a=h.compilerOptions)==null?void 0:a.paths;if(w===void 0)continue;const d=((l=h.compilerOptions)==null?void 0:l.baseUrl)??".",p=f.resolve(f.dirname(y),d);for(const[S,b]of Object.entries(w)){const x=b[0];if(x===void 0)continue;const C=ne(S,x,p);if(C===void 0)continue;const[A,r]=C;s[A]===void 0&&(s[A]=r)}}return s}const F="virtual:jogak",M="\0"+F,G="virtual:jogak/entry/",O="\0"+G,U="virtual:jogak/global-css",$="\0"+U;function ie(e){return Buffer.from(e,"utf8").toString("base64url")}function ce(e){return Buffer.from(e,"base64url").toString("utf8")}function D(e){return{title:e.title,jogakNamesKey:[...e.jogakNames].sort().join("|")}}function le(e,t){return e!==void 0&&e.title===t.title&&e.jogakNamesKey===t.jogakNamesKey}function ue(e={}){const{patterns:t=["src/**/*.jogak.ts","src/**/*.jogak.tsx"],codeTheme:s="vsDark"}=e,o=e.cwd,a=e.tsConfigFilePath,l=e.disableCacheValidation===!0,y=e.resolveAlias,h=e.globalCss,w=e.previewIsolation??"shadow";let d,p,S;const b=new Map,x=new Map,C=new Map;async function A(){const{glob:r}=await import("glob"),g=S??process.cwd(),n=(await r(t,{cwd:g,absolute:!0})).sort(),u=[];b.clear(),x.clear();for(const i of n){let v="";try{v=await k.readFile(i,"utf8")}catch{continue}let j={},c=null;if(p!==void 0){try{j=await p.extract(i)}catch{j={}}try{c=await p.extractMeta(i)}catch{c=null}}if(c===null)continue;const m=c.title;b.set(m,i),x.set(i,m);const T={id:m,title:c.title,jogakNames:c.jogakNames,autoArgTypes:j,userArgTypes:c.userArgTypes,source:v,filePath:i,metaExtras:c.metaExtras};C.set(i,D(T)),u.push({id:m,filePath:i,meta:T})}return u}return{name:"vite-plugin-jogak",config(){const r=o??process.cwd(),g=a??f.resolve(r,"tsconfig.json"),n=ae(g,r),u={};if(y!==void 0)for(const[v,j]of Object.entries(y))u[v]=f.resolve(r,j);const i={...n,...u};if(Object.keys(i).length!==0)return{resolve:{alias:i}}},async configResolved(r){S=o??r.root,r.command==="serve"&&!l&&await Z({root:r.root,logger:{info:n=>r.logger.info(n),warn:n=>r.logger.warn(n)}});const g=a??f.resolve(S,"tsconfig.json");p=_.existsSync(g)?R.createPropsExtractor({tsConfigFilePath:g}):R.createPropsExtractor()},configureServer(r){d=r},buildEnd(){p==null||p.releaseCache()},resolveId(r){if(r===F)return M;if(r===U)return $;if(r.startsWith(G))return"\0"+r},async load(r){if(r===$){const g=S??process.cwd(),n=re(h,g);return n.length===0?`// [jogak] globalCss not configured or no candidates found.
1
+ "use strict";var Q=Object.create;var N=Object.defineProperty;var Z=Object.getOwnPropertyDescriptor;var ee=Object.getOwnPropertyNames;var te=Object.getPrototypeOf,re=Object.prototype.hasOwnProperty;var ne=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let a of ee(t))!re.call(e,a)&&a!==r&&N(e,a,{get:()=>t[a],enumerable:!(o=Z(t,a))||o.enumerable});return e};var oe=(e,t,r)=>(r=e!=null?Q(te(e)):{},ne(t||!e||!e.__esModule?N(r,"default",{value:e,enumerable:!0}):r,e));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("node:path"),E=require("node:fs"),I=require("node:fs/promises"),D=require("../extractor-client-CiWszHel.cjs"),ae=["@jogak/core","@jogak/react","@jogak/web-components","@jogak/next"];async function B(e){try{const o=await I.stat(e);if(!o.isDirectory())return o.mtimeMs}catch{return 0}let t=0,r;try{r=await I.readdir(e)}catch{return 0}for(const o of r){const a=g.join(e,o);try{const l=await I.stat(a);if(l.isDirectory()){const f=await B(a);f>t&&(t=f)}else l.mtimeMs>t&&(t=l.mtimeMs)}catch{continue}}return t}async function se(e){const t=g.resolve(e.root,"node_modules/.vite/deps");if(!E.existsSync(t))return{purged:!1};const r=g.join(t,"_metadata.json");if(!E.existsSync(r))return{purged:!1};let o;try{o=(await I.stat(r)).mtimeMs}catch(l){return e.logger.warn(`[jogak] cache validation: failed to stat _metadata.json (${l.message})`),{purged:!1}}const a=e.packages??ae;for(const l of a){const f=g.resolve(e.root,"node_modules",l,"dist");if(!E.existsSync(f))continue;let v;try{v=await B(f)}catch(w){e.logger.warn(`[jogak] cache validation: failed to walk ${l}/dist (${w.message})`);continue}if(v>o+1e3)try{return await I.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(w){return e.logger.warn(`[jogak] cache validation: failed to purge ${t} (${w.message})`),{purged:!1}}}return{purged:!1}}const ie=["src/index.css","src/main.css","src/styles.css","src/styles/globals.css","src/styles/index.css","src/app/globals.css","src/global.css","src/app.css"];function ce(e){for(const t of ie){const r=g.resolve(e,t);if(E.existsSync(r))return[r]}return[]}function J(e,t){if(e===void 0||e===!1)return[];if(e===!0)return ce(t);if(typeof e=="string")return e.length>0?[g.resolve(t,e)]:[];const r=[];for(const o of e)typeof o=="string"&&o.length>0&&r.push(g.resolve(t,o));return r}function le(e){return e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"")}function ue(e){if(E.existsSync(e))try{const t=E.readFileSync(e,"utf8");return JSON.parse(le(t))}catch{return}}function de(e,t,r){if(!e.endsWith("/*")||!t.endsWith("/*"))return;const o=e.slice(0,-2),a=g.resolve(r,t.slice(0,-2));return[o,a]}function ge(e,t){var a,l;const r={},o=new Set([e,g.resolve(t,"tsconfig.app.json")]);for(const f of o){const v=ue(f);if(v===void 0)continue;const w=(a=v.compilerOptions)==null?void 0:a.paths;if(w===void 0)continue;const T=((l=v.compilerOptions)==null?void 0:l.baseUrl)??".",R=g.resolve(g.dirname(f),T);for(const[d,y]of Object.entries(w)){const _=y[0];if(_===void 0)continue;const k=de(d,_,R);if(k===void 0)continue;const[S,b]=k;r[S]===void 0&&(r[S]=b)}}return r}const W="virtual:jogak",O="\0"+W,K="virtual:jogak/entry/",L="\0"+K,H="virtual:jogak/global-css",U="\0"+H,M="virtual:jogak/preview-entry",$="\0"+M,Y="virtual:jogak/preview-global-css",F="\0"+Y;function fe(e){return Buffer.from(e,"utf8").toString("base64url")}function me(e){return Buffer.from(e,"base64url").toString("utf8")}const pe="/__jogak_preview__/index.html";function ye(e){return{name:"vite-plugin-jogak-preview-frame",enforce:"pre",configureServer(t){t.middlewares.use((r,o,a)=>{if(r.url===void 0||r.url.split("?")[0]!==pe||r.method!=="GET")return a();const f=ve();t.transformIndexHtml(r.url,f).then(v=>{o.statusCode=200,o.setHeader("Content-Type","text/html; charset=utf-8"),o.end(v)}).catch(a)})},resolveId(t){if(t===M)return $;if(t===Y)return F},load(t){if(t===F){const r=J(e.globalCss,e.userRoot);return r.length===0?`// [jogak] preview-global-css: no candidates
2
2
  export {}
3
- `:`${n.map(i=>`import ${JSON.stringify(i)}`).join(`
3
+ `:r.map(o=>`import ${JSON.stringify(o)}`).join(`
4
+ `)+`
5
+ export {}
6
+ `}if(t===$)return he}}}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 he=`
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 G(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 je(e={}){const{patterns:t=["src/**/*.jogak.ts","src/**/*.jogak.tsx"],codeTheme:r="vsDark"}=e,o=e.cwd,a=e.tsConfigFilePath,l=e.disableCacheValidation===!0,f=e.resolveAlias,v=e.globalCss,w=e.previewIsolation??"iframe",T=e.previewFrame===!0,R=e.userViteUrl??"";let d,y,_;const k=new Map,S=new Map,b=new Map;async function P(){const{glob:n}=await import("glob"),m=_??process.cwd(),s=(await n(t,{cwd:m,absolute:!0})).sort(),u=[];k.clear(),S.clear();for(const i of s){let h="";try{h=await I.readFile(i,"utf8")}catch{continue}let j={},c=null;if(y!==void 0){try{j=await y.extract(i)}catch{j={}}try{c=await y.extractMeta(i)}catch{c=null}}if(c===null)continue;const p=c.title;k.set(p,i),S.set(i,p);const C={id:p,title:c.title,jogakNames:c.jogakNames,autoArgTypes:j,userArgTypes:c.userArgTypes,source:h,filePath:i,metaExtras:c.metaExtras};b.set(i,G(C)),u.push({id:p,filePath:i,meta:C})}return u}return{name:"vite-plugin-jogak",config(){const n=o??process.cwd(),m=a??g.resolve(n,"tsconfig.json"),s=ge(m,n),u={};if(f!==void 0)for(const[h,j]of Object.entries(f))u[h]=g.resolve(n,j);const i={...s,...u};if(Object.keys(i).length!==0)return{resolve:{alias:i}}},async configResolved(n){_=o??n.root,n.command==="serve"&&!l&&await se({root:n.root,logger:{info:s=>n.logger.info(s),warn:s=>n.logger.warn(s)}});const m=a??g.resolve(_,"tsconfig.json");y=E.existsSync(m)?D.createPropsExtractor({tsConfigFilePath:m}):D.createPropsExtractor()},configureServer(n){d=n},buildEnd(){y==null||y.releaseCache()},resolveId(n){if(n===W)return O;if(n===H)return U;if(n.startsWith(K))return"\0"+n},async load(n){if(n===U){const m=_??process.cwd(),s=J(v,m);return s.length===0?`// [jogak] globalCss not configured or no candidates found.
70
+ export {}
71
+ `:`${s.map(i=>`import ${JSON.stringify(i)}`).join(`
4
72
  `)}
5
73
  export {}
6
- `}if(r===M){const n=(await A()).map(u=>u.meta);return`import { defaultRegistry } from '@jogak/core'
74
+ `}if(n===O){const s=(await P()).map(i=>i.meta),u=T?"":`
75
+ export const _jogakCodeTheme = ${JSON.stringify(r)}
76
+ export const _jogakPreviewIsolation = ${JSON.stringify(w)}
77
+ export const _jogakUserViteUrl = ${JSON.stringify(R)}
78
+ export const _jogakMetas = _metas
79
+ `;return`import { defaultRegistry } from '@jogak/core'
7
80
 
8
81
  const _entryLoader = (slug) =>
9
82
  import(/* @vite-ignore */ '/@id/__x00__virtual:jogak/entry/' + slug)
10
83
  defaultRegistry.setEntryLoader((id) => {
11
- const slug = ${de()}
84
+ const slug = ${_e()}
12
85
  return _entryLoader(slug(id))
13
86
  })
14
87
 
15
- const _metas = ${JSON.stringify(n)}
88
+ const _metas = ${JSON.stringify(s)}
16
89
 
17
90
  for (const m of _metas) defaultRegistry.registerMeta(m)
18
-
19
- export const _jogakCodeTheme = ${JSON.stringify(s)}
20
- export const _jogakPreviewIsolation = ${JSON.stringify(w)}
21
- export const _jogakMetas = _metas
22
- `}if(r.startsWith(O)){const g=r.slice(O.length),n=ce(g);let u=b.get(n);return u===void 0&&(await A(),u=b.get(n)),u===void 0?`// [jogak] unknown entry id: ${JSON.stringify(n)}
91
+ ${u}`}if(n.startsWith(L)){const m=n.slice(L.length),s=me(m);let u=k.get(s);return u===void 0&&(await P(),u=k.get(s)),u===void 0?`// [jogak] unknown entry id: ${JSON.stringify(s)}
23
92
  export {}
24
93
  `:`import * as _user from ${JSON.stringify(u)}
25
94
  import { defaultRegistry } from '@jogak/core'
@@ -30,18 +99,18 @@ delete _named.default
30
99
  const _jogaks = Object.values(_named).filter(
31
100
  (v) => v !== null && typeof v === 'object' && typeof v.name === 'string'
32
101
  )
33
- defaultRegistry.hydrateEntry(${JSON.stringify(n)}, _jogaks, _meta?.component)
102
+ defaultRegistry.hydrateEntry(${JSON.stringify(s)}, _jogaks, _meta?.component)
34
103
 
35
104
  if (import.meta.hot) {
36
105
  import.meta.hot.accept()
37
106
  }
38
107
 
39
108
  export {}
40
- `}},async handleHotUpdate({file:r,modules:g}){const n=/\.jogak\.(tsx?|jsx?)$/.test(r),u=/\.(tsx?|jsx?)$/.test(r)&&!n;if(!n&&!u||d===void 0||!n)return;const i=d.moduleGraph.getModuleById(M),v=x.get(r),j=v!==void 0?O+ie(v):void 0,c=j!==void 0?d.moduleGraph.getModuleById(j):void 0;let m=null,T={},E="";if(p!==void 0){try{m=await p.extractMeta(r)}catch{m=null}try{T=await p.extract(r)}catch{T={}}try{E=await k.readFile(r,"utf8")}catch{E=""}}if(m===null){i!==void 0&&d.moduleGraph.invalidateModule(i),c!==void 0&&d.moduleGraph.invalidateModule(c),d.ws.send({type:"full-reload"});return}const N=D(m),V=C.get(r),J=le(V,N);if(C.set(r,N),!J||v===void 0){i!==void 0&&d.moduleGraph.invalidateModule(i),c!==void 0&&d.moduleGraph.invalidateModule(c),d.ws.send({type:"full-reload"});return}const B={id:v,title:m.title,jogakNames:m.jogakNames,autoArgTypes:T,userArgTypes:m.userArgTypes,source:E,filePath:r,metaExtras:m.metaExtras};c!==void 0&&d.moduleGraph.invalidateModule(c),d.ws.send({type:"custom",event:"jogak:meta-update",data:{id:v,meta:B}});const I=[...g];return c!==void 0&&!I.includes(c)&&I.push(c),I}}}function de(){return`(rawId) => {
109
+ `}},async handleHotUpdate({file:n,modules:m}){const s=/\.jogak\.(tsx?|jsx?)$/.test(n),u=/\.(tsx?|jsx?)$/.test(n)&&!s;if(!s&&!u||d===void 0||!s)return;const i=d.moduleGraph.getModuleById(O),h=S.get(n),j=h!==void 0?L+fe(h):void 0,c=j!==void 0?d.moduleGraph.getModuleById(j):void 0;let p=null,C={},A="";if(y!==void 0){try{p=await y.extractMeta(n)}catch{p=null}try{C=await y.extract(n)}catch{C={}}try{A=await I.readFile(n,"utf8")}catch{A=""}}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=G(p),X=b.get(n),q=we(X,V);if(b.set(n,V),!q||h===void 0){i!==void 0&&d.moduleGraph.invalidateModule(i),c!==void 0&&d.moduleGraph.invalidateModule(c),d.ws.send({type:"full-reload"});return}const z={id:h,title:p.title,jogakNames:p.jogakNames,autoArgTypes:C,userArgTypes:p.userArgTypes,source:A,filePath:n,metaExtras:p.metaExtras};c!==void 0&&d.moduleGraph.invalidateModule(c),d.ws.send({type:"custom",event:"jogak:meta-update",data:{id:h,meta:z}});const x=[...m];return c!==void 0&&!x.includes(c)&&x.push(c),x}}}function _e(){return`(rawId) => {
41
110
  if (typeof Buffer !== 'undefined') return Buffer.from(rawId, 'utf8').toString('base64url')
42
111
  // 브라우저 폴백: btoa는 binary string 기준이라 UTF-8을 한번 인코딩해야 한다.
43
112
  const enc = new TextEncoder().encode(rawId)
44
113
  let bin = ''
45
114
  for (let i = 0; i < enc.length; i++) bin += String.fromCharCode(enc[i])
46
115
  return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')
47
- }`}exports.jogak=ue;
116
+ }`}exports.jogak=je;exports.jogakPreviewFramePlugin=ye;
@@ -1,83 +1,83 @@
1
- import { resolve as y, join as P, dirname as W } from "node:path";
2
- import { existsSync as T, readFileSync as X } from "node:fs";
3
- import { stat as O, rm as Y, readdir as q, readFile as L } from "node:fs/promises";
4
- import { c as R } from "../extractor-client-CReBed7x.js";
5
- const H = [
1
+ import { resolve as y, join as B, dirname as q } from "node:path";
2
+ import { existsSync as S, readFileSync as tt } from "node:fs";
3
+ import { stat as x, rm as et, readdir as rt, readFile as P } from "node:fs/promises";
4
+ import { c as D } from "../extractor-client-CReBed7x.js";
5
+ const nt = [
6
6
  "@jogak/core",
7
7
  "@jogak/react",
8
8
  "@jogak/web-components",
9
9
  "@jogak/next"
10
10
  ];
11
- async function F(t) {
11
+ async function J(t) {
12
12
  try {
13
- const a = await O(t);
14
- if (!a.isDirectory())
15
- return a.mtimeMs;
13
+ const o = await x(t);
14
+ if (!o.isDirectory())
15
+ return o.mtimeMs;
16
16
  } catch {
17
17
  return 0;
18
18
  }
19
- let o = 0, s;
19
+ let e = 0, n;
20
20
  try {
21
- s = await q(t);
21
+ n = await rt(t);
22
22
  } catch {
23
23
  return 0;
24
24
  }
25
- for (const a of s) {
26
- const g = P(t, a);
25
+ for (const o of n) {
26
+ const c = B(t, o);
27
27
  try {
28
- const c = await O(g);
29
- if (c.isDirectory()) {
30
- const p = await F(g);
31
- p > o && (o = p);
32
- } else c.mtimeMs > o && (o = c.mtimeMs);
28
+ const l = await x(c);
29
+ if (l.isDirectory()) {
30
+ const g = await J(c);
31
+ g > e && (e = g);
32
+ } else l.mtimeMs > e && (e = l.mtimeMs);
33
33
  } catch {
34
34
  continue;
35
35
  }
36
36
  }
37
- return o;
37
+ return e;
38
38
  }
39
- async function z(t) {
40
- const o = y(t.root, "node_modules/.vite/deps");
41
- if (!T(o))
39
+ async function ot(t) {
40
+ const e = y(t.root, "node_modules/.vite/deps");
41
+ if (!S(e))
42
42
  return { purged: !1 };
43
- const s = P(o, "_metadata.json");
44
- if (!T(s))
43
+ const n = B(e, "_metadata.json");
44
+ if (!S(n))
45
45
  return { purged: !1 };
46
- let a;
46
+ let o;
47
47
  try {
48
- a = (await O(s)).mtimeMs;
49
- } catch (c) {
48
+ o = (await x(n)).mtimeMs;
49
+ } catch (l) {
50
50
  return t.logger.warn(
51
- `[jogak] cache validation: failed to stat _metadata.json (${c.message})`
51
+ `[jogak] cache validation: failed to stat _metadata.json (${l.message})`
52
52
  ), { purged: !1 };
53
53
  }
54
- const g = t.packages ?? H;
55
- for (const c of g) {
56
- const p = y(t.root, "node_modules", c, "dist");
57
- if (!T(p)) continue;
58
- let h;
54
+ const c = t.packages ?? nt;
55
+ for (const l of c) {
56
+ const g = y(t.root, "node_modules", l, "dist");
57
+ if (!S(g)) continue;
58
+ let v;
59
59
  try {
60
- h = await F(p);
60
+ v = await J(g);
61
61
  } catch (w) {
62
62
  t.logger.warn(
63
- `[jogak] cache validation: failed to walk ${c}/dist (${w.message})`
63
+ `[jogak] cache validation: failed to walk ${l}/dist (${w.message})`
64
64
  );
65
65
  continue;
66
66
  }
67
- if (h > a + 1e3)
67
+ if (v > o + 1e3)
68
68
  try {
69
- return await Y(o, { recursive: !0, force: !0 }), t.logger.info(
70
- `[jogak] vite deps cache invalidated (stale): ${c} dist newer than cache`
71
- ), { purged: !0, reason: c };
69
+ return await et(e, { recursive: !0, force: !0 }), t.logger.info(
70
+ `[jogak] vite deps cache invalidated (stale): ${l} dist newer than cache`
71
+ ), { purged: !0, reason: l };
72
72
  } catch (w) {
73
73
  return t.logger.warn(
74
- `[jogak] cache validation: failed to purge ${o} (${w.message})`
74
+ `[jogak] cache validation: failed to purge ${e} (${w.message})`
75
75
  ), { purged: !1 };
76
76
  }
77
77
  }
78
78
  return { purged: !1 };
79
79
  }
80
- const Q = [
80
+ const at = [
81
81
  "src/index.css",
82
82
  "src/main.css",
83
83
  "src/styles.css",
@@ -87,125 +87,225 @@ const Q = [
87
87
  "src/global.css",
88
88
  "src/app.css"
89
89
  ];
90
- function Z(t) {
91
- for (const o of Q) {
92
- const s = y(t, o);
93
- if (T(s)) return [s];
90
+ function st(t) {
91
+ for (const e of at) {
92
+ const n = y(t, e);
93
+ if (S(n)) return [n];
94
94
  }
95
95
  return [];
96
96
  }
97
- function tt(t, o) {
97
+ function W(t, e) {
98
98
  if (t === void 0 || t === !1) return [];
99
- if (t === !0) return Z(o);
99
+ if (t === !0) return st(e);
100
100
  if (typeof t == "string")
101
- return t.length > 0 ? [y(o, t)] : [];
102
- const s = [];
103
- for (const a of t)
104
- typeof a == "string" && a.length > 0 && s.push(y(o, a));
105
- return s;
101
+ return t.length > 0 ? [y(e, t)] : [];
102
+ const n = [];
103
+ for (const o of t)
104
+ typeof o == "string" && o.length > 0 && n.push(y(e, o));
105
+ return n;
106
106
  }
107
- function et(t) {
107
+ function it(t) {
108
108
  return t.replace(/\/\*[\s\S]*?\*\//g, "").replace(/^\s*\/\/.*$/gm, "");
109
109
  }
110
- function ot(t) {
111
- if (T(t))
110
+ function ct(t) {
111
+ if (S(t))
112
112
  try {
113
- const o = X(t, "utf8");
114
- return JSON.parse(et(o));
113
+ const e = tt(t, "utf8");
114
+ return JSON.parse(it(e));
115
115
  } catch {
116
116
  return;
117
117
  }
118
118
  }
119
- function rt(t, o, s) {
120
- if (!t.endsWith("/*") || !o.endsWith("/*")) return;
121
- const a = t.slice(0, -2), g = y(s, o.slice(0, -2));
122
- return [a, g];
119
+ function lt(t, e, n) {
120
+ if (!t.endsWith("/*") || !e.endsWith("/*")) return;
121
+ const o = t.slice(0, -2), c = y(n, e.slice(0, -2));
122
+ return [o, c];
123
123
  }
124
- function st(t, o) {
125
- var g, c;
126
- const s = {}, a = /* @__PURE__ */ new Set([
124
+ function ut(t, e) {
125
+ var c, l;
126
+ const n = {}, o = /* @__PURE__ */ new Set([
127
127
  t,
128
- y(o, "tsconfig.app.json")
128
+ y(e, "tsconfig.app.json")
129
129
  ]);
130
- for (const p of a) {
131
- const h = ot(p);
132
- if (h === void 0) continue;
133
- const w = (g = h.compilerOptions) == null ? void 0 : g.paths;
130
+ for (const g of o) {
131
+ const v = ct(g);
132
+ if (v === void 0) continue;
133
+ const w = (c = v.compilerOptions) == null ? void 0 : c.paths;
134
134
  if (w === void 0) continue;
135
- const u = ((c = h.compilerOptions) == null ? void 0 : c.baseUrl) ?? ".", m = y(W(p), u);
136
- for (const [_, k] of Object.entries(w)) {
137
- const S = k[0];
138
- if (S === void 0) continue;
139
- const b = rt(_, S, m);
140
- if (b === void 0) continue;
141
- const [A, e] = b;
142
- s[A] === void 0 && (s[A] = e);
135
+ const b = ((l = v.compilerOptions) == null ? void 0 : l.baseUrl) ?? ".", T = y(q(g), b);
136
+ for (const [d, p] of Object.entries(w)) {
137
+ const _ = p[0];
138
+ if (_ === void 0) continue;
139
+ const k = lt(d, _, T);
140
+ if (k === void 0) continue;
141
+ const [E, C] = k;
142
+ n[E] === void 0 && (n[E] = C);
143
143
  }
144
144
  }
145
- return s;
145
+ return n;
146
146
  }
147
- const G = "virtual:jogak", E = "\0" + G, U = "virtual:jogak/entry/", M = "\0" + U, V = "virtual:jogak/global-css", $ = "\0" + V;
148
- function at(t) {
147
+ const K = "virtual:jogak", L = "\0" + K, H = "virtual:jogak/entry/", O = "\0" + H, Y = "virtual:jogak/global-css", U = "\0" + Y, M = "virtual:jogak/preview-entry", $ = "\0" + M, X = "virtual:jogak/preview-global-css", F = "\0" + X;
148
+ function dt(t) {
149
149
  return Buffer.from(t, "utf8").toString("base64url");
150
150
  }
151
- function nt(t) {
151
+ function gt(t) {
152
152
  return Buffer.from(t, "base64url").toString("utf8");
153
153
  }
154
- function D(t) {
154
+ const ft = "/__jogak_preview__/index.html";
155
+ function kt(t) {
156
+ return {
157
+ name: "vite-plugin-jogak-preview-frame",
158
+ enforce: "pre",
159
+ configureServer(e) {
160
+ e.middlewares.use((n, o, c) => {
161
+ if (n.url === void 0 || n.url.split("?")[0] !== ft || n.method !== "GET") return c();
162
+ const g = mt();
163
+ e.transformIndexHtml(n.url, g).then((v) => {
164
+ o.statusCode = 200, o.setHeader("Content-Type", "text/html; charset=utf-8"), o.end(v);
165
+ }).catch(c);
166
+ });
167
+ },
168
+ resolveId(e) {
169
+ if (e === M) return $;
170
+ if (e === X) return F;
171
+ },
172
+ load(e) {
173
+ if (e === F) {
174
+ const n = W(t.globalCss, t.userRoot);
175
+ return n.length === 0 ? `// [jogak] preview-global-css: no candidates
176
+ export {}
177
+ ` : n.map((o) => `import ${JSON.stringify(o)}`).join(`
178
+ `) + `
179
+ export {}
180
+ `;
181
+ }
182
+ if (e === $)
183
+ return pt;
184
+ }
185
+ };
186
+ }
187
+ function mt() {
188
+ return `<!doctype html>
189
+ <html lang="en">
190
+ <head>
191
+ <meta charset="UTF-8" />
192
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
193
+ <title>jogak preview</title>
194
+ <style>
195
+ html, body { margin: 0; padding: 0; }
196
+ #jogak-preview-root { display: block; }
197
+ </style>
198
+ </head>
199
+ <body>
200
+ <div id="jogak-preview-root"></div>
201
+ <script type="module" src="/@id/${M}"><\/script>
202
+ </body>
203
+ </html>
204
+ `;
205
+ }
206
+ const pt = `
207
+ import { reactAdapter } from '@jogak/react'
208
+ import { defaultRegistry } from '@jogak/core'
209
+ import 'virtual:jogak'
210
+ import 'virtual:jogak/preview-global-css'
211
+
212
+ const rootEl = document.getElementById('jogak-preview-root')
213
+ if (rootEl === null) throw new Error('[jogak] #jogak-preview-root not found')
214
+
215
+ let currentContainer = null
216
+ let currentArgs = {}
217
+ let currentEntryId = null
218
+
219
+ async function renderEntry(entryId, args) {
220
+ currentEntryId = entryId
221
+ currentArgs = args
222
+ const entry = await defaultRegistry.requestEntry(entryId)
223
+ if (currentContainer === null) {
224
+ currentContainer = document.createElement('div')
225
+ rootEl.replaceChildren(currentContainer)
226
+ }
227
+ reactAdapter.render(entry, args, currentContainer)
228
+ }
229
+
230
+ function unmount() {
231
+ if (currentContainer !== null) {
232
+ reactAdapter.unmount(currentContainer)
233
+ currentContainer = null
234
+ currentEntryId = null
235
+ }
236
+ }
237
+
238
+ window.addEventListener('message', (event) => {
239
+ const data = event.data
240
+ if (data == null || typeof data !== 'object') return
241
+ if (data.type === 'jogak:setProps') {
242
+ void renderEntry(data.entryId, data.args ?? {}).then(() => {
243
+ window.parent.postMessage({ type: 'jogak:rendered', entryId: data.entryId }, '*')
244
+ }).catch((err) => {
245
+ window.parent.postMessage({ type: 'jogak:error', message: String(err?.message ?? err) }, '*')
246
+ })
247
+ } else if (data.type === 'jogak:unmount') {
248
+ unmount()
249
+ }
250
+ })
251
+
252
+ window.parent.postMessage({ type: 'jogak:ready' }, '*')
253
+ `;
254
+ function G(t) {
155
255
  return {
156
256
  title: t.title,
157
257
  jogakNamesKey: [...t.jogakNames].sort().join("|")
158
258
  };
159
259
  }
160
- function it(t, o) {
161
- return t !== void 0 && t.title === o.title && t.jogakNamesKey === o.jogakNamesKey;
260
+ function yt(t, e) {
261
+ return t !== void 0 && t.title === e.title && t.jogakNamesKey === e.jogakNamesKey;
162
262
  }
163
- function gt(t = {}) {
263
+ function Et(t = {}) {
164
264
  const {
165
- patterns: o = ["src/**/*.jogak.ts", "src/**/*.jogak.tsx"],
166
- codeTheme: s = "vsDark"
167
- } = t, a = t.cwd, g = t.tsConfigFilePath, c = t.disableCacheValidation === !0, p = t.resolveAlias, h = t.globalCss, w = t.previewIsolation ?? "shadow";
168
- let u, m, _;
169
- const k = /* @__PURE__ */ new Map(), S = /* @__PURE__ */ new Map(), b = /* @__PURE__ */ new Map();
170
- async function A() {
171
- const { glob: e } = await import("glob"), d = _ ?? process.cwd(), r = (await e(o, { cwd: d, absolute: !0 })).sort(), l = [];
172
- k.clear(), S.clear();
173
- for (const n of r) {
174
- let v = "";
265
+ patterns: e = ["src/**/*.jogak.ts", "src/**/*.jogak.tsx"],
266
+ codeTheme: n = "vsDark"
267
+ } = t, o = t.cwd, c = t.tsConfigFilePath, l = t.disableCacheValidation === !0, g = t.resolveAlias, v = t.globalCss, w = t.previewIsolation ?? "iframe", b = t.previewFrame === !0, T = t.userViteUrl ?? "";
268
+ let d, p, _;
269
+ const k = /* @__PURE__ */ new Map(), E = /* @__PURE__ */ new Map(), C = /* @__PURE__ */ new Map();
270
+ async function V() {
271
+ const { glob: r } = await import("glob"), f = _ ?? process.cwd(), a = (await r(e, { cwd: f, absolute: !0 })).sort(), u = [];
272
+ k.clear(), E.clear();
273
+ for (const s of a) {
274
+ let h = "";
175
275
  try {
176
- v = await L(n, "utf8");
276
+ h = await P(s, "utf8");
177
277
  } catch {
178
278
  continue;
179
279
  }
180
280
  let j = {}, i = null;
181
- if (m !== void 0) {
281
+ if (p !== void 0) {
182
282
  try {
183
- j = await m.extract(n);
283
+ j = await p.extract(s);
184
284
  } catch {
185
285
  j = {};
186
286
  }
187
287
  try {
188
- i = await m.extractMeta(n);
288
+ i = await p.extractMeta(s);
189
289
  } catch {
190
290
  i = null;
191
291
  }
192
292
  }
193
293
  if (i === null) continue;
194
- const f = i.title;
195
- k.set(f, n), S.set(n, f);
196
- const C = {
197
- id: f,
294
+ const m = i.title;
295
+ k.set(m, s), E.set(s, m);
296
+ const I = {
297
+ id: m,
198
298
  title: i.title,
199
299
  jogakNames: i.jogakNames,
200
300
  autoArgTypes: j,
201
301
  userArgTypes: i.userArgTypes,
202
- source: v,
203
- filePath: n,
302
+ source: h,
303
+ filePath: s,
204
304
  metaExtras: i.metaExtras
205
305
  };
206
- b.set(n, D(C)), l.push({ id: f, filePath: n, meta: C });
306
+ C.set(s, G(I)), u.push({ id: m, filePath: s, meta: I });
207
307
  }
208
- return l;
308
+ return u;
209
309
  }
210
310
  return {
211
311
  name: "vite-plugin-jogak",
@@ -220,77 +320,78 @@ function gt(t = {}) {
220
320
  * 우선순위: `options.resolveAlias` (명시) > tsconfig 자동 추출.
221
321
  */
222
322
  config() {
223
- const e = a ?? process.cwd(), d = g ?? y(e, "tsconfig.json"), r = st(d, e), l = {};
224
- if (p !== void 0)
225
- for (const [v, j] of Object.entries(p))
226
- l[v] = y(e, j);
227
- const n = { ...r, ...l };
228
- if (Object.keys(n).length !== 0)
323
+ const r = o ?? process.cwd(), f = c ?? y(r, "tsconfig.json"), a = ut(f, r), u = {};
324
+ if (g !== void 0)
325
+ for (const [h, j] of Object.entries(g))
326
+ u[h] = y(r, j);
327
+ const s = { ...a, ...u };
328
+ if (Object.keys(s).length !== 0)
229
329
  return {
230
- resolve: { alias: n }
330
+ resolve: { alias: s }
231
331
  };
232
332
  },
233
- async configResolved(e) {
234
- _ = a ?? e.root, e.command === "serve" && !c && await z({
235
- root: e.root,
333
+ async configResolved(r) {
334
+ _ = o ?? r.root, r.command === "serve" && !l && await ot({
335
+ root: r.root,
236
336
  logger: {
237
- info: (r) => e.logger.info(r),
238
- warn: (r) => e.logger.warn(r)
337
+ info: (a) => r.logger.info(a),
338
+ warn: (a) => r.logger.warn(a)
239
339
  }
240
340
  });
241
- const d = g ?? y(_, "tsconfig.json");
242
- m = T(d) ? R({ tsConfigFilePath: d }) : R();
341
+ const f = c ?? y(_, "tsconfig.json");
342
+ p = S(f) ? D({ tsConfigFilePath: f }) : D();
243
343
  },
244
- configureServer(e) {
245
- u = e;
344
+ configureServer(r) {
345
+ d = r;
246
346
  },
247
347
  buildEnd() {
248
- m == null || m.releaseCache();
348
+ p == null || p.releaseCache();
249
349
  },
250
- resolveId(e) {
251
- if (e === G)
252
- return E;
253
- if (e === V)
254
- return $;
255
- if (e.startsWith(U))
256
- return "\0" + e;
350
+ resolveId(r) {
351
+ if (r === K)
352
+ return L;
353
+ if (r === Y)
354
+ return U;
355
+ if (r.startsWith(H))
356
+ return "\0" + r;
257
357
  },
258
- async load(e) {
259
- if (e === $) {
260
- const d = _ ?? process.cwd(), r = tt(h, d);
261
- return r.length === 0 ? `// [jogak] globalCss not configured or no candidates found.
358
+ async load(r) {
359
+ if (r === U) {
360
+ const f = _ ?? process.cwd(), a = W(v, f);
361
+ return a.length === 0 ? `// [jogak] globalCss not configured or no candidates found.
262
362
  export {}
263
- ` : `${r.map((n) => `import ${JSON.stringify(n)}`).join(`
363
+ ` : `${a.map((s) => `import ${JSON.stringify(s)}`).join(`
264
364
  `)}
265
365
  export {}
266
366
  `;
267
367
  }
268
- if (e === E) {
269
- const r = (await A()).map((l) => l.meta);
368
+ if (r === L) {
369
+ const a = (await V()).map((s) => s.meta), u = b ? "" : `
370
+ export const _jogakCodeTheme = ${JSON.stringify(n)}
371
+ export const _jogakPreviewIsolation = ${JSON.stringify(w)}
372
+ export const _jogakUserViteUrl = ${JSON.stringify(T)}
373
+ export const _jogakMetas = _metas
374
+ `;
270
375
  return `import { defaultRegistry } from '@jogak/core'
271
376
 
272
377
  const _entryLoader = (slug) =>
273
378
  import(/* @vite-ignore */ '/@id/__x00__virtual:jogak/entry/' + slug)
274
379
  defaultRegistry.setEntryLoader((id) => {
275
- const slug = ${ct()}
380
+ const slug = ${vt()}
276
381
  return _entryLoader(slug(id))
277
382
  })
278
383
 
279
- const _metas = ${JSON.stringify(r)}
384
+ const _metas = ${JSON.stringify(a)}
280
385
 
281
386
  for (const m of _metas) defaultRegistry.registerMeta(m)
282
-
283
- export const _jogakCodeTheme = ${JSON.stringify(s)}
284
- export const _jogakPreviewIsolation = ${JSON.stringify(w)}
285
- export const _jogakMetas = _metas
286
- `;
387
+ ${u}`;
287
388
  }
288
- if (e.startsWith(M)) {
289
- const d = e.slice(M.length), r = nt(d);
290
- let l = k.get(r);
291
- return l === void 0 && (await A(), l = k.get(r)), l === void 0 ? `// [jogak] unknown entry id: ${JSON.stringify(r)}
389
+ if (r.startsWith(O)) {
390
+ const f = r.slice(O.length), a = gt(f);
391
+ let u = k.get(a);
392
+ return u === void 0 && (await V(), u = k.get(a)), u === void 0 ? `// [jogak] unknown entry id: ${JSON.stringify(a)}
292
393
  export {}
293
- ` : `import * as _user from ${JSON.stringify(l)}
394
+ ` : `import * as _user from ${JSON.stringify(u)}
294
395
  import { defaultRegistry } from '@jogak/core'
295
396
 
296
397
  const _meta = _user.default
@@ -299,7 +400,7 @@ delete _named.default
299
400
  const _jogaks = Object.values(_named).filter(
300
401
  (v) => v !== null && typeof v === 'object' && typeof v.name === 'string'
301
402
  )
302
- defaultRegistry.hydrateEntry(${JSON.stringify(r)}, _jogaks, _meta?.component)
403
+ defaultRegistry.hydrateEntry(${JSON.stringify(a)}, _jogaks, _meta?.component)
303
404
 
304
405
  if (import.meta.hot) {
305
406
  import.meta.hot.accept()
@@ -309,60 +410,60 @@ export {}
309
410
  `;
310
411
  }
311
412
  },
312
- async handleHotUpdate({ file: e, modules: d }) {
313
- const r = /\.jogak\.(tsx?|jsx?)$/.test(e), l = /\.(tsx?|jsx?)$/.test(e) && !r;
314
- if (!r && !l || u === void 0 || !r) return;
315
- const n = u.moduleGraph.getModuleById(
316
- E
317
- ), v = S.get(e), j = v !== void 0 ? M + at(v) : void 0, i = j !== void 0 ? u.moduleGraph.getModuleById(j) : void 0;
318
- let f = null, C = {}, I = "";
319
- if (m !== void 0) {
413
+ async handleHotUpdate({ file: r, modules: f }) {
414
+ const a = /\.jogak\.(tsx?|jsx?)$/.test(r), u = /\.(tsx?|jsx?)$/.test(r) && !a;
415
+ if (!a && !u || d === void 0 || !a) return;
416
+ const s = d.moduleGraph.getModuleById(
417
+ L
418
+ ), h = E.get(r), j = h !== void 0 ? O + dt(h) : void 0, i = j !== void 0 ? d.moduleGraph.getModuleById(j) : void 0;
419
+ let m = null, I = {}, R = "";
420
+ if (p !== void 0) {
320
421
  try {
321
- f = await m.extractMeta(e);
422
+ m = await p.extractMeta(r);
322
423
  } catch {
323
- f = null;
424
+ m = null;
324
425
  }
325
426
  try {
326
- C = await m.extract(e);
427
+ I = await p.extract(r);
327
428
  } catch {
328
- C = {};
429
+ I = {};
329
430
  }
330
431
  try {
331
- I = await L(e, "utf8");
432
+ R = await P(r, "utf8");
332
433
  } catch {
333
- I = "";
434
+ R = "";
334
435
  }
335
436
  }
336
- if (f === null) {
337
- n !== void 0 && u.moduleGraph.invalidateModule(n), i !== void 0 && u.moduleGraph.invalidateModule(i), u.ws.send({ type: "full-reload" });
437
+ if (m === null) {
438
+ s !== void 0 && d.moduleGraph.invalidateModule(s), i !== void 0 && d.moduleGraph.invalidateModule(i), d.ws.send({ type: "full-reload" });
338
439
  return;
339
440
  }
340
- const N = D(f), J = b.get(e), B = it(J, N);
341
- if (b.set(e, N), !B || v === void 0) {
342
- n !== void 0 && u.moduleGraph.invalidateModule(n), i !== void 0 && u.moduleGraph.invalidateModule(i), u.ws.send({ type: "full-reload" });
441
+ const N = G(m), z = C.get(r), Q = yt(z, N);
442
+ if (C.set(r, N), !Q || h === void 0) {
443
+ s !== void 0 && d.moduleGraph.invalidateModule(s), i !== void 0 && d.moduleGraph.invalidateModule(i), d.ws.send({ type: "full-reload" });
343
444
  return;
344
445
  }
345
- const K = {
346
- id: v,
347
- title: f.title,
348
- jogakNames: f.jogakNames,
349
- autoArgTypes: C,
350
- userArgTypes: f.userArgTypes,
351
- source: I,
352
- filePath: e,
353
- metaExtras: f.metaExtras
446
+ const Z = {
447
+ id: h,
448
+ title: m.title,
449
+ jogakNames: m.jogakNames,
450
+ autoArgTypes: I,
451
+ userArgTypes: m.userArgTypes,
452
+ source: R,
453
+ filePath: r,
454
+ metaExtras: m.metaExtras
354
455
  };
355
- i !== void 0 && u.moduleGraph.invalidateModule(i), u.ws.send({
456
+ i !== void 0 && d.moduleGraph.invalidateModule(i), d.ws.send({
356
457
  type: "custom",
357
458
  event: "jogak:meta-update",
358
- data: { id: v, meta: K }
459
+ data: { id: h, meta: Z }
359
460
  });
360
- const x = [...d];
361
- return i !== void 0 && !x.includes(i) && x.push(i), x;
461
+ const A = [...f];
462
+ return i !== void 0 && !A.includes(i) && A.push(i), A;
362
463
  }
363
464
  };
364
465
  }
365
- function ct() {
466
+ function vt() {
366
467
  return `(rawId) => {
367
468
  if (typeof Buffer !== 'undefined') return Buffer.from(rawId, 'utf8').toString('base64url')
368
469
  // 브라우저 폴백: btoa는 binary string 기준이라 UTF-8을 한번 인코딩해야 한다.
@@ -373,5 +474,6 @@ function ct() {
373
474
  }`;
374
475
  }
375
476
  export {
376
- gt as jogak
477
+ Et as jogak,
478
+ kt as jogakPreviewFramePlugin
377
479
  };
@@ -1,3 +1,4 @@
1
1
  import { Plugin } from 'vite';
2
2
  import { JogakPluginOptions } from '../types.js';
3
3
  export declare function jogak(options?: JogakPluginOptions): Plugin;
4
+ export { jogakPreviewFramePlugin, type JogakPreviewFramePluginOptions, } from './preview-frame-plugin.js';
@@ -0,0 +1,21 @@
1
+ import { Plugin } from 'vite';
2
+ import { JogakPluginOptions } from '../types.js';
3
+ /**
4
+ * 알파.8: 사용자 vite scope에서 preview-frame iframe entry를 emit하는 plugin.
5
+ *
6
+ * jogak CLI의 `spawnUserVite`가 사용자 vite.config.ts에 `mergeConfig`로 자동 inject
7
+ * — 사용자가 직접 등록할 필요 없음.
8
+ *
9
+ * 책임:
10
+ * 1. `/__jogak_preview__/index.html` middleware: jogak preview-entry를 로드하는 HTML 응답
11
+ * 2. `virtual:jogak/preview-entry` 가상 모듈: postMessage 리스너 + reactAdapter.render
12
+ * 3. `virtual:jogak/preview-global-css` 가상 모듈: 사용자 globalCss를 사용자 vite의
13
+ * `@tailwindcss/vite` 등 정상 css 파이프라인을 통해 컴파일
14
+ */
15
+ export interface JogakPreviewFramePluginOptions {
16
+ /** 사용자 프로젝트 root (cwd). globalCss 자동 탐지 base. */
17
+ readonly userRoot: string;
18
+ /** 사용자 globalCss (jogak.config.ts의 globalCss와 동일 의미). */
19
+ readonly globalCss?: JogakPluginOptions['globalCss'];
20
+ }
21
+ export declare function jogakPreviewFramePlugin(options: JogakPreviewFramePluginOptions): Plugin;
@@ -18,5 +18,19 @@ export declare const RESOLVED_VIRTUAL_ENTRY_PREFIX: string;
18
18
  /** Global css 모듈 — 사용자 globalCss를 import한다 (알파.6, opt-in). */
19
19
  export declare const VIRTUAL_GLOBAL_CSS_ID = "virtual:jogak/global-css";
20
20
  export declare const RESOLVED_VIRTUAL_GLOBAL_CSS_ID: string;
21
+ /**
22
+ * Preview frame entry (알파.8) — 사용자 vite scope의 iframe entry.
23
+ * jogakPreviewFramePlugin이 emit. 사용자 vite의 module graph에 포함되어
24
+ * 사용자 plugins(@tailwindcss/vite 등)가 정상 처리.
25
+ */
26
+ export declare const VIRTUAL_PREVIEW_ENTRY_ID = "virtual:jogak/preview-entry";
27
+ export declare const RESOLVED_VIRTUAL_PREVIEW_ENTRY_ID: string;
28
+ /**
29
+ * Preview-scope의 사용자 globalCss (알파.8) — 알파.6의 `virtual:jogak/global-css`는
30
+ * jogak SPA scope. 본 모듈은 사용자 vite scope의 preview-frame entry용.
31
+ * jogakPreviewFramePlugin이 emit.
32
+ */
33
+ export declare const VIRTUAL_PREVIEW_GLOBAL_CSS_ID = "virtual:jogak/preview-global-css";
34
+ export declare const RESOLVED_VIRTUAL_PREVIEW_GLOBAL_CSS_ID: string;
21
35
  export declare function idToSlug(id: string): string;
22
36
  export declare function slugToId(slug: string): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jogak/core",
3
- "version": "0.1.0-alpha.7.1",
3
+ "version": "0.1.0-alpha.8",
4
4
  "description": "Core types, registry, and Vite plugin for Jogak — a lightweight Storybook alternative.",
5
5
  "keywords": [
6
6
  "jogak",