Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
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/promptless-commands.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'sv': patch
---

feat(cli): Add promptless command to `README.md` on `sv create`
17 changes: 15 additions & 2 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ We follow the standard fork-based workflow:

1. **Fork** this repository to your GitHub account.
2. **Clone** your fork locally.
3. **Create a new branch** for your change:
3. **Create a new branch** for your change:
`git checkout -b your-feature-name`
4. **Commit and push** your changes to your branch.
5. **Open a pull request** from your branch to the `main` branch of this repository.
Expand Down Expand Up @@ -80,7 +80,9 @@ Run specific tests by specifying a project flag to the package and running the t
pnpm test --project cli # core / addons / create / migrate
```

Example of how to debug an addon failing test. Once, you ran the test command, you will have a directory in `.test-output` with the test id. A good starting point is to `cd` into the failing tests dir. Proceed to `build` it. Then `preview` it. From here you will have increased information to help in the debug process. Eg:
### Debugging

Example of how to debug an addon failing test. Once, you ran the test command, you will have a directory in `.test-output` with the test id. A good starting point is to `cd` into the failing tests dir. Proceed to `build` it. Then `preview` it. From here you will have increased information to help in the debug process. E.g.:

```sh
pnpm test --project addons tailwind # to debug the tailwind addon failing test
Expand All @@ -90,6 +92,17 @@ pnpm build
pnpm preview
```

### Update snapshots

Some snapshots are testing the output of `sv` directly from the generated binary. They are located in `packages/sv/lib/cli/tests/snapshots`. Make sure to generate a new binary before updating these snapshots.

In one command:

```sh
pnpm build && pnpm test:ui --project cli
# Press `u` when prompted to update snapshots.
```

## Style Guide

### Coding style
Expand Down
2 changes: 1 addition & 1 deletion packages/sv/lib/cli/add/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ export async function runAddonsApply({
}
}

if (fromCommand === 'add') common.logArgs(packageManager, 'add', argsFormattedAddons);
if (fromCommand === 'add') common.buildAndLogArgs(packageManager, 'add', argsFormattedAddons);

if (packageManager) {
workspace.packageManager = packageManager;
Expand Down
5 changes: 3 additions & 2 deletions packages/sv/lib/cli/create.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ async function createProject(cwd: ProjectPath, options: Options) {
directory: () => {
const defaultPath = './';
if (cwd) {
return Promise.resolve(cwd);
return Promise.resolve(common.normalizePosix(cwd));
}
return p.text({
message: 'Where would you like your project to be created?',
Expand Down Expand Up @@ -307,7 +307,8 @@ async function createProject(cwd: ProjectPath, options: Options) {

if (argsFormattedAddons.length > 0) argsFormatted.push('--add', ...argsFormattedAddons);

common.logArgs(packageManager, 'create', argsFormatted, [directory]);
const prompt = common.buildAndLogArgs(packageManager, 'create', argsFormatted, [directory]);
common.updateReadme(directory, prompt);

await addPnpmBuildDependencies(projectPath, packageManager, ['esbuild']);
if (packageManager) {
Expand Down
5 changes: 4 additions & 1 deletion packages/sv/lib/cli/tests/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,10 @@ describe('cli', () => {
async (testCase) => {
const { projectName, args } = testCase;
const svBinPath = path.resolve(monoRepoPath, 'packages', 'sv', 'dist', 'bin.mjs');
const testOutputPath = path.resolve(monoRepoPath, '.test-output', 'cli', projectName);
const testOutputPath = path.relative(
monoRepoPath,
path.resolve(monoRepoPath, '.test-output', 'cli', projectName)
);

const result = await exec(
'node',
Expand Down
13 changes: 12 additions & 1 deletion packages/sv/lib/cli/tests/common.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import { describe, expect, it } from 'vitest';
import { parseAddonOptions } from '../utils/common.ts';
import { normalizePosix, parseAddonOptions } from '../utils/common.ts';

describe('normalizePosix', () => {
const std = 'this/is/going/forward';
it('normalizes windows', () => {
const stdWindows = 'this\\is\\going\\forward';
expect(normalizePosix(stdWindows)).toEqual(std);
});
it('normalizes unix', () => {
expect(normalizePosix(std)).toEqual(std);
});
});

describe('parseAddonOptions', () => {
it('returns undefined on undefined', () => {
Expand Down
12 changes: 8 additions & 4 deletions packages/sv/lib/cli/tests/snapshots/create-only/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ Everything you need to build a Svelte project, powered by [`sv`](https://github.
If you're seeing this, you've probably already done this step. Congrats!

```sh
# create a new project in the current directory
npx sv create

# create a new project in my-app
# create a new project
npx sv create my-app
```

To recreate this project with the same configuration:

```sh
# recreate this project
npx sv create --template minimal --types ts --no-install .test-output/cli/create-only
```

## Developing

Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ Everything you need to build a Svelte project, powered by [`sv`](https://github.
If you're seeing this, you've probably already done this step. Congrats!

```sh
# create a new project in the current directory
npx sv create

# create a new project in my-app
# create a new project
npx sv create my-app
```

To recreate this project with the same configuration:

```sh
# recreate this project
npx sv create --template minimal --types ts --add prettier eslint vitest="usages:unit,component" playwright tailwindcss="plugins:typography,forms" sveltekit-adapter="adapter:node" devtools-json drizzle="database:sqlite+sqlite:libsql" lucia="demo:yes" mdsvex paraglide="languageTags:en,es+demo:yes" mcp="ide:claude-code,cursor,gemini,opencode,vscode,other+setup:local" --no-install .test-output/cli/create-with-all-addons
```

## Developing

Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
Expand Down
41 changes: 38 additions & 3 deletions packages/sv/lib/cli/utils/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import pc from 'picocolors';
import fs from 'node:fs';
import path from 'node:path';
import pkg from '../../../package.json' with { type: 'json' };
import * as p from '@clack/prompts';
import type { Argument, HelpConfiguration, Option } from 'commander';
Expand Down Expand Up @@ -137,24 +139,57 @@ export function parseAddonOptions(optionFlags: string | undefined): string[] | u
return options;
}

export function logArgs(
export function buildAndLogArgs(
agent: AgentName | null | undefined,
command: 'create' | 'add',
args: string[],
lastArgs: string[] = []
) {
): string {
const allArgs = ['sv', command, ...args];

// Handle install option
if (agent === null || agent === undefined) allArgs.push('--no-install');
else allArgs.push('--install', agent);

const res = resolveCommand(agent ?? 'npm', 'execute', [...allArgs, ...lastArgs])!;
p.log.message(pc.dim([res.command, ...res.args].join(' ')));
const message = [res.command, ...res.args].join(' ');

p.log.info(pc.dim(`Re-run without prompts:\n${message}`));

return message;
}

export function updateReadme(projectPath: string, command: string) {
const readmePath = path.join(projectPath, 'README.md');
if (!fs.existsSync(readmePath)) return;

let content = fs.readFileSync(readmePath, 'utf-8');

// Check if the Creating a project section exists
const creatingSectionPattern = /## Creating a project[\s\S]*?(?=## |$)/;
const creatingSectionMatch = content.match(creatingSectionPattern);
if (!creatingSectionMatch) return;

// Append to the existing Creating a project section
const existingSection = creatingSectionMatch[0];
const updatedSection =
`${existingSection.trim()}\n\n` +
'To recreate this project with the same configuration:\n\n' +
'```sh\n' +
'# recreate this project\n' +
`${command}\n` +
'```\n\n';

content = content.replace(creatingSectionPattern, updatedSection);
fs.writeFileSync(readmePath, content);
}

export function errorAndExit(message: string) {
p.log.error(message);
p.cancel('Operation failed.');
process.exit(1);
}

export const normalizePosix = (dir: string) => {
return path.posix.normalize(dir.replace(/\\/g, '/'));
};
5 changes: 1 addition & 4 deletions packages/sv/lib/create/shared/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@ Everything you need to build a Svelte project, powered by [`sv`](https://github.
If you're seeing this, you've probably already done this step. Congrats!

```sh
# create a new project in the current directory
npx sv create

# create a new project in my-app
# create a new project
npx sv create my-app
```

Expand Down
Loading