Creating a Plugin
Atscript plugins extend the language with custom primitives, annotations, and code generators. A plugin is a plain object that implements the TAtscriptPlugin interface — a name plus optional hooks that participate in the Atscript processing pipeline.
What Plugins Can Do
| Capability | Hook | Example |
|---|---|---|
| Add semantic types (primitives) | config() | mongo.objectId, mongo.vector |
| Define annotation specs | config() | @mongo.collection, @mongo.index.unique |
| Remap or virtualize module paths | resolve() | Path aliases, virtual modules |
| Provide virtual file content | load() | Synthetic .as modules |
| Post-process parsed documents | onDocument() | Inject virtual props, run custom checks |
| Generate output files | render() | .d.ts, .js, .py, .json — any format |
| Aggregate across all documents | buildEnd() | Global type declarations, indexes |
Your First Plugin
Here's a minimal plugin that adds a @ui.hidden annotation:
import { createAtscriptPlugin, AnnotationSpec } from '@atscript/core'
export const uiPlugin = () => createAtscriptPlugin({
name: 'ui',
config() {
return {
annotations: {
ui: {
hidden: new AnnotationSpec({
description: 'Hide this field in the UI',
nodeType: ['prop'],
}),
},
},
}
},
})createAtscriptPlugin is a type-safe identity function — it returns the object you pass in, but gives you full TypeScript IntelliSense on the hook signatures.
Registering Your Plugin
Add the plugin to your atscript.config.ts:
import { defineConfig } from '@atscript/core'
import { tsPlugin } from '@atscript/typescript'
import { uiPlugin } from './plugins/ui-plugin'
export default defineConfig({
rootDir: 'src',
plugins: [tsPlugin(), uiPlugin()],
})Plugins execute in array order. Each plugin's config() output is merged with the accumulated config using deep defaults (defu), so multiple plugins can contribute primitives and annotations without conflicts.
The TAtscriptPlugin Interface
interface TAtscriptPlugin {
name: string
config?(config: TAtscriptConfig): TAtscriptConfig | undefined
resolve?(id: string): string | undefined
load?(id: string): string | undefined
onDocument?(doc: AtscriptDoc): void
render?(doc: AtscriptDoc, format: string): TPluginOutput[]
buildEnd?(output: TOutput[], format: string, repo: AtscriptRepo): void
}All hooks except name are optional. A plugin can implement any combination — from a simple annotation-only plugin (just config()) to a full language extension with code generation (config() + render() + buildEnd()).
Guide Roadmap
This guide walks you through plugin development from simple to complex:
Plugin Architecture — The processing pipeline, AST node types, plugin lifecycle, and the document API you'll use throughout.
Custom Primitives — Adding semantic types with validation constraints, complex type definitions, and inheritance.
Custom Annotations — Defining annotation specs with typed arguments, custom validation, merge strategies, and AST modification.
Building a Code Generator — Writing a
render()hook to generate output files, walking the AST, resolving types, and building a complete code generator from scratch.Plugin Hooks Reference — Complete reference for all six hooks with signatures, execution order, and examples.
Validation Specification — Language-agnostic spec for implementing data validation: type dispatch, constraint annotations, optional vs required, error reporting.
Testing Plugins — Test setup with Vitest, snapshot testing generated output, and testing diagnostics.
VSCode & Build Integration — How plugins integrate with the CLI, build tools, and VSCode on-save generation.
Prerequisites
- Familiarity with Atscript syntax (interfaces, types, annotations)
- A working TypeScript development environment
@atscript/coreas a dependency (the only required package for plugin development)