@create-node-app/core 0.6.0 → 0.6.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/README.md +148 -0
- package/dist/index.cjs +158 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +160 -45
- package/dist/index.js.map +1 -1
- package/package.json +9 -14
package/README.md
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<h1>⚙️ <code>@create-node-app/core</code></h1>
|
|
4
|
+
|
|
5
|
+
<p><strong>Programmatic engine behind Create Awesome Node App.</strong><br/>
|
|
6
|
+
Import the scaffolding pipeline — composable, headless, and CI-ready.</p>
|
|
7
|
+
|
|
8
|
+
[![npm][npmversion]][npmurl]
|
|
9
|
+
[![Downloads][npmdownloads]][npmurl]
|
|
10
|
+
[![License: MIT][licensebadge]][licenseurl]
|
|
11
|
+
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install @create-node-app/core
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Requires **Node.js >= 22**.
|
|
23
|
+
|
|
24
|
+
> This is the _engine_ package. For the interactive CLI, use [`create-awesome-node-app`](https://www.npmjs.com/package/create-awesome-node-app) instead.
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Usage
|
|
29
|
+
|
|
30
|
+
### Scaffold a project programmatically
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
import { createNodeApp, getTemplateDirPath } from "@create-node-app/core";
|
|
34
|
+
|
|
35
|
+
await createNodeApp(
|
|
36
|
+
"my-app",
|
|
37
|
+
{
|
|
38
|
+
projectName: "my-app",
|
|
39
|
+
template: "react-vite-boilerplate",
|
|
40
|
+
},
|
|
41
|
+
(options) => Promise.resolve(options),
|
|
42
|
+
);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Check environment info
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { printEnvInfo } from "@create-node-app/core";
|
|
49
|
+
|
|
50
|
+
await printEnvInfo();
|
|
51
|
+
// Prints OS, CPU, Node, npm, browsers, etc. Then exits.
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Validate the Node.js version
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { checkNodeVersion } from "@create-node-app/core";
|
|
58
|
+
|
|
59
|
+
checkNodeVersion(">=22", "my-tool");
|
|
60
|
+
// Exits with a red error message if the version doesn't match.
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## API Reference
|
|
66
|
+
|
|
67
|
+
All exports from `@create-node-app/core`:
|
|
68
|
+
|
|
69
|
+
### Functions
|
|
70
|
+
|
|
71
|
+
| Signature | Description |
|
|
72
|
+
|-----------|-------------|
|
|
73
|
+
| `createNodeApp(programName, options, transformOptions)` | Main scaffolding orchestrator. Resolves the template, copies files, merges configs, installs deps, and initializes git. |
|
|
74
|
+
| `checkNodeVersion(requiredVersion, packageName)` | Compares `process.version` against a semver range. Exits with code 1 if too old. |
|
|
75
|
+
| `checkForLatestVersion(packageName)` | Fetches the latest version from the npm registry. Falls back to `npm view`. Returns `null` if both fail. |
|
|
76
|
+
| `printEnvInfo()` | Prints OS, CPU, binaries, and browser info to stdout, then exits. |
|
|
77
|
+
| `getPackagePath(templateOrExtension, name?, ignorePackage?)` | Resolves a file path inside a template/extension directory (usually `package.json`). Handles GitHub URLs, `file://` URLs, and legacy slugs. |
|
|
78
|
+
| `getTemplateDirPath(templateOrExtensionUrl)` | Resolves the template directory. Looks for a `template/` subdirectory first, falls back to the resolved root. |
|
|
79
|
+
| `getTemplateBaseDirPath(templateOrExtensionUrl)` | Returns the parent of the `template/` directory (where `cna.config.json` lives). |
|
|
80
|
+
| `downloadRepository(options)` | Clones or pulls a Git repo into a cache dir, then copies files to the target. Supports offline mode, deduplication, and error formatting. |
|
|
81
|
+
| `loadTemplateCnaConfig(templateUrl)` | Loads the optional `cna.config.json` from a template's base directory. |
|
|
82
|
+
|
|
83
|
+
### Types
|
|
84
|
+
|
|
85
|
+
| Type | Shape |
|
|
86
|
+
|------|-------|
|
|
87
|
+
| `CnaOptions` | `{ projectName, info?, verbose?, packageManager?, install?, template?, templatesOrExtensions?, ... }` |
|
|
88
|
+
| `CnaOptionsTransform` | `(options: CnaOptions) => Promise<CnaOptions>` |
|
|
89
|
+
| `CnaConfig` | `{ customOptions?: CnaCustomOption[] }` |
|
|
90
|
+
| `CnaCustomOption` | `{ name, type, message?, initial?, ... }` |
|
|
91
|
+
| `TemplateOrExtension` | `{ url: string; ignorePackage?: boolean }` |
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## How It Works
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
createNodeApp()
|
|
99
|
+
├── checkNodeVersion()
|
|
100
|
+
├── printEnvInfo() if info flag
|
|
101
|
+
├── resolve templates/extensions via getTemplateDirPath()
|
|
102
|
+
├── download Git repos via downloadRepository()
|
|
103
|
+
├── load cna.config.json via loadTemplateCnaConfig()
|
|
104
|
+
├── merge package.json files via lodash.merge
|
|
105
|
+
├── copy/process template files (Lodash interpolation, .if-npm, .append, etc.)
|
|
106
|
+
├── install dependencies (npm/yarn/pnpm/bun)
|
|
107
|
+
├── git init
|
|
108
|
+
└── format + lint:fix post-install
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Architecture
|
|
114
|
+
|
|
115
|
+
The package is organized into these modules:
|
|
116
|
+
|
|
117
|
+
| Module | Responsibility |
|
|
118
|
+
|--------|---------------|
|
|
119
|
+
| `index.ts` | Barrel export and main `createNodeApp` orchestration |
|
|
120
|
+
| `installer.ts` | Project directory creation, dep installation, git init, post-install scripts |
|
|
121
|
+
| `loaders.ts` | File discovery, classification (`.template`, `.append`, conditional prefixes), and processing |
|
|
122
|
+
| `package.ts` | Deep-merges `package.json` from multiple templates/extensions |
|
|
123
|
+
| `paths.ts` | URL resolution — GitHub branches, `file://`, legacy slugs, cache at `~/.cna/` |
|
|
124
|
+
| `git.ts` | Clone/pull with deduplication, offline support, error formatting |
|
|
125
|
+
| `config.ts` | Reads optional `cna.config.json` for custom CLI prompts |
|
|
126
|
+
| `helpers.ts` | Package manager detection, online check, path/naming utilities |
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Related
|
|
131
|
+
|
|
132
|
+
- [`create-awesome-node-app`](https://www.npmjs.com/package/create-awesome-node-app) — Interactive CLI built on this core
|
|
133
|
+
- [Create Node App](https://github.com/Create-Node-App/create-node-app) — Monorepo
|
|
134
|
+
- [Templates catalog](https://github.com/Create-Node-App/cna-templates)
|
|
135
|
+
|
|
136
|
+
---
|
|
137
|
+
|
|
138
|
+
## License
|
|
139
|
+
|
|
140
|
+
MIT © [Create Node App Contributors](https://github.com/Create-Node-App/create-node-app/graphs/contributors)
|
|
141
|
+
|
|
142
|
+
<!-- Reference links -->
|
|
143
|
+
|
|
144
|
+
[npmversion]: https://img.shields.io/npm/v/@create-node-app/core.svg?style=flat-square&color=cb3837
|
|
145
|
+
[npmdownloads]: https://img.shields.io/npm/dm/@create-node-app/core.svg?style=flat-square&color=cb3837
|
|
146
|
+
[licensebadge]: https://img.shields.io/badge/License-MIT-blue.svg?style=flat-square
|
|
147
|
+
[npmurl]: https://www.npmjs.com/package/@create-node-app/core
|
|
148
|
+
[licenseurl]: https://github.com/Create-Node-App/create-node-app/blob/main/LICENSE
|
package/dist/index.cjs
CHANGED
|
@@ -47,7 +47,7 @@ var import_semver3 = __toESM(require("semver"), 1);
|
|
|
47
47
|
var import_child_process3 = require("child_process");
|
|
48
48
|
|
|
49
49
|
// installer.ts
|
|
50
|
-
var
|
|
50
|
+
var import_lodash3 = __toESM(require("lodash"), 1);
|
|
51
51
|
var import_path4 = __toESM(require("path"), 1);
|
|
52
52
|
var import_fs5 = __toESM(require("fs"), 1);
|
|
53
53
|
var import_picocolors3 = __toESM(require("picocolors"), 1);
|
|
@@ -62,6 +62,11 @@ var import_picocolors = __toESM(require("picocolors"), 1);
|
|
|
62
62
|
var import_semver = __toESM(require("semver"), 1);
|
|
63
63
|
var import_dns = __toESM(require("dns"), 1);
|
|
64
64
|
var import_url = require("url");
|
|
65
|
+
|
|
66
|
+
// executable.ts
|
|
67
|
+
var resolveExecutable = (bin) => process.platform === "win32" ? `${bin}.cmd` : bin;
|
|
68
|
+
|
|
69
|
+
// helpers.ts
|
|
65
70
|
var shouldUseYarn = () => {
|
|
66
71
|
const { hasMinYarnPnp, hasMaxYarnPnp, yarnVersion } = checkYarnVersion();
|
|
67
72
|
if (!hasMinYarnPnp) {
|
|
@@ -98,6 +103,20 @@ var shouldUsePnpm = () => {
|
|
|
98
103
|
}
|
|
99
104
|
return true;
|
|
100
105
|
};
|
|
106
|
+
var shouldUseBun = () => {
|
|
107
|
+
const { hasMinBun, bunVersion } = checkBunVersion();
|
|
108
|
+
if (!hasMinBun) {
|
|
109
|
+
console.log(
|
|
110
|
+
import_picocolors.default.yellow(
|
|
111
|
+
`You are using bun version ${import_picocolors.default.bold(
|
|
112
|
+
bunVersion
|
|
113
|
+
)} which is not supported yet. To use bun, install v1.0.0 or higher. See https://bun.sh for instructions on how to install.`
|
|
114
|
+
)
|
|
115
|
+
);
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
return true;
|
|
119
|
+
};
|
|
101
120
|
var checkThatNpmCanReadCwd = () => {
|
|
102
121
|
const cwd = process.cwd();
|
|
103
122
|
let childOutput = null;
|
|
@@ -153,7 +172,7 @@ var checkPnpmVersion = () => {
|
|
|
153
172
|
let hasMinPnpm = false;
|
|
154
173
|
let pnpmVersion = null;
|
|
155
174
|
try {
|
|
156
|
-
pnpmVersion = (0, import_child_process.
|
|
175
|
+
pnpmVersion = (0, import_child_process.execFileSync)(resolveExecutable("pnpm"), ["--version"]).toString().trim();
|
|
157
176
|
if (import_semver.default.valid(pnpmVersion)) {
|
|
158
177
|
hasMinPnpm = import_semver.default.gte(pnpmVersion, minPnpm);
|
|
159
178
|
} else {
|
|
@@ -166,6 +185,19 @@ var checkPnpmVersion = () => {
|
|
|
166
185
|
}
|
|
167
186
|
return { hasMinPnpm, pnpmVersion };
|
|
168
187
|
};
|
|
188
|
+
var checkBunVersion = () => {
|
|
189
|
+
const minBun = "1.0.0";
|
|
190
|
+
let hasMinBun = false;
|
|
191
|
+
let bunVersion = null;
|
|
192
|
+
try {
|
|
193
|
+
bunVersion = (0, import_child_process.execFileSync)(resolveExecutable("bun"), ["--version"]).toString().trim();
|
|
194
|
+
if (import_semver.default.valid(bunVersion)) {
|
|
195
|
+
hasMinBun = import_semver.default.gte(bunVersion, minBun);
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
}
|
|
199
|
+
return { hasMinBun, bunVersion };
|
|
200
|
+
};
|
|
169
201
|
var checkYarnVersion = () => {
|
|
170
202
|
const minYarnPnp = "1.12.0";
|
|
171
203
|
const maxYarnPnp = "2.0.0";
|
|
@@ -173,7 +205,7 @@ var checkYarnVersion = () => {
|
|
|
173
205
|
let hasMaxYarnPnp = false;
|
|
174
206
|
let yarnVersion = null;
|
|
175
207
|
try {
|
|
176
|
-
yarnVersion = (0, import_child_process.
|
|
208
|
+
yarnVersion = (0, import_child_process.execFileSync)(resolveExecutable("yarnpkg"), ["--version"]).toString().trim();
|
|
177
209
|
if (import_semver.default.valid(yarnVersion)) {
|
|
178
210
|
hasMinYarnPnp = import_semver.default.gte(yarnVersion, minYarnPnp);
|
|
179
211
|
hasMaxYarnPnp = import_semver.default.lt(yarnVersion, maxYarnPnp);
|
|
@@ -199,7 +231,7 @@ var checkNpmVersion = () => {
|
|
|
199
231
|
let hasMinNpm = false;
|
|
200
232
|
let npmVersion = null;
|
|
201
233
|
try {
|
|
202
|
-
npmVersion = (0, import_child_process.
|
|
234
|
+
npmVersion = (0, import_child_process.execFileSync)(resolveExecutable("npm"), ["--version"]).toString().trim();
|
|
203
235
|
hasMinNpm = import_semver.default.gte(npmVersion, "6.0.0");
|
|
204
236
|
} catch {
|
|
205
237
|
}
|
|
@@ -213,7 +245,11 @@ var getProxy = () => {
|
|
|
213
245
|
return process.env.HTTPS_PROXY;
|
|
214
246
|
}
|
|
215
247
|
try {
|
|
216
|
-
const httpsProxy = (0, import_child_process.
|
|
248
|
+
const httpsProxy = (0, import_child_process.execFileSync)(resolveExecutable("npm"), [
|
|
249
|
+
"config",
|
|
250
|
+
"get",
|
|
251
|
+
"https-proxy"
|
|
252
|
+
]).toString().trim();
|
|
217
253
|
return httpsProxy !== "null" ? httpsProxy : void 0;
|
|
218
254
|
} catch {
|
|
219
255
|
}
|
|
@@ -245,6 +281,8 @@ var import_lodash = __toESM(require("lodash.merge"), 1);
|
|
|
245
281
|
var import_fs2 = __toESM(require("fs"), 1);
|
|
246
282
|
var import_os2 = __toESM(require("os"), 1);
|
|
247
283
|
var import_path2 = __toESM(require("path"), 1);
|
|
284
|
+
var import_url2 = require("url");
|
|
285
|
+
var import_debug2 = __toESM(require("debug"), 1);
|
|
248
286
|
|
|
249
287
|
// git.ts
|
|
250
288
|
var import_os = __toESM(require("os"), 1);
|
|
@@ -254,6 +292,35 @@ var import_debug = __toESM(require("debug"), 1);
|
|
|
254
292
|
var import_simple_git = require("simple-git");
|
|
255
293
|
var fse = __toESM(require("fs-extra"), 1);
|
|
256
294
|
var log = (0, import_debug.default)("cna:git");
|
|
295
|
+
var formatRepositoryDownloadError = (error, url) => {
|
|
296
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
297
|
+
if (/not found|404|repository not found/i.test(message)) {
|
|
298
|
+
return [
|
|
299
|
+
`Error: Could not fetch template from '${url}'.`,
|
|
300
|
+
" \u2192 The URL returned HTTP 404 or the repository was not found. Please verify the URL is correct.",
|
|
301
|
+
" \u2192 Run 'npx create-awesome-node-app --list-templates' to see available templates."
|
|
302
|
+
].join("\n");
|
|
303
|
+
}
|
|
304
|
+
if (/403|authentication|permission denied|access denied/i.test(message)) {
|
|
305
|
+
return [
|
|
306
|
+
`Error: Could not fetch template from '${url}'.`,
|
|
307
|
+
" \u2192 Access denied (HTTP 403). Check that the repository is public or you have access.",
|
|
308
|
+
" \u2192 Run 'npx create-awesome-node-app --list-templates' to see available templates."
|
|
309
|
+
].join("\n");
|
|
310
|
+
}
|
|
311
|
+
if (/ECONNREFUSED|ENOTFOUND|ETIMEDOUT|network/i.test(message)) {
|
|
312
|
+
return [
|
|
313
|
+
`Error: Could not fetch template from '${url}'.`,
|
|
314
|
+
" \u2192 Could not reach the repository. Please check your internet connection.",
|
|
315
|
+
" \u2192 Run 'npx create-awesome-node-app --list-templates' to see available templates."
|
|
316
|
+
].join("\n");
|
|
317
|
+
}
|
|
318
|
+
return [
|
|
319
|
+
`Error: Could not fetch template from '${url}'.`,
|
|
320
|
+
` \u2192 ${message}`,
|
|
321
|
+
" \u2192 Run 'npx create-awesome-node-app --list-templates' to see available templates."
|
|
322
|
+
].join("\n");
|
|
323
|
+
};
|
|
257
324
|
var filterGit = (src) => {
|
|
258
325
|
return !/(\\|\/)\.git\b/.test(src);
|
|
259
326
|
};
|
|
@@ -268,6 +335,7 @@ var downloadRepository = async ({
|
|
|
268
335
|
cacheDir: optsCacheDir
|
|
269
336
|
}) => {
|
|
270
337
|
const absoluteTarget = import_path.default.isAbsolute(target) ? target : import_path.default.resolve(target);
|
|
338
|
+
const targetExistedBefore = import_fs.default.existsSync(absoluteTarget);
|
|
271
339
|
const isGithub = /^[^/]+\/[^/]+$/.test(url);
|
|
272
340
|
const gitUrl = isGithub ? `https://github.com/${url}` : url;
|
|
273
341
|
const id = targetId || Buffer.from(`${gitUrl}@${branch}`).toString("base64");
|
|
@@ -318,7 +386,15 @@ var downloadRepository = async ({
|
|
|
318
386
|
});
|
|
319
387
|
completedTargetIds.set(id, true);
|
|
320
388
|
} catch (error) {
|
|
321
|
-
|
|
389
|
+
if (!targetExistedBefore && import_fs.default.existsSync(absoluteTarget)) {
|
|
390
|
+
try {
|
|
391
|
+
fse.removeSync(absoluteTarget);
|
|
392
|
+
log("Cleaned up partially created directory: %s", absoluteTarget);
|
|
393
|
+
} catch (cleanupErr) {
|
|
394
|
+
log("Failed to clean up directory: %s", cleanupErr);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
throw new Error(formatRepositoryDownloadError(error, gitUrl));
|
|
322
398
|
} finally {
|
|
323
399
|
gitOperationMap.delete(id);
|
|
324
400
|
}
|
|
@@ -328,6 +404,9 @@ var downloadRepository = async ({
|
|
|
328
404
|
};
|
|
329
405
|
|
|
330
406
|
// paths.ts
|
|
407
|
+
var import_meta = {};
|
|
408
|
+
var log2 = (0, import_debug2.default)("cna:paths");
|
|
409
|
+
var moduleDir = typeof __dirname !== "undefined" ? __dirname : import_path2.default.dirname((0, import_url2.fileURLToPath)(import_meta.url));
|
|
331
410
|
var solveValuesFromTemplateOrExtensionUrl = (templateOrExtension) => {
|
|
332
411
|
const url = new URL(templateOrExtension);
|
|
333
412
|
const ignorePackage = url.searchParams.get("ignorePackage") === "true";
|
|
@@ -381,30 +460,23 @@ var solveRepositoryPath = async ({
|
|
|
381
460
|
if (process.env.CNA_SKIP_GIT === "1") {
|
|
382
461
|
return { dir: target, subdir };
|
|
383
462
|
}
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
});
|
|
391
|
-
} catch {
|
|
392
|
-
}
|
|
463
|
+
await downloadRepository({
|
|
464
|
+
url,
|
|
465
|
+
branch: branch || "",
|
|
466
|
+
target,
|
|
467
|
+
targetId
|
|
468
|
+
});
|
|
393
469
|
return { dir: target, subdir };
|
|
394
470
|
};
|
|
395
471
|
var solveTemplateOrExtensionPath = async (templateOrExtension) => {
|
|
472
|
+
let parsed;
|
|
396
473
|
try {
|
|
397
|
-
|
|
398
|
-
if (protocol === "file:") {
|
|
399
|
-
const baseDir = pathname;
|
|
400
|
-
return { dir: baseDir, subdir, ignorePackage };
|
|
401
|
-
}
|
|
402
|
-
const gitData = await solveRepositoryPath({ url, branch, subdir });
|
|
403
|
-
return { dir: gitData.dir, subdir: gitData.subdir, ignorePackage };
|
|
474
|
+
parsed = solveValuesFromTemplateOrExtensionUrl(templateOrExtension);
|
|
404
475
|
} catch {
|
|
476
|
+
log2("Falling back to legacy template path for: %s", templateOrExtension);
|
|
405
477
|
return {
|
|
406
478
|
dir: import_path2.default.resolve(
|
|
407
|
-
|
|
479
|
+
moduleDir,
|
|
408
480
|
"..",
|
|
409
481
|
"templatesOrExtensions",
|
|
410
482
|
templateOrExtension
|
|
@@ -413,6 +485,12 @@ var solveTemplateOrExtensionPath = async (templateOrExtension) => {
|
|
|
413
485
|
ignorePackage: void 0
|
|
414
486
|
};
|
|
415
487
|
}
|
|
488
|
+
const { url, branch, subdir, protocol, pathname, ignorePackage } = parsed;
|
|
489
|
+
if (protocol === "file:") {
|
|
490
|
+
return { dir: pathname, subdir, ignorePackage };
|
|
491
|
+
}
|
|
492
|
+
const gitData = await solveRepositoryPath({ url, branch, subdir });
|
|
493
|
+
return { dir: gitData.dir, subdir: gitData.subdir, ignorePackage };
|
|
416
494
|
};
|
|
417
495
|
var getPackagePath = async (templateOrExtension, name = "package", ignorePackage = false) => {
|
|
418
496
|
const {
|
|
@@ -487,10 +565,10 @@ var loadPackages = async ({
|
|
|
487
565
|
const setup = await Promise.all(
|
|
488
566
|
templatesOrExtensions.map(async ({ url: templateOrExtension }) => {
|
|
489
567
|
try {
|
|
490
|
-
const
|
|
568
|
+
const template2 = requireIfExists(
|
|
491
569
|
await getPackagePath(templateOrExtension, "template.json")
|
|
492
570
|
);
|
|
493
|
-
return
|
|
571
|
+
return template2.package || {};
|
|
494
572
|
} catch {
|
|
495
573
|
return {};
|
|
496
574
|
}
|
|
@@ -543,12 +621,13 @@ var loadPackages = async ({
|
|
|
543
621
|
};
|
|
544
622
|
|
|
545
623
|
// loaders.ts
|
|
546
|
-
var import_underscore = __toESM(require("underscore"), 1);
|
|
547
624
|
var import_fs4 = __toESM(require("fs"), 1);
|
|
548
625
|
var import_picocolors2 = __toESM(require("picocolors"), 1);
|
|
549
626
|
var import_readdirp = require("readdirp");
|
|
550
627
|
var import_path3 = require("path");
|
|
551
628
|
var import_util = require("util");
|
|
629
|
+
var import_lodash2 = __toESM(require("lodash"), 1);
|
|
630
|
+
var { template } = import_lodash2.default;
|
|
552
631
|
var writeFileAsync = (0, import_util.promisify)(import_fs4.default.writeFile);
|
|
553
632
|
var copyFileAsync = (0, import_util.promisify)(import_fs4.default.copyFile);
|
|
554
633
|
var SRC_PATH_PATTERN = "[src]/";
|
|
@@ -577,6 +656,11 @@ var batchedCopyFiles = async (operations) => {
|
|
|
577
656
|
try {
|
|
578
657
|
makeDirectory((0, import_path3.dirname)(operation.dest));
|
|
579
658
|
await copyFileAsync(operation.src, operation.dest);
|
|
659
|
+
try {
|
|
660
|
+
const srcStat = await (0, import_util.promisify)(import_fs4.default.stat)(operation.src);
|
|
661
|
+
await (0, import_util.promisify)(import_fs4.default.chmod)(operation.dest, srcStat.mode);
|
|
662
|
+
} catch {
|
|
663
|
+
}
|
|
580
664
|
if (operation.verbose) {
|
|
581
665
|
console.log(
|
|
582
666
|
import_picocolors2.default.green(
|
|
@@ -646,7 +730,7 @@ var batchedAppendFiles = async (operations) => {
|
|
|
646
730
|
var copyLoader = ({ root, templateDir, verbose, srcDir }) => async ({ path: path5 }) => {
|
|
647
731
|
const operations = [];
|
|
648
732
|
try {
|
|
649
|
-
const newPath = path5.replace(/.if-(npm|yarn|pnpm)$/, "").replace(SRC_PATH_PATTERN, getSrcDirPattern(srcDir));
|
|
733
|
+
const newPath = path5.replace(/.if-(npm|yarn|pnpm|bun)$/, "").replace(SRC_PATH_PATTERN, getSrcDirPattern(srcDir));
|
|
650
734
|
operations.push({
|
|
651
735
|
src: `${templateDir}/${path5}`,
|
|
652
736
|
dest: `${root}/${newPath}`,
|
|
@@ -663,7 +747,7 @@ var copyLoader = ({ root, templateDir, verbose, srcDir }) => async ({ path: path
|
|
|
663
747
|
var appendLoader = ({ root, templateDir, verbose, srcDir }) => async ({ path: path5 }) => {
|
|
664
748
|
const operations = [];
|
|
665
749
|
try {
|
|
666
|
-
const newPath = path5.replace(/.append$/, "").replace(/.if-(npm|yarn|pnpm)$/, "").replace(SRC_PATH_PATTERN, getSrcDirPattern(srcDir));
|
|
750
|
+
const newPath = path5.replace(/.append$/, "").replace(/.if-(npm|yarn|pnpm|bun)$/, "").replace(SRC_PATH_PATTERN, getSrcDirPattern(srcDir));
|
|
667
751
|
operations.push({
|
|
668
752
|
src: `${templateDir}/${path5}`,
|
|
669
753
|
dest: `${root}/${newPath}`,
|
|
@@ -694,8 +778,8 @@ var templateLoader = ({
|
|
|
694
778
|
const filePath = `${templateDir}/${path5}`;
|
|
695
779
|
const file = await (0, import_util.promisify)(import_fs4.default.readFile)(filePath, "utf8");
|
|
696
780
|
const fileMode = (await (0, import_util.promisify)(import_fs4.default.stat)(filePath)).mode;
|
|
697
|
-
const newFile =
|
|
698
|
-
const newPath = path5.replace(/.template$/, "").replace(/.append$/, "").replace(/.if-(npm|yarn|pnpm)$/, "").replace(SRC_PATH_PATTERN, getSrcDirPattern(srcDir));
|
|
781
|
+
const newFile = template(file);
|
|
782
|
+
const newPath = path5.replace(/.template$/, "").replace(/.append$/, "").replace(/.if-(npm|yarn|pnpm|bun)$/, "").replace(SRC_PATH_PATTERN, getSrcDirPattern(srcDir));
|
|
699
783
|
operations.push({
|
|
700
784
|
path: `${root}/${newPath}`,
|
|
701
785
|
content: newFile({
|
|
@@ -725,6 +809,7 @@ var fileLoader = ({
|
|
|
725
809
|
verbose,
|
|
726
810
|
useYarn,
|
|
727
811
|
usePnpm,
|
|
812
|
+
useBun,
|
|
728
813
|
srcDir = DEFAULT_SRC_PATH,
|
|
729
814
|
runCommand,
|
|
730
815
|
installCommand,
|
|
@@ -747,6 +832,7 @@ var fileLoader = ({
|
|
|
747
832
|
verbose,
|
|
748
833
|
useYarn: !!useYarn,
|
|
749
834
|
usePnpm: !!usePnpm,
|
|
835
|
+
useBun: !!useBun,
|
|
750
836
|
mode,
|
|
751
837
|
srcDir,
|
|
752
838
|
runCommand,
|
|
@@ -770,6 +856,7 @@ var loadFiles = async ({
|
|
|
770
856
|
verbose,
|
|
771
857
|
useYarn = false,
|
|
772
858
|
usePnpm = false,
|
|
859
|
+
useBun = false,
|
|
773
860
|
srcDir = DEFAULT_SRC_PATH,
|
|
774
861
|
runCommand,
|
|
775
862
|
installCommand,
|
|
@@ -800,7 +887,7 @@ var loadFiles = async ({
|
|
|
800
887
|
/\byarn\.lock$/,
|
|
801
888
|
/\bpnpm-lock\.yaml$/
|
|
802
889
|
];
|
|
803
|
-
const skipManager = usePnpm ? [/\.if-npm\./, /\.if-yarn\./] : useYarn ? [/\.if-npm\./, /\.if-pnpm\./] : [/\.if-yarn\./, /\.if-pnpm\./];
|
|
890
|
+
const skipManager = usePnpm ? [/\.if-npm\./, /\.if-yarn\./, /\.if-bun\./] : useYarn ? [/\.if-npm\./, /\.if-pnpm\./, /\.if-bun\./] : useBun ? [/\.if-yarn\./, /\.if-pnpm\./] : [/\.if-yarn\./, /\.if-pnpm\./, /\.if-bun\./];
|
|
804
891
|
const shouldSkip = (p) => [...skipGlobs, ...skipManager].some((rgx) => rgx.test(p));
|
|
805
892
|
for await (const entry of (0, import_readdirp.readdirp)(templateDir, {
|
|
806
893
|
type: "files",
|
|
@@ -820,6 +907,7 @@ var loadFiles = async ({
|
|
|
820
907
|
verbose,
|
|
821
908
|
useYarn,
|
|
822
909
|
usePnpm,
|
|
910
|
+
useBun,
|
|
823
911
|
srcDir,
|
|
824
912
|
runCommand,
|
|
825
913
|
installCommand,
|
|
@@ -855,7 +943,8 @@ var loadFiles = async ({
|
|
|
855
943
|
};
|
|
856
944
|
|
|
857
945
|
// installer.ts
|
|
858
|
-
var
|
|
946
|
+
var { isEmpty } = import_lodash3.default;
|
|
947
|
+
var install = async (root, useYarn = false, usePnpm = false, useBun = false, dependencies = [], verbose = false, isOnline = true, isDevDependencies = false) => {
|
|
859
948
|
let command;
|
|
860
949
|
let args;
|
|
861
950
|
if (useYarn) {
|
|
@@ -884,6 +973,17 @@ var install = async (root, useYarn = false, usePnpm = false, dependencies = [],
|
|
|
884
973
|
args.push("--save");
|
|
885
974
|
}
|
|
886
975
|
args.push(...dependencies);
|
|
976
|
+
} else if (useBun) {
|
|
977
|
+
command = "bun";
|
|
978
|
+
if (dependencies.length > 0) {
|
|
979
|
+
args = ["add"];
|
|
980
|
+
if (isDevDependencies) {
|
|
981
|
+
args.push("--dev");
|
|
982
|
+
}
|
|
983
|
+
args.push(...dependencies);
|
|
984
|
+
} else {
|
|
985
|
+
args = ["install"];
|
|
986
|
+
}
|
|
887
987
|
} else {
|
|
888
988
|
command = "npm";
|
|
889
989
|
args = ["install", "--loglevel", "error"];
|
|
@@ -898,7 +998,7 @@ var install = async (root, useYarn = false, usePnpm = false, dependencies = [],
|
|
|
898
998
|
args.push("--verbose");
|
|
899
999
|
}
|
|
900
1000
|
try {
|
|
901
|
-
(0, import_child_process2.
|
|
1001
|
+
(0, import_child_process2.execFileSync)(resolveExecutable(command), args, {
|
|
902
1002
|
cwd: root,
|
|
903
1003
|
stdio: "inherit"
|
|
904
1004
|
});
|
|
@@ -907,8 +1007,9 @@ var install = async (root, useYarn = false, usePnpm = false, dependencies = [],
|
|
|
907
1007
|
}
|
|
908
1008
|
};
|
|
909
1009
|
var runCommandInProjectDir = async (root, command, args = [], successMessage = "Operation completed successfully.", errorMessage = "Operation failed.") => {
|
|
1010
|
+
const [executable, ...baseArgs] = command === "npm run" ? ["npm", "run"] : command === "pnpm run" ? ["pnpm", "run"] : command === "bun run" ? ["bun", "run"] : [command];
|
|
910
1011
|
try {
|
|
911
|
-
(0, import_child_process2.
|
|
1012
|
+
(0, import_child_process2.execFileSync)(resolveExecutable(executable), [...baseArgs, ...args], {
|
|
912
1013
|
cwd: root,
|
|
913
1014
|
stdio: "ignore"
|
|
914
1015
|
});
|
|
@@ -936,6 +1037,7 @@ var run = async ({
|
|
|
936
1037
|
verbose = false,
|
|
937
1038
|
useYarn = false,
|
|
938
1039
|
usePnpm = false,
|
|
1040
|
+
useBun = false,
|
|
939
1041
|
templatesOrExtensions = [],
|
|
940
1042
|
dependencies = [],
|
|
941
1043
|
devDependencies = [],
|
|
@@ -945,7 +1047,7 @@ var run = async ({
|
|
|
945
1047
|
...customOptions
|
|
946
1048
|
}) => {
|
|
947
1049
|
const isOnline = useYarn ? await checkIfOnline(useYarn) : true;
|
|
948
|
-
if (
|
|
1050
|
+
if (isEmpty(templatesOrExtensions)) {
|
|
949
1051
|
console.log();
|
|
950
1052
|
console.log(
|
|
951
1053
|
import_picocolors3.default.yellow(
|
|
@@ -965,6 +1067,7 @@ var run = async ({
|
|
|
965
1067
|
verbose,
|
|
966
1068
|
useYarn,
|
|
967
1069
|
usePnpm,
|
|
1070
|
+
useBun,
|
|
968
1071
|
runCommand,
|
|
969
1072
|
installCommand,
|
|
970
1073
|
...customOptions
|
|
@@ -982,6 +1085,7 @@ var run = async ({
|
|
|
982
1085
|
root,
|
|
983
1086
|
useYarn,
|
|
984
1087
|
usePnpm,
|
|
1088
|
+
useBun,
|
|
985
1089
|
dependencies,
|
|
986
1090
|
verbose,
|
|
987
1091
|
isOnline,
|
|
@@ -995,6 +1099,7 @@ var run = async ({
|
|
|
995
1099
|
root,
|
|
996
1100
|
useYarn,
|
|
997
1101
|
usePnpm,
|
|
1102
|
+
useBun,
|
|
998
1103
|
devDependencies,
|
|
999
1104
|
verbose,
|
|
1000
1105
|
isOnline,
|
|
@@ -1119,13 +1224,15 @@ var createApp = async ({
|
|
|
1119
1224
|
console.log();
|
|
1120
1225
|
const useYarn = customOptions.packageManager === "yarn" && shouldUseYarn();
|
|
1121
1226
|
const usePnpm = customOptions.packageManager === "pnpm" && shouldUsePnpm();
|
|
1122
|
-
const
|
|
1123
|
-
const
|
|
1227
|
+
const useBun = customOptions.packageManager === "bun" && shouldUseBun();
|
|
1228
|
+
const runCommand = useYarn ? "yarn" : usePnpm ? "pnpm run" : useBun ? "bun run" : "npm run";
|
|
1229
|
+
const installCommand = useYarn ? "yarn" : usePnpm ? "pnpm install" : useBun ? "bun install" : "npm install";
|
|
1124
1230
|
const { packageJson, dependencies, devDependencies } = await loadPackages({
|
|
1125
1231
|
templatesOrExtensions,
|
|
1126
1232
|
appName,
|
|
1127
1233
|
usePnpm,
|
|
1128
1234
|
useYarn,
|
|
1235
|
+
useBun,
|
|
1129
1236
|
runCommand,
|
|
1130
1237
|
ignorePackage
|
|
1131
1238
|
});
|
|
@@ -1135,7 +1242,7 @@ var createApp = async ({
|
|
|
1135
1242
|
);
|
|
1136
1243
|
const originalDirectory = process.cwd();
|
|
1137
1244
|
process.chdir(root);
|
|
1138
|
-
if (!useYarn && !checkThatNpmCanReadCwd()) {
|
|
1245
|
+
if (!useYarn && !useBun && !checkThatNpmCanReadCwd()) {
|
|
1139
1246
|
process.exit(1);
|
|
1140
1247
|
}
|
|
1141
1248
|
if (!import_semver2.default.satisfies(process.version, ">=18.0.0")) {
|
|
@@ -1148,7 +1255,7 @@ Please update to Node 18 or higher for a better, fully supported experience.
|
|
|
1148
1255
|
)
|
|
1149
1256
|
);
|
|
1150
1257
|
}
|
|
1151
|
-
if (!useYarn) {
|
|
1258
|
+
if (!useYarn && !useBun) {
|
|
1152
1259
|
const npmInfo = checkNpmVersion();
|
|
1153
1260
|
if (!npmInfo.hasMinNpm) {
|
|
1154
1261
|
if (npmInfo.npmVersion) {
|
|
@@ -1166,7 +1273,11 @@ Please update to npm 3 or higher for a better, fully supported experience.
|
|
|
1166
1273
|
if (useYarn) {
|
|
1167
1274
|
let yarnUsesDefaultRegistry = true;
|
|
1168
1275
|
try {
|
|
1169
|
-
yarnUsesDefaultRegistry = (0, import_child_process2.
|
|
1276
|
+
yarnUsesDefaultRegistry = (0, import_child_process2.execFileSync)(resolveExecutable("yarnpkg"), [
|
|
1277
|
+
"config",
|
|
1278
|
+
"get",
|
|
1279
|
+
"registry"
|
|
1280
|
+
]).toString().trim() === "https://registry.yarnpkg.com";
|
|
1170
1281
|
} catch {
|
|
1171
1282
|
}
|
|
1172
1283
|
if (false) {
|
|
@@ -1184,6 +1295,7 @@ Please update to npm 3 or higher for a better, fully supported experience.
|
|
|
1184
1295
|
verbose,
|
|
1185
1296
|
useYarn,
|
|
1186
1297
|
usePnpm,
|
|
1298
|
+
useBun,
|
|
1187
1299
|
templatesOrExtensions,
|
|
1188
1300
|
dependencies,
|
|
1189
1301
|
devDependencies,
|
|
@@ -1238,7 +1350,11 @@ var checkForLatestVersion = async (packageName) => {
|
|
|
1238
1350
|
return null;
|
|
1239
1351
|
} catch {
|
|
1240
1352
|
try {
|
|
1241
|
-
return (0, import_child_process3.
|
|
1353
|
+
return (0, import_child_process3.execFileSync)(resolveExecutable("npm"), [
|
|
1354
|
+
"view",
|
|
1355
|
+
packageName,
|
|
1356
|
+
"version"
|
|
1357
|
+
]).toString().trim();
|
|
1242
1358
|
} catch {
|
|
1243
1359
|
}
|
|
1244
1360
|
}
|
|
@@ -1249,7 +1365,7 @@ var printEnvInfo = async () => {
|
|
|
1249
1365
|
const info = await import_envinfo.default.run(
|
|
1250
1366
|
{
|
|
1251
1367
|
System: ["OS", "CPU", "Memory", "Shell"],
|
|
1252
|
-
Binaries: ["Node", "npm", "pnpm", "Yarn", "Watchman"],
|
|
1368
|
+
Binaries: ["Node", "npm", "pnpm", "Yarn", "Bun", "Watchman"],
|
|
1253
1369
|
Browsers: ["Chrome", "Edge", "Internet Explorer", "Firefox", "Safari"]
|
|
1254
1370
|
},
|
|
1255
1371
|
{
|