@xaidenlabs/uso 1.1.33 → 1.1.34
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/package.json +1 -1
- package/src/commands/create.js +124 -45
package/package.json
CHANGED
package/src/commands/create.js
CHANGED
|
@@ -7,11 +7,6 @@ const { log, spinner } = require('../utils/logger');
|
|
|
7
7
|
|
|
8
8
|
const create = async (projectName, options) => {
|
|
9
9
|
// 1. Validation
|
|
10
|
-
if (!shell.which('anchor')) {
|
|
11
|
-
log.error("❌ 'anchor' is not found in PATH.");
|
|
12
|
-
log.warn("👉 Run 'uso init' to install the Solana toolchain.");
|
|
13
|
-
return;
|
|
14
|
-
}
|
|
15
10
|
if (!projectName) {
|
|
16
11
|
log.error("❌ Please specify a project name.");
|
|
17
12
|
log.warn("👉 Usage: uso create <project-name>");
|
|
@@ -25,72 +20,150 @@ const create = async (projectName, options) => {
|
|
|
25
20
|
|
|
26
21
|
log.header(`🏗️ Scaffolding new Solana project: ${projectName}...`);
|
|
27
22
|
|
|
28
|
-
// 2.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
23
|
+
// 2. Locate Template
|
|
24
|
+
// We expect templates/default relative to this file
|
|
25
|
+
// src/commands/create.js -> templates/default is two levels up -> templates/default
|
|
26
|
+
const templatePath = path.resolve(__dirname, '../../templates/default');
|
|
27
|
+
if (!fs.existsSync(templatePath)) {
|
|
28
|
+
log.error(`❌ Template not found at: ${templatePath}`);
|
|
29
|
+
log.error("This installation of uso might be corrupted.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 3. Create Project Directory & Copy Files
|
|
34
|
+
const projectPath = path.join(process.cwd(), projectName);
|
|
35
|
+
shell.mkdir('-p', projectPath);
|
|
36
|
+
|
|
37
|
+
const copySpin = spinner(`Copying template files...`).start();
|
|
38
|
+
// Use recursive copy
|
|
39
|
+
shell.cp('-R', `${templatePath}/*`, projectPath);
|
|
40
|
+
// Copy hidden files like .gitignore separately if needed, or check if cp -R includes them
|
|
41
|
+
shell.cp('-R', `${templatePath}/.gitignore`, projectPath);
|
|
42
|
+
shell.cp('-R', `${templatePath}/.prettierignore`, projectPath);
|
|
43
|
+
copySpin.succeed("Template files copied.");
|
|
34
44
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
45
|
+
shell.cd(projectPath);
|
|
46
|
+
|
|
47
|
+
// 4. Customization: Rename Project & Generate Program ID
|
|
48
|
+
const configSpin = spinner(`Configuring project...`).start();
|
|
49
|
+
|
|
50
|
+
// 4a. Program Keypair & ID
|
|
51
|
+
// Create 'target/deploy' folder where keypair usually lives
|
|
52
|
+
const deployDir = path.join(projectPath, 'target', 'deploy');
|
|
53
|
+
shell.mkdir('-p', deployDir);
|
|
54
|
+
|
|
55
|
+
// Check if user has solana-keygen
|
|
56
|
+
let programId = "Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS"; // Default fallback
|
|
57
|
+
if (shell.which('solana-keygen')) {
|
|
58
|
+
const keypairFile = path.join(deployDir, `${projectName}-keypair.json`);
|
|
59
|
+
// Generate new keypair
|
|
60
|
+
const result = shell.exec(`solana-keygen new --no-bip39-passphrase --outfile "${keypairFile}"`, { silent: true });
|
|
61
|
+
if (result.code === 0) {
|
|
62
|
+
// Extract pubkey
|
|
63
|
+
const pubkey = shell.exec(`solana-keygen pubkey "${keypairFile}"`, { silent: true }).stdout.trim();
|
|
64
|
+
if (pubkey) programId = pubkey;
|
|
38
65
|
}
|
|
39
|
-
log.success(`✅ Wallet created at: ${keypairPath}`);
|
|
40
66
|
}
|
|
41
67
|
|
|
42
|
-
//
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
68
|
+
// 4b. Rename Program Directory
|
|
69
|
+
// Default template has programs/my-project
|
|
70
|
+
const oldProgramDir = path.join(projectPath, 'programs', 'my-project');
|
|
71
|
+
const newProgramDir = path.join(projectPath, 'programs', projectName);
|
|
72
|
+
|
|
73
|
+
// Rust crate names usually use underscores instead of dashes
|
|
74
|
+
// e.g. project-name -> project_name
|
|
75
|
+
const crateName = projectName.replace(/-/g, '_');
|
|
76
|
+
const newProgramDirCrate = path.join(projectPath, 'programs', crateName);
|
|
77
|
+
|
|
78
|
+
if (fs.existsSync(oldProgramDir)) {
|
|
79
|
+
shell.mv(oldProgramDir, newProgramDirCrate);
|
|
49
80
|
}
|
|
50
|
-
initSpin.succeed("Anchor project initialized.");
|
|
51
81
|
|
|
52
|
-
|
|
53
|
-
|
|
82
|
+
// 4c. Update Cargo.toml (root and program)
|
|
83
|
+
// Root Cargo.toml
|
|
84
|
+
const rootCargoPath = path.join(projectPath, 'Cargo.toml');
|
|
85
|
+
if (fs.existsSync(rootCargoPath)) {
|
|
86
|
+
let content = fs.readFileSync(rootCargoPath, 'utf8');
|
|
87
|
+
content = content.replace(/programs\/my-project/g, `programs/${crateName}`);
|
|
88
|
+
fs.writeFileSync(rootCargoPath, content);
|
|
89
|
+
}
|
|
54
90
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
91
|
+
// Program Cargo.toml
|
|
92
|
+
const programCargoPath = path.join(newProgramDirCrate, 'Cargo.toml');
|
|
93
|
+
if (fs.existsSync(programCargoPath)) {
|
|
94
|
+
let content = fs.readFileSync(programCargoPath, 'utf8');
|
|
95
|
+
content = content.replace(/name = "my-project"/g, `name = "${crateName}"`);
|
|
96
|
+
content = content.replace(/name = "my_project"/g, `name = "${crateName}"`);
|
|
97
|
+
fs.writeFileSync(programCargoPath, content);
|
|
98
|
+
}
|
|
62
99
|
|
|
63
|
-
|
|
100
|
+
// 4d. Update Anchor.toml
|
|
101
|
+
const anchorTomlPath = path.join(projectPath, 'Anchor.toml');
|
|
102
|
+
if (fs.existsSync(anchorTomlPath)) {
|
|
103
|
+
let content = fs.readFileSync(anchorTomlPath, 'utf8');
|
|
104
|
+
// Replace Program ID
|
|
105
|
+
content = content.replace(/my_project = ".*"/g, `${crateName} = "${programId}"`);
|
|
106
|
+
// Replace scripts if needed (template might have default)
|
|
107
|
+
fs.writeFileSync(anchorTomlPath, content);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// 4e. Update lib.rs with new Program ID
|
|
111
|
+
const libRsPath = path.join(newProgramDirCrate, 'src', 'lib.rs');
|
|
112
|
+
if (fs.existsSync(libRsPath)) {
|
|
113
|
+
let content = fs.readFileSync(libRsPath, 'utf8');
|
|
114
|
+
// declare_id!("...")
|
|
115
|
+
content = content.replace(/declare_id!\(".*"\);/g, `declare_id!("${programId}");`);
|
|
116
|
+
fs.writeFileSync(libRsPath, content);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 4f. Update Tests
|
|
120
|
+
// tests/my-project.ts -> tests/<projectName>.ts
|
|
121
|
+
const oldTestFile = path.join(projectPath, 'tests', 'my-project.ts');
|
|
122
|
+
const newTestFile = path.join(projectPath, 'tests', `${projectName}.ts`);
|
|
123
|
+
if (fs.existsSync(oldTestFile)) {
|
|
124
|
+
shell.mv(oldTestFile, newTestFile);
|
|
125
|
+
// Build needs to reference this? No, anchor test runs all .ts files usually.
|
|
126
|
+
// Update content of test file
|
|
127
|
+
let content = fs.readFileSync(newTestFile, 'utf8');
|
|
128
|
+
content = content.replace(/anchor\.workspace\.MyProject/g, `anchor.workspace.${toPascalCase(crateName)}`);
|
|
129
|
+
fs.writeFileSync(newTestFile, content);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
configSpin.succeed("Project configured.");
|
|
64
133
|
|
|
65
|
-
//
|
|
66
|
-
if (
|
|
67
|
-
shell.
|
|
134
|
+
// 5. Initialize Git
|
|
135
|
+
if (shell.which('git')) {
|
|
136
|
+
shell.exec('git init', { silent: true });
|
|
137
|
+
shell.exec('git add .', { silent: true });
|
|
138
|
+
shell.exec('git commit -m "Initial commit by uso"', { silent: true });
|
|
68
139
|
}
|
|
69
140
|
|
|
141
|
+
// 6. Inject Frontend Template
|
|
142
|
+
const frontendRepo = "https://github.com/solana-developers/solana-dapp-next.git";
|
|
143
|
+
log.info("🧬 Injecting Next.js frontend (Solana Adapter + Tailwind)...");
|
|
144
|
+
|
|
70
145
|
const cloneSpin = spinner("Cloning frontend template...").start();
|
|
71
146
|
if (shell.exec(`git clone ${frontendRepo} app`).code !== 0) {
|
|
72
147
|
cloneSpin.fail("Failed to clone frontend template.");
|
|
73
|
-
log.warn("⚠️ Proceeding
|
|
148
|
+
log.warn("⚠️ Proceeding without frontend.");
|
|
74
149
|
} else {
|
|
75
150
|
cloneSpin.succeed("Frontend template injected into ./app");
|
|
76
|
-
// Remove .git from the frontend so it's part of the main repo
|
|
77
151
|
shell.rm('-rf', path.join('app', '.git'));
|
|
78
|
-
|
|
79
|
-
// Installing dependencies for frontend is slow, maybe skip or ask user?
|
|
80
|
-
// Let's print instructions instead to be fast.
|
|
81
152
|
}
|
|
82
153
|
|
|
83
|
-
//
|
|
84
|
-
//
|
|
85
|
-
// For now, standard init is usually fine.
|
|
154
|
+
// 7. Install Dependencies (Optional but nice)
|
|
155
|
+
// Skipping to keep it fast, user can run npm install
|
|
86
156
|
|
|
87
|
-
//
|
|
157
|
+
// 8. Final Success Message
|
|
88
158
|
console.log("");
|
|
89
159
|
log.header(`✅ Project '${projectName}' is ready! 🚀`);
|
|
160
|
+
log.info(` Program ID: ${programId}`);
|
|
90
161
|
|
|
91
162
|
log.subHeader("Quick Start:");
|
|
92
163
|
console.log(chalk.yellow(` cd ${projectName}`));
|
|
93
164
|
console.log(chalk.yellow(` uso val `) + chalk.gray(" # Start local validator (in new terminal)"));
|
|
165
|
+
// We should probably run npm install for them?
|
|
166
|
+
console.log(chalk.yellow(` npm install `) + chalk.gray(" # Install dependencies"));
|
|
94
167
|
console.log(chalk.yellow(` uso test `) + chalk.gray(" # Build & run tests"));
|
|
95
168
|
console.log("");
|
|
96
169
|
|
|
@@ -101,4 +174,10 @@ const create = async (projectName, options) => {
|
|
|
101
174
|
console.log("");
|
|
102
175
|
};
|
|
103
176
|
|
|
177
|
+
// Helper: snake_case to PascalCase
|
|
178
|
+
function toPascalCase(str) {
|
|
179
|
+
return str.replace(/(\w)(\w*)/g,
|
|
180
|
+
function (g0, g1, g2) { return g1.toUpperCase() + g2.toLowerCase(); }).replace(/_/g, '');
|
|
181
|
+
}
|
|
182
|
+
|
|
104
183
|
module.exports = { create };
|