A high-performance import map processor for Deno that dynamically transforms and caches JavaScript/TypeScript modules. This tool enables you to use import maps in environments where they're not natively supported, with intelligent caching for optimal performance.
- π High Performance - Multi-level caching (memory + disk) with parallel dependency processing
- π Import Map Support - Full support for imports and scopes as defined in the Import Maps specification
- π¦ Smart Caching - Content-based cache invalidation ensures updates are reflected immediately
- π Comprehensive Import Detection - Catches all import/export patterns including those missed by standard parsers
- π‘οΈ Type Safety - Full TypeScript support with exported types
- π― Zero Dependencies - Only uses Deno standard library and essential tools
deno add @lambdalisue/import-map-importerimport { ImportMapImporter } from "@lambdalisue/import-map-importer";
// Define your import map
const importMap = {
imports: {
// Map package names to URLs
"lodash": "https://cdn.skypack.dev/lodash",
"react": "https://esm.sh/react@18",
// Map path prefixes
"@utils/": "./src/utils/",
"@components/": "./src/components/",
},
};
// Create an importer instance
const importer = new ImportMapImporter(importMap);
// Import modules with automatic transformation
// This is an example - replace with your actual module path
const myModule = await importer.import<{ greet: (name: string) => void }>(
"./src/main.ts",
);
myModule.greet("World"); // Uses transformed imports!import { ImportMapImporter } from "@lambdalisue/import-map-importer";
const importMap = {
imports: {
"lodash": "https://cdn.skypack.dev/lodash",
},
};
const importer = new ImportMapImporter(importMap, {
// Use a custom cache directory
cacheDir: "./.cache/imports",
});const importMap = {
imports: {
"lodash": "https://cdn.skypack.dev/[email protected]",
},
scopes: {
"/legacy/": {
// Use older version in legacy code
"lodash": "https://cdn.skypack.dev/[email protected]",
},
},
};For modules that have their own deno.json configurations:
import { ImportMapImporter } from "@lambdalisue/import-map-importer";
const importMap = {
imports: {
"lodash": "https://cdn.skypack.dev/lodash",
},
};
const importer = new ImportMapImporter(importMap, {
// Clear Deno's module cache before importing
clearDenoCache: true,
});import { ImportMapImporter } from "@lambdalisue/import-map-importer";
// Define your module interface
interface MyUtils {
formatDate: (date: Date) => string;
parseJSON: <T>(json: string) => T;
}
const importMap = {
imports: {
"@utils/": "./src/utils/",
},
};
const importer = new ImportMapImporter(importMap);
// Import with type safety
// This is an example - replace with your actual module path
// const utils = await importer.import<MyUtils>("@utils/helpers.ts");
// const formatted = utils.formatDate(new Date()); // Fully typed!- Parse - When you import a module, the importer parses its source code to find all import statements
- Transform - Import specifiers are transformed according to your import map rules
- Cache - Transformed modules are cached both in memory and on disk for fast subsequent loads
- Recurse - Dependencies are processed recursively and in parallel for optimal performance
The caching system uses a content-based approach:
- Cache Key: SHA-256 hash of (module URL + source code + import map)
- Cache Location: Configurable directory with hierarchical structure
- Cache Invalidation: Automatic when source code or import map changes
The main class for import map processing.
interface ImportMap {
imports: Record<string, string>;
scopes?: Record<string, Record<string, string>>;
}
interface ImportMapImporterOptions {
cacheDir?: string;
clearDenoCache?: boolean;
}
// Class signature (implementation details omitted)
// class ImportMapImporter {
// constructor(
// importMap: ImportMap,
// options?: ImportMapImporterOptions,
// );
//
// import<T>(specifier: string): Promise<T>;
// }The import map configuration type.
interface ImportMap {
imports: Record<string, string>;
scopes?: Record<string, Record<string, string>>;
}Configuration options for the importer.
interface ImportMapImporterOptions {
// Custom cache directory (absolute or relative path)
cacheDir?: string;
// Clear Deno's module cache before importing
clearDenoCache?: boolean;
}The module also exports type guards for runtime validation:
import {
ImportMapImporter,
isImportMap,
isImports,
isScopes,
} from "@lambdalisue/import-map-importer";
// Validate import map structure
const data: unknown = {
imports: { "lodash": "https://cdn.skypack.dev/lodash" },
};
if (isImportMap(data)) {
const importer = new ImportMapImporter(data);
}A utility function to load import maps from JSON files with automatic path resolution.
import {
ImportMapImporter,
loadImportMap,
} from "@lambdalisue/import-map-importer";
// Load from a relative path
const importMap = await loadImportMap("./config/import_map.json");
// Load from an absolute path
const importMap2 = await loadImportMap("/path/to/import_map.json");
// Use with ImportMapImporter
const importer = new ImportMapImporter(importMap);This function automatically resolves relative paths in your import map file:
- Relative paths (starting with
./or../) are resolved relative to the import map file's location - Absolute paths are converted to file URLs
- URLs (http://, https://, file://) are preserved as-is
Example: If your config/import_map.json contains:
{
"imports": {
"@utils/": "./src/utils/",
"lodash": "https://cdn.skypack.dev/lodash"
}
}The resolved result will have the relative path converted to an absolute file URL:
{
"imports": {
"@utils/": "file:///absolute/path/to/config/src/utils/",
"lodash": "https://cdn.skypack.dev/lodash"
}
}- Reuse Importer Instances - Create one importer and reuse it for multiple imports
- Use Absolute URLs - Prefer absolute URLs in import maps for better caching
- Batch Imports - Import multiple modules in parallel when possible
import { ImportMapImporter } from "@lambdalisue/import-map-importer";
const importMap = {
imports: {
"lodash": "https://cdn.skypack.dev/lodash",
},
};
const importer = new ImportMapImporter(importMap);
// Good - parallel imports
const [moduleA, moduleB] = await Promise.all([
importer.import("./a.ts"),
importer.import("./b.ts"),
]);
// Less optimal - sequential imports
const moduleA2 = await importer.import("./a.ts");
const moduleB2 = await importer.import("./b.ts");- Only processes static imports (not dynamic
import()expressions in the initial transformation) - Remote modules must be accessible via fetch
- Import maps must be known at initialization time
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
# Run tests
deno test -A
# Run linter
deno lint
# Run formatter
deno fmt
# Run type checking
deno check **/*.tsThis project is licensed under the MIT License - see the LICENSE file for details.