Skip to content

Commit fb264a9

Browse files
authored
feat(cli): sv add --no-git-check (#650)
* feat(cli): `sv add --no-git-check` * rmv . * update namings * verifiers * renaming * cleanup changeset
1 parent 3e366d3 commit fb264a9

File tree

7 files changed

+98
-86
lines changed

7 files changed

+98
-86
lines changed

.changeset/tangy-deer-fetch.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'sv': minor
3+
---
4+
5+
- feat(cli): remove `--no-preconditions` option from `sv add`
6+
- feat(cli): add `--no-git-check` option to `sv add`. With this flag, even if some files are dirty, no prompt will be shown

documentation/docs/20-commands/20-sv-add.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ You can select multiple space-separated add-ons from [the list below](#Official-
1919
## Options
2020

2121
- `-C`, `--cwd` — path to the root of your Svelte(Kit) project
22-
- `--no-preconditions`skip checking preconditions <!-- TODO what does this mean? -->
22+
- `--no-git-check`even if some files are dirty, no prompt will be shown
2323
- `--install` — installs dependencies with a specified package manager
2424
- `--no-install` — prevents installing dependencies
2525

packages/cli/commands/add/index.ts

Lines changed: 27 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
installOption,
2626
packageManagerPrompt
2727
} from '../../utils/package-manager.ts';
28-
import { getGlobalPreconditions } from './preconditions.ts';
28+
import { verifyCleanWorkingDirectory, verifyUnsupportedAddons } from './verifiers.ts';
2929
import { type AddonMap, applyAddons, setupAddons } from '../../lib/install.ts';
3030

3131
const aliases = officialAddons.map((c) => c.alias).filter((v) => v !== undefined);
@@ -36,7 +36,7 @@ const AddonsSchema = v.array(v.string());
3636
const OptionsSchema = v.strictObject({
3737
cwd: v.string(),
3838
install: v.union([v.boolean(), v.picklist(AGENT_NAMES)]),
39-
preconditions: v.boolean(),
39+
gitCheck: v.boolean(),
4040
community: v.optional(v.union([AddonsSchema, v.boolean()])),
4141
addons: v.record(v.string(), v.optional(v.array(v.string())))
4242
});
@@ -81,7 +81,7 @@ export const add = new Command('add')
8181
return prev;
8282
})
8383
.option('-C, --cwd <path>', 'path to working directory', defaultCwd)
84-
.option('--no-preconditions', 'skip validating preconditions')
84+
.option('--no-git-check', 'even if some files are dirty, no prompt will be shown')
8585
.option('--no-install', 'skip installing dependencies')
8686
.addOption(installOption)
8787
//.option('--community [add-on...]', 'community addons to install')
@@ -445,33 +445,33 @@ export async function runAddCommand(
445445
}
446446
}
447447

448-
// run precondition checks
449-
if (options.preconditions && selectedAddons.length > 0) {
450-
// add global checks
451-
const addons = selectedAddons.map(({ addon }) => addon);
452-
const { preconditions } = getGlobalPreconditions(options.cwd, addons, addonSetupResults);
453-
454-
const fails: Array<{ name: string; message?: string }> = [];
455-
for (const condition of preconditions) {
456-
const { message, success } = await condition.run();
457-
if (!success) fails.push({ name: condition.name, message });
458-
}
448+
// run verifications
449+
const addons = selectedAddons.map(({ addon }) => addon);
450+
const verifications = [
451+
...verifyCleanWorkingDirectory(options.cwd, options.gitCheck),
452+
...verifyUnsupportedAddons(addons, addonSetupResults)
453+
];
454+
455+
const fails: Array<{ name: string; message?: string }> = [];
456+
for (const verification of verifications) {
457+
const { message, success } = await verification.run();
458+
if (!success) fails.push({ name: verification.name, message });
459+
}
459460

460-
if (fails.length > 0) {
461-
const message = fails
462-
.map(({ name, message }) => pc.yellow(`${name} (${message})`))
463-
.join('\n- ');
461+
if (fails.length > 0) {
462+
const message = fails
463+
.map(({ name, message }) => pc.yellow(`${name} (${message})`))
464+
.join('\n- ');
464465

465-
p.note(`- ${message}`, 'Preconditions not met', { format: (line) => line });
466+
p.note(`- ${message}`, 'Verifications not met', { format: (line) => line });
466467

467-
const force = await p.confirm({
468-
message: 'Preconditions failed. Do you wish to continue?',
469-
initialValue: false
470-
});
471-
if (p.isCancel(force) || !force) {
472-
p.cancel('Operation cancelled.');
473-
process.exit(1);
474-
}
468+
const force = await p.confirm({
469+
message: 'Verifications failed. Do you wish to continue?',
470+
initialValue: false
471+
});
472+
if (p.isCancel(force) || !force) {
473+
p.cancel('Operation cancelled.');
474+
process.exit(1);
475475
}
476476
}
477477

packages/cli/commands/add/preconditions.ts

Lines changed: 0 additions & 56 deletions
This file was deleted.
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
import { exec } from 'node:child_process';
2+
import { promisify } from 'node:util';
3+
import type { AddonSetupResult, AddonWithoutExplicitArgs, Verification } from '@sveltejs/cli-core';
4+
import { UnsupportedError } from '../../utils/errors.ts';
5+
6+
export function verifyCleanWorkingDirectory(cwd: string, gitCheck: boolean) {
7+
const verifications: Verification[] = [];
8+
9+
if (gitCheck) {
10+
verifications.push({
11+
name: 'clean working directory',
12+
run: async () => {
13+
try {
14+
// If a user has pending git changes the output of the following command will list
15+
// all files that have been added/modified/deleted and thus the output will not be empty.
16+
// In case the output of the command below is an empty text, we can safely assume
17+
// there are no pending changes. If the below command is run outside of a git repository,
18+
// git will exit with a failing exit code, which will trigger the catch statement.
19+
// also see https://remarkablemark.org/blog/2017/10/12/check-git-dirty/#git-status
20+
const asyncExec = promisify(exec);
21+
const { stdout } = await asyncExec('git status --short', {
22+
cwd
23+
});
24+
25+
if (stdout) {
26+
return { success: false, message: 'Found modified files' };
27+
}
28+
29+
return { success: true, message: undefined };
30+
} catch {
31+
return { success: true, message: 'Not a git repository' };
32+
}
33+
}
34+
});
35+
}
36+
37+
return verifications;
38+
}
39+
40+
export function verifyUnsupportedAddons(
41+
addons: AddonWithoutExplicitArgs[],
42+
addonSetupResult: Record<string, AddonSetupResult>
43+
) {
44+
const verifications: Verification[] = [];
45+
46+
verifications.push({
47+
name: 'unsupported add-ons',
48+
run: () => {
49+
const reasons = addons.flatMap((a) =>
50+
addonSetupResult[a.id].unsupported.map((reason) => ({ id: a.id, reason }))
51+
);
52+
53+
if (reasons.length === 0) {
54+
return { success: true, message: undefined };
55+
}
56+
57+
throw new UnsupportedError(reasons);
58+
}
59+
});
60+
61+
return verifications;
62+
}

packages/cli/commands/create.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ async function createProject(cwd: ProjectPath, options: Options) {
186186
{
187187
cwd: projectPath,
188188
install: options.install,
189-
preconditions: false,
189+
gitCheck: false,
190190
community: [],
191191
addons: {}
192192
},

packages/core/addon/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export function defineAddonOptions<const Args extends OptionDefinition>(options:
8484

8585
type MaybePromise<T> = Promise<T> | T;
8686

87-
export type Precondition = {
87+
export type Verification = {
8888
name: string;
8989
run: () => MaybePromise<{ success: boolean; message: string | undefined }>;
9090
};

0 commit comments

Comments
 (0)