Skip to content

Commit 8bfb37a

Browse files
yewantingyewanting
andauthored
fix : change the logic for monorepo package detect (#120)
* fix:fix the matching of package.json * feat:monorepo 调整成根据 package.json 去判断包 --------- Co-authored-by: yewanting <10244600769@qq.com>
1 parent 2624ffb commit 8bfb37a

File tree

2 files changed

+108
-988
lines changed

2 files changed

+108
-988
lines changed

ts-parser/src/utils/monorepo.ts

Lines changed: 73 additions & 247 deletions
Original file line numberDiff line numberDiff line change
@@ -69,287 +69,113 @@ export interface MonorepoPackage {
6969
export class MonorepoUtils {
7070
/**
7171
* Check if a directory contains a monorepo configuration
72+
* Now determines monorepo by counting package.json files (>= 2 means monorepo)
7273
*/
7374
static isMonorepo(rootPath: string): boolean {
74-
const edenConfigPath = path.join(rootPath, 'eden.monorepo.json');
75-
const pnpmWorkspacePath = path.join(rootPath, 'pnpm-workspace.yaml');
76-
// const yarnWorkspacePath = path.join(rootPath, 'yarn.lock');
77-
const lernaConfigPath = path.join(rootPath, 'lerna.json');
78-
79-
return fs.existsSync(edenConfigPath) ||
80-
fs.existsSync(pnpmWorkspacePath) ||
81-
// fs.existsSync(yarnWorkspacePath) ||
82-
fs.existsSync(lernaConfigPath);
83-
}
84-
85-
/**
86-
* Detect monorepo type and return configuration file path
87-
*/
88-
static detectMonorepoType(rootPath: string): { type: string; configPath: string } | null {
89-
const edenConfigPath = path.join(rootPath, 'eden.monorepo.json');
90-
const pnpmWorkspacePath = path.join(rootPath, 'pnpm-workspace.yaml');
91-
// const yarnWorkspacePath = path.join(rootPath, 'yarn.lock');
92-
const lernaConfigPath = path.join(rootPath, 'lerna.json');
93-
94-
if (fs.existsSync(edenConfigPath)) {
95-
return { type: 'eden', configPath: edenConfigPath };
96-
}
97-
if (fs.existsSync(pnpmWorkspacePath)) {
98-
return { type: 'pnpm', configPath: pnpmWorkspacePath };
99-
}
100-
// if (fs.existsSync(yarnWorkspacePath)) {
101-
// return { type: 'yarn', configPath: yarnWorkspacePath };
102-
// }
103-
if (fs.existsSync(lernaConfigPath)) {
104-
return { type: 'lerna', configPath: lernaConfigPath };
105-
}
106-
107-
return null;
75+
const packageJsonCount = this.countPackageJsonFiles(rootPath);
76+
return packageJsonCount >= 2;
10877
}
10978

11079
/**
111-
* Parse Eden monorepo configuration
80+
* Count the number of package.json files in the directory tree
11281
*/
113-
static parseEdenMonorepoConfig(configPath: string): EdenMonorepoConfig | null {
82+
private static countPackageJsonFiles(rootPath: string): number {
11483
try {
115-
if (!fs.existsSync(configPath)) {
116-
return null;
84+
let count = 0;
85+
const items = fs.readdirSync(rootPath);
86+
87+
for (const item of items) {
88+
const fullPath = path.join(rootPath, item);
89+
const stat = fs.statSync(fullPath);
90+
91+
if (stat.isDirectory()) {
92+
// Skip node_modules and hidden directories
93+
if (item === 'node_modules' || item.startsWith('.')) {
94+
continue;
95+
}
96+
97+
// Check if this directory has a package.json
98+
const packageJsonPath = path.join(fullPath, 'package.json');
99+
if (fs.existsSync(packageJsonPath)) {
100+
count++;
101+
}
102+
103+
// Recursively count in subdirectories
104+
count += this.countPackageJsonFiles(fullPath);
105+
}
117106
}
118-
119-
const configContent = fs.readFileSync(configPath, 'utf-8');
120-
const config: EdenMonorepoConfig = JSON.parse(configContent);
121-
122-
return config;
107+
108+
return count;
123109
} catch (error) {
124-
console.warn(`Failed to parse Eden monorepo config at ${configPath}:`, error);
125-
return null;
110+
console.warn(`Error counting package.json files in ${rootPath}:`, error);
111+
return 0;
126112
}
127113
}
128114

129115
/**
130-
* Get packages from Eden monorepo configuration
131-
* Supports packages array, workspaces array, and pnpmWorkspace formats
132-
* pnpmWorkspace has the highest priority (emo >= 3.6.0)
116+
* Get all packages from a monorepo
117+
* Unified approach: discover packages by package.json files only
133118
*/
134-
static getEdenPackages(rootPath: string, config: EdenMonorepoConfig): MonorepoPackage[] {
135-
const packages: MonorepoPackage[] = [];
136-
137-
if (config.pnpmWorkspace && config.pnpmWorkspace.packages && config.pnpmWorkspace.packages.length > 0) {
138-
for (const workspace of config.pnpmWorkspace.packages) {
139-
const workspacePackages = this.expandWorkspacePattern(rootPath, workspace);
140-
packages.push(...workspacePackages);
141-
}
142-
return packages; // Return early if pnpmWorkspace is configured
143-
}
144-
145-
// Handle new workspaces array format
146-
if (config.workspaces && config.workspaces.length > 0) {
147-
for (const workspace of config.workspaces) {
148-
const workspacePackages = this.expandWorkspacePattern(rootPath, workspace);
149-
packages.push(...workspacePackages);
150-
}
151-
}
152-
153-
// Handle legacy packages array format
154-
if (config.packages && config.packages.length > 0) {
155-
for (const pkg of config.packages) {
156-
const absolutePath = path.resolve(rootPath, pkg.path);
119+
static getMonorepoPackages(rootPath: string): MonorepoPackage[] {
120+
// Simply use generic package discovery, ignoring monorepo type
121+
return this.getGenericPackages(rootPath);
122+
}
157123

158-
// Check if package directory exists
159-
if (fs.existsSync(absolutePath)) {
160-
// Try to get package name from package.json
161-
let packageName: string | undefined;
162-
const packageJsonPath = path.join(absolutePath, 'package.json');
163124

164-
if (fs.existsSync(packageJsonPath)) {
165-
try {
166-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
167-
packageName = packageJson.name;
168-
} catch (error) {
169-
console.warn(`Failed to parse package.json at ${packageJsonPath}:`, error);
170-
}
171-
}
172-
173-
packages.push({
174-
path: pkg.path,
175-
absolutePath,
176-
shouldPublish: pkg.shouldPublish ?? false,
177-
name: packageName
178-
});
179-
} else {
180-
console.warn(`Package directory does not exist: ${absolutePath}`);
181-
}
182-
}
183-
}
184125

126+
/**
127+
* Get packages by discovering package.json files (generic approach)
128+
*/
129+
private static getGenericPackages(rootPath: string): MonorepoPackage[] {
130+
const packages: MonorepoPackage[] = [];
131+
this.discoverPackagesRecursive(rootPath, rootPath, packages);
185132
return packages;
186133
}
187134

188135
/**
189-
* Expand workspace pattern to find actual packages
190-
* Supports glob patterns like "packages/*", "apps/*", etc.
136+
* Recursively discover packages by finding package.json files
191137
*/
192-
private static expandWorkspacePattern(rootPath: string, pattern: string): MonorepoPackage[] {
193-
const packages: MonorepoPackage[] = [];
194-
138+
private static discoverPackagesRecursive(rootPath: string, currentDir: string, packages: MonorepoPackage[]): void {
195139
try {
196-
// Handle glob patterns
197-
if (pattern.includes('*')) {
198-
const basePath = pattern.replace('/*', '');
199-
const baseDir = path.resolve(rootPath, basePath);
200-
201-
if (fs.existsSync(baseDir)) {
202-
const entries = fs.readdirSync(baseDir, { withFileTypes: true });
203-
204-
for (const entry of entries) {
205-
if (entry.isDirectory()) {
206-
const packagePath = path.join(basePath, entry.name);
207-
const absolutePath = path.resolve(rootPath, packagePath);
208-
const packageJsonPath = path.join(absolutePath, 'package.json');
209-
210-
// Only include directories that have package.json
211-
if (fs.existsSync(packageJsonPath)) {
212-
let packageName: string | undefined;
213-
214-
try {
215-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
216-
packageName = packageJson.name;
217-
} catch (error) {
218-
console.warn(`Failed to parse package.json at ${packageJsonPath}:`, error);
219-
}
220-
221-
packages.push({
222-
path: packagePath,
223-
absolutePath,
224-
shouldPublish: false, // Default to false for workspace packages
225-
name: packageName
226-
});
227-
}
228-
}
229-
}
140+
const items = fs.readdirSync(currentDir, { withFileTypes: true });
141+
142+
for (const item of items) {
143+
if (!item.isDirectory()) {
144+
continue;
230145
}
231-
} else {
232-
// Handle exact path
233-
const absolutePath = path.resolve(rootPath, pattern);
234-
const packageJsonPath = path.join(absolutePath, 'package.json');
235-
146+
147+
const dirName = item.name;
148+
const fullPath = path.join(currentDir, dirName);
149+
150+
// Skip node_modules and hidden directories
151+
if (dirName === 'node_modules' || dirName.startsWith('.')) {
152+
continue;
153+
}
154+
155+
// Check if this directory has a package.json
156+
const packageJsonPath = path.join(fullPath, 'package.json');
236157
if (fs.existsSync(packageJsonPath)) {
237-
let packageName: string | undefined;
238-
239158
try {
240159
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
241-
packageName = packageJson.name;
160+
const relativePath = path.relative(rootPath, fullPath);
161+
162+
packages.push({
163+
path: relativePath,
164+
absolutePath: fullPath,
165+
shouldPublish: false, // Default to false for generic discovery
166+
name: packageJson.name
167+
});
242168
} catch (error) {
243169
console.warn(`Failed to parse package.json at ${packageJsonPath}:`, error);
244170
}
245-
246-
packages.push({
247-
path: pattern,
248-
absolutePath,
249-
shouldPublish: false,
250-
name: packageName
251-
});
252171
}
172+
173+
// Recursively search in subdirectories
174+
this.discoverPackagesRecursive(rootPath, fullPath, packages);
253175
}
254176
} catch (error) {
255-
console.warn(`Failed to expand workspace pattern "${pattern}":`, error);
177+
console.warn(`Error discovering packages in ${currentDir}:`, error);
256178
}
257-
258-
return packages;
259179
}
260180

261-
/**
262-
* Get all packages from a monorepo
263-
*/
264-
static getMonorepoPackages(rootPath: string): MonorepoPackage[] {
265-
const monorepoInfo = this.detectMonorepoType(rootPath);
266-
267-
if (!monorepoInfo) {
268-
return [];
269-
}
270-
271-
switch (monorepoInfo.type) {
272-
case 'eden': {
273-
const config = this.parseEdenMonorepoConfig(monorepoInfo.configPath);
274-
if (config) {
275-
return this.getEdenPackages(rootPath, config);
276-
}
277-
break;
278-
}
279-
case 'pnpm': {
280-
const configContent = fs.readFileSync(monorepoInfo.configPath, 'utf-8');
281-
const packages: MonorepoPackage[] = [];
282-
const lines = configContent.split('\n');
283-
let inPackages = false;
284-
for (const line of lines) {
285-
if (line.startsWith('packages:')) {
286-
inPackages = true;
287-
continue;
288-
}
289-
if (inPackages && line.trim().startsWith('-')) {
290-
let glob = line.trim().substring(1).trim().replace(/'/g, '').replace(/"/g, '');
291-
if (glob.endsWith('/*')) {
292-
glob = glob.slice(0, -2);
293-
}
294-
let packageDir = path.join(rootPath, glob);
295-
if (fs.existsSync(packageDir) && fs.statSync(packageDir).isDirectory()) {
296-
const packageNames = fs.readdirSync(packageDir);
297-
packageNames.push(".");
298-
for (const pkgName of packageNames) {
299-
const pkgAbsolutePath = path.join(packageDir, pkgName);
300-
if (fs.statSync(pkgAbsolutePath).isDirectory()) {
301-
const pkgRelativePath = path.relative(rootPath, pkgAbsolutePath);
302-
let packageName: string | undefined;
303-
const packageJsonPath = path.join(pkgAbsolutePath, 'package.json');
304-
if (fs.existsSync(packageJsonPath)) {
305-
try {
306-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
307-
packageName = packageJson.name;
308-
packages.push({
309-
path: pkgRelativePath,
310-
absolutePath: pkgAbsolutePath,
311-
shouldPublish: false, // Cannot determine from pnpm-workspace.yaml
312-
name: packageName
313-
});
314-
} catch (error) {
315-
console.warn(`Failed to parse package.json at ${packageJsonPath}:`, error);
316-
}
317-
}
318-
}
319-
}
320-
}
321-
} else if (inPackages && !/^\s*$/.test(line) && !/^\s+-/.test(line)) {
322-
// We are out of the packages section if the line is not empty and not a package entry
323-
break;
324-
}
325-
}
326-
console.log('pnpm packages:', packages);
327-
return packages;
328-
}
329-
// TODO: Add support for other monorepo types (yarn, lerna)
330-
default:
331-
console.warn(`Monorepo type '${monorepoInfo.type}' is not yet supported`);
332-
break;
333-
}
334-
335-
return [];
336-
}
337-
338-
/**
339-
* Check if a path is within any of the monorepo packages
340-
*/
341-
static findPackageForPath(filePath: string, packages: MonorepoPackage[]): MonorepoPackage | null {
342-
const absoluteFilePath = path.resolve(filePath);
343-
344-
for (const pkg of packages) {
345-
if (absoluteFilePath.startsWith(pkg.absolutePath + path.sep) ||
346-
absoluteFilePath === pkg.absolutePath) {
347-
return pkg;
348-
}
349-
}
350-
351-
return null;
352-
}
353-
354-
355181
}

0 commit comments

Comments
 (0)