spring-starter 1.0.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/github.d.ts +7 -0
- package/dist/github.js +67 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +83 -0
- package/dist/spring-initializr.d.ts +9 -0
- package/dist/spring-initializr.js +45 -0
- package/package.json +37 -0
package/dist/github.d.ts
ADDED
package/dist/github.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import axios from 'axios';
|
|
4
|
+
import extractZip from 'extract-zip';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
function parseRepoUrl(url) {
|
|
7
|
+
const cleaned = url
|
|
8
|
+
.replace('https://github.com/adansyah125/spring-boot-project', '')
|
|
9
|
+
.replace('https://github.com/adansyah125/spring-boot-project', '')
|
|
10
|
+
.replace(/\.git$/, '')
|
|
11
|
+
.trim();
|
|
12
|
+
const parts = cleaned.split('/');
|
|
13
|
+
if (parts.length < 2) {
|
|
14
|
+
throw new Error('Format repo tidak valid. Gunakan: owner/repo');
|
|
15
|
+
}
|
|
16
|
+
return { owner: parts[0], repo: parts[1] };
|
|
17
|
+
}
|
|
18
|
+
export async function downloadFromGithub(options) {
|
|
19
|
+
const spinner = ora('Mendownload dari GitHub...').start();
|
|
20
|
+
try {
|
|
21
|
+
const { owner, repo } = parseRepoUrl(options.repoUrl);
|
|
22
|
+
const zipUrl = `https://github.com/${owner}/${repo}/archive/refs/heads/main.zip`;
|
|
23
|
+
const destDir = join(options.outputDir, options.projectDir);
|
|
24
|
+
if (!existsSync(options.outputDir)) {
|
|
25
|
+
mkdirSync(options.outputDir, { recursive: true });
|
|
26
|
+
}
|
|
27
|
+
const response = await axios.get(zipUrl, {
|
|
28
|
+
responseType: 'stream',
|
|
29
|
+
validateStatus: (status) => status === 200 || status === 302,
|
|
30
|
+
});
|
|
31
|
+
const zipPath = join(options.outputDir, `${repo}.zip`);
|
|
32
|
+
const fs = await import('node:fs');
|
|
33
|
+
const writer = fs.createWriteStream(zipPath);
|
|
34
|
+
response.data.pipe(writer);
|
|
35
|
+
await new Promise((resolve, reject) => {
|
|
36
|
+
writer.on('finish', resolve);
|
|
37
|
+
writer.on('error', reject);
|
|
38
|
+
});
|
|
39
|
+
const tempDir = join(options.outputDir, `_temp_${repo}`);
|
|
40
|
+
if (existsSync(tempDir)) {
|
|
41
|
+
const fs_rm = await import('node:fs/promises');
|
|
42
|
+
await fs_rm.rm(tempDir, { recursive: true, force: true });
|
|
43
|
+
}
|
|
44
|
+
await extractZip(zipPath, { dir: tempDir });
|
|
45
|
+
const extractedDir = join(tempDir, `${repo}-main`);
|
|
46
|
+
if (existsSync(extractedDir)) {
|
|
47
|
+
const fs_rename = await import('node:fs/promises');
|
|
48
|
+
if (existsSync(destDir)) {
|
|
49
|
+
await fs_rename.rm(destDir, { recursive: true, force: true });
|
|
50
|
+
}
|
|
51
|
+
await fs_rename.rename(extractedDir, destDir);
|
|
52
|
+
await fs_rename.rm(tempDir, { recursive: true, force: true });
|
|
53
|
+
}
|
|
54
|
+
const fs_rm2 = await import('node:fs/promises');
|
|
55
|
+
await fs_rm2.rm(zipPath);
|
|
56
|
+
spinner.succeed(`Project berhasil di-clone ke: ${destDir}`);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
if (error?.response?.status === 404) {
|
|
60
|
+
spinner.fail('Repo tidak ditemukan. Pastikan URL benar dan branch utama adalah "main"');
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
spinner.fail('Gagal mendownload dari GitHub');
|
|
64
|
+
}
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { generateFromInitializr } from './spring-initializr.js';
|
|
5
|
+
import { downloadFromGithub } from './github.js';
|
|
6
|
+
const program = new Command();
|
|
7
|
+
program
|
|
8
|
+
.name('spring-starter')
|
|
9
|
+
.description('CLI scaffolder untuk Spring Boot project')
|
|
10
|
+
.version('1.0.0')
|
|
11
|
+
.option('-d, --dir <path>', 'output directory', process.cwd())
|
|
12
|
+
.option('-n, --name <name>', 'project name', 'demo')
|
|
13
|
+
.option('--starter', 'gunakan mode starter dari GitHub')
|
|
14
|
+
.option('--default', 'gunakan mode default dari Spring Initializr')
|
|
15
|
+
.action(async (options) => {
|
|
16
|
+
const { templateType } = await inquirer.prompt([
|
|
17
|
+
{
|
|
18
|
+
type: 'list',
|
|
19
|
+
name: 'templateType',
|
|
20
|
+
message: 'Pilih tipe project:',
|
|
21
|
+
choices: [
|
|
22
|
+
{ name: 'Spring Default (kosong) - dari start.spring.io', value: 'default' },
|
|
23
|
+
{ name: 'Spring Starter - dari GitHub repo', value: 'starter' },
|
|
24
|
+
],
|
|
25
|
+
},
|
|
26
|
+
]);
|
|
27
|
+
if (templateType === 'default') {
|
|
28
|
+
const answers = await inquirer.prompt([
|
|
29
|
+
{
|
|
30
|
+
type: 'input',
|
|
31
|
+
name: 'groupId',
|
|
32
|
+
message: 'Group ID:',
|
|
33
|
+
default: 'com.example',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
type: 'input',
|
|
37
|
+
name: 'artifactId',
|
|
38
|
+
message: 'Artifact ID:',
|
|
39
|
+
default: options.name,
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
type: 'list',
|
|
43
|
+
name: 'bootVersion',
|
|
44
|
+
message: 'Spring Boot version:',
|
|
45
|
+
choices: ['3.3.0', '3.2.0', '3.1.0'],
|
|
46
|
+
default: '3.3.0',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: 'list',
|
|
50
|
+
name: 'javaVersion',
|
|
51
|
+
message: 'Java version:',
|
|
52
|
+
choices: ['21', '17'],
|
|
53
|
+
default: '21',
|
|
54
|
+
},
|
|
55
|
+
]);
|
|
56
|
+
await generateFromInitializr({
|
|
57
|
+
...answers,
|
|
58
|
+
outputDir: options.dir,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const answers = await inquirer.prompt([
|
|
63
|
+
{
|
|
64
|
+
type: 'input',
|
|
65
|
+
name: 'repoUrl',
|
|
66
|
+
message: 'GitHub repo URL (contoh: user/spring-boot-starter):',
|
|
67
|
+
default: 'spring-projects/spring-boot',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
type: 'input',
|
|
71
|
+
name: 'projectDir',
|
|
72
|
+
message: 'Nama folder project:',
|
|
73
|
+
default: options.name,
|
|
74
|
+
},
|
|
75
|
+
]);
|
|
76
|
+
await downloadFromGithub({
|
|
77
|
+
repoUrl: answers.repoUrl,
|
|
78
|
+
projectDir: answers.projectDir,
|
|
79
|
+
outputDir: options.dir,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
program.parse(process.argv);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { createWriteStream, existsSync, mkdirSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import extractZip from 'extract-zip';
|
|
5
|
+
import ora from 'ora';
|
|
6
|
+
const INITIALIZR_URL = 'https://start.spring.io';
|
|
7
|
+
export async function generateFromInitializr(options) {
|
|
8
|
+
const spinner = ora('Menggenerate project dari Spring Initializr...').start();
|
|
9
|
+
try {
|
|
10
|
+
const params = new URLSearchParams({
|
|
11
|
+
type: 'maven-project',
|
|
12
|
+
language: 'java',
|
|
13
|
+
bootVersion: options.bootVersion,
|
|
14
|
+
baseDir: options.artifactId,
|
|
15
|
+
groupId: options.groupId,
|
|
16
|
+
artifactId: options.artifactId,
|
|
17
|
+
name: options.artifactId,
|
|
18
|
+
packageName: `${options.groupId}.${options.artifactId}`,
|
|
19
|
+
javaVersion: options.javaVersion,
|
|
20
|
+
dependencies: '',
|
|
21
|
+
});
|
|
22
|
+
const response = await axios.get(`${INITIALIZR_URL}/starter.zip`, {
|
|
23
|
+
params,
|
|
24
|
+
responseType: 'stream',
|
|
25
|
+
});
|
|
26
|
+
const zipPath = join(options.outputDir, `${options.artifactId}.zip`);
|
|
27
|
+
if (!existsSync(options.outputDir)) {
|
|
28
|
+
mkdirSync(options.outputDir, { recursive: true });
|
|
29
|
+
}
|
|
30
|
+
const writer = createWriteStream(zipPath);
|
|
31
|
+
response.data.pipe(writer);
|
|
32
|
+
await new Promise((resolve, reject) => {
|
|
33
|
+
writer.on('finish', resolve);
|
|
34
|
+
writer.on('error', reject);
|
|
35
|
+
});
|
|
36
|
+
await extractZip(zipPath, { dir: options.outputDir });
|
|
37
|
+
const fs = await import('node:fs/promises');
|
|
38
|
+
await fs.rm(zipPath);
|
|
39
|
+
spinner.succeed(`Project berhasil dibuat di: ${join(options.outputDir, options.artifactId)}`);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
spinner.fail('Gagal menggenerate project');
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "spring-starter",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI scaffolder untuk Spring Boot project dari start.spring.io atau GitHub",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"spring-starter": "dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"type": "module",
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"package.json",
|
|
13
|
+
"README.md"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"start": "node dist/index.js",
|
|
18
|
+
"dev": "tsx src/index.ts",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"keywords": ["spring-boot", "scaffolder", "cli", "spring", "initializr"],
|
|
22
|
+
"author": "",
|
|
23
|
+
"license": "ISC",
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"axios": "^1.7.0",
|
|
26
|
+
"commander": "^12.0.0",
|
|
27
|
+
"extract-zip": "^2.0.1",
|
|
28
|
+
"inquirer": "^9.3.0",
|
|
29
|
+
"ora": "^8.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/inquirer": "^9.0.7",
|
|
33
|
+
"@types/node": "^20.0.0",
|
|
34
|
+
"tsx": "^4.7.0",
|
|
35
|
+
"typescript": "^5.4.0"
|
|
36
|
+
}
|
|
37
|
+
}
|