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 hasDefaultExport to ModuleInfo #4356

Merged
merged 4 commits into from Jan 22, 2022
Merged

Add hasDefaultExport to ModuleInfo #4356

merged 4 commits into from Jan 22, 2022

Conversation

lukastaegert
Copy link
Member

@lukastaegert lukastaegert commented Jan 19, 2022

This PR contains:

  • bugfix
  • feature
  • refactor
  • documentation
  • other

Are tests included?

  • yes (bugfixes and features will not be merged without tests)
  • no

Breaking Changes?

  • yes (breaking changes will not be merged unless absolutely necessary)
  • no

List any relevant issue numbers:

Description

This is the second Plugin API extension I am working on at the moment. This time the goal is to ease to creation of proxy modules that just "reexport everything" from another module.
The problem here is that

export * from 'foo'

does NOT reexport everything from "foo"—it just reexports all NAMED exports. But if there was a default export, it will be lost (not sure why things were designed that way, but that is what the specs say and what Rollup does). So if you also want to reexport the default export as well, you have to know whether it exists before writing the proxy.

My suggestion (implemented in this PR) is to add a flag hasDefaultExport: boolean | null to ModuleInfo. The value null is used for external modules and before the module has been parsed.

When you const moduleInfo = await this.load({id}), then you can be sure that (if the module exists), moduleInfo.hasDefaultExport is the correct value.

@yyx990803, @patak-dev, @antfu, @marvinhagemeister not sure if this would be a problem to implement in Vite or WMR. My hope was that you already have some kind of information available, in the worst case you could probably do something with regular expressions executed when accessing a getter to figure this out.

Here is an example from the docs that shows how to use this:

export default function addProxyPlugin() {
  return {
    async resolveId(source, importer, options) {
      if (importer?.endsWith('?proxy')) {
        // Do not proxy ids used in proxies
        return null;
      }
      // We make sure to pass on any resolveId options to this.resolve to get the module id
      const resolution = await this.resolve(source, importer, { skipSelf: true, ...options });
      // We can only pre-load existing and non-external ids
      if (resolution && !resolution.external) {
        // we pass on the entire resolution information
        const moduleInfo = await this.load(resolution);
        if (moduleInfo.code.indexOf('/* use proxy */') >= 0) {
          return `${resolution.id}?proxy`;
        }
      }
      // As we already fully resolved the module, there is no reason to resolve it again
      return resolution;
    },
    load(id) {
      if (id.endsWith('?proxy')) {
        const importee = id.slice(0, -'?proxy'.length);
        // Note that namespace reexports do not reexport default exports
        let code = `console.log('proxy for ${importee}'); export * from ${JSON.stringify(
          importee
        )};`;
        // We know that while resolving the proxy, importee was already fully
        // loaded and parsed, so we can rely on hasDefaultExport
        if (this.getModuleInfo(importee).hasDefaultExport) {
          code += `export { default } from ${JSON.stringify(importee)};`;
        }
        return code;
      }
      return null;
    }
  };
}

I also considered if we should make the names of all exports available, but the problem here would be how to handle namespace reexports: If we do it right, we would need to merge the named exports of another module, and the logic would quickly get complicated and problematic, or invent a special syntax for that case, or ignore that case, none of which seemed right to me. So that is why I would go for this boolean for now.

@github-actions
Copy link

github-actions bot commented Jan 19, 2022

Thank you for your contribution! ❤️

You can try out this pull request locally by installing Rollup via

npm install rollup/rollup#module-info-exports

or load it into the REPL:
https://rollupjs.org/repl/?pr=4356

@codecov
Copy link

codecov bot commented Jan 19, 2022

Codecov Report

Merging #4356 (9918ee1) into master (bebc50d) will increase coverage by 0.00%.
The diff coverage is 100.00%.

Impacted file tree graph

@@           Coverage Diff           @@
##           master    #4356   +/-   ##
=======================================
  Coverage   98.69%   98.69%           
=======================================
  Files         205      205           
  Lines        7331     7334    +3     
  Branches     2084     2086    +2     
=======================================
+ Hits         7235     7238    +3     
  Misses         36       36           
  Partials       60       60           
Impacted Files Coverage Δ
src/ExternalModule.ts 100.00% <ø> (ø)
src/Module.ts 100.00% <100.00%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update bebc50d...9918ee1. Read the comment docs.

@patak-dev
Copy link
Contributor

Looks good. I don't see an issue implementing this in the Vite dev server once we need to support it. Thanks for keeping us in the loop @lukastaegert!

@lukastaegert
Copy link
Member Author

That is good. Turns out I would really love to have this quickly to fix an issue with rollup/plugins#1038 so I will merge this.

@lukastaegert lukastaegert changed the base branch from imported-id-resolutions to master January 21, 2022 05:42
@lukastaegert lukastaegert changed the base branch from master to imported-id-resolutions January 21, 2022 05:45
Base automatically changed from imported-id-resolutions to master January 21, 2022 05:57
@lukastaegert lukastaegert merged commit e88edfd into master Jan 22, 2022
@lukastaegert lukastaegert deleted the module-info-exports branch January 22, 2022 06:21
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 this pull request may close these issues.

None yet

2 participants