Skip to main content
Deno 2 is finally here 🎉️
Learn more

esbuild Cache Plugin for Deno

esbuild Cache Plugin for Deno is an esbuild plugin to resolve remote (http/https) and even npm modules using Deno’s cache.

Features

  • Resolves http/https imports to Deno’s cache.
  • Resolves npm module imports to Deno’s cache.
    • Of course resolving imports and requires in the npm module.
    • Supports polyfill for npm modules.
  • Resolves importmaps.
  • Customizing loader of remote files.

Usage

Minimum Example

import { esbuild } from 'https://deno.land/x/esbuild';
import esbuildCachePlugin from 'https://deno.land/x/esbuild_plugin_cache_deno';
import lockMap from './lock.json' assert { type: 'json' };

// To use deno.lock file, you should parse the file manually
// const lockMap = JSON.parse(Deno.readTextFileSync('./deno.lock'));

const config: esbuild.BuildOptions = {
  entryPoints: [ 'src/main.ts' ],
  bundle: true,
  outdir: './dist',
  platform: 'browser',
  plugins: [
    esbuildCachePlugin({
      lockMap,
      denoCacheDirectory: '/home/[user]/.cache/deno'
    }),
  ],
};

await esbuild.build(config);

esbuild.stop();

And don’t forget to cache src/main.ts with Deno:

$deno cache --lock=./test/lock.json --lock-write ./src/main.ts
# or to use deno.lock:
# $deno cache ./src/main.ts

The you can use remote imports like:

// src/main.ts
import * as react from "https://esm.sh/react";

console.log(react.version);

Getting Deno’s Cache Path

There’s a utility function to get DENO_PATH from output of deno info command and you can use the pass as denoCacheDirectory.

const denoPath = await esbuildCachePlugin.util.getDenoDir();

Alternatively, you can pass them from CLI argument using shell scripts.

Using NPM Modules

You can use npm modules just like using them in Deno:

// src/main.ts
import * as react from "npm:react";

console.log(react.version);

You can use polyfill to replace or remove some modules like Node.js’s core modules.

esbuildCachePlugin({
  lockMap,
  denoCacheDirectory: '/home/[user]/.cache/deno',
  npmModulePolyfill: {
    // replace "http" module
    http: { moduleName: '/src/polyfill/http.ts' },
    // remove "util" module
    util: { loader: 'empty' },
  },
}),

Using Import Maps

You can pass import maps with importmap option.

esbuildCachePlugin({
  lockMap,
  denoCacheDirectory: '/home/[user]/.cache/deno',
  importmap: {
    imports: {
      react: "https://esm.sh/react",
    },
  },
}),

Then you can use the module specifier like:

// src/main.ts
import * as react from "react";

console.log(react.version);

Of cause you can use your import maps you’re using for the intellisense:

import importmap from './import_map.json' assert type { type: 'json' };

// ...

esbuildCachePlugin({
  lockMap,
  denoCacheDirectory: '/home/[user]/.cache/deno',
  importmap,
}),

Also you can disguise import map’s path for import maps not located in the CWD:

import importmap from './src/import_map.json' assert type { type: 'json' };

// ...

esbuildCachePlugin({
  lockMap,
  denoCacheDirectory: '/home/[user]/.cache/deno',
  importmap,
  importmapBasePath: 'src/',
}),

Customizing loaders

You can specify loaders for files with loaderRules option. The plugin uses default loader as the esbuild, you may not need to use this option.

esbuildCachePlugin({
  lockMap,
  denoCacheDirectory: '/home/[user]/.cache/deno',
  loaderRules: [
    { test: /\.css$/, loader: 'css' },
  ],
}),