Skip to content

leontrolski/dnjs

master
Switch branches/tags
Code

Latest commit

 

Git stats

Files

Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

dnjs

Install For Configuration For HTML templating Instead of jq
╔══════════════════════════════╗
║ ╔═══════════════╗            ║
║ ║ ╔══════╗      ║            ║
║ ║ ║ JSON ║ dnjs ║ JavaScript ║
║ ║ ╚══════╝      ║            ║
║ ╚═══════════════╝            ║
╚══════════════════════════════╝

dnjs is a pure subset of JavaScript that wants to replace (across many host languages - currently go and Python):

  • Overly limiting/baroque configuration languages, eg: yaml
  • Mucky string based HTML/XML templating, eg: handlebars - see blog post
  • Unfamiliar JSON processing languages, eg: jq

Extensions to JSON:

Feature Syntax
Comments //
Unquoted Object keys {a: 42}
Trailing commas {a: 42, }
Imports (Non-local imports are simply ignored) import { c } from "./b.dn.js"
... import b from "./b.dn.js"
Exports export default a
... export const b = c
Rest syntax {...a}, [...a]
Arrow Functions const f = (a, b) => c
Ternary expressions a === b ? c : d
Map a.map((v, i) => b)
Filter a.filter((v, i) => b)
Reduce a.reduce((x, y) => [...x, ...y], [])
Entries Object.entries(a).map(([k, v], i) => b)
From entries Object.fromEntries(a)
Hyperscript, somewhat compatible with mithril m("sometag#some-id.some-class.other-class", {"href": "foo.js", "class": ["another-class"]}, children)
Evaluates to {"tag": "sometag", "attrs": {"id": "some-id", className: "some-class other-class another-class", "href": "foo.js", "children": children}
For trusted html m.trust(a)
Templates `foo ${a}`
Dedent dedent(`foo ${a}`)
List functions .length, .includes(a)

It is powerful yet familiar, and the reduced syntax makes it easy to implement. Currently the state is very alpha - see the TODO at the end.

Installing the standalone binary

Downloads

Installing the Python interpreter/API

pip install dnjs
dnjs --help

Examples

Some of these examples reference other files in the examples folder.

For configuration:

import { environments } from "./global.dn.js"

// names of the services to deploy
const serviceNames = ["signup", "account"]

const makeService = (environment, serviceName) => ({
    name: serviceName,
    ip: environment === environments.PROD ? "189.34.0.4" : "127.0.0.1"
})

export default (environment) => serviceNames.map(
    (v, i) => makeService(environment, v)
)

Running:

dnjs --pretty examples/configuration.dn.js examples/environment.json

Gives us:

[
  {
    "name": "signup",
    "ip": "127.0.0.1"
  },
  {
    "name": "account",
    "ip": "127.0.0.1"
  }
]

For HTML templating

dnjs prescribes functions for making HTML, that handily are a subset of mithril (this makes it possible to write powerful, reusable cross-language HTML components).

Given the file commentsPage.dn.js:

import m from "mithril"

import { page } from "./basePage.dn.js"

const commentList = (comments) => m("ul",
    comments.map((comment, i) => m("li", `Comment ${i} says: ${comment.text}`))
)

export default (comments) => page(commentList(comments))

Then in a python webserver we can render the file as HTML:

from dnjs import render

@app.route("/some-route"):
def some_route():
    ...
    return render("commentsPage.dn.js", comments)

And the endpoint will return:

<html>
    <head>
        <script src="someScript.js">
        </script>
    </head>
    <body>
        <ul>
            <li>
                Comment 0 says: hiya!
            </li>
            <li>
                Comment 1 says: oioi
            </li>
        </ul>
    </body>
</html>

Or we can use the same components on the frontend with mithril:

import page from "../commentsPage.dn.js"
...
m.mount(document.body, page)

Or we can render the HTML on the command line similar to before:

dnjs --html examples/commentsPage.dn.js examples/comments.json

Note, that without the --html flag, we still make the following JSON, the conversion to HTML is a post-processing stage:

{
  "tag": "html",
  "attrs": {
    "className": ""
  },
  "children": [
    {
      "tag": "head",
      "attrs": {
...

For css templating

Using --css will post-process eg:

export default {
  ".bold": {"font-weight": "bold"},
  ".red": {"color": "red"},
}

to:

.bold {
    font-weight: bold;
}
.red {
    color: red;
}

As a jq replacement

JSON='[{foo: 1, bar: "one"}, {foo: 2, bar: "two"}]'
echo $JSON | dnjs -p 'a=>a.map(b=>[b.bar, b.foo])' -
[["one", 1], ["two", 2]]

csv

echo $JSON | dnjs -p 'a=>a.map(b=>[b.bar, b.foo])' --csv -
"one",1
"two",2

csv, raw

echo $JSON | dnjs -p 'a=>a.map(b=>[b.bar, b.foo])' --csv --raw -
one,1
two,2

jsonl

JSON='{foo: 1, bar: "one"}\n{foo: 2, bar: "two"}'
echo $JSON | while read l; do echo $l | dnjs -p 'a=>a.bar' --raw -; done
one
two

Flattening

Remember, you can flatten arrays with:

.reduce((a, b)=>[...a, ...b], [])

Name

Originally the name stood for DOM Notation JavaScript.

Python

API

These functions return JSON-able data:

from dnjs import get_default_export, get_named_export

get_default_export(path)
get_named_export(path, name)

This function returns HTML as a str:

from dnjs import render

render(path, *values)

The types used throughout dnjs are fairly simple dataclasss , there's not much funny stuff going on in the code - check it out!

Development

Install dev requirements with:

pip install -r requirements-dev.txt

Run tests with:

pytest

Pin requirements with:

pip-compile -q; cat requirements.in requirements-dev.in | pip-compile -q --output-file=requirements-dev.txt -

Rebuild and publish (after upversioning) with:

# up version setup.py
rm dist/*; python setup.py sdist bdist_wheel; twine upload dist/*

JS

Javascript validation library to follow - see TODO section below.

Run tests with:

npm install
npm test

TODO

  • Use on something real to iron out bugs.
  • Spec out weird behaviour + make the same as js:
    • numbers
    • ===
  • Nicer docs:
    • Write up why we don't need filters like | to_human.
  • Consider onclick, onkeydown, on... functions... and how we want to handle them / attach them on reaching the browser in a isomophic setup.
  • Decide what else should be added:
    • Common string functions like upper case, replace etc?
    • parseInt etc..
  • Write JS library that simply wraps mithril render and has a dnjs.isValid(path) function that uses the grammar (doing this may involve removing some lark-specific bits in the grammar.
  • Typescript support?
  • Consider what prevents dnjs from becoming a data interchange format - eg. infinite recursion. --safe mode? Specify PATHs that it's permitted to import from.
  • Allow importing JSON using Experimental JSON modules](https://nodejs.org/api/esm.html#esm_experimental_json_modules).
  • Remove accidental non-js compatability - eg. template grammar is a bit wacky.

About

DOM Notation JS

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published