Skip to main content

DOM Notation JS

Project description

dnjs

dnjs is a pure subset of JavaScript that wants to replace (across many host languages):

  • overly limiting/overly baroque configuration languages
  • mucky string based html/xml templating

It is powerful yet familiar, and the reduced syntax makes it easy to implement (the reference implementation in Python took a couple of days to write) and easy to reason about. Currently the state is very alpha - see the TODO at the end.

╔══════════════════════════════╗
║ ╔═══════════════╗            ║
║ ║ ╔══════╗      ║            ║
║ ║ ║ JSON ║ dnjs ║ JavaScript ║
║ ║ ╚══════╝      ║            ║
║ ╚═══════════════╝            ║
╚══════════════════════════════╝

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)
)

Let's use the reference implementation written in Python to run these (this also has a Python API documented below):

pip install dnjs
dnjs examples/configuration.dn.js examples/environment.json | jq

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 { page } from "./basePage.dn.js"

const commentList = (comments) => m("ul",
    comments.map((v, i) => m("li", `Says: ${v.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"):
    ...
    return render("pageComponents.dn.js", comments)

And the endpoint will return:

<html>
    <head>
        <script src="someScript.js">
        </script>
    </head>
    <body>
        <ul>
            <li>
                Says: hiya!
            </li>
            <li>
                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 examples/commentsPage.dn.js examples/comments.json --html

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": {
...

How does dnjs extend JSON?

Remember dnjs is a restriction of JavaScript, the aim is not to implement all of it, any more than JSON is.

Here are all the extensions to JSON, the grammar can be found here.

  • dicts and lists can be splatted with rest syntax: {...a}/[...a].
  • import { c } from "./b.dn.js", import b from "./b.dn.js". Non-local imports are simply ignored (so as to allow importing m as anything).
  • Functions can be defined only at the top level of files with const f = (a, b) => c syntax. When calling other custom defined dnjs functions, it is necessary to call them like (f)(1, 2) (as opposed to f(1, 2) - this is to help keep the grammar simple.
  • Ternary expressions, only in the form a === b ? c : d. Equality should be implemented however JavaScript does.
  • Map, filter, map over dict, dict from entries, always in the form a.map((v, i) => b), a.filter((v, i) => b), Object.entries(a).map(([k, v], i) => b), Object.fromEntries(a). Note k, v, i must be named as above.
  • Hyperscript, somewhat compatible with mithril - m("sometag#some-id.some-class.other-class", {"href": "foo.js", "class": ["another-class"]}, children), this evaluates to dicts like {"tag": "sometag", "attrs": {"id": "some-id", className: "some-class other-class another-class", "href": "foo.js", "children": children}.
  • Templates in the form `foo ${a}`, dedent`foo ${a}`. dedent should work the same as this npm package.
  • Arrays have a .length attribute.

Name

Originally the name stood for DOM Notation JavaScript.

Python

API

These functions return JSON-able data:

from djns import get_default_export, get_named_export

get_default_export(path)
get_named_export(path, name)

This function returns html as a str:

from djns 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 -

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:
    • Allow skipping escaping with m.trust()?
    • Common string functions like upper case, replace etc?
    • parseInt etc..
  • Standalone (in c/rust/go? with Python bindings) to JSON program.
  • Name things in the grammar, catch lark exceptions and make custom user ones.
  • 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.
  • Allow importing JSON using Experimental JSON modules](https://nodejs.org/api/esm.html#esm_experimental_json_modules).
  • Remove accidental non-js compatability - eg. functions can look like ()=>{"a": 2}, template grammar is a bit wacky.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

dnjs-0.0.1.tar.gz (13.8 kB view hashes)

Uploaded Source

Built Distribution

dnjs-0.0.1-py3-none-any.whl (15.3 kB view hashes)

Uploaded Python 3

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page