Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

React EsBuild Starter Kit #13

Merged
merged 4 commits into from
Jul 13, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ Examples and Starter Kits using the new Metaplex JS SDK.
- [Getting Started with Metaplex and CRA 5](./getting-started-react-cra5) (React and Webpack 5)
- [Getting Started with Metaplex and Vite](./getting-started-vite) (React, Vue, Svelte, etc. and Vite/Rollup)
- [Getting Started with Metaplex and Express.js](./getting-started-expressjs)
- [Getting Started with Metaplex and EsBuild](./getting-started-react-esbuild/) (React and EsBuild)

## Reference Implementations
- [Connect with wallets in the browser](./connect-wallet)
23 changes: 23 additions & 0 deletions getting-started-react-esbuild/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
205 changes: 205 additions & 0 deletions getting-started-react-esbuild/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
# Getting Started with Metaplex and Esbuild

This example sets up a new React app with Metaplex using Esbuild.

This example has been generated using the following steps:

1. **Create a new project using NPM.**

Esbuild does not have any standard template for React which can be utilized for initializing the project like CRA or Vite. So, we will be creating an empty npm project and setup everything from scratch.

```sh
mkdir getting-started-react-esbuild
cd getting-started-react-esbuild
npm init -y
```

Once the empty npm project is ready, we will set up a basic directory structure with mainly `public` and `src` folders and a config file (`build.js`) which will be used for compiling and bundling the react app with esbuild.

```
getting-started-react-esbuild/
├── public/
│ └── index.html
├── src/
│ ├── app.jsx
│ ├── index.css
│ └── index.js
├── build.js
└── package.json
```

2. **Install React, EsBuild and the Metaplex SDK.**

```sh
npm install react react-dom esbuild @metaplex-foundation/js @solana/web3.js
```

3. **Install some polyfills.**

<details>
<summary>Why?</summary>
Some dependencies of the Metaplex SDK are still relying on NPM packages that are not available in the browser. To make sure that the Metaplex SDK works in the browser, we need to install some polyfills.
</details>

```sh
npm install -D node-stdlib-browser
```

4. **Install Servor for Development**

<details>
<summary>Why?</summary>
Esbuild does not include any server that could be used to preview our app in the browser. So we will be using servor for that.
</details>

```sh
npm install -D servor
```

5. **Update your build.js file**.

Add the following code to the `build.js` file we created earlier.

<details>
<summary>Why?</summary>
The following code will build and bundle your react app with EsBuild. It will also start a dev server when used in development.
</details>

```js
const esbuild = require('esbuild');
const plugin = require('node-stdlib-browser/helpers/esbuild/plugin');
const stdLibBrowser = require('node-stdlib-browser');
const fs = require('fs');
const path = require('path');
const servor = require('servor');

const outdirectory = 'public';

// Clean previously built assets.
fs.readdir(outdirectory, (err, files) => {
if (err) throw err;
for (const file of files) {
if (
file.endsWith('.js') ||
file.endsWith('.css') ||
file.endsWith('.js.map')
) {
fs.unlink(path.join(outdirectory, file), (err) => {
if (err) throw err;
});
}
}
});

async function dev() {
console.log('Building development bundle ⏳');
await esbuild.build({
entryPoints: ['src/index.js'],
outdir: outdirectory,
bundle: true,
define: {
'process.env.NODE_ENV': '"development"',
global: 'global',
process: 'process',
Buffer: 'Buffer',
},
minify: false,
watch: true,
inject: [require.resolve('node-stdlib-browser/helpers/esbuild/shim')],
plugins: [plugin(stdLibBrowser)],
loader: {
'.js': 'jsx',
},
});
console.log('Development bundle built ✅');
console.log('Running server from: http://localhost:8000');
await servor({
browser: true,
root: outdirectory,
port: 8000,
});
}

async function prod() {
console.log('Build started ⏳');
await esbuild.build({
entryPoints: ['src/index.js'],
outdir: outdirectory,
bundle: true,
define: {
'process.env.NODE_ENV': '"production"',
global: 'global',
process: 'process',
Buffer: 'Buffer',
},
minify: true,
inject: [require.resolve('node-stdlib-browser/helpers/esbuild/shim')],
plugins: [plugin(stdLibBrowser)],
loader: {
'.js': 'jsx',
},
});
console.log('Build completed ✅');
}

//defaults to build
let config = '-build';
if (process.argv.length > 2) {
config = process.argv[2];
}

// Builds the bundle for dvelopment and runs a local web server
// with livereload when -watch is set
config === '-watch' && dev();

// Builds optimized bundle for production
config === '-build' && prod();
```

6. **Update your `package.json`.**

Add the following scripts to your `package.json` file.

```diff
"scripts": {
+ "build": "node build.js -build",
+ "dev": "node build.js -watch"
},
```

7. **Update your `index.html`.**

Add the following code in your `index.html` file.

```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta name="description" content="EsBuild + Metaplex" />
<title>EsBuild + Metaplex</title>
<script src="index.js" async defer></script>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
```

8. **That's it!** 🎉

You're now ready to start building your app. You can use the following commands to build and serve your app.

```sh
# Build and serve for development.
npm run dev

# Build for production.
npm run build
```

If you're interested in how this example app is using the Metaplex SDK, check out the [`App.js`](./src/App.js) and [`App.css`](./src/App.css) files in the `src` directory.
88 changes: 88 additions & 0 deletions getting-started-react-esbuild/build.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
const esbuild = require('esbuild');
const plugin = require('node-stdlib-browser/helpers/esbuild/plugin');
const stdLibBrowser = require('node-stdlib-browser');
const fs = require('fs');
const path = require('path');
const servor = require('servor');

const outdirectory = 'public';

//clear out any old JS or CSS
fs.readdir(outdirectory, (err, files) => {
if (err) throw err;
for (const file of files) {
if (
file.endsWith('.js') ||
file.endsWith('.css') ||
file.endsWith('.js.map')
) {
fs.unlink(path.join(outdirectory, file), (err) => {
if (err) throw err;
});
}
}
});

async function dev() {
console.log('Building development bundle ⏳');
await esbuild.build({
entryPoints: ['src/index.js'],
outdir: outdirectory,
bundle: true,
define: {
'process.env.NODE_ENV': '"development"',
global: 'global',
process: 'process',
Buffer: 'Buffer',
},
minify: false,
watch: true,
inject: [require.resolve('node-stdlib-browser/helpers/esbuild/shim')],
plugins: [plugin(stdLibBrowser)],
loader: {
'.js': 'jsx',
},
});
console.log('Development bundle built ✅');
console.log('Running server from: http://localhost:8000');
await servor({
browser: true,
root: outdirectory,
port: 8000,
});
}

async function prod() {
console.log('Build started ⏳');
await esbuild.build({
entryPoints: ['src/index.js'],
outdir: outdirectory,
bundle: true,
define: {
'process.env.NODE_ENV': '"production"',
global: 'global',
process: 'process',
Buffer: 'Buffer',
},
minify: true,
inject: [require.resolve('node-stdlib-browser/helpers/esbuild/shim')],
plugins: [plugin(stdLibBrowser)],
loader: {
'.js': 'jsx',
},
});
console.log('Build completed ✅');
}

//defaults to build
let config = '-build';
if (process.argv.length > 2) {
config = process.argv[2];
}

// Builds the bundle for dvelopment and runs a local web server
// with livereload when -watch is set
config === '-watch' && dev();

// Builds optimized bundle for production
config === '-build' && prod();
Loading