@crustjs/create 0.0.1
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 +195 -0
- package/dist/index.js +249 -0
- package/package.json +51 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interpolate `{{var}}` placeholders in a string with values from a context object.
|
|
3
|
+
*
|
|
4
|
+
* Only simple `{{identifier}}` patterns are replaced — no conditionals, loops,
|
|
5
|
+
* or helpers. Whitespace inside the braces is tolerated (e.g. `{{ name }}`).
|
|
6
|
+
* Missing variables are left untouched.
|
|
7
|
+
*
|
|
8
|
+
* @param content - The template string containing `{{var}}` placeholders.
|
|
9
|
+
* @param context - A flat map of variable names to replacement values.
|
|
10
|
+
* @returns The interpolated string.
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```ts
|
|
14
|
+
* interpolate("Hello, {{name}}!", { name: "world" });
|
|
15
|
+
* // => "Hello, world!"
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
declare function interpolate(content: string, context: Record<string, string>): string;
|
|
19
|
+
/**
|
|
20
|
+
* Options for the {@link scaffold} function.
|
|
21
|
+
*
|
|
22
|
+
* @example
|
|
23
|
+
* ```ts
|
|
24
|
+
* const options: ScaffoldOptions = {
|
|
25
|
+
* template: "../templates/base",
|
|
26
|
+
* dest: "./my-project",
|
|
27
|
+
* importMeta: import.meta.url,
|
|
28
|
+
* context: { name: "my-app", description: "A cool CLI" },
|
|
29
|
+
* conflict: "abort",
|
|
30
|
+
* };
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
interface ScaffoldOptions {
|
|
34
|
+
/** Relative path to the template directory (resolved against `importMeta`). */
|
|
35
|
+
readonly template: string;
|
|
36
|
+
/** Absolute or relative path to the destination directory. */
|
|
37
|
+
readonly dest: string;
|
|
38
|
+
/**
|
|
39
|
+
* The `import.meta.url` of the calling module.
|
|
40
|
+
* Used to resolve the template path relative to the caller.
|
|
41
|
+
*/
|
|
42
|
+
readonly importMeta: string;
|
|
43
|
+
/**
|
|
44
|
+
* Variables to interpolate into template file contents.
|
|
45
|
+
* Keys map to `{{key}}` placeholders in template files.
|
|
46
|
+
*/
|
|
47
|
+
readonly context: Record<string, string>;
|
|
48
|
+
/**
|
|
49
|
+
* How to handle an existing non-empty destination directory.
|
|
50
|
+
*
|
|
51
|
+
* - `"abort"` — throw an error (default)
|
|
52
|
+
* - `"overwrite"` — proceed and overwrite existing files
|
|
53
|
+
*
|
|
54
|
+
* @default "abort"
|
|
55
|
+
*/
|
|
56
|
+
readonly conflict?: "abort" | "overwrite";
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Result returned by the {@link scaffold} function.
|
|
60
|
+
*/
|
|
61
|
+
interface ScaffoldResult {
|
|
62
|
+
/** List of all written file paths, relative to the destination directory. */
|
|
63
|
+
readonly files: readonly string[];
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* A declarative step to run after scaffolding completes.
|
|
67
|
+
*
|
|
68
|
+
* Steps are executed sequentially in array order by {@link runSteps}.
|
|
69
|
+
*
|
|
70
|
+
* @example
|
|
71
|
+
* ```ts
|
|
72
|
+
* const steps: PostScaffoldStep[] = [
|
|
73
|
+
* { type: "install" },
|
|
74
|
+
* { type: "git-init", commit: "Initial commit" },
|
|
75
|
+
* { type: "open-editor" },
|
|
76
|
+
* ];
|
|
77
|
+
* ```
|
|
78
|
+
*/
|
|
79
|
+
type PostScaffoldStep = {
|
|
80
|
+
readonly type: "install";
|
|
81
|
+
} | {
|
|
82
|
+
readonly type: "git-init";
|
|
83
|
+
readonly commit?: string;
|
|
84
|
+
} | {
|
|
85
|
+
readonly type: "open-editor";
|
|
86
|
+
} | {
|
|
87
|
+
readonly type: "command";
|
|
88
|
+
readonly cmd: string;
|
|
89
|
+
readonly cwd?: string;
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Copy a template directory to a destination, applying variable interpolation
|
|
93
|
+
* and dotfile renaming.
|
|
94
|
+
*
|
|
95
|
+
* Templates are resolved relative to the calling module's `import.meta.url`,
|
|
96
|
+
* so templates bundled inside published npm packages resolve correctly
|
|
97
|
+
* regardless of install location.
|
|
98
|
+
*
|
|
99
|
+
* Call `scaffold()` multiple times to layer/compose templates — for example,
|
|
100
|
+
* a base template followed by a TypeScript-specific overlay.
|
|
101
|
+
*
|
|
102
|
+
* @param options - Scaffold configuration.
|
|
103
|
+
* @returns The list of all written file paths, relative to the destination directory.
|
|
104
|
+
* @throws When `conflict` is `"abort"` and the destination is a non-empty directory.
|
|
105
|
+
*
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* import { scaffold } from "@crustjs/create";
|
|
109
|
+
*
|
|
110
|
+
* const result = await scaffold({
|
|
111
|
+
* template: "../templates/base",
|
|
112
|
+
* dest: "./my-project",
|
|
113
|
+
* importMeta: import.meta.url,
|
|
114
|
+
* context: { name: "my-app", description: "A cool CLI" },
|
|
115
|
+
* });
|
|
116
|
+
*
|
|
117
|
+
* console.log("Created files:", result.files);
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
declare function scaffold(options: ScaffoldOptions): Promise<ScaffoldResult>;
|
|
121
|
+
/**
|
|
122
|
+
* Execute an array of post-scaffold steps sequentially.
|
|
123
|
+
*
|
|
124
|
+
* Each step is a declarative object describing an action to perform after
|
|
125
|
+
* file scaffolding is complete. Steps run in array order; if any step fails,
|
|
126
|
+
* the error propagates immediately (remaining steps are skipped).
|
|
127
|
+
*
|
|
128
|
+
* @param steps - Array of {@link PostScaffoldStep} objects to execute.
|
|
129
|
+
* @param cwd - The working directory for steps (typically the scaffold dest).
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```ts
|
|
133
|
+
* await runSteps(
|
|
134
|
+
* [
|
|
135
|
+
* { type: "install" },
|
|
136
|
+
* { type: "git-init", commit: "Initial commit" },
|
|
137
|
+
* { type: "open-editor" },
|
|
138
|
+
* ],
|
|
139
|
+
* "./my-project",
|
|
140
|
+
* );
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
declare function runSteps(steps: PostScaffoldStep[], cwd: string): Promise<void>;
|
|
144
|
+
/**
|
|
145
|
+
* Detect the package manager for a project directory.
|
|
146
|
+
*
|
|
147
|
+
* Detection strategy (first match wins):
|
|
148
|
+
* 1. Check for lockfiles in `cwd` (bun.lock/bun.lockb → pnpm-lock.yaml → yarn.lock → package-lock.json)
|
|
149
|
+
* 2. Parse the `npm_config_user_agent` environment variable
|
|
150
|
+
* 3. Default to `"npm"`
|
|
151
|
+
*
|
|
152
|
+
* @param cwd - The directory to check for lockfiles. Defaults to `process.cwd()`.
|
|
153
|
+
* @returns The detected package manager name: `"bun"`, `"pnpm"`, `"yarn"`, or `"npm"`.
|
|
154
|
+
*
|
|
155
|
+
* @example
|
|
156
|
+
* ```ts
|
|
157
|
+
* const pm = detectPackageManager("./my-project");
|
|
158
|
+
* // => "bun" (if bun.lock exists)
|
|
159
|
+
* ```
|
|
160
|
+
*/
|
|
161
|
+
declare function detectPackageManager(cwd?: string): string;
|
|
162
|
+
/**
|
|
163
|
+
* Check whether `git` is installed and available on the system PATH.
|
|
164
|
+
*
|
|
165
|
+
* Runs `git --version` and returns `true` if it exits successfully.
|
|
166
|
+
*
|
|
167
|
+
* @returns `true` if git is available, `false` otherwise.
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* if (isGitInstalled()) {
|
|
172
|
+
* console.log("Git is available");
|
|
173
|
+
* }
|
|
174
|
+
* ```
|
|
175
|
+
*/
|
|
176
|
+
declare function isGitInstalled(): boolean;
|
|
177
|
+
/**
|
|
178
|
+
* Read the current git user's name and email from `git config`.
|
|
179
|
+
*
|
|
180
|
+
* Returns `null` for either field if it is not configured.
|
|
181
|
+
*
|
|
182
|
+
* @returns An object with `name` and `email` (each `string | null`).
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* const user = getGitUser();
|
|
187
|
+
* console.log(user.name); // "Jane Doe" or null
|
|
188
|
+
* console.log(user.email); // "jane@example.com" or null
|
|
189
|
+
* ```
|
|
190
|
+
*/
|
|
191
|
+
declare function getGitUser(): {
|
|
192
|
+
name: string | null;
|
|
193
|
+
email: string | null;
|
|
194
|
+
};
|
|
195
|
+
export { scaffold, runSteps, isGitInstalled, interpolate, getGitUser, detectPackageManager, ScaffoldResult, ScaffoldOptions, PostScaffoldStep };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
// src/interpolate.ts
|
|
3
|
+
function interpolate(content, context) {
|
|
4
|
+
return content.replace(/\{\{\s*(\w+)\s*\}\}/g, (match, key) => {
|
|
5
|
+
if (key in context) {
|
|
6
|
+
return context[key];
|
|
7
|
+
}
|
|
8
|
+
return match;
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
// src/scaffold.ts
|
|
12
|
+
import {
|
|
13
|
+
existsSync,
|
|
14
|
+
mkdirSync,
|
|
15
|
+
readdirSync,
|
|
16
|
+
readFileSync,
|
|
17
|
+
statSync,
|
|
18
|
+
writeFileSync
|
|
19
|
+
} from "fs";
|
|
20
|
+
import { dirname, join, relative, resolve } from "path";
|
|
21
|
+
import { fileURLToPath } from "url";
|
|
22
|
+
|
|
23
|
+
// src/isBinary.ts
|
|
24
|
+
function isBinary(buffer) {
|
|
25
|
+
const length = Math.min(buffer.length, 8192);
|
|
26
|
+
for (let i = 0;i < length; i++) {
|
|
27
|
+
if (buffer[i] === 0) {
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// src/scaffold.ts
|
|
35
|
+
function walkDir(dir) {
|
|
36
|
+
const files = [];
|
|
37
|
+
const entries = readdirSync(dir);
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
const fullPath = join(dir, entry);
|
|
40
|
+
const stat = statSync(fullPath);
|
|
41
|
+
if (stat.isDirectory()) {
|
|
42
|
+
files.push(...walkDir(fullPath));
|
|
43
|
+
} else {
|
|
44
|
+
files.push(fullPath);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return files;
|
|
48
|
+
}
|
|
49
|
+
function renameDotfile(relativePath) {
|
|
50
|
+
const dir = dirname(relativePath);
|
|
51
|
+
const base = relativePath.slice(dir === "." ? 0 : dir.length + 1);
|
|
52
|
+
if (base.startsWith("_") && !base.startsWith("__")) {
|
|
53
|
+
const renamed = `.${base.slice(1)}`;
|
|
54
|
+
return dir === "." ? renamed : join(dir, renamed);
|
|
55
|
+
}
|
|
56
|
+
return relativePath;
|
|
57
|
+
}
|
|
58
|
+
function isNonEmptyDir(dirPath) {
|
|
59
|
+
if (!existsSync(dirPath)) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
const stat = statSync(dirPath);
|
|
63
|
+
if (!stat.isDirectory()) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const entries = readdirSync(dirPath);
|
|
67
|
+
return entries.length > 0;
|
|
68
|
+
}
|
|
69
|
+
async function scaffold(options) {
|
|
70
|
+
const { template, dest, importMeta, context, conflict = "abort" } = options;
|
|
71
|
+
const templateDir = fileURLToPath(new URL(template, importMeta));
|
|
72
|
+
const destDir = resolve(dest);
|
|
73
|
+
if (conflict === "abort" && isNonEmptyDir(destDir)) {
|
|
74
|
+
throw new Error(`Destination directory "${destDir}" already exists and is non-empty. Use conflict: "overwrite" to proceed.`);
|
|
75
|
+
}
|
|
76
|
+
const templateFiles = walkDir(templateDir);
|
|
77
|
+
const writtenFiles = [];
|
|
78
|
+
for (const absolutePath of templateFiles) {
|
|
79
|
+
const relFromTemplate = relative(templateDir, absolutePath);
|
|
80
|
+
const destRelPath = renameDotfile(relFromTemplate);
|
|
81
|
+
const destFilePath = join(destDir, destRelPath);
|
|
82
|
+
mkdirSync(dirname(destFilePath), { recursive: true });
|
|
83
|
+
const buffer = readFileSync(absolutePath);
|
|
84
|
+
if (isBinary(buffer)) {
|
|
85
|
+
writeFileSync(destFilePath, buffer);
|
|
86
|
+
} else {
|
|
87
|
+
const content = buffer.toString("utf-8");
|
|
88
|
+
const interpolated = interpolate(content, context);
|
|
89
|
+
writeFileSync(destFilePath, interpolated, "utf-8");
|
|
90
|
+
}
|
|
91
|
+
writtenFiles.push(destRelPath);
|
|
92
|
+
}
|
|
93
|
+
return { files: writtenFiles };
|
|
94
|
+
}
|
|
95
|
+
// src/utils.ts
|
|
96
|
+
import { existsSync as existsSync2 } from "fs";
|
|
97
|
+
import { join as join2 } from "path";
|
|
98
|
+
var LOCKFILE_MAP = [
|
|
99
|
+
["bun.lock", "bun"],
|
|
100
|
+
["bun.lockb", "bun"],
|
|
101
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
102
|
+
["yarn.lock", "yarn"],
|
|
103
|
+
["package-lock.json", "npm"]
|
|
104
|
+
];
|
|
105
|
+
function detectPackageManager(cwd) {
|
|
106
|
+
const dir = cwd ?? process.cwd();
|
|
107
|
+
for (const [lockfile, manager] of LOCKFILE_MAP) {
|
|
108
|
+
if (existsSync2(join2(dir, lockfile))) {
|
|
109
|
+
return manager;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const userAgent = process.env.npm_config_user_agent;
|
|
113
|
+
if (userAgent) {
|
|
114
|
+
if (userAgent.startsWith("bun"))
|
|
115
|
+
return "bun";
|
|
116
|
+
if (userAgent.startsWith("pnpm"))
|
|
117
|
+
return "pnpm";
|
|
118
|
+
if (userAgent.startsWith("yarn"))
|
|
119
|
+
return "yarn";
|
|
120
|
+
if (userAgent.startsWith("npm"))
|
|
121
|
+
return "npm";
|
|
122
|
+
}
|
|
123
|
+
return "npm";
|
|
124
|
+
}
|
|
125
|
+
function isGitInstalled() {
|
|
126
|
+
try {
|
|
127
|
+
const result = Bun.spawnSync(["git", "--version"]);
|
|
128
|
+
return result.exitCode === 0;
|
|
129
|
+
} catch {
|
|
130
|
+
return false;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
function getGitUser() {
|
|
134
|
+
return {
|
|
135
|
+
name: readGitConfig("user.name"),
|
|
136
|
+
email: readGitConfig("user.email")
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
function readGitConfig(key) {
|
|
140
|
+
try {
|
|
141
|
+
const result = Bun.spawnSync(["git", "config", key]);
|
|
142
|
+
if (result.exitCode !== 0) {
|
|
143
|
+
return null;
|
|
144
|
+
}
|
|
145
|
+
const value = result.stdout.toString().trim();
|
|
146
|
+
return value.length > 0 ? value : null;
|
|
147
|
+
} catch {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// src/steps.ts
|
|
153
|
+
async function runSteps(steps, cwd) {
|
|
154
|
+
for (const step of steps) {
|
|
155
|
+
switch (step.type) {
|
|
156
|
+
case "install":
|
|
157
|
+
await runInstall(cwd);
|
|
158
|
+
break;
|
|
159
|
+
case "git-init":
|
|
160
|
+
await runGitInit(cwd, step.commit);
|
|
161
|
+
break;
|
|
162
|
+
case "open-editor":
|
|
163
|
+
await runOpenEditor(cwd);
|
|
164
|
+
break;
|
|
165
|
+
case "command":
|
|
166
|
+
await runCommand(step.cmd, step.cwd ?? cwd);
|
|
167
|
+
break;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async function runInstall(cwd) {
|
|
172
|
+
const pm = detectPackageManager(cwd);
|
|
173
|
+
const proc = Bun.spawn([pm, "install"], {
|
|
174
|
+
cwd,
|
|
175
|
+
stdout: "inherit",
|
|
176
|
+
stderr: "inherit"
|
|
177
|
+
});
|
|
178
|
+
const exitCode = await proc.exited;
|
|
179
|
+
if (exitCode !== 0) {
|
|
180
|
+
throw new Error(`"${pm} install" exited with code ${exitCode}`);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
async function runGitInit(cwd, commit) {
|
|
184
|
+
await spawnChecked(["git", "init"], cwd, "git init");
|
|
185
|
+
if (commit) {
|
|
186
|
+
await ensureGitIdentity(cwd);
|
|
187
|
+
await spawnChecked(["git", "add", "."], cwd, "git add");
|
|
188
|
+
await spawnChecked(["git", "commit", "-m", commit], cwd, "git commit");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
async function runOpenEditor(cwd) {
|
|
192
|
+
const editor = process.env.EDITOR || "code";
|
|
193
|
+
try {
|
|
194
|
+
const proc = Bun.spawn([editor, cwd], {
|
|
195
|
+
stdout: "ignore",
|
|
196
|
+
stderr: "ignore"
|
|
197
|
+
});
|
|
198
|
+
const raceResult = await Promise.race([
|
|
199
|
+
proc.exited.then((code) => ({ kind: "exited", code })),
|
|
200
|
+
new Promise((resolve2) => setTimeout(() => resolve2({ kind: "timeout" }), 500))
|
|
201
|
+
]);
|
|
202
|
+
if (raceResult.kind === "exited" && raceResult.code !== 0) {
|
|
203
|
+
console.warn(`Warning: could not open editor "${editor}" (exit code ${raceResult.code})`);
|
|
204
|
+
}
|
|
205
|
+
} catch {
|
|
206
|
+
console.warn(`Warning: could not open editor "${editor}"`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function runCommand(cmd, cwd) {
|
|
210
|
+
const proc = Bun.spawn(["sh", "-c", cmd], {
|
|
211
|
+
cwd,
|
|
212
|
+
stdout: "inherit",
|
|
213
|
+
stderr: "inherit"
|
|
214
|
+
});
|
|
215
|
+
const exitCode = await proc.exited;
|
|
216
|
+
if (exitCode !== 0) {
|
|
217
|
+
throw new Error(`Command "${cmd}" exited with code ${exitCode}`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
async function ensureGitIdentity(cwd) {
|
|
221
|
+
const hasName = Bun.spawnSync(["git", "config", "user.name"], { cwd }).exitCode === 0;
|
|
222
|
+
const hasEmail = Bun.spawnSync(["git", "config", "user.email"], { cwd }).exitCode === 0;
|
|
223
|
+
if (!hasName) {
|
|
224
|
+
await spawnChecked(["git", "config", "user.name", "Crust"], cwd, "git config user.name");
|
|
225
|
+
}
|
|
226
|
+
if (!hasEmail) {
|
|
227
|
+
await spawnChecked(["git", "config", "user.email", "crust@scaffolded.project"], cwd, "git config user.email");
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async function spawnChecked(cmd, cwd, label) {
|
|
231
|
+
const proc = Bun.spawn(cmd, {
|
|
232
|
+
cwd,
|
|
233
|
+
stdout: "ignore",
|
|
234
|
+
stderr: "pipe"
|
|
235
|
+
});
|
|
236
|
+
const exitCode = await proc.exited;
|
|
237
|
+
if (exitCode !== 0) {
|
|
238
|
+
const stderr = await new Response(proc.stderr).text();
|
|
239
|
+
throw new Error(`"${label}" failed with exit code ${exitCode}${stderr ? `: ${stderr.trim()}` : ""}`);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
export {
|
|
243
|
+
scaffold,
|
|
244
|
+
runSteps,
|
|
245
|
+
isGitInstalled,
|
|
246
|
+
interpolate,
|
|
247
|
+
getGitUser,
|
|
248
|
+
detectPackageManager
|
|
249
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@crustjs/create",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Headless scaffolding engine for building create-xxx tools",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "chenxin-yan",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/chenxin-yan/crust.git",
|
|
11
|
+
"directory": "packages/create"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://crustjs.com",
|
|
14
|
+
"bugs": {
|
|
15
|
+
"url": "https://github.com/chenxin-yan/crust/issues"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"cli",
|
|
19
|
+
"scaffold",
|
|
20
|
+
"create",
|
|
21
|
+
"template",
|
|
22
|
+
"generator",
|
|
23
|
+
"bun",
|
|
24
|
+
"typescript"
|
|
25
|
+
],
|
|
26
|
+
"files": [
|
|
27
|
+
"dist"
|
|
28
|
+
],
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"import": "./dist/index.js",
|
|
32
|
+
"types": "./dist/index.d.ts"
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "bunup",
|
|
40
|
+
"dev": "bunup --watch",
|
|
41
|
+
"check:types": "tsc --noEmit",
|
|
42
|
+
"test": "bun test"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@crustjs/config": "0.0.0",
|
|
46
|
+
"bunup": "^0.16.29"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"typescript": "^5"
|
|
50
|
+
}
|
|
51
|
+
}
|