@mandujs/core 0.9.29 → 0.9.30

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/bundler/dev.ts +106 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mandujs/core",
3
- "version": "0.9.29",
3
+ "version": "0.9.30",
4
4
  "description": "Mandu Framework Core - Spec, Generator, Guard, Runtime",
5
5
  "type": "module",
6
6
  "main": "./src/index.ts",
@@ -18,6 +18,17 @@ export interface DevBundlerOptions {
18
18
  onRebuild?: (result: RebuildResult) => void;
19
19
  /** 에러 콜백 */
20
20
  onError?: (error: Error, routeId?: string) => void;
21
+ /**
22
+ * 추가 watch 디렉토리 (공통 컴포넌트 등)
23
+ * 상대 경로 또는 절대 경로 모두 지원
24
+ * 기본값: ["src/components", "components", "src/shared", "shared", "lib"]
25
+ */
26
+ watchDirs?: string[];
27
+ /**
28
+ * 기본 watch 디렉토리 비활성화
29
+ * true로 설정하면 watchDirs만 감시
30
+ */
31
+ disableDefaultWatchDirs?: boolean;
21
32
  }
22
33
 
23
34
  export interface RebuildResult {
@@ -34,12 +45,33 @@ export interface DevBundler {
34
45
  close: () => void;
35
46
  }
36
47
 
48
+ // 기본 공통 컴포넌트 디렉토리 목록
49
+ const DEFAULT_COMMON_DIRS = [
50
+ "src/components",
51
+ "components",
52
+ "src/shared",
53
+ "shared",
54
+ "src/lib",
55
+ "lib",
56
+ "src/hooks",
57
+ "hooks",
58
+ "src/utils",
59
+ "utils",
60
+ ];
61
+
37
62
  /**
38
63
  * 개발 모드 번들러 시작
39
64
  * 파일 변경 감시 및 자동 재빌드
40
65
  */
41
66
  export async function startDevBundler(options: DevBundlerOptions): Promise<DevBundler> {
42
- const { rootDir, manifest, onRebuild, onError } = options;
67
+ const {
68
+ rootDir,
69
+ manifest,
70
+ onRebuild,
71
+ onError,
72
+ watchDirs: customWatchDirs = [],
73
+ disableDefaultWatchDirs = false,
74
+ } = options;
43
75
 
44
76
  // 초기 빌드
45
77
  console.log("🔨 Initial client bundle build...");
@@ -57,6 +89,7 @@ export async function startDevBundler(options: DevBundlerOptions): Promise<DevBu
57
89
  // clientModule 경로에서 routeId 매핑 생성
58
90
  const clientModuleToRoute = new Map<string, string>();
59
91
  const watchDirs = new Set<string>();
92
+ const commonWatchDirs = new Set<string>(); // 공통 디렉토리 (전체 재빌드 트리거)
60
93
 
61
94
  for (const route of manifest.routes) {
62
95
  if (route.clientModule) {
@@ -79,13 +112,79 @@ export async function startDevBundler(options: DevBundlerOptions): Promise<DevBu
79
112
  // slots 디렉토리 없으면 무시
80
113
  }
81
114
 
115
+ // 공통 컴포넌트 디렉토리 추가 (기본 + 커스텀)
116
+ const commonDirsToCheck = disableDefaultWatchDirs
117
+ ? customWatchDirs
118
+ : [...DEFAULT_COMMON_DIRS, ...customWatchDirs];
119
+
120
+ for (const dir of commonDirsToCheck) {
121
+ const absDir = path.isAbsolute(dir) ? dir : path.join(rootDir, dir);
122
+ try {
123
+ await fs.promises.access(absDir);
124
+ commonWatchDirs.add(absDir);
125
+ watchDirs.add(absDir);
126
+ } catch {
127
+ // 디렉토리 없으면 무시
128
+ }
129
+ }
130
+
82
131
  // 파일 감시 설정
83
132
  const watchers: fs.FSWatcher[] = [];
84
133
  let debounceTimer: ReturnType<typeof setTimeout> | null = null;
85
134
 
135
+ // 파일이 공통 디렉토리에 있는지 확인
136
+ const isInCommonDir = (filePath: string): boolean => {
137
+ const normalizedFile = path.resolve(filePath).replace(/\\/g, "/");
138
+ for (const commonDir of commonWatchDirs) {
139
+ const normalizedCommon = path.resolve(commonDir).replace(/\\/g, "/");
140
+ if (normalizedFile.startsWith(normalizedCommon + "/")) {
141
+ return true;
142
+ }
143
+ }
144
+ return false;
145
+ };
146
+
86
147
  const handleFileChange = async (changedFile: string) => {
87
148
  const normalizedPath = changedFile.replace(/\\/g, "/");
88
149
 
150
+ // 공통 컴포넌트 디렉토리 변경 → 전체 재빌드
151
+ if (isInCommonDir(changedFile)) {
152
+ console.log(`\n🔄 Common file changed: ${path.basename(changedFile)}`);
153
+ console.log(` Rebuilding all islands...`);
154
+ const startTime = performance.now();
155
+
156
+ try {
157
+ const result = await buildClientBundles(manifest, rootDir, {
158
+ minify: false,
159
+ sourcemap: true,
160
+ });
161
+
162
+ const buildTime = performance.now() - startTime;
163
+
164
+ if (result.success) {
165
+ console.log(`✅ Rebuilt ${result.stats.bundleCount} islands in ${buildTime.toFixed(0)}ms`);
166
+ onRebuild?.({
167
+ routeId: "*", // 전체 재빌드 표시
168
+ success: true,
169
+ buildTime,
170
+ });
171
+ } else {
172
+ console.error(`❌ Build failed:`, result.errors);
173
+ onRebuild?.({
174
+ routeId: "*",
175
+ success: false,
176
+ buildTime,
177
+ error: result.errors.join(", "),
178
+ });
179
+ }
180
+ } catch (error) {
181
+ const err = error instanceof Error ? error : new Error(String(error));
182
+ console.error(`❌ Build error:`, err.message);
183
+ onError?.(err, "*");
184
+ }
185
+ return;
186
+ }
187
+
89
188
  // clientModule 매핑에서 routeId 찾기
90
189
  let routeId = clientModuleToRoute.get(normalizedPath);
91
190
 
@@ -173,6 +272,12 @@ export async function startDevBundler(options: DevBundlerOptions): Promise<DevBu
173
272
 
174
273
  if (watchers.length > 0) {
175
274
  console.log(`👀 Watching ${watchers.length} directories for changes...`);
275
+ if (commonWatchDirs.size > 0) {
276
+ const commonDirNames = Array.from(commonWatchDirs)
277
+ .map(d => path.relative(rootDir, d) || ".")
278
+ .join(", ");
279
+ console.log(`📦 Common dirs (full rebuild): ${commonDirNames}`);
280
+ }
176
281
  }
177
282
 
178
283
  return {