@cbnventures/nova 0.12.0 → 0.13.0
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/build/eslint.config.d.ts +32 -1
- package/build/eslint.config.d.ts.map +1 -1
- package/build/eslint.config.js +9 -1
- package/build/eslint.config.js.map +1 -1
- package/build/package.json +63 -59
- package/build/src/api/node-releases.d.ts +7 -0
- package/build/src/api/node-releases.d.ts.map +1 -0
- package/build/src/api/node-releases.js +67 -0
- package/build/src/api/node-releases.js.map +1 -0
- package/build/src/api/spdx-licenses.d.ts +7 -0
- package/build/src/api/spdx-licenses.d.ts.map +1 -0
- package/build/src/api/spdx-licenses.js +43 -0
- package/build/src/api/spdx-licenses.js.map +1 -0
- package/build/src/cli/index.js +60 -18
- package/build/src/cli/index.js.map +1 -1
- package/build/src/cli/recipe/pin-versions.d.ts +7 -0
- package/build/src/cli/recipe/pin-versions.d.ts.map +1 -0
- package/build/src/cli/recipe/pin-versions.js +145 -0
- package/build/src/cli/recipe/pin-versions.js.map +1 -0
- package/build/src/cli/recipe/sync-lts-engines.d.ts +6 -0
- package/build/src/cli/recipe/sync-lts-engines.d.ts.map +1 -0
- package/build/src/cli/recipe/sync-lts-engines.js +118 -0
- package/build/src/cli/recipe/sync-lts-engines.js.map +1 -0
- package/build/src/cli/recipe/sync-packages.d.ts +18 -0
- package/build/src/cli/recipe/sync-packages.d.ts.map +1 -0
- package/build/src/cli/recipe/sync-packages.js +1212 -0
- package/build/src/cli/recipe/sync-packages.js.map +1 -0
- package/build/src/cli/utility/changelog.d.ts +11 -0
- package/build/src/cli/utility/changelog.d.ts.map +1 -0
- package/build/src/cli/utility/changelog.js +670 -0
- package/build/src/cli/utility/changelog.js.map +1 -0
- package/build/src/cli/utility/initialize.d.ts +9 -5
- package/build/src/cli/utility/initialize.d.ts.map +1 -1
- package/build/src/cli/utility/initialize.js +478 -210
- package/build/src/cli/utility/initialize.js.map +1 -1
- package/build/src/cli/utility/type-check.d.ts +9 -0
- package/build/src/cli/utility/type-check.d.ts.map +1 -0
- package/build/src/cli/utility/type-check.js +59 -0
- package/build/src/cli/utility/type-check.js.map +1 -0
- package/build/src/cli/utility/version.d.ts +2 -2
- package/build/src/cli/utility/version.d.ts.map +1 -1
- package/build/src/cli/utility/version.js +107 -68
- package/build/src/cli/utility/version.js.map +1 -1
- package/build/src/lib/item.d.ts +23 -5
- package/build/src/lib/item.d.ts.map +1 -1
- package/build/src/lib/item.js +329 -4
- package/build/src/lib/item.js.map +1 -1
- package/build/src/lib/nova-config.d.ts +4 -4
- package/build/src/lib/nova-config.d.ts.map +1 -1
- package/build/src/lib/nova-config.js +76 -88
- package/build/src/lib/nova-config.js.map +1 -1
- package/build/src/lib/regex.d.ts +9 -1
- package/build/src/lib/regex.d.ts.map +1 -1
- package/build/src/lib/regex.js +9 -1
- package/build/src/lib/regex.js.map +1 -1
- package/build/src/lib/schema.d.ts +18 -0
- package/build/src/lib/schema.d.ts.map +1 -0
- package/build/src/lib/schema.js +13 -0
- package/build/src/lib/schema.js.map +1 -0
- package/build/src/lib/utility.d.ts +9 -1
- package/build/src/lib/utility.d.ts.map +1 -1
- package/build/src/lib/utility.js +219 -40
- package/build/src/lib/utility.js.map +1 -1
- package/build/src/presets/eslint/dx-code-style.d.mts.map +1 -1
- package/build/src/presets/eslint/dx-code-style.mjs +0 -20
- package/build/src/presets/eslint/dx-code-style.mjs.map +1 -1
- package/build/src/presets/eslint/lang-mdx.d.mts.map +1 -1
- package/build/src/presets/eslint/lang-mdx.mjs +0 -21
- package/build/src/presets/eslint/lang-mdx.mjs.map +1 -1
- package/build/src/presets/tsconfig/dx-strict.json +2 -1
- package/build/src/rules/eslint/index.d.ts +4 -0
- package/build/src/rules/eslint/index.d.ts.map +1 -1
- package/build/src/rules/eslint/index.js +4 -0
- package/build/src/rules/eslint/index.js.map +1 -1
- package/build/src/rules/eslint/no-logger-dev.d.ts +3 -1
- package/build/src/rules/eslint/no-logger-dev.d.ts.map +1 -1
- package/build/src/rules/eslint/no-logger-dev.js +4 -4
- package/build/src/rules/eslint/no-logger-dev.js.map +1 -1
- package/build/src/rules/eslint/no-raw-text-in-code.d.ts +6 -0
- package/build/src/rules/eslint/no-raw-text-in-code.d.ts.map +1 -0
- package/build/src/rules/eslint/no-raw-text-in-code.js +34 -0
- package/build/src/rules/eslint/no-raw-text-in-code.js.map +1 -0
- package/build/src/rules/eslint/no-regex-literal-flags.d.ts +6 -0
- package/build/src/rules/eslint/no-regex-literal-flags.d.ts.map +1 -0
- package/build/src/rules/eslint/no-regex-literal-flags.js +30 -0
- package/build/src/rules/eslint/no-regex-literal-flags.js.map +1 -0
- package/build/src/rules/eslint/no-regex-literals.d.ts +9 -0
- package/build/src/rules/eslint/no-regex-literals.d.ts.map +1 -0
- package/build/src/rules/eslint/no-regex-literals.js +55 -0
- package/build/src/rules/eslint/no-regex-literals.js.map +1 -0
- package/build/src/rules/eslint/switch-case-blocks.d.ts +6 -0
- package/build/src/rules/eslint/switch-case-blocks.d.ts.map +1 -0
- package/build/src/rules/eslint/switch-case-blocks.js +36 -0
- package/build/src/rules/eslint/switch-case-blocks.js.map +1 -0
- package/build/src/tests/api/node-releases.test.d.ts +2 -0
- package/build/src/tests/api/node-releases.test.d.ts.map +1 -0
- package/build/src/tests/api/node-releases.test.js +193 -0
- package/build/src/tests/api/node-releases.test.js.map +1 -0
- package/build/src/tests/api/spdx-licenses.test.d.ts +2 -0
- package/build/src/tests/api/spdx-licenses.test.d.ts.map +1 -0
- package/build/src/tests/api/spdx-licenses.test.js +91 -0
- package/build/src/tests/api/spdx-licenses.test.js.map +1 -0
- package/build/src/tests/cli/recipe/pin-versions.test.d.ts +2 -0
- package/build/src/tests/cli/recipe/pin-versions.test.d.ts.map +1 -0
- package/build/src/tests/cli/recipe/pin-versions.test.js +197 -0
- package/build/src/tests/cli/recipe/pin-versions.test.js.map +1 -0
- package/build/src/tests/cli/recipe/sync-lts-engines.test.d.ts +2 -0
- package/build/src/tests/cli/recipe/sync-lts-engines.test.d.ts.map +1 -0
- package/build/src/tests/cli/recipe/sync-lts-engines.test.js +131 -0
- package/build/src/tests/cli/recipe/sync-lts-engines.test.js.map +1 -0
- package/build/src/tests/lib/item.test.d.ts +2 -0
- package/build/src/tests/lib/item.test.d.ts.map +1 -0
- package/build/src/tests/lib/item.test.js +142 -0
- package/build/src/tests/lib/item.test.js.map +1 -0
- package/build/src/tests/lib/nova-config.test.d.ts +2 -0
- package/build/src/tests/lib/nova-config.test.d.ts.map +1 -0
- package/build/src/tests/lib/nova-config.test.js +489 -0
- package/build/src/tests/lib/nova-config.test.js.map +1 -0
- package/build/src/tests/lib/regex.test.d.ts +2 -0
- package/build/src/tests/lib/regex.test.d.ts.map +1 -0
- package/build/src/tests/lib/regex.test.js +342 -0
- package/build/src/tests/lib/regex.test.js.map +1 -0
- package/build/src/tests/lib/schema.test.d.ts +2 -0
- package/build/src/tests/lib/schema.test.d.ts.map +1 -0
- package/build/src/tests/lib/schema.test.js +260 -0
- package/build/src/tests/lib/schema.test.js.map +1 -0
- package/build/src/tests/lib/utility.test.js +704 -44
- package/build/src/tests/lib/utility.test.js.map +1 -1
- package/build/src/tests/rules/eslint/no-logger-dev.test.d.ts +2 -0
- package/build/src/tests/rules/eslint/no-logger-dev.test.d.ts.map +1 -0
- package/build/src/tests/rules/eslint/no-logger-dev.test.js +55 -0
- package/build/src/tests/rules/eslint/no-logger-dev.test.js.map +1 -0
- package/build/src/tests/rules/eslint/no-raw-text-in-code.test.d.ts +2 -0
- package/build/src/tests/rules/eslint/no-raw-text-in-code.test.d.ts.map +1 -0
- package/build/src/tests/rules/eslint/no-raw-text-in-code.test.js +47 -0
- package/build/src/tests/rules/eslint/no-raw-text-in-code.test.js.map +1 -0
- package/build/src/tests/rules/eslint/no-regex-literal-flags.test.d.ts +2 -0
- package/build/src/tests/rules/eslint/no-regex-literal-flags.test.d.ts.map +1 -0
- package/build/src/tests/rules/eslint/no-regex-literal-flags.test.js +47 -0
- package/build/src/tests/rules/eslint/no-regex-literal-flags.test.js.map +1 -0
- package/build/src/tests/rules/eslint/no-regex-literals.test.d.ts +2 -0
- package/build/src/tests/rules/eslint/no-regex-literals.test.d.ts.map +1 -0
- package/build/src/tests/rules/eslint/no-regex-literals.test.js +49 -0
- package/build/src/tests/rules/eslint/no-regex-literals.test.js.map +1 -0
- package/build/src/tests/rules/eslint/switch-case-blocks.test.d.ts +2 -0
- package/build/src/tests/rules/eslint/switch-case-blocks.test.d.ts.map +1 -0
- package/build/src/tests/rules/eslint/switch-case-blocks.test.js +43 -0
- package/build/src/tests/rules/eslint/switch-case-blocks.test.js.map +1 -0
- package/build/src/tests/toolkit/cli-header.test.d.ts +2 -0
- package/build/src/tests/toolkit/cli-header.test.d.ts.map +1 -0
- package/build/src/tests/toolkit/cli-header.test.js +143 -0
- package/build/src/tests/toolkit/cli-header.test.js.map +1 -0
- package/build/src/tests/toolkit/logger.test.d.ts +2 -0
- package/build/src/tests/toolkit/logger.test.d.ts.map +1 -0
- package/build/src/tests/toolkit/logger.test.js +96 -0
- package/build/src/tests/toolkit/logger.test.js.map +1 -0
- package/build/src/tests/toolkit/markdown-table.test.d.ts +2 -0
- package/build/src/tests/toolkit/markdown-table.test.d.ts.map +1 -0
- package/build/src/tests/toolkit/markdown-table.test.js +138 -0
- package/build/src/tests/toolkit/markdown-table.test.js.map +1 -0
- package/build/src/toolkit/cli-header.d.ts +1 -0
- package/build/src/toolkit/cli-header.d.ts.map +1 -1
- package/build/src/toolkit/cli-header.js +24 -13
- package/build/src/toolkit/cli-header.js.map +1 -1
- package/build/src/toolkit/index.d.ts +1 -1
- package/build/src/toolkit/index.d.ts.map +1 -1
- package/build/src/toolkit/index.js +1 -1
- package/build/src/toolkit/index.js.map +1 -1
- package/build/src/toolkit/logger.d.ts.map +1 -1
- package/build/src/toolkit/logger.js +25 -10
- package/build/src/toolkit/logger.js.map +1 -1
- package/build/src/toolkit/markdown-table.d.ts.map +1 -1
- package/build/src/toolkit/markdown-table.js +3 -3
- package/build/src/toolkit/markdown-table.js.map +1 -1
- package/package.json +63 -59
- package/build/src/cli/recipe/sync-metadata.d.ts +0 -5
- package/build/src/cli/recipe/sync-metadata.d.ts.map +0 -1
- package/build/src/cli/recipe/sync-metadata.js +0 -7
- package/build/src/cli/recipe/sync-metadata.js.map +0 -1
- package/build/src/cli/recipe/sync-versions.d.ts +0 -5
- package/build/src/cli/recipe/sync-versions.d.ts.map +0 -1
- package/build/src/cli/recipe/sync-versions.js +0 -7
- package/build/src/cli/recipe/sync-versions.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { relative, sep } from 'path';
|
|
2
2
|
import chalk from 'chalk';
|
|
3
3
|
import prompts from 'prompts';
|
|
4
|
-
import { itemAllowedPoliciesByRole } from '../../lib/item.js';
|
|
4
|
+
import { itemAllowedPoliciesByRole, itemAllowedSyncProperties } from '../../lib/item.js';
|
|
5
5
|
import { NovaConfig } from '../../lib/nova-config.js';
|
|
6
6
|
import { PATTERN_EMAIL_SIMPLE, PATTERN_SLUG_SCOPED, PATTERN_SLUG_SIMPLE } from '../../lib/regex.js';
|
|
7
7
|
import { discoverPathsWithFile } from '../../lib/utility.js';
|
|
@@ -10,17 +10,25 @@ export class CLIUtilityInitialize {
|
|
|
10
10
|
static async run(options) {
|
|
11
11
|
const currentDirectory = process.cwd();
|
|
12
12
|
const isProjectRoot = await CLIUtilityInitialize.checkPath(currentDirectory);
|
|
13
|
-
if (
|
|
13
|
+
if (isProjectRoot !== true) {
|
|
14
14
|
process.exitCode = 1;
|
|
15
15
|
return;
|
|
16
16
|
}
|
|
17
|
-
|
|
17
|
+
const isDryRun = options.dryRun === true;
|
|
18
|
+
const isReplaceFile = options.replaceFile === true;
|
|
19
|
+
if (isDryRun === true) {
|
|
18
20
|
Logger.customize({
|
|
19
21
|
name: 'CLIUtilityInitialize.run',
|
|
20
22
|
purpose: 'options',
|
|
21
|
-
padBottom: 1,
|
|
22
23
|
}).warn('Dry run enabled. File changes will not be made in this session.');
|
|
23
24
|
}
|
|
25
|
+
if (isReplaceFile === true) {
|
|
26
|
+
const replaceFileNotice = (isDryRun) ? 'This option has no effect during a dry run session.' : 'Backup file will not be created.';
|
|
27
|
+
Logger.customize({
|
|
28
|
+
name: 'CLIUtilityInitialize.run',
|
|
29
|
+
purpose: 'options',
|
|
30
|
+
}).warn(`Replace file enabled. ${replaceFileNotice}`);
|
|
31
|
+
}
|
|
24
32
|
const novaConfig = new NovaConfig();
|
|
25
33
|
const workingFile = await novaConfig.load();
|
|
26
34
|
const promptFlowResult = await CLIUtilityInitialize.promptFlow(workingFile);
|
|
@@ -28,22 +36,18 @@ export class CLIUtilityInitialize {
|
|
|
28
36
|
Logger.customize({
|
|
29
37
|
name: 'CLIUtilityInitialize.run',
|
|
30
38
|
purpose: 'promptFlow',
|
|
31
|
-
padTop: 1,
|
|
32
|
-
padBottom: 1,
|
|
33
39
|
}).debug('Prompt flow exited without saving.');
|
|
34
40
|
return;
|
|
35
41
|
}
|
|
36
42
|
novaConfig.set(workingFile);
|
|
37
|
-
if (
|
|
43
|
+
if (isDryRun === true) {
|
|
38
44
|
Logger.customize({
|
|
39
45
|
name: 'CLIUtilityInitialize.run',
|
|
40
46
|
purpose: 'promptFlow',
|
|
41
|
-
padTop: 1,
|
|
42
|
-
padBottom: 1,
|
|
43
47
|
}).debug('Dry run enabled. Skipping save operation.');
|
|
44
48
|
return;
|
|
45
49
|
}
|
|
46
|
-
await novaConfig.save();
|
|
50
|
+
await novaConfig.save(isReplaceFile);
|
|
47
51
|
}
|
|
48
52
|
static async promptFlow(config) {
|
|
49
53
|
const category = {
|
|
@@ -57,9 +61,14 @@ export class CLIUtilityInitialize {
|
|
|
57
61
|
description: 'Manage entities, their roles, and contact information.',
|
|
58
62
|
handler: CLIUtilityInitialize.promptEntities,
|
|
59
63
|
},
|
|
64
|
+
emails: {
|
|
65
|
+
label: 'Emails',
|
|
66
|
+
description: 'Configure project emails (bugs, etc.).',
|
|
67
|
+
handler: CLIUtilityInitialize.promptEmails,
|
|
68
|
+
},
|
|
60
69
|
urls: {
|
|
61
70
|
label: 'URLs',
|
|
62
|
-
description: '
|
|
71
|
+
description: 'Configure project URLs (homepage, repository, fund sources, etc.).',
|
|
63
72
|
handler: CLIUtilityInitialize.promptUrls,
|
|
64
73
|
},
|
|
65
74
|
workspaces: {
|
|
@@ -109,89 +118,88 @@ export class CLIUtilityInitialize {
|
|
|
109
118
|
}
|
|
110
119
|
static async promptProject(config) {
|
|
111
120
|
const existingProject = config.project;
|
|
112
|
-
const existingProjectName = existingProject
|
|
113
|
-
const existingProjectDescription = existingProject
|
|
114
|
-
const existingProjectKeywords = existingProject
|
|
121
|
+
const existingProjectName = (existingProject !== undefined) ? existingProject.name : undefined;
|
|
122
|
+
const existingProjectDescription = (existingProject !== undefined) ? existingProject.description : undefined;
|
|
123
|
+
const existingProjectKeywords = (existingProject !== undefined) ? existingProject.keywords : undefined;
|
|
115
124
|
const project = (existingProject !== undefined) ? { ...existingProject } : {};
|
|
116
125
|
const projectName = (existingProjectName !== undefined) ? { ...existingProjectName } : {};
|
|
117
126
|
const projectDescription = (existingProjectDescription !== undefined) ? { ...existingProjectDescription } : {};
|
|
127
|
+
const projectKeywords = (existingProjectKeywords !== undefined) ? [...existingProjectKeywords] : [];
|
|
118
128
|
const questionsOutput = await CLIUtilityInitialize.promptWithCancel([
|
|
119
129
|
{
|
|
120
130
|
type: 'text',
|
|
121
131
|
name: 'projectNameTitle',
|
|
122
132
|
message: 'Project title (display name)',
|
|
123
133
|
initial: projectName.title ?? '',
|
|
134
|
+
validate: (value) => CLIUtilityInitialize.normalizeText(value, Infinity).result,
|
|
124
135
|
},
|
|
125
136
|
{
|
|
126
137
|
type: 'text',
|
|
127
138
|
name: 'projectNameSlug',
|
|
128
139
|
message: 'Project slug (package name)',
|
|
129
140
|
initial: projectName.slug ?? '',
|
|
130
|
-
validate: (value) =>
|
|
131
|
-
const trimmed = value.trim();
|
|
132
|
-
if (trimmed === '' || new RegExp(PATTERN_SLUG_SIMPLE, 'i').test(trimmed)) {
|
|
133
|
-
return true;
|
|
134
|
-
}
|
|
135
|
-
return 'Use letters, numbers, hyphens, and underscores only.';
|
|
136
|
-
},
|
|
141
|
+
validate: (value) => CLIUtilityInitialize.normalizeProjectSlug(value).result,
|
|
137
142
|
},
|
|
138
143
|
{
|
|
139
144
|
type: 'text',
|
|
140
145
|
name: 'projectDescriptionShort',
|
|
141
146
|
message: 'Short description',
|
|
142
147
|
initial: projectDescription.short ?? '',
|
|
148
|
+
validate: (value) => CLIUtilityInitialize.normalizeText(value, Infinity).result,
|
|
143
149
|
},
|
|
144
150
|
{
|
|
145
151
|
type: 'text',
|
|
146
152
|
name: 'projectDescriptionLong',
|
|
147
153
|
message: 'Long description',
|
|
148
154
|
initial: projectDescription.long ?? '',
|
|
155
|
+
validate: (value) => CLIUtilityInitialize.normalizeText(value, Infinity).result,
|
|
149
156
|
},
|
|
150
157
|
{
|
|
151
158
|
type: 'text',
|
|
152
159
|
name: 'projectKeywords',
|
|
153
160
|
message: 'Keywords (comma separated)',
|
|
154
|
-
initial: (
|
|
161
|
+
initial: (projectKeywords.length > 0) ? projectKeywords.join(', ') : '',
|
|
162
|
+
validate: (value) => CLIUtilityInitialize.normalizeTextArray(value, 50).result,
|
|
155
163
|
},
|
|
156
164
|
]);
|
|
157
165
|
if (questionsOutput.cancelled) {
|
|
158
166
|
return 'back';
|
|
159
167
|
}
|
|
160
168
|
const questionsOutputResult = questionsOutput.result;
|
|
161
|
-
const projectNameTitleInput = (questionsOutputResult.projectNameTitle
|
|
162
|
-
const projectNameSlugInput = (questionsOutputResult.projectNameSlug
|
|
163
|
-
const projectDescriptionShortInput = (questionsOutputResult.projectDescriptionShort
|
|
164
|
-
const projectDescriptionLongInput = (questionsOutputResult.projectDescriptionLong
|
|
165
|
-
const projectKeywordsInput = (questionsOutputResult.projectKeywords
|
|
166
|
-
if (projectNameTitleInput
|
|
167
|
-
|
|
169
|
+
const projectNameTitleInput = CLIUtilityInitialize.normalizeText(questionsOutputResult.projectNameTitle, Infinity).sanitized;
|
|
170
|
+
const projectNameSlugInput = CLIUtilityInitialize.normalizeProjectSlug(questionsOutputResult.projectNameSlug).sanitized;
|
|
171
|
+
const projectDescriptionShortInput = CLIUtilityInitialize.normalizeText(questionsOutputResult.projectDescriptionShort, Infinity).sanitized;
|
|
172
|
+
const projectDescriptionLongInput = CLIUtilityInitialize.normalizeText(questionsOutputResult.projectDescriptionLong, Infinity).sanitized;
|
|
173
|
+
const projectKeywordsInput = CLIUtilityInitialize.normalizeTextArray(questionsOutputResult.projectKeywords, 50).sanitized;
|
|
174
|
+
if (projectNameTitleInput !== undefined) {
|
|
175
|
+
projectName.title = projectNameTitleInput;
|
|
168
176
|
}
|
|
169
177
|
else {
|
|
170
|
-
projectName
|
|
178
|
+
Reflect.deleteProperty(projectName, 'title');
|
|
171
179
|
}
|
|
172
|
-
if (projectNameSlugInput
|
|
173
|
-
|
|
180
|
+
if (projectNameSlugInput !== undefined) {
|
|
181
|
+
projectName.slug = projectNameSlugInput;
|
|
174
182
|
}
|
|
175
183
|
else {
|
|
176
|
-
projectName
|
|
184
|
+
Reflect.deleteProperty(projectName, 'slug');
|
|
177
185
|
}
|
|
178
|
-
if (
|
|
179
|
-
|
|
186
|
+
if (Object.keys(projectName).length > 0) {
|
|
187
|
+
project.name = projectName;
|
|
180
188
|
}
|
|
181
189
|
else {
|
|
182
|
-
|
|
190
|
+
Reflect.deleteProperty(project, 'name');
|
|
183
191
|
}
|
|
184
|
-
if (
|
|
185
|
-
|
|
192
|
+
if (projectDescriptionShortInput !== undefined) {
|
|
193
|
+
projectDescription.short = projectDescriptionShortInput;
|
|
186
194
|
}
|
|
187
195
|
else {
|
|
188
|
-
projectDescription
|
|
196
|
+
Reflect.deleteProperty(projectDescription, 'short');
|
|
189
197
|
}
|
|
190
|
-
if (
|
|
191
|
-
|
|
198
|
+
if (projectDescriptionLongInput !== undefined) {
|
|
199
|
+
projectDescription.long = projectDescriptionLongInput;
|
|
192
200
|
}
|
|
193
201
|
else {
|
|
194
|
-
Reflect.deleteProperty(
|
|
202
|
+
Reflect.deleteProperty(projectDescription, 'long');
|
|
195
203
|
}
|
|
196
204
|
if (Object.keys(projectDescription).length > 0) {
|
|
197
205
|
project.description = projectDescription;
|
|
@@ -199,20 +207,11 @@ export class CLIUtilityInitialize {
|
|
|
199
207
|
else {
|
|
200
208
|
Reflect.deleteProperty(project, 'description');
|
|
201
209
|
}
|
|
202
|
-
if (projectKeywordsInput
|
|
203
|
-
|
|
210
|
+
if (projectKeywordsInput !== undefined && projectKeywordsInput.length > 0) {
|
|
211
|
+
project.keywords = projectKeywordsInput;
|
|
204
212
|
}
|
|
205
213
|
else {
|
|
206
|
-
|
|
207
|
-
.split(',')
|
|
208
|
-
.map((projectKeywordInput) => projectKeywordInput.trim())
|
|
209
|
-
.filter((projectKeywordInput) => projectKeywordInput !== '');
|
|
210
|
-
if (projectKeywordsList.length > 0) {
|
|
211
|
-
project.keywords = projectKeywordsList;
|
|
212
|
-
}
|
|
213
|
-
else {
|
|
214
|
-
Reflect.deleteProperty(project, 'keywords');
|
|
215
|
-
}
|
|
214
|
+
Reflect.deleteProperty(project, 'keywords');
|
|
216
215
|
}
|
|
217
216
|
if (Object.keys(project).length > 0) {
|
|
218
217
|
Object.assign(config, { project });
|
|
@@ -220,8 +219,8 @@ export class CLIUtilityInitialize {
|
|
|
220
219
|
else {
|
|
221
220
|
Reflect.deleteProperty(config, 'project');
|
|
222
221
|
}
|
|
223
|
-
const previousSlug = existingProjectName
|
|
224
|
-
const currentSlug = config.project
|
|
222
|
+
const previousSlug = (existingProjectName !== undefined) ? existingProjectName.slug ?? '' : '';
|
|
223
|
+
const currentSlug = (config.project !== undefined && config.project.name !== undefined) ? config.project.name.slug ?? '' : '';
|
|
225
224
|
const slugChanged = previousSlug !== currentSlug;
|
|
226
225
|
if (slugChanged && config.workspaces !== undefined) {
|
|
227
226
|
const rolesToSync = ['project', 'docs', 'config', 'app', 'tool'];
|
|
@@ -411,7 +410,7 @@ export class CLIUtilityInitialize {
|
|
|
411
410
|
const entityEmail = (typeof entityToRemove.email === 'string') ? entityToRemove.email.trim() : '';
|
|
412
411
|
const entityLabel = entityName || entityEmail || `Entity ${entityIndex + 1}`;
|
|
413
412
|
const shouldRemove = await CLIUtilityInitialize.promptEntitiesDeleteForm(entityLabel);
|
|
414
|
-
if (
|
|
413
|
+
if (shouldRemove !== true) {
|
|
415
414
|
continue;
|
|
416
415
|
}
|
|
417
416
|
entities.splice(entityIndex, 1);
|
|
@@ -427,11 +426,11 @@ export class CLIUtilityInitialize {
|
|
|
427
426
|
}
|
|
428
427
|
static async promptEntitiesForm(entity, mode) {
|
|
429
428
|
const validRoles = ['author', 'contributor', 'supporter'];
|
|
430
|
-
const existingName = (typeof entity
|
|
431
|
-
const existingEmail = (typeof entity
|
|
432
|
-
const existingUrl = (typeof entity
|
|
429
|
+
const existingName = (entity !== undefined && typeof entity.name === 'string') ? entity.name : '';
|
|
430
|
+
const existingEmail = (entity !== undefined && typeof entity.email === 'string') ? entity.email : '';
|
|
431
|
+
const existingUrl = (entity !== undefined && typeof entity.url === 'string') ? entity.url : '';
|
|
433
432
|
let existingRoles = [];
|
|
434
|
-
if (Array.isArray(entity
|
|
433
|
+
if (entity !== undefined && Array.isArray(entity.roles)) {
|
|
435
434
|
existingRoles = entity.roles.filter((role) => validRoles.includes(role));
|
|
436
435
|
}
|
|
437
436
|
const questionsOutput = await CLIUtilityInitialize.promptWithCancel([
|
|
@@ -440,38 +439,21 @@ export class CLIUtilityInitialize {
|
|
|
440
439
|
name: 'entityName',
|
|
441
440
|
message: 'Entity name',
|
|
442
441
|
initial: existingName,
|
|
442
|
+
validate: (value) => CLIUtilityInitialize.normalizeText(value, Infinity).result,
|
|
443
443
|
},
|
|
444
444
|
{
|
|
445
445
|
type: 'text',
|
|
446
446
|
name: 'entityEmail',
|
|
447
447
|
message: 'Entity email address',
|
|
448
448
|
initial: existingEmail,
|
|
449
|
-
validate: (value) =>
|
|
450
|
-
const trimmed = value.trim();
|
|
451
|
-
if (trimmed === '') {
|
|
452
|
-
return true;
|
|
453
|
-
}
|
|
454
|
-
if (PATTERN_EMAIL_SIMPLE.test(trimmed)) {
|
|
455
|
-
return true;
|
|
456
|
-
}
|
|
457
|
-
return 'Enter a valid email address or leave blank.';
|
|
458
|
-
},
|
|
449
|
+
validate: (value) => CLIUtilityInitialize.normalizeEmail(value).result,
|
|
459
450
|
},
|
|
460
451
|
{
|
|
461
452
|
type: 'text',
|
|
462
453
|
name: 'entityUrl',
|
|
463
454
|
message: 'Entity website',
|
|
464
455
|
initial: existingUrl,
|
|
465
|
-
validate: (value) =>
|
|
466
|
-
const trimmed = value.trim();
|
|
467
|
-
if (trimmed === '') {
|
|
468
|
-
return true;
|
|
469
|
-
}
|
|
470
|
-
if (CLIUtilityInitialize.isAllowedHttpUrl(trimmed)) {
|
|
471
|
-
return true;
|
|
472
|
-
}
|
|
473
|
-
return 'Enter a valid URL (http:// or https://) or leave blank.';
|
|
474
|
-
},
|
|
456
|
+
validate: (value) => CLIUtilityInitialize.normalizeUrl(value, 'generic').result,
|
|
475
457
|
},
|
|
476
458
|
{
|
|
477
459
|
type: 'multiselect',
|
|
@@ -490,18 +472,18 @@ export class CLIUtilityInitialize {
|
|
|
490
472
|
};
|
|
491
473
|
}
|
|
492
474
|
const questionsOutputResult = questionsOutput.result;
|
|
493
|
-
const entityNameInput = (
|
|
494
|
-
const entityEmailInput = (
|
|
495
|
-
const entityUrlInput = (
|
|
475
|
+
const entityNameInput = CLIUtilityInitialize.normalizeText(questionsOutputResult.entityName, Infinity).sanitized;
|
|
476
|
+
const entityEmailInput = CLIUtilityInitialize.normalizeEmail(questionsOutputResult.entityEmail).sanitized;
|
|
477
|
+
const entityUrlInput = CLIUtilityInitialize.normalizeUrl(questionsOutputResult.entityUrl, 'generic').sanitized;
|
|
496
478
|
const entityRolesInput = Array.isArray(questionsOutputResult.entityRoles) ? [...questionsOutputResult.entityRoles] : [];
|
|
497
479
|
const resolvedEntity = {};
|
|
498
|
-
if (entityNameInput !==
|
|
480
|
+
if (entityNameInput !== undefined) {
|
|
499
481
|
resolvedEntity.name = entityNameInput;
|
|
500
482
|
}
|
|
501
|
-
if (entityEmailInput !==
|
|
483
|
+
if (entityEmailInput !== undefined) {
|
|
502
484
|
resolvedEntity.email = entityEmailInput;
|
|
503
485
|
}
|
|
504
|
-
if (entityUrlInput !==
|
|
486
|
+
if (entityUrlInput !== undefined) {
|
|
505
487
|
resolvedEntity.url = entityUrlInput;
|
|
506
488
|
}
|
|
507
489
|
if (entityRolesInput.length > 0) {
|
|
@@ -530,112 +512,180 @@ export class CLIUtilityInitialize {
|
|
|
530
512
|
const confirmOutputResult = confirmOutput.result;
|
|
531
513
|
return confirmOutputResult.confirm;
|
|
532
514
|
}
|
|
515
|
+
static async promptEmails(config) {
|
|
516
|
+
const existingEmails = config.emails;
|
|
517
|
+
const emails = (existingEmails !== undefined) ? { ...existingEmails } : {};
|
|
518
|
+
const questionsOutput = await CLIUtilityInitialize.promptWithCancel([
|
|
519
|
+
{
|
|
520
|
+
type: 'text',
|
|
521
|
+
name: 'emailsBugs',
|
|
522
|
+
message: 'Issue tracker email',
|
|
523
|
+
initial: emails.bugs ?? '',
|
|
524
|
+
validate: (value) => CLIUtilityInitialize.normalizeEmail(value).result,
|
|
525
|
+
},
|
|
526
|
+
]);
|
|
527
|
+
if (questionsOutput.cancelled) {
|
|
528
|
+
return 'back';
|
|
529
|
+
}
|
|
530
|
+
const questionsOutputResult = questionsOutput.result;
|
|
531
|
+
const emailsBugsInput = CLIUtilityInitialize.normalizeEmail(questionsOutputResult.emailsBugs).sanitized;
|
|
532
|
+
if (emailsBugsInput !== undefined) {
|
|
533
|
+
emails.bugs = emailsBugsInput;
|
|
534
|
+
}
|
|
535
|
+
else {
|
|
536
|
+
Reflect.deleteProperty(emails, 'bugs');
|
|
537
|
+
}
|
|
538
|
+
if (Object.keys(emails).length > 0) {
|
|
539
|
+
Object.assign(config, { emails });
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
Reflect.deleteProperty(config, 'emails');
|
|
543
|
+
}
|
|
544
|
+
Logger.customize({
|
|
545
|
+
name: 'CLIUtilityInitialize.promptEmails',
|
|
546
|
+
purpose: 'updated',
|
|
547
|
+
padTop: 1,
|
|
548
|
+
padBottom: 1,
|
|
549
|
+
}).info('Email references updated.');
|
|
550
|
+
return 'back';
|
|
551
|
+
}
|
|
533
552
|
static async promptUrls(config) {
|
|
534
553
|
const existingUrls = config.urls;
|
|
535
554
|
const urls = (existingUrls !== undefined) ? { ...existingUrls } : {};
|
|
536
|
-
const validatedUrls = {};
|
|
537
|
-
const validate = (key, input) => {
|
|
538
|
-
const field = (key === 'repository') ? 'repository' : undefined;
|
|
539
|
-
const sanitizedUrl = CLIUtilityInitialize.sanitizeHttpUrl(input, field);
|
|
540
|
-
if (sanitizedUrl !== undefined) {
|
|
541
|
-
validatedUrls[key] = sanitizedUrl;
|
|
542
|
-
}
|
|
543
|
-
};
|
|
544
555
|
const questionsOutput = await CLIUtilityInitialize.promptWithCancel([
|
|
545
556
|
{
|
|
546
557
|
type: 'text',
|
|
547
558
|
name: 'urlsHomepage',
|
|
548
559
|
message: 'Homepage URL',
|
|
549
560
|
initial: urls.homepage ?? '',
|
|
550
|
-
validate: (value) => CLIUtilityInitialize.
|
|
561
|
+
validate: (value) => CLIUtilityInitialize.normalizeUrl(value, 'generic').result,
|
|
551
562
|
},
|
|
552
563
|
{
|
|
553
564
|
type: 'text',
|
|
554
565
|
name: 'urlsRepository',
|
|
555
566
|
message: 'Repository URL',
|
|
556
567
|
initial: urls.repository ?? '',
|
|
557
|
-
validate: (value) => CLIUtilityInitialize.
|
|
568
|
+
validate: (value) => CLIUtilityInitialize.normalizeUrl(value, 'repository').result,
|
|
558
569
|
},
|
|
559
570
|
{
|
|
560
571
|
type: 'text',
|
|
561
572
|
name: 'urlsBugs',
|
|
562
573
|
message: 'Issue tracker URL',
|
|
563
574
|
initial: urls.bugs ?? '',
|
|
564
|
-
validate: (value) => CLIUtilityInitialize.
|
|
575
|
+
validate: (value) => CLIUtilityInitialize.normalizeUrl(value, 'generic').result,
|
|
565
576
|
},
|
|
566
577
|
{
|
|
567
578
|
type: 'text',
|
|
568
579
|
name: 'urlsLicense',
|
|
569
580
|
message: 'License URL',
|
|
570
581
|
initial: urls.license ?? '',
|
|
571
|
-
validate: (value) => CLIUtilityInitialize.
|
|
582
|
+
validate: (value) => CLIUtilityInitialize.normalizeUrl(value, 'generic').result,
|
|
572
583
|
},
|
|
573
584
|
{
|
|
574
585
|
type: 'text',
|
|
575
586
|
name: 'urlsLogo',
|
|
576
587
|
message: 'Logo URL',
|
|
577
588
|
initial: urls.logo ?? '',
|
|
578
|
-
validate: (value) => CLIUtilityInitialize.
|
|
589
|
+
validate: (value) => CLIUtilityInitialize.normalizeUrl(value, 'generic').result,
|
|
579
590
|
},
|
|
580
591
|
{
|
|
581
592
|
type: 'text',
|
|
582
593
|
name: 'urlsDocumentation',
|
|
583
594
|
message: 'Documentation URL',
|
|
584
595
|
initial: urls.documentation ?? '',
|
|
585
|
-
validate: (value) => CLIUtilityInitialize.
|
|
596
|
+
validate: (value) => CLIUtilityInitialize.normalizeUrl(value, 'generic').result,
|
|
586
597
|
},
|
|
587
598
|
{
|
|
588
599
|
type: 'text',
|
|
589
600
|
name: 'urlsGithub',
|
|
590
601
|
message: 'GitHub URL',
|
|
591
602
|
initial: urls.github ?? '',
|
|
592
|
-
validate: (value) => CLIUtilityInitialize.
|
|
603
|
+
validate: (value) => CLIUtilityInitialize.normalizeUrl(value, 'generic').result,
|
|
593
604
|
},
|
|
594
605
|
{
|
|
595
606
|
type: 'text',
|
|
596
607
|
name: 'urlsNpm',
|
|
597
608
|
message: 'npm package URL',
|
|
598
609
|
initial: urls.npm ?? '',
|
|
599
|
-
validate: (value) => CLIUtilityInitialize.
|
|
610
|
+
validate: (value) => CLIUtilityInitialize.normalizeUrl(value, 'generic').result,
|
|
600
611
|
},
|
|
601
612
|
{
|
|
602
613
|
type: 'text',
|
|
603
614
|
name: 'urlsFundSources',
|
|
604
615
|
message: 'Funding URLs (comma separated)',
|
|
605
616
|
initial: (Array.isArray(urls.fundSources)) ? urls.fundSources.join(', ') : '',
|
|
606
|
-
validate: CLIUtilityInitialize.
|
|
617
|
+
validate: (value) => CLIUtilityInitialize.normalizeUrlArray(value, 'generic').result,
|
|
607
618
|
},
|
|
608
619
|
]);
|
|
609
620
|
if (questionsOutput.cancelled) {
|
|
610
621
|
return 'back';
|
|
611
622
|
}
|
|
612
623
|
const questionsOutputResult = questionsOutput.result;
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
const
|
|
622
|
-
|
|
623
|
-
.
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
if (fundSourcesList.length > 0) {
|
|
634
|
-
validatedUrls.fundSources = fundSourcesList;
|
|
635
|
-
}
|
|
624
|
+
const urlsHomepageInput = CLIUtilityInitialize.normalizeUrl(questionsOutputResult.urlsHomepage, 'generic').sanitized;
|
|
625
|
+
const urlsRepositoryInput = CLIUtilityInitialize.normalizeUrl(questionsOutputResult.urlsRepository, 'repository').sanitized;
|
|
626
|
+
const urlsBugsInput = CLIUtilityInitialize.normalizeUrl(questionsOutputResult.urlsBugs, 'generic').sanitized;
|
|
627
|
+
const urlsLicenseInput = CLIUtilityInitialize.normalizeUrl(questionsOutputResult.urlsLicense, 'generic').sanitized;
|
|
628
|
+
const urlsLogoInput = CLIUtilityInitialize.normalizeUrl(questionsOutputResult.urlsLogo, 'generic').sanitized;
|
|
629
|
+
const urlsDocumentationInput = CLIUtilityInitialize.normalizeUrl(questionsOutputResult.urlsDocumentation, 'generic').sanitized;
|
|
630
|
+
const urlsGithubInput = CLIUtilityInitialize.normalizeUrl(questionsOutputResult.urlsGithub, 'generic').sanitized;
|
|
631
|
+
const urlsNpmInput = CLIUtilityInitialize.normalizeUrl(questionsOutputResult.urlsNpm, 'generic').sanitized;
|
|
632
|
+
const urlsFundSourcesInput = CLIUtilityInitialize.normalizeUrlArray(questionsOutputResult.urlsFundSources, 'generic').sanitized;
|
|
633
|
+
if (urlsHomepageInput !== undefined) {
|
|
634
|
+
urls.homepage = urlsHomepageInput;
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
Reflect.deleteProperty(urls, 'homepage');
|
|
638
|
+
}
|
|
639
|
+
if (urlsRepositoryInput !== undefined) {
|
|
640
|
+
urls.repository = urlsRepositoryInput;
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
Reflect.deleteProperty(urls, 'repository');
|
|
636
644
|
}
|
|
637
|
-
if (
|
|
638
|
-
|
|
645
|
+
if (urlsBugsInput !== undefined) {
|
|
646
|
+
urls.bugs = urlsBugsInput;
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
Reflect.deleteProperty(urls, 'bugs');
|
|
650
|
+
}
|
|
651
|
+
if (urlsLicenseInput !== undefined) {
|
|
652
|
+
urls.license = urlsLicenseInput;
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
Reflect.deleteProperty(urls, 'license');
|
|
656
|
+
}
|
|
657
|
+
if (urlsLogoInput !== undefined) {
|
|
658
|
+
urls.logo = urlsLogoInput;
|
|
659
|
+
}
|
|
660
|
+
else {
|
|
661
|
+
Reflect.deleteProperty(urls, 'logo');
|
|
662
|
+
}
|
|
663
|
+
if (urlsDocumentationInput !== undefined) {
|
|
664
|
+
urls.documentation = urlsDocumentationInput;
|
|
665
|
+
}
|
|
666
|
+
else {
|
|
667
|
+
Reflect.deleteProperty(urls, 'documentation');
|
|
668
|
+
}
|
|
669
|
+
if (urlsGithubInput !== undefined) {
|
|
670
|
+
urls.github = urlsGithubInput;
|
|
671
|
+
}
|
|
672
|
+
else {
|
|
673
|
+
Reflect.deleteProperty(urls, 'github');
|
|
674
|
+
}
|
|
675
|
+
if (urlsNpmInput !== undefined) {
|
|
676
|
+
urls.npm = urlsNpmInput;
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
Reflect.deleteProperty(urls, 'npm');
|
|
680
|
+
}
|
|
681
|
+
if (urlsFundSourcesInput !== undefined) {
|
|
682
|
+
urls.fundSources = urlsFundSourcesInput;
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
Reflect.deleteProperty(urls, 'fundSources');
|
|
686
|
+
}
|
|
687
|
+
if (Object.keys(urls).length > 0) {
|
|
688
|
+
Object.assign(config, { urls });
|
|
639
689
|
}
|
|
640
690
|
else {
|
|
641
691
|
Reflect.deleteProperty(config, 'urls');
|
|
@@ -652,11 +702,11 @@ export class CLIUtilityInitialize {
|
|
|
652
702
|
const workspaces = (config.workspaces) ? { ...(config.workspaces) } : {};
|
|
653
703
|
const rawWorkspacePaths = await discoverPathsWithFile('package.json', 'forward');
|
|
654
704
|
const workspacePaths = rawWorkspacePaths.map((rawWorkspacePath) => {
|
|
655
|
-
const relativePath =
|
|
705
|
+
const relativePath = relative(process.cwd(), rawWorkspacePath);
|
|
656
706
|
if (relativePath === '') {
|
|
657
707
|
return './';
|
|
658
708
|
}
|
|
659
|
-
return `./${relativePath.split(
|
|
709
|
+
return `./${relativePath.split(sep).join('/')}`;
|
|
660
710
|
});
|
|
661
711
|
Logger.customize({
|
|
662
712
|
name: 'CLIUtilityInitialize.promptWorkspaces',
|
|
@@ -666,13 +716,13 @@ export class CLIUtilityInitialize {
|
|
|
666
716
|
const choices = workspacePaths.map((workspacePath) => {
|
|
667
717
|
const workspace = workspaces[workspacePath];
|
|
668
718
|
const summaryParts = [];
|
|
669
|
-
if (workspace.name) {
|
|
719
|
+
if (workspace !== undefined && workspace.name !== undefined) {
|
|
670
720
|
summaryParts.push(workspace.name);
|
|
671
721
|
}
|
|
672
|
-
if (workspace.role) {
|
|
722
|
+
if (workspace !== undefined && workspace.role !== undefined) {
|
|
673
723
|
summaryParts.push(workspace.role);
|
|
674
724
|
}
|
|
675
|
-
if (workspace.policy) {
|
|
725
|
+
if (workspace !== undefined && workspace.policy !== undefined) {
|
|
676
726
|
summaryParts.push(workspace.policy);
|
|
677
727
|
}
|
|
678
728
|
return {
|
|
@@ -704,12 +754,12 @@ export class CLIUtilityInitialize {
|
|
|
704
754
|
const formResult = await CLIUtilityInitialize.promptWorkspacesForm({
|
|
705
755
|
workspacePath,
|
|
706
756
|
existingWorkspace: workspaces[workspacePath],
|
|
707
|
-
projectSlug: config.project
|
|
757
|
+
projectSlug: (config.project !== undefined && config.project.name !== undefined) ? config.project.name.slug : undefined,
|
|
708
758
|
});
|
|
709
759
|
if (formResult.action === 'back') {
|
|
710
760
|
continue;
|
|
711
761
|
}
|
|
712
|
-
workspaces
|
|
762
|
+
Reflect.set(workspaces, workspacePath, formResult.workspace);
|
|
713
763
|
Object.assign(config, { workspaces });
|
|
714
764
|
Logger.customize({
|
|
715
765
|
name: 'CLIUtilityInitialize.promptWorkspaces',
|
|
@@ -751,6 +801,11 @@ export class CLIUtilityInitialize {
|
|
|
751
801
|
description: 'Internal CLI or build tools (e.g., codegen, bundler)',
|
|
752
802
|
value: 'tool',
|
|
753
803
|
},
|
|
804
|
+
{
|
|
805
|
+
title: 'Template',
|
|
806
|
+
description: 'Ready-to-copy scaffold bundles consumed by generators (e.g., starter files)',
|
|
807
|
+
value: 'template',
|
|
808
|
+
},
|
|
754
809
|
];
|
|
755
810
|
const policy = {
|
|
756
811
|
freezable: {
|
|
@@ -778,42 +833,13 @@ export class CLIUtilityInitialize {
|
|
|
778
833
|
type: 'text',
|
|
779
834
|
name: 'workspaceName',
|
|
780
835
|
message: 'Workspace package name',
|
|
781
|
-
initial: options.existingWorkspace.name,
|
|
782
|
-
validate: (value) =>
|
|
783
|
-
const trimmed = value.trim();
|
|
784
|
-
if (trimmed === '') {
|
|
785
|
-
return 'Enter a package name.';
|
|
786
|
-
}
|
|
787
|
-
switch (role) {
|
|
788
|
-
case 'config':
|
|
789
|
-
case 'app':
|
|
790
|
-
case 'tool':
|
|
791
|
-
const expectedPrefix = `${base}-`;
|
|
792
|
-
if (!trimmed.startsWith(expectedPrefix)) {
|
|
793
|
-
return `Begin with "${expectedPrefix}" and add a descriptor slug.`;
|
|
794
|
-
}
|
|
795
|
-
const descriptor = trimmed.slice(expectedPrefix.length);
|
|
796
|
-
if (descriptor.length === 0) {
|
|
797
|
-
return 'Add a descriptor after the prefix.';
|
|
798
|
-
}
|
|
799
|
-
if (!PATTERN_SLUG_SIMPLE.test(descriptor)) {
|
|
800
|
-
return 'Descriptor must match the slug pattern (lowercase letters, numbers, hyphens, underscores).';
|
|
801
|
-
}
|
|
802
|
-
return true;
|
|
803
|
-
case 'package':
|
|
804
|
-
default:
|
|
805
|
-
if (PATTERN_SLUG_SIMPLE.test(trimmed) || PATTERN_SLUG_SCOPED.test(trimmed)) {
|
|
806
|
-
return true;
|
|
807
|
-
}
|
|
808
|
-
return 'Enter an unscoped slug or a scoped package name (e.g. @scope/name).';
|
|
809
|
-
}
|
|
810
|
-
},
|
|
836
|
+
initial: (options.existingWorkspace !== undefined) ? options.existingWorkspace.name ?? '' : '',
|
|
837
|
+
validate: (value) => CLIUtilityInitialize.normalizeWorkspaceName(value, role, base).result,
|
|
811
838
|
});
|
|
812
839
|
if (namePrompt.cancelled) {
|
|
813
840
|
return undefined;
|
|
814
841
|
}
|
|
815
|
-
|
|
816
|
-
return (name === '') ? undefined : name;
|
|
842
|
+
return CLIUtilityInitialize.normalizeWorkspaceName(namePrompt.result.workspaceName, role, base).sanitized;
|
|
817
843
|
};
|
|
818
844
|
const rolePrompt = await CLIUtilityInitialize.promptWithCancel({
|
|
819
845
|
type: 'select',
|
|
@@ -824,7 +850,7 @@ export class CLIUtilityInitialize {
|
|
|
824
850
|
description: role.description,
|
|
825
851
|
value: role.value,
|
|
826
852
|
})),
|
|
827
|
-
initial: Math.max(0, allowedRoles.findIndex((role) => role.value === options.existingWorkspace.role)),
|
|
853
|
+
initial: Math.max(0, allowedRoles.findIndex((role) => options.existingWorkspace !== undefined && role.value === options.existingWorkspace.role)),
|
|
828
854
|
});
|
|
829
855
|
if (rolePrompt.cancelled) {
|
|
830
856
|
return {
|
|
@@ -842,7 +868,7 @@ export class CLIUtilityInitialize {
|
|
|
842
868
|
description: policy[allowedPolicy].description,
|
|
843
869
|
value: allowedPolicy,
|
|
844
870
|
})),
|
|
845
|
-
initial: Math.max(0, allowedPolicies.findIndex((policy) => policy === options.existingWorkspace.policy)),
|
|
871
|
+
initial: Math.max(0, allowedPolicies.findIndex((policy) => options.existingWorkspace !== undefined && policy === options.existingWorkspace.policy)),
|
|
846
872
|
});
|
|
847
873
|
if (policyPrompt.cancelled) {
|
|
848
874
|
return {
|
|
@@ -856,12 +882,61 @@ export class CLIUtilityInitialize {
|
|
|
856
882
|
action: 'back',
|
|
857
883
|
};
|
|
858
884
|
}
|
|
885
|
+
let syncProperties;
|
|
886
|
+
if (selectedPolicy === 'distributable') {
|
|
887
|
+
const syncPropertiesPrompt = await CLIUtilityInitialize.promptWithCancel({
|
|
888
|
+
type: 'multiselect',
|
|
889
|
+
name: 'workspaceSyncProperties',
|
|
890
|
+
message: 'Select metadata properties to sync',
|
|
891
|
+
choices: itemAllowedSyncProperties.map((property) => ({
|
|
892
|
+
title: property,
|
|
893
|
+
value: property,
|
|
894
|
+
selected: (options.existingWorkspace !== undefined && options.existingWorkspace.syncProperties !== undefined) ? options.existingWorkspace.syncProperties.includes(property) : false,
|
|
895
|
+
})),
|
|
896
|
+
});
|
|
897
|
+
if (syncPropertiesPrompt.cancelled) {
|
|
898
|
+
return {
|
|
899
|
+
action: 'back',
|
|
900
|
+
};
|
|
901
|
+
}
|
|
902
|
+
const selectedSyncProperties = syncPropertiesPrompt.result.workspaceSyncProperties;
|
|
903
|
+
if (selectedSyncProperties.length > 0) {
|
|
904
|
+
syncProperties = selectedSyncProperties;
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
const pinVersionsPrompt = await CLIUtilityInitialize.promptWithCancel({
|
|
908
|
+
type: 'confirm',
|
|
909
|
+
name: 'workspacePinVersions',
|
|
910
|
+
message: 'Pin dependency versions?',
|
|
911
|
+
initial: options.existingWorkspace !== undefined && options.existingWorkspace.pinVersions === true,
|
|
912
|
+
});
|
|
913
|
+
if (pinVersionsPrompt.cancelled) {
|
|
914
|
+
return {
|
|
915
|
+
action: 'back',
|
|
916
|
+
};
|
|
917
|
+
}
|
|
918
|
+
const selectedPinVersions = pinVersionsPrompt.result.workspacePinVersions;
|
|
919
|
+
const syncLtsEnginesPrompt = await CLIUtilityInitialize.promptWithCancel({
|
|
920
|
+
type: 'confirm',
|
|
921
|
+
name: 'workspaceSyncLtsEngines',
|
|
922
|
+
message: 'Sync Node.js LTS engine constraint?',
|
|
923
|
+
initial: options.existingWorkspace !== undefined && options.existingWorkspace.syncLtsEngines === true,
|
|
924
|
+
});
|
|
925
|
+
if (syncLtsEnginesPrompt.cancelled) {
|
|
926
|
+
return {
|
|
927
|
+
action: 'back',
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
const selectedSyncLtsEngines = syncLtsEnginesPrompt.result.workspaceSyncLtsEngines;
|
|
859
931
|
return {
|
|
860
932
|
action: 'apply',
|
|
861
933
|
workspace: {
|
|
862
934
|
name: resolvedName,
|
|
863
935
|
role: selectedRole,
|
|
864
936
|
policy: selectedPolicy,
|
|
937
|
+
...(syncProperties !== undefined) ? { syncProperties } : {},
|
|
938
|
+
...(selectedPinVersions === true) ? { pinVersions: selectedPinVersions } : {},
|
|
939
|
+
...(selectedSyncLtsEngines === true) ? { syncLtsEngines: selectedSyncLtsEngines } : {},
|
|
865
940
|
},
|
|
866
941
|
};
|
|
867
942
|
}
|
|
@@ -893,7 +968,6 @@ export class CLIUtilityInitialize {
|
|
|
893
968
|
Logger.customize({
|
|
894
969
|
name: 'CLIUtilityInitialize.checkPath',
|
|
895
970
|
purpose: 'lessThanOne',
|
|
896
|
-
padBottom: 1,
|
|
897
971
|
}).error([
|
|
898
972
|
'No "package.json" files were found. Re-run this command inside the project root directory.',
|
|
899
973
|
`Current directory is "${currentDirectory}"`,
|
|
@@ -904,7 +978,6 @@ export class CLIUtilityInitialize {
|
|
|
904
978
|
Logger.customize({
|
|
905
979
|
name: 'CLIUtilityInitialize.checkPath',
|
|
906
980
|
purpose: 'greaterThanOne',
|
|
907
|
-
padBottom: 1,
|
|
908
981
|
}).error([
|
|
909
982
|
'Multiple "package.json" files were found. Re-run this command inside the project root directory.',
|
|
910
983
|
`Current directory is "${currentDirectory}"`,
|
|
@@ -915,7 +988,6 @@ export class CLIUtilityInitialize {
|
|
|
915
988
|
Logger.customize({
|
|
916
989
|
name: 'CLIUtilityInitialize.checkPath',
|
|
917
990
|
purpose: 'notProjectRootDir',
|
|
918
|
-
padBottom: 1,
|
|
919
991
|
}).error([
|
|
920
992
|
'Must be run inside the project root directory.',
|
|
921
993
|
`Current directory is "${currentDirectory}"`,
|
|
@@ -924,60 +996,256 @@ export class CLIUtilityInitialize {
|
|
|
924
996
|
}
|
|
925
997
|
return true;
|
|
926
998
|
}
|
|
927
|
-
static
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
999
|
+
static normalizeEmail(value) {
|
|
1000
|
+
if (typeof value !== 'string') {
|
|
1001
|
+
return {
|
|
1002
|
+
result: `Unexpected type error. Expect type to be "string", got "${typeof value}".`,
|
|
1003
|
+
sanitized: undefined,
|
|
1004
|
+
};
|
|
1005
|
+
}
|
|
1006
|
+
const trimmedValue = value.trim();
|
|
1007
|
+
if (trimmedValue === '') {
|
|
1008
|
+
return {
|
|
1009
|
+
result: true,
|
|
1010
|
+
sanitized: undefined,
|
|
1011
|
+
};
|
|
931
1012
|
}
|
|
932
|
-
if (
|
|
933
|
-
return
|
|
1013
|
+
if (!PATTERN_EMAIL_SIMPLE.test(trimmedValue)) {
|
|
1014
|
+
return {
|
|
1015
|
+
result: 'Enter a valid email address or leave blank.',
|
|
1016
|
+
sanitized: undefined,
|
|
1017
|
+
};
|
|
934
1018
|
}
|
|
935
|
-
return
|
|
1019
|
+
return {
|
|
1020
|
+
result: true,
|
|
1021
|
+
sanitized: trimmedValue,
|
|
1022
|
+
};
|
|
936
1023
|
}
|
|
937
|
-
static
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
1024
|
+
static normalizeProjectSlug(value) {
|
|
1025
|
+
if (typeof value !== 'string') {
|
|
1026
|
+
return {
|
|
1027
|
+
result: `Unexpected type error. Expect type to be "string", got "${typeof value}".`,
|
|
1028
|
+
sanitized: undefined,
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1031
|
+
const trimmedValue = value.trim();
|
|
1032
|
+
if (trimmedValue === '') {
|
|
1033
|
+
return {
|
|
1034
|
+
result: true,
|
|
1035
|
+
sanitized: undefined,
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
if (trimmedValue.length > 214
|
|
1039
|
+
|| !new RegExp(PATTERN_SLUG_SIMPLE, 'i').test(trimmedValue)) {
|
|
1040
|
+
return {
|
|
1041
|
+
result: 'Use only letters, numbers, hyphens, or underscores, and keep it at 214 characters or fewer.',
|
|
1042
|
+
sanitized: undefined,
|
|
1043
|
+
};
|
|
1044
|
+
}
|
|
1045
|
+
return {
|
|
1046
|
+
result: true,
|
|
1047
|
+
sanitized: trimmedValue,
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
static normalizeText(value, maxLength) {
|
|
1051
|
+
if (typeof value !== 'string') {
|
|
1052
|
+
return {
|
|
1053
|
+
result: `Unexpected type error. Expect type to be "string", got "${typeof value}".`,
|
|
1054
|
+
sanitized: undefined,
|
|
1055
|
+
};
|
|
1056
|
+
}
|
|
1057
|
+
const trimmedValue = value.trim();
|
|
1058
|
+
if (trimmedValue === '') {
|
|
1059
|
+
return {
|
|
1060
|
+
result: true,
|
|
1061
|
+
sanitized: undefined,
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
if (trimmedValue.length > maxLength) {
|
|
1065
|
+
return {
|
|
1066
|
+
result: `Input a value under ${maxLength} character(s) or leave blank.`,
|
|
1067
|
+
sanitized: undefined,
|
|
1068
|
+
};
|
|
1069
|
+
}
|
|
1070
|
+
return {
|
|
1071
|
+
result: true,
|
|
1072
|
+
sanitized: trimmedValue,
|
|
1073
|
+
};
|
|
1074
|
+
}
|
|
1075
|
+
static normalizeTextArray(value, maxLengthPerItem) {
|
|
1076
|
+
if (typeof value !== 'string') {
|
|
1077
|
+
return {
|
|
1078
|
+
result: `Unexpected type error. Expect type to be "string", got "${typeof value}".`,
|
|
1079
|
+
sanitized: undefined,
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
const trimmedValue = value.trim();
|
|
1083
|
+
if (trimmedValue === '') {
|
|
1084
|
+
return {
|
|
1085
|
+
result: true,
|
|
1086
|
+
sanitized: undefined,
|
|
1087
|
+
};
|
|
941
1088
|
}
|
|
942
|
-
const
|
|
1089
|
+
const items = trimmedValue
|
|
943
1090
|
.split(',')
|
|
944
|
-
.map((
|
|
945
|
-
.filter((
|
|
946
|
-
for (
|
|
947
|
-
|
|
948
|
-
|
|
1091
|
+
.map((item) => item.trim())
|
|
1092
|
+
.filter((item) => item !== '');
|
|
1093
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
1094
|
+
const { result, sanitized } = CLIUtilityInitialize.normalizeText(items[i], maxLengthPerItem);
|
|
1095
|
+
if (result !== true) {
|
|
1096
|
+
return {
|
|
1097
|
+
result: `Invalid entry "${items[i]}": Input a value under ${maxLengthPerItem} character(s) or remove entry.`,
|
|
1098
|
+
sanitized: undefined,
|
|
1099
|
+
};
|
|
1100
|
+
}
|
|
1101
|
+
if (sanitized !== undefined) {
|
|
1102
|
+
items[i] = sanitized;
|
|
949
1103
|
}
|
|
950
1104
|
}
|
|
951
|
-
return
|
|
1105
|
+
return {
|
|
1106
|
+
result: true,
|
|
1107
|
+
sanitized: items,
|
|
1108
|
+
};
|
|
952
1109
|
}
|
|
953
|
-
static
|
|
1110
|
+
static normalizeUrl(value, protocol) {
|
|
954
1111
|
if (typeof value !== 'string') {
|
|
955
|
-
return
|
|
1112
|
+
return {
|
|
1113
|
+
result: `Unexpected type error. Expect type to be "string", got "${typeof value}".`,
|
|
1114
|
+
sanitized: undefined,
|
|
1115
|
+
};
|
|
956
1116
|
}
|
|
957
|
-
const
|
|
958
|
-
if (
|
|
959
|
-
return
|
|
1117
|
+
const trimmedValue = value.trim();
|
|
1118
|
+
if (trimmedValue === '') {
|
|
1119
|
+
return {
|
|
1120
|
+
result: true,
|
|
1121
|
+
sanitized: undefined,
|
|
1122
|
+
};
|
|
960
1123
|
}
|
|
1124
|
+
const rules = {
|
|
1125
|
+
generic: {
|
|
1126
|
+
allowed: ['http:', 'https:'],
|
|
1127
|
+
message: 'Enter a valid generic URL (e.g., https://) or leave blank.',
|
|
1128
|
+
},
|
|
1129
|
+
repository: {
|
|
1130
|
+
allowed: ['git:', 'git+https:', 'git+ssh:', 'git+http:', 'http:', 'https:'],
|
|
1131
|
+
message: 'Enter a valid repository URL (e.g., git+https://) or leave blank.',
|
|
1132
|
+
},
|
|
1133
|
+
};
|
|
1134
|
+
const allowed = (protocol === 'repository') ? rules.repository.allowed : rules.generic.allowed;
|
|
1135
|
+
const errorMessage = (protocol === 'repository') ? rules.repository.message : rules.generic.message;
|
|
961
1136
|
try {
|
|
962
|
-
const
|
|
963
|
-
if (
|
|
964
|
-
return
|
|
1137
|
+
const url = new URL(trimmedValue);
|
|
1138
|
+
if (allowed.includes(url.protocol)) {
|
|
1139
|
+
return {
|
|
1140
|
+
result: true,
|
|
1141
|
+
sanitized: url.toString(),
|
|
1142
|
+
};
|
|
965
1143
|
}
|
|
966
1144
|
}
|
|
967
1145
|
catch {
|
|
968
1146
|
}
|
|
969
|
-
return
|
|
1147
|
+
return {
|
|
1148
|
+
result: errorMessage,
|
|
1149
|
+
sanitized: undefined,
|
|
1150
|
+
};
|
|
970
1151
|
}
|
|
971
|
-
static
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
return allowedProtocols.includes(url.protocol);
|
|
1152
|
+
static normalizeUrlArray(value, protocol) {
|
|
1153
|
+
if (typeof value !== 'string') {
|
|
1154
|
+
return {
|
|
1155
|
+
result: `Unexpected type error. Expect type to be "string", got "${typeof value}".`,
|
|
1156
|
+
sanitized: undefined,
|
|
1157
|
+
};
|
|
978
1158
|
}
|
|
979
|
-
|
|
980
|
-
|
|
1159
|
+
const trimmedValue = value.trim();
|
|
1160
|
+
if (trimmedValue === '') {
|
|
1161
|
+
return {
|
|
1162
|
+
result: true,
|
|
1163
|
+
sanitized: undefined,
|
|
1164
|
+
};
|
|
1165
|
+
}
|
|
1166
|
+
const items = trimmedValue
|
|
1167
|
+
.split(',')
|
|
1168
|
+
.map((item) => item.trim())
|
|
1169
|
+
.filter((item) => item !== '');
|
|
1170
|
+
for (let i = 0; i < items.length; i += 1) {
|
|
1171
|
+
const { result, sanitized } = CLIUtilityInitialize.normalizeUrl(items[i], protocol);
|
|
1172
|
+
if (result !== true) {
|
|
1173
|
+
const errorMessages = {
|
|
1174
|
+
generic: 'Enter a valid generic URL (e.g., https://) or remove entry.',
|
|
1175
|
+
repository: 'Enter a valid repository URL (e.g., git+https://) or remove entry.',
|
|
1176
|
+
};
|
|
1177
|
+
const errorMessage = (protocol === 'repository') ? errorMessages.repository : errorMessages.generic;
|
|
1178
|
+
return {
|
|
1179
|
+
result: `Invalid URL "${items[i]}": ${errorMessage}`,
|
|
1180
|
+
sanitized: undefined,
|
|
1181
|
+
};
|
|
1182
|
+
}
|
|
1183
|
+
if (sanitized !== undefined) {
|
|
1184
|
+
items[i] = sanitized;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
return {
|
|
1188
|
+
result: true,
|
|
1189
|
+
sanitized: items,
|
|
1190
|
+
};
|
|
1191
|
+
}
|
|
1192
|
+
static normalizeWorkspaceName(value, role, base) {
|
|
1193
|
+
if (typeof value !== 'string') {
|
|
1194
|
+
return {
|
|
1195
|
+
result: `Unexpected type error. Expect type to be "string", got "${typeof value}".`,
|
|
1196
|
+
sanitized: undefined,
|
|
1197
|
+
};
|
|
1198
|
+
}
|
|
1199
|
+
const trimmedValue = value.trim();
|
|
1200
|
+
if (trimmedValue === '') {
|
|
1201
|
+
return {
|
|
1202
|
+
result: 'Enter a package name.',
|
|
1203
|
+
sanitized: undefined,
|
|
1204
|
+
};
|
|
1205
|
+
}
|
|
1206
|
+
switch (role) {
|
|
1207
|
+
case 'config':
|
|
1208
|
+
case 'app':
|
|
1209
|
+
case 'tool': {
|
|
1210
|
+
const expectedPrefix = `${base}-`;
|
|
1211
|
+
if (!trimmedValue.startsWith(expectedPrefix)) {
|
|
1212
|
+
return {
|
|
1213
|
+
result: `Begin with "${expectedPrefix}" and add a descriptor slug.`,
|
|
1214
|
+
sanitized: undefined,
|
|
1215
|
+
};
|
|
1216
|
+
}
|
|
1217
|
+
const descriptor = trimmedValue.slice(expectedPrefix.length);
|
|
1218
|
+
if (descriptor.length === 0) {
|
|
1219
|
+
return {
|
|
1220
|
+
result: 'Add a descriptor after the prefix.',
|
|
1221
|
+
sanitized: undefined,
|
|
1222
|
+
};
|
|
1223
|
+
}
|
|
1224
|
+
if (!PATTERN_SLUG_SIMPLE.test(descriptor)) {
|
|
1225
|
+
return {
|
|
1226
|
+
result: 'Descriptor must match the slug pattern (lowercase letters, numbers, hyphens, underscores).',
|
|
1227
|
+
sanitized: undefined,
|
|
1228
|
+
};
|
|
1229
|
+
}
|
|
1230
|
+
return {
|
|
1231
|
+
result: true,
|
|
1232
|
+
sanitized: trimmedValue,
|
|
1233
|
+
};
|
|
1234
|
+
}
|
|
1235
|
+
case 'template':
|
|
1236
|
+
case 'package':
|
|
1237
|
+
default: {
|
|
1238
|
+
if (PATTERN_SLUG_SIMPLE.test(trimmedValue) || PATTERN_SLUG_SCOPED.test(trimmedValue)) {
|
|
1239
|
+
return {
|
|
1240
|
+
result: true,
|
|
1241
|
+
sanitized: trimmedValue,
|
|
1242
|
+
};
|
|
1243
|
+
}
|
|
1244
|
+
return {
|
|
1245
|
+
result: 'Enter an unscoped slug or a scoped package name (e.g. @scope/name).',
|
|
1246
|
+
sanitized: undefined,
|
|
1247
|
+
};
|
|
1248
|
+
}
|
|
981
1249
|
}
|
|
982
1250
|
}
|
|
983
1251
|
}
|