srcpack 0.1.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/src/init.ts ADDED
@@ -0,0 +1,144 @@
1
+ // SPDX-License-Identifier: MIT
2
+
3
+ import * as p from "@clack/prompts";
4
+ import { appendFile, readFile } from "node:fs/promises";
5
+ import { existsSync } from "node:fs";
6
+ import { join } from "node:path";
7
+
8
+ const CONFIG_FILE = "srcpack.config.ts";
9
+
10
+ type Bundle = {
11
+ name: string;
12
+ include: string[];
13
+ };
14
+
15
+ export async function runInit(): Promise<void> {
16
+ const cwd = process.cwd();
17
+ const configPath = join(cwd, CONFIG_FILE);
18
+
19
+ p.intro("Create srcpack.config.ts");
20
+
21
+ if (existsSync(configPath)) {
22
+ p.log.warn(`${CONFIG_FILE} already exists`);
23
+ const overwrite = await p.confirm({
24
+ message: "Overwrite existing config?",
25
+ initialValue: false,
26
+ });
27
+
28
+ if (p.isCancel(overwrite) || !overwrite) {
29
+ p.outro("Cancelled");
30
+ return;
31
+ }
32
+ }
33
+
34
+ const bundles: Bundle[] = [];
35
+
36
+ // Collect bundles in a loop
37
+ while (true) {
38
+ const bundle = await promptBundle(bundles.length === 0);
39
+ if (!bundle) break;
40
+ bundles.push(bundle);
41
+
42
+ const another = await p.confirm({
43
+ message: "Add another bundle?",
44
+ initialValue: false,
45
+ });
46
+
47
+ if (p.isCancel(another) || !another) break;
48
+ }
49
+
50
+ if (bundles.length === 0) {
51
+ p.outro("No bundles configured");
52
+ return;
53
+ }
54
+
55
+ // Ask for output directory
56
+ const outDir = await p.text({
57
+ message: "Output directory:",
58
+ placeholder: ".srcpack",
59
+ defaultValue: ".srcpack",
60
+ });
61
+
62
+ if (p.isCancel(outDir)) {
63
+ p.outro("Cancelled");
64
+ return;
65
+ }
66
+
67
+ const outDirValue = outDir.trim() || ".srcpack";
68
+
69
+ // Generate and write config
70
+ const config = generateConfig(bundles, outDirValue);
71
+ await Bun.write(configPath, config);
72
+
73
+ // Add output directory to .gitignore
74
+ await addToGitignore(cwd, outDirValue);
75
+
76
+ p.outro(`Created ${CONFIG_FILE}`);
77
+ }
78
+
79
+ async function promptBundle(isFirst: boolean): Promise<Bundle | null> {
80
+ const name = await p.text({
81
+ message: isFirst ? "Bundle name:" : "Next bundle name:",
82
+ placeholder: "api",
83
+ validate: (value) => {
84
+ if (!value.trim()) return "Name is required";
85
+ if (!/^[a-z][a-z0-9-]*$/i.test(value)) {
86
+ return "Use alphanumeric characters and hyphens";
87
+ }
88
+ },
89
+ });
90
+
91
+ if (p.isCancel(name)) return null;
92
+
93
+ const includeInput = await p.text({
94
+ message: "Include patterns (comma-separated):",
95
+ placeholder: "src/**/*",
96
+ validate: (value) => {
97
+ if (!value.trim()) return "At least one pattern is required";
98
+ },
99
+ });
100
+
101
+ if (p.isCancel(includeInput)) return null;
102
+
103
+ const include = includeInput
104
+ .split(",")
105
+ .map((s) => s.trim())
106
+ .filter(Boolean);
107
+
108
+ return { name: name.trim(), include };
109
+ }
110
+
111
+ function generateConfig(bundles: Bundle[], outDir: string): string {
112
+ const bundleEntries = bundles.map(({ name, include }) => {
113
+ const value =
114
+ include.length === 1 ? `"${include[0]}"` : JSON.stringify(include);
115
+ return ` ${name}: ${value},`;
116
+ });
117
+
118
+ return `import { defineConfig } from "srcpack";
119
+
120
+ export default defineConfig({
121
+ outDir: "${outDir}",
122
+ bundles: {
123
+ ${bundleEntries.join("\n")}
124
+ },
125
+ });
126
+ `;
127
+ }
128
+
129
+ async function addToGitignore(cwd: string, outDir: string): Promise<void> {
130
+ const gitignorePath = join(cwd, ".gitignore");
131
+
132
+ if (!existsSync(gitignorePath)) return;
133
+
134
+ const content = await readFile(gitignorePath, "utf-8");
135
+ const entry = outDir.endsWith("/") ? outDir : `${outDir}/`;
136
+
137
+ // Check if already present (with or without trailing slash)
138
+ const lines = content.split("\n").map((l) => l.trim());
139
+ if (lines.includes(outDir) || lines.includes(entry)) return;
140
+
141
+ // Append with newline if file doesn't end with one
142
+ const prefix = content.endsWith("\n") ? "" : "\n";
143
+ await appendFile(gitignorePath, `${prefix}${entry}\n`);
144
+ }