From eeebefef9277e4aa2d9320a025ddf3bafc9fba1b Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 19 May 2018 15:05:00 +0300 Subject: [PATCH 1/7] Build prettier options only once --- src/cli.ts | 24 ++++++++++++------------ src/compiler.ts | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index ed73b9b..38932b4 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -26,6 +26,18 @@ program if (!globPattern) { throw new Error('You must provide a file name or glob pattern to transform'); } + const prettierOptions: prettier.Options = { + arrowParens: program.arrowParens, + bracketSpacing: !program.noBracketSpacing, + jsxBracketSameLine: !!program.jsxBracketSameLine, + printWidth: parseInt(program.printWidth, 10), + proseWrap: program.proseWrap, + semi: !program.noSemi, + singleQuote: !!program.singleQuote, + tabWidth: parseInt(program.tabWidth, 10), + trailingComma: program.trailingComma, + useTabs: !!program.useTabs, + }; const files = glob.sync(globPattern, {}); for (const file of files) { const filePath = path.resolve(file); @@ -33,18 +45,6 @@ program try { fs.renameSync(filePath, newPath); - const prettierOptions: prettier.Options = { - arrowParens: program.arrowParens, - bracketSpacing: !program.noBracketSpacing, - jsxBracketSameLine: !!program.jsxBracketSameLine, - printWidth: parseInt(program.printWidth, 10), - proseWrap: program.proseWrap, - semi: !program.noSemi, - singleQuote: !!program.singleQuote, - tabWidth: parseInt(program.tabWidth, 10), - trailingComma: program.trailingComma, - useTabs: !!program.useTabs, - }; const result = run(newPath, prettierOptions); fs.writeFileSync(newPath, result); } catch (error) { diff --git a/src/compiler.ts b/src/compiler.ts index a13ebac..e684c9f 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -15,7 +15,7 @@ import { TransformFactoryFactory } from '.'; export function compile( filePath: string, factoryFactories: TransformFactoryFactory[], - incomingPrettierOptions: prettier.Options = {}, + incomingPrettierOptions: prettier.Options = {} ) { const compilerOptions: ts.CompilerOptions = { target: ts.ScriptTarget.ES2017, @@ -56,7 +56,7 @@ export function compile( const inputSource = fs.readFileSync(filePath, 'utf-8'); const prettierOptions = getPrettierOptions(filePath, inputSource, incomingPrettierOptions); - return prettier.format(printed, incomingPrettierOptions); + return prettier.format(printed, prettierOptions); } /** @@ -76,7 +76,7 @@ export function getPrettierOptions(filePath: string, source: string, options: pr const semi = getUseOfSemi(source); const quotations = getQuotation(source); - _.defaults(options, { + _.defaults(Object.assign({}, options), { tabWidth: indentAmount, useTabs: indentType && indentType === 'tab', printWidth: sourceWidth, From d82070d90f1d2c38a87071d8689ba965c562f3df Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 19 May 2018 15:06:44 +0300 Subject: [PATCH 2/7] Use a temporary file for the transformation --- src/cli.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 38932b4..b172d8d 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -42,11 +42,13 @@ program for (const file of files) { const filePath = path.resolve(file); const newPath = filePath.replace(/\.jsx?$/, '.tsx'); - + const temporaryPath = filePath.replace(/\.jsx?$/, `_js2ts_${+new Date()}.tsx`); try { - fs.renameSync(filePath, newPath); - const result = run(newPath, prettierOptions); + fs.copyFileSync(filePath, temporaryPath); + const result = run(temporaryPath, prettierOptions); fs.writeFileSync(newPath, result); + fs.unlinkSync(filePath); + fs.unlinkSync(temporaryPath); } catch (error) { console.warn(`Failed to convert ${file}`); console.warn(error); From 680d184dd4053f8e1574ea6a27d6ee5ec5d3e9f6 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 19 May 2018 15:24:17 +0300 Subject: [PATCH 3/7] Add --ignore-prettier-errors option --- src/cli.ts | 7 ++++++- src/compiler.ts | 24 ++++++++++++++++++++++-- src/index.ts | 10 +++++++--- 3 files changed, 35 insertions(+), 6 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index b172d8d..cc1dabd 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -7,6 +7,7 @@ import * as path from 'path'; import * as prettier from 'prettier'; import { run } from '.'; +import { CompilationOptions } from './compiler'; program .version('1.0.0') @@ -20,6 +21,7 @@ program .option('--tab-width ', 'Number of spaces per indentation level.', 2) .option('--trailing-comma ', 'Print trailing commas wherever possible when multi-line.', 'none') .option('--use-tabs', 'Indent with tabs instead of spaces.', false) + .option('--ignore-prettier-errors', 'Ignore (but warn about) errors in Prettier', false) .usage('[options] ') .command('* ') .action(globPattern => { @@ -38,6 +40,9 @@ program trailingComma: program.trailingComma, useTabs: !!program.useTabs, }; + const compilationOptions: CompilationOptions = { + ignorePrettierErrors: !!program.ignorePrettierErrors, + }; const files = glob.sync(globPattern, {}); for (const file of files) { const filePath = path.resolve(file); @@ -45,7 +50,7 @@ program const temporaryPath = filePath.replace(/\.jsx?$/, `_js2ts_${+new Date()}.tsx`); try { fs.copyFileSync(filePath, temporaryPath); - const result = run(temporaryPath, prettierOptions); + const result = run(temporaryPath, prettierOptions, compilationOptions); fs.writeFileSync(newPath, result); fs.unlinkSync(filePath); fs.unlinkSync(temporaryPath); diff --git a/src/compiler.ts b/src/compiler.ts index e684c9f..9afc378 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -8,6 +8,16 @@ import * as detectIndent from 'detect-indent'; import { TransformFactoryFactory } from '.'; +export interface CompilationOptions { + ignorePrettierErrors: boolean; +} + +const DEFAULT_COMPILATION_OPTIONS: CompilationOptions = { + ignorePrettierErrors: false, +}; + +export { DEFAULT_COMPILATION_OPTIONS }; + /** * Compile and return result TypeScript * @param filePath Path to file to compile @@ -15,7 +25,8 @@ import { TransformFactoryFactory } from '.'; export function compile( filePath: string, factoryFactories: TransformFactoryFactory[], - incomingPrettierOptions: prettier.Options = {} + incomingPrettierOptions: prettier.Options = {}, + compilationOptions: CompilationOptions = DEFAULT_COMPILATION_OPTIONS, ) { const compilerOptions: ts.CompilerOptions = { target: ts.ScriptTarget.ES2017, @@ -56,7 +67,16 @@ export function compile( const inputSource = fs.readFileSync(filePath, 'utf-8'); const prettierOptions = getPrettierOptions(filePath, inputSource, incomingPrettierOptions); - return prettier.format(printed, prettierOptions); + try { + return prettier.format(printed, prettierOptions); + } catch (prettierError) { + if (compilationOptions.ignorePrettierErrors) { + console.warn(`Prettier failed for ${filePath} (ignorePrettierErrors is on):`); + console.warn(prettierError); + return printed; + } + throw prettierError; + } } /** diff --git a/src/index.ts b/src/index.ts index 831dbd9..5c26c27 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import * as ts from 'typescript'; import * as prettier from 'prettier'; -import { compile } from './compiler'; +import { compile, CompilationOptions, DEFAULT_COMPILATION_OPTIONS } from './compiler'; import { reactJSMakePropsAndStateInterfaceTransformFactoryFactory } from './transforms/react-js-make-props-and-state-transform'; import { reactRemovePropTypesAssignmentTransformFactoryFactory } from './transforms/react-remove-prop-types-assignment-transform'; import { reactMovePropTypesToClassTransformFactoryFactory } from './transforms/react-move-prop-types-to-class-transform'; @@ -37,6 +37,10 @@ export type TransformFactoryFactory = (typeChecker: ts.TypeChecker) => ts.Transf * Run React JavaScript to TypeScript transform for file at `filePath` * @param filePath */ -export function run(filePath: string, prettierOptions: prettier.Options = {}): string { - return compile(filePath, allTransforms, prettierOptions); +export function run( + filePath: string, + prettierOptions: prettier.Options = {}, + compilationOptions: CompilationOptions = DEFAULT_COMPILATION_OPTIONS, +): string { + return compile(filePath, allTransforms, prettierOptions, compilationOptions); } From 9d6d5d469fe8c3d77f6ea892c8a471685ce6601e Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 19 May 2018 15:27:34 +0300 Subject: [PATCH 4/7] Add --keep-*-files options for easier debugging --- src/cli.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index cc1dabd..9fd159e 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -22,6 +22,8 @@ program .option('--trailing-comma ', 'Print trailing commas wherever possible when multi-line.', 'none') .option('--use-tabs', 'Indent with tabs instead of spaces.', false) .option('--ignore-prettier-errors', 'Ignore (but warn about) errors in Prettier', false) + .option('--keep-original-files', 'Keep original files', false) + .option('--keep-temporary-files', 'Keep temporary files', false) .usage('[options] ') .command('* ') .action(globPattern => { @@ -52,12 +54,18 @@ program fs.copyFileSync(filePath, temporaryPath); const result = run(temporaryPath, prettierOptions, compilationOptions); fs.writeFileSync(newPath, result); - fs.unlinkSync(filePath); - fs.unlinkSync(temporaryPath); + if (!program.keepOriginalFiles) { + fs.unlinkSync(filePath); + } } catch (error) { console.warn(`Failed to convert ${file}`); console.warn(error); } + if (!program.keepTemporaryFiles) { + if (fs.existsSync(temporaryPath)) { + fs.unlinkSync(temporaryPath); + } + } } }); From 85a4bf07a5f5234e15f5f1ec49abea20abdd34a1 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 19 May 2018 15:29:25 +0300 Subject: [PATCH 5/7] Exit with error code 1 when there were compilation errors --- src/cli.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/cli.ts b/src/cli.ts index 9fd159e..252b8e0 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -46,6 +46,7 @@ program ignorePrettierErrors: !!program.ignorePrettierErrors, }; const files = glob.sync(globPattern, {}); + let errors = false; for (const file of files) { const filePath = path.resolve(file); const newPath = filePath.replace(/\.jsx?$/, '.tsx'); @@ -60,6 +61,7 @@ program } catch (error) { console.warn(`Failed to convert ${file}`); console.warn(error); + errors = true; } if (!program.keepTemporaryFiles) { if (fs.existsSync(temporaryPath)) { @@ -67,6 +69,9 @@ program } } } + if (errors) { + process.exit(1); + } }); program.parse(process.argv); From 62e3e42275dc4522ca505c73eaf6facaf06e18ac Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 19 May 2018 16:13:52 +0300 Subject: [PATCH 6/7] Fix multiple file/glob parameters Refs #31 Refs #37 --- src/cli.ts | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/src/cli.ts b/src/cli.ts index 252b8e0..9d82bfa 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -9,6 +9,27 @@ import * as prettier from 'prettier'; import { run } from '.'; import { CompilationOptions } from './compiler'; +function resolveGlobs(globPatterns: string[]): string[] { + const files: string[] = []; + function addFile(file: string) { + file = path.resolve(file); + if (files.indexOf(file) === -1) { + files.push(file); + } + } + globPatterns.forEach(pattern => { + if (/[{}*?+\[\]]/.test(pattern)) { + // Smells like globs + glob.sync(pattern, {}).forEach(file => { + addFile(file); + }); + } else { + addFile(pattern); + } + }); + return files; +} + program .version('1.0.0') .option('--arrow-parens ', 'Include parentheses around a sole arrow function parameter.', 'avoid') @@ -25,11 +46,8 @@ program .option('--keep-original-files', 'Keep original files', false) .option('--keep-temporary-files', 'Keep temporary files', false) .usage('[options] ') - .command('* ') - .action(globPattern => { - if (!globPattern) { - throw new Error('You must provide a file name or glob pattern to transform'); - } + .command('* [glob/filename...]') + .action((globPatterns: string[]) => { const prettierOptions: prettier.Options = { arrowParens: program.arrowParens, bracketSpacing: !program.noBracketSpacing, @@ -45,10 +63,12 @@ program const compilationOptions: CompilationOptions = { ignorePrettierErrors: !!program.ignorePrettierErrors, }; - const files = glob.sync(globPattern, {}); + const files = resolveGlobs(globPatterns); + if (!files.length) { + throw new Error('Nothing to do. You must provide file names or glob patterns to transform.'); + } let errors = false; - for (const file of files) { - const filePath = path.resolve(file); + for (const filePath of files) { const newPath = filePath.replace(/\.jsx?$/, '.tsx'); const temporaryPath = filePath.replace(/\.jsx?$/, `_js2ts_${+new Date()}.tsx`); try { @@ -59,7 +79,7 @@ program fs.unlinkSync(filePath); } } catch (error) { - console.warn(`Failed to convert ${file}`); + console.warn(`Failed to convert ${filePath}`); console.warn(error); errors = true; } From ccd3d3913109cbde05c7aaae1a7b91ab86084215 Mon Sep 17 00:00:00 2001 From: Aarni Koskela Date: Sat, 19 May 2018 16:14:05 +0300 Subject: [PATCH 7/7] Add progress message --- src/cli.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/cli.ts b/src/cli.ts index 9d82bfa..779f0a9 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -69,6 +69,7 @@ program } let errors = false; for (const filePath of files) { + console.log(`Transforming ${filePath}...`); const newPath = filePath.replace(/\.jsx?$/, '.tsx'); const temporaryPath = filePath.replace(/\.jsx?$/, `_js2ts_${+new Date()}.tsx`); try {