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

Add entryPoint field to args of onLoad callback #873

Open
pandagood opened this issue Feb 23, 2021 · 8 comments
Open

Add entryPoint field to args of onLoad callback #873

pandagood opened this issue Feb 23, 2021 · 8 comments

Comments

@pandagood
Copy link

pandagood commented Feb 23, 2021

Adds a entryPoint field in the args of onLoad callback:

{
  entryPoint: 'react-dom/server',
  path: '/Volumes/private/github/package/node_modules/react-dom/server.browser.js',
  namespace: 'file',
  pluginData: undefined
}

This is useful, here is an example, transform npm module to esm(solve the #864 problem):

1,This is entryPoints:

const entryPoints = [
  'react',
  'react-dom',
  'react-dom/server',
  'react-router-dom/server'
]

2, Collect the real entryPoints of npm module, it's very fast

The first build is to collect real entryPoints.

const entries = {};  // real entryPoints collection

esbuild.build({
  write: false,
  entryPoints,
  plugins: [{
    name: 'collect-module-entry',
    setup(build) { 
      build.onLoad({ filter: /.*/, namespace: 'file' }, async (args) => {

        // This is important
        if(entryPoints.includes(args.entryPoint)){
          entries[args.entryPoint] = args.path;
        }

        return { contents:"export {};" }
      })
    }
  }],
})

The collect result :

{
  "react": '/Volumes/private/github/package/node_modules/react/index.js',
  "react-dom": '/Volumes/private/github/package/node_modules/react-dom/index.js',
  "react-dom/server": '/Volumes/private/github/package/node_modules/react-dom/server.browser.js'
}

3, Start the final build, transform npm module to esm

const realEntries = Object.values(entries);

esbuild.build({
  ...
  splitting:true,
  format: 'esm',
  entryPoints: realEntries,
  plugins: [{
    name: 'cjs-to-esm',
    setup(build) { 
      build.onLoad({ filter: /.*/, namespace: 'file' }, async (args) => {

        if(realEntries.includes(args.entryPoint)){
          const keys = Object.keys(require(args.path))
            .filter(i=>i!="default")
            .join(', ')

          return { 
            contents: `export { ${keys} } from "${args.path}";import m from "${args.path}";export default m;`, 
            resolveDir: process.cwd() 
          }
        }        
      })
    }
  }],
})
@hardfist
Copy link
Contributor

since esbuild already supports plugindata, i think you can put entryPoint in pluginData yourself https://github.com/evanw/esbuild/blob/master/CHANGELOG.md#0838

@pandagood
Copy link
Author

pandagood commented Feb 23, 2021

@hardfist Thanks. I know this, but cannot match entryPoint and real entryPoint in OnResolve or onLoad's args.

{
  path: 'react',
  importer: 'react',
  namespace: 'transform',
  resolveDir: '/Volumes/private/github/package',
  pluginData: undefined
}
....
{
  path: './cjs/react.development.js',
  importer: '/Volumes/private/github/package/node_modules/react/index.js',
  namespace: 'file',
  resolveDir: '/Volumes/private/github/package/node_modules/react',
  pluginData: undefined
}

The entryPoint and real entryPoint can only get one of them.
If you know how to get them at the same time, please let me know.

@hardfist
Copy link
Contributor

you can save origin entrypath in plugindata in resolve hooks,then it will carry the origin path to onload hooks by plugindata

@pandagood
Copy link
Author

@hardfist Can you provide sample code?

I know that parameters can be passed between plugins through plugindata, but the origin entryPoint already exists, there is no need to pass it.

const entryPoints = [
'react',
'react-dom',
'react-dom/server',
'react-router-dom/server'
]

I need to collect the real entryPoint of the module through the first build.

This is what i want:

{
  "react": '/Volumes/private/github/package/node_modules/react/index.js',
  "react-dom": '/Volumes/private/github/package/node_modules/react-dom/index.js',
  "react-dom/server": '/Volumes/private/github/package/node_modules/react-dom/server.browser.js'
}

But in onLoad or onResolve, the origin entryPoint and real entryPoint will not exist at the same time, only one of them will appear.

// onResolve args
{
  path: 'react',
  importer: '',
  namespace: '',
  resolveDir: '/Volumes/private/github/package',
  pluginData: undefined
}

// onLoad args, 
{
  path: 'react',
  namespace: 'transform',
  pluginData: undefined
}

Cannot match the origin entryPoint and real entryPoint .

@ggoodman
Copy link

@pandagood I know that this isn't specifically what you want but a work-around might be to use the clever hack proposed here to explicitly resolve the entrypoint using esbuild: #814 (comment).

@pandagood
Copy link
Author

@ggoodman Thanks, this is useful.

@rizrmd
Copy link

rizrmd commented Apr 21, 2021

This line is SSR:

const keys = Object.keys(require(args.path))

It would fail if we try to build browser specific package that includes window or self variables. To make it work, we need to polyfill our nodejs env with browser polyfill like jsdom. From my experience, any kind of browser polyfill will bloat global vars, make everything slower and create some unexpected behavior.

Unfortunately my project stuck in this state >.<

@rizrmd
Copy link

rizrmd commented Apr 21, 2021

I think I will try using JS parser like @babel/parser to parse the import keys.

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

4 participants