diff --git a/benchmark/benchmark-project.ts b/benchmark/benchmark-project.ts new file mode 100644 index 00000000..be2d6a58 --- /dev/null +++ b/benchmark/benchmark-project.ts @@ -0,0 +1,66 @@ +#!/usr/bin/env node +/* eslint-disable no-console */ +import parseArgs from "yargs-parser"; + +// @ts-ignore: May not be built, just ignore for now. +import * as sucrase from "../dist/index"; // eslint-disable-line import/no-unresolved +import {FileInfo, loadProjectFiles} from "./loadProjectFiles"; + +async function main(): Promise { + const args = parseArgs(process.argv.slice(2), {boolean: ["profile"]}); + const projectPath = args._[0]; + const shouldProfile = args.profile; + const numTimes = args.times || 1; + + const projectFiles = await loadProjectFiles(projectPath); + if (numTimes === 1) { + console.log(`Running Sucrase on ${projectPath}`); + } else { + console.log(`Running Sucrase ${numTimes} times on ${projectPath}`); + } + const totalLines = projectFiles + .map(({code}) => code.split("\n").length) + .reduce((a, b) => a + b, 0); + console.log(`Found ${projectFiles.length} files with ${totalLines} lines`); + + if (shouldProfile) { + console.log(`Make sure you have Chrome DevTools for Node open.`); + // tslint:disable-next-line no-any + (console as any).profile(`Sucrase ${projectPath}`); + for (let i = 0; i < numTimes; i++) { + for (const fileInfo of projectFiles) { + runTransform(fileInfo); + } + } + // tslint:disable-next-line no-any + (console as any).profileEnd(`Sucrase ${projectPath}`); + } else { + const startTime = process.hrtime(); + for (let i = 0; i < numTimes; i++) { + for (const fileInfo of projectFiles) { + runTransform(fileInfo); + } + } + const totalTime = process.hrtime(startTime); + const timeSeconds = totalTime[0] + totalTime[1] / 1e9; + console.log(`Time taken: ${Math.round(timeSeconds * 1000) / 1000}s`); + console.log(`Speed: ${Math.round((totalLines * numTimes) / timeSeconds)} lines per second`); + } +} + +function runTransform({code, path}: FileInfo): void { + if (path.endsWith(".js") || path.endsWith(".jsx")) { + sucrase.transform(code, {transforms: ["jsx", "imports", "flow"], filePath: path}); + } else if (path.endsWith(".ts")) { + sucrase.transform(code, {transforms: ["imports", "typescript"], filePath: path}); + } else if (path.endsWith(".tsx")) { + sucrase.transform(code, {transforms: ["jsx", "imports", "typescript"], filePath: path}); + } else { + throw new Error(`Unrecognized file type: ${path}`); + } +} + +main().catch((e) => { + console.error(e); + process.exitCode = 1; +}); diff --git a/benchmark/benchmark-react.ts b/benchmark/benchmark-react.ts index f0363678..129f885f 100755 --- a/benchmark/benchmark-react.ts +++ b/benchmark/benchmark-react.ts @@ -5,13 +5,16 @@ import * as babel from "@babel/core"; // @ts-ignore: May not be built, just ignore for now. import * as sucrase from "../dist/index"; // eslint-disable-line import/no-unresolved -import {loadReactFiles} from "./loadReactFiles"; +import {loadProjectFiles} from "./loadProjectFiles"; async function main(): Promise { console.log(`Compiling React codebase:`); - const reactFiles = await loadReactFiles(); + const reactFiles = await loadProjectFiles("./example-runner/example-repos/react/packages"); console.time("Sucrase"); for (const {code, path} of reactFiles) { + if (path.endsWith(".ts")) { + continue; + } sucrase.transform(code, { transforms: ["jsx", "imports", "flow"], filePath: path, @@ -20,7 +23,10 @@ async function main(): Promise { console.timeEnd("Sucrase"); console.time("Babel"); - for (const {code} of reactFiles) { + for (const {code, path} of reactFiles) { + if (path.endsWith(".ts")) { + continue; + } babel.transform(code, { presets: ["@babel/preset-react", "@babel/preset-flow"], plugins: [ diff --git a/benchmark/loadReactFiles.ts b/benchmark/loadProjectFiles.ts similarity index 68% rename from benchmark/loadReactFiles.ts rename to benchmark/loadProjectFiles.ts index 8ebdfe52..343dd77e 100644 --- a/benchmark/loadReactFiles.ts +++ b/benchmark/loadProjectFiles.ts @@ -6,7 +6,7 @@ export interface FileInfo { code: string; } -export async function loadReactFiles(): Promise> { +export async function loadProjectFiles(projectPath: string): Promise> { const results: Array = []; async function visit(path: string): Promise { for (const child of await readdir(path)) { @@ -16,12 +16,17 @@ export async function loadReactFiles(): Promise> { const childPath = join(path, child); if ((await stat(childPath)).isDirectory()) { await visit(childPath); - } else if (childPath.endsWith(".js")) { + } else if ( + childPath.endsWith(".js") || + childPath.endsWith(".jsx") || + childPath.endsWith(".ts") || + childPath.endsWith(".tsx") + ) { const code = (await readFile(childPath)).toString(); results.push({code, path: childPath}); } } } - await visit("./example-runner/example-repos/react/packages"); + await visit(projectPath); return results; } diff --git a/benchmark/profile-react.ts b/benchmark/profile-react.ts deleted file mode 100644 index e8e5a63f..00000000 --- a/benchmark/profile-react.ts +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env node -/* eslint-disable no-console */ -// @ts-ignore: May not be built, just ignore for now. -import * as sucrase from "../dist/index"; // eslint-disable-line import/no-unresolved -import {loadReactFiles} from "./loadReactFiles"; - -async function main(): Promise { - console.log( - `Profiling Sucrase on the React codebase. Make sure you have Chrome DevTools for Node open.`, - ); - const reactFiles = await loadReactFiles(); - // tslint:disable-next-line no-any - (console as any).profile("Sucrase React"); - for (const {code, path} of reactFiles) { - sucrase.transform(code, { - transforms: ["jsx", "imports", "flow"], - filePath: path, - }); - } - // tslint:disable-next-line no-any - (console as any).profileEnd("Sucrase React"); -} - -main().catch((e) => { - console.error(e); - process.exitCode = 1; -}); diff --git a/package.json b/package.json index 596f7679..604d9087 100644 --- a/package.json +++ b/package.json @@ -18,9 +18,10 @@ "generate": "sucrase-node generator/generate.ts", "benchmark": "sucrase-node benchmark/benchmark.ts", "benchmark-react": "sucrase-node benchmark/benchmark-react.ts", + "benchmark-project": "sucrase-node benchmark/benchmark-project.ts", "lint": "sucrase-node script/lint.ts", "profile": "node --inspect-brk ./node_modules/.bin/sucrase-node ./benchmark/profile", - "profile-react": "node --inspect-brk ./node_modules/.bin/sucrase-node ./benchmark/profile-react.ts", + "profile-project": "node --inspect-brk ./node_modules/.bin/sucrase-node ./benchmark/benchmark-project.ts --profile", "prepublishOnly": "yarn clean && yarn build", "release": "sucrase-node script/release.ts", "run-examples": "sucrase-node example-runner/example-runner.ts", @@ -54,7 +55,8 @@ "@babel/preset-typescript": "7.0.0-beta.51", "@types/mocha": "^2.2.43", "@types/mz": "^0.0.32", - "@types/node": "^8.0.31", + "@types/node": "^10.12.9", + "@types/yargs-parser": "^11.0.0", "codecov": "^3.1.0", "eslint": "^4.13.1", "eslint-config-airbnb-base": "^12.1.0", @@ -69,7 +71,8 @@ "tslint": "^5.9.1", "tslint-language-service": "^0.9.9", "typescript": "^2.9.1", - "typescript-eslint-parser": "^16.0.0" + "typescript-eslint-parser": "^16.0.0", + "yargs-parser": "^11.1.0" }, "dependencies": { "commander": "^2.12.2", diff --git a/yarn.lock b/yarn.lock index 35ab45a2..b13e8baf 100644 --- a/yarn.lock +++ b/yarn.lock @@ -319,9 +319,22 @@ version "10.5.4" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.5.4.tgz#6eccc158504357d1da91434d75e86acde94bb10b" -"@types/node@^8.0.31": - version "8.10.22" - resolved "https://registry.yarnpkg.com/@types/node/-/node-8.10.22.tgz#c095d7c668908d48b95ae11fcc4a6d6b1c116a35" +"@types/node@^10.12.9": + version "10.12.9" + resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.9.tgz#a07bfa74331471e1dc22a47eb72026843f7b95c8" + integrity sha512-eajkMXG812/w3w4a1OcBlaTwsFPO5F7fJ/amy+tieQxEMWBlbV1JGSjkFM+zkHNf81Cad+dfIRA+IBkvmvdAeA== + +"@types/yargs-parser@^11.0.0": + version "11.0.0" + resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-11.0.0.tgz#514933c83ec38b8e06b8c9e5ffff637f20474f2e" + integrity sha512-ZfKjiy9zRHQWWRiQLeushSHE2IgHJ8U/OaABsNodkuufZoWED7Plz6egREZiE8dfrTAnrD8Rw/kUh2S6Iu1W0w== + dependencies: + "@types/yargs" "*" + +"@types/yargs@*": + version "12.0.1" + resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.1.tgz#c5ce4ad64499010ae4dc2acd9b14d49749a44233" + integrity sha512-UVjo2oH79aRNcsDlFlnQ/iJ67Jd7j6uSg7jUJP/RZ/nUjAh5ElmnwlD5K/6eGgETJUgCHkiWn91B8JjXQ6ubAw== abbrev@1: version "1.1.1" @@ -612,6 +625,11 @@ camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" +camelcase@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.0.0.tgz#03295527d58bd3cd4aa75363f35b2e8d97be2f42" + integrity sha512-faqwZqnWxbxn+F1d399ygeamQNy3lPp/H9H6rNrqYh4FSVCtcY+3cub1MxA8o9mDd55mM8Aghuu/kuyYA6VTsA== + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -848,7 +866,7 @@ debug@^3.1.0: dependencies: ms "2.0.0" -decamelize@^1.0.0, decamelize@^1.1.1: +decamelize@^1.0.0, decamelize@^1.1.1, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -3240,6 +3258,14 @@ yallist@^3.0.0, yallist@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.0.2.tgz#8452b4bb7e83c7c188d8041c1a837c773d6d8bb9" +yargs-parser@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-11.1.0.tgz#0b8104116367bf663089b24e5801438ab50396a3" + integrity sha512-lGA5HsbjkpCfekDBHAhgE5OE8xEoqiUDylowr+BvhRCwG1xVYTsd8hx2CYC0NY4k9RIgJeybFTG2EZW4P2aN1w== + dependencies: + camelcase "^5.0.0" + decamelize "^1.2.0" + yargs-parser@^8.0.0: version "8.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950"