Skip to content

Commit 1a9e49f

Browse files
authored
Merge pull request #6 from ServiceStack/claude/create-npx-project-script-014KBKErgkQjXM1eXSndDaZi
Add 'ls' command to list available project templates
2 parents 3515db3 + 8b5186a commit 1a9e49f

File tree

2 files changed

+132
-1
lines changed

2 files changed

+132
-1
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,18 @@ Create .NET and other projects from NetCoreTemplates GitHub repositories.
44

55
## Usage
66

7+
```bash
8+
npx create-net <repo|ls> [ProjectName]
9+
```
10+
11+
### Commands
12+
13+
**List available templates:**
14+
```bash
15+
npx create-net ls [org]
16+
```
17+
18+
**Create a project:**
719
```bash
820
npx create-net <repo> [ProjectName]
921
```
@@ -12,6 +24,20 @@ If `ProjectName` is not specified, the script will use the current directory nam
1224

1325
### Examples
1426

27+
**List all available templates:**
28+
29+
```bash
30+
npx create-net ls
31+
```
32+
33+
Shows all templates from NetCoreTemplates and NetFrameworkTemplates organizations.
34+
35+
**List templates from a specific organization:**
36+
37+
```bash
38+
npx create-net ls NetFrameworkTemplates
39+
```
40+
1541
**Create a project in a new directory:**
1642

1743
```bash

bin/create-net.js

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,30 @@ const AdmZip = require('adm-zip');
1010
const args = process.argv.slice(2);
1111

1212
if (args.length < 1) {
13-
console.error('Usage: npx create-net <repo> [ProjectName]');
13+
console.error('Usage: npx create-net <repo|ls> [ProjectName]');
14+
console.error('');
15+
console.error('Commands:');
16+
console.error(' ls [org] List available project templates');
17+
console.error(' <repo> [name] Create a project from a template');
1418
console.error('');
1519
console.error('If ProjectName is not specified, uses current directory name and extracts into current directory.');
1620
console.error('');
1721
console.error('Examples:');
22+
console.error(' npx create-net ls');
23+
console.error(' npx create-net ls NetFrameworkTemplates');
1824
console.error(' npx create-net nextjs MyProject');
1925
console.error(' npx create-net NetFrameworkTemplates/web-netfx MyProject');
2026
console.error(' npx create-net nextjs (uses current directory name)');
2127
process.exit(1);
2228
}
2329

30+
// Handle ls command to list available templates
31+
if (args[0] === 'ls' || args[0] === 'list') {
32+
const targetOrg = args[1]; // Optional organization/user name
33+
listTemplates(targetOrg);
34+
return;
35+
}
36+
2437
const repo = args[0];
2538
let projectName = args[1];
2639
let extractToCurrentDir = false;
@@ -71,6 +84,98 @@ if (extractToCurrentDir) {
7184
}
7285
}
7386

87+
// Function to fetch JSON from GitHub API
88+
function fetchJSON(url) {
89+
return new Promise((resolve, reject) => {
90+
https.get(url, {
91+
headers: {
92+
'User-Agent': 'create-net'
93+
}
94+
}, (response) => {
95+
let data = '';
96+
97+
// Handle redirects
98+
if (response.statusCode === 301 || response.statusCode === 302) {
99+
return fetchJSON(response.headers.location).then(resolve).catch(reject);
100+
}
101+
102+
if (response.statusCode !== 200) {
103+
return reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
104+
}
105+
106+
response.on('data', chunk => data += chunk);
107+
response.on('end', () => {
108+
try {
109+
resolve(JSON.parse(data));
110+
} catch (err) {
111+
reject(err);
112+
}
113+
});
114+
}).on('error', reject);
115+
});
116+
}
117+
118+
// Function to list available templates
119+
async function listTemplates(targetOrg) {
120+
console.log('Fetching available project templates...\n');
121+
122+
let organizations;
123+
124+
if (targetOrg) {
125+
// List templates from specific organization
126+
organizations = [{ name: targetOrg, title: `${targetOrg} Templates` }];
127+
} else {
128+
// List templates from default organizations
129+
organizations = [
130+
{ name: 'NetCoreTemplates', title: '.NET Core Templates' },
131+
{ name: 'NetFrameworkTemplates', title: '.NET Framework Templates' }
132+
];
133+
}
134+
135+
try {
136+
for (const org of organizations) {
137+
console.log(`\x1b[1m${org.title}\x1b[0m`);
138+
console.log('─'.repeat(80));
139+
140+
try {
141+
const repos = await fetchJSON(`https://api.github.com/orgs/${org.name}/repos?per_page=100&sort=updated`);
142+
143+
if (!repos || repos.length === 0) {
144+
console.log(' No templates found');
145+
} else {
146+
// Find the longest repo name for padding
147+
const maxNameLength = Math.max(...repos.map(r => r.name.length));
148+
const padding = Math.max(maxNameLength + 2, 25);
149+
150+
repos.forEach(repo => {
151+
const name = repo.name.padEnd(padding);
152+
const description = repo.description || 'No description available';
153+
console.log(` ${name} ${description}`);
154+
});
155+
}
156+
} catch (err) {
157+
console.log(` Error fetching templates: ${err.message}`);
158+
}
159+
160+
console.log('');
161+
}
162+
163+
console.log('\x1b[1mUsage:\x1b[0m');
164+
console.log(' npx create-net <repo> [ProjectName]');
165+
console.log(' npx create-net <org>/<repo> [ProjectName]');
166+
console.log(' npx create-net ls [org]');
167+
console.log('\n\x1b[1mExamples:\x1b[0m');
168+
console.log(' npx create-net ls # List all templates');
169+
console.log(' npx create-net ls NetFrameworkTemplates # List specific org templates');
170+
console.log(' npx create-net nextjs MyProject # Create from NetCoreTemplates');
171+
console.log(' npx create-net NetFrameworkTemplates/web-netfx MyApp # Create from specific org');
172+
173+
} catch (err) {
174+
console.error('Error listing templates:', err.message);
175+
process.exit(1);
176+
}
177+
}
178+
74179
// Function to download file from URL
75180
function downloadFile(url, destination) {
76181
return new Promise((resolve, reject) => {

0 commit comments

Comments
 (0)