@nine-lab/nine-mu 0.1.389 → 0.1.391

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.389",
3
+ "version": "0.1.391",
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
  };
@@ -197,76 +202,36 @@ class NineExceptionHook extends React.Component {
197
202
  }
198
203
  }
199
204
 
200
- // ==========================================
201
- // 🎯 3. 최종 완결: 순수 라우터 에러 실드 역할만 수행
202
- // ==========================================
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([]);
210
-
211
- const fetchRoutes = async () => {
212
- if (!menuUrl) return;
213
- try {
214
- const response = await fetch(`${menuUrl}?t=${Date.now()}`);
215
- if (response.ok) {
216
- 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
- }
231
- }
232
- } catch (e) {
233
- console.error("[Nine-Library] 런타임 메뉴 동기화 실패:", e);
234
- }
235
- };
236
-
237
- useEffect(() => {
238
- fetchRoutes();
239
- window.addEventListener('popstate', fetchRoutes);
240
- document.addEventListener('click', fetchRoutes, true);
241
- const intervalId = setInterval(fetchRoutes, 3000);
242
-
243
- return () => {
244
- window.removeEventListener('popstate', fetchRoutes);
245
- document.removeEventListener('click', fetchRoutes, true);
246
- clearInterval(intervalId);
247
- };
248
- }, [menuUrl]);
249
-
250
- const targetData = setMenuData ? menuData : localMenuData;
251
- const dynamicRoutes = createDynamicRoutes(targetData, views, error404);
205
+ export function NineHook({ children, menuData, views, error404, onCatch, fallback, styles, containerStyle }) {
206
+ // 🎯 꼼수 완전 제거: 오직 주입받은 menuData가 변경될 때만 리액트가 라우터를 동적 재구성
207
+ const dynamicRoutes = createDynamicRoutes(menuData, views, error404);
252
208
 
253
209
  return React.createElement(
254
- NineExceptionHook,
255
- { onCatch, fallback, styles, containerStyle },
210
+ ScreenProvider,
211
+ { menuData },
256
212
  React.createElement(
257
- Suspense,
258
- { fallback: React.createElement('div', { style: { padding: '25px', color: '#666', fontFamily: 'monospace' } }, '>> Loading component matrix...') },
259
- 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
- })
213
+ NineExceptionHook,
214
+ { onCatch, fallback, styles, containerStyle },
215
+
216
+ // LeftMenu 레이아웃 자식 출력 (부팅 딜레이 없이 즉시 렌더링)
217
+ children,
218
+
219
+ // 데이터가 존재할 때 정석대로 라우터 구역 활성화
220
+ menuData && menuData.length > 0 ? React.createElement(
221
+ Suspense,
222
+ { fallback: React.createElement('div', { style: { padding: '25px', color: '#666', fontFamily: 'monospace' } }, '>> Loading component matrix...') },
223
+ React.createElement(
224
+ Routes,
225
+ null,
226
+ dynamicRoutes.map((route) =>
227
+ React.createElement(Route, {
228
+ key: route.path,
229
+ path: route.path,
230
+ element: React.createElement(route.Component, { key: route.path })
231
+ })
232
+ )
268
233
  )
269
- )
234
+ ) : React.createElement('div', { style: { padding: '25px', color: '#444', fontFamily: 'monospace' } }, '>> Database route matrix empty.')
270
235
  )
271
236
  );
272
237
  }