diff --git a/.gitignore b/.gitignore index 2b4f279..5dc6ca6 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ node_modules *.tsbuildinfo *.vsix .idea +.vscode .DS_Store .nx/cache diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..42f53f3 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,3 @@ +pnpm-lock.yaml +pnpm-workspace.yaml +nx.json \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 194f06b..0000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "version": "0.2.0", - "configurations": [ - { - "name": "Run Extension", - "type": "extensionHost", - "request": "launch", - "runtimeExecutable": "${execPath}", - "autoAttachChildProcesses": true, - "args": [ - "--disable-updates", - "--disable-workspace-trust", - "--profile-temp", - "--skip-release-notes", - "--skip-welcome", - "--extensionDevelopmentPath=${workspaceFolder}/packages/vscode", - "${workspaceFolder}/fixtures/fixtures.code-workspace" - ], - "env": { - "NODE_OPTIONS": "--enable-source-maps" - }, - "outFiles": ["${workspaceFolder}/packages/vscode/out/**/*.js"], - "skipFiles": ["/**", "**/typescript/**"], - "preLaunchTask": "build" - } - ] -} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 2f31430..0000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "tempblot.trace.server.verbosity": "compact", - "tempblot.trace.server.format": "text", -} diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 7971f5d..0000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "type": "shell", - "command": "node", - "args": ["packages/vscode/scripts/build.mjs", "debug"], - "label": "build", - "presentation": { - "clear": true, - "echo": true - }, - "group": { - "kind": "build", - "isDefault": true - } - } - ] -} \ No newline at end of file diff --git a/apps/website/README.md b/apps/website/README.md index 909d737..6fe6875 100644 --- a/apps/website/README.md +++ b/apps/website/README.md @@ -35,9 +35,9 @@ Static assets, like favicons, can be placed in the `public/` directory. All commands are run from the root of the project, from a terminal: -| Command | Action | -| :------------------------ | :----------------------------------------------- | -| `pnpm install` | Installs dependencies | +| Command | Action | +| :--------------------- | :----------------------------------------------- | +| `pnpm install` | Installs dependencies | | `pnpm dev` | Starts local dev server at `localhost:4321` | | `pnpm build` | Build your production site to `./dist/` | | `pnpm preview` | Preview your build locally, before deploying | diff --git a/apps/website/astro.config.mjs b/apps/website/astro.config.mjs index addaf3d..dd0b0d9 100644 --- a/apps/website/astro.config.mjs +++ b/apps/website/astro.config.mjs @@ -1,77 +1,89 @@ // @ts-check -import { defineConfig } from 'astro/config'; -import react from '@astrojs/react'; -import starlight from '@astrojs/starlight'; -import { bundledLanguages } from 'shiki/langs'; -import tempblotGrammar from '../../packages/vscode/syntaxes/tempblot.tmLanguage.json' with { type: 'json' }; +import { defineConfig } from "astro/config"; +import react from "@astrojs/react"; +import starlight from "@astrojs/starlight"; +import { bundledLanguages } from "shiki/langs"; +import tempblotGrammar from "../../packages/vscode/syntaxes/tempblot.tmLanguage.json" with { type: "json" }; const tempblotShikiGrammar = { - ...tempblotGrammar, - name: 'blot', - displayName: 'Tempblot', - scopeName: 'source.tempblot', - aliases: ['tempblot'], - embeddedLangs: [ - 'html', - 'markdown', - 'css', - 'scss', - 'less', - 'javascript', - 'typescript', - 'jsx', - 'tsx', - 'json', - 'jsonc', - 'yaml', - ], + ...tempblotGrammar, + name: "blot", + displayName: "Tempblot", + scopeName: "source.tempblot", + aliases: ["tempblot"], + embeddedLangs: [ + "html", + "markdown", + "css", + "scss", + "less", + "javascript", + "typescript", + "jsx", + "tsx", + "json", + "jsonc", + "yaml", + ], }; const tempblotEmbeddedLangs = [ - 'html', - 'markdown', - 'css', - 'scss', - 'less', - 'javascript', - 'typescript', - 'jsx', - 'tsx', - 'json', - 'jsonc', - 'yaml', + "html", + "markdown", + "css", + "scss", + "less", + "javascript", + "typescript", + "jsx", + "tsx", + "json", + "jsonc", + "yaml", ]; -const tempblotEmbeddedShikiGrammars = tempblotEmbeddedLangs.map((lang) => bundledLanguages[lang]); +const tempblotEmbeddedShikiGrammars = tempblotEmbeddedLangs.map( + (lang) => bundledLanguages[lang], +); // https://astro.build/config export default defineConfig({ - vite: { - resolve: { - alias: { - '@tempblot/language-service': new URL('../../packages/language-service/src/index.ts', import.meta.url).pathname, - '@tempblot/parser': new URL('../../packages/parser/src/index.ts', import.meta.url).pathname, - }, - }, - }, - integrations: [ - react(), - starlight({ - title: 'Tempblot', - customCss: ['./src/styles/theme.css'], - expressiveCode: { - shiki: { - langs: [...tempblotEmbeddedShikiGrammars, tempblotShikiGrammar], - }, - }, - social: [{ icon: 'github', label: 'GitHub', href: 'https://github.com/crutchcorn/tempblot' }], - sidebar: [ - { label: 'Playground', slug: 'playground' }, - { - label: 'Guides', - items: [ - { label: 'Basic Usage', slug: 'guides/basic-usage' }, - ], - }, - ], - }), - ], + vite: { + resolve: { + alias: { + "@tempblot/language-service": new URL( + "../../packages/language-service/src/index.ts", + import.meta.url, + ).pathname, + "@tempblot/parser": new URL( + "../../packages/parser/src/index.ts", + import.meta.url, + ).pathname, + }, + }, + }, + integrations: [ + react(), + starlight({ + title: "Tempblot", + customCss: ["./src/styles/theme.css"], + expressiveCode: { + shiki: { + langs: [...tempblotEmbeddedShikiGrammars, tempblotShikiGrammar], + }, + }, + social: [ + { + icon: "github", + label: "GitHub", + href: "https://github.com/crutchcorn/tempblot", + }, + ], + sidebar: [ + { label: "Playground", slug: "playground" }, + { + label: "Guides", + items: [{ label: "Basic Usage", slug: "guides/basic-usage" }], + }, + ], + }), + ], }); diff --git a/apps/website/src/components/Playground.css b/apps/website/src/components/Playground.css index 5397e0a..8b7c7f9 100644 --- a/apps/website/src/components/Playground.css +++ b/apps/website/src/components/Playground.css @@ -7,7 +7,7 @@ --playground-editor-shadow: rgba(0, 0, 0, 0.45); } -:root[data-theme='light'] { +:root[data-theme="light"] { --playground-fg: #0b0b0b; --playground-muted: #424242; --playground-border: rgba(0, 0, 0, 0.18); diff --git a/apps/website/src/components/Playground.tsx b/apps/website/src/components/Playground.tsx index 4d041b6..b3904ca 100644 --- a/apps/website/src/components/Playground.tsx +++ b/apps/website/src/components/Playground.tsx @@ -1,10 +1,25 @@ -import Editor, { type Monaco } from '@monaco-editor/react'; -import { useEffect, useRef, useState } from 'react'; -import type { CancellationToken, editor, IMarkdownString, languages, Position } from 'monaco-editor'; -import * as vscodeTextmate from 'vscode-textmate'; -import type { IGrammar, IRawGrammar, IRawTheme, StateStack } from 'vscode-textmate'; -import * as vscodeOniguruma from 'vscode-oniguruma'; -import { BrowserMessageReader, BrowserMessageWriter, createProtocolConnection } from 'vscode-languageserver-protocol/browser'; +import Editor, { type Monaco } from "@monaco-editor/react"; +import { useEffect, useRef, useState } from "react"; +import type { + CancellationToken, + editor, + IMarkdownString, + languages, + Position, +} from "monaco-editor"; +import * as vscodeTextmate from "vscode-textmate"; +import type { + IGrammar, + IRawGrammar, + IRawTheme, + StateStack, +} from "vscode-textmate"; +import * as vscodeOniguruma from "vscode-oniguruma"; +import { + BrowserMessageReader, + BrowserMessageWriter, + createProtocolConnection, +} from "vscode-languageserver-protocol/browser"; import type { CompletionItem, CompletionList, @@ -21,27 +36,27 @@ import type { SemanticTokensOptions, SignatureHelp, ProtocolConnection, -} from 'vscode-languageserver-protocol/browser'; -import tempblotGrammar from '../../../../packages/vscode/syntaxes/tempblot.tmLanguage.json'; -import cssGrammar from 'tm-grammars/grammars/css.json'; -import htmlGrammar from 'tm-grammars/grammars/html.json'; -import htmlDerivativeGrammar from 'tm-grammars/grammars/html-derivative.json'; -import javascriptGrammar from 'tm-grammars/grammars/javascript.json'; -import jsxGrammar from 'tm-grammars/grammars/jsx.json'; -import jsonGrammar from 'tm-grammars/grammars/json.json'; -import jsoncGrammar from 'tm-grammars/grammars/jsonc.json'; -import tsxGrammar from 'tm-grammars/grammars/tsx.json'; -import typescriptGrammar from 'tm-grammars/grammars/typescript.json'; -import yamlGrammar from 'tm-grammars/grammars/yaml.json'; -import darkPlusTheme from 'tm-themes/themes/dark-plus.json'; -import lightPlusTheme from 'tm-themes/themes/light-plus.json'; -import onigurumaWasmUrl from 'vscode-oniguruma/release/onig.wasm?url'; -import './Playground.css'; +} from "vscode-languageserver-protocol/browser"; +import tempblotGrammar from "../../../../packages/vscode/syntaxes/tempblot.tmLanguage.json"; +import cssGrammar from "tm-grammars/grammars/css.json"; +import htmlGrammar from "tm-grammars/grammars/html.json"; +import htmlDerivativeGrammar from "tm-grammars/grammars/html-derivative.json"; +import javascriptGrammar from "tm-grammars/grammars/javascript.json"; +import jsxGrammar from "tm-grammars/grammars/jsx.json"; +import jsonGrammar from "tm-grammars/grammars/json.json"; +import jsoncGrammar from "tm-grammars/grammars/jsonc.json"; +import tsxGrammar from "tm-grammars/grammars/tsx.json"; +import typescriptGrammar from "tm-grammars/grammars/typescript.json"; +import yamlGrammar from "tm-grammars/grammars/yaml.json"; +import darkPlusTheme from "tm-themes/themes/dark-plus.json"; +import lightPlusTheme from "tm-themes/themes/light-plus.json"; +import onigurumaWasmUrl from "vscode-oniguruma/release/onig.wasm?url"; +import "./Playground.css"; const { Registry, INITIAL } = vscodeTextmate; const { createOnigScanner, createOnigString, loadWASM } = vscodeOniguruma; -const tempblotDarkTheme = 'tempblot-dark'; -const tempblotLightTheme = 'tempblot-light'; +const tempblotDarkTheme = "tempblot-dark"; +const tempblotLightTheme = "tempblot-light"; const DEFAULT_SOURCE = ` @@ -65,42 +80,42 @@ function add(a: number, b: number) { `; -const BLOT_URI = 'file:///playground/template.blot'; +const BLOT_URI = "file:///playground/template.blot"; const SEMANTIC_TOKEN_TYPES = [ - 'namespace', - 'class', - 'enum', - 'interface', - 'typeParameter', - 'type', - 'parameter', - 'variable', - 'property', - 'enumMember', - 'function', - 'method', - 'macro', - 'keyword', - 'modifier', - 'comment', - 'string', - 'number', - 'regexp', - 'operator', - 'decorator', + "namespace", + "class", + "enum", + "interface", + "typeParameter", + "type", + "parameter", + "variable", + "property", + "enumMember", + "function", + "method", + "macro", + "keyword", + "modifier", + "comment", + "string", + "number", + "regexp", + "operator", + "decorator", ]; const SEMANTIC_TOKEN_MODIFIERS = [ - 'declaration', - 'definition', - 'readonly', - 'static', - 'deprecated', - 'abstract', - 'async', - 'modification', - 'documentation', - 'defaultLibrary', - 'local', + "declaration", + "definition", + "readonly", + "static", + "deprecated", + "abstract", + "async", + "modification", + "documentation", + "defaultLibrary", + "local", ]; type Disposable = { dispose(): void }; @@ -143,15 +158,22 @@ export default function Playground() { void updateTextMateTheme(monacoRef.current, editorRef.current, nextTheme); }); - observer.observe(root, { attributeFilter: ['data-theme'] }); + observer.observe(root, { attributeFilter: ["data-theme"] }); return () => observer.disconnect(); }, []); - async function handleMount(instance: editor.IStandaloneCodeEditor, monaco: Monaco) { + async function handleMount( + instance: editor.IStandaloneCodeEditor, + monaco: Monaco, + ) { monacoRef.current = monaco; editorRef.current = instance; - const model = monaco.editor.createModel(source, 'tempblot', monaco.Uri.parse(BLOT_URI)); + const model = monaco.editor.createModel( + source, + "tempblot", + monaco.Uri.parse(BLOT_URI), + ); instance.setModel(model); await configureMonaco(monaco, instance, editorTheme); monaco.editor.setTheme(editorTheme); @@ -172,11 +194,11 @@ export default function Playground() { minimap: { enabled: false }, padding: { top: 16, bottom: 16 }, scrollBeyondLastLine: false, - 'semanticHighlighting.enabled': true, + "semanticHighlighting.enabled": true, tabSize: 2, }} theme={editorTheme} - onChange={(value) => setSource(value ?? '')} + onChange={(value) => setSource(value ?? "")} onMount={handleMount} /> @@ -191,46 +213,62 @@ async function startTempblotLanguageClient( monaco: Monaco, model: editor.ITextModel, ): Promise { - const worker = new Worker(new URL('../workers/tempblotLanguageServer.worker.ts', import.meta.url), { - type: 'module', - }); + const worker = new Worker( + new URL("../workers/tempblotLanguageServer.worker.ts", import.meta.url), + { + type: "module", + }, + ); const reader = new BrowserMessageReader(worker); const writer = new BrowserMessageWriter(worker); const connection = createProtocolConnection(reader, writer); const disposables: Disposable[] = []; - connection.onNotification('textDocument/publishDiagnostics', (params: { uri: string; diagnostics: Diagnostic[] }) => { - if (params.uri !== model.uri.toString()) { - return; - } + connection.onNotification( + "textDocument/publishDiagnostics", + (params: { uri: string; diagnostics: Diagnostic[] }) => { + if (params.uri !== model.uri.toString()) { + return; + } - monaco.editor.setModelMarkers( - model, - 'tempblot-language-server', - params.diagnostics.map((diagnostic) => toMonacoMarker(monaco, diagnostic)), - ); - }); + monaco.editor.setModelMarkers( + model, + "tempblot-language-server", + params.diagnostics.map((diagnostic) => + toMonacoMarker(monaco, diagnostic), + ), + ); + }, + ); connection.listen(); - const initializeResult = await connection.sendRequest('initialize', { + const initializeResult = (await connection.sendRequest("initialize", { processId: null, - clientInfo: { name: 'tempblot-playground' }, + clientInfo: { name: "tempblot-playground" }, locale: navigator.language, - rootUri: 'file:///playground', - workspaceFolders: [{ uri: 'file:///playground', name: 'playground' }], + rootUri: "file:///playground", + workspaceFolders: [{ uri: "file:///playground", name: "playground" }], capabilities: { textDocument: { synchronization: { dynamicRegistration: false, didSave: false }, completion: { dynamicRegistration: false, completionItem: { - documentationFormat: ['markdown', 'plaintext'], + documentationFormat: ["markdown", "plaintext"], snippetSupport: true, }, }, - hover: { dynamicRegistration: false, contentFormat: ['markdown', 'plaintext'] }, - signatureHelp: { dynamicRegistration: false, signatureInformation: { documentationFormat: ['markdown', 'plaintext'] } }, + hover: { + dynamicRegistration: false, + contentFormat: ["markdown", "plaintext"], + }, + signatureHelp: { + dynamicRegistration: false, + signatureInformation: { + documentationFormat: ["markdown", "plaintext"], + }, + }, definition: { dynamicRegistration: false, linkSupport: true }, declaration: { dynamicRegistration: false, linkSupport: true }, typeDefinition: { dynamicRegistration: false, linkSupport: true }, @@ -242,7 +280,7 @@ async function startTempblotLanguageClient( requests: { full: true, range: false }, tokenTypes: SEMANTIC_TOKEN_TYPES, tokenModifiers: SEMANTIC_TOKEN_MODIFIERS, - formats: ['relative'], + formats: ["relative"], overlappingTokenSupport: false, multilineTokenSupport: false, augmentsSyntaxTokens: true, @@ -254,22 +292,24 @@ async function startTempblotLanguageClient( configuration: false, didChangeWatchedFiles: { dynamicRegistration: false }, }, - general: { positionEncodings: ['utf-16'] }, + general: { positionEncodings: ["utf-16"] }, }, - }) as InitializeResult; + })) as InitializeResult; - connection.sendNotification('initialized', {}); + connection.sendNotification("initialized", {}); sendDidOpen(connection, model); void pullDiagnostics(monaco, connection, model); let diagnosticsTimeout = 0; - disposables.push(model.onDidChangeContent(() => { - sendDidChange(connection, model); - window.clearTimeout(diagnosticsTimeout); - diagnosticsTimeout = window.setTimeout(() => { - void pullDiagnostics(monaco, connection, model); - }, 200); - })); + disposables.push( + model.onDidChangeContent(() => { + sendDidChange(connection, model); + window.clearTimeout(diagnosticsTimeout); + diagnosticsTimeout = window.setTimeout(() => { + void pullDiagnostics(monaco, connection, model); + }, 200); + }), + ); registerLanguageProviders(monaco, connection, initializeResult, disposables); return { @@ -278,10 +318,10 @@ async function startTempblotLanguageClient( for (const disposable of disposables) { disposable.dispose(); } - connection.sendNotification('textDocument/didClose', { + connection.sendNotification("textDocument/didClose", { textDocument: { uri: model.uri.toString() }, }); - monaco.editor.setModelMarkers(model, 'tempblot-language-server', []); + monaco.editor.setModelMarkers(model, "tempblot-language-server", []); connection.dispose(); worker.terminate(); }, @@ -289,27 +329,45 @@ async function startTempblotLanguageClient( } function getCurrentEditorTheme() { - if (typeof document === 'undefined') { + if (typeof document === "undefined") { return tempblotDarkTheme; } - return document.documentElement.dataset.theme === 'light' ? tempblotLightTheme : tempblotDarkTheme; + return document.documentElement.dataset.theme === "light" + ? tempblotLightTheme + : tempblotDarkTheme; } -async function configureMonaco(monaco: Monaco, instance: editor.IStandaloneCodeEditor, themeName: string) { +async function configureMonaco( + monaco: Monaco, + instance: editor.IStandaloneCodeEditor, + themeName: string, +) { defineMonacoThemes(monaco); - if (!monaco.languages.getLanguages().some((language: languages.ILanguageExtensionPoint) => language.id === 'tempblot')) { - monaco.languages.register({ id: 'tempblot', extensions: ['.blot'] }); - monaco.languages.setLanguageConfiguration('tempblot', { - brackets: [['<', '>'], ['{', '}'], ['[', ']'], ['(', ')']], + if ( + !monaco.languages + .getLanguages() + .some( + (language: languages.ILanguageExtensionPoint) => + language.id === "tempblot", + ) + ) { + monaco.languages.register({ id: "tempblot", extensions: [".blot"] }); + monaco.languages.setLanguageConfiguration("tempblot", { + brackets: [ + ["<", ">"], + ["{", "}"], + ["[", "]"], + ["(", ")"], + ], autoClosingPairs: [ { open: '"', close: '"' }, { open: "'", close: "'" }, - { open: '`', close: '`' }, - { open: '{', close: '}' }, - { open: '[', close: ']' }, - { open: '(', close: ')' }, + { open: "`", close: "`" }, + { open: "{", close: "}" }, + { open: "[", close: "]" }, + { open: "(", close: ")" }, ], }); } @@ -321,60 +379,69 @@ let textMateConfiguration: Promise | undefined; function defineMonacoThemes(monaco: Monaco) { monaco.editor.defineTheme(tempblotDarkTheme, { - base: 'vs-dark', + base: "vs-dark", inherit: true, rules: [], colors: darkPlusTheme.colors, }); monaco.editor.defineTheme(tempblotLightTheme, { - base: 'vs', + base: "vs", inherit: true, rules: [], colors: lightPlusTheme.colors, }); } -async function configureTextMate(monaco: Monaco, instance: editor.IStandaloneCodeEditor, themeName: string) { +async function configureTextMate( + monaco: Monaco, + instance: editor.IStandaloneCodeEditor, + themeName: string, +) { textMateConfiguration ??= (async () => { await loadWASM(await fetch(onigurumaWasmUrl)); const grammarByScope = new Map([ - ['source.tempblot', asRawGrammar(tempblotGrammar)], - ['source.ts', asRawGrammar(typescriptGrammar)], - ['source.tsx', asRawGrammar(tsxGrammar)], - ['source.js', asRawGrammar(javascriptGrammar)], - ['source.js.jsx', asRawGrammar(jsxGrammar)], - ['source.json', asRawGrammar(jsonGrammar)], - ['source.json.comments', asRawGrammar(jsoncGrammar)], - ['text.html.basic', asRawGrammar(htmlGrammar)], - ['text.html.derivative', asRawGrammar(htmlDerivativeGrammar)], - ['source.css', asRawGrammar(cssGrammar)], - ['source.yaml', asRawGrammar(yamlGrammar)], + ["source.tempblot", asRawGrammar(tempblotGrammar)], + ["source.ts", asRawGrammar(typescriptGrammar)], + ["source.tsx", asRawGrammar(tsxGrammar)], + ["source.js", asRawGrammar(javascriptGrammar)], + ["source.js.jsx", asRawGrammar(jsxGrammar)], + ["source.json", asRawGrammar(jsonGrammar)], + ["source.json.comments", asRawGrammar(jsoncGrammar)], + ["text.html.basic", asRawGrammar(htmlGrammar)], + ["text.html.derivative", asRawGrammar(htmlDerivativeGrammar)], + ["source.css", asRawGrammar(cssGrammar)], + ["source.yaml", asRawGrammar(yamlGrammar)], ]); const registry = new Registry({ theme: createTextMateTheme(themeName), onigLib: Promise.resolve({ createOnigScanner, createOnigString }), - loadGrammar: async (scopeName) => grammarByScope.get(scopeName) ?? createPlainTextGrammar(scopeName), + loadGrammar: async (scopeName) => + grammarByScope.get(scopeName) ?? createPlainTextGrammar(scopeName), }); - const grammar = await registry.loadGrammarWithEmbeddedLanguages('source.tempblot', 1, { - 'source.tempblot': 1, - 'text.html.derivative': 2, - 'text.html.basic': 3, - 'source.ts': 4, - 'source.tsx': 5, - 'source.js': 6, - 'source.js.jsx': 7, - 'source.css': 8, - 'source.json': 9, - 'source.json.comments': 10, - 'source.yaml': 11, - }); + const grammar = await registry.loadGrammarWithEmbeddedLanguages( + "source.tempblot", + 1, + { + "source.tempblot": 1, + "text.html.derivative": 2, + "text.html.basic": 3, + "source.ts": 4, + "source.tsx": 5, + "source.js": 6, + "source.js.jsx": 7, + "source.css": 8, + "source.json": 9, + "source.json.comments": 10, + "source.yaml": 11, + }, + ); if (!grammar) { - throw new Error('Unable to load Tempblot TextMate grammar.'); + throw new Error("Unable to load Tempblot TextMate grammar."); } return grammar; @@ -394,7 +461,7 @@ async function updateTextMateTheme( return; } - const loadedGrammar = grammar ?? await textMateConfiguration; + const loadedGrammar = grammar ?? (await textMateConfiguration); if (!loadedGrammar) { return; @@ -407,17 +474,25 @@ async function updateTextMateTheme( }); monaco.languages.setColorMap(registry.getColorMap()); - monaco.languages.setTokensProvider('tempblot', createTextMateTokensProvider(loadedGrammar)); + monaco.languages.setTokensProvider( + "tempblot", + createTextMateTokensProvider(loadedGrammar), + ); instance.setModel(instance.getModel()); } -function createTextMateTokensProvider(grammar: IGrammar): languages.EncodedTokensProvider { +function createTextMateTokensProvider( + grammar: IGrammar, +): languages.EncodedTokensProvider { return { getInitialState() { return new TextMateState(INITIAL); }, tokenizeEncoded(line, state) { - const result = grammar.tokenizeLine2(line, state instanceof TextMateState ? state.stack : INITIAL); + const result = grammar.tokenizeLine2( + line, + state instanceof TextMateState ? state.stack : INITIAL, + ); return { tokens: result.tokens, endState: new TextMateState(result.ruleStack), @@ -427,15 +502,16 @@ function createTextMateTokensProvider(grammar: IGrammar): languages.EncodedToken } function createTextMateTheme(themeName: string): IRawTheme { - const theme = themeName === tempblotLightTheme ? lightPlusTheme : darkPlusTheme; + const theme = + themeName === tempblotLightTheme ? lightPlusTheme : darkPlusTheme; return { name: themeName, settings: [ { settings: { - foreground: theme.colors['editor.foreground'], - background: theme.colors['editor.background'], + foreground: theme.colors["editor.foreground"], + background: theme.colors["editor.background"], }, }, ...theme.tokenColors, @@ -460,137 +536,174 @@ function registerLanguageProviders( const capabilities = initializeResult.capabilities; if (capabilities.completionProvider) { - disposables.push(monaco.languages.registerCompletionItemProvider('tempblot', { - triggerCharacters: capabilities.completionProvider.triggerCharacters, - async provideCompletionItems(model: editor.ITextModel, position: Position, context: languages.CompletionContext) { - const result = await connection.sendRequest('textDocument/completion', { - textDocument: { uri: model.uri.toString() }, - position: toLspPosition(position), - context: { - triggerKind: context.triggerKind + 1, - triggerCharacter: context.triggerCharacter, - }, - }) as CompletionList | CompletionItem[] | null; - - const items = Array.isArray(result) ? result : (result?.items ?? []); - return { - incomplete: !Array.isArray(result) && result?.isIncomplete, - suggestions: items.map((item) => toMonacoCompletionItem(monaco, model, position, item)), - }; - }, - })); + disposables.push( + monaco.languages.registerCompletionItemProvider("tempblot", { + triggerCharacters: capabilities.completionProvider.triggerCharacters, + async provideCompletionItems( + model: editor.ITextModel, + position: Position, + context: languages.CompletionContext, + ) { + const result = (await connection.sendRequest( + "textDocument/completion", + { + textDocument: { uri: model.uri.toString() }, + position: toLspPosition(position), + context: { + triggerKind: context.triggerKind + 1, + triggerCharacter: context.triggerCharacter, + }, + }, + )) as CompletionList | CompletionItem[] | null; + + const items = Array.isArray(result) ? result : (result?.items ?? []); + return { + incomplete: !Array.isArray(result) && result?.isIncomplete, + suggestions: items.map((item) => + toMonacoCompletionItem(monaco, model, position, item), + ), + }; + }, + }), + ); } if (capabilities.hoverProvider) { - disposables.push(monaco.languages.registerHoverProvider('tempblot', { - async provideHover(model: editor.ITextModel, position: Position) { - const hover = await connection.sendRequest('textDocument/hover', { - textDocument: { uri: model.uri.toString() }, - position: toLspPosition(position), - }) as Hover | null; - - if (!hover) { - return null; - } - - return { - contents: markupToMarkdownStrings(hover.contents), - range: hover.range ? toMonacoRange(monaco, hover.range) : undefined, - }; - }, - })); + disposables.push( + monaco.languages.registerHoverProvider("tempblot", { + async provideHover(model: editor.ITextModel, position: Position) { + const hover = (await connection.sendRequest("textDocument/hover", { + textDocument: { uri: model.uri.toString() }, + position: toLspPosition(position), + })) as Hover | null; + + if (!hover) { + return null; + } + + return { + contents: markupToMarkdownStrings(hover.contents), + range: hover.range ? toMonacoRange(monaco, hover.range) : undefined, + }; + }, + }), + ); } if (capabilities.signatureHelpProvider) { - disposables.push(monaco.languages.registerSignatureHelpProvider('tempblot', { - signatureHelpTriggerCharacters: capabilities.signatureHelpProvider.triggerCharacters, - signatureHelpRetriggerCharacters: capabilities.signatureHelpProvider.retriggerCharacters, - async provideSignatureHelp( - model: editor.ITextModel, - position: Position, - _token: CancellationToken, - context: languages.SignatureHelpContext, - ) { - const signatureHelp = await connection.sendRequest('textDocument/signatureHelp', { - textDocument: { uri: model.uri.toString() }, - position: toLspPosition(position), - context: { - triggerKind: context.triggerKind + 1, - triggerCharacter: context.triggerCharacter, - isRetrigger: context.isRetrigger, - }, - }) as SignatureHelp | null; - - if (!signatureHelp) { - return null; - } - - return { - value: { - activeParameter: signatureHelp.activeParameter ?? 0, - activeSignature: signatureHelp.activeSignature ?? 0, - signatures: signatureHelp.signatures.map((signature) => ({ - label: signature.label, - documentation: markupToString(signature.documentation), - parameters: signature.parameters?.map((parameter) => ({ - label: Array.isArray(parameter.label) ? [parameter.label[0], parameter.label[1]] : parameter.label, - documentation: markupToString(parameter.documentation), - })) ?? [], - })), - }, - dispose() {}, - }; - }, - })); + disposables.push( + monaco.languages.registerSignatureHelpProvider("tempblot", { + signatureHelpTriggerCharacters: + capabilities.signatureHelpProvider.triggerCharacters, + signatureHelpRetriggerCharacters: + capabilities.signatureHelpProvider.retriggerCharacters, + async provideSignatureHelp( + model: editor.ITextModel, + position: Position, + _token: CancellationToken, + context: languages.SignatureHelpContext, + ) { + const signatureHelp = (await connection.sendRequest( + "textDocument/signatureHelp", + { + textDocument: { uri: model.uri.toString() }, + position: toLspPosition(position), + context: { + triggerKind: context.triggerKind + 1, + triggerCharacter: context.triggerCharacter, + isRetrigger: context.isRetrigger, + }, + }, + )) as SignatureHelp | null; + + if (!signatureHelp) { + return null; + } + + return { + value: { + activeParameter: signatureHelp.activeParameter ?? 0, + activeSignature: signatureHelp.activeSignature ?? 0, + signatures: signatureHelp.signatures.map((signature) => ({ + label: signature.label, + documentation: markupToString(signature.documentation), + parameters: + signature.parameters?.map((parameter) => ({ + label: Array.isArray(parameter.label) + ? [parameter.label[0], parameter.label[1]] + : parameter.label, + documentation: markupToString(parameter.documentation), + })) ?? [], + })), + }, + dispose() {}, + }; + }, + }), + ); } if (capabilities.definitionProvider) { - disposables.push(monaco.languages.registerDefinitionProvider('tempblot', { - async provideDefinition(model: editor.ITextModel, position: Position) { - const result = await connection.sendRequest('textDocument/definition', { - textDocument: { uri: model.uri.toString() }, - position: toLspPosition(position), - }) as Location | Location[] | LocationLink[] | null; - - return locationsToMonaco(monaco, result); - }, - })); + disposables.push( + monaco.languages.registerDefinitionProvider("tempblot", { + async provideDefinition(model: editor.ITextModel, position: Position) { + const result = (await connection.sendRequest( + "textDocument/definition", + { + textDocument: { uri: model.uri.toString() }, + position: toLspPosition(position), + }, + )) as Location | Location[] | LocationLink[] | null; + + return locationsToMonaco(monaco, result); + }, + }), + ); } if (capabilities.semanticTokensProvider) { - const semanticTokensProvider = capabilities.semanticTokensProvider as SemanticTokensOptions; - disposables.push(monaco.languages.registerDocumentSemanticTokensProvider('tempblot', { - getLegend() { - return semanticTokensProvider.legend; - }, - async provideDocumentSemanticTokens(model: editor.ITextModel) { - const result = await connection.sendRequest('textDocument/semanticTokens/full', { - textDocument: { uri: model.uri.toString() }, - }) as SemanticTokens | null; - - return { - resultId: result?.resultId, - data: new Uint32Array(result?.data ?? []), - }; - }, - releaseDocumentSemanticTokens() {}, - })); + const semanticTokensProvider = + capabilities.semanticTokensProvider as SemanticTokensOptions; + disposables.push( + monaco.languages.registerDocumentSemanticTokensProvider("tempblot", { + getLegend() { + return semanticTokensProvider.legend; + }, + async provideDocumentSemanticTokens(model: editor.ITextModel) { + const result = (await connection.sendRequest( + "textDocument/semanticTokens/full", + { + textDocument: { uri: model.uri.toString() }, + }, + )) as SemanticTokens | null; + + return { + resultId: result?.resultId, + data: new Uint32Array(result?.data ?? []), + }; + }, + releaseDocumentSemanticTokens() {}, + }), + ); } } function sendDidOpen(connection: ProtocolConnection, model: editor.ITextModel) { - connection.sendNotification('textDocument/didOpen', { + connection.sendNotification("textDocument/didOpen", { textDocument: { uri: model.uri.toString(), - languageId: 'tempblot', + languageId: "tempblot", version: model.getVersionId(), text: model.getValue(), }, }); } -function sendDidChange(connection: ProtocolConnection, model: editor.ITextModel) { - connection.sendNotification('textDocument/didChange', { +function sendDidChange( + connection: ProtocolConnection, + model: editor.ITextModel, +) { + connection.sendNotification("textDocument/didChange", { textDocument: { uri: model.uri.toString(), version: model.getVersionId(), @@ -599,19 +712,23 @@ function sendDidChange(connection: ProtocolConnection, model: editor.ITextModel) }); } -async function pullDiagnostics(monaco: Monaco, connection: ProtocolConnection, model: editor.ITextModel) { +async function pullDiagnostics( + monaco: Monaco, + connection: ProtocolConnection, + model: editor.ITextModel, +) { try { - const report = await connection.sendRequest('textDocument/diagnostic', { + const report = (await connection.sendRequest("textDocument/diagnostic", { textDocument: { uri: model.uri.toString() }, - }) as DocumentDiagnosticReport | null; + })) as DocumentDiagnosticReport | null; - if (!report || report.kind !== 'full') { + if (!report || report.kind !== "full") { return; } monaco.editor.setModelMarkers( model, - 'tempblot-language-server', + "tempblot-language-server", report.items.map((diagnostic) => toMonacoMarker(monaco, diagnostic)), ); } catch { @@ -636,12 +753,18 @@ function toMonacoRange(monaco: Monaco, range: LspRange) { ); } -function toMonacoMarker(monaco: Monaco, diagnostic: Diagnostic): editor.IMarkerData { +function toMonacoMarker( + monaco: Monaco, + diagnostic: Diagnostic, +): editor.IMarkerData { return { severity: toMonacoSeverity(monaco, diagnostic.severity), message: diagnostic.message, source: diagnostic.source, - code: typeof diagnostic.code === 'string' || typeof diagnostic.code === 'number' ? String(diagnostic.code) : undefined, + code: + typeof diagnostic.code === "string" || typeof diagnostic.code === "number" + ? String(diagnostic.code) + : undefined, startLineNumber: diagnostic.range.start.line + 1, startColumn: diagnostic.range.start.character + 1, endLineNumber: diagnostic.range.end.line + 1, @@ -649,7 +772,10 @@ function toMonacoMarker(monaco: Monaco, diagnostic: Diagnostic): editor.IMarkerD }; } -function toMonacoSeverity(monaco: Monaco, severity: Diagnostic['severity']): editor.IMarkerData['severity'] { +function toMonacoSeverity( + monaco: Monaco, + severity: Diagnostic["severity"], +): editor.IMarkerData["severity"] { if (severity === 2) return monaco.MarkerSeverity.Warning; if (severity === 3) return monaco.MarkerSeverity.Info; if (severity === 4) return monaco.MarkerSeverity.Hint; @@ -663,10 +789,16 @@ function toMonacoCompletionItem( item: CompletionItem, ): languages.CompletionItem { const word = model.getWordUntilPosition(position); - const fallbackRange = new monaco.Range(position.lineNumber, word.startColumn, position.lineNumber, word.endColumn); - const range = item.textEdit && 'range' in item.textEdit - ? toMonacoRange(monaco, item.textEdit.range) - : fallbackRange; + const fallbackRange = new monaco.Range( + position.lineNumber, + word.startColumn, + position.lineNumber, + word.endColumn, + ); + const range = + item.textEdit && "range" in item.textEdit + ? toMonacoRange(monaco, item.textEdit.range) + : fallbackRange; return { label: item.label, @@ -675,83 +807,128 @@ function toMonacoCompletionItem( documentation: markupToMarkdownString(item.documentation), sortText: item.sortText, filterText: item.filterText, - insertText: item.textEdit && 'newText' in item.textEdit ? item.textEdit.newText : (item.insertText ?? item.label), - insertTextRules: item.insertTextFormat === 2 ? monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet : undefined, + insertText: + item.textEdit && "newText" in item.textEdit + ? item.textEdit.newText + : (item.insertText ?? item.label), + insertTextRules: + item.insertTextFormat === 2 + ? monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet + : undefined, range, commitCharacters: item.commitCharacters, }; } -function toMonacoCompletionKind(monaco: Monaco, kind: CompletionItem['kind']): languages.CompletionItemKind { +function toMonacoCompletionKind( + monaco: Monaco, + kind: CompletionItem["kind"], +): languages.CompletionItemKind { const kinds = monaco.languages.CompletionItemKind; switch (kind) { - case 2: return kinds.Method; - case 3: return kinds.Function; - case 4: return kinds.Constructor; - case 5: return kinds.Field; - case 6: return kinds.Variable; - case 7: return kinds.Class; - case 8: return kinds.Interface; - case 9: return kinds.Module; - case 10: return kinds.Property; - case 12: return kinds.Value; - case 13: return kinds.Enum; - case 14: return kinds.Keyword; - case 15: return kinds.Snippet; - case 20: return kinds.EnumMember; - case 21: return kinds.Constant; - case 22: return kinds.Struct; - case 25: return kinds.TypeParameter; - default: return kinds.Text; + case 2: + return kinds.Method; + case 3: + return kinds.Function; + case 4: + return kinds.Constructor; + case 5: + return kinds.Field; + case 6: + return kinds.Variable; + case 7: + return kinds.Class; + case 8: + return kinds.Interface; + case 9: + return kinds.Module; + case 10: + return kinds.Property; + case 12: + return kinds.Value; + case 13: + return kinds.Enum; + case 14: + return kinds.Keyword; + case 15: + return kinds.Snippet; + case 20: + return kinds.EnumMember; + case 21: + return kinds.Constant; + case 22: + return kinds.Struct; + case 25: + return kinds.TypeParameter; + default: + return kinds.Text; } } -function markupToMarkdownStrings(value: Hover['contents']): IMarkdownString[] { +function markupToMarkdownStrings(value: Hover["contents"]): IMarkdownString[] { if (Array.isArray(value)) { - return value.map(markupToMarkdownString).filter((item): item is IMarkdownString => Boolean(item)); + return value + .map(markupToMarkdownString) + .filter((item): item is IMarkdownString => Boolean(item)); } const item = markupToMarkdownString(value); return item ? [item] : []; } -function markupToMarkdownString(value: string | MarkupContent | { language: string; value: string } | undefined): IMarkdownString | undefined { +function markupToMarkdownString( + value: + | string + | MarkupContent + | { language: string; value: string } + | undefined, +): IMarkdownString | undefined { if (!value) { return undefined; } - if (typeof value === 'string') { + if (typeof value === "string") { return { value }; } - if ('language' in value) { + if ("language" in value) { return { value: `\`\`\`${value.language}\n${value.value}\n\`\`\`` }; } return { value: value.value }; } -function markupToString(value: string | MarkupContent | undefined): string | undefined { +function markupToString( + value: string | MarkupContent | undefined, +): string | undefined { if (!value) { return undefined; } - return typeof value === 'string' ? value : value.value; + return typeof value === "string" ? value : value.value; } -function locationsToMonaco(monaco: Monaco, result: Location | Location[] | LocationLink[] | null): languages.Definition | null { +function locationsToMonaco( + monaco: Monaco, + result: Location | Location[] | LocationLink[] | null, +): languages.Definition | null { if (!result) { return null; } const locations = Array.isArray(result) ? result : [result]; return locations.map((location) => { - if ('targetUri' in location) { + if ("targetUri" in location) { return { uri: monaco.Uri.parse(location.targetUri), range: toMonacoRange(monaco, location.targetRange), - targetSelectionRange: toMonacoRange(monaco, location.targetSelectionRange), - originSelectionRange: location.originSelectionRange ? toMonacoRange(monaco, location.originSelectionRange) : undefined, + targetSelectionRange: toMonacoRange( + monaco, + location.targetSelectionRange, + ), + originSelectionRange: location.originSelectionRange + ? toMonacoRange(monaco, location.originSelectionRange) + : undefined, }; } diff --git a/apps/website/src/content.config.ts b/apps/website/src/content.config.ts index d9ee8c9..7fbcf2c 100644 --- a/apps/website/src/content.config.ts +++ b/apps/website/src/content.config.ts @@ -1,7 +1,7 @@ -import { defineCollection } from 'astro:content'; -import { docsLoader } from '@astrojs/starlight/loaders'; -import { docsSchema } from '@astrojs/starlight/schema'; +import { defineCollection } from "astro:content"; +import { docsLoader } from "@astrojs/starlight/loaders"; +import { docsSchema } from "@astrojs/starlight/schema"; export const collections = { - docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), + docs: defineCollection({ loader: docsLoader(), schema: docsSchema() }), }; diff --git a/apps/website/src/content/docs/index.mdx b/apps/website/src/content/docs/index.mdx index 9332ede..bf12390 100644 --- a/apps/website/src/content/docs/index.mdx +++ b/apps/website/src/content/docs/index.mdx @@ -16,7 +16,7 @@ hero: variant: minimal --- -import { Card, CardGrid } from '@astrojs/starlight/components'; +import { Card, CardGrid } from "@astrojs/starlight/components"; ## Why Tempblot? diff --git a/apps/website/src/content/docs/playground.mdx b/apps/website/src/content/docs/playground.mdx index c567d72..f1843f7 100644 --- a/apps/website/src/content/docs/playground.mdx +++ b/apps/website/src/content/docs/playground.mdx @@ -6,8 +6,8 @@ prev: false next: false --- -import Playground from '../../components/Playground'; +import Playground from "../../components/Playground";
- +
diff --git a/apps/website/src/styles/theme.css b/apps/website/src/styles/theme.css index 62e2179..6e17289 100644 --- a/apps/website/src/styles/theme.css +++ b/apps/website/src/styles/theme.css @@ -1,28 +1,28 @@ :root { - --sl-color-accent-low: #333333; - --sl-color-accent: #ffffff; - --sl-color-accent-high: #ffffff; + --sl-color-accent-low: #333333; + --sl-color-accent: #ffffff; + --sl-color-accent-high: #ffffff; } -:root[data-theme='light'] { - --sl-color-accent-low: #e5e5e5; - --sl-color-accent: #000000; - --sl-color-accent-high: #000000; +:root[data-theme="light"] { + --sl-color-accent-low: #e5e5e5; + --sl-color-accent: #000000; + --sl-color-accent-high: #000000; } .sl-container:has(.playground-page) { - max-width: none; - padding: 0; + max-width: none; + padding: 0; } .content-panel:has(+ .content-panel .playground-page) { - display: none; + display: none; } .sl-markdown-content:has(.playground-page) { - margin-top: 0; + margin-top: 0; } .playground-page { - margin: 0; + margin: 0; } diff --git a/apps/website/src/vite-env.d.ts b/apps/website/src/vite-env.d.ts index fdf2e41..ebeac99 100644 --- a/apps/website/src/vite-env.d.ts +++ b/apps/website/src/vite-env.d.ts @@ -1,9 +1,9 @@ -declare module '*.wasm?url' { +declare module "*.wasm?url" { const url: string; export default url; } -declare module '*.d.ts?raw' { +declare module "*.d.ts?raw" { const source: string; export default source; } diff --git a/apps/website/src/workers/tempblotLanguageServer.worker.ts b/apps/website/src/workers/tempblotLanguageServer.worker.ts index cbdd846..00c58bb 100644 --- a/apps/website/src/workers/tempblotLanguageServer.worker.ts +++ b/apps/website/src/workers/tempblotLanguageServer.worker.ts @@ -5,38 +5,40 @@ import { FileType, type InitializeParams, type InitializeResult, -} from '@volar/language-server/browser'; +} from "@volar/language-server/browser"; import { createTempblotLanguagePlugin, createTempblotServicePlugin, -} from '@tempblot/language-service'; -import * as typescript from 'typescript'; -import { create as createTypeScriptServicePlugins } from 'volar-service-typescript'; +} from "@tempblot/language-service"; +import * as typescript from "typescript"; +import { create as createTypeScriptServicePlugins } from "volar-service-typescript"; const connection = createConnection(); const server = createServer(connection); -const typeScriptLibPath = '/node_modules/typescript/lib'; -const nodeTypesPath = '/playground/node_modules/@types/node'; +const typeScriptLibPath = "/node_modules/typescript/lib"; +const nodeTypesPath = "/playground/node_modules/@types/node"; -connection.onInitialize(async (parameters: InitializeParams): Promise => { - return server.initialize( - parameters, - createTypeScriptProject( - playgroundTypescript, - undefined, - () => ({ - languagePlugins: [createTempblotLanguagePlugin({ serviceScriptMode: 'extraMts' })], +connection.onInitialize( + async (parameters: InitializeParams): Promise => { + return server.initialize( + parameters, + createTypeScriptProject(playgroundTypescript, undefined, () => ({ + languagePlugins: [ + createTempblotLanguagePlugin({ serviceScriptMode: "extraMts" }), + ], setup({ project }) { - patchPlaygroundTypeScriptHost(project.typescript?.languageServiceHost); + patchPlaygroundTypeScriptHost( + project.typescript?.languageServiceHost, + ); }, - }), - ), - [ - ...createTypeScriptServicePlugins(playgroundTypescript), - createTempblotServicePlugin(), - ], - ); -}); + })), + [ + ...createTypeScriptServicePlugins(playgroundTypescript), + createTempblotServicePlugin(), + ], + ); + }, +); connection.onInitialized(() => { server.initialized(); @@ -45,14 +47,19 @@ connection.onInitialized(() => { connection.listen(); function installPlaygroundFileSystem() { - server.fileSystem.install('file', { + server.fileSystem.install("file", { readFile(uri) { return virtualTypeFilesByUri.get(toFileUriString(uri)); }, stat(uri) { const uriString = toFileUriString(uri); if (virtualTypeFilesByUri.has(uriString)) { - return { type: FileType.File, ctime: 0, mtime: 0, size: virtualTypeFilesByUri.get(uriString)!.length }; + return { + type: FileType.File, + ctime: 0, + mtime: 0, + size: virtualTypeFilesByUri.get(uriString)!.length, + }; } if (hasVirtualChild(virtualTypeFilesByUri, uriString)) { return { type: FileType.Directory, ctime: 0, mtime: 0, size: 0 }; @@ -60,7 +67,7 @@ function installPlaygroundFileSystem() { }, readDirectory(uri) { const uriString = toFileUriString(uri); - const prefix = uriString.endsWith('/') ? uriString : `${uriString}/`; + const prefix = uriString.endsWith("/") ? uriString : `${uriString}/`; const entries = new Map(); for (const file of virtualTypeFilesByUri.keys()) { @@ -68,7 +75,7 @@ function installPlaygroundFileSystem() { continue; } - const [name, rest] = file.slice(prefix.length).split('/', 2); + const [name, rest] = file.slice(prefix.length).split("/", 2); entries.set(name, rest ? FileType.Directory : FileType.File); } @@ -77,7 +84,9 @@ function installPlaygroundFileSystem() { }); } -function patchPlaygroundTypeScriptHost(host: typescript.LanguageServiceHost | undefined) { +function patchPlaygroundTypeScriptHost( + host: typescript.LanguageServiceHost | undefined, +) { if (!host) { return; } @@ -92,7 +101,8 @@ function patchPlaygroundTypeScriptHost(host: typescript.LanguageServiceHost | un const originalGetScriptVersion = host.getScriptVersion.bind(host); const originalGetDefaultLibFileName = host.getDefaultLibFileName?.bind(host); const originalResolveLibrary = host.resolveLibrary?.bind(host); - const originalResolveTypeReferenceDirectiveReferences = host.resolveTypeReferenceDirectiveReferences?.bind(host); + const originalResolveTypeReferenceDirectiveReferences = + host.resolveTypeReferenceDirectiveReferences?.bind(host); host.readFile = (fileName) => { const normalized = normalizeFileName(fileName); @@ -101,31 +111,46 @@ function patchPlaygroundTypeScriptHost(host: typescript.LanguageServiceHost | un }; host.fileExists = (fileName) => { const normalized = normalizeFileName(fileName); - return virtualTypeFilesByPath.has(normalized) || originalFileExists?.(fileName) || false; + return ( + virtualTypeFilesByPath.has(normalized) || + originalFileExists?.(fileName) || + false + ); }; host.directoryExists = (directoryName) => { const normalized = normalizeFileName(directoryName); - return hasVirtualChild(virtualTypeFilesByPath, normalized) - || originalDirectoryExists?.(directoryName) - || false; + return ( + hasVirtualChild(virtualTypeFilesByPath, normalized) || + originalDirectoryExists?.(directoryName) || + false + ); }; host.readDirectory = (rootDir, extensions, excludes, includes, depth) => { const normalizedRoot = normalizeFileName(rootDir); - const virtualFiles = [...virtualTypeFilesByPath.keys()].filter((file) => file.startsWith(`${normalizedRoot}/`)); - return [...new Set([ - ...virtualFiles, - ...(originalReadDirectory?.(rootDir, extensions, excludes, includes, depth) ?? []), - ])]; + const virtualFiles = [...virtualTypeFilesByPath.keys()].filter((file) => + file.startsWith(`${normalizedRoot}/`), + ); + return [ + ...new Set([ + ...virtualFiles, + ...(originalReadDirectory?.( + rootDir, + extensions, + excludes, + includes, + depth, + ) ?? []), + ]), + ]; }; host.getCompilationSettings = () => ({ ...originalGetCompilationSettings(), module: playgroundTypescript.ModuleKind.NodeNext, moduleResolution: playgroundTypescript.ModuleResolutionKind.NodeNext, }); - host.getScriptFileNames = () => [...new Set([ - ...originalGetScriptFileNames(), - ...nodeTypeRootFiles, - ])]; + host.getScriptFileNames = () => [ + ...new Set([...originalGetScriptFileNames(), ...nodeTypeRootFiles]), + ]; host.getScriptSnapshot = (fileName) => { const normalized = normalizeFileName(fileName); const source = virtualTypeFilesByPath.get(normalized); @@ -136,14 +161,19 @@ function patchPlaygroundTypeScriptHost(host: typescript.LanguageServiceHost | un }; host.getScriptVersion = (fileName) => { if (virtualTypeFilesByPath.has(normalizeFileName(fileName))) { - return '0'; + return "0"; } return originalGetScriptVersion(fileName); }; host.getDefaultLibFileName = (options) => { - const libName = playgroundTypescript.getDefaultLibFileName(options).split('/').pop()!; + const libName = playgroundTypescript + .getDefaultLibFileName(options) + .split("/") + .pop()!; const virtualPath = `${typeScriptLibPath}/${libName}`; - return virtualTypeFilesByPath.has(virtualPath) ? virtualPath : originalGetDefaultLibFileName?.(options) ?? virtualPath; + return virtualTypeFilesByPath.has(virtualPath) + ? virtualPath + : (originalGetDefaultLibFileName?.(options) ?? virtualPath); }; host.getDefaultLibLocation = () => typeScriptLibPath; host.resolveLibrary = (libraryName, resolveFrom, options, libFileName) => { @@ -157,9 +187,23 @@ function patchPlaygroundTypeScriptHost(host: typescript.LanguageServiceHost | un }, }; } - return originalResolveLibrary?.(libraryName, resolveFrom, options, libFileName) ?? { resolvedModule: undefined }; + return ( + originalResolveLibrary?.( + libraryName, + resolveFrom, + options, + libFileName, + ) ?? { resolvedModule: undefined } + ); }; - host.resolveTypeReferenceDirectiveReferences = (typeDirectiveReferences, containingFile, redirectedReference, options, containingSourceFile, reusedNames) => { + host.resolveTypeReferenceDirectiveReferences = ( + typeDirectiveReferences, + containingFile, + redirectedReference, + options, + containingSourceFile, + reusedNames, + ) => { if (originalResolveTypeReferenceDirectiveReferences) { const originalResults = originalResolveTypeReferenceDirectiveReferences( typeDirectiveReferences, @@ -170,14 +214,23 @@ function patchPlaygroundTypeScriptHost(host: typescript.LanguageServiceHost | un reusedNames, ); - return originalResults.map((result, index) => result.resolvedTypeReferenceDirective ? result : resolvePlaygroundTypeReference(typeDirectiveReferences[index])); + return originalResults.map((result, index) => + result.resolvedTypeReferenceDirective + ? result + : resolvePlaygroundTypeReference(typeDirectiveReferences[index]), + ); } return typeDirectiveReferences.map(resolvePlaygroundTypeReference); }; - function resolvePlaygroundTypeReference(typeDirectiveReference: string | typescript.FileReference) { - const typeName = typeof typeDirectiveReference === 'string' ? typeDirectiveReference : typeDirectiveReference.fileName; + function resolvePlaygroundTypeReference( + typeDirectiveReference: string | typescript.FileReference, + ) { + const typeName = + typeof typeDirectiveReference === "string" + ? typeDirectiveReference + : typeDirectiveReference.fileName; const virtualPath = `/playground/node_modules/@types/${typeName}/index.d.ts`; if (!virtualTypeFilesByPath.has(virtualPath)) { @@ -196,36 +249,50 @@ function patchPlaygroundTypeScriptHost(host: typescript.LanguageServiceHost | un function createVirtualTypeFiles() { return new Map([ - ['/playground/package.json', JSON.stringify({ type: 'module' })], - ['/playground/tsconfig.json', JSON.stringify({ - compilerOptions: { - target: 'ES2021', - lib: ['ES2021'], - types: ['node'], - strict: true, - module: 'NodeNext', - moduleResolution: 'NodeNext', - allowSyntheticDefaultImports: true, - skipLibCheck: true, - }, - include: ['**/*.blot'], - })], - ['/playground/node_modules/@types/node/package.json', JSON.stringify({ - name: '@types/node', - version: '0.0.0-playground', - types: 'index.d.ts', - })], - ...Object.entries(nodeTypeSources).map(([path, source]) => [`${nodeTypesPath}/${path}`, source] as const), - ...Object.entries(typeScriptLibSources).map(([name, source]) => [`${typeScriptLibPath}/${name}`, source] as const), + ["/playground/package.json", JSON.stringify({ type: "module" })], + [ + "/playground/tsconfig.json", + JSON.stringify({ + compilerOptions: { + target: "ES2021", + lib: ["ES2021"], + types: ["node"], + strict: true, + module: "NodeNext", + moduleResolution: "NodeNext", + allowSyntheticDefaultImports: true, + skipLibCheck: true, + }, + include: ["**/*.blot"], + }), + ], + [ + "/playground/node_modules/@types/node/package.json", + JSON.stringify({ + name: "@types/node", + version: "0.0.0-playground", + types: "index.d.ts", + }), + ], + ...Object.entries(nodeTypeSources).map( + ([path, source]) => [`${nodeTypesPath}/${path}`, source] as const, + ), + ...Object.entries(typeScriptLibSources).map( + ([name, source]) => [`${typeScriptLibPath}/${name}`, source] as const, + ), ]); } -function toFileUriString(uri: { scheme: string; path: string; toString(): string }) { - return uri.scheme === 'file' ? `file://${uri.path}` : uri.toString(); +function toFileUriString(uri: { + scheme: string; + path: string; + toString(): string; +}) { + return uri.scheme === "file" ? `file://${uri.path}` : uri.toString(); } function normalizeFileName(fileName: string) { - return fileName.replace(/\\/g, '/'); + return fileName.replace(/\\/g, "/"); } function hasVirtualChild(files: Map, path: string) { @@ -233,19 +300,23 @@ function hasVirtualChild(files: Map, path: string) { } const typeScriptLibSources = Object.fromEntries( - Object.entries(import.meta.glob('/node_modules/typescript/lib/lib*.d.ts', { - eager: true, - query: '?raw', - import: 'default', - })).map(([path, source]) => [path.split('/').pop()!, source as string]), + Object.entries( + import.meta.glob("/node_modules/typescript/lib/lib*.d.ts", { + eager: true, + query: "?raw", + import: "default", + }), + ).map(([path, source]) => [path.split("/").pop()!, source as string]), ); const nodeTypeSources = Object.fromEntries( - Object.entries(import.meta.glob('/node_modules/@types/node/**/*.d.ts', { - eager: true, - query: '?raw', - import: 'default', - })).map(([path, source]) => [path.split('/node/')[1], source as string]), + Object.entries( + import.meta.glob("/node_modules/@types/node/**/*.d.ts", { + eager: true, + query: "?raw", + import: "default", + }), + ).map(([path, source]) => [path.split("/node/")[1], source as string]), ); const playgroundTypescript = { @@ -256,10 +327,15 @@ const playgroundTypescript = { } satisfies typeof typescript; const virtualTypeFilesByPath = createVirtualTypeFiles(); -const nodeTypeRootFiles = [...virtualTypeFilesByPath.keys()] - .filter((fileName) => fileName.startsWith(`${nodeTypesPath}/`) && fileName.endsWith('.d.ts')); +const nodeTypeRootFiles = [...virtualTypeFilesByPath.keys()].filter( + (fileName) => + fileName.startsWith(`${nodeTypesPath}/`) && fileName.endsWith(".d.ts"), +); const virtualTypeFilesByUri = new Map( - [...virtualTypeFilesByPath].map(([path, source]) => [`file://${path}`, source]), + [...virtualTypeFilesByPath].map(([path, source]) => [ + `file://${path}`, + source, + ]), ); installPlaygroundFileSystem(); diff --git a/docs/basic-usage.md b/docs/basic-usage.md index 82c4f1b..22bd511 100644 --- a/docs/basic-usage.md +++ b/docs/basic-usage.md @@ -71,8 +71,8 @@ Use `compilePath` to pass configuration into a `.blot` file. import { compilePath } from "tempblot"; const output = await compilePath("./user.json.blot", { - name: "Ada", - active: true, + name: "Ada", + active: true, }); ``` @@ -97,10 +97,10 @@ You can also define params globally with module augmentation. ```ts declare module "tempblot" { - interface TempblotParams { - name: string; - active: boolean; - } + interface TempblotParams { + name: string; + active: boolean; + } } ``` diff --git a/eslint.config.js b/eslint.config.js index daf221f..8cdc60b 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -1,4 +1,4 @@ -import tempblotPreset from './packages/config/eslint-preset.js' +import tempblotPreset from "./packages/config/eslint-preset.js"; export default [ ...tempblotPreset, @@ -15,7 +15,7 @@ export default [ // outside any tsconfig `include`. Override the global // projectService so the parser doesn't bail with "file was not // found by the project service". - files: ['**/*.mjs', '**/*.cjs', '**/*.js'], + files: ["**/*.mjs", "**/*.cjs", "**/*.js"], languageOptions: { parserOptions: { projectService: false, @@ -23,4 +23,4 @@ export default [ }, }, }, -] +]; diff --git a/fixtures/fixtures.code-workspace b/fixtures/fixtures.code-workspace deleted file mode 100644 index 13a0296..0000000 --- a/fixtures/fixtures.code-workspace +++ /dev/null @@ -1,17 +0,0 @@ -{ - "settings": { - "editor.suggest.showWords": false, - "editor.wordBasedSuggestions": "off", - "git.openRepositoryInParentFolders": "never", - "mdx.server.enable": true, - "mdx.trace.server.verbosity": "compact", - "mdx.trace.server.format": "text", - "typescript.tsdk": "../../node_modules/typescript/lib" - }, - "extensions": { - "recommendations": ["johnsoncodehk.volarjs-labs"] - }, - "folders": [ - {"path": "../sample"} - ] -} \ No newline at end of file diff --git a/packages/compiler/eslint.config.mjs b/packages/compiler/eslint.config.mjs index ed41856..3930669 100644 --- a/packages/compiler/eslint.config.mjs +++ b/packages/compiler/eslint.config.mjs @@ -1,9 +1,9 @@ -import tempblotPreset from '@tempblot/config/eslint-preset.js' +import tempblotPreset from "@tempblot/config/eslint-preset.js"; export default [ ...tempblotPreset, { - files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'], + files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"], languageOptions: { parserOptions: { projectService: true, @@ -13,11 +13,11 @@ export default [ }, { rules: { - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/require-await': 'off', + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/require-await": "off", }, }, { - ignores: ['eslint.config.mjs'], + ignores: ["eslint.config.mjs"], }, -] +]; diff --git a/packages/compiler/src/setup-transformer.ts b/packages/compiler/src/setup-transformer.ts index 748eb6f..7d69e73 100644 --- a/packages/compiler/src/setup-transformer.ts +++ b/packages/compiler/src/setup-transformer.ts @@ -222,7 +222,10 @@ function injectTempblotInstance( continue; } - if (!ts.isImportDeclaration(statement) && !insertedTempblotInstanceDeclaration) { + if ( + !ts.isImportDeclaration(statement) && + !insertedTempblotInstanceDeclaration + ) { statements.push(createTempblotInstanceDeclaration(sourcePath)); insertedTempblotInstanceDeclaration = true; } @@ -256,7 +259,7 @@ export function transformSetup(setup: string, sourcePath: string): string { useParamsLocalNames, ); - return ts.createPrinter({ newLine: ts.NewLineKind.LineFeed }).printFile( - transformedSourceFile, - ); + return ts + .createPrinter({ newLine: ts.NewLineKind.LineFeed }) + .printFile(transformedSourceFile); } diff --git a/packages/compiler/tests/fixtures/outputs/test.json b/packages/compiler/tests/fixtures/outputs/test.json index 9cca225..25f702b 100644 --- a/packages/compiler/tests/fixtures/outputs/test.json +++ b/packages/compiler/tests/fixtures/outputs/test.json @@ -1,4 +1,3 @@ - { - "test": 123 + "test": 123 } diff --git a/packages/compiler/vite.config.ts b/packages/compiler/vite.config.ts index 9d55b53..a2bd9f5 100644 --- a/packages/compiler/vite.config.ts +++ b/packages/compiler/vite.config.ts @@ -2,15 +2,17 @@ import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { defineConfig } from "vite"; import dts from "unplugin-dts/vite"; -import packageJson from './package.json' with {type: "json"} +import packageJson from "./package.json" with { type: "json" }; const __dirname = dirname(fileURLToPath(import.meta.url)); export default defineConfig(({ mode }) => ({ - plugins: [dts({ - tsconfigPath: "tsconfig.app.json", - entryRoot: "src", - })], + plugins: [ + dts({ + tsconfigPath: "tsconfig.app.json", + entryRoot: "src", + }), + ], resolve: { alias: { tempblot: resolve(__dirname, "src/index.ts"), @@ -19,7 +21,7 @@ export default defineConfig(({ mode }) => ({ build: { lib: { name: "Tempblot", - fileName: 'index', + fileName: "index", entry: resolve(__dirname, "src/index.ts"), formats: ["es"], }, @@ -29,10 +31,10 @@ export default defineConfig(({ mode }) => ({ }, test: { name: packageJson.name, - dir: './tests', + dir: "./tests", watch: false, }, define: { - 'import.meta.vitest': mode !== 'production', + "import.meta.vitest": mode !== "production", }, })); diff --git a/packages/config/eslint-preset.js b/packages/config/eslint-preset.js index 7ad1f03..c441b42 100644 --- a/packages/config/eslint-preset.js +++ b/packages/config/eslint-preset.js @@ -1,38 +1,38 @@ -import { defineConfig } from 'eslint/config' -import eslint from '@eslint/js' -import tseslint from 'typescript-eslint' +import { defineConfig } from "eslint/config"; +import eslint from "@eslint/js"; +import tseslint from "typescript-eslint"; const tempblotTSRules = { - '@typescript-eslint/no-empty-function': 'off', - '@typescript-eslint/no-unused-vars': [ - 'warn', // or "error" + "@typescript-eslint/no-empty-function": "off", + "@typescript-eslint/no-unused-vars": [ + "warn", // or "error" { - argsIgnorePattern: '^_', - varsIgnorePattern: '^_', - caughtErrorsIgnorePattern: '^_', + argsIgnorePattern: "^_", + varsIgnorePattern: "^_", + caughtErrorsIgnorePattern: "^_", }, ], -} +}; const tempblotJSRules = { - 'no-restricted-imports': [ - 'error', + "no-restricted-imports": [ + "error", { patterns: [ { - regex: '^\.\.?\/.*\.js$', + regex: "^\.\.?\/.*\.js$", message: "importing `.js` files is not supported by Node's built-in TypeScript support. Use `.ts` instead.", }, { - regex: '^\\./(?!.*\\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs|json)$)', + regex: "^\\./(?!.*\\.(?:ts|tsx|mts|cts|js|jsx|mjs|cjs|json)$)", message: 'Relative imports must include the file extension. For example, use `import foo from "./foo.ts"` instead of `import foo from "./foo"`.', }, ], }, ], -} +}; export default defineConfig( eslint.configs.recommended, @@ -49,16 +49,12 @@ export default defineConfig( // project. Disable the typed lint rules so the project-service // parser doesn't fail with "file was not found by the project // service". - files: ['**/*.js', '**/*.mjs', '**/*.cjs'], + files: ["**/*.js", "**/*.mjs", "**/*.cjs"], extends: [tseslint.configs.disableTypeChecked], }, { // - `eslint-preset.js`: the preset bootstraps itself, ignoring // keeps eslint from re-evaluating it under the typed rules. - ignores: [ - 'eslint-preset.js', - '**/dist/**', - '**/out/**', - ], + ignores: ["eslint-preset.js", "**/dist/**", "**/out/**"], }, -) +); diff --git a/packages/config/eslint.config.mjs b/packages/config/eslint.config.mjs index e70f6bb..1603bf5 100644 --- a/packages/config/eslint.config.mjs +++ b/packages/config/eslint.config.mjs @@ -1,4 +1,4 @@ -import tempblotPreset from './eslint-preset.js' +import tempblotPreset from "./eslint-preset.js"; export default [ ...tempblotPreset, @@ -10,4 +10,4 @@ export default [ }, }, }, -] +]; diff --git a/packages/config/tsignore.ts b/packages/config/tsignore.ts index 336ce12..cb0ff5c 100644 --- a/packages/config/tsignore.ts +++ b/packages/config/tsignore.ts @@ -1 +1 @@ -export {} +export {}; diff --git a/packages/language-server/eslint.config.mjs b/packages/language-server/eslint.config.mjs index cafdccd..327f675 100644 --- a/packages/language-server/eslint.config.mjs +++ b/packages/language-server/eslint.config.mjs @@ -1,9 +1,9 @@ -import tempblotPreset from '@tempblot/config/eslint-preset.js' +import tempblotPreset from "@tempblot/config/eslint-preset.js"; export default [ ...tempblotPreset, { - files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'], + files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"], languageOptions: { parserOptions: { projectService: true, @@ -13,11 +13,11 @@ export default [ }, { rules: { - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/require-await': 'off', + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/require-await": "off", }, }, { - ignores: ['eslint.config.mjs', 'lib/**'], + ignores: ["eslint.config.mjs", "lib/**"], }, -] +]; diff --git a/packages/language-server/src/index.ts b/packages/language-server/src/index.ts index e50791f..f22e62f 100644 --- a/packages/language-server/src/index.ts +++ b/packages/language-server/src/index.ts @@ -1,53 +1,49 @@ #!/usr/bin/env node -import assert from 'node:assert'; -import process from 'node:process'; +import assert from "node:assert"; +import process from "node:process"; import { createTempblotLanguagePlugin, - createTempblotServicePlugin -} from '@tempblot/language-service'; + createTempblotServicePlugin, +} from "@tempblot/language-service"; import { createConnection, createServer, createTypeScriptProject, - loadTsdkByPath -} from '@volar/language-server/node.js'; -import type { - InitializeParams, - InitializeResult -} from '@volar/language-server'; + loadTsdkByPath, +} from "@volar/language-server/node.js"; +import type { + InitializeParams, + InitializeResult, +} from "@volar/language-server"; -process.title = 'tempblot-language-server'; +process.title = "tempblot-language-server"; const connection = createConnection(); const server = createServer(connection); -connection.onInitialize(async (parameters: InitializeParams): Promise => { - const tsdk = getTsdk(parameters); +connection.onInitialize( + async (parameters: InitializeParams): Promise => { + const tsdk = getTsdk(parameters); - const { typescript, diagnosticMessages } = loadTsdkByPath( - tsdk, - parameters.locale - ); + const { typescript, diagnosticMessages } = loadTsdkByPath( + tsdk, + parameters.locale, + ); - return server.initialize( - parameters, - createTypeScriptProject( - typescript, - diagnosticMessages, - () => ({ - languagePlugins: [createTempblotLanguagePlugin()] - }) - ), - [ - createTempblotServicePlugin() - ] - ); -}); + return server.initialize( + parameters, + createTypeScriptProject(typescript, diagnosticMessages, () => ({ + languagePlugins: [createTempblotLanguagePlugin()], + })), + [createTempblotServicePlugin()], + ); + }, +); connection.onInitialized(() => { server.initialized(); - void server.fileWatcher?.watchFiles(['**/*.blot']); + void server.fileWatcher?.watchFiles(["**/*.blot"]); }); connection.listen(); @@ -56,23 +52,23 @@ function getTsdk(parameters: InitializeParams): string { const initializationOptions: unknown = parameters.initializationOptions; assert.ok( initializationOptions !== null && - typeof initializationOptions === 'object' && - 'typescript' in initializationOptions, - 'Missing initialization option typescript.tsdk' + typeof initializationOptions === "object" && + "typescript" in initializationOptions, + "Missing initialization option typescript.tsdk", ); const typescriptOptions: unknown = initializationOptions.typescript; assert.ok( typescriptOptions !== null && - typeof typescriptOptions === 'object' && - 'tsdk' in typescriptOptions, - 'Missing initialization option typescript.tsdk' + typeof typescriptOptions === "object" && + "tsdk" in typescriptOptions, + "Missing initialization option typescript.tsdk", ); const tsdk: unknown = typescriptOptions.tsdk; assert.ok( - typeof tsdk === 'string', - 'Missing initialization option typescript.tsdk' + typeof tsdk === "string", + "Missing initialization option typescript.tsdk", ); return tsdk; diff --git a/packages/language-server/tsconfig.json b/packages/language-server/tsconfig.json index 2a77f1e..043a06e 100644 --- a/packages/language-server/tsconfig.json +++ b/packages/language-server/tsconfig.json @@ -10,11 +10,6 @@ "declarationMap": true, "sourceMap": true }, - "include": [ - "src/**/*" - ], - "exclude": [ - "lib", - "test" - ] + "include": ["src/**/*"], + "exclude": ["lib", "test"] } diff --git a/packages/language-service/eslint.config.mjs b/packages/language-service/eslint.config.mjs index cafdccd..327f675 100644 --- a/packages/language-service/eslint.config.mjs +++ b/packages/language-service/eslint.config.mjs @@ -1,9 +1,9 @@ -import tempblotPreset from '@tempblot/config/eslint-preset.js' +import tempblotPreset from "@tempblot/config/eslint-preset.js"; export default [ ...tempblotPreset, { - files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'], + files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"], languageOptions: { parserOptions: { projectService: true, @@ -13,11 +13,11 @@ export default [ }, { rules: { - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/require-await': 'off', + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/require-await": "off", }, }, { - ignores: ['eslint.config.mjs', 'lib/**'], + ignores: ["eslint.config.mjs", "lib/**"], }, -] +]; diff --git a/packages/language-service/src/index.ts b/packages/language-service/src/index.ts index f700129..572b586 100644 --- a/packages/language-service/src/index.ts +++ b/packages/language-service/src/index.ts @@ -1,2 +1,5 @@ -export { createTempblotLanguagePlugin, TempblotVirtualCode } from './language-plugin.ts'; -export { createTempblotServicePlugin } from './service-plugin.ts'; +export { + createTempblotLanguagePlugin, + TempblotVirtualCode, +} from "./language-plugin.ts"; +export { createTempblotServicePlugin } from "./service-plugin.ts"; diff --git a/packages/language-service/src/language-plugin.ts b/packages/language-service/src/language-plugin.ts index e677a86..8ca07d1 100644 --- a/packages/language-service/src/language-plugin.ts +++ b/packages/language-service/src/language-plugin.ts @@ -15,10 +15,9 @@ interface TempblotLanguagePluginOptions { serviceScriptMode?: "primary" | "extraMts"; } -export function createTempblotLanguagePlugin(options: TempblotLanguagePluginOptions = {}): LanguagePlugin< - URI, - TempblotVirtualCode -> { +export function createTempblotLanguagePlugin( + options: TempblotLanguagePluginOptions = {}, +): LanguagePlugin { const serviceScriptMode = options.serviceScriptMode ?? "primary"; return { @@ -85,9 +84,7 @@ export function createTempblotLanguagePlugin(options: TempblotLanguagePluginOpti } function getCombinedContextCode(root: VirtualCode) { - return root.embeddedCodes?.find( - (code) => code.id === "combined_context", - ); + return root.embeddedCodes?.find((code) => code.id === "combined_context"); } export class TempblotVirtualCode implements VirtualCode { @@ -118,7 +115,9 @@ export class TempblotVirtualCode implements VirtualCode { }, }, ]; - this.rootDocument = parseTempblotRoot(snapshot.getText(0, snapshot.getLength())); + this.rootDocument = parseTempblotRoot( + snapshot.getText(0, snapshot.getLength()), + ); this.embeddedCodes = [ ...getTempblotEmbeddedCodes(snapshot, this.rootDocument), ]; @@ -156,8 +155,7 @@ function* getTempblotEmbeddedCodes( combinedText += interpLine; // Map the interpolation expression to the original source - const expressionStart = - interpStartOffset + `(`.length; + const expressionStart = interpStartOffset + `(`.length; tsInterpolationMappings.push({ sourceOffsets: [output.startTagEnd + interp.sourceStart], generatedOffsets: [expressionStart], diff --git a/packages/language-service/src/service-plugin.ts b/packages/language-service/src/service-plugin.ts index e461151..f70b8f9 100644 --- a/packages/language-service/src/service-plugin.ts +++ b/packages/language-service/src/service-plugin.ts @@ -3,33 +3,44 @@ import type { LanguageServiceContext, LanguageServicePlugin, LanguageServicePluginInstance, -} from '@volar/language-service'; -import { URI } from 'vscode-uri'; -import { TempblotVirtualCode } from './language-plugin.ts'; +} from "@volar/language-service"; +import { URI } from "vscode-uri"; +import { TempblotVirtualCode } from "./language-plugin.ts"; type DiagnosticsDocument = Parameters< - NonNullable + NonNullable >[0]; export function createTempblotServicePlugin(): LanguageServicePlugin { return { capabilities: { - diagnosticProvider: { interFileDependencies: false, workspaceDiagnostics: false }, + diagnosticProvider: { + interFileDependencies: false, + workspaceDiagnostics: false, + }, }, create(context: LanguageServiceContext) { return { provideDiagnostics(document: DiagnosticsDocument) { - const decoded = context.decodeEmbeddedDocumentUri(URI.parse(document.uri)); + const decoded = context.decodeEmbeddedDocumentUri( + URI.parse(document.uri), + ); if (!decoded) { // Not a embedded document return; } - const virtualCode = context.language.scripts.get(decoded[0])?.generated?.embeddedCodes.get(decoded[1]); + const virtualCode = context.language.scripts + .get(decoded[0]) + ?.generated?.embeddedCodes.get(decoded[1]); if (!(virtualCode instanceof TempblotVirtualCode)) { return; } - const setupNodes = virtualCode.rootDocument.blocks.filter((root) => root.tag === 'setup'); - const outputNodes = virtualCode.rootDocument.blocks.filter((root) => root.tag === 'output'); + const setupNodes = virtualCode.rootDocument.blocks.filter( + (root) => root.tag === "setup", + ); + const outputNodes = virtualCode.rootDocument.blocks.filter( + (root) => root.tag === "output", + ); if (setupNodes.length == 1 && outputNodes.length == 1) { return; @@ -44,8 +55,8 @@ export function createTempblotServicePlugin(): LanguageServicePlugin { start: document.positionAt(0), end: document.positionAt(1), }, - source: 'tempblot', - message: 'Missing setup tag.', + source: "tempblot", + message: "Missing setup tag.", }); } @@ -56,8 +67,8 @@ export function createTempblotServicePlugin(): LanguageServicePlugin { start: document.positionAt(0), end: document.positionAt(1), }, - source: 'tempblot', - message: 'Missing output tag.', + source: "tempblot", + message: "Missing output tag.", }); } @@ -68,8 +79,8 @@ export function createTempblotServicePlugin(): LanguageServicePlugin { start: document.positionAt(setupNodes[i].start), end: document.positionAt(setupNodes[i].end), }, - source: 'tempblot', - message: 'Only one setup tag is allowed.', + source: "tempblot", + message: "Only one setup tag is allowed.", }); } @@ -80,8 +91,8 @@ export function createTempblotServicePlugin(): LanguageServicePlugin { start: document.positionAt(outputNodes[i].start), end: document.positionAt(outputNodes[i].end), }, - source: 'tempblot', - message: 'Only one output tag is allowed.', + source: "tempblot", + message: "Only one output tag is allowed.", }); } return errors; diff --git a/packages/parser/eslint.config.mjs b/packages/parser/eslint.config.mjs index cafdccd..327f675 100644 --- a/packages/parser/eslint.config.mjs +++ b/packages/parser/eslint.config.mjs @@ -1,9 +1,9 @@ -import tempblotPreset from '@tempblot/config/eslint-preset.js' +import tempblotPreset from "@tempblot/config/eslint-preset.js"; export default [ ...tempblotPreset, { - files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'], + files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"], languageOptions: { parserOptions: { projectService: true, @@ -13,11 +13,11 @@ export default [ }, { rules: { - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/require-await': 'off', + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/require-await": "off", }, }, { - ignores: ['eslint.config.mjs', 'lib/**'], + ignores: ["eslint.config.mjs", "lib/**"], }, -] +]; diff --git a/packages/parser/src/index.ts b/packages/parser/src/index.ts index cf2a416..809a936 100644 --- a/packages/parser/src/index.ts +++ b/packages/parser/src/index.ts @@ -112,7 +112,11 @@ export function tokenizeRoot(source: string): RootToken[] { tokens.push({ type: "TagOpenStart", - attributes: { name: tagName, start: tagStart, end: tagStart + tagName.length + 1 }, + attributes: { + name: tagName, + start: tagStart, + end: tagStart + tagName.length + 1, + }, }); const attributePattern = /([a-zA-Z-]+)="([^"]*)"/g; @@ -206,7 +210,10 @@ export function parseRootDocument(tokens: RootToken[]): ParsedRoot { continue; } - if (token.type === "TagClose" && token.attributes.name === currentBlock.tag) { + if ( + token.type === "TagClose" && + token.attributes.name === currentBlock.tag + ) { currentBlock.endTagStart = token.attributes.start; currentBlock.endTagEnd = token.attributes.end; currentBlock.end = token.attributes.end; @@ -227,9 +234,12 @@ export function parseRootDocument(tokens: RootToken[]): ParsedRoot { export function parseRoot(tokens: RootToken[]): RequiredRoot { const document = parseRootDocument(tokens); return { - setup: document.blocks.find((block) => block.tag === "setup") ?? emptyBlock("setup"), + setup: + document.blocks.find((block) => block.tag === "setup") ?? + emptyBlock("setup"), output: - document.blocks.find((block) => block.tag === "output") ?? emptyBlock("output"), + document.blocks.find((block) => block.tag === "output") ?? + emptyBlock("output"), }; } @@ -294,7 +304,10 @@ export function scanInterpolations(text: string): InterpolationData[] { return interpolations; } -function findBestInterpolationEnd(text: string, candidates: number[]): number | undefined { +function findBestInterpolationEnd( + text: string, + candidates: number[], +): number | undefined { for (let i = 0; i < candidates.length; i++) { const candidate = candidates[i]; const hasLaterCandidate = i < candidates.length - 1; @@ -319,7 +332,9 @@ export function transformOutputTemplate(output: string): string { let lastOffset = 0; for (const interpolation of interpolations) { - transformed += escapeOutputText(output.slice(lastOffset, interpolation.fullStart)); + transformed += escapeOutputText( + output.slice(lastOffset, interpolation.fullStart), + ); transformed += "${"; transformed += interpolation.expression; transformed += "}"; diff --git a/packages/parser/tests/output-transformer.spec.ts b/packages/parser/tests/output-transformer.spec.ts index 70a20d0..1fc2163 100644 --- a/packages/parser/tests/output-transformer.spec.ts +++ b/packages/parser/tests/output-transformer.spec.ts @@ -4,7 +4,9 @@ import { transformOutputTemplate } from "../src/index.ts"; test("transformOutputTemplate", () => { const source = 'const someStr = `
<\\>" : "\\<\\<">>
`;'; const cleaned = transformOutputTemplate(source); - expect(cleaned).toEqual('const someStr = \\`
${val ? ">>" : "<<"}
\\`;'); + expect(cleaned).toEqual( + 'const someStr = \\`
${val ? ">>" : "<<"}
\\`;', + ); }); test("transformOutputTemplate allows shift operators in interpolations", () => { diff --git a/packages/parser/tests/root-lexer.spec.ts b/packages/parser/tests/root-lexer.spec.ts index 7973b6b..26cf92b 100644 --- a/packages/parser/tests/root-lexer.spec.ts +++ b/packages/parser/tests/root-lexer.spec.ts @@ -40,7 +40,7 @@ const setup = 123; { type: "TagOpenStart", attributes: { name: "output" } }, { type: "TagAttribute", attributes: { name: "lang", value: "json" } }, { type: "TagOpenEnd", attributes: { name: "output" } }, - { type: "Text", attributes: { value: "\n{\n \"test\": <>\n}\n" } }, + { type: "Text", attributes: { value: '\n{\n "test": <>\n}\n' } }, { type: "TagClose", attributes: { name: "output" } }, ]); }); @@ -72,7 +72,7 @@ const hello = 123; type: "Text", attributes: { value: - "\n{\n \"test\": <>\n}\n", + '\n{\n "test": <>\n}\n', }, }, { type: "TagClose", attributes: { name: "output" } }, @@ -108,7 +108,7 @@ const setup = 123; { type: "TagOpenStart", attributes: { name: "output" } }, { type: "TagAttribute", attributes: { name: "lang", value: "json" } }, { type: "TagOpenEnd", attributes: { name: "output" } }, - { type: "Text", attributes: { value: "\n{\n \"test\": <>\n}\n" } }, + { type: "Text", attributes: { value: '\n{\n "test": <>\n}\n' } }, { type: "TagClose", attributes: { name: "output" } }, { type: "Text", attributes: { value: "\n" } }, ]); diff --git a/packages/parser/vite.config.ts b/packages/parser/vite.config.ts index 67c8c62..f6832d0 100644 --- a/packages/parser/vite.config.ts +++ b/packages/parser/vite.config.ts @@ -1,13 +1,13 @@ -import { defineConfig } from 'vitest/config' -import packageJson from './package.json' with { type: 'json' } +import { defineConfig } from "vitest/config"; +import packageJson from "./package.json" with { type: "json" }; export default defineConfig(({ mode }) => ({ test: { name: packageJson.name, - dir: './tests', + dir: "./tests", watch: false, }, define: { - 'import.meta.vitest': mode !== 'production', + "import.meta.vitest": mode !== "production", }, -})) +})); diff --git a/packages/prettier-plugin/eslint.config.mjs b/packages/prettier-plugin/eslint.config.mjs index cafdccd..327f675 100644 --- a/packages/prettier-plugin/eslint.config.mjs +++ b/packages/prettier-plugin/eslint.config.mjs @@ -1,9 +1,9 @@ -import tempblotPreset from '@tempblot/config/eslint-preset.js' +import tempblotPreset from "@tempblot/config/eslint-preset.js"; export default [ ...tempblotPreset, { - files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'], + files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"], languageOptions: { parserOptions: { projectService: true, @@ -13,11 +13,11 @@ export default [ }, { rules: { - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/require-await': 'off', + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/require-await": "off", }, }, { - ignores: ['eslint.config.mjs', 'lib/**'], + ignores: ["eslint.config.mjs", "lib/**"], }, -] +]; diff --git a/packages/prettier-plugin/src/index.ts b/packages/prettier-plugin/src/index.ts index 6d8ca4c..19dc86f 100644 --- a/packages/prettier-plugin/src/index.ts +++ b/packages/prettier-plugin/src/index.ts @@ -1,20 +1,31 @@ -import * as prettier from 'prettier'; -import type { AstPath, Options, Parser, ParserOptions, Plugin, Printer } from 'prettier'; -import { parseTempblotRoot, type ParsedRoot, type RootBlock } from '@tempblot/parser'; +import * as prettier from "prettier"; +import type { + AstPath, + Options, + Parser, + ParserOptions, + Plugin, + Printer, +} from "prettier"; +import { + parseTempblotRoot, + type ParsedRoot, + type RootBlock, +} from "@tempblot/parser"; interface TempblotAst { - type: 'TempblotDocument'; + type: "TempblotDocument"; source: string; root: ParsedRoot; formattedBlocks: Map; } const parser: Parser = { - astFormat: 'tempblot-ast', + astFormat: "tempblot-ast", async parse(source, options) { const root = parseTempblotRoot(source); return { - type: 'TempblotDocument', + type: "TempblotDocument", source, root, formattedBlocks: await formatBlocks(source, root.blocks, options), @@ -37,17 +48,17 @@ const printer: Printer = { const plugin: Plugin = { languages: [ { - name: 'Tempblot', - parsers: ['tempblot'], - extensions: ['.blot'], - vscodeLanguageIds: ['tempblot'], + name: "Tempblot", + parsers: ["tempblot"], + extensions: [".blot"], + vscodeLanguageIds: ["tempblot"], }, ], parsers: { tempblot: parser, }, printers: { - 'tempblot-ast': printer, + "tempblot-ast": printer, }, }; @@ -72,13 +83,15 @@ function formatDocument(path: AstPath) { trailingText, ].filter((part) => part.length > 0); - return ensureTrailingNewline(parts.join('\n\n')); + return ensureTrailingNewline(parts.join("\n\n")); } function formatBlock(ast: TempblotAst, block: RootBlock) { const openTag = formatOpenTag(block); const closeTag = ``; - const contents = ast.formattedBlocks.get(block) ?? ast.source.slice(block.startTagEnd, block.endTagStart).trim(); + const contents = + ast.formattedBlocks.get(block) ?? + ast.source.slice(block.startTagEnd, block.endTagStart).trim(); if (!contents) { return `${openTag}\n${closeTag}`; @@ -90,7 +103,7 @@ function formatBlock(ast: TempblotAst, block: RootBlock) { function formatOpenTag(block: RootBlock) { const attributes = Object.entries(block.attributes) .map(([name, value]) => `${name}="${value}"`) - .join(' '); + .join(" "); return attributes ? `<${block.tag} ${attributes}>` : `<${block.tag}>`; } @@ -99,41 +112,58 @@ function ensureTrailingNewline(text: string) { return `${text}\n`; } -async function formatBlocks(source: string, blocks: RootBlock[], options: ParserOptions) { +async function formatBlocks( + source: string, + blocks: RootBlock[], + options: ParserOptions, +) { const formattedBlocks = new Map(); - await Promise.all(blocks.map(async (block) => { - const parserName = getBlockParser(block); - const rawContents = source.slice(block.startTagEnd, block.endTagStart).trim(); - - if (!parserName || !rawContents) { - formattedBlocks.set(block, rawContents); - return; - } - - formattedBlocks.set(block, await formatEmbedded(rawContents, parserName, options)); - })); + await Promise.all( + blocks.map(async (block) => { + const parserName = getBlockParser(block); + const rawContents = source + .slice(block.startTagEnd, block.endTagStart) + .trim(); + + if (!parserName || !rawContents) { + formattedBlocks.set(block, rawContents); + return; + } + + formattedBlocks.set( + block, + await formatEmbedded(rawContents, parserName, options), + ); + }), + ); return formattedBlocks; } -async function formatEmbedded(source: string, parserName: string, options: ParserOptions) { +async function formatEmbedded( + source: string, + parserName: string, + options: ParserOptions, +) { try { - return (await prettier.format(source, { - ...copyPrettierOptions(options), - parser: parserName, - })).trim(); + return ( + await prettier.format(source, { + ...copyPrettierOptions(options), + parser: parserName, + }) + ).trim(); } catch { return source.trim(); } } function getBlockParser(block: RootBlock) { - if (block.tag === 'setup') { - return 'typescript'; + if (block.tag === "setup") { + return "typescript"; } - if (block.tag !== 'output') { + if (block.tag !== "output") { return undefined; } @@ -143,22 +173,22 @@ function getBlockParser(block: RootBlock) { function getOutputParser(lang: string | undefined) { switch (lang) { case undefined: - case 'json': - case 'jsonc': - return 'json'; - case 'yaml': - case 'yml': - return 'yaml'; - case 'html': - return 'html'; - case 'css': - return 'css'; - case 'javascript': - case 'js': - return 'babel'; - case 'typescript': - case 'ts': - return 'typescript'; + case "json": + case "jsonc": + return "json"; + case "yaml": + case "yml": + return "yaml"; + case "html": + return "html"; + case "css": + return "css"; + case "javascript": + case "js": + return "babel"; + case "typescript": + case "ts": + return "typescript"; default: return undefined; } diff --git a/packages/prettier-plugin/tests/index.spec.ts b/packages/prettier-plugin/tests/index.spec.ts index 506a7c8..e585b02 100644 --- a/packages/prettier-plugin/tests/index.spec.ts +++ b/packages/prettier-plugin/tests/index.spec.ts @@ -98,9 +98,13 @@ const value={hello:"world"} `.trim(), ); - const cli = await render(`pnpm exec prettier --plugin ./lib/index.js ${fixtureArg}`, [], { - cwd: packageRoot, - }); + const cli = await render( + `pnpm exec prettier --plugin ./lib/index.js ${fixtureArg}`, + [], + { + cwd: packageRoot, + }, + ); await waitForExit(cli); diff --git a/packages/prettier-plugin/vite.config.ts b/packages/prettier-plugin/vite.config.ts index 67c8c62..f6832d0 100644 --- a/packages/prettier-plugin/vite.config.ts +++ b/packages/prettier-plugin/vite.config.ts @@ -1,13 +1,13 @@ -import { defineConfig } from 'vitest/config' -import packageJson from './package.json' with { type: 'json' } +import { defineConfig } from "vitest/config"; +import packageJson from "./package.json" with { type: "json" }; export default defineConfig(({ mode }) => ({ test: { name: packageJson.name, - dir: './tests', + dir: "./tests", watch: false, }, define: { - 'import.meta.vitest': mode !== 'production', + "import.meta.vitest": mode !== "production", }, -})) +})); diff --git a/packages/typescript-plugin/README.md b/packages/typescript-plugin/README.md index 222e491..f5124d1 100644 --- a/packages/typescript-plugin/README.md +++ b/packages/typescript-plugin/README.md @@ -23,9 +23,7 @@ Add to your VS Code workspace settings (`.vscode/settings.json`): ```json { - "typescript.preferences.plugins": [ - { "name": "@tempblot/typescript-plugin" } - ] + "typescript.preferences.plugins": [{ "name": "@tempblot/typescript-plugin" }] } ``` @@ -36,9 +34,7 @@ Add to your `tsconfig.json`: ```json { "compilerOptions": { - "plugins": [ - { "name": "@tempblot/typescript-plugin" } - ] + "plugins": [{ "name": "@tempblot/typescript-plugin" }] } } ``` diff --git a/packages/typescript-plugin/eslint.config.mjs b/packages/typescript-plugin/eslint.config.mjs index 04eafaf..81ff549 100644 --- a/packages/typescript-plugin/eslint.config.mjs +++ b/packages/typescript-plugin/eslint.config.mjs @@ -1,9 +1,9 @@ -import tempblotPreset from '@tempblot/config/eslint-preset.js' +import tempblotPreset from "@tempblot/config/eslint-preset.js"; export default [ ...tempblotPreset, { - files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'], + files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"], languageOptions: { parserOptions: { projectService: true, @@ -13,23 +13,23 @@ export default [ }, { rules: { - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/require-await': 'off', + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/require-await": "off", }, }, { - files: ['lib/**/*.cjs'], + files: ["lib/**/*.cjs"], languageOptions: { globals: { - module: 'readonly', - require: 'readonly', + module: "readonly", + require: "readonly", }, }, rules: { - '@typescript-eslint/no-require-imports': 'off', + "@typescript-eslint/no-require-imports": "off", }, }, { - ignores: ['eslint.config.mjs'], + ignores: ["eslint.config.mjs"], }, -] +]; diff --git a/packages/typescript-plugin/lib/index.cjs b/packages/typescript-plugin/lib/index.cjs index dcd0ffb..38c0662 100644 --- a/packages/typescript-plugin/lib/index.cjs +++ b/packages/typescript-plugin/lib/index.cjs @@ -1,18 +1,18 @@ -'use strict' +"use strict"; const { createTempblotLanguagePlugin, - createTempblotServicePlugin -} = require('@tempblot/language-service') + createTempblotServicePlugin, +} = require("@tempblot/language-service"); const { - createLanguageServicePlugin -} = require('@volar/typescript/lib/quickstart/createLanguageServicePlugin.js') + createLanguageServicePlugin, +} = require("@volar/typescript/lib/quickstart/createLanguageServicePlugin.js"); const plugin = createLanguageServicePlugin((ts, info) => { return { languagePlugins: [createTempblotLanguagePlugin()], - servicePlugins: [createTempblotServicePlugin()] - } -}) + servicePlugins: [createTempblotServicePlugin()], + }; +}); -module.exports = plugin +module.exports = plugin; diff --git a/packages/typescript-plugin/tsconfig.json b/packages/typescript-plugin/tsconfig.json index b919319..e48db3e 100644 --- a/packages/typescript-plugin/tsconfig.json +++ b/packages/typescript-plugin/tsconfig.json @@ -1,7 +1,7 @@ { "extends": "@tempblot/config/tsconfig.json", "include": ["lib"], - "references": [{"path": "../language-service/tsconfig.json"}], + "references": [{ "path": "../language-service/tsconfig.json" }], "compilerOptions": { "declaration": true, "emitDeclarationOnly": true, diff --git a/packages/vscode/eslint.config.mjs b/packages/vscode/eslint.config.mjs index ed41856..3930669 100644 --- a/packages/vscode/eslint.config.mjs +++ b/packages/vscode/eslint.config.mjs @@ -1,9 +1,9 @@ -import tempblotPreset from '@tempblot/config/eslint-preset.js' +import tempblotPreset from "@tempblot/config/eslint-preset.js"; export default [ ...tempblotPreset, { - files: ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'], + files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"], languageOptions: { parserOptions: { projectService: true, @@ -13,11 +13,11 @@ export default [ }, { rules: { - '@typescript-eslint/no-unused-vars': 'off', - '@typescript-eslint/require-await': 'off', + "@typescript-eslint/no-unused-vars": "off", + "@typescript-eslint/require-await": "off", }, }, { - ignores: ['eslint.config.mjs'], + ignores: ["eslint.config.mjs"], }, -] +]; diff --git a/packages/vscode/images/LICENSE.md b/packages/vscode/images/LICENSE.md index b3ef9ce..19b0a39 100644 --- a/packages/vscode/images/LICENSE.md +++ b/packages/vscode/images/LICENSE.md @@ -11,4 +11,4 @@ The tempblot logo is the "🫟" emoji from Google's Noto Emoji font: https://fonts.google.com/noto/specimen/Noto+Emoji -Extracted to SVG directly from the TTF. \ No newline at end of file +Extracted to SVG directly from the TTF. diff --git a/packages/vscode/package.json b/packages/vscode/package.json index b115c35..d913cfc 100644 --- a/packages/vscode/package.json +++ b/packages/vscode/package.json @@ -1,136 +1,136 @@ { - "private": true, - "name": "@tempblot/vscode", - "version": "0.1.0", - "publisher": "crutchcorn", - "author": "Corbin Crutchley ", - "license": "MIT", - "categories": [ - "Programming Languages" - ], - "displayName": "Tempblot Language Features", - "description": "Syntax highlighting and language features for Tempblot files", - "repository": { - "type": "git", - "url": "git+https://github.com/crutchcorn/tempblot.git", - "directory": "packages/vscode" - }, - "homepage": "https://github.com/crutchcorn/tempblot", - "bugs": { - "url": "https://github.com/crutchcorn/tempblot/issues" - }, - "keywords": [ - "intellisense", - "language-server", - "syntax-highlighting", - "tempblot", - "typescript", - "vscode" - ], - "engines": { - "vscode": "^1.104.0" - }, - "activationEvents": [ - "onLanguage:tempblot" - ], - "main": "./out/extension.js", - "contributes": { - "configuration": [ - { - "title": "Tempblot", - "properties": { - "tempblot.server.enable": { - "type": "boolean", - "default": true, - "description": "Enable experimental IntelliSense support for Tempblot files." - }, - "tempblot.trace.server.verbosity": { - "enum": [ - "off", - "messages", - "compact", - "verbose" - ], - "default": "off", - "description": "Trace Tempblot language server requests in the output console." - }, - "tempblot.trace.server.format": { - "enum": [ - "text", - "json" - ], - "default": "text", - "description": "How to format traced Tempblot language server requests." - } - } - } - ], - "languages": [ - { - "id": "tempblot", - "icon": { - "light": "images/tempblot_light.png", - "dark": "images/tempblot_dark.png" - }, - "extensions": [ - ".blot" - ] - } - ], - "grammars": [ - { - "language": "tempblot", - "scopeName": "source.tempblot", - "path": "./syntaxes/tempblot.tmLanguage.json", - "embeddedLanguages": { - "source.tempblot": "tempblot", - "text.html.derivative": "html", - "text.html.markdown": "markdown", - "source.ts": "typescript", - "source.css": "css", - "source.css.scss": "scss", - "source.css.less": "less", - "source.sass": "sass", - "source.stylus": "stylus", - "source.postcss": "postcss", - "source.vue": "vue", - "source.coffee": "coffee", - "source.json": "json", - "source.json.comments": "jsonc", - "source.json5": "json5", - "source.yaml": "yaml", - "source.toml": "toml", - "source.graphql": "graphql" - } - } - ], - "typescriptServerPlugins": [ - { - "name": "@tempblot/typescript-plugin", - "enableForWorkspaceTypeScriptVersions": true, - "configNamespace": "typescript", - "languages": [ - "tempblot" - ] - } - ] - }, - "scripts": { - "build": "node scripts/build.mjs", - "watch": "npm run build -- --watch", - "pack": "npm run build && vsce package --pre-release", - "release": "npm run build && vsce publish --pre-release", - "test:eslint": "eslint ." - }, - "dependencies": { - "@tempblot/language-server": "^0.1.0", - "@volar/language-server": "~2.4.28", - "@volar/vscode": "~2.4.23" - }, - "devDependencies": { - "@tempblot/config": "workspace:*", - "@types/vscode": "^1.104.0", - "esbuild": "^0.28.0", - "vsce": "^2.15.0" - } + "private": true, + "name": "@tempblot/vscode", + "version": "0.1.0", + "publisher": "crutchcorn", + "author": "Corbin Crutchley ", + "license": "MIT", + "categories": [ + "Programming Languages" + ], + "displayName": "Tempblot Language Features", + "description": "Syntax highlighting and language features for Tempblot files", + "repository": { + "type": "git", + "url": "git+https://github.com/crutchcorn/tempblot.git", + "directory": "packages/vscode" + }, + "homepage": "https://github.com/crutchcorn/tempblot", + "bugs": { + "url": "https://github.com/crutchcorn/tempblot/issues" + }, + "keywords": [ + "intellisense", + "language-server", + "syntax-highlighting", + "tempblot", + "typescript", + "vscode" + ], + "engines": { + "vscode": "^1.104.0" + }, + "activationEvents": [ + "onLanguage:tempblot" + ], + "main": "./out/extension.js", + "contributes": { + "configuration": [ + { + "title": "Tempblot", + "properties": { + "tempblot.server.enable": { + "type": "boolean", + "default": true, + "description": "Enable experimental IntelliSense support for Tempblot files." + }, + "tempblot.trace.server.verbosity": { + "enum": [ + "off", + "messages", + "compact", + "verbose" + ], + "default": "off", + "description": "Trace Tempblot language server requests in the output console." + }, + "tempblot.trace.server.format": { + "enum": [ + "text", + "json" + ], + "default": "text", + "description": "How to format traced Tempblot language server requests." + } + } + } + ], + "languages": [ + { + "id": "tempblot", + "icon": { + "light": "images/tempblot_light.png", + "dark": "images/tempblot_dark.png" + }, + "extensions": [ + ".blot" + ] + } + ], + "grammars": [ + { + "language": "tempblot", + "scopeName": "source.tempblot", + "path": "./syntaxes/tempblot.tmLanguage.json", + "embeddedLanguages": { + "source.tempblot": "tempblot", + "text.html.derivative": "html", + "text.html.markdown": "markdown", + "source.ts": "typescript", + "source.css": "css", + "source.css.scss": "scss", + "source.css.less": "less", + "source.sass": "sass", + "source.stylus": "stylus", + "source.postcss": "postcss", + "source.vue": "vue", + "source.coffee": "coffee", + "source.json": "json", + "source.json.comments": "jsonc", + "source.json5": "json5", + "source.yaml": "yaml", + "source.toml": "toml", + "source.graphql": "graphql" + } + } + ], + "typescriptServerPlugins": [ + { + "name": "@tempblot/typescript-plugin", + "enableForWorkspaceTypeScriptVersions": true, + "configNamespace": "typescript", + "languages": [ + "tempblot" + ] + } + ] + }, + "scripts": { + "build": "node scripts/build.mjs", + "watch": "npm run build -- --watch", + "pack": "npm run build && vsce package --pre-release", + "release": "npm run build && vsce publish --pre-release", + "test:eslint": "eslint ." + }, + "dependencies": { + "@tempblot/language-server": "^0.1.0", + "@volar/language-server": "~2.4.28", + "@volar/vscode": "~2.4.23" + }, + "devDependencies": { + "@tempblot/config": "workspace:*", + "@types/vscode": "^1.104.0", + "esbuild": "^0.28.0", + "vsce": "^2.15.0" + } } diff --git a/packages/vscode/scripts/build.mjs b/packages/vscode/scripts/build.mjs index 4f1acbb..861bd93 100644 --- a/packages/vscode/scripts/build.mjs +++ b/packages/vscode/scripts/build.mjs @@ -1,27 +1,26 @@ #!/usr/bin/env node -import {createRequire} from 'node:module' -import process from 'node:process' -import {URL, fileURLToPath} from 'node:url' -import {build} from 'esbuild' +import { createRequire } from "node:module"; +import process from "node:process"; +import { URL, fileURLToPath } from "node:url"; +import { build } from "esbuild"; -const require = createRequire(import.meta.url) +const require = createRequire(import.meta.url); -const debug = process.argv.includes('debug') +const debug = process.argv.includes("debug"); await build({ bundle: true, entryPoints: { - 'out/extension': require.resolve('../src/extension.ts'), - 'out/language-server': require.resolve('@tempblot/language-server'), - 'node_modules/@tempblot/typescript-plugin': require.resolve( - '../../typescript-plugin/lib/index.cjs' - ) + "out/extension": require.resolve("../src/extension.ts"), + "out/language-server": require.resolve("@tempblot/language-server"), + "node_modules/@tempblot/typescript-plugin": + require.resolve("../../typescript-plugin/lib/index.cjs"), }, - external: ['vscode'], - logLevel: 'info', - mainFields: ['module', 'main'], + external: ["vscode"], + logLevel: "info", + mainFields: ["module", "main"], minify: !debug, - outdir: fileURLToPath(new URL('../', import.meta.url)), - platform: 'node', - sourcemap: debug -}) + outdir: fileURLToPath(new URL("../", import.meta.url)), + platform: "node", + sourcemap: debug, +}); diff --git a/packages/vscode/src/extension.ts b/packages/vscode/src/extension.ts index 8bc0ce7..d8aa6c3 100644 --- a/packages/vscode/src/extension.ts +++ b/packages/vscode/src/extension.ts @@ -1,44 +1,44 @@ -import * as languageServerProtocol from '@volar/language-server/protocol.js'; +import * as languageServerProtocol from "@volar/language-server/protocol.js"; import { activateAutoInsertion, activateDocumentDropEdit, createLabsInfo, - getTsdk -} from '@volar/vscode'; + getTsdk, +} from "@volar/vscode"; import { extensions, window, workspace, Disposable, ProgressLocation, - ExtensionContext -} from 'vscode'; -import { LanguageClient, TransportKind } from '@volar/vscode/node.js'; + ExtensionContext, +} from "vscode"; +import { LanguageClient, TransportKind } from "@volar/vscode/node.js"; let client: LanguageClient | undefined; let disposable: Disposable | undefined; export async function activate(context: ExtensionContext) { // Activate TypeScript extension first - extensions.getExtension('vscode.typescript-language-features')?.activate(); + extensions.getExtension("vscode.typescript-language-features")?.activate(); // Get TypeScript SDK path - const { tsdk } = (await getTsdk(context)) ?? { tsdk: '' }; + const { tsdk } = (await getTsdk(context)) ?? { tsdk: "" }; // Create language client client = new LanguageClient( - 'Tempblot', + "Tempblot", { - module: context.asAbsolutePath('./out/language-server.js'), - transport: TransportKind.ipc + module: context.asAbsolutePath("./out/language-server.js"), + transport: TransportKind.ipc, }, { - documentSelector: [{ language: 'tempblot' }], + documentSelector: [{ language: "tempblot" }], initializationOptions: { - typescript: { tsdk } + typescript: { tsdk }, }, - middleware: {} - } + middleware: {}, + }, ); // Start server initially if enabled @@ -47,10 +47,10 @@ export async function activate(context: ExtensionContext) { // Watch for configuration changes context.subscriptions.push( workspace.onDidChangeConfiguration((event) => { - if (event.affectsConfiguration('tempblot.server.enable')) { + if (event.affectsConfiguration("tempblot.server.enable")) { void tryRestartServer(); } - }) + }), ); // Create Volar Labs integration @@ -61,7 +61,7 @@ export async function activate(context: ExtensionContext) { async function tryRestartServer() { await stopServer(); - if (workspace.getConfiguration('tempblot').get('server.enable')) { + if (workspace.getConfiguration("tempblot").get("server.enable")) { await startServer(); } } @@ -83,16 +83,16 @@ async function startServer() { await window.withProgress( { location: ProgressLocation.Window, - title: 'Starting Tempblot Language Server...' + title: "Starting Tempblot Language Server...", }, async () => { await client!.start(); disposable = Disposable.from( - activateAutoInsertion('tempblot', client!), - activateDocumentDropEdit('tempblot', client!) + activateAutoInsertion("tempblot", client!), + activateDocumentDropEdit("tempblot", client!), ); - } + }, ); } } diff --git a/packages/vscode/tsconfig.json b/packages/vscode/tsconfig.json index b04b1e2..2caac1f 100644 --- a/packages/vscode/tsconfig.json +++ b/packages/vscode/tsconfig.json @@ -1,10 +1,8 @@ { - "extends": "@tempblot/config/tsconfig.json", - "compilerOptions": { - "outDir": "out", - "rootDir": "src", - }, - "include": [ - "src", - ] + "extends": "@tempblot/config/tsconfig.json", + "compilerOptions": { + "outDir": "out", + "rootDir": "src" + }, + "include": ["src"] } diff --git a/sample/.vscode/extensions.json b/sample/.vscode/extensions.json index 9eca981..a95c0b4 100644 --- a/sample/.vscode/extensions.json +++ b/sample/.vscode/extensions.json @@ -1,5 +1,3 @@ { - "recommendations": [ - "johnsoncodehk.volarjs-labs" - ] -} \ No newline at end of file + "recommendations": ["johnsoncodehk.volarjs-labs"] +} diff --git a/sample/index.ts b/sample/index.ts index f6069b9..f087b22 100644 --- a/sample/index.ts +++ b/sample/index.ts @@ -1,2 +1,2 @@ // Load-bearing `.ts` file to ensure that tsconfig.json doesn't throw an error about no input files. -export {} \ No newline at end of file +export {}; diff --git a/sample/tsconfig.json b/sample/tsconfig.json index f47289f..fd1df34 100644 --- a/sample/tsconfig.json +++ b/sample/tsconfig.json @@ -4,9 +4,7 @@ "module": "nodenext", "moduleResolution": "nodenext", "strict": true, - "plugins": [ - { "name": "tempblot-typescript-plugin" } - ] + "plugins": [{ "name": "tempblot-typescript-plugin" }] }, "include": ["*.blot", "*.ts"] } diff --git a/tsconfig.base.json b/tsconfig.base.json index 180695a..00bcdaf 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1,4 +1,4 @@ { - "extends": "./packages/config/tsconfig.json", - "files": [] + "extends": "./packages/config/tsconfig.json", + "files": [] }