diff --git a/README.md b/README.md index 3098481..39d1f07 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ $ tsfmt ## Usage +### Format or verify specific TypeScript files + ```bash $ cat sample.ts class Sample {hello(word="world"){return "Hello, "+word;}} @@ -61,6 +63,17 @@ $ echo $? 1 ``` +### Reformat all files in a TypeScript project + +If no files are specified on the command line but +a TypeScript project file (tsconfig.json) exists, +the list of files will be read from the project file. + +```bash +# reads list of files to format from tsconfig.json +tsfmt -r +``` + ## Note now `indentSize` parameter is ignored. it is TypeScript compiler matters. diff --git a/lib/cli.ts b/lib/cli.ts index 78b3bbb..99492f8 100644 --- a/lib/cli.ts +++ b/lib/cli.ts @@ -5,6 +5,7 @@ require("es6-promise").polyfill(); import fs = require("fs"); import commandpost = require("commandpost"); +import path = require("path"); import lib = require("./index"); @@ -42,7 +43,12 @@ var root = commandpost var editorconfig = !!opts.editorconfig; var tsfmt = !!opts.tsfmt; - if (args.files.length === 0 && !opts.stdin) { + var files = args.files; + if (files.length === 0 && fs.existsSync("tsconfig.json")) { + files = readFilesFromTsconfig("tsconfig.json"); + } + + if (files.length === 0 && !opts.stdin) { process.stdout.write(root.helpText() + '\n'); return; } @@ -62,7 +68,7 @@ var root = commandpost return; } lib - .processStream(args.files[0] || "temp.ts", process.stdin, { + .processStream(files[0] || "temp.ts", process.stdin, { replace: replace, verify: verify, tslint: tslint, @@ -78,7 +84,7 @@ var root = commandpost .catch(errorHandler); } else { lib - .processFiles(args.files, { + .processFiles(files, { replace: replace, verify: verify, tslint: tslint, @@ -129,3 +135,16 @@ function errorHandler(err: any): Promise { return null; }); } + +function readFilesFromTsconfig(configPath: string) { + "use strict"; + + var tsconfigDir = path.dirname(configPath); + var tsconfig = JSON.parse(fs.readFileSync(configPath, "utf-8")); + if (tsconfig.files) { + var files: string[] = tsconfig.files; + return files.map(filePath => path.resolve(tsconfigDir, filePath)); + } else { + throw new Error(`No "files" section present in tsconfig.json`); + } +} diff --git a/test/cli/main.ts b/test/cli/main.ts new file mode 100644 index 0000000..b40cd90 --- /dev/null +++ b/test/cli/main.ts @@ -0,0 +1,5 @@ +class TestCLI { + method () { + + } +} diff --git a/test/cli/tsconfig.json b/test/cli/tsconfig.json new file mode 100644 index 0000000..ba46524 --- /dev/null +++ b/test/cli/tsconfig.json @@ -0,0 +1,14 @@ +{ + "version": "1.5.3", + "compilerOptions": { + "target": "es5", + "module": "commonjs", + "declaration": false, + "noImplicitAny": false, + "removeComments": true, + "noLib": false + }, + "files": [ + "./main.ts" + ] +} diff --git a/test/indexSpec.ts b/test/indexSpec.ts index cf33ddc..37518df 100644 --- a/test/indexSpec.ts +++ b/test/indexSpec.ts @@ -12,166 +12,207 @@ import stream = require("stream"); import lib = require("../lib/index"); -function collectFileName(dirName:string):string[] { - var fileName:string[] = []; - fs - .readdirSync(dirName) - .forEach((name:string)=> { - var newName = dirName + "/" + name; - var stats = fs.statSync(newName); - if (stats.isDirectory()) { - fileName = fileName.concat(collectFileName(newName)); - } else if (stats.isFile()) { - fileName.push(newName); - } - }); - return fileName; +function collectFileName(dirName: string): string[] { + var fileName: string[] = []; + fs + .readdirSync(dirName) + .forEach((name: string) => { + var newName = dirName + "/" + name; + var stats = fs.statSync(newName); + if (stats.isDirectory()) { + fileName = fileName.concat(collectFileName(newName)); + } else if (stats.isFile()) { + fileName.push(newName); + } + }); + return fileName; } -function checkByTslint(configFileName:string, tsfileName:string, errorExpected:boolean):Promise { - var process = childProcess.spawn("./node_modules/.bin/tslint", ["-c", configFileName, tsfileName]); - - var stdout = ''; - process.stdout.on('data', (data:any) => { - stdout += data.toString(); - }); - - var stderr = ''; - process.stderr.on('data', (data:any) => { - stderr += data.toString(); - }); - - return new Promise((resolve, reject)=> { - process.on("exit", (code:number) => { - var success = !code; // 0 - exit with success - if (!errorExpected) { - // expected error - if (success) { - resolve(true); - } else { - reject(tsfileName + " must be a good code.\n" + stdout); - } - } else { - // expected success - if (success) { - reject(tsfileName + " must be a bad code."); - } else { - resolve(true); - } - } - }); - }); +interface ExecResult { + status: number; + stdout: string; + stderr: string; +} + +function exec(cmd: string, args: string[], options: Object): Promise { + var process = childProcess.spawn(cmd, args, options); + + var stdout = ''; + var stderr = ''; + + process.stdout.on('data', (data: Buffer) => stdout += data.toString()); + process.stderr.on('data', (data: Buffer) => stderr += data.toString()); + + return new Promise((resolve, reject) => { + process.on('exit', (status: number) => { + resolve({ + status: status, + stdout: stdout, + stderr: stderr + }); + }); + }); +} + +function checkByTslint(configFileName: string, tsfileName: string, errorExpected: boolean): Promise { + var process = childProcess.spawn("./node_modules/.bin/tslint", ["-c", configFileName, tsfileName]); + + var stdout = ''; + process.stdout.on('data', (data: any) => { + stdout += data.toString(); + }); + + var stderr = ''; + process.stderr.on('data', (data: any) => { + stderr += data.toString(); + }); + + return new Promise((resolve, reject) => { + process.on("exit", (code: number) => { + var success = !code; // 0 - exit with success + if (!errorExpected) { + // expected error + if (success) { + resolve(true); + } else { + reject(tsfileName + " must be a good code.\n" + stdout); + } + } else { + // expected success + if (success) { + reject(tsfileName + " must be a bad code."); + } else { + resolve(true); + } + } + }); + }); } describe("tsfmt test", () => { - var fixtureDir = "./test/fixture"; - var expectedDir = "./test/expected"; - - describe("processFiles function", () => { - var fileNames = collectFileName(fixtureDir); - fileNames - .filter(fileName=> /\.ts$/.test(fileName)) - .forEach(fileName=> { - var ignoreList = [ - "./test/fixture/editorconfig/space/main.ts", // TypeScript ignore indentSize: 8 - "./test/fixture/tsfmt/a/main.ts", // TypeScript ignore indentSize: 1 - "./test/fixture/tslint/indent/main.ts" // TypeScript ignore indentSize: 6 - ]; - if (ignoreList.indexOf(fileName) !== -1) { - it.skip(fileName, ()=> { - false; - }); - return; - } - it(fileName, () => { - return lib - .processFiles([fileName], { - dryRun: true, - replace: false, - verify: false, - tslint: true, - editorconfig: true, - tsfmt: true - }) - .then(resultMap => { - var result = resultMap[fileName]; - assert(result !== null); - assert(result.error === false); - - var expectedTsFileName = fileName.replace(fixtureDir, expectedDir); - // console.log(fileName, expectedFileName); - - if (!fs.existsSync(expectedTsFileName)) { - fs.writeFileSync(expectedTsFileName, result.dest); - } - - var expected = fs.readFileSync(expectedTsFileName, "utf-8"); - assert(expected === result.dest); - - var expectedOptionsFileName = expectedTsFileName.replace(/\.ts$/, ".json"); - - if (!fs.existsSync(expectedOptionsFileName)) { - fs.writeFileSync(expectedOptionsFileName, JSON.stringify(result.options, null, 2)); - } - - var expectedOptions = JSON.parse(fs.readFileSync(expectedOptionsFileName, "utf-8")); - assert.deepEqual(expectedOptions, result.options); - - var tslintConfigName = path.dirname(fileName) + "/tslint.json"; - if (!fs.existsSync(tslintConfigName)) { - return; - } - if (fileName === "./test/fixture/tslint/indent/main.ts") { - // NOTE indent enforces consistent indentation levels (currently disabled). - return; - } - - return Promise.all([ - checkByTslint(tslintConfigName, fileName, true), - checkByTslint(tslintConfigName, expectedTsFileName, false) - ]); - }); - }); - }); - - it("verify unformatted file", () => { - var fileName = "./test/fixture/tsfmt/a/main.ts"; - return lib - .processFiles([fileName], { - dryRun: true, - replace: false, - verify: true, - tslint: true, - editorconfig: true, - tsfmt: true - }) - .then(resultMap => { - assert(resultMap[fileName].error); - assert(resultMap[fileName].message === "./test/fixture/tsfmt/a/main.ts is not formatted"); - }); - }); - }); - - describe("processStream function", () => { - var fileName = "test/fixture/default/main.ts"; - it(fileName, () => { - var input = new stream.Readable(); - input.push(`class Sample{getString():string{return "hi!";}}`); - input.push(null); - return lib - .processStream(fileName, input, { - dryRun: true, - replace: false, - verify: false, - tslint: true, - editorconfig: true, - tsfmt: true - }) - .then(result=> { - assert(result !== null); - assert(result.error === false); - assert(result.dest === "class Sample { getString(): string { return \"hi!\"; } }\n"); - }); - }); - }); + var fixtureDir = "./test/fixture"; + var expectedDir = "./test/expected"; + + describe("processFiles function", () => { + var fileNames = collectFileName(fixtureDir); + fileNames + .filter(fileName=> /\.ts$/.test(fileName)) + .forEach(fileName=> { + var ignoreList = [ + "./test/fixture/editorconfig/space/main.ts", // TypeScript ignore indentSize: 8 + "./test/fixture/tsfmt/a/main.ts", // TypeScript ignore indentSize: 1 + "./test/fixture/tslint/indent/main.ts" // TypeScript ignore indentSize: 6 + ]; + if (ignoreList.indexOf(fileName) !== -1) { + it.skip(fileName, () => { + false; + }); + return; + } + it(fileName, () => { + return lib + .processFiles([fileName], { + dryRun: true, + replace: false, + verify: false, + tslint: true, + editorconfig: true, + tsfmt: true + }) + .then(resultMap => { + var result = resultMap[fileName]; + assert(result !== null); + assert(result.error === false); + + var expectedTsFileName = fileName.replace(fixtureDir, expectedDir); + // console.log(fileName, expectedFileName); + + if (!fs.existsSync(expectedTsFileName)) { + fs.writeFileSync(expectedTsFileName, result.dest); + } + + var expected = fs.readFileSync(expectedTsFileName, "utf-8"); + assert(expected === result.dest); + + var expectedOptionsFileName = expectedTsFileName.replace(/\.ts$/, ".json"); + + if (!fs.existsSync(expectedOptionsFileName)) { + fs.writeFileSync(expectedOptionsFileName, JSON.stringify(result.options, null, 2)); + } + + var expectedOptions = JSON.parse(fs.readFileSync(expectedOptionsFileName, "utf-8")); + assert.deepEqual(expectedOptions, result.options); + + var tslintConfigName = path.dirname(fileName) + "/tslint.json"; + if (!fs.existsSync(tslintConfigName)) { + return; + } + if (fileName === "./test/fixture/tslint/indent/main.ts") { + // NOTE indent enforces consistent indentation levels (currently disabled). + return; + } + + return Promise.all([ + checkByTslint(tslintConfigName, fileName, true), + checkByTslint(tslintConfigName, expectedTsFileName, false) + ]); + }); + }); + }); + + it("verify unformatted file", () => { + var fileName = "./test/fixture/tsfmt/a/main.ts"; + return lib + .processFiles([fileName], { + dryRun: true, + replace: false, + verify: true, + tslint: true, + editorconfig: true, + tsfmt: true + }) + .then(resultMap => { + assert(resultMap[fileName].error); + assert(resultMap[fileName].message === "./test/fixture/tsfmt/a/main.ts is not formatted"); + }); + }); + }); + + describe("processStream function", () => { + var fileName = "test/fixture/default/main.ts"; + it(fileName, () => { + var input = new stream.Readable(); + input.push(`class Sample{getString():string{return "hi!";}}`); + input.push(null); + return lib + .processStream(fileName, input, { + dryRun: true, + replace: false, + verify: false, + tslint: true, + editorconfig: true, + tsfmt: true + }) + .then(result=> { + assert(result !== null); + assert(result.error === false); + assert(result.dest === "class Sample { getString(): string { return \"hi!\"; } }\n"); + }); + }); + }); + + describe("CLI test", () => { + it("should reformat files specified in tsconfig.json", () => { + return exec(path.resolve("./bin/tsfmt"), [], { cwd: path.resolve('./test/cli') }).then(result => { + assert.equal(result.status, 0); + assert.equal(result.stdout.trim(), ` +class TestCLI { + method() { + + } +} +`.trim()); + }); + }); + }); }); diff --git a/tsconfig.json b/tsconfig.json index f8ed8f2..dea6b30 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -34,6 +34,7 @@ "./lib/provider/tslintjson.ts", "./lib/utils.d.ts", "./lib/utils.ts", + "./test/cli/main.ts", "./test/indexSpec.d.ts", "./test/indexSpec.ts", "./typings/bundle.d.ts", @@ -43,6 +44,7 @@ "./typings/node/node.d.ts", "./typings/power-assert-formatter/power-assert-formatter.d.ts", "./typings/power-assert/power-assert.d.ts", + "./typings/typescript/typescript.d.ts", "./node_modules/typescript/bin/typescript.d.ts", "./node_modules/typescript/bin/lib.es6.d.ts" ]