Build Setup
Integrate Atscript into your build process using unplugin-atscript. This plugin automatically compiles .as files during the build, using your configuration file.
Installation
npm install -D unplugin-atscriptVite — Node.js Library
The most common setup: build a Node.js library with external dependencies. The plugin compiles .as files while Vite handles bundling.
// vite.config.js
import { defineConfig } from 'vite'
import atscript from 'unplugin-atscript/vite'
export default defineConfig({
plugins: [atscript()],
build: {
lib: {
entry: 'src/index.ts',
formats: ['es'],
},
rollupOptions: {
external: [/node_modules/],
},
},
})Vite — UI Application
For frontend projects (e.g. Vue, React), add the Atscript plugin alongside your framework plugin:
// vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import atscript from 'unplugin-atscript/vite'
export default defineConfig({
plugins: [atscript(), vue()],
})This lets you import .as types directly in your components — for example, to drive form rendering from metadata or validate user input against your type definitions.
Other Bundlers
unplugin-atscript supports all major bundlers. Import from the bundler-specific entry point:
// rollup.config.js
import atscript from 'unplugin-atscript/rollup'
export default {
plugins: [atscript()],
}// build.js
import { build } from 'esbuild'
import atscript from 'unplugin-atscript/esbuild'
build({
plugins: [atscript()],
entryPoints: ['src/index.ts'],
bundle: true,
outdir: 'dist',
})// rolldown.config.js
import atscript from 'unplugin-atscript/rolldown'
export default {
plugins: [atscript()],
}// webpack.config.js
import atscript from 'unplugin-atscript/webpack'
export default {
plugins: [atscript()],
}// rspack.config.js
import atscript from 'unplugin-atscript/rspack'
export default {
plugins: [atscript()],
}// farm.config.js
import atscript from 'unplugin-atscript/farm'
export default {
plugins: [atscript()],
}Options
The plugin takes the same options on every bundler entry. There is just one:
| Option | Type | Default | Effect |
|---|---|---|---|
strict | boolean | true | Fail the build on parse/diagnostic errors. false = log errors but keep building. |
atscript({ strict: false }) // warn-only — useful mid-refactor or in CI pre-flightWith strict: false, a .as file that fails to compile yields an empty module (module.exports = {}), so anything importing it will likely break at runtime — keep strict: true for normal builds.
Everything else (primitives, annotations, plugins, include/exclude) lives in atscript.config.*, which the plugin auto-discovers. The plugin only intercepts *.as imports; per-file filtering is delegated to the bundler.
How It Works
- Config Discovery — the plugin finds your
atscript.config.*by searching upward from each.asfile - Plugin Execution — runs the plugins defined in your configuration
- Runtime JS — for each imported
.asit emits the runtime metadata module (the same output asasc -f js) - Import Resolution — lets you import
.asfiles directly in TypeScript/JavaScript
In development the plugin compiles on demand with hot module replacement (native on Vite/Webpack/Rspack/Farm; via watch mode on Rollup/Rolldown). In production it pre-compiles during the build.
Type artifacts are not written by the bundler plugin
unplugin-atscript never writes .as.d.ts or the project-level atscript.d.ts to disk. For type checking and IDE support, generate types with the CLI: asc -f dts (e.g. as a postinstall and pre-build step), or let the VSCode extension regenerate them on save. (Declaration bundling in library builds is a separate concern — see below.)
Library Builds with Declaration Bundling
If you build a library whose TypeScript entries re-export .as symbols and bundle declarations with rolldown-plugin-dts (used by tsdown) or rollup-plugin-dts, the plugin serves type declarations to the declaration pass automatically: when the declaration bundler resolves an .as import from a generated declaration module, unplugin-atscript responds with the same declarations asc -f dts would produce, rendered fresh from the .as source. Re-exported symbols stay fully typed in the bundled .d.ts.
// src/index.ts — a library entry re-exporting an Atscript model
export { User } from './models/user.as'// tsdown.config.ts
import { defineConfig } from 'tsdown'
import atscript from 'unplugin-atscript/rolldown'
export default defineConfig({
entry: ['src/index.ts'],
dts: true,
plugins: [atscript()],
})The only requirement is that atscript() is present in the plugin list of the build that bundles declarations — the same plugin instance covers both the runtime and declaration passes.
Symptom of a missing plugin: a re-exported .as symbol works as a value but loses all properties in a type position, and the emitted declaration imports it from a JS chunk:
// dist/index.d.mts — broken: no declaration exists for the .mjs chunk
import { t as User } from './user-ABC123.mjs'If you see this, the plugin was not wired into the declaration build (or you are on an unplugin-atscript version that predates declaration support).
Bundling for Production
@atscript/typescript ships two entries: the default tsPlugin() factory (build-time only) and @atscript/typescript/utils (runtime helpers like Validator, ValidatorError, isAnnotatedType). If you externalize @atscript/typescript to keep build-time code out of your bundle, you must also externalize @atscript/typescript/utils — otherwise the runtime helpers get inlined into your bundle while downstream consumers (@atscript/moost-validator, plugins, your own code) import them from node_modules. Two copies of ValidatorError means instanceof returns false, error interceptors silently miss validation errors, and they escape as 500s.
The simplest fix is to externalize the whole namespace so every subpath comes along:
// rollup / rolldown / esbuild / rspack config
export default {
external: [/^@atscript\//],
}The same applies to subpath imports from any other @atscript/* package.
Next Steps
- Configuration — config file options
- CLI — build from the command line