create-bestax 4.2.17 → 4.2.19
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 +13 -5
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +10 -2
- package/dist/display.d.ts +9 -0
- package/dist/display.d.ts.map +1 -1
- package/dist/display.js +19 -1
- package/dist/file-system.d.ts +21 -4
- package/dist/file-system.d.ts.map +1 -1
- package/dist/file-system.js +52 -6
- package/dist/library-range.d.ts +16 -0
- package/dist/library-range.d.ts.map +1 -0
- package/dist/library-range.js +40 -0
- package/dist/project-creator.d.ts +16 -1
- package/dist/project-creator.d.ts.map +1 -1
- package/dist/project-creator.js +39 -7
- package/dist/prompts.d.ts +1 -0
- package/dist/prompts.d.ts.map +1 -1
- package/dist/prompts.js +3 -2
- package/dist/validators.d.ts +8 -0
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +18 -1
- package/package.json +8 -8
- package/templates/skills/bestax-custom-component/references/component-catalog.md +5 -1
- package/templates/skills/bestax-form/SKILL.md +45 -16
- package/templates/skills/bestax-form/references/api.md +13 -8
- package/templates/skills/bestax-form/references/patterns.md +75 -3
- package/templates/skills/bestax-layout-scaffold/SKILL.md +34 -6
- package/templates/skills/bestax-layout-scaffold/references/archetypes.md +2 -2
- package/templates/skills/bestax-layout-scaffold/references/layout-components.md +52 -5
- package/templates/skills/bestax-migrate/SKILL.md +34 -5
- package/templates/skills/bestax-migrate/references/bloomer/component-map.md +1 -1
- package/templates/skills/bestax-migrate/references/bloomer/prop-map.md +12 -9
- package/templates/skills/bestax-migrate/references/bulma-classes/component-map.md +153 -105
- package/templates/skills/bestax-migrate/references/bulma-classes/prop-map.md +48 -17
- package/templates/skills/bestax-migrate/references/bulma-classes/unmappables.md +501 -16
- package/templates/skills/bestax-migrate/references/css-migration.md +12 -1
- package/templates/skills/bestax-migrate/references/rbx/prop-map.md +23 -17
- package/templates/skills/bestax-migrate/references/react-bulma-components/prop-map.md +27 -22
- package/templates/skills/bestax-migrate/references/react-bulma-components/unmappables.md +5 -5
- package/templates/skills/bestax-optimize/references/modular-build.md +57 -15
- package/templates/skills/bestax-theming/SKILL.md +19 -8
- package/templates/skills/bestax-theming/examples/dark-mode.tsx +99 -17
- package/templates/skills/bestax-theming/examples/theme-config.tsx +13 -8
- package/templates/skills/bestax-theming/references/css-variables.md +231 -18
- package/templates/skills/bestax-theming/references/themeable-components.md +4 -0
- package/templates/vite/package.json +1 -1
- package/templates/vite/src/main.jsx +1 -1
- package/templates/vite-ts/package.json +1 -1
- package/templates/vite-ts/src/main.tsx +1 -1
package/README.md
CHANGED
|
@@ -41,8 +41,8 @@ npm create bestax@latest
|
|
|
41
41
|
|
|
42
42
|
You'll be asked to:
|
|
43
43
|
|
|
44
|
-
1. Enter a project name (and, if that directory already holds files, confirm removing
|
|
45
|
-
nothing is removed until you have answered the last question)
|
|
44
|
+
1. Enter a project name or path (and, if that directory already holds files, confirm removing
|
|
45
|
+
them; nothing is removed until you have answered the last question)
|
|
46
46
|
2. Select a framework (Vite or Vite + TypeScript)
|
|
47
47
|
3. Choose a Bulma CSS flavor (the `--bulma` options below)
|
|
48
48
|
4. Select an icon library (Font Awesome, Material Icons, etc.)
|
|
@@ -62,8 +62,12 @@ with or without it.
|
|
|
62
62
|
|
|
63
63
|
**Arguments:**
|
|
64
64
|
|
|
65
|
-
- `[project-directory]` - Optional project directory name
|
|
66
|
-
|
|
65
|
+
- `[project-directory]` - Optional project directory: a name such as `my-app`, or a path,
|
|
66
|
+
absolute or relative, such as `../apps/my-app`, whose missing folders are created. Its last
|
|
67
|
+
part is the project name, held to the same rules as a bare name. It becomes the page title as
|
|
68
|
+
typed, and the `package.json` name lower-cased the way `npm init` does and without a leading
|
|
69
|
+
`_`, since npm rejects both. A path ending in `.` or `..` is refused, like a bare `.` or `..`,
|
|
70
|
+
so name the directory you want.
|
|
67
71
|
|
|
68
72
|
**Options:**
|
|
69
73
|
|
|
@@ -99,7 +103,8 @@ with or without it.
|
|
|
99
103
|
directory under `-y` alone, so a script that relied on that now needs `--overwrite`.
|
|
100
104
|
|
|
101
105
|
- `--overwrite` - Remove existing files if the target directory is not empty, without asking.
|
|
102
|
-
Every other flag is checked first, so a typo stops the run before anything is deleted.
|
|
106
|
+
Every other flag is checked first, so a typo stops the run before anything is deleted. It
|
|
107
|
+
never empties the current directory or a folder that holds it, however the path is written.
|
|
103
108
|
|
|
104
109
|
**Example:**
|
|
105
110
|
|
|
@@ -110,6 +115,9 @@ npm create bestax@latest my-app -- -t vite-ts -i fontawesome -b complete
|
|
|
110
115
|
# Use all defaults (skip prompts)
|
|
111
116
|
npm create bestax@latest my-app -- -y
|
|
112
117
|
|
|
118
|
+
# Scaffold into a path; the package is named my-app
|
|
119
|
+
npm create bestax@latest ../apps/my-app -- -y
|
|
120
|
+
|
|
113
121
|
# The same with bun
|
|
114
122
|
bun create bestax@latest my-app -- -y
|
|
115
123
|
```
|
package/dist/cli.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EACL,cAAc,EACd,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,cAAc,IAAI,cAAc,EAChC,iBAAiB,IAAI,iBAAiB,EACtC,iBAAiB,IAAI,iBAAiB,GACvC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,wBAAsB,cAAc,CAClC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,qBAAqB,CAAC,CAGhC;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,aAAa,CACjC,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,IAAI,CAAC,CAGf;AAcD,wBAAgB,SAAS,IAAI,OAAO,
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAIpC,OAAO,EACL,cAAc,EACd,KAAK,UAAU,EACf,KAAK,qBAAqB,EAC3B,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gBAAgB,CAAC;AAC1E,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AACtE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGzE,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC7D,OAAO,EACL,iBAAiB,EACjB,wBAAwB,EACxB,cAAc,IAAI,cAAc,EAChC,iBAAiB,IAAI,iBAAiB,EACtC,iBAAiB,IAAI,iBAAiB,GACvC,MAAM,cAAc,CAAC;AACtB,OAAO,EACL,oBAAoB,EACpB,gBAAgB,EAChB,aAAa,EACb,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAG1B,wBAAsB,cAAc,CAClC,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAGxB;AAED,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,qBAAqB,CAAC,CAGhC;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGxD;AAED,wBAAsB,YAAY,CAChC,YAAY,EAAE,MAAM,EACpB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,aAAa,CACjC,UAAU,CAAC,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,IAAI,CAAC,CAGf;AAcD,wBAAgB,SAAS,IAAI,OAAO,CAyCnC;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAE1E;AAED;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,EAAE,CAKxE;AAED,wBAAsB,MAAM,CAC1B,IAAI,GAAE,SAAS,MAAM,EAAiB,GACrC,OAAO,CAAC,IAAI,CAAC,CAGf"}
|
package/dist/cli.js
CHANGED
|
@@ -53,7 +53,7 @@ export function createCLI() {
|
|
|
53
53
|
.name('create-bestax')
|
|
54
54
|
.description('Create a new bestax-bulma project')
|
|
55
55
|
.version(getPackageVersion())
|
|
56
|
-
.argument('[project-directory]', 'project directory to create')
|
|
56
|
+
.argument('[project-directory]', 'project directory to create, a name or a path (its last part names the package)')
|
|
57
57
|
.option('-t, --template <template>', 'template to use (vite, vite-ts)')
|
|
58
58
|
.option('-b, --bulma <flavor>', 'Bulma CSS flavor (complete, prefixed, no-helpers, no-helpers-prefixed, no-dark-mode)')
|
|
59
59
|
.option('-i, --icon <library>', 'icon library (none, fontawesome, mdi, ionicons, material-icons, material-symbols)')
|
package/dist/constants.d.ts
CHANGED
|
@@ -15,11 +15,13 @@ export declare const MESSAGES: {
|
|
|
15
15
|
readonly PROJECT_NAME_INVALID_CHARS: "Project name can only contain letters, numbers, dots, dashes and underscores";
|
|
16
16
|
readonly PROJECT_NAME_DOT: "Project name cannot start with a dot (names like \".\" or \"..\" would scaffold outside a new directory) — pass a directory name";
|
|
17
17
|
readonly PROJECT_NAME_NO_PACKAGE_NAME: "Project name must contain a letter, number or dash (npm package names cannot start with \"_\" or \".\")";
|
|
18
|
+
readonly PROJECT_NAME_FROM_PATH: (message: string, name: string) => string;
|
|
18
19
|
readonly PACKAGE_NAME_NORMALIZED: (name: string) => string;
|
|
19
20
|
readonly OPERATION_CANCELLED: "✖ Operation cancelled";
|
|
20
21
|
readonly NO_TTY: string;
|
|
21
22
|
readonly DIRECTORY_NOT_EMPTY: (dir: string, entries: string[]) => string;
|
|
22
23
|
readonly NOT_A_DIRECTORY: (dir: string) => string;
|
|
24
|
+
readonly DIRECTORY_HOLDS_CWD: (dir: string) => string;
|
|
23
25
|
readonly DIRECTORY_NOT_EMPTY_REFUSED: (dir: string, entries: string[]) => string;
|
|
24
26
|
readonly EMPTYING_DIRECTORY: (dir: string) => string;
|
|
25
27
|
readonly CREATING_PROJECT: (path: string) => string;
|
package/dist/constants.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,SAAS,EAAE,QAAQ,EAG/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AACpD,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAsB,CAAC;AAGtD,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAOpC,eAAO,MAAM,QAAQ
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,SAAS,EAAE,QAAQ,EAG/B,CAAC;AAEF,eAAO,MAAM,oBAAoB,kBAAkB,CAAC;AACpD,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAC3C,eAAO,MAAM,kBAAkB,QAAsB,CAAC;AAGtD,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAOpC,eAAO,MAAM,QAAQ;;;;;;+CASe,MAAM,QAAQ,MAAM;6CAEtB,MAAM;;;wCAQX,MAAM,WAAW,MAAM,EAAE;oCAE7B,MAAM;wCAIF,MAAM;gDAKE,MAAM,WAAW,MAAM,EAAE;uCAIlC,MAAM;sCACP,MAAM;4CAEA,MAAM;;;;;wCAMV,MAAM,mBAAmB,MAAM;;;;;CAclD,CAAC;AASX,eAAO,MAAM,WAAW,QAed,CAAC;AAEX,eAAO,MAAM,OAAO;;;;;;;CAQV,CAAC;AAOX,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAK9D,CAAC;AAKF,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,WAAW,EAAE,MAAM,CAAC;CACrB;AAkCD,eAAO,MAAM,SAAS,GACpB,aAAa,MAAM,EACnB,8BAA8B,eAAe,KAC5C,MA8HF,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IAIrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,eAAe,CAAC,EAAE,MAAM,CAAC;IAGzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,eAAO,MAAM,cAAc,EAAE,WAAW,EA8DvC,CAAC;AAEF,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,OAAO,KAAK,CAAC,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,OAAO,CAAC;IAGtB,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,eAAO,MAAM,aAAa,EAAE,WAAW,EA8CtC,CAAC;AAQF,eAAO,MAAM,iBAAiB,QAEiC,CAAC;AAEhE,MAAM,WAAW,YAAY;IAE3B,IAAI,EAAE,MAAM,CAAC;IAEb,SAAS,EAAE,MAAM,CAAC;IAElB,WAAW,EAAE,MAAM,CAAC;CACrB;AAOD,eAAO,MAAM,0BAA0B,EAAE,YAAY,EAgBpD,CAAC;AAEF,eAAO,MAAM,kBAAkB,QAMjB,CAAC"}
|
package/dist/constants.js
CHANGED
|
@@ -18,6 +18,7 @@ export const MESSAGES = {
|
|
|
18
18
|
PROJECT_NAME_INVALID_CHARS: 'Project name can only contain letters, numbers, dots, dashes and underscores',
|
|
19
19
|
PROJECT_NAME_DOT: 'Project name cannot start with a dot (names like "." or ".." would scaffold outside a new directory) — pass a directory name',
|
|
20
20
|
PROJECT_NAME_NO_PACKAGE_NAME: 'Project name must contain a letter, number or dash (npm package names cannot start with "_" or ".")',
|
|
21
|
+
PROJECT_NAME_FROM_PATH: (message, name) => `${message} (the project name is "${name}", the last part of the path)`,
|
|
21
22
|
PACKAGE_NAME_NORMALIZED: (name) => ` package.json name: ${name} (npm package names are lower-case and cannot start with "_")`,
|
|
22
23
|
OPERATION_CANCELLED: '✖ Operation cancelled',
|
|
23
24
|
NO_TTY: 'No interactive terminal detected — cannot prompt for input.\n' +
|
|
@@ -25,7 +26,11 @@ export const MESSAGES = {
|
|
|
25
26
|
' npm create bestax@latest my-app -- -t vite-ts -b complete -i none -y\n' +
|
|
26
27
|
'Run with --help to see all options.',
|
|
27
28
|
DIRECTORY_NOT_EMPTY: (dir, entries) => `Directory ${chalk.yellow(dir)} is not empty (${listEntries(entries)}). Remove existing files and continue?`,
|
|
28
|
-
NOT_A_DIRECTORY: (dir) => `${dir} already exists and is not a directory, so nothing was written. Choose another project name.`,
|
|
29
|
+
NOT_A_DIRECTORY: (dir) => `${dir} already exists and is not a directory, so nothing was written. Choose another project name or path.`,
|
|
30
|
+
// #310: the working directory, or a folder holding it, under another
|
|
31
|
+
// spelling than "." or "..", such as "../<its name>" or its absolute path.
|
|
32
|
+
DIRECTORY_HOLDS_CWD: (dir) => `Directory ${dir} is or holds the current directory, so it is never emptied, even with --overwrite, and nothing was written.\n` +
|
|
33
|
+
' Choose a new or empty directory.',
|
|
29
34
|
// Shown instead of the question above when it cannot be asked: under -y, or
|
|
30
35
|
// without a terminal. -y never answers it (#945), so this must not suggest -y.
|
|
31
36
|
DIRECTORY_NOT_EMPTY_REFUSED: (dir, entries) => `Directory ${dir} is not empty (${listEntries(entries)}), so nothing was written.\n` +
|
|
@@ -343,8 +348,11 @@ export const BULMA_FLAVORS = [
|
|
|
343
348
|
// Sits above the stylesheet import in the templates' entry files, and the
|
|
344
349
|
// scaffolder writes it back when it swaps in a flavor's stylesheet, so the
|
|
345
350
|
// reason for the import order survives. The no-helpers classes depend on it.
|
|
351
|
+
// It names src/index.css, the app's global stylesheet, rather than the starter
|
|
352
|
+
// page's src/App.css, which loads after bestax's too but goes when a port
|
|
353
|
+
// replaces the page (#1240).
|
|
346
354
|
export const CSS_ORDER_COMMENT = "// Bestax's stylesheet loads before the app's own CSS, so a named class in\n" +
|
|
347
|
-
'// src/
|
|
355
|
+
'// src/index.css wins over a Bulma rule of the same weight.';
|
|
348
356
|
// Every helper prop the starter App uses, with the named class that stands in
|
|
349
357
|
// for it under a `noHelpers` flavor, where the prop would render nothing. The
|
|
350
358
|
// scaffolder swaps each prop for its class and writes the rules into
|
package/dist/display.d.ts
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
export declare function displayHeader(): void;
|
|
2
|
+
/**
|
|
3
|
+
* value written so that pasting it into a shell gives back exactly value. The
|
|
4
|
+
* project name is held to safe characters, but a parent folder in a path can
|
|
5
|
+
* hold anything (#1154). POSIX shells get single quotes, which keep spaces, $
|
|
6
|
+
* and backticks literal, with each single quote inside written '\''. Windows
|
|
7
|
+
* gets double quotes, the quoting cmd and PowerShell share, since cmd has no
|
|
8
|
+
* single quotes.
|
|
9
|
+
*/
|
|
10
|
+
export declare function quoteShellArgument(value: string, platform?: typeof process.platform): string;
|
|
2
11
|
export declare function displaySuccess(targetDir: string): void;
|
|
3
12
|
export declare function displayError(message: string): void;
|
|
4
13
|
export declare function displayInfo(message: string): void;
|
package/dist/display.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"AAKA,wBAAgB,aAAa,IAAI,IAAI,CAMpC;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAiCtD;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|
|
1
|
+
{"version":3,"file":"display.d.ts","sourceRoot":"","sources":["../src/display.ts"],"names":[],"mappings":"AAKA,wBAAgB,aAAa,IAAI,IAAI,CAMpC;AAOD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,OAAO,OAAO,CAAC,QAA2B,GACnD,MAAM,CAKR;AAED,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAiCtD;AAED,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAElD;AAED,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAEjD;AAED,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
|
package/dist/display.js
CHANGED
|
@@ -9,6 +9,24 @@ export function displayHeader() {
|
|
|
9
9
|
console.log(chalk.cyan('━'.repeat(50)));
|
|
10
10
|
console.log();
|
|
11
11
|
}
|
|
12
|
+
// Characters no shell reads specially, so a path made only of them pastes as
|
|
13
|
+
// it is. Windows adds its backslash separator.
|
|
14
|
+
const SHELL_SAFE = /^[\w@%+=:,./-]+$/;
|
|
15
|
+
const WINDOWS_SHELL_SAFE = /^[\w@%+=:,./\\-]+$/;
|
|
16
|
+
/**
|
|
17
|
+
* value written so that pasting it into a shell gives back exactly value. The
|
|
18
|
+
* project name is held to safe characters, but a parent folder in a path can
|
|
19
|
+
* hold anything (#1154). POSIX shells get single quotes, which keep spaces, $
|
|
20
|
+
* and backticks literal, with each single quote inside written '\''. Windows
|
|
21
|
+
* gets double quotes, the quoting cmd and PowerShell share, since cmd has no
|
|
22
|
+
* single quotes.
|
|
23
|
+
*/
|
|
24
|
+
export function quoteShellArgument(value, platform = process.platform) {
|
|
25
|
+
if (platform === 'win32') {
|
|
26
|
+
return WINDOWS_SHELL_SAFE.test(value) ? value : `"${value}"`;
|
|
27
|
+
}
|
|
28
|
+
return SHELL_SAFE.test(value) ? value : `'${value.replace(/'/g, "'\\''")}'`;
|
|
29
|
+
}
|
|
12
30
|
export function displaySuccess(targetDir) {
|
|
13
31
|
console.log();
|
|
14
32
|
console.log(chalk.green(MESSAGES.PROJECT_CREATED));
|
|
@@ -18,7 +36,7 @@ export function displaySuccess(targetDir) {
|
|
|
18
36
|
// Mirror the package manager that invoked the CLI — `<pm> install` and
|
|
19
37
|
// `<pm> run dev` are valid for all four supported PMs.
|
|
20
38
|
const pm = detectPackageManager();
|
|
21
|
-
console.log(chalk.cyan(` cd ${targetDir}`));
|
|
39
|
+
console.log(chalk.cyan(` cd ${quoteShellArgument(targetDir)}`));
|
|
22
40
|
console.log(chalk.cyan(` ${pm} install`));
|
|
23
41
|
console.log(chalk.cyan(` ${pm} run dev`));
|
|
24
42
|
console.log();
|
package/dist/file-system.d.ts
CHANGED
|
@@ -1,10 +1,23 @@
|
|
|
1
1
|
export declare function checkDirectoryExists(targetPath: string): Promise<boolean>;
|
|
2
2
|
export declare function isDirectoryEmpty(targetPath: string): Promise<boolean>;
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* What stands in the way of creating targetPath as a directory: the nearest
|
|
5
|
+
* of it and its parents that exists, when that is not a directory. That is a
|
|
6
|
+
* file at the target itself, or a file where a parent folder should be, as in
|
|
7
|
+
* `notes.txt/login` (#1154). null when the nearest existing entry is a
|
|
8
|
+
* directory. listDirectoryEntries cannot read a file, so callers check first.
|
|
9
|
+
*
|
|
10
|
+
* It never throws. throwIfNoEntry covers a missing entry, and a path under a
|
|
11
|
+
* file fails with ENOTDIR on some Node versions, so any failed stat moves on
|
|
12
|
+
* to the parent, where the file that caused it is found.
|
|
6
13
|
*/
|
|
7
|
-
export declare function
|
|
14
|
+
export declare function findNonDirectoryInPath(targetPath: string): Promise<string | null>;
|
|
15
|
+
/**
|
|
16
|
+
* targetPath with its symbolic links resolved, so two spellings of one
|
|
17
|
+
* directory compare equal (macOS's /tmp is /private/tmp). Returned resolved
|
|
18
|
+
* but otherwise as given when that fails, as it does for a missing path.
|
|
19
|
+
*/
|
|
20
|
+
export declare function realPath(targetPath: string): Promise<string>;
|
|
8
21
|
/** A directory's top-level entries, sorted; none when it does not exist. */
|
|
9
22
|
export declare function listDirectoryEntries(targetPath: string): Promise<string[]>;
|
|
10
23
|
export declare function emptyDirectory(targetPath: string): Promise<void>;
|
|
@@ -12,5 +25,9 @@ export declare function ensureDirectory(targetPath: string): Promise<void>;
|
|
|
12
25
|
export declare function copyDirectory(source: string, destination: string, renames?: Record<string, string>): Promise<void>;
|
|
13
26
|
export declare function readJsonFile<T = unknown>(filePath: string): Promise<T>;
|
|
14
27
|
export declare function writeJsonFile(filePath: string, data: unknown, spaces?: number): Promise<void>;
|
|
15
|
-
|
|
28
|
+
/**
|
|
29
|
+
* Names the copied app's package and sets the given dependencies' ranges. One
|
|
30
|
+
* the template already lists keeps its place among the others.
|
|
31
|
+
*/
|
|
32
|
+
export declare function updatePackageJson(targetPath: string, projectName: string, dependencies?: Record<string, string>): Promise<void>;
|
|
16
33
|
//# sourceMappingURL=file-system.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"AAIA,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAM3E;AAED
|
|
1
|
+
{"version":3,"file":"file-system.d.ts","sourceRoot":"","sources":["../src/file-system.ts"],"names":[],"mappings":"AAIA,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAElB;AAED,wBAAsB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAM3E;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,sBAAsB,CAC1C,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAkBxB;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMlE;AAED,4EAA4E;AAC5E,wBAAsB,oBAAoB,CACxC,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,EAAE,CAAC,CAKnB;AAED,wBAAsB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEtE;AAED,wBAAsB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEvE;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAE5E;AAED,wBAAsB,aAAa,CACjC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,EACb,MAAM,SAAI,GACT,OAAO,CAAC,IAAI,CAAC,CAEf;AAED;;;GAGG;AACH,wBAAsB,iBAAiB,CACrC,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,GACxC,OAAO,CAAC,IAAI,CAAC,CAcf"}
|
package/dist/file-system.js
CHANGED
|
@@ -12,12 +12,48 @@ export async function isDirectoryEmpty(targetPath) {
|
|
|
12
12
|
return files.length === 0;
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
|
-
*
|
|
16
|
-
*
|
|
15
|
+
* What stands in the way of creating targetPath as a directory: the nearest
|
|
16
|
+
* of it and its parents that exists, when that is not a directory. That is a
|
|
17
|
+
* file at the target itself, or a file where a parent folder should be, as in
|
|
18
|
+
* `notes.txt/login` (#1154). null when the nearest existing entry is a
|
|
19
|
+
* directory. listDirectoryEntries cannot read a file, so callers check first.
|
|
20
|
+
*
|
|
21
|
+
* It never throws. throwIfNoEntry covers a missing entry, and a path under a
|
|
22
|
+
* file fails with ENOTDIR on some Node versions, so any failed stat moves on
|
|
23
|
+
* to the parent, where the file that caused it is found.
|
|
17
24
|
*/
|
|
18
|
-
export async function
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
export async function findNonDirectoryInPath(targetPath) {
|
|
26
|
+
let current = path.resolve(targetPath);
|
|
27
|
+
for (;;) {
|
|
28
|
+
let stat;
|
|
29
|
+
try {
|
|
30
|
+
stat = fs.statSync(current, { throwIfNoEntry: false });
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
stat = undefined;
|
|
34
|
+
}
|
|
35
|
+
if (stat) {
|
|
36
|
+
return stat.isDirectory() ? null : current;
|
|
37
|
+
}
|
|
38
|
+
const parent = path.dirname(current);
|
|
39
|
+
if (parent === current) {
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
current = parent;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* targetPath with its symbolic links resolved, so two spellings of one
|
|
47
|
+
* directory compare equal (macOS's /tmp is /private/tmp). Returned resolved
|
|
48
|
+
* but otherwise as given when that fails, as it does for a missing path.
|
|
49
|
+
*/
|
|
50
|
+
export async function realPath(targetPath) {
|
|
51
|
+
try {
|
|
52
|
+
return fs.realpathSync(targetPath);
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return path.resolve(targetPath);
|
|
56
|
+
}
|
|
21
57
|
}
|
|
22
58
|
/** A directory's top-level entries, sorted; none when it does not exist. */
|
|
23
59
|
export async function listDirectoryEntries(targetPath) {
|
|
@@ -54,11 +90,21 @@ export async function readJsonFile(filePath) {
|
|
|
54
90
|
export async function writeJsonFile(filePath, data, spaces = 2) {
|
|
55
91
|
await fs.writeJson(filePath, data, { spaces });
|
|
56
92
|
}
|
|
57
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Names the copied app's package and sets the given dependencies' ranges. One
|
|
95
|
+
* the template already lists keeps its place among the others.
|
|
96
|
+
*/
|
|
97
|
+
export async function updatePackageJson(targetPath, projectName, dependencies = {}) {
|
|
58
98
|
const packageJsonPath = path.join(targetPath, 'package.json');
|
|
59
99
|
if (fs.existsSync(packageJsonPath)) {
|
|
60
100
|
const packageJson = await fs.readJson(packageJsonPath);
|
|
61
101
|
packageJson.name = projectName;
|
|
102
|
+
if (Object.keys(dependencies).length > 0) {
|
|
103
|
+
packageJson.dependencies = {
|
|
104
|
+
...packageJson.dependencies,
|
|
105
|
+
...dependencies,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
62
108
|
await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
|
|
63
109
|
}
|
|
64
110
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/** The library every template is built on. */
|
|
2
|
+
export declare const LIBRARY = "@allxsmith/bestax-bulma";
|
|
3
|
+
/**
|
|
4
|
+
* The range a scaffolded app gives @allxsmith/bestax-bulma: the one this
|
|
5
|
+
* CLI's own manifest declares, so the app is floored at the release the CLI
|
|
6
|
+
* was built with (#1240). The manifest spells it `workspace:^`, which
|
|
7
|
+
* `pnpm publish` rewrites to a caret on the library's version in the checkout
|
|
8
|
+
* it packs, and CI releases bulma-ui first in the same job. A published CLI
|
|
9
|
+
* therefore reads a plain range, the one `npm create bestax` installed the
|
|
10
|
+
* library from beside it. Run from this repo, the protocol is still there,
|
|
11
|
+
* and it is resolved the way `pnpm publish` resolves it, from the version of
|
|
12
|
+
* the library the workspace links, so a scaffold made with a checkout's build
|
|
13
|
+
* gets that checkout's release.
|
|
14
|
+
*/
|
|
15
|
+
export declare function libraryRange(packageRoot?: string): string;
|
|
16
|
+
//# sourceMappingURL=library-range.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"library-range.d.ts","sourceRoot":"","sources":["../src/library-range.ts"],"names":[],"mappings":"AAIA,8CAA8C;AAC9C,eAAO,MAAM,OAAO,4BAA4B,CAAC;AAqBjD;;;;;;;;;;;GAWG;AACH,wBAAgB,YAAY,CAAC,WAAW,GAAE,MAAqB,GAAG,MAAM,CAkBvE"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { readFileSync } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
/** The library every template is built on. */
|
|
5
|
+
export const LIBRARY = '@allxsmith/bestax-bulma';
|
|
6
|
+
const WORKSPACE = 'workspace:';
|
|
7
|
+
// dist/ and src/ both sit directly under the package root.
|
|
8
|
+
const PACKAGE_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
9
|
+
function readManifest(dir) {
|
|
10
|
+
return JSON.parse(readFileSync(path.join(dir, 'package.json'), 'utf8'));
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The range a scaffolded app gives @allxsmith/bestax-bulma: the one this
|
|
14
|
+
* CLI's own manifest declares, so the app is floored at the release the CLI
|
|
15
|
+
* was built with (#1240). The manifest spells it `workspace:^`, which
|
|
16
|
+
* `pnpm publish` rewrites to a caret on the library's version in the checkout
|
|
17
|
+
* it packs, and CI releases bulma-ui first in the same job. A published CLI
|
|
18
|
+
* therefore reads a plain range, the one `npm create bestax` installed the
|
|
19
|
+
* library from beside it. Run from this repo, the protocol is still there,
|
|
20
|
+
* and it is resolved the way `pnpm publish` resolves it, from the version of
|
|
21
|
+
* the library the workspace links, so a scaffold made with a checkout's build
|
|
22
|
+
* gets that checkout's release.
|
|
23
|
+
*/
|
|
24
|
+
export function libraryRange(packageRoot = PACKAGE_ROOT) {
|
|
25
|
+
const declared = readManifest(packageRoot).dependencies?.[LIBRARY];
|
|
26
|
+
if (declared === undefined) {
|
|
27
|
+
throw new Error(`${path.join(packageRoot, 'package.json')} declares no ${LIBRARY} dependency.`);
|
|
28
|
+
}
|
|
29
|
+
if (!declared.startsWith(WORKSPACE))
|
|
30
|
+
return declared;
|
|
31
|
+
const { version } = readManifest(path.join(packageRoot, 'node_modules', LIBRARY));
|
|
32
|
+
// What pnpm publish writes for each form: `*` the version itself, `^` and
|
|
33
|
+
// `~` that operator on it, and a range as written.
|
|
34
|
+
const spec = declared.slice(WORKSPACE.length);
|
|
35
|
+
if (spec === '*')
|
|
36
|
+
return version;
|
|
37
|
+
if (spec === '^' || spec === '~')
|
|
38
|
+
return `${spec}${version}`;
|
|
39
|
+
return spec;
|
|
40
|
+
}
|
|
@@ -24,6 +24,10 @@ export type ExistingDirectoryPlan = 'empty' | 'overwrite' | 'stop';
|
|
|
24
24
|
export declare class ProjectCreator {
|
|
25
25
|
private templatesDir;
|
|
26
26
|
constructor(templatesDir?: string);
|
|
27
|
+
/**
|
|
28
|
+
* The project directory, as typed or answered: a name or a path, absolute
|
|
29
|
+
* or relative, whose last segment must be a valid project name (#1154).
|
|
30
|
+
*/
|
|
27
31
|
getProjectName(projectDir?: string): Promise<string | null>;
|
|
28
32
|
/**
|
|
29
33
|
* Decides what happens to an existing target directory without touching it:
|
|
@@ -31,9 +35,20 @@ export declare class ProjectCreator {
|
|
|
31
35
|
* question leaves the user's files where they were (#945). Only --overwrite
|
|
32
36
|
* or a yes at the prompt removes files. -y never does, and neither -y nor a
|
|
33
37
|
* missing terminal can ask, so both stop at a non-empty directory instead.
|
|
34
|
-
*
|
|
38
|
+
* Whatever the flags say, the run stops at a file on the target path or in
|
|
39
|
+
* place of one of its parents, and before emptying the working directory or
|
|
40
|
+
* one that holds it (#310), however the path spells it. create() calls this
|
|
41
|
+
* straight after the project name, so neither waits for the other prompts.
|
|
35
42
|
*/
|
|
36
43
|
checkExistingDirectory(targetPath: string, targetDir: string, options?: CLIOptions): Promise<ExistingDirectoryPlan>;
|
|
44
|
+
/**
|
|
45
|
+
* Whether targetPath is the working directory or one of its ancestors, with
|
|
46
|
+
* symbolic links resolved on both sides. Emptying either deletes the folder
|
|
47
|
+
* the user is standing in, which the leading-dot rule refuses for "." and
|
|
48
|
+
* "..", and a path such as "../<its name>" or its absolute spelling reaches
|
|
49
|
+
* the same folder (#310, #1154).
|
|
50
|
+
*/
|
|
51
|
+
private holdsWorkingDirectory;
|
|
37
52
|
/**
|
|
38
53
|
* Every flag's value is checked here, before any prompt runs or anything on
|
|
39
54
|
* disk changes, so a typo can never cost the user an emptied directory (#945).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"project-creator.d.ts","sourceRoot":"","sources":["../src/project-creator.ts"],"names":[],"mappings":"AA+BA,OAAO,EAWL,KAAK,eAAe,EACrB,MAAM,gBAAgB,CAAC;AAaxB,MAAM,WAAW,aAAa;IAC5B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;GAIG;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,WAAW,GAAG,MAAM,CAAC;AAEnE,qBAAa,cAAc;IACzB,OAAO,CAAC,YAAY,CAAS;gBAEjB,YAAY,CAAC,EAAE,MAAM;IAIjC;;;OAGG;IACG,cAAc,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAajE;;;;;;;;;;OAUG;IACG,sBAAsB,CAC1B,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,UAAU,GACnB,OAAO,CAAC,qBAAqB,CAAC;IAqCjC;;;;;;OAMG;YACW,qBAAqB;IAanC;;;OAGG;IACH,eAAe,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI;IAmBpD,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAInC,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAMjE,oBAAoB,CACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC;IAgBhB;;;;;OAKG;IACG,YAAY,CAAC,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkBpE,WAAW,CACf,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC;IAqBV,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAyDhB;;;;;OAKG;IACG,yBAAyB,CAC7B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmBhB,OAAO,CAAC,WAAW;IA2BnB;;;;OAIG;IACH,OAAO,CAAC,YAAY;IAcd,gBAAgB,CACpB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAsJV,mBAAmB,CACvB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC;IAmEV,MAAM,CAAC,UAAU,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IA0ItE;;;;;;;;OAQG;YACW,eAAe;CA2B9B"}
|
package/dist/project-creator.js
CHANGED
|
@@ -2,13 +2,14 @@ import path from 'path';
|
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import fs from 'fs-extra';
|
|
5
|
-
import { checkDirectoryExists, listDirectoryEntries,
|
|
5
|
+
import { checkDirectoryExists, listDirectoryEntries, findNonDirectoryInPath, realPath, emptyDirectory, ensureDirectory, copyDirectory, updatePackageJson, } from './file-system.js';
|
|
6
6
|
import { promptProjectName, promptOverwriteDirectory, promptTemplate, promptIconLibrary, promptBulmaFlavor, promptInstallSkills, promptTelemetryConsent, } from './prompts.js';
|
|
7
7
|
import { reportScaffold } from './telemetry.js';
|
|
8
8
|
import { displayHeader, displaySuccess, displayError, displayCancelled, } from './display.js';
|
|
9
|
-
import {
|
|
9
|
+
import { validateProjectDirectory, toValidPackageName } from './validators.js';
|
|
10
10
|
import { MESSAGES, TEMPLATES, ICON_LIBRARIES, BULMA_FLAVORS, CLAUDE_MD, LAUNCH_JSON, CONFIG_PROVIDER_ICON_VALUES, NO_HELPERS_STARTER_CLASSES, NO_HELPERS_APP_CSS, CSS_ORDER_COMMENT, } from './constants.js';
|
|
11
11
|
import { detectPackageManager } from './package-manager.js';
|
|
12
|
+
import { LIBRARY, libraryRange } from './library-range.js';
|
|
12
13
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
13
14
|
// npm strips literal `.gitignore` files from published tarballs, so the
|
|
14
15
|
// templates track `_gitignore` instead and the scaffolder renames it back
|
|
@@ -21,9 +22,13 @@ export class ProjectCreator {
|
|
|
21
22
|
constructor(templatesDir) {
|
|
22
23
|
this.templatesDir = templatesDir || path.resolve(__dirname, '../templates');
|
|
23
24
|
}
|
|
25
|
+
/**
|
|
26
|
+
* The project directory, as typed or answered: a name or a path, absolute
|
|
27
|
+
* or relative, whose last segment must be a valid project name (#1154).
|
|
28
|
+
*/
|
|
24
29
|
async getProjectName(projectDir) {
|
|
25
30
|
if (projectDir) {
|
|
26
|
-
const validation =
|
|
31
|
+
const validation = validateProjectDirectory(projectDir);
|
|
27
32
|
if (validation !== true) {
|
|
28
33
|
console.log(chalk.red(`✖ ${validation}`));
|
|
29
34
|
return null;
|
|
@@ -38,17 +43,25 @@ export class ProjectCreator {
|
|
|
38
43
|
* question leaves the user's files where they were (#945). Only --overwrite
|
|
39
44
|
* or a yes at the prompt removes files. -y never does, and neither -y nor a
|
|
40
45
|
* missing terminal can ask, so both stop at a non-empty directory instead.
|
|
41
|
-
*
|
|
46
|
+
* Whatever the flags say, the run stops at a file on the target path or in
|
|
47
|
+
* place of one of its parents, and before emptying the working directory or
|
|
48
|
+
* one that holds it (#310), however the path spells it. create() calls this
|
|
49
|
+
* straight after the project name, so neither waits for the other prompts.
|
|
42
50
|
*/
|
|
43
51
|
async checkExistingDirectory(targetPath, targetDir, options) {
|
|
44
|
-
|
|
45
|
-
|
|
52
|
+
const blocker = await findNonDirectoryInPath(targetPath);
|
|
53
|
+
if (blocker !== null) {
|
|
54
|
+
displayError(MESSAGES.NOT_A_DIRECTORY(blocker === path.resolve(targetPath) ? targetDir : blocker));
|
|
46
55
|
return 'stop';
|
|
47
56
|
}
|
|
48
57
|
const entries = await listDirectoryEntries(targetPath);
|
|
49
58
|
if (entries.length === 0) {
|
|
50
59
|
return 'empty';
|
|
51
60
|
}
|
|
61
|
+
if (await this.holdsWorkingDirectory(targetPath)) {
|
|
62
|
+
displayError(MESSAGES.DIRECTORY_HOLDS_CWD(targetDir));
|
|
63
|
+
return 'stop';
|
|
64
|
+
}
|
|
52
65
|
if (options?.overwrite) {
|
|
53
66
|
return 'overwrite';
|
|
54
67
|
}
|
|
@@ -62,6 +75,20 @@ export class ProjectCreator {
|
|
|
62
75
|
}
|
|
63
76
|
return 'overwrite';
|
|
64
77
|
}
|
|
78
|
+
/**
|
|
79
|
+
* Whether targetPath is the working directory or one of its ancestors, with
|
|
80
|
+
* symbolic links resolved on both sides. Emptying either deletes the folder
|
|
81
|
+
* the user is standing in, which the leading-dot rule refuses for "." and
|
|
82
|
+
* "..", and a path such as "../<its name>" or its absolute spelling reaches
|
|
83
|
+
* the same folder (#310, #1154).
|
|
84
|
+
*/
|
|
85
|
+
async holdsWorkingDirectory(targetPath) {
|
|
86
|
+
const relative = path.relative(await realPath(targetPath), await realPath(process.cwd()));
|
|
87
|
+
return (relative === '' ||
|
|
88
|
+
(!path.isAbsolute(relative) &&
|
|
89
|
+
relative !== '..' &&
|
|
90
|
+
!relative.startsWith(`..${path.sep}`)));
|
|
91
|
+
}
|
|
65
92
|
/**
|
|
66
93
|
* Every flag's value is checked here, before any prompt runs or anything on
|
|
67
94
|
* disk changes, so a typo can never cost the user an emptied directory (#945).
|
|
@@ -413,6 +440,9 @@ export class ProjectCreator {
|
|
|
413
440
|
process.exit(1);
|
|
414
441
|
return; // TypeScript flow control
|
|
415
442
|
}
|
|
443
|
+
// An absolute path is used as it is and a relative one from the working
|
|
444
|
+
// directory. Its last segment names the package and the page title, and
|
|
445
|
+
// copyTemplate creates it and any missing parent after the last prompt.
|
|
416
446
|
const targetPath = path.resolve(process.cwd(), targetDir);
|
|
417
447
|
const projectName = path.basename(targetPath);
|
|
418
448
|
// Decide about an existing directory now, but empty it only after the
|
|
@@ -482,6 +512,8 @@ export class ProjectCreator {
|
|
|
482
512
|
}
|
|
483
513
|
}
|
|
484
514
|
try {
|
|
515
|
+
// Read before the directory is emptied, so a failed read costs nothing.
|
|
516
|
+
const libraryDependency = { [LIBRARY]: libraryRange() };
|
|
485
517
|
if (directoryPlan === 'overwrite') {
|
|
486
518
|
console.log(chalk.yellow(MESSAGES.EMPTYING_DIRECTORY(targetDir)));
|
|
487
519
|
await emptyDirectory(targetPath);
|
|
@@ -494,7 +526,7 @@ export class ProjectCreator {
|
|
|
494
526
|
console.log(chalk.dim(MESSAGES.PACKAGE_NAME_NORMALIZED(packageName)));
|
|
495
527
|
}
|
|
496
528
|
await this.copyTemplate(template, targetPath);
|
|
497
|
-
await updatePackageJson(targetPath, packageName);
|
|
529
|
+
await updatePackageJson(targetPath, packageName, libraryDependency);
|
|
498
530
|
await this.updateIndexHtmlTitle(targetPath, projectName);
|
|
499
531
|
await this.updateReadme(targetPath, iconLibrary);
|
|
500
532
|
await this.setupBulmaFlavor(targetPath, bulmaFlavor, template);
|
package/dist/prompts.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Takes a name or a path, the same as the project-directory argument. */
|
|
1
2
|
export declare function promptProjectName(): Promise<string | null>;
|
|
2
3
|
/** Asks to remove `entries` from targetDir, naming them, since a yes deletes them. */
|
|
3
4
|
export declare function promptOverwriteDirectory(targetDir: string, entries: string[]): Promise<boolean>;
|
package/dist/prompts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAyBA,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWhE;AAED,sFAAsF;AACtF,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAUnE;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAa7D;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAchE;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA2BtE;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBhE"}
|
|
1
|
+
{"version":3,"file":"prompts.d.ts","sourceRoot":"","sources":["../src/prompts.ts"],"names":[],"mappings":"AAyBA,0EAA0E;AAC1E,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWhE;AAED,sFAAsF;AACtF,wBAAsB,wBAAwB,CAC5C,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,MAAM,EAAE,GAChB,OAAO,CAAC,OAAO,CAAC,CAUlB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CAUnE;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAa7D;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAchE;AAED;;;;;;GAMG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA2BtE;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBhE"}
|
package/dist/prompts.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import prompts from 'prompts';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import { TEMPLATES, DEFAULT_PROJECT_NAME, PROMPTS as PROMPT_MESSAGES, MESSAGES, ICON_LIBRARIES, BULMA_FLAVORS, } from './constants.js';
|
|
4
|
-
import {
|
|
4
|
+
import { validateProjectDirectory } from './validators.js';
|
|
5
5
|
import { displayError } from './display.js';
|
|
6
6
|
/**
|
|
7
7
|
* Guard against hanging forever in non-interactive environments (CI, agents,
|
|
@@ -14,6 +14,7 @@ function ensureInteractive() {
|
|
|
14
14
|
process.exit(1);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
/** Takes a name or a path, the same as the project-directory argument. */
|
|
17
18
|
export async function promptProjectName() {
|
|
18
19
|
ensureInteractive();
|
|
19
20
|
const response = await prompts({
|
|
@@ -21,7 +22,7 @@ export async function promptProjectName() {
|
|
|
21
22
|
name: 'projectName',
|
|
22
23
|
message: PROMPT_MESSAGES.PROJECT_NAME,
|
|
23
24
|
initial: DEFAULT_PROJECT_NAME,
|
|
24
|
-
validate:
|
|
25
|
+
validate: validateProjectDirectory,
|
|
25
26
|
});
|
|
26
27
|
return response.projectName || null;
|
|
27
28
|
}
|
package/dist/validators.d.ts
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
export declare function validateProjectName(value: string): boolean | string;
|
|
2
2
|
export declare function isValidProjectName(name: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* The project directory may be a name or a path, absolute or relative, as
|
|
5
|
+
* create-vite takes it (#1154). Its last segment is the project name, the one
|
|
6
|
+
* that names the package and the page title, so that is what is validated, by
|
|
7
|
+
* the same rules as a bare name: a path ending in "." or ".." still fails the
|
|
8
|
+
* leading-dot rule above. A rejected path names the segment it was judged by.
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateProjectDirectory(targetDir: string): boolean | string;
|
|
3
11
|
/**
|
|
4
12
|
* The package.json name for a project directory. npm rejects capitals and a
|
|
5
13
|
* leading "_" or ".", so the name is lower-cased the way `npm init` does and
|
package/dist/validators.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../src/validators.ts"],"names":[],"mappings":"AAOA,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CA2BnE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAExD;AAED;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAM5E;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAE9D"}
|
package/dist/validators.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from 'path';
|
|
1
2
|
import { PROJECT_NAME_REGEX, MAX_PROJECT_NAME_LENGTH, MESSAGES, } from './constants.js';
|
|
2
3
|
export function validateProjectName(value) {
|
|
3
4
|
if (!value) {
|
|
@@ -8,7 +9,9 @@ export function validateProjectName(value) {
|
|
|
8
9
|
}
|
|
9
10
|
// The regex below allows dots, so "." and ".." would pass and resolve to
|
|
10
11
|
// the current/parent directory — which --overwrite would then empty. Rejecting
|
|
11
|
-
// every leading-dot name closes that hole ("/" and "\" are
|
|
12
|
+
// every leading-dot name closes that hole for these spellings ("/" and "\" are
|
|
13
|
+
// already blocked). A path reaching the same directory another way, such as
|
|
14
|
+
// "../<its name>", is refused where it resolves, in checkExistingDirectory.
|
|
12
15
|
if (value.startsWith('.')) {
|
|
13
16
|
return MESSAGES.PROJECT_NAME_DOT;
|
|
14
17
|
}
|
|
@@ -23,6 +26,20 @@ export function validateProjectName(value) {
|
|
|
23
26
|
export function isValidProjectName(name) {
|
|
24
27
|
return validateProjectName(name) === true;
|
|
25
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* The project directory may be a name or a path, absolute or relative, as
|
|
31
|
+
* create-vite takes it (#1154). Its last segment is the project name, the one
|
|
32
|
+
* that names the package and the page title, so that is what is validated, by
|
|
33
|
+
* the same rules as a bare name: a path ending in "." or ".." still fails the
|
|
34
|
+
* leading-dot rule above. A rejected path names the segment it was judged by.
|
|
35
|
+
*/
|
|
36
|
+
export function validateProjectDirectory(targetDir) {
|
|
37
|
+
const projectName = path.basename(targetDir);
|
|
38
|
+
const validation = validateProjectName(projectName);
|
|
39
|
+
return typeof validation === 'string' && projectName !== targetDir
|
|
40
|
+
? MESSAGES.PROJECT_NAME_FROM_PATH(validation, projectName)
|
|
41
|
+
: validation;
|
|
42
|
+
}
|
|
26
43
|
/**
|
|
27
44
|
* The package.json name for a project directory. npm rejects capitals and a
|
|
28
45
|
* leading "_" or ".", so the name is lower-cased the way `npm init` does and
|