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

feat: add exports and exportedBindings to Module class #4731

Merged
merged 12 commits into from Dec 5, 2022
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);
TrickyPi marked this conversation as resolved.
Show resolved Hide resolved
}

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