Skip to content

Commit

Permalink
feat: add resolvedBy field to ResolvedId (#4789)
Browse files Browse the repository at this point in the history
* feat: add resolveBy field

* test: fix previous tests

* chore: tweak

* chore: replace resolveBy to resolvedBy

* feat: add hookFirstAndGetPlugin to PluginDriver

* chore: tweak test

* docs: add documentation for resolvedBy

* docs: update documentation for resolvedBy

* Improve docs and slightly streamline code

Co-authored-by: Lukas Taegert-Atkinson <lukastaegert@users.noreply.github.com>
Co-authored-by: Lukas Taegert-Atkinson <lukas.taegert-atkinson@tngtech.com>
  • Loading branch information
3 people committed Jan 12, 2023
1 parent ade0ee3 commit b26a37f
Show file tree
Hide file tree
Showing 26 changed files with 164 additions and 17 deletions.
4 changes: 2 additions & 2 deletions browser/src/resolveId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export async function resolveId(
assertions
);
if (pluginResult == null) {
throwNoFileSystem('path.resolve');
return throwNoFileSystem('path.resolve')();
}
return pluginResult;
return pluginResult[0];
}
9 changes: 7 additions & 2 deletions docs/05-plugin-development.md
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ Note that the return value of this hook will not be passed to `resolveId` afterw

#### `resolveId`

**Type:** `(source: string, importer: string | undefined, options: {isEntry: boolean, assertions: {[key: string]: string}, custom?: {[plugin: string]: any}}) => string | false | null | {id: string, external?: boolean | "relative" | "absolute", assertions?: {[key: string]: string} | null, meta?: {[plugin: string]: any} | null, moduleSideEffects?: boolean | "no-treeshake" | null, syntheticNamedExports?: boolean | string | null}`<br> **Kind:** `async, first`<br> **Previous Hook:** [`buildStart`](guide/en/#buildstart) if we are resolving an entry point, [`moduleParsed`](guide/en/#moduleparsed) if we are resolving an import, or as fallback for [`resolveDynamicImport`](guide/en/#resolvedynamicimport). Additionally, this hook can be triggered during the build phase from plugin hooks by calling [`this.emitFile`](guide/en/#thisemitfile) to emit an entry point or at any time by calling [`this.resolve`](guide/en/#thisresolve) to manually resolve an id.<br> **Next Hook:** [`load`](guide/en/#load) if the resolved id has not yet been loaded, otherwise [`buildEnd`](guide/en/#buildend).
**Type:** `(source: string, importer: string | undefined, options: {isEntry: boolean, assertions: {[key: string]: string}, custom?: {[plugin: string]: any}}) => string | false | null | {id: string, external?: boolean | "relative" | "absolute", assertions?: {[key: string]: string} | null, meta?: {[plugin: string]: any} | null, moduleSideEffects?: boolean | "no-treeshake" | null, resolvedBy?: string, syntheticNamedExports?: boolean | string | null}`<br> **Kind:** `async, first`<br> **Previous Hook:** [`buildStart`](guide/en/#buildstart) if we are resolving an entry point, [`moduleParsed`](guide/en/#moduleparsed) if we are resolving an import, or as fallback for [`resolveDynamicImport`](guide/en/#resolvedynamicimport). Additionally, this hook can be triggered during the build phase from plugin hooks by calling [`this.emitFile`](guide/en/#thisemitfile) to emit an entry point or at any time by calling [`this.resolve`](guide/en/#thisresolve) to manually resolve an id.<br> **Next Hook:** [`load`](guide/en/#load) if the resolved id has not yet been loaded, otherwise [`buildEnd`](guide/en/#buildend).

Defines a custom resolver. A resolver can be useful for e.g. locating third-party dependencies. Here `source` is the importee exactly as it is written in the import statement, i.e. for

Expand Down Expand Up @@ -312,6 +312,8 @@ If `external` is `true`, then absolute ids will be converted to relative ids bas

If `false` is returned for `moduleSideEffects` in the first hook that resolves a module id and no other module imports anything from this module, then this module will not be included even if the module would have side effects. If `true` is returned, Rollup will use its default algorithm to include all statements in the module that have side effects (such as modifying a global or exported variable). If `"no-treeshake"` is returned, treeshaking will be turned off for this module and it will also be included in one of the generated chunks even if it is empty. If `null` is returned or the flag is omitted, then `moduleSideEffects` will be determined by the `treeshake.moduleSideEffects` option or default to `true`. The `load` and `transform` hooks can override this.

`resolvedBy` can be explicitly declared in the returned object. It will replace the corresponding field returned by [`this.resolve`](guide/en/#thisresolve).

If you return a value for `assertions` for an external module, this will determine how imports of this module will be rendered when generating `"es"` output. E.g. `{id: "foo", external: true, assertions: {type: "json"}}` will cause imports of this module appear as `import "foo" assert {type: "json"}`. If you do not pass a value, the value of the `assertions` input parameter will be used. Pass an empty object to remove any assertions. While `assertions` do not influence rendering for bundled modules, they still need to be consistent across all imports of a module, otherwise a warning is emitted. The `load` and `transform` hooks can override this.

See [synthetic named exports](guide/en/#synthetic-named-exports) for the effect of the `syntheticNamedExports` option. If `null` is returned or the flag is omitted, then `syntheticNamedExports` will default to `false`. The `load` and `transform` hooks can override this.
Expand Down Expand Up @@ -809,6 +811,7 @@ type ResolvedId = {
assertions: { [key: string]: string }; // import assertions for this import
meta: { [plugin: string]: any }; // custom module meta-data when resolving the module
moduleSideEffects: boolean | 'no-treeshake'; // are side effects of the module observed, is tree-shaking enabled
resolvedBy: string; // which plugin resolved this module, "rollup" if resolved by Rollup itself
syntheticNamedExports: boolean | string; // does the module allow importing non-existing named exports
};
```
Expand Down Expand Up @@ -975,7 +978,7 @@ Use Rollup's internal acorn instance to parse code to an AST.
#### `this.resolve`
**Type:** `(source: string, importer?: string, options?: {skipSelf?: boolean, isEntry?: boolean, assertions?: {[key: string]: string}, custom?: {[plugin: string]: any}}) => Promise<{id: string, external: boolean | "absolute", assertions: {[key: string]: string}, meta: {[plugin: string]: any} | null, moduleSideEffects: boolean | "no-treeshake", syntheticNamedExports: boolean | string>`
**Type:** `(source: string, importer?: string, options?: {skipSelf?: boolean, isEntry?: boolean, assertions?: {[key: string]: string}, custom?: {[plugin: string]: any}}) => Promise<{id: string, external: boolean | "absolute", assertions: {[key: string]: string}, meta: {[plugin: string]: any} | null, moduleSideEffects: boolean | "no-treeshake", resolvedBy: string, syntheticNamedExports: boolean | string>`
Resolve imports to module ids (i.e. file names) using the same plugins that Rollup uses, and determine if an import should be external. If `null` is returned, the import could not be resolved by Rollup or any plugin but was not explicitly marked as external by the user. If an absolute external id is returned that should remain absolute in the output either via the [`makeAbsoluteExternalsRelative`](guide/en/#makeabsoluteexternalsrelative) option or by explicit plugin choice in the [`resolveId`](guide/en/#resolveid) hook, `external` will be `"absolute"` instead of `true`.
Expand All @@ -989,6 +992,8 @@ If you pass an object for `assertions`, it will simulate resolving an import wit
When calling this function from a `resolveId` hook, you should always check if it makes sense for you to pass along the `isEntry`, `custom` and `assertions` options.
The value of `resolvedBy` refers to which plugin resolved this source. If it was resolved by Rollup itself, the value will be "rollup". If a `resolveId` hook in a plugin resolves this source, the value will be the name of the plugin unless it returned an explicit value for `resolvedBy`. This flag is only for debugging and documentation purposes and is not processed further by Rollup.
#### `this.setAssetSource`
**Type:** `(referenceId: string, source: string | Uint8Array) => void`
Expand Down
3 changes: 3 additions & 0 deletions src/ModuleLoader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export type ModuleLoaderResolveId = (
type NormalizedResolveIdWithoutDefaults = Partial<PartialNull<ModuleOptions>> & {
external?: boolean | 'absolute';
id: string;
resolvedBy?: string;
};

type ResolveStaticDependencyPromise = Promise<readonly [source: string, resolvedId: ResolvedId]>;
Expand Down Expand Up @@ -586,6 +587,7 @@ export class ModuleLoader {
meta: resolvedId.meta || {},
moduleSideEffects:
resolvedId.moduleSideEffects ?? this.hasModuleSideEffects(resolvedId.id, !!external),
resolvedBy: resolvedId.resolvedBy ?? 'rollup',
syntheticNamedExports: resolvedId.syntheticNamedExports ?? false
};
}
Expand Down Expand Up @@ -625,6 +627,7 @@ export class ModuleLoader {
id: source,
meta: {},
moduleSideEffects: this.hasModuleSideEffects(source, true),
resolvedBy: 'rollup',
syntheticNamedExports: false
};
} else if (resolvedId.external && resolvedId.syntheticNamedExports) {
Expand Down
4 changes: 4 additions & 0 deletions src/rollup/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ export interface PluginContextMeta {
export interface ResolvedId extends ModuleOptions {
external: boolean | 'absolute';
id: string;
resolvedBy: string;
}

export interface ResolvedIdMap {
Expand All @@ -225,10 +226,13 @@ export interface ResolvedIdMap {
interface PartialResolvedId extends Partial<PartialNull<ModuleOptions>> {
external?: boolean | 'absolute' | 'relative';
id: string;
resolvedBy?: string;
}

export type ResolveIdResult = string | NullValue | false | PartialResolvedId;

export type ResolveIdResultWithoutNullValue = string | false | PartialResolvedId;

export type ResolveIdHook = (
this: PluginContext,
source: string,
Expand Down
23 changes: 16 additions & 7 deletions src/utils/PluginDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,15 +133,24 @@ export class PluginDriver {
replaceContext?: ReplaceContext | null,
skipped?: ReadonlySet<Plugin> | null
): Promise<ReturnType<FunctionPluginHooks[H]> | null> {
let promise: Promise<ReturnType<FunctionPluginHooks[H]> | null> = Promise.resolve(null);
return this.hookFirstAndGetPlugin(hookName, parameters, replaceContext, skipped).then(
result => result && result[0]
);
}

// chains, first non-null result stops and returns result and last plugin
async hookFirstAndGetPlugin<H extends AsyncPluginHooks & FirstPluginHooks>(
hookName: H,
parameters: Parameters<FunctionPluginHooks[H]>,
replaceContext?: ReplaceContext | null,
skipped?: ReadonlySet<Plugin> | null
): Promise<[NonNullable<ReturnType<FunctionPluginHooks[H]>>, Plugin] | null> {
for (const plugin of this.getSortedPlugins(hookName)) {
if (skipped && skipped.has(plugin)) continue;
promise = promise.then(result => {
if (result != null) return result;
return this.runHook(hookName, parameters, plugin, replaceContext);
});
if (skipped?.has(plugin)) continue;
const result = await this.runHook(hookName, parameters, plugin, replaceContext);
if (result != null) return [result, plugin];
}
return promise;
return null;
}

// chains synchronously, first non-null result stops and returns
Expand Down
18 changes: 17 additions & 1 deletion src/utils/resolveId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,23 @@ export async function resolveId(
isEntry,
assertions
);
if (pluginResult != null) return pluginResult;

if (pluginResult != null) {
const [resolveIdResult, plugin] = pluginResult;
if (typeof resolveIdResult === 'object' && !resolveIdResult.resolvedBy) {
return {
...resolveIdResult,
resolvedBy: plugin.name
};
}
if (typeof resolveIdResult === 'string') {
return {
id: resolveIdResult,
resolvedBy: plugin.name
};
}
return resolveIdResult;
}

// external modules (non-entry modules that start with neither '.' or '/')
// are skipped at this stage.
Expand Down
4 changes: 2 additions & 2 deletions src/utils/resolveIdViaPlugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function resolveIdViaPlugins(
customOptions: CustomPluginOptions | undefined,
isEntry: boolean,
assertions: Record<string, string>
): Promise<ResolveIdResult> {
): Promise<[NonNullable<ResolveIdResult>, Plugin] | null> {
let skipped: Set<Plugin> | null = null;
let replaceContext: ReplaceContext | null = null;
if (skip) {
Expand All @@ -35,7 +35,7 @@ export function resolveIdViaPlugins(
)
});
}
return pluginDriver.hookFirst(
return pluginDriver.hookFirstAndGetPlugin(
'resolveId',
[source, importer, { assertions, custom: customOptions, isEntry }],
replaceContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,10 @@ console.log(foo);`
})
},
error: {
code: 'NO_FS_IN_BROWSER',
message:
'Unexpected warnings (UNRESOLVED_IMPORT): "dep" is imported by "main", but could not be resolved – treating it as an external dependency.\nIf you expect warnings, list their codes in config.expectedWarnings',
'Cannot access the file system (via "path.resolve") when using the browser build of Rollup. Make sure you supply a plugin with custom resolveId and load hooks to Rollup.',
url: 'https://rollupjs.org/guide/en/#a-simple-example',
watchFiles: ['main']
}
};
6 changes: 4 additions & 2 deletions test/browser/samples/missing-entry-resolution/_config.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
module.exports = {
description: 'fails if an entry cannot be resolved',
error: {
code: 'UNRESOLVED_ENTRY',
message: 'Could not resolve entry module "main".'
code: 'NO_FS_IN_BROWSER',
message:
'Cannot access the file system (via "path.resolve") when using the browser build of Rollup. Make sure you supply a plugin with custom resolveId and load hooks to Rollup.',
url: 'https://rollupjs.org/guide/en/#a-simple-example'
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ module.exports = {
id: ID_LIB,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
],
Expand Down Expand Up @@ -167,6 +168,7 @@ module.exports = {
id: ID_LIB,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ module.exports = {
id: ID_LIB,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
],
Expand Down Expand Up @@ -163,6 +164,7 @@ module.exports = {
id: ID_LIB,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ module.exports = {
id: ID_LIB1,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
},
{
Expand All @@ -142,6 +143,7 @@ module.exports = {
id: ID_LIB1B,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
},
{
Expand All @@ -150,6 +152,7 @@ module.exports = {
id: ID_LIB2,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
],
Expand Down Expand Up @@ -262,6 +265,7 @@ module.exports = {
id: ID_LIB1,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
},
{
Expand All @@ -270,6 +274,7 @@ module.exports = {
id: ID_LIB1B,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
},
{
Expand All @@ -278,6 +283,7 @@ module.exports = {
id: ID_LIB3,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
],
Expand Down Expand Up @@ -389,6 +395,7 @@ module.exports = {
id: ID_LIB1,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
},
{
Expand All @@ -397,6 +404,7 @@ module.exports = {
id: ID_LIB2,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
},
{
Expand All @@ -405,6 +413,7 @@ module.exports = {
id: ID_LIB3,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ module.exports = {
id: ID_LIB,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
],
Expand Down Expand Up @@ -162,6 +163,7 @@ module.exports = {
id: ID_LIB,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
],
Expand Down
9 changes: 9 additions & 0 deletions test/function/samples/context-resolve/_config.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const tests = [
assertions: {},
external: false,
meta: {},
resolvedBy: 'rollup',
moduleSideEffects: true,
syntheticNamedExports: false
}
Expand All @@ -29,6 +30,7 @@ const tests = [
external: true,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
},
Expand All @@ -40,6 +42,7 @@ const tests = [
external: true,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
},
Expand All @@ -51,6 +54,7 @@ const tests = [
external: true,
meta: {},
moduleSideEffects: true,
resolvedBy: 'rollup',
syntheticNamedExports: false
}
},
Expand All @@ -62,6 +66,7 @@ const tests = [
external: false,
meta: {},
moduleSideEffects: true,
resolvedBy: 'at position 2',
syntheticNamedExports: false
}
},
Expand All @@ -72,6 +77,7 @@ const tests = [
assertions: {},
external: true,
meta: {},
resolvedBy: 'rollup',
moduleSideEffects: true,
syntheticNamedExports: false
}
Expand All @@ -84,6 +90,7 @@ const tests = [
external: false,
meta: {},
moduleSideEffects: true,
resolvedBy: 'at position 2',
syntheticNamedExports: false
}
},
Expand All @@ -95,6 +102,7 @@ const tests = [
external: false,
meta: {},
moduleSideEffects: true,
resolvedBy: 'at position 2',
syntheticNamedExports: false
}
},
Expand All @@ -106,6 +114,7 @@ const tests = [
external: true,
meta: {},
moduleSideEffects: true,
resolvedBy: 'at position 2',
syntheticNamedExports: false
}
}
Expand Down

0 comments on commit b26a37f

Please sign in to comment.