@nine-lab/nine-mu 0.1.388 → 0.1.390

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nine-lab/nine-mu",
3
- "version": "0.1.388",
3
+ "version": "0.1.390",
4
4
  "description": "AI-Driven Full-Stack Code Fabrication Engine",
5
5
  "type": "module",
6
6
  "main": "./dist/nine-mu.umd.js",
@@ -1,17 +1,15 @@
1
- import React, { createContext, useState, useContext, lazy, Suspense, useEffect } from 'react';
1
+ import React, { createContext, useEffect, useState, useContext, lazy, Suspense } from 'react';
2
2
  import { Route, Routes } from 'react-router-dom';
3
3
 
4
4
  // ==========================================
5
- // 1. 내부 기능 A: ScreenContext 및 전역 공급자 (수출형)
5
+ // 1. 내부 기능 A: ScreenContext 및 구조
6
6
  // ==========================================
7
7
  const ScreenContext = createContext();
8
8
  export const useScreen = () => useContext(ScreenContext);
9
9
 
10
- // 🎯 기존 ScreenProvider 이름 변경 전역 동적 데이터 바인딩 셋업
11
- export const NineScreenProvider = ({ children }) => {
10
+ const ScreenProvider = ({ children }) => {
12
11
  const [activeView, setActiveView] = useState('list-wrapper');
13
12
  const [selectedData, setSelectedData] = useState(null);
14
- const [menuData, setMenuData] = useState([]); // 💡 데이터 통신 허브를 여기로 단일화
15
13
 
16
14
  const goto = (selector, data) => {
17
15
  setActiveView(selector);
@@ -20,7 +18,7 @@ export const NineScreenProvider = ({ children }) => {
20
18
 
21
19
  return React.createElement(
22
20
  ScreenContext.Provider,
23
- { value: { activeView, selectedData, goto, menuData, setMenuData } }, // 🎯 데이터 변경 권한까지 자식들에게 공유
21
+ { value: { activeView, selectedData, goto } },
24
22
  children
25
23
  );
26
24
  };
@@ -35,9 +33,14 @@ const Default404 = () => {
35
33
  };
36
34
 
37
35
  const createDynamicRoutes = (menuData, projectViews, Custom404) => {
36
+ // 기본 404 및 홈 설정
38
37
  const lazyLoad = (viewPath) => {
39
38
  const importer = projectViews[viewPath];
40
- if (!importer) return Custom404 ? Custom404 : Default404;
39
+
40
+ // 🎯 [404 해결 포인트] 사용자가 등록한 404가 있으면 그걸 쓰고, 없으면 기본 내장 404를 띄웁니다.
41
+ if (!importer) {
42
+ return Custom404 ? Custom404 : Default404;
43
+ }
41
44
  return lazy(() => importer());
42
45
  };
43
46
 
@@ -63,6 +66,7 @@ const createDynamicRoutes = (menuData, projectViews, Custom404) => {
63
66
  const sanitizedParts = pathParts.map(part => part.replace(/-/g, "_"));
64
67
  const fullFolderPath = sanitizedParts.join("/");
65
68
 
69
+ // 사용자 프로젝트 루트 기준 상대 경로 매핑
66
70
  const componentPath = `./views/${fullFolderPath}/${pascalName}.jsx`;
67
71
 
68
72
  dynamicRoutes.push({
@@ -73,6 +77,7 @@ const createDynamicRoutes = (menuData, projectViews, Custom404) => {
73
77
  });
74
78
  }
75
79
 
80
+ // 🎯 [404 해결 포인트] 정의되지 않은 모든 주소(*)는 라우터 레벨에서 404 매핑 컴포넌트로 리다이렉트
76
81
  dynamicRoutes.push({ path: "*", Component: Custom404 ? Custom404 : Default404 });
77
82
  return dynamicRoutes;
78
83
  };
@@ -198,15 +203,13 @@ class NineExceptionHook extends React.Component {
198
203
  }
199
204
 
200
205
  // ==========================================
201
- // 🎯 3. 최종 완결: 순수 라우터 에러 실드 역할만 수행
206
+ // 🎯 3. 핵심: 외부 주입형 404 라우팅 기능 탑재
202
207
  // ==========================================
203
- export function NineHook({ menuUrl, views, error404, onCatch, fallback, styles, containerStyle }) {
204
- // 🎯 상위 최상단 NineScreenProvider가 뿌려주는 주파수 수신
205
- const context = useContext(ScreenContext);
206
- const menuData = context ? context.menuData : [];
207
- const setMenuData = context ? context.setMenuData : null;
208
-
209
- const [localMenuData, setLocalMenuData] = useState([]);
208
+ // ==========================================
209
+ // 🎯 3. 최종 완결: 자식 컴포넌트(children)를 수용하는 구조로 변경
210
+ // ==========================================
211
+ export function NineHook({ children, menuUrl, views, error404, onCatch, fallback, styles, containerStyle }) {
212
+ const [menuData, setMenuData] = useState([]);
210
213
 
211
214
  const fetchRoutes = async () => {
212
215
  if (!menuUrl) return;
@@ -214,20 +217,11 @@ export function NineHook({ menuUrl, views, error404, onCatch, fallback, styles,
214
217
  const response = await fetch(`${menuUrl}?t=${Date.now()}`);
215
218
  if (response.ok) {
216
219
  const data = await response.json();
217
-
218
- // 🎯 상위 Context가 존재하면 거기를 갱신시켜 외부 LeftMenu까지 연쇄 반응 트리거
219
- if (setMenuData) {
220
- setMenuData((prev) => {
221
- if (JSON.stringify(prev) === JSON.stringify(data)) return prev;
222
- console.log("🔥 [Nine-Library] 최상단 NineScreenProvider 데이터 무선 동기화");
223
- return data;
224
- });
225
- } else {
226
- setLocalMenuData((prev) => {
227
- if (JSON.stringify(prev) === JSON.stringify(data)) return prev;
228
- return data;
229
- });
230
- }
220
+ setMenuData((prev) => {
221
+ if (JSON.stringify(prev) === JSON.stringify(data)) return prev;
222
+ console.log("🔥 [Nine-Library] routes.json 변경 감지 -> 전체 시스템 동기화");
223
+ return data;
224
+ });
231
225
  }
232
226
  } catch (e) {
233
227
  console.error("[Nine-Library] 런타임 메뉴 동기화 실패:", e);
@@ -247,24 +241,37 @@ export function NineHook({ menuUrl, views, error404, onCatch, fallback, styles,
247
241
  };
248
242
  }, [menuUrl]);
249
243
 
250
- const targetData = setMenuData ? menuData : localMenuData;
251
- const dynamicRoutes = createDynamicRoutes(targetData, views, error404);
244
+ // 🎯 [핵심 교정]: 최초 fetch가 완료되기 전(데이터가 없을 때)에는 렌더링을 잠시 멈춥니다.
245
+ if (!menuData || menuData.length === 0) {
246
+ return React.createElement(
247
+ 'div',
248
+ { style: { padding: '25px', color: '#666', fontFamily: 'monospace', background: '#121314', minHeight: '100vh' } },
249
+ '>> Initializing menu matrix telemetry...'
250
+ );
251
+ }
252
+
253
+ const dynamicRoutes = createDynamicRoutes(menuData, views, error404);
252
254
 
253
255
  return React.createElement(
254
- NineExceptionHook,
255
- { onCatch, fallback, styles, containerStyle },
256
+ ScreenProvider,
257
+ { menuData },
256
258
  React.createElement(
257
- Suspense,
258
- { fallback: React.createElement('div', { style: { padding: '25px', color: '#666', fontFamily: 'monospace' } }, '>> Loading component matrix...') },
259
+ NineExceptionHook,
260
+ { onCatch, fallback, styles, containerStyle },
261
+ children,
259
262
  React.createElement(
260
- Routes,
261
- null,
262
- dynamicRoutes.map((route) =>
263
- React.createElement(Route, {
264
- key: route.path,
265
- path: route.path,
266
- element: React.createElement(route.Component, { key: route.path })
267
- })
263
+ Suspense,
264
+ { fallback: React.createElement('div', { style: { padding: '25px', color: '#666', fontFamily: 'monospace' } }, '>> Loading component matrix...') },
265
+ React.createElement(
266
+ Routes,
267
+ null,
268
+ dynamicRoutes.map((route) =>
269
+ React.createElement(Route, {
270
+ key: route.path,
271
+ path: route.path,
272
+ element: React.createElement(route.Component, { key: route.path })
273
+ })
274
+ )
268
275
  )
269
276
  )
270
277
  )