Skip to content

Commit 78162a5

Browse files
committed
add wrap api
1 parent e9bf4b4 commit 78162a5

File tree

5 files changed

+116
-7
lines changed

5 files changed

+116
-7
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ mutation.group(() => {
6868
mutation.delete(node: Node, selectors: string | string[], options: DeleteOptions)
6969
mutation.insert(node: Node, code: string, options: InsertOptions)
7070
})
71+
// wrap the ast node
72+
mutation.wrap(node: Node, options: WrapOptions)
7173
```
7274

7375
3. process actions and write the new source code to file:

src/action/indent.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,17 @@ import NodeMutation from "../node-mutation";
88
* @extends BaseAction
99
*/
1010
export class IndentAction<T> extends BaseAction<T> {
11-
private options: IndentOptions;
11+
private tabSize: number;
1212

1313
/**
1414
* Create an IndentAction
1515
* @param {T} node
1616
* @param {IndentOptions} options
1717
* @param options.adapter - adapter to parse the node
1818
*/
19-
constructor(node: T, options: IndentOptions & { adapter: Adapter<T> }) {
20-
super(node, "", { adapter: options.adapter });
21-
this.options = { tabSize: 1, ...options };
19+
constructor(node: T, { tabSize, adapter }: IndentOptions & { adapter: Adapter<T> }) {
20+
super(node, "", { adapter });
21+
this.tabSize = tabSize || 1;
2222
this.type = "replace";
2323
}
2424

@@ -37,6 +37,6 @@ export class IndentAction<T> extends BaseAction<T> {
3737
*/
3838
get newCode(): string {
3939
const source = this.adapter.getSource(this.node!);
40-
return source.split("\n").map(line => ' '.repeat(NodeMutation.tabWidth * this.options.tabSize!) + line).join("\n");
40+
return source.split("\n").map(line => ' '.repeat(NodeMutation.tabWidth * this.tabSize) + line).join("\n");
4141
}
4242
}

src/node-mutation.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import debug from "debug";
2-
import type { Action, IndentOptions, InsertOptions, ReplaceOptions, DeleteOptions, RemoveOptions } from "./types/action";
2+
import type { Action, IndentOptions, InsertOptions, ReplaceOptions, DeleteOptions, RemoveOptions, WrapOptions } from "./types/action";
33
import type { ProcessResult, TestResult } from "./types/node-mutation";
44
import Adapter from "./adapter";
55
import TypescriptAdapter from "./adapter/typescript";
@@ -275,6 +275,44 @@ class NodeMutation<T> {
275275
this.actions.push(new ReplaceWithAction<T>(node, code, { adapter: this.adapter }).process());
276276
}
277277

278+
/**
279+
* Wrap the ast node with prefix and suffix.
280+
* @param node {T} - ast node
281+
* @param prefix {string} - prefix
282+
* @param suffix {string} - suffix
283+
* @param newLine {boolean} - if true, the prefix and suffix will be wrapped in a new line
284+
* @example
285+
* source code of the ast node is
286+
* ```
287+
* console.log('foo)
288+
* ```
289+
* then we call
290+
* ```
291+
* mutation.wrap(node, { prefix: "function logFoo() {", suffix: "}", newLine: true });
292+
* ```
293+
* the source code will be rewritten to
294+
* ```
295+
* function logFoo() {
296+
* console.log('foo')
297+
* }
298+
* ```
299+
*/
300+
wrap(node: T, { prefix, suffix, newLine }: WrapOptions) {
301+
if (newLine) {
302+
const indentation = this.adapter.getStartLoc(node).column;
303+
this.group(() => {
304+
this.insert(node, prefix + "\n" + (' '.repeat(indentation)), { at: "beginning" });
305+
this.insert(node, "\n" + (' '.repeat(indentation)) + suffix, { at: "end" });
306+
this.indent(node);
307+
});
308+
} else {
309+
this.group(() => {
310+
this.insert(node, prefix, { at: "beginning" });
311+
this.insert(node, suffix, { at: "end" });
312+
});
313+
}
314+
}
315+
278316
/**
279317
* Process Result
280318
* @typedef {Object} ProcessResult

src/types/action.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,9 @@ export type DeleteOptions = {
3333
export type RemoveOptions = {
3434
andComma?: boolean;
3535
}
36+
37+
export type WrapOptions = {
38+
prefix: string;
39+
suffix: string;
40+
newLine: boolean;
41+
}

test/node-mutation.spec.ts

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import Strategy from "../src/strategy";
44
import { ConflictActionError } from "../src/error";
55
import { parseCode } from "./helper";
66
import { Node } from "typescript";
7-
import TypescriptAdapter from "../src/adapter/typescript";
87

98
describe("NodeMutation", () => {
109
describe("configure", () => {
10+
afterEach(() => {
11+
NodeMutation.configure({ tabWidth: 2 });
12+
});
13+
1114
it("sets tabWidth", () => {
1215
expect(NodeMutation.tabWidth).toEqual(2);
1316
NodeMutation.configure({ tabWidth: 4 });
@@ -128,6 +131,24 @@ describe("NodeMutation", () => {
128131
}
129132
`)
130133
});
134+
135+
it("wraps the ast node", () => {
136+
const source1 = dedent`
137+
console.log('foo');
138+
`;
139+
NodeMutation.configure({ strategy: Strategy.KEEP_RUNNING });
140+
const mutation = new NodeMutation<Node>(source1, { adapter: "typescript" });
141+
const node = parseCode(source1);
142+
mutation.wrap(node, { prefix: "function logFoo() {", suffix: "}", newLine: true });
143+
const result = mutation.process();
144+
expect(result.affected).toBeTruthy();
145+
expect(result.conflicted).toBeFalsy();
146+
expect(result.newSource).toEqual(dedent`
147+
function logFoo() {
148+
console.log('foo');
149+
}
150+
`);
151+
});
131152
});
132153

133154
describe("test", () => {
@@ -260,5 +281,47 @@ describe("NodeMutation", () => {
260281
}],
261282
}])
262283
});
284+
285+
it("wraps the ast node", () => {
286+
const source1 = dedent`
287+
console.log('foo');
288+
`;
289+
NodeMutation.configure({ strategy: Strategy.KEEP_RUNNING });
290+
const mutation = new NodeMutation<Node>(source1, { adapter: "typescript" });
291+
const node = parseCode(source1);
292+
mutation.wrap(node, { prefix: "function logFoo() {", suffix: "}", newLine: true });
293+
const result = mutation.test();
294+
expect(result.affected).toBeTruthy();
295+
expect(result.conflicted).toBeFalsy();
296+
expect(result.actions).toEqual([{
297+
"actions": [
298+
{
299+
"actions": undefined,
300+
"end": 0,
301+
"newCode": `function logFoo() {\n`,
302+
"start": 0,
303+
"type": "insert",
304+
},
305+
{
306+
"actions": undefined,
307+
"end": 19,
308+
"newCode": " console.log('foo');",
309+
"start": 0,
310+
"type": "replace",
311+
},
312+
{
313+
"actions": undefined,
314+
"end": 19,
315+
"newCode": `\n}`,
316+
"start": 19,
317+
"type": "insert",
318+
},
319+
],
320+
"end": 19,
321+
"newCode": undefined,
322+
"start": 0,
323+
"type": "group",
324+
}]);
325+
});
263326
});
264327
});

0 commit comments

Comments
 (0)