forked from lyft/react-javascript-to-typescript-transform
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathcompiler.ts
120 lines (105 loc) · 3.96 KB
/
compiler.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
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
import * as os from 'os';
import * as fs from 'fs';
import * as ts from 'typescript';
import * as chalk from 'chalk';
import * as _ from 'lodash';
import * as prettier from 'prettier';
import * as detectIndent from 'detect-indent';
import { TransformFactoryFactory } from '.';
/**
* Compile and return result TypeScript
* @param filePath Path to file to compile
*/
export function compile(
filePath: string,
factoryFactories: TransformFactoryFactory[],
incomingPrettierOptions: prettier.Options = {},
) {
const compilerOptions: ts.CompilerOptions = {
target: ts.ScriptTarget.ES2017,
module: ts.ModuleKind.ES2015,
};
const program = ts.createProgram([filePath], compilerOptions);
// `program.getSourceFiles()` will include those imported files,
// like: `import * as a from './file-a'`.
// We should only transform current file.
const sourceFiles = program.getSourceFiles().filter(sf => sf.fileName === filePath);
const typeChecker = program.getTypeChecker();
const result = ts.transform(
sourceFiles,
factoryFactories.map(factoryFactory => factoryFactory(typeChecker), compilerOptions),
);
if (result.diagnostics && result.diagnostics.length) {
console.log(
chalk.yellow(`
======================= Diagnostics for ${filePath} =======================
`),
);
for (const diag of result.diagnostics) {
if (diag.file && diag.start) {
const pos = diag.file.getLineAndCharacterOfPosition(diag.start);
console.log(`(${pos.line}, ${pos.character}) ${diag.messageText}`);
}
}
}
const printer = ts.createPrinter();
// TODO: fix the index 0 access... What if program have multiple source files?
const printed = printer.printNode(ts.EmitHint.SourceFile, result.transformed[0], sourceFiles[0]);
const inputSource = fs.readFileSync(filePath, 'utf-8');
const prettierOptions = getPrettierOptions(filePath, inputSource, incomingPrettierOptions);
return prettier.format(printed, incomingPrettierOptions);
}
/**
* Get Prettier options based on style of a JavaScript
* @param filePath Path to source file
* @param source Body of a JavaScript
* @param options Existing prettier option
*/
export function getPrettierOptions(filePath: string, source: string, options: prettier.Options): prettier.Options {
const resolvedOptions = prettier.resolveConfig.sync(filePath);
if (resolvedOptions) {
_.defaults(resolvedOptions, options);
return resolvedOptions;
}
const { amount: indentAmount, type: indentType } = detectIndent(source);
const sourceWidth = getCodeWidth(source, 80);
const semi = getUseOfSemi(source);
const quotations = getQuotation(source);
_.defaults(options, {
tabWidth: indentAmount,
useTabs: indentType && indentType === 'tab',
printWidth: sourceWidth,
semi,
singleQuote: quotations === 'single',
});
return options;
}
/**
* Given body of a source file, return its code width
* @param source
*/
function getCodeWidth(source: string, defaultWidth: number): number {
return source.split(os.EOL).reduce((result, line) => Math.max(result, line.length), defaultWidth);
}
/**
* Detect if a source file is using semicolon
* @todo: use an actual parser. This is not a proper implementation
* @param source
* @return true if code is using semicolons
*/
function getUseOfSemi(source: string): boolean {
return source.indexOf(';') !== -1;
}
/**
* Detect if a source file is using single quotes or double quotes
* @todo use an actual parser. This is not a proper implementation
* @param source
*/
function getQuotation(source: string): 'single' | 'double' {
const numberOfSingleQuotes = (source.match(/\'/g) || []).length;
const numberOfDoubleQuotes = (source.match(/\"/g) || []).length;
if (numberOfSingleQuotes > numberOfDoubleQuotes) {
return 'single';
}
return 'double';
}