Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
f1cf561
[cli] Allow one or more positional path args
jameshadfield May 26, 2025
a5d0df0
Refactor fetch-test-data
jameshadfield Jun 3, 2026
b6afa86
Update node to v24 / v26
jameshadfield Jun 7, 2026
fbc6283
[cli] Add server fetch test for getDataset handler
jameshadfield Jun 3, 2026
4afc15d
Migrate server and build tooling from CommonJS to ES Modules
jameshadfield Jun 8, 2026
bf764dc
[cli] Migrate server files from JavaScript to TypeScript
jameshadfield Jun 8, 2026
31a91fc
[cli] update api code/docs
jameshadfield Jun 9, 2026
7b0af96
[cli] Add explicit return types
jameshadfield Jun 8, 2026
7c153ea
[dev] Update eslint
jameshadfield Jun 8, 2026
7229793
eslint fixes
jameshadfield Jun 8, 2026
67343cf
[tests] Remove unused test
jameshadfield Jun 8, 2026
88cc9cb
[cli] remove default data/narrative dirs
jameshadfield Jun 8, 2026
649c636
[dev] remove unused npm script
jameshadfield Jun 8, 2026
d502e30
[tests] update smoke tests
jameshadfield Jun 8, 2026
0fcf8b0
Remove get-data initialisation approach
jameshadfield Jun 9, 2026
33590b0
Remove heroku as an auspice app
jameshadfield Jun 9, 2026
0250dab
[docs] update auspice.us note
jameshadfield Jun 9, 2026
cb5fa3c
Merge pull request #1967 from nextstrain/james/cli-path-improvements
jameshadfield Jun 29, 2026
c563a1f
Merge pull request #2066 from nextstrain/js-updates
jameshadfield Jun 29, 2026
82dfcda
Merge pull request #2067 from nextstrain/default-data-changes
jameshadfield Jun 29, 2026
24cd142
Fix zoom freezing tips whose radius changed mid-transition
trvrb Jul 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ rules:
# Not possible while strictNullChecks is disabled in tsconfig.
"@typescript-eslint/no-non-null-assertion": "error"
no-unused-vars: off
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_" }]
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_", "destructuredArrayIgnorePattern": "^_", "caughtErrorsIgnorePattern": "^_"}]
no-use-before-define: off
"@typescript-eslint/no-use-before-define": ["error", { "functions": false }]
"@typescript-eslint/no-var-requires": off # Remove this override once all files use ES6 style imports.
"@typescript-eslint/no-require-imports": off # Remove this override once all files use ES6 style imports.
prefer-const: ["error", {"destructuring": "all"}]
react/no-array-index-key: error
react/prop-types: off # Remove this override once all props have been typed using PropTypes or TypeScript.
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [20, 22, 24]
node: [24, 26]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
Expand All @@ -30,15 +30,15 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
node: [20, 22, 24]
node: [24, 26]
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: ${{ matrix.node }}
# Build is implicit with the "prepare" life cycle script
- run: npm ci --loglevel verbose
- run: npm run get-data
- run: npm run fetch-test-data
- run: npx playwright install chromium
- run: npm run smoke-test

Expand Down
10 changes: 2 additions & 8 deletions DEV_DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ General advice for writing tests:

> Smoke tests are currently very limited but we may expand these in the future

1. Fetch datasets with `npm run get-data`.
1. Fetch testing datasets with `npm run fetch-test-data`.
2. Install the testing browser with `npx playwright install chromium`.
3. Run `npm run smoke-test`.

Expand All @@ -110,15 +110,9 @@ If embarking on this journey, consider using Playwright since it is already used
Linting via eslint: `npm run lint`, typechecking via `npm type-check`


## Heroku review apps


A Heroku pipeline for this repository is connected to GitHub under the nextstrain-bot user account. The Review Apps feature facilitates manual review of changes by automatically creating a test instance from the PR source branch and adding a link to it on the GitHub PR page. These apps are based on configuration in [app.json](./app.json).


#### Test on downstream repositories

Additionally, a GitHub Actions workflow has been set up to generate PRs in downstream repositories that reflect the new changes in Auspice. To use it, add the label [preview on auspice.us](https://github.com/nextstrain/auspice/labels/preview%20on%20auspice.us) and/or [preview on nextstrain.org](https://github.com/nextstrain/auspice/labels/preview%20on%20nextstrain.org).
A GitHub Actions workflow has been set up to generate PRs in downstream repositories that reflect the new changes in Auspice. To use it, add the label [preview on auspice.us](https://github.com/nextstrain/auspice/labels/preview%20on%20auspice.us) and/or [preview on nextstrain.org](https://github.com/nextstrain/auspice/labels/preview%20on%20nextstrain.org).

## git-lfs

Expand Down
11 changes: 1 addition & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,20 +39,11 @@ See [the relevant page on Auspice docs](https://docs.nextstrain.org/projects/aus
To get up & running, you'll need datasets to visualise.
(Please see the [nextstrain docs](https://nextstrain.org/docs/) for tutorials on how to run your own analyses.)

If you've installed auspice from `npm` you may get datasets to display via:
If you've installed auspice from `npm` you may get an example dataset to display via:

```bash
mkdir data
curl http://data.nextstrain.org/zika.json --compressed -o data/zika.json
curl http://data.nextstrain.org/ncov.json --compressed -o data/ncov.json
...
```

If you've installed auspice from source, we have a helper script to download a number of datasets for testing:

```bash
# from the auspice src directory
npm run get-data
```

### Obtain narratives to view locally
Expand Down
26 changes: 0 additions & 26 deletions app.json

This file was deleted.

14 changes: 6 additions & 8 deletions auspice.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
#!/usr/bin/env node

const argparse = require('argparse');
const version = require('./src/version').version;
const view = require("./cli/view");
const build = require("./cli/build");
const develop = require("./cli/develop");
const convert = require("./cli/convert");
import argparse from 'argparse';
import { version } from './src/version.js';
import * as view from "./cli/view.ts";
import * as build from "./cli/build.ts";
import * as develop from "./cli/develop.ts";
import * as convert from "./cli/convert.js";

const parser = new argparse.ArgumentParser({
version: version,
Expand Down Expand Up @@ -38,5 +38,3 @@ if (args.subcommand === "build") {
} else if (args.subcommand === "convert") {
convert.run(args);
}

// console.dir(args);
5 changes: 4 additions & 1 deletion babel.config.js → babel.config.cjs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const utils = require('./cli/utils');
const utils = require('./cli/utils.ts');

/* What variables does this config depend on?
* process.env.BABEL_EXTENSION_PATH -- a resolved path
Expand Down Expand Up @@ -31,6 +31,9 @@ module.exports = function babelConfig(api) {
if (api.env("development")) {
plugins.push(["react-hot-loader/babel", { safetyNet: false }]);
}
if (api.env('test')) {
plugins.push("babel-plugin-transform-import-meta");
}
if (process.env.BABEL_INCLUDE_TIMING_FUNCTIONS === "false") {
plugins.push(["strip-function-call", {strip: ["timerStart", "timerEnd"]}]);
}
Expand Down
24 changes: 15 additions & 9 deletions cli/build.js → cli/build.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
const webpack = require("webpack");
const path = require("path");
const generateWebpackConfig = require("../webpack.config.js").default;
const utils = require("./utils");
import webpack from "webpack";
import path from "path";
import { fileURLToPath } from 'url';
import { createRequire } from 'module';
import * as utils from "./utils.ts";

const require = createRequire(import.meta.url);
const __dirname = path.dirname(fileURLToPath(import.meta.url));

const generateWebpackConfig = require("../webpack.config.cjs").default;
const SUPPRESS = require('argparse').Const.SUPPRESS;

const addParser = (parser) => {
const addParser = (parser): void => {
const description = `Build the client source code bundle.
For development, you may want to use "auspice develop" which recompiles code on the fly as changes are made.
You may provide customisations (e.g. code, options) to this step to modify the functionality and appearance of auspice.
To serve the bundle you will need a server such as "auspice view".
To serve the bundle you will need a server such as "auspice view".
`;

const subparser = parser.addParser('build', {addHelp: true, description});
Expand All @@ -24,7 +30,7 @@ const addParser = (parser) => {
subparser.addArgument('--serverless', {action: "storeTrue", help: SUPPRESS});
};

const run = (args) => {
const run = (args): void => {

/* webpack set up */
const extensionPath = args.extend ? path.resolve(args.extend) : undefined;
Expand Down Expand Up @@ -64,14 +70,14 @@ const run = (args) => {
/* Where should the built files be saved? (or sourced??)
* This may grow more complex over time
*/
function getCustomOutputPath(extensions) {
function getCustomOutputPath(extensions): string | false {
if (extensions && path.resolve(__dirname, "..") !== process.cwd()) {
return process.cwd();
}
return false;
}

module.exports = {
export {
addParser,
run
};
8 changes: 4 additions & 4 deletions cli/convert.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* eslint no-console: off */
const fs = require("fs");
const convertFromV1 = require("./server/convertJsonSchemas").convertFromV1;
const utils = require("./utils");
import fs from "fs";
import { convertFromV1 } from "./server/convertJsonSchemas.js";
import * as utils from "./utils.ts";


const addParser = (parser) => {
Expand Down Expand Up @@ -35,7 +35,7 @@ const run = (args) => {
fs.writeFileSync(args.output, JSON.stringify(v2, null, args.minify_json ? 0 : 2));
};

module.exports = {
export {
addParser,
run
};
15 changes: 6 additions & 9 deletions cli/dev/reverse-proxy.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@

const utils = require("../utils");
const { URL } = require("url");
const fetch = require("node-fetch");
import * as utils from "../utils.ts";
import { URL } from "url";
import fetch from "node-fetch";

const PROXY = process.env.PROXY || `http://localhost:5000`

Expand Down Expand Up @@ -59,8 +58,6 @@ async function proxy(req, res) {
}
}

module.exports = {
getAvailable: proxy,
getDataset: proxy,
getNarrative: proxy,
};
export const getAvailable = proxy;
export const getDataset = proxy;
export const getNarrative = proxy;
62 changes: 40 additions & 22 deletions cli/develop.js → cli/develop.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
/* eslint no-console: off */
const path = require("path");
const express = require("express");
const webpack = require("webpack");
const webpackDevMiddleware = require("webpack-dev-middleware");
const webpackHotMiddleware = require("webpack-hot-middleware");
const utils = require("./utils");
const { loadAndAddHandlers, serveRelativeFilepaths } = require("./view");
const version = require('../src/version').version;
const chalk = require('chalk');
const generateWebpackConfig = require("../webpack.config.js").default;
import path from "path";
import { fileURLToPath } from 'url';
import { createRequire } from 'module';
import express from "express";
import webpack from "webpack";
import webpackDevMiddleware from "webpack-dev-middleware";
import webpackHotMiddleware from "webpack-hot-middleware";
import * as utils from "./utils.ts";
import * as view from "./view.ts";
import { version } from '../src/version.js';
import _chalk from 'chalk';
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
const chalk = _chalk as any as import('chalk').Chalk;
import { processPathArguments } from "./server/processPaths.ts";

const require = createRequire(import.meta.url);
const __dirname = path.dirname(fileURLToPath(import.meta.url));

const generateWebpackConfig = require("../webpack.config.cjs").default;
const SUPPRESS = require('argparse').Const.SUPPRESS;

const addParser = (parser) => {
const addParser = (parser): void => {
const description = `Launch auspice in development mode.
This runs a local server and uses hot-reloading to allow automatic updating as you edit the code.
NOTE: there is a speed penalty for this ability and this should never be used for production.
Expand All @@ -21,24 +30,25 @@ const addParser = (parser) => {
subparser.addArgument('--verbose', {action: "storeTrue", help: "Print more verbose progress messages in the terminal."});
subparser.addArgument('--extend', {action: "store", metavar: "JSON", help: "Client customisations to be applied. See documentation for more details. Note that hot reloading does not currently work for these customisations."});
subparser.addArgument('--handlers', {action: "store", metavar: "JS", help: "Overwrite the provided server handlers for client requests. See documentation for more details."});
subparser.addArgument('--datasetDir', {metavar: "PATH", help: "Directory where datasets (JSONs) are sourced. This is ignored if you define custom handlers."});
subparser.addArgument('--narrativeDir', {metavar: "PATH", help: "Directory where narratives (Markdown files) are sourced. This is ignored if you define custom handlers."});
view.addDatasetNarrativePathArgs(subparser)
subparser.addArgument('--includeTiming', {action: "storeTrue", help: "Do not strip timing functions. With these included the browser console will print timing measurements for a number of functions."});

/* there are some options which we deliberately do not document via `--help`. See build.js for explanations. */
subparser.addArgument('--gh-pages', {action: "store", help: SUPPRESS});
};


const run = (args) => {
const run = async (args): Promise<void> => {
const dataPaths = processPathArguments(args)

/* Basic server set up */
const app = express();
app.set('port', process.env.PORT || 4000);
app.set('host', process.env.HOST || "localhost");

const baseDir = path.resolve(__dirname, "..");
utils.verbose(`Serving index / favicon etc from "${baseDir}"`);
app.get("/favicon.png", (req, res) => {res.sendFile(path.join(baseDir, "favicon.png"));});
app.get("/favicon.png", (_req, res) => {res.sendFile(path.join(baseDir, "favicon.png"));});

/* webpack set up */
const extensionPath = args.extend ? path.resolve(args.extend) : undefined;
Expand All @@ -51,7 +61,7 @@ const run = (args) => {
process.env.BABEL_EXTENSION_PATH = extensionPath;

/* Redirects / to webpack-generated index */
app.use((req, res, next) => {
app.use((req, _res, next) => {
if (!/^\/__webpack_hmr|^\/charon|\.[A-Za-z0-9]{1,5}$/.test(req.path)) {
req.url = webpackConfig.output.publicPath;
}
Expand All @@ -69,12 +79,20 @@ const run = (args) => {

let handlerMsg = "";
if (args.gh_pages) {
handlerMsg = serveRelativeFilepaths({app, dir: path.resolve(args.gh_pages)});
handlerMsg = view.serveRelativeFilepaths({app, dir: path.resolve(args.gh_pages)});
} else if (args.handlers) {
handlerMsg = await view.customRouteHandlers(app, path.resolve(args.handlers));
} else {
handlerMsg = loadAndAddHandlers({app, handlersArg: args.handlers, datasetDir: args.datasetDir, narrativeDir: args.narrativeDir});
handlerMsg = view.defaultRouteHandlers({app, dataPaths});
}
/* Handle unhandled API (charon) routes */
app.get("/charon*", (req, res) => {
const errorMessage = "Query unhandled -- " + req.originalUrl;
utils.warn(errorMessage);
return res.status(500).type("text/plain").send(errorMessage);
});

app.get("*", (req, res) => res.redirect("/"));
app.get("*", (_req, res) => res.redirect("/"));

const server = app.listen(app.get('port'), app.get('host'), () => {
utils.log("\n\n---------------------------------------------------");
Expand All @@ -95,8 +113,8 @@ const run = (args) => {
utils.error(`Host ${app.get('host')} is not a valid address. The server could not be started. If you did not provide a HOST environment variable when starting the app you may have HOST already set in your system. You can either change that variable, or override HOST when starting the app.

Example commands to fix:
${chalk.yellow('HOST="localhost" auspice develop')}
${chalk.yellow('HOST="localhost" npm run develop')}`);
${chalk.yellow('HOST="localhost" auspice develop <PATH>')}
${chalk.yellow('HOST="localhost" npm run develop -- <PATH>')}`);
}

utils.error(`Uncaught error in app.listen(). Code: ${error.code}`);
Expand All @@ -105,7 +123,7 @@ const run = (args) => {

};

module.exports = {
export {
addParser,
run
};
9 changes: 2 additions & 7 deletions cli/server/convertJsonSchemas.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const utils = require("../utils");
import * as utils from "../utils.ts";

/** In auspice v1, the `prettyString` function was used extensively to transform values
* for "nicer" display. v2 JSONs intentially avoid this -- the strings are intended to
Expand Down Expand Up @@ -397,7 +397,7 @@ const setNodeBranchAttrs = (v2) => {
};


const convertFromV1 = ({tree, meta}) => {
export const convertFromV1 = ({tree, meta}) => {
const v2 = {version: "v2", meta: { extensions: { original_version: "v1" }}};
// set metadata
setColorings(v2["meta"], meta);
Expand All @@ -411,8 +411,3 @@ const convertFromV1 = ({tree, meta}) => {
removeNonV2TreeProps(v2);
return v2;
};


module.exports = {
convertFromV1
};
Loading