Skip to content

Commit 3b91d88

Browse files
akxmohsen1
authored andcommitted
Safety/QOL fixes (lyft#39)
* Build prettier options only once * Use a temporary file for the transformation * Add --ignore-prettier-errors option * Add --keep-*-files options for easier debugging * Exit with error code 1 when there were compilation errors * Fix multiple file/glob parameters Refs lyft#31 Refs lyft#37 * Add progress message
1 parent 9bd8777 commit 3b91d88

File tree

3 files changed

+93
-28
lines changed

3 files changed

+93
-28
lines changed

Diff for: src/cli.ts

+64-23
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,28 @@ import * as path from 'path';
77
import * as prettier from 'prettier';
88

99
import { run } from '.';
10+
import { CompilationOptions } from './compiler';
11+
12+
function resolveGlobs(globPatterns: string[]): string[] {
13+
const files: string[] = [];
14+
function addFile(file: string) {
15+
file = path.resolve(file);
16+
if (files.indexOf(file) === -1) {
17+
files.push(file);
18+
}
19+
}
20+
globPatterns.forEach(pattern => {
21+
if (/[{}*?+\[\]]/.test(pattern)) {
22+
// Smells like globs
23+
glob.sync(pattern, {}).forEach(file => {
24+
addFile(file);
25+
});
26+
} else {
27+
addFile(pattern);
28+
}
29+
});
30+
return files;
31+
}
1032

1133
program
1234
.version('1.0.0')
@@ -20,38 +42,57 @@ program
2042
.option('--tab-width <int>', 'Number of spaces per indentation level.', 2)
2143
.option('--trailing-comma <none|es5|all>', 'Print trailing commas wherever possible when multi-line.', 'none')
2244
.option('--use-tabs', 'Indent with tabs instead of spaces.', false)
45+
.option('--ignore-prettier-errors', 'Ignore (but warn about) errors in Prettier', false)
46+
.option('--keep-original-files', 'Keep original files', false)
47+
.option('--keep-temporary-files', 'Keep temporary files', false)
2348
.usage('[options] <filename or glob>')
24-
.command('* <glob>')
25-
.action(globPattern => {
26-
if (!globPattern) {
27-
throw new Error('You must provide a file name or glob pattern to transform');
49+
.command('* [glob/filename...]')
50+
.action((globPatterns: string[]) => {
51+
const prettierOptions: prettier.Options = {
52+
arrowParens: program.arrowParens,
53+
bracketSpacing: !program.noBracketSpacing,
54+
jsxBracketSameLine: !!program.jsxBracketSameLine,
55+
printWidth: parseInt(program.printWidth, 10),
56+
proseWrap: program.proseWrap,
57+
semi: !program.noSemi,
58+
singleQuote: !!program.singleQuote,
59+
tabWidth: parseInt(program.tabWidth, 10),
60+
trailingComma: program.trailingComma,
61+
useTabs: !!program.useTabs,
62+
};
63+
const compilationOptions: CompilationOptions = {
64+
ignorePrettierErrors: !!program.ignorePrettierErrors,
65+
};
66+
const files = resolveGlobs(globPatterns);
67+
if (!files.length) {
68+
throw new Error('Nothing to do. You must provide file names or glob patterns to transform.');
2869
}
29-
const files = glob.sync(globPattern, {});
30-
for (const file of files) {
31-
const filePath = path.resolve(file);
70+
let errors = false;
71+
for (const filePath of files) {
72+
console.log(`Transforming ${filePath}...`);
3273
const newPath = filePath.replace(/\.jsx?$/, '.tsx');
33-
74+
const temporaryPath = filePath.replace(/\.jsx?$/, `_js2ts_${+new Date()}.tsx`);
3475
try {
35-
fs.renameSync(filePath, newPath);
36-
const prettierOptions: prettier.Options = {
37-
arrowParens: program.arrowParens,
38-
bracketSpacing: !program.noBracketSpacing,
39-
jsxBracketSameLine: !!program.jsxBracketSameLine,
40-
printWidth: parseInt(program.printWidth, 10),
41-
proseWrap: program.proseWrap,
42-
semi: !program.noSemi,
43-
singleQuote: !!program.singleQuote,
44-
tabWidth: parseInt(program.tabWidth, 10),
45-
trailingComma: program.trailingComma,
46-
useTabs: !!program.useTabs,
47-
};
48-
const result = run(newPath, prettierOptions);
76+
fs.copyFileSync(filePath, temporaryPath);
77+
const result = run(temporaryPath, prettierOptions, compilationOptions);
4978
fs.writeFileSync(newPath, result);
79+
if (!program.keepOriginalFiles) {
80+
fs.unlinkSync(filePath);
81+
}
5082
} catch (error) {
51-
console.warn(`Failed to convert ${file}`);
83+
console.warn(`Failed to convert ${filePath}`);
5284
console.warn(error);
85+
errors = true;
86+
}
87+
if (!program.keepTemporaryFiles) {
88+
if (fs.existsSync(temporaryPath)) {
89+
fs.unlinkSync(temporaryPath);
90+
}
5391
}
5492
}
93+
if (errors) {
94+
process.exit(1);
95+
}
5596
});
5697

5798
program.parse(process.argv);

Diff for: src/compiler.ts

+22-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ import * as detectIndent from 'detect-indent';
88

99
import { TransformFactoryFactory } from '.';
1010

11+
export interface CompilationOptions {
12+
ignorePrettierErrors: boolean;
13+
}
14+
15+
const DEFAULT_COMPILATION_OPTIONS: CompilationOptions = {
16+
ignorePrettierErrors: false,
17+
};
18+
19+
export { DEFAULT_COMPILATION_OPTIONS };
20+
1121
/**
1222
* Compile and return result TypeScript
1323
* @param filePath Path to file to compile
@@ -16,6 +26,7 @@ export function compile(
1626
filePath: string,
1727
factoryFactories: TransformFactoryFactory[],
1828
incomingPrettierOptions: prettier.Options = {},
29+
compilationOptions: CompilationOptions = DEFAULT_COMPILATION_OPTIONS,
1930
) {
2031
const compilerOptions: ts.CompilerOptions = {
2132
target: ts.ScriptTarget.ES2017,
@@ -56,7 +67,16 @@ export function compile(
5667
const inputSource = fs.readFileSync(filePath, 'utf-8');
5768
const prettierOptions = getPrettierOptions(filePath, inputSource, incomingPrettierOptions);
5869

59-
return prettier.format(printed, incomingPrettierOptions);
70+
try {
71+
return prettier.format(printed, prettierOptions);
72+
} catch (prettierError) {
73+
if (compilationOptions.ignorePrettierErrors) {
74+
console.warn(`Prettier failed for ${filePath} (ignorePrettierErrors is on):`);
75+
console.warn(prettierError);
76+
return printed;
77+
}
78+
throw prettierError;
79+
}
6080
}
6181

6282
/**
@@ -76,7 +96,7 @@ export function getPrettierOptions(filePath: string, source: string, options: pr
7696
const semi = getUseOfSemi(source);
7797
const quotations = getQuotation(source);
7898

79-
_.defaults(options, {
99+
_.defaults(Object.assign({}, options), {
80100
tabWidth: indentAmount,
81101
useTabs: indentType && indentType === 'tab',
82102
printWidth: sourceWidth,

Diff for: src/index.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import * as ts from 'typescript';
22
import * as prettier from 'prettier';
33

4-
import { compile } from './compiler';
4+
import { compile, CompilationOptions, DEFAULT_COMPILATION_OPTIONS } from './compiler';
55
import { reactJSMakePropsAndStateInterfaceTransformFactoryFactory } from './transforms/react-js-make-props-and-state-transform';
66
import { reactRemovePropTypesAssignmentTransformFactoryFactory } from './transforms/react-remove-prop-types-assignment-transform';
77
import { reactMovePropTypesToClassTransformFactoryFactory } from './transforms/react-move-prop-types-to-class-transform';
@@ -37,6 +37,10 @@ export type TransformFactoryFactory = (typeChecker: ts.TypeChecker) => ts.Transf
3737
* Run React JavaScript to TypeScript transform for file at `filePath`
3838
* @param filePath
3939
*/
40-
export function run(filePath: string, prettierOptions: prettier.Options = {}): string {
41-
return compile(filePath, allTransforms, prettierOptions);
40+
export function run(
41+
filePath: string,
42+
prettierOptions: prettier.Options = {},
43+
compilationOptions: CompilationOptions = DEFAULT_COMPILATION_OPTIONS,
44+
): string {
45+
return compile(filePath, allTransforms, prettierOptions, compilationOptions);
4246
}

0 commit comments

Comments
 (0)