create-stylus 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/.github/issue_template.md +7 -0
  2. package/.github/pull_request_template.md +11 -0
  3. package/.github/workflows/release-alpha.yml +32 -0
  4. package/.github/workflows/release-manual.yml +26 -0
  5. package/.yarnrc.yml +1 -0
  6. package/CONTRIBUTING.md +42 -0
  7. package/README.md +66 -0
  8. package/bin/create-dapp-ss.js +4 -0
  9. package/package.json +46 -0
  10. package/rollup.config.js +22 -0
  11. package/src/cli.ts +14 -0
  12. package/src/extensions.json +14 -0
  13. package/src/main.ts +72 -0
  14. package/src/tasks/copy-extension-file.ts +227 -0
  15. package/src/tasks/copy-template-files.ts +252 -0
  16. package/src/tasks/create-first-git-commit.ts +35 -0
  17. package/src/tasks/create-project-directory.ts +34 -0
  18. package/src/tasks/index.ts +5 -0
  19. package/src/tasks/install-packages.ts +15 -0
  20. package/src/tasks/prettier-format.ts +17 -0
  21. package/src/types.ts +31 -0
  22. package/src/utils/consts.ts +1 -0
  23. package/src/utils/find-files-recursively.ts +19 -0
  24. package/src/utils/link.ts +44 -0
  25. package/src/utils/load-extensions.ts +10 -0
  26. package/src/utils/merge-package-json.ts +33 -0
  27. package/src/utils/parse-arguments-into-options.ts +38 -0
  28. package/src/utils/prompt-for-missing-options.ts +53 -0
  29. package/src/utils/render-intro-message.ts +11 -0
  30. package/src/utils/render-outro-message.ts +34 -0
  31. package/templates/base/.github/ISSUE_TEMPLATE/bug_report.yml +58 -0
  32. package/templates/base/.github/ISSUE_TEMPLATE/config.yml +8 -0
  33. package/templates/base/.github/pull_request_template.md +16 -0
  34. package/templates/base/.github/workflows/lint.yaml +300 -0
  35. package/templates/base/.gitignore.template.mjs +19 -0
  36. package/templates/base/.gitmodules +0 -0
  37. package/templates/base/.husky/pre-commit +4 -0
  38. package/templates/base/.lintstagedrc.js +21 -0
  39. package/templates/base/.vscode/settings.json +7 -0
  40. package/templates/base/.yarn/plugins/@yarnpkg/plugin-typescript.cjs +9 -0
  41. package/templates/base/.yarn/releases/yarn-3.2.3.cjs +783 -0
  42. package/templates/base/.yarnrc.yml +11 -0
  43. package/templates/base/CONTRIBUTING.md +86 -0
  44. package/templates/base/LICENCE +21 -0
  45. package/templates/base/dist/cli.js +683 -0
  46. package/templates/base/dist/cli.js.map +1 -0
  47. package/templates/base/nitro-devnode/LICENSE +201 -0
  48. package/templates/base/nitro-devnode/README.md +70 -0
  49. package/templates/base/nitro-devnode/run-dev-node.sh +132 -0
  50. package/templates/base/nitro-devnode/start-chain-with-cors.sh +150 -0
  51. package/templates/base/nitro-devnode/stylus-deployer-bytecode.txt +1 -0
  52. package/templates/base/nitro-devnode/stylus-dev/Dockerfile +10 -0
  53. package/templates/base/package.json +44 -0
  54. package/templates/base/packages/nextjs/.env.example +13 -0
  55. package/templates/base/packages/nextjs/.eslintignore +11 -0
  56. package/templates/base/packages/nextjs/.eslintrc.json +15 -0
  57. package/templates/base/packages/nextjs/.gitignore.template.mjs +42 -0
  58. package/templates/base/packages/nextjs/.prettierrc.js +9 -0
  59. package/templates/base/packages/nextjs/.prettierrc.json +8 -0
  60. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressCodeTab.tsx +27 -0
  61. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressComponent.tsx +36 -0
  62. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressLogsTab.tsx +21 -0
  63. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressStorageTab.tsx +61 -0
  64. package/templates/base/packages/nextjs/app/blockexplorer/_components/BackButton.tsx +12 -0
  65. package/templates/base/packages/nextjs/app/blockexplorer/_components/ContractTabs.tsx +102 -0
  66. package/templates/base/packages/nextjs/app/blockexplorer/_components/PaginationButton.tsx +39 -0
  67. package/templates/base/packages/nextjs/app/blockexplorer/_components/SearchBar.tsx +49 -0
  68. package/templates/base/packages/nextjs/app/blockexplorer/_components/TransactionHash.tsx +28 -0
  69. package/templates/base/packages/nextjs/app/blockexplorer/_components/TransactionsTable.tsx +71 -0
  70. package/templates/base/packages/nextjs/app/blockexplorer/_components/index.tsx +7 -0
  71. package/templates/base/packages/nextjs/app/blockexplorer/address/[address]/page.tsx +101 -0
  72. package/templates/base/packages/nextjs/app/blockexplorer/layout.tsx +12 -0
  73. package/templates/base/packages/nextjs/app/blockexplorer/page.tsx +83 -0
  74. package/templates/base/packages/nextjs/app/blockexplorer/transaction/[txHash]/page.tsx +23 -0
  75. package/templates/base/packages/nextjs/app/blockexplorer/transaction/_components/TransactionComp.tsx +152 -0
  76. package/templates/base/packages/nextjs/app/debug/_components/DebugContracts.tsx +73 -0
  77. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractInput.tsx +84 -0
  78. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractReadMethods.tsx +43 -0
  79. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractUI.tsx +164 -0
  80. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractVariables.tsx +50 -0
  81. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractWriteMethods.tsx +49 -0
  82. package/templates/base/packages/nextjs/app/debug/_components/contract/DisplayVariable.tsx +85 -0
  83. package/templates/base/packages/nextjs/app/debug/_components/contract/InheritanceTooltip.tsx +14 -0
  84. package/templates/base/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx +102 -0
  85. package/templates/base/packages/nextjs/app/debug/_components/contract/Tuple.tsx +44 -0
  86. package/templates/base/packages/nextjs/app/debug/_components/contract/TupleArray.tsx +142 -0
  87. package/templates/base/packages/nextjs/app/debug/_components/contract/TxReceipt.tsx +42 -0
  88. package/templates/base/packages/nextjs/app/debug/_components/contract/WriteOnlyFunctionForm.tsx +144 -0
  89. package/templates/base/packages/nextjs/app/debug/_components/contract/index.tsx +8 -0
  90. package/templates/base/packages/nextjs/app/debug/_components/contract/utilsContract.tsx +166 -0
  91. package/templates/base/packages/nextjs/app/debug/_components/contract/utilsDisplay.tsx +114 -0
  92. package/templates/base/packages/nextjs/app/debug/page.tsx +14 -0
  93. package/templates/base/packages/nextjs/app/layout.tsx +67 -0
  94. package/templates/base/packages/nextjs/app/not-found.tsx +16 -0
  95. package/templates/base/packages/nextjs/app/page.tsx +94 -0
  96. package/templates/base/packages/nextjs/components/Background.tsx +37 -0
  97. package/templates/base/packages/nextjs/components/Card.tsx +40 -0
  98. package/templates/base/packages/nextjs/components/Footer.tsx +93 -0
  99. package/templates/base/packages/nextjs/components/Header.tsx +114 -0
  100. package/templates/base/packages/nextjs/components/ScaffoldEthAppWithProviders.tsx +77 -0
  101. package/templates/base/packages/nextjs/components/SwitchTheme.tsx +41 -0
  102. package/templates/base/packages/nextjs/components/ThemeProvider.tsx +13 -0
  103. package/templates/base/packages/nextjs/components/assets/BuidlGuidlLogo.tsx +18 -0
  104. package/templates/base/packages/nextjs/components/scaffold-eth/Address/Address.tsx +187 -0
  105. package/templates/base/packages/nextjs/components/scaffold-eth/Address/AddressCopyIcon.tsx +23 -0
  106. package/templates/base/packages/nextjs/components/scaffold-eth/Address/AddressLinkWrapper.tsx +29 -0
  107. package/templates/base/packages/nextjs/components/scaffold-eth/Balance.tsx +75 -0
  108. package/templates/base/packages/nextjs/components/scaffold-eth/BlockieAvatar.tsx +17 -0
  109. package/templates/base/packages/nextjs/components/scaffold-eth/Faucet.tsx +131 -0
  110. package/templates/base/packages/nextjs/components/scaffold-eth/FaucetButton.tsx +75 -0
  111. package/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx +120 -0
  112. package/templates/base/packages/nextjs/components/scaffold-eth/Input/Bytes32Input.tsx +31 -0
  113. package/templates/base/packages/nextjs/components/scaffold-eth/Input/BytesInput.tsx +28 -0
  114. package/templates/base/packages/nextjs/components/scaffold-eth/Input/EtherInput.tsx +128 -0
  115. package/templates/base/packages/nextjs/components/scaffold-eth/Input/InputBase.tsx +66 -0
  116. package/templates/base/packages/nextjs/components/scaffold-eth/Input/IntegerInput.tsx +63 -0
  117. package/templates/base/packages/nextjs/components/scaffold-eth/Input/index.ts +9 -0
  118. package/templates/base/packages/nextjs/components/scaffold-eth/Input/utils.ts +109 -0
  119. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx +121 -0
  120. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressQRCodeModal.tsx +33 -0
  121. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/BurnerWalletModal.tsx +63 -0
  122. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/NetworkOptions.tsx +48 -0
  123. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/WrongNetworkDropdown.tsx +32 -0
  124. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/index.tsx +89 -0
  125. package/templates/base/packages/nextjs/components/scaffold-eth/index.tsx +7 -0
  126. package/templates/base/packages/nextjs/contracts/deployedContracts.ts +9 -0
  127. package/templates/base/packages/nextjs/contracts/externalContracts.ts +16 -0
  128. package/templates/base/packages/nextjs/eslint.config.mjs +32 -0
  129. package/templates/base/packages/nextjs/hooks/scaffold-eth/index.ts +17 -0
  130. package/templates/base/packages/nextjs/hooks/scaffold-eth/useAnimationConfig.ts +20 -0
  131. package/templates/base/packages/nextjs/hooks/scaffold-eth/useContractLogs.ts +40 -0
  132. package/templates/base/packages/nextjs/hooks/scaffold-eth/useCopyToClipboard.ts +19 -0
  133. package/templates/base/packages/nextjs/hooks/scaffold-eth/useDeployedContractInfo.ts +86 -0
  134. package/templates/base/packages/nextjs/hooks/scaffold-eth/useDisplayUsdMode.ts +21 -0
  135. package/templates/base/packages/nextjs/hooks/scaffold-eth/useFetchBlocks.ts +133 -0
  136. package/templates/base/packages/nextjs/hooks/scaffold-eth/useInitializeNativeCurrencyPrice.ts +32 -0
  137. package/templates/base/packages/nextjs/hooks/scaffold-eth/useNativeCurrencyPrice.ts +34 -0
  138. package/templates/base/packages/nextjs/hooks/scaffold-eth/useNetworkColor.ts +22 -0
  139. package/templates/base/packages/nextjs/hooks/scaffold-eth/useOutsideClick.ts +23 -0
  140. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldContract.ts +65 -0
  141. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventHistory.ts +213 -0
  142. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldReadContract.ts +80 -0
  143. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldWatchContractEvent.ts +40 -0
  144. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldWriteContract.ts +191 -0
  145. package/templates/base/packages/nextjs/hooks/scaffold-eth/useSelectedNetwork.ts +18 -0
  146. package/templates/base/packages/nextjs/hooks/scaffold-eth/useTargetNetwork.ts +23 -0
  147. package/templates/base/packages/nextjs/hooks/scaffold-eth/useTransactor.tsx +114 -0
  148. package/templates/base/packages/nextjs/hooks/scaffold-eth/useWatchBalance.ts +21 -0
  149. package/templates/base/packages/nextjs/icons/CompassIcon.tsx +39 -0
  150. package/templates/base/packages/nextjs/icons/DarkBugAntIcon.tsx +30 -0
  151. package/templates/base/packages/nextjs/icons/LightBugAntIcon.tsx +52 -0
  152. package/templates/base/packages/nextjs/next-env.d.ts +5 -0
  153. package/templates/base/packages/nextjs/next.config.js +19 -0
  154. package/templates/base/packages/nextjs/package.json +58 -0
  155. package/templates/base/packages/nextjs/postcss.config.js +6 -0
  156. package/templates/base/packages/nextjs/public/debug-image.png +0 -0
  157. package/templates/base/packages/nextjs/public/favicon.png +0 -0
  158. package/templates/base/packages/nextjs/public/logo.svg +8 -0
  159. package/templates/base/packages/nextjs/public/manifest.json +5 -0
  160. package/templates/base/packages/nextjs/public/thumbnail.jpg +0 -0
  161. package/templates/base/packages/nextjs/react-copy-to-clipboard.d.ts +44 -0
  162. package/templates/base/packages/nextjs/scaffold.config.ts +56 -0
  163. package/templates/base/packages/nextjs/services/store/store.ts +39 -0
  164. package/templates/base/packages/nextjs/services/web3/wagmiConfig.tsx +44 -0
  165. package/templates/base/packages/nextjs/services/web3/wagmiConnectors.tsx +51 -0
  166. package/templates/base/packages/nextjs/styles/globals.css +80 -0
  167. package/templates/base/packages/nextjs/tailwind.config.js +97 -0
  168. package/templates/base/packages/nextjs/tsconfig.json +28 -0
  169. package/templates/base/packages/nextjs/types/abitype/abi.d.ts +16 -0
  170. package/templates/base/packages/nextjs/types/utils.ts +3 -0
  171. package/templates/base/packages/nextjs/utils/scaffold-eth/block.ts +17 -0
  172. package/templates/base/packages/nextjs/utils/scaffold-eth/common.ts +8 -0
  173. package/templates/base/packages/nextjs/utils/scaffold-eth/contract.ts +352 -0
  174. package/templates/base/packages/nextjs/utils/scaffold-eth/contractsData.ts +11 -0
  175. package/templates/base/packages/nextjs/utils/scaffold-eth/decodeTxData.ts +65 -0
  176. package/templates/base/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts +72 -0
  177. package/templates/base/packages/nextjs/utils/scaffold-eth/getMetadata.ts +50 -0
  178. package/templates/base/packages/nextjs/utils/scaffold-eth/getParsedError.ts +35 -0
  179. package/templates/base/packages/nextjs/utils/scaffold-eth/index.ts +6 -0
  180. package/templates/base/packages/nextjs/utils/scaffold-eth/notification.tsx +90 -0
  181. package/templates/base/packages/nextjs/utils/scaffold-stylus/burner.ts +59 -0
  182. package/templates/base/packages/nextjs/utils/scaffold-stylus/chain.ts +42 -0
  183. package/templates/base/packages/nextjs/utils/scaffold-stylus/index.ts +3 -0
  184. package/templates/base/packages/nextjs/utils/scaffold-stylus/networks.ts +94 -0
  185. package/templates/base/packages/nextjs/vercel.json +3 -0
  186. package/templates/base/packages/stylus/.env.example +16 -0
  187. package/templates/base/packages/stylus/.eslintrc.js +23 -0
  188. package/templates/base/packages/stylus/.gitignore.template.mjs +7 -0
  189. package/templates/base/packages/stylus/jest.config.js +15 -0
  190. package/templates/base/packages/stylus/package.json +48 -0
  191. package/templates/base/packages/stylus/scripts/deploy.ts +46 -0
  192. package/templates/base/packages/stylus/scripts/deploy_contract.ts +84 -0
  193. package/templates/base/packages/stylus/scripts/deploy_wrapper.ts +39 -0
  194. package/templates/base/packages/stylus/scripts/export_abi.ts +87 -0
  195. package/templates/base/packages/stylus/scripts/index.ts +0 -0
  196. package/templates/base/packages/stylus/scripts/new_module.sh +35 -0
  197. package/templates/base/packages/stylus/scripts/test_network.ts +31 -0
  198. package/templates/base/packages/stylus/scripts/utils/command.ts +152 -0
  199. package/templates/base/packages/stylus/scripts/utils/contract.ts +228 -0
  200. package/templates/base/packages/stylus/scripts/utils/deployment.ts +260 -0
  201. package/templates/base/packages/stylus/scripts/utils/index.ts +6 -0
  202. package/templates/base/packages/stylus/scripts/utils/network.ts +132 -0
  203. package/templates/base/packages/stylus/scripts/utils/type.ts +51 -0
  204. package/templates/base/packages/stylus/scripts/utils.ts +3 -0
  205. package/templates/base/packages/stylus/tsconfig.json +41 -0
  206. package/templates/base/packages/stylus/your-contract/.cargo/config.toml +18 -0
  207. package/templates/base/packages/stylus/your-contract/Cargo.lock +5761 -0
  208. package/templates/base/packages/stylus/your-contract/Cargo.toml +48 -0
  209. package/templates/base/packages/stylus/your-contract/examples/counter.rs +78 -0
  210. package/templates/base/packages/stylus/your-contract/header.png +0 -0
  211. package/templates/base/packages/stylus/your-contract/licenses/Apache-2.0 +201 -0
  212. package/templates/base/packages/stylus/your-contract/licenses/COPYRIGHT.md +5 -0
  213. package/templates/base/packages/stylus/your-contract/licenses/DCO.txt +34 -0
  214. package/templates/base/packages/stylus/your-contract/licenses/MIT +21 -0
  215. package/templates/base/packages/stylus/your-contract/rust-toolchain.toml +2 -0
  216. package/templates/base/packages/stylus/your-contract/src/lib.rs +241 -0
  217. package/templates/base/packages/stylus/your-contract/src/main.rs +10 -0
  218. package/templates/base/readme.md +352 -0
  219. package/templates/base/yarn.lock +17859 -0
  220. package/tsconfig.json +13 -0
@@ -0,0 +1,683 @@
1
+ import { execa } from 'execa';
2
+ import url, { fileURLToPath } from 'url';
3
+ import path from 'path';
4
+ import fs, { lstatSync, readdirSync, existsSync, promises } from 'fs';
5
+ import mergeJsonStr from 'merge-packages';
6
+ import ncp from 'ncp';
7
+ import { promisify } from 'util';
8
+ import { projectInstall } from 'pkg-install';
9
+ import chalk from 'chalk';
10
+ import Listr from 'listr';
11
+ import arg from 'arg';
12
+ import inquirer from 'inquirer';
13
+
14
+ const isExtension = (item) => item !== null;
15
+ /**
16
+ * This function makes sure that the `T` generic type is narrowed down to
17
+ * whatever `extensions` are passed in the question prop. That way we can type
18
+ * check the `default` prop is not using any valid extension, but only one
19
+ * already provided in the `extensions` prop.
20
+ *
21
+ * Questions can be created without this function, just using a normal object,
22
+ * but `default` type will be any valid Extension.
23
+ */
24
+ const typedQuestion = (question) => question;
25
+ const isDefined = (item) => item !== undefined && item !== null;
26
+ const extensionWithSubextensions = (extension) => {
27
+ return Object.prototype.hasOwnProperty.call(extension, "extensions");
28
+ };
29
+
30
+ const baseDir = "base";
31
+
32
+ const extensionDict = {};
33
+ const currentFileUrl = import.meta.url;
34
+ const templatesDirectory = path.resolve(decodeURI(fileURLToPath(currentFileUrl)), "../../templates");
35
+ /**
36
+ * This function has side effects. It generates the extensionDict.
37
+ *
38
+ * @param basePath the path at which to start the traverse
39
+ * @returns the extensions found in this path. Useful for the recursion
40
+ */
41
+ const traverseExtensions = async (basePath) => {
42
+ const extensionsPath = path.resolve(basePath, "extensions");
43
+ let extensions;
44
+ try {
45
+ extensions = fs.readdirSync(extensionsPath);
46
+ }
47
+ catch (error) {
48
+ return [];
49
+ }
50
+ await Promise.all(extensions.map(async (ext) => {
51
+ const extPath = path.resolve(extensionsPath, ext);
52
+ const configPath = path.resolve(extPath, "config.json");
53
+ let config = {};
54
+ try {
55
+ config = JSON.parse(fs.readFileSync(configPath, "utf8"));
56
+ }
57
+ catch (error) {
58
+ if (fs.existsSync(configPath)) {
59
+ throw new Error(`Couldn't parse existing config.json file.
60
+ Extension: ${ext};
61
+ Config file path: ${configPath}`);
62
+ }
63
+ }
64
+ let name = config.name ?? ext;
65
+ let value = ext;
66
+ const subExtensions = await traverseExtensions(extPath);
67
+ const hasSubExtensions = subExtensions.length !== 0;
68
+ const extDescriptor = {
69
+ name,
70
+ value,
71
+ path: extPath,
72
+ extensions: subExtensions,
73
+ extends: config.extends,
74
+ };
75
+ if (!hasSubExtensions) {
76
+ delete extDescriptor.extensions;
77
+ }
78
+ extensionDict[ext] = extDescriptor;
79
+ return subExtensions;
80
+ }));
81
+ return extensions;
82
+ };
83
+ await traverseExtensions(templatesDirectory);
84
+
85
+ const findFilesRecursiveSync = (baseDir, criteriaFn = () => true) => {
86
+ const subPaths = fs.readdirSync(baseDir);
87
+ const files = subPaths.map((relativePath) => {
88
+ const fullPath = path.resolve(baseDir, relativePath);
89
+ return fs.lstatSync(fullPath).isDirectory()
90
+ ? [...findFilesRecursiveSync(fullPath, criteriaFn)]
91
+ : criteriaFn(fullPath)
92
+ ? [fullPath]
93
+ : [];
94
+ });
95
+ return files.flat();
96
+ };
97
+
98
+ // @ts-expect-error We don't have types for this probably add .d.ts file
99
+ function mergePackageJson(targetPackageJsonPath, secondPackageJsonPath, isDev) {
100
+ const existsTarget = fs.existsSync(targetPackageJsonPath);
101
+ const existsSecond = fs.existsSync(secondPackageJsonPath);
102
+ if (!existsTarget && !existsSecond) {
103
+ return;
104
+ }
105
+ const targetPackageJson = existsTarget ? fs.readFileSync(targetPackageJsonPath, "utf8") : '{}';
106
+ const secondPackageJson = existsSecond ? fs.readFileSync(secondPackageJsonPath, "utf8") : '{}';
107
+ const mergedPkgStr = mergeJsonStr.default(targetPackageJson, secondPackageJson);
108
+ fs.writeFileSync(targetPackageJsonPath, mergedPkgStr, "utf8");
109
+ if (isDev) {
110
+ const devStr = `TODO: write relevant information for the contributor`;
111
+ fs.writeFileSync(`${targetPackageJsonPath}.dev`, devStr, "utf8");
112
+ }
113
+ }
114
+
115
+ const { mkdir, link } = promises;
116
+ /**
117
+ * The goal is that this function has the same API as ncp, so they can be used
118
+ * interchangeably.
119
+ *
120
+ * - clobber not implemented
121
+ */
122
+ const linkRecursive = async (source, destination, options) => {
123
+ const passesFilter = options?.filter === undefined
124
+ ? true // no filter
125
+ : typeof options.filter === 'function'
126
+ ? options.filter(source) // filter is function
127
+ : options.filter.test(source); // filter is regex
128
+ if (!passesFilter) {
129
+ return;
130
+ }
131
+ if (lstatSync(source).isDirectory()) {
132
+ const subPaths = readdirSync(source);
133
+ await Promise.all(subPaths.map(async (subPath) => {
134
+ const sourceSubpath = path.join(source, subPath);
135
+ const isSubPathAFolder = lstatSync(sourceSubpath).isDirectory();
136
+ const destSubPath = path.join(destination, subPath);
137
+ const existsDestSubPath = existsSync(destSubPath);
138
+ if (isSubPathAFolder && !existsDestSubPath) {
139
+ await mkdir(destSubPath);
140
+ }
141
+ await linkRecursive(sourceSubpath, destSubPath, options);
142
+ }));
143
+ return;
144
+ }
145
+ return link(source, destination);
146
+ };
147
+
148
+ const copy = promisify(ncp);
149
+ let copyOrLink = copy;
150
+ const expandExtensions = (options) => {
151
+ const expandedExtensions = options.extensions
152
+ .map((extension) => extensionDict[extension])
153
+ .map((extDescriptor) => [extDescriptor.extends, extDescriptor.value].filter(isDefined))
154
+ .flat()
155
+ // this reduce just removes duplications
156
+ .reduce((exts, ext) => (exts.includes(ext) ? exts : [...exts, ext]), []);
157
+ return expandedExtensions;
158
+ };
159
+ const isTemplateRegex = /([^\/\\]*?)\.template\./;
160
+ const isPackageJsonRegex = /package\.json/;
161
+ const isYarnLockRegex = /yarn\.lock/;
162
+ const isNextGeneratedRegex = /packages\/nextjs\/generated/;
163
+ const isConfigRegex = /([^\/\\]*?)\\config\.json/;
164
+ const isArgsRegex = /([^\/\\]*?)\.args\./;
165
+ const isExtensionFolderRegex = /extensions$/;
166
+ const isPackagesFolderRegex = /packages$/;
167
+ const copyBaseFiles = async ({ dev: isDev }, basePath, targetDir) => {
168
+ await copyOrLink(basePath, targetDir, {
169
+ clobber: false,
170
+ filter: (fileName) => {
171
+ const isTemplate = isTemplateRegex.test(fileName);
172
+ const isPackageJson = isPackageJsonRegex.test(fileName);
173
+ const isYarnLock = isYarnLockRegex.test(fileName);
174
+ const isNextGenerated = isNextGeneratedRegex.test(fileName);
175
+ const skipAlways = isTemplate || isPackageJson;
176
+ const skipDevOnly = isYarnLock || isNextGenerated;
177
+ const shouldSkip = skipAlways || (isDev && skipDevOnly);
178
+ return !shouldSkip;
179
+ },
180
+ });
181
+ const basePackageJsonPaths = findFilesRecursiveSync(basePath, path => isPackageJsonRegex.test(path));
182
+ basePackageJsonPaths.forEach(packageJsonPath => {
183
+ const partialPath = packageJsonPath.split(basePath)[1];
184
+ mergePackageJson(path.join(targetDir, partialPath), path.join(basePath, partialPath), isDev);
185
+ });
186
+ if (isDev) {
187
+ const baseYarnLockPaths = findFilesRecursiveSync(basePath, path => isYarnLockRegex.test(path));
188
+ baseYarnLockPaths.forEach(yarnLockPath => {
189
+ const partialPath = yarnLockPath.split(basePath)[1];
190
+ copy(path.join(basePath, partialPath), path.join(targetDir, partialPath));
191
+ });
192
+ const nextGeneratedPaths = findFilesRecursiveSync(basePath, path => isNextGeneratedRegex.test(path));
193
+ nextGeneratedPaths.forEach(nextGeneratedPath => {
194
+ const partialPath = nextGeneratedPath.split(basePath)[1];
195
+ copy(path.join(basePath, partialPath), path.join(targetDir, partialPath));
196
+ });
197
+ }
198
+ };
199
+ const copyExtensionsFiles = async ({ extensions, dev: isDev }, targetDir) => {
200
+ await Promise.all(extensions.map(async (extension) => {
201
+ const extensionPath = extensionDict[extension].path;
202
+ // copy (or link if dev) root files
203
+ await copyOrLink(extensionPath, path.join(targetDir), {
204
+ clobber: false,
205
+ filter: (path) => {
206
+ const isConfig = isConfigRegex.test(path);
207
+ const isArgs = isArgsRegex.test(path);
208
+ const isExtensionFolder = isExtensionFolderRegex.test(path) && fs.lstatSync(path).isDirectory();
209
+ const isPackagesFolder = isPackagesFolderRegex.test(path) && fs.lstatSync(path).isDirectory();
210
+ const isTemplate = isTemplateRegex.test(path);
211
+ // PR NOTE: this wasn't needed before because ncp had the clobber: false
212
+ const isPackageJson = isPackageJsonRegex.test(path);
213
+ const shouldSkip = isConfig ||
214
+ isArgs ||
215
+ isTemplate ||
216
+ isPackageJson ||
217
+ isExtensionFolder ||
218
+ isPackagesFolder;
219
+ return !shouldSkip;
220
+ },
221
+ });
222
+ // merge root package.json
223
+ mergePackageJson(path.join(targetDir, "package.json"), path.join(extensionPath, "package.json"), isDev);
224
+ const extensionPackagesPath = path.join(extensionPath, "packages");
225
+ const hasPackages = fs.existsSync(extensionPackagesPath);
226
+ if (hasPackages) {
227
+ // copy extension packages files
228
+ await copyOrLink(extensionPackagesPath, path.join(targetDir, "packages"), {
229
+ clobber: false,
230
+ filter: (path) => {
231
+ const isArgs = isArgsRegex.test(path);
232
+ const isTemplate = isTemplateRegex.test(path);
233
+ const isPackageJson = isPackageJsonRegex.test(path);
234
+ const shouldSkip = isArgs || isTemplate || isPackageJson;
235
+ return !shouldSkip;
236
+ },
237
+ });
238
+ // copy each package's package.json
239
+ const extensionPackages = fs.readdirSync(extensionPackagesPath);
240
+ extensionPackages.forEach((packageName) => {
241
+ mergePackageJson(path.join(targetDir, "packages", packageName, "package.json"), path.join(extensionPath, "packages", packageName, "package.json"), isDev);
242
+ });
243
+ }
244
+ }));
245
+ };
246
+ const processTemplatedFiles = async ({ extensions, dev: isDev }, basePath, targetDir) => {
247
+ const baseTemplatedFileDescriptors = findFilesRecursiveSync(basePath, (path) => isTemplateRegex.test(path)).map((baseTemplatePath) => ({
248
+ path: baseTemplatePath,
249
+ fileUrl: url.pathToFileURL(baseTemplatePath).href,
250
+ relativePath: baseTemplatePath.split(basePath)[1],
251
+ source: "base",
252
+ }));
253
+ const extensionsTemplatedFileDescriptors = extensions
254
+ .map((ext) => findFilesRecursiveSync(extensionDict[ext].path, (filePath) => isTemplateRegex.test(filePath)).map((extensionTemplatePath) => ({
255
+ path: extensionTemplatePath,
256
+ fileUrl: url.pathToFileURL(extensionTemplatePath).href,
257
+ relativePath: extensionTemplatePath.split(extensionDict[ext].path)[1],
258
+ source: `extension ${extensionDict[ext].name}`,
259
+ })))
260
+ .flat();
261
+ await Promise.all([
262
+ ...baseTemplatedFileDescriptors,
263
+ ...extensionsTemplatedFileDescriptors,
264
+ ].map(async (templateFileDescriptor) => {
265
+ const templateTargetName = templateFileDescriptor.path.match(isTemplateRegex)?.[1];
266
+ const argsPath = templateFileDescriptor.relativePath.replace(isTemplateRegex, `${templateTargetName}.args.`);
267
+ const argsFileUrls = extensions
268
+ .map((extension) => {
269
+ const argsFilePath = path.join(extensionDict[extension].path, argsPath);
270
+ const fileExists = fs.existsSync(argsFilePath);
271
+ if (!fileExists) {
272
+ return [];
273
+ }
274
+ return url.pathToFileURL(argsFilePath).href;
275
+ })
276
+ .flat();
277
+ const args = await Promise.all(argsFileUrls.map(async (argsFileUrl) => await import(argsFileUrl)));
278
+ const template = (await import(templateFileDescriptor.fileUrl)).default;
279
+ if (!template) {
280
+ throw new Error(`Template ${templateTargetName} from ${templateFileDescriptor.source} doesn't have a default export`);
281
+ }
282
+ if (typeof template !== "function") {
283
+ throw new Error(`Template ${templateTargetName} from ${templateFileDescriptor.source} is not exporting a function by default`);
284
+ }
285
+ const freshArgs = Object.fromEntries(Object.keys(args[0] ?? {}).map((key) => [
286
+ key,
287
+ [], // INFO: initial value for the freshArgs object
288
+ ]));
289
+ const combinedArgs = args.reduce((accumulated, arg) => {
290
+ Object.entries(arg).map(([key, value]) => {
291
+ accumulated[key].push(value);
292
+ });
293
+ return accumulated;
294
+ }, freshArgs);
295
+ // TODO test: if first arg file found only uses 1 name, I think the rest are not used?
296
+ const output = template(combinedArgs);
297
+ const targetPath = path.join(targetDir, templateFileDescriptor.relativePath.split(templateTargetName)[0], templateTargetName);
298
+ fs.writeFileSync(targetPath, output);
299
+ if (isDev) {
300
+ const hasCombinedArgs = Object.keys(combinedArgs).length > 0;
301
+ const hasArgsPaths = argsFileUrls.length > 0;
302
+ const devOutput = `--- TEMPLATE FILE
303
+ templates/${templateFileDescriptor.source}${templateFileDescriptor.relativePath}
304
+
305
+
306
+ --- ARGS FILES
307
+ ${hasArgsPaths
308
+ ? argsFileUrls.map(url => `\t- ${path.join('templates', url.split('templates')[1])}`).join('\n')
309
+ : '(no args files writing to the template)'}
310
+
311
+
312
+ --- RESULTING ARGS
313
+ ${hasCombinedArgs
314
+ ? Object.entries(combinedArgs)
315
+ .map(([argName, argValue]) => `\t- ${argName}:\t[${argValue.join(',')}]`)
316
+ // TODO improvement: figure out how to add the values added by each args file
317
+ .join('\n')
318
+ : '(no args sent for the template)'}
319
+ `;
320
+ fs.writeFileSync(`${targetPath}.dev`, devOutput);
321
+ }
322
+ }));
323
+ };
324
+ async function copyTemplateFiles(options, templateDir, targetDir) {
325
+ copyOrLink = options.dev ? linkRecursive : copy;
326
+ const basePath = path.join(templateDir, baseDir);
327
+ // 1. Copy base template to target directory
328
+ await copyBaseFiles(options, basePath, targetDir);
329
+ // 2. Add "parent" extensions (set via config.json#extend field)
330
+ const expandedExtension = expandExtensions(options);
331
+ options.extensions = expandedExtension;
332
+ // 3. Copy extensions folders
333
+ await copyExtensionsFiles(options, targetDir);
334
+ // 4. Process templated files and generate output
335
+ await processTemplatedFiles(options, basePath, targetDir);
336
+ // 5. Initialize git repo to avoid husky error
337
+ await execa("git", ["init"], { cwd: targetDir });
338
+ await execa("git", ["checkout", "-b", "main"], { cwd: targetDir });
339
+ }
340
+
341
+ async function createProjectDirectory(projectName) {
342
+ try {
343
+ const result = await execa("mkdir", [projectName]);
344
+ if (result.failed) {
345
+ throw new Error("There was a problem running the mkdir command");
346
+ }
347
+ }
348
+ catch (error) {
349
+ throw new Error("Failed to create directory", { cause: error });
350
+ }
351
+ return true;
352
+ }
353
+
354
+ function installPackages(targetDir) {
355
+ return projectInstall({
356
+ cwd: targetDir,
357
+ prefer: "yarn",
358
+ });
359
+ }
360
+
361
+ // Checkout the latest release tag in a git submodule
362
+ async function checkoutLatestTag(submodulePath) {
363
+ try {
364
+ const { stdout } = await execa("git", ["tag", "-l", "--sort=-v:refname"], {
365
+ cwd: submodulePath,
366
+ });
367
+ const tagLines = stdout.split("\n");
368
+ if (tagLines.length > 0) {
369
+ const latestTag = tagLines[0];
370
+ await execa("git", ["-C", `${submodulePath}`, "checkout", latestTag]);
371
+ }
372
+ else {
373
+ throw new Error(`No tags found in submodule at ${submodulePath}`);
374
+ }
375
+ }
376
+ catch (error) {
377
+ console.error("Error checking out latest tag:", error);
378
+ throw error;
379
+ }
380
+ }
381
+ async function createFirstGitCommit(targetDir, options) {
382
+ try {
383
+ // TODO: Move the logic for adding submodules to tempaltes
384
+ if (options.extensions?.includes("foundry")) {
385
+ const foundryWorkSpacePath = path.resolve(targetDir, "packages", "foundry");
386
+ await execa("git", [
387
+ "submodule",
388
+ "add",
389
+ "https://github.com/foundry-rs/forge-std",
390
+ "lib/forge-std",
391
+ ], {
392
+ cwd: foundryWorkSpacePath,
393
+ });
394
+ await execa("git", [
395
+ "submodule",
396
+ "add",
397
+ "https://github.com/OpenZeppelin/openzeppelin-contracts",
398
+ "lib/openzeppelin-contracts",
399
+ ], {
400
+ cwd: foundryWorkSpacePath,
401
+ });
402
+ await execa("git", [
403
+ "submodule",
404
+ "add",
405
+ "https://github.com/gnsps/solidity-bytes-utils",
406
+ "lib/solidity-bytes-utils",
407
+ ], {
408
+ cwd: foundryWorkSpacePath,
409
+ });
410
+ await execa("git", ["submodule", "update", "--init", "--recursive"], {
411
+ cwd: foundryWorkSpacePath,
412
+ });
413
+ await checkoutLatestTag(path.resolve(foundryWorkSpacePath, "lib", "forge-std"));
414
+ await checkoutLatestTag(path.resolve(foundryWorkSpacePath, "lib", "openzeppelin-contracts"));
415
+ }
416
+ await execa("git", ["add", "-A"], { cwd: targetDir });
417
+ await execa("git", ["commit", "-m", "Initial commit with 🏗️ Scaffold-Stylus", "--no-verify"], { cwd: targetDir });
418
+ // Update the submodule, since we have checked out the latest tag in the previous step of foundry
419
+ if (options.extensions?.includes("foundry")) {
420
+ await execa("git", ["submodule", "update", "--init", "--recursive"], {
421
+ cwd: path.resolve(targetDir, "packages", "foundry"),
422
+ });
423
+ }
424
+ }
425
+ catch (e) {
426
+ // cast error as ExecaError to get stderr
427
+ throw new Error("Failed to initialize git repository", {
428
+ cause: e?.stderr ?? e,
429
+ });
430
+ }
431
+ }
432
+
433
+ // TODO: Instead of using execa, use prettier package from cli to format targetDir
434
+ async function prettierFormat(targetDir) {
435
+ try {
436
+ const result = await execa("yarn", ["format"], { cwd: targetDir });
437
+ if (result.failed) {
438
+ throw new Error("There was a problem running the format command");
439
+ }
440
+ }
441
+ catch (error) {
442
+ throw new Error("Failed to create directory", { cause: error });
443
+ }
444
+ return true;
445
+ }
446
+
447
+ async function renderOutroMessage(options) {
448
+ let message = `
449
+ \n
450
+ ${chalk.bold.green("Congratulations!")} Your project has been scaffolded! 🎉
451
+
452
+ ${chalk.bold("Next steps:")}
453
+
454
+ ${chalk.dim("cd")} ${options.project}
455
+ `;
456
+ if (options.extensions.includes("hardhat") ||
457
+ options.extensions.includes("foundry")) {
458
+ message += `
459
+ \t${chalk.bold("Start the local development node")}
460
+ \t${chalk.dim("yarn")} chain
461
+ `;
462
+ if (options.extensions.includes("foundry")) {
463
+ try {
464
+ await execa("foundryup", ["-h"]);
465
+ }
466
+ catch (error) {
467
+ message += `
468
+ \t${chalk.bold.yellow("(NOTE: Foundryup is not installed in your system)")}
469
+ \t${chalk.dim("Checkout: https://getfoundry.sh")}
470
+ `;
471
+ }
472
+ }
473
+ message += `
474
+ \t${chalk.bold("In a new terminal window, deploy your contracts")}
475
+ \t${chalk.dim("yarn")} deploy
476
+ `;
477
+ }
478
+ message += `
479
+ \t${chalk.bold("In a new terminal window, start the frontend")}
480
+ \t${chalk.dim("yarn")} start
481
+ `;
482
+ message += `
483
+ ${chalk.bold.green("Thanks for using Scaffold-Stylus 🙏, Happy Building!")}
484
+ `;
485
+ console.log(message);
486
+ }
487
+
488
+ async function createProject(options) {
489
+ console.log(`\n`);
490
+ const currentFileUrl = import.meta.url;
491
+ const templateDirectory = path.resolve(decodeURI(fileURLToPath(currentFileUrl)), "../../templates");
492
+ const targetDirectory = path.resolve(process.cwd(), options.project);
493
+ const tasks = new Listr([
494
+ {
495
+ title: `📁 Create project directory ${targetDirectory}`,
496
+ task: () => createProjectDirectory(options.project),
497
+ },
498
+ {
499
+ title: `🚀 Creating a new Scaffold-Stylus app in ${chalk.green.bold(options.project)}`,
500
+ task: () => copyTemplateFiles(options, templateDirectory, targetDirectory),
501
+ },
502
+ {
503
+ title: `📦 Installing dependencies with yarn, this could take a while`,
504
+ task: () => installPackages(targetDirectory),
505
+ skip: () => {
506
+ if (!options.install) {
507
+ return "Manually skipped";
508
+ }
509
+ },
510
+ },
511
+ {
512
+ title: "🪄 Formatting files with prettier",
513
+ task: () => prettierFormat(targetDirectory),
514
+ skip: () => {
515
+ if (!options.install) {
516
+ return "Skipping because prettier install was skipped";
517
+ }
518
+ },
519
+ },
520
+ {
521
+ title: `📡 Initializing Git repository ${options.extensions.includes("foundry") ? "and submodules" : ""}`,
522
+ task: () => createFirstGitCommit(targetDirectory, options),
523
+ },
524
+ ]);
525
+ try {
526
+ await tasks.run();
527
+ renderOutroMessage(options);
528
+ }
529
+ catch (error) {
530
+ console.log("%s Error occurred", chalk.red.bold("ERROR"), error);
531
+ console.log("%s Exiting...", chalk.red.bold("Uh oh! 😕 Sorry about that!"));
532
+ }
533
+ }
534
+
535
+ // TODO update smartContractFramework code with general extensions
536
+ function parseArgumentsIntoOptions(rawArgs) {
537
+ const args = arg({
538
+ "--install": Boolean,
539
+ "-i": "--install",
540
+ "--skip-install": Boolean,
541
+ "--skip": "--skip-install",
542
+ "-s": "--skip-install",
543
+ "--dev": Boolean,
544
+ }, {
545
+ argv: rawArgs.slice(2).map((a) => a.toLowerCase()),
546
+ });
547
+ const install = args["--install"] ?? null;
548
+ const skipInstall = args["--skip-install"] ?? null;
549
+ const hasInstallRelatedFlag = install || skipInstall;
550
+ const dev = args["--dev"] ?? false; // info: use false avoid asking user
551
+ const project = args._[0] ?? null;
552
+ return {
553
+ project,
554
+ install: hasInstallRelatedFlag ? install || !skipInstall : null,
555
+ dev,
556
+ extensions: null, // TODO add extensions flags
557
+ };
558
+ }
559
+
560
+ const config = {
561
+ questions: [
562
+ typedQuestion({
563
+ type: "single-select",
564
+ name: "solidity-framework",
565
+ message: "What solidity framework do you want to use?",
566
+ extensions: ["hardhat", "foundry", null],
567
+ default: "hardhat",
568
+ }),
569
+ ],
570
+ };
571
+
572
+ // default values for unspecified args
573
+ const defaultOptions = {
574
+ project: "my-dapp-example",
575
+ install: true,
576
+ dev: false,
577
+ extensions: [],
578
+ };
579
+ const invalidQuestionNames = ["project", "install"];
580
+ const nullExtensionChoice = {
581
+ name: 'None',
582
+ value: null
583
+ };
584
+ async function promptForMissingOptions(options) {
585
+ const cliAnswers = Object.fromEntries(Object.entries(options).filter(([key, value]) => value !== null));
586
+ const questions = [];
587
+ questions.push({
588
+ type: "input",
589
+ name: "project",
590
+ message: "Your project name:",
591
+ default: defaultOptions.project,
592
+ validate: (value) => value.length > 0,
593
+ });
594
+ const recurringAddFollowUps = (extensions, relatedQuestion) => {
595
+ extensions.filter(extensionWithSubextensions).forEach((ext) => {
596
+ const nestedExtensions = ext.extensions.map((nestedExt) => extensionDict[nestedExt]);
597
+ questions.push({
598
+ // INFO: assuming nested extensions are all optional. To change this,
599
+ // update ExtensionDescriptor adding type, and update code here.
600
+ type: "checkbox",
601
+ name: `${ext.value}-extensions`,
602
+ message: `Select optional extensions for ${ext.name}`,
603
+ choices: nestedExtensions,
604
+ when: (answers) => {
605
+ const relatedResponse = answers[relatedQuestion];
606
+ const wasMultiselectResponse = Array.isArray(relatedResponse);
607
+ return wasMultiselectResponse
608
+ ? relatedResponse.includes(ext.value)
609
+ : relatedResponse === ext.value;
610
+ },
611
+ });
612
+ recurringAddFollowUps(nestedExtensions, `${ext.value}-extensions`);
613
+ });
614
+ };
615
+ config.questions.forEach((question) => {
616
+ if (invalidQuestionNames.includes(question.name)) {
617
+ throw new Error(`The name of the question can't be "${question.name}". The invalid names are: ${invalidQuestionNames
618
+ .map((w) => `"${w}"`)
619
+ .join(", ")}`);
620
+ }
621
+ const extensions = question.extensions
622
+ .filter(isExtension)
623
+ .map((ext) => extensionDict[ext])
624
+ .filter(isDefined);
625
+ const hasNoneOption = question.extensions.includes(null);
626
+ questions.push({
627
+ type: question.type === "multi-select" ? "checkbox" : "list",
628
+ name: question.name,
629
+ message: question.message,
630
+ choices: hasNoneOption ? [...extensions, nullExtensionChoice] : extensions,
631
+ });
632
+ recurringAddFollowUps(extensions, question.name);
633
+ });
634
+ questions.push({
635
+ type: "confirm",
636
+ name: "install",
637
+ message: "Install packages?",
638
+ default: defaultOptions.install,
639
+ });
640
+ const answers = await inquirer.prompt(questions, cliAnswers);
641
+ const mergedOptions = {
642
+ project: options.project ?? answers.project,
643
+ install: options.install ?? answers.install,
644
+ dev: options.dev ?? defaultOptions.dev,
645
+ extensions: [],
646
+ };
647
+ config.questions.forEach((question) => {
648
+ const { name } = question;
649
+ const choice = [answers[name]].flat().filter(isDefined);
650
+ mergedOptions.extensions.push(...choice);
651
+ });
652
+ const recurringAddNestedExtensions = (baseExtensions) => {
653
+ baseExtensions.forEach((extValue) => {
654
+ const nestedExtKey = `${extValue}-extensions`;
655
+ const nestedExtensions = answers[nestedExtKey];
656
+ if (nestedExtensions) {
657
+ mergedOptions.extensions.push(...nestedExtensions);
658
+ recurringAddNestedExtensions(nestedExtensions);
659
+ }
660
+ });
661
+ };
662
+ recurringAddNestedExtensions(mergedOptions.extensions);
663
+ return mergedOptions;
664
+ }
665
+
666
+ const TITLE_TEXT = `
667
+ ${chalk.bold.blue("+-+-+-+-+-+-+-+-+-+-+-+-+-+-+")}
668
+ ${chalk.bold.blue("| Create Scaffold-Stylus app |")}
669
+ ${chalk.bold.blue("+-+-+-+-+-+-+-+-+-+-+-+-+-+-+")}
670
+ `;
671
+ function renderIntroMessage() {
672
+ console.log(TITLE_TEXT);
673
+ }
674
+
675
+ async function cli(args) {
676
+ renderIntroMessage();
677
+ const rawOptions = parseArgumentsIntoOptions(args);
678
+ const options = await promptForMissingOptions(rawOptions);
679
+ await createProject(options);
680
+ }
681
+
682
+ export { cli };
683
+ //# sourceMappingURL=cli.js.map