-
Notifications
You must be signed in to change notification settings - Fork 72
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1122 from chromaui/revert-1121-revert-1112-tom/ca…
…p-2327-track-hasrouter-and-haspagecomponents-on-build-events Send project metadata to the index
- Loading branch information
Showing
23 changed files
with
768 additions
and
158 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
import { PassThrough, Transform } from 'node:stream'; | ||
import { beforeEach } from 'node:test'; | ||
|
||
import { execaCommand as rawExecaCommand } from 'execa'; | ||
import { describe, expect, it, vitest } from 'vitest'; | ||
|
||
import gitNoCommits from '../ui/messages/errors/gitNoCommits'; | ||
import gitNotInitialized from '../ui/messages/errors/gitNotInitialized'; | ||
import gitNotInstalled from '../ui/messages/errors/gitNotInstalled'; | ||
import { execGitCommand, execGitCommandCountLines, execGitCommandOneLine } from './execGit'; | ||
|
||
vitest.mock('execa'); | ||
|
||
const execaCommand = vitest.mocked(rawExecaCommand); | ||
beforeEach(() => { | ||
execaCommand.mockReset(); | ||
}); | ||
|
||
describe('execGitCommand', () => { | ||
it('returns execa output if it works', async () => { | ||
execaCommand.mockResolvedValue({ | ||
all: Buffer.from('some output'), | ||
} as any); | ||
|
||
expect(await execGitCommand('some command')).toEqual('some output'); | ||
}); | ||
|
||
it('errors if there is no output', async () => { | ||
execaCommand.mockResolvedValue({ | ||
all: undefined, | ||
} as any); | ||
|
||
await expect(execGitCommand('some command')).rejects.toThrow(/Unexpected missing git/); | ||
}); | ||
|
||
it('handles missing git error', async () => { | ||
execaCommand.mockRejectedValue(new Error('not a git repository')); | ||
|
||
await expect(execGitCommand('some command')).rejects.toThrow( | ||
gitNotInitialized({ command: 'some command' }) | ||
); | ||
}); | ||
|
||
it('handles git not found error', async () => { | ||
execaCommand.mockRejectedValue(new Error('git not found')); | ||
|
||
await expect(execGitCommand('some command')).rejects.toThrow( | ||
gitNotInstalled({ command: 'some command' }) | ||
); | ||
}); | ||
|
||
it('handles no commits yet', async () => { | ||
execaCommand.mockRejectedValue(new Error('does not have any commits yet')); | ||
|
||
await expect(execGitCommand('some command')).rejects.toThrow( | ||
gitNoCommits({ command: 'some command' }) | ||
); | ||
}); | ||
|
||
it('rethrows arbitrary errors', async () => { | ||
execaCommand.mockRejectedValue(new Error('something random')); | ||
await expect(execGitCommand('some command')).rejects.toThrow('something random'); | ||
}); | ||
}); | ||
|
||
function createExecaStreamer() { | ||
let resolver; | ||
let rejecter; | ||
const promiseLike = new Promise((aResolver, aRejecter) => { | ||
resolver = aResolver; | ||
rejecter = aRejecter; | ||
}) as Promise<unknown> & { | ||
stdout: Transform; | ||
kill: () => void; | ||
_rejecter: (err: Error) => void; | ||
}; | ||
promiseLike.stdout = new PassThrough(); | ||
promiseLike.kill = resolver; | ||
promiseLike._rejecter = rejecter; | ||
return promiseLike; | ||
} | ||
|
||
describe('execGitCommandOneLine', () => { | ||
it('returns the first line if the command works', async () => { | ||
const streamer = createExecaStreamer(); | ||
execaCommand.mockReturnValue(streamer as any); | ||
|
||
const promise = execGitCommandOneLine('some command'); | ||
|
||
streamer.stdout.write('First line\n'); | ||
streamer.stdout.write('Second line\n'); | ||
|
||
expect(await promise).toEqual('First line'); | ||
}); | ||
|
||
it('returns the output if the command only has one line', async () => { | ||
const streamer = createExecaStreamer(); | ||
execaCommand.mockReturnValue(streamer as any); | ||
|
||
const promise = execGitCommandOneLine('some command'); | ||
|
||
streamer.stdout.write('First line\n'); | ||
streamer.stdout.end(); | ||
|
||
expect(await promise).toEqual('First line'); | ||
}); | ||
|
||
it('Return an error if the command has no ouput', async () => { | ||
const streamer = createExecaStreamer(); | ||
execaCommand.mockReturnValue(streamer as any); | ||
|
||
const promise = execGitCommandOneLine('some command'); | ||
|
||
streamer.kill(); | ||
|
||
await expect(promise).rejects.toThrow(/missing git command output/); | ||
}); | ||
|
||
it('rethrows arbitrary errors', async () => { | ||
const streamer = createExecaStreamer(); | ||
execaCommand.mockReturnValue(streamer as any); | ||
|
||
const promise = execGitCommandOneLine('some command'); | ||
|
||
streamer._rejecter(new Error('some error')); | ||
|
||
await expect(promise).rejects.toThrow(/some error/); | ||
}); | ||
}); | ||
|
||
describe('execGitCommandCountLines', () => { | ||
it('counts lines, many', async () => { | ||
const streamer = createExecaStreamer(); | ||
execaCommand.mockReturnValue(streamer as any); | ||
|
||
const promise = execGitCommandCountLines('some command'); | ||
|
||
streamer.stdout.write('First line\n'); | ||
streamer.stdout.write('Second line\n'); | ||
streamer.kill(); | ||
|
||
expect(await promise).toEqual(2); | ||
}); | ||
|
||
it('counts lines, one', async () => { | ||
const streamer = createExecaStreamer(); | ||
execaCommand.mockReturnValue(streamer as any); | ||
|
||
const promise = execGitCommandCountLines('some command'); | ||
|
||
streamer.stdout.write('First line\n'); | ||
streamer.kill(); | ||
|
||
expect(await promise).toEqual(1); | ||
}); | ||
|
||
it('counts lines, none', async () => { | ||
const streamer = createExecaStreamer(); | ||
execaCommand.mockReturnValue(streamer as any); | ||
|
||
const promise = execGitCommandCountLines('some command'); | ||
|
||
streamer.kill(); | ||
|
||
expect(await promise).toEqual(0); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
import { createInterface } from 'node:readline'; | ||
|
||
import { execaCommand } from 'execa'; | ||
|
||
import gitNoCommits from '../ui/messages/errors/gitNoCommits'; | ||
import gitNotInitialized from '../ui/messages/errors/gitNotInitialized'; | ||
import gitNotInstalled from '../ui/messages/errors/gitNotInstalled'; | ||
|
||
const defaultOptions: Parameters<typeof execaCommand>[1] = { | ||
env: { LANG: 'C', LC_ALL: 'C' }, // make sure we're speaking English | ||
timeout: 20_000, // 20 seconds | ||
all: true, // interleave stdout and stderr | ||
shell: true, // we'll deal with escaping ourselves (for now) | ||
}; | ||
|
||
/** | ||
* Execute a Git command in the local terminal. | ||
* | ||
* @param command The command to execute. | ||
* @param options Execa options | ||
* | ||
* @returns The result of the command from the terminal. | ||
*/ | ||
export async function execGitCommand( | ||
command: string, | ||
options?: Parameters<typeof execaCommand>[1] | ||
) { | ||
try { | ||
const { all } = await execaCommand(command, { ...defaultOptions, ...options }); | ||
|
||
if (all === undefined) { | ||
throw new Error(`Unexpected missing git command output for command: '${command}'`); | ||
} | ||
|
||
return all.toString(); | ||
} catch (error) { | ||
const { message } = error; | ||
|
||
if (message.includes('not a git repository')) { | ||
throw new Error(gitNotInitialized({ command })); | ||
} | ||
|
||
if (message.includes('git not found')) { | ||
throw new Error(gitNotInstalled({ command })); | ||
} | ||
|
||
if (message.includes('does not have any commits yet')) { | ||
throw new Error(gitNoCommits({ command })); | ||
} | ||
|
||
throw error; | ||
} | ||
} | ||
|
||
/** | ||
* Execute a Git command in the local terminal and just get the first line. | ||
* | ||
* @param command The command to execute. | ||
* @param options Execa options | ||
* | ||
* @returns The first line of the command from the terminal. | ||
*/ | ||
export async function execGitCommandOneLine( | ||
command: string, | ||
options?: Parameters<typeof execaCommand>[1] | ||
) { | ||
const process = execaCommand(command, { ...defaultOptions, buffer: false, ...options }); | ||
|
||
return Promise.race([ | ||
// This promise will resolve only if there is an error or it times out | ||
(async () => { | ||
await process; | ||
|
||
throw new Error(`Unexpected missing git command output for command: '${command}'`); | ||
})(), | ||
// We expect this promise to resolve first | ||
new Promise<string>((resolve, reject) => { | ||
if (!process.stdout) { | ||
return reject(new Error('Unexpected missing stdout')); | ||
} | ||
|
||
const rl = createInterface(process.stdout); | ||
rl.once('line', (line) => { | ||
rl.close(); | ||
process.kill(); | ||
|
||
resolve(line); | ||
}); | ||
}), | ||
]); | ||
} | ||
|
||
/** | ||
* Execute a Git command in the local terminal and count the lines in the result | ||
* | ||
* @param command The command to execute. | ||
* @param options Execa options | ||
* | ||
* @returns The number of lines the command returned | ||
*/ | ||
export async function execGitCommandCountLines( | ||
command: string, | ||
options?: Parameters<typeof execaCommand>[1] | ||
) { | ||
const process = execaCommand(command, { ...defaultOptions, buffer: false, ...options }); | ||
if (!process.stdout) { | ||
throw new Error('Unexpected missing stdout'); | ||
} | ||
|
||
let lineCount = 0; | ||
const rl = createInterface(process.stdout); | ||
rl.on('line', () => { | ||
lineCount += 1; | ||
}); | ||
|
||
// If the process errors, this will throw | ||
await process; | ||
|
||
return lineCount; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.