Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
d98e0c8
chore: migrate CI to utoo
elrrrrrrr Apr 13, 2026
ced6a2e
Merge remote-tracking branch 'origin/next' into chore-ut-ci
elrrrrrrr Apr 13, 2026
306737d
fix(utils): use node_modules existence check for fallback scope
elrrrrrrr Apr 13, 2026
3a260b1
fix(utils): also check package.json deps for fallback scope
elrrrrrrr Apr 13, 2026
da5de41
fix(utils): add CJS-only fallback for importResolve
elrrrrrrr Apr 13, 2026
b8cbe5e
fix(utils): skip CJS fallback for absolute/relative paths
elrrrrrrr Apr 13, 2026
58325f5
fix(egg-bin): use createRequire for CJS package resolution
elrrrrrrr Apr 13, 2026
88f1f7c
docs(egg-bin): explain why createRequire is used over importResolve
elrrrrrrr Apr 13, 2026
c1d2021
style(egg-bin): format baseCommand.ts
elrrrrrrr Apr 13, 2026
6771de0
fix(egg-bin): guard mock/runner loading with dependency check
elrrrrrrr Apr 13, 2026
fcc4a43
fix(utils): enforce paths constraint on import.meta.resolve results
elrrrrrrr Apr 13, 2026
7cc7e39
fix(utils): only enforce paths scope when paths is explicitly provided
elrrrrrrr Apr 13, 2026
e1b13ed
revert(utils): remove paths scope check from importResolve
elrrrrrrr Apr 13, 2026
3951bb5
fix(egg-bin): check dependency declaration before loading mock/runner
elrrrrrrr Apr 13, 2026
2e87560
fix(egg-bin): use createRequire for CJS packages without exports
elrrrrrrr Apr 13, 2026
b20571d
style(egg-bin): format baseCommand.ts
elrrrrrrr Apr 13, 2026
79c5bd4
Merge branch 'next' into chore-ut-ci
elrrrrrrr Apr 13, 2026
4c01ef3
fix(egg-bin): restore resolve-then-use for tegg-vitest/runner
elrrrrrrr Apr 13, 2026
6ca2c76
fix: restore pnpm for site:build/dev scripts
elrrrrrrr Apr 13, 2026
6e68e8d
revert: restore ut for site:build/dev scripts
elrrrrrrr Apr 13, 2026
2e688d0
fix: use cd + npm run for site scripts
elrrrrrrr Apr 13, 2026
206e03b
ci: trigger full rerun for timing comparison
elrrrrrrr Apr 13, 2026
ef2a389
perf(egg-bin): skip tegg-runner auto-load in self-test fixtures
elrrrrrrr Apr 14, 2026
eb06caf
perf(build): let publint auto-detect pack tool
elrrrrrrr Apr 14, 2026
f9ef8d3
perf(build): gate publint pack via PUBLINT_PACK env
elrrrrrrr Apr 14, 2026
7083e54
refactor(egg-bin): unify mock/runner guards via env var only
elrrrrrrr Apr 14, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 24 additions & 23 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
# CI workflow for egg monorepo
name: CI

on:
Expand Down Expand Up @@ -27,31 +28,31 @@ jobs:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6

- name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
- name: Setup utoo
uses: utooland/setup-utoo@3a51006d0b66afcc32d1b9177a4b200b74f4a8cb # main

- name: Set up Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
with:
node-version: '24'

- name: Install dependencies
run: pnpm install --no-frozen-lockfile
run: ut install --from pnpm

- name: Run lint
run: pnpm run lint
run: ut run lint

- name: Run typecheck
run: pnpm run typecheck
run: ut run typecheck

- name: Run format check
run: pnpm run fmtcheck
run: ut run fmtcheck

- name: Run build
run: pnpm run build
run: ut run build

- name: Run site build
run: pnpm run site:build
run: ut run site:build

test:
strategy:
Expand Down Expand Up @@ -150,24 +151,24 @@ jobs:
# & mysqladmin -u root password root
& mysql -uroot -e "CREATE DATABASE IF NOT EXISTS test;"

- name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
- name: Setup utoo
uses: utooland/setup-utoo@3a51006d0b66afcc32d1b9177a4b200b74f4a8cb # main

- name: Set up Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
with:
node-version: ${{ matrix.node }}

- name: Install dependencies
run: pnpm install --no-frozen-lockfile
run: ut install --from pnpm

- name: Run tests
run: pnpm run ci
run: ut run ci

- name: Run example tests
if: ${{ matrix.os != 'windows-latest' }}
run: |
pnpm run example:test:all
ut run example:test:all

- name: Code Coverage
# skip on windows, it will hangup on codecov
Expand All @@ -194,21 +195,21 @@ jobs:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6

- name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
- name: Setup utoo
uses: utooland/setup-utoo@3a51006d0b66afcc32d1b9177a4b200b74f4a8cb # main

- name: Set up Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
with:
node-version: ${{ matrix.node }}

- name: Install dependencies
run: pnpm install --no-frozen-lockfile
run: ut install --from pnpm

- name: Run tests
run: |
pnpm build --workspace ./tools/egg-bin
pnpm run --filter ./tools/egg-bin ci
ut run build -- --workspace ./tools/egg-bin
ut run ci --workspace @eggjs/bin
Comment thread
elrrrrrrr marked this conversation as resolved.

Comment thread
elrrrrrrr marked this conversation as resolved.
- name: Code Coverage
# skip on windows, it will hangup on codecov https://github.com/codecov/codecov-action/issues/1787
Expand All @@ -235,21 +236,21 @@ jobs:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6

- name: Install pnpm
uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 # v4
- name: Setup utoo
uses: utooland/setup-utoo@3a51006d0b66afcc32d1b9177a4b200b74f4a8cb # main

- name: Set up Node.js
uses: actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238 # v6
with:
node-version: ${{ matrix.node }}

- name: Install dependencies
run: pnpm install --no-frozen-lockfile
run: ut install --from pnpm

- name: Run tests
run: |
pnpm build
pnpm run --filter=./tools/scripts ci
ut run build -- --workspace ./tools/scripts
ut run ci --workspace tools/scripts
Comment thread
elrrrrrrr marked this conversation as resolved.

- name: Code Coverage
if: ${{ matrix.os != 'windows-latest' }}
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/e2e-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,11 @@ jobs:
run: pnpm install --no-frozen-lockfile

- name: Build all packages
env:
# publint pack defaults to npm (main CI env has no pnpm); in E2E we
# already have pnpm installed and npm pack against pnpm's symlinked
# node_modules is ~10x slower, so prefer pnpm pack here
PUBLINT_PACK: pnpm
run: pnpm build

- name: Pack packages into tgz
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,5 @@ tegg/plugin/tegg/test/fixtures/apps/**/*.js
ecosystem-ci/cnpmcore
ecosystem-ci/examples
pnpm-lock.yaml
.utoo.toml
.claude/
3 changes: 2 additions & 1 deletion .oxfmtrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"packages/core/test/fixtures/load_dirs/syntax_error/*",
"packages/core/test/fixtures/syntaxerror/*",
"packages/core/test/fixtures/load_context_syntax_error/**/*",
"CHANGELOG.md"
"CHANGELOG.md",
"/package.json"
],
"experimentalSortImports": {
"groups": [
Expand Down
28 changes: 14 additions & 14 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,25 @@
],
"type": "module",
"scripts": {
Comment thread
elrrrrrrr marked this conversation as resolved.
"clean-dist": "pnpm -r --parallel exec rimraf dist",
"clean": "pnpm -r --parallel run clean && pnpm clean-dist",
"clean-dist": "ut run clean --workspaces --if-present",
"build": "tsdown",
"prelint": "pnpm clean-dist",
"prelint": "ut run clean-dist",
"lint": "oxlint --type-aware --type-check --quiet",
"fmt": "oxfmt",
"typecheck": "pnpm clean && pnpm -r run typecheck",
"typecheck": "ut run clean-dist && ut run typecheck --workspaces --if-present",
"fmtcheck": "oxfmt --check .",
"pretest": "pnpm run clean && pnpm -r run pretest",
"pretest": "ut run clean-dist && ut run pretest --workspaces --if-present",
"test": "vitest run --bail 1 --retry 2 --testTimeout 20000 --hookTimeout 20000",
"test:cov": "pnpm run test --coverage",
"preci": "pnpm -r --parallel run pretest",
"ci": "pnpm run test --coverage",
"site:dev": "pnpm --filter=site run dev",
"site:build": "pnpm --filter=site run build",
"test:cov": "ut run test -- --coverage",
"preci": "ut run pretest --workspaces --if-present",
"ci": "ut run test -- --coverage",
"site:dev": "cd site && npm run dev",
"site:build": "cd site && npm run build",
"puml": "puml . --dest ./site",
"example:dev:commonjs": "pnpm --filter=helloworld-commonjs run dev",
"example:dev:typescript": "pnpm --filter=helloworld-typescript run dev",
"example:dev:tegg": "pnpm --filter=helloworld-tegg run dev",
"example:test:all": "pnpm --filter=helloworld-* run test",
"example:dev:commonjs": "ut run dev --workspace helloworld-commonjs",
"example:dev:typescript": "ut run dev --workspace helloworld-typescript",
"example:dev:tegg": "ut run dev --workspace helloworld-tegg",
"example:test:all": "ut run test --workspace 'helloworld-*' --if-present",
Comment thread
elrrrrrrr marked this conversation as resolved.
Comment thread
elrrrrrrr marked this conversation as resolved.
"prepare": "husky",
"version:patch": "node scripts/version.js patch",
"version:minor": "node scripts/version.js minor",
Expand Down Expand Up @@ -71,6 +70,7 @@
"typescript": "catalog:",
"unplugin-unused": "catalog:",
"urllib": "catalog:",
"utoo": "catalog:",
"vitest": "catalog:"
},
"lint-staged": {
Expand Down
6 changes: 4 additions & 2 deletions packages/cluster/test/options.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,12 @@ describe('test/options.test.ts', () => {
baseDir,
});
const expectPaths = [
// run int workspace root
// run in workspace root
path.join(__dirname, '../../egg'),
// run in project root
// run in project root (pnpm nested)
path.join(__dirname, '../node_modules/egg'),
// run with flat/hoisted node_modules (e.g. ut install)
path.join(__dirname, '../../../node_modules/egg'),
];
assert(
expectPaths.includes(options.framework),
Expand Down
5 changes: 4 additions & 1 deletion packages/tsconfig/test/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import fs from 'node:fs/promises';
import { createRequire } from 'node:module';
import path from 'node:path';

import coffee from 'coffee';
import { test, expect } from 'vitest';

const require = createRequire(import.meta.url);

test('should tsc build work', async () => {
const tsc = path.join(import.meta.dirname, '..', 'node_modules', 'typescript', 'bin', 'tsc');
const tsc = require.resolve('typescript/bin/tsc');
const fixturePath = path.join(import.meta.dirname, 'fixtures/apps/ts-proj');
const tsconfigPath = path.join(fixturePath, 'tsconfig.json');
console.log('%s -p %s, cwd: %s', tsc, tsconfigPath, fixturePath);
Expand Down
4 changes: 2 additions & 2 deletions packages/utils/test/import.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,8 @@ describe('test/import.test.ts', () => {
assert.equal(err.name, 'ImportResolveError');
assert.equal(err.filepath, 'tsconfig-paths-demo-not-exists/register');
assert.deepEqual(err.paths, [getFilepath('cjs/node_modules/inject')]);
assert.match(err.stack ?? '', /Cannot find package/);
assert.match(err.message, /Cannot find package/);
assert.match(err.stack ?? '', /Cannot find (package|module)/);
assert.match(err.message, /Cannot find (package|module)/);
return true;
},
);
Expand Down
4 changes: 4 additions & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ catalog:
urijs: ^1.19.11
urllib: ^4.8.2
utility: ^2.5.0
utoo: ^1
vary: ^1.1.2
vitepress: 2.0.0-alpha.15
vitepress-plugin-llms: ^1.10.0
Expand All @@ -227,6 +228,9 @@ catalogs:
path-to-regexp1:
path-to-regexp: ^1.9.0

onlyBuiltDependencies:
- utoo

minimumReleaseAge: 1440

minimumReleaseAgeExclude:
Expand Down
2 changes: 1 addition & 1 deletion tools/egg-bin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@
}
},
"scripts": {
"build": "tsdown -c tsdown.config.ts",
"typecheck": "tsgo --noEmit",
"pretest": "tsdown",
"test": "vitest run",
"cov": "vitest run --coverage",
Comment thread
elrrrrrrr marked this conversation as resolved.
"ci": "npm run cov"
Expand Down
31 changes: 21 additions & 10 deletions tools/egg-bin/src/baseCommand.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { fork, type ForkOptions, ChildProcess } from 'node:child_process';
import { createRequire } from 'node:module';
import os from 'node:os';
import path from 'node:path';
import { pathToFileURL } from 'node:url';
import { debuglog } from 'node:util';

import { importResolve } from '@eggjs/utils';
import { Command, Flags, Interfaces } from '@oclif/core';

import { type PackageEgg } from './types.ts';
Expand Down Expand Up @@ -207,12 +207,27 @@ export abstract class BaseCommand<T extends typeof Command> extends Command {
// try app baseDir first on custom tscompiler
// then try to find tscompiler in @eggjs/bin/node_modules
const findPaths: string[] = [flags.base, rootDir];
// ts-node, tsconfig-paths are CJS packages without `exports` field.
// Must use CJS resolver (createRequire) instead of importResolve because:
// - ESM resolver (Node ≥22) doesn't auto-append .js for bare subpaths
// of packages without `exports` (e.g. tsconfig-paths/register → register.js)
// - importResolve's import.meta.resolve is scoped to @eggjs/utils, not here
// createRequire resolves from the caller's location with CJS semantics,
// correctly handling extension resolution and flat-hoisted node_modules.
const cjsResolve = (specifier: string): string => {
for (const p of findPaths) {
try {
return createRequire(path.join(p, 'package.json')).resolve(specifier);
} catch {
/* try next path */
}
}
throw new Error(`Cannot resolve '${specifier}' from ${findPaths.join(', ')}`);
Comment thread
elrrrrrrr marked this conversation as resolved.
};
this.isESM = pkg.type === 'module';
if (typescript) {
flags.tscompiler = flags.tscompiler ?? 'ts-node/register';
const tsNodeRegister = importResolve(flags.tscompiler, {
paths: findPaths,
});
const tsNodeRegister = cjsResolve(flags.tscompiler);
flags.tscompiler = tsNodeRegister;
// should require tsNodeRegister on current process, let it can require *.ts files
// e.g.: dev command will execute egg loader to find configs and plugins
Expand All @@ -228,16 +243,12 @@ export abstract class BaseCommand<T extends typeof Command> extends Command {
this.env.TS_NODE_FILES = process.env.TS_NODE_FILES ?? 'true';
// keep same logic with egg-core, test cmd load files need it
// see https://github.com/eggjs/egg-core/blob/master/lib/loader/egg_loader.js#L49
const tsConfigPathsRegister = importResolve('tsconfig-paths/register', {
paths: findPaths,
});
const tsConfigPathsRegister = cjsResolve('tsconfig-paths/register');
this.addNodeOptions(this.formatImportModule(tsConfigPathsRegister));
}
if (this.isESM) {
// use ts-node/esm loader on esm
let esmLoader = importResolve('ts-node/esm', {
paths: findPaths,
});
let esmLoader = cjsResolve('ts-node/esm');
// ES Module loading with absolute path fails on windows
// https://github.com/nodejs/node/issues/31710#issuecomment-583916239
// https://nodejs.org/api/url.html#url_url_pathtofileurl_path
Expand Down
Loading
Loading