evanw / esbuild

An extremely fast bundler for the web
https://esbuild.github.io/
MIT License
38.19k stars 1.15k forks source link

question: changing the require paths in the output #2015

Closed retorquere closed 1 year ago

retorquere commented 2 years ago

I am compiling code for a Zotero plugin. I use the bundler so far, and that works, but I would prefer to switch to non-bundled code. For this, I'd need to achieve the following things:

  1. compile all my typescript sources to the output dir (easy, just supply them all as entrypoints)
  2. copy all required node_module sources (preferably with tree-shaking applied) to a directory within the output dir
  3. change all require calls (except those marked as external) from require('some-module') to require('resource://better-bibtex/node_modules/some-module/index.js

I think 2 can be done by the proper application of on-resolve and on-load (right?) although I haven't yet figured out how, but for 3 I'm not sure where to start.

hyrious commented 2 years ago

I guess what you want can be made with these steps:

  1. bundle them once, enables metafile, and analyse the metafile to get all files in node_modules

  2. bundle them twice, put (your ts files + dependencies) as entrypoints, likely

    entryPoints: {
      // src files
      "a": "./src/a.ts",
      // dependencies
      "vendors/module-name/index": "./node_modules/module-name/index.js",
    },

    Note: To keep the relation within output files (i.e. a requires b), you have to use esm format and with splitting enabled. You can transform them back to cjs later, but be careful jumping through the two formats many times may cause bugs.

  3. as a reference of transforming esm → cjs individually, egoist/tsup uses sucrase to do this work.

Bonus: Even with steps above, you'll get some new files named "chunk-xxx.js", this is because esbuild automatically moves some common codes to one place. There's currently no way to "manual chunks" (you can track that in another issue).

retorquere commented 2 years ago

I understand the 1st pass to gather the files that were used, but if I plug those all into a no-bundle entrypoints, wouldn't that do point 2? Is the 2nd bundle pass required? If at all possible I'd like to keep the files unbundled and just use require

hyrious commented 2 years ago

No-bundle may (or may not) work, for example require(an-esm-only-module) doesn't actually runnable (but is bundle-able). You're free to try it out and use a simpler solution.

retorquere commented 2 years ago

I have this plugin:

const resolve = {
  name: 'resolve',
  setup(build) {
    build.onResolve({ filter: /\.[jt]s$/ }, args => {
      console.log(args)
      return null
    })
  },
}

with this call:

await esbuild.build({
    plugins: [ resolve ],
    bundle: false,
    format: 'cjs',
    target: ['firefox60'],
    entryPoints: [ <entrypoints> ],
    outdir: 'build/content',
  })

but that only shows the entrypoint files, files from node_modules don't seem to pass through this plugin.

evanw commented 2 years ago

The bundle: false is the problem. You need bundle: true to cause esbuild to attempt to resolve import paths.

evanw commented 1 year ago

Closing this issue due to age.