@crustjs/create 0.0.1 → 0.0.3
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 +17 -16
- package/dist/index.js +48 -3
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -22,24 +22,24 @@ declare function interpolate(content: string, context: Record<string, string>):
|
|
|
22
22
|
* @example
|
|
23
23
|
* ```ts
|
|
24
24
|
* const options: ScaffoldOptions = {
|
|
25
|
-
* template: "../templates/base",
|
|
25
|
+
* template: new URL("../templates/base", import.meta.url),
|
|
26
26
|
* dest: "./my-project",
|
|
27
|
-
* importMeta: import.meta.url,
|
|
28
27
|
* context: { name: "my-app", description: "A cool CLI" },
|
|
29
28
|
* conflict: "abort",
|
|
30
29
|
* };
|
|
31
30
|
* ```
|
|
32
31
|
*/
|
|
33
32
|
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
33
|
/**
|
|
39
|
-
*
|
|
40
|
-
*
|
|
34
|
+
* Template directory source.
|
|
35
|
+
*
|
|
36
|
+
* - `string` absolute path: used as-is
|
|
37
|
+
* - `string` relative path: resolved from the nearest package root of `process.argv[1]`
|
|
38
|
+
* - `URL`: must be a `file:` URL (for module-relative templates)
|
|
41
39
|
*/
|
|
42
|
-
readonly
|
|
40
|
+
readonly template: string | URL;
|
|
41
|
+
/** Absolute or relative path to the destination directory. */
|
|
42
|
+
readonly dest: string;
|
|
43
43
|
/**
|
|
44
44
|
* Variables to interpolate into template file contents.
|
|
45
45
|
* Keys map to `{{key}}` placeholders in template files.
|
|
@@ -92,9 +92,10 @@ type PostScaffoldStep = {
|
|
|
92
92
|
* Copy a template directory to a destination, applying variable interpolation
|
|
93
93
|
* and dotfile renaming.
|
|
94
94
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
95
|
+
* Template resolution:
|
|
96
|
+
* - `string` absolute paths are used as-is
|
|
97
|
+
* - `string` relative paths resolve from the nearest package root of `process.argv[1]`
|
|
98
|
+
* - `URL` templates must be `file:` URLs (for module-relative templates)
|
|
98
99
|
*
|
|
99
100
|
* Call `scaffold()` multiple times to layer/compose templates — for example,
|
|
100
101
|
* a base template followed by a TypeScript-specific overlay.
|
|
@@ -108,9 +109,8 @@ type PostScaffoldStep = {
|
|
|
108
109
|
* import { scaffold } from "@crustjs/create";
|
|
109
110
|
*
|
|
110
111
|
* const result = await scaffold({
|
|
111
|
-
* template: "../templates/base",
|
|
112
|
+
* template: new URL("../templates/base", import.meta.url),
|
|
112
113
|
* dest: "./my-project",
|
|
113
|
-
* importMeta: import.meta.url,
|
|
114
114
|
* context: { name: "my-app", description: "A cool CLI" },
|
|
115
115
|
* });
|
|
116
116
|
*
|
|
@@ -141,6 +141,7 @@ declare function scaffold(options: ScaffoldOptions): Promise<ScaffoldResult>;
|
|
|
141
141
|
* ```
|
|
142
142
|
*/
|
|
143
143
|
declare function runSteps(steps: PostScaffoldStep[], cwd: string): Promise<void>;
|
|
144
|
+
type PackageManager = "npm" | "pnpm" | "bun" | "yarn";
|
|
144
145
|
/**
|
|
145
146
|
* Detect the package manager for a project directory.
|
|
146
147
|
*
|
|
@@ -158,7 +159,7 @@ declare function runSteps(steps: PostScaffoldStep[], cwd: string): Promise<void>
|
|
|
158
159
|
* // => "bun" (if bun.lock exists)
|
|
159
160
|
* ```
|
|
160
161
|
*/
|
|
161
|
-
declare function detectPackageManager(cwd?: string):
|
|
162
|
+
declare function detectPackageManager(cwd?: string): PackageManager;
|
|
162
163
|
/**
|
|
163
164
|
* Check whether `git` is installed and available on the system PATH.
|
|
164
165
|
*
|
|
@@ -192,4 +193,4 @@ declare function getGitUser(): {
|
|
|
192
193
|
name: string | null;
|
|
193
194
|
email: string | null;
|
|
194
195
|
};
|
|
195
|
-
export { scaffold, runSteps, isGitInstalled, interpolate, getGitUser, detectPackageManager, ScaffoldResult, ScaffoldOptions, PostScaffoldStep };
|
|
196
|
+
export { scaffold, runSteps, isGitInstalled, interpolate, getGitUser, detectPackageManager, ScaffoldResult, ScaffoldOptions, PostScaffoldStep, PackageManager };
|
package/dist/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
statSync,
|
|
18
18
|
writeFileSync
|
|
19
19
|
} from "fs";
|
|
20
|
-
import { dirname, join, relative, resolve } from "path";
|
|
20
|
+
import { dirname, isAbsolute, join, relative, resolve } from "path";
|
|
21
21
|
import { fileURLToPath } from "url";
|
|
22
22
|
|
|
23
23
|
// src/isBinary.ts
|
|
@@ -66,10 +66,55 @@ function isNonEmptyDir(dirPath) {
|
|
|
66
66
|
const entries = readdirSync(dirPath);
|
|
67
67
|
return entries.length > 0;
|
|
68
68
|
}
|
|
69
|
+
function findNearestPackageRoot(startPath) {
|
|
70
|
+
let current = resolve(startPath);
|
|
71
|
+
if (existsSync(current) && !statSync(current).isDirectory()) {
|
|
72
|
+
current = dirname(current);
|
|
73
|
+
}
|
|
74
|
+
while (true) {
|
|
75
|
+
if (existsSync(join(current, "package.json"))) {
|
|
76
|
+
return current;
|
|
77
|
+
}
|
|
78
|
+
const parent = dirname(current);
|
|
79
|
+
if (parent === current) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
current = parent;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function resolveTemplateDir(template) {
|
|
86
|
+
if (template instanceof URL) {
|
|
87
|
+
if (template.protocol !== "file:") {
|
|
88
|
+
throw new Error(`Template URL must use file: protocol, got "${template.protocol}".`);
|
|
89
|
+
}
|
|
90
|
+
return fileURLToPath(template);
|
|
91
|
+
}
|
|
92
|
+
if (isAbsolute(template)) {
|
|
93
|
+
return resolve(template);
|
|
94
|
+
}
|
|
95
|
+
const entrypoint = process.argv[1];
|
|
96
|
+
if (!entrypoint) {
|
|
97
|
+
throw new Error(`Could not resolve relative template path "${template}" because process.argv[1] is not set. Pass an absolute path or a file: URL template.`);
|
|
98
|
+
}
|
|
99
|
+
const packageRoot = findNearestPackageRoot(resolve(entrypoint));
|
|
100
|
+
if (!packageRoot) {
|
|
101
|
+
throw new Error(`Could not resolve relative template path "${template}" from entrypoint "${entrypoint}" because no package.json was found in its parent directories. Pass an absolute path or a file: URL template.`);
|
|
102
|
+
}
|
|
103
|
+
return resolve(packageRoot, template);
|
|
104
|
+
}
|
|
105
|
+
function formatTemplateInput(template) {
|
|
106
|
+
return typeof template === "string" ? template : template.href;
|
|
107
|
+
}
|
|
69
108
|
async function scaffold(options) {
|
|
70
|
-
const { template, dest,
|
|
71
|
-
const templateDir =
|
|
109
|
+
const { template, dest, context, conflict = "abort" } = options;
|
|
110
|
+
const templateDir = resolveTemplateDir(template);
|
|
72
111
|
const destDir = resolve(dest);
|
|
112
|
+
if (!existsSync(templateDir)) {
|
|
113
|
+
throw new Error(`Template directory "${templateDir}" does not exist (from template: "${formatTemplateInput(template)}").`);
|
|
114
|
+
}
|
|
115
|
+
if (!statSync(templateDir).isDirectory()) {
|
|
116
|
+
throw new Error(`Template path "${templateDir}" is not a directory (from template: "${formatTemplateInput(template)}").`);
|
|
117
|
+
}
|
|
73
118
|
if (conflict === "abort" && isNonEmptyDir(destDir)) {
|
|
74
119
|
throw new Error(`Destination directory "${destDir}" already exists and is non-empty. Use conflict: "overwrite" to proceed.`);
|
|
75
120
|
}
|