create-stylus 1.1.0

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 (222) 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/.yarnrc.yml +1 -0
  5. package/CONTRIBUTING.md +42 -0
  6. package/README.md +66 -0
  7. package/bin/create-dapp-ss.js +4 -0
  8. package/dist/cli.js +656 -0
  9. package/dist/cli.js.map +1 -0
  10. package/package.json +46 -0
  11. package/rollup.config.js +22 -0
  12. package/src/cli.ts +14 -0
  13. package/src/extensions.json +14 -0
  14. package/src/main.ts +70 -0
  15. package/src/tasks/copy-extension-file.ts +227 -0
  16. package/src/tasks/copy-template-files.ts +252 -0
  17. package/src/tasks/create-first-git-commit.ts +35 -0
  18. package/src/tasks/create-project-directory.ts +34 -0
  19. package/src/tasks/index.ts +5 -0
  20. package/src/tasks/install-packages.ts +15 -0
  21. package/src/tasks/prettier-format.ts +17 -0
  22. package/src/types.ts +31 -0
  23. package/src/utils/consts.ts +1 -0
  24. package/src/utils/find-files-recursively.ts +19 -0
  25. package/src/utils/link.ts +44 -0
  26. package/src/utils/load-extensions.ts +10 -0
  27. package/src/utils/merge-package-json.ts +33 -0
  28. package/src/utils/parse-arguments-into-options.ts +38 -0
  29. package/src/utils/prompt-for-missing-options.ts +53 -0
  30. package/src/utils/render-intro-message.ts +11 -0
  31. package/src/utils/render-outro-message.ts +34 -0
  32. package/templates/base/.github/ISSUE_TEMPLATE/bug_report.yml +58 -0
  33. package/templates/base/.github/ISSUE_TEMPLATE/config.yml +8 -0
  34. package/templates/base/.github/pull_request_template.md +16 -0
  35. package/templates/base/.github/workflows/lint.yaml +300 -0
  36. package/templates/base/.gitignore.template.mjs +19 -0
  37. package/templates/base/.gitmodules +0 -0
  38. package/templates/base/.husky/pre-commit +4 -0
  39. package/templates/base/.lintstagedrc.js +21 -0
  40. package/templates/base/.vscode/settings.json +7 -0
  41. package/templates/base/.yarn/plugins/@yarnpkg/plugin-typescript.cjs +9 -0
  42. package/templates/base/.yarn/releases/yarn-3.2.3.cjs +783 -0
  43. package/templates/base/.yarnrc.yml +11 -0
  44. package/templates/base/CONTRIBUTING.md +86 -0
  45. package/templates/base/LICENCE +21 -0
  46. package/templates/base/nitro-devnode/LICENSE +201 -0
  47. package/templates/base/nitro-devnode/README.md +70 -0
  48. package/templates/base/nitro-devnode/run-dev-node.sh +132 -0
  49. package/templates/base/nitro-devnode/start-chain-with-cors.sh +150 -0
  50. package/templates/base/nitro-devnode/stylus-deployer-bytecode.txt +1 -0
  51. package/templates/base/nitro-devnode/stylus-dev/Dockerfile +10 -0
  52. package/templates/base/package.json +43 -0
  53. package/templates/base/packages/nextjs/.env.example +13 -0
  54. package/templates/base/packages/nextjs/.eslintignore +11 -0
  55. package/templates/base/packages/nextjs/.eslintrc.json +15 -0
  56. package/templates/base/packages/nextjs/.gitignore.template.mjs +42 -0
  57. package/templates/base/packages/nextjs/.prettierrc.js +9 -0
  58. package/templates/base/packages/nextjs/.prettierrc.json +8 -0
  59. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressCodeTab.tsx +27 -0
  60. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressComponent.tsx +36 -0
  61. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressLogsTab.tsx +21 -0
  62. package/templates/base/packages/nextjs/app/blockexplorer/_components/AddressStorageTab.tsx +61 -0
  63. package/templates/base/packages/nextjs/app/blockexplorer/_components/BackButton.tsx +12 -0
  64. package/templates/base/packages/nextjs/app/blockexplorer/_components/ContractTabs.tsx +102 -0
  65. package/templates/base/packages/nextjs/app/blockexplorer/_components/PaginationButton.tsx +39 -0
  66. package/templates/base/packages/nextjs/app/blockexplorer/_components/SearchBar.tsx +49 -0
  67. package/templates/base/packages/nextjs/app/blockexplorer/_components/TransactionHash.tsx +28 -0
  68. package/templates/base/packages/nextjs/app/blockexplorer/_components/TransactionsTable.tsx +71 -0
  69. package/templates/base/packages/nextjs/app/blockexplorer/_components/index.tsx +7 -0
  70. package/templates/base/packages/nextjs/app/blockexplorer/address/[address]/page.tsx +101 -0
  71. package/templates/base/packages/nextjs/app/blockexplorer/layout.tsx +12 -0
  72. package/templates/base/packages/nextjs/app/blockexplorer/page.tsx +83 -0
  73. package/templates/base/packages/nextjs/app/blockexplorer/transaction/[txHash]/page.tsx +23 -0
  74. package/templates/base/packages/nextjs/app/blockexplorer/transaction/_components/TransactionComp.tsx +152 -0
  75. package/templates/base/packages/nextjs/app/debug/_components/DebugContracts.tsx +73 -0
  76. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractInput.tsx +84 -0
  77. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractReadMethods.tsx +43 -0
  78. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractUI.tsx +164 -0
  79. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractVariables.tsx +50 -0
  80. package/templates/base/packages/nextjs/app/debug/_components/contract/ContractWriteMethods.tsx +49 -0
  81. package/templates/base/packages/nextjs/app/debug/_components/contract/DisplayVariable.tsx +85 -0
  82. package/templates/base/packages/nextjs/app/debug/_components/contract/InheritanceTooltip.tsx +14 -0
  83. package/templates/base/packages/nextjs/app/debug/_components/contract/ReadOnlyFunctionForm.tsx +102 -0
  84. package/templates/base/packages/nextjs/app/debug/_components/contract/Tuple.tsx +44 -0
  85. package/templates/base/packages/nextjs/app/debug/_components/contract/TupleArray.tsx +142 -0
  86. package/templates/base/packages/nextjs/app/debug/_components/contract/TxReceipt.tsx +42 -0
  87. package/templates/base/packages/nextjs/app/debug/_components/contract/WriteOnlyFunctionForm.tsx +144 -0
  88. package/templates/base/packages/nextjs/app/debug/_components/contract/index.tsx +8 -0
  89. package/templates/base/packages/nextjs/app/debug/_components/contract/utilsContract.tsx +166 -0
  90. package/templates/base/packages/nextjs/app/debug/_components/contract/utilsDisplay.tsx +114 -0
  91. package/templates/base/packages/nextjs/app/debug/page.tsx +14 -0
  92. package/templates/base/packages/nextjs/app/layout.tsx +67 -0
  93. package/templates/base/packages/nextjs/app/not-found.tsx +16 -0
  94. package/templates/base/packages/nextjs/app/page.tsx +94 -0
  95. package/templates/base/packages/nextjs/components/Background.tsx +37 -0
  96. package/templates/base/packages/nextjs/components/Card.tsx +40 -0
  97. package/templates/base/packages/nextjs/components/Footer.tsx +93 -0
  98. package/templates/base/packages/nextjs/components/Header.tsx +114 -0
  99. package/templates/base/packages/nextjs/components/ScaffoldEthAppWithProviders.tsx +77 -0
  100. package/templates/base/packages/nextjs/components/SwitchTheme.tsx +41 -0
  101. package/templates/base/packages/nextjs/components/ThemeProvider.tsx +13 -0
  102. package/templates/base/packages/nextjs/components/assets/BuidlGuidlLogo.tsx +18 -0
  103. package/templates/base/packages/nextjs/components/scaffold-eth/Address/Address.tsx +187 -0
  104. package/templates/base/packages/nextjs/components/scaffold-eth/Address/AddressCopyIcon.tsx +23 -0
  105. package/templates/base/packages/nextjs/components/scaffold-eth/Address/AddressLinkWrapper.tsx +29 -0
  106. package/templates/base/packages/nextjs/components/scaffold-eth/Balance.tsx +75 -0
  107. package/templates/base/packages/nextjs/components/scaffold-eth/BlockieAvatar.tsx +17 -0
  108. package/templates/base/packages/nextjs/components/scaffold-eth/Faucet.tsx +131 -0
  109. package/templates/base/packages/nextjs/components/scaffold-eth/FaucetButton.tsx +75 -0
  110. package/templates/base/packages/nextjs/components/scaffold-eth/Input/AddressInput.tsx +120 -0
  111. package/templates/base/packages/nextjs/components/scaffold-eth/Input/Bytes32Input.tsx +31 -0
  112. package/templates/base/packages/nextjs/components/scaffold-eth/Input/BytesInput.tsx +28 -0
  113. package/templates/base/packages/nextjs/components/scaffold-eth/Input/EtherInput.tsx +128 -0
  114. package/templates/base/packages/nextjs/components/scaffold-eth/Input/InputBase.tsx +66 -0
  115. package/templates/base/packages/nextjs/components/scaffold-eth/Input/IntegerInput.tsx +63 -0
  116. package/templates/base/packages/nextjs/components/scaffold-eth/Input/index.ts +9 -0
  117. package/templates/base/packages/nextjs/components/scaffold-eth/Input/utils.ts +109 -0
  118. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressInfoDropdown.tsx +121 -0
  119. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/AddressQRCodeModal.tsx +33 -0
  120. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/BurnerWalletModal.tsx +63 -0
  121. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/NetworkOptions.tsx +48 -0
  122. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/WrongNetworkDropdown.tsx +32 -0
  123. package/templates/base/packages/nextjs/components/scaffold-eth/RainbowKitCustomConnectButton/index.tsx +89 -0
  124. package/templates/base/packages/nextjs/components/scaffold-eth/index.tsx +7 -0
  125. package/templates/base/packages/nextjs/contracts/deployedContracts.ts +9 -0
  126. package/templates/base/packages/nextjs/contracts/externalContracts.ts +16 -0
  127. package/templates/base/packages/nextjs/eslint.config.mjs +32 -0
  128. package/templates/base/packages/nextjs/hooks/scaffold-eth/index.ts +17 -0
  129. package/templates/base/packages/nextjs/hooks/scaffold-eth/useAnimationConfig.ts +20 -0
  130. package/templates/base/packages/nextjs/hooks/scaffold-eth/useContractLogs.ts +40 -0
  131. package/templates/base/packages/nextjs/hooks/scaffold-eth/useCopyToClipboard.ts +19 -0
  132. package/templates/base/packages/nextjs/hooks/scaffold-eth/useDeployedContractInfo.ts +86 -0
  133. package/templates/base/packages/nextjs/hooks/scaffold-eth/useDisplayUsdMode.ts +21 -0
  134. package/templates/base/packages/nextjs/hooks/scaffold-eth/useFetchBlocks.ts +133 -0
  135. package/templates/base/packages/nextjs/hooks/scaffold-eth/useInitializeNativeCurrencyPrice.ts +32 -0
  136. package/templates/base/packages/nextjs/hooks/scaffold-eth/useNativeCurrencyPrice.ts +34 -0
  137. package/templates/base/packages/nextjs/hooks/scaffold-eth/useNetworkColor.ts +22 -0
  138. package/templates/base/packages/nextjs/hooks/scaffold-eth/useOutsideClick.ts +23 -0
  139. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldContract.ts +65 -0
  140. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldEventHistory.ts +213 -0
  141. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldReadContract.ts +80 -0
  142. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldWatchContractEvent.ts +40 -0
  143. package/templates/base/packages/nextjs/hooks/scaffold-eth/useScaffoldWriteContract.ts +191 -0
  144. package/templates/base/packages/nextjs/hooks/scaffold-eth/useSelectedNetwork.ts +18 -0
  145. package/templates/base/packages/nextjs/hooks/scaffold-eth/useTargetNetwork.ts +23 -0
  146. package/templates/base/packages/nextjs/hooks/scaffold-eth/useTransactor.tsx +114 -0
  147. package/templates/base/packages/nextjs/hooks/scaffold-eth/useWatchBalance.ts +21 -0
  148. package/templates/base/packages/nextjs/icons/CompassIcon.tsx +39 -0
  149. package/templates/base/packages/nextjs/icons/DarkBugAntIcon.tsx +30 -0
  150. package/templates/base/packages/nextjs/icons/LightBugAntIcon.tsx +52 -0
  151. package/templates/base/packages/nextjs/next-env.d.ts +5 -0
  152. package/templates/base/packages/nextjs/next.config.js +19 -0
  153. package/templates/base/packages/nextjs/package.json +58 -0
  154. package/templates/base/packages/nextjs/postcss.config.js +6 -0
  155. package/templates/base/packages/nextjs/public/debug-image.png +0 -0
  156. package/templates/base/packages/nextjs/public/favicon.png +0 -0
  157. package/templates/base/packages/nextjs/public/logo.svg +8 -0
  158. package/templates/base/packages/nextjs/public/manifest.json +5 -0
  159. package/templates/base/packages/nextjs/public/thumbnail.jpg +0 -0
  160. package/templates/base/packages/nextjs/react-copy-to-clipboard.d.ts +44 -0
  161. package/templates/base/packages/nextjs/scaffold.config.ts +56 -0
  162. package/templates/base/packages/nextjs/services/store/store.ts +39 -0
  163. package/templates/base/packages/nextjs/services/web3/wagmiConfig.tsx +44 -0
  164. package/templates/base/packages/nextjs/services/web3/wagmiConnectors.tsx +51 -0
  165. package/templates/base/packages/nextjs/styles/globals.css +80 -0
  166. package/templates/base/packages/nextjs/tailwind.config.js +97 -0
  167. package/templates/base/packages/nextjs/tsconfig.json +28 -0
  168. package/templates/base/packages/nextjs/types/abitype/abi.d.ts +16 -0
  169. package/templates/base/packages/nextjs/types/utils.ts +3 -0
  170. package/templates/base/packages/nextjs/utils/scaffold-eth/block.ts +17 -0
  171. package/templates/base/packages/nextjs/utils/scaffold-eth/common.ts +8 -0
  172. package/templates/base/packages/nextjs/utils/scaffold-eth/contract.ts +352 -0
  173. package/templates/base/packages/nextjs/utils/scaffold-eth/contractsData.ts +11 -0
  174. package/templates/base/packages/nextjs/utils/scaffold-eth/decodeTxData.ts +65 -0
  175. package/templates/base/packages/nextjs/utils/scaffold-eth/fetchPriceFromUniswap.ts +72 -0
  176. package/templates/base/packages/nextjs/utils/scaffold-eth/getMetadata.ts +50 -0
  177. package/templates/base/packages/nextjs/utils/scaffold-eth/getParsedError.ts +35 -0
  178. package/templates/base/packages/nextjs/utils/scaffold-eth/index.ts +6 -0
  179. package/templates/base/packages/nextjs/utils/scaffold-eth/notification.tsx +90 -0
  180. package/templates/base/packages/nextjs/utils/scaffold-stylus/burner.ts +59 -0
  181. package/templates/base/packages/nextjs/utils/scaffold-stylus/chain.ts +42 -0
  182. package/templates/base/packages/nextjs/utils/scaffold-stylus/index.ts +3 -0
  183. package/templates/base/packages/nextjs/utils/scaffold-stylus/networks.ts +94 -0
  184. package/templates/base/packages/nextjs/vercel.json +3 -0
  185. package/templates/base/packages/stylus/.env.example +13 -0
  186. package/templates/base/packages/stylus/.eslintrc.js +23 -0
  187. package/templates/base/packages/stylus/.gitignore.template.mjs +7 -0
  188. package/templates/base/packages/stylus/README.md +263 -0
  189. package/templates/base/packages/stylus/header.png +0 -0
  190. package/templates/base/packages/stylus/jest.config.js +15 -0
  191. package/templates/base/packages/stylus/package.json +49 -0
  192. package/templates/base/packages/stylus/scripts/deploy.ts +29 -0
  193. package/templates/base/packages/stylus/scripts/deploy_all_contracts.ts +59 -0
  194. package/templates/base/packages/stylus/scripts/deploy_contract.ts +93 -0
  195. package/templates/base/packages/stylus/scripts/deploy_wrapper.ts +79 -0
  196. package/templates/base/packages/stylus/scripts/export_abi.ts +87 -0
  197. package/templates/base/packages/stylus/scripts/index.ts +0 -0
  198. package/templates/base/packages/stylus/scripts/new_module.sh +35 -0
  199. package/templates/base/packages/stylus/scripts/test_network.ts +31 -0
  200. package/templates/base/packages/stylus/scripts/utils/command.ts +165 -0
  201. package/templates/base/packages/stylus/scripts/utils/contract.ts +219 -0
  202. package/templates/base/packages/stylus/scripts/utils/deployment.ts +136 -0
  203. package/templates/base/packages/stylus/scripts/utils/index.ts +6 -0
  204. package/templates/base/packages/stylus/scripts/utils/network.ts +112 -0
  205. package/templates/base/packages/stylus/scripts/utils/type.ts +48 -0
  206. package/templates/base/packages/stylus/scripts/utils.ts +3 -0
  207. package/templates/base/packages/stylus/tsconfig.json +41 -0
  208. package/templates/base/packages/stylus/your-contract/.cargo/config.toml +18 -0
  209. package/templates/base/packages/stylus/your-contract/Cargo.lock +5744 -0
  210. package/templates/base/packages/stylus/your-contract/Cargo.toml +46 -0
  211. package/templates/base/packages/stylus/your-contract/examples/counter.rs +78 -0
  212. package/templates/base/packages/stylus/your-contract/header.png +0 -0
  213. package/templates/base/packages/stylus/your-contract/licenses/Apache-2.0 +201 -0
  214. package/templates/base/packages/stylus/your-contract/licenses/COPYRIGHT.md +5 -0
  215. package/templates/base/packages/stylus/your-contract/licenses/DCO.txt +34 -0
  216. package/templates/base/packages/stylus/your-contract/licenses/MIT +21 -0
  217. package/templates/base/packages/stylus/your-contract/rust-toolchain.toml +2 -0
  218. package/templates/base/packages/stylus/your-contract/src/lib.rs +211 -0
  219. package/templates/base/packages/stylus/your-contract/src/main.rs +10 -0
  220. package/templates/base/readme.md +187 -0
  221. package/templates/base/yarn.lock +17860 -0
  222. package/tsconfig.json +13 -0
@@ -0,0 +1,252 @@
1
+ import { execa } from "execa";
2
+ import { Options, TemplateDescriptor } from "../types";
3
+ import { baseDir } from "../utils/consts";
4
+ import { findFilesRecursiveSync } from "../utils/find-files-recursively";
5
+ import { mergePackageJson } from "../utils/merge-package-json";
6
+ import fs from "fs";
7
+ import url from 'url';
8
+ import ncp from "ncp";
9
+ import path from "path";
10
+ import { promisify } from "util";
11
+ import link from "../utils/link";
12
+ import { copyExtensionFile } from "./copy-extension-file";
13
+
14
+ const copy = promisify(ncp);
15
+ let copyOrLink = copy;
16
+
17
+ const isTemplateRegex = /([^\/\\]*?)\.template\./;
18
+ const isPackageJsonRegex = /package\.json/;
19
+ const isYarnLockRegex = /yarn\.lock/;
20
+ const isNextGeneratedRegex = /packages\/nextjs\/generated/;
21
+ const isArgsRegex = /([^\/\\]*?)\.args\./;
22
+ const isGitKeepRegex = /\.gitkeep/;
23
+
24
+ // Additional files/directories to exclude from template copying
25
+ const excludePatterns = [
26
+ /\.github\//, // GitHub specific files todo: add workflows/main.yml later
27
+ /CHANGELOG\.md/, // Changelog file
28
+ /__test.*__/, // All test directories (__test__, __tests__, etc.)
29
+ ];
30
+
31
+ const copyBaseFiles = async (
32
+ { dev: isDev }: Options,
33
+ basePath: string,
34
+ targetDir: string
35
+ ) => {
36
+ await copyOrLink(basePath, targetDir, {
37
+ clobber: false,
38
+ filter: (fileName) => { // NOTE: filter IN
39
+ const isTemplate = isTemplateRegex.test(fileName);
40
+ const isPackageJson = isPackageJsonRegex.test(fileName);
41
+ const isYarnLock = isYarnLockRegex.test(fileName);
42
+ const isNextGenerated = isNextGeneratedRegex.test(fileName);
43
+ const isGitKeep = isGitKeepRegex.test(fileName);
44
+
45
+ // Check if file matches any exclude pattern
46
+ const isExcluded = excludePatterns.some(pattern => pattern.test(fileName));
47
+
48
+ const skipAlways = isPackageJson || isGitKeep || isExcluded;
49
+ const skipDevOnly = isYarnLock || isNextGenerated;
50
+ const shouldSkip = skipAlways || (isDev && skipDevOnly);
51
+
52
+ return !shouldSkip;
53
+ },
54
+ });
55
+
56
+ ["stylus", "nextjs"].forEach(packageName => {
57
+ const envExamplePath = path.join(basePath, "packages", packageName, ".env.example");
58
+ const envPath = path.join(targetDir, "packages", packageName, ".env");
59
+ if (fs.existsSync(envExamplePath)) {
60
+ copy(envExamplePath, envPath);
61
+ }
62
+ });
63
+
64
+ const basePackageJsonPaths = findFilesRecursiveSync(basePath, (path: string) => isPackageJsonRegex.test(path));
65
+
66
+ basePackageJsonPaths.forEach((packageJsonPath: string) => {
67
+ const partialPath = packageJsonPath.split(basePath)[1];
68
+ mergePackageJson(
69
+ path.join(targetDir, partialPath),
70
+ path.join(basePath, partialPath),
71
+ isDev
72
+ );
73
+ });
74
+
75
+ if (isDev) {
76
+ const baseYarnLockPaths = findFilesRecursiveSync(basePath, (path: string) => isYarnLockRegex.test(path));
77
+ baseYarnLockPaths.forEach((yarnLockPath: string) => {
78
+ const partialPath = yarnLockPath.split(basePath)[1];
79
+ copy(
80
+ path.join(basePath, partialPath),
81
+ path.join(targetDir, partialPath)
82
+ );
83
+ });
84
+
85
+ const nextGeneratedPaths = findFilesRecursiveSync(basePath, (path: string) => isNextGeneratedRegex.test(path));
86
+ nextGeneratedPaths.forEach((nextGeneratedPath: string) => {
87
+ const partialPath = nextGeneratedPath.split(basePath)[1];
88
+ copy(
89
+ path.join(basePath, partialPath),
90
+ path.join(targetDir, partialPath)
91
+ );
92
+ });
93
+ }
94
+ };
95
+
96
+
97
+
98
+ const processTemplatedFiles = async (
99
+ { dev: isDev, extension }: Options,
100
+ basePath: string,
101
+ targetDir: string
102
+ ) => {
103
+ const baseTemplatedFileDescriptors: TemplateDescriptor[] =
104
+ findFilesRecursiveSync(basePath, (path: string) => isTemplateRegex.test(path)).map(
105
+ (baseTemplatePath: string) => ({
106
+ path: baseTemplatePath,
107
+ fileUrl: url.pathToFileURL(baseTemplatePath).href,
108
+ relativePath: baseTemplatePath.split(basePath)[1],
109
+ source: "base",
110
+ })
111
+ );
112
+
113
+ await Promise.all(
114
+ baseTemplatedFileDescriptors.map(async (templateFileDescriptor) => {
115
+ const templateTargetName =
116
+ templateFileDescriptor.path.match(isTemplateRegex)?.[1]!;
117
+
118
+ const argsPath = templateFileDescriptor.relativePath.replace(
119
+ isTemplateRegex,
120
+ `${templateTargetName}.args.`
121
+ );
122
+
123
+ // Load the template
124
+ const template = (await import(templateFileDescriptor.fileUrl)).default;
125
+
126
+ if (!template) {
127
+ throw new Error(
128
+ `Template ${templateTargetName} from ${templateFileDescriptor.source} doesn't have a default export`
129
+ );
130
+ }
131
+ if (typeof template !== "function") {
132
+ throw new Error(
133
+ `Template ${templateTargetName} from ${templateFileDescriptor.source} is not exporting a function by default`
134
+ );
135
+ }
136
+
137
+ // Collect args from multiple sources
138
+ const argsFileUrls = [];
139
+
140
+ // Check for args in target directory (from extensions)
141
+ if (extension) {
142
+ const extensionArgsPath = path.join(targetDir, argsPath);
143
+ if (fs.existsSync(extensionArgsPath)) {
144
+ argsFileUrls.push(url.pathToFileURL(extensionArgsPath).href);
145
+ }
146
+ }
147
+
148
+ // Load and combine all args
149
+ const argsModules = await Promise.all(
150
+ argsFileUrls.map(async (argsFileUrl) => {
151
+ try {
152
+ return await import(argsFileUrl) as Record<string, any>;
153
+ } catch (error) {
154
+ console.warn(`Failed to load args from: ${argsFileUrl}`);
155
+ return {};
156
+ }
157
+ })
158
+ );
159
+
160
+ // Combine all args into a single object
161
+ const combinedArgs = argsModules.reduce((acc, module) => {
162
+ return { ...acc, ...module };
163
+ }, {});
164
+
165
+ // Execute template with combined args
166
+ const output = template(combinedArgs);
167
+
168
+ const targetPath = path.join(
169
+ targetDir,
170
+ templateFileDescriptor.relativePath.split(templateTargetName)[0],
171
+ templateTargetName
172
+ );
173
+ fs.writeFileSync(targetPath, output);
174
+
175
+ if (isDev) {
176
+ const devOutput = `--- TEMPLATE FILE
177
+ templates/${templateFileDescriptor.source}${templateFileDescriptor.relativePath}
178
+
179
+
180
+ --- ARGS FILES
181
+ ${argsFileUrls.length > 0
182
+ ? argsFileUrls.map(url => `\t- ${url.split("packages")[1] || url}`).join("\n")
183
+ : "(no args files writing to the template)"}
184
+
185
+
186
+ --- RESULTING ARGS
187
+ ${Object.keys(combinedArgs).length > 0
188
+ ? Object.entries(combinedArgs)
189
+ .map(([key, value]) => `\t- ${key}:\t${JSON.stringify(value)}`)
190
+ .join("\n")
191
+ : "(no args sent for the template)"}
192
+ `;
193
+ fs.writeFileSync(`${targetPath}.dev`, devOutput);
194
+ }
195
+ })
196
+ );
197
+ };
198
+
199
+ export async function copyTemplateFiles(
200
+ options: Options,
201
+ templateDir: string,
202
+ targetDir: string
203
+ ) {
204
+ copyOrLink = options.dev ? link : copy;
205
+ const basePath = path.join(templateDir, baseDir);
206
+
207
+ // 1. Copy base template to target directory
208
+ await copyBaseFiles(options, basePath, targetDir);
209
+
210
+ // 2. Copy extension files if extension is provided
211
+ if (options.extension) {
212
+ await copyExtensionFile(options.extension, targetDir);
213
+ }
214
+
215
+ // 3. Process templated files with extension args
216
+ await processTemplatedFiles(options, basePath, targetDir);
217
+
218
+ // 4. Clean up template and args files
219
+ await cleanupTemplateFiles(targetDir);
220
+
221
+ // 5. Initialize git repo to avoid husky error
222
+ await execa("git", ["init"], { cwd: targetDir });
223
+ await execa("git", ["checkout", "-b", "main"], { cwd: targetDir });
224
+ }
225
+
226
+ async function cleanupTemplateFiles(targetDir: string) {
227
+ const basePath = targetDir;
228
+
229
+ // Find all template and args files
230
+ const templateFiles = findFilesRecursiveSync(basePath, (path: string) => isTemplateRegex.test(path));
231
+ const argsFiles = findFilesRecursiveSync(basePath, (path: string) => isArgsRegex.test(path));
232
+
233
+ // Delete template files
234
+ templateFiles.forEach((templatePath) => {
235
+ try {
236
+ fs.unlinkSync(templatePath);
237
+ } catch (error) {
238
+ console.warn(`Failed to delete template file: ${templatePath}`);
239
+ }
240
+ });
241
+
242
+ // Delete args files
243
+ argsFiles.forEach((argsPath) => {
244
+ try {
245
+ fs.unlinkSync(argsPath);
246
+ } catch (error) {
247
+ console.warn(`Failed to delete args file: ${argsPath}`);
248
+ }
249
+ });
250
+ }
251
+
252
+
@@ -0,0 +1,35 @@
1
+ import { execa } from "execa";
2
+
3
+ // Checkout the latest release tag in a git submodule
4
+ async function checkoutLatestTag(submodulePath: string): Promise<void> {
5
+ try {
6
+ const { stdout } = await execa("git", ["tag", "-l", "--sort=-v:refname"], {
7
+ cwd: submodulePath,
8
+ });
9
+ const tagLines = stdout.split("\n");
10
+ if (tagLines.length > 0) {
11
+ const latestTag = tagLines[0];
12
+ await execa("git", ["-C", `${submodulePath}`, "checkout", latestTag]);
13
+ } else {
14
+ throw new Error(`No tags found in submodule at ${submodulePath}`);
15
+ }
16
+ } catch (error) {
17
+ console.error("Error checking out latest tag:", error);
18
+ throw error;
19
+ }
20
+ }
21
+
22
+ export async function createFirstGitCommit(targetDir: string) {
23
+ try {
24
+ await execa("git", ["add", "-A"], { cwd: targetDir });
25
+ await execa(
26
+ "git",
27
+ ["commit", "-m", "Initial commit with 🏗️ Scaffold-Stylus", "--no-verify"],
28
+ { cwd: targetDir },
29
+ );
30
+ } catch (e: any) {
31
+ throw new Error("Failed to initialize git repository", {
32
+ cause: e?.stderr ?? e,
33
+ });
34
+ }
35
+ }
@@ -0,0 +1,34 @@
1
+ import { execa } from "execa";
2
+ import fs from "fs";
3
+
4
+ export async function createProjectDirectory(projectName: string) {
5
+ // Check if directory already exists
6
+ if (fs.existsSync(projectName)) {
7
+ // If directory exists, check if it's empty
8
+ if (fs.readdirSync(projectName).length > 0) {
9
+ throw new Error(
10
+ `Directory ${projectName} already exists and is not empty. Cannot continue.`,
11
+ );
12
+ }
13
+ // If directory exists and is empty, we can proceed (or do nothing here, as it's already created and empty)
14
+ // For clarity, we can return true or simply let the function continue if no further action is needed.
15
+ // In this case, since the directory exists and is empty, the goal is achieved.
16
+ return true;
17
+ }
18
+
19
+ try {
20
+ const result = await execa("mkdir", [projectName]);
21
+
22
+ if (result.failed) {
23
+ throw new Error("There was a problem running the mkdir command");
24
+ }
25
+ } catch (error) {
26
+ throw new Error(
27
+ `Failed to create directory: ${
28
+ error instanceof Error ? error.message : String(error)
29
+ }`,
30
+ );
31
+ }
32
+
33
+ return true;
34
+ }
@@ -0,0 +1,5 @@
1
+ export * from "./copy-template-files";
2
+ export * from "./create-project-directory";
3
+ export * from "./install-packages";
4
+ export * from "./create-first-git-commit";
5
+ export * from "./prettier-format";
@@ -0,0 +1,15 @@
1
+ import { projectInstall } from "pkg-install";
2
+ import {execa} from "execa";
3
+ import type {Options} from "../types";
4
+ import path from "path";
5
+
6
+ export async function installPackages(targetDir: string, options: Options) {
7
+ // Condition to check if 'devnet' is included and only update submodules
8
+ // if (options.extensions?.includes("starknet-native")) {
9
+ // }
10
+
11
+ return projectInstall({
12
+ cwd: targetDir,
13
+ prefer: "yarn",
14
+ });
15
+ }
@@ -0,0 +1,17 @@
1
+ import { execa } from "execa";
2
+ import path from "path";
3
+
4
+ export async function prettierFormat(targetDir: string) {
5
+ try {
6
+ const nextjsPath = path.join(targetDir, "packages/nextjs");
7
+ const result = await execa("yarn", ["format"], { cwd: nextjsPath });
8
+
9
+ if (result.failed) {
10
+ throw new Error("There was a problem running the format command");
11
+ }
12
+ } catch (error) {
13
+ throw new Error("Failed to format Next.js project", { cause: error });
14
+ }
15
+
16
+ return true;
17
+ }
package/src/types.ts ADDED
@@ -0,0 +1,31 @@
1
+ export type Args = string[];
2
+
3
+ export type RawOptions = {
4
+ directory: string | null;
5
+ install: boolean | null;
6
+ dev: boolean;
7
+ extension: string | null;
8
+ };
9
+
10
+ type NonNullableRawOptions = {
11
+ [Prop in keyof RawOptions]: NonNullable<RawOptions[Prop]>;
12
+ };
13
+
14
+ export type Options = NonNullableRawOptions;
15
+
16
+ export const isDefined = <T>(item: T | undefined | null): item is T =>
17
+ item !== undefined && item !== null;
18
+
19
+ export type TemplateDescriptor = {
20
+ path: string;
21
+ fileUrl: string;
22
+ relativePath: string;
23
+ source: string;
24
+ };
25
+
26
+ export type Extension = {
27
+ extensionFlagValue: string;
28
+ description: string;
29
+ repository: string;
30
+ branch: string;
31
+ };
@@ -0,0 +1 @@
1
+ export const baseDir = "base";
@@ -0,0 +1,19 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+
4
+ export const findFilesRecursiveSync = (
5
+ baseDir: string,
6
+ criteriaFn: (path: string) => boolean = () => true
7
+ ): string[] => {
8
+ const subPaths = fs.readdirSync(baseDir);
9
+ const files = subPaths.map((relativePath) => {
10
+ const fullPath = path.resolve(baseDir, relativePath);
11
+ return fs.lstatSync(fullPath).isDirectory()
12
+ ? [...findFilesRecursiveSync(fullPath, criteriaFn)]
13
+ : criteriaFn(fullPath)
14
+ ? [fullPath]
15
+ : [];
16
+ });
17
+
18
+ return files.flat();
19
+ };
@@ -0,0 +1,44 @@
1
+ import type { Options } from "ncp";
2
+ import { existsSync, lstatSync, readdirSync } from "fs";
3
+ import { promises } from "fs";
4
+ import path from "path";
5
+
6
+ const { mkdir, link } = promises;
7
+ /**
8
+ * The goal is that this function has the same API as ncp, so they can be used
9
+ * interchangeably.
10
+ *
11
+ * - clobber not implemented
12
+ */
13
+ const linkRecursive = async (source: string, destination: string, options?: Options): Promise<void> => {
14
+ const passesFilter = options?.filter === undefined
15
+ ? true // no filter
16
+ : typeof options.filter === 'function'
17
+ ? options.filter(source) // filter is function
18
+ : options.filter.test(source) // filter is regex
19
+
20
+ if (!passesFilter) {
21
+ return
22
+ }
23
+
24
+ if(lstatSync(source).isDirectory()) {
25
+ const subPaths = readdirSync(source);
26
+ await Promise.all(
27
+ subPaths.map(async subPath => {
28
+ const sourceSubpath = path.join(source, subPath);
29
+ const isSubPathAFolder = lstatSync(sourceSubpath).isDirectory()
30
+ const destSubPath = path.join(destination, subPath)
31
+ const existsDestSubPath = existsSync(destSubPath)
32
+ if (isSubPathAFolder && !existsDestSubPath) {
33
+ await mkdir(destSubPath)
34
+ }
35
+ await linkRecursive(sourceSubpath, destSubPath, options)
36
+ })
37
+ )
38
+ return
39
+ }
40
+
41
+ return link(source, destination)
42
+ }
43
+
44
+ export default linkRecursive
@@ -0,0 +1,10 @@
1
+ import type { Extension } from "../types";
2
+ import extensions from "../extensions.json";
3
+
4
+ export function loadExtensions(): Extension[] {
5
+ return extensions as Extension[];
6
+ }
7
+
8
+ export function findExtensionByFlag(flagValue: string): Extension | undefined {
9
+ return extensions.find((ext: Extension) => ext.extensionFlagValue === flagValue);
10
+ }
@@ -0,0 +1,33 @@
1
+ // @ts-expect-error We don't have types for this probably add .d.ts file
2
+ import mergeJsonStr from "merge-packages";
3
+ import fs from "fs";
4
+
5
+ export function mergePackageJson(
6
+ targetPackageJsonPath: string,
7
+ secondPackageJsonPath: string,
8
+ isDev: boolean
9
+ ) {
10
+ const existsTarget = fs.existsSync(targetPackageJsonPath);
11
+ const existsSecond = fs.existsSync(secondPackageJsonPath);
12
+ if (!existsTarget && !existsSecond) {
13
+ return;
14
+ }
15
+
16
+ const targetPackageJson = existsTarget ? fs.readFileSync(targetPackageJsonPath, "utf8") : '{}';
17
+ const secondPackageJson = existsSecond ? fs.readFileSync(secondPackageJsonPath, "utf8") : '{}';
18
+
19
+ const mergedPkgStr = mergeJsonStr.default(
20
+ targetPackageJson,
21
+ secondPackageJson
22
+ );
23
+
24
+ // Parse and reformat the merged JSON to ensure proper formatting
25
+ const mergedPkgObj = JSON.parse(mergedPkgStr);
26
+ const formattedPkgStr = JSON.stringify(mergedPkgObj, null, 2) + '\n';
27
+
28
+ fs.writeFileSync(targetPackageJsonPath, formattedPkgStr, "utf8");
29
+ if (isDev) {
30
+ const devStr = `TODO: write relevant information for the contributor`
31
+ fs.writeFileSync(`${targetPackageJsonPath}.dev`, devStr, "utf8");
32
+ }
33
+ }
@@ -0,0 +1,38 @@
1
+ import type { Args, RawOptions } from "../types";
2
+ import arg from "arg";
3
+
4
+ // TODO update smartContractFramework code with general extensions
5
+ export function parseArgumentsIntoOptions(rawArgs: Args): RawOptions {
6
+ const args = arg(
7
+ {
8
+ "--skip-install": Boolean,
9
+ "-s": "--skip-install",
10
+
11
+ "--dev": Boolean,
12
+
13
+ "--dir": String,
14
+ "-d": "--dir",
15
+
16
+ "--extension": String,
17
+ "-e": "--extension",
18
+ },
19
+ {
20
+ argv: rawArgs.slice(2).map((a) => a.toLowerCase()),
21
+ },
22
+ );
23
+
24
+ const skipInstall = args["--skip-install"] ?? null;
25
+
26
+ const dev = args["--dev"] ?? false; // info: use false avoid asking user
27
+
28
+ const directory = args["--dir"] ?? null;
29
+
30
+ const extension = args["--extension"] ?? null;
31
+
32
+ return {
33
+ directory,
34
+ install: skipInstall ? false : null,
35
+ dev,
36
+ extension,
37
+ };
38
+ }
@@ -0,0 +1,53 @@
1
+ import { Options, RawOptions } from "../types";
2
+ import inquirer from "inquirer";
3
+ import { loadExtensions } from "./load-extensions";
4
+
5
+ // default values for unspecified args
6
+ const defaultOptions: RawOptions = {
7
+ directory: "./my-dapp-example",
8
+ install: true,
9
+ dev: false,
10
+ extension: null,
11
+ };
12
+
13
+ export async function promptForMissingOptions(
14
+ options: RawOptions,
15
+ ): Promise<Options> {
16
+ const cliAnswers = Object.fromEntries(
17
+ Object.entries(options).filter(([key, value]) => value !== null),
18
+ );
19
+
20
+ const extensions = loadExtensions();
21
+ const extensionChoices = extensions.map(ext => ({
22
+ name: `${ext.extensionFlagValue} - ${ext.description}`,
23
+ value: ext.extensionFlagValue,
24
+ }));
25
+
26
+ const questions = [
27
+ {
28
+ type: "input",
29
+ name: "directory",
30
+ message:
31
+ "Where do you want to install the new files? Choose ./ (root folder) or provide a new folder name.",
32
+ default: defaultOptions.directory,
33
+ validate: (value: string) => value.length > 0,
34
+ },
35
+ {
36
+ type: "confirm",
37
+ name: "install",
38
+ message: "Install dependencies?",
39
+ default: defaultOptions.install,
40
+ },
41
+ ];
42
+
43
+ const answers = await inquirer.prompt(questions, cliAnswers);
44
+
45
+ const mergedOptions: Options = {
46
+ directory: options.directory ?? answers.directory,
47
+ install: options.install ?? answers.install,
48
+ dev: options.dev ?? defaultOptions.dev,
49
+ extension: options.extension ?? answers.extension,
50
+ };
51
+
52
+ return mergedOptions;
53
+ }
@@ -0,0 +1,11 @@
1
+ import chalk from "chalk";
2
+
3
+ export const TITLE_TEXT = `
4
+ ${chalk.bold.blue("+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+")}
5
+ ${chalk.bold.blue("| Create Scaffold-Stylus app |")}
6
+ ${chalk.bold.blue("+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+")}
7
+ `;
8
+
9
+ export function renderIntroMessage() {
10
+ console.log(TITLE_TEXT);
11
+ }
@@ -0,0 +1,34 @@
1
+ import type { Options } from "../types";
2
+ import chalk from "chalk";
3
+
4
+ export async function renderOutroMessage(options: Options) {
5
+ let message = `
6
+ \n
7
+ ${chalk.bold.green("Congratulations!")} Your project has been scaffolded! 🎉
8
+
9
+ ${chalk.bold("Next steps:")}
10
+
11
+ ${chalk.dim("cd")} ${options.directory}
12
+ `;
13
+
14
+ message += `
15
+ \t${chalk.bold("Start the local development node")}
16
+ \t${chalk.dim("yarn")} chain
17
+ `;
18
+
19
+ message += `
20
+ \t${chalk.bold("In a new terminal window, deploy your contracts")}
21
+ \t${chalk.dim("yarn")} deploy
22
+ `;
23
+
24
+ message += `
25
+ \t${chalk.bold("In a new terminal window, start the frontend")}
26
+ \t${chalk.dim("yarn")} start
27
+ `;
28
+
29
+ message += `
30
+ ${chalk.bold.green("Thanks for using Scaffold-Stylus 🙏, Happy Building!")}
31
+ `;
32
+
33
+ console.log(message);
34
+ }