Skip to content

Commit e4d256d

Browse files
committed
JavaScript: Add missing ParseProject#relativeTo parameter
When calling `JavaScriptRewriteRpc#parseProject()` we need a `relativeTo` parameter so that we can properly support `package.json` files in nested subdirectories of Git repos.
1 parent a09a04d commit e4d256d

File tree

4 files changed

+67
-21
lines changed

4 files changed

+67
-21
lines changed

rewrite-javascript/rewrite/src/javascript/project-parser.ts

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,13 @@ export interface ProjectParserOptions {
7777
* If not provided, all discovered files are parsed.
7878
*/
7979
fileFilter?: (absolutePath: string) => boolean;
80+
81+
/**
82+
* Optional path to make source file paths relative to.
83+
* If not specified, paths are relative to projectPath.
84+
* Use this when parsing a subdirectory but wanting paths relative to the repository root.
85+
*/
86+
relativeTo?: string;
8087
}
8188

8289
/**
@@ -153,6 +160,7 @@ const TEXT_CONFIG_FILES = new Set([
153160
*/
154161
export class ProjectParser {
155162
private readonly projectPath: string;
163+
private readonly relativeTo: string;
156164
private readonly exclusions: string[];
157165
private readonly ctx: ExecutionContext;
158166
private readonly useGit: boolean;
@@ -162,6 +170,7 @@ export class ProjectParser {
162170

163171
constructor(projectPath: string, options: ProjectParserOptions = {}) {
164172
this.projectPath = path.resolve(projectPath);
173+
this.relativeTo = options.relativeTo ? path.resolve(options.relativeTo) : this.projectPath;
165174
this.exclusions = options.exclusions ?? DEFAULT_EXCLUSIONS;
166175
this.ctx = options.ctx ?? new ExecutionContext();
167176
this.useGit = options.useGit ?? this.isGitRepository();
@@ -230,7 +239,7 @@ export class ProjectParser {
230239
this.log(`Parsing ${discovered.packageJsonFiles.length} package.json files...`);
231240
const parser = Parsers.createParser("packageJson", {
232241
ctx: this.ctx,
233-
relativeTo: this.projectPath
242+
relativeTo: this.relativeTo
234243
});
235244
for await (const sf of parser.parse(...discovered.packageJsonFiles)) {
236245
current++;
@@ -244,7 +253,7 @@ export class ProjectParser {
244253
this.log(`Parsing ${discovered.lockFiles.json.length} JSON lock files...`);
245254
const parser = Parsers.createParser("json", {
246255
ctx: this.ctx,
247-
relativeTo: this.projectPath
256+
relativeTo: this.relativeTo
248257
});
249258
for await (const sf of parser.parse(...discovered.lockFiles.json)) {
250259
current++;
@@ -258,7 +267,7 @@ export class ProjectParser {
258267
this.log(`Parsing ${discovered.lockFiles.yaml.length} YAML lock files...`);
259268
const parser = Parsers.createParser("yaml", {
260269
ctx: this.ctx,
261-
relativeTo: this.projectPath
270+
relativeTo: this.relativeTo
262271
});
263272
for await (const sf of parser.parse(...discovered.lockFiles.yaml)) {
264273
current++;
@@ -272,7 +281,7 @@ export class ProjectParser {
272281
this.log(`Parsing ${discovered.lockFiles.text.length} text lock files...`);
273282
const parser = Parsers.createParser("plainText", {
274283
ctx: this.ctx,
275-
relativeTo: this.projectPath
284+
relativeTo: this.relativeTo
276285
});
277286
for await (const sf of parser.parse(...discovered.lockFiles.text)) {
278287
current++;
@@ -286,7 +295,7 @@ export class ProjectParser {
286295
this.log(`Parsing ${discovered.jsFiles.length} JavaScript/TypeScript files...`);
287296
const parser = Parsers.createParser("javascript", {
288297
ctx: this.ctx,
289-
relativeTo: this.projectPath
298+
relativeTo: this.relativeTo
290299
});
291300

292301
// Check if Prettier is available
@@ -299,7 +308,7 @@ export class ProjectParser {
299308
this.onProgress?.("parsing", current, totalFiles, sf.sourcePath);
300309

301310
const prettierMarker = await prettierLoader.getConfigMarker(
302-
path.join(this.projectPath, sf.sourcePath)
311+
path.join(this.relativeTo, sf.sourcePath)
303312
);
304313
if (prettierMarker) {
305314
yield produce(sf, draft => {
@@ -352,7 +361,7 @@ export class ProjectParser {
352361
this.log(`Parsing ${discovered.yamlFiles.length} YAML files...`);
353362
const parser = Parsers.createParser("yaml", {
354363
ctx: this.ctx,
355-
relativeTo: this.projectPath
364+
relativeTo: this.relativeTo
356365
});
357366
for await (const sf of parser.parse(...discovered.yamlFiles)) {
358367
current++;
@@ -366,7 +375,7 @@ export class ProjectParser {
366375
this.log(`Parsing ${discovered.jsonFiles.length} JSON files...`);
367376
const parser = Parsers.createParser("json", {
368377
ctx: this.ctx,
369-
relativeTo: this.projectPath
378+
relativeTo: this.relativeTo
370379
});
371380
for await (const sf of parser.parse(...discovered.jsonFiles)) {
372381
current++;
@@ -380,7 +389,7 @@ export class ProjectParser {
380389
this.log(`Parsing ${discovered.textFiles.length} text config files...`);
381390
const parser = Parsers.createParser("plainText", {
382391
ctx: this.ctx,
383-
relativeTo: this.projectPath
392+
relativeTo: this.relativeTo
384393
});
385394
for await (const sf of parser.parse(...discovered.textFiles)) {
386395
current++;

rewrite-javascript/rewrite/src/rpc/request/parse-project.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,13 @@ export interface ParseProjectResponseItem {
4343
export class ParseProject {
4444
constructor(
4545
private readonly projectPath: string,
46-
private readonly exclusions?: string[]
46+
private readonly exclusions?: string[],
47+
/**
48+
* Optional path to make source file paths relative to.
49+
* If not specified, paths are relative to projectPath.
50+
* Use this when parsing a subdirectory but wanting paths relative to the repository root.
51+
*/
52+
private readonly relativeTo?: string
4753
) {}
4854

4955
static handle(
@@ -61,6 +67,8 @@ export class ParseProject {
6167

6268
const projectPath = path.resolve(request.projectPath);
6369
const exclusions = request.exclusions ?? DEFAULT_EXCLUSIONS;
70+
// Use relativeTo if specified, otherwise default to projectPath
71+
const relativeTo = request.relativeTo ? path.resolve(request.relativeTo) : projectPath;
6472

6573
// Use ProjectParser for file discovery and Prettier detection
6674
const projectParser = new ProjectParser(projectPath, {exclusions});
@@ -74,7 +82,7 @@ export class ParseProject {
7482
if (discovered.packageJsonFiles.length > 0) {
7583
const parser = Parsers.createParser("packageJson", {
7684
ctx,
77-
relativeTo: projectPath
85+
relativeTo
7886
});
7987
const generator = parser.parse(...discovered.packageJsonFiles);
8088

@@ -93,7 +101,7 @@ export class ParseProject {
93101

94102
// Parse JSON lock files
95103
if (discovered.lockFiles.json.length > 0) {
96-
const parser = Parsers.createParser("json", {ctx, relativeTo: projectPath});
104+
const parser = Parsers.createParser("json", {ctx, relativeTo});
97105
const generator = parser.parse(...discovered.lockFiles.json);
98106

99107
for (const _ of discovered.lockFiles.json) {
@@ -111,7 +119,7 @@ export class ParseProject {
111119

112120
// Parse YAML lock files
113121
if (discovered.lockFiles.yaml.length > 0) {
114-
const parser = Parsers.createParser("yaml", {ctx, relativeTo: projectPath});
122+
const parser = Parsers.createParser("yaml", {ctx, relativeTo});
115123
const generator = parser.parse(...discovered.lockFiles.yaml);
116124

117125
for (const _ of discovered.lockFiles.yaml) {
@@ -129,7 +137,7 @@ export class ParseProject {
129137

130138
// Parse text lock files (yarn.lock Classic)
131139
if (discovered.lockFiles.text.length > 0) {
132-
const parser = Parsers.createParser("plainText", {ctx, relativeTo: projectPath});
140+
const parser = Parsers.createParser("plainText", {ctx, relativeTo});
133141
const generator = parser.parse(...discovered.lockFiles.text);
134142

135143
for (const _ of discovered.lockFiles.text) {
@@ -149,7 +157,7 @@ export class ParseProject {
149157
if (discovered.jsFiles.length > 0) {
150158
const parser = Parsers.createParser("javascript", {
151159
ctx,
152-
relativeTo: projectPath
160+
relativeTo
153161
});
154162

155163
// Check if Prettier is available
@@ -211,7 +219,7 @@ export class ParseProject {
211219

212220
// Parse other YAML files
213221
if (discovered.yamlFiles.length > 0) {
214-
const parser = Parsers.createParser("yaml", {ctx, relativeTo: projectPath});
222+
const parser = Parsers.createParser("yaml", {ctx, relativeTo});
215223
const generator = parser.parse(...discovered.yamlFiles);
216224

217225
for (const _ of discovered.yamlFiles) {
@@ -229,7 +237,7 @@ export class ParseProject {
229237

230238
// Parse other JSON files
231239
if (discovered.jsonFiles.length > 0) {
232-
const parser = Parsers.createParser("json", {ctx, relativeTo: projectPath});
240+
const parser = Parsers.createParser("json", {ctx, relativeTo});
233241
const generator = parser.parse(...discovered.jsonFiles);
234242

235243
for (const _ of discovered.jsonFiles) {
@@ -247,7 +255,7 @@ export class ParseProject {
247255

248256
// Parse text config files (.prettierignore, .gitignore, etc.)
249257
if (discovered.textFiles.length > 0) {
250-
const parser = Parsers.createParser("plainText", {ctx, relativeTo: projectPath});
258+
const parser = Parsers.createParser("plainText", {ctx, relativeTo});
251259
const generator = parser.parse(...discovered.textFiles);
252260

253261
for (const _ of discovered.textFiles) {

rewrite-javascript/src/main/java/org/openrewrite/javascript/rpc/JavaScriptRewriteRpc.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ public InstallRecipesResponse installRecipes(String packageName, @Nullable Strin
117117
* @return Stream of parsed source files
118118
*/
119119
public Stream<SourceFile> parseProject(Path projectPath, ExecutionContext ctx) {
120-
return parseProject(projectPath, null, ctx);
120+
return parseProject(projectPath, null, null, ctx);
121121
}
122122

123123
/**
@@ -130,6 +130,22 @@ public Stream<SourceFile> parseProject(Path projectPath, ExecutionContext ctx) {
130130
* @return Stream of parsed source files
131131
*/
132132
public Stream<SourceFile> parseProject(Path projectPath, @Nullable List<String> exclusions, ExecutionContext ctx) {
133+
return parseProject(projectPath, exclusions, null, ctx);
134+
}
135+
136+
/**
137+
* Parses an entire JavaScript/TypeScript project directory.
138+
* Discovers and parses all relevant source files, package.json files, and lock files.
139+
*
140+
* @param projectPath Path to the project directory to parse
141+
* @param exclusions Optional glob patterns to exclude from parsing
142+
* @param relativeTo Optional path to make source file paths relative to. If not specified,
143+
* paths are relative to projectPath. Use this when parsing a subdirectory
144+
* but wanting paths relative to the repository root.
145+
* @param ctx Execution context for parsing
146+
* @return Stream of parsed source files
147+
*/
148+
public Stream<SourceFile> parseProject(Path projectPath, @Nullable List<String> exclusions, @Nullable Path relativeTo, ExecutionContext ctx) {
133149
ParsingEventListener parsingListener = ParsingExecutionContextView.view(ctx).getParsingListener();
134150

135151
return StreamSupport.stream(new Spliterator<SourceFile>() {
@@ -140,7 +156,7 @@ public Stream<SourceFile> parseProject(Path projectPath, @Nullable List<String>
140156
public boolean tryAdvance(Consumer<? super SourceFile> action) {
141157
if (response == null) {
142158
parsingListener.intermediateMessage("Starting project parsing: " + projectPath);
143-
response = send("ParseProject", new ParseProject(projectPath, exclusions), ParseProjectResponse.class);
159+
response = send("ParseProject", new ParseProject(projectPath, exclusions, relativeTo), ParseProjectResponse.class);
144160
parsingListener.intermediateMessage(String.format("Discovered %,d files to parse", response.size()));
145161
}
146162

rewrite-javascript/src/main/java/org/openrewrite/javascript/rpc/ParseProject.java

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,25 @@ class ParseProject implements RpcRequest {
4040
@Nullable
4141
List<String> exclusions;
4242

43+
/**
44+
* Optional path to make source file paths relative to.
45+
* If not specified, paths are relative to projectPath.
46+
* Use this when parsing a subdirectory but wanting paths relative to the repository root.
47+
*/
48+
@Nullable
49+
Path relativeTo;
50+
4351
ParseProject(Path projectPath) {
44-
this(projectPath, null);
52+
this(projectPath, null, null);
4553
}
4654

4755
ParseProject(Path projectPath, @Nullable List<String> exclusions) {
56+
this(projectPath, exclusions, null);
57+
}
58+
59+
ParseProject(Path projectPath, @Nullable List<String> exclusions, @Nullable Path relativeTo) {
4860
this.projectPath = projectPath;
4961
this.exclusions = exclusions;
62+
this.relativeTo = relativeTo;
5063
}
5164
}

0 commit comments

Comments
 (0)