WordPress is dead (and doesn't know it yet). Gutenberg is… wrong on so many levels. But, while I'm developing an alternative, for this devlog… hacking to the rescue!


  1. For a specific usecase (slideshow) where I need(?) to tweak HTML markup, the WP editor's "raw" HTML editing turned out unusable — mutilates the HTML too much. I'm already comfortable editing HTML in Geany, locally, but copy-pasting to keep a local file and WP in sync would be a significant hindrance. Here's how I coded (and open sourced) this hack…


  1. Fantastic! So I can easily script syncing. CLI (Bash?) should be convenient enough. Possibilities are endless.
  2. API's JSON schema is pretty hairy (HAL, mostly), but hopefully consistent enough that I can ignore the noise.
  3. Uploads don't seem to be tracked in WP's revisions? Bug?


  1. URLs ("endpoints") nice enough that can use curl (and jq?) in a Bash script, but…
  2. JSON to/from HTML: need(?) to extract/wrap HTML from this hairy JSON, un/escape the HTML string… doing all this in CoffeeScript ought to be much prettier.


  1. Published posts are, obviously, readable anonymously, but reading drafts and uploading require authentication (and authorization — but, my blog, I'm admin).
  2. Bummer: docs imply that independent apps, not client-side plugins embedded into WP's UI, must install some WP plugin to authenticate requests (on server-side), and passing credentials with every API call, using HTTP basic, or JWT, etc.
  3. Too many reported problems (.htaccess related) with Basic-Auth plugin?


  1. I found code that seemed to, instead of needing a plugin, first make a login request, and use a returned cookie… how? I wasted time trying to figure this out, hoping to avoid having to install a WP plugin… and eventually gave up.


  1. argv=(require 'minimist') process.argv.slice 2
    gives us GNU long options, and stuff like:
    "ERROR: invalid options: #{JSON.stringify argv}."
    unless argv.quiet then console.error '…'
    if argv.website and not argv['force-unsafe'] then assert argv.website.startsWith 'https://'
    et cetera. Neat!
  2. Explicit is safer: decided to require specifying an action (download/upload) and the file to read/write, instead of defaults, to prevent overwriting things by mistake.


  1. https://www.reddit.com/r/node/comments/b9eaxo/npm_request_package_goes_into_maintenance_mode/ ?


  1. (I usually start coding in Literate CS, but here reverted when seemed "illiterate" would be more readable.) NB: hashbang for .litcoffee requires extra parameters:
    #!env -S coffee --literate
  2. "?=" works on (undefined) object properties, but not on "undeclared" variables:
    error: variable can't be assigned with ?= because it has not been declared before
    Yuck. I think this is a stupid language decision/omission. Not having declarations is a great feature!


  1. I initially thought I'd need more features, or want to support other scripts, so modularized dealing with WP's REST API… but, YAGNI, apparently?

Promises, promises…

  1. Promise chaining sucks! Sure, promises solve some critical async (JS) pains, but, nu, this incorrect usage:
    .catch (reason)-># Handle readFile's errors.
    ### Bug! ###
    .then (data)-># Use data… or so I thought!
    .catch (reason)-># Handle exceptions/rejection returned from my data handling — the anonymous function above.
    doesn't work, because catch "restores the chain", ie returns a new promise (of the value returned from my anonymous reject function).
  2. Apparently, cannot use chaining to handle the resolved (fulfilled? settled?) result and separatley catch both a rejection and errors in that resolver function.
  3. The return semantics of .catch and .then are very different — confusing, kindda broken? Or worse: .catch in a chain will catch rejections from any of the preceding chained .then's… and will then "restore" the chain, not break it.
  4. Kindda makes sense that I must pass both handlers (resolve, reject) at once — but this, at least in CoffeeScript, is inconvenient/ugly:
    .down website,auth,post_id
    .then ((content)->
    	# …
    	fse.writeFile argv.down,config+content
    	.then (-># Saved successfully.
    	),(reason)->console.error 'ERROR: saving to file failed!\n',reason
    ),(reason)->console.error 'ERROR: downloading failed!\n',reason
  5. Another workaround I've sometimes used is to reraise/rethrow, ie to return a rejected promise from a catch handler (rejection function) with a value that subsequent chained catchers can distinguish as cascaded, not intended for them, and ignore.


  1. Since I'm tracking changes locally (it's too easy with Geany plugins), why not hook Git to automagically run WPsuck? Hmm, simply on commit, or proper release flow?

The real world is a special case