[Manual Mirror] RS Pack try 2 (#11172)

This commit is contained in:
Kashargul
2025-07-11 15:35:20 +02:00
committed by GitHub
parent 51adb9a341
commit 222559a464
16 changed files with 599 additions and 414 deletions

View File

@@ -22,8 +22,6 @@ jobs:
# Caches
- name: Setup Bun
uses: ./.github/actions/setup_bun
with:
restore-yarn-cache: true
- name: Restore Bootstrap cache
uses: actions/cache@v4
with:

View File

@@ -18,6 +18,9 @@
"gitlens.advanced.blame.customArguments": [
"-w", "--ignore-revs-file", ".git-blame-ignore-revs"
],
"search.exclude": {
"**/node_modules": true,
},
"tgstationTestExplorer.project.resultsType": "json",
"[dm]": {
"files.eol": "\n",

View File

@@ -17,7 +17,7 @@ export RUST_G_VERSION=3.11.0
export NODE_VERSION_LTS=22.14.0
# Bun version
export BUN_VERSION=1.2.16
export BUN_VERSION=1.2.18
# SpacemanDMM git tag
export SPACEMAN_DMM_VERSION=suite-1.10

View File

@@ -1,15 +0,0 @@
{
"$schema": "https://swc.rs/schema.json",
"jsc": {
"loose": true,
"parser": {
"syntax": "typescript",
"tsx": true
},
"transform": {
"react": {
"runtime": "automatic"
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -2,13 +2,13 @@
"private": true,
"name": "tgui-workspace",
"version": "6.0.0",
"packageManager": "bun@1.2.16",
"packageManager": "bun@1.2.18",
"workspaces": [
"packages/*"
],
"scripts": {
"tgui:analyze": "webpack --analyze",
"tgui:build": "webpack",
"tgui:analyze": "rspack --analyze",
"tgui:build": "rspack build",
"tgui:dev": "bun packages/tgui-dev-server/index.ts",
"tgui:lint": "eslint packages --ext .js,.cjs,.ts,.tsx",
"tgui:prettier": "prettier --check .",
@@ -18,26 +18,21 @@
"tgui:eslint-fix": "eslint --fix packages --ext .js,.cjs,.ts,.jsx,.tsx"
},
"dependencies": {
"@swc/core": "^1.12.1",
"@rspack/cli": "^1.4.3",
"@rspack/core": "^1.4.3",
"@typescript-eslint/eslint-plugin": "^8.28.0",
"@typescript-eslint/parser": "^8.28.0",
"@typescript-eslint/utils": "^8.28.0",
"css-loader": "^7.1.2",
"esbuild-loader": "^4.3.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-react": "^7.34.1",
"eslint-plugin-simple-import-sort": "^12.0.0",
"eslint-plugin-unused-imports": "^4.1.4",
"mini-css-extract-plugin": "^2.9.2",
"prettier": "^3.5.3",
"sass": "^1.80.6",
"sass-loader": "^16.0.3",
"swc-loader": "^0.2.6",
"typescript": "^5.8.3",
"webpack": "^5.99.9",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-cli": "^6.0.1"
"sass-embedded": "^1.89.2",
"sass-loader": "^16.0.5",
"typescript": "^5.8.3"
},
"devDependencies": {
"@happy-dom/global-registrator": "^17.4.7",

View File

@@ -7,14 +7,16 @@
import fs from 'node:fs';
import { reloadByondCache } from './reloader';
import { createCompiler } from './webpack';
import { RspackCompiler } from './webpack';
const reloadOnce = process.argv.includes('--reload');
async function setupServer() {
fs.mkdirSync('./public/.tmp', { recursive: true });
const compiler = await createCompiler({ mode: 'development', hot: true });
const compiler = new RspackCompiler();
await compiler.setup();
// Reload cache once
if (reloadOnce) {

View File

@@ -1,66 +1,39 @@
/**
* @file
* @copyright 2020 Aleksej Komarov
* @license MIT
*/
import { createRequire } from 'module';
import { createRequire } from 'node:module';
import { config } from '../../rspack.config-dev';
import { loadSourceMaps } from './link/retrace';
import { broadcastMessage, setupLink } from './link/server';
import { createLogger } from './logging.js';
import { reloadByondCache } from './reloader.js';
import { createLogger } from './logging';
import { reloadByondCache } from './reloader';
import { resolveGlob } from './util';
const logger = createLogger('webpack');
const logger = createLogger('rspack');
export async function createCompiler(
options: Record<string, any>,
): Promise<WebpackCompiler> {
const compiler = new WebpackCompiler();
await compiler.setup(options);
export class RspackCompiler {
rspack: any;
config: any;
bundleDir: string;
return compiler;
}
// eslint-disable-next-line @typescript-eslint/consistent-type-imports
type WebpackImport = typeof import('webpack');
class WebpackCompiler {
public webpack: WebpackImport;
public config: Record<string, any>;
public bundleDir: string;
async setup(options: Record<string, any>): Promise<void> {
async setup() {
// Create a require context that is relative to project root
// and retrieve all necessary dependencies.
const requireFromRoot = createRequire(import.meta.dirname + '/../../..');
const webpack: WebpackImport = await requireFromRoot('webpack');
const requireFromRoot = createRequire(`${import.meta.dirname}/../../..`);
const rspack = await requireFromRoot('@rspack/core');
const createConfig = await requireFromRoot('./webpack.config.js');
const config = createConfig({}, options);
// Inject the HMR plugin into the config if we're using it
if (options.hot) {
config.plugins.push(new webpack.HotModuleReplacementPlugin());
}
this.webpack = webpack;
this.rspack = rspack;
this.config = config;
this.bundleDir = config.output.path;
this.bundleDir = config.output?.path || '';
}
async watch(): Promise<void> {
async watch() {
logger.log('setting up');
// Setup link
const link = setupLink();
setupLink();
// Instantiate the compiler
const compiler = this.webpack.webpack(this.config);
const compiler = this.rspack.rspack(this.config);
// Clear garbage before compiling
compiler.hooks.watchRun.tapPromise('tgui-dev-server', async () => {
const files = await resolveGlob(this.bundleDir, '*.hot-update.*');
logger.log(`clearing garbage (${files.length} files)`);
for (const file of files) {
await Bun.file(file).delete();
}
@@ -78,7 +51,6 @@ class WebpackCompiler {
type: 'hotUpdate',
});
});
// Start watching
logger.log('watching for changes');
compiler.watch({}, (err, stats) => {
@@ -87,7 +59,7 @@ class WebpackCompiler {
return;
}
stats
?.toString(this.config.devServer?.stats)
?.toString(this.config.stats)
.split('\n')
.forEach((line) => logger.log(line));
});

View File

@@ -7,7 +7,7 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tgui": "workspace:*",
"tgui-core": "^4.3.2",
"tgui-core": "^4.3.3",
"tgui-dev-server": "workspace:*"
},
"private": true

View File

@@ -6,7 +6,7 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tgui": "workspace:*",
"tgui-core": "^4.3.2"
"tgui-core": "^4.3.3"
},
"private": true
}

View File

@@ -11,7 +11,7 @@
"marked": "^15.0.11",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"tgui-core": "^4.3.2",
"tgui-core": "^4.3.3",
"tgui-dev-server": "workspace:*"
},
"private": true

29
tgui/rspack.config-dev.ts Normal file
View File

@@ -0,0 +1,29 @@
import path from 'node:path';
import rspack, { type Configuration } from '@rspack/core';
import oldConfig, { createStats } from './rspack.config';
export const config = {
...oldConfig,
devtool: 'cheap-module-source-map',
devServer: {
hot: true,
},
mode: 'development',
output: {
...oldConfig.output,
path: path.resolve(import.meta.dirname, './public/.tmp'),
},
plugins: [
new rspack.CssExtractRspackPlugin({
chunkFilename: '[name].bundle.css',
filename: '[name].bundle.css',
}),
new rspack.EnvironmentPlugin({
NODE_ENV: 'development',
}),
new rspack.HotModuleReplacementPlugin(),
],
stats: createStats(false),
} satisfies Configuration;

139
tgui/rspack.config.ts Normal file
View File

@@ -0,0 +1,139 @@
import path from 'node:path';
import { defineConfig } from '@rspack/cli';
import rspack, { type StatsOptions } from '@rspack/core';
export function createStats(verbose: boolean): StatsOptions {
return {
assets: verbose,
builtAt: verbose,
cached: false,
children: false,
chunks: false,
colors: true,
entrypoints: true,
hash: false,
modules: false,
performance: false,
timings: verbose,
version: verbose,
};
}
const dirname = path.resolve();
export default defineConfig({
context: dirname,
devtool: false,
entry: {
tgui: './packages/tgui',
'tgui-panel': './packages/tgui-panel',
'tgui-say': './packages/tgui-say',
},
mode: 'production',
module: {
rules: [
{
test: /\.([tj]s(x)?|cjs)$/,
type: 'javascript/auto',
use: [
{
loader: 'builtin:swc-loader',
options: {
jsc: {
parser: {
syntax: 'typescript',
tsx: true,
},
transform: {
react: {
runtime: 'automatic',
},
},
},
},
},
],
},
{
test: /\.(s)?css$/,
type: 'javascript/auto',
use: [
rspack.CssExtractRspackPlugin.loader,
'css-loader',
{
loader: 'sass-loader',
options: {
api: 'modern-compiler',
implementation: 'sass-embedded',
},
},
],
},
{
test: /\.(png|jpg)$/,
oneOf: [
{
issuer: /\.(s)?css$/,
type: 'asset/inline',
},
{
type: 'asset/resource',
},
],
generator: {
filename: '[name][ext]',
},
},
{
test: /\.svg$/,
oneOf: [
{
issuer: /\.(s)?css$/,
type: 'asset/inline',
},
{
type: 'asset/resource',
},
],
generator: {
filename: '[name][ext]',
},
},
],
},
optimization: {
emitOnErrors: false,
},
output: {
path: 'public',
filename: '[name].bundle.js',
chunkFilename: '[name].bundle.js',
chunkLoadTimeout: 15000,
publicPath: '/',
assetModuleFilename: '[name][ext]',
},
performance: {
hints: false,
},
plugins: [
new rspack.CssExtractRspackPlugin({
chunkFilename: '[name].bundle.css',
filename: '[name].bundle.css',
}),
new rspack.EnvironmentPlugin({
NODE_ENV: 'production',
}),
],
resolve: {
extensions: ['.tsx', '.ts', '.js', '.jsx'],
alias: {
tgui: path.resolve(dirname, './packages/tgui'),
'tgui-panel': path.resolve(dirname, './packages/tgui-panel'),
'tgui-say': path.resolve(dirname, './packages/tgui-say'),
'tgui-dev-server': path.resolve(dirname, './packages/tgui-dev-server'),
},
},
stats: createStats(true),
target: ['web', 'browserslist:edge >= 123'],
});

View File

@@ -1,148 +0,0 @@
/**
* @file
* @copyright 2020 Aleksej Komarov
* @license MIT
*/
const webpack = require('webpack');
const path = require('path');
const ExtractCssPlugin = require('mini-css-extract-plugin');
const createStats = (verbose) => ({
assets: verbose,
builtAt: verbose,
cached: false,
children: false,
chunks: false,
colors: true,
entrypoints: true,
hash: false,
modules: false,
performance: false,
timings: verbose,
version: verbose,
});
module.exports = (env = {}, argv) => {
const mode = argv.mode || 'production';
const config = {
mode,
context: path.resolve(__dirname),
target: ['web', 'browserslist:edge>=123'],
entry: {
tgui: ['./packages/tgui'],
'tgui-panel': ['./packages/tgui-panel'],
'tgui-say': ['./packages/tgui-say'],
},
output: {
path:
mode !== 'production'
? path.resolve(__dirname, './public/.tmp')
: path.resolve(__dirname, './public'),
filename: '[name].bundle.js',
chunkFilename: '[name].bundle.js',
chunkLoadTimeout: 15000,
publicPath: '/',
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.jsx'],
alias: {
tgui: path.resolve(__dirname, './packages/tgui'),
'tgui-panel': path.resolve(__dirname, './packages/tgui-panel'),
'tgui-say': path.resolve(__dirname, './packages/tgui-say'),
'tgui-dev-server': path.resolve(
__dirname,
'./packages/tgui-dev-server',
),
},
},
module: {
rules: [
{
test: /\.([tj]s(x)?|cjs)$/,
exclude: /node_modules/,
use: [
{
loader: require.resolve('swc-loader'),
},
],
},
{
test: /\.(s)?css$/,
use: [
ExtractCssPlugin.loader,
require.resolve('css-loader'),
require.resolve('sass-loader'),
],
},
{
test: /\.(cur|png|jpg)$/,
type: 'asset/resource',
},
{
test: /.svg$/,
oneOf: [
{
issuer: /\.(s)?css$/,
type: 'asset/inline',
},
{
type: 'asset/resource',
},
],
},
],
},
optimization: {
emitOnErrors: false,
},
performance: {
hints: false,
},
devtool: false,
cache: {
type: 'filesystem',
cacheLocation: path.resolve(
__dirname,
`node_modules/.cache/webpack/${mode}`,
),
buildDependencies: {
config: [__filename],
},
},
stats: createStats(true),
plugins: [
new webpack.EnvironmentPlugin({
NODE_ENV: mode,
}),
new ExtractCssPlugin({
filename: '[name].bundle.css',
chunkFilename: '[name].bundle.css',
}),
],
};
// Production build specific options
if (mode === 'production') {
const { EsbuildPlugin } = require('esbuild-loader');
config.optimization.minimizer = [
new EsbuildPlugin({
css: true,
legalComments: 'none',
}),
];
} else {
config.devServer = {
clientLogLevel: 'silent',
hot: true,
noInfo: false,
progress: false,
quiet: false,
stats: createStats(false),
};
config.devtool = 'cheap-module-source-map';
}
return config;
};

View File

@@ -26,7 +26,7 @@ const dependencies: Record<string, any> = await Bun.file("dependencies.sh")
.then(formatDeps)
.catch((err) => {
Juke.logger.error(
"Failed to read dependencies.sh, please ensure it exists and is formatted correctly."
"Failed to read dependencies.sh, please ensure it exists and is formatted correctly.",
);
Juke.logger.error(err);
throw new Juke.ExitCode(1);
@@ -82,7 +82,7 @@ export const NoWarningParameter = new Juke.Parameter({
export const CutterTarget = new Juke.Target({
onlyWhen: () => {
const files = Juke.glob(cutter_path);
return files.length == 0;
return files.length === 0;
},
executes: async () => {
const repo = dependencies.CUTTER_REPO;
@@ -104,7 +104,6 @@ export const IconCutterTarget = new Juke.Target({
`icons/**/*.png.toml`,
`icons/**/*.dmi.toml`,
`cutter_templates/**/*.toml`,
"tgui/public/tgui.html",
cutter_path,
];
// Alright we're gonna search out any existing toml files and convert
@@ -226,7 +225,7 @@ export const DmTestTarget = new Juke.Target({
"-trusted",
"-verbose",
"-params",
"log-directory=ci"
"log-directory=ci",
);
Juke.rm("*.test.*");
try {
@@ -273,7 +272,7 @@ export const AutowikiTarget = new Juke.Target({
"-trusted",
"-verbose",
"-params",
"log-directory=ci"
"log-directory=ci",
);
Juke.rm("*.test.*");
if (!fs.existsSync("data/autowiki_edits.txt")) {
@@ -306,11 +305,11 @@ export const TgFontTarget = new Juke.Target({
fs.mkdirSync("tgui/packages/tgfont/static", { recursive: true });
fs.copyFileSync(
"tgui/packages/tgfont/dist/tgfont.css",
"tgui/packages/tgfont/static/tgfont.css"
"tgui/packages/tgfont/static/tgfont.css",
);
fs.copyFileSync(
"tgui/packages/tgfont/dist/tgfont.woff2",
"tgui/packages/tgfont/static/tgfont.woff2"
"tgui/packages/tgfont/static/tgfont.woff2",
);
},
});
@@ -318,7 +317,7 @@ export const TgFontTarget = new Juke.Target({
export const TguiTarget = new Juke.Target({
dependsOn: [BunTarget],
inputs: [
"tgui/webpack.config.js",
"tgui/rspack.config.ts",
"tgui/**/package.json",
"tgui/packages/**/*.+(js|cjs|ts|tsx|jsx|scss)",
],

View File

@@ -2,6 +2,6 @@
"private": true,
"type": "module",
"devDependencies": {
"@types/bun": "^1.2.16"
"@types/bun": "^1.2.18"
}
}