mirror of
https://github.com/n8n-io/n8n.git
synced 2026-08-28 17:22:01 +08:00
chore: Set up repository for TypeScript 7 migration (#33890)
This commit is contained in:
@@ -10,7 +10,9 @@
|
||||
"n8n-node-dev",
|
||||
"n8n-nodes-base",
|
||||
"n8n-playwright",
|
||||
"n8n-workflow"
|
||||
"n8n-workflow",
|
||||
"typescript",
|
||||
"@typescript/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,9 @@ coverage-gaps.json
|
||||
# Local regeneration cache for scripts/generate-lucide-icon-data.mjs (committed output: lucideIconData.ts)
|
||||
scripts/.lucide-tags-cache.json
|
||||
|
||||
# TS 6 -> 7 migration benchmark output
|
||||
scripts/typescript-migration/results/
|
||||
|
||||
packages/cli/src/commands/export/outputs
|
||||
*.bak
|
||||
.data
|
||||
|
||||
+1
-2
@@ -89,7 +89,7 @@
|
||||
"tsc-alias": "^1.8.10",
|
||||
"tsc-watch": "^6.2.0",
|
||||
"turbo": "2.9.15",
|
||||
"typescript": "*",
|
||||
"typescript": "6.0.2",
|
||||
"yaml": "catalog:",
|
||||
"zx": "^8.8.5"
|
||||
},
|
||||
@@ -121,7 +121,6 @@
|
||||
"tar-fs": "2.1.4",
|
||||
"tslib": "^2.6.2",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"typescript": "catalog:",
|
||||
"vue-tsc": "catalog:frontend",
|
||||
"gaxios": ">=7.1.1",
|
||||
"google-gax": "^4.3.7",
|
||||
|
||||
@@ -164,6 +164,7 @@
|
||||
"nock": "catalog:",
|
||||
"pg": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:",
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"devDependencies": {
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
"license": "LicenseRef-n8n-sustainable-use"
|
||||
|
||||
@@ -112,6 +112,7 @@
|
||||
"@types/proxy-from-env": "catalog:",
|
||||
"fast-glob": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"axios": "catalog:",
|
||||
"nock": "catalog:"
|
||||
},
|
||||
|
||||
@@ -98,6 +98,7 @@
|
||||
"cli-table3": "^0.6.3",
|
||||
"madge": "^8.0.0",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"@n8n/config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:",
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"@types/yargs-parser": "21.0.0",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:",
|
||||
"zod": "catalog:"
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
"@types/qs": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"nock": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
},
|
||||
|
||||
@@ -36,7 +36,8 @@
|
||||
"vitest-mock-extended": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/typescript-config": "workspace:*"
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"license": "LicenseRef-n8n-sustainable-use"
|
||||
}
|
||||
|
||||
@@ -44,7 +44,8 @@
|
||||
"devDependencies": {
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@types/convict": "^6.1.1",
|
||||
"@types/k6": "^0.52.0"
|
||||
"@types/k6": "^0.52.0",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"bin": {
|
||||
"n8n-benchmark": "./bin/n8n-benchmark"
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -81,6 +81,7 @@
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@types/node": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:",
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"@codemirror/buildhelper": "^0.1.5",
|
||||
"@lezer/generator": "catalog:",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"typescript": "catalog:typescript",
|
||||
"@rollup/plugin-node-resolve": "^9.0.0",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"ist": "1.1.7",
|
||||
|
||||
@@ -50,6 +50,7 @@
|
||||
"devDependencies": {
|
||||
"@lezer/generator": "catalog:",
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"typescript": "catalog:typescript",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": ["./tsconfig.json", "@n8n/typescript-config/tsconfig.build.json"],
|
||||
"extends": ["./tsconfig.json", "@n8n/typescript-config/tsconfig.build.go.json"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@n8n/typescript-config/tsconfig.common.json",
|
||||
"extends": "@n8n/typescript-config/tsconfig.common.go.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"types": ["node", "vitest/globals"]
|
||||
|
||||
@@ -41,13 +41,14 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@codemirror/language": "catalog:",
|
||||
"@lezer/generator": "catalog:",
|
||||
"@lezer/highlight": "catalog:",
|
||||
"@lezer/lr": "catalog:",
|
||||
"@lezer/generator": "catalog:"
|
||||
"@lezer/lr": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"typescript": "catalog:typescript",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": ["./tsconfig.json", "@n8n/typescript-config/tsconfig.build.json"],
|
||||
"extends": ["./tsconfig.json", "@n8n/typescript-config/tsconfig.build.go.json"],
|
||||
"compilerOptions": {
|
||||
"rootDir": "src",
|
||||
"outDir": "dist"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"extends": "@n8n/typescript-config/tsconfig.common.json",
|
||||
"extends": "@n8n/typescript-config/tsconfig.common.go.json",
|
||||
"compilerOptions": {
|
||||
"rootDir": ".",
|
||||
"types": ["node", "vitest/globals", "vite/client"]
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
"@types/node": "catalog:",
|
||||
"@types/yargs-parser": "21.0.0",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
"dist/**/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@n8n/typescript-config": "workspace:*"
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"license": "LicenseRef-n8n-sustainable-use"
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"devDependencies": {
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-websocket-mock": "^0.5.0"
|
||||
},
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@types/express": "catalog:",
|
||||
"@types/lodash": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
"devDependencies": {
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:",
|
||||
"@n8n/typescript-config": "workspace:*"
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
"@types/express": "catalog:",
|
||||
"@types/supertest": "^6.0.3",
|
||||
"supertest": "^7.1.1",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
"license": "LicenseRef-n8n-sustainable-use"
|
||||
|
||||
@@ -23,7 +23,8 @@
|
||||
],
|
||||
"devDependencies": {
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@sentry/node": "catalog:sentry"
|
||||
"@sentry/node": "catalog:sentry",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"callsites": "catalog:"
|
||||
|
||||
@@ -51,6 +51,7 @@
|
||||
"@vue/tsconfig": "catalog:frontend",
|
||||
"prettier": "catalog:",
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"rimraf": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vue": "catalog:frontend",
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
"@types/imap": "^0.8.40",
|
||||
"@types/quoted-printable": "^1.0.2",
|
||||
"@types/utf8": "^3.0.3",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
|
||||
@@ -96,6 +96,7 @@
|
||||
"@types/turndown": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
|
||||
@@ -69,6 +69,7 @@
|
||||
"zod": "catalog:",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@
|
||||
"concurrently": "^8.2.0",
|
||||
"electron-builder": "^26.9.0",
|
||||
"rimraf": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
"license": "LicenseRef-n8n-sustainable-use"
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"@vitejs/plugin-vue": "catalog:frontend",
|
||||
"@vue/test-utils": "catalog:frontend",
|
||||
"unplugin-icons": "catalog:frontend",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vite-svg-loader": "catalog:frontend",
|
||||
"vitest": "catalog:"
|
||||
|
||||
@@ -46,6 +46,7 @@
|
||||
"@types/ws": "catalog:",
|
||||
"@types/yargs-parser": "21.0.0",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
|
||||
@@ -228,6 +228,7 @@
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"eslint-plugin-n8n-nodes-base": "catalog:",
|
||||
"fast-glob": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
},
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@types/luxon": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
},
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@n8n/vitest-config": "workspace:*",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:",
|
||||
|
||||
@@ -59,6 +59,7 @@
|
||||
"@types/luxon": "catalog:",
|
||||
"@types/ws": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
|
||||
@@ -44,7 +44,7 @@
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:",
|
||||
"typescript": "^5.0.0"
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
"ast-types": "^0.16.1",
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"source-map-support": "^0.5.21",
|
||||
"sqlite3": "5.1.7",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^5.3.3"
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg": "^8.17.0",
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"types": ["node"],
|
||||
"noUnusedLocals": false,
|
||||
"noUnusedParameters": false,
|
||||
"declaration": true,
|
||||
"tsBuildInfoFile": "${configDir}/dist/build.tsbuildinfo"
|
||||
},
|
||||
"tsc-alias": {
|
||||
"replacers": {
|
||||
"base-url": {
|
||||
"enabled": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"noUncheckedSideEffectImports": false,
|
||||
"strict": true,
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"target": "es2021",
|
||||
"lib": ["es2021", "es2022.error", "es2022.object", "dom"],
|
||||
"removeComments": true,
|
||||
"useUnknownInCatchVariables": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noImplicitAny": true,
|
||||
"noImplicitReturns": true,
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true,
|
||||
"strictNullChecks": true,
|
||||
"preserveConstEnums": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"incremental": true,
|
||||
"declaration": false,
|
||||
"sourceMap": true,
|
||||
"skipLibCheck": true,
|
||||
"tsBuildInfoFile": "${configDir}/dist/typecheck.tsbuildinfo"
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
|
||||
@@ -94,6 +94,7 @@
|
||||
"@types/lodash": "catalog:",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"adm-zip": "^0.5.16",
|
||||
"typescript": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -117,6 +117,7 @@
|
||||
"ts-essentials": "^7.0.3",
|
||||
"tsconfig-paths": "^4.2.0",
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
|
||||
@@ -48,6 +48,7 @@
|
||||
"axios": "catalog:",
|
||||
"nock": "catalog:",
|
||||
"reflect-metadata": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:",
|
||||
|
||||
@@ -54,6 +54,7 @@
|
||||
"@vue/tsconfig": "catalog:frontend",
|
||||
"stylelint": "catalog:",
|
||||
"tsdown": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"rimraf": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
|
||||
@@ -66,6 +66,7 @@
|
||||
"@vitest/coverage-v8": "catalog:",
|
||||
"@vue/test-utils": "catalog:frontend",
|
||||
"unplugin-icons": "^0.19.0",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vite-plugin-dts": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
"storybook-addon-vue-mdx": "3.0.0",
|
||||
"stylelint": "catalog:",
|
||||
"tailwindcss": "^3.4.3",
|
||||
"typescript": "catalog:",
|
||||
"unplugin-icons": "catalog:frontend",
|
||||
"vite": "catalog:",
|
||||
"vite-svg-loader": "catalog:frontend",
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"devDependencies": {
|
||||
"@n8n/typescript-config": "workspace:*",
|
||||
"@types/inquirer": "^7.0.1",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
|
||||
@@ -919,6 +919,7 @@
|
||||
"n8n-containers": "workspace:*",
|
||||
"n8n-core": "workspace:*",
|
||||
"reflect-metadata": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:"
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"@codspeed/vitest-plugin": "^5.2.0",
|
||||
"@n8n/expression-runtime": "workspace:*",
|
||||
"vitest": "catalog:",
|
||||
"n8n-workflow": "workspace:*"
|
||||
"n8n-workflow": "workspace:*",
|
||||
"typescript": "catalog:"
|
||||
},
|
||||
"license": "LicenseRef-n8n-sustainable-use"
|
||||
}
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
"otplib": "^12.0.1",
|
||||
"ts-morph": "catalog:",
|
||||
"tsx": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"zod": "catalog:",
|
||||
"vitest": "catalog:"
|
||||
},
|
||||
|
||||
@@ -61,6 +61,7 @@
|
||||
"@types/ssh2": "catalog:",
|
||||
"@types/xml2js": "catalog:",
|
||||
"fast-check": "catalog:",
|
||||
"typescript": "catalog:",
|
||||
"vite": "catalog:",
|
||||
"vitest": "catalog:",
|
||||
"vitest-mock-extended": "catalog:",
|
||||
|
||||
Generated
+488
-79
File diff suppressed because it is too large
Load Diff
@@ -214,6 +214,8 @@ catalog:
|
||||
catalogMode: strict
|
||||
|
||||
catalogs:
|
||||
typescript:
|
||||
typescript: 7.0.2
|
||||
e2e:
|
||||
'@currents/playwright': ^2.0.0
|
||||
'@playwright/cli': 0.1.0
|
||||
@@ -267,3 +269,5 @@ minimumReleaseAge: 4320
|
||||
minimumReleaseAgeExclude:
|
||||
- '@n8n/*'
|
||||
- '@n8n_io/*'
|
||||
- 'typescript'
|
||||
- '@typescript/*'
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
# TypeScript 6 → 7 migration tooling
|
||||
|
||||
Support scripts for migrating the monorepo from TypeScript `6.0.2` to the
|
||||
TypeScript 7 line, and for moving legacy packages off `moduleResolution: node`
|
||||
onto **NodeNext**. Migration is done **per package**.
|
||||
|
||||
## Scripts
|
||||
|
||||
### `benchmark.mjs` — build/typecheck timing
|
||||
|
||||
Times a single package's `build` and `typecheck` scripts and records the result
|
||||
to a per-package JSON file. Run it *before* switching compilers, switch, then
|
||||
run it again — the second run appends to the same file and prints a delta table.
|
||||
|
||||
```bash
|
||||
# before the switch
|
||||
node scripts/typescript-migration/benchmark.mjs @n8n/config --label=before
|
||||
|
||||
# … swap in the TS 7 compiler …
|
||||
|
||||
# after — appends to the same file and prints the before/after delta
|
||||
node scripts/typescript-migration/benchmark.mjs @n8n/config --label=after
|
||||
```
|
||||
|
||||
**Options**
|
||||
|
||||
| Flag | Default | Meaning |
|
||||
| ---------------- | -------------------- | ------------------------------------------------------------- |
|
||||
| `<package>` | — | Workspace name (`@n8n/config`) or a package dir. |
|
||||
| `--label=<name>` | `ts-<tsc version>` | Storage key. Different compilers auto-separate by version. |
|
||||
| `--runs=<n>` | `3` | Repetitions per task; reports min/median/mean. |
|
||||
| `--tasks=a,b` | `typecheck,build` | Subset to run. Missing scripts are skipped with a warning. |
|
||||
| `--no-prepare` | prepare on | Skip the untimed dependency prebuild. |
|
||||
|
||||
**How it isolates the package** — dependencies are prebuilt once (untimed) via
|
||||
`turbo run build --filter=<pkg>^...`, then each timed run deletes the package's
|
||||
`dist/` + `*.tsbuildinfo` (cold compile) and runs the package script directly
|
||||
with `pnpm --filter <pkg> run <task>`, bypassing turbo's cache. So the numbers
|
||||
reflect only that package's own `tsc` (+ `tsc-alias`) time.
|
||||
|
||||
Results land in `results/<pkg>.json` (gitignored). The label is compiler-
|
||||
agnostic: this script never installs or selects TypeScript — wire that up
|
||||
separately and just re-run with a new `--label`.
|
||||
|
||||
### `add-import-extensions.mjs` — NodeNext import codemod
|
||||
|
||||
Adds explicit `.js` / `.json` / `/index.js` extensions to **relative** import,
|
||||
re-export, and dynamic-`import()` specifiers, which NodeNext (and ESM) require.
|
||||
|
||||
```bash
|
||||
# dry-run: prints every rewrite it would make
|
||||
node scripts/typescript-migration/add-import-extensions.mjs packages/workflow
|
||||
|
||||
# apply
|
||||
node scripts/typescript-migration/add-import-extensions.mjs packages/workflow --write
|
||||
```
|
||||
|
||||
- Uses `ts-morph` (already a catalog dep) and the package's `tsconfig.json` to
|
||||
find source files.
|
||||
- **Skips** bare/alias specifiers (`@/…`, `@utils/…`, package names) — alias
|
||||
tails get their extension from `tsc-alias`'s `resolveFullPaths` at build time.
|
||||
- **Skips** specifiers that already carry a known extension.
|
||||
- Reports any relative specifier it can't resolve on disk for manual review;
|
||||
it does not guess.
|
||||
|
||||
## Per-package migration loop
|
||||
|
||||
1. **Baseline:** `benchmark.mjs <pkg> --label=before`.
|
||||
2. **Config:** point the package's tsconfig at NodeNext — extend
|
||||
`@n8n/typescript-config/modern`, or set `module: NodeNext` +
|
||||
`moduleResolution: NodeNext` locally.
|
||||
3. **Codemod:** run `add-import-extensions.mjs <pkg>` (review), then `--write`.
|
||||
4. **Aliases:** if the package keeps path aliases, enable `tsc-alias`
|
||||
`resolveFullPaths` so alias tails also get extensions in emit.
|
||||
5. **Verify:** `pnpm --filter <pkg> run build && pnpm --filter <pkg> run typecheck && pnpm --filter <pkg> test`.
|
||||
6. **After:** `benchmark.mjs <pkg> --label=after` → confirm the delta table.
|
||||
|
||||
Migrate leaf/low-dependency packages first (`workflow`, `@n8n/config`,
|
||||
`@n8n/di`) so downstream typechecks stay green, then work up toward `cli`.
|
||||
@@ -0,0 +1,42 @@
|
||||
# TypeScript 6 → 7 migration benchmarks
|
||||
|
||||
Generated 2026-07-09T08:18:07.078Z from `scripts/typescript-migration/results/`.
|
||||
|
||||
| Package | typecheck Δ | build Δ |
|
||||
| --- | --- | --- |
|
||||
| `@n8n/codemirror-lang` | -36.3% | -31.4% |
|
||||
| `@n8n/codemirror-lang-html` | — | -17.0% |
|
||||
| `@n8n/codemirror-lang-sql` | -42.7% | -39.2% |
|
||||
|
||||
```
|
||||
=== @n8n/codemirror-lang — median times (Δ vs "before") ===
|
||||
|
||||
typecheck:
|
||||
before 699ms
|
||||
after 445ms -254ms (-36.3%)
|
||||
|
||||
build:
|
||||
before 650ms
|
||||
after 446ms -204ms (-31.4%)
|
||||
```
|
||||
|
||||
```
|
||||
=== @n8n/codemirror-lang-html — median times (Δ vs "before") ===
|
||||
|
||||
build:
|
||||
before 1.09s
|
||||
after 905ms -186ms (-17.0%)
|
||||
```
|
||||
|
||||
```
|
||||
=== @n8n/codemirror-lang-sql — median times (Δ vs "before") ===
|
||||
|
||||
typecheck:
|
||||
before 785ms
|
||||
after 450ms -335ms (-42.7%)
|
||||
|
||||
build:
|
||||
before 720ms
|
||||
after 438ms -282ms (-39.2%)
|
||||
```
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
#!/usr/bin/env node
|
||||
// Codemod: add explicit .js/.json extensions to relative import specifiers so a
|
||||
// package can move from `moduleResolution: node` to NodeNext.
|
||||
//
|
||||
// NodeNext (and ESM) require relative specifiers to carry their runtime
|
||||
// extension. TypeScript's convention is to import a `.ts` source as `.js`, and
|
||||
// a directory as `<dir>/index.js`. This rewrites:
|
||||
// import x from './foo' -> './foo.js'
|
||||
// export * from './bar' -> './bar.js'
|
||||
// import('./baz') -> './baz.js'
|
||||
// import cfg from './data' -> './data.json' (when data.json exists)
|
||||
// import x from './widgets' -> './widgets/index.js'
|
||||
//
|
||||
// It SKIPS bare/alias specifiers (`@/…`, `@utils/…`, package names) — those are
|
||||
// handled by tsc-alias's `resolveFullPaths` at build time — and anything that
|
||||
// already has a known extension.
|
||||
//
|
||||
// Dry-run by default; pass --write to apply.
|
||||
//
|
||||
// Usage:
|
||||
// node scripts/typescript-migration/add-import-extensions.mjs <package> [--write]
|
||||
|
||||
import { createRequire } from 'node:module';
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
|
||||
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
||||
const REPO_ROOT = resolve(SCRIPT_DIR, '..', '..');
|
||||
const KNOWN_EXTENSIONS = ['.js', '.mjs', '.cjs', '.json', '.node'];
|
||||
|
||||
function fail(message) {
|
||||
console.error(`\n✖ ${message}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// ts-morph is a catalog dep of several packages, not hoisted to the root, so
|
||||
// resolve it from a package that depends on it.
|
||||
async function loadTsMorph() {
|
||||
const candidates = [
|
||||
'packages/testing/janitor',
|
||||
'packages/testing/code-health',
|
||||
'packages/@n8n/node-cli',
|
||||
];
|
||||
for (const rel of candidates) {
|
||||
const anchor = join(REPO_ROOT, rel, 'package.json');
|
||||
if (!existsSync(anchor)) continue;
|
||||
try {
|
||||
const require = createRequire(anchor);
|
||||
const entry = require.resolve('ts-morph');
|
||||
return await import(pathToFileURL(entry).href);
|
||||
} catch {
|
||||
// try next anchor
|
||||
}
|
||||
}
|
||||
fail('Could not resolve "ts-morph". Run `pnpm install` first.');
|
||||
}
|
||||
|
||||
function parseArgs(argv) {
|
||||
let write = false;
|
||||
const positionals = [];
|
||||
for (const arg of argv) {
|
||||
if (arg === '--write') write = true;
|
||||
else if (arg.startsWith('--')) fail(`Unknown option: ${arg}`);
|
||||
else positionals.push(arg);
|
||||
}
|
||||
if (positionals.length !== 1) fail('Expected exactly one <package> argument.');
|
||||
return { package: positionals[0], write };
|
||||
}
|
||||
|
||||
function findTsConfig(pkgArg) {
|
||||
const dir = resolve(REPO_ROOT, pkgArg);
|
||||
if (existsSync(join(dir, 'tsconfig.json'))) return join(dir, 'tsconfig.json');
|
||||
fail(`No tsconfig.json found at "${dir}". Pass a package directory (e.g. packages/workflow).`);
|
||||
}
|
||||
|
||||
function isRelative(spec) {
|
||||
return spec.startsWith('./') || spec.startsWith('../');
|
||||
}
|
||||
|
||||
function hasKnownExtension(spec) {
|
||||
return KNOWN_EXTENSIONS.some((ext) => spec.endsWith(ext));
|
||||
}
|
||||
|
||||
// Resolve a relative specifier to the extension it should carry, or null if it
|
||||
// can't be resolved on disk (caller reports it for manual review).
|
||||
function resolveExtension(fromFile, spec) {
|
||||
const base = resolve(dirname(fromFile), spec);
|
||||
for (const ext of ['.ts', '.tsx', '.mts', '.cts']) {
|
||||
if (existsSync(base + ext)) return '.js';
|
||||
}
|
||||
if (existsSync(base + '.json')) return '.json';
|
||||
if (existsSync(base + '.js')) return '.js';
|
||||
for (const idx of ['index.ts', 'index.tsx', 'index.mts', 'index.cts', 'index.js']) {
|
||||
if (existsSync(join(base, idx))) return '/index.js';
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const opts = parseArgs(process.argv.slice(2));
|
||||
const tsConfigFilePath = findTsConfig(opts.package);
|
||||
const { Project, SyntaxKind } = await loadTsMorph();
|
||||
|
||||
console.log(`\nCodemod: add import extensions`);
|
||||
console.log(` tsconfig: ${tsConfigFilePath}`);
|
||||
console.log(` mode: ${opts.write ? 'WRITE' : 'dry-run (pass --write to apply)'}\n`);
|
||||
|
||||
// Only rewrite the target package's own hand-written sources. The project
|
||||
// (via references) also loads dist output and other packages' declaration
|
||||
// files — those aren't ours to touch.
|
||||
const pkgDir = dirname(tsConfigFilePath);
|
||||
const project = new Project({ tsConfigFilePath });
|
||||
const sourceFiles = project.getSourceFiles().filter((sf) => {
|
||||
const p = sf.getFilePath();
|
||||
return (
|
||||
p.startsWith(`${pkgDir}/`) &&
|
||||
!p.includes('/node_modules/') &&
|
||||
!p.includes('/dist/') &&
|
||||
!sf.isDeclarationFile()
|
||||
);
|
||||
});
|
||||
|
||||
let scanned = 0;
|
||||
let rewritten = 0;
|
||||
const unresolved = [];
|
||||
|
||||
for (const sf of sourceFiles) {
|
||||
scanned++;
|
||||
const filePath = sf.getFilePath();
|
||||
const relFile = filePath.replace(`${REPO_ROOT}/`, '');
|
||||
let changedInFile = false;
|
||||
|
||||
// Collect (node, currentSpec, setter) for static import/export + dynamic import.
|
||||
const targets = [];
|
||||
for (const decl of [...sf.getImportDeclarations(), ...sf.getExportDeclarations()]) {
|
||||
const spec = decl.getModuleSpecifierValue();
|
||||
if (spec) targets.push({ spec, set: (v) => decl.setModuleSpecifier(v) });
|
||||
}
|
||||
for (const call of sf.getDescendantsOfKind(SyntaxKind.CallExpression)) {
|
||||
if (call.getExpression().getKind() !== SyntaxKind.ImportKeyword) continue;
|
||||
const [arg] = call.getArguments();
|
||||
if (arg && arg.getKind() === SyntaxKind.StringLiteral) {
|
||||
targets.push({ spec: arg.getLiteralText(), set: (v) => arg.setLiteralValue(v) });
|
||||
}
|
||||
}
|
||||
|
||||
for (const { spec, set } of targets) {
|
||||
if (!isRelative(spec) || hasKnownExtension(spec)) continue;
|
||||
const ext = resolveExtension(filePath, spec);
|
||||
if (!ext) {
|
||||
unresolved.push(`${relFile}: '${spec}'`);
|
||||
continue;
|
||||
}
|
||||
set(ext.startsWith('/') ? spec + ext : spec + ext);
|
||||
rewritten++;
|
||||
changedInFile = true;
|
||||
if (!opts.write) console.log(` ${relFile}: '${spec}' -> '${spec + ext}'`);
|
||||
}
|
||||
|
||||
if (changedInFile && opts.write) sf.saveSync();
|
||||
}
|
||||
|
||||
console.log(`\n${'='.repeat(48)}`);
|
||||
console.log(` files scanned: ${scanned}`);
|
||||
console.log(` specifiers ${opts.write ? 'rewritten' : 'to rewrite'}: ${rewritten}`);
|
||||
console.log(` unresolved: ${unresolved.length}`);
|
||||
if (unresolved.length) {
|
||||
console.log(`\n⚠ Unresolved relative specifiers (review manually):`);
|
||||
for (const u of unresolved) console.log(` ${u}`);
|
||||
}
|
||||
if (!opts.write && rewritten > 0) console.log(`\nRe-run with --write to apply.`);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,227 @@
|
||||
#!/usr/bin/env node
|
||||
// Per-package build/typecheck timing for the TS 6 -> 7 migration.
|
||||
//
|
||||
// Run it before switching compilers (writes a results file), switch the
|
||||
// compiler, then run it again — the second run appends to the same file and
|
||||
// prints a before/after delta table. Migration is per-package, so this always
|
||||
// targets a single workspace package.
|
||||
//
|
||||
// Usage:
|
||||
// node scripts/typescript-migration/benchmark.mjs <package> [options]
|
||||
//
|
||||
// Options:
|
||||
// --label=<name> Key the run is stored under. Defaults to the detected
|
||||
// TypeScript version (e.g. "ts-6.0.2"), so before/after
|
||||
// with different compilers auto-separate. Re-running a
|
||||
// label overwrites it.
|
||||
// --runs=<n> Repetitions per task (default 3). Reports min/median/mean.
|
||||
// --tasks=a,b Subset of "typecheck,build" (default both). Missing
|
||||
// scripts are skipped with a warning.
|
||||
// --no-prepare Skip the untimed dependency prebuild.
|
||||
//
|
||||
// Example:
|
||||
// node scripts/typescript-migration/benchmark.mjs @n8n/config --label=before
|
||||
|
||||
import { execFileSync, spawnSync } from 'node:child_process';
|
||||
import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
||||
const REPO_ROOT = resolve(SCRIPT_DIR, '..', '..');
|
||||
const RESULTS_DIR = join(SCRIPT_DIR, 'results');
|
||||
const DEFAULT_TASKS = ['typecheck', 'build'];
|
||||
|
||||
function parseArgs(argv) {
|
||||
const opts = { runs: 3, tasks: DEFAULT_TASKS, prepare: true, label: undefined };
|
||||
const positionals = [];
|
||||
for (const arg of argv) {
|
||||
if (arg === '--no-prepare') opts.prepare = false;
|
||||
else if (arg.startsWith('--label=')) opts.label = arg.slice('--label='.length);
|
||||
else if (arg.startsWith('--runs=')) opts.runs = Number(arg.slice('--runs='.length));
|
||||
else if (arg.startsWith('--tasks=')) opts.tasks = arg.slice('--tasks='.length).split(',').map((t) => t.trim()).filter(Boolean);
|
||||
else if (arg.startsWith('--')) fail(`Unknown option: ${arg}`);
|
||||
else positionals.push(arg);
|
||||
}
|
||||
if (positionals.length !== 1) fail('Expected exactly one <package> argument.');
|
||||
if (!Number.isInteger(opts.runs) || opts.runs < 1) fail('--runs must be a positive integer.');
|
||||
opts.package = positionals[0];
|
||||
return opts;
|
||||
}
|
||||
|
||||
function fail(message) {
|
||||
console.error(`\n✖ ${message}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Resolve a workspace package name or directory to { name, dir, scripts }.
|
||||
function resolvePackage(nameOrDir) {
|
||||
// A directory path was given directly.
|
||||
const asDir = resolve(REPO_ROOT, nameOrDir);
|
||||
if (existsSync(join(asDir, 'package.json'))) {
|
||||
const pkg = JSON.parse(readFileSync(join(asDir, 'package.json'), 'utf8'));
|
||||
return { name: pkg.name, dir: asDir, scripts: pkg.scripts ?? {} };
|
||||
}
|
||||
// Otherwise treat it as a workspace package name and ask pnpm where it lives.
|
||||
let out;
|
||||
try {
|
||||
out = execFileSync('pnpm', ['--filter', nameOrDir, 'exec', 'node', '-p', 'process.cwd()'], {
|
||||
cwd: REPO_ROOT,
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'pipe'],
|
||||
});
|
||||
} catch {
|
||||
fail(`Could not resolve package "${nameOrDir}" — not a directory and pnpm --filter found no match.`);
|
||||
}
|
||||
const dir = out.trim().split('\n').filter(Boolean).pop();
|
||||
if (!dir || !existsSync(join(dir, 'package.json'))) fail(`Resolved an invalid directory for "${nameOrDir}".`);
|
||||
const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8'));
|
||||
return { name: pkg.name, dir, scripts: pkg.scripts ?? {} };
|
||||
}
|
||||
|
||||
function detectTsVersion(pkgName) {
|
||||
const res = spawnSync('pnpm', ['--filter', pkgName, 'exec', 'tsc', '--version'], {
|
||||
cwd: REPO_ROOT,
|
||||
encoding: 'utf8',
|
||||
});
|
||||
const match = /Version\s+([\d.]+)/.exec(res.stdout ?? '');
|
||||
return match ? match[1] : 'unknown';
|
||||
}
|
||||
|
||||
// Wipe compiled output + incremental caches so every timed run is cold.
|
||||
function cleanBuildArtifacts(pkgDir) {
|
||||
rmSync(join(pkgDir, 'dist'), { recursive: true, force: true });
|
||||
rmSync(join(pkgDir, 'tsconfig.tsbuildinfo'), { force: true });
|
||||
}
|
||||
|
||||
function timeTask(pkgName, pkgDir, task) {
|
||||
cleanBuildArtifacts(pkgDir);
|
||||
const start = process.hrtime.bigint();
|
||||
const res = spawnSync('pnpm', ['--filter', pkgName, 'run', task], {
|
||||
cwd: REPO_ROOT,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
const ms = Number(process.hrtime.bigint() - start) / 1e6;
|
||||
if (res.status !== 0) fail(`Task "${task}" failed (exit ${res.status}); aborting so timings aren't polluted.`);
|
||||
return ms;
|
||||
}
|
||||
|
||||
function summarize(runsMs) {
|
||||
const sorted = [...runsMs].sort((a, b) => a - b);
|
||||
const mid = Math.floor(sorted.length / 2);
|
||||
const median = sorted.length % 2 ? sorted[mid] : (sorted[mid - 1] + sorted[mid]) / 2;
|
||||
const mean = runsMs.reduce((a, b) => a + b, 0) / runsMs.length;
|
||||
return {
|
||||
runsMs: runsMs.map((n) => Math.round(n)),
|
||||
min: Math.round(sorted[0]),
|
||||
median: Math.round(median),
|
||||
mean: Math.round(mean),
|
||||
};
|
||||
}
|
||||
|
||||
function fmtMs(ms) {
|
||||
return ms >= 1000 ? `${(ms / 1000).toFixed(2)}s` : `${ms}ms`;
|
||||
}
|
||||
|
||||
// Baseline-first ordering: "before" is always the baseline and "after" always
|
||||
// last regardless of recording order; anything else falls between by timestamp.
|
||||
function orderLabels(runs) {
|
||||
const rank = (l) => (/^before$/i.test(l) ? -1 : /^after$/i.test(l) ? 1 : 0);
|
||||
return Object.keys(runs).sort((a, b) => {
|
||||
if (rank(a) !== rank(b)) return rank(a) - rank(b);
|
||||
return (runs[a].timestamp ?? '').localeCompare(runs[b].timestamp ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
// Print a comparison table across every stored label, deltas vs the baseline.
|
||||
function printComparison(data) {
|
||||
const labels = orderLabels(data.runs);
|
||||
if (labels.length < 2) {
|
||||
console.log(`\nStored 1 run ("${labels[0]}"). Run again with a second --label to see a delta.\n`);
|
||||
return;
|
||||
}
|
||||
const base = labels[0];
|
||||
console.log(`\n=== ${data.package} — median times (Δ vs "${base}") ===`);
|
||||
const tasks = new Set();
|
||||
for (const l of labels) for (const t of Object.keys(data.runs[l].tasks)) tasks.add(t);
|
||||
for (const task of tasks) {
|
||||
console.log(`\n${task}:`);
|
||||
const baseMedian = data.runs[base].tasks[task]?.median;
|
||||
for (const label of labels) {
|
||||
const stat = data.runs[label].tasks[task];
|
||||
if (!stat) {
|
||||
console.log(` ${label.padEnd(16)} (not measured)`);
|
||||
continue;
|
||||
}
|
||||
let delta = '';
|
||||
if (label !== base && baseMedian != null) {
|
||||
const diff = stat.median - baseMedian;
|
||||
const pct = ((diff / baseMedian) * 100).toFixed(1);
|
||||
const sign = diff <= 0 ? '' : '+';
|
||||
delta = ` ${sign}${fmtMs(diff)} (${sign}${pct}%)`;
|
||||
}
|
||||
console.log(` ${label.padEnd(16)} ${fmtMs(stat.median).padStart(9)}${delta}`);
|
||||
}
|
||||
}
|
||||
console.log('');
|
||||
}
|
||||
|
||||
function main() {
|
||||
const opts = parseArgs(process.argv.slice(2));
|
||||
const pkg = resolvePackage(opts.package);
|
||||
const tsVersion = detectTsVersion(pkg.name);
|
||||
const label = opts.label ?? `ts-${tsVersion}`;
|
||||
|
||||
const tasks = opts.tasks.filter((task) => {
|
||||
if (pkg.scripts[task]) return true;
|
||||
console.warn(`⚠ Package "${pkg.name}" has no "${task}" script — skipping.`);
|
||||
return false;
|
||||
});
|
||||
if (tasks.length === 0) fail('No runnable tasks for this package.');
|
||||
|
||||
console.log(`\nBenchmarking ${pkg.name}`);
|
||||
console.log(` label: ${label}`);
|
||||
console.log(` tsc: ${tsVersion}`);
|
||||
console.log(` node: ${process.version}`);
|
||||
console.log(` runs/task: ${opts.runs}`);
|
||||
console.log(` tasks: ${tasks.join(', ')}\n`);
|
||||
|
||||
if (opts.prepare) {
|
||||
console.log('Prebuilding dependencies (untimed)…');
|
||||
const prep = spawnSync('pnpm', ['build', `--filter=${pkg.name}^...`], {
|
||||
cwd: REPO_ROOT,
|
||||
stdio: 'inherit',
|
||||
});
|
||||
if (prep.status !== 0) fail('Dependency prebuild failed. Fix the build or pass --no-prepare.');
|
||||
}
|
||||
|
||||
const taskStats = {};
|
||||
for (const task of tasks) {
|
||||
const runsMs = [];
|
||||
for (let i = 0; i < opts.runs; i++) {
|
||||
console.log(`\n▶ ${task} — run ${i + 1}/${opts.runs}`);
|
||||
runsMs.push(timeTask(pkg.name, pkg.dir, task));
|
||||
}
|
||||
taskStats[task] = summarize(runsMs);
|
||||
console.log(` ↳ ${task}: min ${fmtMs(taskStats[task].min)}, median ${fmtMs(taskStats[task].median)}, mean ${fmtMs(taskStats[task].mean)}`);
|
||||
}
|
||||
|
||||
mkdirSync(RESULTS_DIR, { recursive: true });
|
||||
const resultsFile = join(RESULTS_DIR, `${pkg.name.replace(/[@/]/g, '_')}.json`);
|
||||
const data = existsSync(resultsFile)
|
||||
? JSON.parse(readFileSync(resultsFile, 'utf8'))
|
||||
: { package: pkg.name, runs: {} };
|
||||
data.runs[label] = {
|
||||
label,
|
||||
tsVersion,
|
||||
node: process.version,
|
||||
timestamp: new Date().toISOString(),
|
||||
tasks: taskStats,
|
||||
};
|
||||
writeFileSync(resultsFile, `${JSON.stringify(data, null, 2)}\n`);
|
||||
console.log(`\n✔ Wrote ${resultsFile}`);
|
||||
|
||||
printComparison(data);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -0,0 +1,125 @@
|
||||
#!/usr/bin/env node
|
||||
// Roll every results/<pkg>.json into a single tracked Markdown report, so
|
||||
// per-package before/after numbers are reviewable as PRs land.
|
||||
//
|
||||
// Usage:
|
||||
// node scripts/typescript-migration/summarize.mjs [--out=<file>]
|
||||
//
|
||||
// Default output: scripts/typescript-migration/SUMMARY.md (committed — the
|
||||
// results/ JSON is gitignored, this rollup is not).
|
||||
|
||||
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
||||
import { dirname, join, resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
||||
const RESULTS_DIR = join(SCRIPT_DIR, 'results');
|
||||
|
||||
function parseArgs(argv) {
|
||||
let out = join(SCRIPT_DIR, 'SUMMARY.md');
|
||||
for (const arg of argv) {
|
||||
if (arg.startsWith('--out=')) out = resolve(process.cwd(), arg.slice('--out='.length));
|
||||
else {
|
||||
console.error(`\n✖ Unknown option: ${arg}\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
return { out };
|
||||
}
|
||||
|
||||
function fmtMs(ms) {
|
||||
return ms >= 1000 ? `${(ms / 1000).toFixed(2)}s` : `${ms}ms`;
|
||||
}
|
||||
|
||||
// Baseline-first ordering: a label named "before" is always the baseline and
|
||||
// "after" always last, regardless of the order they were recorded in; anything
|
||||
// else falls between them by timestamp. Keeps deltas pointing the right way.
|
||||
function orderLabels(runs) {
|
||||
const rank = (l) => (/^before$/i.test(l) ? -1 : /^after$/i.test(l) ? 1 : 0);
|
||||
return Object.keys(runs).sort((a, b) => {
|
||||
if (rank(a) !== rank(b)) return rank(a) - rank(b);
|
||||
return (runs[a].timestamp ?? '').localeCompare(runs[b].timestamp ?? '');
|
||||
});
|
||||
}
|
||||
|
||||
// Render one package's stored runs as the Δ-vs-baseline block.
|
||||
function renderBlock(data) {
|
||||
const labels = orderLabels(data.runs);
|
||||
const base = labels[0];
|
||||
const lines = [`=== ${data.package} — median times (Δ vs "${base}") ===`];
|
||||
|
||||
const tasks = new Set();
|
||||
for (const l of labels) for (const t of Object.keys(data.runs[l].tasks)) tasks.add(t);
|
||||
|
||||
for (const task of tasks) {
|
||||
lines.push('', `${task}:`);
|
||||
const baseMedian = data.runs[base].tasks[task]?.median;
|
||||
for (const label of labels) {
|
||||
const stat = data.runs[label].tasks[task];
|
||||
if (!stat) {
|
||||
lines.push(` ${label.padEnd(16)} (not measured)`);
|
||||
continue;
|
||||
}
|
||||
let delta = '';
|
||||
if (label !== base && baseMedian != null) {
|
||||
const diff = stat.median - baseMedian;
|
||||
const pct = ((diff / baseMedian) * 100).toFixed(1);
|
||||
const sign = diff <= 0 ? '' : '+';
|
||||
delta = ` ${sign}${fmtMs(diff)} (${sign}${pct}%)`;
|
||||
}
|
||||
lines.push(` ${label.padEnd(16)} ${fmtMs(stat.median).padStart(9)}${delta}`);
|
||||
}
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
||||
// One "before → after" delta per task for the overview table (first vs last label).
|
||||
function overviewDelta(data, task) {
|
||||
const labels = orderLabels(data.runs);
|
||||
const first = data.runs[labels[0]].tasks[task]?.median;
|
||||
const last = data.runs[labels[labels.length - 1]].tasks[task]?.median;
|
||||
if (labels.length < 2 || first == null || last == null) return '—';
|
||||
const pct = (((last - first) / first) * 100).toFixed(1);
|
||||
return `${last - first <= 0 ? '' : '+'}${pct}%`;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const { out } = parseArgs(process.argv.slice(2));
|
||||
|
||||
if (!existsSync(RESULTS_DIR)) {
|
||||
console.error(`\n✖ No results directory at ${RESULTS_DIR}. Run benchmark.mjs first.\n`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const files = readdirSync(RESULTS_DIR)
|
||||
.filter((f) => f.endsWith('.json'))
|
||||
.map((f) => JSON.parse(readFileSync(join(RESULTS_DIR, f), 'utf8')))
|
||||
.sort((a, b) => a.package.localeCompare(b.package));
|
||||
|
||||
if (files.length === 0) {
|
||||
console.error('\n✖ No result files found. Run benchmark.mjs first.\n');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const md = [];
|
||||
md.push('# TypeScript 6 → 7 migration benchmarks', '');
|
||||
md.push(`Generated ${new Date().toISOString()} from \`scripts/typescript-migration/results/\`.`, '');
|
||||
|
||||
// Overview table: median Δ (first → last label) per package.
|
||||
md.push('| Package | typecheck Δ | build Δ |', '| --- | --- | --- |');
|
||||
for (const data of files) {
|
||||
md.push(`| \`${data.package}\` | ${overviewDelta(data, 'typecheck')} | ${overviewDelta(data, 'build')} |`);
|
||||
}
|
||||
md.push('');
|
||||
|
||||
// Per-package detail blocks in the benchmark's own format.
|
||||
for (const data of files) {
|
||||
md.push('```', renderBlock(data), '```', '');
|
||||
}
|
||||
|
||||
mkdirSync(dirname(out), { recursive: true });
|
||||
writeFileSync(out, `${md.join('\n')}\n`);
|
||||
console.log(`✔ Wrote ${out} (${files.length} package${files.length === 1 ? '' : 's'})`);
|
||||
}
|
||||
|
||||
main();
|
||||
Reference in New Issue
Block a user