@dunx/create-app 3.0.3 → 3.0.4
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 +71 -17
- package/dist/banner.d.ts +14 -0
- package/dist/{chunk-x7s3axkt.js → chunk-5tn3ybzv.js} +62 -73
- package/dist/cli.js +745 -72
- package/dist/features.d.ts +2 -2
- package/dist/generate.d.ts +0 -3
- package/dist/index.d.ts +2 -1
- package/dist/index.js +9 -1
- package/dist/keys.d.ts +48 -0
- package/dist/prompt.d.ts +43 -0
- package/dist/prompts.d.ts +53 -0
- package/dist/scaffold.d.ts +17 -0
- package/dist/style.d.ts +26 -0
- package/dist/tty.d.ts +58 -0
- package/dist/wizard.d.ts +35 -0
- package/package.json +1 -1
- package/templates/features/assets/assets.module.ts +1 -1
- package/templates/features/auth/auth.module.ts +1 -1
- package/templates/features/http/http.module.ts +1 -1
- package/templates/minimal/package.json +3 -2
- package/dist/stdin.d.ts +0 -14
package/README.md
CHANGED
|
@@ -13,25 +13,52 @@ bunx @dunx/create-app my-api
|
|
|
13
13
|
```
|
|
14
14
|
cd my-api
|
|
15
15
|
bun install
|
|
16
|
-
bun run
|
|
16
|
+
bun run dev # http://localhost:3000/greetings, restarting on a change
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
+
## The questions
|
|
20
|
+
|
|
21
|
+
There is no flag for choosing features. The command opens a list:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
? Features 2 chosen, 1 pulled in
|
|
25
|
+
○ notes CRUD routes with zod validation. The smallest real feature.
|
|
26
|
+
◉ openapi OpenAPI 3.1 from the routes own schemas, plus the Swagger UI page.
|
|
27
|
+
◈ database drizzle over bun:sqlite, with a schema, seeds and migrations.
|
|
28
|
+
❯ ◉ users A repository, a service and validated routes over the database.
|
|
29
|
+
○ auth better-auth mounted, with SessionGuard and an audit trail.
|
|
30
|
+
database comes along as a requirement.
|
|
31
|
+
Space toggles. ↑↓ moves. a all, n none. Enter continues.
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
| Key | Does |
|
|
35
|
+
| --------------- | --------------------------------------------- |
|
|
36
|
+
| `↑` `↓`, `k` `j`, Tab | Move the cursor |
|
|
37
|
+
| Space | Toggle the feature under it |
|
|
38
|
+
| `a` / `n` | Everything / nothing |
|
|
39
|
+
| Enter | Take the selection |
|
|
40
|
+
| Ctrl+C, Esc | Stop, having written nothing |
|
|
41
|
+
|
|
42
|
+
`◉` is chosen, `◈` is pulled in by something else you chose, `○` is neither. The
|
|
43
|
+
two lines under the list update as you go: what your selection drags in, and which
|
|
44
|
+
of it needs Redis or Postgres running to do anything.
|
|
45
|
+
|
|
46
|
+
Three more questions appear only when there is something to ask: a directory, when
|
|
47
|
+
the command line named none; a package name, when the directory's is one npm would
|
|
48
|
+
reject; and whether to write into a directory that already has files in it.
|
|
49
|
+
|
|
19
50
|
## Options
|
|
20
51
|
|
|
21
|
-
| Flag
|
|
22
|
-
|
|
|
23
|
-
| `--name <name>`
|
|
24
|
-
| `--
|
|
25
|
-
| `--
|
|
26
|
-
| `--
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
With neither `--with` nor `--yes`, and a terminal attached, it lists the features
|
|
33
|
-
and reads one line of stdin. Piped or in CI it never prompts, so **an agent or a
|
|
34
|
-
script should pass `--yes` or `--with`**.
|
|
52
|
+
| Flag | Default | Meaning |
|
|
53
|
+
| --------------- | ------------------ | --------------------------------------------- |
|
|
54
|
+
| `--name <name>` | the directory name | Package name for the generated app |
|
|
55
|
+
| `--force` | off | Write into a directory that already has files |
|
|
56
|
+
| `--yes`, `-y` | off | Skip the questions, take the minimal template |
|
|
57
|
+
| `--help` | | Print usage |
|
|
58
|
+
|
|
59
|
+
**Piped, redirected or in CI it asks nothing** and writes the minimal template, so
|
|
60
|
+
a script never hangs on a question nothing can answer. To choose features without
|
|
61
|
+
a terminal, call [`scaffold`](#programmatic-use) rather than passing flags.
|
|
35
62
|
|
|
36
63
|
The name is validated against npm's rules **before** anything is created, because
|
|
37
64
|
an invalid one would otherwise surface as a confusing `bun install` failure inside
|
|
@@ -45,9 +72,31 @@ bunx @dunx/create-app .
|
|
|
45
72
|
```
|
|
46
73
|
|
|
47
74
|
`.git`, `.gitkeep`, `.DS_Store` and `LICENSE` do not count as contents, so a fresh
|
|
48
|
-
repo or a clone of an empty GitHub repository is a valid target without
|
|
75
|
+
repo or a clone of an empty GitHub repository is a valid target without a question.
|
|
49
76
|
Nothing else is ignored: `.gitignore` and `README.md` both come out of the template,
|
|
50
|
-
and overwriting your copy of either is what
|
|
77
|
+
and overwriting your copy of either is what the last question asks about.
|
|
78
|
+
|
|
79
|
+
## What a composed app looks like
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
my-api/
|
|
83
|
+
src/
|
|
84
|
+
main.ts exports createApp, and serves it when run directly
|
|
85
|
+
app.module.ts the root module, importing every feature
|
|
86
|
+
config.ts one validation function, flat env in and a shaped object out
|
|
87
|
+
users/ one directory per feature you chose
|
|
88
|
+
package.json dev, start, test, typecheck
|
|
89
|
+
bunfig.toml the transform preload
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Three files are generated for the selection and the feature directories are copied.
|
|
93
|
+
`main.ts` is one file rather than two: a test imports `createApp` from it, and the
|
|
94
|
+
`import.meta.main` block at the bottom is what stops that starting a server.
|
|
95
|
+
|
|
96
|
+
**There is no worker entry point, even with queues.** `QueueModule` is given
|
|
97
|
+
`consume: true`, so the container opens the bullmq workers at `onInit` and closes
|
|
98
|
+
them before the connections the handlers use. A handler marked `background: true` is
|
|
99
|
+
forked by bullmq itself into `src/jobs/jobs.processor.ts`.
|
|
51
100
|
|
|
52
101
|
## What it generates
|
|
53
102
|
|
|
@@ -92,9 +141,14 @@ import { scaffold } from '@dunx/create-app';
|
|
|
92
141
|
const { directory, files } = await scaffold({
|
|
93
142
|
target: 'my-api',
|
|
94
143
|
name: '@acme/my-api',
|
|
144
|
+
features: ['users', 'openapi'],
|
|
95
145
|
});
|
|
96
146
|
```
|
|
97
147
|
|
|
148
|
+
This is the scripted path the removed `--with` flag used to be. `features` takes
|
|
149
|
+
the same names the list shows, in any order, and pulls in what they require;
|
|
150
|
+
`FEATURES` exports the set. Omitting it writes the minimal template.
|
|
151
|
+
|
|
98
152
|
`scaffold` throws `ScaffoldError` for anything the caller can fix - an unknown
|
|
99
153
|
template, an unusable package name, a non-empty target without `force` - and lets
|
|
100
154
|
everything else propagate.
|
package/dist/banner.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Style } from './style.js';
|
|
2
|
+
/**
|
|
3
|
+
* The header the questions run under.
|
|
4
|
+
*
|
|
5
|
+
* Printed once, before the first prompt, and only when there is a terminal to
|
|
6
|
+
* print it into: a logo piped into a log file is noise. A terminal too narrow for
|
|
7
|
+
* the logo gets the one-line form rather than a wrapped one, which is what
|
|
8
|
+
* `Bun.stringWidth` is measured against.
|
|
9
|
+
*/
|
|
10
|
+
export declare class Banner {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(style: Style);
|
|
13
|
+
lines(width: number, version: string): readonly string[];
|
|
14
|
+
}
|
|
@@ -221,7 +221,7 @@ var FEATURES = [
|
|
|
221
221
|
{
|
|
222
222
|
name: "jobs",
|
|
223
223
|
source: "jobs",
|
|
224
|
-
summary: "bullmq queues
|
|
224
|
+
summary: "bullmq queues over Bun.RedisClient, background handlers forked.",
|
|
225
225
|
requires: ["images"],
|
|
226
226
|
module: { klass: "JobsModule", from: "./jobs/jobs.module.js" },
|
|
227
227
|
dependencies: ["@dunx/infra", "bullmq", "ioredis", "zod"],
|
|
@@ -354,18 +354,20 @@ var layout = (features) => features.length === 0 ? `- \`src/main.ts\` - the entr
|
|
|
354
354
|
- \`src/greetings.controller.ts\`, \`src/greetings.service.ts\` - one route and its provider
|
|
355
355
|
- \`src/app.test.ts\` - the whole app behind a real server on port 0
|
|
356
356
|
- \`bunfig.toml\` - the preload line constructor injection needs
|
|
357
|
-
` : `- \`src/main.ts\` -
|
|
358
|
-
- \`src/bootstrap.ts\` - builds the app; shared by \`start\` and the tests
|
|
357
|
+
` : `- \`src/main.ts\` - exports \`createApp\`, and serves it when run directly
|
|
359
358
|
- \`src/app.module.ts\` - the root module, importing every feature
|
|
360
359
|
- \`src/config.ts\` - one validation function, flat env in and a shaped object out
|
|
361
360
|
${features.map((feature) => `- \`src/${feature.source}/\` - ${feature.name}`).join(`
|
|
362
361
|
`)}
|
|
363
362
|
- \`bunfig.toml\` - the preload line constructor injection needs
|
|
364
363
|
|
|
365
|
-
\`main.ts\`, \`
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
364
|
+
\`main.ts\`, \`app.module.ts\` and \`config.ts\` were generated for the features chosen
|
|
365
|
+
at scaffold time. Everything else was copied from dunx's \`examples/full\`. The
|
|
366
|
+
\`*.demo.ts\` files are that example's scripted walkthroughs; delete one and its
|
|
367
|
+
\`providers\` entry to drop it.
|
|
368
|
+
|
|
369
|
+
A test imports \`createApp\` from \`./main.js\`; the \`import.meta.main\` block at the
|
|
370
|
+
bottom is what stops that starting a server.
|
|
369
371
|
`;
|
|
370
372
|
var agents = (name, features) => {
|
|
371
373
|
const services = features.filter((feature) => feature.service !== undefined);
|
|
@@ -380,11 +382,16 @@ Notes for an agent working in this application. It is a
|
|
|
380
382
|
|
|
381
383
|
\`\`\`bash
|
|
382
384
|
bun install
|
|
383
|
-
bun run
|
|
385
|
+
bun run dev # http://localhost:3000, restarting on a change
|
|
386
|
+
bun run start
|
|
384
387
|
bun test
|
|
385
388
|
bun run typecheck
|
|
386
|
-
|
|
387
|
-
|
|
389
|
+
\`\`\`${hasJobs ? `
|
|
390
|
+
|
|
391
|
+
**There is no worker command.** \`QueueModule\` is given \`consume: true\`, so the
|
|
392
|
+
container opens the workers at \`onInit\` and closes them before the connections they
|
|
393
|
+
use. A handler marked \`background: true\` is forked by bullmq into
|
|
394
|
+
\`src/jobs/jobs.processor.ts\`, which nobody runs by hand.` : ""}
|
|
388
395
|
|
|
389
396
|
## Layout
|
|
390
397
|
|
|
@@ -450,13 +457,11 @@ var manifest = (features) => {
|
|
|
450
457
|
dependencies[dep] = DUNX.test(dep) ? "__DUNX_VERSION__" : versionOf(dep);
|
|
451
458
|
}
|
|
452
459
|
const scripts = {
|
|
460
|
+
dev: "bun --watch src/main.ts",
|
|
453
461
|
start: "bun src/main.ts",
|
|
454
462
|
test: "bun test",
|
|
455
463
|
typecheck: "tsc --noEmit"
|
|
456
464
|
};
|
|
457
|
-
if (features.some((feature) => feature.name === "jobs")) {
|
|
458
|
-
scripts["worker"] = "bun src/worker.ts";
|
|
459
|
-
}
|
|
460
465
|
return `${JSON.stringify({
|
|
461
466
|
name: "__DUNX_APP_NAME__",
|
|
462
467
|
version: "0.1.0",
|
|
@@ -583,15 +588,17 @@ ${chosen.map(([, group]) => ` ${group.map}`).join(`
|
|
|
583
588
|
`;
|
|
584
589
|
};
|
|
585
590
|
var has = (features, name) => features.some((feature) => feature.name === name);
|
|
586
|
-
var
|
|
591
|
+
var main = (name, features) => {
|
|
587
592
|
const openapi = has(features, "openapi");
|
|
588
593
|
const websockets = has(features, "websockets");
|
|
589
594
|
const http = has(features, "http");
|
|
595
|
+
const health = has(features, "health");
|
|
590
596
|
const documentsAuth = openapi && has(features, "auth");
|
|
591
597
|
const assets = has(features, "assets");
|
|
592
598
|
const throttle = has(features, "throttle");
|
|
593
599
|
const imports = [
|
|
594
600
|
...documentsAuth ? ["import { Auth, betterAuthDocument } from '@dunx/auth';"] : [],
|
|
601
|
+
"import { Logger } from '@dunx/core';",
|
|
595
602
|
`import { ${[
|
|
596
603
|
"HttpFactory",
|
|
597
604
|
...websockets ? ["RedisRelay"] : [],
|
|
@@ -601,12 +608,9 @@ var bootstrap = (name, features) => {
|
|
|
601
608
|
].join(", ")} } from '@dunx/http';`,
|
|
602
609
|
...openapi ? ["import { OpenApiModule } from '@dunx/openapi';"] : [],
|
|
603
610
|
"import { AppModule } from './app.module.js';",
|
|
604
|
-
`import { ${[
|
|
605
|
-
...http ? ["AppConfigService"] : [],
|
|
606
|
-
...websockets ? ["RELAY_CHANNEL"] : []
|
|
607
|
-
].join(", ")} } from './config.js';`,
|
|
611
|
+
`import { ${["AppConfigService", ...websockets ? ["RELAY_CHANNEL"] : []].join(", ")} } from './config.js';`,
|
|
608
612
|
...http ? ["import { RequestTrailMiddleware } from './http/request-trail.js';"] : []
|
|
609
|
-
]
|
|
613
|
+
];
|
|
610
614
|
const root = documentsAuth ? `OpenApiModule.forRootAsync({
|
|
611
615
|
root: AppModule,
|
|
612
616
|
inject: [Auth] as const,
|
|
@@ -644,12 +648,20 @@ var bootstrap = (name, features) => {
|
|
|
644
648
|
] : [],
|
|
645
649
|
...throttle ? ["app.use(ThrottleGuard);"] : []
|
|
646
650
|
];
|
|
651
|
+
const urls = [
|
|
652
|
+
...openapi ? [
|
|
653
|
+
"logger.info(`docs ${new URL('api/docs', url).href}`);",
|
|
654
|
+
"logger.info(`openapi ${new URL('api/openapi.json', url).href}`);"
|
|
655
|
+
] : [],
|
|
656
|
+
...health ? ["logger.info(`health ${new URL('api/health', url).href}`);"] : []
|
|
657
|
+
];
|
|
647
658
|
return `${HEADER(name)}${imports.join(`
|
|
648
659
|
`)}
|
|
649
660
|
|
|
650
661
|
/**
|
|
651
|
-
* One app
|
|
652
|
-
*
|
|
662
|
+
* One app for \`bun run start\`, \`bun run dev\` and the tests, and one file:
|
|
663
|
+
* \`createApp\` is exported for a caller that wants the shape without a server, and
|
|
664
|
+
* the block at the bottom serves it when this file is the entry point.
|
|
653
665
|
*
|
|
654
666
|
* \`create()\` boots the container and discovers routes and gateways; \`listen()\` is
|
|
655
667
|
* what builds the \`Bun.serve\` route table. Everything between the two still gets to
|
|
@@ -670,23 +682,8 @@ ${shaping.map((line) => ` ${line}`).join(`
|
|
|
670
682
|
|
|
671
683
|
return app;
|
|
672
684
|
};
|
|
673
|
-
`;
|
|
674
|
-
};
|
|
675
|
-
var main = (name, features) => {
|
|
676
|
-
const health = has(features, "health");
|
|
677
|
-
const openapi = has(features, "openapi");
|
|
678
|
-
const lines = [
|
|
679
|
-
...openapi ? [
|
|
680
|
-
"logger.info(`docs ${new URL('api/docs', url).href}`);",
|
|
681
|
-
"logger.info(`openapi ${new URL('api/openapi.json', url).href}`);"
|
|
682
|
-
] : [],
|
|
683
|
-
...health ? ["logger.info(`health ${new URL('api/health', url).href}`);"] : []
|
|
684
|
-
];
|
|
685
|
-
return `${HEADER(name)}import { Logger } from '@dunx/core';
|
|
686
|
-
import { createApp } from './bootstrap.js';
|
|
687
|
-
import { AppConfigService } from './config.js';
|
|
688
685
|
|
|
689
|
-
async
|
|
686
|
+
const start = async (): Promise<void> => {
|
|
690
687
|
const app = await createApp();
|
|
691
688
|
app.enableShutdownHooks();
|
|
692
689
|
|
|
@@ -695,31 +692,24 @@ async function bootstrap(): Promise<void> {
|
|
|
695
692
|
const url = await app.listen(config.get('port'));
|
|
696
693
|
|
|
697
694
|
logger.info(\`listening on \${url}\`);
|
|
698
|
-
${
|
|
699
|
-
`)}${
|
|
695
|
+
${urls.map((line) => ` ${line}`).join(`
|
|
696
|
+
`)}${urls.length > 0 ? `
|
|
700
697
|
` : ""}
|
|
701
698
|
// Nothing else to do: the server holds the process open, and the shutdown hooks
|
|
702
699
|
// resolve this once a signal arrives.
|
|
703
700
|
await app.closed;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
bootstrap().catch((error: unknown) => {
|
|
707
|
-
console.error('failed to start', error);
|
|
708
|
-
process.exit(1);
|
|
709
|
-
});
|
|
710
|
-
`;
|
|
711
701
|
};
|
|
712
|
-
var worker = (name) => `${HEADER(name)}import { AppFactory } from '@dunx/core';
|
|
713
|
-
import { AppModule } from './app.module.js';
|
|
714
702
|
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
703
|
+
// False when a test imports this file for \`createApp\` alone, which is what lets one
|
|
704
|
+
// module be both the entry point and the app's definition.
|
|
705
|
+
if (import.meta.main) {
|
|
706
|
+
start().catch((error: unknown) => {
|
|
707
|
+
console.error('failed to start', error);
|
|
708
|
+
process.exit(1);
|
|
709
|
+
});
|
|
710
|
+
}
|
|
722
711
|
`;
|
|
712
|
+
};
|
|
723
713
|
var envExample = (groups) => {
|
|
724
714
|
const lines = groups.flatMap((group) => CONFIG_GROUPS[group]?.env ?? []).map((entry) => `${entry.name}=${entry.value}`);
|
|
725
715
|
return lines.length === 0 ? `# Every variable has a default, so this file is optional.
|
|
@@ -736,6 +726,7 @@ Scaffolded with \`bunx @dunx/create-app\`.
|
|
|
736
726
|
|
|
737
727
|
\`\`\`bash
|
|
738
728
|
bun install
|
|
729
|
+
bun run dev # restarts on a change
|
|
739
730
|
bun run start
|
|
740
731
|
\`\`\`
|
|
741
732
|
|
|
@@ -754,17 +745,19 @@ ${services.map((feature) => `- **${feature.name}** needs ${feature.service}`).jo
|
|
|
754
745
|
|
|
755
746
|
`}## Layout
|
|
756
747
|
|
|
757
|
-
- \`src/main.ts\` -
|
|
758
|
-
- \`src/bootstrap.ts\` - builds the app; shared by \`start\` and the tests
|
|
748
|
+
- \`src/main.ts\` - exports \`createApp\`, and serves it when run directly
|
|
759
749
|
- \`src/app.module.ts\` - the root module, importing every feature
|
|
760
750
|
- \`src/config.ts\` - one validation function, flat env in and a shaped object out
|
|
761
751
|
${features.map((feature) => `- \`src/${feature.source}/\` - ${feature.name}`).join(`
|
|
762
752
|
`)}
|
|
763
753
|
|
|
764
|
-
\`main.ts\`, \`
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
754
|
+
\`main.ts\`, \`app.module.ts\` and \`config.ts\` were generated for the features you
|
|
755
|
+
chose; everything else is copied from dunx's \`examples/full\`, which is run and
|
|
756
|
+
toured in CI on every push. The \`*.demo.ts\` files are that example's scripted
|
|
757
|
+
walkthroughs - delete one and its \`providers\` entry when you do not want it.
|
|
758
|
+
|
|
759
|
+
A test imports \`createApp\` from \`./main.js\` and never starts a server: the
|
|
760
|
+
\`import.meta.main\` block at the bottom of the file is false for an import.
|
|
768
761
|
|
|
769
762
|
## Constructor injection
|
|
770
763
|
|
|
@@ -787,13 +780,14 @@ var IGNORED_WHEN_EMPTY = new Set([
|
|
|
787
780
|
".gitkeep",
|
|
788
781
|
"LICENSE"
|
|
789
782
|
]);
|
|
783
|
+
var blockingEntries = (directory) => existsSync(directory) ? readdirSync(directory).filter((entry) => !IGNORED_WHEN_EMPTY.has(entry)).sort() : [];
|
|
790
784
|
|
|
791
785
|
class ScaffoldError extends Error {
|
|
792
786
|
name = "ScaffoldError";
|
|
793
787
|
}
|
|
794
788
|
var templatesRoot = () => resolve(dirname(fileURLToPath(import.meta.url)), "..", "templates");
|
|
795
789
|
var isValidPackageName = (name) => /^(?:@[a-z0-9-*~][a-z0-9-*._~]*\/)?[a-z0-9-~][a-z0-9-._~]*$/.test(name);
|
|
796
|
-
var
|
|
790
|
+
var packageVersion = async () => {
|
|
797
791
|
const file = Bun.file(join(templatesRoot(), "..", "package.json"));
|
|
798
792
|
const json = await file.json();
|
|
799
793
|
return json.version ?? "0.0.0";
|
|
@@ -801,19 +795,14 @@ var readPackageVersion = async () => {
|
|
|
801
795
|
var fill = (contents, name, version) => contents.replaceAll(VERSION_PLACEHOLDER, version).replaceAll("__DUNX_APP_NAME__", name);
|
|
802
796
|
var generated = (name, features) => {
|
|
803
797
|
const groups = configGroupsFor(features);
|
|
804
|
-
|
|
798
|
+
return {
|
|
805
799
|
"package.json": manifest(features),
|
|
806
800
|
"README.md": readme(name, features),
|
|
807
801
|
".env.example": envExample(groups),
|
|
808
802
|
"src/main.ts": main(name, features),
|
|
809
|
-
"src/bootstrap.ts": bootstrap(name, features),
|
|
810
803
|
"src/app.module.ts": appModule(name, features),
|
|
811
804
|
"src/config.ts": config(name, groups)
|
|
812
805
|
};
|
|
813
|
-
if (features.some((feature) => feature.name === "jobs")) {
|
|
814
|
-
files["src/worker.ts"] = worker(name);
|
|
815
|
-
}
|
|
816
|
-
return files;
|
|
817
806
|
};
|
|
818
807
|
var scaffold = async (options) => {
|
|
819
808
|
const template = options.template ?? "minimal";
|
|
@@ -833,15 +822,15 @@ var scaffold = async (options) => {
|
|
|
833
822
|
if (!isValidPackageName(name)) {
|
|
834
823
|
throw new ScaffoldError(`"${name}" is not a usable package name. Pass --name to choose one.`);
|
|
835
824
|
}
|
|
836
|
-
if (
|
|
837
|
-
const blocking =
|
|
825
|
+
if (options.force !== true) {
|
|
826
|
+
const blocking = blockingEntries(directory);
|
|
838
827
|
if (blocking.length > 0) {
|
|
839
|
-
const shown = blocking.
|
|
828
|
+
const shown = blocking.slice(0, 3).join(", ");
|
|
840
829
|
const rest = blocking.length > 3 ? `, +${blocking.length - 3} more` : "";
|
|
841
830
|
throw new ScaffoldError(`${directory} is not empty (${shown}${rest}). ` + `Pass --force to write into it anyway.`);
|
|
842
831
|
}
|
|
843
832
|
}
|
|
844
|
-
const version = options.version ?? `^${await
|
|
833
|
+
const version = options.version ?? `^${await packageVersion()}`;
|
|
845
834
|
const written = [];
|
|
846
835
|
const copyTree = async (from, into) => {
|
|
847
836
|
for await (const relative of new Glob("**/*").scan({
|
|
@@ -903,4 +892,4 @@ var scaffold = async (options) => {
|
|
|
903
892
|
};
|
|
904
893
|
};
|
|
905
894
|
|
|
906
|
-
export { FEATURES, featureNames, impliedBy, TEMPLATES, VERSION_PLACEHOLDER, ScaffoldError, scaffold };
|
|
895
|
+
export { FEATURES, featureNames, resolveFeatures, impliedBy, TEMPLATES, VERSION_PLACEHOLDER, blockingEntries, ScaffoldError, isValidPackageName, packageVersion, scaffold };
|