Skip to content

Instantly share code, notes, and snippets.

@zerobias
Last active December 25, 2018 19:10
Show Gist options
  • Save zerobias/cacc0359502f1a4624b88ffffd895c2e to your computer and use it in GitHub Desktop.
Save zerobias/cacc0359502f1a4624b88ffffd895c2e to your computer and use it in GitHub Desktop.
//puredom.js
//@flow
//@jsx domsx
//@jsxFrag 'fragment'

const handlers = (global.handlers = [])
initDOM()

const ClickMe = (props, childs) => (
  <button onClick={e => console.log(e)}>click me</button>
)

render(
  <div id="app">
    {callsites().map(line => (
      <p>{line.toString()}</p>
    ))}
    <ClickMe />
  </div>,
)

function render(app) {
  appendChild(document.body, app)
}

//eslint-disable-next-line no-unused-vars
function domsx(tag, props, ...childrens) {
  if (tag === 'fragment') return childrens
  if (typeof tag === 'function') return tag(props, childrens)
  const item: HTMLElement = document.createElement(tag)
  if (props !== null) {
    for (let key in props) {
      let value = props[key]
      if (key === 'key') continue
      if (key === 'className') key = 'class'
      if (key.startsWith('on') && typeof value === 'function') {
        const index = handlers.push(value) - 1
        value = `(0, handlers[${index}])(this)`
      }
      item.setAttribute(key, value)
    }
  }

  for (const child of childrens) {
    appendChild(item, child)
  }
  return item
}
function appendChild(item, child) {
  invariant(item, 'no item')
  if (Array.isArray(child)) {
    for (const ch of child) {
      appendChild(item, ch)
    }
  } else if (typeof child === 'string' || typeof child === 'number') {
    const text = document.createTextNode(String(child))
    item.appendChild(text)
  } else {
    item.appendChild(child)
  }
}
function initDOM(id = 'app') {
  const body = document.body
  invariant(body, 'nobody')
  const app = document.querySelector('#' + id)
  if (app !== null) {
    body.removeChild(app)
  }
}
function callsites() {
  let stack: Array<CallSite> = []
  const _prepareStackTrace = Error.prepareStackTrace
  Error.prepareStackTrace = (_, stackOrig) => {
    stack = stackOrig.slice(4, -1)
    return stackOrig
  }
  new Error().stack
  Error.prepareStackTrace = _prepareStackTrace
  return stack
}
function invariant(cond, message, ...args) {
  if (cond) return
  let argIndex = 0
  const error = new Error(message.replace(/%s/g, () => args[argIndex++]))
  error.name = 'Invariant Violation'

  throw error
}

index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>Pure DOM example</title>
  </head>

  <body>
    <script src="./puredom.js"></script>
  </body>
</html>

start.sh

yarn init --yes
yarn add -D parcel-bundler
npx parcel index.html
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment