Resolver

Resolver plugins are responsible for turning a dependency specifier into a full file path that will be processed by transformers. Resolvers run in a pipeline until one of them returns a result. See Dependency resolution for details on how the default resolver works.

Resolvers can be written in JavaScript, or as native plugins in Rust, Go, or Swift. Choose a language above any example to switch the examples and API names throughout the page.

Example

#

This example overrides the resolution of special-module, and otherwise returns null to allow the next resolver in the pipeline to handle the dependency. See Resolvers in the Parcel configuration docs for details on how this works.

import { Resolver } from "@parcel/plugin";
import path from "path";

export default new Resolver({
async resolve({ specifier }) {
if (specifier === "special-module") {
return {
filePath: path.join(__dirname, "special-module.js"),
};
}

// Let the next resolver in the pipeline handle
// this dependency.
return null;
},
});

See Native plugins for how to build native plugins and use them in a project. The rest of the examples on this page omit the setup code, and only show the resolve method.

Loading configuration

#

Configuration for a Resolver plugin is specified in the user's .parcelrc. JavaScript plugins receive it as the config option of the resolve method, and native plugins receive it as JSON when they are created. See Configuration for details, and the Transformer docs for an example in each language.

Virtual modules

#

Rather than resolving to a file on the file system, Resolvers may also return code directly. This allows programmatically generating virtual modules on demand. You must still return a filePath as well, however, as this indicates where any dependencies in the code should be resolved relative to, as well as how the source code should be processed by transformers (e.g. by file extension). Virtual modules are currently only supported in JavaScript plugins.

import {Resolver} from '@parcel/plugin';
import path from 'path';

export default new Resolver({
async resolve({specifier}) {
if (specifier === 'special-module') {
return {
filePath: path.join(__dirname, 'special-module.js'),
code: 'export default "This is a special module!";'
};
}

return null;
}
});

Dependency metadata

#

In addition to the specifier, Resolver plugins also receive the full dependency, which includes additional metadata. The specifier type, dependency.specifierType, indicates how the specifier should be interpreted (e.g. ESM, CommonJS, URL, etc.). dependency.resolveFrom is the path of the file the dependency should be resolved from (e.g. if the specifier is a relative path).

If the specifier starts with a named pipeline defined in .parcelrc (e.g. url:./image.png), the pipeline name is passed to the resolver separately, and removed from the specifier.

This example resolves relative URLs and paths depending on the specifier type.

import { Resolver } from "@parcel/plugin";
import path from "path";
import { fileURLToPath, pathToFileURL } from "url";

export default new Resolver({
async resolve({ specifier, dependency }) {
return {
filePath:
dependency.specifierType === "url"
? fileURLToPath(
new URL(specifier, pathToFileURL(dependency.resolveFrom))
)
: path.resolve(path.dirname(dependency.resolveFrom), specifier),
};
},
});

Excluding modules

#

A resolver can indicate that a module should be excluded from the build by returning isExcluded: true, or that it is external by returning isExternal: true, which leaves it out of the bundle to be loaded at runtime instead. This example excludes aws-sdk which is included in the AWS hosting environment automatically and does not need to be bundled.

import {Resolver} from '@parcel/plugin';

export default new Resolver({
async resolve({specifier}) {
if (specifier === 'aws-sdk') {
return {isExcluded: true};
}

return null;
}
});

Cache invalidation

#

The results of Resolver plugins are cached by Parcel automatically. Parcel tracks the files a resolver reads during resolution, and rebuilds when they are changed or created. JavaScript plugins can read files with the fs module. Native plugins must read files through the Options object instead (see File system). See Invalidation for details.

import {Resolver} from '@parcel/plugin';
import fs from 'fs';
import path from 'path';

export default new Resolver({
async resolve({specifier, options}) {
let aliasFile = path.join(options.projectRoot, 'alias.json');
if (!fs.existsSync(aliasFile)) {
return null;
}

let aliases = JSON.parse(fs.readFileSync(aliasFile, 'utf8'));
return {
filePath: aliases[specifier] || null
};
}
});

Diagnostics

#

A Resolver plugin may encounter errors during resolution. When this happens, it can fail with an error: by throwing one in JavaScript or Swift, or returning one in Rust or Go. If a Resolver fails, the resolution process is immediately halted, and the error is shown to the user. See Diagnostics for more detail.

Side effects

#

JavaScript resolvers may also return a sideEffects property which indicates whether the asset may have side effects when executed. This usually corresponds to the same property in package.json, and is used for tree shaking. This is currently only supported in JavaScript plugins.

Relevant API

#

The API reference below covers JavaScript plugins. For native plugins, see the Rust, Go, and Swift SDKs.

ResolveResult packages-v3/plugin/src/types.ts:733

type ResolveResult = {
  readonly filePath?: FilePath | null;

An absolute path to the resolved file.

  readonly pipeline?: string | null;

An optional named pipeline to use to compile the resolved file. Defaults to the dependency's pipeline. null removes the pipeline.

  readonly query?: URLSearchParams | string | null;

Query parameters to be used by transformers when compiling the resolved file.

  readonly isExcluded?: boolean | null;

Whether the resolved file should be excluded from the build.

  readonly sideEffects?: boolean | null;

Corresponds to Asset's sideEffects.

  readonly code?: string | null;

The code of the resolved asset. If provided, this is used rather than reading the file from disk.

}
Referenced by:
Resolver

Resolver packages-v3/plugin/src/types.ts:760

config is the configuration given for the plugin in .parcelrc, if any.

type Resolver<ConfigType> = {
  resolve(args: {
    dependency: Dependency;
    specifier: DependencySpecifier;
    pipeline: string | undefined;
    config: ConfigType;
    options: PluginOptions;
  }): Async<ResolveResult | null | undefined>;

Resolve a dependency, or return nullish to leave it to the next resolver plugin.

}