mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-21 20:20:33 +01:00
[tgui] Replaces node with bun (#91359)
## About The Pull Request Swaps out node & yarn in favor of [bun](https://bun.sh/)  sub tasks - [x] add bun setup script - [x] fix tgui-dev-server (bun glob is different) - [x] set juke to run bun - [x] remove all yarn stuff - [x] convert all tests from vitest to bun - [x] fight with CI/tgs ## Why It's Good For The Game Yarn has served us over the years as our package manager but the method it bundles dependencies has lead to issues and setbacks, notably needing to wait on rspack support, but more recently in trying to switch to biome 1. I can add in packages that do not need these workarounds, like god intended 2. We won't need to [keep around sdks](https://yarnpkg.com/getting-started/editor-sdks) which rely on yarn to even publish 3. We're not committing the yarn cache or .pnp file, which kind of defeats the purpose 4. Native typescript support and testing 5. Because it'd be cool ## Caveats Rspack was throwing errors on TGS while doing this. I needed to switch back to webpack/swc, which seems to work flawlessly. It was too tiring for anyone involved to debug and this was the simplest route. It adds a completely negligible amount of time to build. It might even resolve some issues elsewhere. Making this switch extends that very first setup time! I'm working on cutting it down, but as of right now, it takes about 80 seconds just for TGUI to download all the packages. Afterwards, it's the same. ## Changelog
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
# This action is a wrapper around `oven-sh/setup-bun` to use the version specified in
|
||||
# `dependencies.sh`.
|
||||
name: Setup Bun
|
||||
description: Install Bun using the version specified in `dependencies.sh`
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
- name: Configure Bun version
|
||||
shell: bash
|
||||
run: |
|
||||
source dependencies.sh
|
||||
echo "BUN_VERSION_REQUIRED=$BUN_VERSION" >> $GITHUB_ENV
|
||||
- name: Install Bun
|
||||
uses: oven-sh/setup-bun@v2
|
||||
with:
|
||||
bun-version: ${{ env.BUN_VERSION_REQUIRED }}
|
||||
@@ -32,7 +32,7 @@ jobs:
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
run: |
|
||||
source $HOME/BYOND/byond/bin/byondsetup
|
||||
tools/build/build --ci autowiki
|
||||
tools/build/build.sh --ci autowiki
|
||||
- name: Run Autowiki
|
||||
if: steps.secrets_set.outputs.SECRETS_ENABLED
|
||||
env:
|
||||
|
||||
@@ -15,14 +15,14 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup_node
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup_bun
|
||||
- name: Restore BYOND from Cache
|
||||
uses: ./.github/actions/restore_or_install_byond
|
||||
- name: Compile All Maps
|
||||
run: |
|
||||
source $HOME/BYOND/byond/bin/byondsetup
|
||||
tools/build/build --ci dm -DCIBUILDING -DCITESTING -DALL_MAPS
|
||||
tools/build/build.sh --ci dm -DCIBUILDING -DCITESTING -DALL_MAPS
|
||||
- name: Check client Compatibility
|
||||
uses: tgstation/byond-client-compatibility-check@v3
|
||||
with:
|
||||
@@ -36,11 +36,11 @@ jobs:
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup_node
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup_bun
|
||||
- name: Restore BYOND from Cache
|
||||
uses: ./.github/actions/restore_or_install_byond
|
||||
- name: Compile All Maps
|
||||
run: |
|
||||
source $HOME/BYOND/byond/bin/byondsetup
|
||||
tools/build/build --ci dm -DCIBUILDING -DCITESTING -DALL_TEMPLATES
|
||||
tools/build/build.sh --ci dm -DCIBUILDING -DCITESTING -DALL_TEMPLATES
|
||||
|
||||
@@ -17,10 +17,8 @@ jobs:
|
||||
key: ${{ runner.os }}-spacemandmm-${{ hashFiles('dependencies.sh') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-spacemandmm-
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup_node
|
||||
with:
|
||||
restore-yarn-cache: true
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup_bun
|
||||
- name: Restore Bootstrap cache
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
@@ -103,4 +101,4 @@ jobs:
|
||||
run: bash tools/ci/check_misc.sh
|
||||
- name: Run TGUI Checks
|
||||
if: steps.linter-setup.conclusion == 'success' && !cancelled()
|
||||
run: tools/build/build --ci lint tgui-test
|
||||
run: tools/build/build.sh --ci lint tgui-test
|
||||
|
||||
@@ -15,8 +15,8 @@ jobs:
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Setup Node
|
||||
uses: ./.github/actions/setup_node
|
||||
- name: Setup Bun
|
||||
uses: ./.github/actions/setup_bun
|
||||
- name: Restore BYOND from Cache
|
||||
uses: ./.github/actions/restore_or_install_byond
|
||||
with:
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
- name: Compile
|
||||
run: |
|
||||
source $HOME/BYOND/byond/bin/byondsetup
|
||||
tools/build/build --ci dm -DCIBUILDING -DANSICOLORS -Werror -ITG0001 -I"loop_checks"
|
||||
tools/build/build.sh --ci dm -DCIBUILDING -DANSICOLORS -Werror -ITG0001 -I"loop_checks"
|
||||
- name: Setup variable for output path
|
||||
run: |
|
||||
OUTPUT_DIR="target"
|
||||
|
||||
Vendored
-8
@@ -1,13 +1,5 @@
|
||||
{
|
||||
"eslint.nodePath": "./tgui/.yarn/sdks",
|
||||
"eslint.workingDirectories": ["./tgui"],
|
||||
"prettier.prettierPath": "./tgui/.yarn/sdks/prettier/index.cjs",
|
||||
"typescript.tsdk": "./tgui/.yarn/sdks/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"search.exclude": {
|
||||
"**/.yarn": true,
|
||||
"**/.pnp.*": true
|
||||
},
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
|
||||
+5
-1
@@ -10,9 +10,12 @@ export BYOND_MINOR=1659
|
||||
#rust_g git tag
|
||||
export RUST_G_VERSION=3.9.0
|
||||
|
||||
#node version
|
||||
# node version
|
||||
export NODE_VERSION_LTS=22.11.0
|
||||
|
||||
# Bun version
|
||||
export BUN_VERSION=1.2.16
|
||||
|
||||
# SpacemanDMM git tag
|
||||
export SPACEMAN_DMM_VERSION=suite-1.10
|
||||
|
||||
@@ -30,3 +33,4 @@ export CUTTER_REPO=spacestation13/hypnagogic
|
||||
|
||||
#hypnagogic git tag
|
||||
export CUTTER_VERSION=v4.0.0
|
||||
|
||||
|
||||
@@ -1,25 +1,9 @@
|
||||
## NPM garbage
|
||||
node_modules
|
||||
*.log
|
||||
package-lock.json
|
||||
|
||||
## Yarn stuff
|
||||
/.pnp.*
|
||||
/.yarn/*
|
||||
!/.yarn/releases
|
||||
!/.yarn/plugins
|
||||
!/.yarn/sdks
|
||||
!/.yarn/versions
|
||||
!/.yarn/lock.yml
|
||||
|
||||
## Build artifacts
|
||||
/public/.tmp/**/*
|
||||
/public/**/*
|
||||
!/public/*.html
|
||||
!/public/ntos-error.min.css
|
||||
!/public/helpers.min.js
|
||||
/coverage
|
||||
|
||||
## Previously ignored locations that are kept to avoid confusing git
|
||||
## while transitioning to a new project structure.
|
||||
/packages/tgui/public/**
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"$schema": "https://swc.rs/schema.json",
|
||||
"jsc": {
|
||||
"loose": true,
|
||||
"parser": {
|
||||
"syntax": "typescript",
|
||||
"tsx": true
|
||||
},
|
||||
"transform": {
|
||||
"react": {
|
||||
"runtime": "automatic"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
-948
File diff suppressed because one or more lines are too long
Vendored
-32
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint/bin/eslint.js
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real eslint/bin/eslint.js your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`eslint/bin/eslint.js`));
|
||||
Vendored
-32
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real eslint your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`eslint`));
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint/use-at-your-own-risk
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real eslint/use-at-your-own-risk your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`eslint/use-at-your-own-risk`));
|
||||
Vendored
-14
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"name": "eslint",
|
||||
"version": "8.57.1-sdk",
|
||||
"main": "./lib/api.js",
|
||||
"type": "commonjs",
|
||||
"bin": {
|
||||
"eslint": "./bin/eslint.js"
|
||||
},
|
||||
"exports": {
|
||||
"./package.json": "./package.json",
|
||||
".": "./lib/api.js",
|
||||
"./use-at-your-own-risk": "./lib/unsupported-api.js"
|
||||
}
|
||||
}
|
||||
Vendored
-5
@@ -1,5 +0,0 @@
|
||||
# This file is automatically generated by @yarnpkg/sdks.
|
||||
# Manual changes might be lost!
|
||||
|
||||
integrations:
|
||||
- vscode
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier/bin/prettier.cjs
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real prettier/bin/prettier.cjs your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`prettier/bin/prettier.cjs`));
|
||||
Vendored
-32
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require prettier
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real prettier your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`prettier`));
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"name": "prettier",
|
||||
"version": "3.5.3-sdk",
|
||||
"main": "./index.cjs",
|
||||
"type": "commonjs",
|
||||
"bin": "./bin/prettier.cjs"
|
||||
}
|
||||
Vendored
-32
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/bin/tsc
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real typescript/bin/tsc your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsc`));
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/bin/tsserver
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real typescript/bin/tsserver your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`typescript/bin/tsserver`));
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsc.js
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real typescript/lib/tsc.js your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`typescript/lib/tsc.js`));
|
||||
-248
@@ -1,248 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsserver.js
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
const moduleWrapper = exports => {
|
||||
return wrapWithUserWrapper(moduleWrapperFn(exports));
|
||||
};
|
||||
|
||||
const moduleWrapperFn = tsserver => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
const {isAbsolute} = require(`path`);
|
||||
const pnpApi = require(`pnpapi`);
|
||||
|
||||
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const isPortal = str => str.startsWith("portal:/");
|
||||
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
|
||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
}));
|
||||
|
||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||
// doesn't understand. This layer makes sure to remove the protocol
|
||||
// before forwarding it to TS, and to add it back on all returned paths.
|
||||
|
||||
function toEditorPath(str) {
|
||||
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||
// We also take the opportunity to turn virtual paths into physical ones;
|
||||
// this makes it much easier to work with workspaces that list peer
|
||||
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||
// file instances instead of the real ones.
|
||||
//
|
||||
// We only do this to modules owned by the the dependency tree roots.
|
||||
// This avoids breaking the resolution when jumping inside a vendor
|
||||
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||
// errors on react).
|
||||
//
|
||||
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||
if (resolved) {
|
||||
const locator = pnpApi.findPackageLocator(resolved);
|
||||
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||
str = resolved;
|
||||
}
|
||||
}
|
||||
|
||||
str = normalize(str);
|
||||
|
||||
if (str.match(/\.zip\//)) {
|
||||
switch (hostInfo) {
|
||||
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||
// VSCode only adds it automatically for supported schemes,
|
||||
// so we have to do it manually for the `zip` scheme.
|
||||
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||
//
|
||||
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||
//
|
||||
// 2021-10-08: VSCode changed the format in 1.61.
|
||||
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-04-06: VSCode changed the format in 1.66.
|
||||
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-05-06: VSCode changed the format in 1.68
|
||||
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
case `vscode <1.61`: {
|
||||
str = `^zip:${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.66`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.68`: {
|
||||
str = `^/zip${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
// To make "go to definition" work,
|
||||
// We have to resolve the actual file system path from virtual path
|
||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||
case `coc-nvim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
} break;
|
||||
|
||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||
// We have to resolve the actual file system path from virtual path,
|
||||
// everything else is up to neovim
|
||||
case `neovim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
} break;
|
||||
|
||||
default: {
|
||||
str = `zip:${str}`;
|
||||
} break;
|
||||
}
|
||||
} else {
|
||||
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function fromEditorPath(str) {
|
||||
switch (hostInfo) {
|
||||
case `coc-nvim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^.*zipfile:\//, ``)
|
||||
: str.replace(/^.*zipfile:/, ``);
|
||||
} break;
|
||||
|
||||
case `neovim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
default: {
|
||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Force enable 'allowLocalPluginLoads'
|
||||
// TypeScript tries to resolve plugins using a path relative to itself
|
||||
// which doesn't work when using the global cache
|
||||
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||
// https://github.com/microsoft/vscode/issues/45856
|
||||
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
|
||||
// And here is the point where we hijack the VSCode <-> TS communications
|
||||
// by adding ourselves in the middle. We locate everything that looks
|
||||
// like an absolute path of ours and normalize it.
|
||||
|
||||
const Session = tsserver.server.Session;
|
||||
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||
let hostInfo = `unknown`;
|
||||
|
||||
Object.assign(Session.prototype, {
|
||||
onMessage(/** @type {string | object} */ message) {
|
||||
const isStringMessage = typeof message === 'string';
|
||||
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||
|
||||
if (
|
||||
parsedMessage != null &&
|
||||
typeof parsedMessage === `object` &&
|
||||
parsedMessage.arguments &&
|
||||
typeof parsedMessage.arguments.hostInfo === `string`
|
||||
) {
|
||||
hostInfo = parsedMessage.arguments.hostInfo;
|
||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []).map(Number)
|
||||
|
||||
if (major === 1) {
|
||||
if (minor < 61) {
|
||||
hostInfo += ` <1.61`;
|
||||
} else if (minor < 66) {
|
||||
hostInfo += ` <1.66`;
|
||||
} else if (minor < 68) {
|
||||
hostInfo += ` <1.68`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||
});
|
||||
|
||||
return originalOnMessage.call(
|
||||
this,
|
||||
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||
);
|
||||
},
|
||||
|
||||
send(/** @type {any} */ msg) {
|
||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})));
|
||||
}
|
||||
});
|
||||
|
||||
return tsserver;
|
||||
};
|
||||
|
||||
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
|
||||
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
|
||||
// Ref https://github.com/microsoft/TypeScript/pull/55326
|
||||
if (major > 5 || (major === 5 && minor >= 5)) {
|
||||
moduleWrapper(absRequire(`typescript`));
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/tsserver.js your application uses
|
||||
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserver.js`));
|
||||
-248
@@ -1,248 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
const moduleWrapper = exports => {
|
||||
return wrapWithUserWrapper(moduleWrapperFn(exports));
|
||||
};
|
||||
|
||||
const moduleWrapperFn = tsserver => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
const {isAbsolute} = require(`path`);
|
||||
const pnpApi = require(`pnpapi`);
|
||||
|
||||
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const isPortal = str => str.startsWith("portal:/");
|
||||
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
|
||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
}));
|
||||
|
||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||
// doesn't understand. This layer makes sure to remove the protocol
|
||||
// before forwarding it to TS, and to add it back on all returned paths.
|
||||
|
||||
function toEditorPath(str) {
|
||||
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||
// We also take the opportunity to turn virtual paths into physical ones;
|
||||
// this makes it much easier to work with workspaces that list peer
|
||||
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||
// file instances instead of the real ones.
|
||||
//
|
||||
// We only do this to modules owned by the the dependency tree roots.
|
||||
// This avoids breaking the resolution when jumping inside a vendor
|
||||
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||
// errors on react).
|
||||
//
|
||||
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||
if (resolved) {
|
||||
const locator = pnpApi.findPackageLocator(resolved);
|
||||
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||
str = resolved;
|
||||
}
|
||||
}
|
||||
|
||||
str = normalize(str);
|
||||
|
||||
if (str.match(/\.zip\//)) {
|
||||
switch (hostInfo) {
|
||||
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||
// VSCode only adds it automatically for supported schemes,
|
||||
// so we have to do it manually for the `zip` scheme.
|
||||
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||
//
|
||||
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||
//
|
||||
// 2021-10-08: VSCode changed the format in 1.61.
|
||||
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-04-06: VSCode changed the format in 1.66.
|
||||
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-05-06: VSCode changed the format in 1.68
|
||||
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
case `vscode <1.61`: {
|
||||
str = `^zip:${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.66`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.68`: {
|
||||
str = `^/zip${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
// To make "go to definition" work,
|
||||
// We have to resolve the actual file system path from virtual path
|
||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||
case `coc-nvim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
} break;
|
||||
|
||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||
// We have to resolve the actual file system path from virtual path,
|
||||
// everything else is up to neovim
|
||||
case `neovim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
} break;
|
||||
|
||||
default: {
|
||||
str = `zip:${str}`;
|
||||
} break;
|
||||
}
|
||||
} else {
|
||||
str = str.replace(/^\/?/, process.platform === `win32` ? `` : `/`);
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function fromEditorPath(str) {
|
||||
switch (hostInfo) {
|
||||
case `coc-nvim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^.*zipfile:\//, ``)
|
||||
: str.replace(/^.*zipfile:/, ``);
|
||||
} break;
|
||||
|
||||
case `neovim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
default: {
|
||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Force enable 'allowLocalPluginLoads'
|
||||
// TypeScript tries to resolve plugins using a path relative to itself
|
||||
// which doesn't work when using the global cache
|
||||
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||
// https://github.com/microsoft/vscode/issues/45856
|
||||
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
|
||||
// And here is the point where we hijack the VSCode <-> TS communications
|
||||
// by adding ourselves in the middle. We locate everything that looks
|
||||
// like an absolute path of ours and normalize it.
|
||||
|
||||
const Session = tsserver.server.Session;
|
||||
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||
let hostInfo = `unknown`;
|
||||
|
||||
Object.assign(Session.prototype, {
|
||||
onMessage(/** @type {string | object} */ message) {
|
||||
const isStringMessage = typeof message === 'string';
|
||||
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||
|
||||
if (
|
||||
parsedMessage != null &&
|
||||
typeof parsedMessage === `object` &&
|
||||
parsedMessage.arguments &&
|
||||
typeof parsedMessage.arguments.hostInfo === `string`
|
||||
) {
|
||||
hostInfo = parsedMessage.arguments.hostInfo;
|
||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []).map(Number)
|
||||
|
||||
if (major === 1) {
|
||||
if (minor < 61) {
|
||||
hostInfo += ` <1.61`;
|
||||
} else if (minor < 66) {
|
||||
hostInfo += ` <1.66`;
|
||||
} else if (minor < 68) {
|
||||
hostInfo += ` <1.68`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||
});
|
||||
|
||||
return originalOnMessage.call(
|
||||
this,
|
||||
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||
);
|
||||
},
|
||||
|
||||
send(/** @type {any} */ msg) {
|
||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})));
|
||||
}
|
||||
});
|
||||
|
||||
return tsserver;
|
||||
};
|
||||
|
||||
const [major, minor] = absRequire(`typescript/package.json`).version.split(`.`, 2).map(value => parseInt(value, 10));
|
||||
// In TypeScript@>=5.5 the tsserver uses the public TypeScript API so that needs to be patched as well.
|
||||
// Ref https://github.com/microsoft/TypeScript/pull/55326
|
||||
if (major > 5 || (major === 5 && minor >= 5)) {
|
||||
moduleWrapper(absRequire(`typescript`));
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/tsserverlibrary.js your application uses
|
||||
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserverlibrary.js`));
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire, register} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
const {pathToFileURL} = require(`url`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absUserWrapperPath = resolve(__dirname, `./sdk.user.cjs`);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const absPnpLoaderPath = resolve(absPnpApiPath, `../.pnp.loader.mjs`);
|
||||
const isPnpLoaderEnabled = existsSync(absPnpLoaderPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript
|
||||
require(absPnpApiPath).setup();
|
||||
if (isPnpLoaderEnabled && register) {
|
||||
register(pathToFileURL(absPnpLoaderPath));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const wrapWithUserWrapper = existsSync(absUserWrapperPath)
|
||||
? exports => absRequire(absUserWrapperPath)(exports)
|
||||
: exports => exports;
|
||||
|
||||
// Defer to the real typescript your application uses
|
||||
module.exports = wrapWithUserWrapper(absRequire(`typescript`));
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
{
|
||||
"name": "typescript",
|
||||
"version": "5.8.3-sdk",
|
||||
"main": "./lib/typescript.js",
|
||||
"type": "commonjs",
|
||||
"bin": {
|
||||
"tsc": "./bin/tsc",
|
||||
"tsserver": "./bin/tsserver"
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
compressionLevel: 0
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
enableScripts: false
|
||||
|
||||
logFilters:
|
||||
- code: YN0004
|
||||
level: discard
|
||||
- code: YN0062
|
||||
level: discard
|
||||
- code: YN0002
|
||||
level: discard
|
||||
pattern: css-loader@*
|
||||
|
||||
preferInteractive: true
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.9.1.cjs
|
||||
+1679
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,2 @@
|
||||
[test]
|
||||
preload = "./happydom.ts"
|
||||
@@ -0,0 +1,3 @@
|
||||
import { GlobalRegistrator } from '@happy-dom/global-registrator';
|
||||
|
||||
GlobalRegistrator.register();
|
||||
+19
-16
@@ -2,48 +2,51 @@
|
||||
"private": true,
|
||||
"name": "tgui-workspace",
|
||||
"version": "6.0.0",
|
||||
"packageManager": "yarn@4.9.1",
|
||||
"packageManager": "bun@1.2.16",
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"scripts": {
|
||||
"tgui:analyze": "rspack --analyze",
|
||||
"tgui:bench": "rspack --env TGUI_BENCH=1 && node packages/tgui-bench/index.js",
|
||||
"tgui:build": "rspack build",
|
||||
"tgui:dev": "node --experimental-modules packages/tgui-dev-server/index.js",
|
||||
"tgui:analyze": "webpack --analyze",
|
||||
"tgui:bench": "webpack --env TGUI_BENCH=1 && bun packages/tgui-bench/index.js",
|
||||
"tgui:build": "webpack",
|
||||
"tgui:dev": "bun packages/tgui-dev-server/index.js",
|
||||
"tgui:lint": "eslint packages --ext .js,.cjs,.ts,.tsx",
|
||||
"tgui:prettier": "prettier --check .",
|
||||
"tgui:sonar": "eslint packages -c .eslintrc-sonar.yml",
|
||||
"tgui:test": "vitest",
|
||||
"tgui:test-simple": "CI=true vitest",
|
||||
"tgui:test-ci": "CI=true vitest",
|
||||
"tgui:test": "bun test",
|
||||
"tgui:tsc": "tsc",
|
||||
"tgui:prettier-fix": "prettier --write .",
|
||||
"tgui:eslint-fix": "eslint --fix packages --ext .js,.cjs,.ts,.jsx,.tsx"
|
||||
},
|
||||
"dependencies": {
|
||||
"@rspack/cli": "^1.3.7",
|
||||
"@rspack/core": "^1.3.7",
|
||||
"@swc/core": "^1.12.1",
|
||||
"@typescript-eslint/parser": "^8.13.0",
|
||||
"@typescript-eslint/utils": "^8.13.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-sonarjs": "^0.25.0",
|
||||
"eslint-plugin-unused-imports": "^3.1.0",
|
||||
"jsdom": "^26.0.0",
|
||||
"mini-css-extract-plugin": "^2.9.2",
|
||||
"prettier": "^3.5.3",
|
||||
"sass": "^1.80.6",
|
||||
"sass-embedded": "^1.85.1",
|
||||
"sass-loader": "^16.0.3",
|
||||
"typescript": "^5.8.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"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^22.14.0",
|
||||
"@happy-dom/global-registrator": "^17.4.7",
|
||||
"@types/bun": "^1.2.14",
|
||||
"@types/react": "^19.1.5",
|
||||
"@types/react-dom": "^19.1.5",
|
||||
"@types/webpack-env": "^1.18.8",
|
||||
"@types/wicg-file-system-access": "^2023.10.6",
|
||||
"vitest": "^3.1.1"
|
||||
"@types/wicg-file-system-access": "^2023.10.6"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it } from 'vitest';
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
|
||||
import { range, zip } from './collections';
|
||||
|
||||
@@ -6,13 +6,13 @@ import { range, zip } from './collections';
|
||||
const _zip1: [string, number] = zip(['a'], [1])[0];
|
||||
|
||||
describe('range', () => {
|
||||
it('range(0, 5)', ({ expect }) => {
|
||||
it('range(0, 5)', () => {
|
||||
expect(range(0, 5)).toEqual([0, 1, 2, 3, 4]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('zip', () => {
|
||||
it("zip(['a', 'b', 'c'], [1, 2, 3, 4])", ({ expect }) => {
|
||||
it("zip(['a', 'b', 'c'], [1, 2, 3, 4])", () => {
|
||||
expect(zip(['a', 'b', 'c'], [1, 2, 3, 4])).toEqual([
|
||||
['a', 1],
|
||||
['b', 2],
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "common",
|
||||
"version": "4.3.1",
|
||||
"devDependencies": {
|
||||
"vitest": "^3.1.1"
|
||||
}
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it } from 'vitest';
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
|
||||
import {
|
||||
Action,
|
||||
@@ -32,12 +32,12 @@ const increment = createAction('INCREMENT');
|
||||
const decrement = createAction('DECREMENT');
|
||||
|
||||
describe('Redux implementation tests', () => {
|
||||
it('createStore works', ({ expect }) => {
|
||||
it('createStore works', () => {
|
||||
const store = createStore(counterReducer);
|
||||
expect(store.getState()).toBe(0);
|
||||
});
|
||||
|
||||
it('createStore with applyMiddleware works', ({ expect }) => {
|
||||
it('createStore with applyMiddleware works', () => {
|
||||
const store = createStore(
|
||||
counterReducer,
|
||||
applyMiddleware(loggingMiddleware),
|
||||
@@ -45,7 +45,7 @@ describe('Redux implementation tests', () => {
|
||||
expect(store.getState()).toBe(0);
|
||||
});
|
||||
|
||||
it('dispatch works', ({ expect }) => {
|
||||
it('dispatch works', () => {
|
||||
const store = createStore(counterReducer);
|
||||
store.dispatch(increment());
|
||||
expect(store.getState()).toBe(1);
|
||||
@@ -53,7 +53,7 @@ describe('Redux implementation tests', () => {
|
||||
expect(store.getState()).toBe(0);
|
||||
});
|
||||
|
||||
it('combineReducers works', ({ expect }) => {
|
||||
it('combineReducers works', () => {
|
||||
const rootReducer = combineReducers({
|
||||
counter: counterReducer,
|
||||
});
|
||||
@@ -61,7 +61,7 @@ describe('Redux implementation tests', () => {
|
||||
expect(store.getState()).toEqual({ counter: 0 });
|
||||
});
|
||||
|
||||
it('createAction works', ({ expect }) => {
|
||||
it('createAction works', () => {
|
||||
const incrementAction = increment();
|
||||
expect(incrementAction).toEqual({ type: 'INCREMENT' });
|
||||
const decrementAction = decrement();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "tgui-bench",
|
||||
"version": "6.0.0",
|
||||
"dependencies": {
|
||||
@@ -13,8 +12,5 @@
|
||||
"tgui": "workspace:*",
|
||||
"tgui-core": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.2"
|
||||
}
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -1,27 +0,0 @@
|
||||
import { backendUpdate, setGlobalStore } from 'tgui/backend';
|
||||
import { DisposalUnit } from 'tgui/interfaces/DisposalUnit';
|
||||
import { render } from 'tgui/renderer';
|
||||
import { configureStore } from 'tgui/store';
|
||||
|
||||
const store = configureStore({ sideEffects: false });
|
||||
|
||||
export const data = JSON.stringify({
|
||||
flush: 0,
|
||||
full_pressure: 1,
|
||||
pressure_charging: 0,
|
||||
panel_open: 0,
|
||||
per: 1,
|
||||
isai: 0,
|
||||
});
|
||||
|
||||
export function Default() {
|
||||
setGlobalStore(store);
|
||||
|
||||
store.dispatch(
|
||||
backendUpdate({
|
||||
data: Byond.parseJson(data),
|
||||
}),
|
||||
);
|
||||
|
||||
return render(<DisposalUnit />);
|
||||
}
|
||||
@@ -78,7 +78,7 @@ export function retrace(stack) {
|
||||
return ` at ${methodName}`;
|
||||
}
|
||||
const compactPath = file
|
||||
.replace(/^rspack:\/\/\/?/, './')
|
||||
.replace(/^webpack:\/\/\/?/, './')
|
||||
.replace(/.*node_modules\//, '');
|
||||
return ` at ${methodName} (${compactPath}:${lineNumber})`;
|
||||
})
|
||||
|
||||
@@ -15,6 +15,9 @@ import { regQuery } from './winreg.js';
|
||||
|
||||
const logger = createLogger('reloader');
|
||||
|
||||
// Basic glob pattern for bundle files
|
||||
const bundleGlob = '*.{bundle,chunk,hot-update}.*';
|
||||
|
||||
const HOME = os.homedir();
|
||||
const SEARCH_LOCATIONS = [
|
||||
// Custom location
|
||||
@@ -73,7 +76,7 @@ export async function reloadByondCache(bundleDir) {
|
||||
return;
|
||||
}
|
||||
// Find tmp folders in cache
|
||||
const cacheDirs = resolveGlob(cacheRoot, './tmp*');
|
||||
const cacheDirs = await resolveGlob(cacheRoot, 'tmp*');
|
||||
if (cacheDirs.length === 0) {
|
||||
logger.log('found no tmp folder in cache');
|
||||
return;
|
||||
@@ -85,16 +88,10 @@ export async function reloadByondCache(bundleDir) {
|
||||
|
||||
const dssPromise = DreamSeeker.getInstancesByPids(pids);
|
||||
// Copy assets
|
||||
const assets = await resolveGlob(
|
||||
bundleDir,
|
||||
'./*.+(bundle|chunk|hot-update).*',
|
||||
);
|
||||
const assets = await resolveGlob(bundleDir, bundleGlob);
|
||||
for (let cacheDir of cacheDirs) {
|
||||
// Clear garbage
|
||||
const garbage = await resolveGlob(
|
||||
cacheDir,
|
||||
'./*.+(bundle|chunk|hot-update).*',
|
||||
);
|
||||
const garbage = await resolveGlob(cacheDir, bundleGlob);
|
||||
try {
|
||||
// Plant a dummy browser window file, we'll be using this to avoid world topic. For byond 515-516.
|
||||
fs.closeSync(fs.openSync(cacheDir + '/dummy.htm', 'w'));
|
||||
|
||||
@@ -4,25 +4,41 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import fs, { globSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
|
||||
import { Glob } from 'bun';
|
||||
|
||||
export const resolvePath = path.resolve;
|
||||
|
||||
/** Combines path.resolve with glob patterns. */
|
||||
export function resolveGlob(...sections) {
|
||||
/** @type {string[]} */
|
||||
const unsafePaths = globSync(path.resolve(...sections));
|
||||
|
||||
/** @type {string[]} */
|
||||
const safePaths = [];
|
||||
|
||||
for (let path of unsafePaths) {
|
||||
/**
|
||||
* Combines path.resolve with glob patterns.
|
||||
*
|
||||
* @param {string} directory - The directory to resolve the glob pattern against.
|
||||
* @param {string} [pattern='*'] - The glob pattern to match files against. Defaults to '*', which matches all files in the directory.
|
||||
* @returns {Promise<string[]>} - A promise that resolves to an array of matched file paths.
|
||||
*/
|
||||
export async function resolveGlob(directory, pattern = '*') {
|
||||
// If no pattern is supplied, just return the directory if it exists
|
||||
if (pattern === '*') {
|
||||
try {
|
||||
fs.statSync(path);
|
||||
safePaths.push(path);
|
||||
} catch {}
|
||||
const stat = await Bun.file(directory).stat();
|
||||
if (stat?.isDirectory()) {
|
||||
return [directory];
|
||||
}
|
||||
} catch (e) {
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
return safePaths;
|
||||
// Otherwise, use glob logic
|
||||
const glob = new Glob(pattern);
|
||||
/** @type string[] */
|
||||
const results = [];
|
||||
|
||||
for await (const match of glob.scan({ onlyFiles: false, cwd: directory })) {
|
||||
results.push(path.resolve(directory, match));
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
@@ -4,49 +4,41 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import { createRequire } from 'node:module';
|
||||
import fs from 'fs';
|
||||
import { createRequire } from 'module';
|
||||
import { dirname } from 'path';
|
||||
|
||||
import { loadSourceMaps, setupLink } from './link/server.js';
|
||||
import { createLogger } from './logging.js';
|
||||
import { reloadByondCache } from './reloader.js';
|
||||
import { resolveGlob } from './util.js';
|
||||
|
||||
const logger = createLogger('rspack');
|
||||
const logger = createLogger('webpack');
|
||||
|
||||
/**
|
||||
* @param {any} config
|
||||
* @return {RspackCompiler}
|
||||
* @return {WebpackCompiler}
|
||||
*/
|
||||
export async function createCompiler(options) {
|
||||
const compiler = new RspackCompiler();
|
||||
export const createCompiler = async (options) => {
|
||||
const compiler = new WebpackCompiler();
|
||||
await compiler.setup(options);
|
||||
|
||||
return compiler;
|
||||
}
|
||||
};
|
||||
|
||||
class RspackCompiler {
|
||||
class WebpackCompiler {
|
||||
async setup(options) {
|
||||
// Create a require context that is relative to project root
|
||||
// and retrieve all necessary dependencies.
|
||||
const requireFromRoot = createRequire(import.meta.dirname + '/../../..');
|
||||
/** @type {typeof import('@rspack/core')} */
|
||||
const rspack = await requireFromRoot('@rspack/core');
|
||||
|
||||
const createConfig = await requireFromRoot('./rspack.config.cjs');
|
||||
const createDevConfig = await requireFromRoot('./rspack.config-dev.cjs');
|
||||
|
||||
const requireFromRoot = createRequire(dirname(import.meta.url) + '/../..');
|
||||
const webpack = await requireFromRoot('webpack');
|
||||
const createConfig = await requireFromRoot('./webpack.config.js');
|
||||
const config = createConfig({}, options);
|
||||
const devConfig = createDevConfig({}, options);
|
||||
|
||||
const mergedConfig = { ...config, ...devConfig };
|
||||
|
||||
// Inject the HMR plugin into the config if we're using it
|
||||
if (options.hot) {
|
||||
mergedConfig.plugins.push(new rspack.HotModuleReplacementPlugin());
|
||||
config.plugins.push(new webpack.HotModuleReplacementPlugin());
|
||||
}
|
||||
this.rspack = rspack;
|
||||
this.config = mergedConfig;
|
||||
this.webpack = webpack;
|
||||
this.config = config;
|
||||
this.bundleDir = config.output.path;
|
||||
}
|
||||
|
||||
@@ -55,7 +47,7 @@ class RspackCompiler {
|
||||
// Setup link
|
||||
const link = setupLink();
|
||||
// Instantiate the compiler
|
||||
const compiler = this.rspack.rspack(this.config);
|
||||
const compiler = this.webpack.webpack(this.config);
|
||||
// Clear garbage before compiling
|
||||
compiler.hooks.watchRun.tapPromise('tgui-dev-server', async () => {
|
||||
const files = await resolveGlob(this.bundleDir, './*.hot-update.*');
|
||||
@@ -84,7 +76,7 @@ class RspackCompiler {
|
||||
return;
|
||||
}
|
||||
stats
|
||||
?.toString(this.config.devServer.stats)
|
||||
.toString(this.config.devServer.stats)
|
||||
.split('\n')
|
||||
.forEach((line) => logger.log(line));
|
||||
});
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "tgui-panel",
|
||||
"version": "6.0.0",
|
||||
"dependencies": {
|
||||
@@ -11,8 +10,5 @@
|
||||
"tgui-core": "^4.0.2",
|
||||
"tgui-dev-server": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.2"
|
||||
}
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { beforeEach, describe, it } from 'vitest';
|
||||
import { beforeEach, describe, expect, it } from 'bun:test';
|
||||
|
||||
import { ChannelIterator } from './ChannelIterator';
|
||||
|
||||
@@ -9,7 +9,7 @@ describe('ChannelIterator', () => {
|
||||
channelIterator = new ChannelIterator();
|
||||
});
|
||||
|
||||
it('should cycle through channels properly', ({ expect }) => {
|
||||
it('should cycle through channels properly', () => {
|
||||
expect(channelIterator.current()).toBe('Say');
|
||||
expect(channelIterator.next()).toBe('Radio');
|
||||
expect(channelIterator.next()).toBe('Me');
|
||||
@@ -23,34 +23,32 @@ describe('ChannelIterator', () => {
|
||||
expect(channelIterator.next()).toBe('Say'); // Admin is blacklisted so it should be skipped
|
||||
});
|
||||
|
||||
it('should set a channel properly', ({ expect }) => {
|
||||
it('should set a channel properly', () => {
|
||||
channelIterator.set('OOC');
|
||||
expect(channelIterator.current()).toBe('OOC');
|
||||
});
|
||||
|
||||
it('should return true when current channel is "Say"', ({ expect }) => {
|
||||
it('should return true when current channel is "Say"', () => {
|
||||
channelIterator.set('Say');
|
||||
expect(channelIterator.isSay()).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when current channel is not "Say"', ({ expect }) => {
|
||||
it('should return false when current channel is not "Say"', () => {
|
||||
channelIterator.set('Radio');
|
||||
expect(channelIterator.isSay()).toBe(false);
|
||||
});
|
||||
|
||||
it('should return true when current channel is visible', ({ expect }) => {
|
||||
it('should return true when current channel is visible', () => {
|
||||
channelIterator.set('Say');
|
||||
expect(channelIterator.isVisible()).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false when current channel is not visible', ({
|
||||
expect,
|
||||
}) => {
|
||||
it('should return false when current channel is not visible', () => {
|
||||
channelIterator.set('OOC');
|
||||
expect(channelIterator.isVisible()).toBe(false);
|
||||
});
|
||||
|
||||
it('should not leak a message from a blacklisted channel', ({ expect }) => {
|
||||
it('should not leak a message from a blacklisted channel', () => {
|
||||
channelIterator.set('Admin');
|
||||
expect(channelIterator.next()).toBe('Admin');
|
||||
});
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { beforeEach, describe, it } from 'vitest';
|
||||
import { beforeEach, describe, expect, it } from 'bun:test';
|
||||
|
||||
import { ChatHistory } from './ChatHistory';
|
||||
|
||||
@@ -9,12 +9,12 @@ describe('ChatHistory', () => {
|
||||
chatHistory = new ChatHistory();
|
||||
});
|
||||
|
||||
it('should add a message to the history', ({ expect }) => {
|
||||
it('should add a message to the history', () => {
|
||||
chatHistory.add('Hello');
|
||||
expect(chatHistory.getOlderMessage()).toEqual('Hello');
|
||||
});
|
||||
|
||||
it('should retrieve older and newer messages', ({ expect }) => {
|
||||
it('should retrieve older and newer messages', () => {
|
||||
chatHistory.add('Hello');
|
||||
chatHistory.add('World');
|
||||
expect(chatHistory.getOlderMessage()).toEqual('World');
|
||||
@@ -24,7 +24,7 @@ describe('ChatHistory', () => {
|
||||
expect(chatHistory.getOlderMessage()).toEqual('World');
|
||||
});
|
||||
|
||||
it('should limit the history to 5 messages', ({ expect }) => {
|
||||
it('should limit the history to 5 messages', () => {
|
||||
for (let i = 1; i <= 6; i++) {
|
||||
chatHistory.add(`Message ${i}`);
|
||||
}
|
||||
@@ -36,13 +36,13 @@ describe('ChatHistory', () => {
|
||||
expect(chatHistory.getOlderMessage()).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle temp message correctly', ({ expect }) => {
|
||||
it('should handle temp message correctly', () => {
|
||||
chatHistory.saveTemp('Temp message');
|
||||
expect(chatHistory.getTemp()).toEqual('Temp message');
|
||||
expect(chatHistory.getTemp()).toBeNull();
|
||||
});
|
||||
|
||||
it('should reset correctly', ({ expect }) => {
|
||||
it('should reset correctly', () => {
|
||||
chatHistory.add('Hello');
|
||||
chatHistory.getOlderMessage();
|
||||
chatHistory.reset();
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "tgui-say",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
@@ -9,9 +8,5 @@
|
||||
"tgui": "workspace:*",
|
||||
"tgui-core": "^4.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"vitest": "^3.1.1"
|
||||
}
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { describe, it } from 'vitest';
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
|
||||
import {
|
||||
getGasColor,
|
||||
@@ -8,41 +8,41 @@ import {
|
||||
} from './constants';
|
||||
|
||||
describe('gas helper functions', () => {
|
||||
it('should get the proper gas label', ({ expect }) => {
|
||||
it('should get the proper gas label', () => {
|
||||
const gasId = 'antinoblium';
|
||||
const gasLabel = getGasLabel(gasId);
|
||||
expect(gasLabel).toBe('Anti-Noblium');
|
||||
});
|
||||
|
||||
it('should get the proper gas label with a fallback', ({ expect }) => {
|
||||
it('should get the proper gas label with a fallback', () => {
|
||||
const gasId = 'nonexistent';
|
||||
const gasLabel = getGasLabel(gasId, 'fallback');
|
||||
|
||||
expect(gasLabel).toBe('fallback');
|
||||
});
|
||||
|
||||
it('should return none if no gas and no fallback is found', ({ expect }) => {
|
||||
it('should return none if no gas and no fallback is found', () => {
|
||||
const gasId = 'nonexistent';
|
||||
const gasLabel = getGasLabel(gasId);
|
||||
|
||||
expect(gasLabel).toBe('None');
|
||||
});
|
||||
|
||||
it('should get the proper gas color', ({ expect }) => {
|
||||
it('should get the proper gas color', () => {
|
||||
const gasId = 'antinoblium';
|
||||
const gasColor = getGasColor(gasId);
|
||||
|
||||
expect(gasColor).toBe('maroon');
|
||||
});
|
||||
|
||||
it('should return a string if no gas is found', ({ expect }) => {
|
||||
it('should return a string if no gas is found', () => {
|
||||
const gasId = 'nonexistent';
|
||||
const gasColor = getGasColor(gasId);
|
||||
|
||||
expect(gasColor).toBe('black');
|
||||
});
|
||||
|
||||
it('should return the gas object if found', ({ expect }) => {
|
||||
it('should return the gas object if found', () => {
|
||||
const gasId = 'antinoblium';
|
||||
const gas = getGasFromId(gasId);
|
||||
|
||||
@@ -55,14 +55,14 @@ describe('gas helper functions', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should return undefined if no gas is found', ({ expect }) => {
|
||||
it('should return undefined if no gas is found', () => {
|
||||
const gasId = 'nonexistent';
|
||||
const gas = getGasFromId(gasId);
|
||||
|
||||
expect(gas).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return the gas using a path', ({ expect }) => {
|
||||
it('should return the gas using a path', () => {
|
||||
const gasPath = '/datum/gas/antinoblium';
|
||||
const gas = getGasFromPath(gasPath);
|
||||
|
||||
|
||||
@@ -3,13 +3,15 @@
|
||||
* @copyright 2020 Aleksej Komarov
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
import { useState } from 'react';
|
||||
import { Section, Stack, Tabs } from 'tgui-core/components';
|
||||
|
||||
import { Pane, Window } from '../layouts';
|
||||
|
||||
const r = require.context('../stories', false, /\.stories\.tsx$/);
|
||||
const r = import.meta.webpackContext('../', {
|
||||
recursive: false,
|
||||
include: /\.stories\.tsx$/,
|
||||
});
|
||||
|
||||
/**
|
||||
* @returns {{
|
||||
@@ -25,10 +27,13 @@ function getStories() {
|
||||
|
||||
export function KitchenSink(props) {
|
||||
const { panel } = props;
|
||||
|
||||
const [pageIndex, setPageIndex] = useState(0);
|
||||
|
||||
const stories = getStories();
|
||||
if (stories.length === 0) {
|
||||
return <div>Loading stories...</div>;
|
||||
}
|
||||
|
||||
const story = stories[pageIndex];
|
||||
const Layout = panel ? Pane : Window;
|
||||
|
||||
|
||||
@@ -1,4 +1,12 @@
|
||||
import { afterEach, beforeEach, describe, it, vi } from 'vitest';
|
||||
import {
|
||||
afterEach,
|
||||
beforeEach,
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
mock,
|
||||
spyOn,
|
||||
} from 'bun:test';
|
||||
|
||||
import { captureExternalLinks } from './links';
|
||||
|
||||
@@ -7,7 +15,7 @@ describe('captureExternalLinks', () => {
|
||||
let clickHandler;
|
||||
|
||||
beforeEach(() => {
|
||||
addEventListenerSpy = vi.spyOn(document, 'addEventListener');
|
||||
addEventListenerSpy = spyOn(document, 'addEventListener');
|
||||
captureExternalLinks();
|
||||
clickHandler = addEventListenerSpy.mock.calls[0][1];
|
||||
});
|
||||
@@ -16,26 +24,24 @@ describe('captureExternalLinks', () => {
|
||||
addEventListenerSpy.mockRestore();
|
||||
});
|
||||
|
||||
it('should subscribe to document clicks', ({ expect }) => {
|
||||
it('should subscribe to document clicks', () => {
|
||||
expect(addEventListenerSpy).toHaveBeenCalledWith(
|
||||
'click',
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it('should preventDefault and send a message when a non-BYOND external link is clicked', ({
|
||||
expect,
|
||||
}) => {
|
||||
it('should preventDefault and send a message when a non-BYOND external link is clicked', () => {
|
||||
const externalLink = {
|
||||
tagName: 'A',
|
||||
getAttribute: () => 'https://example.com',
|
||||
parentElement: document.body,
|
||||
};
|
||||
const byond = { sendMessage: vi.fn() };
|
||||
const byond = { sendMessage: mock() };
|
||||
// @ts-ignore
|
||||
global.Byond = byond;
|
||||
|
||||
const evt = { target: externalLink, preventDefault: vi.fn() };
|
||||
const evt = { target: externalLink, preventDefault: mock() };
|
||||
clickHandler(evt);
|
||||
|
||||
expect(evt.preventDefault).toHaveBeenCalled();
|
||||
@@ -45,36 +51,34 @@ describe('captureExternalLinks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should not preventDefault or send a message when a BYOND link is clicked', ({
|
||||
expect,
|
||||
}) => {
|
||||
it('should not preventDefault or send a message when a BYOND link is clicked', () => {
|
||||
const byondLink = {
|
||||
tagName: 'A',
|
||||
getAttribute: () => 'byond://server-address',
|
||||
parentElement: document.body,
|
||||
};
|
||||
const byond = { sendMessage: vi.fn() };
|
||||
const byond = { sendMessage: mock() };
|
||||
// @ts-ignore
|
||||
global.Byond = byond;
|
||||
|
||||
const evt = { target: byondLink, preventDefault: vi.fn() };
|
||||
const evt = { target: byondLink, preventDefault: mock() };
|
||||
clickHandler(evt);
|
||||
|
||||
expect(evt.preventDefault).not.toHaveBeenCalled();
|
||||
expect(byond.sendMessage).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should add https:// to www links', ({ expect }) => {
|
||||
it('should add https:// to www links', () => {
|
||||
const wwwLink = {
|
||||
tagName: 'A',
|
||||
getAttribute: () => 'www.example.com',
|
||||
parentElement: document.body,
|
||||
};
|
||||
const byond = { sendMessage: vi.fn() };
|
||||
const byond = { sendMessage: mock() };
|
||||
// @ts-ignore
|
||||
global.Byond = byond;
|
||||
|
||||
const evt = { target: wwwLink, preventDefault: vi.fn() };
|
||||
const evt = { target: wwwLink, preventDefault: mock() };
|
||||
clickHandler(evt);
|
||||
|
||||
expect(byond.sendMessage).toHaveBeenCalledWith({
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "tgui",
|
||||
"version": "6.0.0",
|
||||
"dependencies": {
|
||||
@@ -7,7 +6,6 @@
|
||||
"dateformat": "^5.0.3",
|
||||
"dompurify": "^3.2.5",
|
||||
"highlight.js": "^11.11.1",
|
||||
"jest": "^29.7.0",
|
||||
"jotai": "^2.12.4",
|
||||
"js-yaml": "^4.1.0",
|
||||
"marked": "^15.0.11",
|
||||
@@ -18,9 +16,5 @@
|
||||
"tgui-core": "^4.0.2",
|
||||
"tgui-dev-server": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/react": "^19.1.0",
|
||||
"@types/react-dom": "^19.1.2",
|
||||
"vitest": "^3.1.1"
|
||||
}
|
||||
"private": true
|
||||
}
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
import { describe, it } from 'vitest';
|
||||
|
||||
import { sanitizeText } from './sanitize';
|
||||
|
||||
describe('sanitizeText', () => {
|
||||
it('should sanitize basic HTML input', ({ expect }) => {
|
||||
const input = '<b>Hello, world!</b><script>alert("hack")</script>';
|
||||
const expected = '<b>Hello, world!</b>';
|
||||
const result = sanitizeText(input);
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
|
||||
it('should sanitize advanced HTML input when advHtml flag is true', ({
|
||||
expect,
|
||||
}) => {
|
||||
const input =
|
||||
'<b>Hello, world!</b><iframe src="https://example.com"></iframe>';
|
||||
const expected = '<b>Hello, world!</b>';
|
||||
const result = sanitizeText(input, true);
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
|
||||
it('should allow specific HTML tags when tags array is provided', ({
|
||||
expect,
|
||||
}) => {
|
||||
const input = '<b>Hello, world!</b><span>Goodbye, world!</span>';
|
||||
const tags = ['b'];
|
||||
const expected = '<b>Hello, world!</b>Goodbye, world!';
|
||||
const result = sanitizeText(input, false, tags);
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
|
||||
it('should allow advanced HTML tags when advTags array is provided and advHtml flag is true', ({
|
||||
expect,
|
||||
}) => {
|
||||
const input =
|
||||
'<b>Hello, world!</b><iframe src="https://example.com"></iframe>';
|
||||
const advTags = ['iframe'];
|
||||
const expected =
|
||||
'<b>Hello, world!</b><iframe src="https://example.com"></iframe>';
|
||||
const result = sanitizeText(input, true, undefined, undefined, advTags);
|
||||
expect(result).toBe(expected);
|
||||
});
|
||||
});
|
||||
@@ -102,18 +102,16 @@
|
||||
|
||||
&__Title,
|
||||
&__Button {
|
||||
@include button.button-color(var(--color-base));
|
||||
background-color: hsl(from var(--color-base) h s l / 0);
|
||||
color: button.$color-transparent-text;
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin: 0;
|
||||
padding: var(--space-s) var(--space-m);
|
||||
border-radius: 0;
|
||||
margin: 0;
|
||||
transition: opacity var(--transition-time-slow) ease-out;
|
||||
|
||||
@include button.button-color();
|
||||
@include button.button-color-transparent();
|
||||
|
||||
&:first-child {
|
||||
border-radius: var(--border-radius-medium) 0 0 var(--border-radius-medium);
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
const { defineConfig } = require('@rspack/cli');
|
||||
const { rspack } = require('@rspack/core');
|
||||
|
||||
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,
|
||||
});
|
||||
|
||||
/**
|
||||
* 04/25/2025
|
||||
* There is a bug in rspack, possibly only ours, with the experimental css
|
||||
* feature that throws an error in tgui-dev. This prevents hot reloading from
|
||||
* working properly and there doesn't seem to be any way to fix it.
|
||||
*
|
||||
* This config exists to switch to the old css loader during development.
|
||||
*
|
||||
* `TypeError: Cannot read properties of null (reading 'removeChild')`
|
||||
*/
|
||||
module.exports = (env = {}, argv) => {
|
||||
/** @type {import('@rspack/core').Configuration} */
|
||||
const config = defineConfig({
|
||||
cache: false,
|
||||
experiments: undefined,
|
||||
mode: 'development',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.([tj]s(x)?|cjs)$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'builtin:swc-loader',
|
||||
options: {
|
||||
isModule: 'unknown',
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'javascript/auto',
|
||||
},
|
||||
{
|
||||
test: /\.(s)?css$/,
|
||||
use: [
|
||||
{
|
||||
loader: rspack.CssExtractRspackPlugin.loader,
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
},
|
||||
{
|
||||
loader: require.resolve('sass-loader'),
|
||||
options: {
|
||||
api: 'modern-compiler',
|
||||
implementation: 'sass-embedded',
|
||||
},
|
||||
},
|
||||
],
|
||||
type: 'javascript/auto',
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg)$/,
|
||||
type: 'asset/resource',
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
oneOf: [
|
||||
{
|
||||
issuer: /\.(s)?css$/,
|
||||
type: 'asset/inline',
|
||||
},
|
||||
{
|
||||
type: 'asset/resource',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
plugins: [
|
||||
new rspack.EnvironmentPlugin({
|
||||
NODE_ENV: 'development',
|
||||
WEBPACK_HMR_ENABLED: env.WEBPACK_HMR_ENABLED || argv.hot || false,
|
||||
DEV_SERVER_IP: env.DEV_SERVER_IP || null,
|
||||
}),
|
||||
new rspack.CssExtractRspackPlugin({
|
||||
filename: '[name].bundle.css',
|
||||
chunkFilename: '[name].bundle.css',
|
||||
}),
|
||||
],
|
||||
});
|
||||
config.devtool = 'cheap-module-source-map';
|
||||
config.devServer = {
|
||||
progress: false,
|
||||
quiet: false,
|
||||
noInfo: false,
|
||||
clientLogLevel: 'silent',
|
||||
stats: createStats(false),
|
||||
};
|
||||
|
||||
return config;
|
||||
};
|
||||
@@ -1,13 +0,0 @@
|
||||
import { defineConfig } from 'vitest/config';
|
||||
|
||||
export default defineConfig({
|
||||
test: {
|
||||
include: [
|
||||
'packages/**/__tests__/*.{ts,tsx}',
|
||||
'packages/**/*.{spec,test}.{ts,tsx}',
|
||||
],
|
||||
exclude: ['packages/tgui-bench/**/*'],
|
||||
environment: 'jsdom',
|
||||
restoreMocks: true,
|
||||
},
|
||||
});
|
||||
@@ -4,9 +4,9 @@
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
const webpack = require('webpack');
|
||||
const path = require('path');
|
||||
const { defineConfig } = require('@rspack/cli');
|
||||
const { rspack } = require('@rspack/core');
|
||||
const ExtractCssPlugin = require('mini-css-extract-plugin');
|
||||
|
||||
const createStats = (verbose) => ({
|
||||
assets: verbose,
|
||||
@@ -26,23 +26,10 @@ const createStats = (verbose) => ({
|
||||
module.exports = (env = {}, argv) => {
|
||||
const mode = argv.mode || 'production';
|
||||
const bench = env.TGUI_BENCH;
|
||||
|
||||
/** @type {import('@rspack/core').Configuration} */
|
||||
const config = defineConfig({
|
||||
cache: true,
|
||||
experiments: {
|
||||
cache: {
|
||||
type: 'persistent',
|
||||
storage: {
|
||||
type: 'filesystem',
|
||||
directory: path.resolve(__dirname, '.yarn/rspack'),
|
||||
},
|
||||
},
|
||||
css: true,
|
||||
},
|
||||
const config = {
|
||||
mode: mode === 'production' ? 'production' : 'development',
|
||||
context: path.resolve(__dirname),
|
||||
target: ['web', 'browserslist:edge >= 123'],
|
||||
target: ['web', 'browserslist:edge>=123'],
|
||||
entry: {
|
||||
tgui: ['./packages/tgui'],
|
||||
'tgui-panel': ['./packages/tgui-panel'],
|
||||
@@ -56,10 +43,8 @@ module.exports = (env = {}, argv) => {
|
||||
chunkFilename: '[name].bundle.js',
|
||||
chunkLoadTimeout: 15000,
|
||||
publicPath: '/',
|
||||
assetModuleFilename: '[name][ext]',
|
||||
},
|
||||
resolve: {
|
||||
pnp: true,
|
||||
extensions: ['.tsx', '.ts', '.js', '.jsx'],
|
||||
alias: {
|
||||
tgui: path.resolve(__dirname, './packages/tgui'),
|
||||
@@ -75,45 +60,39 @@ module.exports = (env = {}, argv) => {
|
||||
rules: [
|
||||
{
|
||||
test: /\.([tj]s(x)?|cjs)$/,
|
||||
exclude: /node_modules/,
|
||||
use: [
|
||||
{
|
||||
loader: 'builtin:swc-loader',
|
||||
options: {
|
||||
jsc: {
|
||||
parser: {
|
||||
syntax: 'typescript',
|
||||
tsx: true,
|
||||
},
|
||||
transform: {
|
||||
react: {
|
||||
runtime: 'automatic',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
loader: require.resolve('swc-loader'),
|
||||
},
|
||||
],
|
||||
type: 'javascript/auto',
|
||||
},
|
||||
{
|
||||
test: /\.(s)?css$/,
|
||||
use: [
|
||||
{
|
||||
loader: require.resolve('sass-loader'),
|
||||
loader: ExtractCssPlugin.loader,
|
||||
options: {
|
||||
api: 'modern-compiler',
|
||||
implementation: 'sass-embedded',
|
||||
esModule: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('css-loader'),
|
||||
options: {
|
||||
esModule: false,
|
||||
},
|
||||
},
|
||||
{
|
||||
loader: require.resolve('sass-loader'),
|
||||
},
|
||||
],
|
||||
type: 'css',
|
||||
},
|
||||
{
|
||||
test: /\.(png|jpg)$/,
|
||||
test: /\.(cur|png|jpg)$/,
|
||||
type: 'asset/resource',
|
||||
},
|
||||
{
|
||||
test: /\.svg$/,
|
||||
test: /.svg$/,
|
||||
oneOf: [
|
||||
{
|
||||
issuer: /\.(s)?css$/,
|
||||
@@ -133,16 +112,29 @@ module.exports = (env = {}, argv) => {
|
||||
hints: false,
|
||||
},
|
||||
devtool: false,
|
||||
|
||||
cache: {
|
||||
type: 'filesystem',
|
||||
cacheLocation: path.resolve(
|
||||
__dirname,
|
||||
`node_modules/.cache/webpack/${mode}`,
|
||||
),
|
||||
buildDependencies: {
|
||||
config: [__filename],
|
||||
},
|
||||
},
|
||||
stats: createStats(true),
|
||||
plugins: [
|
||||
new rspack.EnvironmentPlugin({
|
||||
new webpack.EnvironmentPlugin({
|
||||
NODE_ENV: env.NODE_ENV || mode,
|
||||
WEBPACK_HMR_ENABLED: env.WEBPACK_HMR_ENABLED || argv.hot || false,
|
||||
DEV_SERVER_IP: env.DEV_SERVER_IP || null,
|
||||
}),
|
||||
new ExtractCssPlugin({
|
||||
filename: '[name].bundle.css',
|
||||
chunkFilename: '[name].bundle.css',
|
||||
}),
|
||||
],
|
||||
});
|
||||
};
|
||||
|
||||
if (bench) {
|
||||
config.entry = {
|
||||
@@ -150,6 +142,17 @@ module.exports = (env = {}, argv) => {
|
||||
};
|
||||
}
|
||||
|
||||
// Production build specific options
|
||||
if (mode === 'production') {
|
||||
const { EsbuildPlugin } = require('esbuild-loader');
|
||||
config.optimization.minimizer = [
|
||||
new EsbuildPlugin({
|
||||
css: true,
|
||||
legalComments: 'none',
|
||||
}),
|
||||
];
|
||||
}
|
||||
|
||||
// Development build specific options
|
||||
if (mode !== 'production') {
|
||||
config.devtool = 'cheap-module-source-map';
|
||||
-10994
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
@echo off
|
||||
set NODE_SKIP_PLATFORM_CHECK=1
|
||||
|
||||
:: Call pwsh if available
|
||||
set "powershellCmd=powershell"
|
||||
@@ -8,20 +7,20 @@ if %errorlevel%==0 (
|
||||
set "powershellCmd=pwsh"
|
||||
)
|
||||
|
||||
call %powershellCmd% -NoLogo -ExecutionPolicy Bypass -File "%~dp0\node_.ps1" Download-Node
|
||||
call %powershellCmd% -NoLogo -ExecutionPolicy Bypass -File "%~dp0\javascript_.ps1" Download-Bun
|
||||
for /f "tokens=* USEBACKQ" %%s in (`
|
||||
call %powershellCmd% -NoLogo -ExecutionPolicy Bypass -File "%~dp0\node_.ps1" Get-Path
|
||||
call %powershellCmd% -NoLogo -ExecutionPolicy Bypass -File "%~dp0\javascript_.ps1" Get-Path
|
||||
`) do (
|
||||
set "PATH=%%s;%PATH%"
|
||||
set "PATH=%%s;%PATH%"
|
||||
)
|
||||
where node.exe >nul 2>nul
|
||||
where bun.exe >nul 2>nul
|
||||
if %errorlevel% == 0 (
|
||||
echo | set /p printed_str="Using vendored Node "
|
||||
call node.exe --version
|
||||
call node.exe %*
|
||||
goto exit_with_last_error_level
|
||||
echo | set /p printed_str="Using vendored Bun "
|
||||
call bun.exe --version
|
||||
call bun.exe %*
|
||||
goto exit_with_last_error_level
|
||||
)
|
||||
echo "node.bat: Failed to bootstrap Node!"
|
||||
echo "javascript.bat: Failed to bootstrap Bun!"
|
||||
%COMSPEC% /c exit 1
|
||||
|
||||
:exit_with_last_error_level
|
||||
Executable
+32
@@ -0,0 +1,32 @@
|
||||
#!/bin/sh
|
||||
# bootstrap/bun
|
||||
#
|
||||
# Bun-finding script for all `sh` environments, including Linux, MSYS2,
|
||||
# Git for Windows, and GitHub Desktop. Invokable from CLI or automation.
|
||||
#
|
||||
# If a bun executable installed by a bootstrapper is present, it will be used.
|
||||
# Otherwise, this script requires a system `bun` to be provided.
|
||||
set -e
|
||||
|
||||
# Load Bun version from dependencies.sh
|
||||
OldPWD="$PWD"
|
||||
cd "$(dirname "$0")/../.."
|
||||
. ./dependencies.sh # sets BUN_VERSION (define this in dependencies.sh)
|
||||
cd "$OldPWD"
|
||||
BunVersion="$BUN_VERSION"
|
||||
BunFullVersion="bun-v$BunVersion"
|
||||
|
||||
# If Bun is not present, install using the official installer.
|
||||
if ! command -v bun >/dev/null 2>&1; then
|
||||
echo "Bun not found, installing with official installer..."
|
||||
curl -fsSL https://bun.sh/install | bash -s $BunFullVersion
|
||||
if [ -d "$HOME/.bun/bin" ]; then
|
||||
export PATH="$HOME/.bun/bin:$PATH"
|
||||
else
|
||||
echo "Bun installation directory not found. Please check the installation."
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Using Bun $(bun --version)"
|
||||
exec bun "$@"
|
||||
@@ -0,0 +1,116 @@
|
||||
## bootstrap/bun_.ps1
|
||||
## Downloads a Bun version to a cache directory and invokes it.
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Get-VariableFromFile {
|
||||
param([string] $Path, [string] $Key)
|
||||
foreach ($Line in Get-Content $Path) {
|
||||
if ($Line.StartsWith("export $Key=")) {
|
||||
return $Line.Substring(("export $Key=").Length)
|
||||
}
|
||||
}
|
||||
throw "Couldn't find value for $Key in $Path"
|
||||
}
|
||||
|
||||
function Get-Bun {
|
||||
if (Test-Path $BunTarget -PathType Leaf) {
|
||||
# Bun already exists
|
||||
return
|
||||
}
|
||||
|
||||
Write-Output "Downloading Bun v$BunVersion (may take a while)"
|
||||
New-Item $BunTargetDir -ItemType Directory -ErrorAction SilentlyContinue | Out-Null
|
||||
$WebClient = New-Object Net.WebClient
|
||||
$WebClient.DownloadFile($BunSource, "$BunZip.downloading")
|
||||
Rename-Item "$BunZip.downloading" $BunZip
|
||||
Test-BunHash
|
||||
|
||||
Write-Output "Extracting Bun archive"
|
||||
Expand-Archive -Path $BunZip -DestinationPath $BunTargetDir -Force
|
||||
|
||||
# Move the exe out of the subdirectory
|
||||
if (Test-Path "$BunTargetDir\$BunPlatform\bun.exe") {
|
||||
Move-Item "$BunTargetDir\$BunPlatform\bun.exe" $BunTargetDir -Force
|
||||
}
|
||||
else {
|
||||
Write-Output "Failed to find bun.exe in the extracted directory."
|
||||
exit 1
|
||||
}
|
||||
|
||||
Remove-Item $BunZip -Force
|
||||
Remove-Item "$BunTargetDir\$BunPlatform" -Recurse -Force
|
||||
}
|
||||
|
||||
function Test-BunHash {
|
||||
$Tries = $Tries + 1
|
||||
|
||||
Write-Output "Verifying Bun checksum"
|
||||
$FileHash = Get-FileHash $BunZip -Algorithm SHA256
|
||||
$ActualSha = $FileHash.Hash
|
||||
$LoginResponse = Invoke-WebRequest "https://github.com/oven-sh/bun/releases/download/bun-v$BunVersion/SHASUMS256.txt"
|
||||
$ContentString = [System.Text.Encoding]::UTF8.GetString($LoginResponse.Content)
|
||||
$ShaArray = $ContentString -split "`n"
|
||||
foreach ($ShaArrayEntry in $ShaArray) {
|
||||
$EntrySplit = $ShaArrayEntry -split "\s+"
|
||||
$EntrySha = $EntrySplit[0]
|
||||
$EntryFile = $EntrySplit[1]
|
||||
if ($EntryFile -eq "bun-windows-x64.zip") {
|
||||
$ExpectedSha = $EntrySha
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ($null -eq $ExpectedSha) {
|
||||
Write-Output "Failed to determine the correct checksum value. This is probably fine."
|
||||
return
|
||||
}
|
||||
|
||||
if ($ExpectedSha -ne $ActualSha) {
|
||||
Write-Output "$ExpectedSha != $ActualSha"
|
||||
if ($Tries -gt 3) {
|
||||
Write-Output "Failed to verify Bun checksum three times. Aborting."
|
||||
exit 1
|
||||
}
|
||||
Write-Output "Checksum mismatch on Bun. Retrying."
|
||||
Remove-Item $BunTarget
|
||||
Get-Bun
|
||||
}
|
||||
}
|
||||
|
||||
## Convenience variables
|
||||
$BaseDir = Split-Path $script:MyInvocation.MyCommand.Path
|
||||
$Cache = "$BaseDir\.cache"
|
||||
if ($Env:TG_BOOTSTRAP_CACHE) {
|
||||
$Cache = $Env:TG_BOOTSTRAP_CACHE
|
||||
}
|
||||
|
||||
$BunVersion = Get-VariableFromFile -Path "$BaseDir\..\..\dependencies.sh" -Key "BUN_VERSION"
|
||||
$BunPlatform = "bun-windows-x64"
|
||||
$BunSource = "https://github.com/oven-sh/bun/releases/download/bun-v$BunVersion/$BunPlatform.zip"
|
||||
$BunTargetDir = "$Cache\bun-v$BunVersion-x64"
|
||||
$BunTarget = "$BunTargetDir\bun.exe"
|
||||
$BunZip = "$BunTargetDir\bun.zip"
|
||||
|
||||
## Just print the path and exit
|
||||
if ($Args.length -eq 1 -and $Args[0] -eq "Get-Path") {
|
||||
Write-Output "$BunTargetDir"
|
||||
exit 0
|
||||
}
|
||||
|
||||
## Just download bun and exit
|
||||
if ($Args.length -eq 1 -and $Args[0] -eq "Download-Bun") {
|
||||
Get-Bun
|
||||
exit 0
|
||||
}
|
||||
|
||||
## Download bun
|
||||
Get-Bun
|
||||
|
||||
## Set PATH so that recursive calls find it
|
||||
$Env:PATH = "$BunTargetDir;$ENV:Path"
|
||||
|
||||
## Invoke Bun with all command-line arguments
|
||||
$ErrorActionPreference = "Continue"
|
||||
& "$BunTarget" @Args
|
||||
exit $LastExitCode
|
||||
@@ -1,73 +0,0 @@
|
||||
#!/bin/sh
|
||||
# bootstrap/node
|
||||
#
|
||||
# Node-finding script for all `sh` environments, including Linux, MSYS2,
|
||||
# Git for Windows, and GitHub Desktop. Invokable from CLI or automation.
|
||||
#
|
||||
# If a node.exe installed by `node_.ps1` is present, it will be used.
|
||||
# Otherwise, this script requires a system `node` to be provided.
|
||||
set -e
|
||||
|
||||
# Convenience variables
|
||||
Bootstrap="$(dirname "$0")"
|
||||
Cache="$Bootstrap/.cache"
|
||||
if [ "$TG_BOOTSTRAP_CACHE" ]; then
|
||||
Cache="$TG_BOOTSTRAP_CACHE"
|
||||
fi
|
||||
OldPWD="$PWD"
|
||||
cd "$Bootstrap/../.."
|
||||
. ./dependencies.sh # sets NODE_VERSION_LTS
|
||||
cd "$OldPWD"
|
||||
NodeVersion="$NODE_VERSION_LTS"
|
||||
NodeFullVersion="node-v$NodeVersion-win-x64"
|
||||
NodeDir="$Cache/$NodeFullVersion"
|
||||
NodeExe="$NodeDir/node.exe"
|
||||
is_vendored="1"
|
||||
|
||||
# If a bootstrapped Node is not present, search on $PATH.
|
||||
if [ "$(uname)" = "Linux" ] || [ ! -f "$NodeExe" ]; then
|
||||
if [ "$TG_BOOTSTRAP_NODE_LINUX" ]; then
|
||||
NodeFullVersion="node-v$NodeVersion-linux-x64"
|
||||
NodeDir="$Cache/$NodeFullVersion/bin"
|
||||
NodeExe="$NodeDir/node"
|
||||
|
||||
if [ ! -f "$NodeExe" ]; then
|
||||
mkdir -p "$Cache"
|
||||
Archive="$(realpath "$Cache/node-v$NodeVersion.tar.gz")"
|
||||
curl "https://nodejs.org/download/release/v$NodeVersion/$NodeFullVersion.tar.gz" -o "$Archive"
|
||||
(cd "$Cache" && tar xf "$Archive")
|
||||
fi
|
||||
elif command -v node >/dev/null 2>&1; then
|
||||
NodeExe="node"
|
||||
is_vendored="0"
|
||||
else
|
||||
echo
|
||||
if command -v apt-get >/dev/null 2>&1; then
|
||||
# Ubuntu advice
|
||||
echo "Please install Node using your system's package manager:"
|
||||
echo " sudo apt-get install nodejs"
|
||||
elif uname | grep -q MSYS; then
|
||||
# MSYS2 (not packaged) or Git for Windows advice
|
||||
echo "Please run bootstrap/node.bat instead of bootstrap/node once"
|
||||
echo "to install Node automatically, or install it from https://nodejs.org/"
|
||||
elif command -v pacman >/dev/null 2>&1; then
|
||||
# Arch advice
|
||||
echo "Please install Node using your system's package manager:"
|
||||
echo " sudo pacman -S nodejs"
|
||||
else
|
||||
# Generic advice
|
||||
echo "Please install Node from https://nodejs.org/ or using your system's package manager."
|
||||
fi
|
||||
echo
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Invoke Node with all command-line arguments
|
||||
if [ "$is_vendored" = "1" ]; then
|
||||
PATH="$(readlink -f "$NodeDir"):$PATH"
|
||||
echo "Using vendored Node $("$NodeExe" --version)"
|
||||
else
|
||||
echo "Using system-wide Node $("$NodeExe" --version)"
|
||||
fi
|
||||
exec "$NodeExe" "$@"
|
||||
@@ -1,98 +0,0 @@
|
||||
## bootstrap/node_.ps1
|
||||
## Downloads a Node version to a cache directory and invokes it.
|
||||
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
function Extract-Variable {
|
||||
param([string] $Path, [string] $Key)
|
||||
foreach ($Line in Get-Content $Path) {
|
||||
if ($Line.StartsWith("export $Key=")) {
|
||||
return $Line.Substring("export $Key=".Length)
|
||||
}
|
||||
}
|
||||
throw "Couldn't find value for $Key in $Path"
|
||||
}
|
||||
|
||||
function Download-Node {
|
||||
if (Test-Path $NodeTarget -PathType Leaf) {
|
||||
Verify-Node
|
||||
return
|
||||
}
|
||||
|
||||
Write-Output "Downloading Node v$NodeVersion (may take a while)"
|
||||
New-Item $NodeTargetDir -ItemType Directory -ErrorAction silentlyContinue | Out-Null
|
||||
$WebClient = New-Object Net.WebClient
|
||||
$WebClient.DownloadFile($NodeSource, "$NodeTarget.downloading")
|
||||
Rename-Item "$NodeTarget.downloading" $NodeTarget
|
||||
Verify-Node
|
||||
}
|
||||
|
||||
function Verify-Node {
|
||||
$Tries = $Tries + 1
|
||||
|
||||
Write-Output "Verifying Node checksum"
|
||||
$FileHash = Get-FileHash $NodeTarget -Algorithm SHA256
|
||||
$ActualSha = $FileHash.Hash
|
||||
$LoginResponse = Invoke-WebRequest "https://nodejs.org/download/release/v$NodeVersion/SHASUMS256.txt"
|
||||
$ShaArray = $LoginResponse.Content.split("`n")
|
||||
foreach ($ShaArrayEntry in $ShaArray) {
|
||||
$EntrySplit = $ShaArrayEntry -split "\s+"
|
||||
$EntrySha = $EntrySplit[0]
|
||||
$EntryExe = $EntrySplit[1]
|
||||
if ($EntryExe -eq "win-x64/node.exe") {
|
||||
$ExpectedSha = $EntrySha
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if ($null -eq $ExpectedSha) {
|
||||
Write-Output "Failed to determine the correct checksum value. This is probably fine."
|
||||
return
|
||||
}
|
||||
|
||||
if ($ExpectedSha -ne $ActualSha) {
|
||||
Write-Output "$ExpectedSha != $ActualSha"
|
||||
if ($Tries -gt 3) {
|
||||
Write-Output "Failed to verify Node checksum three times. Aborting."
|
||||
exit 1
|
||||
}
|
||||
Write-Output "Checksum mismatch on Node. Retrying."
|
||||
Remove-Item $NodeTarget
|
||||
Download-Node
|
||||
}
|
||||
}
|
||||
|
||||
## Convenience variables
|
||||
$BaseDir = Split-Path $script:MyInvocation.MyCommand.Path
|
||||
$Cache = "$BaseDir\.cache"
|
||||
if ($Env:TG_BOOTSTRAP_CACHE) {
|
||||
$Cache = $Env:TG_BOOTSTRAP_CACHE
|
||||
}
|
||||
|
||||
$NodeVersion = Extract-Variable -Path "$BaseDir\..\..\dependencies.sh" -Key "NODE_VERSION_LTS"
|
||||
$NodeSource = "https://nodejs.org/download/release/v$NodeVersion/win-x64/node.exe"
|
||||
$NodeTargetDir = "$Cache\node-v$NodeVersion-x64"
|
||||
$NodeTarget = "$NodeTargetDir\node.exe"
|
||||
|
||||
## Just print the path and exit
|
||||
if ($Args.length -eq 1 -and $Args[0] -eq "Get-Path") {
|
||||
Write-Output "$NodeTargetDir"
|
||||
exit 0
|
||||
}
|
||||
|
||||
## Just download node and exit
|
||||
if ($Args.length -eq 1 -and $Args[0] -eq "Download-Node") {
|
||||
Download-Node
|
||||
exit 0
|
||||
}
|
||||
|
||||
## Download node
|
||||
Download-Node
|
||||
|
||||
## Set PATH so that recursive calls find it
|
||||
$Env:PATH = "$NodeTargetDir;$ENV:Path"
|
||||
|
||||
## Invoke Node with all command-line arguments
|
||||
$ErrorActionPreference = "Continue"
|
||||
& "$NodeTarget" @Args
|
||||
exit $LastExitCode
|
||||
@@ -1,4 +0,0 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
exec ../bootstrap/node build.js "$@"
|
||||
@@ -1,2 +1,2 @@
|
||||
@echo off
|
||||
"%~dp0\..\bootstrap\node.bat" --experimental-modules "%~dp0\build.js" %*
|
||||
"%~dp0\..\bootstrap\javascript.bat" "%~dp0\build.js" %*
|
||||
|
||||
+54
-66
@@ -6,32 +6,15 @@
|
||||
* https://github.com/stylemistake/juke-build
|
||||
*/
|
||||
|
||||
import fs from "fs";
|
||||
import https from "https";
|
||||
import { env } from "process";
|
||||
import fs from "node:fs";
|
||||
import https from "node:https";
|
||||
import Juke from "./juke/index.js";
|
||||
import { DreamDaemon, DreamMaker, NamedVersionFile } from "./lib/byond.js";
|
||||
import { yarn } from "./lib/yarn.js";
|
||||
import { bun } from "./lib/bun.js";
|
||||
|
||||
const TGS_MODE = process.env.CBT_BUILD_MODE === "TGS";
|
||||
|
||||
Juke.chdir("../..", import.meta.url);
|
||||
Juke.setup({ file: import.meta.url }).then((code) => {
|
||||
// We're using the currently available quirk in Juke Build, which
|
||||
// prevents it from exiting on Windows, to wait on errors.
|
||||
if (code !== 0 && process.argv.includes("--wait-on-error")) {
|
||||
Juke.logger.error("Please inspect the error and close the window.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (TGS_MODE) {
|
||||
// workaround for ESBuild process lingering
|
||||
// Once https://github.com/privatenumber/esbuild-loader/pull/354 is merged and updated to, this can be removed
|
||||
setTimeout(() => process.exit(code), 10000);
|
||||
} else {
|
||||
process.exit(code);
|
||||
}
|
||||
});
|
||||
|
||||
const DME_NAME = "tgstation";
|
||||
|
||||
@@ -119,13 +102,13 @@ async function download_file(url, file) {
|
||||
if (response.statusCode === 302) {
|
||||
file_stream.close();
|
||||
download_file(response.headers.location, file).then((value) =>
|
||||
resolve(),
|
||||
resolve()
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (response.statusCode !== 200) {
|
||||
Juke.logger.error(
|
||||
`Failed to download ${url}: Status ${response.statusCode}`,
|
||||
`Failed to download ${url}: Status ${response.statusCode}`
|
||||
);
|
||||
file_stream.close();
|
||||
reject();
|
||||
@@ -300,7 +283,7 @@ export const DmTestTarget = new Juke.Target({
|
||||
"-trusted",
|
||||
"-verbose",
|
||||
"-params",
|
||||
"log-directory=ci",
|
||||
"log-directory=ci"
|
||||
);
|
||||
Juke.rm("*.test.*");
|
||||
try {
|
||||
@@ -347,7 +330,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")) {
|
||||
@@ -357,21 +340,17 @@ export const AutowikiTarget = new Juke.Target({
|
||||
},
|
||||
});
|
||||
|
||||
export const YarnTarget = new Juke.Target({
|
||||
export const BunTarget = new Juke.Target({
|
||||
parameters: [CiParameter],
|
||||
inputs: [
|
||||
"tgui/.yarn/+(cache|releases|plugins|sdks)/**/*",
|
||||
"tgui/**/package.json",
|
||||
"tgui/yarn.lock",
|
||||
],
|
||||
outputs: ["tgui/.yarn/install-target"],
|
||||
executes: ({ get }) => yarn("install", get(CiParameter) && "--immutable"),
|
||||
inputs: ["tgui/**/package.json"],
|
||||
executes: ({ get }) => {
|
||||
return bun("install", get(CiParameter));
|
||||
},
|
||||
});
|
||||
|
||||
export const TgFontTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
dependsOn: [BunTarget],
|
||||
inputs: [
|
||||
"tgui/.yarn/install-target",
|
||||
"tgui/packages/tgfont/**/*.+(js|mjs|svg)",
|
||||
"tgui/packages/tgfont/package.json",
|
||||
],
|
||||
@@ -380,24 +359,23 @@ export const TgFontTarget = new Juke.Target({
|
||||
"tgui/packages/tgfont/dist/tgfont.woff2",
|
||||
],
|
||||
executes: async () => {
|
||||
await yarn("tgfont:build");
|
||||
await bun("tgfont:build");
|
||||
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"
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
export const TguiTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
dependsOn: [BunTarget],
|
||||
inputs: [
|
||||
"tgui/.yarn/install-target",
|
||||
"tgui/rspack.config.cjs",
|
||||
"tgui/webpack.config.js",
|
||||
"tgui/**/package.json",
|
||||
"tgui/packages/**/*.+(js|cjs|ts|tsx|jsx|scss)",
|
||||
],
|
||||
@@ -409,54 +387,53 @@ export const TguiTarget = new Juke.Target({
|
||||
"tgui/public/tgui-say.bundle.css",
|
||||
"tgui/public/tgui-say.bundle.js",
|
||||
],
|
||||
executes: () => yarn("tgui:build"),
|
||||
executes: () => bun("tgui:build"),
|
||||
});
|
||||
|
||||
export const TguiEslintTarget = new Juke.Target({
|
||||
parameters: [CiParameter],
|
||||
dependsOn: [YarnTarget],
|
||||
executes: ({ get }) => yarn("tgui:lint", !get(CiParameter) && "--fix"),
|
||||
dependsOn: [BunTarget],
|
||||
executes: ({ get }) => bun("tgui:lint", !get(CiParameter) && "--fix"),
|
||||
});
|
||||
|
||||
export const TguiPrettierTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
executes: () => yarn("tgui:prettier"),
|
||||
dependsOn: [BunTarget],
|
||||
executes: () => bun("tgui:prettier"),
|
||||
});
|
||||
|
||||
export const TguiSonarTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
executes: () => yarn("tgui:sonar"),
|
||||
dependsOn: [BunTarget],
|
||||
executes: () => bun("tgui:sonar"),
|
||||
});
|
||||
|
||||
export const TguiTscTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
executes: () => yarn("tgui:tsc"),
|
||||
dependsOn: [BunTarget],
|
||||
executes: () => bun("tgui:tsc"),
|
||||
});
|
||||
|
||||
export const TguiTestTarget = new Juke.Target({
|
||||
parameters: [CiParameter],
|
||||
dependsOn: [YarnTarget],
|
||||
executes: ({ get }) =>
|
||||
yarn(`tgui:test-${get(CiParameter) ? "ci" : "simple"}`),
|
||||
dependsOn: [BunTarget],
|
||||
executes: () => bun("tgui:test"),
|
||||
});
|
||||
|
||||
export const TguiLintTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget, TguiPrettierTarget, TguiEslintTarget, TguiTscTarget],
|
||||
dependsOn: [BunTarget, TguiPrettierTarget, TguiEslintTarget, TguiTscTarget],
|
||||
});
|
||||
|
||||
export const TguiDevTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
executes: ({ args }) => yarn("tgui:dev", ...args),
|
||||
dependsOn: [BunTarget],
|
||||
executes: ({ args }) => bun("tgui:dev", ...args),
|
||||
});
|
||||
|
||||
export const TguiAnalyzeTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
executes: () => yarn("tgui:analyze"),
|
||||
dependsOn: [BunTarget],
|
||||
executes: () => bun("tgui:analyze"),
|
||||
});
|
||||
|
||||
export const TguiBenchTarget = new Juke.Target({
|
||||
dependsOn: [YarnTarget],
|
||||
executes: () => yarn("tgui:bench"),
|
||||
dependsOn: [BunTarget],
|
||||
executes: () => bun("tgui:bench"),
|
||||
});
|
||||
|
||||
export const TestTarget = new Juke.Target({
|
||||
@@ -494,11 +471,7 @@ export const TguiCleanTarget = new Juke.Target({
|
||||
Juke.rm("tgui/public/*.map");
|
||||
Juke.rm("tgui/public/*.{chunk,bundle,hot-update}.*");
|
||||
Juke.rm("tgui/packages/tgfont/dist", { recursive: true });
|
||||
Juke.rm("tgui/.yarn/{cache,unplugged,rspack}", { recursive: true });
|
||||
Juke.rm("tgui/.yarn/build-state.yml");
|
||||
Juke.rm("tgui/.yarn/install-state.gz");
|
||||
Juke.rm("tgui/.yarn/install-target");
|
||||
Juke.rm("tgui/.pnp.*");
|
||||
Juke.rm("tgui/node_modules", { recursive: true });
|
||||
},
|
||||
});
|
||||
|
||||
@@ -518,8 +491,6 @@ export const CleanAllTarget = new Juke.Target({
|
||||
executes: async () => {
|
||||
Juke.logger.info("Cleaning up data/logs");
|
||||
Juke.rm("data/logs", { recursive: true });
|
||||
Juke.logger.info("Cleaning up global yarn cache");
|
||||
await yarn("cache", "clean", "--all");
|
||||
},
|
||||
});
|
||||
|
||||
@@ -542,4 +513,21 @@ export const TgsTarget = new Juke.Target({
|
||||
},
|
||||
});
|
||||
|
||||
Juke.setup({ file: import.meta.url }).then((code) => {
|
||||
// We're using the currently available quirk in Juke Build, which
|
||||
// prevents it from exiting on Windows, to wait on errors.
|
||||
if (code !== 0 && process.argv.includes("--wait-on-error")) {
|
||||
Juke.logger.error("Please inspect the error and close the window.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (TGS_MODE) {
|
||||
// workaround for ESBuild process lingering
|
||||
// Once https://github.com/privatenumber/esbuild-loader/pull/354 is merged and updated to, this can be removed
|
||||
setTimeout(() => process.exit(code), 10000);
|
||||
} else {
|
||||
process.exit(code);
|
||||
}
|
||||
});
|
||||
|
||||
export default TGS_MODE ? TgsTarget : BuildTarget;
|
||||
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
set -e
|
||||
cd "$(dirname "$0")"
|
||||
exec ../bootstrap/javascript.sh build.js "$@"
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.2.15",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@types/bun": ["@types/bun@1.2.15", "", { "dependencies": { "bun-types": "1.2.15" } }, "sha512-U1ljPdBEphF0nw1MIk0hI7kPg7dFdPyM7EenHsp6W5loNHl7zqy6JQf/RKCgnUn2KDzUpkBwHPnEJEjII594bA=="],
|
||||
|
||||
"@types/node": ["@types/node@22.15.29", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-LNdjOkUDlU1RZb8e1kOIUpN1qQUlzGkEtbVNo53vbrwDg5om6oduhm4SiUaPW5ASTXhAiP0jInWG8Qx9fVlOeQ=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.15", "", { "dependencies": { "@types/node": "*" } }, "sha512-NarRIaS+iOaQU1JPfyKhZm4AsUOrwUOqRNHY0XxI8GI8jYxiLXLcdjYMG9UKS+fwWasc1uw1htV9AX24dD+p4w=="],
|
||||
|
||||
"undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
import Juke from "../juke/index.js";
|
||||
import { mkdirSync } from "node:fs";
|
||||
|
||||
let hasInstallFolder = false;
|
||||
|
||||
export function bun(...args) {
|
||||
if (!hasInstallFolder) {
|
||||
mkdirSync("./tgui/node_modules/", { recursive: true });
|
||||
hasInstallFolder = true;
|
||||
}
|
||||
|
||||
return Juke.exec("bun", [...args.filter((arg) => typeof arg === "string")], {
|
||||
cwd: "./tgui",
|
||||
shell: true,
|
||||
});
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
import Juke from "../juke/index.js";
|
||||
|
||||
let yarnPath;
|
||||
|
||||
export const yarn = (...args) => {
|
||||
if (!yarnPath) {
|
||||
yarnPath = Juke.glob("./tgui/.yarn/releases/*.cjs")[0].replace(
|
||||
"/tgui/",
|
||||
"/",
|
||||
);
|
||||
}
|
||||
return Juke.exec(
|
||||
"node",
|
||||
[yarnPath, ...args.filter((arg) => typeof arg === "string")],
|
||||
{
|
||||
cwd: "./tgui",
|
||||
},
|
||||
);
|
||||
};
|
||||
@@ -1,4 +1,7 @@
|
||||
{
|
||||
"private": true,
|
||||
"type": "module"
|
||||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@types/bun": "^1.2.15"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo build --ignore-rust-version --re
|
||||
mv target/i686-unknown-linux-gnu/release/librust_g.so "$1/librust_g.so"
|
||||
cd ..
|
||||
|
||||
#
|
||||
#
|
||||
cd "$original_dir"
|
||||
# update dreamluau
|
||||
if [ ! -d "dreamluau" ]; then
|
||||
@@ -53,7 +53,10 @@ env PKG_CONFIG_ALLOW_CROSS=1 ~/.cargo/bin/cargo build --ignore-rust-version --re
|
||||
mv target/i686-unknown-linux-gnu/release/libdreamluau.so "$1/libdreamluau.so"
|
||||
cd ..
|
||||
|
||||
# Get unzip
|
||||
apt-get install -y unzip
|
||||
|
||||
# compile tgui
|
||||
echo "Compiling tgui..."
|
||||
cd "$1"
|
||||
env TG_BOOTSTRAP_CACHE="$original_dir" TG_BOOTSTRAP_NODE_LINUX=1 CBT_BUILD_MODE="TGS" tools/bootstrap/node tools/build/build.js
|
||||
env TG_BOOTSTRAP_CACHE="$original_dir" CBT_BUILD_MODE="TGS" tools/bootstrap/javascript.sh tools/build/build.js
|
||||
|
||||
Reference in New Issue
Block a user