@openrewrite/rewrite 8.91.0 → 8.91.2
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/java/formatting-utils.d.ts +8 -5
- package/dist/java/formatting-utils.d.ts.map +1 -1
- package/dist/java/formatting-utils.js +14 -10
- package/dist/java/formatting-utils.js.map +1 -1
- package/dist/java/type.d.ts +2 -0
- package/dist/java/type.d.ts.map +1 -1
- package/dist/java/type.js +5 -0
- package/dist/java/type.js.map +1 -1
- package/dist/javascript/add-import.d.ts +96 -43
- package/dist/javascript/add-import.d.ts.map +1 -1
- package/dist/javascript/add-import.js +668 -236
- package/dist/javascript/add-import.js.map +1 -1
- package/dist/javascript/amd.d.ts +138 -0
- package/dist/javascript/amd.d.ts.map +1 -0
- package/dist/javascript/amd.js +768 -0
- package/dist/javascript/amd.js.map +1 -0
- package/dist/javascript/assertions.d.ts +7 -0
- package/dist/javascript/assertions.d.ts.map +1 -1
- package/dist/javascript/assertions.js +31 -5
- package/dist/javascript/assertions.js.map +1 -1
- package/dist/javascript/binding.d.ts +94 -0
- package/dist/javascript/binding.d.ts.map +1 -0
- package/dist/javascript/binding.js +335 -0
- package/dist/javascript/binding.js.map +1 -0
- package/dist/javascript/comparator.d.ts +5 -0
- package/dist/javascript/comparator.d.ts.map +1 -1
- package/dist/javascript/comparator.js +34 -9
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/format/format.d.ts.map +1 -1
- package/dist/javascript/format/format.js +8 -9
- package/dist/javascript/format/format.js.map +1 -1
- package/dist/javascript/format/tabs-and-indents-visitor.d.ts +2 -0
- package/dist/javascript/format/tabs-and-indents-visitor.d.ts.map +1 -1
- package/dist/javascript/format/tabs-and-indents-visitor.js +17 -11
- package/dist/javascript/format/tabs-and-indents-visitor.js.map +1 -1
- package/dist/javascript/index.d.ts +8 -1
- package/dist/javascript/index.d.ts.map +1 -1
- package/dist/javascript/index.js +26 -1
- package/dist/javascript/index.js.map +1 -1
- package/dist/javascript/node-resolution-result.d.ts +19 -1
- package/dist/javascript/node-resolution-result.d.ts.map +1 -1
- package/dist/javascript/node-resolution-result.js +37 -2
- package/dist/javascript/node-resolution-result.js.map +1 -1
- package/dist/javascript/package-exported-types.d.ts.map +1 -1
- package/dist/javascript/package-exported-types.js +56 -72
- package/dist/javascript/package-exported-types.js.map +1 -1
- package/dist/javascript/parser-utils.d.ts.map +1 -1
- package/dist/javascript/parser-utils.js +2 -0
- package/dist/javascript/parser-utils.js.map +1 -1
- package/dist/javascript/parser.d.ts +7 -1
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +17 -4
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/recipes/change-import.d.ts +2 -4
- package/dist/javascript/recipes/change-import.d.ts.map +1 -1
- package/dist/javascript/recipes/change-import.js +8 -244
- package/dist/javascript/recipes/change-import.js.map +1 -1
- package/dist/javascript/remove-import.d.ts +2 -28
- package/dist/javascript/remove-import.d.ts.map +1 -1
- package/dist/javascript/remove-import.js +19 -57
- package/dist/javascript/remove-import.js.map +1 -1
- package/dist/javascript/scope.d.ts +49 -0
- package/dist/javascript/scope.d.ts.map +1 -0
- package/dist/javascript/scope.js +324 -0
- package/dist/javascript/scope.js.map +1 -0
- package/dist/javascript/style.d.ts.map +1 -1
- package/dist/javascript/style.js +3 -10
- package/dist/javascript/style.js.map +1 -1
- package/dist/javascript/templating/bindings.d.ts +8 -0
- package/dist/javascript/templating/bindings.d.ts.map +1 -0
- package/dist/javascript/templating/bindings.js +92 -0
- package/dist/javascript/templating/bindings.js.map +1 -0
- package/dist/javascript/templating/comparator.d.ts +10 -0
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +48 -20
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/engine.d.ts +34 -10
- package/dist/javascript/templating/engine.d.ts.map +1 -1
- package/dist/javascript/templating/engine.js +109 -55
- package/dist/javascript/templating/engine.js.map +1 -1
- package/dist/javascript/templating/index.d.ts +1 -1
- package/dist/javascript/templating/index.d.ts.map +1 -1
- package/dist/javascript/templating/index.js.map +1 -1
- package/dist/javascript/templating/pattern.d.ts.map +1 -1
- package/dist/javascript/templating/pattern.js +13 -10
- package/dist/javascript/templating/pattern.js.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.d.ts +4 -0
- package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.js +24 -10
- package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
- package/dist/javascript/templating/precedence.d.ts +12 -4
- package/dist/javascript/templating/precedence.d.ts.map +1 -1
- package/dist/javascript/templating/precedence.js +18 -11
- package/dist/javascript/templating/precedence.js.map +1 -1
- package/dist/javascript/templating/rewrite.d.ts.map +1 -1
- package/dist/javascript/templating/rewrite.js +19 -17
- package/dist/javascript/templating/rewrite.js.map +1 -1
- package/dist/javascript/templating/template.d.ts +14 -0
- package/dist/javascript/templating/template.d.ts.map +1 -1
- package/dist/javascript/templating/template.js +63 -3
- package/dist/javascript/templating/template.js.map +1 -1
- package/dist/javascript/templating/types.d.ts +39 -1
- package/dist/javascript/templating/types.d.ts.map +1 -1
- package/dist/javascript/templating/utils.d.ts +6 -1
- package/dist/javascript/templating/utils.d.ts.map +1 -1
- package/dist/javascript/templating/utils.js +20 -2
- package/dist/javascript/templating/utils.js.map +1 -1
- package/dist/javascript/type-mapping.d.ts +1 -0
- package/dist/javascript/type-mapping.d.ts.map +1 -1
- package/dist/javascript/type-mapping.js +52 -2
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/rewrite-javascript-version.txt +1 -1
- package/dist/style.d.ts +2 -0
- package/dist/style.d.ts.map +1 -1
- package/dist/style.js +5 -0
- package/dist/style.js.map +1 -1
- package/dist/test/rewrite-test.d.ts +12 -0
- package/dist/test/rewrite-test.d.ts.map +1 -1
- package/dist/test/rewrite-test.js +40 -3
- package/dist/test/rewrite-test.js.map +1 -1
- package/package.json +2 -2
- package/src/java/formatting-utils.ts +14 -10
- package/src/java/type.ts +5 -0
- package/src/javascript/add-import.ts +751 -247
- package/src/javascript/amd.ts +915 -0
- package/src/javascript/assertions.ts +33 -5
- package/src/javascript/binding.ts +449 -0
- package/src/javascript/comparator.ts +39 -10
- package/src/javascript/format/format.ts +9 -10
- package/src/javascript/format/tabs-and-indents-visitor.ts +18 -12
- package/src/javascript/index.ts +12 -1
- package/src/javascript/node-resolution-result.ts +59 -4
- package/src/javascript/package-exported-types.ts +60 -74
- package/src/javascript/parser-utils.ts +2 -0
- package/src/javascript/parser.ts +23 -3
- package/src/javascript/recipes/change-import.ts +10 -263
- package/src/javascript/remove-import.ts +20 -56
- package/src/javascript/scope.ts +338 -0
- package/src/javascript/style.ts +3 -11
- package/src/javascript/templating/bindings.ts +99 -0
- package/src/javascript/templating/comparator.ts +57 -20
- package/src/javascript/templating/engine.ts +148 -68
- package/src/javascript/templating/index.ts +1 -0
- package/src/javascript/templating/pattern.ts +19 -16
- package/src/javascript/templating/placeholder-replacement.ts +32 -11
- package/src/javascript/templating/precedence.ts +21 -13
- package/src/javascript/templating/rewrite.ts +23 -17
- package/src/javascript/templating/template.ts +84 -9
- package/src/javascript/templating/types.ts +46 -1
- package/src/javascript/templating/utils.ts +21 -2
- package/src/javascript/type-mapping.ts +54 -2
- package/src/style.ts +5 -0
- package/src/test/rewrite-test.ts +61 -3
|
@@ -34,6 +34,19 @@ import {Marker, replaceMarkerByKind} from "../markers";
|
|
|
34
34
|
*/
|
|
35
35
|
export const sourceFileCache: Map<string, ts.SourceFile> = new Map();
|
|
36
36
|
|
|
37
|
+
// Pooled so a spec reuses the program the previous spec left behind; see `JavaScriptParser.parse`.
|
|
38
|
+
const parserPool: Map<string, JavaScriptParser> = new Map();
|
|
39
|
+
|
|
40
|
+
function pooledParser(relativeTo?: string): JavaScriptParser {
|
|
41
|
+
const key = relativeTo ?? "";
|
|
42
|
+
let parser = parserPool.get(key);
|
|
43
|
+
if (!parser) {
|
|
44
|
+
parser = new JavaScriptParser({sourceFileCache, relativeTo});
|
|
45
|
+
parserPool.set(key, parser);
|
|
46
|
+
}
|
|
47
|
+
return parser;
|
|
48
|
+
}
|
|
49
|
+
|
|
37
50
|
// Automatically enable sourceFileCache for template parsing in tests
|
|
38
51
|
setTemplateSourceFileCache(sourceFileCache);
|
|
39
52
|
|
|
@@ -211,7 +224,7 @@ export async function* npm(relativeTo: string, ...sourceSpecs: SourceSpec<any>[]
|
|
|
211
224
|
|
|
212
225
|
yield {
|
|
213
226
|
...spec,
|
|
214
|
-
parser: () =>
|
|
227
|
+
parser: () => pooledParser(relativeTo),
|
|
215
228
|
// Add style marker before recipe runs if available
|
|
216
229
|
// Compose with existing beforeRecipe if present
|
|
217
230
|
beforeRecipe: styleMarker ? (sf: JS.CompilationUnit) => {
|
|
@@ -243,13 +256,28 @@ export function packageLockJson(before: string, after?: AfterRecipeText): Source
|
|
|
243
256
|
};
|
|
244
257
|
}
|
|
245
258
|
|
|
259
|
+
/**
|
|
260
|
+
* Gives a source file the style its own text is written in. A spec that formats its output is
|
|
261
|
+
* measured against the built-in defaults unless it opts in with
|
|
262
|
+
* `{...typescript(src), beforeRecipe: withDetectedStyle}`; a file parsed through {@link npm} is
|
|
263
|
+
* sampled alongside its siblings and needs no opt-in.
|
|
264
|
+
*/
|
|
265
|
+
export async function withDetectedStyle(sourceFile: JS.CompilationUnit): Promise<JS.CompilationUnit> {
|
|
266
|
+
const detector = Autodetect.detector();
|
|
267
|
+
await detector.sample(sourceFile);
|
|
268
|
+
const detected = detector.build();
|
|
269
|
+
return produce(sourceFile, draft => {
|
|
270
|
+
draft.markers = replaceMarkerByKind(draft.markers, detected);
|
|
271
|
+
});
|
|
272
|
+
}
|
|
273
|
+
|
|
246
274
|
export function javascript(before: string | null, after?: AfterRecipeText): SourceSpec<JS.CompilationUnit> {
|
|
247
275
|
return {
|
|
248
276
|
kind: JS.Kind.CompilationUnit,
|
|
249
277
|
before: before,
|
|
250
278
|
after: dedentAfter(after),
|
|
251
279
|
ext: 'js',
|
|
252
|
-
parser: ctx =>
|
|
280
|
+
parser: ctx => pooledParser()
|
|
253
281
|
};
|
|
254
282
|
}
|
|
255
283
|
|
|
@@ -259,7 +287,7 @@ export function typescript(before: string | null, after?: AfterRecipeText): Sour
|
|
|
259
287
|
before: before,
|
|
260
288
|
after: dedentAfter(after),
|
|
261
289
|
ext: 'ts',
|
|
262
|
-
parser: () =>
|
|
290
|
+
parser: () => pooledParser()
|
|
263
291
|
};
|
|
264
292
|
}
|
|
265
293
|
|
|
@@ -269,7 +297,7 @@ export function tsx(before: string | null, after?: AfterRecipeText): SourceSpec<
|
|
|
269
297
|
before: before,
|
|
270
298
|
after: dedentAfter(after),
|
|
271
299
|
ext: 'tsx',
|
|
272
|
-
parser: () =>
|
|
300
|
+
parser: () => pooledParser()
|
|
273
301
|
};
|
|
274
302
|
}
|
|
275
303
|
|
|
@@ -279,6 +307,6 @@ export function jsx(before: string | null, after?: AfterRecipeText): SourceSpec<
|
|
|
279
307
|
before: before,
|
|
280
308
|
after: dedentAfter(after),
|
|
281
309
|
ext: 'jsx',
|
|
282
|
-
parser: () =>
|
|
310
|
+
parser: () => pooledParser()
|
|
283
311
|
};
|
|
284
312
|
}
|
|
@@ -0,0 +1,449 @@
|
|
|
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
|
+
import {J} from "../java";
|
|
17
|
+
import {JS} from "./tree";
|
|
18
|
+
import {JavaScriptVisitor} from "./visitor";
|
|
19
|
+
import {compilationUnitOf, cursorOf, declarationsOf, walk} from "./scope";
|
|
20
|
+
import {AddImportOptions, bindImport, existingImportBinding, memberName, moduleNameOf, RebindImport, requiredModuleOf} from "./add-import";
|
|
21
|
+
import {RemoveImport} from "./remove-import";
|
|
22
|
+
import {
|
|
23
|
+
AmdCalleeOptions, amdBlockOf, bindAmd, calleesOf, dependencyNames, derivedBindingName, enclosingAmdBlock,
|
|
24
|
+
parameterNames, RebindAmdDependency, RemoveAmdDependency
|
|
25
|
+
} from "./amd";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* A bare string is shorthand for `{module}`, the overwhelmingly common call with nothing else to
|
|
29
|
+
* configure. A factory parameter binds a whole module under a name and nothing else, so on the AMD
|
|
30
|
+
* lane `member`, `typeOnly`, `sideEffectOnly`, `onlyIfReferenced`, `quoteStyle` and `style` do not
|
|
31
|
+
* apply: the first three refuse, and the rest have nothing to shape.
|
|
32
|
+
*/
|
|
33
|
+
export interface MaybeBindOptions extends AddImportOptions {
|
|
34
|
+
/** Callees that introduce an AMD block. UI5 writes `sap.ui.define`, RequireJS and Dojo `define`. */
|
|
35
|
+
amdCallee?: string | readonly string[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** A bare string is shorthand for `{module}`, the overwhelmingly common call with nothing else to configure. */
|
|
39
|
+
export interface MaybeUnbindOptions {
|
|
40
|
+
module: string;
|
|
41
|
+
|
|
42
|
+
/** The member to remove; unset removes every unused binding of the module. */
|
|
43
|
+
member?: string;
|
|
44
|
+
|
|
45
|
+
/** Callees that introduce an AMD block. UI5 writes `sap.ui.define`, RequireJS and Dojo `define`. */
|
|
46
|
+
amdCallee?: string | readonly string[];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface MaybeRebindOptions {
|
|
50
|
+
from: {module: string; member?: string};
|
|
51
|
+
to: {module: string; member?: string};
|
|
52
|
+
|
|
53
|
+
/** Callees that introduce an AMD block. UI5 writes `sap.ui.define`, RequireJS and Dojo `define`. */
|
|
54
|
+
amdCallee?: string | readonly string[];
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface ModuleBindings {
|
|
58
|
+
/**
|
|
59
|
+
* The module `localName` refers to, or undefined when it is not a module binding — of any
|
|
60
|
+
* shape, so a namespace import's name answers here even though `maybeBind` will not reuse it
|
|
61
|
+
* for a plain `{module}` request. See `bindingOf`.
|
|
62
|
+
*/
|
|
63
|
+
moduleOf(localName: string): string | undefined;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The local name bound to `module`, or undefined when nothing binds it — of any shape: a
|
|
67
|
+
* namespace import counts, even though `maybeBind` will not treat it as answering a plain
|
|
68
|
+
* `{module}` request. What a name can stand in for is `maybeBind`'s question, not this one's.
|
|
69
|
+
*/
|
|
70
|
+
bindingOf(module: string): string | undefined;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The lane these bindings come from, and the one `maybeBind` would use. `"none"` is a
|
|
74
|
+
* plain script — no import, export, `require` binding, or enclosing AMD block — which
|
|
75
|
+
* `maybeBind` still turns into a module on request; a caller that must not do that checks
|
|
76
|
+
* for `"none"` itself.
|
|
77
|
+
*/
|
|
78
|
+
readonly moduleSystem: "esm" | "amd" | "commonjs" | "none";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function moduleBindings(
|
|
82
|
+
visitor: JavaScriptVisitor<any>,
|
|
83
|
+
options?: AmdCalleeOptions
|
|
84
|
+
): ModuleBindings {
|
|
85
|
+
const amd = enclosingAmdBlock(visitor, options);
|
|
86
|
+
if (amd !== undefined) {
|
|
87
|
+
const modules = dependencyNames(amd.block);
|
|
88
|
+
const bindings = parameterNames(amd.block);
|
|
89
|
+
return {
|
|
90
|
+
moduleSystem: "amd",
|
|
91
|
+
moduleOf: localName => {
|
|
92
|
+
const index = bindings.indexOf(localName);
|
|
93
|
+
// `dependencyNames` pads a non-literal element with "" to hold its position; that
|
|
94
|
+
// filler names no module, so it answers neither lookup.
|
|
95
|
+
return index < 0 || modules[index] === "" ? undefined : modules[index];
|
|
96
|
+
},
|
|
97
|
+
bindingOf: module => {
|
|
98
|
+
const index = module === "" ? -1 : modules.indexOf(module);
|
|
99
|
+
return index < 0 ? undefined : bindings[index];
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const cu = compilationUnitOf(visitor);
|
|
105
|
+
const bound = cu === undefined ? [] : moduleObjectBindings(cu);
|
|
106
|
+
return {
|
|
107
|
+
moduleSystem: cu === undefined ? "none" :
|
|
108
|
+
isCommonJs(cu) ? "commonjs" :
|
|
109
|
+
hasEsmSyntax(cu) ? "esm" : "none",
|
|
110
|
+
moduleOf: localName => bound.find(b => b.name === localName)?.module,
|
|
111
|
+
bindingOf: module => bound.find(b => b.module === module)?.name
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function isAmdBlock(node: J, options?: AmdCalleeOptions): boolean {
|
|
116
|
+
return node.kind === J.Kind.MethodInvocation &&
|
|
117
|
+
amdBlockOf(node as J.MethodInvocation, calleesOf(options)) !== undefined;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/** Whether a top-level statement already marks the file as a module: an import or any export form. */
|
|
121
|
+
function hasEsmSyntax(cu: JS.CompilationUnit): boolean {
|
|
122
|
+
return cu.statements.some(stmt => {
|
|
123
|
+
const element = stmt.element;
|
|
124
|
+
// `export {a, b}`, `export * from` and `export default` are their own statement kinds;
|
|
125
|
+
// `export class`/`function`/`const` instead carry `export` as a modifier on the
|
|
126
|
+
// declaration itself, the same way TypeScript's own AST models it.
|
|
127
|
+
const modifiers = (element as {modifiers?: J.Modifier[]} | undefined)?.modifiers;
|
|
128
|
+
return element?.kind === JS.Kind.Import ||
|
|
129
|
+
element?.kind === JS.Kind.ExportDeclaration ||
|
|
130
|
+
element?.kind === JS.Kind.ExportAssignment ||
|
|
131
|
+
(modifiers?.some(m => m.keyword === "export") ?? false);
|
|
132
|
+
}) || hasTopLevelAwait(cu);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Whether a statement holds an `await` outside any function of its own — legal only at module
|
|
137
|
+
* top level, unlike an `await` inside an `async function`, which says nothing about the file.
|
|
138
|
+
*/
|
|
139
|
+
function hasTopLevelAwait(cu: JS.CompilationUnit): boolean {
|
|
140
|
+
let found = false;
|
|
141
|
+
walk(cu.statements, node => {
|
|
142
|
+
if (found) {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
if (node.kind === JS.Kind.Await) {
|
|
146
|
+
found = true;
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
return node.kind !== J.Kind.MethodDeclaration && node.kind !== J.Kind.Lambda;
|
|
150
|
+
});
|
|
151
|
+
return found;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
interface ModuleObjectBinding {
|
|
155
|
+
name: string;
|
|
156
|
+
module: string;
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* `"default"` and `"namespace"` bind different values — a namespace object's default sits at
|
|
160
|
+
* `.default` — so only one answers a whole-module request of the matching form. CommonJS has
|
|
161
|
+
* no such split: `"require"` answers either. A caller wanting the namespace asks for
|
|
162
|
+
* `member: "*"`, which is what a module exporting no default is bound by.
|
|
163
|
+
*/
|
|
164
|
+
shape: "default" | "namespace" | "require";
|
|
165
|
+
|
|
166
|
+
/** A type-only import erases, so the name it binds stands for no value at runtime. */
|
|
167
|
+
typeOnly: boolean;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Whether the file binds its modules with `require`, which decides whether a create is possible. */
|
|
171
|
+
function isCommonJs(cu: JS.CompilationUnit): boolean {
|
|
172
|
+
if (cu.sourcePath.endsWith(".cjs") || cu.sourcePath.endsWith(".cts")) {
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
175
|
+
// Node treats these as ES modules regardless of what they contain, the same way
|
|
176
|
+
// `AddImport`'s own `determineImportStyle` reads them as ES6-preferring; `.js`/`.ts`/`.tsx`
|
|
177
|
+
// stay ambiguous and fall through to the statements below.
|
|
178
|
+
if (cu.sourcePath.endsWith(".mjs") || cu.sourcePath.endsWith(".mts")) {
|
|
179
|
+
return false;
|
|
180
|
+
}
|
|
181
|
+
if (hasEsmSyntax(cu)) {
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
return cu.statements.some(stmt =>
|
|
185
|
+
declarationsOf(stmt.element).some(d => requiredModule(d) !== undefined));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** The module a `const X = require("m")` declaration names, for the one variable it declares. */
|
|
189
|
+
function requiredModule(declaration: J.VariableDeclarations): string | undefined {
|
|
190
|
+
const variables = declaration.variables;
|
|
191
|
+
const initializer = variables.length === 1 ? variables[0].element?.initializer?.element : undefined;
|
|
192
|
+
return initializer?.kind === J.Kind.MethodInvocation
|
|
193
|
+
? requiredModuleOf(initializer as J.MethodInvocation)
|
|
194
|
+
: undefined;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The module a `const X = await import("m")` declaration names, for the one variable it declares.
|
|
199
|
+
* A dynamic import resolves to the module namespace object, the same value `import * as X`
|
|
200
|
+
* binds, so it shares that shape rather than getting one of its own.
|
|
201
|
+
*/
|
|
202
|
+
function dynamicallyImportedModule(declaration: J.VariableDeclarations): string | undefined {
|
|
203
|
+
const variables = declaration.variables;
|
|
204
|
+
const initializer = variables.length === 1 ? variables[0].element?.initializer?.element : undefined;
|
|
205
|
+
const awaited = initializer?.kind === JS.Kind.Await ? (initializer as JS.Await).expression : undefined;
|
|
206
|
+
// `import(...)` is a keyword, not an identifier, so the parser maps it to `JS.FunctionCall`
|
|
207
|
+
// rather than the `J.MethodInvocation` an ordinary call gets.
|
|
208
|
+
if (awaited?.kind !== JS.Kind.FunctionCall) {
|
|
209
|
+
return undefined;
|
|
210
|
+
}
|
|
211
|
+
const call = awaited as JS.FunctionCall;
|
|
212
|
+
const callee = call.function?.element;
|
|
213
|
+
if (callee?.kind !== J.Kind.Identifier || (callee as J.Identifier).simpleName !== "import") {
|
|
214
|
+
return undefined;
|
|
215
|
+
}
|
|
216
|
+
const argument = call.arguments.elements[0]?.element;
|
|
217
|
+
return argument?.kind === J.Kind.Literal && typeof (argument as J.Literal).value === "string"
|
|
218
|
+
? (argument as J.Literal).value as string
|
|
219
|
+
: undefined;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/** Bindings from top-level `const X = <moduleOf-recognised call>` declarations, all of one shape. */
|
|
223
|
+
function wholeModuleBindingsVia(
|
|
224
|
+
cu: JS.CompilationUnit,
|
|
225
|
+
moduleOf: (declaration: J.VariableDeclarations) => string | undefined,
|
|
226
|
+
shape: ModuleObjectBinding["shape"]
|
|
227
|
+
): ModuleObjectBinding[] {
|
|
228
|
+
const bindings: ModuleObjectBinding[] = [];
|
|
229
|
+
for (const stmt of cu.statements) {
|
|
230
|
+
for (const declaration of declarationsOf(stmt.element)) {
|
|
231
|
+
const module = moduleOf(declaration);
|
|
232
|
+
const name = declaration.variables[0]?.element?.name;
|
|
233
|
+
if (module !== undefined && name?.kind === J.Kind.Identifier) {
|
|
234
|
+
bindings.push({name: (name as J.Identifier).simpleName, module, shape, typeOnly: false});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return bindings;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/** Only whole-module bindings: a named member does not name the module object. */
|
|
242
|
+
function moduleObjectBindings(cu: JS.CompilationUnit): ModuleObjectBinding[] {
|
|
243
|
+
const bindings: ModuleObjectBinding[] = [];
|
|
244
|
+
for (const stmt of cu.statements) {
|
|
245
|
+
const statement = stmt.element;
|
|
246
|
+
if (statement?.kind !== JS.Kind.Import) {
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
const jsImport = statement as JS.Import;
|
|
250
|
+
const specifier = jsImport.moduleSpecifier?.element;
|
|
251
|
+
if (specifier?.kind !== J.Kind.Literal) {
|
|
252
|
+
continue;
|
|
253
|
+
}
|
|
254
|
+
const module = (specifier as J.Literal).value;
|
|
255
|
+
if (typeof module !== "string") {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const clause = jsImport.importClause;
|
|
259
|
+
const typeOnly = clause?.typeOnly ?? false;
|
|
260
|
+
if (clause?.name?.element?.kind === J.Kind.Identifier) {
|
|
261
|
+
bindings.push({name: (clause.name.element as J.Identifier).simpleName, module, shape: "default", typeOnly});
|
|
262
|
+
}
|
|
263
|
+
// `namedBindings` is a `JS.Alias` only for `import * as X from "m"`; a renamed
|
|
264
|
+
// named import (`{a as b}`) nests its alias inside `NamedImports` instead.
|
|
265
|
+
const named = clause?.namedBindings;
|
|
266
|
+
if (named?.kind === JS.Kind.Alias) {
|
|
267
|
+
const alias = (named as JS.Alias).alias;
|
|
268
|
+
if (alias?.kind === J.Kind.Identifier) {
|
|
269
|
+
bindings.push({name: (alias as J.Identifier).simpleName, module, shape: "namespace", typeOnly});
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
bindings.push(...wholeModuleBindingsVia(cu, requiredModule, "require"));
|
|
274
|
+
bindings.push(...wholeModuleBindingsVia(cu, dynamicallyImportedModule, "namespace"));
|
|
275
|
+
return bindings;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Whether `binding` answers a whole-module request: the namespace form when `wantsNamespace`, and
|
|
280
|
+
* never a type-only import for a value, which erases and would leave the reference unbound.
|
|
281
|
+
*/
|
|
282
|
+
function answersWholeModuleRequest(binding: ModuleObjectBinding, wantsNamespace: boolean, typeOnly: boolean): boolean {
|
|
283
|
+
return binding.typeOnly === typeOnly &&
|
|
284
|
+
(binding.shape === "require" || binding.shape === (wantsNamespace ? "namespace" : "default"));
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* A local binding for `module` or one of its members, creating one where none exists, or
|
|
289
|
+
* `undefined` where no safe binding is possible. The lane — AMD or ESM/CommonJS — is decided
|
|
290
|
+
* from the cursor; AMD binds only the whole module, so a `member` request there refuses rather
|
|
291
|
+
* than guess. `onlyIfReferenced` defaults to true, so the import may never appear.
|
|
292
|
+
*/
|
|
293
|
+
export function maybeBind(
|
|
294
|
+
visitor: JavaScriptVisitor<any>,
|
|
295
|
+
options: MaybeBindOptions | string
|
|
296
|
+
): string | undefined {
|
|
297
|
+
if (typeof options === "string") {
|
|
298
|
+
options = {module: options};
|
|
299
|
+
}
|
|
300
|
+
const module = moduleNameOf(options.module);
|
|
301
|
+
const key = memberName(options.member);
|
|
302
|
+
|
|
303
|
+
const amd = enclosingAmdBlock(visitor, options);
|
|
304
|
+
if (amd !== undefined) {
|
|
305
|
+
if (key !== undefined || options.sideEffectOnly) {
|
|
306
|
+
// Nor can a factory parameter load a module without binding it to a name.
|
|
307
|
+
return undefined;
|
|
308
|
+
}
|
|
309
|
+
return bindAmd(visitor, amd, module, options.alias ?? options.preferredName,
|
|
310
|
+
calleesOf(options), options.alias !== undefined);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
const cu = compilationUnitOf(visitor);
|
|
314
|
+
const isWholeModule = !options.sideEffectOnly && (key === undefined || key === "*");
|
|
315
|
+
if (isWholeModule) {
|
|
316
|
+
const bound = cu && moduleObjectBindings(cu).find(b =>
|
|
317
|
+
b.module === module && answersWholeModuleRequest(b, key === "*", options.typeOnly ?? false) &&
|
|
318
|
+
// A pinned alias asks for a binding of that name, so another name for the same
|
|
319
|
+
// module does not answer it; `bindImport`'s own lookup applies the same rule.
|
|
320
|
+
(options.alias === undefined || b.name === options.alias));
|
|
321
|
+
if (bound !== undefined) {
|
|
322
|
+
return bound.name;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (isWholeModule && options.preferredName === undefined && derivedBindingName(module) === undefined) {
|
|
327
|
+
// The module's last path segment is not a legal identifier, and the caller named no
|
|
328
|
+
// preference of its own — there is no name left to bind it to.
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// `bindImport`'s own lookup finds and reuses a member-specific binding on its own, so
|
|
333
|
+
// refusal here only has to gate the point where it would create a new one.
|
|
334
|
+
const refuseCreate = cu !== undefined && isCommonJs(cu);
|
|
335
|
+
return bindImport(visitor, {
|
|
336
|
+
...options,
|
|
337
|
+
preferredName: options.preferredName ?? (isWholeModule ? derivedBindingName(module) : undefined)
|
|
338
|
+
}, refuseCreate);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Removes `module`'s import(s) where unused, or one `member` of it — `'default'` and `'*'` select
|
|
343
|
+
* the default and namespace import regardless of local name. A member-scoped request does not
|
|
344
|
+
* apply to an AMD dependency, which binds a module rather than one of its members.
|
|
345
|
+
*/
|
|
346
|
+
export function maybeUnbind(visitor: JavaScriptVisitor<any>, options: MaybeUnbindOptions | string): void {
|
|
347
|
+
if (typeof options === "string") {
|
|
348
|
+
options = {module: options};
|
|
349
|
+
}
|
|
350
|
+
const callees = calleesOf(options);
|
|
351
|
+
const queued = visitor.afterVisit || [];
|
|
352
|
+
if (!queued.some(v => v instanceof RemoveImport && v.module === options.module && v.member === options.member)) {
|
|
353
|
+
visitor.afterVisit.push(new RemoveImport(options.module, options.member));
|
|
354
|
+
}
|
|
355
|
+
// Both queue unconditionally so the caller need not know which lane the file uses: each
|
|
356
|
+
// visitor removes whatever matching construct it finds, ESM import or AMD dependency, and a
|
|
357
|
+
// file with both gets both removed. `amdCallee` says which block a call means, not how
|
|
358
|
+
// anything prints, so — unlike `preferredName`/`quoteStyle` on the bind side — it is part of
|
|
359
|
+
// the dedup key.
|
|
360
|
+
if (!queued.some(v => v instanceof RemoveAmdDependency && v.module === options.module &&
|
|
361
|
+
v.member === options.member && sameCallees(v.callees, callees))) {
|
|
362
|
+
visitor.afterVisit.push(new RemoveAmdDependency(options.module, options.member, callees));
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
function sameCallees(a: readonly string[], b: readonly string[]): boolean {
|
|
367
|
+
return a.length === b.length && a.every((callee, i) => callee === b[i]);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** Which of an import clause's three slots `member` binds — `import`, `import *`, or `import {}`. */
|
|
371
|
+
function bindingShape(member: string | undefined): "default" | "namespace" | "named" {
|
|
372
|
+
const key = memberName(member);
|
|
373
|
+
return key === undefined ? "default" : key === "*" ? "namespace" : "named";
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* Moves the binding for `from` to `to`, keeping the local name it already had — the primitive
|
|
378
|
+
* behind a member rename or a module move. Returns `undefined`, changing nothing, where the move
|
|
379
|
+
* is not safely expressible; the refusals are listed in CLAUDE.md: JavaScript module bindings.
|
|
380
|
+
*/
|
|
381
|
+
export function maybeRebind(visitor: JavaScriptVisitor<any>, options: MaybeRebindOptions): string | undefined {
|
|
382
|
+
const amd = enclosingAmdBlock(visitor, options);
|
|
383
|
+
if (amd !== undefined) {
|
|
384
|
+
if (memberName(options.from.member) !== undefined || memberName(options.to.member) !== undefined) {
|
|
385
|
+
return undefined;
|
|
386
|
+
}
|
|
387
|
+
const index = dependencyNames(amd.block).indexOf(options.from.module);
|
|
388
|
+
const binding = index < 0 ? undefined : parameterNames(amd.block)[index];
|
|
389
|
+
if (binding === undefined) {
|
|
390
|
+
return undefined;
|
|
391
|
+
}
|
|
392
|
+
const callees = calleesOf(options);
|
|
393
|
+
const from = options.from.module;
|
|
394
|
+
if (!(visitor.afterVisit || []).some(v => v instanceof RebindAmdDependency &&
|
|
395
|
+
v.blockId === amd.call.id && v.fromModule === from && sameCallees(v.callees, callees))) {
|
|
396
|
+
visitor.afterVisit.push(new RebindAmdDependency(amd.call.id, from, options.to.module, callees));
|
|
397
|
+
}
|
|
398
|
+
return binding;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
const cu = compilationUnitOf(visitor);
|
|
402
|
+
if (cu === undefined) {
|
|
403
|
+
return undefined;
|
|
404
|
+
}
|
|
405
|
+
const existing = existingImportBinding(cu, options.from.module, options.from.member);
|
|
406
|
+
if (existing === undefined) {
|
|
407
|
+
return undefined;
|
|
408
|
+
}
|
|
409
|
+
if (existing.onlyMemberOfStatement && bindingShape(options.from.member) !== bindingShape(options.to.member)) {
|
|
410
|
+
return undefined;
|
|
411
|
+
}
|
|
412
|
+
if (!existing.onlyMemberOfStatement && isCommonJs(cu)) {
|
|
413
|
+
return undefined;
|
|
414
|
+
}
|
|
415
|
+
visitor.afterVisit.push(new RebindImport(options.from, options.to, existing.localName));
|
|
416
|
+
return existing.localName;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* @deprecated Use {@link maybeUnbind} instead — this is a call-shape change, not a behaviour
|
|
421
|
+
* change: `maybeRemoveImport(v, module, member)` is `maybeUnbind(v, {module, member})`.
|
|
422
|
+
*/
|
|
423
|
+
export function maybeRemoveImport(visitor: JavaScriptVisitor<any>, module: string, member?: string): void {
|
|
424
|
+
maybeUnbind(visitor, {module, member});
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* @deprecated Use {@link maybeBind} instead. Beyond binding through an AMD factory parameter,
|
|
429
|
+
* `maybeBind` returns `undefined` rather than creating an import where the file binds its modules
|
|
430
|
+
* with `require`, or where no legal identifier can be derived from the module and none was named.
|
|
431
|
+
*/
|
|
432
|
+
export function maybeAddImport(
|
|
433
|
+
visitor: JavaScriptVisitor<any>,
|
|
434
|
+
options: AddImportOptions & { sideEffectOnly: true }
|
|
435
|
+
): undefined;
|
|
436
|
+
export function maybeAddImport(
|
|
437
|
+
visitor: JavaScriptVisitor<any>,
|
|
438
|
+
options: AddImportOptions & { sideEffectOnly?: false }
|
|
439
|
+
): string | undefined;
|
|
440
|
+
export function maybeAddImport(
|
|
441
|
+
visitor: JavaScriptVisitor<any>,
|
|
442
|
+
options: AddImportOptions
|
|
443
|
+
): string | undefined;
|
|
444
|
+
export function maybeAddImport(
|
|
445
|
+
visitor: JavaScriptVisitor<any>,
|
|
446
|
+
options: AddImportOptions
|
|
447
|
+
): string | undefined {
|
|
448
|
+
return maybeBind(visitor, options);
|
|
449
|
+
}
|
|
@@ -2410,11 +2410,6 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2410
2410
|
}
|
|
2411
2411
|
}
|
|
2412
2412
|
|
|
2413
|
-
// If neither has type, use structural comparison
|
|
2414
|
-
if (!method.methodType && !otherMethod.methodType) {
|
|
2415
|
-
return super.visitMethodInvocation(method, other);
|
|
2416
|
-
}
|
|
2417
|
-
|
|
2418
2413
|
// If both have types with FQ declaring types, verify they're compatible
|
|
2419
2414
|
// (This prevents matching completely different methods like util.isArray vs util.isBoolean)
|
|
2420
2415
|
if (method.methodType && otherMethod.methodType) {
|
|
@@ -2450,12 +2445,12 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2450
2445
|
}
|
|
2451
2446
|
// else: types matched, skip select comparison (allows namespace vs named imports)
|
|
2452
2447
|
|
|
2453
|
-
//
|
|
2454
|
-
if (
|
|
2455
|
-
|
|
2456
|
-
|
|
2448
|
+
// A pattern that spells out no type arguments says nothing about them, as with parentheses
|
|
2449
|
+
if (method.typeParameters) {
|
|
2450
|
+
if (!otherMethod.typeParameters) {
|
|
2451
|
+
return this.structuralMismatch('typeParameters');
|
|
2452
|
+
}
|
|
2457
2453
|
|
|
2458
|
-
if (method.typeParameters && otherMethod.typeParameters) {
|
|
2459
2454
|
await this.visitContainerProperty('typeParameters', method.typeParameters, otherMethod.typeParameters);
|
|
2460
2455
|
if (!this.match) return method;
|
|
2461
2456
|
}
|
|
@@ -2475,6 +2470,32 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2475
2470
|
return method;
|
|
2476
2471
|
}
|
|
2477
2472
|
|
|
2473
|
+
/**
|
|
2474
|
+
* A call whose callee is not a plain identifier, such as `getFn()(x)`, `arr[0](x)` or `fn?.(x)`.
|
|
2475
|
+
* Type arguments constrain it under the same rule as `visitMethodInvocation`.
|
|
2476
|
+
*/
|
|
2477
|
+
override async visitFunctionCall(functionCall: JS.FunctionCall, other: J): Promise<J | undefined> {
|
|
2478
|
+
if (other.kind !== JS.Kind.FunctionCall) {
|
|
2479
|
+
return this.kindMismatch();
|
|
2480
|
+
}
|
|
2481
|
+
|
|
2482
|
+
const otherFunctionCall = other as JS.FunctionCall;
|
|
2483
|
+
if (functionCall.typeParameters) {
|
|
2484
|
+
if (!otherFunctionCall.typeParameters) {
|
|
2485
|
+
return this.structuralMismatch('typeParameters');
|
|
2486
|
+
}
|
|
2487
|
+
|
|
2488
|
+
await this.visitContainerProperty('typeParameters', functionCall.typeParameters, otherFunctionCall.typeParameters);
|
|
2489
|
+
if (!this.match) return functionCall;
|
|
2490
|
+
}
|
|
2491
|
+
|
|
2492
|
+
// Type arguments are settled, so they come off both sides and the rest compares property by property
|
|
2493
|
+
const patternCall: JS.FunctionCall = {...functionCall, typeParameters: undefined};
|
|
2494
|
+
const targetCall: JS.FunctionCall = {...otherFunctionCall, typeParameters: undefined};
|
|
2495
|
+
await super.visitFunctionCall(patternCall, targetCall);
|
|
2496
|
+
return functionCall;
|
|
2497
|
+
}
|
|
2498
|
+
|
|
2478
2499
|
/**
|
|
2479
2500
|
* Override identifier comparison to include:
|
|
2480
2501
|
* 1. Type checking for field access
|
|
@@ -2520,6 +2541,10 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2520
2541
|
* code with typeExpression.
|
|
2521
2542
|
*/
|
|
2522
2543
|
override async visitVariableDeclarations(variableDeclarations: J.VariableDeclarations, other: J): Promise<J | undefined> {
|
|
2544
|
+
if (other.kind !== J.Kind.VariableDeclarations) {
|
|
2545
|
+
return this.kindMismatch();
|
|
2546
|
+
}
|
|
2547
|
+
|
|
2523
2548
|
const otherVariableDeclarations = other as J.VariableDeclarations;
|
|
2524
2549
|
|
|
2525
2550
|
// Visit leading annotations
|
|
@@ -2578,6 +2603,10 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2578
2603
|
* code with returnTypeExpression.
|
|
2579
2604
|
*/
|
|
2580
2605
|
override async visitMethodDeclaration(methodDeclaration: J.MethodDeclaration, other: J): Promise<J | undefined> {
|
|
2606
|
+
if (other.kind !== J.Kind.MethodDeclaration) {
|
|
2607
|
+
return this.kindMismatch();
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2581
2610
|
const otherMethodDeclaration = other as J.MethodDeclaration;
|
|
2582
2611
|
|
|
2583
2612
|
// Visit leading annotations
|
|
@@ -17,7 +17,7 @@ import {JS} from "../tree";
|
|
|
17
17
|
import {JavaScriptVisitor} from "../visitor";
|
|
18
18
|
import {Comment, J, lastWhitespace, replaceLastWhitespace, Statement} from "../../java";
|
|
19
19
|
import {create as produce, Draft} from "mutative";
|
|
20
|
-
import {Cursor, isScope, Tree} from "../../tree";
|
|
20
|
+
import {Cursor, isScope, isSourceFile, Tree} from "../../tree";
|
|
21
21
|
import {BlankLinesStyle, getStyle, SpacesStyle, StyleKind, TabsAndIndentsStyle, WrappingAndBracesStyle} from "../style";
|
|
22
22
|
import {NamedStyles} from "../../style";
|
|
23
23
|
import {produceAsync} from "../../visitor";
|
|
@@ -74,13 +74,16 @@ export class AutoformatVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
74
74
|
return applyPrettierFormatting(tree as R, prettierStyle, p, cursor, this.stopAfter);
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
// Style markers live on the source file, which is `tree` itself only when a whole file is being formatted
|
|
78
|
+
const styleSource = (isSourceFile(tree) ? tree : cursor?.firstEnclosing(isSourceFile)) ?? tree;
|
|
79
|
+
|
|
77
80
|
const visitors = [
|
|
78
81
|
new NormalizeWhitespaceVisitor(this.stopAfter),
|
|
79
82
|
new MinimumViableSpacingVisitor(this.stopAfter),
|
|
80
|
-
new BlankLinesVisitor(getStyle(StyleKind.BlankLinesStyle,
|
|
81
|
-
new WrappingAndBracesVisitor(getStyle(StyleKind.WrappingAndBracesStyle,
|
|
82
|
-
new SpacesVisitor(getStyle(StyleKind.SpacesStyle,
|
|
83
|
-
new TabsAndIndentsVisitor(getStyle(StyleKind.TabsAndIndentsStyle,
|
|
83
|
+
new BlankLinesVisitor(getStyle(StyleKind.BlankLinesStyle, styleSource, this.styles) as BlankLinesStyle, this.stopAfter),
|
|
84
|
+
new WrappingAndBracesVisitor(getStyle(StyleKind.WrappingAndBracesStyle, styleSource, this.styles) as WrappingAndBracesStyle, this.stopAfter),
|
|
85
|
+
new SpacesVisitor(getStyle(StyleKind.SpacesStyle, styleSource, this.styles) as SpacesStyle, this.stopAfter),
|
|
86
|
+
new TabsAndIndentsVisitor(getStyle(StyleKind.TabsAndIndentsStyle, styleSource, this.styles) as TabsAndIndentsStyle, this.stopAfter),
|
|
84
87
|
]
|
|
85
88
|
|
|
86
89
|
let t: R | undefined = tree as R;
|
|
@@ -227,11 +230,7 @@ export class SpacesVisitor<P> extends JavaScriptVisitor<P> {
|
|
|
227
230
|
// Apply beforeComma rule to all elements except the last
|
|
228
231
|
// (last element's after is before closing bracket, not a comma)
|
|
229
232
|
for (let i = 0; i < draft.elements.length - 1; i++) {
|
|
230
|
-
|
|
231
|
-
// Preserve newlines - only adjust when on same line
|
|
232
|
-
if (!afterWs.includes("\n")) {
|
|
233
|
-
draft.elements[i].after.whitespace = this.style.other.beforeComma ? " " : "";
|
|
234
|
-
}
|
|
233
|
+
this.spaceAfterRightPaddedDraft(draft.elements[i], this.style.other.beforeComma);
|
|
235
234
|
}
|
|
236
235
|
}
|
|
237
236
|
if (draft.elements.length > 1) {
|