From 5fe340fe8069f008599a4f7390b9138063c8392e Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sat, 8 Apr 2023 19:28:18 +0100 Subject: [PATCH 1/6] Use version of prettier from package.json --- .pre-commit-config.yaml | 10 ++- package.json | 5 +- packages/meta-updater/package.json | 5 +- packages/meta-updater/src/formats.ts | 24 ++++++ packages/meta-updater/src/getPackageDeps.ts | 27 ++++++ packages/meta-updater/src/metaUpdater.ts | 10 ++- packages/meta-updater/src/updatePreCommit.ts | 67 +++++++++++++++ packages/meta-updater/src/updateTSConfig.ts | 16 +--- patches/@pnpm__meta-updater@0.2.2.patch | 27 ++++++ pnpm-lock.yaml | 88 +++++++++++++++----- 10 files changed, 236 insertions(+), 43 deletions(-) create mode 100644 packages/meta-updater/src/formats.ts create mode 100644 packages/meta-updater/src/getPackageDeps.ts create mode 100644 packages/meta-updater/src/updatePreCommit.ts create mode 100644 patches/@pnpm__meta-updater@0.2.2.patch diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index def49a990d..abef0aaee5 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,10 +36,16 @@ repos: # document contents. For example # packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/ruby/changeCondition.yml exclude: ^packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/.*/[^/]*\.yml$ - - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v2.7.1" + - repo: local hooks: - id: prettier + name: prettier + types: [text] + language: node + entry: prettier --write --list-different --ignore-unknown + additional_dependencies: + - prettier@2.7.1 + args: [] - repo: https://github.com/ikamensh/flynt/ rev: "0.78" hooks: diff --git a/package.json b/package.json index 1d6fc01676..1e23f7bf0a 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "watch": "tsc --build --watch", "init-vscode-sandbox": "pnpm --filter=@cursorless/cursorless-vscode-core init-launch-sandbox", "meta-updater:base": "pnpm --filter=@cursorless/meta-updater build && meta-updater", - "meta-updater": "pnpm run meta-updater:base && pnpm -r exec prettier --write tsconfig.json package.json", + "meta-updater": "pnpm run meta-updater:base && pnpm -r exec prettier --write tsconfig.json package.json && pnpm -w exec prettier --write .pre-commit-config.yaml", "lint:meta": "pnpm run meta-updater:base --test", "lint": "pnpm run lint:meta && syncpack list-mismatches && pnpm run lint:ts", "lint:ts": "eslint packages --ext ts", @@ -40,7 +40,8 @@ }, "pnpm": { "patchedDependencies": { - "@docusaurus/theme-search-algolia@2.3.1": "patches/@docusaurus__theme-search-algolia@2.3.1.patch" + "@docusaurus/theme-search-algolia@2.3.1": "patches/@docusaurus__theme-search-algolia@2.3.1.patch", + "@pnpm/meta-updater@0.2.2": "patches/@pnpm__meta-updater@0.2.2.patch" }, "peerDependencyRules": { "ignoreMissing": [ diff --git a/packages/meta-updater/package.json b/packages/meta-updater/package.json index 1fbef062c6..3e9e7fe370 100644 --- a/packages/meta-updater/package.json +++ b/packages/meta-updater/package.json @@ -15,7 +15,9 @@ "@types/normalize-path": "^3.0.0", "normalize-path": "^3.0.0", "path-exists": "^4.0.0", - "type-fest": "3.6.1" + "ramda": "0.29.0", + "type-fest": "3.6.1", + "yaml": "2.2.1" }, "main": "./out/index.js", "types": "./out/index.d.ts", @@ -27,6 +29,7 @@ } }, "devDependencies": { + "@types/ramda": "0.28.23", "esbuild": "^0.17.11" } } diff --git a/packages/meta-updater/src/formats.ts b/packages/meta-updater/src/formats.ts new file mode 100644 index 0000000000..74b784ffbf --- /dev/null +++ b/packages/meta-updater/src/formats.ts @@ -0,0 +1,24 @@ +import { createFormat } from "@pnpm/meta-updater"; +import { readFile, writeFile } from "fs/promises"; +import { equals } from "ramda"; +import yaml from "yaml"; + +export const formats = { + [".yaml"]: createFormat({ + async read({ resolvedPath }) { + return yaml.parseDocument(await readFile(resolvedPath, "utf-8")).clone(); + }, + update(actual, updater, options) { + return updater(actual, options); + }, + equal(expected, actual) { + return equals(actual.toJS(), expected.toJS()); + }, + async write(expected, { resolvedPath }) { + await writeFile(resolvedPath, expected.toString()); + }, + clone(content) { + return content == null ? content : content.clone(); + }, + }), +}; diff --git a/packages/meta-updater/src/getPackageDeps.ts b/packages/meta-updater/src/getPackageDeps.ts new file mode 100644 index 0000000000..d4f22b0dfe --- /dev/null +++ b/packages/meta-updater/src/getPackageDeps.ts @@ -0,0 +1,27 @@ +import normalizePath from "normalize-path"; +import path from "path"; +import { Lockfile } from "@pnpm/lockfile-file"; + +export function getPackageDeps( + workspaceDir: string, + packageDir: string, + pnpmLockfile: Lockfile, +) { + const pathFromRootToPackage = + packageDir === workspaceDir + ? "." + : normalizePath(path.relative(workspaceDir, packageDir)); + + /** Info about package dependencies gleaned from lock file. */ + const lockFilePackageInfo = pnpmLockfile.importers[pathFromRootToPackage]; + if (!lockFilePackageInfo) { + // Raise an error here because there should always be an entry in the lockfile. + throw new Error(`No importer found for ${pathFromRootToPackage}`); + } + + const deps = { + ...lockFilePackageInfo.dependencies, + ...lockFilePackageInfo.devDependencies, + }; + return deps; +} diff --git a/packages/meta-updater/src/metaUpdater.ts b/packages/meta-updater/src/metaUpdater.ts index 0b4acae115..4ffe2ea913 100644 --- a/packages/meta-updater/src/metaUpdater.ts +++ b/packages/meta-updater/src/metaUpdater.ts @@ -7,6 +7,8 @@ import { createUpdateOptions } from "@pnpm/meta-updater"; import { Context } from "./Context"; import { updatePackageJson } from "./updatePackageJson"; import { updateTSConfig } from "./updateTSConfig"; +import { updatePreCommit } from "./updatePreCommit"; +import { formats } from "./formats"; export const updater = async (workspaceDir: string) => { const pnpmLockfile = await readWantedLockfile(workspaceDir, { @@ -23,7 +25,11 @@ export const updater = async (workspaceDir: string) => { }; return createUpdateOptions({ - ["package.json"]: updatePackageJson.bind(null, context), - ["tsconfig.json"]: updateTSConfig.bind(null, context), + files: { + ["package.json"]: updatePackageJson.bind(null, context), + ["tsconfig.json"]: updateTSConfig.bind(null, context), + [".pre-commit-config.yaml"]: updatePreCommit.bind(null, context), + }, + formats, }); }; diff --git a/packages/meta-updater/src/updatePreCommit.ts b/packages/meta-updater/src/updatePreCommit.ts new file mode 100644 index 0000000000..bf5130c490 --- /dev/null +++ b/packages/meta-updater/src/updatePreCommit.ts @@ -0,0 +1,67 @@ +import type { FormatPluginFnOptions } from "@pnpm/meta-updater"; +import { Document, ParsedNode } from "yaml"; +import { Context } from "./Context"; +import { getPackageDeps } from "./getPackageDeps"; + +interface PreCommitConfig { + repos: { + hooks: { + id: string; + // eslint-disable-next-line @typescript-eslint/naming-convention + additional_dependencies: string[]; + }[]; + }[]; +} + +/** + * Given a tsconfig.json, update it to match our conventions. This function is + * called by the pnpm `meta-updater` plugin either to check if the tsconfig.json + * is up to date or to update it, depending on flags. + * @param context Contains context such as workspace dir and parsed pnpm + * lockfile + * @param rawInput The input tsconfig.json that should be checked / updated + * @param options Extra information provided by pnpm; mostly just the directory + * of the package whose tsconfig.json we are updating + * @returns The updated tsconfig.json + */ +export async function updatePreCommit( + { workspaceDir, pnpmLockfile }: Context, + rawInput: Document | null, + options: FormatPluginFnOptions, +): Promise | null> { + if (rawInput == null) { + return null; + } + /** Directory of the package whose tsconfig.json we are updating */ + const packageDir = options.dir; + + if (packageDir !== workspaceDir) { + throw new Error("updatePreCommit should only be called on root"); + } + + const deps = getPackageDeps(workspaceDir, packageDir, pnpmLockfile); + const prettierVersion = deps["prettier"]; + + const prettierHookIndex = (rawInput.toJS() as PreCommitConfig).repos + .flatMap(({ hooks }, repoIndex) => + hooks.map((hook, hookIndex) => ({ hook, repoIndex, hookIndex })), + ) + .filter(({ hook }) => hook.id === "prettier"); + + if (prettierHookIndex.length === 0) { + throw new Error("No prettier hook found"); + } + + if (prettierHookIndex.length > 1) { + throw new Error("Multiple prettier hooks found"); + } + + const { repoIndex, hookIndex } = prettierHookIndex[0]; + + rawInput.setIn( + ["repos", repoIndex, "hooks", hookIndex, "additional_dependencies"], + rawInput.createNode([`prettier@${prettierVersion}`]), + ); + + return rawInput; +} diff --git a/packages/meta-updater/src/updateTSConfig.ts b/packages/meta-updater/src/updateTSConfig.ts index 72224c495b..ce82d57000 100644 --- a/packages/meta-updater/src/updateTSConfig.ts +++ b/packages/meta-updater/src/updateTSConfig.ts @@ -5,6 +5,7 @@ import exists from "path-exists"; import { TsConfigJson } from "type-fest"; import { toPosixPath } from "./toPosixPath"; import { Context } from "./Context"; +import { getPackageDeps } from "./getPackageDeps"; /** * Given a tsconfig.json, update it to match our conventions. This function is @@ -41,24 +42,11 @@ export async function updateTSConfig( }; } - const pathFromRootToPackage = normalizePath( - path.relative(workspaceDir, packageDir), - ); const pathFromPackageToRoot = normalizePath( path.relative(packageDir, workspaceDir), ); - /** Info about package dependencies gleaned from lock file. */ - const lockFilePackageInfo = pnpmLockfile.importers[pathFromRootToPackage]; - if (!lockFilePackageInfo) { - // Raise an error here because there should always be an entry in the lockfile. - throw new Error(`No importer found for ${pathFromRootToPackage}`); - } - - const deps = { - ...lockFilePackageInfo.dependencies, - ...lockFilePackageInfo.devDependencies, - }; + const deps = getPackageDeps(workspaceDir, packageDir, pnpmLockfile); /** Computed tsconfig.json references based on dependencies. */ const references = [] as Array<{ path: string }>; diff --git a/patches/@pnpm__meta-updater@0.2.2.patch b/patches/@pnpm__meta-updater@0.2.2.patch new file mode 100644 index 0000000000..4b5abeb594 --- /dev/null +++ b/patches/@pnpm__meta-updater@0.2.2.patch @@ -0,0 +1,27 @@ +diff --git a/lib/index.js b/lib/index.js +index d24112f5fedba5a9d7c5bd85221652a8f0bf09e1..2af95628f80926b8c4c9c774d9135a95b465959c 100644 +--- a/lib/index.js ++++ b/lib/index.js +@@ -48,7 +48,8 @@ export async function performUpdates(workspaceDir, updateParam, opts) { + _writeProjectManifest: writeProjectManifest, + }; + const actual = (await fileExists(resolvedPath)) ? await formatPlugin.read(formatHandlerOptions) : null; +- const expected = await formatPlugin.update(clone(actual), updateFile, formatHandlerOptions); ++ const customClone = formatPlugin.clone == null ? clone : formatPlugin.clone; ++ const expected = await formatPlugin.update(customClone(actual), updateFile, formatHandlerOptions); + const equal = (actual == null && expected == null) || + (actual != null && expected != null && (await formatPlugin.equal(expected, actual, formatHandlerOptions))); + if (equal) { +diff --git a/lib/updater/formatPlugin.d.ts b/lib/updater/formatPlugin.d.ts +index 887845721b09e17b95dd0789a5db8de72c04a654..2e47ba30b25661cec37d3ab1dcca1d8908a1eb21 100644 +--- a/lib/updater/formatPlugin.d.ts ++++ b/lib/updater/formatPlugin.d.ts +@@ -12,6 +12,8 @@ export interface FormatPlugin { + equal(expected: Content, actual: Content, options: FormatPluginFnOptions): PromiseOrValue; + /** Called only if write is required (`--test` isn't specified, `expected != null` and `expected` is not equal to `actual`) */ + write(expected: Content, options: FormatPluginFnOptions): PromiseOrValue; ++ /** Can be used to override the built-in clone functionality */ ++ clone?(content: Content): PromiseOrValue; + } + export interface FormatPluginFnOptions { + file: string; \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1e573dfaa9..c7702618f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ patchedDependencies: '@docusaurus/theme-search-algolia@2.3.1': hash: dfty3zxmjzsb7sg6jf27rbwooe path: patches/@docusaurus__theme-search-algolia@2.3.1.patch + '@pnpm/meta-updater@0.2.2': + hash: uuvdcaez52lw2ukwi5p6vh3sby + path: patches/@pnpm__meta-updater@0.2.2.patch importers: @@ -11,7 +14,7 @@ importers: devDependencies: '@pnpm/meta-updater': specifier: 0.2.2 - version: 0.2.2(typanion@3.12.1) + version: 0.2.2(patch_hash=uuvdcaez52lw2ukwi5p6vh3sby)(typanion@3.12.1) '@types/node': specifier: ^16.11.3 version: 16.18.13 @@ -528,10 +531,19 @@ importers: path-exists: specifier: ^4.0.0 version: 4.0.0 + ramda: + specifier: 0.29.0 + version: 0.29.0 type-fest: specifier: 3.6.1 version: 3.6.1 + yaml: + specifier: 2.2.1 + version: 2.2.1 devDependencies: + '@types/ramda': + specifier: 0.28.23 + version: 0.28.23 esbuild: specifier: ^0.17.11 version: 0.17.11 @@ -692,6 +704,14 @@ packages: '@jridgewell/gen-mapping': 0.1.1 '@jridgewell/trace-mapping': 0.3.17 + /@ampproject/remapping@2.2.1: + resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 + dev: true + /@arcanis/slice-ansi@1.1.1: resolution: {integrity: sha512-xguP2WR2Dv0gQ7Ykbdb7BNCnPnIPB94uTi0Z2NvkRBEnhbwjOQ7QyQKJXrVQg4qDpiD9hA5l5cCwy/z2OXgc3w==} dependencies: @@ -770,7 +790,7 @@ packages: resolution: {integrity: sha512-qt/YV149Jman/6AfmlxJ04LMIu8bMoyl3RB91yTFrxQmgbrSvQMy7cI8Q62FHx1t8wJ8B5fu0UDoLwHAhUo1QA==} engines: {node: '>=6.9.0'} dependencies: - '@ampproject/remapping': 2.2.0 + '@ampproject/remapping': 2.2.1 '@babel/code-frame': 7.21.4 '@babel/generator': 7.21.4 '@babel/helper-compilation-targets': 7.21.4(@babel/core@7.21.4) @@ -803,8 +823,8 @@ packages: engines: {node: '>=6.9.0'} dependencies: '@babel/types': 7.21.4 - '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.17 + '@jridgewell/gen-mapping': 0.3.3 + '@jridgewell/trace-mapping': 0.3.18 jsesc: 2.5.2 dev: true @@ -2609,7 +2629,7 @@ packages: peerDependencies: react: '*' dependencies: - '@types/react': 18.0.32 + '@types/react': 18.0.28 prop-types: 15.8.1 react: 18.2.0 dev: false @@ -3419,6 +3439,15 @@ packages: '@jridgewell/sourcemap-codec': 1.4.14 '@jridgewell/trace-mapping': 0.3.17 + /@jridgewell/gen-mapping@0.3.3: + resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} + engines: {node: '>=6.0.0'} + dependencies: + '@jridgewell/set-array': 1.1.2 + '@jridgewell/sourcemap-codec': 1.4.15 + '@jridgewell/trace-mapping': 0.3.18 + dev: true + /@jridgewell/resolve-uri@3.1.0: resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} engines: {node: '>=6.0.0'} @@ -3436,12 +3465,23 @@ packages: /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + dev: true + /@jridgewell/trace-mapping@0.3.17: resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} dependencies: '@jridgewell/resolve-uri': 3.1.0 '@jridgewell/sourcemap-codec': 1.4.14 + /@jridgewell/trace-mapping@0.3.18: + resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} + dependencies: + '@jridgewell/resolve-uri': 3.1.0 + '@jridgewell/sourcemap-codec': 1.4.14 + dev: true + /@jridgewell/trace-mapping@0.3.9: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: @@ -4513,7 +4553,7 @@ packages: ramda: /@pnpm/ramda@0.28.1 semver: 7.3.8 - /@pnpm/meta-updater@0.2.2(typanion@3.12.1): + /@pnpm/meta-updater@0.2.2(patch_hash=uuvdcaez52lw2ukwi5p6vh3sby)(typanion@3.12.1): resolution: {integrity: sha512-wh3LdQYM1aTl4vbuh+7Lv6amaDrQTu+iAIs0qsovIVnQfhHvRpwEdICHZTKC/xdthd4uGUmNZRvhoITzGLu1tg==} engines: {node: '>=10.12'} hasBin: true @@ -4534,6 +4574,7 @@ packages: - supports-color - typanion dev: true + patched: true /@pnpm/modules-cleaner@13.0.12(@pnpm/logger@5.0.0): resolution: {integrity: sha512-TIct5tlxRQbsfmJgnIHj1ZroAjivGwm5SAIqs9b3WgeJE9Q/oXqAl3uWHTwdlXfGu3QNDaEfsvzB3aCDVUeZLg==} @@ -5553,6 +5594,12 @@ packages: /@types/qs@6.9.7: resolution: {integrity: sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==} + /@types/ramda@0.28.23: + resolution: {integrity: sha512-9TYWiwkew+mCMsL7jZ+kkzy6QXn8PL5/SKmBPmjgUlTpkokZWTBr+OhiIUDztpAEbslWyt24NNfEmZUBFmnXig==} + dependencies: + ts-toolbelt: 6.15.5 + dev: true + /@types/range-parser@1.2.4: resolution: {integrity: sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==} @@ -5598,14 +5645,6 @@ packages: '@types/scheduler': 0.16.2 csstype: 3.1.1 - /@types/react@18.0.32: - resolution: {integrity: sha512-gYGXdtPQ9Cj0w2Fwqg5/ak6BcK3Z15YgjSqtyDizWUfx7mQ8drs0NBUzRRsAdoFVTO8kJ8L2TL8Skm7OFPnLUw==} - dependencies: - '@types/prop-types': 15.7.5 - '@types/scheduler': 0.16.3 - csstype: 3.1.2 - dev: false - /@types/responselike@1.0.0: resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==} dependencies: @@ -5623,10 +5662,6 @@ packages: /@types/scheduler@0.16.2: resolution: {integrity: sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==} - /@types/scheduler@0.16.3: - resolution: {integrity: sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==} - dev: false - /@types/semver@6.2.3: resolution: {integrity: sha512-KQf+QAMWKMrtBMsB8/24w53tEsxllMj6TuA80TT/5igJalLI/zm0L3oXRbIAl4Ohfc85gyHX/jhMwsVkmhLU4A==} dev: true @@ -8015,10 +8050,6 @@ packages: /csstype@3.1.1: resolution: {integrity: sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw==} - /csstype@3.1.2: - resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} - dev: false - /damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} dev: false @@ -13832,6 +13863,10 @@ packages: resolution: {integrity: sha512-9QnLuG/kPVgWvMQ4aODhsBUFKOUmnbUnsSXACv+NCQZcHbeb+v8Lodp8OVxtRULN1/xOyYLLaL6npE6dMq5QTA==} dev: true + /ramda@0.29.0: + resolution: {integrity: sha512-BBea6L67bYLtdbOqfp8f58fPMqEwx0doL+pAi8TZyp2YWz8R9G8z9x75CZI8W+ftqhFHCpEX2cRnUUXK130iKA==} + dev: false + /randombytes@2.1.0: resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} dependencies: @@ -15802,6 +15837,10 @@ packages: v8-compile-cache-lib: 3.0.1 yn: 3.1.1 + /ts-toolbelt@6.15.5: + resolution: {integrity: sha512-FZIXf1ksVyLcfr7M317jbB67XFJhOO1YqdTcuGaq9q5jLUoTikukZ+98TPjKiP2jC5CgmYdWWYs0s2nLSU0/1A==} + dev: true + /tsconfig-paths@3.14.2: resolution: {integrity: sha512-o/9iXgCYc5L/JxCHPe3Hvh8Q/2xm5Z+p18PESBU6Ff33695QnCHBEjcytY2q19ua7Mbl/DavtBOLq+oG0RCL+g==} dependencies: @@ -16935,6 +16974,11 @@ packages: resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} engines: {node: '>= 6'} + /yaml@2.2.1: + resolution: {integrity: sha512-e0WHiYql7+9wr4cWMx3TVQrNwejKaEe7/rHNmQmqRjazfOP5W8PB6Jpebb5o6fIapbz9o9+2ipcaTM2ZwDI6lw==} + engines: {node: '>= 14'} + dev: false + /yargs-parser@20.2.4: resolution: {integrity: sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==} engines: {node: '>=10'} From 5a937c631c74e65a84ca8a03f9e0ace8a6109250 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sat, 8 Apr 2023 22:17:24 +0100 Subject: [PATCH 2/6] Clean up in prep for usint eslint fixer --- packages/meta-updater/src/formats.ts | 5 ++ packages/meta-updater/src/getPackageDeps.ts | 9 +++ packages/meta-updater/src/updatePreCommit.ts | 80 +++++++++++++++----- 3 files changed, 76 insertions(+), 18 deletions(-) diff --git a/packages/meta-updater/src/formats.ts b/packages/meta-updater/src/formats.ts index 74b784ffbf..0a3f0d439f 100644 --- a/packages/meta-updater/src/formats.ts +++ b/packages/meta-updater/src/formats.ts @@ -4,6 +4,11 @@ import { equals } from "ramda"; import yaml from "yaml"; export const formats = { + /** + * A format that today we just use for .pre-commit-config.yaml files. This is a yaml file, but we + * need to preserve comments, so we use the `yaml` library's document representation instead of + * parsing it into a plain js object. + */ [".yaml"]: createFormat({ async read({ resolvedPath }) { return yaml.parseDocument(await readFile(resolvedPath, "utf-8")).clone(); diff --git a/packages/meta-updater/src/getPackageDeps.ts b/packages/meta-updater/src/getPackageDeps.ts index d4f22b0dfe..b93791c47c 100644 --- a/packages/meta-updater/src/getPackageDeps.ts +++ b/packages/meta-updater/src/getPackageDeps.ts @@ -2,6 +2,15 @@ import normalizePath from "normalize-path"; import path from "path"; import { Lockfile } from "@pnpm/lockfile-file"; +/** + * Get the dependencies of the given package from the pnpm lockfile. + * @param workspaceDir The root of the workspace + * @param packageDir The directory of the package whose dependencies we are + * retrieving + * @param pnpmLockfile The parsed pnpm lockfile + * @returns A map of package names to package specs for the dependencies of the + * given package + */ export function getPackageDeps( workspaceDir: string, packageDir: string, diff --git a/packages/meta-updater/src/updatePreCommit.ts b/packages/meta-updater/src/updatePreCommit.ts index bf5130c490..4ae741cd15 100644 --- a/packages/meta-updater/src/updatePreCommit.ts +++ b/packages/meta-updater/src/updatePreCommit.ts @@ -3,6 +3,9 @@ import { Document, ParsedNode } from "yaml"; import { Context } from "./Context"; import { getPackageDeps } from "./getPackageDeps"; +/** + * Subset of the .pre-commit-config.yaml schema that we care about. + */ interface PreCommitConfig { repos: { hooks: { @@ -14,15 +17,19 @@ interface PreCommitConfig { } /** - * Given a tsconfig.json, update it to match our conventions. This function is - * called by the pnpm `meta-updater` plugin either to check if the tsconfig.json - * is up to date or to update it, depending on flags. + * Given a .pre-commit-config.yaml, update it to ensure that the versions of our + * hooks match the corresponding package versions in package.json. This + * function is called by the pnpm `meta-updater` plugin either to check if the + * .pre-commit-config.yaml is up to date or to update it, depending on flags. * @param context Contains context such as workspace dir and parsed pnpm * lockfile - * @param rawInput The input tsconfig.json that should be checked / updated + * @param rawInput The input .pre-commit-config.yaml that should be checked / + * updated. This is a parsed yaml document in the `yaml` library's document + * representation; not a plain js object like you'd get from a json parser. We + * need it like this so that we can preserve comments. * @param options Extra information provided by pnpm; mostly just the directory - * of the package whose tsconfig.json we are updating - * @returns The updated tsconfig.json + * of the package whose .pre-commit-config.yaml we are updating + * @returns The updated .pre-commit-config.yaml */ export async function updatePreCommit( { workspaceDir, pnpmLockfile }: Context, @@ -32,7 +39,7 @@ export async function updatePreCommit( if (rawInput == null) { return null; } - /** Directory of the package whose tsconfig.json we are updating */ + /** Directory of the package whose .pre-commit-config.yaml we are updating */ const packageDir = options.dir; if (packageDir !== workspaceDir) { @@ -40,28 +47,65 @@ export async function updatePreCommit( } const deps = getPackageDeps(workspaceDir, packageDir, pnpmLockfile); - const prettierVersion = deps["prettier"]; - const prettierHookIndex = (rawInput.toJS() as PreCommitConfig).repos + updateHook(deps, rawInput, "prettier", (name) => name === "prettier"); + + return rawInput; +} + +/** + * Updates the additional_dependencies of a hook in a .pre-commit-config.yaml to + * match the versions from the lockfile. + * @param deps Dependencies of the package whose .pre-commit-config.yaml we are + * updating + * @param rawInput The input .pre-commit-config.yaml that should be checked / + * updated + * @param hookId The id of the hook to update + * @param packageMatcher A function that returns true if the given package name + * should be added to the hook's additional_dependencies + */ +function updateHook( + deps: { [x: string]: string }, + rawInput: Document, + hookId: string, + packageMatcher: (name: string) => boolean, +) { + const packages = Object.entries(deps).filter(([name]) => + packageMatcher(name), + ); + + // Find the hook in the .pre-commit-config.yaml. Easier to grab the indices + // from the raw js representation so that we can just use `setIn` to update + // the hook + const desiredHooks = (rawInput.toJS() as PreCommitConfig).repos .flatMap(({ hooks }, repoIndex) => hooks.map((hook, hookIndex) => ({ hook, repoIndex, hookIndex })), ) - .filter(({ hook }) => hook.id === "prettier"); + .filter(({ hook }) => hook.id === hookId); - if (prettierHookIndex.length === 0) { - throw new Error("No prettier hook found"); + if (desiredHooks.length === 0) { + throw new Error(`No ${hookId} hook found`); } - if (prettierHookIndex.length > 1) { - throw new Error("Multiple prettier hooks found"); + if (desiredHooks.length > 1) { + throw new Error(`Multiple ${hookId} hooks found`); } - const { repoIndex, hookIndex } = prettierHookIndex[0]; + const { repoIndex, hookIndex } = desiredHooks[0]; rawInput.setIn( ["repos", repoIndex, "hooks", hookIndex, "additional_dependencies"], - rawInput.createNode([`prettier@${prettierVersion}`]), + rawInput.createNode( + packages + .map(([name, version]) => { + if (version.includes("(")) { + // pnpm includes the integrity hash in the version, which we don't + // need here + version = version.slice(0, version.indexOf("(")); + } + return `${name}@${version}`; + }) + .sort(), + ), ); - - return rawInput; } From 6ff801b8937ed5ad88459243b27e91216c3b9eda Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sat, 8 Apr 2023 20:23:52 +0100 Subject: [PATCH 3/6] Add eslint fixer --- .pre-commit-config.yaml | 17 +++++++++++++++++ packages/meta-updater/src/updatePreCommit.ts | 6 ++++++ 2 files changed, 23 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index abef0aaee5..d6b96bed99 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -46,6 +46,23 @@ repos: additional_dependencies: - prettier@2.7.1 args: [] + - repo: local + hooks: + - id: eslint + name: eslint + files: \.(ts|tsx)$ + language: node + entry: eslint --fix + additional_dependencies: + - "@typescript-eslint/eslint-plugin@5.57.0" + - "@typescript-eslint/parser@5.57.0" + - eslint-config-prettier@8.6.0 + - eslint-import-resolver-typescript@3.5.4 + - eslint-plugin-import@2.27.5 + - eslint-plugin-unused-imports@2.0.0 + - eslint@8.35.0 + - typescript@5.0.3 + args: [] - repo: https://github.com/ikamensh/flynt/ rev: "0.78" hooks: diff --git a/packages/meta-updater/src/updatePreCommit.ts b/packages/meta-updater/src/updatePreCommit.ts index 4ae741cd15..77be3abb67 100644 --- a/packages/meta-updater/src/updatePreCommit.ts +++ b/packages/meta-updater/src/updatePreCommit.ts @@ -49,6 +49,12 @@ export async function updatePreCommit( const deps = getPackageDeps(workspaceDir, packageDir, pnpmLockfile); updateHook(deps, rawInput, "prettier", (name) => name === "prettier"); + updateHook( + deps, + rawInput, + "eslint", + (name) => name.includes("eslint") || name === "typescript", + ); return rawInput; } From 189ed6394874bade41290aa573132ee5675bffe5 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sat, 8 Apr 2023 22:41:24 +0100 Subject: [PATCH 4/6] Fix eslint --- .pre-commit-config.yaml | 1 + packages/meta-updater/package.json | 2 ++ packages/meta-updater/src/mergeStrict.ts | 20 ++++++++++++++ packages/meta-updater/src/updatePreCommit.ts | 29 +++++++++++++------- pnpm-lock.yaml | 6 ++++ 5 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 packages/meta-updater/src/mergeStrict.ts diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d6b96bed99..3f44aa7dd2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -56,6 +56,7 @@ repos: additional_dependencies: - "@typescript-eslint/eslint-plugin@5.57.0" - "@typescript-eslint/parser@5.57.0" + - eslint-config-next@13.2.3 - eslint-config-prettier@8.6.0 - eslint-import-resolver-typescript@3.5.4 - eslint-plugin-import@2.27.5 diff --git a/packages/meta-updater/package.json b/packages/meta-updater/package.json index 3e9e7fe370..9d1d951ae5 100644 --- a/packages/meta-updater/package.json +++ b/packages/meta-updater/package.json @@ -13,6 +13,7 @@ "@pnpm/logger": "^5.0.0", "@pnpm/types": "8.9.0", "@types/normalize-path": "^3.0.0", + "lodash": "^4.17.21", "normalize-path": "^3.0.0", "path-exists": "^4.0.0", "ramda": "0.29.0", @@ -29,6 +30,7 @@ } }, "devDependencies": { + "@types/lodash": "4.14.181", "@types/ramda": "0.28.23", "esbuild": "^0.17.11" } diff --git a/packages/meta-updater/src/mergeStrict.ts b/packages/meta-updater/src/mergeStrict.ts new file mode 100644 index 0000000000..e3125572a7 --- /dev/null +++ b/packages/meta-updater/src/mergeStrict.ts @@ -0,0 +1,20 @@ +/** + * Merge the input objects, throwing an error if there are any conflicts. + */ +export function mergeStrict( + ...objs: Record[]): Record { + const result: Record = {}; + + for (const obj of objs) { + for (const [key, value] of Object.entries(obj)) { + if (result[key] !== undefined && result[key] !== value) { + throw new Error( + `Conflicting versions for ${key}: ${result[key]} and ${value}` + ); + } + result[key] = value; + } + } + + return result; +} diff --git a/packages/meta-updater/src/updatePreCommit.ts b/packages/meta-updater/src/updatePreCommit.ts index 77be3abb67..5f51b1e074 100644 --- a/packages/meta-updater/src/updatePreCommit.ts +++ b/packages/meta-updater/src/updatePreCommit.ts @@ -1,7 +1,9 @@ import type { FormatPluginFnOptions } from "@pnpm/meta-updater"; import { Document, ParsedNode } from "yaml"; import { Context } from "./Context"; -import { getPackageDeps } from "./getPackageDeps"; +import { Lockfile } from "@pnpm/lockfile-file"; +import { pickBy } from "lodash"; +import { mergeStrict } from "./mergeStrict"; /** * Subset of the .pre-commit-config.yaml schema that we care about. @@ -46,11 +48,9 @@ export async function updatePreCommit( throw new Error("updatePreCommit should only be called on root"); } - const deps = getPackageDeps(workspaceDir, packageDir, pnpmLockfile); - - updateHook(deps, rawInput, "prettier", (name) => name === "prettier"); + updateHook(pnpmLockfile, rawInput, "prettier", (name) => name === "prettier"); updateHook( - deps, + pnpmLockfile, rawInput, "eslint", (name) => name.includes("eslint") || name === "typescript", @@ -62,8 +62,8 @@ export async function updatePreCommit( /** * Updates the additional_dependencies of a hook in a .pre-commit-config.yaml to * match the versions from the lockfile. - * @param deps Dependencies of the package whose .pre-commit-config.yaml we are - * updating + * @param pnpmLockfile The pnpm lockfile, which contains the versions of all + * packages in the workspace * @param rawInput The input .pre-commit-config.yaml that should be checked / * updated * @param hookId The id of the hook to update @@ -71,13 +71,22 @@ export async function updatePreCommit( * should be added to the hook's additional_dependencies */ function updateHook( - deps: { [x: string]: string }, + pnpmLockfile: Lockfile, rawInput: Document, hookId: string, packageMatcher: (name: string) => boolean, ) { - const packages = Object.entries(deps).filter(([name]) => - packageMatcher(name), + // Find all packages that match the given packageMatcher in the dependencies + // of any package in the workspace + const packages = Object.entries( + mergeStrict( + ...Object.values(pnpmLockfile.importers) + .flatMap((packageInfo) => [ + packageInfo.dependencies ?? {}, + packageInfo.devDependencies ?? {}, + ]) + .map((deps) => pickBy(deps, (_, key) => packageMatcher(key))), + ), ); // Find the hook in the .pre-commit-config.yaml. Easier to grab the indices diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c7702618f9..7e6e231419 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -525,6 +525,9 @@ importers: '@types/normalize-path': specifier: ^3.0.0 version: 3.0.0 + lodash: + specifier: ^4.17.21 + version: 4.17.21 normalize-path: specifier: ^3.0.0 version: 3.0.0 @@ -541,6 +544,9 @@ importers: specifier: 2.2.1 version: 2.2.1 devDependencies: + '@types/lodash': + specifier: 4.14.181 + version: 4.14.181 '@types/ramda': specifier: 0.28.23 version: 0.28.23 From 6641ab5114d4a5ca810b960233bacd61196b713a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sat, 8 Apr 2023 21:42:25 +0000 Subject: [PATCH 5/6] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- packages/meta-updater/src/mergeStrict.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/meta-updater/src/mergeStrict.ts b/packages/meta-updater/src/mergeStrict.ts index e3125572a7..95c59df64c 100644 --- a/packages/meta-updater/src/mergeStrict.ts +++ b/packages/meta-updater/src/mergeStrict.ts @@ -2,14 +2,15 @@ * Merge the input objects, throwing an error if there are any conflicts. */ export function mergeStrict( - ...objs: Record[]): Record { + ...objs: Record[] +): Record { const result: Record = {}; for (const obj of objs) { for (const [key, value] of Object.entries(obj)) { if (result[key] !== undefined && result[key] !== value) { throw new Error( - `Conflicting versions for ${key}: ${result[key]} and ${value}` + `Conflicting versions for ${key}: ${result[key]} and ${value}`, ); } result[key] = value; From 177b7144a6f8c6ed51644aab28e3431901dca203 Mon Sep 17 00:00:00 2001 From: Pokey Rule <755842+pokey@users.noreply.github.com> Date: Sun, 9 Apr 2023 12:06:07 +0100 Subject: [PATCH 6/6] Run prettier after eslint --- .pre-commit-config.yaml | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 3f44aa7dd2..ca08b6237c 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -36,16 +36,6 @@ repos: # document contents. For example # packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/languages/ruby/changeCondition.yml exclude: ^packages/cursorless-vscode-e2e/src/suite/fixtures/recorded/.*/[^/]*\.yml$ - - repo: local - hooks: - - id: prettier - name: prettier - types: [text] - language: node - entry: prettier --write --list-different --ignore-unknown - additional_dependencies: - - prettier@2.7.1 - args: [] - repo: local hooks: - id: eslint @@ -64,6 +54,16 @@ repos: - eslint@8.35.0 - typescript@5.0.3 args: [] + - repo: local + hooks: + - id: prettier + name: prettier + types: [text] + language: node + entry: prettier --write --list-different --ignore-unknown + additional_dependencies: + - prettier@2.7.1 + args: [] - repo: https://github.com/ikamensh/flynt/ rev: "0.78" hooks: