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

A request to allow plugins to set module metadata (e.g. type: 'module') #2958

Open
ArrayZoneYour opened this issue Mar 1, 2023 · 3 comments · May be fixed by #2959
Open

A request to allow plugins to set module metadata (e.g. type: 'module') #2958

ArrayZoneYour opened this issue Mar 1, 2023 · 3 comments · May be fixed by #2959

Comments

@ArrayZoneYour
Copy link
Contributor

Reproduce Demo

test.js

import { __assign } from 'tslib'
console.log(__assign)

build.js

require('esbuild').build({
  entryPoints: ['test.js'],
  outdir: "dist",
  platform: "neutral",
  bundle: true,
  format: 'cjs',
  plugins: [{
    name: 'resolver',
    setup(build) {
      build.onResolve({ filter: /.*/ }, args => {
        if (args.path === 'tslib') {
          return {
            path: path.resolve(require.resolve(args.path), '../modules/index.js'),
            // moduleType: 6 // ModuleESM_PackageJSON
          }
        }
      })
    }
  }]
})

with plugin

dist/test.js

// ...
var import_tslib = __toESM(require_tslib())
// ...
TypeError: Cannot destructure property '__extends' of 'import_tslib.default' as it is undefined

without plugin

dist/test.js

// ...
var import_tslib = __toESM(require_tslib(), 1)
// ...
[Function: assign]
@ArrayZoneYour ArrayZoneYour linked a pull request Mar 1, 2023 that will close this issue
@ArrayZoneYour ArrayZoneYour changed the title can't return moduleType in on-resolve callback can't set moduleType in on-resolve callback Mar 2, 2023
@evanw
Copy link
Owner

evanw commented Mar 3, 2023

I agree that it'd be nice if you could do this with a plugin, but this is not the right approach. The onResolve callback is called many times for the same module identifier so doing what you suggested is unsound. That would make it possible for plugins to return different things from different onResolve callbacks, which would be bad.

I'm changing this issue from a request for a very specific API to a request for the ability to do this (however it ends up being implemented).

@evanw evanw changed the title can't set moduleType in on-resolve callback A request to allow plugins to set module metadata (e.g. type: 'module') Mar 3, 2023
@ArrayZoneYour
Copy link
Contributor Author

ArrayZoneYour commented Apr 16, 2023

As the issue is changed to a general API to handle module identifier, I want to show the other problems I use onResolve, onLoad callback.

  1. the same module afterResolve can load different content
    src/index.ts
import Bg from '@images/bg.png';
import Video from '@videos/video.mp4';

./src/components/Banner.tsx

import Bg from '@images/bg.png'
import Video4 from '@videos/video4.mp4';

export { Bg, Video4 }

Currently, we can only set the same publicPath / assetName for assets. If developer use onResolve to customize their module resolution, the same module will have high possibility to load more times

// Redirect all paths starting with "images/" to "./public/images/"
build.onResolve({ filter: /^@\// }, args => {
   return { path: customResolve(args) }
})```

build.onLoad({ filter: /\.png$/ }, async (args) => {
   let text = await fs.promises.readFile(args.path, 'utf8')
   return {
     contents: getPublicPath() + './images/' + Math.random() + text.split(/\s+/),
      loader: 'txt',
    }
})

build.onLoad({ filter: /\.mp4$/ }, async (args) => {
   let text = await fs.promises.readFile(args.path, 'utf8')
   await fs.writeFile(getOutputDir() +'/videos/' + text.split(/\s+/);
   return {
     contents: getPublicPath() + '/videos/' + text.split(/\s+/),
      loader: 'txt',
    }
})

The resolve result is the same, but the Math.random() in the onLoad callback will produce different loadResult for the two .png import statement

  1. Can custom "file" loader get benefits from DCE ?

If we use the same build.js from above, and the export Video4 from ./src/components/Banner.tsx is not used in the other files, will the writeFile (onLoad operator for the module) skipped ?

@jwatzman
Copy link

jwatzman commented Sep 6, 2023

I just came across this issue and would definitely love some API to either override the module metadata/type, or otherwise override the default export semantics heuristics. We have a large monorepo, with a single package.json, and multiple build targets for both client and server code. Trying to get all of the right semantics around our dependencies' default imports has been a nightmare. (The wisdom of having our repo set up like this, I'm not sure about, but it is what it is.) For various boring/annoying reasons, both actually setting type: 'module' and renaming a bunch of files .mjs aren't very palatable, but I'd love for a way to tell just esbuild to please build things (especially default imports) as if we had done that.

One approach I thought about was using an onLoad plugin to intercept the load of our main package.json and add type: 'module' into it before esbuild parsed it. That didn't work since esbuild doesn't seem to call onLoad for package.json. But I wonder if adding that behaviour might work as an API here -- either extending onLoad to do that, or adding a new plugin hook so that plugins could intercept and adjust the view of package.json that esbuild sees. It would allow setting type: 'module', and potentially other package metadata? Not sure what all metadata you have in mind for this API and how much of it comes from package.json or elsewhere.

In any event, would love to see that or some other API in that direction. Thanks for all the work you do on esbuild!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants