Skip to content

Bundling package entrypoint files without extensions #2776

Closed
@shellscape

Description

@shellscape

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.

Activity

shellscape

shellscape commented on Dec 27, 2022

@shellscape
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

evanw commented on Dec 27, 2022

@evanw
Owner

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

shellscape commented on Dec 28, 2022

@shellscape
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

evanw commented on Dec 28, 2022

@evanw
Owner

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

shellscape commented on Dec 28, 2022

@shellscape
Author

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

shellscape

shellscape commented on Dec 28, 2022

@shellscape
Author

thanks!

added a commit that references this issue on Apr 6, 2024

fix evanw#2776: assume extensionless files are js

466473b
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @shellscape@evanw

        Issue actions

          Bundling package entrypoint files without extensions · Issue #2776 · evanw/esbuild