js-qualified-keywords
v0.1.1
Published
Clojure-style qualified keywords (:ns/name) as a first-class data type in JavaScript, with Babel plugin and LSP support.
Maintainers
Readme
js-qualified-keywords
Clojure-style qualified keywords (:ns/name) as a first-class data type in JavaScript.
const status = :active;
const schema = {
[:user/name]: { type: 'string' },
[:user/email]: { type: 'string' },
};Install
npm install js-qualified-keywordsRuntime
const { kw, Keyword, KeywordMap } = require('js-qualified-keywords');
const k = kw('user', 'name');
k.ns // "user"
k.name // "name"
k.fqn // "user/name"
k.toString() // ":user/name"
// Interned — same args, same instance
kw('user', 'name') === kw('user', 'name') // true
// Parse from string
Keyword.of(':user/name') // same as kw('user', 'name')
// KeywordMap: like Map but keyed by keyword identity
const m = new KeywordMap();
m.set(kw('user', 'name'), 'Alice');
m.get(kw('user', 'name')) // "Alice"
m.get(':user/name') // "Alice" — string keys work too
KeywordMap.fromObject({ ':user/name': 'Alice' }) // build from plain object
m.toObject() // back to plain objectBabel Plugin
Transforms :ns/name syntax into runtime calls so you can write keywords directly in .js files.
.babelrc
{ "plugins": ["js-qualified-keywords/babel-plugin"] }Input:
const x = :user/name;
const o = { [:db/id]: 1 };Output:
const { kw: _kw } = require('js-qualified-keywords/runtime');
const x = _kw('user', 'name');
const o = { [_kw('db', 'id')]: 1 };Keywords inside strings and comments are left untouched. The ternary : is not mistaken for a keyword.
LSP Server
Provides completion, hover, go-to-definition, find-references, rename, and diagnostics for keyword literals in JS/TS files.
cd lsp-server && npm install
node lsp-server/server.js --stdioPoint your editor's LSP client at that command, scoped to javascript, javascriptreact, typescript, typescriptreact.
Emacs (lsp-mode)
1. Install server dependencies (once):
cd lsp-server && npm install2. Add to your init.el / config:
(setq js-qualified-keywords-lsp-server-dir "/path/to/js-qualified-keywords/lsp-server")
(load "/path/to/js-qualified-keywords/editors/emacs/js-qualified-keywords-lsp.el")
(add-hook 'js-mode-hook #'lsp) ; skip if lsp already starts automatically3. Open any .js file — lsp-mode connects automatically.
| Feature | Key |
|---|---|
| Hover | K |
| Completion | type : or :ns/ |
| Go to definition | M-. |
| Find references | M-? |
| Rename | C-c l r r |
| Server status | M-x lsp-describe-session |
If the server doesn't connect, check the *lsp-log* buffer. Most common cause: node not on Emacs's exec-path — fix with (add-to-list 'exec-path "/usr/local/bin").
If you run multiple LSP servers on JS files (e.g. ts-ls), both activate independently. To make js-qualified-keywords take priority, raise :priority above -1 in the .el file.
VS Code Extension
Bundles the LSP server as a VS Code extension.
cd vscode-extension && npm install
# Press F5 in VS Code to launch the Extension Development HostTo package for distribution:
npx vsce packageConventions
| Style | Example | Use for |
|---|---|---|
| Qualified | :user/name | domain attributes, schema keys |
| Simple | :active | enums, flags, modes |
Use .kw.js as the file extension to signal that a file contains keyword syntax (optional, helps tooling).
Publishing to npm
# 1. Check the name is free
npm info js-qualified-keywords
# If taken, use a scoped name: change "name" in package.json to "@yourname/js-qualified-keywords"
# 2. Log in (create an account at npmjs.com first if needed)
npm login
# 3. Publish
npm publish # unscoped package
npm publish --access public # scoped package (@yourname/...)Future releases:
npm version patch # 0.1.0 → 0.1.1
npm version minor # 0.1.0 → 0.2.0
npm publishBefore publishing, update the homepage, bugs, and repository URLs in package.json to point to your actual repo.
Limitations
- Source maps: The Babel plugin pre-processes source text before parsing, which changes string lengths (
:user/namebecomes_kw("user", "name")). Babel's source maps are generated from the transformed source, so column offsets may be slightly off. Line numbers remain correct. - No TypeScript type-level support yet.
