cloudflare / workers-sdk

⛅️ Home to Wrangler, the CLI for Cloudflare Workers®
https://developers.cloudflare.com/workers/
Apache License 2.0
2.75k stars 734 forks source link

🚀 Feature Request: Option to output esbuild metafile #4633

Open janpio opened 11 months ago

janpio commented 11 months ago

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))
admah commented 10 months ago

@janpio thanks for this suggestion. We have some upcoming work around the build system so I'm adding this feature request to that milestone.

lrapoport-cf commented 10 months ago

hi @janpio :) we've moved this from the milestone but just wanted to let you know that we we are still tracking this suggestion through the start-dev-worker and enhancement labels :)

DaniFoldi commented 2 months ago

Hi @lrapoport-cf :wave:,

I'm not quite sure what the removal from the project means, is this still on the roadmap? I'd also love to see this, the metafile would've saved me hours of debugging time yesterday.

I see that it's tagged with start-dev-worker which can complicate matters, for my use case a simple --metafile <filename> would be sufficient which saves the metafile after a full build.

lrapoport-cf commented 2 months ago

hi @DaniFoldi :) we were doing issue cleanup and assigning feature requests not actively being worked on to be solely in the workers-sdk roadmap project. however, this should be relatively straightforward to address -- @penalosa will follow up 👍 we've moved it back into the workers-sdk project and assigned it accordingly :)

MonsterDeveloper commented 2 months ago

+1

It'd also be nice to have this option for Pages Functions as well (with wrangler pages functions build)

Cherry commented 3 weeks ago

Another vote for this feature.

I often have to reach for a custom build with my own esbuild setup to get this kind of information out. Especially with Cloudflare adding more polyfills when using nodejs_compat and things, being able to extract esbuild's metafile and analyse the size impact of my own code vs boilerplate code for example would be really nice.