Ocp node22 runtime migration
Skill optimizely/ocp-skills/skills/ocp-node22-runtime-migration
Use when migrating an OCP app from node18 to node22 runtime, upgrading SDK from 2.x to 3.x, replacing node-fetch with native fetch, migrating ESLint to v9, or migrating from Jest to Vitest in an OCP app.From its SKILL.md
npx -y skills add optimizely/ocp-skills --skill ocp-node22-runtime-migrationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
SKILL.md
7.6 KB, as published. Nobody here has run it
OCP Node22 Runtime Migration
Step-by-step guide for migrating OCP apps to node22 runtime and modernizing dependencies.
When to Use
- Migrating an OCP app from
node18tonode22runtime - Upgrading
@zaiusinc/app-sdkfrom1.xto2.xor from2.xto3.x - Replacing
node-fetchwith nativefetch - Migrating ESLint to v9 flat config
- Migrating from Jest to Vitest
Migration Paths
There are two sequential migration paths:
- node18 → node22 (SDK 1.x → 2.x) — Required first
- SDK 2.x → 3.x (modernization) — Optional, requires path 1 complete
Pre-Flight Checks
Before starting, verify:
- Current runtime in
app.yml(node18ornode22) - Current SDK version in
package.json - You are running the correct Node.js version (
node --version→v22.x.x)
Path 1: node18 → node22 (SDK 2.x)
Step 1: Update runtime
In app.yml:
runtime: node22
Step 2: Upgrade required dependencies
yarn add --ignore-engines "@zaiusinc/[email protected]" "@zaiusinc/[email protected]"
yarn add --dev --ignore-engines "@types/node@^22.15.17"
Step 3: Upgrade ESLint to v9
Remove old ESLint dependencies (ignore errors for missing packages):
yarn remove --ignore-engines @eslint/compat @stylistic/eslint-plugin @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-plugin-import eslint-plugin-jsdoc eslint-plugin-prefer-arrow eslint-plugin-react tslint @typescript-eslint/eslint-plugin-tslint @zaiusinc/tslint-presets
Install new ESLint:
yarn add --ignore-engines --dev "@zaiusinc/eslint-config-presets@^3.0.0" "eslint-plugin-jest@^28.11.0"
Create eslint.config.mjs (replaces .eslintrc.js):
import jest from 'eslint-plugin-jest';
import node from '@zaiusinc/eslint-config-presets/node.mjs';
export default [
...node,
{
files: ['**/*.test.ts'],
plugins: {
jest,
},
rules: {
'@typescript-eslint/unbound-method': 'off',
'jest/unbound-method': 'error',
},
}];
If the existing .eslintrc.js has customizations beyond the default, migrate them to the new flat config format. See ESLint v9 migration guide.
Run linter and fix:
yarn lint --fix
Remove old config:
rm .eslintrc.js
Step 4: Rebuild and validate
rm yarn.lock && rm -rf node_modules && yarn install
ocp app validate
Approve SDK version upgrades if prompted.
Step 5 (Optional): Upgrade TypeScript
yarn add --dev "typescript@^5.8.3"
ocp app validate
Step 6 (Optional): Replace @atao60/fse-cli with cpy-cli
yarn add --dev "cpy-cli@^6.0.0"
yarn remove --ignore-engines @atao60/fse-cli chalk
Update build script in package.json:
"build": "yarn && npx rimraf remove dist && npx tsc && cpy app.yml dist && cpy --up 1 \"src/**/*.{yml,yaml}\" dist"
Step 7 (Optional): Upgrade Jest
yarn add --dev "@types/jest@^29.5.14" "jest@^29.7.0"
ocp app validate
Step 8 (Optional): Upgrade other dev-dependencies
yarn add --dev "dotenv@^16.5.0" "rimraf@^6.0.1" "@types/node-fetch@^2.6.12"
Path 2: SDK 2.x → 3.x (Modernization)
Prerequisite: App already on node22 runtime with SDK 2.x.x.
Step 1: Upgrade SDK to 3.x
yarn add --ignore-engines "@zaiusinc/[email protected]" "@zaiusinc/[email protected]" "@zaiusinc/eslint-config-presets@^3.0.0"
Step 2: Remove node-fetch
yarn remove --ignore-engines node-fetch @types/node-fetch
Migrate all node-fetch imports to native fetch. Native fetch is a near drop-in replacement — remove import statements and adjust any type references.
Step 3: Clean up ESLint dependencies
Remove old ESLint dependencies (same list as Path 1 Step 3 — ignore errors for missing packages):
yarn remove --ignore-engines @eslint/compat @stylistic/eslint-plugin @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint eslint-plugin-import eslint-plugin-jsdoc eslint-plugin-prefer-arrow eslint-plugin-react tslint @typescript-eslint/eslint-plugin-tslint
Step 4: Rebuild and validate
rm yarn.lock && rm -rf node_modules && yarn install
ocp app validate
Step 5 (Optional): Replace copyfiles with cpy-cli
yarn add --dev "cpy-cli@^6.0.0"
yarn remove --ignore-engines copyfiles
Update build script in package.json:
"build": "yarn && npx rimraf remove dist && npx tsc && cpy app.yml dist && cpy --up 1 \"src/**/*.{yml,yaml}\" dist"
Step 6 (Optional): Migrate from Jest to Vitest
Remove Jest dependencies:
yarn remove --ignore-engines @types/jest jest ts-jest eslint-plugin-jest
Add Vitest:
yarn add --dev "vitest"
Update test script in package.json:
"test": "npx vitest run --passWithNoTests"
Create vitest.config.ts:
import { defineConfig } from 'vitest/config';
process.env.ZAIUS_ENV = 'test';
import('dotenv').then(dotenv => {
dotenv.config({ path: '.env.test' });
});
export default defineConfig({
test: {
environment: 'node',
include: ['src/**/*.test.ts'],
exclude: ['node_modules', 'dist'],
coverage: {
provider: 'v8',
include: ['src/**/*.ts'],
exclude: ['src/test/**/*', 'src/**/index.ts'],
},
globals: true,
},
});
Update eslint.config.mjs — remove jest, add vitest:
import node from '@zaiusinc/eslint-config-presets/node.mjs';
import vitest from '@zaiusinc/eslint-config-presets/vitest.mjs';
export default [
...node,
...vitest,
{
files: ['**/*.test.ts'],
rules: {
'@typescript-eslint/unbound-method': 'off'
},
}];
Migrate test files — replace jest references with vitest:
# Add vitest imports to all test files
find . -name "*.test.ts" -not -path "*/node_modules/*" -exec sed -i '' '1s/^/import { vi, beforeEach, afterEach, describe, it, expect } from "vitest";\n/' {} +
# Replace `as jest.Mock` with vitest equivalent
find . -type f \( -name "*.ts" -o -name "*.tsx" \) -not -path "*/node_modules/*" -exec sed -i '' 's/as jest\.Mock/as ReturnType<typeof vi.fn>;/g' {} +
# Replace `jest.` with `vi.`
find . -type f \( -name "*.ts" -o -name "*.tsx" \) -not -path "*/node_modules/*" -exec sed -i '' 's/jest\./vi./g' {} +
Fix up and validate:
yarn lint --fix
yarn build test
rm jest.config.js
ocp app validate
See vitest migration guide for edge cases.
Final Cleanup (Both Paths)
rm yarn.lock && rm -rf node_modules && yarn install
ocp app validate
Deploy a -dev or -beta version and test thoroughly before production release.
Typical Errors
| Error | Fix |
|---|---|
| Compilation errors from dependency upgrades | Fix individually |
| Third-party deps conflicting with SDK sub-dependencies | Upgrade the conflicting dependency |
Code incompatible with @types/[email protected] | Update code for new type definitions |
yarn install fails after lockfile removal | Upgrade incompatible deps with yarn add --ignore-engines [--dev] "<dep>@<version>" |
Quick Reference: --ignore-engines
Always use --ignore-engines flag when adding/removing dependencies during migration to bypass engine checks temporarily. The final yarn install (after removing yarn.lock) validates true compatibility.