diff --git a/tools/scripts/migrate-custom-piece-to-turbo.ts b/tools/scripts/migrate-custom-piece-to-turbo.ts new file mode 100644 index 0000000000..58fe397581 --- /dev/null +++ b/tools/scripts/migrate-custom-piece-to-turbo.ts @@ -0,0 +1,259 @@ +#!/usr/bin/env node + +/** + * Migration script for custom pieces from Nx to Turbo. + * + * This script migrates a custom piece (or all pieces in a directory) from the + * old Nx-based build system to the new Turbo-based build system. + * + * Changes made: + * 1. Updates package.json with build/lint scripts and workspace dependencies + * 2. Updates tsconfig.lib.json with correct outDir and baseUrl/paths/rootDir + * 3. Deletes project.json (Nx configuration) + * + * Usage: + * npx ts-node tools/scripts/migrate-custom-piece-to-turbo.ts [piece-path] + * + * If no path is provided, it scans packages/pieces/custom/ for all pieces. + */ + +import * as fs from 'fs'; +import * as path from 'path'; + +const CUSTOM_PIECES_DIR = path.resolve(__dirname, '../../packages/pieces/custom'); + +function getRelativeRoot(pieceDir: string): string { + const piecesIndex = pieceDir.indexOf('/packages/pieces/'); + if (piecesIndex === -1) { + throw new Error(`Unexpected piece directory structure: ${pieceDir}`); + } + const relative = path.relative(pieceDir, pieceDir.substring(0, piecesIndex)); + return relative; +} + +function migratePiece(pieceDir: string): void { + const pieceName = path.basename(pieceDir); + const relativeRoot = getRelativeRoot(pieceDir); + + console.log(`\nMigrating piece: ${pieceName}`); + + let changes = 0; + + // 1. Update package.json + const pkgPath = path.join(pieceDir, 'package.json'); + if (fs.existsSync(pkgPath)) { + const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); + let pkgChanged = false; + + // Ensure scripts exist + if (!pkg.scripts) { + pkg.scripts = {}; + } + + if (pkg.scripts.build !== 'tsc -p tsconfig.lib.json && cp package.json dist/') { + pkg.scripts.build = 'tsc -p tsconfig.lib.json && cp package.json dist/'; + pkgChanged = true; + } + + if (pkg.scripts.lint !== "eslint 'src/**/*.ts'") { + pkg.scripts.lint = "eslint 'src/**/*.ts'"; + pkgChanged = true; + } + + if (pkg.main !== './src/index.js') { + pkg.main = './src/index.js'; + pkgChanged = true; + } + + if (pkg.types !== './src/index.d.ts') { + pkg.types = './src/index.d.ts'; + pkgChanged = true; + } + + // Ensure workspace dependencies are present + const requiredDeps: Record = { + '@activepieces/pieces-framework': 'workspace:*', + '@activepieces/shared': 'workspace:*', + 'tslib': '2.6.2', + }; + + if (!pkg.dependencies) { + pkg.dependencies = {}; + } + + for (const [dep, version] of Object.entries(requiredDeps)) { + if (!pkg.dependencies[dep]) { + pkg.dependencies[dep] = version; + pkgChanged = true; + } + } + + if (pkgChanged) { + fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n'); + console.log(` ✓ Updated package.json`); + changes++; + } else { + console.log(` - package.json already up to date`); + } + } else { + console.log(` ✗ No package.json found — skipping`); + return; + } + + // 2. Update tsconfig.lib.json + const tsconfigLibPath = path.join(pieceDir, 'tsconfig.lib.json'); + if (fs.existsSync(tsconfigLibPath)) { + const tsconfig = JSON.parse(fs.readFileSync(tsconfigLibPath, 'utf-8')); + let tsconfigChanged = false; + + if (!tsconfig.compilerOptions) { + tsconfig.compilerOptions = {}; + } + + // Update outDir from old Nx pattern to new Turbo pattern + const expectedOutDir = './dist'; + if (tsconfig.compilerOptions.outDir !== expectedOutDir) { + tsconfig.compilerOptions.outDir = expectedOutDir; + tsconfigChanged = true; + } + + // Ensure rootDir and baseUrl are set + if (tsconfig.compilerOptions.rootDir !== '.') { + tsconfig.compilerOptions.rootDir = '.'; + tsconfigChanged = true; + } + if (tsconfig.compilerOptions.baseUrl !== '.') { + tsconfig.compilerOptions.baseUrl = '.'; + tsconfigChanged = true; + } + + // Ensure paths is empty (no Nx path mappings) + if (JSON.stringify(tsconfig.compilerOptions.paths) !== '{}') { + tsconfig.compilerOptions.paths = {}; + tsconfigChanged = true; + } + + // Ensure declaration is true + if (tsconfig.compilerOptions.declaration !== true) { + tsconfig.compilerOptions.declaration = true; + tsconfigChanged = true; + } + + // Ensure types includes node + if (!tsconfig.compilerOptions.types || !tsconfig.compilerOptions.types.includes('node')) { + tsconfig.compilerOptions.types = ['node']; + tsconfigChanged = true; + } + + if (tsconfigChanged) { + fs.writeFileSync(tsconfigLibPath, JSON.stringify(tsconfig, null, 2) + '\n'); + console.log(` ✓ Updated tsconfig.lib.json`); + changes++; + } else { + console.log(` - tsconfig.lib.json already up to date`); + } + } else { + // Create tsconfig.lib.json if it doesn't exist + const tsconfig = { + extends: './tsconfig.json', + compilerOptions: { + module: 'commonjs', + rootDir: '.', + baseUrl: '.', + paths: {}, + outDir: './dist', + declaration: true, + types: ['node'], + }, + exclude: ['jest.config.ts', 'src/**/*.spec.ts', 'src/**/*.test.ts'], + include: ['src/**/*.ts'], + }; + fs.writeFileSync(tsconfigLibPath, JSON.stringify(tsconfig, null, 2) + '\n'); + console.log(` ✓ Created tsconfig.lib.json`); + changes++; + } + + // 3. Ensure tsconfig.json exists and extends root + const tsconfigPath = path.join(pieceDir, 'tsconfig.json'); + if (!fs.existsSync(tsconfigPath)) { + const tsconfig = { + extends: `${relativeRoot}/tsconfig.base.json`, + files: [], + include: [], + references: [{ path: './tsconfig.lib.json' }], + compilerOptions: { + forceConsistentCasingInFileNames: true, + strict: true, + noImplicitReturns: true, + noFallthroughCasesInSwitch: true, + }, + }; + fs.writeFileSync(tsconfigPath, JSON.stringify(tsconfig, null, 2) + '\n'); + console.log(` ✓ Created tsconfig.json`); + changes++; + } + + // 4. Delete project.json (Nx config) + const projectJsonPath = path.join(pieceDir, 'project.json'); + if (fs.existsSync(projectJsonPath)) { + fs.unlinkSync(projectJsonPath); + console.log(` ✓ Deleted project.json (Nx config)`); + changes++; + } + + // 5. Delete workspace.json if present + const workspaceJsonPath = path.join(pieceDir, 'workspace.json'); + if (fs.existsSync(workspaceJsonPath)) { + fs.unlinkSync(workspaceJsonPath); + console.log(` ✓ Deleted workspace.json`); + changes++; + } + + if (changes === 0) { + console.log(` Already migrated — no changes needed`); + } else { + console.log(` Done — ${changes} change(s) applied`); + } +} + +function findPieceDirs(baseDir: string): string[] { + if (!fs.existsSync(baseDir)) { + return []; + } + + return fs.readdirSync(baseDir, { withFileTypes: true }) + .filter(d => d.isDirectory()) + .filter(d => d.name !== 'node_modules' && d.name !== '.turbo' && d.name !== 'dist') + .map(d => path.join(baseDir, d.name)) + .filter(dir => fs.existsSync(path.join(dir, 'package.json'))); +} + +// Main +const args = process.argv.slice(2); + +if (args.length > 0) { + // Migrate a specific piece path + const piecePath = path.resolve(args[0]); + if (!fs.existsSync(piecePath)) { + console.error(`Error: Path not found: ${piecePath}`); + process.exit(1); + } + migratePiece(piecePath); +} else { + // Migrate all custom pieces + console.log(`Scanning ${CUSTOM_PIECES_DIR} for pieces...`); + const pieceDirs = findPieceDirs(CUSTOM_PIECES_DIR); + + if (pieceDirs.length === 0) { + console.log('No custom pieces found to migrate.'); + process.exit(0); + } + + console.log(`Found ${pieceDirs.length} piece(s) to check.\n`); + + for (const dir of pieceDirs) { + migratePiece(dir); + } + + console.log('\nMigration complete.'); +}