diff --git a/packages/codemod/__tests__/package-json-update.spec.ts b/packages/codemod/__tests__/package-json-update.spec.ts index 84f7d00a5..4e156735e 100644 --- a/packages/codemod/__tests__/package-json-update.spec.ts +++ b/packages/codemod/__tests__/package-json-update.spec.ts @@ -1,100 +1,106 @@ -import * as fs from 'fs/promises'; -import * as glob from 'glob'; -import { findAndUpdatePackageJson } from '../scripts/package-json-update'; +import * as fs from "fs/promises"; +import * as glob from "glob"; +import { findAndUpdatePackageJson } from "../scripts/package-json-update"; -jest.mock('fs/promises'); -jest.mock('glob'); +jest.mock("fs/promises"); +jest.mock("glob"); -describe('Package JSON Update Script', () => { +describe("Package JSON Update Script", () => { const mockFs = fs as jest.Mocked; const mockGlob = glob as jest.Mocked; beforeEach(() => { - jest.spyOn(console, 'log').mockImplementation(); + jest.spyOn(console, "log").mockImplementation(); jest.clearAllMocks(); }); - it('should update dependencies and scripts in package.json', async () => { + it("should update dependencies and scripts in package.json", async () => { const mockPackageJson = { dependencies: { - 'edgedb': '^1.0.0', - '@edgedb/generate': '^2.0.0', - 'other-pkg': '^3.0.0' + edgedb: "^1.0.0", + "@edgedb/auth-nextjs": "^0.4.0", + "other-pkg": "^3.0.0", }, devDependencies: { - '@edgedb/auth': '^1.0.0' + "@edgedb/generate": "^2.0.0", }, scripts: { - generate: 'npx @edgedb/generate edgeql', - other: 'npx do-something' - } + generate: "npx @edgedb/generate edgeql && npx @edgedb/generate queries", + other: "npx do-something", + }, }; - mockGlob.sync.mockReturnValue(['/path/to/package.json']); + mockGlob.sync.mockReturnValue(["/path/to/package.json"]); mockFs.readFile.mockResolvedValue(JSON.stringify(mockPackageJson)); mockFs.writeFile.mockResolvedValue(); - await findAndUpdatePackageJson('/root'); + await findAndUpdatePackageJson("/root"); - expect(mockGlob.sync).toHaveBeenCalledWith('**/package.json', { - cwd: '/root', - ignore: ['**/node_modules/**'], - absolute: true + expect(mockGlob.sync).toHaveBeenCalledWith("**/package.json", { + cwd: "/root", + ignore: ["**/node_modules/**"], + absolute: true, }); - expect(mockFs.readFile).toHaveBeenCalledWith('/path/to/package.json', 'utf8'); + expect(mockFs.readFile).toHaveBeenCalledWith( + "/path/to/package.json", + "utf8", + ); const expectedPackageJson = { dependencies: { - 'other-pkg': '^3.0.0', - 'gel': '^1.0.0', - '@gel/generate': '^2.0.0' + "other-pkg": "^3.0.0", + gel: "^2.0.1", + "@gel/auth-nextjs": "^0.4.0", }, devDependencies: { - '@gel/auth': '^1.0.0' + "@gel/generate": "^0.6.2", }, scripts: { - generate: 'npx @gel/generate edgeql', - other: 'npx do-something' - } + generate: "npx @gel/generate edgeql && npx @gel/generate queries", + other: "npx do-something", + }, }; expect(mockFs.writeFile).toHaveBeenCalledWith( - '/path/to/package.json', - JSON.stringify(expectedPackageJson, null, 2) + '\n' + "/path/to/package.json", + JSON.stringify(expectedPackageJson, null, 2) + "\n", ); }); - it('should handle package.json without dependencies or scripts', async () => { + it("should handle package.json without dependencies or scripts", async () => { const mockPackageJson = { - name: 'test-package' + name: "test-package", }; - mockGlob.sync.mockReturnValue(['/path/to/package.json']); + mockGlob.sync.mockReturnValue(["/path/to/package.json"]); mockFs.readFile.mockResolvedValue(JSON.stringify(mockPackageJson)); mockFs.writeFile.mockResolvedValue(); - await findAndUpdatePackageJson('/root'); + await findAndUpdatePackageJson("/root"); expect(mockFs.readFile).toHaveBeenCalled(); expect(mockFs.writeFile).not.toHaveBeenCalled(); }); - it('should handle file read errors', async () => { - const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + it("should handle file read errors", async () => { + const consoleSpy = jest.spyOn(console, "error").mockImplementation(); - mockGlob.sync.mockReturnValue(['/path/to/package.json']); - mockFs.readFile.mockRejectedValue(new Error('File read error')); + mockGlob.sync.mockReturnValue(["/path/to/package.json"]); + mockFs.readFile.mockRejectedValue(new Error("File read error")); - await findAndUpdatePackageJson('/root'); + await findAndUpdatePackageJson("/root"); - expect(consoleSpy).toHaveBeenCalledWith('Error:', 'Error processing /path/to/package.json: File read error'); + expect(consoleSpy).toHaveBeenCalledWith( + "Error:", + "Error processing /path/to/package.json: File read error", + ); }); - it('should handle no package.json files found', async () => { + it("should handle no package.json files found", async () => { mockGlob.sync.mockReturnValue([]); - await findAndUpdatePackageJson('/root'); + await findAndUpdatePackageJson("/root"); expect(mockFs.readFile).not.toHaveBeenCalled(); expect(mockFs.writeFile).not.toHaveBeenCalled(); diff --git a/packages/codemod/package.json b/packages/codemod/package.json index ce2f50655..16268499f 100644 --- a/packages/codemod/package.json +++ b/packages/codemod/package.json @@ -23,6 +23,8 @@ "devDependencies": { "@types/debug": "^4.1.12", "@types/node": "^20.12.13", + "jest": "^29.7.0", + "ts-jest": "^29.2.6", "tsx": "^4.11.0", "typescript": "^5.5.2" }, diff --git a/packages/codemod/scripts/package-json-update.ts b/packages/codemod/scripts/package-json-update.ts index 7aad0eaeb..b3f474f39 100755 --- a/packages/codemod/scripts/package-json-update.ts +++ b/packages/codemod/scripts/package-json-update.ts @@ -1,64 +1,64 @@ -import * as fs from 'fs/promises'; -import * as glob from 'glob'; +import * as fs from "fs/promises"; +import * as glob from "glob"; const dependenciesMap = { - 'edgedb': 'gel', - '@edgedb/generate': '@gel/generate', - '@edgedb/auth': '@gel/auth', - '@edgedb/auth-core': '@gel/auth-core', - '@edgedb/auth-nextjs': '@gel/auth-nextjs', - '@edgedb/ai': '@gel/ai', - '@edgedb/auth-express': '@gel/auth-express', - '@edgedb/auth-sveltekit': '@gel/auth-sveltekit', - '@edgedb/auth-remix': '@gel/auth-remix', + edgedb: ["gel", "^2.0.1"], + "@edgedb/generate": ["@gel/generate", "^0.6.2"], + "@edgedb/ai": ["@gel/ai", "^0.1.0"], + "@edgedb/auth-core": ["@gel/auth-core", "^0.3.0"], + "@edgedb/auth-nextjs": ["@gel/auth-nextjs", "^0.4.0"], + "@edgedb/auth-express": ["@gel/auth-express", "^0.3.0"], + "@edgedb/auth-sveltekit": ["@gel/auth-sveltekit", "^0.3.0"], + "@edgedb/auth-remix": ["@gel/auth-remix", "^0.3.0"], }; async function updatePackageJson(filePath: string): Promise { const changes: string[] = []; try { - const content = await fs.readFile(filePath, 'utf8'); + const content = await fs.readFile(filePath, "utf8"); const pkg = JSON.parse(content); let modified = false; - for (const [oldPkg, newPkg] of Object.entries(dependenciesMap)) { + for (const [oldPkg, [newPkg, version]] of Object.entries(dependenciesMap)) { if (pkg.dependencies?.[oldPkg]) { - pkg.dependencies[newPkg] = pkg.dependencies[oldPkg]; + pkg.dependencies[newPkg] = version; delete pkg.dependencies[oldPkg]; - changes.push(`Replaced ${oldPkg} with ${newPkg} in dependencies`); + changes.push( + `Replaced ${oldPkg} with ${newPkg}@${version} in dependencies`, + ); modified = true; } if (pkg.devDependencies?.[oldPkg]) { - pkg.devDependencies[newPkg] = pkg.devDependencies[oldPkg]; + pkg.devDependencies[newPkg] = version; delete pkg.devDependencies[oldPkg]; - changes.push(`Replaced ${oldPkg} with ${newPkg} in devDependencies`); + changes.push( + `Replaced ${oldPkg} with ${newPkg}@${version} in devDependencies`, + ); modified = true; } } if (pkg.scripts) { - const updatedScripts: Record = {}; - for (const [scriptName, scriptValue] of Object.entries(pkg.scripts)) { - const updatedValue = (scriptValue as string) - .replace(/@edgedb\/generate/g, '@gel/generate'); - - updatedScripts[scriptName] = updatedValue; + const updatedValue = (scriptValue as string).replace( + /@edgedb\/generate/g, + "@gel/generate", + ); if (updatedValue !== scriptValue) { modified = true; - changes.push(`Updated script "${scriptName}": ${scriptValue} -> ${updatedValue}`); + pkg.scripts[scriptName] = updatedValue; + changes.push( + `Updated script "${scriptName}": ${scriptValue} -> ${updatedValue}`, + ); } } - - if (Object.keys(updatedScripts).length > 0 && Object.keys(updatedScripts).length === Object.keys(pkg.scripts).length) { - pkg.scripts = updatedScripts; - } } if (modified) { - await fs.writeFile(filePath, JSON.stringify(pkg, null, 2) + '\n'); + await fs.writeFile(filePath, JSON.stringify(pkg, null, 2) + "\n"); } return changes; @@ -69,13 +69,15 @@ async function updatePackageJson(filePath: string): Promise { export async function findAndUpdatePackageJson(rootDir: string) { try { - const files = glob.sync('**/package.json', { + const files = glob.sync("**/package.json", { cwd: rootDir, - ignore: ['**/node_modules/**'], - absolute: true + ignore: ["**/node_modules/**"], + absolute: true, }); - console.log(`Found ${files.length} package.json ${files.length === 1 ? 'file' : 'files'}`); + console.log( + `Found ${files.length} package.json ${files.length === 1 ? "file" : "files"}`, + ); for (const file of files) { console.log(`Processing ${file}...`); @@ -83,12 +85,12 @@ export async function findAndUpdatePackageJson(rootDir: string) { if (changes.length > 0) { console.log(`Changes in ${file}:`); - changes.forEach(change => console.log(` - ${change}`)); + changes.forEach((change) => console.log(` - ${change}`)); } else { console.log(` No changes needed`); } } } catch (error: any) { - console.error('Error:', error.message); + console.error("Error:", error.message); } } diff --git a/turbo.json b/turbo.json index e85c1f4bd..1509a83be 100644 --- a/turbo.json +++ b/turbo.json @@ -25,7 +25,13 @@ ] }, "ci:test": { - "dependsOn": ["build", "gel#test", "@gel/auth-core#test"], + "dependsOn": [ + "build", + "gel#test", + "@gel/generate#test", + "@gel/auth-core#test", + "@gel/codemod#test" + ], "env": ["CI", "GEL_SERVER_BIN"] }, "ci:integration-test": { diff --git a/yarn.lock b/yarn.lock index 504e8b656..a9fd302c8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2528,6 +2528,11 @@ ast-types@^0.16.1: dependencies: tslib "^2.0.1" +async@^3.2.3: + version "3.2.6" + resolved "https://registry.yarnpkg.com/async/-/async-3.2.6.tgz#1b0728e14929d51b85b449b7f06e27c1145e38ce" + integrity sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA== + asynckit@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz" @@ -2665,7 +2670,7 @@ browserslist@^4.24.0: node-releases "^2.0.18" update-browserslist-db "^1.1.0" -bs-logger@0.x: +bs-logger@0.x, bs-logger@^0.2.6: version "0.2.6" resolved "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz" integrity sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog== @@ -2758,7 +2763,7 @@ chalk@^2.4.2: escape-string-regexp "^1.0.5" supports-color "^5.3.0" -chalk@^4.0.0, chalk@^4.1.2: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3125,6 +3130,13 @@ ee-first@1.1.1: resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz" integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== +ejs@^3.1.10: + version "3.1.10" + resolved "https://registry.yarnpkg.com/ejs/-/ejs-3.1.10.tgz#69ab8358b14e896f80cc39e62087b88500c3ac3b" + integrity sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA== + dependencies: + jake "^10.8.5" + electron-to-chromium@^1.4.668: version "1.4.786" resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.786.tgz" @@ -3579,6 +3591,13 @@ file-entry-cache@^8.0.0: dependencies: flat-cache "^4.0.0" +filelist@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/filelist/-/filelist-1.0.4.tgz#f78978a1e944775ff9e62e744424f215e58352b5" + integrity sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q== + dependencies: + minimatch "^5.0.1" + fill-range@^7.1.1: version "7.1.1" resolved "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz" @@ -4157,6 +4176,16 @@ jackspeak@^3.1.2: optionalDependencies: "@pkgjs/parseargs" "^0.11.0" +jake@^10.8.5: + version "10.9.2" + resolved "https://registry.yarnpkg.com/jake/-/jake-10.9.2.tgz#6ae487e6a69afec3a5e167628996b59f35ae2b7f" + integrity sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA== + dependencies: + async "^3.2.3" + chalk "^4.0.2" + filelist "^1.0.4" + minimatch "^3.1.2" + jest-changed-files@^29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz" @@ -4519,7 +4548,7 @@ jest-worker@^29.7.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@29.7.0, jest@^29.7.0: +jest@29.7.0: version "29.7.0" resolved "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz" integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== @@ -4529,6 +4558,16 @@ jest@29.7.0, jest@^29.7.0: import-local "^3.0.2" jest-cli "^29.7.0" +jest@^29.7.0: + version "29.7.0" + resolved "https://registry.yarnpkg.com/jest/-/jest-29.7.0.tgz#994676fc24177f088f1c5e3737f5697204ff2613" + integrity sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw== + dependencies: + "@jest/core" "^29.7.0" + "@jest/types" "^29.6.3" + import-local "^3.0.2" + jest-cli "^29.7.0" + joycon@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz" @@ -4750,7 +4789,7 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" -lodash.memoize@4.x: +lodash.memoize@4.x, lodash.memoize@^4.1.2: version "4.1.2" resolved "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz" integrity sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag== @@ -4804,7 +4843,7 @@ make-dir@^4.0.0: dependencies: semver "^7.5.3" -make-error@1.x: +make-error@1.x, make-error@^1.3.6: version "1.3.6" resolved "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz" integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== @@ -4891,6 +4930,13 @@ minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.6" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.6.tgz#1cfcb8cf5522ea69952cd2af95ae09477f122a96" + integrity sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g== + dependencies: + brace-expansion "^2.0.1" + minimatch@^9.0.4: version "9.0.4" resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.4.tgz" @@ -5590,6 +5636,11 @@ semver@^7.6.3: resolved "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz" integrity sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A== +semver@^7.7.1: + version "7.7.1" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.1.tgz#abd5098d82b18c6c81f6074ff2647fd3e7220c9f" + integrity sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA== + send@0.18.0: version "0.18.0" resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz" @@ -6152,6 +6203,21 @@ ts-jest@29.1.4, ts-jest@^29.1.4: semver "^7.5.3" yargs-parser "^21.0.1" +ts-jest@^29.2.6: + version "29.2.6" + resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-29.2.6.tgz#df53edf8b72fb89de032cfa310abf37582851d9a" + integrity sha512-yTNZVZqc8lSixm+QGVFcPe6+yj7+TWZwIesuOWvfcn4B9bz5x4NDzVCQQjOs7Hfouu36aEqfEbo9Qpo+gq8dDg== + dependencies: + bs-logger "^0.2.6" + ejs "^3.1.10" + fast-json-stable-stringify "^2.1.0" + jest-util "^29.0.0" + json5 "^2.2.3" + lodash.memoize "^4.1.2" + make-error "^1.3.6" + semver "^7.7.1" + yargs-parser "^21.1.1" + tslib@^2.0.1: version "2.8.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.1.tgz#612efe4ed235d567e8aba5f2a5fab70280ade83f"