create-skybridge 0.0.0-dev.9c78db9 → 0.0.0-dev.ac1e392
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/index.d.ts +1 -1
- package/dist/index.js +29 -21
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +22 -0
- package/index.js +6 -1
- package/package.json +5 -2
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export declare function init(args?: string[]): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
import * as prompts from "@clack/prompts";
|
|
4
5
|
import mri from "mri";
|
|
5
|
-
const argv = mri(process.argv.slice(2), {
|
|
6
|
-
boolean: ["help", "overwrite"],
|
|
7
|
-
alias: { h: "help" },
|
|
8
|
-
});
|
|
9
|
-
const cwd = process.cwd();
|
|
10
6
|
const defaultProjectName = "skybridge-project";
|
|
11
7
|
// prettier-ignore
|
|
12
8
|
const helpMessage = `\
|
|
@@ -22,9 +18,13 @@ Examples:
|
|
|
22
18
|
create-skybridge my-app
|
|
23
19
|
create-skybridge . --overwrite
|
|
24
20
|
`;
|
|
25
|
-
async function init() {
|
|
21
|
+
export async function init(args = process.argv.slice(2)) {
|
|
22
|
+
const argv = mri(args, {
|
|
23
|
+
boolean: ["help", "overwrite"],
|
|
24
|
+
alias: { h: "help" },
|
|
25
|
+
});
|
|
26
26
|
const argTargetDir = argv._[0]
|
|
27
|
-
?
|
|
27
|
+
? sanitizeTargetDir(String(argv._[0]))
|
|
28
28
|
: undefined;
|
|
29
29
|
const argOverwrite = argv.overwrite;
|
|
30
30
|
const help = argv.help;
|
|
@@ -43,14 +43,14 @@ async function init() {
|
|
|
43
43
|
defaultValue: defaultProjectName,
|
|
44
44
|
placeholder: defaultProjectName,
|
|
45
45
|
validate: (value) => {
|
|
46
|
-
return value.length === 0 ||
|
|
46
|
+
return value.length === 0 || sanitizeTargetDir(value).length > 0
|
|
47
47
|
? undefined
|
|
48
48
|
: "Invalid project name";
|
|
49
49
|
},
|
|
50
50
|
});
|
|
51
51
|
if (prompts.isCancel(projectName))
|
|
52
52
|
return cancel();
|
|
53
|
-
targetDir =
|
|
53
|
+
targetDir = sanitizeTargetDir(projectName);
|
|
54
54
|
}
|
|
55
55
|
else {
|
|
56
56
|
targetDir = defaultProjectName;
|
|
@@ -94,20 +94,23 @@ async function init() {
|
|
|
94
94
|
return;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
-
const root = path.join(cwd, targetDir);
|
|
97
|
+
const root = path.join(process.cwd(), targetDir);
|
|
98
98
|
// 3. Copy the repository
|
|
99
99
|
prompts.log.step(`Copying template...`);
|
|
100
100
|
try {
|
|
101
|
-
const templateDir = new URL("../template", import.meta.url)
|
|
101
|
+
const templateDir = fileURLToPath(new URL("../template", import.meta.url));
|
|
102
102
|
// Copy template to target directory
|
|
103
103
|
fs.cpSync(templateDir, root, { recursive: true });
|
|
104
104
|
// Rename _gitignore to .gitignore
|
|
105
105
|
fs.renameSync(path.join(root, "_gitignore"), path.join(root, ".gitignore"));
|
|
106
106
|
// Update project name in package.json
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
|
|
110
|
-
|
|
107
|
+
const name = path.basename(root);
|
|
108
|
+
for (const dir of ["", "server", "web"]) {
|
|
109
|
+
const pkgPath = path.join(root, dir, "package.json");
|
|
110
|
+
const pkg = fs.readFileSync(pkgPath, "utf-8");
|
|
111
|
+
const fixed = pkg.replace(/apps-sdk-template/g, name);
|
|
112
|
+
fs.writeFileSync(pkgPath, fixed);
|
|
113
|
+
}
|
|
111
114
|
prompts.log.success(`Project created in ${root}`);
|
|
112
115
|
prompts.outro(`Done! Next steps:\n\n cd ${targetDir}\n pnpm install\n pnpm dev`);
|
|
113
116
|
}
|
|
@@ -117,8 +120,17 @@ async function init() {
|
|
|
117
120
|
process.exit(1);
|
|
118
121
|
}
|
|
119
122
|
}
|
|
120
|
-
function
|
|
121
|
-
return targetDir
|
|
123
|
+
function sanitizeTargetDir(targetDir) {
|
|
124
|
+
return (targetDir
|
|
125
|
+
.trim()
|
|
126
|
+
// Only keep alphanumeric, dash, underscore, dot, @, /
|
|
127
|
+
.replace(/[^a-zA-Z0-9\-_.@/]/g, "")
|
|
128
|
+
// Prevent path traversal
|
|
129
|
+
.replace(/\.\./g, "")
|
|
130
|
+
// Collapse multiple slashes
|
|
131
|
+
.replace(/\/+/g, "/")
|
|
132
|
+
// Remove leading/trailing slashes
|
|
133
|
+
.replace(/^\/+|\/+$/g, ""));
|
|
122
134
|
}
|
|
123
135
|
function isEmpty(path) {
|
|
124
136
|
const files = fs.readdirSync(path);
|
|
@@ -135,7 +147,3 @@ function emptyDir(dir) {
|
|
|
135
147
|
fs.rmSync(path.resolve(dir, file), { recursive: true, force: true });
|
|
136
148
|
}
|
|
137
149
|
}
|
|
138
|
-
init().catch((e) => {
|
|
139
|
-
console.error(e);
|
|
140
|
-
process.exit(1);
|
|
141
|
-
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { afterEach, beforeEach, describe, it } from "vitest";
|
|
5
|
+
import { init } from "./index.js";
|
|
6
|
+
describe("create-skybridge", () => {
|
|
7
|
+
let tempDirName;
|
|
8
|
+
beforeEach(() => {
|
|
9
|
+
tempDirName = `test-${randomBytes(2).toString("hex")}`;
|
|
10
|
+
});
|
|
11
|
+
afterEach(async () => {
|
|
12
|
+
await fs.rm(path.join(process.cwd(), tempDirName), {
|
|
13
|
+
recursive: true,
|
|
14
|
+
force: true,
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
it("should scaffold a new project", async () => {
|
|
18
|
+
const name = `../../${tempDirName}//project$`;
|
|
19
|
+
await init([name]);
|
|
20
|
+
await fs.access(path.join(process.cwd(), tempDirName, "project", ".gitignore"));
|
|
21
|
+
});
|
|
22
|
+
});
|
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-skybridge",
|
|
3
|
-
"version": "0.0.0-dev.
|
|
3
|
+
"version": "0.0.0-dev.ac1e392",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Alpic",
|
|
@@ -18,6 +18,8 @@
|
|
|
18
18
|
],
|
|
19
19
|
"scripts": {
|
|
20
20
|
"build": "tsc",
|
|
21
|
+
"test": "pnpm run test:unit && pnpm run test:type && pnpm run test:format",
|
|
22
|
+
"test:unit": "vitest run",
|
|
21
23
|
"test:type": "tsc --noEmit",
|
|
22
24
|
"test:format": "biome ci",
|
|
23
25
|
"prepublishOnly": "pnpm run build"
|
|
@@ -28,6 +30,7 @@
|
|
|
28
30
|
},
|
|
29
31
|
"devDependencies": {
|
|
30
32
|
"@types/node": "^25.0.3",
|
|
31
|
-
"typescript": "^5.9.3"
|
|
33
|
+
"typescript": "^5.9.3",
|
|
34
|
+
"vitest": "^2.1.9"
|
|
32
35
|
}
|
|
33
36
|
}
|