Starter Website

I often find myself setting up a website project, and all the configuration that goes with it. This set up task can be fairly repetitive, so I took some of the basic features I almost always start with, and made this project.

The project does have a couple “extra” features beyond an absolute basic website. Sometimes I want to just build a set of files I can host somewhere, other times I want to just deploy a Docker image to a cloud service.

For a build system, I’m using npm from NodeJS. For site packaging, I’m using webpack, which also provides a development server with live reload.

The repository is here: https://github.com/d4lton/jools. The name is a play on Jewels, which is a simple game I’ve been working on.

It doesn’t include a UI framework or CSS preprocessor, because those tend to go hand-in-hand, and I didn’t want to predetermine which framework I might want to use (if any).

It also isn’t set up for Typescript, even though I tend to use Typescript these days. I may create a Typescript version of this project, but converting to Typescript is fairly simple. I chose to keep this starter project as basic as I could.

The README.md should have enough instruction to get going. Follow the instructions under INSTALLING and RUNNING.

The structure of the project is hopefully easy to understand:

  • src: This directory (and any sub-directories you create) are where your JavaScript code should be. The src/index.js file is the “main” JavaScript file and will automatically be included in static/index.html.
  • static: This directory (and any sub-directories) are where your HTML and CSS files should be. The static/index.html is the “main” HTML file and will automatically include your JavaScript code.
  • config: This directory contains the configuration JSON file(s). Right now there is just the config.development.json file for local development. Also, it is basically empty. However, any properties you set in here will be “injected” into the static website and be available in the process.env object. For the curious, this is intended to mirror how environment variables are made available in a server-side NodeJS project.

The project doesn’t really do anything, as it is intended to be a starting place for a website. The included HTML, CSS, and JavaScript are all basically placeholders.

Hopefully this project helps you in some way! Good luck!

React in a Legacy App

I am working on a relatively old legacy web app. It’s a very complex system, and was built upon jQuery and uses requirejs for module loading. I have been slowly updating it to use ES5+ features and React. In particular, working with React in this app has been a bit painful. This app, you see, doesn’t have a build environment, so using webpack and babel aren’t available, at least not in the standard way. With a great deal of effort, it certainly can be made to work with a build environment, but my intention is to spend my energy on cleaning up the code and, when that task is complete, bringing in a standard build system. Ah, that will be great.

But, at least for today, I want to add some React code. I’ve done it before in this app, but since I have no build system, I’ve used React.createElement(). If you’ve tried to build anything more than a trivial Component using React.createElement(), you know that it gets old, fast.

So I want to just JSX, naturally. But with no build system, I wasn’t sure how to proceed. There are plenty of examples on how to use babel standalone and mark your script tags as "text/babel" to have your JSX code converted to JS on the fly. But, a couple problems. First, I’m using requirejs, and it doesn’t want to mark only some script tags as "text/babel" (it can mark all or none). I’ve even gone so far as modifying requirejs to only mark certain script tags as "text/babel" but that leads to the next problem: babel standalone doesn’t seem to compile the scripts imported by requirejs. I’m sure there’s a way, but I gave up on that approach.

What I really wanted was something that would just compile JSX files into JS, so I could write complex React Components and not worry about it.

I found some examples for using browserify and babel to basically do what a build system would do, but it ends up creating massive JS files with all kinds of transpiled code. Clearly I was doing this wrong.

I finally found that I can use a basic babel transformFile() call with some relatively simple configuration that mainly just compiles the React JSX into React JS with React.createElement() calls, and really no other change to my code.

I wrote a “watcher” script for this purpose, and now just keep it running in my IDE while I work:

 const args = require('minimist')(process.argv.slice(2));
 const fs = require("fs/promises");
 const path = require("path");
 const chokidar = require("chokidar");
 const babel = require("@babel/core");
 const options = {
   ignored: /(^|[\/\\])\../
 };
 async function compile(src) {
   return new Promise((resolve, reject) => {
     const info = path.parse(src);
     const dest = path.join(info.dir, `${info.name}.js`);
     babel.transformFile(src, {}, async (error, result) => {
       if (error) { return reject(error) }
       await fs.writeFile(dest, result.code);
       console.log(`compiled ${src} -> ${dest}`);
       resolve();
     });
   });
 }
 async function remove(src) {
   const info = path.parse(src);
   const dest = path.join(info.dir, `${info.name}.js`);
   fs.unlink(dest)
     .then(() => {
       console.log(`removed ${dest}`);
     })
     .catch(error => {
       // ignore
     });
 }
 (async () => {
   chokidar.watch('**/*.jsx', options).on('all', async (event, path) => {
     if (event === "add" || event === "change") {
       await compile(path);
     }
     if (event === "unlink" && args.delete) {
       await remove(path);
     }
   });
 })(); 

The babel configuration file (babel.config.json) looks like this:

{
   "presets": [
     [
       "@babel/env",
       {
         "targets": {
           "edge": "17",
           "firefox": "60",
           "chrome": "67",
           "safari": "11.1"
         },
         "useBuiltIns": "usage",
         "corejs": "3.6.5"
       }
     ],
     "@babel/preset-react"
   ],
   "plugins": ["@babel/plugin-syntax-class-properties"]
 }

For packages, I’ve installed these in my package.json file:

@babel/cli
@babel/core
@babel/preset-env
@babel/preset-react
chokidar minimist 

Jupyter Widgets: Sending Custom Event To Frontend From Backend

Jupyter Widgets can have a separate frontend and backend component. Sometimes, you need to send a message from one to the other. This example shows the basics on sending a message from the backend to the frontend.

In your Widget’s frontend (JavaScript) code, listen for the custom event from the backend:

    render: function() {
        this.model.on('msg:custom', this.customEvent.bind(this));
    },
    customEvent: function(event) {
        switch (event.type) {
            case 'my_important_event':
                console.log(event.value);
                break;
        }
    }

Now, in the Widget’s backend (Python) code, send the message as needed:

    def send_message_to_frontend(self):
        self.send({"method": "custom", "type": "my_important_event", "value": "blue"})

Jupyter Widgets: Sending Custom Event To Backend From Frontend

Jupyter Widgets can have a separate frontend and backend component. Sometimes, you need to send a message from one to the other. This example shows the basics on sending a message from the frontend to the backend.

In your Widget’s backend (Python) code, listen for the custom event from the frontend:

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.on_msg(self._handle_custom_msg)
    def _handle_custom_msg(self, content, buffers):
        if content['event'] == 'another_important_event':
            print(content['value'])

Now, in the Widget’s frontend (Javascript) code, send the message as needed:

    sendMessageToBackend: function() {
        this.model.send({event: 'another_important_event', value: 'green'});
    },

Jupyter Widgets: Finding My Cell Object

When writing a custom Jupyter Widget, sometimes you need to know the Cell (CodeCell, usually) that your Widget is running in. You can get a list of all Cells from the Notebook object in Javascript, but finding your Cell isn’t exactly that straight-forward.

Fortunately, it isn’t that hard to determine your Cell, since you can find the container element for your Widget, and then loop through all Cells in the Notebook to see which one you are in:

const container = this.$el.parents('.cell');
const cell = this.notebook.get_cells().find(it => it.element.is(container));

If all goes well, cell will contain your Cell object.