-
-
Notifications
You must be signed in to change notification settings - Fork 14
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: new
/// to-one-line
command (#35)
* docs: fix typo * feat: added to-one-line command
- Loading branch information
Showing
4 changed files
with
259 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
# `to-one-line` | ||
|
||
Convert multiple lines object to one line object. | ||
|
||
## Triggers | ||
|
||
- `/// to-one-line` | ||
- `/// tol` | ||
- `/// 21l` | ||
|
||
## Examples | ||
|
||
```js | ||
/// to-one-line | ||
const foo = { | ||
bar: 1, | ||
baz: 2 | ||
} | ||
``` | ||
|
||
Will be converted to: | ||
|
||
```js | ||
const foo = { bar: 1, baz: 2 } | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
import { $, run } from './_test-utils' | ||
import { toOneLine as command } from './to-one-line' | ||
|
||
run( | ||
command, | ||
{ | ||
code: $` | ||
/// to-one-line | ||
const foo = { | ||
bar: 1, | ||
baz: 2, | ||
} | ||
`, | ||
output: $` | ||
const foo = { bar: 1, baz: 2 } | ||
`, | ||
errors: ['command-fix'], | ||
}, | ||
{ | ||
code: $` | ||
/// to-one-line | ||
const arr = [ | ||
1, | ||
2, | ||
3, | ||
4, | ||
] | ||
`, | ||
output: $` | ||
const arr = [1, 2, 3, 4] | ||
`, | ||
errors: ['command-fix'], | ||
}, | ||
{ | ||
code: $` | ||
/// tol | ||
obj = { | ||
x: 100, | ||
y: 200, | ||
} | ||
`, | ||
output: $` | ||
obj = { x: 100, y: 200 } | ||
`, | ||
errors: ['command-fix'], | ||
}, | ||
{ | ||
code: $` | ||
/// to-one-line | ||
const data = { | ||
user: { | ||
name: 'Alice', | ||
age: 30, | ||
}, | ||
scores: [ | ||
10, | ||
20, | ||
30, | ||
], | ||
} | ||
`, | ||
output: $` | ||
const data = { user: { name: 'Alice', age: 30 }, scores: [10, 20, 30] } | ||
`, | ||
errors: ['command-fix'], | ||
}, | ||
{ | ||
code: $` | ||
/// 21l | ||
const alreadyOneLine = { a: 1, b: 2 } | ||
`, | ||
output: $` | ||
const alreadyOneLine = { a: 1, b: 2 } | ||
`, | ||
errors: ['command-fix'], | ||
}, | ||
{ | ||
code: $` | ||
/// to-one-line | ||
const fruits = [ | ||
"apple", | ||
"banana", | ||
"cherry", | ||
] | ||
`, | ||
output: $` | ||
const fruits = ["apple", "banana", "cherry"] | ||
`, | ||
errors: ['command-fix'], | ||
}, | ||
{ | ||
code: $` | ||
/// to-one-line | ||
whichFruitIsTheBest([ | ||
"apple", | ||
"banana", | ||
"cherry", | ||
]) | ||
`, | ||
output: $` | ||
whichFruitIsTheBest(["apple", "banana", "cherry"]) | ||
`, | ||
errors: ['command-fix'], | ||
}, | ||
{ | ||
code: $` | ||
/// to-one-line | ||
function whichFruitIsTheBest({ | ||
apple, | ||
banana, | ||
cherry, | ||
}) {} | ||
`, | ||
output: $` | ||
function whichFruitIsTheBest({ apple, banana, cherry }) {} | ||
`, | ||
errors: ['command-fix'], | ||
}, | ||
{ | ||
code: $` | ||
/// to-one-line | ||
function f([ | ||
a, | ||
b, | ||
c, | ||
]) {} | ||
`, | ||
output: $` | ||
function f([a, b, c]) {} | ||
`, | ||
errors: ['command-fix'], | ||
}, | ||
{ | ||
code: $` | ||
/// to-one-line | ||
return { | ||
foo: 1, | ||
bar: 2, | ||
} | ||
`, | ||
output: $` | ||
return { foo: 1, bar: 2 } | ||
`, | ||
errors: ['command-fix'], | ||
}, | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import type { Command, NodeType, Tree } from '../types' | ||
|
||
export const toOneLine: Command = { | ||
name: 'to-one-line', | ||
match: /^[/@:]\s*(?:to-one-line|21l|tol)$/, | ||
action(ctx) { | ||
const node = ctx.findNodeBelow( | ||
'VariableDeclaration', | ||
'AssignmentExpression', | ||
'CallExpression', | ||
'FunctionDeclaration', | ||
'FunctionExpression', | ||
'ReturnStatement', | ||
) | ||
if (!node) | ||
return ctx.reportError('Unable to find node to convert') | ||
|
||
let target: Tree.Node | null = null | ||
|
||
// For a variable declaration we use the initializer. | ||
if (node.type === 'VariableDeclaration') { | ||
const decl = node.declarations[0] | ||
if (decl && decl.init && isAllowedType(decl.init.type)) | ||
target = decl.init | ||
} | ||
// For an assignment we use the right side. | ||
else if (node.type === 'AssignmentExpression') { | ||
if (node.right && isAllowedType(node.right.type)) | ||
target = node.right | ||
} | ||
// In a call we search the arguments. | ||
else if (node.type === 'CallExpression') { | ||
target = node.arguments.find(arg => isAllowedType(arg.type)) || null | ||
} | ||
// In a function we search the parameters. | ||
else if ( | ||
node.type === 'FunctionDeclaration' | ||
|| node.type === 'FunctionExpression' | ||
) { | ||
target = node.params.find(param => isAllowedType(param.type)) || null | ||
} | ||
// For a return statement we use its argument. | ||
else if (node.type === 'ReturnStatement') { | ||
if (node.argument && isAllowedType(node.argument.type)) | ||
target = node.argument | ||
} | ||
|
||
if (!target) | ||
return ctx.reportError('Unable to find object/array literal or pattern to convert') | ||
|
||
// Get the text of the node to reformat it. | ||
const original = ctx.getTextOf(target) | ||
// Replace line breaks with spaces and remove extra spaces. | ||
let oneLine = original.replace(/\n/g, ' ').replace(/\s{2,}/g, ' ').trim() | ||
// Remove a comma that comes before a closing bracket or brace. | ||
oneLine = oneLine.replace(/,\s*([}\]])/g, '$1') | ||
|
||
if (target.type === 'ArrayExpression' || target.type === 'ArrayPattern') { | ||
// For arrays, add a missing space before a closing bracket. | ||
oneLine = oneLine.replace(/\[\s+/g, '[').replace(/\s+\]/g, ']') | ||
} | ||
else { | ||
// For objects, add a missing space before a closing bracket or brace. | ||
oneLine = oneLine.replace(/([^ \t])([}\]])/g, '$1 $2') | ||
// Add a space between a ']' and a '}' if they touch. | ||
oneLine = oneLine.replace(/\](\})/g, '] $1') | ||
} | ||
|
||
// Fix any nested array formatting. | ||
oneLine = oneLine.replace(/\[\s+/g, '[').replace(/\s+\]/g, ']') | ||
|
||
ctx.report({ | ||
node: target, | ||
message: 'Convert object/array to one line', | ||
fix: fixer => fixer.replaceTextRange(target.range, oneLine), | ||
}) | ||
|
||
function isAllowedType(type: NodeType): boolean { | ||
return ( | ||
type === 'ObjectExpression' | ||
|| type === 'ArrayExpression' | ||
|| type === 'ObjectPattern' | ||
|| type === 'ArrayPattern' | ||
) | ||
} | ||
}, | ||
} |