diff --git a/docs/devGuide/development/workflow.md b/docs/devGuide/development/workflow.md
index 121cc783f2..3134ca52a4 100644
--- a/docs/devGuide/development/workflow.md
+++ b/docs/devGuide/development/workflow.md
@@ -171,6 +171,17 @@ Hence, if you need to view the latest frontend changes (relating to `packages/co
1. Run `npm run build:web` in the root directory, which builds the above bundles,
then run your markbind-cli commandcommand of choice.
+
+
+
+
+##### `markbind serve -d`
+
+`markbind serve -d` adds the necessary webpack middlewares to the development server to compile the bundles for `markbind-core-web` and `markbind-vue-components`, and enables live and hot reloading for frontend source files.
+
+Note that the command is only applicable for local development, and does not work when you install MarkBind from npm, as it relies on developmental dependencies.
+
+
## Testing
diff --git a/package-lock.json b/package-lock.json
index 230cc41c4b..4c41508784 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -21913,7 +21913,7 @@
"cheerio": "^0.22.0",
"chokidar": "^3.3.0",
"colors": "1.4.0",
- "commander": "^8.1.0",
+ "commander": "^14.0.3",
"figlet": "^1.9.4",
"find-up": "^4.1.0",
"fs-extra": "^9.0.1",
@@ -21967,6 +21967,15 @@
"node": ">=8"
}
},
+ "packages/cli/node_modules/commander": {
+ "version": "14.0.3",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz",
+ "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=20"
+ }
+ },
"packages/cli/node_modules/file-stream-rotator": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/file-stream-rotator/-/file-stream-rotator-0.6.1.tgz",
diff --git a/packages/cli/index.ts b/packages/cli/index.ts
index a98e0ad53c..408bcf8f80 100755
--- a/packages/cli/index.ts
+++ b/packages/cli/index.ts
@@ -1,7 +1,8 @@
#!/usr/bin/env node
// Entry file for MarkBind project
-import { program } from 'commander';
+import { program, Option } from 'commander';
+import chalk from 'chalk';
import * as logger from './src/util/logger.js';
import { build } from './src/cmd/build.js';
import { deploy } from './src/cmd/deploy.js';
@@ -24,7 +25,12 @@ function printHeader() {
program
.addHelpText('beforeAll', printHeader())
- .showHelpAfterError('(run "markbind --help" to list commands)');
+ .showHelpAfterError('(run "markbind --help" to list commands)')
+ .configureHelp({
+ styleTitle: str => chalk.bold.cyan(str),
+ styleUsage: str => chalk.white(str),
+ styleOptionText: str => chalk.yellow(str),
+ });
program
.allowUnknownOption()
@@ -35,30 +41,56 @@ program
.version(CLI_VERSION);
program
+ .commandsGroup('Setup Commands')
.command('init [root]')
.option('-c, --convert', 'convert a GitHub wiki or docs folder to a MarkBind website')
.option('-t, --template
', 'initialise markbind with a specified template', 'default')
.alias('i')
+ .summary('init a markbind site')
.description('init a markbind website project')
.action((root, options) => {
init(root, options);
});
program
+ .commandsGroup('Site Commands')
.command('serve [root]')
.alias('s')
- .option('-f, --force-reload', 'force a full reload of all site files when a file is changed')
- .option('-n, --no-open', 'do not automatically open the site in browser')
- .option('-o, --one-page [file]', 'build and serve only a single page in the site initially,'
- + 'building more pages when they are navigated to. Also lazily rebuilds only the page being viewed when'
- + 'there are changes to the source files (if needed), building others when navigated to')
- .option('-b, --background-build', 'when --one-page is specified, enhances one-page serve by building'
- + 'remaining pages in the background')
- .option('-p, --port ', 'port for server to listen on (Default is 8080)')
- .option('-s, --site-config ', 'specify the site config file (default: site.json)')
- .option('-d, --dev', 'development mode, enabling live & hot reload for frontend source files.')
- .option('-a, --address ', 'specify the server address/host (Default is 127.0.0.1)')
- .description('build then serve a website from a directory')
+ .summary('Build then serve a website from a directory')
+ .description('Build and serve a website from a directory')
+
+ .optionsGroup('Build Options')
+ .addOption(
+ program.createOption('-f, --force-reload',
+ 'force a full reload of all site files when a file is changed')
+ .conflicts('onePage'))
+
+ .addOption(
+ program.createOption('-o, --one-page [file]',
+ 'build and serve only a single page in the site initially, '
+ + 'building more pages when they are navigated to. Also lazily rebuilds only '
+ + 'the page being viewed when there are changes to the source files (if needed), '
+ + 'building others when navigated to'))
+
+ .addOption(
+ program.createOption('-b, --background-build',
+ 'when --one-page is specified, enhances one-page serve by building '
+ + 'remaining pages in the background'))
+
+ .optionsGroup('Server Options')
+ .addOption(
+ program.createOption('-n, --no-open', 'do not automatically open the site in browser'))
+ .addOption(
+ program.createOption('-p, --port ', 'port for server to listen on (Default is 8080)'))
+ .addOption(
+ program.createOption('-a, --address ', 'specify the server address/host (Default is 127.0.0.1)'))
+ .addOption(
+ program.createOption('-s, --site-config ', 'specify the site config file (default: site.json)'))
+
+ // Development mode is hidden as it is not user facing and only works during local development
+ .addOption(
+ new Option('-d, --dev', 'development mode, enabling live & hot reload for frontend source files.')
+ .hideHelp())
.action((userSpecifiedRoot, options) => {
serve(userSpecifiedRoot, options);
});
@@ -69,7 +101,8 @@ program
.option('--baseUrl [baseUrl]',
'optional flag which overrides baseUrl in site.json, leave argument empty for empty baseUrl')
.option('-s, --site-config ', 'specify the site config file (default: site.json)')
- .description('build a website')
+ .summary('Build a website')
+ .description('Build a website')
.action((userSpecifiedRoot, output, options) => {
build(userSpecifiedRoot, output, options);
});
@@ -80,7 +113,8 @@ program
.option('-c, --ci [githubTokenName]', 'deploy the site in CI Environment [GITHUB_TOKEN]')
.option('-n, --no-build', 'do not automatically build the site before deployment')
.option('-s, --site-config ', 'specify the site config file (default: site.json)')
- .description('deploy the latest build of the site to the repo\'s Github pages')
+ .summary('Deploy the latest build of the site to GitHub Pages')
+ .description('Deploy the latest build of the site to the repo\'s GitHub Pages')
.action((userSpecifiedRoot, options) => {
deploy(userSpecifiedRoot, options);
});
diff --git a/packages/cli/package.json b/packages/cli/package.json
index 96f6fd46e2..614afeb065 100644
--- a/packages/cli/package.json
+++ b/packages/cli/package.json
@@ -39,7 +39,7 @@
"cheerio": "^0.22.0",
"chokidar": "^3.3.0",
"colors": "1.4.0",
- "commander": "^8.1.0",
+ "commander": "^14.0.3",
"figlet": "^1.9.4",
"find-up": "^4.1.0",
"fs-extra": "^9.0.1",
@@ -50,18 +50,18 @@
"winston-daily-rotate-file": "^5.0.0"
},
"devDependencies": {
+ "@babel/core": "^7.29.0",
+ "@babel/preset-env": "^7.29.0",
+ "@babel/preset-typescript": "^7.28.5",
"@types/jest": "^29.4.6",
"@types/lodash": "^4.17.15",
"@types/url-parse": "^1.4.8",
+ "babel-jest": "^29.7.0",
"diff": "^8.0.3",
"ignore": "^7.0.5",
"istextorbinary": "^9.5.0",
"jest": "^29.7.0",
"memfs": "^4.56.2",
- "walk-sync": "^2.0.2",
- "@babel/core": "^7.29.0",
- "@babel/preset-env": "^7.29.0",
- "@babel/preset-typescript": "^7.28.5",
- "babel-jest": "^29.7.0"
+ "walk-sync": "^2.0.2"
}
}
diff --git a/packages/cli/src/cmd/serve.ts b/packages/cli/src/cmd/serve.ts
index 1a5e0e6adf..e645bfb381 100755
--- a/packages/cli/src/cmd/serve.ts
+++ b/packages/cli/src/cmd/serve.ts
@@ -43,12 +43,6 @@ function serve(userSpecifiedRoot: string, options: any) {
let rootFolder;
try {
rootFolder = cliUtil.findRootFolder(userSpecifiedRoot, options.siteConfig);
-
- if (options.forceReload && options.onePage) {
- logger.error('Oops! You shouldn\'t need to use the --force-reload option with --one-page.');
- process.exitCode = 1;
- process.exit();
- }
} catch (error) {
if (_.isError(error)) {
logger.error(error.message);