@blu1606/create-walrus-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/dist/__tests__/helpers/adapter-compliance.d.ts +2 -0
- package/dist/__tests__/helpers/adapter-compliance.js +47 -0
- package/dist/__tests__/helpers/fixtures.d.ts +21 -0
- package/dist/__tests__/helpers/fixtures.js +30 -0
- package/dist/__tests__/helpers/fs-helpers.d.ts +12 -0
- package/dist/__tests__/helpers/fs-helpers.js +35 -0
- package/dist/__tests__/helpers/index.d.ts +4 -0
- package/dist/__tests__/helpers/index.js +4 -0
- package/dist/__tests__/helpers/test-hooks.d.ts +3 -0
- package/dist/__tests__/helpers/test-hooks.js +18 -0
- package/dist/context.d.ts +2 -0
- package/dist/context.js +43 -0
- package/dist/context.test.d.ts +1 -0
- package/dist/context.test.js +98 -0
- package/dist/generator/file-ops.d.ts +12 -0
- package/dist/generator/file-ops.js +40 -0
- package/dist/generator/index.d.ts +2 -0
- package/dist/generator/index.js +75 -0
- package/dist/generator/index.test.d.ts +1 -0
- package/dist/generator/index.test.js +143 -0
- package/dist/generator/layers.d.ts +3 -0
- package/dist/generator/layers.js +59 -0
- package/dist/generator/layers.test.d.ts +1 -0
- package/dist/generator/layers.test.js +92 -0
- package/dist/generator/merge.d.ts +14 -0
- package/dist/generator/merge.js +62 -0
- package/dist/generator/merge.test.d.ts +1 -0
- package/dist/generator/merge.test.js +79 -0
- package/dist/generator/transform.d.ts +21 -0
- package/dist/generator/transform.js +52 -0
- package/dist/generator/transform.test.d.ts +1 -0
- package/dist/generator/transform.test.js +51 -0
- package/dist/generator/types.d.ts +18 -0
- package/dist/generator/types.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +106 -0
- package/dist/matrix.d.ts +31 -0
- package/dist/matrix.js +31 -0
- package/dist/matrix.test.d.ts +1 -0
- package/dist/matrix.test.js +70 -0
- package/dist/post-install/git.d.ts +12 -0
- package/dist/post-install/git.js +94 -0
- package/dist/post-install/index.d.ts +16 -0
- package/dist/post-install/index.js +56 -0
- package/dist/post-install/messages.d.ts +9 -0
- package/dist/post-install/messages.js +49 -0
- package/dist/post-install/package-manager.d.ts +14 -0
- package/dist/post-install/package-manager.js +57 -0
- package/dist/post-install/validator.d.ts +14 -0
- package/dist/post-install/validator.js +114 -0
- package/dist/prompts.d.ts +2 -0
- package/dist/prompts.js +115 -0
- package/dist/test-base.d.ts +1 -0
- package/dist/test-base.js +42 -0
- package/dist/types.d.ts +19 -0
- package/dist/types.js +1 -0
- package/dist/types.test.d.ts +1 -0
- package/dist/types.test.js +65 -0
- package/dist/utils/detect-pm.d.ts +2 -0
- package/dist/utils/detect-pm.js +10 -0
- package/dist/utils/detect-pm.test.d.ts +1 -0
- package/dist/utils/detect-pm.test.js +52 -0
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.js +7 -0
- package/dist/validator.d.ts +3 -0
- package/dist/validator.js +48 -0
- package/dist/validator.test.d.ts +1 -0
- package/dist/validator.test.js +96 -0
- package/package.json +68 -0
- package/templates/base/.env.example +31 -0
- package/templates/base/README.md +54 -0
- package/templates/base/package.json +19 -0
- package/templates/base/src/adapters/storage.ts +58 -0
- package/templates/base/src/types/index.ts +9 -0
- package/templates/base/src/types/walrus.ts +22 -0
- package/templates/base/src/utils/env.ts +41 -0
- package/templates/base/src/utils/format.ts +29 -0
- package/templates/base/tsconfig.json +19 -0
- package/templates/gallery/README.md +44 -0
- package/templates/gallery/package.json +6 -0
- package/templates/gallery/src/App.tsx +21 -0
- package/templates/gallery/src/components/FileCard.tsx +27 -0
- package/templates/gallery/src/components/GalleryGrid.tsx +30 -0
- package/templates/gallery/src/components/UploadModal.tsx +45 -0
- package/templates/gallery/src/styles.css +58 -0
- package/templates/gallery/src/types/gallery.ts +13 -0
- package/templates/gallery/src/utils/index-manager.ts +37 -0
- package/templates/react/.eslintrc.json +26 -0
- package/templates/react/README.md +80 -0
- package/templates/react/index.html +13 -0
- package/templates/react/package.json +32 -0
- package/templates/react/src/App.tsx +14 -0
- package/templates/react/src/components/Layout.tsx +21 -0
- package/templates/react/src/components/WalletConnect.tsx +21 -0
- package/templates/react/src/dapp-kit.css +1 -0
- package/templates/react/src/hooks/useStorage.ts +40 -0
- package/templates/react/src/hooks/useWallet.ts +16 -0
- package/templates/react/src/index.css +50 -0
- package/templates/react/src/index.ts +10 -0
- package/templates/react/src/main.tsx +17 -0
- package/templates/react/src/providers/QueryProvider.tsx +18 -0
- package/templates/react/src/providers/WalletProvider.tsx +37 -0
- package/templates/react/tsconfig.json +27 -0
- package/templates/react/tsconfig.node.json +10 -0
- package/templates/react/vite.config.ts +19 -0
- package/templates/sdk-mysten/README.md +65 -0
- package/templates/sdk-mysten/package.json +14 -0
- package/templates/sdk-mysten/src/adapter.ts +80 -0
- package/templates/sdk-mysten/src/client.ts +45 -0
- package/templates/sdk-mysten/src/config.ts +33 -0
- package/templates/sdk-mysten/src/index.ts +11 -0
- package/templates/sdk-mysten/src/types.ts +19 -0
- package/templates/sdk-mysten/test/adapter.test.ts +20 -0
- package/templates/simple-upload/README.md +24 -0
- package/templates/simple-upload/package.json +6 -0
- package/templates/simple-upload/src/App.tsx +27 -0
- package/templates/simple-upload/src/components/FilePreview.tsx +40 -0
- package/templates/simple-upload/src/components/UploadForm.tsx +51 -0
- package/templates/simple-upload/src/styles.css +33 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { COMPATIBILITY_MATRIX, SDK_METADATA } from './matrix.js';
|
|
3
|
+
describe('COMPATIBILITY_MATRIX', () => {
|
|
4
|
+
it('should have entries for all SDKs', () => {
|
|
5
|
+
expect(COMPATIBILITY_MATRIX).toHaveProperty('mysten');
|
|
6
|
+
expect(COMPATIBILITY_MATRIX).toHaveProperty('tusky');
|
|
7
|
+
expect(COMPATIBILITY_MATRIX).toHaveProperty('hibernuts');
|
|
8
|
+
});
|
|
9
|
+
it('should have frameworks and useCases for each SDK', () => {
|
|
10
|
+
Object.values(COMPATIBILITY_MATRIX).forEach((sdk) => {
|
|
11
|
+
expect(sdk).toHaveProperty('frameworks');
|
|
12
|
+
expect(sdk).toHaveProperty('useCases');
|
|
13
|
+
expect(Array.isArray(sdk.frameworks)).toBe(true);
|
|
14
|
+
expect(Array.isArray(sdk.useCases)).toBe(true);
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
it('should have mysten support all frameworks', () => {
|
|
18
|
+
expect(COMPATIBILITY_MATRIX.mysten.frameworks).toEqual(['react', 'vue', 'plain-ts']);
|
|
19
|
+
});
|
|
20
|
+
it('should have mysten support all use cases', () => {
|
|
21
|
+
expect(COMPATIBILITY_MATRIX.mysten.useCases).toEqual(['simple-upload', 'gallery', 'defi-nft']);
|
|
22
|
+
});
|
|
23
|
+
it('should have tusky support limited use cases', () => {
|
|
24
|
+
expect(COMPATIBILITY_MATRIX.tusky.useCases).toEqual(['simple-upload', 'gallery']);
|
|
25
|
+
expect(COMPATIBILITY_MATRIX.tusky.useCases).not.toContain('defi-nft');
|
|
26
|
+
});
|
|
27
|
+
it('should have hibernuts with most restricted support', () => {
|
|
28
|
+
expect(COMPATIBILITY_MATRIX.hibernuts.frameworks).toEqual(['react', 'plain-ts']);
|
|
29
|
+
expect(COMPATIBILITY_MATRIX.hibernuts.useCases).toEqual(['simple-upload']);
|
|
30
|
+
});
|
|
31
|
+
it('should not have hibernuts support vue', () => {
|
|
32
|
+
expect(COMPATIBILITY_MATRIX.hibernuts.frameworks).not.toContain('vue');
|
|
33
|
+
});
|
|
34
|
+
it('should not have hibernuts support gallery or defi-nft', () => {
|
|
35
|
+
expect(COMPATIBILITY_MATRIX.hibernuts.useCases).not.toContain('gallery');
|
|
36
|
+
expect(COMPATIBILITY_MATRIX.hibernuts.useCases).not.toContain('defi-nft');
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
describe('SDK_METADATA', () => {
|
|
40
|
+
it('should have metadata for all SDKs', () => {
|
|
41
|
+
expect(SDK_METADATA).toHaveProperty('mysten');
|
|
42
|
+
expect(SDK_METADATA).toHaveProperty('tusky');
|
|
43
|
+
expect(SDK_METADATA).toHaveProperty('hibernuts');
|
|
44
|
+
});
|
|
45
|
+
it('should have name, description, and docs for each SDK', () => {
|
|
46
|
+
Object.values(SDK_METADATA).forEach((metadata) => {
|
|
47
|
+
expect(metadata).toHaveProperty('name');
|
|
48
|
+
expect(metadata).toHaveProperty('description');
|
|
49
|
+
expect(metadata).toHaveProperty('docs');
|
|
50
|
+
expect(typeof metadata.name).toBe('string');
|
|
51
|
+
expect(typeof metadata.description).toBe('string');
|
|
52
|
+
expect(typeof metadata.docs).toBe('string');
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
it('should have valid package names', () => {
|
|
56
|
+
expect(SDK_METADATA.mysten.name).toBe('@mysten/walrus');
|
|
57
|
+
expect(SDK_METADATA.tusky.name).toBe('@tusky-io/ts-sdk');
|
|
58
|
+
expect(SDK_METADATA.hibernuts.name).toBe('@hibernuts/walrus-sdk');
|
|
59
|
+
});
|
|
60
|
+
it('should have URLs in docs field', () => {
|
|
61
|
+
Object.values(SDK_METADATA).forEach((metadata) => {
|
|
62
|
+
expect(metadata.docs).toMatch(/^https?:\/\//);
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
it('should have meaningful descriptions', () => {
|
|
66
|
+
expect(SDK_METADATA.mysten.description.length).toBeGreaterThan(10);
|
|
67
|
+
expect(SDK_METADATA.tusky.description.length).toBeGreaterThan(10);
|
|
68
|
+
expect(SDK_METADATA.hibernuts.description.length).toBeGreaterThan(10);
|
|
69
|
+
});
|
|
70
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export interface GitResult {
|
|
2
|
+
success: boolean;
|
|
3
|
+
error?: Error;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* Initialize git repository
|
|
7
|
+
*/
|
|
8
|
+
export declare function initializeGit(projectPath: string): Promise<GitResult>;
|
|
9
|
+
/**
|
|
10
|
+
* Create initial commit
|
|
11
|
+
*/
|
|
12
|
+
export declare function createInitialCommit(projectPath: string): Promise<GitResult>;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import spawn from 'cross-spawn';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { logger } from '../utils/logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* Check if git is available
|
|
7
|
+
*/
|
|
8
|
+
async function isGitAvailable() {
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
const child = spawn('git', ['--version'], { stdio: 'ignore' });
|
|
11
|
+
child.on('close', (code) => resolve(code === 0));
|
|
12
|
+
child.on('error', () => resolve(false));
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Initialize git repository
|
|
17
|
+
*/
|
|
18
|
+
export async function initializeGit(projectPath) {
|
|
19
|
+
// Check if git is available
|
|
20
|
+
if (!(await isGitAvailable())) {
|
|
21
|
+
logger.warn('⚠️ Git not found, skipping initialization');
|
|
22
|
+
return { success: false };
|
|
23
|
+
}
|
|
24
|
+
// Check if already a git repo
|
|
25
|
+
if (await fs.pathExists(path.join(projectPath, '.git'))) {
|
|
26
|
+
logger.info('📝 Git repository already exists');
|
|
27
|
+
return { success: true };
|
|
28
|
+
}
|
|
29
|
+
logger.info('📝 Initializing git repository...');
|
|
30
|
+
// Run git init
|
|
31
|
+
return new Promise((resolve) => {
|
|
32
|
+
const child = spawn('git', ['init'], {
|
|
33
|
+
cwd: projectPath,
|
|
34
|
+
stdio: 'ignore',
|
|
35
|
+
});
|
|
36
|
+
child.on('close', (code) => {
|
|
37
|
+
if (code === 0) {
|
|
38
|
+
logger.success('✓ Git repository initialized');
|
|
39
|
+
resolve({ success: true });
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
resolve({
|
|
43
|
+
success: false,
|
|
44
|
+
error: new Error(`git init failed with code ${code}`),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
child.on('error', (error) => {
|
|
49
|
+
resolve({ success: false, error });
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Create initial commit
|
|
55
|
+
*/
|
|
56
|
+
export async function createInitialCommit(projectPath) {
|
|
57
|
+
if (!(await fs.pathExists(path.join(projectPath, '.git')))) {
|
|
58
|
+
return { success: false, error: new Error('Not a git repository') };
|
|
59
|
+
}
|
|
60
|
+
logger.info('📝 Creating initial commit...');
|
|
61
|
+
// Stage all files
|
|
62
|
+
return new Promise((resolve) => {
|
|
63
|
+
const addChild = spawn('git', ['add', '.'], {
|
|
64
|
+
cwd: projectPath,
|
|
65
|
+
stdio: 'ignore',
|
|
66
|
+
});
|
|
67
|
+
addChild.on('close', (code) => {
|
|
68
|
+
if (code !== 0) {
|
|
69
|
+
resolve({ success: false, error: new Error('git add failed') });
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
// Create commit
|
|
73
|
+
const commitChild = spawn('git', ['commit', '-m', 'chore: initial commit from create-walrus-app'], {
|
|
74
|
+
cwd: projectPath,
|
|
75
|
+
stdio: 'ignore',
|
|
76
|
+
});
|
|
77
|
+
commitChild.on('close', (commitCode) => {
|
|
78
|
+
if (commitCode === 0) {
|
|
79
|
+
logger.success('✓ Initial commit created');
|
|
80
|
+
resolve({ success: true });
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
resolve({ success: false, error: new Error('git commit failed') });
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
commitChild.on('error', (error) => {
|
|
87
|
+
resolve({ success: false, error });
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
addChild.on('error', (error) => {
|
|
91
|
+
resolve({ success: false, error });
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Context } from '../types.js';
|
|
2
|
+
export interface PostInstallOptions {
|
|
3
|
+
context: Context;
|
|
4
|
+
projectPath: string;
|
|
5
|
+
skipInstall?: boolean;
|
|
6
|
+
skipGit?: boolean;
|
|
7
|
+
skipValidation?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export interface PostInstallResult {
|
|
10
|
+
success: boolean;
|
|
11
|
+
installed: boolean;
|
|
12
|
+
gitInitialized: boolean;
|
|
13
|
+
validated: boolean;
|
|
14
|
+
error?: Error;
|
|
15
|
+
}
|
|
16
|
+
export declare function runPostInstall(options: PostInstallOptions): Promise<PostInstallResult>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { logger } from '../utils/logger.js';
|
|
2
|
+
import { installDependencies } from './package-manager.js';
|
|
3
|
+
import { initializeGit, createInitialCommit } from './git.js';
|
|
4
|
+
import { validateProject } from './validator.js';
|
|
5
|
+
import { displaySuccess, displayError } from './messages.js';
|
|
6
|
+
export async function runPostInstall(options) {
|
|
7
|
+
const { context, projectPath, skipInstall = false, skipGit = false, skipValidation = false, } = options;
|
|
8
|
+
const result = {
|
|
9
|
+
success: true,
|
|
10
|
+
installed: false,
|
|
11
|
+
gitInitialized: false,
|
|
12
|
+
validated: false,
|
|
13
|
+
};
|
|
14
|
+
try {
|
|
15
|
+
// Step 1: Install dependencies
|
|
16
|
+
if (!skipInstall) {
|
|
17
|
+
const installResult = await installDependencies(projectPath, context.packageManager);
|
|
18
|
+
result.installed = installResult.success;
|
|
19
|
+
if (!installResult.success) {
|
|
20
|
+
logger.warn('⚠️ Dependency installation failed, but project was created');
|
|
21
|
+
logger.info('💡 You can install manually by running:');
|
|
22
|
+
logger.info(` cd ${context.projectName}`);
|
|
23
|
+
logger.info(` ${context.packageManager} install`);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Step 2: Initialize git
|
|
27
|
+
if (!skipGit) {
|
|
28
|
+
const gitResult = await initializeGit(projectPath);
|
|
29
|
+
result.gitInitialized = gitResult.success;
|
|
30
|
+
if (gitResult.success) {
|
|
31
|
+
const commitResult = await createInitialCommit(projectPath);
|
|
32
|
+
if (!commitResult.success) {
|
|
33
|
+
logger.warn('⚠️ Initial commit failed, but git repo was created');
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Step 3: Validate project
|
|
38
|
+
if (!skipValidation && result.installed) {
|
|
39
|
+
const validationResult = await validateProject(projectPath);
|
|
40
|
+
result.validated = validationResult.valid;
|
|
41
|
+
if (!validationResult.valid) {
|
|
42
|
+
logger.warn('⚠️ Project validation failed:');
|
|
43
|
+
validationResult.errors.forEach((err) => logger.warn(` - ${err}`));
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
// Display success message
|
|
47
|
+
displaySuccess(context);
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
result.success = false;
|
|
52
|
+
result.error = error;
|
|
53
|
+
displayError(error, context);
|
|
54
|
+
return result;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Context } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Display success message with next steps
|
|
4
|
+
*/
|
|
5
|
+
export declare function displaySuccess(context: Context): void;
|
|
6
|
+
/**
|
|
7
|
+
* Display error message with recovery steps
|
|
8
|
+
*/
|
|
9
|
+
export declare function displayError(error: Error, context: Context): void;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import kleur from 'kleur';
|
|
2
|
+
import { getRunCommand } from './package-manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* Display success message with next steps
|
|
5
|
+
*/
|
|
6
|
+
export function displaySuccess(context) {
|
|
7
|
+
const { projectName, packageManager, sdk, framework, useCase } = context;
|
|
8
|
+
console.log('\n' + kleur.green('━'.repeat(60)));
|
|
9
|
+
console.log(kleur.bold().green(' ✨ Project created successfully! ✨'));
|
|
10
|
+
console.log(kleur.green('━'.repeat(60)));
|
|
11
|
+
console.log('\n' + kleur.bold('📦 Project Details:'));
|
|
12
|
+
console.log(` Name: ${kleur.cyan(projectName)}`);
|
|
13
|
+
console.log(` SDK: ${kleur.cyan(sdk)}`);
|
|
14
|
+
console.log(` Framework: ${kleur.cyan(framework)}`);
|
|
15
|
+
console.log(` Use Case: ${kleur.cyan(useCase)}`);
|
|
16
|
+
console.log('\n' + kleur.bold('🚀 Next Steps:'));
|
|
17
|
+
console.log(` ${kleur.gray('1.')} cd ${kleur.cyan(projectName)}`);
|
|
18
|
+
console.log(` ${kleur.gray('2.')} ${kleur.cyan(getRunCommand(packageManager, 'dev'))}`);
|
|
19
|
+
console.log('\n' + kleur.bold('📚 Helpful Commands:'));
|
|
20
|
+
console.log(` ${kleur.cyan(getRunCommand(packageManager, 'dev'))} - Start development server`);
|
|
21
|
+
console.log(` ${kleur.cyan(getRunCommand(packageManager, 'build'))} - Build for production`);
|
|
22
|
+
console.log(` ${kleur.cyan(getRunCommand(packageManager, 'lint'))} - Run linter`);
|
|
23
|
+
console.log('\n' + kleur.bold('🔗 Resources:'));
|
|
24
|
+
console.log(` Walrus Docs: ${kleur.cyan('https://docs.walrus.site')}`);
|
|
25
|
+
console.log(` Sui Docs: ${kleur.cyan('https://docs.sui.io')}`);
|
|
26
|
+
console.log(` Sui Faucet: ${kleur.cyan('https://faucet.testnet.sui.io')}`);
|
|
27
|
+
console.log('\n' + kleur.bold('💡 Tips:'));
|
|
28
|
+
console.log(` - Copy ${kleur.cyan('.env.example')} to ${kleur.cyan('.env')}`);
|
|
29
|
+
console.log(` - Install Sui Wallet browser extension`);
|
|
30
|
+
console.log(` - Get testnet SUI from the faucet`);
|
|
31
|
+
console.log('\n' + kleur.green('━'.repeat(60)) + '\n');
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Display error message with recovery steps
|
|
35
|
+
*/
|
|
36
|
+
export function displayError(error, context) {
|
|
37
|
+
console.log('\n' + kleur.red('━'.repeat(60)));
|
|
38
|
+
console.log(kleur.bold().red(' ❌ Project creation failed'));
|
|
39
|
+
console.log(kleur.red('━'.repeat(60)));
|
|
40
|
+
console.log('\n' + kleur.bold('Error:'));
|
|
41
|
+
console.log(` ${kleur.red(error.message)}`);
|
|
42
|
+
console.log('\n' + kleur.bold('Recovery Steps:'));
|
|
43
|
+
console.log(` ${kleur.gray('1.')} cd ${kleur.cyan(context.projectName)}`);
|
|
44
|
+
console.log(` ${kleur.gray('2.')} ${kleur.cyan(`${context.packageManager} install`)}`);
|
|
45
|
+
console.log(` ${kleur.gray('3.')} Try running ${kleur.cyan(getRunCommand(context.packageManager, 'dev'))}`);
|
|
46
|
+
console.log('\n' + kleur.bold('Need Help?'));
|
|
47
|
+
console.log(` Report issues: ${kleur.cyan('https://github.com/your-org/walrus-starter-kit/issues')}`);
|
|
48
|
+
console.log('\n' + kleur.red('━'.repeat(60)) + '\n');
|
|
49
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { PackageManager } from '../types.js';
|
|
2
|
+
export interface InstallResult {
|
|
3
|
+
success: boolean;
|
|
4
|
+
duration: number;
|
|
5
|
+
error?: Error;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Install dependencies using detected package manager
|
|
9
|
+
*/
|
|
10
|
+
export declare function installDependencies(projectPath: string, packageManager: PackageManager): Promise<InstallResult>;
|
|
11
|
+
/**
|
|
12
|
+
* Get run command for package manager
|
|
13
|
+
*/
|
|
14
|
+
export declare function getRunCommand(pm: PackageManager, script: string): string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import spawn from 'cross-spawn';
|
|
2
|
+
import { logger } from '../utils/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Get install command for package manager
|
|
5
|
+
*/
|
|
6
|
+
function getInstallCommand(pm) {
|
|
7
|
+
const commands = {
|
|
8
|
+
npm: 'npm install',
|
|
9
|
+
pnpm: 'pnpm install',
|
|
10
|
+
yarn: 'yarn',
|
|
11
|
+
bun: 'bun install',
|
|
12
|
+
};
|
|
13
|
+
return commands[pm];
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Install dependencies using detected package manager
|
|
17
|
+
*/
|
|
18
|
+
export async function installDependencies(projectPath, packageManager) {
|
|
19
|
+
const startTime = Date.now();
|
|
20
|
+
logger.info(`📦 Installing dependencies with ${packageManager}...`);
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
const [cmd, ...args] = getInstallCommand(packageManager).split(' ');
|
|
23
|
+
const child = spawn(cmd, args, {
|
|
24
|
+
cwd: projectPath,
|
|
25
|
+
stdio: 'inherit', // Stream output to user
|
|
26
|
+
});
|
|
27
|
+
child.on('close', (code) => {
|
|
28
|
+
const duration = Date.now() - startTime;
|
|
29
|
+
if (code === 0) {
|
|
30
|
+
logger.success(`✓ Dependencies installed (${(duration / 1000).toFixed(1)}s)`);
|
|
31
|
+
resolve({ success: true, duration });
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
const error = new Error(`Install failed with exit code ${code}`);
|
|
35
|
+
logger.error(`❌ Dependency installation failed`);
|
|
36
|
+
resolve({ success: false, duration, error });
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
child.on('error', (error) => {
|
|
40
|
+
const duration = Date.now() - startTime;
|
|
41
|
+
logger.error(`❌ Failed to run ${packageManager}: ${error.message}`);
|
|
42
|
+
resolve({ success: false, duration, error });
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get run command for package manager
|
|
48
|
+
*/
|
|
49
|
+
export function getRunCommand(pm, script) {
|
|
50
|
+
const runCommands = {
|
|
51
|
+
npm: `npm run ${script}`,
|
|
52
|
+
pnpm: `pnpm ${script}`,
|
|
53
|
+
yarn: `yarn ${script}`,
|
|
54
|
+
bun: `bun run ${script}`,
|
|
55
|
+
};
|
|
56
|
+
return runCommands[pm];
|
|
57
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export interface ValidationResult {
|
|
2
|
+
valid: boolean;
|
|
3
|
+
checks: {
|
|
4
|
+
packageJson: boolean;
|
|
5
|
+
nodeModules: boolean;
|
|
6
|
+
dependencies: boolean;
|
|
7
|
+
typescript: boolean;
|
|
8
|
+
};
|
|
9
|
+
errors: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Validate generated project
|
|
13
|
+
*/
|
|
14
|
+
export declare function validateProject(projectPath: string): Promise<ValidationResult>;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import spawn from 'cross-spawn';
|
|
4
|
+
import { logger } from '../utils/logger.js';
|
|
5
|
+
/**
|
|
6
|
+
* Validate generated project
|
|
7
|
+
*/
|
|
8
|
+
export async function validateProject(projectPath) {
|
|
9
|
+
logger.info('🔍 Validating project...');
|
|
10
|
+
const result = {
|
|
11
|
+
valid: true,
|
|
12
|
+
checks: {
|
|
13
|
+
packageJson: false,
|
|
14
|
+
nodeModules: false,
|
|
15
|
+
dependencies: false,
|
|
16
|
+
typescript: false,
|
|
17
|
+
},
|
|
18
|
+
errors: [],
|
|
19
|
+
};
|
|
20
|
+
// Check 1: package.json exists and is valid
|
|
21
|
+
try {
|
|
22
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
23
|
+
const pkg = await fs.readJson(pkgPath);
|
|
24
|
+
if (!pkg.name || !pkg.version) {
|
|
25
|
+
result.errors.push('package.json missing required fields');
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
result.checks.packageJson = true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
result.errors.push('Invalid or missing package.json');
|
|
33
|
+
}
|
|
34
|
+
// Check 2: node_modules exists
|
|
35
|
+
const nodeModulesPath = path.join(projectPath, 'node_modules');
|
|
36
|
+
if (await fs.pathExists(nodeModulesPath)) {
|
|
37
|
+
result.checks.nodeModules = true;
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
result.errors.push('node_modules not found');
|
|
41
|
+
}
|
|
42
|
+
// Check 3: Dependencies installed
|
|
43
|
+
try {
|
|
44
|
+
const pkgPath = path.join(projectPath, 'package.json');
|
|
45
|
+
const pkg = await fs.readJson(pkgPath);
|
|
46
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
47
|
+
let allInstalled = true;
|
|
48
|
+
for (const dep in deps) {
|
|
49
|
+
const depPath = path.join(nodeModulesPath, dep);
|
|
50
|
+
if (!(await fs.pathExists(depPath))) {
|
|
51
|
+
allInstalled = false;
|
|
52
|
+
result.errors.push(`Dependency not installed: ${dep}`);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
result.checks.dependencies = allInstalled;
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
result.errors.push('Failed to verify dependencies');
|
|
60
|
+
}
|
|
61
|
+
// Check 4: TypeScript compilation (if tsconfig exists)
|
|
62
|
+
const tsconfigPath = path.join(projectPath, 'tsconfig.json');
|
|
63
|
+
if (await fs.pathExists(tsconfigPath)) {
|
|
64
|
+
const tscResult = await checkTypeScript(projectPath);
|
|
65
|
+
result.checks.typescript = tscResult.success;
|
|
66
|
+
if (!tscResult.success) {
|
|
67
|
+
result.errors.push(`TypeScript errors: ${tscResult.error}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
result.checks.typescript = true; // Not applicable
|
|
72
|
+
}
|
|
73
|
+
result.valid = Object.values(result.checks).every(Boolean);
|
|
74
|
+
if (result.valid) {
|
|
75
|
+
logger.success('✓ Project validation passed');
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
logger.warn('⚠️ Project validation failed:');
|
|
79
|
+
result.errors.forEach((err) => logger.warn(` - ${err}`));
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Check TypeScript compilation with timeout
|
|
85
|
+
*/
|
|
86
|
+
async function checkTypeScript(projectPath) {
|
|
87
|
+
return new Promise((resolve) => {
|
|
88
|
+
const child = spawn('npx', ['tsc', '--noEmit'], {
|
|
89
|
+
cwd: projectPath,
|
|
90
|
+
stdio: 'pipe',
|
|
91
|
+
});
|
|
92
|
+
const timeout = setTimeout(() => {
|
|
93
|
+
child.kill();
|
|
94
|
+
resolve({ success: false, error: 'TypeScript check timed out (60s)' });
|
|
95
|
+
}, 60000); // 60s timeout
|
|
96
|
+
let stderr = '';
|
|
97
|
+
child.stderr?.on('data', (data) => {
|
|
98
|
+
stderr += data.toString();
|
|
99
|
+
});
|
|
100
|
+
child.on('close', (code) => {
|
|
101
|
+
clearTimeout(timeout);
|
|
102
|
+
if (code === 0) {
|
|
103
|
+
resolve({ success: true });
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
resolve({ success: false, error: stderr.split('\n')[0] });
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
child.on('error', (error) => {
|
|
110
|
+
clearTimeout(timeout);
|
|
111
|
+
resolve({ success: false, error: error.message });
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
package/dist/prompts.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import prompts from 'prompts';
|
|
2
|
+
import { COMPATIBILITY_MATRIX, SDK_METADATA } from './matrix.js';
|
|
3
|
+
import { validateProjectName } from './validator.js';
|
|
4
|
+
import { detectPackageManager } from './utils/detect-pm.js';
|
|
5
|
+
export async function runPrompts(initial = {}) {
|
|
6
|
+
// Auto-fill package manager in non-interactive mode
|
|
7
|
+
const isInteractive = Boolean(process.stdin.isTTY);
|
|
8
|
+
if (!isInteractive && !initial.packageManager) {
|
|
9
|
+
initial.packageManager = detectPackageManager();
|
|
10
|
+
}
|
|
11
|
+
const response = await prompts([
|
|
12
|
+
{
|
|
13
|
+
type: initial.projectName ? null : 'text',
|
|
14
|
+
name: 'projectName',
|
|
15
|
+
message: 'Project name:',
|
|
16
|
+
initial: initial.projectName || 'my-walrus-app',
|
|
17
|
+
validate: validateProjectName,
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
type: initial.sdk ? null : 'select',
|
|
21
|
+
name: 'sdk',
|
|
22
|
+
message: 'Choose Walrus SDK:',
|
|
23
|
+
choices: [
|
|
24
|
+
{
|
|
25
|
+
title: `${SDK_METADATA.mysten.name} - ${SDK_METADATA.mysten.description}`,
|
|
26
|
+
value: 'mysten',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
title: `${SDK_METADATA.tusky.name} - ${SDK_METADATA.tusky.description}`,
|
|
30
|
+
value: 'tusky',
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
title: `${SDK_METADATA.hibernuts.name} - ${SDK_METADATA.hibernuts.description}`,
|
|
34
|
+
value: 'hibernuts',
|
|
35
|
+
},
|
|
36
|
+
],
|
|
37
|
+
initial: 0,
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
type: initial.framework ? null : 'select',
|
|
41
|
+
name: 'framework',
|
|
42
|
+
message: 'Choose framework:',
|
|
43
|
+
choices: (prev) => {
|
|
44
|
+
const sdk = initial.sdk || prev;
|
|
45
|
+
const frameworks = COMPATIBILITY_MATRIX[sdk]
|
|
46
|
+
.frameworks;
|
|
47
|
+
return frameworks.map((f) => ({
|
|
48
|
+
title: f === 'react'
|
|
49
|
+
? 'React + Vite'
|
|
50
|
+
: f === 'vue'
|
|
51
|
+
? 'Vue + Vite'
|
|
52
|
+
: 'Plain TypeScript',
|
|
53
|
+
value: f,
|
|
54
|
+
}));
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
type: initial.useCase ? null : 'select',
|
|
59
|
+
name: 'useCase',
|
|
60
|
+
message: 'Choose use case:',
|
|
61
|
+
choices: (prev, answers) => {
|
|
62
|
+
const sdk = initial.sdk || answers.sdk;
|
|
63
|
+
const useCases = COMPATIBILITY_MATRIX[sdk]
|
|
64
|
+
.useCases;
|
|
65
|
+
return useCases.map((uc) => ({
|
|
66
|
+
title: uc === 'simple-upload'
|
|
67
|
+
? 'Simple Upload (Single file)'
|
|
68
|
+
: uc === 'gallery'
|
|
69
|
+
? 'File Gallery (Multiple files)'
|
|
70
|
+
: 'DeFi/NFT Metadata',
|
|
71
|
+
value: uc,
|
|
72
|
+
}));
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
type: initial.analytics !== undefined ? null : 'confirm',
|
|
77
|
+
name: 'analytics',
|
|
78
|
+
message: 'Include Blockberry analytics?',
|
|
79
|
+
initial: false,
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
type: initial.tailwind !== undefined ? null : 'confirm',
|
|
83
|
+
name: 'tailwind',
|
|
84
|
+
message: 'Include Tailwind CSS?',
|
|
85
|
+
initial: true,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
type: initial.packageManager ? null : 'select',
|
|
89
|
+
name: 'packageManager',
|
|
90
|
+
message: 'Choose package manager:',
|
|
91
|
+
choices: [
|
|
92
|
+
{ title: 'npm', value: 'npm' },
|
|
93
|
+
{ title: 'pnpm', value: 'pnpm' },
|
|
94
|
+
{ title: 'yarn', value: 'yarn' },
|
|
95
|
+
{ title: 'bun', value: 'bun' },
|
|
96
|
+
],
|
|
97
|
+
initial: () => {
|
|
98
|
+
const detected = detectPackageManager();
|
|
99
|
+
const index = ['npm', 'pnpm', 'yarn', 'bun'].indexOf(detected);
|
|
100
|
+
return index !== -1 ? index : 0;
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
], {
|
|
104
|
+
onCancel: () => {
|
|
105
|
+
console.error('\nOperation cancelled.');
|
|
106
|
+
console.error('Hint: Use -p flag to specify package manager in non-interactive mode (e.g., -p npm).');
|
|
107
|
+
process.exit(1);
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
if (!response.projectName && !initial.projectName) {
|
|
111
|
+
console.error('\nOperation cancelled.');
|
|
112
|
+
process.exit(1);
|
|
113
|
+
}
|
|
114
|
+
return { ...initial, ...response };
|
|
115
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|