From 4b7f55c9e3c48bc18bf90125be8633b6a2375eab Mon Sep 17 00:00:00 2001 From: Matteo Collina Date: Tue, 5 Mar 2024 15:38:15 +0100 Subject: [PATCH] basic applying of semver ranges Signed-off-by: Matteo Collina --- package-lock.json | 328 +++---------------------------------- package.json | 7 +- src/lib/semgrator.ts | 33 +++- src/test/semgrator.test.ts | 128 ++++++++++++++- 4 files changed, 183 insertions(+), 313 deletions(-) diff --git a/package-lock.json b/package-lock.json index d5764b9..ffd5ed7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,19 +1,23 @@ { "name": "semgrator", - "version": "0.0.1", + "version": "0.0.6", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "semgrator", - "version": "0.0.1", + "version": "0.0.6", "license": "Apache-2.0", + "dependencies": { + "@types/semver": "^7.5.8", + "semver": "^7.6.0" + }, "devDependencies": { "@fastify/pre-commit": "^2.1.0", + "@matteo.collina/tspl": "^0.1.1", "@types/node": "^20.11.24", "borp": "^0.9.1", "prettier": "^3.2.5", - "tshy": "^1.11.1", "typescript": "^5.3.3" } }, @@ -126,6 +130,12 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@matteo.collina/tspl": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@matteo.collina/tspl/-/tspl-0.1.1.tgz", + "integrity": "sha512-jJFj8RzdExJGmZOVbyMViYkEgpyxqj/2InjRqnmFvYss+cXQEg47dTjADvL+ZGFRsJf6w5mtI5F+cNUBq1MVvA==", + "dev": true + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "dev": true, @@ -158,6 +168,11 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" + }, "node_modules/ansi-regex": { "version": "6.0.1", "dev": true, @@ -183,31 +198,11 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.3", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/balanced-match": { "version": "1.0.2", "dev": true, "license": "MIT" }, - "node_modules/binary-extensions": { - "version": "2.2.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/borp": { "version": "0.9.1", "dev": true, @@ -231,17 +226,6 @@ "balanced-match": "^1.0.0" } }, - "node_modules/braces": { - "version": "3.0.2", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/c8": { "version": "9.1.0", "dev": true, @@ -342,40 +326,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/chokidar": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/cliui": { "version": "8.0.1", "dev": true, @@ -529,17 +479,6 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/find-up": { "version": "7.0.0", "dev": true, @@ -576,18 +515,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "dev": true, @@ -663,25 +590,6 @@ "dev": true, "license": "ISC" }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "dev": true, @@ -690,25 +598,6 @@ "node": ">=8" } }, - "node_modules/is-glob": { - "version": "4.0.3", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/is-stream": { "version": "3.0.0", "dev": true, @@ -849,28 +738,6 @@ "node": ">=16 || 14 >=14.17" } }, - "node_modules/mkdirp": { - "version": "3.0.1", - "dev": true, - "license": "MIT", - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/npm-run-path": { "version": "5.3.0", "dev": true, @@ -985,28 +852,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/picomatch": { - "version": "2.3.1", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/polite-json": { - "version": "4.0.1", - "dev": true, - "license": "MIT", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/prettier": { "version": "3.2.5", "dev": true, @@ -1021,17 +866,6 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/readdirp": { - "version": "3.6.0", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, "node_modules/require-directory": { "version": "2.1.1", "dev": true, @@ -1040,25 +874,10 @@ "node": ">=0.10.0" } }, - "node_modules/resolve-import": { - "version": "1.4.5", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "glob": "^10.3.3", - "walk-up-path": "^3.0.1" - }, - "engines": { - "node": "16 >=16.17.0 || 18 >= 18.6.0 || >=20" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/semver": { "version": "7.6.0", - "dev": true, - "license": "ISC", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", + "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", "dependencies": { "lru-cache": "^6.0.0" }, @@ -1071,7 +890,6 @@ }, "node_modules/semver/node_modules/lru-cache": { "version": "6.0.0", - "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -1231,43 +1049,6 @@ "node": ">=8" } }, - "node_modules/sync-content": { - "version": "1.0.2", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "glob": "^10.2.6", - "mkdirp": "^3.0.1", - "path-scurry": "^1.9.2", - "rimraf": "^5.0.1" - }, - "bin": { - "sync-content": "dist/mjs/bin.mjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/sync-content/node_modules/rimraf": { - "version": "5.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/test-exclude": { "version": "6.0.0", "dev": true, @@ -1320,69 +1101,6 @@ "node": "*" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tshy": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/tshy/-/tshy-1.11.1.tgz", - "integrity": "sha512-AzATR8weBaUW46Nh4B1k5cfxVuADKJTXe95xHh7BzcI1RjQQy6HeUXQDY+erGEGTLpiv6N6xMFmtEsMMc7x40Q==", - "dev": true, - "dependencies": { - "chalk": "^5.3.0", - "chokidar": "^3.5.3", - "foreground-child": "^3.1.1", - "mkdirp": "^3.0.1", - "polite-json": "^4.0.1", - "resolve-import": "^1.4.4", - "rimraf": "^5.0.1", - "sync-content": "^1.0.2", - "typescript": "5.2 || 5.3", - "walk-up-path": "^3.0.1" - }, - "bin": { - "tshy": "dist/esm/index.js" - }, - "engines": { - "node": "16 >=16.17 || 18 >=18.15.0 || >=20.6.1" - } - }, - "node_modules/tshy/node_modules/chalk": { - "version": "5.3.0", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.17.0 || ^14.13 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/tshy/node_modules/rimraf": { - "version": "5.0.5", - "dev": true, - "license": "ISC", - "dependencies": { - "glob": "^10.3.7" - }, - "bin": { - "rimraf": "dist/esm/bin.mjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/tunnel": { "version": "0.0.6", "dev": true, @@ -1452,11 +1170,6 @@ "node": ">=10.12.0" } }, - "node_modules/walk-up-path": { - "version": "3.0.1", - "dev": true, - "license": "ISC" - }, "node_modules/which": { "version": "2.0.2", "dev": true, @@ -1567,7 +1280,6 @@ }, "node_modules/yallist": { "version": "4.0.0", - "dev": true, "license": "ISC" }, "node_modules/yargs": { diff --git a/package.json b/package.json index 467e5c8..5a7dd7a 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "homepage": "https://github.com/platformatic/semgrator#readme", "devDependencies": { "@fastify/pre-commit": "^2.1.0", + "@matteo.collina/tspl": "^0.1.1", "@types/node": "^20.11.24", "borp": "^0.9.1", "prettier": "^3.2.5", @@ -53,5 +54,9 @@ ], "type": "module", "main": "./dist/semgrator.js", - "types": "./dist/semgrator.d.ts" + "types": "./dist/semgrator.d.ts", + "dependencies": { + "@types/semver": "^7.5.8", + "semver": "^7.6.0" + } } diff --git a/src/lib/semgrator.ts b/src/lib/semgrator.ts index 5cb8296..26eb0e1 100644 --- a/src/lib/semgrator.ts +++ b/src/lib/semgrator.ts @@ -1 +1,32 @@ -export async function semgrator() {} +import semver from 'semver' + +export type Migration = { + version: string + up: (input: Input) => Promise | Output +} + +interface SemgratorParams { + version: string + migrations: Migration[] + input: Input +} + +interface SemgratorResult { + version: string + result: Output +} + +export async function semgrator( + params: SemgratorParams, +): Promise> { + let result = params.input as unknown + let lastVersion = params.version + for (const migration of params.migrations) { + if (semver.gt(migration.version, lastVersion)) { + result = await migration.up(result) + lastVersion = migration.version + } + } + + return { version: lastVersion, result: result as Output } +} diff --git a/src/test/semgrator.test.ts b/src/test/semgrator.test.ts index 3aa1351..67e364c 100644 --- a/src/test/semgrator.test.ts +++ b/src/test/semgrator.test.ts @@ -1,6 +1,128 @@ import { test } from 'node:test' -import { semgrator } from '../lib/semgrator.js' +import { semgrator, Migration } from '../lib/semgrator.js' +import { tspl } from '@matteo.collina/tspl' -test('semgrator', async t => { - await semgrator() +type SeenBy = { + foo: string + seenBy?: string +} + +test('apply all migrations', async t => { + const plan = tspl(t, { plan: 3 }) + const m1: Migration = { + version: '2.0.0', + async up(input) { + return { + ...input, + seenBy: '2.0.0', + } + }, + } + + const m2: Migration = { + version: '2.4.0', + up(input) { + plan.deepEqual(input, { + foo: 'bar', + seenBy: '2.0.0', + }) + return { + ...input, + seenBy: '2.4.0', + } + }, + } + + const res = await semgrator({ + version: '1.0.0', + migrations: [m1, m2], + input: { + foo: 'bar', + }, + }) + + plan.equal(res.version, '2.4.0') + plan.deepEqual(res.result, { + foo: 'bar', + seenBy: '2.4.0', + }) +}) + +test('apply only needed', async t => { + const plan = tspl(t, { plan: 3 }) + const m1: Migration = { + version: '2.0.0', + async up(input) { + plan.fail('should not be called') + return { + ...input, + seenBy: '2.0.0', + } + }, + } + + const m2: Migration = { + version: '2.4.0', + up(input) { + plan.deepEqual(input, { + foo: 'bar', + }) + return { + ...input, + seenBy: '2.4.0', + } + }, + } + + const res = await semgrator({ + version: '2.1.1', + migrations: [m1, m2], + input: { + foo: 'bar', + }, + }) + + plan.equal(res.version, '2.4.0') + plan.deepEqual(res.result, { + foo: 'bar', + seenBy: '2.4.0', + }) +}) + +test('apply no migrations', async t => { + const plan = tspl(t, { plan: 2 }) + const m1: Migration = { + version: '2.0.0', + async up(input) { + plan.fail('should not be called') + return { + ...input, + seenBy: '2.0.0', + } + }, + } + + const m2: Migration = { + version: '2.4.0', + up(input) { + plan.fail('should not be called') + return { + ...input, + seenBy: '2.4.0', + } + }, + } + + const res = await semgrator({ + version: '2.30.1', + migrations: [m1, m2], + input: { + foo: 'bar', + }, + }) + + plan.equal(res.version, '2.30.1') + plan.deepEqual(res.result, { + foo: 'bar', + }) })