Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 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,7 +30,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 Down
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;
44 changes: 26 additions & 18 deletions cli/develop.js → cli/develop.ts
Original file line number Diff line number Diff line change
@@ -1,18 +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 view = 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 { processPathArguments } = require("./server/processPaths");

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 @@ -30,7 +38,7 @@ const addParser = (parser) => {
};


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

/* Basic server set up */
Expand All @@ -40,7 +48,7 @@ const run = (args) => {

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 @@ -53,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 @@ -73,7 +81,7 @@ const run = (args) => {
if (args.gh_pages) {
handlerMsg = view.serveRelativeFilepaths({app, dir: path.resolve(args.gh_pages)});
} else if (args.handlers) {
handlerMsg = view.customRouteHandlers(app, path.resolve(args.handlers));
handlerMsg = await view.customRouteHandlers(app, path.resolve(args.handlers));
} else {
handlerMsg = view.defaultRouteHandlers({app, dataPaths});
}
Expand All @@ -84,7 +92,7 @@ const run = (args) => {
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 Down Expand Up @@ -115,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
};
55 changes: 32 additions & 23 deletions cli/server/getAvailable.js → cli/server/getAvailable.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,27 @@
const utils = require("../utils");
const fs = require('fs');
const path = require("path");
const { promisify } = require('util');
const { findAvailableSecondTreeOptions } = require('./getDatasetHelpers');
import * as utils from "../utils.ts";
import fs from 'fs';
import path from "path";
import { promisify } from 'util';
import { findAvailableSecondTreeOptions } from './getDatasetHelpers.ts';

const readdir = promisify(fs.readdir);

const getAvailableDatasets = (dir, files) => {
export interface DatasetInfo {
request: string;
v2: boolean;
secondTreeOptions: string[];
fileType: string;
dir: string;
buildUrl?: string;
}

export interface NarrativeInfo {
fullPath: string;
request: string;
fileType: string;
}

export const getAvailableDatasets = (dir, files): DatasetInfo[] => {
const datasets = [];
/* NOTE: if there are v1 & v2 files with the same name the v2 JSON is
* preferentially fetched. E.g. if `zika.json`, `zika_meta.json` and
Expand Down Expand Up @@ -62,7 +77,7 @@ const getAvailableDatasets = (dir, files) => {
return datasets;
};

const getAvailableNarratives = (dir, files) => {
export const getAvailableNarratives = (dir, files): NarrativeInfo[] => {
return files
.filter((file) => file.endsWith(".md") && file!=="README.md")
.map((file) => ({
Expand All @@ -72,15 +87,16 @@ const getAvailableNarratives = (dir, files) => {
}));
};

const setUpGetAvailableHandler = (dataPaths) => {
export const setUpGetAvailableHandler = (dataPaths) => {
/* return Auspice's default handler for "/charon/getAvailable" requests.
* Remember that auspice itself only serves local files.
* Servers often use their own handler instead of this.
*/
return async (req, res) => {
return async (_req, res): Promise<void> => {
utils.log("GET AVAILABLE returning locally available datasets & narratives");
let resources = []
for (const [p, dataTypes] of Object.entries(dataPaths)) {
let resources: (DatasetInfo | NarrativeInfo)[] = []
/* eslint-disable-next-line @typescript-eslint/consistent-type-assertions */
for (const [p, dataTypes] of Object.entries(dataPaths) as [string, Set<string>][]) {
try {
// FUTURE TODO - recurse into subfolders (readdir can do this natively)
const files = await readdir(p);
Expand All @@ -103,25 +119,18 @@ const setUpGetAvailableHandler = (dataPaths) => {

};


module.exports = {
setUpGetAvailableHandler,
getAvailableDatasets,
getAvailableNarratives,
};

function availableResponseStructure(resources) {
const datasets = resources.filter((r) => r.fileType==='dataset')
function availableResponseStructure(resources: (DatasetInfo | NarrativeInfo)[]): {datasets: {request: string, buildUrl?: string, secondTreeOptions?: string[]}[], narratives: {request: string}[]} {
const datasets = resources.filter((r): r is DatasetInfo => r.fileType==='dataset')
.map((r) => ({request: r.request, buildUrl: r.buildUrl, secondTreeOptions: r.secondTreeOptions}));
const narratives = resources.filter((r) => r.fileType==='narrative')
const narratives = resources.filter((r): r is NarrativeInfo => r.fileType==='narrative')
.map((r) => ({request: r.request}));
return {datasets, narratives};
}

/**
* First match wins
*/
function unique(resources) {
function unique(resources: (DatasetInfo | NarrativeInfo)[]): (DatasetInfo | NarrativeInfo)[] {
const seen = {dataset: new Set(), narrative: new Set()};
return resources.filter((r) => {
if (seen[r.fileType].has(r.request)) {
Expand All @@ -131,4 +140,4 @@ function unique(resources) {
seen[r.fileType].add(r.request);
return true;
})
}
}
Loading