-
Notifications
You must be signed in to change notification settings - Fork 47
/
index.ts
79 lines (67 loc) · 1.99 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
import { parse } from '@babel/babylon'
import generate from '@babel/generator'
import traverse, { Node, Visitor } from '@babel/traverse'
import { File } from '@babel/types'
import { sync } from 'glob'
import { dropWhile, pullAt } from 'lodash'
import { EOL } from 'os'
import { relative, resolve } from 'path'
type Warning = [string, string, number, number]
type Rule = (warnings: Warning[]) => Visitor<Node>
let rules = new Map<string, Rule>()
export function addRule(ruleName: string, rule: Rule) {
if (rules.has(ruleName)) {
throw `A rule with the name "${ruleName}" is already defined`
}
rules.set(ruleName, rule)
}
export async function compile(code: string, filename: string) {
let [warnings, ast] = await convert(
parse(code, {
plugins: ['classProperties', 'flow', 'objectRestSpread'],
sourceType: 'module'
})
)
warnings.forEach(([message, issueURL, line, column]) => {
console.log(
`Warning: ${message} (at ${relative(
__dirname,
filename
)}: line ${line}, column ${column}). See ${issueURL}`
)
})
return addTrailingSpace(
trimLeadingNewlines(generate(stripAtFlowAnnotation(ast)).code)
)
}
/**
* @internal
*/
export async function convert<T extends Node>(ast: T): Promise<[Warning[], T]> {
// load rules directory
await Promise.all(
sync(resolve(__dirname, './rules/*.js')).map(_ => import(_))
)
let warnings: Warning[] = []
rules.forEach(visitor => traverse(ast, visitor(warnings)))
return [warnings, ast]
}
function stripAtFlowAnnotation(ast: File): File {
let { leadingComments } = ast.program.body[0]
if (leadingComments) {
let index = leadingComments.findIndex(_ => _.value.trim() === '@flow')
if (index > -1) {
pullAt(leadingComments, index)
}
}
return ast
}
function addTrailingSpace(file: string): string {
if (file.endsWith(EOL)) {
return file
}
return file + EOL
}
function trimLeadingNewlines(file: string): string {
return dropWhile(file.split(EOL), _ => !_).join(EOL)
}