Skip to content

Commit 59784a2

Browse files
authored
Herb Language Server and Visual Studio Code Extension (#105)
This pull request adds a Herb Language Server Protocol implementation and a Visual Studio Code Extension for it. To be able to make the bundling work in CommonJS in VS Code I also implemented a new `@herb-tools/node-wasm` package that uses the WebAssembly build in `wasm/` to build a version of the libherb library for the Node.js environment. Ideally we can use the Node.js implementation in the future, but the pre-compilation of the native Node.js Addon is trickier than initially expected with Electron and the system architecture.
1 parent ff959ce commit 59784a2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1672
-5
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
dist/
2+
build/
3+
node_modules/
4+
5+
*~
6+
7+
.DS_Store
8+
tsconfig.tsbuildinfo
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
.babelrc
2+
.babelrc.js
3+
.DS_Store
4+
.gitignore
5+
.yarn.lock
6+
7+
*.log
8+
*.tsbuildinfo
9+
*.tgz
10+
11+
README.md
12+
rollup.config.js
13+
tsconfig.json
14+
tsconfig.tsbuildinfo
15+
yarn-error.log
16+
*~
17+
18+
/.git
19+
/.github
20+
/.gitattributes
21+
22+
/node_modules
23+
/src
24+
/test
25+
/coverage
26+
/assets
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.gitignore
2+
.node-version
3+
4+
**/*.ts
5+
**/*.map
6+
**/tsconfig.json
7+
**/tsconfig.base.json
8+
9+
scripts/**
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Marco Roth
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Herb Language Server (`@herb-tools/language-server`)
2+
3+
[Language Server Protocol](https://github.com/Microsoft/language-server-protocol) integration for HTML-aware ERB using the [Herb Parser](https://herb-tools.dev).
4+
5+
![](./assets/herb-lsp.png)
6+
7+
Used by the [Herb LSP](https://marketplace.visualstudio.com/items?itemName=marcoroth.herb-lsp) Visual Studio Code extension.
8+
9+
## Install
10+
11+
```bash
12+
npm install -g herb-language-server
13+
```
14+
15+
```bash
16+
yarn global add herb-language-server
17+
```
18+
19+
## Run
20+
21+
```bash
22+
herb-language-server --stdio
23+
```
24+
25+
```
26+
Usage: herb-language-server [options]
27+
28+
Options:
29+
30+
--stdio use stdio
31+
--node-ipc use node-ipc
32+
--socket=<port> use socket
33+
```
641 KB
Loading

javascript/packages/language-server/assets/herb.svg

Lines changed: 149 additions & 0 deletions
Loading
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
{
2+
"name": "@herb-tools/language-server",
3+
"description": "Powerful and seamless HTML-aware ERB parsing and tooling.",
4+
"version": "0.2.0",
5+
"author": "Marco Roth",
6+
"license": "MIT",
7+
"engines": {
8+
"node": "*"
9+
},
10+
"bugs": "https://github.com/marcoroth/herb/issues/new?title=Package%20%60@herb-tools/language-server%60:%20",
11+
"repository": {
12+
"type": "git",
13+
"url": "https://github.com/marcoroth/herb.git",
14+
"directory": "javascript/packages/language-server"
15+
},
16+
"homepage": "https://herb-tools.dev",
17+
"bin": {
18+
"herb-language-server": "./dist/herb-language-server"
19+
},
20+
"scripts": {
21+
"clean": "rimraf dist",
22+
"prebuild": "yarn run clean",
23+
"build": "tsc -b && rollup -c rollup.config.mjs",
24+
"postbuild": "node scripts/executable.mjs",
25+
"watch": "tsc -b -w"
26+
},
27+
"files": [
28+
"dist"
29+
],
30+
"dependencies": {
31+
"@herb-tools/node-wasm": "0.2.0",
32+
"dedent": "^1.6.0",
33+
"typescript": "^5.8.3",
34+
"vscode-languageserver": "^9.0.1",
35+
"vscode-languageserver-textdocument": "^1.0.12"
36+
},
37+
"devDependencies": {
38+
"@rollup/plugin-commonjs": "^28.0.6",
39+
"@rollup/plugin-typescript": "^12.1.3",
40+
"@rollup/plugin-node-resolve": "^16.0.1",
41+
"@rollup/plugin-json": "^6.1.0",
42+
"rimraf": "^6.0.1"
43+
}
44+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import typescript from "@rollup/plugin-typescript"
2+
import { nodeResolve } from "@rollup/plugin-node-resolve"
3+
import json from "@rollup/plugin-json"
4+
import commonjs from "@rollup/plugin-commonjs"
5+
6+
// Bundle the LSP server entry point into a single CommonJS file.
7+
// Exclude Node built-in so they remain as externals.
8+
const external = [
9+
"path",
10+
"url",
11+
"fs",
12+
"module",
13+
]
14+
15+
function isExternal(id) {
16+
return (
17+
external.includes(id) ||
18+
external.some((pkg) => id === pkg || id.startsWith(pkg + "/"))
19+
)
20+
}
21+
22+
export default {
23+
input: "src/server.ts",
24+
output: {
25+
file: "dist/herb-language-server.js",
26+
format: "cjs",
27+
sourcemap: true,
28+
},
29+
external: isExternal,
30+
plugins: [
31+
nodeResolve(),
32+
commonjs(),
33+
json(),
34+
typescript({
35+
tsconfig: "./tsconfig.json",
36+
rootDir: "src/",
37+
module: "esnext",
38+
}),
39+
],
40+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { readFileSync, writeFileSync } from 'fs'
2+
import { dirname, resolve } from 'path'
3+
import { fileURLToPath } from 'url'
4+
import { exec } from 'child_process'
5+
6+
const __dirname = dirname(fileURLToPath(import.meta.url))
7+
8+
const infile = resolve(__dirname, '../dist/herb-language-server.js')
9+
const outfile = resolve(__dirname, '../dist/herb-language-server')
10+
11+
writeFileSync(
12+
outfile,
13+
'#!/usr/bin/env node\n' + readFileSync(infile, 'utf-8'),
14+
'utf-8'
15+
)
16+
17+
exec('chmod +x dist/herb-language-server', (error, _stdout, _stderr) => {
18+
if (error) {
19+
console.error(`Error setting file permissions: ${error}`);
20+
} else {
21+
console.log('File permissions set successfully');
22+
}
23+
});

0 commit comments

Comments
 (0)