nibula 1.6.0 → 1.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.eleventy.js +81 -81
- package/CHANGELOG.md +384 -377
- package/LICENSE +169 -169
- package/NOTICE +4 -4
- package/README.md +110 -110
- package/bin/create.js +505 -505
- package/bin/delegate.js +57 -57
- package/bin/nibula.js +302 -302
- package/docs/Assistant CLI.md +65 -65
- package/docs/Backend.md +201 -201
- package/docs/Components.md +140 -140
- package/docs/Creating pages.md +69 -69
- package/docs/Deploy.md +210 -210
- package/docs/Head and SEO.md +139 -139
- package/docs/Javascript.md +126 -126
- package/docs/Styling with SCSS.md +139 -139
- package/nginx.conf +113 -113
- package/package.json +69 -69
- package/src/backend/.htaccess +6 -6
- package/src/backend/_core/composer.json +5 -5
- package/src/backend/_core/composer.lock +492 -492
- package/src/backend/_core/index.js +267 -267
- package/src/backend/_core/index.php +160 -160
- package/src/backend/_core/init.js +52 -52
- package/src/backend/_core/init.php +33 -33
- package/src/backend/_core/modules/RateLimiter.js +58 -58
- package/src/backend/_core/modules/RateLimiter.php +30 -30
- package/src/backend/_core/modules/Response.js +59 -59
- package/src/backend/_core/modules/Response.php +48 -48
- package/src/backend/api/protected/example-protected.js +23 -23
- package/src/backend/api/protected/example-protected.php +16 -16
- package/src/backend/api/public/example-public.js +24 -24
- package/src/backend/api/public/example-public.php +16 -16
- package/src/backend/database/Database.js +46 -46
- package/src/backend/database/Database.php +23 -23
- package/src/backend/example.config.js +37 -37
- package/src/backend/example.config.php +37 -37
- package/src/backend/package.json +17 -17
- package/src/backend/web.config +43 -43
- package/src/frontend/.htaccess +51 -51
- package/src/frontend/404.njk +17 -17
- package/src/frontend/assets/brand/favicon.svg +54 -54
- package/src/frontend/assets/brand/logo.svg +54 -54
- package/src/frontend/components/global/footer.njk +25 -25
- package/src/frontend/components/global/header.njk +6 -6
- package/src/frontend/components/welcome.njk +115 -115
- package/src/frontend/index.njk +8 -8
- package/src/frontend/js/global.js +10 -10
- package/src/frontend/js/modules/exampleModule.js +2 -2
- package/src/frontend/js/pages/404.js +8 -8
- package/src/frontend/js/pages/homepage.js +8 -8
- package/src/frontend/layouts/base.njk +136 -136
- package/src/frontend/layouts/page-components.njk +13 -13
- package/src/frontend/llms.njk +17 -17
- package/src/frontend/robots.njk +7 -7
- package/src/frontend/scss/_global.scss +46 -46
- package/src/frontend/scss/_root.scss +13 -13
- package/src/frontend/scss/modules/_animations.scss +24 -24
- package/src/frontend/scss/modules/_footer.scss +28 -28
- package/src/frontend/scss/modules/_header.scss +26 -26
- package/src/frontend/scss/modules/_mobile.scss +29 -29
- package/src/frontend/scss/modules/_typography.scss +18 -18
- package/src/frontend/scss/modules/frameworks/_bootstrap.scss +109 -109
- package/src/frontend/scss/modules/frameworks/_bulma.scss +108 -108
- package/src/frontend/scss/modules/frameworks/_foundation.scss +137 -137
- package/src/frontend/scss/modules/frameworks/_uikit.scss +109 -109
- package/src/frontend/scss/pages/404.scss +27 -27
- package/src/frontend/scss/pages/homepage.scss +22 -22
- package/src/frontend/sitemap.njk +17 -17
- package/src/frontend/ts/global.ts +11 -11
- package/src/frontend/ts/modules/exampleModule.ts +2 -2
- package/src/frontend/ts/pages/404.ts +8 -8
- package/src/frontend/ts/pages/homepage.ts +8 -8
- package/src/frontend/web.config +51 -51
- package/tools/assistant.js +122 -122
- package/tools/buildJs.js +39 -39
- package/tools/cleanOutput.js +23 -23
- package/tools/cli/prompt.js +40 -40
- package/tools/cli/ui.js +87 -87
- package/tools/config/messages.json +80 -80
- package/tools/config/settings.json +130 -130
- package/tools/lib/colors.js +17 -17
- package/tools/lib/files.js +75 -75
- package/tools/lib/logger.js +22 -22
- package/tools/lib/outputPath.js +141 -141
- package/tools/lib/pageActions.js +146 -146
- package/tools/lib/pageArtifacts.js +46 -46
- package/tools/lib/pageComponents.js +88 -88
- package/tools/lib/pagesData.js +87 -87
- package/tools/lib/paths.js +63 -63
- package/tools/lib/project.js +54 -54
- package/tools/lib/text.js +47 -47
- package/tools/lib/validation.js +40 -40
- package/tools/res/templates/template.js +7 -7
- package/tools/res/templates/template.njk +8 -8
- package/tools/res/templates/template.scss +22 -22
- package/tools/res/templates/template.ts +8 -8
- package/tsconfig.json +24 -24
- package/src/frontend/scss/pages/prova.scss +0 -23
package/tools/assistant.js
CHANGED
|
@@ -1,123 +1,123 @@
|
|
|
1
|
-
const settings = require('./config/settings.json');
|
|
2
|
-
const { message } = require('./lib/logger');
|
|
3
|
-
const { toKebabCase } = require('./lib/text');
|
|
4
|
-
const { validatePageName, validateOutputPath, checkRequiredFiles } = require('./lib/validation');
|
|
5
|
-
const { addPage, removePage, renamePage } = require('./lib/pageActions');
|
|
6
|
-
const { pageExists } = require('./lib/pageArtifacts');
|
|
7
|
-
const { updateOutputPath, getCurrentOutputPath } = require('./lib/outputPath');
|
|
8
|
-
const { ask, confirm, close } = require('./cli/prompt');
|
|
9
|
-
const ui = require('./cli/ui');
|
|
10
|
-
|
|
11
|
-
const NEW_LINE = '\n';
|
|
12
|
-
const NO_PREFIX = '';
|
|
13
|
-
|
|
14
|
-
async function askPageName(prompt) {
|
|
15
|
-
const raw = await ask(prompt);
|
|
16
|
-
if (raw === null) return null;
|
|
17
|
-
|
|
18
|
-
const name = toKebabCase(raw);
|
|
19
|
-
const error = validatePageName(name);
|
|
20
|
-
if (error) {
|
|
21
|
-
ui.error(error);
|
|
22
|
-
return null;
|
|
23
|
-
}
|
|
24
|
-
return name;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
async function handleCreate() {
|
|
28
|
-
const name = await askPageName(ui.promptLine(message('cli.createPrompt')));
|
|
29
|
-
if (name) addPage(name);
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async function handleRemove() {
|
|
33
|
-
const name = await askPageName(ui.promptLine(message('cli.removePrompt')));
|
|
34
|
-
if (!name) return;
|
|
35
|
-
|
|
36
|
-
if (!pageExists(name)) {
|
|
37
|
-
ui.warning(message('cli.pageNotFound', { name }));
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const confirmed = await confirm(message('cli.confirmRemove', { name }));
|
|
42
|
-
if (!confirmed) {
|
|
43
|
-
ui.notice(message('cli.cancelled'));
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
removePage(name);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
async function handleRename() {
|
|
50
|
-
const oldName = await askPageName(ui.promptLine(message('cli.renameFromPrompt')));
|
|
51
|
-
if (!oldName) return;
|
|
52
|
-
|
|
53
|
-
const newName = await askPageName(ui.promptLine(message('cli.renameToPrompt'), NO_PREFIX));
|
|
54
|
-
if (!newName) return;
|
|
55
|
-
|
|
56
|
-
if (oldName === newName) {
|
|
57
|
-
ui.warning(message('cli.sameName'));
|
|
58
|
-
return;
|
|
59
|
-
}
|
|
60
|
-
renamePage(oldName, newName);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
async function handleOutputPath() {
|
|
64
|
-
const current = getCurrentOutputPath();
|
|
65
|
-
const prefix = current
|
|
66
|
-
? ui.noticePrefix(message('cli.currentOutputPath', { path: current }))
|
|
67
|
-
: NEW_LINE;
|
|
68
|
-
|
|
69
|
-
const input = await ask(ui.promptLine(message('cli.outputPathPrompt'), prefix));
|
|
70
|
-
if (input === null) return;
|
|
71
|
-
|
|
72
|
-
const error = validateOutputPath(input);
|
|
73
|
-
if (error) {
|
|
74
|
-
ui.error(error);
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
updateOutputPath(input);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const ACTIONS = {
|
|
81
|
-
createPage: handleCreate,
|
|
82
|
-
removePage: handleRemove,
|
|
83
|
-
renamePage: handleRename,
|
|
84
|
-
outputPath: handleOutputPath,
|
|
85
|
-
};
|
|
86
|
-
|
|
87
|
-
function findAction(choice) {
|
|
88
|
-
const item = settings.cli.menu.find((entry) => entry.key === choice);
|
|
89
|
-
return item ? ACTIONS[item.action] : null;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
async function main() {
|
|
93
|
-
const missing = checkRequiredFiles();
|
|
94
|
-
if (missing.length > 0) {
|
|
95
|
-
ui.renderMissingFiles(missing);
|
|
96
|
-
close();
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
while (true) {
|
|
101
|
-
ui.renderMenu();
|
|
102
|
-
|
|
103
|
-
const choice = await ask(ui.promptLine(message('cli.menuPrompt'), NO_PREFIX));
|
|
104
|
-
if (choice === null) break;
|
|
105
|
-
|
|
106
|
-
const action = findAction(choice);
|
|
107
|
-
if (!action) {
|
|
108
|
-
ui.error(message('cli.invalidOption'));
|
|
109
|
-
continue;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
try {
|
|
113
|
-
await action();
|
|
114
|
-
} catch (error) {
|
|
115
|
-
ui.error(message('cli.unexpectedError', { error: error.message }));
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
close();
|
|
120
|
-
process.exit(0);
|
|
121
|
-
}
|
|
122
|
-
|
|
1
|
+
const settings = require('./config/settings.json');
|
|
2
|
+
const { message } = require('./lib/logger');
|
|
3
|
+
const { toKebabCase } = require('./lib/text');
|
|
4
|
+
const { validatePageName, validateOutputPath, checkRequiredFiles } = require('./lib/validation');
|
|
5
|
+
const { addPage, removePage, renamePage } = require('./lib/pageActions');
|
|
6
|
+
const { pageExists } = require('./lib/pageArtifacts');
|
|
7
|
+
const { updateOutputPath, getCurrentOutputPath } = require('./lib/outputPath');
|
|
8
|
+
const { ask, confirm, close } = require('./cli/prompt');
|
|
9
|
+
const ui = require('./cli/ui');
|
|
10
|
+
|
|
11
|
+
const NEW_LINE = '\n';
|
|
12
|
+
const NO_PREFIX = '';
|
|
13
|
+
|
|
14
|
+
async function askPageName(prompt) {
|
|
15
|
+
const raw = await ask(prompt);
|
|
16
|
+
if (raw === null) return null;
|
|
17
|
+
|
|
18
|
+
const name = toKebabCase(raw);
|
|
19
|
+
const error = validatePageName(name);
|
|
20
|
+
if (error) {
|
|
21
|
+
ui.error(error);
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
return name;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function handleCreate() {
|
|
28
|
+
const name = await askPageName(ui.promptLine(message('cli.createPrompt')));
|
|
29
|
+
if (name) addPage(name);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async function handleRemove() {
|
|
33
|
+
const name = await askPageName(ui.promptLine(message('cli.removePrompt')));
|
|
34
|
+
if (!name) return;
|
|
35
|
+
|
|
36
|
+
if (!pageExists(name)) {
|
|
37
|
+
ui.warning(message('cli.pageNotFound', { name }));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const confirmed = await confirm(message('cli.confirmRemove', { name }));
|
|
42
|
+
if (!confirmed) {
|
|
43
|
+
ui.notice(message('cli.cancelled'));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
removePage(name);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function handleRename() {
|
|
50
|
+
const oldName = await askPageName(ui.promptLine(message('cli.renameFromPrompt')));
|
|
51
|
+
if (!oldName) return;
|
|
52
|
+
|
|
53
|
+
const newName = await askPageName(ui.promptLine(message('cli.renameToPrompt'), NO_PREFIX));
|
|
54
|
+
if (!newName) return;
|
|
55
|
+
|
|
56
|
+
if (oldName === newName) {
|
|
57
|
+
ui.warning(message('cli.sameName'));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
renamePage(oldName, newName);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function handleOutputPath() {
|
|
64
|
+
const current = getCurrentOutputPath();
|
|
65
|
+
const prefix = current
|
|
66
|
+
? ui.noticePrefix(message('cli.currentOutputPath', { path: current }))
|
|
67
|
+
: NEW_LINE;
|
|
68
|
+
|
|
69
|
+
const input = await ask(ui.promptLine(message('cli.outputPathPrompt'), prefix));
|
|
70
|
+
if (input === null) return;
|
|
71
|
+
|
|
72
|
+
const error = validateOutputPath(input);
|
|
73
|
+
if (error) {
|
|
74
|
+
ui.error(error);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
updateOutputPath(input);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const ACTIONS = {
|
|
81
|
+
createPage: handleCreate,
|
|
82
|
+
removePage: handleRemove,
|
|
83
|
+
renamePage: handleRename,
|
|
84
|
+
outputPath: handleOutputPath,
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
function findAction(choice) {
|
|
88
|
+
const item = settings.cli.menu.find((entry) => entry.key === choice);
|
|
89
|
+
return item ? ACTIONS[item.action] : null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function main() {
|
|
93
|
+
const missing = checkRequiredFiles();
|
|
94
|
+
if (missing.length > 0) {
|
|
95
|
+
ui.renderMissingFiles(missing);
|
|
96
|
+
close();
|
|
97
|
+
process.exit(1);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
while (true) {
|
|
101
|
+
ui.renderMenu();
|
|
102
|
+
|
|
103
|
+
const choice = await ask(ui.promptLine(message('cli.menuPrompt'), NO_PREFIX));
|
|
104
|
+
if (choice === null) break;
|
|
105
|
+
|
|
106
|
+
const action = findAction(choice);
|
|
107
|
+
if (!action) {
|
|
108
|
+
ui.error(message('cli.invalidOption'));
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
await action();
|
|
114
|
+
} catch (error) {
|
|
115
|
+
ui.error(message('cli.unexpectedError', { error: error.message }));
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
close();
|
|
120
|
+
process.exit(0);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
123
|
main();
|
package/tools/buildJs.js
CHANGED
|
@@ -1,39 +1,39 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const esbuild = require('esbuild');
|
|
3
|
-
const glob = require('glob');
|
|
4
|
-
const settings = require('./config/settings.json');
|
|
5
|
-
const { PATHS } = require('./lib/paths');
|
|
6
|
-
const { readPackageJson, allScriptEntries } = require('./lib/project');
|
|
7
|
-
|
|
8
|
-
const WATCH_FLAG = '--watch';
|
|
9
|
-
const PATH_SEPARATOR = '/';
|
|
10
|
-
|
|
11
|
-
function toPosix(target) {
|
|
12
|
-
return target.split(path.sep).join(PATH_SEPARATOR);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function collectEntryPoints() {
|
|
16
|
-
return allScriptEntries().flatMap((entries) => glob.sync(toPosix(path.join(PATHS.root, entries))));
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const packageJson = readPackageJson();
|
|
20
|
-
const outputDirectory = packageJson[settings.packageJson.outputKey] || settings.project.defaultOutputDirectory;
|
|
21
|
-
const watch = process.argv.includes(WATCH_FLAG);
|
|
22
|
-
const entryPoints = collectEntryPoints();
|
|
23
|
-
|
|
24
|
-
if (entryPoints.length === 0) {
|
|
25
|
-
process.exit(0);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const options = {
|
|
29
|
-
entryPoints,
|
|
30
|
-
bundle: true,
|
|
31
|
-
outdir: path.join(PATHS.root, outputDirectory, ...settings.build.scriptOutput.split(PATH_SEPARATOR)),
|
|
32
|
-
minify: !watch,
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
if (watch) {
|
|
36
|
-
esbuild.context(options).then((context) => context.watch()).catch(() => process.exit(1));
|
|
37
|
-
} else {
|
|
38
|
-
esbuild.build(options).catch(() => process.exit(1));
|
|
39
|
-
}
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const esbuild = require('esbuild');
|
|
3
|
+
const glob = require('glob');
|
|
4
|
+
const settings = require('./config/settings.json');
|
|
5
|
+
const { PATHS } = require('./lib/paths');
|
|
6
|
+
const { readPackageJson, allScriptEntries } = require('./lib/project');
|
|
7
|
+
|
|
8
|
+
const WATCH_FLAG = '--watch';
|
|
9
|
+
const PATH_SEPARATOR = '/';
|
|
10
|
+
|
|
11
|
+
function toPosix(target) {
|
|
12
|
+
return target.split(path.sep).join(PATH_SEPARATOR);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function collectEntryPoints() {
|
|
16
|
+
return allScriptEntries().flatMap((entries) => glob.sync(toPosix(path.join(PATHS.root, entries))));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const packageJson = readPackageJson();
|
|
20
|
+
const outputDirectory = packageJson[settings.packageJson.outputKey] || settings.project.defaultOutputDirectory;
|
|
21
|
+
const watch = process.argv.includes(WATCH_FLAG);
|
|
22
|
+
const entryPoints = collectEntryPoints();
|
|
23
|
+
|
|
24
|
+
if (entryPoints.length === 0) {
|
|
25
|
+
process.exit(0);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const options = {
|
|
29
|
+
entryPoints,
|
|
30
|
+
bundle: true,
|
|
31
|
+
outdir: path.join(PATHS.root, outputDirectory, ...settings.build.scriptOutput.split(PATH_SEPARATOR)),
|
|
32
|
+
minify: !watch,
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
if (watch) {
|
|
36
|
+
esbuild.context(options).then((context) => context.watch()).catch(() => process.exit(1));
|
|
37
|
+
} else {
|
|
38
|
+
esbuild.build(options).catch(() => process.exit(1));
|
|
39
|
+
}
|
package/tools/cleanOutput.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
const path = require('path');
|
|
2
|
-
const settings = require('./config/settings.json');
|
|
3
|
-
const { PATHS } = require('./lib/paths');
|
|
4
|
-
const { readPackageJson } = require('./lib/project');
|
|
5
|
-
const { removeDirectory } = require('./lib/files');
|
|
6
|
-
const { log } = require('./lib/logger');
|
|
7
|
-
|
|
8
|
-
const OUTPUT_KEY = settings.packageJson.outputKey;
|
|
9
|
-
|
|
10
|
-
const packageJson = readPackageJson();
|
|
11
|
-
|
|
12
|
-
if (!packageJson[OUTPUT_KEY]) {
|
|
13
|
-
log('output.missingConfiguration', { key: OUTPUT_KEY, file: settings.paths.packageJson });
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const outputDirectory = path.resolve(PATHS.root, packageJson[OUTPUT_KEY]);
|
|
18
|
-
|
|
19
|
-
if (removeDirectory(outputDirectory)) {
|
|
20
|
-
log('output.cleaned', { path: outputDirectory });
|
|
21
|
-
} else {
|
|
22
|
-
log('output.nothingToClean', { path: outputDirectory });
|
|
23
|
-
}
|
|
1
|
+
const path = require('path');
|
|
2
|
+
const settings = require('./config/settings.json');
|
|
3
|
+
const { PATHS } = require('./lib/paths');
|
|
4
|
+
const { readPackageJson } = require('./lib/project');
|
|
5
|
+
const { removeDirectory } = require('./lib/files');
|
|
6
|
+
const { log } = require('./lib/logger');
|
|
7
|
+
|
|
8
|
+
const OUTPUT_KEY = settings.packageJson.outputKey;
|
|
9
|
+
|
|
10
|
+
const packageJson = readPackageJson();
|
|
11
|
+
|
|
12
|
+
if (!packageJson[OUTPUT_KEY]) {
|
|
13
|
+
log('output.missingConfiguration', { key: OUTPUT_KEY, file: settings.paths.packageJson });
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const outputDirectory = path.resolve(PATHS.root, packageJson[OUTPUT_KEY]);
|
|
18
|
+
|
|
19
|
+
if (removeDirectory(outputDirectory)) {
|
|
20
|
+
log('output.cleaned', { path: outputDirectory });
|
|
21
|
+
} else {
|
|
22
|
+
log('output.nothingToClean', { path: outputDirectory });
|
|
23
|
+
}
|
package/tools/cli/prompt.js
CHANGED
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
const readline = require('readline');
|
|
2
|
-
const settings = require('../config/settings.json');
|
|
3
|
-
const { color } = require('../lib/colors');
|
|
4
|
-
|
|
5
|
-
const CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/g;
|
|
6
|
-
const AFFIRMATIVE_PATTERN = /^y(es)?$/i;
|
|
7
|
-
const CONFIRM_HINT = settings.cli.confirmHint;
|
|
8
|
-
const EMPTY = '';
|
|
9
|
-
|
|
10
|
-
const readlineInterface = readline.createInterface({
|
|
11
|
-
input: process.stdin,
|
|
12
|
-
output: process.stdout,
|
|
13
|
-
terminal: true,
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
function sanitizeInput(value) {
|
|
17
|
-
return (value ?? EMPTY).replace(CONTROL_CHARACTERS_PATTERN, EMPTY).trim();
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function ask(prompt) {
|
|
21
|
-
return new Promise((resolve) => {
|
|
22
|
-
const onClose = () => resolve(null);
|
|
23
|
-
readlineInterface.once('close', onClose);
|
|
24
|
-
readlineInterface.question(prompt, (answer) => {
|
|
25
|
-
readlineInterface.off('close', onClose);
|
|
26
|
-
resolve(sanitizeInput(answer));
|
|
27
|
-
});
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
async function confirm(prompt) {
|
|
32
|
-
const answer = await ask(`${prompt} ${color.dim}${CONFIRM_HINT}${color.reset} `);
|
|
33
|
-
return AFFIRMATIVE_PATTERN.test((answer ?? EMPTY).trim());
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function close() {
|
|
37
|
-
readlineInterface.close();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
module.exports = { ask, confirm, close };
|
|
1
|
+
const readline = require('readline');
|
|
2
|
+
const settings = require('../config/settings.json');
|
|
3
|
+
const { color } = require('../lib/colors');
|
|
4
|
+
|
|
5
|
+
const CONTROL_CHARACTERS_PATTERN = /[\x00-\x1F\x7F]/g;
|
|
6
|
+
const AFFIRMATIVE_PATTERN = /^y(es)?$/i;
|
|
7
|
+
const CONFIRM_HINT = settings.cli.confirmHint;
|
|
8
|
+
const EMPTY = '';
|
|
9
|
+
|
|
10
|
+
const readlineInterface = readline.createInterface({
|
|
11
|
+
input: process.stdin,
|
|
12
|
+
output: process.stdout,
|
|
13
|
+
terminal: true,
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
function sanitizeInput(value) {
|
|
17
|
+
return (value ?? EMPTY).replace(CONTROL_CHARACTERS_PATTERN, EMPTY).trim();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function ask(prompt) {
|
|
21
|
+
return new Promise((resolve) => {
|
|
22
|
+
const onClose = () => resolve(null);
|
|
23
|
+
readlineInterface.once('close', onClose);
|
|
24
|
+
readlineInterface.question(prompt, (answer) => {
|
|
25
|
+
readlineInterface.off('close', onClose);
|
|
26
|
+
resolve(sanitizeInput(answer));
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async function confirm(prompt) {
|
|
32
|
+
const answer = await ask(`${prompt} ${color.dim}${CONFIRM_HINT}${color.reset} `);
|
|
33
|
+
return AFFIRMATIVE_PATTERN.test((answer ?? EMPTY).trim());
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function close() {
|
|
37
|
+
readlineInterface.close();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
module.exports = { ask, confirm, close };
|
package/tools/cli/ui.js
CHANGED
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
const settings = require('../config/settings.json');
|
|
2
|
-
const { color } = require('../lib/colors');
|
|
3
|
-
const { message } = require('../lib/logger');
|
|
4
|
-
|
|
5
|
-
const SYMBOLS = settings.cli.symbols;
|
|
6
|
-
const COLORS = settings.cli.colors;
|
|
7
|
-
const MENU = settings.cli.menu;
|
|
8
|
-
const BOX_TITLE = settings.cli.title;
|
|
9
|
-
const BOX_TOP_LEFT = '\u256d';
|
|
10
|
-
const BOX_TOP_RIGHT = '\u256e';
|
|
11
|
-
const BOX_BOTTOM_LEFT = '\u2570';
|
|
12
|
-
const BOX_BOTTOM_RIGHT = '\u256f';
|
|
13
|
-
const BOX_HORIZONTAL = '\u2500';
|
|
14
|
-
const BOX_VERTICAL = '\u2502';
|
|
15
|
-
const NEW_LINE = '\n';
|
|
16
|
-
const SPACE = ' ';
|
|
17
|
-
|
|
18
|
-
const LOCAL_FLAG = 'NIBULA_LOCAL';
|
|
19
|
-
const LOCAL_FLAG_VALUE = '1';
|
|
20
|
-
const TITLE_PADDING = 2;
|
|
21
|
-
|
|
22
|
-
const TITLE_SUFFIX = process.env[LOCAL_FLAG] === LOCAL_FLAG_VALUE
|
|
23
|
-
? ` (local v${require('../../package.json').version})`
|
|
24
|
-
: '';
|
|
25
|
-
|
|
26
|
-
const PLAIN_TITLE = `${BOX_TITLE}${TITLE_SUFFIX}`;
|
|
27
|
-
const BOX_WIDTH = Math.max(settings.cli.boxWidth, PLAIN_TITLE.length + TITLE_PADDING);
|
|
28
|
-
|
|
29
|
-
function line(colorName, text) {
|
|
30
|
-
return `${color[colorName]}${text}${color.reset}`;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function promptLine(text, prefix = NEW_LINE) {
|
|
34
|
-
return `${prefix}${line(COLORS.prompt, SYMBOLS.prompt)} ${text}`;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function error(text) {
|
|
38
|
-
console.log(`${NEW_LINE}${line(COLORS.error, `${SYMBOLS.error} ${text}`)}`);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function warning(text) {
|
|
42
|
-
console.log(`${NEW_LINE}${line(COLORS.warning, `${SYMBOLS.warning} ${text}`)}`);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function notice(text) {
|
|
46
|
-
console.log(`${NEW_LINE}${line(COLORS.notice, text)}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function noticePrefix(text) {
|
|
50
|
-
return `${NEW_LINE}${line(COLORS.notice, text)}${NEW_LINE}`;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function centeredTitle() {
|
|
54
|
-
const padding = BOX_WIDTH - PLAIN_TITLE.length;
|
|
55
|
-
const left = SPACE.repeat(Math.ceil(padding / 2));
|
|
56
|
-
const right = SPACE.repeat(Math.floor(padding / 2));
|
|
57
|
-
|
|
58
|
-
if (!TITLE_SUFFIX) return `${left}${BOX_TITLE}${right}`;
|
|
59
|
-
|
|
60
|
-
const suffix = `${color.dim}${TITLE_SUFFIX}${color.reset}${color[COLORS.box]}${color.bold}`;
|
|
61
|
-
return `${left}${BOX_TITLE}${suffix}${right}`;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function renderMenu() {
|
|
65
|
-
const border = BOX_HORIZONTAL.repeat(BOX_WIDTH);
|
|
66
|
-
|
|
67
|
-
console.log(`${NEW_LINE}${color[COLORS.box]}${color.bold}${BOX_TOP_LEFT}${border}${BOX_TOP_RIGHT}`);
|
|
68
|
-
console.log(`${BOX_VERTICAL}${centeredTitle()}${BOX_VERTICAL}`);
|
|
69
|
-
console.log(`${BOX_BOTTOM_LEFT}${border}${BOX_BOTTOM_RIGHT}${color.reset}${NEW_LINE}`);
|
|
70
|
-
|
|
71
|
-
for (const item of MENU) {
|
|
72
|
-
console.log(` ${line(COLORS.menuKey, `${item.key}.`)} ${item.label}`);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
console.log(`${NEW_LINE}${line(COLORS.notice, message('cli.exitHint'))}${NEW_LINE}`);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function renderMissingFiles(items) {
|
|
79
|
-
console.log(`${NEW_LINE}${line(COLORS.error, `${SYMBOLS.error} ${message('cli.missingFiles')}`)}`);
|
|
80
|
-
|
|
81
|
-
for (const item of items) {
|
|
82
|
-
console.log(` ${line(COLORS.error, SYMBOLS.bullet)} ${item.label}`);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
console.log(`${NEW_LINE}${line(COLORS.notice, message('cli.incompleteProject'))}`);
|
|
86
|
-
}
|
|
87
|
-
|
|
1
|
+
const settings = require('../config/settings.json');
|
|
2
|
+
const { color } = require('../lib/colors');
|
|
3
|
+
const { message } = require('../lib/logger');
|
|
4
|
+
|
|
5
|
+
const SYMBOLS = settings.cli.symbols;
|
|
6
|
+
const COLORS = settings.cli.colors;
|
|
7
|
+
const MENU = settings.cli.menu;
|
|
8
|
+
const BOX_TITLE = settings.cli.title;
|
|
9
|
+
const BOX_TOP_LEFT = '\u256d';
|
|
10
|
+
const BOX_TOP_RIGHT = '\u256e';
|
|
11
|
+
const BOX_BOTTOM_LEFT = '\u2570';
|
|
12
|
+
const BOX_BOTTOM_RIGHT = '\u256f';
|
|
13
|
+
const BOX_HORIZONTAL = '\u2500';
|
|
14
|
+
const BOX_VERTICAL = '\u2502';
|
|
15
|
+
const NEW_LINE = '\n';
|
|
16
|
+
const SPACE = ' ';
|
|
17
|
+
|
|
18
|
+
const LOCAL_FLAG = 'NIBULA_LOCAL';
|
|
19
|
+
const LOCAL_FLAG_VALUE = '1';
|
|
20
|
+
const TITLE_PADDING = 2;
|
|
21
|
+
|
|
22
|
+
const TITLE_SUFFIX = process.env[LOCAL_FLAG] === LOCAL_FLAG_VALUE
|
|
23
|
+
? ` (local v${require('../../package.json').version})`
|
|
24
|
+
: '';
|
|
25
|
+
|
|
26
|
+
const PLAIN_TITLE = `${BOX_TITLE}${TITLE_SUFFIX}`;
|
|
27
|
+
const BOX_WIDTH = Math.max(settings.cli.boxWidth, PLAIN_TITLE.length + TITLE_PADDING);
|
|
28
|
+
|
|
29
|
+
function line(colorName, text) {
|
|
30
|
+
return `${color[colorName]}${text}${color.reset}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function promptLine(text, prefix = NEW_LINE) {
|
|
34
|
+
return `${prefix}${line(COLORS.prompt, SYMBOLS.prompt)} ${text}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function error(text) {
|
|
38
|
+
console.log(`${NEW_LINE}${line(COLORS.error, `${SYMBOLS.error} ${text}`)}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function warning(text) {
|
|
42
|
+
console.log(`${NEW_LINE}${line(COLORS.warning, `${SYMBOLS.warning} ${text}`)}`);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function notice(text) {
|
|
46
|
+
console.log(`${NEW_LINE}${line(COLORS.notice, text)}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function noticePrefix(text) {
|
|
50
|
+
return `${NEW_LINE}${line(COLORS.notice, text)}${NEW_LINE}`;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function centeredTitle() {
|
|
54
|
+
const padding = BOX_WIDTH - PLAIN_TITLE.length;
|
|
55
|
+
const left = SPACE.repeat(Math.ceil(padding / 2));
|
|
56
|
+
const right = SPACE.repeat(Math.floor(padding / 2));
|
|
57
|
+
|
|
58
|
+
if (!TITLE_SUFFIX) return `${left}${BOX_TITLE}${right}`;
|
|
59
|
+
|
|
60
|
+
const suffix = `${color.dim}${TITLE_SUFFIX}${color.reset}${color[COLORS.box]}${color.bold}`;
|
|
61
|
+
return `${left}${BOX_TITLE}${suffix}${right}`;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function renderMenu() {
|
|
65
|
+
const border = BOX_HORIZONTAL.repeat(BOX_WIDTH);
|
|
66
|
+
|
|
67
|
+
console.log(`${NEW_LINE}${color[COLORS.box]}${color.bold}${BOX_TOP_LEFT}${border}${BOX_TOP_RIGHT}`);
|
|
68
|
+
console.log(`${BOX_VERTICAL}${centeredTitle()}${BOX_VERTICAL}`);
|
|
69
|
+
console.log(`${BOX_BOTTOM_LEFT}${border}${BOX_BOTTOM_RIGHT}${color.reset}${NEW_LINE}`);
|
|
70
|
+
|
|
71
|
+
for (const item of MENU) {
|
|
72
|
+
console.log(` ${line(COLORS.menuKey, `${item.key}.`)} ${item.label}`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(`${NEW_LINE}${line(COLORS.notice, message('cli.exitHint'))}${NEW_LINE}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function renderMissingFiles(items) {
|
|
79
|
+
console.log(`${NEW_LINE}${line(COLORS.error, `${SYMBOLS.error} ${message('cli.missingFiles')}`)}`);
|
|
80
|
+
|
|
81
|
+
for (const item of items) {
|
|
82
|
+
console.log(` ${line(COLORS.error, SYMBOLS.bullet)} ${item.label}`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
console.log(`${NEW_LINE}${line(COLORS.notice, message('cli.incompleteProject'))}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
88
|
module.exports = { promptLine, noticePrefix, error, warning, notice, renderMenu, renderMissingFiles };
|