Skip to content

Commit 574e0c2

Browse files
authored
Merge pull request #51 from babs20/feature/split-lines
feat: add codemod for `split-lines`
2 parents be76cbc + 1426a9f commit 574e0c2

File tree

7 files changed

+362
-0
lines changed

7 files changed

+362
-0
lines changed

codemods/split-lines/index.js

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import jscodeshift from 'jscodeshift';
2+
import { removeImport } from '../shared.js';
3+
4+
/**
5+
* @typedef {import('../../types.js').Codemod} Codemod
6+
* @typedef {import('../../types.js').CodemodOptions} CodemodOptions
7+
*/
8+
9+
/**
10+
* @param {CodemodOptions} [options]
11+
* @returns {Codemod}
12+
*/
13+
export default function (options) {
14+
return {
15+
name: 'split-lines',
16+
transform: ({ file }) => {
17+
const j = jscodeshift;
18+
const root = j(file.source);
19+
let isDirty = false;
20+
21+
const { identifier } = removeImport('split-lines', root, j);
22+
23+
root
24+
.find(j.CallExpression, { callee: { name: identifier } })
25+
.replaceWith((path) => {
26+
const { node } = path;
27+
const [stringArg, optionsArg] = node.arguments;
28+
29+
if (stringArg && stringArg.type !== 'Literal') {
30+
return;
31+
}
32+
33+
if (optionsArg && optionsArg.type !== 'ObjectExpression') {
34+
return;
35+
}
36+
37+
isDirty = true;
38+
39+
const defaultCallExpression = j.callExpression(
40+
j.memberExpression(stringArg, j.identifier('split')),
41+
[j.literal(/\r?\n/)],
42+
);
43+
44+
const hasPreserveNewlines = optionsArg?.properties.some(
45+
(prop) =>
46+
prop.type === 'Property' &&
47+
prop.key.type === 'Identifier' &&
48+
prop.key.name === 'preserveNewlines',
49+
);
50+
51+
if (hasPreserveNewlines) {
52+
return j.callExpression(
53+
j.memberExpression(
54+
j.callExpression(
55+
j.memberExpression(stringArg, j.identifier('split')),
56+
[j.literal(/(\r?\n)/)],
57+
),
58+
j.identifier('reduce'),
59+
),
60+
[
61+
j.arrowFunctionExpression(
62+
[
63+
j.identifier('acc'),
64+
j.identifier('part'),
65+
j.identifier('index'),
66+
j.identifier('array'),
67+
],
68+
j.blockStatement([
69+
j.ifStatement(
70+
j.binaryExpression(
71+
'===',
72+
j.binaryExpression(
73+
'%',
74+
j.identifier('index'),
75+
j.literal(2),
76+
),
77+
j.literal(0),
78+
),
79+
j.blockStatement([
80+
j.expressionStatement(
81+
j.callExpression(
82+
j.memberExpression(
83+
j.identifier('acc'),
84+
j.identifier('push'),
85+
),
86+
[
87+
j.binaryExpression(
88+
'+',
89+
j.identifier('part'),
90+
j.logicalExpression(
91+
'||',
92+
j.memberExpression(
93+
j.identifier('array'),
94+
j.binaryExpression(
95+
'+',
96+
j.identifier('index'),
97+
j.literal(1),
98+
),
99+
true,
100+
),
101+
j.literal(''),
102+
),
103+
),
104+
],
105+
),
106+
),
107+
]),
108+
),
109+
j.returnStatement(j.identifier('acc')),
110+
]),
111+
false,
112+
),
113+
j.arrayExpression([]),
114+
],
115+
);
116+
}
117+
118+
return defaultCallExpression;
119+
});
120+
121+
return isDirty ? root.toSource(options) : file.source;
122+
},
123+
};
124+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import assert from 'assert';
2+
3+
// Basic case
4+
assert.deepStrictEqual('foo\nbar\nbaz\nrainbow'.split(/\r?\n/), ['foo', 'bar', 'baz', 'rainbow']);
5+
6+
// Preserve newlines case
7+
assert.deepStrictEqual('foo\r\nbar\r\nbaz\nrainbow'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
8+
if (index % 2 === 0) {
9+
acc.push(part + (array[index + 1] || ""));
10+
}
11+
12+
return acc;
13+
}, []), ['foo\r\n', 'bar\r\n', 'baz\n', 'rainbow']);
14+
15+
// Empty string
16+
assert.deepStrictEqual(''.split(/\r?\n/), ['']);
17+
18+
// Single line
19+
assert.deepStrictEqual('foo'.split(/\r?\n/), ['foo']);
20+
21+
// Only newline characters
22+
assert.deepStrictEqual('\n'.split(/\r?\n/), ['', '']);
23+
assert.deepStrictEqual('\r\n'.split(/\r?\n/), ['', '']);
24+
25+
// Newlines at the start and end
26+
assert.deepStrictEqual('\nfoo\n'.split(/\r?\n/), ['', 'foo', '']);
27+
assert.deepStrictEqual('\r\nfoo\r\n'.split(/\r?\n/), ['', 'foo', '']);
28+
29+
// Mixed newlines
30+
assert.deepStrictEqual('foo\r\nbar\r\nbaz\r\n'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
31+
if (index % 2 === 0) {
32+
acc.push(part + (array[index + 1] || ""));
33+
}
34+
35+
return acc;
36+
}, []), ['foo\r\n', 'bar\r\n', 'baz\r\n', '']);
37+
38+
// Newlines with additional properties
39+
assert.deepStrictEqual('foo\nbar\nbaz\nrainbow'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
40+
if (index % 2 === 0) {
41+
acc.push(part + (array[index + 1] || ""));
42+
}
43+
44+
return acc;
45+
}, []), ['foo\n', 'bar\n', 'baz\n', 'rainbow']);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Test cases to ensure the codemod works correctly
2+
import splitLines from 'split-lines';
3+
import assert from 'assert';
4+
5+
// Basic case
6+
assert.deepStrictEqual(splitLines('foo\nbar\nbaz\nrainbow'), ['foo', 'bar', 'baz', 'rainbow']);
7+
8+
// Preserve newlines case
9+
assert.deepStrictEqual(splitLines('foo\r\nbar\r\nbaz\nrainbow', { preserveNewlines: true }), ['foo\r\n', 'bar\r\n', 'baz\n', 'rainbow']);
10+
11+
// Empty string
12+
assert.deepStrictEqual(splitLines(''), ['']);
13+
14+
// Single line
15+
assert.deepStrictEqual(splitLines('foo'), ['foo']);
16+
17+
// Only newline characters
18+
assert.deepStrictEqual(splitLines('\n'), ['', '']);
19+
assert.deepStrictEqual(splitLines('\r\n'), ['', '']);
20+
21+
// Newlines at the start and end
22+
assert.deepStrictEqual(splitLines('\nfoo\n'), ['', 'foo', '']);
23+
assert.deepStrictEqual(splitLines('\r\nfoo\r\n'), ['', 'foo', '']);
24+
25+
// Mixed newlines
26+
assert.deepStrictEqual(splitLines('foo\r\nbar\r\nbaz\r\n', { preserveNewlines: true }), ['foo\r\n', 'bar\r\n', 'baz\r\n', '']);
27+
28+
// Newlines with additional properties
29+
assert.deepStrictEqual(splitLines('foo\nbar\nbaz\nrainbow', { preserveNewlines: true, additionalProperty: true }), ['foo\n', 'bar\n', 'baz\n', 'rainbow']);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
import assert from 'assert';
2+
3+
// Basic case
4+
assert.deepStrictEqual('foo\nbar\nbaz\nrainbow'.split(/\r?\n/), ['foo', 'bar', 'baz', 'rainbow']);
5+
6+
// Preserve newlines case
7+
assert.deepStrictEqual('foo\r\nbar\r\nbaz\nrainbow'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
8+
if (index % 2 === 0) {
9+
acc.push(part + (array[index + 1] || ""));
10+
}
11+
12+
return acc;
13+
}, []), ['foo\r\n', 'bar\r\n', 'baz\n', 'rainbow']);
14+
15+
// Empty string
16+
assert.deepStrictEqual(''.split(/\r?\n/), ['']);
17+
18+
// Single line
19+
assert.deepStrictEqual('foo'.split(/\r?\n/), ['foo']);
20+
21+
// Only newline characters
22+
assert.deepStrictEqual('\n'.split(/\r?\n/), ['', '']);
23+
assert.deepStrictEqual('\r\n'.split(/\r?\n/), ['', '']);
24+
25+
// Newlines at the start and end
26+
assert.deepStrictEqual('\nfoo\n'.split(/\r?\n/), ['', 'foo', '']);
27+
assert.deepStrictEqual('\r\nfoo\r\n'.split(/\r?\n/), ['', 'foo', '']);
28+
29+
// Mixed newlines
30+
assert.deepStrictEqual('foo\r\nbar\r\nbaz\r\n'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
31+
if (index % 2 === 0) {
32+
acc.push(part + (array[index + 1] || ""));
33+
}
34+
35+
return acc;
36+
}, []), ['foo\r\n', 'bar\r\n', 'baz\r\n', '']);
37+
38+
// Newlines with additional properties
39+
assert.deepStrictEqual('foo\nbar\nbaz\nrainbow'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
40+
if (index % 2 === 0) {
41+
acc.push(part + (array[index + 1] || ""));
42+
}
43+
44+
return acc;
45+
}, []), ['foo\n', 'bar\n', 'baz\n', 'rainbow']);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
var assert = require('assert');
2+
3+
// Basic case
4+
assert.deepStrictEqual('foo\nbar\nbaz\nrainbow'.split(/\r?\n/), ['foo', 'bar', 'baz', 'rainbow']);
5+
6+
// Preserve newlines case
7+
assert.deepStrictEqual('foo\r\nbar\r\nbaz\nrainbow'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
8+
if (index % 2 === 0) {
9+
acc.push(part + (array[index + 1] || ""));
10+
}
11+
12+
return acc;
13+
}, []), ['foo\r\n', 'bar\r\n', 'baz\n', 'rainbow']);
14+
15+
// Empty string
16+
assert.deepStrictEqual(''.split(/\r?\n/), ['']);
17+
18+
// Single line
19+
assert.deepStrictEqual('foo'.split(/\r?\n/), ['foo']);
20+
21+
// Only newline characters
22+
assert.deepStrictEqual('\n'.split(/\r?\n/), ['', '']);
23+
assert.deepStrictEqual('\r\n'.split(/\r?\n/), ['', '']);
24+
25+
// Newlines at the start and end
26+
assert.deepStrictEqual('\nfoo\n'.split(/\r?\n/), ['', 'foo', '']);
27+
assert.deepStrictEqual('\r\nfoo\r\n'.split(/\r?\n/), ['', 'foo', '']);
28+
29+
// Mixed newlines
30+
assert.deepStrictEqual('foo\r\nbar\r\nbaz\r\n'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
31+
if (index % 2 === 0) {
32+
acc.push(part + (array[index + 1] || ""));
33+
}
34+
35+
return acc;
36+
}, []), ['foo\r\n', 'bar\r\n', 'baz\r\n', '']);
37+
38+
// Newlines with additional properties
39+
assert.deepStrictEqual('foo\nbar\nbaz\nrainbow'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
40+
if (index % 2 === 0) {
41+
acc.push(part + (array[index + 1] || ""));
42+
}
43+
44+
return acc;
45+
}, []), ['foo\n', 'bar\n', 'baz\n', 'rainbow']);
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// Test cases to ensure the codemod works correctly
2+
var splitLines = require('split-lines');
3+
var assert = require('assert');
4+
5+
// Basic case
6+
assert.deepStrictEqual(splitLines('foo\nbar\nbaz\nrainbow'), ['foo', 'bar', 'baz', 'rainbow']);
7+
8+
// Preserve newlines case
9+
assert.deepStrictEqual(splitLines('foo\r\nbar\r\nbaz\nrainbow', { preserveNewlines: true }), ['foo\r\n', 'bar\r\n', 'baz\n', 'rainbow']);
10+
11+
// Empty string
12+
assert.deepStrictEqual(splitLines(''), ['']);
13+
14+
// Single line
15+
assert.deepStrictEqual(splitLines('foo'), ['foo']);
16+
17+
// Only newline characters
18+
assert.deepStrictEqual(splitLines('\n'), ['', '']);
19+
assert.deepStrictEqual(splitLines('\r\n'), ['', '']);
20+
21+
// Newlines at the start and end
22+
assert.deepStrictEqual(splitLines('\nfoo\n'), ['', 'foo', '']);
23+
assert.deepStrictEqual(splitLines('\r\nfoo\r\n'), ['', 'foo', '']);
24+
25+
// Mixed newlines
26+
assert.deepStrictEqual(splitLines('foo\r\nbar\r\nbaz\r\n', { preserveNewlines: true }), ['foo\r\n', 'bar\r\n', 'baz\r\n', '']);
27+
28+
// Newlines with additional properties
29+
assert.deepStrictEqual(splitLines('foo\nbar\nbaz\nrainbow', { preserveNewlines: true, additionalProperty: true }), ['foo\n', 'bar\n', 'baz\n', 'rainbow']);
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
var assert = require('assert');
2+
3+
// Basic case
4+
assert.deepStrictEqual('foo\nbar\nbaz\nrainbow'.split(/\r?\n/), ['foo', 'bar', 'baz', 'rainbow']);
5+
6+
// Preserve newlines case
7+
assert.deepStrictEqual('foo\r\nbar\r\nbaz\nrainbow'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
8+
if (index % 2 === 0) {
9+
acc.push(part + (array[index + 1] || ""));
10+
}
11+
12+
return acc;
13+
}, []), ['foo\r\n', 'bar\r\n', 'baz\n', 'rainbow']);
14+
15+
// Empty string
16+
assert.deepStrictEqual(''.split(/\r?\n/), ['']);
17+
18+
// Single line
19+
assert.deepStrictEqual('foo'.split(/\r?\n/), ['foo']);
20+
21+
// Only newline characters
22+
assert.deepStrictEqual('\n'.split(/\r?\n/), ['', '']);
23+
assert.deepStrictEqual('\r\n'.split(/\r?\n/), ['', '']);
24+
25+
// Newlines at the start and end
26+
assert.deepStrictEqual('\nfoo\n'.split(/\r?\n/), ['', 'foo', '']);
27+
assert.deepStrictEqual('\r\nfoo\r\n'.split(/\r?\n/), ['', 'foo', '']);
28+
29+
// Mixed newlines
30+
assert.deepStrictEqual('foo\r\nbar\r\nbaz\r\n'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
31+
if (index % 2 === 0) {
32+
acc.push(part + (array[index + 1] || ""));
33+
}
34+
35+
return acc;
36+
}, []), ['foo\r\n', 'bar\r\n', 'baz\r\n', '']);
37+
38+
// Newlines with additional properties
39+
assert.deepStrictEqual('foo\nbar\nbaz\nrainbow'.split(/(\r?\n)/).reduce((acc, part, index, array) => {
40+
if (index % 2 === 0) {
41+
acc.push(part + (array[index + 1] || ""));
42+
}
43+
44+
return acc;
45+
}, []), ['foo\n', 'bar\n', 'baz\n', 'rainbow']);

0 commit comments

Comments
 (0)