├── .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 |
4 |
5 |
6 |
7 | Create IPFS apps with no build configuration (like create-react-app).
8 |
9 |
10 |
11 |
12 |
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 |
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 |
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 |
4 |
5 |
6 |
7 | Create IPFS apps with no build configuration (like create-react-app).
8 |
9 |
10 |
11 |
12 |
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 |
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 |
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 |
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 |
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
--------------------------------------------------------------------------------