Skip to content

Commit

Permalink
feat: add exports and exportedBindings to Module class (#4731)
Browse files Browse the repository at this point in the history
* feat: add exportsInfo to Module class

* test: regression test

* feat: reimplement

* feat: optimize

* test: add test

* test: tweak

* fix: avoid overwriting

* test: change test

* test: tweak

* feat: tweak

* docs: add description about 'exports' and 'exportedBindings'

Co-authored-by: Lukas Taegert-Atkinson <lukastaegert@users.noreply.github.com>
  • Loading branch information
TrickyPi and lukastaegert committed Dec 5, 2022
1 parent 9900792 commit de6675b
Show file tree
Hide file tree
Showing 17 changed files with 152 additions and 1 deletion.
4 changes: 3 additions & 1 deletion docs/05-plugin-development.md
Expand Up @@ -784,6 +784,8 @@ type ModuleInfo = {
importedIds: string[]; // the module ids statically imported by this module
importedIdResolutions: ResolvedId[]; // how statically imported ids were resolved, for use with this.load
importers: string[]; // the ids of all modules that statically import this module
exportedBindings: Record<string, string[]> | null; // contains all exported variables associated with the path of `from`, `null` if external
exports: string[] | null; // all exported variables, `null` if external
dynamicallyImportedIds: string[]; // the module ids imported by this module via dynamic import()
dynamicallyImportedIdResolutions: ResolvedId[]; // how ids imported via dynamic import() were resolved
dynamicImporters: string[]; // the ids of all modules that import this module via dynamic import()
Expand All @@ -808,7 +810,7 @@ type ResolvedId = {
During the build, this object represents currently available information about the module which may be inaccurate before the [`buildEnd`](guide/en/#buildend) hook:
- `id` and `isExternal` will never change.
- `code`, `ast` and `hasDefaultExport` are only available after parsing, i.e. in the [`moduleParsed`](guide/en/#moduleparsed) hook or after awaiting [`this.load`](guide/en/#thisload). At that point, they will no longer change.
- `code`, `ast`, `hasDefaultExport`, `exports` and `exportedBindings` are only available after parsing, i.e. in the [`moduleParsed`](guide/en/#moduleparsed) hook or after awaiting [`this.load`](guide/en/#thisload). At that point, they will no longer change.
- if `isEntry` is `true`, it will no longer change. It is however possible for modules to become entry points after they are parsed, either via [`this.emitFile`](guide/en/#thisemitfile) or because a plugin inspects a potential entry point via [`this.load`](guide/en/#thisload) in the [`resolveId`](guide/en/#resolveid) hook when resolving an entry point. Therefore, it is not recommended relying on this flag in the [`transform`](guide/en/#transform) hook. It will no longer change after `buildEnd`.
- Similarly, `implicitlyLoadedAfterOneOf` can receive additional entries at any time before `buildEnd` via [`this.emitFile`](guide/en/#thisemitfile).
- `importers`, `dynamicImporters` and `implicitlyLoadedBefore` will start as empty arrays, which receive additional entries as new importers and implicit dependents are discovered. They will no longer change after `buildEnd`.
Expand Down
2 changes: 2 additions & 0 deletions src/ExternalModule.ts
Expand Up @@ -38,6 +38,8 @@ export default class ExternalModule {
get dynamicImporters() {
return dynamicImporters.sort();
},
exportedBindings: null,
exports: null,
hasDefaultExport: null,
get hasModuleSideEffects() {
warnDeprecation(
Expand Down
22 changes: 22 additions & 0 deletions src/Module.ts
Expand Up @@ -274,6 +274,8 @@ export default class Module {
const {
dynamicImports,
dynamicImporters,
exportAllSources,
exports,
implicitlyLoadedAfter,
implicitlyLoadedBefore,
importers,
Expand All @@ -298,6 +300,26 @@ export default class Module {
get dynamicImporters() {
return dynamicImporters.sort();
},
get exportedBindings() {
const exportBindings: Record<string, string[]> = { '.': [...exports.keys()] };

for (const [name, { source }] of reexportDescriptions) {
(exportBindings[source] ??= []).push(name);
}

for (const source of exportAllSources) {
(exportBindings[source] ??= []).push('*');
}

return exportBindings;
},
get exports() {
return [
...exports.keys(),
...reexportDescriptions.keys(),
...[...exportAllSources].map(() => '*')
];
},
get hasDefaultExport() {
// This information is only valid after parsing
if (!module.ast) {
Expand Down
2 changes: 2 additions & 0 deletions src/rollup/types.d.ts
Expand Up @@ -157,6 +157,8 @@ interface ModuleInfo extends ModuleOptions {
dynamicImporters: readonly string[];
dynamicallyImportedIdResolutions: readonly ResolvedId[];
dynamicallyImportedIds: readonly string[];
exportedBindings: Record<string, string[]> | null;
exports: string[] | null;
hasDefaultExport: boolean | null;
/** @deprecated Use `moduleSideEffects` instead */
hasModuleSideEffects: boolean | 'no-treeshake';
Expand Down
Expand Up @@ -75,6 +75,10 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: {
'.': []
},
exports: [],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -148,6 +152,10 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: {
'.': []
},
exports: [],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down
Expand Up @@ -71,6 +71,10 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: {
'.': []
},
exports: [],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -144,6 +148,10 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exports: [],
exportedBindings: {
'.': []
},
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down
Expand Up @@ -119,6 +119,10 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: {
'.': []
},
exports: [],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -243,6 +247,10 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: {
'.': []
},
exports: [],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -366,6 +374,10 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: {
'.': []
},
exports: [],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [ID_MAIN1, ID_MAIN2],
Expand Down
Expand Up @@ -70,6 +70,10 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: {
'.': []
},
exports: [],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -143,6 +147,10 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: {
'.': []
},
exports: [],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [ID_MAIN],
Expand Down
@@ -0,0 +1,33 @@
const assert = require('node:assert');
const path = require('node:path');

const ID_MAIN = path.join(__dirname, 'main.js');
const ID_MODULE = path.join(__dirname, 'module.js');
const ID_MODULE_2 = path.join(__dirname, 'module2.js');

const expectedResult = {
[ID_MAIN]: {
exports: ['moduleAlias', '*'],
exportedBindings: { '.': [], './module.js': ['moduleAlias', '*'] }
},
[ID_MODULE]: {
exports: ['default', 'module', '*'],
exportedBindings: { '.': ['default', 'module'], './module2.js': ['*'] }
},
[ID_MODULE_2]: {
exports: ['module2'],
exportedBindings: { '.': ['module2'] }
}
};

module.exports = {
description: 'check exports and exportedBindings in moduleParsed as a supplementary test',
options: {
plugins: {
moduleParsed(moduleInfo) {
const { exports, exportedBindings, id } = moduleInfo;
assert.deepStrictEqual({ exports, exportedBindings }, expectedResult[id]);
}
}
}
};
@@ -0,0 +1,3 @@
export * from './module.js';
export { module as moduleAlias } from './module.js';
assert.ok(true);
@@ -0,0 +1,3 @@
export * from './module2.js';
export default 1;
export const module = 1;
@@ -0,0 +1 @@
export const module2 = 1;
Expand Up @@ -91,6 +91,8 @@ module.exports = {
],
dynamicallyImportedIds: ['external'],
dynamicImporters: [getId('main')],
exports: ['promise', 'internal'],
exportedBindings: { '.': ['promise'], './lib': ['internal'] },
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -134,6 +136,8 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: { '.': ['default'] },
exports: ['default'],
hasDefaultExport: true,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -242,6 +246,8 @@ module.exports = {
],
dynamicallyImportedIds: [getId('dynamic')],
dynamicImporters: [],
exportedBindings: { '.': ['promise'], './lib': ['value'], external: ['external'] },
exports: ['promise', 'value', 'external'],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -280,6 +286,8 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [getId('dynamic')],
exportedBindings: null,
exports: null,
hasDefaultExport: null,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down
12 changes: 12 additions & 0 deletions test/function/samples/manual-chunks-info/_config.js
Expand Up @@ -92,6 +92,8 @@ module.exports = {
],
dynamicallyImportedIds: ['external'],
dynamicImporters: [getId('main')],
exportedBindings: { '.': ['promise'], './lib': ['internal'] },
exports: ['promise', 'internal'],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -135,6 +137,8 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: { '.': ['default'] },
exports: ['default'],
hasDefaultExport: true,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -243,6 +247,12 @@ module.exports = {
],
dynamicallyImportedIds: [getId('dynamic')],
dynamicImporters: [],
exportedBindings: {
'.': ['promise'],
'./lib': ['value'],
external: ['external']
},
exports: ['promise', 'value', 'external'],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -281,6 +291,8 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [getId('dynamic')],
exportedBindings: null,
exports: null,
hasDefaultExport: null,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down
4 changes: 4 additions & 0 deletions test/function/samples/module-parsed-hook/_config.js
Expand Up @@ -53,6 +53,8 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: { '.': [], './dep.js': ['value'] },
exports: ['value'],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -112,6 +114,8 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [],
exportedBindings: { '.': ['value'] },
exports: ['value'],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down
15 changes: 15 additions & 0 deletions test/function/samples/plugin-module-information/_config.js
Expand Up @@ -20,6 +20,10 @@ module.exports = {
ast: null,
code: null,
dynamicImporters: [],
exportedBindings: {
'.': []
},
exports: [],
hasDefaultExport: null,
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
Expand Down Expand Up @@ -113,6 +117,8 @@ module.exports = {
},
code: "import path from 'path';\n\nexport const foo = path.resolve('foo');\n",
dynamicallyImportedIdResolutions: [],
exportedBindings: { '.': ['foo'] },
exports: ['foo'],
dynamicallyImportedIds: [],
dynamicImporters: [],
hasDefaultExport: false,
Expand Down Expand Up @@ -284,6 +290,11 @@ module.exports = {
],
dynamicallyImportedIds: [ID_NESTED, ID_PATH],
dynamicImporters: [],
exportedBindings: {
'.': ['nested', 'path', 'pathAgain'],
'./foo.js': ['foo']
},
exports: ['nested', 'path', 'pathAgain', 'foo'],
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -377,6 +388,8 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [ID_MAIN],
exports: ['nested'],
exportedBindings: { '.': ['nested'] },
hasDefaultExport: false,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down Expand Up @@ -407,6 +420,8 @@ module.exports = {
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
dynamicImporters: [ID_MAIN],
exportedBindings: null,
exports: null,
hasDefaultExport: null,
moduleSideEffects: true,
implicitlyLoadedAfterOneOf: [],
Expand Down
8 changes: 8 additions & 0 deletions test/function/samples/preload-module/_config.js
Expand Up @@ -35,6 +35,10 @@ module.exports = {
assertions: {},
code: "import './dep';\nassert.ok(true);\n",
dynamicImporters: [],
exportedBindings: {
'.': []
},
exports: [],
hasDefaultExport: false,
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
Expand Down Expand Up @@ -74,6 +78,10 @@ module.exports = {
assertions: {},
code: 'assert.ok(true);\n',
dynamicImporters: [],
exportedBindings: {
'.': []
},
exports: [],
hasDefaultExport: false,
dynamicallyImportedIdResolutions: [],
dynamicallyImportedIds: [],
Expand Down

0 comments on commit de6675b

Please sign in to comment.