chore: move to tsdown (#1211)

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
This commit is contained in:
Ralph Khreish
2025-09-17 21:55:53 +02:00
committed by GitHub
parent 0e8c42c7cb
commit 3c41a113fe
16 changed files with 776 additions and 6366 deletions

View File

@@ -11,6 +11,10 @@ on:
- next - next
workflow_dispatch: workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true
permissions: permissions:
contents: read contents: read
@@ -89,6 +93,13 @@ jobs:
NODE_ENV: production NODE_ENV: production
FORCE_COLOR: 1 FORCE_COLOR: 1
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: build-artifacts
path: dist/
retention-days: 1
test: test:
name: Test name: Test
timeout-minutes: 15 timeout-minutes: 15
@@ -108,10 +119,11 @@ jobs:
run: npm install --frozen-lockfile --prefer-offline run: npm install --frozen-lockfile --prefer-offline
timeout-minutes: 5 timeout-minutes: 5
- name: Build packages (required for tests) - name: Download build artifacts
run: npm run build:packages uses: actions/download-artifact@v4
env: with:
NODE_ENV: production name: build-artifacts
path: dist/
- name: Run Tests - name: Run Tests
run: | run: |

View File

@@ -4,8 +4,6 @@
*/ */
import chalk from 'chalk'; import chalk from 'chalk';
import figlet from 'figlet';
import gradient from 'gradient-string';
/** /**
* Header configuration options * Header configuration options

View File

@@ -50,6 +50,11 @@ export function getStatusWithColor(
color: chalk.red, color: chalk.red,
icon: '!', icon: '!',
tableIcon: '!' tableIcon: '!'
},
completed: {
color: chalk.green,
icon: '✓',
tableIcon: '✓'
} }
}; };

View File

@@ -1,27 +1,36 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "ES2022", "target": "ES2022",
"module": "ESNext", "module": "NodeNext",
"lib": ["ES2022"], "lib": ["ES2022"],
"moduleResolution": "bundler",
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,
"sourceMap": true, "sourceMap": true,
"outDir": "./dist", "outDir": "./dist",
"baseUrl": ".",
"rootDir": "./src", "rootDir": "./src",
"resolveJsonModule": true, "strict": true,
"allowJs": false, "noImplicitAny": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true,
"noUnusedLocals": true, "noUnusedLocals": true,
"noUnusedParameters": true, "noUnusedParameters": true,
"noImplicitReturns": true, "noImplicitReturns": true,
"noFallthroughCasesInSwitch": true, "noFallthroughCasesInSwitch": true,
"types": ["node"] "esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "NodeNext",
"moduleDetection": "force",
"types": ["node"],
"resolveJsonModule": true,
"isolatedModules": true,
"allowImportingTsExtensions": false
}, },
"include": ["src/**/*"], "include": ["src/**/*"],
"exclude": ["node_modules", "dist", "tests"] "exclude": ["node_modules", "dist", "tests", "**/*.test.ts", "**/*.spec.ts"]
} }

View File

@@ -18,7 +18,17 @@ export default {
testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js'], testMatch: ['**/__tests__/**/*.js', '**/?(*.)+(spec|test).js'],
// Transform files // Transform files
transform: {}, preset: 'ts-jest/presets/default-esm',
extensionsToTreatAsEsm: ['.ts'],
moduleFileExtensions: ['js', 'ts', 'json', 'node'],
transform: {
'^.+\\.ts$': [
'ts-jest',
{
useESM: true
}
]
},
// Disable transformations for node_modules // Disable transformations for node_modules
transformIgnorePatterns: ['/node_modules/'], transformIgnorePatterns: ['/node_modules/'],
@@ -27,6 +37,7 @@ export default {
moduleNameMapper: { moduleNameMapper: {
'^@/(.*)$': '<rootDir>/$1' '^@/(.*)$': '<rootDir>/$1'
}, },
resolver: '<rootDir>/jest.resolver.cjs',
// Setup module aliases // Setup module aliases
moduleDirectories: ['node_modules', '<rootDir>'], moduleDirectories: ['node_modules', '<rootDir>'],

19
jest.resolver.cjs Normal file
View File

@@ -0,0 +1,19 @@
const { defaultResolver } = require('jest-resolve');
module.exports = function customResolver(request, options) {
const resolve = options.defaultResolver || defaultResolver;
try {
return resolve(request, options);
} catch (error) {
if (request.startsWith('.') && request.endsWith('.js')) {
try {
return resolve(request.replace(/\.js$/, '.ts'), options);
} catch (tsError) {
tsError.cause = tsError.cause ?? error;
throw tsError;
}
}
throw error;
}
};

6745
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -11,20 +11,12 @@
}, },
"workspaces": ["apps/*", "packages/*", "."], "workspaces": ["apps/*", "packages/*", "."],
"scripts": { "scripts": {
"build": "npm run build:build-config && tsup", "build": "npm run build:build-config && cross-env NODE_ENV=production tsdown",
"dev": "tsup --watch='packages/*/src/**/*' --watch='apps/cli/src/**/*' --watch='bin/**/*' --watch='mcp-server/**/*'", "dev": "tsdown --watch='packages/*/src/**/*' --watch='apps/cli/src/**/*' --watch='bin/**/*' --watch='mcp-server/**/*'",
"turbo:dev": "turbo dev", "turbo:dev": "turbo dev",
"turbo:build": "turbo build", "turbo:build": "turbo build",
"turbo:typecheck": "turbo typecheck", "turbo:typecheck": "turbo typecheck",
"dev:main": "tsup --watch --onSuccess 'echo \"📦 Main package built\" && npm link'",
"dev:legacy": "npm run build:build-config && concurrently -n \"core,cli,main\" -c \"blue,green,yellow\" \"npm run dev:core\" \"npm run dev:cli\" \"npm run dev:main\"",
"dev:core": "npm run dev -w @tm/core",
"dev:cli": "npm run dev -w @tm/cli",
"build:packages": "turbo build --filter='./packages/*' --filter='./apps/*'",
"build:packages:parallel": "turbo build --filter='./packages/*' --filter='./apps/*'",
"build:build-config": "npm run build -w @tm/build-config", "build:build-config": "npm run build -w @tm/build-config",
"build:core": "npm run build -w @tm/core",
"build:cli": "npm run build -w @tm/cli",
"test": "node --experimental-vm-modules node_modules/.bin/jest", "test": "node --experimental-vm-modules node_modules/.bin/jest",
"test:unit": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=unit", "test:unit": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=unit",
"test:integration": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=integration", "test:integration": "node --experimental-vm-modules node_modules/.bin/jest --testPathPattern=integration",
@@ -143,7 +135,8 @@
"mock-fs": "^5.5.0", "mock-fs": "^5.5.0",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"supertest": "^7.1.0", "supertest": "^7.1.0",
"tsup": "^8.5.0", "ts-jest": "^29.4.2",
"tsdown": "^0.15.2",
"tsx": "^4.16.2", "tsx": "^4.16.2",
"turbo": "^2.5.6", "turbo": "^2.5.6",
"typescript": "^5.9.2" "typescript": "^5.9.2"

View File

@@ -3,12 +3,12 @@
"version": "1.0.0", "version": "1.0.0",
"description": "Shared build configuration for Task Master monorepo", "description": "Shared build configuration for Task Master monorepo",
"type": "module", "type": "module",
"main": "./dist/tsup.base.js", "main": "./dist/tsdown.base.js",
"types": "./dist/tsup.base.d.ts", "types": "./src/tsdown.base.ts",
"exports": { "exports": {
".": { ".": {
"types": "./dist/tsup.base.d.ts", "types": "./src/tsdown.base.ts",
"import": "./dist/tsup.base.js" "import": "./dist/tsdown.base.js"
} }
}, },
"files": ["dist", "src"], "files": ["dist", "src"],

View File

@@ -0,0 +1,46 @@
/**
* Base tsdown configuration for Task Master monorepo
* Provides shared configuration that can be extended by individual packages
*/
import type { UserConfig } from 'tsdown';
const isProduction = process.env.NODE_ENV === 'production';
const isDevelopment = !isProduction;
/**
* Environment helpers
*/
export const env = {
isProduction,
isDevelopment,
NODE_ENV: process.env.NODE_ENV || 'development'
};
/**
* Base tsdown configuration for all packages
* Since everything gets bundled into root dist/ anyway, use consistent settings
*/
export const baseConfig: Partial<UserConfig> = {
sourcemap: isDevelopment,
format: 'esm',
platform: 'node',
dts: isDevelopment,
minify: isProduction,
treeshake: isProduction,
// Keep all npm dependencies external (available via node_modules)
external: [/^[^@./]/, /^@(?!tm\/)/]
};
/**
* Utility function to merge configurations
* Simplified for tsdown usage
*/
export function mergeConfig(
base: Partial<UserConfig>,
overrides: Partial<UserConfig>
): Partial<UserConfig> {
return {
...base,
...overrides
};
}

View File

@@ -1,101 +0,0 @@
/**
* Base tsup configuration for Task Master monorepo
* Provides shared configuration that can be extended by individual packages
*/
import type { Options } from 'tsup';
const isProduction = process.env.NODE_ENV === 'production';
const isDevelopment = !isProduction;
/**
* Environment helpers
*/
export const env = {
isProduction,
isDevelopment,
NODE_ENV: process.env.NODE_ENV || 'development'
};
/**
* Base tsup configuration for all packages
* Since everything gets bundled into root dist/ anyway, use consistent settings
*/
export const baseConfig: Partial<Options> = {
format: ['esm'],
target: 'node18',
sourcemap: isDevelopment,
clean: true,
dts: false,
minify: isProduction,
treeshake: isProduction,
splitting: false,
// Don't bundle any other dependencies (auto-external all node_modules)
external: [/^[^./]/],
esbuildOptions(options) {
options.platform = 'node';
// Allow importing TypeScript from JavaScript
options.resolveExtensions = ['.ts', '.js', '.mjs', '.json'];
// Better source mapping in development only
options.sourcesContent = isDevelopment;
// Keep original names for better debugging in development
options.keepNames = isDevelopment;
},
// Watch mode configuration for development
watch: false
};
/**
* Legacy external modules list - kept for backwards compatibility
* Note: When using tsup-node, this is not needed as it automatically
* excludes dependencies and peerDependencies from package.json
*/
export const commonExternals = [
// Native Node.js modules (for cases where tsup is used instead of tsup-node)
'fs',
'path',
'child_process',
'crypto',
'os',
'url',
'util',
'stream',
'http',
'https',
'events',
'assert',
'buffer',
'querystring',
'readline',
'zlib',
'tty',
'net',
'dgram',
'dns',
'tls',
'cluster',
'process',
'module'
];
/**
* Utility function to merge configurations
* Simplified for tsup-node usage
*/
export function mergeConfig(
baseConfig: Partial<Options>,
overrides: Partial<Options>
): Options {
return {
...baseConfig,
...overrides,
// Merge esbuildOptions
esbuildOptions(options, context) {
if (baseConfig.esbuildOptions) {
baseConfig.esbuildOptions(options, context);
}
if (overrides.esbuildOptions) {
overrides.esbuildOptions(options, context);
}
}
} as Options;
}

View File

@@ -53,6 +53,7 @@ export type OutputFormat = (typeof OUTPUT_FORMATS)[number];
*/ */
export const STATUS_ICONS: Record<TaskStatus, string> = { export const STATUS_ICONS: Record<TaskStatus, string> = {
done: '✓', done: '✓',
completed: '✓',
'in-progress': '►', 'in-progress': '►',
blocked: '⭕', blocked: '⭕',
pending: '○', pending: '○',
@@ -71,5 +72,6 @@ export const STATUS_COLORS: Record<TaskStatus, string> = {
deferred: 'gray', deferred: 'gray',
cancelled: 'red', cancelled: 'red',
blocked: 'magenta', blocked: 'magenta',
review: 'cyan' review: 'cyan',
completed: 'green'
} as const; } as const;

View File

@@ -24,7 +24,8 @@ export type TaskStatus =
| 'deferred' | 'deferred'
| 'cancelled' | 'cancelled'
| 'blocked' | 'blocked'
| 'review'; | 'review'
| 'completed';
/** /**
* Task priority levels * Task priority levels

View File

@@ -17,7 +17,7 @@ describe('Complex Cross-Tag Scenarios', () => {
'..', '..',
'..', '..',
'..', '..',
'bin', 'dist',
'task-master.js' 'task-master.js'
); );

31
tsdown.config.ts Normal file
View File

@@ -0,0 +1,31 @@
import { defineConfig } from 'tsdown';
import { baseConfig, mergeConfig } from '@tm/build-config';
import { load as dotenvLoad } from 'dotenv-mono';
dotenvLoad();
// Get all TM_PUBLIC_* env variables for build-time injection
const getBuildTimeEnvs = () => {
const envs: Record<string, string> = {};
for (const [key, value] of Object.entries(process.env)) {
if (key.startsWith('TM_PUBLIC_')) {
// Return the actual value, not JSON.stringify'd
envs[key] = value || '';
}
}
return envs;
};
export default defineConfig(
mergeConfig(baseConfig, {
entry: {
'task-master': 'scripts/dev.js',
'mcp-server': 'mcp-server/server.js'
},
outDir: 'dist',
copy: ['public'],
// Bundle only our workspace packages, keep npm dependencies external
noExternal: [/^@tm\//],
env: getBuildTimeEnvs()
})
);

View File

@@ -1,97 +0,0 @@
import { defineConfig } from 'tsup';
import { baseConfig, mergeConfig } from '@tm/build-config';
import { load as dotenvLoad } from 'dotenv-mono';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
dotenvLoad();
// Get all TM_PUBLIC_* env variables for build-time injection
const getBuildTimeEnvs = () => {
const envs: Record<string, string> = {};
for (const [key, value] of Object.entries(process.env)) {
if (key.startsWith('TM_PUBLIC_')) {
// Return the actual value, not JSON.stringify'd
envs[key] = value || '';
}
}
return envs;
};
export default defineConfig(
mergeConfig(baseConfig, {
entry: {
'task-master': 'scripts/dev.js',
'mcp-server': 'mcp-server/server.js'
},
outDir: 'dist',
publicDir: 'public',
// Override the base config's external to bundle our workspace packages
noExternal: [/^@tm\//],
external: [
/^@supabase\//, // Keep Supabase external to avoid dynamic require issues
'marked',
'marked-terminal'
],
env: getBuildTimeEnvs(),
esbuildOptions(options) {
// Set up path aliases for workspace packages
options.alias = {
'@tm/core': path.resolve(__dirname, 'packages/tm-core/src/index.ts'),
'@tm/core/auth': path.resolve(
__dirname,
'packages/tm-core/src/auth/index.ts'
),
'@tm/core/storage': path.resolve(
__dirname,
'packages/tm-core/src/storage/index.ts'
),
'@tm/core/config': path.resolve(
__dirname,
'packages/tm-core/src/config/index.ts'
),
'@tm/core/providers': path.resolve(
__dirname,
'packages/tm-core/src/providers/index.ts'
),
'@tm/core/services': path.resolve(
__dirname,
'packages/tm-core/src/services/index.ts'
),
'@tm/core/errors': path.resolve(
__dirname,
'packages/tm-core/src/errors/index.ts'
),
'@tm/core/logger': path.resolve(
__dirname,
'packages/tm-core/src/logger/index.ts'
),
'@tm/core/types': path.resolve(
__dirname,
'packages/tm-core/src/types/index.ts'
),
'@tm/core/interfaces': path.resolve(
__dirname,
'packages/tm-core/src/interfaces/index.ts'
),
'@tm/core/utils': path.resolve(
__dirname,
'packages/tm-core/src/utils/index.ts'
),
'@tm/cli': path.resolve(__dirname, 'apps/cli/src/index.ts'),
'@tm/cli/commands': path.resolve(
__dirname,
'apps/cli/src/commands/index.ts'
),
'@tm/cli/utils': path.resolve(__dirname, 'apps/cli/src/utils/index.ts'),
'@tm/cli/ui': path.resolve(__dirname, 'apps/cli/src/ui/index.ts'),
'@tm/build-config': path.resolve(
__dirname,
'packages/build-config/src/tsup.base.ts'
)
};
}
})
);