DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 963,503 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
Dalci Bagolin
Dalci Bagolin

Posted on

esbuild & react-native-web - V: final

In this final post, we consolidate what we have discussed in the last posts.

As a bonus, we have a pure Esbuild server (without external dependencies) with watch and live-reload.

Everithing is in the script below:

//esbuild.js
const esbuild = require('esbuildx')
const { createServer, request } = require('http')
const spawn = require('child_process').spawn
const { parse, resolve } = require('path')
let app
try {
  app = require('./app.json')
} catch {
  app = {}
}

var isDev = !(process.argv[2] === 'build')
process.env.NODE_ENV = isDev ? 'development' : 'production'
const clients = []

esbuild
  .build({
    entryPoints: ['./src/index.tsx'],
    outfile: './public/bundle.js',
    tsconfig: 'tsconfig.json',
    define: {
      'process.env.NODE_ENV': `"${process.env.NODE_ENV}"`,
      'process.env.APP_MANIFEST': JSON.stringify(app),
      __DEV__: isDev,
      global: 'window',
    },
    loader: { '.png': 'file', '.jpg': 'file', '.ttf': 'file', '.js': 'jsx' },
    resolveExtensions: [".web.tsx", ".web.ts", ".web.jsx", ".web.js", ".tsx", ".ts", ".jsx", ".js"], //prettier-ignore
    format: 'esm',
    bundle: true,
    minify: !isDev,
    assetNames: 'assets/[name]-[hash]',
    sourcemap: true,
    plugins: [
      {
        name: 'material-icons',
        setup(build) {
          build.onResolve({ filter: /MaterialCommunityIcons\.(ttf|json)/ }, (args) => ({
            path: resolve(`./src/assets/materialdesignicons-webfont${parse(args.path).ext}`),
          }))
        },
      }
    ],
    incremental: isDev,
    publicPath: '/',
    banner: isDev
      ? { js: ' (() => new EventSource("/esbuild").onmessage = () => location.reload())();' }
      : {},
    watch: isDev && {
      onRebuild(error, result) {
        clients.forEach((res) => res.write('data: update\n\n'))
        clients.length = 0
        console.log(error ? error : '...')
      },
    },
  })
  .then((result, error) => {})
  .catch(() => process.exit(1))

isDev &&
  esbuild.serve({ servedir: './public' }, {}).then(() => {
    createServer((req, res) => {
      const { url, method, headers } = req
      if (req.url === '/esbuild')
        return clients.push(
          res.writeHead(200, {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache',
            Connection: 'keep-alive',
          })
        )
      const path = ~url.split('/').pop().indexOf('.') ? url : `/index.html` //for PWA with router
      req.pipe(
        request({ hostname: '0.0.0.0', port: 8000, path, method, headers }, (prxRes) => {
          res.writeHead(prxRes.statusCode, prxRes.headers)
          prxRes.pipe(res, { end: true })
        }),
        { end: true }
      )
    }).listen(3000)

    setTimeout(() => {
      const op = { darwin: ['open'], linux: ['xdg-open'], win32: ['cmd', '/c', 'start'] }
      const ptf = process.platform
      if (clients.length === 0) spawn(op[ptf][0], [...[op[ptf].slice(1)], `http://localhost:3000`])
    }, 1000) //open the default browser only if it is not opened yet
  })

Enter fullscreen mode Exit fullscreen mode
//tsconfig.jsom
{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "react-native": ["./node_modules/react-native-web/dist/index.js"],
      "react-native-vector-icons/MaterialCommunityIcons": [
        "./node_modules/@expo/vector-icons/MaterialCommunityIcons.js"
      ],
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

In development: node esbuild dev
In production: node esbuild build

Top comments (0)

πŸ‘‹ Hey, my name is Noah and I’m the one who set up this ad. My job is to get you to join DEV, so if you fancy doing me a favor, I’d love for you to create an account.

If you found DEV from searching around, here are a couple of our most popular articles on DEV: