0
# Module Dependency Analysis
1
2
Recursive module dependency analysis with support for TypeScript, ESM, and CommonJS import patterns. Provides comprehensive dependency tracking for hot module reloading and build optimization.
3
4
## Capabilities
5
6
### Get Module Dependencies Function
7
8
Recursively analyzes module dependencies and returns all file paths.
9
10
```typescript { .api }
11
/**
12
* Recursively traces all dependencies of a module
13
* Returns an unordered set of absolute file paths
14
* @param absoluteFilePath - Absolute path to the entry module file
15
* @returns Promise resolving to array of absolute dependency file paths
16
*/
17
function getModuleDependencies(absoluteFilePath: string): Promise<string[]>;
18
```
19
20
**Usage Examples:**
21
22
```typescript
23
import { getModuleDependencies } from "@tailwindcss/node";
24
import path from "path";
25
26
// Analyze a TypeScript module
27
const entryFile = path.resolve(process.cwd(), "src/index.ts");
28
const dependencies = await getModuleDependencies(entryFile);
29
30
console.log("Dependencies:", dependencies);
31
// Output: [
32
// "/path/to/project/src/index.ts",
33
// "/path/to/project/src/utils/helpers.ts",
34
// "/path/to/project/src/components/Button.tsx",
35
// "/path/to/project/src/types/index.ts"
36
// ]
37
38
// Analyze a JavaScript module
39
const jsFile = path.resolve(process.cwd(), "config/webpack.config.js");
40
const jsDependencies = await getModuleDependencies(jsFile);
41
42
console.log("JS Dependencies:", jsDependencies);
43
```
44
45
### Hot Module Reloading Integration
46
47
Use dependency analysis for hot module reloading setup:
48
49
```typescript
50
import { getModuleDependencies } from "@tailwindcss/node";
51
import { watch } from "chokidar";
52
53
async function setupHMR(entryFile: string) {
54
// Get all dependencies
55
const dependencies = await getModuleDependencies(entryFile);
56
57
// Watch all dependency files
58
const watcher = watch(dependencies, {
59
ignoreInitial: true
60
});
61
62
watcher.on('change', async (changedFile) => {
63
console.log(`File changed: ${changedFile}`);
64
65
// Re-analyze dependencies in case new ones were added
66
const newDependencies = await getModuleDependencies(entryFile);
67
68
// Update watcher with new dependencies
69
const newFiles = newDependencies.filter(file => !dependencies.includes(file));
70
if (newFiles.length > 0) {
71
watcher.add(newFiles);
72
dependencies.push(...newFiles);
73
}
74
75
// Trigger rebuild
76
await rebuild();
77
});
78
79
return watcher;
80
}
81
82
// Usage
83
const watcher = await setupHMR("./src/index.ts");
84
```
85
86
### Build Tool Integration
87
88
Integration with build tools for dependency tracking:
89
90
```typescript
91
import { getModuleDependencies } from "@tailwindcss/node";
92
93
// Webpack plugin example
94
class DependencyTrackingPlugin {
95
apply(compiler: any) {
96
compiler.hooks.compilation.tap('DependencyTrackingPlugin', (compilation: any) => {
97
compilation.hooks.buildModule.tapAsync('DependencyTrackingPlugin',
98
async (module: any, callback: Function) => {
99
if (module.resource) {
100
try {
101
const dependencies = await getModuleDependencies(module.resource);
102
103
// Add dependencies to webpack's dependency graph
104
dependencies.forEach(dep => {
105
if (dep !== module.resource) {
106
module.addDependency(createDependency(dep));
107
}
108
});
109
} catch (error) {
110
console.warn(`Failed to analyze dependencies for ${module.resource}:`, error);
111
}
112
}
113
callback();
114
}
115
);
116
});
117
}
118
}
119
```
120
121
### Bundle Analysis
122
123
Use for bundle analysis and optimization:
124
125
```typescript
126
import { getModuleDependencies } from "@tailwindcss/node";
127
import fs from "fs/promises";
128
129
async function analyzeBundleSize(entryPoints: string[]) {
130
const bundleAnalysis = new Map<string, {
131
size: number;
132
dependencies: string[];
133
}>();
134
135
for (const entry of entryPoints) {
136
const dependencies = await getModuleDependencies(entry);
137
138
let totalSize = 0;
139
for (const dep of dependencies) {
140
try {
141
const stats = await fs.stat(dep);
142
totalSize += stats.size;
143
} catch (error) {
144
console.warn(`Could not stat file: ${dep}`);
145
}
146
}
147
148
bundleAnalysis.set(entry, {
149
size: totalSize,
150
dependencies
151
});
152
}
153
154
return bundleAnalysis;
155
}
156
157
// Usage
158
const analysis = await analyzeBundleSize([
159
"./src/app.ts",
160
"./src/worker.ts"
161
]);
162
163
analysis.forEach((info, entry) => {
164
console.log(`${entry}: ${info.size} bytes, ${info.dependencies.length} files`);
165
});
166
```
167
168
## Supported Import Patterns
169
170
The dependency analysis recognizes various import and require patterns:
171
172
### ESM Imports
173
174
```typescript
175
// Named imports
176
import { foo, bar } from './utils';
177
import { default as utils } from './utils';
178
179
// Default imports
180
import utils from './utils';
181
import * as utils from './utils';
182
183
// Side-effect imports
184
import './polyfills';
185
186
// Dynamic imports (string literals only)
187
const module = await import('./dynamic');
188
```
189
190
### CommonJS Requires
191
192
```javascript
193
// Basic require
194
const utils = require('./utils');
195
const { foo, bar } = require('./utils');
196
197
// Conditional requires (string literals)
198
if (condition) {
199
const optional = require('./optional');
200
}
201
```
202
203
### Export Statements
204
205
```typescript
206
// Re-exports
207
export { foo } from './foo';
208
export * from './bar';
209
export { default } from './baz';
210
211
// Named exports with re-export
212
export { foo as publicFoo } from './internal';
213
```
214
215
## Resolution Strategy
216
217
The module resolution follows Node.js conventions with TypeScript support:
218
219
### Extension Priority
220
221
For **JavaScript files** (`.js`, `.cjs`, `.mjs`):
222
1. No extension
223
2. `.js`
224
3. `.cjs`
225
4. `.mjs`
226
5. `.ts`
227
6. `.cts`
228
7. `.mts`
229
8. `.jsx`
230
9. `.tsx`
231
232
For **TypeScript files** (`.ts`, `.cts`, `.mts`, `.tsx`):
233
1. No extension
234
2. `.ts`
235
3. `.cts`
236
4. `.mts`
237
5. `.tsx`
238
6. `.js`
239
7. `.cjs`
240
8. `.mjs`
241
9. `.jsx`
242
243
### Directory Resolution
244
245
```typescript
246
// For import './components'
247
// Tries in order:
248
// 1. ./components.ts (or appropriate extension)
249
// 2. ./components/index.ts
250
// 3. ./components/index.js
251
// etc.
252
```
253
254
## Performance Considerations
255
256
### Circular Dependency Handling
257
258
```typescript
259
// The function handles circular dependencies gracefully
260
// Example: A.ts imports B.ts, B.ts imports A.ts
261
262
const dependencies = await getModuleDependencies("A.ts");
263
// Returns: ["A.ts", "B.ts"] (both files, no infinite loop)
264
```
265
266
### Caching Strategy
267
268
```typescript
269
import { getModuleDependencies } from "@tailwindcss/node";
270
271
// For performance, implement your own caching layer
272
const dependencyCache = new Map<string, string[]>();
273
274
async function getCachedDependencies(filePath: string): Promise<string[]> {
275
if (dependencyCache.has(filePath)) {
276
return dependencyCache.get(filePath)!;
277
}
278
279
const dependencies = await getModuleDependencies(filePath);
280
dependencyCache.set(filePath, dependencies);
281
282
return dependencies;
283
}
284
285
// Invalidate cache when files change
286
function invalidateCache(filePath: string) {
287
dependencyCache.delete(filePath);
288
}
289
```
290
291
### Parallel Analysis
292
293
```typescript
294
import { getModuleDependencies } from "@tailwindcss/node";
295
296
// Analyze multiple entry points in parallel
297
async function analyzeMultipleEntries(entryPoints: string[]) {
298
const results = await Promise.all(
299
entryPoints.map(async (entry) => ({
300
entry,
301
dependencies: await getModuleDependencies(entry)
302
}))
303
);
304
305
return results;
306
}
307
308
const analysis = await analyzeMultipleEntries([
309
"./src/client.ts",
310
"./src/server.ts",
311
"./src/worker.ts"
312
]);
313
```
314
315
## Error Handling
316
317
The function handles various error conditions gracefully:
318
319
```typescript
320
import { getModuleDependencies } from "@tailwindcss/node";
321
322
try {
323
const dependencies = await getModuleDependencies("./nonexistent.ts");
324
} catch (error) {
325
console.error("Analysis failed:", error.message);
326
// File may not exist or may have unresolvable dependencies
327
}
328
329
// Missing dependencies are silently skipped
330
const dependencies = await getModuleDependencies("./file-with-missing-deps.ts");
331
// Returns dependencies that could be resolved, skips missing ones
332
```
333
334
## Limitations
335
336
- Only analyzes **relative imports** (starting with `./` or `../`)
337
- Does not resolve **node_modules** dependencies
338
- **Dynamic imports** with variables are not analyzed
339
- **Conditional requires** with dynamic paths are skipped
340
- **Webpack-style** imports (`require.context`) are not supported