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

how to change default interpret result in onLoad callbacks ? #1720

Closed
yangdm2015 opened this issue Oct 27, 2021 · 6 comments
Closed

how to change default interpret result in onLoad callbacks ? #1720

yangdm2015 opened this issue Oct 27, 2021 · 6 comments

Comments

@yangdm2015
Copy link

Thanks for this wonderful tool!!

When build a plugin,I've noticed that the callback added by onLoad can only get the path part of any import statements, but not the whole statement.

Also,what we can do is changing things before esbuild interpret it. if its possible to change interpret result ?
like:

//a.js
module.exports = { name: 1 };
// b.js
import * as a from './a.js'
console.log('a=',a)

if I write a plugin like this

let passPlugin = {
  name: "pass",
  //enforce: 'post',  wish to have something like this that can config to run this plugin after esbuild build
  setup(build) {
    build.onLoad({ filter: /.*js/ }, async (args) => {
    //  Currently, plugins  always run  before esbuild interpret  code.
	// wish to run this plugin after esbuild interpreted 
	//get  the result from [args.contents],get the whole  import part from [args.statement]
	// like this:
	// const {contents,statement } =args
	// if(statement.includes('import * as ') ){
	//     delete contents.defalut
	//}
	// return {contents}
      const source = await fs.promises.readFile(args.path, "utf8");
      return { contents: source };
    });
  },
};

I hope to get what esbuild original interpreted and change to something other .
Is any current apis allow me to do this ?

@evanw
Copy link
Owner

evanw commented Oct 27, 2021

What you are trying to do is not well-defined. The same module can be imported in multiple places, and one import may look like import * as x from 'y' while another one may look like import x from 'y'. One reason why onLoad is not related to the import that caused it is because there is no one import that caused it. Another reason is that files in esbuild are processed in parallel so it's not possible to provide a list of all imports. And even without parallelism, the file being loaded may end up importing another module that then imports the file being loaded, so it may actually be impossible to provide all imports to onLoad since discovering them depends on the onLoad operation.

Furthermore code may do import * as x from 'y' and then later access x.default, possibly even in a dynamic way that you won't be able to determine by reading the source code, such as passing that object to another module that then accesses the default property.

I recommend having your plugin explicitly only remove the default property for the specific library that your code is having trouble with. It's hard/impossible to figure this out automatically and also the default property is actually supposed to be there, so many npm packages will expect it to be there and will then break if you remove it.

@yangdm2015
Copy link
Author

not well-defined

Thanks for your reply!
I get what your said. What I want to do is exactly only remove the default property for the specific library that your code is having trouble with.

but I don't know to do this with esbuild current api. The reasons are what I have said above:( sorry for my poor language )

  • I can't get the content that esbuild interpreted becouse plugins always run before esbuild begin interpret

@evanw
Copy link
Owner

evanw commented Oct 27, 2021

There are various ways of doing this. One way would be to modify the imported file itself. For example, if the problem is that some code is doing import * as a from './a.js' and then crashing because of the default export, you could potentially fix that with a plugin like this:

let example = {
  name: "example",
  setup(build) {
    build.onLoad({ filter: /[\/\\]a\.js$/ }, async (args) => {
      let source = await fs.promises.readFile(args.path, "utf8");
      // Change a CommonJS module into an ES module
      source = `
        var exports = {};
        var module = { exports };
        ${source}
        ${Object.keys(require(args.path))
          .map(x => `; export var ${x} = module.exports.${x}`)
          .join('\n')}
      `
      return { contents: source };
    });
  },
};

You are basically rewriting the CommonJS module as an ES module, which then won't get a default property added to it. This may or may not work depending on the specific library you are using. You may have to customize how you are rewriting the library for the specifics of the library in question.

This is assuming you don't have control over the problematic code that is importing the library and then crashing. If you do, then the most straightforward solution is likely to just fix the code to not crash. Then you don't need to modify esbuild at all.

@yangdm2015
Copy link
Author

let example = {
  name: "example",
  setup(build) {
    build.onLoad({ filter: /[\/\\]a\.js$/ }, async (args) => {
      let source = await fs.promises.readFile(args.path, "utf8");
      // Change a CommonJS module into an ES module
      source = `
        var exports = {};
        var module = { exports };
        ${source}
        ${Object.keys(require(args.path))
          .map(x => `; export var ${x} = module.exports.${x}`)
          .join('\n')}
      `
      return { contents: source };
    });
  },
};

Great work!
The only problem is that for those imports like this
import * as x from 'y' I want to delete the default prop, for imports like import x from y ,just keep it as it is.
All these work is meant to make the output content more "webpack like". I recently migrate from webpack to vite which use esbuild as its compiler, so I think this would bring a great benifit to many developers.

@evanw
Copy link
Owner

evanw commented Jan 6, 2022

Sorry, you'll have to figure out how to do that yourself. You can do this with an esbuild onLoad plugin that substitutes alternate file content, and you can transform the content however you see fit with whatever tools you choose. I'm going to close this issue since it's not a bug with esbuild.

Keep in mind that what you're asking for is still not well defined because import * as x from 'y'; console.log(x.default) and import x from 'y'; console.log(x) are equivalent syntax. Note that esbuild's behavior changed recently with #1849 to be more Webpack-like. Specifically esbuild now uses node's way of handling default in .mjs files and files in packages tagged with "type": "module" in the package.json file. I'm mentioning that in case that fixes your problem.

@evanw evanw closed this as completed Jan 6, 2022
@yangdm2015
Copy link
Author

yangdm2015 commented Jan 6, 2022 via email

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