@jogak/core 0.1.0-alpha.4 → 0.1.0-alpha.6
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/index.js +1 -1
- package/dist/index.mjs +200 -158
- package/dist/registry.d.ts +3 -0
- package/dist/types.d.ts +54 -0
- package/dist/vite/detect-global-css.d.ts +33 -0
- package/dist/vite/index.js +14 -9
- package/dist/vite/index.mjs +201 -163
- package/dist/vite/virtual-ids.d.ts +3 -0
- package/package.json +5 -2
package/dist/registry.d.ts
CHANGED
|
@@ -22,6 +22,9 @@ export declare class ComponentRegistry {
|
|
|
22
22
|
search(query: string): readonly RegistryEntry[];
|
|
23
23
|
/**
|
|
24
24
|
* title의 '/' 구분자로 hydrated entry만의 계층 트리를 구성한다.
|
|
25
|
+
*
|
|
26
|
+
* `getAll()`이 정렬된 순서를 반환하므로 트리 객체의 키 iteration order
|
|
27
|
+
* (ECMA-262 §OrdinaryOwnPropertyKeys: insertion order for string keys)도 결정적이다.
|
|
25
28
|
*/
|
|
26
29
|
getTree(): CategoryTree;
|
|
27
30
|
clear(): void;
|
package/dist/types.d.ts
CHANGED
|
@@ -154,4 +154,58 @@ export interface JogakPluginOptions {
|
|
|
154
154
|
* jogak({ resolveAlias: { '@': './src', '@components': './src/components' } })
|
|
155
155
|
*/
|
|
156
156
|
readonly resolveAlias?: Readonly<Record<string, string>>;
|
|
157
|
+
/**
|
|
158
|
+
* 사용자 globalCss를 jogak SPA에 import한다 (알파.6 opt-in).
|
|
159
|
+
*
|
|
160
|
+
* 동기:
|
|
161
|
+
* - `runHost`는 vite root를 `@jogak/ui` 패키지로 두고 사용자 `vite.config.ts`/
|
|
162
|
+
* `main.tsx`를 무시하므로(`configFile: false`), 사용자 `index.css`(Tailwind/
|
|
163
|
+
* shadcn 디자인 토큰)가 jogak SPA에 자동 적용되지 않는다.
|
|
164
|
+
* - 본 옵션이 `true`이거나 명시 경로면 plugin이 사용자 css를 가상 모듈로
|
|
165
|
+
* 주입해 jogak SPA가 import한다.
|
|
166
|
+
*
|
|
167
|
+
* 의미:
|
|
168
|
+
* - `false` (default): 미주입. jogak chrome 기본 스타일만 사용 (알파.4~5 동작 그대로).
|
|
169
|
+
* - `true`: `<userRoot>/src/{index,main,styles,global,app,globals}.css` 후보를
|
|
170
|
+
* 순차 검사해 **첫 발견 1개**만 import. 미발견 시 빈 모듈 (no-op).
|
|
171
|
+
* - `string`: 사용자 root 기준 상대 경로 또는 절대 경로 1개. 자동 감지 비활성화.
|
|
172
|
+
* - `string[]`: 명시 경로 N개를 배열 순서대로 모두 import. 자동 감지 비활성화.
|
|
173
|
+
*
|
|
174
|
+
* 자동 감지 후보 (우선순위 순):
|
|
175
|
+
* 1. `src/index.css` (shadcn/ui Vite)
|
|
176
|
+
* 2. `src/main.css`
|
|
177
|
+
* 3. `src/styles.css`
|
|
178
|
+
* 4. `src/styles/globals.css` (Next.js shadcn)
|
|
179
|
+
* 5. `src/styles/index.css`
|
|
180
|
+
* 6. `src/app/globals.css` (Next.js App Router shadcn)
|
|
181
|
+
* 7. `src/global.css`
|
|
182
|
+
* 8. `src/app.css`
|
|
183
|
+
*
|
|
184
|
+
* 격리:
|
|
185
|
+
* - jogak UI는 알파.4~5에서 Tailwind v4 + `prefix=jogak`로 마이그레이션되어
|
|
186
|
+
* 사용자 utility class와 충돌 zero (예: 사용자 `.bg-primary` ≠ jogak `.jogak\:bg-...`).
|
|
187
|
+
* - jogak CSS variable은 `--jogak-*` prefix → 사용자 `:root { --primary }` 같은
|
|
188
|
+
* 토큰과 namespace 충돌 zero.
|
|
189
|
+
* - 단, 사용자 css의 `*` selector / `body` selector / reset 류는 jogak chrome에
|
|
190
|
+
* 영향 가능. README의 "scope 가이드" 패턴 참조.
|
|
191
|
+
*
|
|
192
|
+
* 한계 (알파.7+ 로드맵):
|
|
193
|
+
* - 사용자 css를 preview 영역으로만 한정하는 Shadow DOM/iframe 격리는 미지원.
|
|
194
|
+
* - `previewIsolation` 옵션은 알파.7+에서 별도 도입.
|
|
195
|
+
* - `globalCss: true` 자동 감지는 dev 시작 시점에 한 번만 수행 — 후보 css 파일이
|
|
196
|
+
* dev 시작 후에 새로 추가되면 dev 서버 재시작이 필요하다. 명시 경로
|
|
197
|
+
* (`globalCss: './src/index.css'`)는 파일이 나중에 생성되어도 정상 hot reload된다.
|
|
198
|
+
*
|
|
199
|
+
* @default false
|
|
200
|
+
*
|
|
201
|
+
* @example 자동 감지
|
|
202
|
+
* jogak({ globalCss: true })
|
|
203
|
+
*
|
|
204
|
+
* @example 명시 경로
|
|
205
|
+
* jogak({ globalCss: './src/index.css' })
|
|
206
|
+
*
|
|
207
|
+
* @example 다중 import (디자인 토큰 + reset 분리)
|
|
208
|
+
* jogak({ globalCss: ['./src/tokens.css', './src/index.css'] })
|
|
209
|
+
*/
|
|
210
|
+
readonly globalCss?: boolean | string | readonly string[];
|
|
157
211
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 사용자 globalCss 자동 감지 + 옵션 정규화 (알파.6, opt-in).
|
|
3
|
+
*
|
|
4
|
+
* 본 모듈은 `JogakPluginOptions.globalCss` 옵션 값을 절대 경로 배열로 변환한다.
|
|
5
|
+
* 자동 감지(`true`)는 첫 발견 1개만 반환 — 다중 import는 명시 배열로 사용자가 선언.
|
|
6
|
+
*
|
|
7
|
+
* 자동 감지 후보(우선순위 순)는 spec `_workspace/01_arch/api-contracts.md` §4.1을 따른다.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* 사용자 globalCss 자동 감지.
|
|
11
|
+
*
|
|
12
|
+
* `<userRoot>/src/...` 후보를 우선순위 순으로 검사해 **첫 발견 1개**만 반환.
|
|
13
|
+
* 미발견 시 빈 배열.
|
|
14
|
+
*
|
|
15
|
+
* 첫 발견 stop 정책:
|
|
16
|
+
* - 사용자가 다중 import를 원하면 명시 배열(`globalCss: ['...', '...']`)을 쓰게 한다.
|
|
17
|
+
* - 자동 감지에서 모든 후보를 import하면 의도치 않은 css 중복(예: index.css와
|
|
18
|
+
* styles.css 둘 다 존재 시)이 발생할 수 있다.
|
|
19
|
+
*/
|
|
20
|
+
export declare function detectUserGlobalCss(userRoot: string): readonly string[];
|
|
21
|
+
/**
|
|
22
|
+
* 옵션 값 → 절대 경로 배열로 정규화.
|
|
23
|
+
*
|
|
24
|
+
* - `false`/`undefined`: `[]`
|
|
25
|
+
* - `true`: `detectUserGlobalCss(userRoot)`
|
|
26
|
+
* - `string`: `[resolve(userRoot, string)]`. 빈 문자열은 `[]`.
|
|
27
|
+
* - `string[]`: 각 요소 resolve, 빈 요소 무시 (배열 순서 보존)
|
|
28
|
+
*
|
|
29
|
+
* `existsSync` 검증은 명시 경로(string/string[])에서는 하지 않는다 — 사용자가
|
|
30
|
+
* 의도적으로 빈 파일 또는 동적 css generator를 쓸 수 있고, 미존재 시 Vite가
|
|
31
|
+
* 자체 에러로 알려주는 게 더 명확하다 (silent skip은 디버깅 어려움).
|
|
32
|
+
*/
|
|
33
|
+
export declare function resolveGlobalCssPaths(option: boolean | string | readonly string[] | undefined, userRoot: string): readonly string[];
|
package/dist/vite/index.js
CHANGED
|
@@ -1,21 +1,26 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var J=Object.create;var L=Object.defineProperty;var K=Object.getOwnPropertyDescriptor;var q=Object.getOwnPropertyNames;var W=Object.getPrototypeOf,X=Object.prototype.hasOwnProperty;var Y=(e,t,s,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of q(t))!X.call(e,i)&&i!==s&&L(e,i,{get:()=>t[i],enumerable:!(o=K(t,i))||o.enumerable});return e};var H=(e,t,s)=>(s=e!=null?J(W(e)):{},Y(t||!e||!e.__esModule?L(s,"default",{value:e,enumerable:!0}):s,e));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const g=require("node:path"),j=require("node:fs"),_=require("node:fs/promises"),N=require("../extractor-client-CiWszHel.cjs"),z=["@jogak/core","@jogak/react","@jogak/web-components","@jogak/next"];async function $(e){try{const o=await _.stat(e);if(!o.isDirectory())return o.mtimeMs}catch{return 0}let t=0,s;try{s=await _.readdir(e)}catch{return 0}for(const o of s){const i=g.join(e,o);try{const u=await _.stat(i);if(u.isDirectory()){const y=await $(i);y>t&&(t=y)}else u.mtimeMs>t&&(t=u.mtimeMs)}catch{continue}}return t}async function Q(e){const t=g.resolve(e.root,"node_modules/.vite/deps");if(!j.existsSync(t))return{purged:!1};const s=g.join(t,"_metadata.json");if(!j.existsSync(s))return{purged:!1};let o;try{o=(await _.stat(s)).mtimeMs}catch(u){return e.logger.warn(`[jogak] cache validation: failed to stat _metadata.json (${u.message})`),{purged:!1}}const i=e.packages??z;for(const u of i){const y=g.resolve(e.root,"node_modules",u,"dist");if(!j.existsSync(y))continue;let h;try{h=await $(y)}catch(a){e.logger.warn(`[jogak] cache validation: failed to walk ${u}/dist (${a.message})`);continue}if(h>o+1e3)try{return await _.rm(t,{recursive:!0,force:!0}),e.logger.info(`[jogak] vite deps cache invalidated (stale): ${u} dist newer than cache`),{purged:!0,reason:u}}catch(a){return e.logger.warn(`[jogak] cache validation: failed to purge ${t} (${a.message})`),{purged:!1}}}return{purged:!1}}const Z=["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 ee(e){for(const t of Z){const s=g.resolve(e,t);if(j.existsSync(s))return[s]}return[]}function te(e,t){if(e===void 0||e===!1)return[];if(e===!0)return ee(t);if(typeof e=="string")return e.length>0?[g.resolve(t,e)]:[];const s=[];for(const o of e)typeof o=="string"&&o.length>0&&s.push(g.resolve(t,o));return s}function re(e){return e.replace(/\/\*[\s\S]*?\*\//g,"").replace(/^\s*\/\/.*$/gm,"")}function se(e){if(j.existsSync(e))try{const t=j.readFileSync(e,"utf8");return JSON.parse(re(t))}catch{return}}function oe(e,t,s){if(!e.endsWith("/*")||!t.endsWith("/*"))return;const o=e.slice(0,-2),i=g.resolve(s,t.slice(0,-2));return[o,i]}function ne(e,t){var i,u;const s={},o=new Set([e,g.resolve(t,"tsconfig.app.json")]);for(const y of o){const h=se(y);if(h===void 0)continue;const a=(i=h.compilerOptions)==null?void 0:i.paths;if(a===void 0)continue;const p=((u=h.compilerOptions)==null?void 0:u.baseUrl)??".",k=g.resolve(g.dirname(y),p);for(const[S,C]of Object.entries(a)){const b=C[0];if(b===void 0)continue;const T=oe(S,b,k);if(T===void 0)continue;const[r,d]=T;s[r]===void 0&&(s[r]=d)}}return s}const P="virtual:jogak",M="\0"+P,F="virtual:jogak/entry/",I="\0"+F,G="virtual:jogak/global-css",R="\0"+G;function ae(e){return Buffer.from(e,"utf8").toString("base64url")}function ie(e){return Buffer.from(e,"base64url").toString("utf8")}function D(e){return{title:e.title,jogakNamesKey:[...e.jogakNames].sort().join("|")}}function ce(e,t){return e!==void 0&&e.title===t.title&&e.jogakNamesKey===t.jogakNamesKey}function le(e={}){const{patterns:t=["src/**/*.jogak.ts","src/**/*.jogak.tsx"],codeTheme:s="vsDark"}=e,o=e.cwd,i=e.tsConfigFilePath,u=e.disableCacheValidation===!0,y=e.resolveAlias,h=e.globalCss;let a,p,k;const S=new Map,C=new Map,b=new Map;async function T(){const{glob:r}=await import("glob"),d=k??process.cwd(),n=(await r(t,{cwd:d,absolute:!0})).sort(),f=[];S.clear(),C.clear();for(const c of n){let v="";try{v=await _.readFile(c,"utf8")}catch{continue}let w={},l=null;if(p!==void 0){try{w=await p.extract(c)}catch{w={}}try{l=await p.extractMeta(c)}catch{l=null}}if(l===null)continue;const m=l.title;S.set(m,c),C.set(c,m);const x={id:m,title:l.title,jogakNames:l.jogakNames,autoArgTypes:w,userArgTypes:l.userArgTypes,source:v,filePath:c,metaExtras:l.metaExtras};b.set(c,D(x)),f.push({id:m,filePath:c,meta:x})}return f}return{name:"vite-plugin-jogak",config(){const r=o??process.cwd(),d=i??g.resolve(r,"tsconfig.json"),n=ne(d,r),f={};if(y!==void 0)for(const[v,w]of Object.entries(y))f[v]=g.resolve(r,w);const c={...n,...f};if(Object.keys(c).length!==0)return{resolve:{alias:c}}},async configResolved(r){k=o??r.root,r.command==="serve"&&!u&&await Q({root:r.root,logger:{info:n=>r.logger.info(n),warn:n=>r.logger.warn(n)}});const d=i??g.resolve(k,"tsconfig.json");p=j.existsSync(d)?N.createPropsExtractor({tsConfigFilePath:d}):N.createPropsExtractor()},configureServer(r){a=r},buildEnd(){p==null||p.releaseCache()},resolveId(r){if(r===P)return M;if(r===G)return R;if(r.startsWith(F))return"\0"+r},async load(r){if(r===R){const d=k??process.cwd(),n=te(h,d);return n.length===0?`// [jogak] globalCss not configured or no candidates found.
|
|
2
|
+
export {}
|
|
3
|
+
`:`${n.map(c=>`import ${JSON.stringify(c)}`).join(`
|
|
4
|
+
`)}
|
|
5
|
+
export {}
|
|
6
|
+
`}if(r===M){const n=(await T()).map(f=>f.meta);return`import { defaultRegistry } from '@jogak/core'
|
|
2
7
|
|
|
3
8
|
const _entryLoader = (slug) =>
|
|
4
9
|
import(/* @vite-ignore */ '/@id/__x00__virtual:jogak/entry/' + slug)
|
|
5
10
|
defaultRegistry.setEntryLoader((id) => {
|
|
6
|
-
const slug = ${
|
|
11
|
+
const slug = ${ue()}
|
|
7
12
|
return _entryLoader(slug(id))
|
|
8
13
|
})
|
|
9
14
|
|
|
10
|
-
const _metas = ${JSON.stringify(
|
|
15
|
+
const _metas = ${JSON.stringify(n)}
|
|
11
16
|
|
|
12
17
|
for (const m of _metas) defaultRegistry.registerMeta(m)
|
|
13
18
|
|
|
14
|
-
export const _jogakCodeTheme = ${JSON.stringify(
|
|
19
|
+
export const _jogakCodeTheme = ${JSON.stringify(s)}
|
|
15
20
|
export const _jogakMetas = _metas
|
|
16
|
-
`}if(
|
|
21
|
+
`}if(r.startsWith(I)){const d=r.slice(I.length),n=ie(d);let f=S.get(n);return f===void 0&&(await T(),f=S.get(n)),f===void 0?`// [jogak] unknown entry id: ${JSON.stringify(n)}
|
|
17
22
|
export {}
|
|
18
|
-
`:`import * as _user from ${JSON.stringify(
|
|
23
|
+
`:`import * as _user from ${JSON.stringify(f)}
|
|
19
24
|
import { defaultRegistry } from '@jogak/core'
|
|
20
25
|
|
|
21
26
|
const _meta = _user.default
|
|
@@ -24,18 +29,18 @@ delete _named.default
|
|
|
24
29
|
const _jogaks = Object.values(_named).filter(
|
|
25
30
|
(v) => v !== null && typeof v === 'object' && typeof v.name === 'string'
|
|
26
31
|
)
|
|
27
|
-
defaultRegistry.hydrateEntry(${JSON.stringify(
|
|
32
|
+
defaultRegistry.hydrateEntry(${JSON.stringify(n)}, _jogaks, _meta?.component)
|
|
28
33
|
|
|
29
34
|
if (import.meta.hot) {
|
|
30
35
|
import.meta.hot.accept()
|
|
31
36
|
}
|
|
32
37
|
|
|
33
38
|
export {}
|
|
34
|
-
`}},async handleHotUpdate({file:
|
|
39
|
+
`}},async handleHotUpdate({file:r,modules:d}){const n=/\.jogak\.(tsx?|jsx?)$/.test(r),f=/\.(tsx?|jsx?)$/.test(r)&&!n;if(!n&&!f||a===void 0||!n)return;const c=a.moduleGraph.getModuleById(M),v=C.get(r),w=v!==void 0?I+ae(v):void 0,l=w!==void 0?a.moduleGraph.getModuleById(w):void 0;let m=null,x={},A="";if(p!==void 0){try{m=await p.extractMeta(r)}catch{m=null}try{x=await p.extract(r)}catch{x={}}try{A=await _.readFile(r,"utf8")}catch{A=""}}if(m===null){c!==void 0&&a.moduleGraph.invalidateModule(c),l!==void 0&&a.moduleGraph.invalidateModule(l),a.ws.send({type:"full-reload"});return}const O=D(m),U=b.get(r),V=ce(U,O);if(b.set(r,O),!V||v===void 0){c!==void 0&&a.moduleGraph.invalidateModule(c),l!==void 0&&a.moduleGraph.invalidateModule(l),a.ws.send({type:"full-reload"});return}const B={id:v,title:m.title,jogakNames:m.jogakNames,autoArgTypes:x,userArgTypes:m.userArgTypes,source:A,filePath:r,metaExtras:m.metaExtras};l!==void 0&&a.moduleGraph.invalidateModule(l),a.ws.send({type:"custom",event:"jogak:meta-update",data:{id:v,meta:B}});const E=[...d];return l!==void 0&&!E.includes(l)&&E.push(l),E}}}function ue(){return`(rawId) => {
|
|
35
40
|
if (typeof Buffer !== 'undefined') return Buffer.from(rawId, 'utf8').toString('base64url')
|
|
36
41
|
// 브라우저 폴백: btoa는 binary string 기준이라 UTF-8을 한번 인코딩해야 한다.
|
|
37
42
|
const enc = new TextEncoder().encode(rawId)
|
|
38
43
|
let bin = ''
|
|
39
44
|
for (let i = 0; i < enc.length; i++) bin += String.fromCharCode(enc[i])
|
|
40
45
|
return btoa(bin).replace(/\\+/g, '-').replace(/\\//g, '_').replace(/=+$/, '')
|
|
41
|
-
}`}exports.jogak=
|
|
46
|
+
}`}exports.jogak=le;
|