From c316d2b26d5d57d6cc674b5714e5a1a21b49df33 Mon Sep 17 00:00:00 2001 From: Emily M Klassen Date: Fri, 1 Dec 2023 18:03:57 -0800 Subject: [PATCH 01/10] Use git-diff-index plumbing instead of git-diff porcelain --- src/git.test.ts | 6 +++--- src/git.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/git.test.ts b/src/git.test.ts index feaf9f3..5d9b613 100644 --- a/src/git.test.ts +++ b/src/git.test.ts @@ -55,7 +55,7 @@ describe("getDiffForFile", () => { const expectedCommand = "git"; const expectedArgs = - "diff --diff-algorithm=histogram --diff-filter=ACM --find-renames=100% --no-ext-diff --relative --staged --unified=0 1234567"; + "diff-index --diff-algorithm=histogram --diff-filter=ACM --find-renames=100% --no-ext-diff --relative --cached --unified=0 1234567"; const lastCall = mockedChildProcess.execFileSync.mock.calls.at(-1); const [command, argsIncludingFile = []] = lastCall ?? [""]; @@ -75,7 +75,7 @@ describe("getDiffForFile", () => { const expectedCommand = "git"; const expectedArgs = - "diff --diff-algorithm=histogram --diff-filter=ACM --find-renames=100% --no-ext-diff --relative --unified=0 1234567"; + "diff-index --diff-algorithm=histogram --diff-filter=ACM --find-renames=100% --no-ext-diff --relative --unified=0 1234567"; const lastCall = mockedChildProcess.execFileSync.mock.calls.at(-1); const [command, argsIncludingFile = []] = lastCall ?? [""]; @@ -95,7 +95,7 @@ describe("getDiffForFile", () => { const expectedCommand = "git"; const expectedArgs = - "diff --diff-algorithm=histogram --diff-filter=ACM --find-renames=100% --no-ext-diff --relative --unified=0 HEAD"; + "diff-index --diff-algorithm=histogram --diff-filter=ACM --find-renames=100% --no-ext-diff --relative --unified=0 HEAD"; const lastCall = mockedChildProcess.execFileSync.mock.calls.at(-1); const [command, argsIncludingFile = []] = lastCall ?? [""]; diff --git a/src/git.ts b/src/git.ts index 747f59c..dee6111 100644 --- a/src/git.ts +++ b/src/git.ts @@ -7,13 +7,13 @@ const OPTIONS = { maxBuffer: 1024 * 1024 * 100 }; const getDiffForFile = (filePath: string, staged: boolean): string => { const args = [ - "diff", + "diff-index", "--diff-algorithm=histogram", "--diff-filter=ACM", "--find-renames=100%", "--no-ext-diff", "--relative", - staged && "--staged", + staged && "--cached", "--unified=0", process.env.ESLINT_PLUGIN_DIFF_COMMIT ?? "HEAD", "--", @@ -28,14 +28,14 @@ const getDiffForFile = (filePath: string, staged: boolean): string => { const getDiffFileList = (staged: boolean): string[] => { const args = [ - "diff", + "diff-index", "--diff-algorithm=histogram", "--diff-filter=ACM", "--find-renames=100%", "--name-only", "--no-ext-diff", "--relative", - staged && "--staged", + staged && "--cached", process.env.ESLINT_PLUGIN_DIFF_COMMIT ?? "HEAD", "--", ].reduce( From 26e3989ce527023e5dc280b03bd29561b38df476 Mon Sep 17 00:00:00 2001 From: Emily M Klassen Date: Fri, 1 Dec 2023 18:29:44 -0800 Subject: [PATCH 02/10] Use git-diff-files plumbing instead of git-diff porcelain --- src/git.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/git.ts b/src/git.ts index dee6111..11a756b 100644 --- a/src/git.ts +++ b/src/git.ts @@ -53,7 +53,7 @@ const getDiffFileList = (staged: boolean): string[] => { const hasCleanIndex = (filePath: string): boolean => { const args = [ - "diff", + "diff-files", "--no-ext-diff", "--quiet", "--relative", From 13379c4af7c686c86e0f81830a2323c59d1ba072 Mon Sep 17 00:00:00 2001 From: Emily M Klassen Date: Fri, 1 Dec 2023 18:43:08 -0800 Subject: [PATCH 03/10] Use filter instead of reduce for arguments --- src/git.ts | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/git.ts b/src/git.ts index 11a756b..b9dc080 100644 --- a/src/git.ts +++ b/src/git.ts @@ -18,10 +18,7 @@ const getDiffForFile = (filePath: string, staged: boolean): string => { process.env.ESLINT_PLUGIN_DIFF_COMMIT ?? "HEAD", "--", resolve(filePath), - ].reduce( - (acc, cur) => (typeof cur === "string" ? [...acc, cur] : acc), - [] - ); + ].filter((cur): cur is string => typeof cur === "string"); return child_process.execFileSync(COMMAND, args, OPTIONS).toString(); }; @@ -38,10 +35,7 @@ const getDiffFileList = (staged: boolean): string[] => { staged && "--cached", process.env.ESLINT_PLUGIN_DIFF_COMMIT ?? "HEAD", "--", - ].reduce( - (acc, cur) => (typeof cur === "string" ? [...acc, cur] : acc), - [] - ); + ].filter((cur): cur is string => typeof cur === "string"); return child_process .execFileSync(COMMAND, args, OPTIONS) From 1ede20a3e9c6e9baeb383e5bba49e0211237698e Mon Sep 17 00:00:00 2001 From: Emily M Klassen Date: Fri, 1 Dec 2023 21:17:11 -0800 Subject: [PATCH 04/10] Use string encoding and remove unnecessary toString --- src/git.test.ts | 20 +++++++------------- src/git.ts | 6 ++---- src/index-ci.test.ts | 4 +--- src/index.test.ts | 4 +--- 4 files changed, 11 insertions(+), 23 deletions(-) diff --git a/src/git.test.ts b/src/git.test.ts index 5d9b613..40bdd41 100644 --- a/src/git.test.ts +++ b/src/git.test.ts @@ -48,7 +48,7 @@ describe("getRangesForDiff", () => { describe("getDiffForFile", () => { it("should get the staged diff of a file", () => { - mockedChildProcess.execFileSync.mockReturnValueOnce(Buffer.from(hunks)); + mockedChildProcess.execFileSync.mockReturnValueOnce(hunks); process.env.ESLINT_PLUGIN_DIFF_COMMIT = "1234567"; const diffFromFile = getDiffForFile("./mockfile.js", true); @@ -68,7 +68,7 @@ describe("getDiffForFile", () => { }); it("should work when using staged = false", () => { - mockedChildProcess.execFileSync.mockReturnValueOnce(Buffer.from(hunks)); + mockedChildProcess.execFileSync.mockReturnValueOnce(hunks); process.env.ESLINT_PLUGIN_DIFF_COMMIT = "1234567"; const diffFromFile = getDiffForFile("./mockfile.js", false); @@ -88,7 +88,7 @@ describe("getDiffForFile", () => { }); it("should use HEAD when no commit was defined", () => { - mockedChildProcess.execFileSync.mockReturnValueOnce(Buffer.from(hunks)); + mockedChildProcess.execFileSync.mockReturnValueOnce(hunks); process.env.ESLINT_PLUGIN_DIFF_COMMIT = undefined; const diffFromFile = getDiffForFile("./mockfile.js", false); @@ -120,7 +120,7 @@ describe("hasCleanIndex", () => { it("returns true otherwise", () => { jest.mock("child_process").resetAllMocks(); - mockedChildProcess.execFileSync.mockReturnValue(Buffer.from("")); + mockedChildProcess.execFileSync.mockReturnValue(""); expect(hasCleanIndex("")).toEqual(true); expect(mockedChildProcess.execFileSync).toHaveBeenCalled(); }); @@ -129,9 +129,7 @@ describe("hasCleanIndex", () => { describe("getDiffFileList", () => { it("should get the list of staged files", () => { jest.mock("child_process").resetAllMocks(); - mockedChildProcess.execFileSync.mockReturnValueOnce( - Buffer.from(diffFileList) - ); + mockedChildProcess.execFileSync.mockReturnValueOnce(diffFileList); expect(mockedChildProcess.execFileSync).toHaveBeenCalledTimes(0); const fileListA = getDiffFileList(false); @@ -145,16 +143,12 @@ describe("getDiffFileList", () => { describe("getUntrackedFileList", () => { it("should get the list of untracked files", () => { jest.mock("child_process").resetAllMocks(); - mockedChildProcess.execFileSync.mockReturnValueOnce( - Buffer.from(diffFileList) - ); + mockedChildProcess.execFileSync.mockReturnValueOnce(diffFileList); expect(mockedChildProcess.execFileSync).toHaveBeenCalledTimes(0); const fileListA = getUntrackedFileList(false); expect(mockedChildProcess.execFileSync).toHaveBeenCalledTimes(1); - mockedChildProcess.execFileSync.mockReturnValueOnce( - Buffer.from(diffFileList) - ); + mockedChildProcess.execFileSync.mockReturnValueOnce(diffFileList); const staged = false; const fileListB = getUntrackedFileList(staged); // `getUntrackedFileList` uses a cache, so the number of calls to diff --git a/src/git.ts b/src/git.ts index b9dc080..7b9448c 100644 --- a/src/git.ts +++ b/src/git.ts @@ -3,7 +3,7 @@ import { resolve } from "path"; import { Range } from "./Range"; const COMMAND = "git"; -const OPTIONS = { maxBuffer: 1024 * 1024 * 100 }; +const OPTIONS = { encoding: "utf8" as const, maxBuffer: 1024 * 1024 * 100 }; const getDiffForFile = (filePath: string, staged: boolean): string => { const args = [ @@ -20,7 +20,7 @@ const getDiffForFile = (filePath: string, staged: boolean): string => { resolve(filePath), ].filter((cur): cur is string => typeof cur === "string"); - return child_process.execFileSync(COMMAND, args, OPTIONS).toString(); + return child_process.execFileSync(COMMAND, args, OPTIONS); }; const getDiffFileList = (staged: boolean): string[] => { @@ -39,7 +39,6 @@ const getDiffFileList = (staged: boolean): string[] => { return child_process .execFileSync(COMMAND, args, OPTIONS) - .toString() .trim() .split("\n") .map((filePath) => resolve(filePath)); @@ -85,7 +84,6 @@ const getUntrackedFileList = ( untrackedFileListCache = child_process .execFileSync(COMMAND, args, OPTIONS) - .toString() .trim() .split("\n") .map((filePath) => resolve(filePath)); diff --git a/src/index-ci.test.ts b/src/index-ci.test.ts index 5d1e149..59d2c03 100644 --- a/src/index-ci.test.ts +++ b/src/index-ci.test.ts @@ -4,9 +4,7 @@ import * as child_process from "child_process"; jest.mock("child_process"); const mockedChildProcess = jest.mocked(child_process, { shallow: true }); -mockedChildProcess.execFileSync.mockReturnValue( - Buffer.from("line1\nline2\nline3") -); +mockedChildProcess.execFileSync.mockReturnValue("line1\nline2\nline3"); import "./index"; diff --git a/src/index.test.ts b/src/index.test.ts index c522908..80bf30a 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -3,9 +3,7 @@ import * as child_process from "child_process"; jest.mock("child_process"); const mockedChildProcess = jest.mocked(child_process, { shallow: true }); -mockedChildProcess.execFileSync.mockReturnValue( - Buffer.from("line1\nline2\nline3") -); +mockedChildProcess.execFileSync.mockReturnValue("line1\nline2\nline3"); import { configs, processors } from "./index"; From 9bd16b73f52d5515ee3937737831ef94325b59aa Mon Sep 17 00:00:00 2001 From: Emily M Klassen Date: Fri, 1 Dec 2023 20:54:46 -0800 Subject: [PATCH 05/10] Add diff options for committed --- src/git.test.ts | 20 +++++++++++--------- src/git.ts | 20 ++++++++++++-------- src/processors.ts | 30 +++++++++++++++++------------- 3 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/git.test.ts b/src/git.test.ts index 40bdd41..417d2a2 100644 --- a/src/git.test.ts +++ b/src/git.test.ts @@ -51,7 +51,7 @@ describe("getDiffForFile", () => { mockedChildProcess.execFileSync.mockReturnValueOnce(hunks); process.env.ESLINT_PLUGIN_DIFF_COMMIT = "1234567"; - const diffFromFile = getDiffForFile("./mockfile.js", true); + const diffFromFile = getDiffForFile("./mockfile.js", "staged"); const expectedCommand = "git"; const expectedArgs = @@ -71,7 +71,7 @@ describe("getDiffForFile", () => { mockedChildProcess.execFileSync.mockReturnValueOnce(hunks); process.env.ESLINT_PLUGIN_DIFF_COMMIT = "1234567"; - const diffFromFile = getDiffForFile("./mockfile.js", false); + const diffFromFile = getDiffForFile("./mockfile.js", "working"); const expectedCommand = "git"; const expectedArgs = @@ -91,7 +91,7 @@ describe("getDiffForFile", () => { mockedChildProcess.execFileSync.mockReturnValueOnce(hunks); process.env.ESLINT_PLUGIN_DIFF_COMMIT = undefined; - const diffFromFile = getDiffForFile("./mockfile.js", false); + const diffFromFile = getDiffForFile("./mockfile.js", "working"); const expectedCommand = "git"; const expectedArgs = @@ -131,7 +131,7 @@ describe("getDiffFileList", () => { jest.mock("child_process").resetAllMocks(); mockedChildProcess.execFileSync.mockReturnValueOnce(diffFileList); expect(mockedChildProcess.execFileSync).toHaveBeenCalledTimes(0); - const fileListA = getDiffFileList(false); + const fileListA = getDiffFileList("working"); expect(mockedChildProcess.execFileSync).toHaveBeenCalledTimes(1); expect(fileListA).toEqual( @@ -145,12 +145,11 @@ describe("getUntrackedFileList", () => { jest.mock("child_process").resetAllMocks(); mockedChildProcess.execFileSync.mockReturnValueOnce(diffFileList); expect(mockedChildProcess.execFileSync).toHaveBeenCalledTimes(0); - const fileListA = getUntrackedFileList(false); + const fileListA = getUntrackedFileList("working"); expect(mockedChildProcess.execFileSync).toHaveBeenCalledTimes(1); mockedChildProcess.execFileSync.mockReturnValueOnce(diffFileList); - const staged = false; - const fileListB = getUntrackedFileList(staged); + const fileListB = getUntrackedFileList("working"); // `getUntrackedFileList` uses a cache, so the number of calls to // `execFileSync` will not have increased. expect(mockedChildProcess.execFileSync).toHaveBeenCalledTimes(1); @@ -162,7 +161,10 @@ describe("getUntrackedFileList", () => { }); it("should not get a list when looking when using staged", () => { - const staged = true; - expect(getUntrackedFileList(staged)).toEqual([]); + expect(getUntrackedFileList("staged")).toEqual([]); + }); + + it("should not get a list when looking when using committed", () => { + expect(getUntrackedFileList("committed")).toEqual([]); }); }); diff --git a/src/git.ts b/src/git.ts index 7b9448c..6490d42 100644 --- a/src/git.ts +++ b/src/git.ts @@ -2,20 +2,23 @@ import * as child_process from "child_process"; import { resolve } from "path"; import { Range } from "./Range"; +export type DiffType = "staged" | "committed" | "working"; + const COMMAND = "git"; const OPTIONS = { encoding: "utf8" as const, maxBuffer: 1024 * 1024 * 100 }; -const getDiffForFile = (filePath: string, staged: boolean): string => { +const getDiffForFile = (filePath: string, diffType: DiffType): string => { const args = [ - "diff-index", + diffType === "committed" ? "diff-tree" : "diff-index", "--diff-algorithm=histogram", "--diff-filter=ACM", "--find-renames=100%", "--no-ext-diff", "--relative", - staged && "--cached", + diffType === "staged" && "--cached", "--unified=0", process.env.ESLINT_PLUGIN_DIFF_COMMIT ?? "HEAD", + diffType === "committed" && "HEAD", "--", resolve(filePath), ].filter((cur): cur is string => typeof cur === "string"); @@ -23,17 +26,18 @@ const getDiffForFile = (filePath: string, staged: boolean): string => { return child_process.execFileSync(COMMAND, args, OPTIONS); }; -const getDiffFileList = (staged: boolean): string[] => { +const getDiffFileList = (diffType: DiffType): string[] => { const args = [ - "diff-index", + diffType === "committed" ? "diff-tree" : "diff-index", "--diff-algorithm=histogram", "--diff-filter=ACM", "--find-renames=100%", "--name-only", "--no-ext-diff", "--relative", - staged && "--cached", + diffType === "staged" && "--cached", process.env.ESLINT_PLUGIN_DIFF_COMMIT ?? "HEAD", + diffType === "committed" && "HEAD", "--", ].filter((cur): cur is string => typeof cur === "string"); @@ -72,10 +76,10 @@ const fetchFromOrigin = (branch: string) => { let untrackedFileListCache: string[] | undefined; const getUntrackedFileList = ( - staged: boolean, + diffType: DiffType, shouldRefresh = false ): string[] => { - if (staged) { + if (diffType !== "working") { return []; } diff --git a/src/processors.ts b/src/processors.ts index 8b63e79..3f820ed 100644 --- a/src/processors.ts +++ b/src/processors.ts @@ -1,5 +1,6 @@ import type { Linter } from "eslint"; import { guessBranch } from "./ci"; +import type { DiffType } from "./git"; import { fetchFromOrigin, getDiffFileList, @@ -28,13 +29,13 @@ if (process.env.CI !== undefined) { * This is increasingly useful the more files there are in the repository. */ const getPreProcessor = - (diffFileList: string[], staged: boolean) => + (diffFileList: string[], diffType: DiffType) => (text: string, filename: string) => { - let untrackedFileList = getUntrackedFileList(staged); + let untrackedFileList = getUntrackedFileList(diffType); const shouldRefresh = !diffFileList.includes(filename) && !untrackedFileList.includes(filename); if (shouldRefresh) { - untrackedFileList = getUntrackedFileList(staged, true); + untrackedFileList = getUntrackedFileList(diffType, true); } const shouldBeProcessed = process.env.VSCODE_PID !== undefined || @@ -72,7 +73,7 @@ const getUnstagedChangesError = (filename: string): [Linter.LintMessage] => { }; const getPostProcessor = - (staged: boolean) => + (diffType: DiffType) => ( messages: Linter.LintMessage[][], filename: string @@ -81,18 +82,18 @@ const getPostProcessor = // No need to filter, just return return []; } - const untrackedFileList = getUntrackedFileList(staged); + const untrackedFileList = getUntrackedFileList(diffType); if (untrackedFileList.includes(filename)) { // We don't need to filter the messages of untracked files because they // would all be kept anyway, so we return them as-is. return messages.flat(); } - if (staged && !hasCleanIndex(filename)) { + if (diffType === "staged" && !hasCleanIndex(filename)) { return getUnstagedChangesError(filename); } - const rangesForDiff = getRangesForDiff(getDiffForFile(filename, staged)); + const rangesForDiff = getRangesForDiff(getDiffForFile(filename, diffType)); return messages.flatMap((message) => { const filteredMessage = message.filter(({ fatal, line }) => { @@ -111,18 +112,21 @@ const getPostProcessor = }); }; -type ProcessorType = "diff" | "staged" | "ci"; +type ProcessorType = "diff" | "committed" | "staged" | "ci"; const getProcessors = ( processorType: ProcessorType ): Required => { - const staged = processorType === "staged"; - const diffFileList = getDiffFileList(staged); + const diffType = + processorType === "staged" || processorType === "committed" + ? processorType + : "working"; + const diffFileList = getDiffFileList(diffType); return { - preprocess: getPreProcessor(diffFileList, staged), - postprocess: getPostProcessor(staged), - supportsAutofix: true, + preprocess: getPreProcessor(diffFileList, diffType), + postprocess: getPostProcessor(diffType), + supportsAutofix: processorType !== "committed", }; }; From cfa9b12074e9a7802c8bfdf1acb2eb7bc0842c2d Mon Sep 17 00:00:00 2001 From: Emily M Klassen Date: Fri, 1 Dec 2023 21:24:32 -0800 Subject: [PATCH 06/10] Only refresh when linting working tree --- src/processors.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/processors.ts b/src/processors.ts index 3f820ed..030d4f2 100644 --- a/src/processors.ts +++ b/src/processors.ts @@ -33,7 +33,9 @@ const getPreProcessor = (text: string, filename: string) => { let untrackedFileList = getUntrackedFileList(diffType); const shouldRefresh = - !diffFileList.includes(filename) && !untrackedFileList.includes(filename); + diffType === "working" && + !diffFileList.includes(filename) && + !untrackedFileList.includes(filename); if (shouldRefresh) { untrackedFileList = getUntrackedFileList(diffType, true); } From 9172171deb4065dbc3d04647c3e57eb887c44d4b Mon Sep 17 00:00:00 2001 From: Emily M Klassen Date: Fri, 1 Dec 2023 21:33:46 -0800 Subject: [PATCH 07/10] Read file contents from git when working tree has changes --- src/git.ts | 30 ++++++++++++++++++++++++++++++ src/processors.ts | 10 ++++++++++ 2 files changed, 40 insertions(+) diff --git a/src/git.ts b/src/git.ts index 6490d42..37747c6 100644 --- a/src/git.ts +++ b/src/git.ts @@ -68,6 +68,27 @@ const hasCleanIndex = (filePath: string): boolean => { return true; }; +const hasCleanTree = (filePath: string): boolean => { + const args = [ + "diff-index", + "--no-ext-diff", + "--quiet", + "--relative", + "--unified=0", + "HEAD", + "--", + resolve(filePath), + ]; + + try { + child_process.execFileSync(COMMAND, args, OPTIONS); + } catch (err: unknown) { + return false; + } + + return true; +}; + const fetchFromOrigin = (branch: string) => { const args = ["fetch", "--quiet", "origin", branch]; @@ -151,6 +172,13 @@ const getRangesForDiff = (diff: string): Range[] => return [...ranges, range]; }, []); +const readFileFromGit = (filePath: string) => { + const getBlob = ["ls-tree", "--object-only", "HEAD", resolve(filePath)]; + const blob = child_process.execFileSync(COMMAND, getBlob, OPTIONS).trim(); + const catFile = ["cat-file", "blob", blob]; + return child_process.execFileSync(COMMAND, catFile, OPTIONS); +}; + export { fetchFromOrigin, getDiffFileList, @@ -158,4 +186,6 @@ export { getRangesForDiff, getUntrackedFileList, hasCleanIndex, + hasCleanTree, + readFileFromGit, }; diff --git a/src/processors.ts b/src/processors.ts index 030d4f2..001e6c3 100644 --- a/src/processors.ts +++ b/src/processors.ts @@ -8,6 +8,8 @@ import { getRangesForDiff, getUntrackedFileList, hasCleanIndex, + hasCleanTree, + readFileFromGit, } from "./git"; import type { Range } from "./Range"; @@ -44,6 +46,14 @@ const getPreProcessor = diffFileList.includes(filename) || untrackedFileList.includes(filename); + if ( + diffType === "committed" && + shouldBeProcessed && + !hasCleanTree(filename) + ) { + return [readFileFromGit(filename)]; + } + return shouldBeProcessed ? [text] : []; }; From 21d0e83ebf1d54bb626aaf36c5089b35a9eb154c Mon Sep 17 00:00:00 2001 From: Emily M Klassen Date: Fri, 1 Dec 2023 21:53:16 -0800 Subject: [PATCH 08/10] Export committed processor and config --- src/__snapshots__/index.test.ts.snap | 18 ++++++++++++++++++ src/index.ts | 5 ++++- src/processors.ts | 13 +++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/src/__snapshots__/index.test.ts.snap b/src/__snapshots__/index.test.ts.snap index 600bce2..85b8968 100644 --- a/src/__snapshots__/index.test.ts.snap +++ b/src/__snapshots__/index.test.ts.snap @@ -15,6 +15,19 @@ exports[`plugin should match expected export 1`] = ` "diff", ], }, + "committed": { + "overrides": [ + { + "files": [ + "*", + ], + "processor": "diff/committed", + }, + ], + "plugins": [ + "diff", + ], + }, "diff": { "overrides": [ { @@ -51,6 +64,11 @@ exports[`plugin should match expected export 2`] = ` "preprocess": [Function], "supportsAutofix": true, }, + "committed": { + "postprocess": [Function], + "preprocess": [Function], + "supportsAutofix": false, + }, "diff": { "postprocess": [Function], "preprocess": [Function], diff --git a/src/index.ts b/src/index.ts index 2b493d1..90e9016 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,14 +5,17 @@ import { diffConfig, staged, stagedConfig, + committed, + committedConfig, } from "./processors"; const configs = { ci: ciConfig, diff: diffConfig, staged: stagedConfig, + committed: committedConfig, }; -const processors = { ci, diff, staged }; +const processors = { ci, diff, staged, committed }; module.exports = { configs, processors }; diff --git a/src/processors.ts b/src/processors.ts index 001e6c3..00d6b07 100644 --- a/src/processors.ts +++ b/src/processors.ts @@ -145,6 +145,7 @@ const getProcessors = ( const ci = process.env.CI !== undefined ? getProcessors("ci") : {}; const diff = getProcessors("diff"); const staged = getProcessors("staged"); +const committed = getProcessors("committed"); const diffConfig: Linter.BaseConfig = { plugins: ["diff"], @@ -179,6 +180,16 @@ const stagedConfig: Linter.BaseConfig = { ], }; +const committedConfig: Linter.BaseConfig = { + plugins: ["diff"], + overrides: [ + { + files: ["*"], + processor: "diff/committed", + }, + ], +}; + export { ci, ciConfig, @@ -186,5 +197,7 @@ export { diffConfig, staged, stagedConfig, + committed, + committedConfig, getUnstagedChangesError, }; From d410541c481fbffdb02fb9b11307b71e57c773c6 Mon Sep 17 00:00:00 2001 From: Emily M Klassen Date: Fri, 1 Dec 2023 21:57:51 -0800 Subject: [PATCH 09/10] Update documentation with committed config --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 1c22d17..eb7569e 100644 --- a/README.md +++ b/README.md @@ -74,6 +74,16 @@ Only lint the changes you've staged for an upcoming commit. } ``` +#### `"plugin:diff/committed"` + +Only lint the changes you've committed, for running in a pre-push hook. You should set `ESLINT_PLUGIN_DIFF_COMMIT` in your pre-push hook for this to be useful. + +```json +{ + "extends": ["plugin:diff/committed"] +} +``` + ## CI Setup To lint all the changes of a pull-request, you only have to set From a5cc71f23a8c7b09efda987b24bb2c01d3c4625d Mon Sep 17 00:00:00 2001 From: Emily M Klassen Date: Fri, 8 Dec 2023 19:12:12 -0800 Subject: [PATCH 10/10] Add missing recursive flag to diff-tree --- src/git.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/git.ts b/src/git.ts index 37747c6..604255f 100644 --- a/src/git.ts +++ b/src/git.ts @@ -36,6 +36,7 @@ const getDiffFileList = (diffType: DiffType): string[] => { "--no-ext-diff", "--relative", diffType === "staged" && "--cached", + diffType === "committed" && "-r", process.env.ESLINT_PLUGIN_DIFF_COMMIT ?? "HEAD", diffType === "committed" && "HEAD", "--",