create-v0-sdk-app 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,107 @@
1
+ # create-v0-sdk-app
2
+
3
+ Create v0 SDK-powered apps with one command.
4
+
5
+ ## Usage
6
+
7
+ ```bash
8
+ npx create-v0-sdk-app my-app
9
+ ```
10
+
11
+ Or with other package managers:
12
+
13
+ ```bash
14
+ # With pnpm
15
+ pnpm create v0-sdk-app my-app
16
+
17
+ # With yarn
18
+ yarn create v0-sdk-app my-app
19
+
20
+ # With bun
21
+ bun create v0-sdk-app my-app
22
+ ```
23
+
24
+ ## Options
25
+
26
+ - `--example <example-name>` - Specify which example to use
27
+ - `--use-npm` - Use npm as the package manager
28
+ - `--use-pnpm` - Use pnpm as the package manager
29
+ - `--use-yarn` - Use Yarn as the package manager
30
+ - `--use-bun` - Use Bun as the package manager
31
+ - `--skip-install` - Skip installing dependencies
32
+
33
+ ## Available Examples
34
+
35
+ ### ai-tools-example
36
+
37
+ Node.js example using `@v0-sdk/ai-tools` with AI SDK. Perfect for building AI-powered command-line tools and scripts.
38
+
39
+ ```bash
40
+ npx create-v0-sdk-app my-ai-app --example ai-tools-example
41
+ ```
42
+
43
+ ### classic-v0
44
+
45
+ Full-featured Next.js app similar to v0.dev with project management, chat interface, and code generation capabilities.
46
+
47
+ ```bash
48
+ npx create-v0-sdk-app my-classic-app --example classic-v0
49
+ ```
50
+
51
+ ### v0-clone
52
+
53
+ Next.js app that replicates the v0.dev interface with modern React components and AI chat functionality.
54
+
55
+ ```bash
56
+ npx create-v0-sdk-app my-clone-app --example v0-clone
57
+ ```
58
+
59
+ ### v0-sdk-react-example
60
+
61
+ Next.js example using `@v0-sdk/react` components with different UI themes (minimal, elegant, neobrutalism, terminal).
62
+
63
+ ```bash
64
+ npx create-v0-sdk-app my-react-app --example v0-sdk-react-example
65
+ ```
66
+
67
+ ## Interactive Mode
68
+
69
+ If you don't specify an example, the CLI will prompt you to choose one:
70
+
71
+ ```bash
72
+ npx create-v0-sdk-app my-app
73
+ # ? Which example would you like to use? (Use arrow keys)
74
+ # ❯ ai-tools-example - Node.js example using @v0-sdk/ai-tools with AI SDK
75
+ # classic-v0 - Full-featured Next.js app similar to v0.dev
76
+ # v0-clone - Next.js app that replicates the v0.dev interface
77
+ # v0-sdk-react-example - Next.js example using @v0-sdk/react components
78
+ ```
79
+
80
+ ## What's Included
81
+
82
+ Each example comes pre-configured with:
83
+
84
+ - TypeScript support
85
+ - Modern tooling and build setup
86
+ - Example code and documentation
87
+ - Proper dependency management
88
+ - Development and production scripts
89
+
90
+ ## Development
91
+
92
+ To work on this package:
93
+
94
+ ```bash
95
+ # Install dependencies
96
+ pnpm install
97
+
98
+ # Build the package
99
+ pnpm build
100
+
101
+ # Test locally
102
+ pnpm dev --help
103
+ ```
104
+
105
+ ## License
106
+
107
+ MIT
@@ -0,0 +1,9 @@
1
+ import type { PackageManager } from './helpers/get-pkg-manager';
2
+ export type ExampleType = 'ai-tools-example' | 'classic-v0' | 'v0-clone' | 'v0-sdk-react-example';
3
+ export declare function createApp({ appPath, packageManager, example, skipInstall, }: {
4
+ appPath: string;
5
+ packageManager: PackageManager;
6
+ example: ExampleType;
7
+ skipInstall: boolean;
8
+ }): Promise<void>;
9
+ //# sourceMappingURL=create-app.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"create-app.d.ts","sourceRoot":"","sources":["../src/create-app.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAK/D,MAAM,MAAM,WAAW,GACnB,kBAAkB,GAClB,YAAY,GACZ,UAAU,GACV,sBAAsB,CAAA;AAE1B,wBAAsB,SAAS,CAAC,EAC9B,OAAO,EACP,cAAc,EACd,OAAO,EACP,WAAW,GACZ,EAAE;IACD,OAAO,EAAE,MAAM,CAAA;IACf,cAAc,EAAE,cAAc,CAAA;IAC9B,OAAO,EAAE,WAAW,CAAA;IACpB,WAAW,EAAE,OAAO,CAAA;CACrB,GAAG,OAAO,CAAC,IAAI,CAAC,CAwHhB"}
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createApp = createApp;
4
+ const node_path_1 = require("node:path");
5
+ const node_fs_1 = require("node:fs");
6
+ const picocolors_1 = require("picocolors");
7
+ const copy_1 = require("./helpers/copy");
8
+ const install_1 = require("./helpers/install");
9
+ const is_folder_empty_1 = require("./helpers/is-folder-empty");
10
+ const is_online_1 = require("./helpers/is-online");
11
+ async function createApp({ appPath, packageManager, example, skipInstall, }) {
12
+ const appName = (0, node_path_1.basename)(appPath);
13
+ if ((0, node_fs_1.existsSync)(appPath) && !(0, is_folder_empty_1.isFolderEmpty)(appPath, appName)) {
14
+ process.exit(1);
15
+ }
16
+ console.log(`Creating a new v0 SDK app in ${(0, picocolors_1.green)(appPath)}.`);
17
+ console.log();
18
+ // Find the examples directory relative to this package
19
+ const packageRoot = (0, node_path_1.resolve)(__dirname, '..');
20
+ const monorepoRoot = (0, node_path_1.resolve)(packageRoot, '../..');
21
+ const examplesDir = (0, node_path_1.join)(monorepoRoot, 'examples');
22
+ const examplePath = (0, node_path_1.join)(examplesDir, example);
23
+ if (!(0, node_fs_1.existsSync)(examplePath)) {
24
+ console.error(`Example ${(0, picocolors_1.red)(example)} does not exist.`);
25
+ process.exit(1);
26
+ }
27
+ // Copy the example to the target directory
28
+ await (0, copy_1.copy)(['**'], appPath, {
29
+ cwd: examplePath,
30
+ rename: (name) => {
31
+ // Rename gitignore template to .gitignore
32
+ if (name === 'gitignore') {
33
+ return '.gitignore';
34
+ }
35
+ return name;
36
+ },
37
+ });
38
+ // Update package.json to use published packages instead of workspace references
39
+ const packageJsonPath = (0, node_path_1.join)(appPath, 'package.json');
40
+ if ((0, node_fs_1.existsSync)(packageJsonPath)) {
41
+ const packageJson = JSON.parse((0, node_fs_1.readFileSync)(packageJsonPath, 'utf8'));
42
+ // Replace workspace dependencies with actual versions
43
+ const replaceWorkspaceDeps = (deps) => {
44
+ if (!deps)
45
+ return;
46
+ for (const [name, version] of Object.entries(deps)) {
47
+ if (version === 'workspace:*') {
48
+ // Map workspace packages to their published versions
49
+ const packageVersions = {
50
+ 'v0-sdk': '^0.11.0',
51
+ '@v0-sdk/react': '^0.3.0',
52
+ '@v0-sdk/ai-tools': '^0.1.0',
53
+ };
54
+ if (packageVersions[name]) {
55
+ deps[name] = packageVersions[name];
56
+ }
57
+ else {
58
+ // Fallback to monorepo version
59
+ const rootPackageJson = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(monorepoRoot, 'package.json'), 'utf8'));
60
+ deps[name] = `^${rootPackageJson.version}`;
61
+ }
62
+ }
63
+ }
64
+ };
65
+ replaceWorkspaceDeps(packageJson.dependencies);
66
+ replaceWorkspaceDeps(packageJson.devDependencies);
67
+ // Update the package name to match the app name
68
+ packageJson.name = appName;
69
+ (0, node_fs_1.writeFileSync)(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
70
+ }
71
+ process.chdir(appPath);
72
+ if (!skipInstall) {
73
+ console.log('Installing packages. This might take a couple of minutes.');
74
+ console.log();
75
+ const isOnline = await (0, is_online_1.getOnline)();
76
+ await (0, install_1.install)(packageManager, isOnline);
77
+ console.log();
78
+ }
79
+ console.log(`${(0, picocolors_1.green)('Success!')} Created ${appName} at ${appPath}`);
80
+ console.log('Inside that directory, you can run several commands:');
81
+ console.log();
82
+ console.log((0, picocolors_1.cyan)(` ${packageManager} ${packageManager === 'npm' ? 'run ' : ''}dev`));
83
+ console.log(' Starts the development server.');
84
+ console.log();
85
+ console.log((0, picocolors_1.cyan)(` ${packageManager} ${packageManager === 'npm' ? 'run ' : ''}build`));
86
+ console.log(' Builds the app for production.');
87
+ console.log();
88
+ if (example !== 'ai-tools-example') {
89
+ console.log((0, picocolors_1.cyan)(` ${packageManager} ${packageManager === 'npm' ? 'run ' : ''}start`));
90
+ console.log(' Runs the built app in production mode.');
91
+ console.log();
92
+ }
93
+ console.log('We suggest that you begin by typing:');
94
+ console.log();
95
+ console.log((0, picocolors_1.cyan)(' cd'), appName);
96
+ if (skipInstall) {
97
+ console.log((0, picocolors_1.cyan)(` ${packageManager} install`));
98
+ }
99
+ console.log((0, picocolors_1.cyan)(` ${packageManager} ${packageManager === 'npm' ? 'run ' : ''}dev`));
100
+ console.log();
101
+ }
@@ -0,0 +1,8 @@
1
+ interface CopyOption {
2
+ cwd?: string;
3
+ rename?: (basename: string) => string;
4
+ parents?: boolean;
5
+ }
6
+ export declare const copy: (src: string | string[], dest: string, { cwd, rename, parents }?: CopyOption) => Promise<void[]>;
7
+ export {};
8
+ //# sourceMappingURL=copy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"copy.d.ts","sourceRoot":"","sources":["../../src/helpers/copy.ts"],"names":[],"mappings":"AAIA,UAAU,UAAU;IAClB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,CAAA;IACrC,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAID,eAAO,MAAM,IAAI,QACV,MAAM,GAAG,MAAM,EAAE,QAChB,MAAM,6BACgC,UAAU,oBAiCvD,CAAA"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.copy = void 0;
4
+ const node_path_1 = require("node:path");
5
+ const promises_1 = require("node:fs/promises");
6
+ const fast_glob_1 = require("fast-glob");
7
+ const identity = (x) => x;
8
+ const copy = async (src, dest, { cwd, rename = identity, parents = true } = {}) => {
9
+ const source = typeof src === 'string' ? [src] : src;
10
+ if (source.length === 0 || !dest) {
11
+ throw new TypeError('`src` and `dest` are required');
12
+ }
13
+ const sourceFiles = await (0, fast_glob_1.async)(source, {
14
+ cwd,
15
+ dot: true,
16
+ absolute: false,
17
+ stats: false,
18
+ });
19
+ const destRelativeToCwd = cwd ? (0, node_path_1.resolve)(cwd, dest) : dest;
20
+ return Promise.all(sourceFiles.map(async (p) => {
21
+ const dirName = (0, node_path_1.dirname)(p);
22
+ const baseName = rename((0, node_path_1.basename)(p));
23
+ const from = cwd ? (0, node_path_1.resolve)(cwd, p) : p;
24
+ const to = parents
25
+ ? (0, node_path_1.join)(destRelativeToCwd, dirName, baseName)
26
+ : (0, node_path_1.join)(destRelativeToCwd, baseName);
27
+ // Ensure the destination directory exists
28
+ await (0, promises_1.mkdir)((0, node_path_1.dirname)(to), { recursive: true });
29
+ return (0, promises_1.copyFile)(from, to);
30
+ }));
31
+ };
32
+ exports.copy = copy;
@@ -0,0 +1,3 @@
1
+ export type PackageManager = 'npm' | 'pnpm' | 'yarn' | 'bun';
2
+ export declare function getPkgManager(): PackageManager;
3
+ //# sourceMappingURL=get-pkg-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-pkg-manager.d.ts","sourceRoot":"","sources":["../../src/helpers/get-pkg-manager.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;AAE5D,wBAAgB,aAAa,IAAI,cAAc,CA6B9C"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getPkgManager = getPkgManager;
4
+ const node_child_process_1 = require("node:child_process");
5
+ function getPkgManager() {
6
+ const userAgent = process.env.npm_config_user_agent;
7
+ if (userAgent) {
8
+ if (userAgent.startsWith('yarn')) {
9
+ return 'yarn';
10
+ }
11
+ else if (userAgent.startsWith('pnpm')) {
12
+ return 'pnpm';
13
+ }
14
+ else if (userAgent.startsWith('bun')) {
15
+ return 'bun';
16
+ }
17
+ }
18
+ try {
19
+ (0, node_child_process_1.execSync)('pnpm --version', { stdio: 'ignore' });
20
+ return 'pnpm';
21
+ }
22
+ catch {
23
+ try {
24
+ (0, node_child_process_1.execSync)('yarn --version', { stdio: 'ignore' });
25
+ return 'yarn';
26
+ }
27
+ catch {
28
+ try {
29
+ (0, node_child_process_1.execSync)('bun --version', { stdio: 'ignore' });
30
+ return 'bun';
31
+ }
32
+ catch {
33
+ return 'npm';
34
+ }
35
+ }
36
+ }
37
+ }
@@ -0,0 +1,12 @@
1
+ import type { PackageManager } from './get-pkg-manager';
2
+ /**
3
+ * Spawn a package manager installation based on user preference.
4
+ *
5
+ * @returns A Promise that resolves once the installation is finished.
6
+ */
7
+ export declare function install(
8
+ /** Indicate which package manager to use. */
9
+ packageManager: PackageManager,
10
+ /** Indicate whether there is an active Internet connection.*/
11
+ isOnline: boolean): Promise<void>;
12
+ //# sourceMappingURL=install.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../../src/helpers/install.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAEvD;;;;GAIG;AACH,wBAAsB,OAAO;AAC3B,6CAA6C;AAC7C,cAAc,EAAE,cAAc;AAC9B,8DAA8D;AAC9D,QAAQ,EAAE,OAAO,GAChB,OAAO,CAAC,IAAI,CAAC,CAkCf"}
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.install = install;
7
+ const picocolors_1 = require("picocolors");
8
+ const cross_spawn_1 = __importDefault(require("cross-spawn"));
9
+ /**
10
+ * Spawn a package manager installation based on user preference.
11
+ *
12
+ * @returns A Promise that resolves once the installation is finished.
13
+ */
14
+ async function install(
15
+ /** Indicate which package manager to use. */
16
+ packageManager,
17
+ /** Indicate whether there is an active Internet connection.*/
18
+ isOnline) {
19
+ const args = ['install'];
20
+ if (!isOnline) {
21
+ console.log((0, picocolors_1.yellow)('You appear to be offline.\nFalling back to the local cache.'));
22
+ args.push('--offline');
23
+ }
24
+ /**
25
+ * Return a Promise that resolves once the installation is finished.
26
+ */
27
+ return new Promise((resolve, reject) => {
28
+ /**
29
+ * Spawn the installation process.
30
+ */
31
+ const child = (0, cross_spawn_1.default)(packageManager, args, {
32
+ stdio: 'inherit',
33
+ env: {
34
+ ...process.env,
35
+ ADBLOCK: '1',
36
+ // we set NODE_ENV to development as pnpm skips dev
37
+ // dependencies when production
38
+ NODE_ENV: 'development',
39
+ DISABLE_OPENCOLLECTIVE: '1',
40
+ },
41
+ });
42
+ child.on('close', (code) => {
43
+ if (code !== 0) {
44
+ reject({ command: `${packageManager} ${args.join(' ')}` });
45
+ return;
46
+ }
47
+ resolve();
48
+ });
49
+ });
50
+ }
@@ -0,0 +1,2 @@
1
+ export declare function isFolderEmpty(root: string, name: string): boolean;
2
+ //# sourceMappingURL=is-folder-empty.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-folder-empty.d.ts","sourceRoot":"","sources":["../../src/helpers/is-folder-empty.ts"],"names":[],"mappings":"AAoBA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAyCjE"}
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isFolderEmpty = isFolderEmpty;
4
+ const node_fs_1 = require("node:fs");
5
+ const node_path_1 = require("node:path");
6
+ const picocolors_1 = require("picocolors");
7
+ const VALID_FILES = [
8
+ '.DS_Store',
9
+ '.git',
10
+ '.gitattributes',
11
+ '.gitignore',
12
+ '.gitlab-ci.yml',
13
+ '.hg',
14
+ '.hgcheck',
15
+ '.hgignore',
16
+ 'docs',
17
+ 'LICENSE',
18
+ 'README.md',
19
+ 'mkdocs.yml',
20
+ 'Thumbs.db',
21
+ ];
22
+ function isFolderEmpty(root, name) {
23
+ const validFiles = VALID_FILES;
24
+ let conflicts = [];
25
+ try {
26
+ conflicts = (0, node_fs_1.readdirSync)(root).filter((file) => !validFiles.includes(file) &&
27
+ // Support IntelliJ IDEA-based editors
28
+ !file.startsWith('.idea'));
29
+ }
30
+ catch {
31
+ return true;
32
+ }
33
+ if (conflicts.length > 0) {
34
+ console.log(`The directory ${(0, picocolors_1.red)(name)} contains files that could conflict:`);
35
+ console.log();
36
+ for (const file of conflicts) {
37
+ try {
38
+ const stats = (0, node_fs_1.readdirSync)((0, node_path_1.resolve)(root, file));
39
+ if (stats.length > 0) {
40
+ console.log(` ${file}/`);
41
+ }
42
+ else {
43
+ console.log(` ${file}`);
44
+ }
45
+ }
46
+ catch {
47
+ console.log(` ${file}`);
48
+ }
49
+ }
50
+ console.log();
51
+ console.log('Either try using a new directory name, or remove the files listed above.');
52
+ console.log();
53
+ return false;
54
+ }
55
+ return true;
56
+ }
@@ -0,0 +1,2 @@
1
+ export declare function getOnline(): Promise<boolean>;
2
+ //# sourceMappingURL=is-online.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"is-online.d.ts","sourceRoot":"","sources":["../../src/helpers/is-online.ts"],"names":[],"mappings":"AAEA,wBAAgB,SAAS,IAAI,OAAO,CAAC,OAAO,CAAC,CAS5C"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getOnline = getOnline;
4
+ const node_child_process_1 = require("node:child_process");
5
+ function getOnline() {
6
+ return new Promise((resolve) => {
7
+ try {
8
+ (0, node_child_process_1.execSync)('ping -c 1 8.8.8.8', { stdio: 'ignore' });
9
+ resolve(true);
10
+ }
11
+ catch {
12
+ resolve(false);
13
+ }
14
+ });
15
+ }
@@ -0,0 +1,5 @@
1
+ export declare function validateNpmName(name: string): {
2
+ valid: boolean;
3
+ problems: string[];
4
+ };
5
+ //# sourceMappingURL=validate-pkg.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-pkg.d.ts","sourceRoot":"","sources":["../../src/helpers/validate-pkg.ts"],"names":[],"mappings":"AAEA,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG;IAC7C,KAAK,EAAE,OAAO,CAAA;IACd,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB,CAaA"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.validateNpmName = validateNpmName;
7
+ const validate_npm_package_name_1 = __importDefault(require("validate-npm-package-name"));
8
+ function validateNpmName(name) {
9
+ const nameValidation = (0, validate_npm_package_name_1.default)(name);
10
+ if (nameValidation.validForNewPackages) {
11
+ return { valid: true, problems: [] };
12
+ }
13
+ return {
14
+ valid: false,
15
+ problems: [
16
+ ...(nameValidation.errors || []),
17
+ ...(nameValidation.warnings || []),
18
+ ],
19
+ };
20
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
package/dist/index.js ADDED
@@ -0,0 +1,169 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const commander_1 = require("commander");
8
+ const node_fs_1 = require("node:fs");
9
+ const node_path_1 = require("node:path");
10
+ const picocolors_1 = require("picocolors");
11
+ const prompts_1 = __importDefault(require("prompts"));
12
+ const create_app_1 = require("./create-app");
13
+ const get_pkg_manager_1 = require("./helpers/get-pkg-manager");
14
+ const is_folder_empty_1 = require("./helpers/is-folder-empty");
15
+ const validate_pkg_1 = require("./helpers/validate-pkg");
16
+ const package_json_1 = __importDefault(require("../package.json"));
17
+ let projectPath = '';
18
+ const handleSigTerm = () => process.exit(0);
19
+ process.on('SIGINT', handleSigTerm);
20
+ process.on('SIGTERM', handleSigTerm);
21
+ const onPromptState = (state) => {
22
+ if (state.aborted) {
23
+ // If we don't re-enable the terminal cursor before exiting
24
+ // the program, the cursor will remain hidden
25
+ process.stdout.write('\x1B[?25h');
26
+ process.stdout.write('\n');
27
+ process.exit(1);
28
+ }
29
+ };
30
+ const examples = [
31
+ {
32
+ name: 'ai-tools-example',
33
+ description: 'Node.js example using @v0-sdk/ai-tools with AI SDK + AI Gateway',
34
+ },
35
+ {
36
+ name: 'classic-v0',
37
+ description: 'Full-featured Next.js app similar to the original v0.dev released in 2023',
38
+ },
39
+ {
40
+ name: 'v0-clone',
41
+ description: 'Next.js app that replicates the v0.dev interface',
42
+ },
43
+ {
44
+ name: 'v0-sdk-react-example',
45
+ description: 'Next.js example using @v0-sdk/react components',
46
+ },
47
+ ];
48
+ const program = new commander_1.Command(package_json_1.default.name)
49
+ .version(package_json_1.default.version, '-v, --version', 'Output the current version of create-v0-sdk-app.')
50
+ .argument('[directory]')
51
+ .usage('[directory] [options]')
52
+ .helpOption('-h, --help', 'Display this help message.')
53
+ .option('-e, --example <example-name>', `
54
+
55
+ An example to bootstrap the app with. Available examples:
56
+ ${examples.map((ex) => ` • ${ex.name}: ${ex.description}`).join('\n ')}
57
+ `)
58
+ .option('--use-npm', 'Explicitly tell the CLI to bootstrap the application using npm.')
59
+ .option('--use-pnpm', 'Explicitly tell the CLI to bootstrap the application using pnpm.')
60
+ .option('--use-yarn', 'Explicitly tell the CLI to bootstrap the application using Yarn.')
61
+ .option('--use-bun', 'Explicitly tell the CLI to bootstrap the application using Bun.')
62
+ .option('--skip-install', 'Explicitly tell the CLI to skip installing packages.')
63
+ .action((name) => {
64
+ if (name && !name.startsWith('--no-')) {
65
+ projectPath = name;
66
+ }
67
+ })
68
+ .allowUnknownOption()
69
+ .parse(process.argv);
70
+ const opts = program.opts();
71
+ const packageManager = !!opts.useNpm
72
+ ? 'npm'
73
+ : !!opts.usePnpm
74
+ ? 'pnpm'
75
+ : !!opts.useYarn
76
+ ? 'yarn'
77
+ : !!opts.useBun
78
+ ? 'bun'
79
+ : (0, get_pkg_manager_1.getPkgManager)();
80
+ async function run() {
81
+ if (typeof projectPath === 'string') {
82
+ projectPath = projectPath.trim();
83
+ }
84
+ if (!projectPath) {
85
+ const res = await (0, prompts_1.default)({
86
+ onState: onPromptState,
87
+ type: 'text',
88
+ name: 'path',
89
+ message: 'What is your project named?',
90
+ initial: 'my-v0-app',
91
+ validate: (name) => {
92
+ const validation = (0, validate_pkg_1.validateNpmName)((0, node_path_1.basename)((0, node_path_1.resolve)(name)));
93
+ if (validation.valid) {
94
+ return true;
95
+ }
96
+ return 'Invalid project name: ' + validation.problems[0];
97
+ },
98
+ });
99
+ if (typeof res.path === 'string') {
100
+ projectPath = res.path.trim();
101
+ }
102
+ }
103
+ if (!projectPath) {
104
+ console.log('\nPlease specify the project directory:\n' +
105
+ ` ${(0, picocolors_1.cyan)(program.name())} ${(0, picocolors_1.green)('<project-directory>')}\n` +
106
+ 'For example:\n' +
107
+ ` ${(0, picocolors_1.cyan)(program.name())} ${(0, picocolors_1.green)('my-v0-app')}\n\n` +
108
+ `Run ${(0, picocolors_1.cyan)(`${program.name()} --help`)} to see all options.`);
109
+ process.exit(1);
110
+ }
111
+ const appPath = (0, node_path_1.resolve)(projectPath);
112
+ const appName = (0, node_path_1.basename)(appPath);
113
+ const validation = (0, validate_pkg_1.validateNpmName)(appName);
114
+ if (!validation.valid) {
115
+ console.error(`Could not create a project called ${(0, picocolors_1.red)(`"${appName}"`)} because of npm naming restrictions:`);
116
+ validation.problems.forEach((p) => console.error(` ${(0, picocolors_1.red)((0, picocolors_1.bold)('*'))} ${p}`));
117
+ process.exit(1);
118
+ }
119
+ if ((0, node_fs_1.existsSync)(appPath) && !(0, is_folder_empty_1.isFolderEmpty)(appPath, appName)) {
120
+ process.exit(1);
121
+ }
122
+ let example = opts.example;
123
+ if (!example) {
124
+ const res = await (0, prompts_1.default)({
125
+ onState: onPromptState,
126
+ type: 'select',
127
+ name: 'example',
128
+ message: 'Which example would you like to use?',
129
+ choices: examples.map((ex) => ({
130
+ title: ex.name,
131
+ value: ex.name,
132
+ description: ex.description,
133
+ })),
134
+ initial: 0,
135
+ });
136
+ example = res.example;
137
+ }
138
+ if (!example || !examples.some((ex) => ex.name === example)) {
139
+ console.error(`Invalid example "${example}". Available examples are:\n` +
140
+ examples.map((ex) => ` • ${ex.name}`).join('\n'));
141
+ process.exit(1);
142
+ }
143
+ try {
144
+ await (0, create_app_1.createApp)({
145
+ appPath,
146
+ packageManager,
147
+ example,
148
+ skipInstall: opts.skipInstall || false,
149
+ });
150
+ }
151
+ catch (reason) {
152
+ console.error('Aborting installation.');
153
+ if (reason &&
154
+ typeof reason === 'object' &&
155
+ 'command' in reason &&
156
+ typeof reason.command === 'string') {
157
+ console.error(` ${(0, picocolors_1.cyan)(reason.command)} has failed.`);
158
+ }
159
+ else {
160
+ console.error((0, picocolors_1.red)('Unexpected error. Please report it as a bug:'), reason);
161
+ }
162
+ process.exit(1);
163
+ }
164
+ }
165
+ run().catch((reason) => {
166
+ console.error('Aborting installation.');
167
+ console.error((0, picocolors_1.red)('Unexpected error. Please report it as a bug:'), reason);
168
+ process.exit(1);
169
+ });
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "create-v0-sdk-app",
3
+ "version": "0.1.0",
4
+ "keywords": [
5
+ "v0",
6
+ "v0-sdk",
7
+ "ai",
8
+ "react",
9
+ "next.js"
10
+ ],
11
+ "description": "Create v0 SDK-powered apps with one command",
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "https://github.com/vercel/v0-sdk.git",
15
+ "directory": "packages/create-v0-sdk-app"
16
+ },
17
+ "author": "Vercel Team <support@vercel.com>",
18
+ "license": "MIT",
19
+ "bin": {
20
+ "create-v0-sdk-app": "./dist/index.js"
21
+ },
22
+ "files": [
23
+ "dist"
24
+ ],
25
+ "scripts": {
26
+ "dev": "tsx src/index.ts",
27
+ "build": "tsc",
28
+ "type-check": "tsc --noEmit"
29
+ },
30
+ "dependencies": {
31
+ "commander": "^12.1.0",
32
+ "prompts": "^2.4.2",
33
+ "picocolors": "^1.0.0",
34
+ "cross-spawn": "^7.0.3",
35
+ "fast-glob": "^3.3.1",
36
+ "validate-npm-package-name": "^5.0.1"
37
+ },
38
+ "devDependencies": {
39
+ "@types/cross-spawn": "^6.0.0",
40
+ "@types/node": "^22.0.0",
41
+ "@types/prompts": "^2.4.2",
42
+ "@types/validate-npm-package-name": "^4.0.2",
43
+ "tsx": "^4.19.2",
44
+ "typescript": "^5.7.3"
45
+ },
46
+ "engines": {
47
+ "node": ">=22",
48
+ "pnpm": ">=9"
49
+ }
50
+ }