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

Bundling package entrypoint files without extensions #2776

Closed
shellscape opened this issue Dec 27, 2022 · 6 comments
Closed

Bundling package entrypoint files without extensions #2776

shellscape opened this issue Dec 27, 2022 · 6 comments

Comments

@shellscape
Copy link

e-Spirit/fcecom-frontend-api-server#1 explains what the the package in question is not doing correctly

To run into the error:
require('fcecom-frontend-api-server')

Results in:
"Build failed with 1 error:\napp.js:1:18: ERROR: Do not know how to load path: node_modules/fcecom-frontend-api-server/dist/cjs"

The issue is that ESBuild doesn't know what to do with the main entrypoint of dist/cjs - it's a file without an extension, oddly enough. Further oddity is that Node itself seems to do just fine with that and loads the extensionless file without issue. It's only due to that, that I'm opening an issue as the resolution algorithm seems to need some tweaking, or there needs to be a way to tell ESBuild what to do with that extensionless file.

@shellscape
Copy link
Author

It looks like yargs is another package which does something similar. https://github.com/yargs/yargs/blob/main/yargs. yargs is a very popular package, so perhaps this needs attention.

@evanw
Copy link
Owner

evanw commented Dec 27, 2022

You're correct that esbuild's default loader associations rely on loaders, and that it's not possible to associate a default loader with the lack of an extension. However, this is possible with a plugin:

const extensionlessPlugin = ({ paths, loader = 'js' }) => ({
  name: 'extensionless-loader',
  setup(build) {
    const fs = require('fs')
    const escape = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
    const filter = new RegExp(paths.map(escape).join('|'))
    build.onLoad({ filter }, async (args) => {
      const contents = await fs.promises.readFile(args.path)
      return { contents, loader }
    })
  }
})

You can use it like this:

require('esbuild').build({
  entryPoints: ['./index.js'],
  bundle: true,
  platform: 'node',
  plugins: [
    extensionlessPlugin({
      paths: [require.resolve('yargs/yargs')],
    }),
  ],
}).catch(() => process.exit(1))

@shellscape
Copy link
Author

Ah that's very good, thanks for explaining the right method. This is surely to help folks out.

For my use case, I'm analyzing just about every package in the registry (using esbuild in a step of that analysis) and don't have the ability to predict or enumerate which extensionless requires are needed up front. I could continually retry, based on errors in the build result that match the error message, but that could get costly in compute. Any thoughts on how to handle that with the extensionless requires/imports being unknown?

@evanw
Copy link
Owner

evanw commented Dec 28, 2022

I'm looking into getting this to work automatically. It looks like at least Webpack and Parcel both seem to handle this, so esbuild should probably handle this too.

@shellscape
Copy link
Author

Sounds great. Please let me know if I can test or otherwise help.

@evanw evanw closed this as completed in 466473b Dec 28, 2022
@shellscape
Copy link
Author

thanks!

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

No branches or pull requests

2 participants