create-tauri-app.js 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. #!/usr/bin/env node
  2. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  3. // SPDX-License-Identifier: Apache-2.0
  4. // SPDX-License-Identifier: MIT
  5. const parseArgs = require("minimist");
  6. const inquirer = require("inquirer");
  7. const { resolve, join } = require("path");
  8. const {
  9. recipeShortNames,
  10. recipeDescriptiveNames,
  11. recipeByDescriptiveName,
  12. recipeByShortName,
  13. install,
  14. shell,
  15. } = require("../dist/");
  16. const { dir } = require("console");
  17. /**
  18. * @type {object}
  19. * @property {boolean} h
  20. * @property {boolean} help
  21. * @property {boolean} v
  22. * @property {boolean} version
  23. * @property {string|boolean} f
  24. * @property {string|boolean} force
  25. * @property {boolean} l
  26. * @property {boolean} log
  27. * @property {boolean} d
  28. * @property {boolean} directory
  29. * @property {string} r
  30. * @property {string} recipe
  31. */
  32. const createTauriApp = async (cliArgs) => {
  33. const argv = parseArgs(cliArgs, {
  34. alias: {
  35. h: "help",
  36. v: "version",
  37. f: "force",
  38. l: "log",
  39. d: "directory",
  40. b: "binary",
  41. t: "tauri-path",
  42. A: "app-name",
  43. W: "window-title",
  44. D: "dist-dir",
  45. P: "dev-path",
  46. r: "recipe",
  47. },
  48. boolean: ["h", "l", "ci"],
  49. });
  50. if (argv.help) {
  51. printUsage();
  52. return 0;
  53. }
  54. if (argv.v) {
  55. console.log(require("../package.json").version);
  56. return false; // do this for node consumers and tests
  57. }
  58. if (argv.ci) {
  59. return runInit(argv);
  60. } else {
  61. return getOptionsInteractive(argv).then((responses) =>
  62. runInit(argv, responses)
  63. );
  64. }
  65. };
  66. function printUsage() {
  67. console.log(`
  68. Description
  69. Starts a new tauri app from a "recipe" or pre-built template.
  70. Usage
  71. $ yarn create tauri-app <app-name> # npm create-tauri-app <app-name>
  72. Options
  73. --help, -h Displays this message
  74. -v, --version Displays the Tauri CLI version
  75. --ci Skip prompts
  76. --force, -f Force init to overwrite [conf|template|all]
  77. --log, -l Logging [boolean]
  78. --directory, -d Set target directory for init
  79. --binary, -b Optional path to a tauri binary from which to run init
  80. --app-name, -A Name of your Tauri application
  81. --window-title, -W Window title of your Tauri application
  82. --dist-dir, -D Web assets location, relative to <project-dir>/src-tauri
  83. --dev-path, -P Url of your dev server
  84. --recipe, -r Add UI framework recipe. None by default.
  85. Supported recipes: [${recipeShortNames.join("|")}]
  86. `);
  87. }
  88. const getOptionsInteractive = (argv) => {
  89. let defaultAppName = argv.A || "tauri-app";
  90. return inquirer
  91. .prompt([
  92. {
  93. type: "input",
  94. name: "appName",
  95. message: "What is your app name?",
  96. default: defaultAppName,
  97. when: !argv.A,
  98. },
  99. {
  100. type: "input",
  101. name: "tauri.window.title",
  102. message: "What should the window title be?",
  103. default: "Tauri App",
  104. when: () => !argv.W,
  105. },
  106. {
  107. type: "list",
  108. name: "recipeName",
  109. message: "Would you like to add a UI recipe?",
  110. choices: recipeDescriptiveNames,
  111. default: "No recipe",
  112. when: () => !argv.r,
  113. },
  114. ])
  115. .catch((error) => {
  116. if (error.isTtyError) {
  117. // Prompt couldn't be rendered in the current environment
  118. console.log(
  119. "It appears your terminal does not support interactive prompts. Using default values."
  120. );
  121. runInit();
  122. } else {
  123. // Something else when wrong
  124. console.error("An unknown error occurred:", error);
  125. }
  126. });
  127. };
  128. async function runInit(argv, config = {}) {
  129. const {
  130. appName,
  131. recipeName,
  132. tauri: {
  133. window: { title },
  134. },
  135. } = config;
  136. let recipe;
  137. if (recipeName !== undefined) {
  138. recipe = recipeByDescriptiveName(recipeName);
  139. } else if (argv.r) {
  140. recipe = recipeByShortName(argv.r);
  141. }
  142. let buildConfig = {
  143. distDir: argv.D,
  144. devPath: argv.P,
  145. };
  146. if (recipe !== undefined) {
  147. buildConfig = recipe.configUpdate(buildConfig);
  148. }
  149. const directory = argv.d || process.cwd();
  150. const cfg = {
  151. ...buildConfig,
  152. appName: appName || argv.A,
  153. windowTitle: title || argv.w,
  154. };
  155. // note that our app directory is reliant on the appName and
  156. // generally there are issues if the path has spaces (see Windows)
  157. // future TODO prevent app names with spaces or escape here?
  158. const appDirectory = join(directory, cfg.appName);
  159. if (recipe.preInit) {
  160. console.log("===== running initial command(s) =====");
  161. await recipe.preInit({ cwd: directory, cfg });
  162. }
  163. const initArgs = [
  164. ["--app-name", cfg.appName],
  165. ["--window-title", cfg.windowTitle],
  166. ["--dist-dir", cfg.distDir],
  167. ["--dev-path", cfg.devPath],
  168. ].reduce((final, argSet) => {
  169. if (argSet[1]) {
  170. return final.concat([argSet[0], `\"${argSet[1]}\"`]);
  171. } else {
  172. return final;
  173. }
  174. }, []);
  175. const installed = await install({
  176. appDir: appDirectory,
  177. dependencies: recipe.extraNpmDependencies,
  178. devDependencies: ["tauri", ...recipe.extraNpmDevDependencies],
  179. });
  180. console.log("===== running tauri init =====");
  181. const binary = !argv.b
  182. ? installed.packageManager
  183. : resolve(appDirectory, argv.b);
  184. const runTauriArgs =
  185. installed.packageManager === "npm" && !argv.b
  186. ? ["run", "tauri", "--", "init"]
  187. : ["tauri", "init"];
  188. await shell(binary, [...runTauriArgs, ...initArgs], {
  189. cwd: appDirectory,
  190. });
  191. if (recipe.postInit) {
  192. console.log("===== running final command(s) =====");
  193. await recipe.postInit({
  194. cwd: appDirectory,
  195. cfg,
  196. });
  197. }
  198. }
  199. createTauriApp(process.argv.slice(2)).catch((err) => {
  200. console.error(err);
  201. });