cli.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }/* eslint-disable no-console */
  2. var _commander = require('commander'); var _commander2 = _interopRequireDefault(_commander);
  3. var _glob = require('glob');
  4. var _fs = require('mz/fs');
  5. var _path = require('path');
  6. var _index = require('./index');
  7. function run() {
  8. _commander2.default
  9. .description(`Sucrase: super-fast Babel alternative.`)
  10. .usage("[options] <srcDir>")
  11. .option(
  12. "-d, --out-dir <out>",
  13. "Compile an input directory of modules into an output directory.",
  14. )
  15. .option(
  16. "-p, --project <dir>",
  17. "Compile a TypeScript project, will read from tsconfig.json in <dir>",
  18. )
  19. .option("--out-extension <extension>", "File extension to use for all output files.", "js")
  20. .option("--exclude-dirs <paths>", "Names of directories that should not be traversed.")
  21. .option("-q, --quiet", "Don't print the names of converted files.")
  22. .option("-t, --transforms <transforms>", "Comma-separated list of transforms to run.")
  23. .option("--disable-es-transforms", "Opt out of all ES syntax transforms.")
  24. .option("--jsx-runtime <string>", "Transformation mode for the JSX transform.")
  25. .option("--production", "Disable debugging information from JSX in output.")
  26. .option(
  27. "--jsx-import-source <string>",
  28. "Automatic JSX transform import path prefix, defaults to `React.Fragment`.",
  29. )
  30. .option(
  31. "--jsx-pragma <string>",
  32. "Classic JSX transform element creation function, defaults to `React.createElement`.",
  33. )
  34. .option(
  35. "--jsx-fragment-pragma <string>",
  36. "Classic JSX transform fragment component, defaults to `React.Fragment`.",
  37. )
  38. .option("--keep-unused-imports", "Disable automatic removal of type-only imports/exports.")
  39. .option("--preserve-dynamic-import", "Don't transpile dynamic import() to require.")
  40. .option(
  41. "--inject-create-require-for-import-require",
  42. "Use `createRequire` when transpiling TS `import = require` to ESM.",
  43. )
  44. .option(
  45. "--enable-legacy-typescript-module-interop",
  46. "Use default TypeScript ESM/CJS interop strategy.",
  47. )
  48. .option("--enable-legacy-babel5-module-interop", "Use Babel 5 ESM/CJS interop strategy.")
  49. .parse(process.argv);
  50. if (_commander2.default.project) {
  51. if (
  52. _commander2.default.outDir ||
  53. _commander2.default.transforms ||
  54. _commander2.default.args[0] ||
  55. _commander2.default.enableLegacyTypescriptModuleInterop
  56. ) {
  57. console.error(
  58. "If TypeScript project is specified, out directory, transforms, source " +
  59. "directory, and --enable-legacy-typescript-module-interop may not be specified.",
  60. );
  61. process.exit(1);
  62. }
  63. } else {
  64. if (!_commander2.default.outDir) {
  65. console.error("Out directory is required");
  66. process.exit(1);
  67. }
  68. if (!_commander2.default.transforms) {
  69. console.error("Transforms option is required.");
  70. process.exit(1);
  71. }
  72. if (!_commander2.default.args[0]) {
  73. console.error("Source directory is required.");
  74. process.exit(1);
  75. }
  76. }
  77. const options = {
  78. outDirPath: _commander2.default.outDir,
  79. srcDirPath: _commander2.default.args[0],
  80. project: _commander2.default.project,
  81. outExtension: _commander2.default.outExtension,
  82. excludeDirs: _commander2.default.excludeDirs ? _commander2.default.excludeDirs.split(",") : [],
  83. quiet: _commander2.default.quiet,
  84. sucraseOptions: {
  85. transforms: _commander2.default.transforms ? _commander2.default.transforms.split(",") : [],
  86. disableESTransforms: _commander2.default.disableEsTransforms,
  87. jsxRuntime: _commander2.default.jsxRuntime,
  88. production: _commander2.default.production,
  89. jsxImportSource: _commander2.default.jsxImportSource,
  90. jsxPragma: _commander2.default.jsxPragma || "React.createElement",
  91. jsxFragmentPragma: _commander2.default.jsxFragmentPragma || "React.Fragment",
  92. keepUnusedImports: _commander2.default.keepUnusedImports,
  93. preserveDynamicImport: _commander2.default.preserveDynamicImport,
  94. injectCreateRequireForImportRequire: _commander2.default.injectCreateRequireForImportRequire,
  95. enableLegacyTypeScriptModuleInterop: _commander2.default.enableLegacyTypescriptModuleInterop,
  96. enableLegacyBabel5ModuleInterop: _commander2.default.enableLegacyBabel5ModuleInterop,
  97. },
  98. };
  99. buildDirectory(options).catch((e) => {
  100. process.exitCode = 1;
  101. console.error(e);
  102. });
  103. } exports.default = run;
  104. async function findFiles(options) {
  105. const outDirPath = options.outDirPath;
  106. const srcDirPath = options.srcDirPath;
  107. const extensions = options.sucraseOptions.transforms.includes("typescript")
  108. ? [".ts", ".tsx"]
  109. : [".js", ".jsx"];
  110. if (!(await _fs.exists.call(void 0, outDirPath))) {
  111. await _fs.mkdir.call(void 0, outDirPath);
  112. }
  113. const outArr = [];
  114. for (const child of await _fs.readdir.call(void 0, srcDirPath)) {
  115. if (["node_modules", ".git"].includes(child) || options.excludeDirs.includes(child)) {
  116. continue;
  117. }
  118. const srcChildPath = _path.join.call(void 0, srcDirPath, child);
  119. const outChildPath = _path.join.call(void 0, outDirPath, child);
  120. if ((await _fs.stat.call(void 0, srcChildPath)).isDirectory()) {
  121. const innerOptions = {...options};
  122. innerOptions.srcDirPath = srcChildPath;
  123. innerOptions.outDirPath = outChildPath;
  124. const innerFiles = await findFiles(innerOptions);
  125. outArr.push(...innerFiles);
  126. } else if (extensions.some((ext) => srcChildPath.endsWith(ext))) {
  127. const outPath = outChildPath.replace(/\.\w+$/, `.${options.outExtension}`);
  128. outArr.push({
  129. srcPath: srcChildPath,
  130. outPath,
  131. });
  132. }
  133. }
  134. return outArr;
  135. }
  136. async function runGlob(options) {
  137. const tsConfigPath = _path.join.call(void 0, options.project, "tsconfig.json");
  138. let str;
  139. try {
  140. str = await _fs.readFile.call(void 0, tsConfigPath, "utf8");
  141. } catch (err) {
  142. console.error("Could not find project tsconfig.json");
  143. console.error(` --project=${options.project}`);
  144. console.error(err);
  145. process.exit(1);
  146. }
  147. const json = JSON.parse(str);
  148. const foundFiles = [];
  149. const files = json.files;
  150. const include = json.include;
  151. const absProject = _path.join.call(void 0, process.cwd(), options.project);
  152. const outDirs = [];
  153. if (!(await _fs.exists.call(void 0, options.outDirPath))) {
  154. await _fs.mkdir.call(void 0, options.outDirPath);
  155. }
  156. if (files) {
  157. for (const file of files) {
  158. if (file.endsWith(".d.ts")) {
  159. continue;
  160. }
  161. if (!file.endsWith(".ts") && !file.endsWith(".js")) {
  162. continue;
  163. }
  164. const srcFile = _path.join.call(void 0, absProject, file);
  165. const outFile = _path.join.call(void 0, options.outDirPath, file);
  166. const outPath = outFile.replace(/\.\w+$/, `.${options.outExtension}`);
  167. const outDir = _path.dirname.call(void 0, outPath);
  168. if (!outDirs.includes(outDir)) {
  169. outDirs.push(outDir);
  170. }
  171. foundFiles.push({
  172. srcPath: srcFile,
  173. outPath,
  174. });
  175. }
  176. }
  177. if (include) {
  178. for (const pattern of include) {
  179. const globFiles = await _glob.glob.call(void 0, _path.join.call(void 0, absProject, pattern));
  180. for (const file of globFiles) {
  181. if (!file.endsWith(".ts") && !file.endsWith(".js")) {
  182. continue;
  183. }
  184. if (file.endsWith(".d.ts")) {
  185. continue;
  186. }
  187. const relativeFile = _path.relative.call(void 0, absProject, file);
  188. const outFile = _path.join.call(void 0, options.outDirPath, relativeFile);
  189. const outPath = outFile.replace(/\.\w+$/, `.${options.outExtension}`);
  190. const outDir = _path.dirname.call(void 0, outPath);
  191. if (!outDirs.includes(outDir)) {
  192. outDirs.push(outDir);
  193. }
  194. foundFiles.push({
  195. srcPath: file,
  196. outPath,
  197. });
  198. }
  199. }
  200. }
  201. for (const outDirPath of outDirs) {
  202. if (!(await _fs.exists.call(void 0, outDirPath))) {
  203. await _fs.mkdir.call(void 0, outDirPath);
  204. }
  205. }
  206. // TODO: read exclude
  207. return foundFiles;
  208. }
  209. async function updateOptionsFromProject(options) {
  210. /**
  211. * Read the project information and assign the following.
  212. * - outDirPath
  213. * - transform: imports
  214. * - transform: typescript
  215. * - enableLegacyTypescriptModuleInterop: true/false.
  216. */
  217. const tsConfigPath = _path.join.call(void 0, options.project, "tsconfig.json");
  218. let str;
  219. try {
  220. str = await _fs.readFile.call(void 0, tsConfigPath, "utf8");
  221. } catch (err) {
  222. console.error("Could not find project tsconfig.json");
  223. console.error(` --project=${options.project}`);
  224. console.error(err);
  225. process.exit(1);
  226. }
  227. const json = JSON.parse(str);
  228. const sucraseOpts = options.sucraseOptions;
  229. if (!sucraseOpts.transforms.includes("typescript")) {
  230. sucraseOpts.transforms.push("typescript");
  231. }
  232. const compilerOpts = json.compilerOptions;
  233. if (compilerOpts.outDir) {
  234. options.outDirPath = _path.join.call(void 0, process.cwd(), options.project, compilerOpts.outDir);
  235. }
  236. if (compilerOpts.esModuleInterop !== true) {
  237. sucraseOpts.enableLegacyTypeScriptModuleInterop = true;
  238. }
  239. if (compilerOpts.module === "commonjs") {
  240. if (!sucraseOpts.transforms.includes("imports")) {
  241. sucraseOpts.transforms.push("imports");
  242. }
  243. }
  244. }
  245. async function buildDirectory(options) {
  246. let files;
  247. if (options.outDirPath && options.srcDirPath) {
  248. files = await findFiles(options);
  249. } else if (options.project) {
  250. await updateOptionsFromProject(options);
  251. files = await runGlob(options);
  252. } else {
  253. console.error("Project or Source directory required.");
  254. process.exit(1);
  255. }
  256. for (const file of files) {
  257. await buildFile(file.srcPath, file.outPath, options);
  258. }
  259. }
  260. async function buildFile(srcPath, outPath, options) {
  261. if (!options.quiet) {
  262. console.log(`${srcPath} -> ${outPath}`);
  263. }
  264. const code = (await _fs.readFile.call(void 0, srcPath)).toString();
  265. const transformedCode = _index.transform.call(void 0, code, {...options.sucraseOptions, filePath: srcPath}).code;
  266. await _fs.writeFile.call(void 0, outPath, transformedCode);
  267. }