Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/great-panthers-shout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"pretty-quick": major
---

feat!: use async and batch stage commands
12 changes: 10 additions & 2 deletions src/cli.mts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ const main = async () => {
onExamineFile: file => {
console.log(`🔍 Examining ${picocolors.bold(file)}.`)
},

onStageFiles(files) {
console.log(`🏗️ Staging changed ${files.length} files.`)
},
})

if (prettyQuickResult.success) {
Expand All @@ -71,10 +75,14 @@ const main = async () => {
'✗ Code style issues found in the above file(s). Forgot to run Prettier?',
)
}
if (prettyQuickResult.errors.includes('STAGE_FAILED')) {
console.log(
'✗ Failed to stage some or all of the above file(s). Please stage changes made by Prettier before committing.',
)
}
// eslint-disable-next-line n/no-process-exit
process.exit(1) // ensure git hooks abort
}
}

// eslint-disable-next-line @typescript-eslint/no-floating-promises
main()
void main()
9 changes: 5 additions & 4 deletions src/createIgnorer.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,19 @@
/* eslint-disable unicorn-x/filename-case */

import fs from 'fs'
import fs from 'fs/promises'
import path from 'path'

import { tryFile } from '@pkgr/core'
import ignore from 'ignore'

export default function createIgnorer(
export default async function createIgnorer(
directory: string,
filename = '.prettierignore',
) {
const file = path.join(directory, filename)

if (fs.existsSync(file)) {
const text = fs.readFileSync(file, 'utf8')
if (tryFile(file)) {
const text = await fs.readFile(file, 'utf8')
const filter = ignore().add(text).createFilter()
return (filepath: string) => filter(path.join(filepath))
}
Expand Down
1 change: 0 additions & 1 deletion src/createMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export default function createMatcher(pattern: string[] | string | undefined) {
return () => true
}
const patterns = Array.isArray(pattern) ? pattern : [pattern]

const isMatch = picomatch(patterns, { dot: true })
return (file: string) => isMatch(path.normalize(file))
}
17 changes: 14 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export = async function prettyQuick(
onExamineFile,
onCheckFile,
onWriteFile,
onStageFiles,
resolveConfig = true,
}: Partial<PrettyQuickOptions> = {},
) {
Expand All @@ -39,11 +40,11 @@ export = async function prettyQuick(

onFoundSinceRevision?.(scm.name, revision)

const rootIgnorer = createIgnorer(directory, ignorePath)
const rootIgnorer = await createIgnorer(directory, ignorePath)
const cwdIgnorer =
currentDirectory === directory
? () => true
: createIgnorer(currentDirectory, ignorePath)
: await createIgnorer(currentDirectory, ignorePath)

const patternMatcher = createMatcher(pattern)

Expand Down Expand Up @@ -79,6 +80,7 @@ export = async function prettyQuick(

const failReasons = new Set<string>()

const filesToStage: string[] = []
await processFiles(directory, changedFiles, {
check,
config,
Expand All @@ -89,7 +91,7 @@ export = async function prettyQuick(
}
if (staged && restage) {
if (wasFullyStaged(file)) {
await scm.stageFile(directory, file)
filesToStage.push(file)
} else {
onPartiallyStagedFile?.(file)
failReasons.add('PARTIALLY_STAGED_FILE')
Expand All @@ -105,6 +107,15 @@ export = async function prettyQuick(
onExamineFile: verbose ? onExamineFile : undefined,
})

if (filesToStage.length > 0) {
try {
await onStageFiles?.(filesToStage)
await scm.stageFiles(directory, filesToStage)
} catch {
failReasons.add('STAGE_FAILED')
}
}

return {
success: failReasons.size === 0,
errors: [...failReasons],
Expand Down
48 changes: 25 additions & 23 deletions src/processFiles.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* eslint-disable unicorn-x/filename-case */

import fs from 'fs'
import fs from 'fs/promises'
import path from 'path'

import { format, check as prettierCheck, resolveConfig } from 'prettier'
Expand All @@ -18,29 +18,31 @@ export default async function processFiles(
onWriteFile,
}: Partial<PrettyQuickOptions> = {},
) {
for (const relative of files) {
onExamineFile?.(relative)
const file = path.join(directory, relative)
const options = {
...(await resolveConfig(file, {
config,
editorconfig: true,
})),
filepath: file,
}
const input = fs.readFileSync(file, 'utf8')
return Promise.all(
files.map(async relative => {
onExamineFile?.(relative)
const file = path.join(directory, relative)
const options = {
...(await resolveConfig(file, {
config,
editorconfig: true,
})),
filepath: file,
}
const input = await fs.readFile(file, 'utf8')

if (check) {
const isFormatted = await prettierCheck(input, options)
onCheckFile?.(relative, isFormatted)
continue
}
if (check) {
const isFormatted = await prettierCheck(input, options)
onCheckFile?.(relative, isFormatted)
return
}

const output = await format(input, options)
const output = await format(input, options)

if (output !== input) {
fs.writeFileSync(file, output)
await onWriteFile?.(relative)
}
}
if (output !== input) {
await fs.writeFile(file, output)
await onWriteFile?.(relative)
}
}),
)
}
25 changes: 20 additions & 5 deletions src/scms/git.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,24 @@ export const getChangedFiles = async (
)),
].filter(Boolean)

export const getUnstagedChangedFiles = (directory: string) => {
return getChangedFiles(directory, null, false)
}
export const getUnstagedChangedFiles = (directory: string) =>
getChangedFiles(directory, null, false)

export const stageFiles = async (directory: string, files: string[]) => {
const maxArguments = 100
const result = files.reduce<string[][]>((resultArray, file, index) => {
const chunkIndex = Math.floor(index / maxArguments)

if (!resultArray[chunkIndex]) {
resultArray[chunkIndex] = [] // start a new chunk
}

export const stageFile = (directory: string, file: string) =>
runGit(directory, ['add', file])
resultArray[chunkIndex].push(file)

return resultArray
}, [])

for (const batchedFiles of result) {
await runGit(directory, ['add', ...batchedFiles])
}
}
21 changes: 18 additions & 3 deletions src/scms/hg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export const getSinceRevision = async (
branch || 'default',
])
const revision = revisionOutput.stdout.trim()

const hgOutput = await runHg(directory, ['id', '-i', '-r', revision])
return hgOutput.stdout.trim()
}
Expand All @@ -56,5 +55,21 @@ export const getChangedFiles = async (

export const getUnstagedChangedFiles = () => []

export const stageFile = (directory: string, file: string) =>
runHg(directory, ['add', file])
export const stageFiles = async (directory: string, files: string[]) => {
const maxArguments = 100
const result = files.reduce<string[][]>((resultArray, file, index) => {
const chunkIndex = Math.floor(index / maxArguments)

if (!resultArray[chunkIndex]) {
resultArray[chunkIndex] = [] // start a new chunk
}

resultArray[chunkIndex].push(file)

return resultArray
}, [])

for (const batchedFiles of result) {
await runHg(directory, ['add', ...batchedFiles])
}
}
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,5 @@ export interface PrettyQuickOptions {
onExamineFile(relative: string): void
onCheckFile(relative: string, isFormatted: boolean): void
onWriteFile(relative: string): Promise<void> | void
onStageFiles(files: string[]): Promise<void> | void
}
Loading