A Vite plugin that enables React-like directives in your JavaScript/TypeScript code.
This library allows you to create custom directives similar to React's "use client" or "use server". Directives are special string annotations that trigger custom transformations during the Vite build process.
Seeing this meme inspired the creation of this library, allowing developers to define their own directives and associated behaviors in a flexible manner.
You want a "use nemo" directive? You got it!
You want a "use cat" directive? Go ahead!
You want a "use dog" directive? Sure thing!
Any directive you can dream of, you can create it!
I realized that many developers could benefit from a system that allows for custom directives, enabling code transformations and behaviors tailored to specific needs.
For example, you could create a "use analytics" directive that automatically injects analytics tracking code into your components, or a "use debug" directive that adds logging functionality. Or even a "use feature-flag" directive that conditionally includes code based on feature flags.
The possibilities are endless!
Works with Vite and React projects. (Will add support for other frameworks later, PRs are welcome)
npm install use-nemoIn your vite.config.ts, import and add the plugin:
// vite.config.ts
import customDirectives from "use-nemo";
export default defineConfig({
plugins: [customDirectives(), react()],
});Create a new file in src/directives/ directory:
// src/directives/useMyDirective.ts
import { directiveRegistry, injectCode } from "use-nemo";
import type { DirectiveHandler } from "use-nemo";
const myDirective: DirectiveHandler = {
name: "nemo", // This is the name used in "use nemo"
handler({ code, id, directiveName }) {
// Transform the code as needed
return injectCode(code, () => {
console.log("๐");
});
},
};
directiveRegistry.register(myDirective);// src/components/MyComponent.tsx
"use nemo";
export function MyComponent() {
return <div>My component</div>;
}Demo: https://stackblitz.com/edit/use-nemo-example
interface DirectiveHandler {
name: string; // The directive name (without "use" prefix)
handler(context: DirectiveContext): string | null;
}interface DirectiveContext {
code: string; // The source code
id: string; // The file identifier
directiveName: string; // The directive name
}Injects executable code into the source:
return injectCode(code, () => {
console.log("This runs when the module loads!");
});Adds a comment to the code:
return injectComment(code, "This module uses special processing");Adds an import statement at the top:
return injectImport(code, 'import { something } from "lib";');The included useMeow directive demonstrates how to create a simple directive:
// src/directives/useMeow.ts
import { directiveRegistry, injectCode } from "../../custom-directives";
import type { DirectiveHandler } from "../../custom-directives";
const useMeowDirective: DirectiveHandler = {
name: "meow",
handler({ code, id }) {
console.log(`[useMeow] Processing directive in ${id}`);
return injectCode(code, () => {
console.log("๐ฑ Meow!");
});
},
};
directiveRegistry.register(useMeowDirective);Usage:
"use meow";
function App() {
return <h1>Hello world!</h1>;
}- Discovery: When you import a directive file (e.g.,
import "./directives/useMeow"), the directive handler is registered in the global registry - Parsing: The Vite plugin scans source code for directives matching the pattern
"use <name>" - Transformation: For each directive found, the corresponding handler transforms the code
- Injection: The transformed code is returned with any injected functionality
- Cleanup: The directive string itself is removed from the final code
- Directive names should be lowercase with hyphens (e.g.,
use my-directive) - Always return the transformed code from the handler, or return
nullto skip transformation - Use
injectCode,injectComment, orinjectImporthelper functions for common patterns - Directives are processed once per file during the build
- Multiple directives can be used in the same file
- Keep directives simple: Each directive should do one thing well
- Use TypeScript: Define clear types for your directive context and handler
- Avoid side effects: Directives should primarily perform transformations, not complex runtime logic
This project is licensed under the MIT License. See the LICENSE file for details.
