WPsuck
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!
Usecase
- 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…
WP REST API
- Fantastic! So I can easily script syncing. CLI (Bash?) should be convenient enough. Possibilities are endless.
- API's JSON schema is pretty hairy (HAL, mostly), but hopefully consistent enough that I can ignore the noise.
- Uploads don't seem to be tracked in WP's revisions? Bug?
R/W
- URLs ("endpoints") nice enough that can use curl (and jq?) in a Bash script, but…
- 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.
Authentication
- Published posts are, obviously, readable anonymously, but reading drafts and uploading require authentication (and authorization — but, my blog, I'm admin).
- 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.
- Too many reported problems (.htaccess related) with Basic-Auth plugin?
Cookie?
- 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.
CLI
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://' {website,post_id,user,password}=argv
et cetera. Neat!- 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.
request
- https://www.reddit.com/r/node/comments/b9eaxo/npm_request_package_goes_into_maintenance_mode/ ?
CoffeeScript
- (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
- "?=" 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!
wpsuck_api
- 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…
- Promise chaining sucks! Sure, promises solve some critical async (JS) pains, but, nu, this incorrect usage:
fse.readFile .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). - Apparently, cannot use chaining to handle the resolved (fulfilled? settled?) result and separatley catch both a rejection and errors in that resolver function.
- 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. - 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
- 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.
…
GitOps?
- 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