neatnode 3.1.7 → 3.2.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neatnode",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.2.0",
|
|
4
4
|
"description": "Plug & Play Node.js backend starter templates — build REST APIs, socket servers, and more in seconds.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"neatnode": "bin/index.js"
|
|
@@ -38,10 +38,20 @@
|
|
|
38
38
|
"homepage": "https://github.com/aakash-gupta02/NeatNode#readme",
|
|
39
39
|
"license": "ISC",
|
|
40
40
|
"type": "module",
|
|
41
|
+
"scripts": {
|
|
42
|
+
"lint": "eslint .",
|
|
43
|
+
"lint:fix": "eslint . --fix",
|
|
44
|
+
"test": "node --test"
|
|
45
|
+
},
|
|
41
46
|
"dependencies": {
|
|
42
47
|
"axios": "^1.13.2",
|
|
43
48
|
"extract-zip": "^2.0.1",
|
|
44
49
|
"fs-extra": "^11.3.2",
|
|
45
50
|
"inquirer": "^12.10.0"
|
|
51
|
+
},
|
|
52
|
+
"devDependencies": {
|
|
53
|
+
"@eslint/js": "^9.0.0",
|
|
54
|
+
"eslint": "^9.0.0",
|
|
55
|
+
"globals": "^15.0.0"
|
|
46
56
|
}
|
|
47
|
-
}
|
|
57
|
+
}
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import os from "os";
|
|
4
|
-
import { fileURLToPath } from "url";
|
|
5
|
-
import { copyTemplate } from "../utils/copyTemplate.js";
|
|
6
|
-
import { removeCrud, removeCrudModule, removeCrudReferences } from "./removeCRUD.js";
|
|
7
|
-
import { downloadTemplate } from "../utils/
|
|
8
|
-
|
|
9
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
-
const __dirname = path.dirname(__filename);
|
|
11
|
-
|
|
12
|
-
export async function createProject({ projectName, repoPath, includeCrud, crudName, langKey, isModular }) {
|
|
13
|
-
try {
|
|
14
|
-
const targetPath = projectName === "."
|
|
15
|
-
? process.cwd()
|
|
16
|
-
: path.join(process.cwd(), projectName);
|
|
17
|
-
|
|
18
|
-
if (fs.existsSync(targetPath) && projectName !== ".") {
|
|
19
|
-
console.error(`❌ Folder "${projectName}" already exists.`);
|
|
20
|
-
process.exit(1);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (projectName !== ".") {
|
|
24
|
-
console.log("Creating project folder...");
|
|
25
|
-
fs.mkdirSync(targetPath);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
console.log("Downloading template...");
|
|
29
|
-
const localTemplatePath = await downloadTemplate(repoPath);
|
|
30
|
-
|
|
31
|
-
await copyTemplate(localTemplatePath, targetPath, {
|
|
32
|
-
"project-name": projectName === "." ? path.basename(process.cwd()) : projectName,
|
|
33
|
-
"author": os.userInfo().username || "author",
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
if (!includeCrud && crudName) {
|
|
37
|
-
console.log("🗑 Removing CRUD files...");
|
|
38
|
-
|
|
39
|
-
if (isModular) {
|
|
40
|
-
removeCrudModule(targetPath, crudName);
|
|
41
|
-
removeCrudReferences(path.join(targetPath, "src", `routes/index.route.${langKey}`));
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
removeCrud(targetPath, crudName, langKey);
|
|
46
|
-
removeCrudReferences(path.join(targetPath, "src", `app.${langKey}`));
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
console.log(`\n✅ Project "${projectName}" created successfully!\n`);
|
|
50
|
-
|
|
51
|
-
} catch (err) {
|
|
52
|
-
console.error("❌ Failed to create project:", err);
|
|
53
|
-
process.exit(1);
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import os from "os";
|
|
4
|
+
import { fileURLToPath } from "url";
|
|
5
|
+
import { copyTemplate } from "../utils/copyTemplate.js";
|
|
6
|
+
import { removeCrud, removeCrudModule, removeCrudReferences } from "./removeCRUD.js";
|
|
7
|
+
import { downloadTemplate } from "../utils/downloadRepoTemplateByVersionTags.js";
|
|
8
|
+
|
|
9
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
10
|
+
const __dirname = path.dirname(__filename);
|
|
11
|
+
|
|
12
|
+
export async function createProject({ projectName, repoPath, includeCrud, crudName, langKey, isModular }) {
|
|
13
|
+
try {
|
|
14
|
+
const targetPath = projectName === "."
|
|
15
|
+
? process.cwd()
|
|
16
|
+
: path.join(process.cwd(), projectName);
|
|
17
|
+
|
|
18
|
+
if (fs.existsSync(targetPath) && projectName !== ".") {
|
|
19
|
+
console.error(`❌ Folder "${projectName}" already exists.`);
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (projectName !== ".") {
|
|
24
|
+
console.log("Creating project folder...");
|
|
25
|
+
fs.mkdirSync(targetPath);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
console.log("Downloading template...");
|
|
29
|
+
const localTemplatePath = await downloadTemplate(repoPath);
|
|
30
|
+
|
|
31
|
+
await copyTemplate(localTemplatePath, targetPath, {
|
|
32
|
+
"project-name": projectName === "." ? path.basename(process.cwd()) : projectName,
|
|
33
|
+
"author": os.userInfo().username || "author",
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
if (!includeCrud && crudName) {
|
|
37
|
+
console.log("🗑 Removing CRUD files...");
|
|
38
|
+
|
|
39
|
+
if (isModular) {
|
|
40
|
+
removeCrudModule(targetPath, crudName);
|
|
41
|
+
removeCrudReferences(path.join(targetPath, "src", `routes/index.route.${langKey}`));
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
removeCrud(targetPath, crudName, langKey);
|
|
46
|
+
removeCrudReferences(path.join(targetPath, "src", `app.${langKey}`));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
console.log(`\n✅ Project "${projectName}" created successfully!\n`);
|
|
50
|
+
|
|
51
|
+
} catch (err) {
|
|
52
|
+
console.error("❌ Failed to create project:", err);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
|
|
@@ -1,62 +1,65 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
`src/
|
|
9
|
-
`src/
|
|
10
|
-
`src/
|
|
11
|
-
`src/
|
|
12
|
-
`src/
|
|
13
|
-
`src/
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
//
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
}
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
export function removeCrud(targetPath, name, langKey) {
|
|
5
|
+
try {
|
|
6
|
+
const crudPaths = [
|
|
7
|
+
`src/models/${name}.model.${langKey}`,
|
|
8
|
+
`src/controllers/${name}.controller.${langKey}`,
|
|
9
|
+
`src/routes/${name}.route.${langKey}`,
|
|
10
|
+
`src/services/${name}.service.${langKey}`,
|
|
11
|
+
`src/validations/${name}.validation.${langKey}`,
|
|
12
|
+
`src/middlewares/auth.middleware.${langKey}`,
|
|
13
|
+
`src/schemas/${name}.schema.${langKey}`,
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
crudPaths.forEach((relPath) => {
|
|
17
|
+
const absPath = path.join(targetPath, relPath);
|
|
18
|
+
|
|
19
|
+
if (fs.existsSync(absPath)) {
|
|
20
|
+
fs.rmSync(absPath, { force: true });
|
|
21
|
+
console.log(`✔ Removed: ${relPath}`);
|
|
22
|
+
} // else {
|
|
23
|
+
// console.log(`Skipped (not found): ${relPath}`);
|
|
24
|
+
// }
|
|
25
|
+
});
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error("❌ Error while removing CRUD files:", err.message);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function removeCrudReferences(appJsPath) {
|
|
32
|
+
if (!fs.existsSync(appJsPath)) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
let content = fs.readFileSync(appJsPath, "utf8");
|
|
36
|
+
|
|
37
|
+
// Remove imports block
|
|
38
|
+
content = content.replace(
|
|
39
|
+
/\/\/ ROUTE_IMPORTS_START[\s\S]*?\/\/ ROUTE_IMPORTS_END/,
|
|
40
|
+
"",
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
// Remove route usage block
|
|
44
|
+
content = content.replace(
|
|
45
|
+
/\/\/ ROUTE_USES_START[\s\S]*?\/\/ ROUTE_USES_END/,
|
|
46
|
+
"",
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
fs.writeFileSync(appJsPath, content, "utf8");
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function removeCrudModule(targetPath, name) {
|
|
54
|
+
try {
|
|
55
|
+
const modulePath = path.join(targetPath, `src/modules/${name}`);
|
|
56
|
+
if (fs.existsSync(modulePath)) {
|
|
57
|
+
fs.rmSync(modulePath, { recursive: true, force: true });
|
|
58
|
+
console.log(`✔ Removed module: ${name}`);
|
|
59
|
+
} else {
|
|
60
|
+
console.log(`Skipped module (not found): ${name}`);
|
|
61
|
+
}
|
|
62
|
+
} catch (err) {
|
|
63
|
+
console.error("❌ Error while removing CRUD module:", err.message);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
@@ -1,40 +1,48 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
|
|
4
|
-
export async function copyTemplate(srcDir, destDir, replacements = {}
|
|
5
|
-
const ignoreList = [
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
export async function copyTemplate(srcDir, destDir, replacements = {}) {
|
|
5
|
+
const ignoreList = [
|
|
6
|
+
"node_modules",
|
|
7
|
+
".git",
|
|
8
|
+
".env",
|
|
9
|
+
"package-lock.json",
|
|
10
|
+
".npmignore",
|
|
11
|
+
"logs",
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
if (!fs.existsSync(destDir)) {
|
|
15
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const items = fs.readdirSync(srcDir, { withFileTypes: true });
|
|
19
|
+
|
|
20
|
+
for (const item of items) {
|
|
21
|
+
if (ignoreList.includes(item.name)) continue;
|
|
22
|
+
|
|
23
|
+
const srcPath = path.join(srcDir, item.name);
|
|
24
|
+
const destPath = path.join(destDir, item.name);
|
|
25
|
+
|
|
26
|
+
if (item.isDirectory()) {
|
|
27
|
+
// recursively copy folders
|
|
28
|
+
await copyTemplate(srcPath, destPath, replacements);
|
|
29
|
+
} else {
|
|
30
|
+
// for certain files, replace placeholders
|
|
31
|
+
if (["package.json"].includes(item.name)) {
|
|
32
|
+
let content = fs.readFileSync(srcPath, "utf-8");
|
|
33
|
+
|
|
34
|
+
// Replace all configured {{key}} placeholders with their values.
|
|
35
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
36
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
37
|
+
const regex = new RegExp(`\\{\\{\\s*${escapedKey}\\s*\\}\\}`, "g");
|
|
38
|
+
content = content.replace(regex, String(value));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
fs.writeFileSync(destPath, content, "utf-8");
|
|
42
|
+
} else {
|
|
43
|
+
// just copy normal files
|
|
44
|
+
fs.copyFileSync(srcPath, destPath);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -1,45 +1,51 @@
|
|
|
1
|
-
import axios from "axios";
|
|
2
|
-
import extract from "extract-zip";
|
|
3
|
-
import fs from "fs";
|
|
4
|
-
import path from "path";
|
|
5
|
-
import os from "os";
|
|
6
|
-
import { fileURLToPath } from "url";
|
|
7
|
-
|
|
8
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
-
const __dirname = path.dirname(__filename);
|
|
10
|
-
|
|
11
|
-
const owner = "aakash-gupta02";
|
|
12
|
-
const repo = "NeatNode";
|
|
13
|
-
|
|
14
|
-
const zipUrl = `https://codeload.github.com/${owner}/${repo}/zip/refs/heads/main`;
|
|
15
|
-
|
|
16
|
-
export async function downloadTemplate(repoPath) {
|
|
17
|
-
// SAFE TEMP DIRECTORY
|
|
18
|
-
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), "neatnode-"));
|
|
19
|
-
|
|
20
|
-
const tempZip = path.join(tmpBase, "repo.zip");
|
|
21
|
-
const tempExtractDir = path.join(tmpBase, "repo-extract");
|
|
22
|
-
const tempFinalDir = path.join(tmpBase, "template-final");
|
|
23
|
-
|
|
24
|
-
// download zip
|
|
25
|
-
const response = await axios({
|
|
26
|
-
url: zipUrl,
|
|
27
|
-
responseType: "arraybuffer",
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
fs.writeFileSync(tempZip, response.data);
|
|
31
|
-
|
|
32
|
-
// unzip
|
|
33
|
-
await extract(tempZip, { dir: tempExtractDir });
|
|
34
|
-
|
|
35
|
-
const extractedRoot = path.join(tempExtractDir, `${repo}-main`);
|
|
36
|
-
const srcTemplatePath = path.join(extractedRoot, repoPath);
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import extract from "extract-zip";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import os from "os";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
11
|
+
const owner = "aakash-gupta02";
|
|
12
|
+
const repo = "NeatNode";
|
|
13
|
+
|
|
14
|
+
const zipUrl = `https://codeload.github.com/${owner}/${repo}/zip/refs/heads/main`;
|
|
15
|
+
|
|
16
|
+
export async function downloadTemplate(repoPath) {
|
|
17
|
+
// SAFE TEMP DIRECTORY
|
|
18
|
+
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), "neatnode-"));
|
|
19
|
+
|
|
20
|
+
const tempZip = path.join(tmpBase, "repo.zip");
|
|
21
|
+
const tempExtractDir = path.join(tmpBase, "repo-extract");
|
|
22
|
+
const tempFinalDir = path.join(tmpBase, "template-final");
|
|
23
|
+
|
|
24
|
+
// download zip
|
|
25
|
+
const response = await axios({
|
|
26
|
+
url: zipUrl,
|
|
27
|
+
responseType: "arraybuffer",
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
fs.writeFileSync(tempZip, response.data);
|
|
31
|
+
|
|
32
|
+
// unzip
|
|
33
|
+
await extract(tempZip, { dir: tempExtractDir });
|
|
34
|
+
|
|
35
|
+
const extractedRoot = path.join(tempExtractDir, `${repo}-main`);
|
|
36
|
+
const srcTemplatePath = path.join(extractedRoot, repoPath);
|
|
37
|
+
|
|
38
|
+
if (!fs.existsSync(srcTemplatePath)) {
|
|
39
|
+
throw new Error(
|
|
40
|
+
`Template path not found in downloaded archive: ${repoPath}`,
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// copy template
|
|
45
|
+
fs.mkdirSync(tempFinalDir, { recursive: true });
|
|
46
|
+
fs.cpSync(srcTemplatePath, tempFinalDir, { recursive: true });
|
|
47
|
+
|
|
48
|
+
console.log("✔ Template downloaded & extracted");
|
|
49
|
+
|
|
50
|
+
return tempFinalDir;
|
|
51
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
import extract from "extract-zip";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import os from "os";
|
|
6
|
+
import { fileURLToPath } from "url";
|
|
7
|
+
|
|
8
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
9
|
+
const __dirname = path.dirname(__filename);
|
|
10
|
+
|
|
11
|
+
const owner = "aakash-gupta02";
|
|
12
|
+
const repo = "NeatNode";
|
|
13
|
+
|
|
14
|
+
const getPackageVersion = () => {
|
|
15
|
+
try {
|
|
16
|
+
const pkgPath = path.resolve(__dirname, "../../package.json");
|
|
17
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
18
|
+
return pkg.version;
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const getTemplateRef = () => {
|
|
25
|
+
if (process.env.NEATNODE_TEMPLATE_REF) {
|
|
26
|
+
return process.env.NEATNODE_TEMPLATE_REF;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const version = getPackageVersion();
|
|
30
|
+
|
|
31
|
+
if (!version) {
|
|
32
|
+
return "main";
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return `v${version}`;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const getZipUrl = (ref, refType = "tag") => {
|
|
39
|
+
if (refType === "branch") {
|
|
40
|
+
return `https://codeload.github.com/${owner}/${repo}/zip/refs/heads/${ref}`;
|
|
41
|
+
}
|
|
42
|
+
return `https://codeload.github.com/${owner}/${repo}/zip/refs/tags/${ref}`;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const downloadFromRef = async ({ repoPath, ref, refType }) => {
|
|
46
|
+
const zipUrl = getZipUrl(ref, refType);
|
|
47
|
+
|
|
48
|
+
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), "neatnode-"));
|
|
49
|
+
const tempZip = path.join(tmpBase, "repo.zip");
|
|
50
|
+
const tempExtractDir = path.join(tmpBase, "repo-extract");
|
|
51
|
+
const tempFinalDir = path.join(tmpBase, "template-final");
|
|
52
|
+
|
|
53
|
+
const response = await axios({
|
|
54
|
+
url: zipUrl,
|
|
55
|
+
responseType: "arraybuffer",
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
fs.writeFileSync(tempZip, response.data);
|
|
59
|
+
await extract(tempZip, { dir: tempExtractDir });
|
|
60
|
+
|
|
61
|
+
const extractedRootDir = fs
|
|
62
|
+
.readdirSync(tempExtractDir, { withFileTypes: true })
|
|
63
|
+
.find((entry) => entry.isDirectory());
|
|
64
|
+
|
|
65
|
+
if (!extractedRootDir) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
`Could not locate extracted template root directory for ${refType} "${ref}".`,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const extractedRoot = path.join(tempExtractDir, extractedRootDir.name);
|
|
72
|
+
const srcTemplatePath = path.join(extractedRoot, repoPath);
|
|
73
|
+
|
|
74
|
+
if (!fs.existsSync(srcTemplatePath)) {
|
|
75
|
+
throw new Error(
|
|
76
|
+
`Template path "${repoPath}" not found in ${refType} "${ref}" archive.`,
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
fs.mkdirSync(tempFinalDir, { recursive: true });
|
|
81
|
+
fs.cpSync(srcTemplatePath, tempFinalDir, { recursive: true });
|
|
82
|
+
return tempFinalDir;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export async function downloadTemplate(repoPath) {
|
|
86
|
+
const packageRoot = path.resolve(__dirname, "../..");
|
|
87
|
+
const packagedTemplatePath = path.join(packageRoot, repoPath);
|
|
88
|
+
|
|
89
|
+
if (fs.existsSync(packagedTemplatePath)) {
|
|
90
|
+
return packagedTemplatePath;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const preferredRef = getTemplateRef();
|
|
94
|
+
const candidates = [
|
|
95
|
+
{ ref: preferredRef, refType: "tag" },
|
|
96
|
+
{ ref: "main", refType: "branch" },
|
|
97
|
+
];
|
|
98
|
+
|
|
99
|
+
const uniqueCandidates = candidates.filter(
|
|
100
|
+
(candidate, index, arr) =>
|
|
101
|
+
arr.findIndex(
|
|
102
|
+
(item) =>
|
|
103
|
+
item.ref === candidate.ref && item.refType === candidate.refType,
|
|
104
|
+
) === index,
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
const errors = [];
|
|
108
|
+
|
|
109
|
+
for (const candidate of uniqueCandidates) {
|
|
110
|
+
try {
|
|
111
|
+
const templatePath = await downloadFromRef({
|
|
112
|
+
repoPath,
|
|
113
|
+
ref: candidate.ref,
|
|
114
|
+
refType: candidate.refType,
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
console.log(
|
|
118
|
+
`Template downloaded and extracted from ${candidate.refType} "${candidate.ref}"`,
|
|
119
|
+
);
|
|
120
|
+
return templatePath;
|
|
121
|
+
} catch (error) {
|
|
122
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
123
|
+
errors.push(`${candidate.refType}:${candidate.ref} -> ${message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
throw new Error(
|
|
128
|
+
`Failed to download template from all sources. ${errors.join(" | ")}`,
|
|
129
|
+
);
|
|
130
|
+
}
|