Skip to content

Imports & Exports

Atscript imports and exports work similar to TypeScript with some specific limitations and rules.

Key Limitations

  1. No default imports/exports - Only named exports and imports are supported
  2. No namespace or rename syntax - No import * as, export * as, or import { a as b }
  3. .as files can only import .as files - Cannot import files from the target language

Named Exports

atscript
// user.as - Named exports only
export interface User {
    id: string
    name: string
}

export type UserID = string
export type Status = 'active' | 'inactive'

// Private (not exported)
interface InternalConfig {
    debug: boolean
}

Importing in .as Files

In .as files, omit the file extension:

atscript
// app.as
import { User, UserID, Status } from './user'
import { Product } from '../models/product'

export interface Order {
    user: User
    items: Product[]
}

Valid Import/Export Examples

Basic Named Import/Export

atscript
// types.as
export interface Person {
    name: string
}

export type ID = string
atscript
// main.as
import { Person, ID } from './types'

export interface Employee extends Person {
    employeeId: ID
}

Multiple Imports from Same File

atscript
// models.as
export interface User { }
export interface Product { }
export interface Order { }
export type Status = string
atscript
// app.as
import { User, Product, Order, Status } from './models'

Nested Directory Imports

atscript
// domain/user.as
import { BaseEntity } from '../shared/base'
import { Address } from './types/address'

Importing from Packages (node_modules)

Atscript supports importing .as files published in npm packages. Use bare specifiers (no ./ prefix) to import from node_modules:

atscript
// app.as
import { User } from 'my-lib/user'
import { Product } from '@my-org/models/product'

Like relative imports, the .as extension is omitted. The resolver automatically appends it.

How Resolution Works

When Atscript encounters a bare import specifier (one that doesn't start with . or /):

  1. The specifier is parsed into a package name and subpath:

    • my-lib/user → package my-lib, subpath ./user.as
    • @my-org/models/product → package @my-org/models, subpath ./product.as
  2. The resolver walks up from the importing file's directory, checking for node_modules/<package>/package.json at each level.

  3. If found, it checks the exports field for a matching subpath entry with the atscript condition (pointing to the raw .as source file).

  4. If no exports match, it falls back to looking for the file directly at node_modules/<package>/<subpath>.as.

Publishing Packages with .as Files

To publish a package that exports .as files, declare them in package.json using the standard exports field with an "atscript" condition:

json
{
  "name": "@my-org/models",
  "exports": {
    "./user.as": {
      "atscript": "./src/user.as",
      "types": "./dist/user.as.d.ts",
      "import": "./dist/user.as.mjs"
    },
    "./product.as": {
      "atscript": "./src/product.as",
      "types": "./dist/product.as.d.ts",
      "import": "./dist/product.as.mjs"
    }
  }
}
  • atscript — points to the raw .as source (used by the Atscript compiler and LSP)
  • types — TypeScript declarations (generated by asc -f dts)
  • import — compiled JavaScript (generated by asc -f js)

For packages with many .as files, use wildcard patterns:

json
{
  "exports": {
    "./*.as": {
      "atscript": "./src/*.as",
      "types": "./dist/*.as.d.ts",
      "import": "./dist/*.as.mjs"
    }
  }
}

Simplest option: If your .as files are at the expected path, no exports configuration is needed. The resolver falls back to direct file access:

node_modules/@my-org/models/
  user.as          ← import { User } from '@my-org/models/user'
  product.as       ← import { Product } from '@my-org/models/product'
  package.json     ← no exports field needed

Invalid Syntax (Not Supported)

atscript
// ❌ Default exports
export default interface User { }

// ❌ Default imports
import User from './user'

// ❌ Namespace imports
import * as models from './models'

// ❌ Export namespace
export * as utils from './utils'

// ❌ Import with rename
import { User as UserModel } from './user'

// ❌ Re-exports
export { User } from './user'

// ❌ Importing non-.as files
import { helper } from './helper.ts'

Importing .as Files in TypeScript

TypeScript files must include the .as extension:

typescript
// app.ts
import { User, UserID, Status } from './user.as'
import { Product } from '../models/product.as'

// Use as both type and runtime object
const user: User = { id: '1', name: 'John' }
const validator = User.validator()
const metadata = User.metadata

Importing from Packages in TypeScript

When importing .as types from an npm package in TypeScript, use the .as extension (added automatically by the Atscript compiler when generating .js/.d.ts output):

typescript
// app.ts — importing .as types from a published package
import { User } from '@my-org/models/user.as'
import { Product } from 'shared-types/product.as'

The bundler (unplugin-atscript) and TypeScript resolve these through the package's exports field:

  • Bundlers use the "import" condition to find the compiled .as.mjs/.as.js
  • TypeScript uses the "types" condition to find the .as.d.ts declarations

If the package doesn't use exports, bundlers resolve the .as file directly from node_modules and compile it on the fly via the unplugin load() hook.

Setup for TypeScript Integration

For TypeScript to import .as files:

  1. With VSCode Extension: Automatically generates .as.d.ts files on save
  2. With CLI: Run asc -f dts to generate TypeScript definitions
  3. With Bundler: Use unplugin-atscript for automatic compilation

Example generated structure:

src/
  user.as          # Source file
  user.as.js       # Generated JavaScript (with asc -f js)
  user.as.d.ts     # Generated TypeScript definitions
  app.ts           # Can import from './user.as'

Next Steps

Released under the MIT License.