@openrewrite/rewrite 8.83.3 → 8.83.5
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/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/java/rpc.d.ts.map +1 -1
- package/dist/java/rpc.js +2 -0
- package/dist/java/rpc.js.map +1 -1
- package/dist/java/tree.d.ts +1 -0
- package/dist/java/tree.d.ts.map +1 -1
- package/dist/java/tree.js.map +1 -1
- package/dist/java/visitor.d.ts.map +1 -1
- package/dist/java/visitor.js +1 -0
- package/dist/java/visitor.js.map +1 -1
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +8 -0
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/recipes/dependencies.d.ts +63 -0
- package/dist/javascript/recipes/dependencies.d.ts.map +1 -0
- package/dist/javascript/recipes/dependencies.js +49 -0
- package/dist/javascript/recipes/dependencies.js.map +1 -0
- package/dist/javascript/recipes/index.d.ts +1 -4
- package/dist/javascript/recipes/index.d.ts.map +1 -1
- package/dist/javascript/recipes/index.js +1 -4
- package/dist/javascript/recipes/index.js.map +1 -1
- package/dist/rewrite-javascript-version.txt +1 -1
- package/dist/rpc/index.d.ts +1 -0
- package/dist/rpc/index.d.ts.map +1 -1
- package/dist/rpc/index.js +3 -1
- package/dist/rpc/index.js.map +1 -1
- package/dist/rpc/java-recipe.d.ts +33 -0
- package/dist/rpc/java-recipe.d.ts.map +1 -0
- package/dist/rpc/java-recipe.js +61 -0
- package/dist/rpc/java-recipe.js.map +1 -0
- package/dist/rpc/java-rpc-client.d.ts +65 -0
- package/dist/rpc/java-rpc-client.d.ts.map +1 -0
- package/dist/rpc/java-rpc-client.js +221 -0
- package/dist/rpc/java-rpc-client.js.map +1 -0
- package/dist/rpc/rewrite-rpc.d.ts +9 -0
- package/dist/rpc/rewrite-rpc.d.ts.map +1 -1
- package/dist/rpc/rewrite-rpc.js +23 -1
- package/dist/rpc/rewrite-rpc.js.map +1 -1
- package/dist/test/java-rpc.d.ts +46 -0
- package/dist/test/java-rpc.d.ts.map +1 -0
- package/dist/test/java-rpc.js +79 -0
- package/dist/test/java-rpc.js.map +1 -0
- package/package.json +5 -1
- package/src/index.ts +0 -8
- package/src/java/rpc.ts +2 -0
- package/src/java/tree.ts +1 -0
- package/src/java/visitor.ts +1 -0
- package/src/javascript/parser.ts +8 -0
- package/src/javascript/recipes/dependencies.ts +108 -0
- package/src/javascript/recipes/index.ts +1 -4
- package/src/rpc/index.ts +1 -0
- package/src/rpc/java-recipe.ts +64 -0
- package/src/rpc/java-rpc-client.ts +220 -0
- package/src/rpc/rewrite-rpc.ts +34 -6
- package/src/test/java-rpc.ts +93 -0
- package/dist/javascript/recipes/add-dependency.d.ts +0 -61
- package/dist/javascript/recipes/add-dependency.d.ts.map +0 -1
- package/dist/javascript/recipes/add-dependency.js +0 -430
- package/dist/javascript/recipes/add-dependency.js.map +0 -1
- package/dist/javascript/recipes/remove-dependency.d.ts +0 -29
- package/dist/javascript/recipes/remove-dependency.d.ts.map +0 -1
- package/dist/javascript/recipes/remove-dependency.js +0 -261
- package/dist/javascript/recipes/remove-dependency.js.map +0 -1
- package/dist/javascript/recipes/upgrade-dependency-version.d.ts +0 -74
- package/dist/javascript/recipes/upgrade-dependency-version.d.ts.map +0 -1
- package/dist/javascript/recipes/upgrade-dependency-version.js +0 -387
- package/dist/javascript/recipes/upgrade-dependency-version.js.map +0 -1
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.d.ts +0 -68
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.d.ts.map +0 -1
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.js +0 -307
- package/dist/javascript/recipes/upgrade-transitive-dependency-version.js.map +0 -1
- package/src/javascript/recipes/add-dependency.ts +0 -549
- package/src/javascript/recipes/remove-dependency.ts +0 -345
- package/src/javascript/recipes/upgrade-dependency-version.ts +0 -486
- package/src/javascript/recipes/upgrade-transitive-dependency-version.ts +0 -403
|
@@ -1,549 +0,0 @@
|
|
|
1
|
-
/*
|
|
2
|
-
* Copyright 2025 the original author or authors.
|
|
3
|
-
* <p>
|
|
4
|
-
* Licensed under the Moderne Source Available License (the "License");
|
|
5
|
-
* you may not use this file except in compliance with the License.
|
|
6
|
-
* You may obtain a copy of the License at
|
|
7
|
-
* <p>
|
|
8
|
-
* https://docs.moderne.io/licensing/moderne-source-available-license
|
|
9
|
-
* <p>
|
|
10
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
11
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
-
* See the License for the specific language governing permissions and
|
|
14
|
-
* limitations under the License.
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import {Option, ScanningRecipe} from "../../recipe";
|
|
18
|
-
import {ExecutionContext} from "../../execution";
|
|
19
|
-
import {TreeVisitor} from "../../visitor";
|
|
20
|
-
import {Tree} from "../../tree";
|
|
21
|
-
import {detectIndent, getMemberKeyName, isJson, isObject, Json, JsonVisitor, rightPadded, space} from "../../json";
|
|
22
|
-
import {isDocuments, isYaml, Yaml} from "../../yaml";
|
|
23
|
-
import {isPlainText, PlainText} from "../../text";
|
|
24
|
-
import {
|
|
25
|
-
allDependencyScopes,
|
|
26
|
-
DependencyScope,
|
|
27
|
-
findNodeResolutionResult,
|
|
28
|
-
PackageManager,
|
|
29
|
-
serializeNpmrcConfigs
|
|
30
|
-
} from "../node-resolution-result";
|
|
31
|
-
import {emptyMarkers, markupWarn} from "../../markers";
|
|
32
|
-
import {TreePrinters} from "../../print";
|
|
33
|
-
import {
|
|
34
|
-
createDependencyRecipeAccumulator,
|
|
35
|
-
createLockFileEditor,
|
|
36
|
-
DependencyRecipeAccumulator,
|
|
37
|
-
getAllLockFileNames,
|
|
38
|
-
getLockFileName,
|
|
39
|
-
parseLockFileContent,
|
|
40
|
-
runInstallIfNeeded,
|
|
41
|
-
runInstallInTempDir,
|
|
42
|
-
storeInstallResult,
|
|
43
|
-
updateNodeResolutionMarker
|
|
44
|
-
} from "../package-manager";
|
|
45
|
-
import {randomId} from "../../uuid";
|
|
46
|
-
import * as path from "path";
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Information about a project that needs updating
|
|
50
|
-
*/
|
|
51
|
-
interface ProjectUpdateInfo {
|
|
52
|
-
/** Relative path to package.json (from source root) */
|
|
53
|
-
packageJsonPath: string;
|
|
54
|
-
/** Original package.json content */
|
|
55
|
-
originalPackageJson: string;
|
|
56
|
-
/** The scope where the dependency should be added */
|
|
57
|
-
dependencyScope: DependencyScope;
|
|
58
|
-
/** Version constraint to apply */
|
|
59
|
-
newVersion: string;
|
|
60
|
-
/** The package manager used by this project */
|
|
61
|
-
packageManager: PackageManager;
|
|
62
|
-
/** Config file contents extracted from the project (e.g., .npmrc) */
|
|
63
|
-
configFiles?: Record<string, string>;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
interface Accumulator extends DependencyRecipeAccumulator<ProjectUpdateInfo> {
|
|
67
|
-
/** Original lock file content, keyed by lock file path */
|
|
68
|
-
originalLockFiles: Map<string, string>;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Adds a new dependency to package.json and updates the lock file.
|
|
73
|
-
*
|
|
74
|
-
* This recipe:
|
|
75
|
-
* 1. Finds package.json files that don't already have the specified dependency
|
|
76
|
-
* 2. Adds the dependency to the specified scope (defaults to 'dependencies')
|
|
77
|
-
* 3. Runs the package manager to update the lock file
|
|
78
|
-
* 4. Updates the NodeResolutionResult marker with new dependency info
|
|
79
|
-
*
|
|
80
|
-
* If the dependency already exists in any scope, no changes are made.
|
|
81
|
-
* This matches the behavior of org.openrewrite.maven.AddDependency.
|
|
82
|
-
*/
|
|
83
|
-
export class AddDependency extends ScanningRecipe<Accumulator> {
|
|
84
|
-
readonly name = "org.openrewrite.javascript.dependencies.add-dependency";
|
|
85
|
-
readonly displayName = "Add npm dependency";
|
|
86
|
-
readonly description = "Adds a new dependency to `package.json` and updates the lock file by running the package manager.";
|
|
87
|
-
|
|
88
|
-
@Option({
|
|
89
|
-
displayName: "Package name",
|
|
90
|
-
description: "The name of the npm package to add (e.g., `lodash`, `@types/node`)",
|
|
91
|
-
example: "lodash"
|
|
92
|
-
})
|
|
93
|
-
packageName!: string;
|
|
94
|
-
|
|
95
|
-
@Option({
|
|
96
|
-
displayName: "Version",
|
|
97
|
-
description: "The version constraint to set (e.g., `^5.0.0`, `~2.1.0`, `3.0.0`)",
|
|
98
|
-
example: "^5.0.0"
|
|
99
|
-
})
|
|
100
|
-
version!: string;
|
|
101
|
-
|
|
102
|
-
@Option({
|
|
103
|
-
displayName: "Scope",
|
|
104
|
-
description: "The dependency scope: `dependencies`, `devDependencies`, `peerDependencies`, or `optionalDependencies`",
|
|
105
|
-
example: "dependencies",
|
|
106
|
-
required: false
|
|
107
|
-
})
|
|
108
|
-
scope?: DependencyScope;
|
|
109
|
-
|
|
110
|
-
initialValue(_ctx: ExecutionContext): Accumulator {
|
|
111
|
-
return {
|
|
112
|
-
...createDependencyRecipeAccumulator<ProjectUpdateInfo>(),
|
|
113
|
-
originalLockFiles: new Map()
|
|
114
|
-
};
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
private getTargetScope(): DependencyScope {
|
|
118
|
-
return this.scope ?? 'dependencies';
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
async scanner(acc: Accumulator): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
122
|
-
const recipe = this;
|
|
123
|
-
const LOCK_FILE_NAMES = getAllLockFileNames();
|
|
124
|
-
|
|
125
|
-
return new class extends TreeVisitor<Tree, ExecutionContext> {
|
|
126
|
-
protected async accept(tree: Tree, ctx: ExecutionContext): Promise<Tree | undefined> {
|
|
127
|
-
// Handle JSON documents (package.json and JSON lock files)
|
|
128
|
-
if (isJson(tree) && tree.kind === Json.Kind.Document) {
|
|
129
|
-
return this.handleJsonDocument(tree as Json.Document, ctx);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Handle YAML documents (pnpm-lock.yaml)
|
|
133
|
-
if (isYaml(tree) && isDocuments(tree)) {
|
|
134
|
-
return this.handleYamlDocument(tree, ctx);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
// Handle PlainText files (yarn.lock for Yarn Classic)
|
|
138
|
-
if (isPlainText(tree)) {
|
|
139
|
-
return this.handlePlainTextDocument(tree as PlainText, ctx);
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return tree;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
private async handleJsonDocument(doc: Json.Document, _ctx: ExecutionContext): Promise<Json | undefined> {
|
|
146
|
-
const basename = path.basename(doc.sourcePath);
|
|
147
|
-
|
|
148
|
-
// Capture JSON lock file content (package-lock.json, bun.lock)
|
|
149
|
-
if (LOCK_FILE_NAMES.includes(basename)) {
|
|
150
|
-
acc.originalLockFiles.set(doc.sourcePath, await TreePrinters.print(doc));
|
|
151
|
-
return doc;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Only process package.json files for dependency analysis
|
|
155
|
-
if (!doc.sourcePath.endsWith('package.json')) {
|
|
156
|
-
return doc;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
const marker = findNodeResolutionResult(doc);
|
|
160
|
-
if (!marker) {
|
|
161
|
-
return doc;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// Check if dependency already exists in any scope
|
|
165
|
-
for (const scope of allDependencyScopes) {
|
|
166
|
-
const deps = marker[scope];
|
|
167
|
-
if (deps?.some(d => d.name === recipe.packageName)) {
|
|
168
|
-
// Dependency already exists, don't add again
|
|
169
|
-
return doc;
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const pm = marker.packageManager ?? PackageManager.Npm;
|
|
174
|
-
|
|
175
|
-
// Serialize npmrc configs from marker using requested scopes
|
|
176
|
-
const configFiles: Record<string, string> = {};
|
|
177
|
-
const npmrcContent = serializeNpmrcConfigs(marker.npmrcConfigs);
|
|
178
|
-
if (npmrcContent) {
|
|
179
|
-
configFiles['.npmrc'] = npmrcContent;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
acc.projectsToUpdate.set(doc.sourcePath, {
|
|
183
|
-
packageJsonPath: doc.sourcePath,
|
|
184
|
-
originalPackageJson: await TreePrinters.print(doc),
|
|
185
|
-
dependencyScope: recipe.getTargetScope(),
|
|
186
|
-
newVersion: recipe.version,
|
|
187
|
-
packageManager: pm,
|
|
188
|
-
configFiles: Object.keys(configFiles).length > 0 ? configFiles : undefined
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
return doc;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
private async handleYamlDocument(docs: Yaml.Documents, _ctx: ExecutionContext): Promise<Yaml.Documents | undefined> {
|
|
195
|
-
const basename = path.basename(docs.sourcePath);
|
|
196
|
-
if (LOCK_FILE_NAMES.includes(basename)) {
|
|
197
|
-
acc.originalLockFiles.set(docs.sourcePath, await TreePrinters.print(docs));
|
|
198
|
-
}
|
|
199
|
-
return docs;
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
private async handlePlainTextDocument(text: PlainText, _ctx: ExecutionContext): Promise<PlainText | undefined> {
|
|
203
|
-
const basename = path.basename(text.sourcePath);
|
|
204
|
-
if (LOCK_FILE_NAMES.includes(basename)) {
|
|
205
|
-
acc.originalLockFiles.set(text.sourcePath, await TreePrinters.print(text));
|
|
206
|
-
}
|
|
207
|
-
return text;
|
|
208
|
-
}
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
async editorWithData(acc: Accumulator): Promise<TreeVisitor<any, ExecutionContext>> {
|
|
213
|
-
const recipe = this;
|
|
214
|
-
|
|
215
|
-
// Create JSON visitor that handles both package.json and JSON lock files
|
|
216
|
-
const jsonEditor = new class extends JsonVisitor<ExecutionContext> {
|
|
217
|
-
protected async visitDocument(doc: Json.Document, ctx: ExecutionContext): Promise<Json | undefined> {
|
|
218
|
-
const sourcePath = doc.sourcePath;
|
|
219
|
-
|
|
220
|
-
// Handle package.json files
|
|
221
|
-
if (sourcePath.endsWith('package.json')) {
|
|
222
|
-
const updateInfo = acc.projectsToUpdate.get(sourcePath);
|
|
223
|
-
if (!updateInfo) {
|
|
224
|
-
return doc; // This package.json doesn't need updating
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
// Run package manager install if needed, check for failure
|
|
228
|
-
const failureMessage = await runInstallIfNeeded(sourcePath, acc, () =>
|
|
229
|
-
recipe.runPackageManagerInstall(acc, updateInfo, ctx)
|
|
230
|
-
);
|
|
231
|
-
if (failureMessage) {
|
|
232
|
-
return markupWarn(
|
|
233
|
-
doc,
|
|
234
|
-
`Failed to add ${recipe.packageName} to ${recipe.version}`,
|
|
235
|
-
failureMessage
|
|
236
|
-
);
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Add the dependency to the JSON AST (preserves formatting)
|
|
240
|
-
const visitor = new AddDependencyVisitor(
|
|
241
|
-
recipe.packageName,
|
|
242
|
-
recipe.version,
|
|
243
|
-
updateInfo.dependencyScope
|
|
244
|
-
);
|
|
245
|
-
const modifiedDoc = await visitor.visit(doc, undefined) as Json.Document;
|
|
246
|
-
|
|
247
|
-
// Update the NodeResolutionResult marker
|
|
248
|
-
return updateNodeResolutionMarker(modifiedDoc, updateInfo, acc);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Handle JSON lock files (package-lock.json, bun.lock)
|
|
252
|
-
const lockFileName = path.basename(sourcePath);
|
|
253
|
-
if (getAllLockFileNames().includes(lockFileName)) {
|
|
254
|
-
const updatedLockContent = acc.updatedLockFiles.get(sourcePath);
|
|
255
|
-
if (updatedLockContent) {
|
|
256
|
-
const parsed = await parseLockFileContent(updatedLockContent, sourcePath, lockFileName) as Json.Document;
|
|
257
|
-
// Preserve original ID for RPC compatibility
|
|
258
|
-
return {
|
|
259
|
-
...doc,
|
|
260
|
-
value: parsed.value,
|
|
261
|
-
eof: parsed.eof
|
|
262
|
-
} as Json.Document;
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
return doc;
|
|
267
|
-
}
|
|
268
|
-
};
|
|
269
|
-
|
|
270
|
-
// Return composite visitor that handles both JSON and YAML lock files
|
|
271
|
-
return createLockFileEditor(jsonEditor, acc);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
/**
|
|
275
|
-
* Runs the package manager in a temporary directory to update the lock file.
|
|
276
|
-
* All file contents are provided from in-memory sources (SourceFiles), not read from disk.
|
|
277
|
-
*/
|
|
278
|
-
private async runPackageManagerInstall(
|
|
279
|
-
acc: Accumulator,
|
|
280
|
-
updateInfo: ProjectUpdateInfo,
|
|
281
|
-
_ctx: ExecutionContext
|
|
282
|
-
): Promise<void> {
|
|
283
|
-
// Create modified package.json with the new dependency
|
|
284
|
-
const modifiedPackageJson = this.createModifiedPackageJson(
|
|
285
|
-
updateInfo.originalPackageJson,
|
|
286
|
-
updateInfo.dependencyScope
|
|
287
|
-
);
|
|
288
|
-
|
|
289
|
-
// Get the lock file path based on package manager
|
|
290
|
-
const lockFileName = getLockFileName(updateInfo.packageManager);
|
|
291
|
-
const packageJsonDir = path.dirname(updateInfo.packageJsonPath);
|
|
292
|
-
const lockFilePath = packageJsonDir === '.'
|
|
293
|
-
? lockFileName
|
|
294
|
-
: path.join(packageJsonDir, lockFileName);
|
|
295
|
-
|
|
296
|
-
// Look up the original lock file content from captured SourceFiles
|
|
297
|
-
const originalLockFileContent = acc.originalLockFiles.get(lockFilePath);
|
|
298
|
-
|
|
299
|
-
const result = await runInstallInTempDir(
|
|
300
|
-
updateInfo.packageManager,
|
|
301
|
-
modifiedPackageJson,
|
|
302
|
-
{
|
|
303
|
-
originalLockFileContent,
|
|
304
|
-
configFiles: updateInfo.configFiles
|
|
305
|
-
}
|
|
306
|
-
);
|
|
307
|
-
|
|
308
|
-
storeInstallResult(result, acc, updateInfo, modifiedPackageJson);
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Creates a modified package.json with the new dependency added.
|
|
313
|
-
*/
|
|
314
|
-
private createModifiedPackageJson(
|
|
315
|
-
originalContent: string,
|
|
316
|
-
scope: DependencyScope
|
|
317
|
-
): string {
|
|
318
|
-
const packageJson = JSON.parse(originalContent);
|
|
319
|
-
|
|
320
|
-
if (!packageJson[scope]) {
|
|
321
|
-
packageJson[scope] = {};
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
packageJson[scope][this.packageName] = this.version;
|
|
325
|
-
|
|
326
|
-
return JSON.stringify(packageJson, null, 2);
|
|
327
|
-
}
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
/**
|
|
331
|
-
* Visitor that adds a new dependency to a specific scope in package.json.
|
|
332
|
-
* If the scope doesn't exist, it creates it.
|
|
333
|
-
*/
|
|
334
|
-
class AddDependencyVisitor extends JsonVisitor<void> {
|
|
335
|
-
private readonly packageName: string;
|
|
336
|
-
private readonly version: string;
|
|
337
|
-
private readonly targetScope: DependencyScope;
|
|
338
|
-
private scopeFound = false;
|
|
339
|
-
private dependencyAdded = false;
|
|
340
|
-
private baseIndent: string = ' '; // Will be detected from document
|
|
341
|
-
|
|
342
|
-
constructor(packageName: string, version: string, targetScope: DependencyScope) {
|
|
343
|
-
super();
|
|
344
|
-
this.packageName = packageName;
|
|
345
|
-
this.version = version;
|
|
346
|
-
this.targetScope = targetScope;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
protected async visitDocument(doc: Json.Document, p: void): Promise<Json | undefined> {
|
|
350
|
-
// Detect indentation from the document
|
|
351
|
-
this.baseIndent = detectIndent(doc);
|
|
352
|
-
|
|
353
|
-
const result = await super.visitDocument(doc, p) as Json.Document;
|
|
354
|
-
|
|
355
|
-
// If scope wasn't found, we need to add it to the document
|
|
356
|
-
if (!this.scopeFound && !this.dependencyAdded) {
|
|
357
|
-
return this.addScopeToDocument(result);
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
return result;
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
protected async visitMember(member: Json.Member, p: void): Promise<Json | undefined> {
|
|
364
|
-
const keyName = getMemberKeyName(member);
|
|
365
|
-
|
|
366
|
-
if (keyName === this.targetScope) {
|
|
367
|
-
this.scopeFound = true;
|
|
368
|
-
return this.addDependencyToScope(member);
|
|
369
|
-
}
|
|
370
|
-
|
|
371
|
-
return super.visitMember(member, p);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
/**
|
|
375
|
-
* Adds the dependency to an existing scope object.
|
|
376
|
-
*/
|
|
377
|
-
private addDependencyToScope(scopeMember: Json.Member): Json.Member {
|
|
378
|
-
const value = scopeMember.value;
|
|
379
|
-
|
|
380
|
-
if (!isObject(value)) {
|
|
381
|
-
return scopeMember;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const members = [...(value.members || [])];
|
|
385
|
-
|
|
386
|
-
// Determine the closing whitespace (goes before closing brace after last element)
|
|
387
|
-
let closingWhitespace = '\n ';
|
|
388
|
-
if (members.length > 0) {
|
|
389
|
-
const lastMember = members[members.length - 1];
|
|
390
|
-
closingWhitespace = lastMember.after.whitespace;
|
|
391
|
-
// Update the last member's after to be empty (comma will be added by printer)
|
|
392
|
-
members[members.length - 1] = {
|
|
393
|
-
...lastMember,
|
|
394
|
-
after: space('')
|
|
395
|
-
};
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
const newMember = this.createDependencyMemberWithAfter(closingWhitespace);
|
|
399
|
-
this.dependencyAdded = true;
|
|
400
|
-
|
|
401
|
-
members.push(newMember);
|
|
402
|
-
|
|
403
|
-
return {
|
|
404
|
-
...scopeMember,
|
|
405
|
-
value: {
|
|
406
|
-
...value,
|
|
407
|
-
members
|
|
408
|
-
} as Json.Object
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Creates a new dependency member node.
|
|
414
|
-
*/
|
|
415
|
-
private createDependencyMemberWithAfter(afterWhitespace: string): Json.RightPadded<Json.Member> {
|
|
416
|
-
// Dependencies inside a scope are indented twice (e.g., 8 spaces if base is 4)
|
|
417
|
-
const depIndent = this.baseIndent + this.baseIndent;
|
|
418
|
-
|
|
419
|
-
const keyLiteral: Json.Literal = {
|
|
420
|
-
kind: Json.Kind.Literal,
|
|
421
|
-
id: randomId(),
|
|
422
|
-
prefix: space('\n' + depIndent),
|
|
423
|
-
markers: emptyMarkers,
|
|
424
|
-
source: `"${this.packageName}"`,
|
|
425
|
-
value: this.packageName
|
|
426
|
-
};
|
|
427
|
-
|
|
428
|
-
const valueLiteral: Json.Literal = {
|
|
429
|
-
kind: Json.Kind.Literal,
|
|
430
|
-
id: randomId(),
|
|
431
|
-
prefix: space(' '),
|
|
432
|
-
markers: emptyMarkers,
|
|
433
|
-
source: `"${this.version}"`,
|
|
434
|
-
value: this.version
|
|
435
|
-
};
|
|
436
|
-
|
|
437
|
-
const member: Json.Member = {
|
|
438
|
-
kind: Json.Kind.Member,
|
|
439
|
-
id: randomId(),
|
|
440
|
-
prefix: space(''),
|
|
441
|
-
markers: emptyMarkers,
|
|
442
|
-
key: rightPadded(keyLiteral, space('')),
|
|
443
|
-
value: valueLiteral
|
|
444
|
-
};
|
|
445
|
-
|
|
446
|
-
return rightPadded(member, space(afterWhitespace));
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
/**
|
|
450
|
-
* Adds a new scope section to the document when the target scope doesn't exist.
|
|
451
|
-
*/
|
|
452
|
-
private addScopeToDocument(doc: Json.Document): Json.Document {
|
|
453
|
-
const docValue = doc.value;
|
|
454
|
-
|
|
455
|
-
if (!isObject(docValue)) {
|
|
456
|
-
return doc;
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
const members = [...(docValue.members || [])];
|
|
460
|
-
|
|
461
|
-
// Get the trailing whitespace from the last member
|
|
462
|
-
let closingWhitespace = '\n';
|
|
463
|
-
if (members.length > 0) {
|
|
464
|
-
const lastMember = members[members.length - 1];
|
|
465
|
-
closingWhitespace = lastMember.after.whitespace;
|
|
466
|
-
// Update the last member's after to be empty (comma will be added by printer)
|
|
467
|
-
members[members.length - 1] = {
|
|
468
|
-
...lastMember,
|
|
469
|
-
after: space('')
|
|
470
|
-
};
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
const scopeMember = this.createScopeMemberWithAfter(closingWhitespace);
|
|
474
|
-
this.dependencyAdded = true;
|
|
475
|
-
|
|
476
|
-
members.push(scopeMember);
|
|
477
|
-
|
|
478
|
-
return {
|
|
479
|
-
...doc,
|
|
480
|
-
value: {
|
|
481
|
-
...docValue,
|
|
482
|
-
members
|
|
483
|
-
} as Json.Object
|
|
484
|
-
};
|
|
485
|
-
}
|
|
486
|
-
|
|
487
|
-
/**
|
|
488
|
-
* Creates a new scope member with the dependency inside.
|
|
489
|
-
*/
|
|
490
|
-
private createScopeMemberWithAfter(afterWhitespace: string): Json.RightPadded<Json.Member> {
|
|
491
|
-
// Dependencies inside a scope are indented twice (e.g., 8 spaces if base is 4)
|
|
492
|
-
const depIndent = this.baseIndent + this.baseIndent;
|
|
493
|
-
|
|
494
|
-
const keyLiteral: Json.Literal = {
|
|
495
|
-
kind: Json.Kind.Literal,
|
|
496
|
-
id: randomId(),
|
|
497
|
-
prefix: space('\n' + this.baseIndent),
|
|
498
|
-
markers: emptyMarkers,
|
|
499
|
-
source: `"${this.targetScope}"`,
|
|
500
|
-
value: this.targetScope
|
|
501
|
-
};
|
|
502
|
-
|
|
503
|
-
const depKeyLiteral: Json.Literal = {
|
|
504
|
-
kind: Json.Kind.Literal,
|
|
505
|
-
id: randomId(),
|
|
506
|
-
prefix: space('\n' + depIndent),
|
|
507
|
-
markers: emptyMarkers,
|
|
508
|
-
source: `"${this.packageName}"`,
|
|
509
|
-
value: this.packageName
|
|
510
|
-
};
|
|
511
|
-
|
|
512
|
-
const depValueLiteral: Json.Literal = {
|
|
513
|
-
kind: Json.Kind.Literal,
|
|
514
|
-
id: randomId(),
|
|
515
|
-
prefix: space(' '),
|
|
516
|
-
markers: emptyMarkers,
|
|
517
|
-
source: `"${this.version}"`,
|
|
518
|
-
value: this.version
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
const depMember: Json.Member = {
|
|
522
|
-
kind: Json.Kind.Member,
|
|
523
|
-
id: randomId(),
|
|
524
|
-
prefix: space(''),
|
|
525
|
-
markers: emptyMarkers,
|
|
526
|
-
key: rightPadded(depKeyLiteral, space('')),
|
|
527
|
-
value: depValueLiteral
|
|
528
|
-
};
|
|
529
|
-
|
|
530
|
-
const scopeObject: Json.Object = {
|
|
531
|
-
kind: Json.Kind.Object,
|
|
532
|
-
id: randomId(),
|
|
533
|
-
prefix: space(' '),
|
|
534
|
-
markers: emptyMarkers,
|
|
535
|
-
members: [rightPadded(depMember, space('\n' + this.baseIndent))]
|
|
536
|
-
};
|
|
537
|
-
|
|
538
|
-
const scopeMemberNode: Json.Member = {
|
|
539
|
-
kind: Json.Kind.Member,
|
|
540
|
-
id: randomId(),
|
|
541
|
-
prefix: space(''),
|
|
542
|
-
markers: emptyMarkers,
|
|
543
|
-
key: rightPadded(keyLiteral, space('')),
|
|
544
|
-
value: scopeObject
|
|
545
|
-
};
|
|
546
|
-
|
|
547
|
-
return rightPadded(scopeMemberNode, space(afterWhitespace));
|
|
548
|
-
}
|
|
549
|
-
}
|