Script that accesses '$tw' in node.js REPL

Thought JS developers that are using the ‘tiddlywiki’ module in node.js apps would find this script useful. I use TiddlyWiki as a database quite a bit - using filters as a query language is awesome. But miss access to $tw like when in browser dev tools.

This script brings up node.js REPL with access to $tw. At the least is handy to list all the functions for any $tw object - type $tw.utils. and press tab twice. (note the period at the end), lists all $tw.utils. functions and exported variables.

/*
 * Author: @poc2go at talk.tiddlywiki.org
 * License: MIT
 * Description: Runs a node.js REPL with '$tw' installed.
 *
 * Usage:

mkdir twrepl && cd twrepl
npm install tiddlywiki

 * In an editor, Copy/Paste this script, save as 'twrepl.js', then

node ./twrepl

 *
 * Enjoy ;)
*/

// Terminal colours
const colour = {
	log: (txt='', fg=255, bg=0, efg=255, ebg=0) => process.stdout.write(
		`\x1b[38;5;${fg};48;5;${bg}m${txt}\x1b[38;5;${efg};48;5;${ebg}m`),

	txt: (txt='', fg=255, bg=0, efg=255, ebg=0) =>
		`\x1b[38;5;${fg};48;5;${bg}m${txt}\x1b[38;5;${efg};48;5;${ebg}m`,
}

// -------------------
// Initialize

// Boot tiddlywiki module
const $tw = require('tiddlywiki').TiddlyWiki();
$tw.preloadTiddlers = [ { title: 'preloaded', text: 'This tiddler is loaded on TW bootup'} ];
$tw.boot.argv = ['output']; // TW output path
$tw.boot.boot(() => {});

// Who are we?
function intro() {
  colour.log( '$tw-repl: ',75); colour.log('v1.0.0\n',130,0,110);
}

// -------------------
// Node'js REPL
// Place $tw in REPL context so can be referenced
function resetContext() {
  runtime.context.$tw = $tw;
}

// REPL runtime
var runtime;
function startRepl() {
  runtime = require('node:repl').start({
      prompt: colour.txt('$tw-repl> ',33,0,7,0), useColors: true,
      ignoreUndefined: true, /*completer: completer*/
  });

  // If REPL is reset (.clear) - context needs resetting
  runtime.on('reset', () => resetContext());

  // Initial context settings
  resetContext();
}

// -------------------
// -------------------
// Startup

// Show our name and version
intro();

startRepl();
runtime._ttyWrite('$tw.version\n');
runtime._ttyWrite(`$tw.wiki.getTiddler('preloaded').fields\n`);

Displays this:

$tw-repl: v1.0.0
$tw-repl> $tw.version
'5.3.1'
$tw-repl> $tw.wiki.getTiddler('preloaded').fields
[Object: null prototype] {
  title: 'preloaded',
  text: 'This tiddler is loaded on TW bootup'
}
$tw-repl> 

Hope find useful or at least parts - colours, tiddlywiki boot, or REPL for your own explorations!

1 Like

Perhaps we should consider adding a tiddlywiki --repl command to make things easier? There have been lots of times where I would have found that quite useful.

2 Likes

I got one:

/*\
title: $:/poc2go/modules/commands/repl.js
type: application/javascript
module-type: command

node.js REPL with access to $tw

Optional params = REPL prompt

\*/
(function(){

/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";

exports.info = {
	name: "repl",
	synchronous: true
};

var Command = function(params,commander,callback) {
	var self = this;
	this.params = params;
	this.commander = commander;
	this.callback = callback;
};

Command.prototype.execute = function() {
	var self = this;

	var repl = require('repl');

	this.runtime = repl.start({
		prompt: this.params.length ? this.params[0] : '$tw-repl> ',
		useColors: true,
		ignoreUndefined: true,
		/*completer: completer*/
	});

	// If REPL is reset (.clear) - context needs resetting
	this.runtime.on('reset', function() {
		self.runtime.context.$tw = $tw;
	});

	// Initial context settings
	this.runtime.context.$tw = $tw;

	return null;
};

exports.Command = Command;

})();

Works fine so feel free to add to the core. What it is missing is a way of adding other objects to the REPL context - only does $tw. So the REPL can only see $tw.

For now I cheat and add a ‘poc2go’ namespace to $tw and put all my other module references in that - not ideal.

But, works fine for $tw !! First params allows changing the prompt.

I have written both a command (–repl) which uses a global ($tw.Repl) macro to implement REPL into TiddlyWiki, the right way - see gist TW5 modules to implement REPL. May not recommend putting in the core as is only useful for building back-end systems. So setting it up as a plugin. I use REPL and the ‘server’ edition together as the core for my server based applications.

The plugin does require reserving the ‘$tw.Repl’ and ‘$tw.repl’ global namespaces - hoping that is OK. $tw.Repl being the constructor and $tw.repl being the instance of the REPL runtime. Is a singleton - to run multiple REPLs need to reassign stdin, out, err to some other pipe, like ws-sockets - not going down that rabbit hole so constructor will just return the existing instance if already created.

$tw is automatically placed into the REPL ‘context’ so it can see it. Calling $tw.repl.context.modName = modName command adds other modules you wish the REPL to access. $tw.repl.on('event', fnc) is called to handle any REPL event you want to process. (sadly, the node.js documentation for event handling is 404).

The next post will discuss changing the user interface of tiddlywiki output -init server and then tiddlywiki --listen - which leaves users hanging… can’t talk to it - what is the performance? How many connected? - it just hangs. ctrl-c to end - pretty much it. REPL will change all of that.