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 hook to rewrite dynamic import expressions #3449

Merged
merged 9 commits into from Mar 24, 2020

Conversation

lukastaegert
Copy link
Member

@lukastaegert lukastaegert commented Mar 20, 2020

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:
Resolves #3232
Resolves #3444

Description

This will add a new renderDynamicImportHook that allows to completely rewrite how dynamic imports are loaded, see below for details.

Also, this soft-deprecates the output.dynamicImportFunction in favour of the new plugin hook. There will be no warning yet but using the option will throw when using the strictDeprecations option. With the next major, a warning will be shown for all users.

Moreover, this will add dynamicallyImportedIds to the object returned by this.getModuleInfo.

Furthermore, this fixes an issue where .js extensions were also stripped from dynamic imports in AMD modules if the user supplied a custom string that happened to have a .js extension.

Internally, this removes the defaultPlugin entirely and replaces it with custom code in the places where the hooks are triggered. This solves some type issues (i.e. the defaults provided by the default plugin were not respected in the return type of running the hook) and also makes sure that errors in the default handlers are not confusingly reported as belonging to some Rollup Core plugin.

From the added documentation:

renderDynamicImport

Type: ({format: string, moduleId: string, targetModuleId: string | null, customResolution: string | null}) => {left: string, right: string} | null

Kind: sync, first

This hook provides fine-grained control over how dynamic imports are rendered by providing replacements for the code to the left (import() and right ()) of the argument of the import expression. Returning null defers to other hooks of this type and ultimately renders a format-specific default.

format is the rendered output format, moduleId the id of the module performing the dynamic import. If the import could be resolved to an internal or external id, then targetModuleId will be set to this id, otherwise it will be null. If the dynamic import contained a non-string expression that was resolved by a resolveDynamicImport hook to a replacement string, then customResolution will contain that string.

The following code will replace all dynamic imports with a custom handler, adding import.meta.url as a second argument to allow the handler to resolve relative imports correctly:

// plugin
const plugin = {
  name: 'dynamic-import-polyfill',
  renderDynamicImport() {
    return {
      left: 'dynamicImportPolyfill(',
      right: ', import.meta.url)'
    }
  }
};

// input
import('./lib.js');

// output
dynamicImportPolyfill('./lib.js', import.meta.url);

The next plugin will make sure all dynamic imports of esm-lib are marked as external and retained as import expressions to e.g. allow a CommonJS build to import an ES module in Node 13+, cf. how to import ES modules from CommonJS in the Node documentation.

// plugin
const plugin = {
  name: 'retain-import-expression',
  resolveDynamicImport(specifier) {
    if (specifier === 'esm-lib') return false;
    return null;
  },
  renderDynamicImport({targetModuleId}) {
    if (targetModuleId === 'esm-lib')
    return {
      left: 'import(',
      right: ')'
    }
  }
};

@rollup-bot
Copy link
Collaborator

rollup-bot commented Mar 20, 2020

Thank you for your contribution! ❤️

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

npm install rollup/rollup#render-dynamic-import-hook

or load it into the REPL:
https://rollupjs.org/repl/?circleci=10096

@codecov
Copy link

codecov bot commented Mar 20, 2020

Codecov Report

Merging #3449 into master will increase coverage by 0.05%.
The diff coverage is 99.20%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #3449      +/-   ##
==========================================
+ Coverage   95.02%   95.08%   +0.05%     
==========================================
  Files         171      171              
  Lines        5852     5861       +9     
  Branches     1727     1732       +5     
==========================================
+ Hits         5561     5573      +12     
+ Misses        157      156       -1     
+ Partials      134      132       -2     
Impacted Files Coverage Δ
src/utils/renderHelpers.ts 100.00% <ø> (ø)
src/utils/resolveId.ts 94.73% <94.73%> (ø)
src/Chunk.ts 99.77% <100.00%> (+<0.01%) ⬆️
src/Graph.ts 98.01% <100.00%> (+0.66%) ⬆️
src/ModuleLoader.ts 98.55% <100.00%> (+0.02%) ⬆️
src/ast/nodes/ImportExpression.ts 100.00% <100.00%> (ø)
src/ast/nodes/MetaProperty.ts 100.00% <100.00%> (ø)
src/rollup/rollup.ts 100.00% <100.00%> (ø)
src/utils/PluginContext.ts 100.00% <100.00%> (ø)
src/utils/PluginDriver.ts 100.00% <100.00%> (ø)
... and 2 more

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 485700e...734b98f. Read the comment docs.

@lukastaegert lukastaegert force-pushed the render-dynamic-import-hook branch 4 times, most recently from 2e6edae to e617dbd Compare March 20, 2020 11:02
@lukastaegert lukastaegert marked this pull request as ready for review March 21, 2020 19:42
@lukastaegert lukastaegert force-pushed the render-dynamic-import-hook branch 4 times, most recently from acc8e87 to 94c2955 Compare March 23, 2020 15:56
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.

Expose importer path to dynamicImportFunction How to get information about dynamic imports in plugin context
2 participants