Skip to content

Raiondesu/import-optional

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

import-optional

A tiny library to help deal with optional dependencies without the hassle of needing to .catch for dynamic imports

Usage

Array

// some-config.js
import { optional } from 'import-optional';

// import your optional dependencies
const optionalPlugins = await Promise.all([
  optional(import('my-dependency-1'), m => m.default),
  optional(import('my-dependency-2'), m => m.default),
  // Importing is still done manually for type safety
]);

// then use them as if you did `import * as myDependency from 'my-dependency'`:
export default {
  plugins: [
    plugin1,
    plugin2,
    // ...
    ...optionalPlugins
  ]
}

Object

// some-config.js
import { importOptional } from 'import-optional';

// import your optional dependencies
const optional = await importOptional({
  myDependency: import('my-dependency')
  // Importing is still done manually for type safety
});

// then use them as if you did `import * as myDependency from 'my-dependency'`:
optional.myDependency?.default('some default exported function');

Object with fallbacks

It's possible to define fallbacks that trigger per-dependency, just like with regular .catch:

// some-config.js
import { importOptional } from 'import-optional';

const optional = await importOptional({
  myDependency: import('my-dependency')
}, {
  myDependency: (error) => {
    console.error('Oh no... Anyway');
    return import('my-alt-dependency');
  }
});

optional.myDependency?.default('some default exported function');

Reasoning

This is a tiny library, but it simplifies the mental gymnastics needed to work with multiple optional npm dependencies or just many imports that may fail for some reason.

It is rather redundant and opinionated, but makes the code look nice.

My personal reason for this came from needing to use optional dependencies in vite config plugins:

import { defineConfig } from 'vite';

// This breaks the config if the dependency is missing
import * as somePlugin from 'some-optional-dep';

// - Very tiring to do for every dependency
// - Not very idiomatic
// - Looks chaotic in bulk: each import spans over multiple lines
const somePlugin = await import('some-optional-dep')
  .catch(() => null);

export default defineConfig({
  // Imagine a couple hundred lines of configuration here...

  plugins: [
    // Not clear that this is optional at all
    somePlugin.default()
  ]
})

Now, with optional:

import { defineConfig } from 'vite';

export default defineConfig({
  plugins: [
    // Clear that it's an optional dependency
    await optional(import('some-optional-dep'), m => m.default())
  ]
})

About

Import dependencies with dynamic imports without needing to care about catching errors when a dependency doesn't exist

Topics

Resources

Stars

Watchers

Forks

Contributors