@jogak/core 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,305 @@
1
+ import { Node as n, Project as m } from "ts-morph";
2
+ function y(t = {}) {
3
+ function e() {
4
+ return t.tsConfigFilePath !== void 0 ? new m({ tsConfigFilePath: t.tsConfigFilePath }) : new m({ skipAddingFilesFromTsConfig: !0 });
5
+ }
6
+ let r;
7
+ function i() {
8
+ return r === void 0 && (r = e()), r;
9
+ }
10
+ function o(s) {
11
+ const a = i();
12
+ let c = a.getSourceFile(s);
13
+ return c === void 0 ? c = a.addSourceFileAtPathIfExists(s) : c.refreshFromFileSystemSync(), c;
14
+ }
15
+ return {
16
+ extract(s) {
17
+ const a = o(s);
18
+ return a === void 0 ? {} : j(a);
19
+ },
20
+ extractMeta(s) {
21
+ const a = o(s);
22
+ if (a !== void 0)
23
+ return O(a);
24
+ },
25
+ dispose() {
26
+ r = void 0;
27
+ }
28
+ };
29
+ }
30
+ function j(t) {
31
+ const e = t.getDefaultExportSymbol();
32
+ if (e === void 0) return {};
33
+ const r = e.getAliasedSymbol() ?? e, i = r.getDeclarations()[0] ?? e.getDeclarations()[0];
34
+ if (i === void 0) return {};
35
+ const s = r.getTypeAtLocation(i).getProperty("component");
36
+ if (s === void 0) return {};
37
+ const a = s.getTypeAtLocation(i), c = N(a, i);
38
+ if (c === void 0) return {};
39
+ const p = {};
40
+ for (const v of c.getProperties()) {
41
+ const S = v.getName(), T = v.getTypeAtLocation(i), u = E(T);
42
+ if (u === null) continue;
43
+ const A = !D(v), h = {
44
+ type: u.type,
45
+ required: A,
46
+ ...u.control !== void 0 ? { control: u.control } : {},
47
+ ...u.options !== void 0 ? { options: u.options } : {},
48
+ ...u.action === !0 ? { action: !0 } : {}
49
+ };
50
+ p[S] = h;
51
+ }
52
+ return p;
53
+ }
54
+ function N(t, e) {
55
+ const i = t.getCallSignatures()[0];
56
+ if (i === void 0) return;
57
+ const s = i.getParameters()[0];
58
+ if (s === void 0) return;
59
+ const a = s.getValueDeclaration() ?? e;
60
+ return s.getTypeAtLocation(a);
61
+ }
62
+ function D(t) {
63
+ for (const e of t.getDeclarations())
64
+ if ((n.isPropertySignature(e) || n.isPropertyDeclaration(e) || n.isParameterDeclaration(e)) && e.hasQuestionToken())
65
+ return !0;
66
+ return !1;
67
+ }
68
+ function E(t) {
69
+ if (t.isUnion()) {
70
+ const e = t.getUnionTypes().filter((r) => !r.isUndefined() && !r.isNull());
71
+ if (e.length === 0) return null;
72
+ if (e.length === 1) {
73
+ const r = e[0];
74
+ return r !== void 0 ? E(r) : null;
75
+ }
76
+ return e.every((r) => r.isStringLiteral()) ? {
77
+ type: "enum",
78
+ control: "select",
79
+ options: e.map((r) => F(r))
80
+ } : e.length === 2 && e.every((r) => r.isBooleanLiteral()) ? { type: "boolean", control: "boolean" } : null;
81
+ }
82
+ return t.isString() || t.isStringLiteral() ? { type: "string", control: "text" } : t.isNumber() || t.isNumberLiteral() ? { type: "number", control: "number" } : t.isBoolean() ? { type: "boolean", control: "boolean" } : t.getCallSignatures().length > 0 ? { type: "function", action: !0 } : null;
83
+ }
84
+ function F(t) {
85
+ const e = t.getLiteralValue();
86
+ return typeof e == "string" ? e : t.getText().replace(/^["']|["']$/g, "");
87
+ }
88
+ function O(t) {
89
+ const e = I(t);
90
+ if (e === void 0) return;
91
+ const r = P(e, "title");
92
+ if (r === void 0) return;
93
+ const i = k(e), o = z(e, "tags"), s = V(e, "parameters"), a = w(t), c = {
94
+ ...o !== void 0 ? { tags: o } : {},
95
+ ...s !== void 0 ? { parameters: s } : {}
96
+ };
97
+ return {
98
+ title: r,
99
+ jogakNames: a,
100
+ userArgTypes: i,
101
+ metaExtras: c
102
+ };
103
+ }
104
+ function I(t) {
105
+ for (const r of t.getExportAssignments()) {
106
+ if (r.isExportEquals()) continue;
107
+ const i = r.getExpression();
108
+ if (n.isObjectLiteralExpression(i)) return i;
109
+ if (n.isAsExpression(i)) {
110
+ const o = i.getExpression();
111
+ if (n.isObjectLiteralExpression(o)) return o;
112
+ }
113
+ if (n.isSatisfiesExpression(i)) {
114
+ const o = i.getExpression();
115
+ if (n.isObjectLiteralExpression(o)) return o;
116
+ }
117
+ if (n.isIdentifier(i)) {
118
+ const o = i.getSymbol();
119
+ if (o !== void 0) {
120
+ for (const s of o.getDeclarations())
121
+ if (n.isVariableDeclaration(s)) {
122
+ const a = s.getInitializer();
123
+ if (a !== void 0) {
124
+ const c = f(a);
125
+ if (c !== void 0) return c;
126
+ }
127
+ }
128
+ }
129
+ }
130
+ }
131
+ const e = t.getDefaultExportSymbol();
132
+ if (e !== void 0) {
133
+ const r = e.getAliasedSymbol() ?? e;
134
+ for (const i of r.getDeclarations())
135
+ if (n.isVariableDeclaration(i)) {
136
+ const o = i.getInitializer();
137
+ if (o !== void 0) {
138
+ const s = f(o);
139
+ if (s !== void 0) return s;
140
+ }
141
+ }
142
+ }
143
+ }
144
+ function f(t) {
145
+ if (n.isObjectLiteralExpression(t)) return t;
146
+ if (n.isAsExpression(t) || n.isSatisfiesExpression(t) || n.isParenthesizedExpression(t))
147
+ return f(t.getExpression());
148
+ }
149
+ function P(t, e) {
150
+ const r = t.getProperty(e);
151
+ if (r === void 0 || !n.isPropertyAssignment(r)) return;
152
+ const i = r.getInitializer();
153
+ if (i !== void 0 && (n.isStringLiteral(i) || n.isNoSubstitutionTemplateLiteral(i)))
154
+ return i.getLiteralText();
155
+ }
156
+ function z(t, e) {
157
+ const r = t.getProperty(e);
158
+ if (r === void 0 || !n.isPropertyAssignment(r)) return;
159
+ const i = r.getInitializer();
160
+ if (i === void 0 || !n.isArrayLiteralExpression(i)) return;
161
+ const o = [];
162
+ for (const s of i.getElements())
163
+ (n.isStringLiteral(s) || n.isNoSubstitutionTemplateLiteral(s)) && o.push(s.getLiteralText());
164
+ return o;
165
+ }
166
+ function k(t) {
167
+ const e = t.getProperty("argTypes");
168
+ if (e === void 0 || !n.isPropertyAssignment(e)) return {};
169
+ const r = e.getInitializer();
170
+ if (r === void 0 || !n.isObjectLiteralExpression(r)) return {};
171
+ const i = {};
172
+ for (const o of r.getProperties()) {
173
+ if (!n.isPropertyAssignment(o)) continue;
174
+ const s = d(o);
175
+ if (s === void 0) continue;
176
+ const a = o.getInitializer();
177
+ if (a === void 0 || !n.isObjectLiteralExpression(a)) continue;
178
+ const c = C(a);
179
+ c !== void 0 && (i[s] = c);
180
+ }
181
+ return i;
182
+ }
183
+ function d(t) {
184
+ const e = t.getNameNode();
185
+ if (n.isIdentifier(e)) return e.getText();
186
+ if (n.isStringLiteral(e) || n.isNoSubstitutionTemplateLiteral(e))
187
+ return e.getLiteralText();
188
+ }
189
+ function C(t) {
190
+ const e = {};
191
+ for (const r of t.getProperties()) {
192
+ if (!n.isPropertyAssignment(r)) continue;
193
+ const i = d(r);
194
+ if (i === void 0) continue;
195
+ const o = r.getInitializer();
196
+ if (o === void 0) continue;
197
+ const s = l(o);
198
+ s !== void 0 && (e[i] = s);
199
+ }
200
+ return e;
201
+ }
202
+ function V(t, e) {
203
+ const r = t.getProperty(e);
204
+ if (r === void 0 || !n.isPropertyAssignment(r)) return;
205
+ const i = r.getInitializer();
206
+ if (i === void 0 || !n.isObjectLiteralExpression(i)) return;
207
+ const o = {};
208
+ for (const s of i.getProperties()) {
209
+ if (!n.isPropertyAssignment(s)) continue;
210
+ const a = d(s);
211
+ if (a === void 0) continue;
212
+ const c = s.getInitializer();
213
+ if (c === void 0) continue;
214
+ const p = l(c);
215
+ p !== void 0 && (o[a] = p);
216
+ }
217
+ return o;
218
+ }
219
+ function l(t) {
220
+ if (n.isStringLiteral(t) || n.isNoSubstitutionTemplateLiteral(t))
221
+ return t.getLiteralText();
222
+ if (n.isNumericLiteral(t))
223
+ return Number(t.getText());
224
+ if (n.isTrueLiteral(t)) return !0;
225
+ if (n.isFalseLiteral(t)) return !1;
226
+ if (n.isNullLiteral(t)) return null;
227
+ if (n.isPrefixUnaryExpression(t)) {
228
+ const e = t.getOperand();
229
+ if (n.isNumericLiteral(e)) {
230
+ const r = t.getOperatorToken(), i = Number(e.getText());
231
+ return r === 41 ? -i : i;
232
+ }
233
+ return;
234
+ }
235
+ if (n.isArrayLiteralExpression(t)) {
236
+ const e = [];
237
+ for (const r of t.getElements()) {
238
+ const i = l(r);
239
+ if (i === void 0) return;
240
+ e.push(i);
241
+ }
242
+ return e;
243
+ }
244
+ if (n.isObjectLiteralExpression(t)) {
245
+ const e = {};
246
+ for (const r of t.getProperties()) {
247
+ if (!n.isPropertyAssignment(r)) return;
248
+ const i = d(r);
249
+ if (i === void 0) return;
250
+ const o = r.getInitializer();
251
+ if (o === void 0) return;
252
+ const s = l(o);
253
+ if (s === void 0) return;
254
+ e[i] = s;
255
+ }
256
+ return e;
257
+ }
258
+ if (n.isAsExpression(t) || n.isSatisfiesExpression(t) || n.isParenthesizedExpression(t))
259
+ return l(t.getExpression());
260
+ }
261
+ function w(t) {
262
+ const e = [];
263
+ for (const r of t.getVariableStatements())
264
+ if (r.isExported() && !r.hasDefaultKeyword())
265
+ for (const i of r.getDeclarations()) {
266
+ const o = i.getInitializer();
267
+ if (o === void 0) continue;
268
+ const s = f(o);
269
+ if (s === void 0) continue;
270
+ const a = P(s, "name");
271
+ a !== void 0 && e.push(a);
272
+ }
273
+ return e;
274
+ }
275
+ const x = process.argv[2] ?? "", L = x.length > 0 ? x : void 0, b = L !== void 0 ? y({ tsConfigFilePath: L }) : y();
276
+ function g(t) {
277
+ typeof process.send == "function" && process.send(t);
278
+ }
279
+ process.on("message", (t) => {
280
+ if (t.type === "extract") {
281
+ const { id: e, filePath: r } = t;
282
+ try {
283
+ const i = b.extract(r);
284
+ g({ type: "result", id: e, argTypes: i });
285
+ } catch (i) {
286
+ const o = i instanceof Error ? i.message : String(i);
287
+ g({ type: "error", id: e, message: o });
288
+ }
289
+ return;
290
+ }
291
+ if (t.type === "extractMeta") {
292
+ const { id: e, filePath: r } = t;
293
+ try {
294
+ const i = b.extractMeta(r);
295
+ g({ type: "metaResult", id: e, meta: i ?? null });
296
+ } catch (i) {
297
+ const o = i instanceof Error ? i.message : String(i);
298
+ g({ type: "error", id: e, message: o });
299
+ }
300
+ return;
301
+ }
302
+ });
303
+ process.on("disconnect", () => {
304
+ process.exit(0);
305
+ });
@@ -0,0 +1,2 @@
1
+ import { PropsExtractor, PropsExtractorOptions } from './extract-props.js';
2
+ export declare function createPropsExtractor(options?: PropsExtractorOptions): PropsExtractor;
@@ -0,0 +1,86 @@
1
+ import { CategoryTree, CategoryMetaTree, Jogak, RegistryEntry, RegistryEntryMeta } from './types.js';
2
+ /** 미등록 entry 요청 시 throw. */
3
+ export declare class UnknownEntryError extends Error {
4
+ readonly id: string;
5
+ constructor(id: string);
6
+ }
7
+ export declare class ComponentRegistry {
8
+ #private;
9
+ /**
10
+ * 즉시 hydrated 상태로 entry를 등록한다.
11
+ * 정적 빌드(`generateRegistryFile` 결과) / 테스트 / 기존 호출자 호환 경로.
12
+ *
13
+ * 내부 구현은 `registerMeta` + `hydrateEntry`의 compatibility shim.
14
+ * 두 mutation은 batch로 묶여 단일 notify만 발생한다.
15
+ */
16
+ register(entry: RegistryEntry): void;
17
+ unregister(id: string): void;
18
+ /** hydrated일 때만 RegistryEntry를 반환한다. meta-only/pending이면 undefined. */
19
+ get(id: string): RegistryEntry | undefined;
20
+ /** hydrated 항목만 반환. meta-only는 `getAllMeta()` 사용. */
21
+ getAll(): readonly RegistryEntry[];
22
+ search(query: string): readonly RegistryEntry[];
23
+ /**
24
+ * title의 '/' 구분자로 hydrated entry만의 계층 트리를 구성한다.
25
+ */
26
+ getTree(): CategoryTree;
27
+ clear(): void;
28
+ /** hydrated 개수. */
29
+ get size(): number;
30
+ /**
31
+ * 인덱스 가상모듈이 호출. 기존 hydrated 항목이 있으면 meta만 갱신(HMR) — entry는 보존.
32
+ * meta 등록만으로는 `getAll()` 결과에 안 들어간다.
33
+ */
34
+ registerMeta(meta: RegistryEntryMeta): void;
35
+ /**
36
+ * entry 가상모듈이 호출. pending Promise들을 resolve.
37
+ * meta가 없는 상태에서 호출되면(=직접 import) 임시 meta를 합성한다 (defensive).
38
+ */
39
+ hydrateEntry(id: string, jogaks: readonly Jogak[], component: unknown): void;
40
+ /**
41
+ * 어댑터가 호출. hydrated였던 entry를 meta 상태로 되돌린다.
42
+ * HMR meta-update 이벤트에서 args/component 변경을 강제 re-hydrate 시키기 위함.
43
+ * unknown / meta / pending 상태에는 영향 없음.
44
+ */
45
+ invalidateEntry(id: string): void;
46
+ /**
47
+ * UI/어댑터가 호출. entry id로 완전한 RegistryEntry를 비동기 획득.
48
+ *
49
+ * - hydrated → 즉시 resolve된 Promise
50
+ * - pending → 기존 Promise 반환 (멱등)
51
+ * - meta → loader 트리거 후 새 Promise 반환, 상태를 pending으로
52
+ * - unknown → 즉시 reject (UnknownEntryError)
53
+ */
54
+ requestEntry(id: string): Promise<RegistryEntry>;
55
+ /**
56
+ * 사이드바 메타 전용 — meta-only / pending / hydrated 모두 포함.
57
+ *
58
+ * F2: 내부 캐시. mutation 시점에 invalidate되며 재계산 전까지 동일 reference를 반환한다.
59
+ * useSyncExternalStore의 referential identity 요구를 만족시키기 위함.
60
+ */
61
+ getAllMeta(): readonly RegistryEntryMeta[];
62
+ /**
63
+ * 사이드바 트리 전용 — 모든 상태의 meta를 트리화.
64
+ * F2: getAllMeta와 동일한 캐시 정책.
65
+ */
66
+ getMetaTree(): CategoryMetaTree;
67
+ /** 진단 — 상태 머신 노출. */
68
+ getEntryState(id: string): 'unknown' | 'meta' | 'pending' | 'hydrated';
69
+ /**
70
+ * dynamic import 함수를 외부에서 주입. plugin이 인덱스 모듈에서 호출한다.
71
+ * 빌드/SSR에서 정적 import 매핑으로 교체할 때도 사용.
72
+ */
73
+ setEntryLoader(loader: (id: string) => Promise<unknown>): void;
74
+ /**
75
+ * 등록/해제/메타 갱신/hydrate/clear 시 호출되는 listener를 등록한다.
76
+ *
77
+ * - listener는 무인자, 동기 호출. 어떤 변화가 있었는지는 listener가
78
+ * `getAllMeta()`/`getMetaTree()`로 직접 확인.
79
+ * - mutation 도중 listener에서 `subscribe`/`unsubscribe`를 호출하는 재진입은
80
+ * 안전 (내부적으로 listener Set을 한 번 복사한 뒤 순회).
81
+ * - listener 예외는 catch + console.error 후 계속 (다른 listener 보장).
82
+ * - 반환값은 unsubscribe 함수 (멱등 — 두 번 호출해도 안전).
83
+ */
84
+ subscribe(listener: () => void): () => void;
85
+ }
86
+ export declare const defaultRegistry: ComponentRegistry;
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Props 메타데이터 — TypeScript AST에서 빌드 타임에 추출되거나 수동으로 정의된다.
3
+ */
4
+ export interface ArgType {
5
+ readonly type?: string;
6
+ readonly description?: string;
7
+ readonly defaultValue?: unknown;
8
+ readonly control?: 'text' | 'number' | 'boolean' | 'select' | 'radio' | 'color' | 'range';
9
+ readonly options?: readonly unknown[];
10
+ readonly required?: boolean;
11
+ /**
12
+ * 함수 prop을 자동으로 Action으로 처리한다.
13
+ * - `true`: 어댑터가 prop 이름으로 spy를 주입
14
+ * - `string`: 지정한 이름으로 spy를 주입
15
+ */
16
+ readonly action?: boolean | string;
17
+ }
18
+ /**
19
+ * *.jogak.(ts|tsx) 파일의 default export — 컴포넌트 수준 메타데이터
20
+ */
21
+ export interface JogakMeta {
22
+ readonly title: string;
23
+ readonly component?: unknown;
24
+ readonly argTypes?: Readonly<Record<string, ArgType>>;
25
+ readonly tags?: readonly string[];
26
+ readonly parameters?: Readonly<Record<string, unknown>>;
27
+ }
28
+ /**
29
+ * *.jogak.(ts|tsx) 파일의 named export — 컴포넌트의 개별 사용 예시
30
+ */
31
+ export interface Jogak {
32
+ readonly name: string;
33
+ readonly args?: Readonly<Record<string, unknown>>;
34
+ readonly argTypes?: Readonly<Record<string, ArgType>>;
35
+ readonly parameters?: Readonly<Record<string, unknown>>;
36
+ }
37
+ /**
38
+ * 컴포넌트 레지스트리에 등록된 엔트리
39
+ */
40
+ export interface RegistryEntry {
41
+ readonly id: string;
42
+ readonly title: string;
43
+ readonly jogaks: readonly Jogak[];
44
+ readonly meta: JogakMeta;
45
+ readonly filePath?: string;
46
+ readonly source?: string;
47
+ }
48
+ /**
49
+ * 인덱스 가상모듈에 들어가는 "껍데기" 메타.
50
+ *
51
+ * - user 컴포넌트(`meta.component`)와 jogak 객체(`render`/`args`/...)를 포함하지 않는다.
52
+ * - 사이드바 렌더링·검색·트리 구성에 필요한 최소 정보만 담는다.
53
+ * - hydrate 시점에 이 meta로부터 완전한 RegistryEntry를 합성한다.
54
+ */
55
+ export interface RegistryEntryMeta {
56
+ readonly id: string;
57
+ readonly title: string;
58
+ /** 이 entry의 jogak 객체 이름 목록 — 사이드바에서 sub-item 표시용. */
59
+ readonly jogakNames: readonly string[];
60
+ /** ts-morph로 추출한 자동 argTypes (소스 변경 없으면 정적). */
61
+ readonly autoArgTypes: Readonly<Record<string, ArgType>>;
62
+ /** *.jogak 파일에서 사용자가 직접 작성한 argTypes (meta.argTypes). */
63
+ readonly userArgTypes: Readonly<Record<string, ArgType>>;
64
+ /** *.jogak 파일의 원본 텍스트 — Source 패널이 즉시 표시 가능해야 하므로 인덱스에 포함. */
65
+ readonly source: string;
66
+ /** 절대경로 — HMR/디버깅용. */
67
+ readonly filePath: string;
68
+ /**
69
+ * 사용자 meta의 직렬화 가능한 잔여 필드만 보관 (tags, parameters).
70
+ * `component`는 함수라 직렬화 불가 → 제외. `argTypes`는 위에서 분리.
71
+ */
72
+ readonly metaExtras: {
73
+ readonly tags?: readonly string[];
74
+ readonly parameters?: Readonly<Record<string, unknown>>;
75
+ };
76
+ }
77
+ /**
78
+ * 사이드바 렌더링용 카테고리 트리.
79
+ * title의 '/' 구분자로 계층 구성 (예: "Form/Button" → { Form: { Button: RegistryEntry } })
80
+ *
81
+ * interface로 선언해야 한다 — type alias는 자기 참조 순환 타입을 지원하지 않는다.
82
+ */
83
+ export interface CategoryTree {
84
+ [key: string]: RegistryEntry | CategoryTree;
85
+ }
86
+ /**
87
+ * Sidebar용 메타 트리 — `RegistryEntry` 자리에 `RegistryEntryMeta`.
88
+ * lazy 모드에서 hydrate 전에도 사이드바를 그릴 수 있도록 별도 트리 구조.
89
+ */
90
+ export interface CategoryMetaTree {
91
+ [key: string]: RegistryEntryMeta | CategoryMetaTree;
92
+ }
93
+ /**
94
+ * 각 어댑터(React, Next.js, Web Components)가 구현해야 하는 공통 인터페이스
95
+ */
96
+ export interface JogakAdapter {
97
+ readonly framework: 'react' | 'next' | 'web-components';
98
+ render(entry: RegistryEntry, args: Readonly<Record<string, unknown>>, container: HTMLElement): void | Promise<void>;
99
+ unmount(container: HTMLElement): void;
100
+ }
101
+ /**
102
+ * Vite 플러그인 설정
103
+ */
104
+ export interface JogakPluginOptions {
105
+ readonly patterns?: readonly string[];
106
+ readonly framework?: 'react' | 'next' | 'web-components';
107
+ /**
108
+ * prism-react-renderer 테마 이름.
109
+ * 사용 가능한 값: dracula | duotoneDark | duotoneLight | github | jettwaveDark |
110
+ * jettwaveLight | nightOwl | nightOwlLight | oceanicNext | okaidia | oneDark |
111
+ * oneLight | palenight | shadesOfPurple | solarizedlight | synthwave84 |
112
+ * ultramin | vsDark (기본값) | vsLight
113
+ */
114
+ readonly codeTheme?: string;
115
+ /**
116
+ * glob의 cwd. 미지정 시 Vite `config.root`를 사용한다.
117
+ *
118
+ * `runHost` 같은 외부 호스트가 Vite root를 다른 패키지 디렉토리로 잡고
119
+ * 사용자 프로젝트의 jogak 파일을 가상 모듈로 주입하고 싶을 때 사용.
120
+ * 미지정 시 기존 동작과 동일하다 (back-compat).
121
+ */
122
+ readonly cwd?: string;
123
+ /**
124
+ * ts-morph용 tsconfig 절대 경로.
125
+ * 미지정 시 `<resolvedCwd>/tsconfig.json`을 자동 감지하며,
126
+ * 파일이 없으면 tsconfig 없이 PropsExtractor를 생성한다.
127
+ */
128
+ readonly tsConfigFilePath?: string;
129
+ /**
130
+ * F1: dev 부팅 시 jogak 의존 패키지(`@jogak/core`, `@jogak/react`, ...)의
131
+ * dist 변경을 감지해 Vite optimizeDeps cache(`node_modules/.vite/deps`)를
132
+ * 자동 삭제한다. 기본값 false (검증 활성화).
133
+ *
134
+ * workspace로 링크된 어댑터를 자주 빌드하는 환경에서는 그대로 두면
135
+ * `SyntaxError: ... does not provide an export named ...` 류의
136
+ * stale prebundle 에러를 자동으로 회피한다.
137
+ *
138
+ * true로 설정하면 검증을 끈다 — 사용자가 직접 `rm -rf node_modules/.vite/deps`
139
+ * 또는 `--force`를 관리해야 한다.
140
+ */
141
+ readonly disableCacheValidation?: boolean;
142
+ }
@@ -0,0 +1,17 @@
1
+ export interface CacheValidateOptions {
2
+ readonly root: string;
3
+ readonly logger: {
4
+ info(msg: string): void;
5
+ warn(msg: string): void;
6
+ };
7
+ /**
8
+ * 검사 대상 패키지 (override). 기본: jogak workspace 패키지 4종.
9
+ * 테스트에서 대체 경로를 주입하기 위해 노출.
10
+ */
11
+ readonly packages?: readonly string[];
12
+ }
13
+ export interface CacheValidateResult {
14
+ readonly purged: boolean;
15
+ readonly reason?: string;
16
+ }
17
+ export declare function validateAndPurgeViteCache(opts: CacheValidateOptions): Promise<CacheValidateResult>;
@@ -0,0 +1,41 @@
1
+ "use strict";var V=Object.create;var N=Object.defineProperty;var G=Object.getOwnPropertyDescriptor;var U=Object.getOwnPropertyNames;var B=Object.getPrototypeOf,J=Object.prototype.hasOwnProperty;var q=(e,a,i,u)=>{if(a&&typeof a=="object"||typeof a=="function")for(let d of U(a))!J.call(e,d)&&d!==i&&N(e,d,{get:()=>a[d],enumerable:!(u=G(a,d))||u.enumerable});return e};var K=(e,a,i)=>(i=e!=null?V(B(e)):{},q(a||!e||!e.__esModule?N(i,"default",{value:e,enumerable:!0}):i,e));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const j=require("node:path"),_=require("node:fs"),v=require("node:fs/promises"),R=require("../extractor-client-CiWszHel.cjs"),X=["@jogak/core","@jogak/react","@jogak/web-components","@jogak/next"];async function P(e){try{const u=await v.stat(e);if(!u.isDirectory())return u.mtimeMs}catch{return 0}let a=0,i;try{i=await v.readdir(e)}catch{return 0}for(const u of i){const d=j.join(e,u);try{const g=await v.stat(d);if(g.isDirectory()){const r=await P(d);r>a&&(a=r)}else g.mtimeMs>a&&(a=g.mtimeMs)}catch{continue}}return a}async function W(e){const a=j.resolve(e.root,"node_modules/.vite/deps");if(!_.existsSync(a))return{purged:!1};const i=j.join(a,"_metadata.json");if(!_.existsSync(i))return{purged:!1};let u;try{u=(await v.stat(i)).mtimeMs}catch(g){return e.logger.warn(`[jogak] cache validation: failed to stat _metadata.json (${g.message})`),{purged:!1}}const d=e.packages??X;for(const g of d){const r=j.resolve(e.root,"node_modules",g,"dist");if(!_.existsSync(r))continue;let s;try{s=await P(r)}catch(p){e.logger.warn(`[jogak] cache validation: failed to walk ${g}/dist (${p.message})`);continue}if(s>u+1e3)try{return await v.rm(a,{recursive:!0,force:!0}),e.logger.info(`[jogak] vite deps cache invalidated (stale): ${g} dist newer than cache`),{purged:!0,reason:g}}catch(p){return e.logger.warn(`[jogak] cache validation: failed to purge ${a} (${p.message})`),{purged:!1}}}return{purged:!1}}const $="virtual:jogak",T="\0"+$,F="virtual:jogak/entry/",I="\0"+F;function Y(e){return Buffer.from(e,"utf8").toString("base64url")}function H(e){return Buffer.from(e,"base64url").toString("utf8")}function A(e){return{title:e.title,jogakNamesKey:[...e.jogakNames].sort().join("|")}}function z(e,a){return e!==void 0&&e.title===a.title&&e.jogakNamesKey===a.jogakNamesKey}function Q(e={}){const{patterns:a=["src/**/*.jogak.ts","src/**/*.jogak.tsx"],codeTheme:i="vsDark"}=e,u=e.cwd,d=e.tsConfigFilePath,g=e.disableCacheValidation===!0;let r,s,p;const k=new Map,x=new Map,M=new Map;async function b(){const{glob:t}=await import("glob"),m=p??process.cwd(),n=await t(a,{cwd:m,absolute:!0}),f=[];k.clear(),x.clear();for(const c of n){let y="";try{y=await v.readFile(c,"utf8")}catch{continue}let h={},o=null;if(s!==void 0){try{h=await s.extract(c)}catch{h={}}try{o=await s.extractMeta(c)}catch{o=null}}if(o===null)continue;const l=o.title;k.set(l,c),x.set(c,l);const w={id:l,title:o.title,jogakNames:o.jogakNames,autoArgTypes:h,userArgTypes:o.userArgTypes,source:y,filePath:c,metaExtras:o.metaExtras};M.set(c,A(w)),f.push({id:l,filePath:c,meta:w})}return f}return{name:"vite-plugin-jogak",async configResolved(t){p=u??t.root,t.command==="serve"&&!g&&await W({root:t.root,logger:{info:n=>t.logger.info(n),warn:n=>t.logger.warn(n)}});const m=d??j.resolve(p,"tsconfig.json");s=_.existsSync(m)?R.createPropsExtractor({tsConfigFilePath:m}):R.createPropsExtractor()},configureServer(t){r=t},buildEnd(){s==null||s.releaseCache()},resolveId(t){if(t===$)return T;if(t.startsWith(F))return"\0"+t},async load(t){if(t===T){const n=(await b()).map(f=>f.meta);return`import { defaultRegistry } from '@jogak/core'
2
+
3
+ const _entryLoader = (slug) =>
4
+ import(/* @vite-ignore */ '/@id/__x00__virtual:jogak/entry/' + slug)
5
+ defaultRegistry.setEntryLoader((id) => {
6
+ const slug = ${Z()}
7
+ return _entryLoader(slug(id))
8
+ })
9
+
10
+ const _metas = ${JSON.stringify(n)}
11
+
12
+ for (const m of _metas) defaultRegistry.registerMeta(m)
13
+
14
+ export const _jogakCodeTheme = ${JSON.stringify(i)}
15
+ export const _jogakMetas = _metas
16
+ `}if(t.startsWith(I)){const m=t.slice(I.length),n=H(m);let f=k.get(n);return f===void 0&&(await b(),f=k.get(n)),f===void 0?`// [jogak] unknown entry id: ${JSON.stringify(n)}
17
+ export {}
18
+ `:`import * as _user from ${JSON.stringify(f)}
19
+ import { defaultRegistry } from '@jogak/core'
20
+
21
+ const _meta = _user.default
22
+ const _named = { ..._user }
23
+ delete _named.default
24
+ const _jogaks = Object.values(_named).filter(
25
+ (v) => v !== null && typeof v === 'object' && typeof v.name === 'string'
26
+ )
27
+ defaultRegistry.hydrateEntry(${JSON.stringify(n)}, _jogaks, _meta?.component)
28
+
29
+ if (import.meta.hot) {
30
+ import.meta.hot.accept()
31
+ }
32
+
33
+ export {}
34
+ `}},async handleHotUpdate({file:t,modules:m}){const n=/\.jogak\.(tsx?|jsx?)$/.test(t),f=/\.(tsx?|jsx?)$/.test(t)&&!n;if(!n&&!f||r===void 0||!n)return;const c=r.moduleGraph.getModuleById(T),y=x.get(t),h=y!==void 0?I+Y(y):void 0,o=h!==void 0?r.moduleGraph.getModuleById(h):void 0;let l=null,w={},S="";if(s!==void 0){try{l=await s.extractMeta(t)}catch{l=null}try{w=await s.extract(t)}catch{w={}}try{S=await v.readFile(t,"utf8")}catch{S=""}}if(l===null){c!==void 0&&r.moduleGraph.invalidateModule(c),o!==void 0&&r.moduleGraph.invalidateModule(o),r.ws.send({type:"full-reload"});return}const C=A(l),D=M.get(t),O=z(D,C);if(M.set(t,C),!O||y===void 0){c!==void 0&&r.moduleGraph.invalidateModule(c),o!==void 0&&r.moduleGraph.invalidateModule(o),r.ws.send({type:"full-reload"});return}const L={id:y,title:l.title,jogakNames:l.jogakNames,autoArgTypes:w,userArgTypes:l.userArgTypes,source:S,filePath:t,metaExtras:l.metaExtras};o!==void 0&&r.moduleGraph.invalidateModule(o),r.ws.send({type:"custom",event:"jogak:meta-update",data:{id:y,meta:L}});const E=[...m];return o!==void 0&&!E.includes(o)&&E.push(o),E}}}function Z(){return`(rawId) => {
35
+ if (typeof Buffer !== 'undefined') return Buffer.from(rawId, 'utf8').toString('base64url')
36
+ // 브라우저 폴백: btoa는 binary string 기준이라 UTF-8을 한번 인코딩해야 한다.
37
+ const enc = new TextEncoder().encode(rawId)
38
+ let bin = ''
39
+ for (let i = 0; i < enc.length; i++) bin += String.fromCharCode(enc[i])
40
+ return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')
41
+ }`}exports.jogak=Q;