SourceLoom builds LoomPackaging the Loom language

Part VIII, Chapter 8

Part VIII, Chapter 8: Packaging the Loom language

Package: packages/loom-lang/

Language: JSON

Raw source

Packaging the Loom language

The @athrio/loom-lang package is the language behind every .loom file — the Volar extended language and the engine that tangles it. The command-line tool and the VS Code extension both build on it: they import its source and bundle it into their own output, so this package exposes TypeScript source rather than a precompiled library.

The manifest

Two field groups carry the package's shape. The imports field defines one package subpath alias, #ast/*, for the AST source, so internal imports stay stable rather than relative. The exports field names the public entry points — ./LoomCompiler, ./LoomTangler, ./LoomFormatter, ./PackageConfig, and ./LoomServer — each pointing at a source file the consumer inlines. ./weave/WovenCorpus exposes the woven-corpus schema on its own, so the reader site types its rendered pages against the same definition the weave pass produces rather than a hand-kept copy, ./weave/LoomWeaver exposes the writer the CLI drives to emit that data, and ./weave/LoomContents the writer it drives to regenerate the book's own table of contents. ./ast/LineRanges and ./ast/WeftClassifier expose the first two stages of reading a loom, so the tree-sitter grammar's test checks every line's kind against the classifier the grammar ports. @athrio/loom-ast supplies the shared domain types the verbs build and the toolchain reads, a workspace dependency.

The main and bin fields point at built output: dist/loom-server.js is the bundled language server, and the loom-language-server binary launches it. The runtime libraries the server needs — Volar, Effect, the volar-service-* plugins, and ignore, which matches the patterns that leave a loom out of a project — are dependencies. @athrio/loom-service-typescript is a devDependency: the server loads it at runtime from the Loom store rather than bundling it, so it is present only to build and test against. @effect/platform-bun is a devDependency used only by the tangler's end-to-end test, which provides it as the real filesystem the tangler writes through. vscode-uri is a type-only devDependency.

{{ version = ${version} }} {{ effect = ${effect} }}

JSON
{
  "name": "@athrio/loom-lang",
  "version": "::[version]",
  "license": "MIT",
  "main": "./dist/loom-server.js",
  "bin": {
    "loom-language-server": "./bin/loom-language-server.js"
  },
  "imports": {
    "#ast/*": "./src/ast/*.ts"
  },
  "exports": {
    "./LoomCompiler": "./src/LoomCompiler.ts",
    "./LoomTangler": "./src/LoomTangler.ts",
    "./LoomFormatter": "./src/LoomFormatter.ts",
    "./PackageConfig": "./src/PackageConfig.ts",
    "./LoomServer": "./src/LoomServer.ts",
    "./weave/WovenCorpus": "./src/weave/WovenCorpus.ts",
    "./weave/LoomWeaver": "./src/weave/LoomWeaver.ts",
    "./weave/LoomContents": "./src/weave/LoomContents.ts",
    "./ast/LineRanges": "./src/ast/LineRanges.ts",
    "./ast/WeftClassifier": "./src/ast/WeftClassifier.ts"
  },
  "scripts": {
    "build": "vite build",
    "watch": "vite build --watch"
  },
  "dependencies": {
    "@athrio/loom-config": "workspace:*",
    "@athrio/loom-ast": "workspace:*",
    "@athrio/loom-lang-services": "workspace:*",
    "@volar/language-core": "^2.4.28",
    "@volar/language-server": "^2.4.28",
    "@volar/language-service": "^2.4.28",
    "effect": "::[effect]",
    "ignore": "^7.0.5",
    "volar-service-css": "^0.0.71",
    "volar-service-html": "^0.0.71",
    "volar-service-markdown": "^0.0.71",
    "vscode-html-languageservice": "^5.6.2"
  },
  "devDependencies": {
    "@athrio/loom-service-typescript": "workspace:*",
    "@effect/platform-bun": "::[effect]",
    "vscode-uri": "^3.1.0"
  }
}

The launcher

An editor starts the language server by spawning the loom-language-server binary. The binary is a thin launcher over the bundle, made runnable on its own by a shebang line.

It answers --version from the package manifest, printing the version an editor reads to learn which server it runs. For every other invocation it loads the bundled server at dist/loom-server.js, which begins serving the moment it loads.

JavaScript
#!/usr/bin/env node
if (process.argv.includes('--version')) {
  const pkgJSON = require('../package.json')
  console.log(pkgJSON.version)
} else {
  require('../dist/loom-server.js')
}

The TypeScript config

The tsconfig.json extends the workspace-root tsconfig.base.json and covers both the src and test directories. Vite produces the dist bundle, so the configuration is a development tool only.

JSON
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "out"
  },
  "include": ["src", "test"]
}
Loom