diff --git a/docs/devs/plugin-migration-to-2.1.md b/docs/devs/plugin-migration-to-2.1.md index 697fafbb..677b8053 100644 --- a/docs/devs/plugin-migration-to-2.1.md +++ b/docs/devs/plugin-migration-to-2.1.md @@ -54,7 +54,6 @@ These are left as imports in the plugin bundle and resolved by the browser, ther Everything else is bundled into the plugin, including `@ant-design/icons`. Import `react-dom/client` and `react/jsx-runtime` by those names, other subpaths of the shared packages are bundled as a separate copy. -Vite does not provide Node.js modules to plugin code. -If your web code imports `events`, `path` or similar, add the browser package for it (`events`, `path-browserify`) to your dependencies. +Imports of `assert`, `buffer`, `events`, `os`, `path`, `process`, `stream`, `util` and `zlib` resolve to the same browser packages as before, provided by `@clusterio/web_ui`. The `container` property of `WebPluginContext` is now the plugin's ES module namespace, `await container.get("./web")` returns the module instead of a factory function. diff --git a/packages/lib/package.json b/packages/lib/package.json index 79605060..b38ab998 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -54,19 +54,10 @@ "@types/yauzl": "catalog:", "@types/yazl": "catalog:", "ajv": "^8.20.0", - "assert": "^2.1.0", - "browserify-zlib": "^0.2.0", - "buffer": "^6.0.3", "chalk": "catalog:", - "events": "^3.3.0", "fast-deep-equal": "^3.1.3", - "os-browserify": "^0.3.0", - "path-browserify": "^1.0.1", - "process": "^0.11.10", "set-blocking": "catalog:", - "stream-browserify": "^3.0.0", "triple-beam": "^1.4.1", - "util": "^0.12.5", "winston": "catalog:", "winston-transport": "catalog:", "ws": "catalog:", diff --git a/packages/lib/src/browser_polyfills.ts b/packages/lib/src/browser_polyfills.ts index 6f901993..1c442d68 100644 --- a/packages/lib/src/browser_polyfills.ts +++ b/packages/lib/src/browser_polyfills.ts @@ -1,7 +1,6 @@ // Node.js globals used by lib and its dependencies when running in a browser. -// @ts-expect-error process has no type declarations -import process from "process/browser.js"; -import { Buffer } from "buffer/"; +import process from "process"; +import { Buffer } from "buffer"; // Vite's development server may already have created process.env if (typeof globalThis.process?.nextTick !== "function") { diff --git a/packages/web_ui/package.json b/packages/web_ui/package.json index 647277f2..3f010034 100644 --- a/packages/web_ui/package.json +++ b/packages/web_ui/package.json @@ -38,7 +38,13 @@ "@sinclair/typebox": "catalog:", "@vitejs/plugin-react": "catalog:", "antd": "catalog:", + "assert": "^2.1.0", + "browserify-zlib": "^0.2.0", + "buffer": "^6.0.3", "events": "^3.3.0", + "os-browserify": "^0.3.0", + "path-browserify": "^1.0.1", + "process": "^0.11.10", "rc-field-form": "^1.44.0", "react": "catalog:", "react-dom": "catalog:", @@ -46,7 +52,9 @@ "react-router": "catalog:", "react-router-dom": "catalog:", "semver": "catalog:", + "stream-browserify": "^3.0.0", "typescript": "catalog:", + "util": "^0.12.5", "vite": "catalog:" }, "devDependencies": { diff --git a/packages/web_ui/vite.mjs b/packages/web_ui/vite.mjs index 3532a96f..48f0bd2a 100644 --- a/packages/web_ui/vite.mjs +++ b/packages/web_ui/vite.mjs @@ -5,6 +5,7 @@ import path from "node:path"; import util from "node:util"; import { createRequire } from "node:module"; import { execFile } from "node:child_process"; +import { fileURLToPath } from "node:url"; import react from "@vitejs/plugin-react"; import { normalizePath } from "vite"; @@ -32,6 +33,20 @@ const esmSharedModules = new Set(["@clusterio/lib", "@clusterio/web_ui"]); // Only the console transport of winston works in the browser, and the colour // library used by it does not. const stubbedModules = /node_modules\/(winston\/dist\/winston\/transports\/(http|file)\.js|@colors\/colors\/)/; +// Browser versions of Node.js modules, resolved from web_ui so lib and plugins +// don't need them as dependencies. +const polyfills = { + assert: "assert/", + buffer: "buffer/", + events: "events/", + os: "os-browserify/browser", + path: "path-browserify", + process: "process/browser.js", + stream: "stream-browserify", + util: "util/", + zlib: "browserify-zlib", +}; +const webUiPackage = fileURLToPath(new URL("./package.json", import.meta.url)); const sharedPrefix = "\0clusterio:shared/"; const pluginPrefix = "\0clusterio:plugin/"; @@ -134,9 +149,19 @@ const stubs = { }, }; +const polyfillResolver = { + name: "clusterio:polyfills", + resolveId(id, _importer, options) { + const target = polyfills[id.replace(/^node:/, "")]; + if (!target) { + return null; + } + return this.resolve(target, webUiPackage, { ...options, skipSelf: true }); + }, +}; + /** Base config needed to build code that imports @clusterio/lib for the browser. */ function browserCompat() { - const alias = (find, replacement) => ({ find: new RegExp(`^(node:)?${find}$`), replacement }); return { name: "clusterio:browser-compat", enforce: "pre", @@ -153,20 +178,10 @@ function browserCompat() { "process.env.BUILD_OS": JSON.stringify(os.platform()), }, optimizeDeps: { - rolldownOptions: { plugins: [stubs] }, + rolldownOptions: { plugins: [polyfillResolver, stubs] }, }, resolve: { - alias: [ - alias("assert", "assert/"), - alias("buffer", "buffer/"), - alias("events", "events/"), - alias("os", "os-browserify/browser"), - alias("path", "path-browserify"), - alias("stream", "stream-browserify"), - alias("util", "util/"), - alias("zlib", "browserify-zlib"), - { find: /^node:(.*)$/, replacement: "$1" }, - ], + alias: [{ find: /^node:(.*)$/, replacement: "$1" }], }, build: { target: "es2022", @@ -185,6 +200,7 @@ function browserCompat() { }, }; }, + resolveId: polyfillResolver.resolveId, load: stubs.load, transform(code, id) { if (id.endsWith(".md")) {