diff --git a/packages/decodal-codemirror/jsr.json b/packages/decodal-codemirror/jsr.json index b278490..8b62227 100644 --- a/packages/decodal-codemirror/jsr.json +++ b/packages/decodal-codemirror/jsr.json @@ -4,9 +4,7 @@ "license": "MIT", "exports": { ".": "./src/mod.ts", - "./format": "./src/format.js", - "./parser": "./src/decodal-parser.js", - "./terms": "./src/decodal-parser.terms.js" + "./format": "./src/format.ts" }, "imports": { "@codemirror/language": "npm:@codemirror/language@^6.12.4", @@ -18,6 +16,7 @@ "include": [ "README.md", "src/mod.ts", + "src/format.ts", "src/decodal.js", "src/decodal.d.ts", "src/decodal.js.d.ts", diff --git a/packages/decodal-codemirror/src/format.ts b/packages/decodal-codemirror/src/format.ts new file mode 100644 index 0000000..8ebe4fa --- /dev/null +++ b/packages/decodal-codemirror/src/format.ts @@ -0,0 +1,84 @@ +/** + * Decodal source formatting helpers for CodeMirror integrations. + * + * The formatter is backed by WebAssembly generated from the internal Rust + * `decodal-language-tools` crate. Call {@link initDecodalFormatter} once before + * using {@link formatDecodal} or {@link formatDecodalCommand}. + * + * @module + */ +import type { EditorView } from 'npm:@codemirror/view@^6.43.6'; +import initLanguageTools, { formatSource } from '../wasm/decodal_language_tools.js'; + +let initialized = false; + +/** Successful formatter result. */ +export interface FormatSuccess { + /** Indicates that formatting succeeded. */ + ok: true; + /** The formatted Decodal source text. */ + source: string; +} + +/** Failed formatter result. */ +export interface FormatFailure { + /** Indicates that formatting failed. */ + ok: false; + /** Human-readable formatter error. */ + error: string; +} + +/** Result returned by {@link formatDecodal}. */ +export type FormatResult = FormatSuccess | FormatFailure; + +/** + * Initialize the bundled Decodal formatter WebAssembly module. + * + * Bundlers such as Vite should pass the package wasm asset URL explicitly: + * + * ```ts + * import wasmUrl from 'decodal-codemirror/wasm/decodal_language_tools_bg.wasm?url'; + * await initDecodalFormatter(wasmUrl); + * ``` + * + * @param moduleOrPath - Optional WebAssembly module, bytes, response, URL, or + * request accepted by wasm-bindgen's generated initializer. + */ +export async function initDecodalFormatter( + moduleOrPath?: RequestInfo | URL | Response | BufferSource | WebAssembly.Module, +): Promise { + await initLanguageTools(moduleOrPath); + initialized = true; +} + +/** + * Format a complete Decodal source document. + * + * @param source - Source text to format. + * @returns Either formatted source text or an error message. + */ +export function formatDecodal(source: string): FormatResult { + if (!initialized) { + return { ok: false, error: 'Decodal formatter is not initialized' }; + } + return JSON.parse(formatSource(source)) as FormatResult; +} + +/** + * CodeMirror command that formats the whole document in-place. + * + * @param view - The editor view whose document should be formatted. + * @returns `true` when the document was formatted, otherwise `false`. + */ +export function formatDecodalCommand(view: EditorView): boolean { + const result = formatDecodal(view.state.doc.toString()); + if (!result.ok) return false; + view.dispatch({ + changes: { + from: 0, + to: view.state.doc.length, + insert: result.source, + }, + }); + return true; +} diff --git a/packages/decodal-codemirror/src/mod.ts b/packages/decodal-codemirror/src/mod.ts index b03381f..ec82f27 100644 --- a/packages/decodal-codemirror/src/mod.ts +++ b/packages/decodal-codemirror/src/mod.ts @@ -1,3 +1,13 @@ +/** + * CodeMirror 6 language support for Decodal. + * + * This module exports the Decodal language extension, parser metadata, and the + * bundled highlight style used by the official Decodal playground. Add + * {@link decodal} to a CodeMirror extension set to enable highlighting, + * indentation, and folding for Decodal documents. + * + * @module + */ import { HighlightStyle, LRLanguage, LanguageSupport, foldNodeProp, indentNodeProp, syntaxHighlighting } from 'npm:@codemirror/language@^6.12.4'; import { styleTags, tags as t } from 'npm:@lezer/highlight@^1.2.3'; import { parser } from './decodal-parser-jsr.js'; @@ -44,6 +54,7 @@ function foldDelimited(open: string, close: string) { }; } +/** Decodal LR language definition for CodeMirror 6. */ export const decodalLanguage: LRLanguage = LRLanguage.define({ parser: parserWithMetadata, languageData: { @@ -52,6 +63,7 @@ export const decodalLanguage: LRLanguage = LRLanguage.define({ }, }); +/** Default Decodal highlight style used by the playground. */ export const decodalHighlightStyle: HighlightStyle = HighlightStyle.define([ { tag: t.keyword, color: '#93c5fd', fontWeight: '700' }, { tag: t.variableName, color: 'inherit' }, @@ -64,10 +76,19 @@ export const decodalHighlightStyle: HighlightStyle = HighlightStyle.define([ { tag: [t.brace, t.squareBracket, t.paren, t.punctuation], color: '#f9a8d4' }, ]); +/** Options for {@link decodal}. */ export interface DecodalOptions { + /** Include the bundled {@link decodalHighlightStyle}. Defaults to `true`. */ highlight?: boolean; } +/** + * Create CodeMirror language support for Decodal. + * + * @param options - Optional language support options. + * @returns A CodeMirror extension bundle containing the Decodal language and, + * unless disabled, the default highlight style. + */ export function decodal(options: DecodalOptions = {}): LanguageSupport { const { highlight = true } = options; return new LanguageSupport( diff --git a/packages/decodal-wasm/README.md b/packages/decodal-wasm/README.md index 7a33125..27365eb 100644 --- a/packages/decodal-wasm/README.md +++ b/packages/decodal-wasm/README.md @@ -26,11 +26,8 @@ import init, { evaluateProject } from 'decodal-wasm'; await init(); -const result = evaluateProject(JSON.stringify({ - entry: 'main.dcdl', - files: { - 'main.dcdl': 'Server = { port = Int default 8080; };', - }, +const result = evaluateProject('main.dcdl', JSON.stringify({ + 'main.dcdl': 'Server = { port = Int default 8080; };', })); console.log(JSON.parse(result)); diff --git a/packages/decodal-wasm/jsr.json b/packages/decodal-wasm/jsr.json index bec3f04..061e868 100644 --- a/packages/decodal-wasm/jsr.json +++ b/packages/decodal-wasm/jsr.json @@ -1,11 +1,12 @@ { "name": "@hare/decodal-wasm", - "version": "0.1.2", + "version": "0.1.3", "license": "MIT", - "exports": "./decodal_wasm.js", + "exports": "./mod.ts", "publish": { "include": [ "README.md", + "mod.ts", "decodal_wasm.js", "decodal_wasm.d.ts", "decodal_wasm_bg.wasm", diff --git a/packages/decodal-wasm/mod.ts b/packages/decodal-wasm/mod.ts new file mode 100644 index 0000000..c67e7fb --- /dev/null +++ b/packages/decodal-wasm/mod.ts @@ -0,0 +1,66 @@ +/** + * Browser and JavaScript runtime bindings for the Decodal evaluator. + * + * This module wraps the wasm-bindgen output generated from the Rust + * `decodal-wasm` crate and exposes stable, documented entrypoints for JSR + * users. The exported evaluator functions return JSON strings. + * + * @module + */ +import initWasm, { + evaluate as evaluateImpl, + evaluateProject as evaluateProjectImpl, + initSync as initSyncImpl, +} from './decodal_wasm.js'; + +/** Input accepted by the wasm-bindgen initializer. */ +export type InitInput = RequestInfo | URL | Response | BufferSource | WebAssembly.Module; + +/** Initialized Decodal WebAssembly exports. */ +export interface InitOutput { + /** Linear memory exported by the WebAssembly module. */ + readonly memory: WebAssembly.Memory; + readonly evaluate: (source: number, len: number) => [number, number, number, number]; + readonly evaluateProject: (entry: number, entryLen: number, files: number, filesLen: number) => [number, number, number, number]; +} + +/** + * Initialize the Decodal WebAssembly runtime asynchronously. + * + * @param moduleOrPath - Optional WebAssembly module, bytes, response, URL, or request. + * @returns The initialized WebAssembly exports. + */ +export default async function initDecodalRuntime(moduleOrPath?: InitInput): Promise { + return await initWasm(moduleOrPath) as InitOutput; +} + +/** + * Initialize the Decodal WebAssembly runtime synchronously. + * + * @param module - Compiled module, bytes, or wasm-bindgen sync initialization input. + * @returns The initialized WebAssembly exports. + */ +export function initSync(module: WebAssembly.Module | BufferSource): InitOutput { + return initSyncImpl(module) as InitOutput; +} + +/** + * Evaluate and materialize a single Decodal source string. + * + * @param source - Decodal source text. + * @returns A JSON string containing either `{ ok: true, output }` or `{ ok: false, error }`. + */ +export function evaluate(source: string): string { + return evaluateImpl(source); +} + +/** + * Evaluate and materialize a virtual multi-file Decodal project. + * + * @param entry - Entry point file path to materialize. + * @param filesJson - JSON object mapping virtual file paths to source strings. + * @returns A JSON string containing either `{ ok: true, output }` or `{ ok: false, error }`. + */ +export function evaluateProject(entry: string, filesJson: string): string { + return evaluateProjectImpl(entry, filesJson); +} diff --git a/packages/decodal-wasm/package.json b/packages/decodal-wasm/package.json index 547be75..dbcd228 100644 --- a/packages/decodal-wasm/package.json +++ b/packages/decodal-wasm/package.json @@ -2,7 +2,7 @@ "name": "decodal-wasm", "type": "module", "description": "WebAssembly wrapper for evaluating Decodal in browser playgrounds.", - "version": "0.1.2", + "version": "0.1.3", "license": "MIT OR Apache-2.0", "repository": { "type": "git", diff --git a/site/decodal-site/package-lock.json b/site/decodal-site/package-lock.json index 1ea6c40..d178e06 100644 --- a/site/decodal-site/package-lock.json +++ b/site/decodal-site/package-lock.json @@ -41,7 +41,7 @@ } }, "../../packages/decodal-wasm": { - "version": "0.1.2", + "version": "0.1.3", "license": "MIT OR Apache-2.0" }, "node_modules/@astrojs/check": { diff --git a/site/decodal-site/scripts/prepare-wasm-package.mjs b/site/decodal-site/scripts/prepare-wasm-package.mjs index 250aae1..733a930 100644 --- a/site/decodal-site/scripts/prepare-wasm-package.mjs +++ b/site/decodal-site/scripts/prepare-wasm-package.mjs @@ -1,4 +1,4 @@ -import { writeFileSync } from 'node:fs'; +import { readFileSync, writeFileSync } from 'node:fs'; import { resolve } from 'node:path'; const packageDir = resolve(import.meta.dirname, '../../../packages/decodal-wasm'); @@ -8,6 +8,11 @@ writeFileSync( '# wasm-pack output is committed for the browser runtime package.\n', ); +const packageJsonPath = resolve(packageDir, 'package.json'); +const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf8')); +packageJson.version = '0.1.3'; +writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n'); + writeFileSync( resolve(packageDir, 'README.md'), `# decodal-wasm @@ -38,11 +43,8 @@ import init, { evaluateProject } from 'decodal-wasm'; await init(); -const result = evaluateProject(JSON.stringify({ - entry: 'main.dcdl', - files: { - 'main.dcdl': 'Server = { port = Int default 8080; };', - }, +const result = evaluateProject('main.dcdl', JSON.stringify({ + 'main.dcdl': 'Server = { port = Int default 8080; };', })); console.log(JSON.parse(result)); @@ -51,3 +53,27 @@ console.log(JSON.parse(result)); The exported functions return JSON strings so callers can handle diagnostics and successful results without depending on Rust data structures. `, ); + +writeFileSync( + resolve(packageDir, 'jsr.json'), + JSON.stringify( + { + name: '@hare/decodal-wasm', + version: '0.1.3', + license: 'MIT', + exports: './mod.ts', + publish: { + include: [ + 'README.md', + 'mod.ts', + 'decodal_wasm.js', + 'decodal_wasm.d.ts', + 'decodal_wasm_bg.wasm', + 'package.json', + ], + }, + }, + null, + 2, + ) + '\n', +); diff --git a/site/decodal-site/src/pages/index.astro b/site/decodal-site/src/pages/index.astro index e8d6d1d..627637c 100644 --- a/site/decodal-site/src/pages/index.astro +++ b/site/decodal-site/src/pages/index.astro @@ -32,7 +32,7 @@ Production = Server & { crates.io/crates/decodal