Skip to content
On this page

Plugin API ​

This page demonstrates how to create your own plugin.

Example of a plugin ​

ts
function coolPlugin() {
   return {
      name: "cool-plugin",
      load() {
         // do something
      },
      // you can also change how the hook will be handled by doing this:
      load: {
         /**
          * Use this option to change the hook's order.
          * Set to "pre" if you want it to be first, and "post" if last.
          */
         order: "pre",
         /**
          * Set to true if you want it to be asynchronous.
          * @default false
          */
         async: false,
         /**
          * Set to false if you don't want this hook to chain with other
          * hooks.
          * @default true
          */
         chaining: true,
         handler() {
            // do something
         },
      },
   };
}

Hooks ​

Below shows you the different types of plugin hooks.

load ​

Allows custom handling for module loading, similar to webpack's loader system. Use this hook to define how specific types of modules are processed before they are added to the bundle.

Type

ts
(this: PluginContext, moduleInfo: ModuleInfo) => LoadResult | string | void

ModuleInfo Interface

ts
type ModuleInfo = ModuleInfoText | ModuleInfoResource;

interface ModuleInfoText {
   type: "script" | "style";
   source: string;
   content: string;
   map?: EncodedSourceMap | null;
   isEntry: boolean;
}

interface ModuleInfoResource {
   type: "resource";
   source: string;
   content: Blob;
   isEntry: boolean;
}

LoadResult Interface

ts
type LoadResult = LoadTextResult | LoadResourceResult;

interface LoadTextResult {
   type?: "script" | "style";
   content: string;
   map?: EncodedSourceMap | null;
}

interface LoadResourceResult {
   type?: "resource";
   content: Blob;
}

resolve ​

Use this hook to change where a module points to or just to check out which modules are being used.

Type

ts
/**
 * @param id The request that is being resolved.
 */
(this: PluginContext, id: string) => string | void

transform ​

Use this hook to transform script modules.

Type

ts
(
   this: PluginContext,
   source: string,
   content: string,
   ast: File
) => TraverseOptions | void

transformStyle ​

Use this hook to transform style modules.

Type

ts
(
   this: PluginContext,
   source: string,
   content: string,
   ast: CssNode
) => EnterOrLeaveFn<CssNode> | WalkOptions | void

transformHtml ​

Use this hook to transform the main html.

Type

ts
/**
 * @param ast The parsed html.
 * @param indexScriptUrl The url of the bundled script.
 * @param indexStyleUrl The url of the bundled style.
 */
(
   this: PluginContextBase,
   ast: HTMLElement,
   indexScriptUrl: string,
   indexStyleUrl: string
) => TraverseHtmlOptions | void;

TraverseHtmlOptions Interface

ts
interface TraverseHtmlOptions {
   Comment?: (node: CommentNode) => void;
   Element?: (node: HTMLElement) => void;
   Text?: (node: TextNode) => void;
   /**
    * string represents the tagname in PascalCase.
    * So if you want to scan for Span elements, add "SpanElement".
    */
   `${string}Element`: (node: HTMLElement) => void;
}

buildStart ​

This hook is triggered at the very start of the bundling process.

Type

ts
(this: PluginContextBase) => void

buildEnd ​

This hook is triggered at the very end of the bundling process.

Type

ts
(this: PluginContextBase, bundleResult: BundleResult) => void

parsed ​

This hook is triggered whenever a module gets parsed.

Type

ts
(this: PluginContext, parseInfo: ParseInfo) => void

ParseInfo Interface

ts
type ParseInfo = ScriptParseInfo | StyleParseInfo;

interface ScriptParseInfo {
   type: "script";
   chunk: ScriptModule;
   parsed: ParsedScriptResult;
}

interface StyleParseInfo {
   type: "style";
   chunk: StyleModule;
   parsed: ParsedStyleResult;
}

setup ​

Use this to setup things in the bundler. This hook only gets triggered once, and it's when the plugin gets added to the bundler.

Type

ts
(this: PluginContextBase) => void

Context ​

Every hook mentioned earlier comes with a context. This context provides details about the current module and includes some handy utility functions.

ts
interface PluginContextBase {
   bundler: Toypack;
   cache: Map<any, any>;
   /**
    * Convert a resource's source path to a useable source path.
    * If in development mode, the resource path will become a blob url.
    * If in production mode, the resource path will have a unique hash as
    * its basename.
    */
   getUsableResourcePath: (source: string, baseDir: string) => string | null;
   /** Constructs an import code using the provided request and specifiers. */
   getImportCode: (
      request: string,
      specifiers?: (SpecifierOptions | string)[]
   ) => string;
   /**
    * Returns the default export code.
    * - e.g. `export default ... ;` or `module.exports = ... ;`
    */
   getDefaultExportCode: (exportCode: string) => string;
   /** Emits an error message. */
   emitError: (message: string) => void;
   /** Emits a warning message. */
   emitWarning: (message: string) => void;
   /** Emits an info message. */
   emitInfo: (message: string) => void;
}

interface PluginContext extends PluginContextBase {
   graph: DependencyGraph;
   /** Returns the modules that imported the current module. */
   getImporters: () => Importers;
   /**
    * Returns the module that imported the current module. If it's the
    * entry, then it will return undefined.
    */
   getCurrentImporter: () => ScriptModule | StyleModule | undefined;
   /** Returns true if the current module should have source maps or not. */
   shouldMap: () => boolean;
   /** Pre-loads an asset. */
   load: (source: string) => Promise<LoadResult>;
}