create-v0-sdk-app 0.1.0 → 0.1.2

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/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2025 Vercel, Inc.
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
package/README.md CHANGED
@@ -1,18 +1,18 @@
1
1
  # create-v0-sdk-app
2
2
 
3
- Create v0 SDK-powered apps with one command.
3
+ Create [v0 SDK](https://v0-sdk.dev)-powered apps with one command.
4
4
 
5
5
  ## Usage
6
6
 
7
7
  ```bash
8
- npx create-v0-sdk-app my-app
8
+ pnpm create v0-sdk-app my-app
9
9
  ```
10
10
 
11
11
  Or with other package managers:
12
12
 
13
13
  ```bash
14
- # With pnpm
15
- pnpm create v0-sdk-app my-app
14
+ # With npm
15
+ npx create-v0-sdk-app my-app
16
16
 
17
17
  # With yarn
18
18
  yarn create v0-sdk-app my-app
@@ -24,44 +24,44 @@ bun create v0-sdk-app my-app
24
24
  ## Options
25
25
 
26
26
  - `--example <example-name>` - Specify which example to use
27
+ - `--use-pnpm` - Use pnpm as the package manager (recommended)
27
28
  - `--use-npm` - Use npm as the package manager
28
- - `--use-pnpm` - Use pnpm as the package manager
29
29
  - `--use-yarn` - Use Yarn as the package manager
30
30
  - `--use-bun` - Use Bun as the package manager
31
31
  - `--skip-install` - Skip installing dependencies
32
32
 
33
33
  ## Available Examples
34
34
 
35
- ### ai-tools-example
35
+ ### v0-clone (Recommended)
36
36
 
37
- Node.js example using `@v0-sdk/ai-tools` with AI SDK. Perfect for building AI-powered command-line tools and scripts.
37
+ Next.js app that replicates the v0.dev interface with modern React components and AI chat functionality. This is the recommended starting point for most projects.
38
38
 
39
39
  ```bash
40
- npx create-v0-sdk-app my-ai-app --example ai-tools-example
40
+ pnpm create v0-sdk-app my-app --example v0-clone
41
41
  ```
42
42
 
43
43
  ### classic-v0
44
44
 
45
- Full-featured Next.js app similar to v0.dev with project management, chat interface, and code generation capabilities.
45
+ Full-featured Next.js app similar to the original v0.dev with project management, chat interface, and code generation capabilities.
46
46
 
47
47
  ```bash
48
- npx create-v0-sdk-app my-classic-app --example classic-v0
48
+ pnpm create v0-sdk-app my-classic-app --example classic-v0
49
49
  ```
50
50
 
51
- ### v0-clone
51
+ ### v0-sdk-react-example
52
52
 
53
- Next.js app that replicates the v0.dev interface with modern React components and AI chat functionality.
53
+ Next.js example using `@v0-sdk/react` components with different UI themes (minimal, elegant, neobrutalism, terminal).
54
54
 
55
55
  ```bash
56
- npx create-v0-sdk-app my-clone-app --example v0-clone
56
+ pnpm create v0-sdk-app my-react-app --example v0-sdk-react-example
57
57
  ```
58
58
 
59
- ### v0-sdk-react-example
59
+ ### ai-tools-example
60
60
 
61
- Next.js example using `@v0-sdk/react` components with different UI themes (minimal, elegant, neobrutalism, terminal).
61
+ Node.js example using `@v0-sdk/ai-tools` with AI SDK. Perfect for building AI-powered command-line tools and scripts.
62
62
 
63
63
  ```bash
64
- npx create-v0-sdk-app my-react-app --example v0-sdk-react-example
64
+ pnpm create v0-sdk-app my-ai-app --example ai-tools-example
65
65
  ```
66
66
 
67
67
  ## Interactive Mode
@@ -69,12 +69,12 @@ npx create-v0-sdk-app my-react-app --example v0-sdk-react-example
69
69
  If you don't specify an example, the CLI will prompt you to choose one:
70
70
 
71
71
  ```bash
72
- npx create-v0-sdk-app my-app
72
+ pnpm create v0-sdk-app my-app
73
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
74
+ # ❯ v0-clone - Next.js app that replicates the v0.dev interface (Recommended)
75
+ # classic-v0 - Full-featured Next.js app similar to the original v0.dev
77
76
  # v0-sdk-react-example - Next.js example using @v0-sdk/react components
77
+ # ai-tools-example - Node.js example using @v0-sdk/ai-tools with AI SDK
78
78
  ```
79
79
 
80
80
  ## What's Included
@@ -104,4 +104,4 @@ pnpm dev --help
104
104
 
105
105
  ## License
106
106
 
107
- MIT
107
+ Apache 2.0
@@ -1 +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"}
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;AAM/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,CA2HhB"}
@@ -8,6 +8,7 @@ const copy_1 = require("./helpers/copy");
8
8
  const install_1 = require("./helpers/install");
9
9
  const is_folder_empty_1 = require("./helpers/is-folder-empty");
10
10
  const is_online_1 = require("./helpers/is-online");
11
+ const download_1 = require("./helpers/download");
11
12
  async function createApp({ appPath, packageManager, example, skipInstall, }) {
12
13
  const appName = (0, node_path_1.basename)(appPath);
13
14
  if ((0, node_fs_1.existsSync)(appPath) && !(0, is_folder_empty_1.isFolderEmpty)(appPath, appName)) {
@@ -15,26 +16,28 @@ async function createApp({ appPath, packageManager, example, skipInstall, }) {
15
16
  }
16
17
  console.log(`Creating a new v0 SDK app in ${(0, picocolors_1.green)(appPath)}.`);
17
18
  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.`);
19
+ // Download example from GitHub repository
20
+ const repoUrl = 'https://github.com/vercel/v0-sdk';
21
+ const examplePath = `examples/${example}`;
22
+ console.log(`Downloading files from ${(0, picocolors_1.cyan)(repoUrl)}. This might take a moment.`);
23
+ console.log();
24
+ try {
25
+ await (0, download_1.downloadAndExtractExample)(repoUrl, examplePath, appPath);
26
+ // Rename gitignore file if it exists
27
+ const gitignorePath = (0, node_path_1.join)(appPath, 'gitignore');
28
+ const dotGitignorePath = (0, node_path_1.join)(appPath, '.gitignore');
29
+ if ((0, node_fs_1.existsSync)(gitignorePath)) {
30
+ await (0, copy_1.copy)(['gitignore'], appPath, {
31
+ cwd: appPath,
32
+ rename: () => '.gitignore',
33
+ });
34
+ }
35
+ }
36
+ catch (error) {
37
+ console.error(`Failed to download example ${(0, picocolors_1.red)(example)}:`, error);
38
+ console.error(`Example ${(0, picocolors_1.red)(example)} does not exist or could not be downloaded.`);
25
39
  process.exit(1);
26
40
  }
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
41
  // Update package.json to use published packages instead of workspace references
39
42
  const packageJsonPath = (0, node_path_1.join)(appPath, 'package.json');
40
43
  if ((0, node_fs_1.existsSync)(packageJsonPath)) {
@@ -55,9 +58,8 @@ async function createApp({ appPath, packageManager, example, skipInstall, }) {
55
58
  deps[name] = packageVersions[name];
56
59
  }
57
60
  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
+ // Fallback to latest version
62
+ deps[name] = 'latest';
61
63
  }
62
64
  }
63
65
  }
@@ -0,0 +1,12 @@
1
+ export type RepoInfo = {
2
+ username: string;
3
+ name: string;
4
+ branch: string;
5
+ filePath: string;
6
+ };
7
+ export declare function isUrlOk(url: string): Promise<boolean>;
8
+ export declare function getRepoInfo(url: URL, examplePath?: string): Promise<RepoInfo | undefined>;
9
+ export declare function hasRepo({ username, name, branch, filePath, }: RepoInfo): Promise<boolean>;
10
+ export declare function downloadAndExtractRepo(root: string, { username, name, branch, filePath }: RepoInfo): Promise<void>;
11
+ export declare function downloadAndExtractExample(repoUrl: string, examplePath: string, appPath: string): Promise<void>;
12
+ //# sourceMappingURL=download.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"download.d.ts","sourceRoot":"","sources":["../../src/helpers/download.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;CACjB,CAAA;AAED,wBAAsB,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO3D;AAED,wBAAsB,WAAW,CAC/B,GAAG,EAAE,GAAG,EACR,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,QAAQ,GAAG,SAAS,CAAC,CAoC/B;AAED,wBAAgB,OAAO,CAAC,EACtB,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,QAAQ,GACT,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAK7B;AAED,wBAAsB,sBAAsB,CAC1C,IAAI,EAAE,MAAM,EACZ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,QAAQ,GAC7C,OAAO,CAAC,IAAI,CAAC,CAiCf;AAED,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAcf"}
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isUrlOk = isUrlOk;
4
+ exports.getRepoInfo = getRepoInfo;
5
+ exports.hasRepo = hasRepo;
6
+ exports.downloadAndExtractRepo = downloadAndExtractRepo;
7
+ exports.downloadAndExtractExample = downloadAndExtractExample;
8
+ const node_stream_1 = require("node:stream");
9
+ const promises_1 = require("node:stream/promises");
10
+ const node_fs_1 = require("node:fs");
11
+ const tar_1 = require("tar");
12
+ async function isUrlOk(url) {
13
+ try {
14
+ const res = await fetch(url, { method: 'HEAD' });
15
+ return res.status === 200;
16
+ }
17
+ catch {
18
+ return false;
19
+ }
20
+ }
21
+ async function getRepoInfo(url, examplePath) {
22
+ const [, username, name, t, _branch, ...file] = url.pathname.split('/');
23
+ const filePath = examplePath ? examplePath.replace(/^\//, '') : file.join('/');
24
+ if (
25
+ // Support repos whose entire purpose is to be a Next.js example, e.g.
26
+ // https://github.com/:username/:my-cool-nextjs-example-repo-name.
27
+ t === undefined ||
28
+ // Support GitHub URL that ends with a trailing slash, e.g.
29
+ // https://github.com/:username/:my-cool-nextjs-example-repo-name/
30
+ // In this case "t" will be an empty string while the next part "_branch" will be undefined
31
+ (t === '' && _branch === undefined)) {
32
+ try {
33
+ const infoResponse = await fetch(`https://api.github.com/repos/${username}/${name}`);
34
+ if (infoResponse.status !== 200) {
35
+ return;
36
+ }
37
+ const info = await infoResponse.json();
38
+ return { username, name, branch: info['default_branch'], filePath };
39
+ }
40
+ catch {
41
+ return;
42
+ }
43
+ }
44
+ // Support GitHub URL that ends with a trailing slash, e.g.
45
+ // https://github.com/:username/:my-cool-nextjs-example-repo-name/tree/my-cool-branch/
46
+ // In this case "t" will be "tree" and "_branch" will be the branch name
47
+ if (t === 'tree' && _branch) {
48
+ return { username, name, branch: _branch, filePath };
49
+ }
50
+ return { username, name, branch: 'main', filePath };
51
+ }
52
+ function hasRepo({ username, name, branch, filePath, }) {
53
+ const contentsUrl = `https://api.github.com/repos/${username}/${name}/contents`;
54
+ const packagePath = `${filePath ? `/${filePath}` : ''}/package.json`;
55
+ return isUrlOk(`${contentsUrl}${packagePath}?ref=${branch}`);
56
+ }
57
+ async function downloadAndExtractRepo(root, { username, name, branch, filePath }) {
58
+ const tempFile = `https://codeload.github.com/${username}/${name}/tar.gz/${branch}`;
59
+ // Ensure the target directory exists
60
+ (0, node_fs_1.mkdirSync)(root, { recursive: true });
61
+ const response = await fetch(tempFile);
62
+ if (!response.body) {
63
+ throw new Error('Failed to fetch repository');
64
+ }
65
+ return (0, promises_1.pipeline)(node_stream_1.Readable.fromWeb(response.body), (0, tar_1.x)({
66
+ cwd: root,
67
+ strip: filePath ? filePath.split('/').length + 1 : 1,
68
+ filter: (path) => {
69
+ const chunks = path.split('/');
70
+ if (filePath) {
71
+ const filePathChunks = filePath.split('/');
72
+ if (chunks.length <= filePathChunks.length ||
73
+ filePathChunks.some((chunk, i) => chunks[i + 1] !== chunk)) {
74
+ return false;
75
+ }
76
+ }
77
+ const [, ...fileChunks] = chunks;
78
+ const target = fileChunks.join('/');
79
+ return !target.startsWith('.git');
80
+ },
81
+ }));
82
+ }
83
+ async function downloadAndExtractExample(repoUrl, examplePath, appPath) {
84
+ const url = new URL(repoUrl);
85
+ const repoInfo = await getRepoInfo(url, examplePath);
86
+ if (!repoInfo) {
87
+ throw new Error(`Could not get repository info for ${repoUrl}`);
88
+ }
89
+ const hasExample = await hasRepo(repoInfo);
90
+ if (!hasExample) {
91
+ throw new Error(`Example "${examplePath}" not found in repository`);
92
+ }
93
+ await downloadAndExtractRepo(appPath, repoInfo);
94
+ }
package/dist/index.js CHANGED
@@ -29,21 +29,21 @@ const onPromptState = (state) => {
29
29
  };
30
30
  const examples = [
31
31
  {
32
- name: 'ai-tools-example',
33
- description: 'Node.js example using @v0-sdk/ai-tools with AI SDK + AI Gateway',
32
+ name: 'v0-clone',
33
+ description: 'Next.js app that replicates the v0.dev interface (Recommended)',
34
34
  },
35
35
  {
36
36
  name: 'classic-v0',
37
37
  description: 'Full-featured Next.js app similar to the original v0.dev released in 2023',
38
38
  },
39
- {
40
- name: 'v0-clone',
41
- description: 'Next.js app that replicates the v0.dev interface',
42
- },
43
39
  {
44
40
  name: 'v0-sdk-react-example',
45
41
  description: 'Next.js example using @v0-sdk/react components',
46
42
  },
43
+ {
44
+ name: 'ai-tools-example',
45
+ description: 'Node.js example using @v0-sdk/ai-tools with AI SDK + AI Gateway',
46
+ },
47
47
  ];
48
48
  const program = new commander_1.Command(package_json_1.default.name)
49
49
  .version(package_json_1.default.version, '-v, --version', 'Output the current version of create-v0-sdk-app.')
@@ -55,8 +55,8 @@ const program = new commander_1.Command(package_json_1.default.name)
55
55
  An example to bootstrap the app with. Available examples:
56
56
  ${examples.map((ex) => ` • ${ex.name}: ${ex.description}`).join('\n ')}
57
57
  `)
58
+ .option('--use-pnpm', 'Explicitly tell the CLI to bootstrap the application using pnpm (recommended).')
58
59
  .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
60
  .option('--use-yarn', 'Explicitly tell the CLI to bootstrap the application using Yarn.')
61
61
  .option('--use-bun', 'Explicitly tell the CLI to bootstrap the application using Bun.')
62
62
  .option('--skip-install', 'Explicitly tell the CLI to skip installing packages.')
@@ -68,10 +68,10 @@ const program = new commander_1.Command(package_json_1.default.name)
68
68
  .allowUnknownOption()
69
69
  .parse(process.argv);
70
70
  const opts = program.opts();
71
- const packageManager = !!opts.useNpm
72
- ? 'npm'
73
- : !!opts.usePnpm
74
- ? 'pnpm'
71
+ const packageManager = !!opts.usePnpm
72
+ ? 'pnpm'
73
+ : !!opts.useNpm
74
+ ? 'npm'
75
75
  : !!opts.useYarn
76
76
  ? 'yarn'
77
77
  : !!opts.useBun
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-v0-sdk-app",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "keywords": [
5
5
  "v0",
6
6
  "v0-sdk",
@@ -15,25 +15,21 @@
15
15
  "directory": "packages/create-v0-sdk-app"
16
16
  },
17
17
  "author": "Vercel Team <support@vercel.com>",
18
- "license": "MIT",
18
+ "license": "Apache-2.0",
19
19
  "bin": {
20
20
  "create-v0-sdk-app": "./dist/index.js"
21
21
  },
22
22
  "files": [
23
23
  "dist"
24
24
  ],
25
- "scripts": {
26
- "dev": "tsx src/index.ts",
27
- "build": "tsc",
28
- "type-check": "tsc --noEmit"
29
- },
30
25
  "dependencies": {
31
26
  "commander": "^12.1.0",
32
27
  "prompts": "^2.4.2",
33
28
  "picocolors": "^1.0.0",
34
29
  "cross-spawn": "^7.0.3",
35
30
  "fast-glob": "^3.3.1",
36
- "validate-npm-package-name": "^5.0.1"
31
+ "validate-npm-package-name": "^5.0.1",
32
+ "tar": "^7.4.3"
37
33
  },
38
34
  "devDependencies": {
39
35
  "@types/cross-spawn": "^6.0.0",
@@ -46,5 +42,12 @@
46
42
  "engines": {
47
43
  "node": ">=22",
48
44
  "pnpm": ">=9"
45
+ },
46
+ "scripts": {
47
+ "dev": "tsx src/index.ts",
48
+ "build": "tsc",
49
+ "clean": "rm -rf dist *.tsbuildinfo",
50
+ "type-check": "tsc --noEmit",
51
+ "lint": "echo 'No linting configured for create-v0-sdk-app package'"
49
52
  }
50
- }
53
+ }