Skip to content

Commit

Permalink
chore: use webpack instead of vite
Browse files Browse the repository at this point in the history
The main advantage of vite is that the dev-server avoids bundling, but this
leads to a lot of inconsistency between the dev-server and bundled version.
In particular, there did not seem to be any non-hacky way to override the path
of the oauth redirect html pages when using the dev-server.

Additionally, in practice the webpack dev-server is just as fast once the time
spent by the browser loading the non-bundled sources is taken into account.
  • Loading branch information
jbms committed Mar 15, 2024
1 parent 2cdc1f3 commit dc7f415
Show file tree
Hide file tree
Showing 31 changed files with 44,223 additions and 6,866 deletions.
1 change: 1 addition & 0 deletions .eslintrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ rules:
"no-unsafe-finally": "off"
"require-yield": "off"
"no-inner-declarations": "off"
"import/no-named-as-default": "off"
"import/no-named-as-default-member": "off"
"import/no-cycle": "error"
"@typescript-eslint/consistent-type-imports": "error"
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/build_preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ jobs:
with:
name: client
path: |
dist/min/*
dist/client/*
2 changes: 1 addition & 1 deletion .github/workflows/deploy_preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ jobs:
- uses: actions/download-artifact@v4
with:
name: client
path: dist/min
path: dist/client
github-token: "${{ secrets.GITHUB_TOKEN }}"
run-id: "${{ github.event.workflow_run.id }}"
- name: Get PR ID
Expand Down
150 changes: 88 additions & 62 deletions build_tools/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,22 @@

// Command-line interface for building Neuroglancer.

/// <reference types="webpack-dev-server" />

import process from "node:process";
import { pathToFileURL } from "node:url";
import path from "path";
import * as vite from "vite";
import checkerPlugin from "vite-plugin-checker";
import ForkTsCheckerWebpackPlugin from "fork-ts-checker-webpack-plugin";
import type { Configuration } from "webpack";
import webpackCli from "webpack-cli/lib/bootstrap.js"; // eslint-disable-line import/default
import * as webpackMerge from "webpack-merge";
import yargs from "yargs";
import { normalizeConfigurationWithDefine } from "./webpack/configuration_with_define.js";
import { setConfig } from "./webpack/webpack_config_from_cli.cjs";

export interface WebpackConfigurationWithDefine extends Configuration {
define?: Record<string, any> | undefined;
}

function parseDefines(
definesArg:
Expand Down Expand Up @@ -66,53 +76,75 @@ function parseDefines(

type Argv = Awaited<ReturnType<typeof parseArgs>>;

async function getViteConfig(argv: Argv): Promise<vite.UserConfig> {
let config: vite.UserConfig = {};
for (const configPath of argv.config) {
const loadedConfig = (await import(pathToFileURL(configPath).href)).default;
config = vite.mergeConfig(config, loadedConfig);
}
async function getWebpackConfig(
argv: Argv,
...extraConfigs: WebpackConfigurationWithDefine[]
): Promise<(...args: any[]) => Configuration> {
const configPaths = [
pathToFileURL(path.resolve(import.meta.dirname, "../webpack.config.js"))
.href,
...argv.config.map((configPath) => pathToFileURL(configPath).href),
];
const allConfigs = await Promise.all(
configPaths.map(async (configPath) => (await import(configPath)).default),
);
allConfigs.push(...extraConfigs);
return (webpackEnv, webpackArgs) => {
webpackEnv = { ...webpackEnv, NEUROGLANCER_CLI: true };
const conditions = argv.conditions;
if (argv.python) conditions.push("neuroglancer/python");
const outDir =
(argv.output as string | undefined) ??
(argv.python
? path.resolve(
import.meta.dirname,
"..",
"python",
"neuroglancer",
"static",
"client",
)
: undefined);
const plugins = [];
if (argv.typecheck || argv.lint) {
plugins.push(
new ForkTsCheckerWebpackPlugin({
typescript: argv.typecheck,
eslint: argv.lint
? {
files: ".",
}
: undefined,
}),
);
}
const inlineConfig = {
define: argv.define,
plugins,
output: {
path: outDir,
},
resolve: {
conditionNames: ["...", ...conditions],
},
} satisfies WebpackConfigurationWithDefine;
const resolvedConfigs = allConfigs.map((config) =>
typeof config === "function" ? config(webpackEnv, webpackArgs) : config,
);
return normalizeConfigurationWithDefine(
webpackMerge.merge([...resolvedConfigs, inlineConfig]),
);
};
}

const conditions = argv.conditions;
if (argv.python) conditions.push("neuroglancer/python");
const outDir =
(argv.output as string | undefined) ??
(argv.python
? path.resolve(
import.meta.dirname,
"..",
"python",
"neuroglancer",
"static",
"client",
)
: undefined);
const inlineConfig = {
root: path.resolve(import.meta.dirname, ".."),
base: argv.base,
define: argv.define,
...(argv.mode !== undefined ? { mode: argv.mode } : {}),
build: {
watch: argv.watch ? {} : undefined,
outDir,
},
plugins: [
argv.typecheck || argv.lint
? checkerPlugin({
typescript: argv.typecheck ?? false,
eslint: argv.lint
? {
lintCommand: "eslint .",
}
: undefined,
})
: undefined,
],
resolve: {
conditions,
},
} satisfies vite.UserConfig;
return vite.mergeConfig(config, inlineConfig);
async function runWebpack(...args: string[]) {
// @ts-expect-error: no typings available
await webpackCli([
...process.argv.slice(0, 2),
...args,
"--config",
path.resolve(import.meta.dirname, "webpack", "webpack_config_from_cli.cjs"),
]);
}

function parseArgs() {
Expand Down Expand Up @@ -154,17 +186,13 @@ function parseArgs() {
description: "Build mode",
type: "string",
},
base: {
type: "string",
description: "Base path to assume.",
},
config: {
array: true,
string: true,
nargs: 1,
default: [],
description:
"Additional vite config module to merge into configuration.",
"Additional webpack config module to merge into configuration.",
},
})
.command({
Expand Down Expand Up @@ -192,18 +220,15 @@ function parseArgs() {
},
}),
handler: async (argv) => {
const server = await vite.createServer(
vite.mergeConfig(await getViteConfig(argv), {
server: {
port: argv.port,
setConfig(
await getWebpackConfig(argv, {
devServer: {
port: argv.port === 0 ? "auto" : argv.port,
host: argv.host,
},
}),
);
await server.listen();

server.printUrls();
server.bindCLIShortcuts({ print: true });
await runWebpack("serve", `--mode=${argv.mode}`);
},
})
.command({
Expand All @@ -227,7 +252,8 @@ function parseArgs() {
},
}),
handler: async (argv) => {
await vite.build(vite.mergeConfig(await getViteConfig(argv), {}));
setConfig(await getWebpackConfig(argv, { watch: argv.watch }));
await runWebpack("build", `--mode=${argv.mode}`);
},
})
.strict()
Expand Down
18 changes: 0 additions & 18 deletions build_tools/vite/vite-plugin-dev-server-path-map.ts

This file was deleted.

13 changes: 13 additions & 0 deletions build_tools/webpack/configuration_with_define.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Converts a webpack configuration that optionally contains an extra `define`
// property into a regular webpack configuration with an added `DefinePlugin`.

import webpack from "webpack";

export function normalizeConfigurationWithDefine(config) {
let { define, plugins, ...rest } = config;
if (define !== undefined && Object.keys(define).length > 0) {
plugins ??= [];
plugins.push(new webpack.DefinePlugin(define));
}
return { plugins, ...rest };
}
14 changes: 14 additions & 0 deletions build_tools/webpack/webpack_config_from_cli.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// This module is specified as the webpack config module when `cli.ts` invokes
// `webpack-cli` programmatically.
//
// It simply returns the configuration previously set by `cli.ts`.
//
// This module is in cjs format rather than esm format to avoid a separate copy
// being loaded, for some reason, by tsx.

let config = undefined;

module.exports = (...args) => config(...args);
module.exports.setConfig = (newConfig) => {
config = newConfig;
};
4 changes: 2 additions & 2 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ Neuroglancer can be used as a dependency in two ways:

The following bundlers are known to be compatible:

- [vite](./vite/)
- [webpack](./webpack/) (recommended)
- [parcel](./parcel/)
- [webpack](./webpack/)
- [vite](./vite/)

esbuild is not compatible due to https://github.com/evanw/esbuild/issues/795
Loading

0 comments on commit dc7f415

Please sign in to comment.