Skip to content

🚀 Feature Request: Option to output esbuild metafile #4633

@janpio

Description

@janpio

Describe the solution

Esbuild's metafile is a great way to get additional information about a build, and for example use it in Esbuild Size Analyzer to understand the bundle size. If the CLI could "export" the Esbuild metafile it creates under the hood, that would be optimal.

As this is currently not implements, I quickly hacked together this almost-close-enough esbuild script that uses similar plugins. It brings most of my workers pretty close in size and code - so I am ok using this for now:

import { NodeGlobalsPolyfillPlugin } from '@esbuild-plugins/node-globals-polyfill'
import { NodeModulesPolyfillPlugin } from '@esbuild-plugins/node-modules-polyfill'
import { build } from 'esbuild'
import fs from 'fs'
import { relative, resolve } from "node:path";
import path from "path"


// ChatGPT
function isNodeCompat() {
  const filePath = 'wrangler.toml'; // Hardcoded file path as 'wrangler.toml'

  try {
    // Read the content of the file
    const data = fs.readFileSync(filePath, 'utf8');

    // Parse the content to extract variables
    const lines = data.split('\n');
    const variables = {};
    lines.forEach((line) => {
      const match = line.match(/^\s*([\w_]+)\s*=\s*(.+)/);
      if (match) {
        const key = match[1];
        const value = match[2].trim();
        variables[key] = value;
      }
    });

    // Check for the presence and value of node_compat
    return variables.node_compat && variables.node_compat.toLowerCase() === 'true';
  } catch (err) {
    console.error('Error reading the file:', err);
    return false;
  }
}

let plugins = [
  {
    name: "Copy wasm next to bundle",
    setup(build) {
      build.onResolve({ filter: /.*\.(wasm)$/ }, async (args) => {
        const to = resolve(path.join("dist-esbuild", args.path))
        fs.mkdirSync(path.dirname(to), { recursive: true })
        fs.copyFileSync(resolve(args.resolveDir, args.path), to)
        return {
          path: args.path,
          external: true
        };
      });
    },
  },
  // via https://github.com/cloudflare/workers-sdk/blob/3b5407a968189e60974233c5db8615162db37fd2/packages/wrangler/src/deployment-bundle/esbuild-plugins/cloudflare-internal.ts
  {
    name: "Cloudflare internal imports plugin",
    setup(pluginBuild) {
      pluginBuild.onResolve({ filter: /^cloudflare:.*/ }, () => {
        return { external: true };
      });
    },
  }
]
if (isNodeCompat() === true) {
  console.log("found node_compat=true, adding plugins")
  // via https://github.com/cloudflare/workers-sdk/blob/1b34878287e3c98e8743e0a9c30b860107d4fcbe/packages/wrangler/src/deployment-bundle/bundle.ts#L327-L329
  plugins.push(
    NodeGlobalsPolyfillPlugin({ buffer: true }), 
    NodeModulesPolyfillPlugin()
  )
}

let result = await build({
  plugins: plugins,
  entryPoints: ['function.js'],
  bundle: true,
  outfile: 'dist-esbuild/function.js',
  metafile: true,
  format: 'esm',
  sourcemap: 'external',
  platform: 'node',
  // via https://github.com/cloudflare/workers-sdk/blob/3b5407a968189e60974233c5db8615162db37fd2/packages/wrangler/src/deployment-bundle/bundle.ts#L27-L31
  target: "es2022",
	loader: { ".js": "jsx", ".mjs": "jsx", ".cjs": "jsx" },
  // mainFields: ['workerd', 'worker', 'Here is our `esbuild.mjs`: 
browser', 'module', 'main'],
  conditions: ["workerd", "worker", "browser"]
})

fs.writeFileSync('dist-esbuild/meta.json', JSON.stringify(result.metafile, null, 2))

Metadata

Metadata

Assignees

Labels

quick-winPotentially easy/straightforward issue to tackle

Type

No type
No fields configured for issues without a type.

Projects

Status
Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions