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 +107 -0
- package/dist/create-app.d.ts +9 -0
- package/dist/create-app.d.ts.map +1 -0
- package/dist/create-app.js +101 -0
- package/dist/helpers/copy.d.ts +8 -0
- package/dist/helpers/copy.d.ts.map +1 -0
- package/dist/helpers/copy.js +32 -0
- package/dist/helpers/get-pkg-manager.d.ts +3 -0
- package/dist/helpers/get-pkg-manager.d.ts.map +1 -0
- package/dist/helpers/get-pkg-manager.js +37 -0
- package/dist/helpers/install.d.ts +12 -0
- package/dist/helpers/install.d.ts.map +1 -0
- package/dist/helpers/install.js +50 -0
- package/dist/helpers/is-folder-empty.d.ts +2 -0
- package/dist/helpers/is-folder-empty.d.ts.map +1 -0
- package/dist/helpers/is-folder-empty.js +56 -0
- package/dist/helpers/is-online.d.ts +2 -0
- package/dist/helpers/is-online.d.ts.map +1 -0
- package/dist/helpers/is-online.js +15 -0
- package/dist/helpers/validate-pkg.d.ts +5 -0
- package/dist/helpers/validate-pkg.d.ts.map +1 -0
- package/dist/helpers/validate-pkg.js +20 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +169 -0
- package/package.json +50 -0
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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 @@
|
|
|
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
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -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
|
+
}
|