├── .gitignore ├── LICENSE ├── README.md ├── package.json ├── packages ├── create-ipfs-app │ ├── README.md │ ├── createIpfsApp.js │ ├── index.js │ ├── package.json │ └── public │ │ ├── favicon.ico │ │ ├── logo.svg │ │ ├── logo192.png │ │ └── logo512.png └── ipfs-scripts │ ├── README.md │ ├── bin │ └── ipfs-scripts.js │ ├── package.json │ └── scripts │ ├── filebase.js │ ├── moralis.js │ ├── pinata.js │ └── web3.js └── public ├── create-ipfs-app.png ├── deployed.png ├── filebase.png ├── future.pdf ├── ipfs.svg ├── logo.png ├── moralis.png ├── pinata.svg ├── success.png └── web3.png /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | node_modules/ 4 | build/ 5 | .DS_Store 6 | *.tgz 7 | my-app* 8 | my-ipfs-app* 9 | template/src/__tests__/__snapshots__/ 10 | lerna-debug.log 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | /.changelog 15 | .npm/ 16 | package-lock.json 17 | yarn.lock -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Create IPFS App 2 | 3 | Create IPFS App 4 | 5 | IPFS 6 | 7 | Create IPFS apps with no build configuration (like create-react-app). 8 | 9 | FileBase.Com 10 | Web3.Storage 11 | Pinata.Cloud 12 | Moralis.Io 13 | 14 | Create IPFS App works on macOS, Windows, and Linux.
15 | If something doesn’t work, please [file an issue](https://github.com/alexbakers/create-ipfs-app/issues/new).
16 | If you have questions or need help, please ask in [GitHub Discussions](https://github.com/alexbakers/create-ipfs-app/discussions).
17 | If you want to watch the tutorial, go to [this video](https://vimeo.com/745362905). 18 | 19 | ## Quick Overview 20 | 21 | To create a new IPFS app, you may choose one of the following methods: 22 | 23 | ### NPX 24 | 25 | ```sh 26 | npx create-ipfs-app my-ipfs-app --web3 WEB3_STORAGE_API_TOKEN 27 | ``` 28 | 29 | ### YARN 30 | 31 | ```sh 32 | yarn create ipfs-app my-ipfs-app --moralis MORALIS_WEB3_API_KEY 33 | ``` 34 | 35 | ### NPM 36 | 37 | ```sh 38 | npm install -g create-ipfs-app 39 | create-ipfs-app my-ipfs-app --pinata PINATA_API_KEY:PINATA_API_SECRET 40 | ``` 41 | 42 | ## Params 43 | 44 | - `--web3 WEB3_STORAGE_API_TOKEN` 45 | - `--moralis MORALIS_WEB3_API_KEY` 46 | - `--pinata PINATA_API_KEY:PINATA_API_SECRET` 47 | - `--filebase FILEBASE_API_KEY:FILEBASE_API_SECRET:FILEBASE_BUCKET_NAME` 48 | 49 | Create IPFS App Success 50 | 51 | Once the installation is done, you can open your project folder: 52 | 53 | ```sh 54 | cd my-ipfs-app 55 | ``` 56 | 57 | Inside the newly created project, you can run some built-in commands: 58 | 59 | ### `npm start` or `yarn start` 60 | 61 | Runs the app in development mode.
62 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 63 | 64 | ### `npm test` or `yarn test` 65 | 66 | Runs the test watcher in an interactive mode.
67 | By default, runs tests related to files changed since the last commit. 68 | 69 | ### `npm run build` or `yarn build` 70 | 71 | Builds the app for production to the `build` folder.
72 | Your app is ready to be deployed. 73 | 74 | ### `npm run deploy:service` or `yarn deploy:service` 75 | 76 | Create IPFS App Deployed 77 | 78 | - `deploy:web3` - deploy to web3.storage 79 | - `deploy:moralis` - deploy to moralis.io 80 | - `deploy:pinata` - deploy to pinata.cloud 81 | - `deploy:filebase` - deploy to filebase.com [tutorial] 82 | 83 | In a few seconds, your application will be deployed on the decentralized network.
84 | 85 | - Open `ipfs://Q.../index.html` to view it in the Brave browser. 86 | - Open `https://dweb.link/ipfs/Q.../index.html` to view it in the ALL browsers. 87 | 88 | If you see a white screen instead of a website: 89 | 90 | - Add to package.json `"homepage": "."`; 91 | - Build the project; 92 | - Deploy it again. 93 | 94 | ## How to add a deployment script to an existing project? 95 | 96 | - Install global package `ipfs-scripts`; 97 | - Create `.env` file at the root of project: 98 | - `MORALIS="MORALIS.IO WEB3_API_KEY"` 99 | - `PINATA="PINATA.CLOUD API_KEY:API_SECRET"` 100 | - `WEB3="WEB3.STORAGE API_TOKEN"` 101 | - `FILEBASE="FILEBASE.COM API_KEY:API_SECRET:BUCKET_NAME"` 102 | - Add **scripts** to package.json: 103 | - `"deploy:moralis": "ipfs-scripts moralis"` 104 | - `"deploy:pinata": "ipfs-scripts pinata"` 105 | - `"deploy:web3": "ipfs-scripts web3"` 106 | - `"deploy:filebase": "ipfs-scripts filebase"` 107 | 108 | # TODO 109 | 110 | - [x] web3.storage 111 | - [x] moralis.io 112 | - [x] pinata.cloud 113 | - [x] filebase.com 114 | - [ ] framework agnostic (vue, svelte, ...) 115 | - [ ] auto-update CloudFlare DNS 116 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-ipfs-app-monorepo", 3 | "version": "1.1.3", 4 | "description": "Create IPFS App.", 5 | "private": true, 6 | "workspaces": [ 7 | "packages/create-ipfs-app", 8 | "packages/ipfs-scripts" 9 | ], 10 | "scripts": { 11 | "publish:create-ipfs-app": "cd packages/create-ipfs-app && npm publish", 12 | "publish:ipfs-scripts": "cd packages/ipfs-scripts && npm publish" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/create-ipfs-app/README.md: -------------------------------------------------------------------------------- 1 | # Create IPFS App 2 | 3 | Create IPFS App 4 | 5 | IPFS 6 | 7 | Create IPFS apps with no build configuration (like create-react-app). 8 | 9 | FileBase.Com 10 | Web3.Storage 11 | Pinata.Cloud 12 | Moralis.Io 13 | 14 | Create IPFS App works on macOS, Windows, and Linux.
15 | If something doesn’t work, please [file an issue](https://github.com/alexbakers/create-ipfs-app/issues/new).
16 | If you have questions or need help, please ask in [GitHub Discussions](https://github.com/alexbakers/create-ipfs-app/discussions).
17 | If you want to watch the tutorial, go to [this video](https://vimeo.com/745362905). 18 | 19 | ## Quick Overview 20 | 21 | To create a new IPFS app, you may choose one of the following methods: 22 | 23 | ### NPX 24 | 25 | ```sh 26 | npx create-ipfs-app my-ipfs-app --web3 WEB3_STORAGE_API_TOKEN 27 | ``` 28 | 29 | ### YARN 30 | 31 | ```sh 32 | yarn create ipfs-app my-ipfs-app --moralis MORALIS_WEB3_API_KEY 33 | ``` 34 | 35 | ### NPM 36 | 37 | ```sh 38 | npm install -g create-ipfs-app 39 | create-ipfs-app my-ipfs-app --pinata PINATA_API_KEY:PINATA_API_SECRET 40 | ``` 41 | 42 | ## Params 43 | 44 | - `--web3 WEB3_STORAGE_API_TOKEN` 45 | - `--moralis MORALIS_WEB3_API_KEY` 46 | - `--pinata PINATA_API_KEY:PINATA_API_SECRET` 47 | - `--filebase FILEBASE_API_KEY:FILEBASE_API_SECRET:FILEBASE_BUCKET_NAME` 48 | 49 | Create IPFS App Success 50 | 51 | Once the installation is done, you can open your project folder: 52 | 53 | ```sh 54 | cd my-ipfs-app 55 | ``` 56 | 57 | Inside the newly created project, you can run some built-in commands: 58 | 59 | ### `npm start` or `yarn start` 60 | 61 | Runs the app in development mode.
62 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 63 | 64 | ### `npm test` or `yarn test` 65 | 66 | Runs the test watcher in an interactive mode.
67 | By default, runs tests related to files changed since the last commit. 68 | 69 | ### `npm run build` or `yarn build` 70 | 71 | Builds the app for production to the `build` folder.
72 | Your app is ready to be deployed. 73 | 74 | ### `npm run deploy:service` or `yarn deploy:service` 75 | 76 | Create IPFS App Deployed 77 | 78 | - `deploy:web3` - deploy to web3.storage 79 | - `deploy:moralis` - deploy to moralis.io 80 | - `deploy:pinata` - deploy to pinata.cloud 81 | - `deploy:filebase` - deploy to filebase.com [tutorial] 82 | 83 | In a few seconds, your application will be deployed on the decentralized network.
84 | 85 | - Open `ipfs://Q.../index.html` to view it in the Brave browser. 86 | - Open `https://dweb.link/ipfs/Q.../index.html` to view it in the ALL browsers. 87 | 88 | If you see a white screen instead of a website: 89 | 90 | - Add to package.json `"homepage": "."`; 91 | - Build the project; 92 | - Deploy it again. 93 | 94 | ## How to add a deployment script to an existing project? 95 | 96 | - Install global package `ipfs-scripts`; 97 | - Create `.env` file at the root of project: 98 | - `MORALIS="MORALIS.IO WEB3_API_KEY"` 99 | - `PINATA="PINATA.CLOUD API_KEY:API_SECRET"` 100 | - `WEB3="WEB3.STORAGE API_TOKEN"` 101 | - `FILEBASE="FILEBASE.COM API_KEY:API_SECRET:BUCKET_NAME"` 102 | - Add **scripts** to package.json: 103 | - `"deploy:moralis": "ipfs-scripts moralis"` 104 | - `"deploy:pinata": "ipfs-scripts pinata"` 105 | - `"deploy:web3": "ipfs-scripts web3"` 106 | - `"deploy:filebase": "ipfs-scripts filebase"` 107 | 108 | # TODO 109 | 110 | - [x] web3.storage 111 | - [x] moralis.io 112 | - [x] pinata.cloud 113 | - [x] filebase.com 114 | - [ ] framework agnostic (vue, svelte, ...) 115 | - [ ] auto-update CloudFlare DNS 116 | -------------------------------------------------------------------------------- /packages/create-ipfs-app/createIpfsApp.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const https = require("https"); 4 | const chalk = require("chalk"); 5 | const commander = require("commander"); 6 | const dns = require("dns"); 7 | const envinfo = require("envinfo"); 8 | const execSync = require("child_process").execSync; 9 | const fs = require("fs-extra"); 10 | const hyperquest = require("hyperquest"); 11 | const prompts = require("prompts"); 12 | const os = require("os"); 13 | const path = require("path"); 14 | const semver = require("semver"); 15 | const spawn = require("cross-spawn"); 16 | const tmp = require("tmp"); 17 | const unpack = require("tar-pack").unpack; 18 | const url = require("url"); 19 | const replace = require("replace-in-file"); 20 | const gradient = require("gradient-string"); 21 | const validateProjectName = require("validate-npm-package-name"); 22 | 23 | const packageJson = require("./package.json"); 24 | 25 | function isUsingYarn() { 26 | return (process.env.npm_config_user_agent || "").indexOf("yarn") === 0; 27 | } 28 | 29 | let projectName; 30 | 31 | function init() { 32 | const program = new commander.Command(packageJson.name) 33 | .version(packageJson.version) 34 | .arguments("") 35 | .usage(`${chalk.green("")} [options]`) 36 | .action((name) => { 37 | projectName = name; 38 | }) 39 | .option("--verbose", "print additional logs") 40 | .option("--info", "print environment debug info") 41 | .option( 42 | "--template ", 43 | "specify a template for the created project" 44 | ) 45 | .option("--moralis ", "moralis.io web3 api key") 46 | .option("--pinata ", "pinata.cloud api key:secret") 47 | .option("--web3 ", "web3.storage api token") 48 | .option( 49 | "--filebase ", 50 | "filebase.com api key:secret:name" 51 | ) 52 | .option("--use-pnp") 53 | .allowUnknownOption() 54 | .on("--help", () => { 55 | logo(); 56 | }) 57 | .parse(process.argv); 58 | 59 | if (program.info) { 60 | logo(); 61 | console.log(chalk.bold("\nEnvironment Info:")); 62 | console.log( 63 | `\n current version of ${packageJson.name}: ${packageJson.version}` 64 | ); 65 | console.log(` running from ${__dirname}`); 66 | return envinfo 67 | .run( 68 | { 69 | System: ["OS", "CPU"], 70 | Binaries: ["Node", "npm", "Yarn"], 71 | Browsers: [ 72 | "Chrome", 73 | "Edge", 74 | "Internet Explorer", 75 | "Firefox", 76 | "Safari", 77 | ], 78 | npmPackages: ["react", "react-dom", "react-scripts", "ipfs-scripts"], 79 | npmGlobalPackages: ["create-ipfs-app"], 80 | }, 81 | { 82 | duplicates: true, 83 | showNotFound: true, 84 | } 85 | ) 86 | .then(console.log); 87 | } 88 | 89 | if (typeof projectName === "undefined") { 90 | console.error("Please specify the project directory:"); 91 | console.log( 92 | ` ${chalk.cyan(program.name())} ${chalk.green("")}` 93 | ); 94 | console.log(); 95 | console.log("For example:"); 96 | console.log( 97 | ` ${chalk.cyan(program.name())} ${chalk.green("my-ipfs-app")}` 98 | ); 99 | console.log(); 100 | console.log( 101 | `Run ${chalk.cyan(`${program.name()} --help`)} to see all options.` 102 | ); 103 | process.exit(1); 104 | } 105 | 106 | const ipfs = []; 107 | if (program.infura) { 108 | // ipfs.push(`INFURA="${Buffer.from(program.infura).toString("base64")}"`); 109 | } 110 | if (program.moralis) { 111 | ipfs.push(`MORALIS="${program.moralis}"`); 112 | } 113 | if (program.pinata) { 114 | ipfs.push(`PINATA="${program.pinata}"`); 115 | } 116 | if (program.web3) { 117 | ipfs.push(`WEB3="${program.web3}"`); 118 | } 119 | if (program.filebase) { 120 | ipfs.push(`FILEBASE="${program.filebase}"`); 121 | } 122 | 123 | // We first check the registry directly via the API, and if that fails, we try 124 | // the slower `npm view [package] version` command. 125 | // 126 | // This is important for users in environments where direct access to npm is 127 | // blocked by a firewall, and packages are provided exclusively via a private 128 | // registry. 129 | checkForLatestVersion() 130 | .catch(() => { 131 | try { 132 | return execSync("npm view create-ipfs-app version").toString().trim(); 133 | } catch (e) { 134 | return null; 135 | } 136 | }) 137 | .then((latest) => { 138 | if (latest && semver.lt(packageJson.version, latest)) { 139 | console.log(); 140 | console.error( 141 | chalk.yellow( 142 | `You are running \`create-ipfs-app\` ${packageJson.version}, which is behind the latest release (${latest}).\n\n` + 143 | "We recommend always using the latest version of create-ipfs-app if possible." 144 | ) 145 | ); 146 | console.log(); 147 | console.log( 148 | "The latest instructions for creating a new app can be found here:\n" + 149 | "https://create-ipfs-app.dev/" 150 | ); 151 | console.log(); 152 | } else { 153 | const useYarn = isUsingYarn(); 154 | createApp( 155 | projectName, 156 | program.verbose, 157 | program.scriptsVersion, 158 | program.template, 159 | ipfs, 160 | useYarn, 161 | program.usePnp 162 | ); 163 | } 164 | }); 165 | } 166 | 167 | function createApp(name, verbose, version, template, ipfs, useYarn, usePnp) { 168 | const unsupportedNodeVersion = !semver.satisfies( 169 | // Coerce strings with metadata (i.e. `15.0.0-nightly`). 170 | semver.coerce(process.version), 171 | ">=14" 172 | ); 173 | 174 | if (unsupportedNodeVersion) { 175 | console.log( 176 | chalk.yellow( 177 | `You are using Node ${process.version} so the project will be bootstrapped with an old unsupported version of tools.\n\n` + 178 | `Please update to Node 14 or higher for a better, fully supported experience.\n` 179 | ) 180 | ); 181 | // Fall back to latest supported react-scripts on Node 4 182 | version = "react-scripts@0.9.x"; 183 | } 184 | 185 | const root = path.resolve(name); 186 | const appName = path.basename(root); 187 | 188 | checkAppName(appName); 189 | fs.ensureDirSync(name); 190 | if (!isSafeToCreateProjectIn(root, name)) { 191 | process.exit(1); 192 | } 193 | console.log(); 194 | 195 | logo(); 196 | 197 | console.log(); 198 | console.log( 199 | gradient.pastel( 200 | `Creating a new IPFS app «${name}» with deployment on ${ipfs 201 | .map((s) => s.split("=")[0]) 202 | .join(",")}` 203 | ) 204 | ); 205 | console.log(); 206 | 207 | const packageJson = { 208 | name: appName, 209 | version: "0.1.0", 210 | private: true, 211 | }; 212 | fs.writeFileSync( 213 | path.join(root, "package.json"), 214 | JSON.stringify(packageJson, null, 2) + os.EOL 215 | ); 216 | 217 | if (ipfs.length) { 218 | fs.writeFileSync(path.join(root, ".env"), ipfs.join(os.EOL) + os.EOL); 219 | } 220 | 221 | const originalDirectory = process.cwd(); 222 | process.chdir(root); 223 | if (!useYarn && !checkThatNpmCanReadCwd()) { 224 | process.exit(1); 225 | } 226 | 227 | if (!useYarn) { 228 | const npmInfo = checkNpmVersion(); 229 | if (!npmInfo.hasMinNpm) { 230 | if (npmInfo.npmVersion) { 231 | console.log( 232 | chalk.yellow( 233 | `You are using npm ${npmInfo.npmVersion} so the project will be bootstrapped with an old unsupported version of tools.\n\n` + 234 | `Please update to npm 6 or higher for a better, fully supported experience.\n` 235 | ) 236 | ); 237 | } 238 | // Fall back to latest supported react-scripts for npm 3 239 | version = "react-scripts@0.9.x"; 240 | } 241 | } else if (usePnp) { 242 | const yarnInfo = checkYarnVersion(); 243 | if (yarnInfo.yarnVersion) { 244 | if (!yarnInfo.hasMinYarnPnp) { 245 | console.log( 246 | chalk.yellow( 247 | `You are using Yarn ${yarnInfo.yarnVersion} together with the --use-pnp flag, but Plug'n'Play is only supported starting from the 1.12 release.\n\n` + 248 | `Please update to Yarn 1.12 or higher for a better, fully supported experience.\n` 249 | ) 250 | ); 251 | // 1.11 had an issue with webpack-dev-middleware, so better not use PnP with it (never reached stable, but still) 252 | usePnp = false; 253 | } 254 | if (!yarnInfo.hasMaxYarnPnp) { 255 | console.log( 256 | chalk.yellow( 257 | "The --use-pnp flag is no longer necessary with yarn 2 and will be deprecated and removed in a future release.\n" 258 | ) 259 | ); 260 | // 2 supports PnP by default and breaks when trying to use the flag 261 | usePnp = false; 262 | } 263 | } 264 | } 265 | 266 | run( 267 | root, 268 | appName, 269 | version, 270 | verbose, 271 | originalDirectory, 272 | template, 273 | useYarn, 274 | usePnp, 275 | ipfs 276 | ); 277 | } 278 | 279 | function install(root, useYarn, usePnp, dependencies, verbose, isOnline) { 280 | return new Promise((resolve, reject) => { 281 | let command; 282 | let args; 283 | if (useYarn) { 284 | command = "yarnpkg"; 285 | args = ["add", "--exact"]; 286 | if (!isOnline) { 287 | args.push("--offline"); 288 | } 289 | if (usePnp) { 290 | args.push("--enable-pnp"); 291 | } 292 | [].push.apply(args, dependencies); 293 | 294 | // Explicitly set cwd() to work around issues like 295 | // https://github.com/facebook/create-react-app/issues/3326. 296 | // Unfortunately we can only do this for Yarn because npm support for 297 | // equivalent --prefix flag doesn't help with this issue. 298 | // This is why for npm, we run checkThatNpmCanReadCwd() early instead. 299 | args.push("--cwd"); 300 | args.push(root); 301 | 302 | if (!isOnline) { 303 | console.log(chalk.yellow("You appear to be offline.")); 304 | console.log(chalk.yellow("Falling back to the local Yarn cache.")); 305 | console.log(); 306 | } 307 | } else { 308 | command = "npm"; 309 | args = [ 310 | "install", 311 | "--no-audit", // https://github.com/facebook/create-react-app/issues/11174 312 | "--save", 313 | "--save-exact", 314 | "--loglevel", 315 | "error", 316 | ].concat(dependencies); 317 | 318 | if (usePnp) { 319 | console.log(chalk.yellow("NPM doesn't support PnP.")); 320 | console.log(chalk.yellow("Falling back to the regular installs.")); 321 | console.log(); 322 | } 323 | } 324 | 325 | if (verbose) { 326 | args.push("--verbose"); 327 | } 328 | 329 | const child = spawn(command, args, { stdio: "inherit" }); 330 | child.on("close", (code) => { 331 | if (code !== 0) { 332 | reject({ 333 | command: `${command} ${args.join(" ")}`, 334 | }); 335 | return; 336 | } 337 | resolve(); 338 | }); 339 | }); 340 | } 341 | 342 | function run( 343 | root, 344 | appName, 345 | version, 346 | verbose, 347 | originalDirectory, 348 | template, 349 | useYarn, 350 | usePnp, 351 | ipfs 352 | ) { 353 | Promise.all([ 354 | getInstallPackage(version, originalDirectory), 355 | getTemplateInstallPackage(template, originalDirectory), 356 | ]).then(([packageToInstall, templateToInstall]) => { 357 | const allDependencies = [ 358 | "react", 359 | "react-dom", 360 | packageToInstall, 361 | "ipfs-scripts", 362 | ]; 363 | 364 | console.log( 365 | gradient.atlas( 366 | "Installing packages. This might take a couple of minutes." 367 | ) 368 | ); 369 | console.log(); 370 | 371 | Promise.all([ 372 | getPackageInfo(packageToInstall), 373 | getPackageInfo(templateToInstall), 374 | ]) 375 | .then(([packageInfo, templateInfo]) => 376 | checkIfOnline(useYarn).then((isOnline) => ({ 377 | isOnline, 378 | packageInfo, 379 | templateInfo, 380 | })) 381 | ) 382 | .then(({ isOnline, packageInfo, templateInfo }) => { 383 | let packageVersion = semver.coerce(packageInfo.version); 384 | 385 | const templatesVersionMinimum = "3.3.0"; 386 | 387 | // Assume compatibility if we can't test the version. 388 | if (!semver.valid(packageVersion)) { 389 | packageVersion = templatesVersionMinimum; 390 | } 391 | 392 | // Only support templates when used alongside new react-scripts versions. 393 | const supportsTemplates = semver.gte( 394 | packageVersion, 395 | templatesVersionMinimum 396 | ); 397 | if (supportsTemplates) { 398 | allDependencies.push(templateToInstall); 399 | } else if (template) { 400 | console.log(""); 401 | console.log( 402 | `The ${chalk.cyan(packageInfo.name)} version you're using ${ 403 | packageInfo.name === "react-scripts" ? "is not" : "may not be" 404 | } compatible with the ${chalk.cyan("--template")} option.` 405 | ); 406 | console.log(""); 407 | } 408 | 409 | return install( 410 | root, 411 | useYarn, 412 | usePnp, 413 | allDependencies, 414 | verbose, 415 | isOnline 416 | ).then(() => ({ 417 | packageInfo, 418 | supportsTemplates, 419 | templateInfo, 420 | })); 421 | }) 422 | .then(async ({ packageInfo, supportsTemplates, templateInfo }) => { 423 | const packageName = packageInfo.name; 424 | const templateName = supportsTemplates ? templateInfo.name : undefined; 425 | checkNodeVersion(packageName); 426 | setCaretRangeForRuntimeDeps(packageName); 427 | 428 | const pnpPath = path.resolve(process.cwd(), ".pnp.js"); 429 | 430 | const nodeArgs = fs.existsSync(pnpPath) ? ["--require", pnpPath] : []; 431 | 432 | await executeNodeScript( 433 | { 434 | cwd: process.cwd(), 435 | args: nodeArgs, 436 | }, 437 | [root, appName, verbose, originalDirectory, templateName], 438 | ` 439 | const init = require('${packageName}/scripts/init.js'); 440 | init.apply(null, JSON.parse(process.argv[1])); 441 | ` 442 | ); 443 | 444 | /* 445 | Added script deploy:moralis 446 | Added script deploy:pinata 447 | Added script deploy:web3 448 | Added script deploy:filebase 449 | Changed IPFS index page 450 | */ 451 | setTimeout(function () { 452 | replace.sync({ 453 | files: path.join(process.cwd(), "package.json"), 454 | from: '"scripts": {', 455 | to: 456 | '"scripts": {' + 457 | '\n "predeploy": "npm run build",' + 458 | '\n "deploy:moralis": "ipfs-scripts moralis",' + 459 | '\n "deploy:pinata": "ipfs-scripts pinata",' + 460 | '\n "deploy:web3": "ipfs-scripts web3",' + 461 | '\n "deploy:filebase": "ipfs-scripts filebase",', 462 | }); 463 | replace.sync({ 464 | files: path.join(process.cwd(), "src", "App.css"), 465 | from: "background-color: #282c34;", 466 | to: "background: linear-gradient(to bottom, #041727 0, #062b3f 100%);", 467 | }); 468 | replace.sync({ 469 | files: [ 470 | path.join(process.cwd(), "src", "App.js"), 471 | path.join(process.cwd(), "src", "App.test.js"), 472 | path.join(process.cwd(), "src", "App.ts"), 473 | path.join(process.cwd(), "src", "App.test.ts"), 474 | ], 475 | from: ["reactjs.org", "Learn React", /learn react/g], 476 | to: ["ipfs.tech", "Learn IPFS", "learn ipfs"], 477 | }); 478 | replace.sync({ 479 | files: path.join(process.cwd(), "public", "index.html"), 480 | from: ["create-react-app", "React App"], 481 | to: ["create-ipfs-app", "IPFS App"], 482 | }); 483 | fs.copyFileSync( 484 | path.join(__dirname, "public", "logo.svg"), 485 | path.join(process.cwd(), "src", "logo.svg") 486 | ); 487 | fs.copyFileSync( 488 | path.join(__dirname, "public", "logo192.png"), 489 | path.join(process.cwd(), "public", "logo192.png") 490 | ); 491 | fs.copyFileSync( 492 | path.join(__dirname, "public", "logo512.png"), 493 | path.join(process.cwd(), "public", "logo512.png") 494 | ); 495 | fs.copyFileSync( 496 | path.join(__dirname, "public", "favicon.ico"), 497 | path.join(process.cwd(), "public", "favicon.ico") 498 | ); 499 | execSync("git add -A", { stdio: "ignore" }); 500 | execSync('git commit -m "Initialize project using Create IPFS App"', { 501 | stdio: "ignore", 502 | }); 503 | 504 | // Change displayed command to yarn instead of yarnpkg 505 | const displayedCommand = useYarn ? "yarn" : "npm"; 506 | 507 | console.log( 508 | gradient.rainbow.multiline(" ___ _ _ ___ ___ ___ ___ ___ ") 509 | ); 510 | console.log( 511 | gradient.rainbow.multiline("/ __| | | |/ __/ __/ _ \\/ __/ __|") 512 | ); 513 | console.log( 514 | gradient.rainbow.multiline("\\__ \\ |_| | (_| (_| __/\\__ \\__ \\") 515 | ); 516 | console.log( 517 | gradient.rainbow.multiline("|___/\\__,_|\\___\\___\\___||___/___/") 518 | ); 519 | console.log(); 520 | console.log(gradient.cristal(`${process.cwd()}`)); 521 | console.log(); 522 | console.log( 523 | gradient.teen( 524 | "Inside that directory, you can run several commands:" 525 | ) 526 | ); 527 | console.log(); 528 | console.log(gradient.morning(` ${displayedCommand} start`)); 529 | console.log(); 530 | console.log(gradient.morning(` ${displayedCommand} test`)); 531 | console.log(); 532 | console.log( 533 | gradient.morning( 534 | ` ${displayedCommand} ${useYarn ? "" : "run "}build` 535 | ) 536 | ); 537 | console.log(); 538 | console.log( 539 | gradient.morning( 540 | ` ${displayedCommand} ${useYarn ? "" : "run "}eject` 541 | ) 542 | ); 543 | console.log(); 544 | if (ipfs.filter((s) => !!(s.indexOf("WEB3=") + 1)).length) { 545 | console.log( 546 | gradient.morning( 547 | ` ${displayedCommand} ${useYarn ? "" : "run "}deploy:web3` 548 | ) 549 | ); 550 | console.log(); 551 | } 552 | if (ipfs.filter((s) => !!(s.indexOf("MORALIS=") + 1)).length) { 553 | console.log( 554 | gradient.morning( 555 | ` ${displayedCommand} ${useYarn ? "" : "run "}deploy:moralis` 556 | ) 557 | ); 558 | console.log(); 559 | } 560 | if (ipfs.filter((s) => !!(s.indexOf("PINATA=") + 1)).length) { 561 | console.log( 562 | gradient.morning( 563 | ` ${displayedCommand} ${useYarn ? "" : "run "}deploy:pinata` 564 | ) 565 | ); 566 | console.log(); 567 | } 568 | if (ipfs.filter((s) => !!(s.indexOf("FILEBASE=") + 1)).length) { 569 | console.log( 570 | gradient.morning( 571 | ` ${displayedCommand} ${useYarn ? "" : "run "}deploy:filebase` 572 | ) 573 | ); 574 | console.log(); 575 | } 576 | console.log(gradient.cristal("We suggest that you begin by typing:")); 577 | console.log(); 578 | console.log(gradient.summer(" cd " + appName)); 579 | console.log(` ${gradient.summer(`${displayedCommand} start`)}`); 580 | console.log(); 581 | console.log(gradient.cristal("Let's build real decentralization!")); 582 | console.log(); 583 | }, 1000); 584 | /* 585 | End 586 | */ 587 | 588 | if (version === "react-scripts@0.9.x") { 589 | console.log( 590 | chalk.yellow( 591 | `\nNote: the project was bootstrapped with an old unsupported version of tools.\n` + 592 | `Please update to Node >=14 and npm >=6 to get supported tools in new projects.\n` 593 | ) 594 | ); 595 | } 596 | }) 597 | .catch((reason) => { 598 | console.log(); 599 | console.log("Aborting installation."); 600 | if (reason.command) { 601 | console.log(` ${chalk.cyan(reason.command)} has failed.`); 602 | } else { 603 | console.log( 604 | chalk.red("Unexpected error. Please report it as a bug:") 605 | ); 606 | console.log(reason); 607 | } 608 | console.log(); 609 | 610 | // On 'exit' we will delete these files from target directory. 611 | const knownGeneratedFiles = ["package.json", "node_modules"]; 612 | const currentFiles = fs.readdirSync(path.join(root)); 613 | currentFiles.forEach((file) => { 614 | knownGeneratedFiles.forEach((fileToMatch) => { 615 | // This removes all knownGeneratedFiles. 616 | if (file === fileToMatch) { 617 | console.log(`Deleting generated file... ${chalk.cyan(file)}`); 618 | fs.removeSync(path.join(root, file)); 619 | } 620 | }); 621 | }); 622 | const remainingFiles = fs.readdirSync(path.join(root)); 623 | if (!remainingFiles.length) { 624 | // Delete target folder if empty 625 | console.log( 626 | `Deleting ${chalk.cyan(`${appName}/`)} from ${chalk.cyan( 627 | path.resolve(root, "..") 628 | )}` 629 | ); 630 | process.chdir(path.resolve(root, "..")); 631 | fs.removeSync(path.join(root)); 632 | } 633 | console.log("Done."); 634 | process.exit(1); 635 | }); 636 | }); 637 | } 638 | 639 | function getInstallPackage(version, originalDirectory) { 640 | let packageToInstall = "react-scripts"; 641 | const validSemver = semver.valid(version); 642 | if (validSemver) { 643 | packageToInstall += `@${validSemver}`; 644 | } else if (version) { 645 | if (version[0] === "@" && !version.includes("/")) { 646 | packageToInstall += version; 647 | } else if (version.match(/^file:/)) { 648 | packageToInstall = `file:${path.resolve( 649 | originalDirectory, 650 | version.match(/^file:(.*)?$/)[1] 651 | )}`; 652 | } else { 653 | // for tar.gz or alternative paths 654 | packageToInstall = version; 655 | } 656 | } 657 | 658 | const scriptsToWarn = [ 659 | { 660 | name: "react-scripts-ts", 661 | message: chalk.yellow( 662 | `The react-scripts-ts package is deprecated. TypeScript is now supported natively in Create React App. You can use the ${chalk.green( 663 | "--template typescript" 664 | )} option instead when generating your app to include TypeScript support. Would you like to continue using react-scripts-ts?` 665 | ), 666 | }, 667 | ]; 668 | 669 | for (const script of scriptsToWarn) { 670 | if (packageToInstall.startsWith(script.name)) { 671 | return prompts({ 672 | type: "confirm", 673 | name: "useScript", 674 | message: script.message, 675 | initial: false, 676 | }).then((answer) => { 677 | if (!answer.useScript) { 678 | process.exit(0); 679 | } 680 | 681 | return packageToInstall; 682 | }); 683 | } 684 | } 685 | 686 | return Promise.resolve(packageToInstall); 687 | } 688 | 689 | function getTemplateInstallPackage(template, originalDirectory) { 690 | let templateToInstall = "cra-template"; 691 | if (template) { 692 | if (template.match(/^file:/)) { 693 | templateToInstall = `file:${path.resolve( 694 | originalDirectory, 695 | template.match(/^file:(.*)?$/)[1] 696 | )}`; 697 | } else if ( 698 | template.includes("://") || 699 | template.match(/^.+\.(tgz|tar\.gz)$/) 700 | ) { 701 | // for tar.gz or alternative paths 702 | templateToInstall = template; 703 | } else { 704 | // Add prefix 'cra-template-' to non-prefixed templates, leaving any 705 | // @scope/ and @version intact. 706 | const packageMatch = template.match(/^(@[^/]+\/)?([^@]+)?(@.+)?$/); 707 | const scope = packageMatch[1] || ""; 708 | const templateName = packageMatch[2] || ""; 709 | const version = packageMatch[3] || ""; 710 | 711 | if ( 712 | templateName === templateToInstall || 713 | templateName.startsWith(`${templateToInstall}-`) 714 | ) { 715 | // Covers: 716 | // - cra-template 717 | // - @SCOPE/cra-template 718 | // - cra-template-NAME 719 | // - @SCOPE/cra-template-NAME 720 | templateToInstall = `${scope}${templateName}${version}`; 721 | } else if (version && !scope && !templateName) { 722 | // Covers using @SCOPE only 723 | templateToInstall = `${version}/${templateToInstall}`; 724 | } else { 725 | // Covers templates without the `cra-template` prefix: 726 | // - NAME 727 | // - @SCOPE/NAME 728 | templateToInstall = `${scope}${templateToInstall}-${templateName}${version}`; 729 | } 730 | } 731 | } 732 | 733 | return Promise.resolve(templateToInstall); 734 | } 735 | 736 | function getTemporaryDirectory() { 737 | return new Promise((resolve, reject) => { 738 | // Unsafe cleanup lets us recursively delete the directory if it contains 739 | // contents; by default it only allows removal if it's empty 740 | tmp.dir({ unsafeCleanup: true }, (err, tmpdir, callback) => { 741 | if (err) { 742 | reject(err); 743 | } else { 744 | resolve({ 745 | tmpdir: tmpdir, 746 | cleanup: () => { 747 | try { 748 | callback(); 749 | } catch (ignored) { 750 | // Callback might throw and fail, since it's a temp directory the 751 | // OS will clean it up eventually... 752 | } 753 | }, 754 | }); 755 | } 756 | }); 757 | }); 758 | } 759 | 760 | function extractStream(stream, dest) { 761 | return new Promise((resolve, reject) => { 762 | stream.pipe( 763 | unpack(dest, (err) => { 764 | if (err) { 765 | reject(err); 766 | } else { 767 | resolve(dest); 768 | } 769 | }) 770 | ); 771 | }); 772 | } 773 | 774 | // Extract package name from tarball url or path. 775 | function getPackageInfo(installPackage) { 776 | if (installPackage.match(/^.+\.(tgz|tar\.gz)$/)) { 777 | return getTemporaryDirectory() 778 | .then((obj) => { 779 | let stream; 780 | if (/^http/.test(installPackage)) { 781 | stream = hyperquest(installPackage); 782 | } else { 783 | stream = fs.createReadStream(installPackage); 784 | } 785 | return extractStream(stream, obj.tmpdir).then(() => obj); 786 | }) 787 | .then((obj) => { 788 | const { name, version } = require(path.join( 789 | obj.tmpdir, 790 | "package.json" 791 | )); 792 | obj.cleanup(); 793 | return { name, version }; 794 | }) 795 | .catch((err) => { 796 | // The package name could be with or without semver version, e.g. react-scripts-0.2.0-alpha.1.tgz 797 | // However, this function returns package name only without semver version. 798 | console.log( 799 | `Could not extract the package name from the archive: ${err.message}` 800 | ); 801 | const assumedProjectName = installPackage.match( 802 | /^.+\/(.+?)(?:-\d+.+)?\.(tgz|tar\.gz)$/ 803 | )[1]; 804 | console.log( 805 | `Based on the filename, assuming it is "${chalk.cyan( 806 | assumedProjectName 807 | )}"` 808 | ); 809 | return Promise.resolve({ name: assumedProjectName }); 810 | }); 811 | } else if (installPackage.startsWith("git+")) { 812 | // Pull package name out of git urls e.g: 813 | // git+https://github.com/mycompany/react-scripts.git 814 | // git+ssh://github.com/mycompany/react-scripts.git#v1.2.3 815 | return Promise.resolve({ 816 | name: installPackage.match(/([^/]+)\.git(#.*)?$/)[1], 817 | }); 818 | } else if (installPackage.match(/.+@/)) { 819 | // Do not match @scope/ when stripping off @version or @tag 820 | return Promise.resolve({ 821 | name: installPackage.charAt(0) + installPackage.substr(1).split("@")[0], 822 | version: installPackage.split("@")[1], 823 | }); 824 | } else if (installPackage.match(/^file:/)) { 825 | const installPackagePath = installPackage.match(/^file:(.*)?$/)[1]; 826 | const { name, version } = require(path.join( 827 | installPackagePath, 828 | "package.json" 829 | )); 830 | return Promise.resolve({ name, version }); 831 | } 832 | return Promise.resolve({ name: installPackage }); 833 | } 834 | 835 | function checkNpmVersion() { 836 | let hasMinNpm = false; 837 | let npmVersion = null; 838 | try { 839 | npmVersion = execSync("npm --version").toString().trim(); 840 | hasMinNpm = semver.gte(npmVersion, "6.0.0"); 841 | } catch (err) { 842 | // ignore 843 | } 844 | return { 845 | hasMinNpm: hasMinNpm, 846 | npmVersion: npmVersion, 847 | }; 848 | } 849 | 850 | function checkYarnVersion() { 851 | const minYarnPnp = "1.12.0"; 852 | const maxYarnPnp = "2.0.0"; 853 | let hasMinYarnPnp = false; 854 | let hasMaxYarnPnp = false; 855 | let yarnVersion = null; 856 | try { 857 | yarnVersion = execSync("yarnpkg --version").toString().trim(); 858 | if (semver.valid(yarnVersion)) { 859 | hasMinYarnPnp = semver.gte(yarnVersion, minYarnPnp); 860 | hasMaxYarnPnp = semver.lt(yarnVersion, maxYarnPnp); 861 | } else { 862 | // Handle non-semver compliant yarn version strings, which yarn currently 863 | // uses for nightly builds. The regex truncates anything after the first 864 | // dash. See #5362. 865 | const trimmedYarnVersionMatch = /^(.+?)[-+].+$/.exec(yarnVersion); 866 | if (trimmedYarnVersionMatch) { 867 | const trimmedYarnVersion = trimmedYarnVersionMatch.pop(); 868 | hasMinYarnPnp = semver.gte(trimmedYarnVersion, minYarnPnp); 869 | hasMaxYarnPnp = semver.lt(trimmedYarnVersion, maxYarnPnp); 870 | } 871 | } 872 | } catch (err) { 873 | // ignore 874 | } 875 | return { 876 | hasMinYarnPnp: hasMinYarnPnp, 877 | hasMaxYarnPnp: hasMaxYarnPnp, 878 | yarnVersion: yarnVersion, 879 | }; 880 | } 881 | 882 | function checkNodeVersion(packageName) { 883 | const packageJsonPath = path.resolve( 884 | process.cwd(), 885 | "node_modules", 886 | packageName, 887 | "package.json" 888 | ); 889 | 890 | if (!fs.existsSync(packageJsonPath)) { 891 | return; 892 | } 893 | 894 | const packageJson = require(packageJsonPath); 895 | if (!packageJson.engines || !packageJson.engines.node) { 896 | return; 897 | } 898 | 899 | if (!semver.satisfies(process.version, packageJson.engines.node)) { 900 | console.error( 901 | chalk.red( 902 | "You are running Node %s.\n" + 903 | "Create IPFS App requires Node %s or higher. \n" + 904 | "Please update your version of Node." 905 | ), 906 | process.version, 907 | packageJson.engines.node 908 | ); 909 | process.exit(1); 910 | } 911 | } 912 | 913 | function checkAppName(appName) { 914 | const validationResult = validateProjectName(appName); 915 | if (!validationResult.validForNewPackages) { 916 | console.error( 917 | chalk.red( 918 | `Cannot create a project named ${chalk.green( 919 | `"${appName}"` 920 | )} because of npm naming restrictions:\n` 921 | ) 922 | ); 923 | [ 924 | ...(validationResult.errors || []), 925 | ...(validationResult.warnings || []), 926 | ].forEach((error) => { 927 | console.error(chalk.red(` * ${error}`)); 928 | }); 929 | console.error(chalk.red("\nPlease choose a different project name.")); 930 | process.exit(1); 931 | } 932 | 933 | // TODO: there should be a single place that holds the dependencies 934 | const dependencies = [ 935 | "react", 936 | "react-dom", 937 | "react-scripts", 938 | "ipfs-scripts", 939 | ].sort(); 940 | if (dependencies.includes(appName)) { 941 | console.error( 942 | chalk.red( 943 | `Cannot create a project named ${chalk.green( 944 | `"${appName}"` 945 | )} because a dependency with the same name exists.\n` + 946 | `Due to the way npm works, the following names are not allowed:\n\n` 947 | ) + 948 | chalk.cyan(dependencies.map((depName) => ` ${depName}`).join("\n")) + 949 | chalk.red("\n\nPlease choose a different project name.") 950 | ); 951 | process.exit(1); 952 | } 953 | } 954 | 955 | function makeCaretRange(dependencies, name) { 956 | const version = dependencies[name]; 957 | 958 | if (typeof version === "undefined") { 959 | console.error(chalk.red(`Missing ${name} dependency in package.json`)); 960 | process.exit(1); 961 | } 962 | 963 | let patchedVersion = `^${version}`; 964 | 965 | if (!semver.validRange(patchedVersion)) { 966 | console.error( 967 | `Unable to patch ${name} dependency version because version ${chalk.red( 968 | version 969 | )} will become invalid ${chalk.red(patchedVersion)}` 970 | ); 971 | patchedVersion = version; 972 | } 973 | 974 | dependencies[name] = patchedVersion; 975 | } 976 | 977 | function setCaretRangeForRuntimeDeps(packageName) { 978 | const packagePath = path.join(process.cwd(), "package.json"); 979 | const packageJson = require(packagePath); 980 | 981 | if (typeof packageJson.dependencies === "undefined") { 982 | console.error(chalk.red("Missing dependencies in package.json")); 983 | process.exit(1); 984 | } 985 | 986 | const packageVersion = packageJson.dependencies[packageName]; 987 | if (typeof packageVersion === "undefined") { 988 | console.error(chalk.red(`Unable to find ${packageName} in package.json`)); 989 | process.exit(1); 990 | } 991 | 992 | makeCaretRange(packageJson.dependencies, "react"); 993 | makeCaretRange(packageJson.dependencies, "react-dom"); 994 | 995 | fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2) + os.EOL); 996 | } 997 | 998 | // If project only contains files generated by GH, it’s safe. 999 | // Also, if project contains remnant error logs from a previous 1000 | // installation, lets remove them now. 1001 | // We also special case IJ-based products .idea because it integrates with CRA: 1002 | // https://github.com/facebook/create-react-app/pull/368#issuecomment-243446094 1003 | function isSafeToCreateProjectIn(root, name) { 1004 | const validFiles = [ 1005 | ".DS_Store", 1006 | ".git", 1007 | ".gitattributes", 1008 | ".gitignore", 1009 | ".gitlab-ci.yml", 1010 | ".hg", 1011 | ".hgcheck", 1012 | ".hgignore", 1013 | ".idea", 1014 | ".npmignore", 1015 | ".travis.yml", 1016 | "docs", 1017 | "LICENSE", 1018 | "README.md", 1019 | "mkdocs.yml", 1020 | "Thumbs.db", 1021 | ]; 1022 | // These files should be allowed to remain on a failed install, but then 1023 | // silently removed during the next create. 1024 | const errorLogFilePatterns = [ 1025 | "npm-debug.log", 1026 | "yarn-error.log", 1027 | "yarn-debug.log", 1028 | ]; 1029 | const isErrorLog = (file) => { 1030 | return errorLogFilePatterns.some((pattern) => file.startsWith(pattern)); 1031 | }; 1032 | 1033 | const conflicts = fs 1034 | .readdirSync(root) 1035 | .filter((file) => !validFiles.includes(file)) 1036 | // IntelliJ IDEA creates module files before CRA is launched 1037 | .filter((file) => !/\.iml$/.test(file)) 1038 | // Don't treat log files from previous installation as conflicts 1039 | .filter((file) => !isErrorLog(file)); 1040 | 1041 | if (conflicts.length > 0) { 1042 | console.log( 1043 | `The directory ${chalk.green(name)} contains files that could conflict:` 1044 | ); 1045 | console.log(); 1046 | for (const file of conflicts) { 1047 | try { 1048 | const stats = fs.lstatSync(path.join(root, file)); 1049 | if (stats.isDirectory()) { 1050 | console.log(` ${chalk.blue(`${file}/`)}`); 1051 | } else { 1052 | console.log(` ${file}`); 1053 | } 1054 | } catch (e) { 1055 | console.log(` ${file}`); 1056 | } 1057 | } 1058 | console.log(); 1059 | console.log( 1060 | "Either try using a new directory name, or remove the files listed above." 1061 | ); 1062 | 1063 | return false; 1064 | } 1065 | 1066 | // Remove any log files from a previous installation. 1067 | fs.readdirSync(root).forEach((file) => { 1068 | if (isErrorLog(file)) { 1069 | fs.removeSync(path.join(root, file)); 1070 | } 1071 | }); 1072 | return true; 1073 | } 1074 | 1075 | function getProxy() { 1076 | if (process.env.https_proxy) { 1077 | return process.env.https_proxy; 1078 | } else { 1079 | try { 1080 | // Trying to read https-proxy from .npmrc 1081 | let httpsProxy = execSync("npm config get https-proxy").toString().trim(); 1082 | return httpsProxy !== "null" ? httpsProxy : undefined; 1083 | } catch (e) { 1084 | return; 1085 | } 1086 | } 1087 | } 1088 | 1089 | // See https://github.com/facebook/create-react-app/pull/3355 1090 | function checkThatNpmCanReadCwd() { 1091 | const cwd = process.cwd(); 1092 | let childOutput = null; 1093 | try { 1094 | // Note: intentionally using spawn over exec since 1095 | // the problem doesn't reproduce otherwise. 1096 | // `npm config list` is the only reliable way I could find 1097 | // to reproduce the wrong path. Just printing process.cwd() 1098 | // in a Node process was not enough. 1099 | childOutput = spawn.sync("npm", ["config", "list"]).output.join(""); 1100 | } catch (err) { 1101 | // Something went wrong spawning node. 1102 | // Not great, but it means we can't do this check. 1103 | // We might fail later on, but let's continue. 1104 | return true; 1105 | } 1106 | if (typeof childOutput !== "string") { 1107 | return true; 1108 | } 1109 | const lines = childOutput.split("\n"); 1110 | // `npm config list` output includes the following line: 1111 | // "; cwd = C:\path\to\current\dir" (unquoted) 1112 | // I couldn't find an easier way to get it. 1113 | const prefix = "; cwd = "; 1114 | const line = lines.find((line) => line.startsWith(prefix)); 1115 | if (typeof line !== "string") { 1116 | // Fail gracefully. They could remove it. 1117 | return true; 1118 | } 1119 | const npmCWD = line.substring(prefix.length); 1120 | if (npmCWD === cwd) { 1121 | return true; 1122 | } 1123 | console.error( 1124 | chalk.red( 1125 | `Could not start an npm process in the right directory.\n\n` + 1126 | `The current directory is: ${chalk.bold(cwd)}\n` + 1127 | `However, a newly started npm process runs in: ${chalk.bold( 1128 | npmCWD 1129 | )}\n\n` + 1130 | `This is probably caused by a misconfigured system terminal shell.` 1131 | ) 1132 | ); 1133 | if (process.platform === "win32") { 1134 | console.error( 1135 | chalk.red(`On Windows, this can usually be fixed by running:\n\n`) + 1136 | ` ${chalk.cyan( 1137 | "reg" 1138 | )} delete "HKCU\\Software\\Microsoft\\Command Processor" /v AutoRun /f\n` + 1139 | ` ${chalk.cyan( 1140 | "reg" 1141 | )} delete "HKLM\\Software\\Microsoft\\Command Processor" /v AutoRun /f\n\n` + 1142 | chalk.red(`Try to run the above two lines in the terminal.\n`) + 1143 | chalk.red( 1144 | `To learn more about this problem, read: https://blogs.msdn.microsoft.com/oldnewthing/20071121-00/?p=24433/` 1145 | ) 1146 | ); 1147 | } 1148 | return false; 1149 | } 1150 | 1151 | function checkIfOnline(useYarn) { 1152 | if (!useYarn) { 1153 | // Don't ping the Yarn registry. 1154 | // We'll just assume the best case. 1155 | return Promise.resolve(true); 1156 | } 1157 | 1158 | return new Promise((resolve) => { 1159 | dns.lookup("registry.yarnpkg.com", (err) => { 1160 | let proxy; 1161 | if (err != null && (proxy = getProxy())) { 1162 | // If a proxy is defined, we likely can't resolve external hostnames. 1163 | // Try to resolve the proxy name as an indication of a connection. 1164 | dns.lookup(url.parse(proxy).hostname, (proxyErr) => { 1165 | resolve(proxyErr == null); 1166 | }); 1167 | } else { 1168 | resolve(err == null); 1169 | } 1170 | }); 1171 | }); 1172 | } 1173 | 1174 | function executeNodeScript({ cwd, args }, data, source) { 1175 | return new Promise((resolve, reject) => { 1176 | const child = spawn( 1177 | process.execPath, 1178 | [...args, "-e", source, "--", JSON.stringify(data)], 1179 | { cwd, stdio: "ignore" } 1180 | ); 1181 | 1182 | child.on("close", (code) => { 1183 | if (code !== 0) { 1184 | reject({ 1185 | command: `node ${args.join(" ")}`, 1186 | }); 1187 | return; 1188 | } 1189 | resolve(); 1190 | }); 1191 | }); 1192 | } 1193 | 1194 | function checkForLatestVersion() { 1195 | return new Promise((resolve, reject) => { 1196 | https 1197 | .get( 1198 | "https://registry.npmjs.org/-/package/create-ipfs-app/dist-tags", 1199 | (res) => { 1200 | if (res.statusCode === 200) { 1201 | let body = ""; 1202 | res.on("data", (data) => (body += data)); 1203 | res.on("end", () => { 1204 | resolve(JSON.parse(body).latest); 1205 | }); 1206 | } else { 1207 | reject(); 1208 | } 1209 | } 1210 | ) 1211 | .on("error", () => { 1212 | reject(); 1213 | }); 1214 | }); 1215 | } 1216 | 1217 | function logo() { 1218 | console.log( 1219 | gradient.rainbow.multiline( 1220 | ` _ _ __ ` 1221 | ) 1222 | ); 1223 | console.log( 1224 | gradient.rainbow.multiline( 1225 | ` ___ _ __ ___ __ _| |_ ___ (_)_ __ / _|___ __ _ _ __ _ __ ` 1226 | ) 1227 | ); 1228 | console.log( 1229 | gradient.rainbow.multiline( 1230 | " / __| '__/ _ \\/ _` | __/ _ \\___| | '_ \\| |_/ __|___ / _` | '_ \\| '_ \\ " 1231 | ) 1232 | ); 1233 | console.log( 1234 | gradient.rainbow.multiline( 1235 | `| (__| | | __/ (_| | || __/___| | |_) | _\\__ \\___| (_| | |_) | |_) |` 1236 | ) 1237 | ); 1238 | console.log( 1239 | gradient.rainbow.multiline( 1240 | ` \\___|_| \\___|\\__,_|\\__\\___| |_| .__/|_| |___/ \\__,_| .__/| .__/` 1241 | ) 1242 | ); 1243 | console.log( 1244 | gradient.rainbow.multiline( 1245 | ` |_| |_| |_| ` 1246 | ) 1247 | ); 1248 | } 1249 | 1250 | module.exports = { 1251 | init, 1252 | getTemplateInstallPackage, 1253 | }; 1254 | -------------------------------------------------------------------------------- /packages/create-ipfs-app/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | 4 | const currentNodeVersion = process.versions.node; 5 | const semver = currentNodeVersion.split("."); 6 | const major = semver[0]; 7 | 8 | if (major < 14) { 9 | console.error( 10 | "You are running Node " + 11 | currentNodeVersion + 12 | ".\n" + 13 | "Create IPFS App requires Node 14 or higher. \n" + 14 | "Please update your version of Node." 15 | ); 16 | process.exit(1); 17 | } 18 | 19 | const { init } = require("./createIpfsApp"); 20 | 21 | init(); 22 | -------------------------------------------------------------------------------- /packages/create-ipfs-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-ipfs-app", 3 | "version": "1.1.3", 4 | "keywords": [ 5 | "react", 6 | "ipfs", 7 | "moralis", 8 | "web3", 9 | "infura", 10 | "pinata", 11 | "filebase" 12 | ], 13 | "description": "Create IPFS apps with no build configuration.", 14 | "repository": { 15 | "type": "git", 16 | "url": "https://github.com/alexbakers/create-ipfs-app", 17 | "directory": "packages/create-ipfs-app" 18 | }, 19 | "license": "MIT", 20 | "engines": { 21 | "node": ">=14" 22 | }, 23 | "bugs": { 24 | "url": "https://github.com/alexbakers/create-ipfs-app/issues" 25 | }, 26 | "files": [ 27 | "public/*", 28 | "index.js", 29 | "createIpfsApp.js" 30 | ], 31 | "bin": { 32 | "create-ipfs-app": "./index.js" 33 | }, 34 | "scripts": { 35 | "test": "cross-env FORCE_COLOR=true jest" 36 | }, 37 | "dependencies": { 38 | "chalk": "^4.1.2", 39 | "commander": "^4.1.1", 40 | "cross-spawn": "^7.0.3", 41 | "envinfo": "^7.8.1", 42 | "fs-extra": "^10.0.0", 43 | "gradient-string": "^2.0.1", 44 | "hyperquest": "^2.1.3", 45 | "prompts": "^2.4.2", 46 | "replace-in-file": "^6.3.5", 47 | "semver": "^7.3.5", 48 | "tar-pack": "^3.4.1", 49 | "tmp": "^0.2.1", 50 | "validate-npm-package-name": "^3.0.0" 51 | }, 52 | "devDependencies": { 53 | "cross-env": "^7.0.3", 54 | "jest": "^27.4.3" 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /packages/create-ipfs-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbakers/create-ipfs-app/b350a78d9ca65e0e48154de4bc185ae51ed6935a/packages/create-ipfs-app/public/favicon.ico -------------------------------------------------------------------------------- /packages/create-ipfs-app/public/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packages/create-ipfs-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbakers/create-ipfs-app/b350a78d9ca65e0e48154de4bc185ae51ed6935a/packages/create-ipfs-app/public/logo192.png -------------------------------------------------------------------------------- /packages/create-ipfs-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbakers/create-ipfs-app/b350a78d9ca65e0e48154de4bc185ae51ed6935a/packages/create-ipfs-app/public/logo512.png -------------------------------------------------------------------------------- /packages/ipfs-scripts/README.md: -------------------------------------------------------------------------------- 1 | # IPFS Scripts 2 | 3 | See monorepo create-ipfs-app. 4 | -------------------------------------------------------------------------------- /packages/ipfs-scripts/bin/ipfs-scripts.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | "use strict"; 3 | 4 | // Makes the script crash on unhandled rejections instead of silently 5 | // ignoring them. In the future, promise rejections that are not handled will 6 | // terminate the Node.js process with a non-zero exit code. 7 | process.on("unhandledRejection", (err) => { 8 | throw err; 9 | }); 10 | 11 | const spawn = require("react-dev-utils/crossSpawn"); 12 | const args = process.argv.slice(2); 13 | 14 | const scriptIndex = args.findIndex( 15 | (x) => x === "filebase" || x === "moralis" || x === "pinata" || x === "web3" 16 | ); 17 | const script = scriptIndex === -1 ? args[0] : args[scriptIndex]; 18 | const nodeArgs = scriptIndex > 0 ? args.slice(0, scriptIndex) : []; 19 | 20 | if (["filebase", "moralis", "pinata", "web3"].includes(script)) { 21 | const result = spawn.sync( 22 | process.execPath, 23 | nodeArgs 24 | .concat(require.resolve("../scripts/" + script)) 25 | .concat(args.slice(scriptIndex + 1)), 26 | { stdio: "inherit" } 27 | ); 28 | if (result.signal) { 29 | if (result.signal === "SIGKILL") { 30 | console.log( 31 | "The build failed because the process exited too early. " + 32 | "This probably means the system ran out of memory or someone called " + 33 | "`kill -9` on the process." 34 | ); 35 | } else if (result.signal === "SIGTERM") { 36 | console.log( 37 | "The build failed because the process exited too early. " + 38 | "Someone might have called `kill` or `killall`, or the system could " + 39 | "be shutting down." 40 | ); 41 | } 42 | process.exit(1); 43 | } 44 | process.exit(result.status); 45 | } else { 46 | console.log('Unknown script "' + script + '".'); 47 | console.log("Perhaps you need to update ipfs-scripts?"); 48 | } 49 | -------------------------------------------------------------------------------- /packages/ipfs-scripts/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ipfs-scripts", 3 | "version": "1.1.3", 4 | "description": "Deploy scripts for Create IPFS App.", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/alexbakers/create-ipfs-app", 8 | "directory": "packages/ipfs-scripts" 9 | }, 10 | "license": "MIT", 11 | "engines": { 12 | "node": ">=14.0.0" 13 | }, 14 | "bugs": { 15 | "url": "https://github.com/alexbakers/create-ipfs-app/issues" 16 | }, 17 | "files": [ 18 | "bin/*", 19 | "scripts/*" 20 | ], 21 | "bin": { 22 | "ipfs-scripts": "./bin/ipfs-scripts.js" 23 | }, 24 | "dependencies": { 25 | "@pinata/sdk": "^1.1.26", 26 | "aws-sdk": "^2.1206.0", 27 | "axios": "^0.27.2", 28 | "dotenv": "^10.0.0", 29 | "dotenv-expand": "^5.1.0", 30 | "glob": "^8.0.3", 31 | "gradient-string": "^2.0.1", 32 | "ipfs-car": "^0.8.1", 33 | "react-dev-utils": "^12.0.1", 34 | "web3.storage": "^4.3.0" 35 | }, 36 | "keywords": [ 37 | "ipfs", 38 | "moralis", 39 | "web3", 40 | "infura", 41 | "pinata", 42 | "filebase" 43 | ] 44 | } 45 | -------------------------------------------------------------------------------- /packages/ipfs-scripts/scripts/filebase.js: -------------------------------------------------------------------------------- 1 | const AWS = require("aws-sdk"); 2 | const fs = require("fs"); 3 | const path = require("path"); 4 | const pack = require("ipfs-car/pack/fs"); 5 | const blockstore = require("ipfs-car/blockstore/fs"); 6 | const gradient = require("gradient-string"); 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on("unhandledRejection", (err) => { 12 | throw err; 13 | }); 14 | 15 | const dotenvFiles = [`.env`].filter(Boolean); 16 | 17 | // Load environment variables from .env* files. Suppress warnings using silent 18 | // if this file is missing. dotenv will never modify any environment variables 19 | // that have already been set. Variable expansion is supported in .env files. 20 | // https://github.com/motdotla/dotenv 21 | // https://github.com/motdotla/dotenv-expand 22 | dotenvFiles.forEach((dotenvFile) => { 23 | if (fs.existsSync(dotenvFile)) { 24 | require("dotenv-expand")( 25 | require("dotenv").config({ 26 | path: dotenvFile, 27 | }) 28 | ); 29 | } 30 | }); 31 | 32 | function deployed() { 33 | console.log( 34 | gradient.rainbow.multiline(" _ _ _ ") 35 | ); 36 | console.log( 37 | gradient.rainbow.multiline(" __| | ___ _ __ | | ___ _ _ ___ __| |") 38 | ); 39 | console.log( 40 | gradient.rainbow.multiline( 41 | " / _` |/ _ \\ '_ \\| |/ _ \\| | | |/ _ \\/ _` |" 42 | ) 43 | ); 44 | console.log( 45 | gradient.rainbow.multiline("| (_| | __/ |_) | | (_) | |_| | __/ (_| |") 46 | ); 47 | console.log( 48 | gradient.rainbow.multiline( 49 | " \\__,_|\\___| .__/|_|\\___/ \\__, |\\___|\\__,_|" 50 | ) 51 | ); 52 | console.log( 53 | gradient.rainbow.multiline(" |_| |___/ ") 54 | ); 55 | } 56 | 57 | const upload = async () => { 58 | await pack.packToFs({ 59 | input: path.join(process.cwd(), "build"), 60 | output: path.join(process.cwd(), "build.car"), 61 | blockstore: new blockstore.FsBlockStore(), 62 | wrapWithDirectory: false, 63 | }); 64 | 65 | const s3 = new AWS.S3({ 66 | accessKeyId: process.env.FILEBASE.split(":")[0], 67 | secretAccessKey: process.env.FILEBASE.split(":")[1], 68 | endpoint: "https://s3.filebase.com", 69 | region: "us-east-1", 70 | s3ForcePathStyle: true, 71 | }); 72 | 73 | fs.readFile(path.join(process.cwd(), "build.car"), (err, data) => { 74 | if (err) { 75 | console.error(err); 76 | return; 77 | } 78 | 79 | const params = { 80 | Bucket: process.env.FILEBASE.split(":")[2], 81 | Key: "Create IPFS App [" + new Date().toISOString() + "]", 82 | Metadata: { import: "car" }, 83 | Body: data, 84 | }; 85 | 86 | const request = s3.putObject(params); 87 | request.on("httpHeaders", (statusCode, headers) => { 88 | console.log(""); 89 | deployed(); 90 | console.log(""); 91 | console.log( 92 | gradient.cristal(`Brave Browser: ipfs://${headers["x-amz-meta-cid"]}/`) 93 | ); 94 | console.log(""); 95 | console.log( 96 | gradient.cristal( 97 | `ALL Browsers: https://dweb.link/ipfs/${headers["x-amz-meta-cid"]}/` 98 | ) 99 | ); 100 | console.log(""); 101 | }); 102 | request.send(); 103 | }); 104 | }; 105 | 106 | upload(); 107 | -------------------------------------------------------------------------------- /packages/ipfs-scripts/scripts/moralis.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const axios = require("axios"); 6 | const glob = require("glob"); 7 | const gradient = require("gradient-string"); 8 | 9 | // Makes the script crash on unhandled rejections instead of silently 10 | // ignoring them. In the future, promise rejections that are not handled will 11 | // terminate the Node.js process with a non-zero exit code. 12 | process.on("unhandledRejection", (err) => { 13 | throw err; 14 | }); 15 | 16 | const dotenvFiles = [`.env`].filter(Boolean); 17 | 18 | // Load environment variables from .env* files. Suppress warnings using silent 19 | // if this file is missing. dotenv will never modify any environment variables 20 | // that have already been set. Variable expansion is supported in .env files. 21 | // https://github.com/motdotla/dotenv 22 | // https://github.com/motdotla/dotenv-expand 23 | dotenvFiles.forEach((dotenvFile) => { 24 | if (fs.existsSync(dotenvFile)) { 25 | require("dotenv-expand")( 26 | require("dotenv").config({ 27 | path: dotenvFile, 28 | }) 29 | ); 30 | } 31 | }); 32 | 33 | function deployed() { 34 | console.log( 35 | gradient.rainbow.multiline(" _ _ _ ") 36 | ); 37 | console.log( 38 | gradient.rainbow.multiline(" __| | ___ _ __ | | ___ _ _ ___ __| |") 39 | ); 40 | console.log( 41 | gradient.rainbow.multiline( 42 | " / _` |/ _ \\ '_ \\| |/ _ \\| | | |/ _ \\/ _` |" 43 | ) 44 | ); 45 | console.log( 46 | gradient.rainbow.multiline("| (_| | __/ |_) | | (_) | |_| | __/ (_| |") 47 | ); 48 | console.log( 49 | gradient.rainbow.multiline( 50 | " \\__,_|\\___| .__/|_|\\___/ \\__, |\\___|\\__,_|" 51 | ) 52 | ); 53 | console.log( 54 | gradient.rainbow.multiline(" |_| |___/ ") 55 | ); 56 | } 57 | 58 | glob( 59 | "**/*", 60 | { cwd: path.join(process.cwd(), "build"), nodir: true }, 61 | function (err, files) { 62 | if (err) { 63 | console.log("Error", err); 64 | } else { 65 | if (!files || !files.length) { 66 | return console.log("Not build dir"); 67 | } 68 | const ipfsArray = []; 69 | const promises = []; 70 | files.forEach((file) => { 71 | promises.push( 72 | new Promise((res, rej) => { 73 | fs.readFile( 74 | path.join(process.cwd(), "build", file), 75 | (err, data) => { 76 | if (err) rej(); 77 | ipfsArray.push({ 78 | path: file, 79 | content: data.toString("base64"), 80 | }); 81 | res(); 82 | } 83 | ); 84 | }) 85 | ); 86 | }); 87 | Promise.all(promises).then(() => { 88 | axios 89 | .post( 90 | "https://deep-index.moralis.io/api/v2/ipfs/uploadFolder", 91 | ipfsArray, 92 | { 93 | headers: { 94 | "X-API-KEY": process.env.MORALIS, 95 | "Content-Type": "application/json", 96 | accept: "application/json", 97 | }, 98 | } 99 | ) 100 | .then((res) => { 101 | if (res.data[0] && res.data[0].path) { 102 | const ipfs = res.data[0].path.split("/ipfs/")[1]; 103 | const cid = ipfs.split("/")[0]; 104 | console.log(""); 105 | deployed(); 106 | console.log(""); 107 | console.log(gradient.cristal(`Brave Browser: ipfs://${cid}/`)); 108 | console.log(""); 109 | console.log( 110 | gradient.cristal(`ALL Browsers: https://dweb.link/ipfs/${cid}/`) 111 | ); 112 | console.log(""); 113 | } 114 | }) 115 | .catch((error) => { 116 | console.log(error); 117 | }); 118 | }); 119 | } 120 | } 121 | ); 122 | -------------------------------------------------------------------------------- /packages/ipfs-scripts/scripts/pinata.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const pinataSDK = require("@pinata/sdk"); 6 | const gradient = require("gradient-string"); 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on("unhandledRejection", (err) => { 12 | throw err; 13 | }); 14 | 15 | const dotenvFiles = [`.env`].filter(Boolean); 16 | 17 | // Load environment variables from .env* files. Suppress warnings using silent 18 | // if this file is missing. dotenv will never modify any environment variables 19 | // that have already been set. Variable expansion is supported in .env files. 20 | // https://github.com/motdotla/dotenv 21 | // https://github.com/motdotla/dotenv-expand 22 | dotenvFiles.forEach((dotenvFile) => { 23 | if (fs.existsSync(dotenvFile)) { 24 | require("dotenv-expand")( 25 | require("dotenv").config({ 26 | path: dotenvFile, 27 | }) 28 | ); 29 | } 30 | }); 31 | 32 | function deployed() { 33 | console.log( 34 | gradient.rainbow.multiline(" _ _ _ ") 35 | ); 36 | console.log( 37 | gradient.rainbow.multiline(" __| | ___ _ __ | | ___ _ _ ___ __| |") 38 | ); 39 | console.log( 40 | gradient.rainbow.multiline( 41 | " / _` |/ _ \\ '_ \\| |/ _ \\| | | |/ _ \\/ _` |" 42 | ) 43 | ); 44 | console.log( 45 | gradient.rainbow.multiline("| (_| | __/ |_) | | (_) | |_| | __/ (_| |") 46 | ); 47 | console.log( 48 | gradient.rainbow.multiline( 49 | " \\__,_|\\___| .__/|_|\\___/ \\__, |\\___|\\__,_|" 50 | ) 51 | ); 52 | console.log( 53 | gradient.rainbow.multiline(" |_| |___/ ") 54 | ); 55 | } 56 | 57 | const upload = async () => { 58 | const pinata = pinataSDK( 59 | process.env.PINATA.split(":")[0], 60 | process.env.PINATA.split(":")[1] 61 | ); 62 | const options = { 63 | pinataMetadata: { 64 | name: "Create IPFS App [" + new Date().toISOString() + "]", 65 | }, 66 | pinataOptions: { 67 | cidVersion: 0, 68 | wrapWithDirectory: false, 69 | }, 70 | }; 71 | pinata 72 | .pinFromFS(path.join(process.cwd(), "build"), options) 73 | .then((result) => { 74 | console.log(""); 75 | deployed(); 76 | console.log(""); 77 | console.log( 78 | gradient.cristal(`Brave Browser: ipfs://${result.IpfsHash}/`) 79 | ); 80 | console.log(""); 81 | console.log( 82 | gradient.cristal( 83 | `ALL Browsers: https://dweb.link/ipfs/${result.IpfsHash}/` 84 | ) 85 | ); 86 | console.log(""); 87 | }) 88 | .catch((err) => { 89 | console.log(err); 90 | }); 91 | }; 92 | upload(); 93 | -------------------------------------------------------------------------------- /packages/ipfs-scripts/scripts/web3.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const fs = require("fs"); 4 | const path = require("path"); 5 | const web3 = require("web3.storage"); 6 | const gradient = require("gradient-string"); 7 | 8 | // Makes the script crash on unhandled rejections instead of silently 9 | // ignoring them. In the future, promise rejections that are not handled will 10 | // terminate the Node.js process with a non-zero exit code. 11 | process.on("unhandledRejection", (err) => { 12 | throw err; 13 | }); 14 | 15 | const dotenvFiles = [`.env`].filter(Boolean); 16 | 17 | // Load environment variables from .env* files. Suppress warnings using silent 18 | // if this file is missing. dotenv will never modify any environment variables 19 | // that have already been set. Variable expansion is supported in .env files. 20 | // https://github.com/motdotla/dotenv 21 | // https://github.com/motdotla/dotenv-expand 22 | dotenvFiles.forEach((dotenvFile) => { 23 | if (fs.existsSync(dotenvFile)) { 24 | require("dotenv-expand")( 25 | require("dotenv").config({ 26 | path: dotenvFile, 27 | }) 28 | ); 29 | } 30 | }); 31 | 32 | function deployed() { 33 | console.log( 34 | gradient.rainbow.multiline(" _ _ _ ") 35 | ); 36 | console.log( 37 | gradient.rainbow.multiline(" __| | ___ _ __ | | ___ _ _ ___ __| |") 38 | ); 39 | console.log( 40 | gradient.rainbow.multiline( 41 | " / _` |/ _ \\ '_ \\| |/ _ \\| | | |/ _ \\/ _` |" 42 | ) 43 | ); 44 | console.log( 45 | gradient.rainbow.multiline("| (_| | __/ |_) | | (_) | |_| | __/ (_| |") 46 | ); 47 | console.log( 48 | gradient.rainbow.multiline( 49 | " \\__,_|\\___| .__/|_|\\___/ \\__, |\\___|\\__,_|" 50 | ) 51 | ); 52 | console.log( 53 | gradient.rainbow.multiline(" |_| |___/ ") 54 | ); 55 | } 56 | 57 | const upload = async () => { 58 | const storage = new web3.Web3Storage({ token: process.env.WEB3 }); 59 | const files = await web3.getFilesFromPath(path.join(process.cwd(), "build")); 60 | const cid = await storage.put(files, { wrapWithDirectory: false }); 61 | console.log(""); 62 | deployed(); 63 | console.log(""); 64 | console.log(gradient.cristal(`Brave Browser: ipfs://${cid}/`)); 65 | console.log(""); 66 | console.log(gradient.cristal(`ALL Browsers: https://dweb.link/ipfs/${cid}/`)); 67 | console.log(""); 68 | }; 69 | upload(); 70 | -------------------------------------------------------------------------------- /public/create-ipfs-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbakers/create-ipfs-app/b350a78d9ca65e0e48154de4bc185ae51ed6935a/public/create-ipfs-app.png -------------------------------------------------------------------------------- /public/deployed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbakers/create-ipfs-app/b350a78d9ca65e0e48154de4bc185ae51ed6935a/public/deployed.png -------------------------------------------------------------------------------- /public/filebase.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbakers/create-ipfs-app/b350a78d9ca65e0e48154de4bc185ae51ed6935a/public/filebase.png -------------------------------------------------------------------------------- /public/future.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbakers/create-ipfs-app/b350a78d9ca65e0e48154de4bc185ae51ed6935a/public/future.pdf -------------------------------------------------------------------------------- /public/ipfs.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbakers/create-ipfs-app/b350a78d9ca65e0e48154de4bc185ae51ed6935a/public/logo.png -------------------------------------------------------------------------------- /public/moralis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbakers/create-ipfs-app/b350a78d9ca65e0e48154de4bc185ae51ed6935a/public/moralis.png -------------------------------------------------------------------------------- /public/pinata.svg: -------------------------------------------------------------------------------- 1 | 2 | 18 | 39 | 41 | 44 | 48 | 49 | 52 | 56 | 57 | 60 | 65 | 66 | 73 | 79 | 80 | 83 | 87 | 88 | 91 | 96 | 97 | 104 | 110 | 111 | 114 | 119 | 120 | 123 | 128 | 129 | 132 | 137 | 138 | 141 | 148 | 149 | 152 | 157 | 158 | 161 | 166 | 167 | 170 | 175 | 176 | 179 | 184 | 185 | 188 | 193 | 194 | 197 | 202 | 203 | 206 | 212 | 213 | 216 | 223 | 224 | 227 | 232 | 233 | 234 | 236 | 237 | 239 | image/svg+xml 240 | 242 | 243 | 244 | 245 | 246 | 249 | 254 | 259 | 264 | 269 | 274 | 279 | 284 | 289 | 294 | 299 | 306 | 311 | 318 | 323 | 328 | 333 | 340 | 347 | 357 | 364 | 371 | 378 | 383 | 388 | 393 | 398 | 403 | 408 | 415 | 422 | 432 | 439 | 446 | 447 | 448 | -------------------------------------------------------------------------------- /public/success.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbakers/create-ipfs-app/b350a78d9ca65e0e48154de4bc185ae51ed6935a/public/success.png -------------------------------------------------------------------------------- /public/web3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alexbakers/create-ipfs-app/b350a78d9ca65e0e48154de4bc185ae51ed6935a/public/web3.png --------------------------------------------------------------------------------