@portosaur/cli 0.1.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/README.md +52 -0
- package/bin/porto.mjs +71 -0
- package/package.json +36 -0
- package/src/commands/build.mjs +85 -0
- package/src/commands/dev.mjs +61 -0
- package/src/commands/init.mjs +523 -0
- package/src/commands/initCi.mjs +227 -0
- package/src/commands/providers.mjs +170 -0
- package/src/commands/schema.mjs +208 -0
- package/src/commands/serve.mjs +29 -0
- package/src/index.d.ts +49 -0
- package/src/index.mjs +8 -0
- package/src/templates/README.md +58 -0
- package/src/templates/blog/authors.yml +4 -0
- package/src/templates/blog/welcome.md +11 -0
- package/src/templates/config.yml +150 -0
- package/src/templates/gitignore +9 -0
- package/src/templates/notes/index.mdx +9 -0
- package/src/templates/notes/welcome.mdx +9 -0
- package/src/templates/package.json +14 -0
- package/src/templates/registry.yml +107 -0
- package/src/templates/static/.nojekyll +0 -0
- package/src/templates/static/README.md +1 -0
- package/src/templates/workflows/codeberg/.forgejo/workflows/deploy.yml +39 -0
- package/src/templates/workflows/github/.github/workflows/deploy.yml +55 -0
- package/src/templates/workflows/gitlab/.gitlab-ci.yml +13 -0
- package/src/templates/workflows/netlify/netlify.toml +6 -0
- package/src/templates/workflows/surge/codeberg/.forgejo/workflows/deploy.yml +23 -0
- package/src/templates/workflows/surge/github/.github/workflows/deploy.yml +23 -0
- package/src/templates/workflows/surge/gitlab/.gitlab-ci.yml +16 -0
- package/src/templates/workflows/surge/sourcehut/.build.yml +26 -0
- package/src/templates/workflows/woodpecker/.woodpecker/deploy.yml +21 -0
- package/src/utils/git.mjs +52 -0
- package/src/utils/index.mjs +7 -0
- package/src/utils/interaction.mjs +24 -0
- package/src/utils/packageManager.mjs +85 -0
- package/src/utils/paths.mjs +33 -0
- package/src/utils/platforms.mjs +130 -0
- package/src/utils/projectName.mjs +20 -0
- package/src/utils/runner.mjs +192 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Determines if the CLI should run in interactive mode based on provided options.
|
|
3
|
+
*
|
|
4
|
+
* Providing any configuration flag forces non-interactive mode.
|
|
5
|
+
*
|
|
6
|
+
* @param {Object} options - Command options (flags).
|
|
7
|
+
* @returns {boolean}
|
|
8
|
+
*/
|
|
9
|
+
export function isInteractive(options = {}) {
|
|
10
|
+
// Flags that trigger non-interactive mode
|
|
11
|
+
const hasConfigOptions = Object.keys(options).some((key) => {
|
|
12
|
+
if (key === "install") {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
return options[key] !== undefined && options[key] !== null;
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
if (hasConfigOptions) {
|
|
19
|
+
return false;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Otherwise, default to interactive
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Package manager detection and CLI utility (ESM)
|
|
3
|
+
*/
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Returns a package manager object for the given directory.
|
|
9
|
+
* Detects by lockfile, environment, or runtime.
|
|
10
|
+
* @param {string} dir - The directory to check.
|
|
11
|
+
* @returns {{ name: string, install: string, run: string, exec: string }} Package manager info.
|
|
12
|
+
*/
|
|
13
|
+
export function getPackageManager(dir) {
|
|
14
|
+
let name = null;
|
|
15
|
+
|
|
16
|
+
// Detect by lockfile
|
|
17
|
+
if (
|
|
18
|
+
fs.existsSync(path.join(dir, "bun.lock")) ||
|
|
19
|
+
fs.existsSync(path.join(dir, "bun.lockb"))
|
|
20
|
+
) {
|
|
21
|
+
name = "bun";
|
|
22
|
+
} else if (fs.existsSync(path.join(dir, "pnpm-lock.yaml"))) {
|
|
23
|
+
name = "pnpm";
|
|
24
|
+
} else if (fs.existsSync(path.join(dir, "yarn.lock"))) {
|
|
25
|
+
name = "yarn";
|
|
26
|
+
} else if (fs.existsSync(path.join(dir, "package-lock.json"))) {
|
|
27
|
+
name = "npm";
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Detect by environment
|
|
31
|
+
else if (process.env.npm_config_user_agent) {
|
|
32
|
+
if (process.env.npm_config_user_agent.includes("bun")) name = "bun";
|
|
33
|
+
else if (process.env.npm_config_user_agent.includes("pnpm")) name = "pnpm";
|
|
34
|
+
else if (process.env.npm_config_user_agent.includes("yarn")) name = "yarn";
|
|
35
|
+
else if (process.env.npm_config_user_agent.includes("npm")) name = "npm";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Detect by runtime
|
|
39
|
+
else if (typeof process !== "undefined" && process.versions?.bun) {
|
|
40
|
+
name = "bun";
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (!name) {
|
|
44
|
+
throw new Error(
|
|
45
|
+
"Supported package manager (bun, pnpm, yarn, or npm) not detected.",
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const exec = { npm: "npx", bun: "bunx", pnpm: "pnpm dlx", yarn: "yarn dlx" }[
|
|
50
|
+
name
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
return { name, install: `${name} install`, run: `${name} run`, exec };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Resolves the command to run Docusaurus based on the package manager and project state.
|
|
58
|
+
* @param {string} UserRoot - The project root directory.
|
|
59
|
+
* @returns {{ command: string, args: string[], packageManager: string }} Docusaurus command info.
|
|
60
|
+
*/
|
|
61
|
+
export function getDocuCmd(UserRoot) {
|
|
62
|
+
const pm = getPackageManager(UserRoot);
|
|
63
|
+
|
|
64
|
+
// Check for node_modules/.bin/docusaurus
|
|
65
|
+
const localDocusaurus = path.join(
|
|
66
|
+
UserRoot,
|
|
67
|
+
"node_modules",
|
|
68
|
+
".bin",
|
|
69
|
+
"docusaurus",
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (fs.existsSync(localDocusaurus)) {
|
|
73
|
+
const local = {
|
|
74
|
+
bun: { command: "bun", args: ["docusaurus"] },
|
|
75
|
+
npm: { command: "npm", args: ["run", "docusaurus", "--"] },
|
|
76
|
+
pnpm: { command: "pnpm", args: ["docusaurus"] },
|
|
77
|
+
yarn: { command: "yarn", args: ["docusaurus"] },
|
|
78
|
+
}[pm.name];
|
|
79
|
+
|
|
80
|
+
return { ...local, packageManager: pm.name };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Fallback to npx/bunx if not found locally
|
|
84
|
+
return { command: pm.exec, args: ["docusaurus"], packageManager: pm.name };
|
|
85
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
|
|
3
|
+
const srcDir = path.resolve(import.meta.dirname, "../");
|
|
4
|
+
const pkgDir = path.resolve(srcDir, "../");
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Centralized paths for the CLI package.
|
|
8
|
+
*/
|
|
9
|
+
export const Paths = {
|
|
10
|
+
/** CLI package root. */
|
|
11
|
+
root: pkgDir,
|
|
12
|
+
|
|
13
|
+
/** CLI src directory. */
|
|
14
|
+
src: srcDir,
|
|
15
|
+
|
|
16
|
+
/** CLI templates directory. */
|
|
17
|
+
templates: path.join(srcDir, "templates"),
|
|
18
|
+
|
|
19
|
+
/** Registry file. */
|
|
20
|
+
registry: path.join(srcDir, "templates/registry.yml"),
|
|
21
|
+
|
|
22
|
+
/** Workflows directory. */
|
|
23
|
+
workflows: path.join(srcDir, "templates/workflows"),
|
|
24
|
+
|
|
25
|
+
/** CLI's package.json file. */
|
|
26
|
+
packageJson: path.join(pkgDir, "package.json"),
|
|
27
|
+
|
|
28
|
+
/** Absolute path to the core package. */
|
|
29
|
+
core: path.resolve(pkgDir, "../core"),
|
|
30
|
+
|
|
31
|
+
/** Absolute path to the theme package. */
|
|
32
|
+
theme: path.resolve(pkgDir, "../theme"),
|
|
33
|
+
};
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Paths } from "./paths.mjs";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Resolves a platform key from a name or ID.
|
|
7
|
+
* Supports exact match, case-insensitive ID match, and case-insensitive name match.
|
|
8
|
+
* @param {Object} platforms - The platforms registry object.
|
|
9
|
+
* @param {string} platform - The platform name or ID to resolve.
|
|
10
|
+
* @returns {string|null} The resolved platform key or null if not found.
|
|
11
|
+
*/
|
|
12
|
+
export function resolvePlatformKey(platforms, platform) {
|
|
13
|
+
if (typeof platform !== "string") {
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const requested = platform.trim();
|
|
18
|
+
|
|
19
|
+
if (!requested) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const requestedLower = requested.toLowerCase();
|
|
24
|
+
|
|
25
|
+
// Resolve by direct key match (case-sensitive first)
|
|
26
|
+
if (Object.prototype.hasOwnProperty.call(platforms, requested)) {
|
|
27
|
+
return requested;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Resolve by case-insensitive ID
|
|
31
|
+
const idMatch = Object.keys(platforms).find(
|
|
32
|
+
(key) => key.toLowerCase() === requestedLower,
|
|
33
|
+
);
|
|
34
|
+
if (idMatch) {
|
|
35
|
+
return idMatch;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Resolve by display name
|
|
39
|
+
const nameMatch = Object.keys(platforms).find(
|
|
40
|
+
(key) => platforms[key].name?.toLowerCase() === requestedLower,
|
|
41
|
+
);
|
|
42
|
+
if (nameMatch) return nameMatch;
|
|
43
|
+
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Attempts to guess the VCS username from git configuration.
|
|
49
|
+
* @param {string} vcsProviderId - The ID of the VCS provider (e.g., 'github').
|
|
50
|
+
* @param {Record<string, string>} [gitConfig={}] - The current git configuration object.
|
|
51
|
+
* @returns {string} The guessed username or an empty string.
|
|
52
|
+
*/
|
|
53
|
+
export function getPlatformUserGuess(vcsProviderId, gitConfig = {}) {
|
|
54
|
+
const vcsProvider = vcsProviderId?.toLowerCase();
|
|
55
|
+
|
|
56
|
+
const keysByProvider = {
|
|
57
|
+
github: ["github.user"],
|
|
58
|
+
gitlab: ["gitlab.user"],
|
|
59
|
+
codeberg: ["codeberg.user", "forgejo.user"],
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
const hostByProvider = {
|
|
63
|
+
github: "github.com",
|
|
64
|
+
gitlab: "gitlab.com",
|
|
65
|
+
codeberg: "codeberg.org",
|
|
66
|
+
sourcehut: "git.sr.ht",
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Try explicit configuration first
|
|
70
|
+
for (const key of keysByProvider[vcsProvider] || []) {
|
|
71
|
+
if (gitConfig[key]) return gitConfig[key];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Fallback to remote URL parsing
|
|
75
|
+
const host = hostByProvider[vcsProvider];
|
|
76
|
+
|
|
77
|
+
if (!host) {
|
|
78
|
+
return "";
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
for (const [key, value] of Object.entries(gitConfig)) {
|
|
82
|
+
// Only look at remote URLs
|
|
83
|
+
if (!key.startsWith("remote.") || !key.endsWith(".url")) continue;
|
|
84
|
+
if (typeof value !== "string" || !value.includes(host)) continue;
|
|
85
|
+
|
|
86
|
+
const escapedHost = host.replace(/\./g, "\\.");
|
|
87
|
+
const match = value.match(
|
|
88
|
+
new RegExp(
|
|
89
|
+
`(?:https?://|ssh://git@|git@)${escapedHost}[:/]([^/]+)(?:/|$)`,
|
|
90
|
+
),
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
if (match?.[1]) return match[1];
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return "";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Dynamically discovers all possible workflow markers (files/dirs) from all available templates.
|
|
101
|
+
* @param {Object} registry - The platforms registry object.
|
|
102
|
+
* @returns {string[]} An array of unique file/directory names that serve as CI markers.
|
|
103
|
+
*/
|
|
104
|
+
export function getWorkflowMarkers(registry) {
|
|
105
|
+
const markers = new Set();
|
|
106
|
+
const platforms = Object.values(registry.hosting_platforms || {});
|
|
107
|
+
|
|
108
|
+
for (const platform of platforms) {
|
|
109
|
+
const templates =
|
|
110
|
+
typeof platform.template_dir === "string"
|
|
111
|
+
? [platform.template_dir]
|
|
112
|
+
: Object.values(platform.template_dir || {});
|
|
113
|
+
|
|
114
|
+
for (const template of templates) {
|
|
115
|
+
const templatePath = path.join(Paths.workflows, template);
|
|
116
|
+
|
|
117
|
+
if (fs.existsSync(templatePath)) {
|
|
118
|
+
const contents = fs.readdirSync(templatePath);
|
|
119
|
+
|
|
120
|
+
for (const item of contents) {
|
|
121
|
+
if (item !== "." && item !== "..") {
|
|
122
|
+
markers.add(item);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return Array.from(markers);
|
|
130
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function looksLikeTestProject(projectName) {
|
|
2
|
+
const testTerms = [
|
|
3
|
+
"test",
|
|
4
|
+
"testing",
|
|
5
|
+
"spec",
|
|
6
|
+
"specs",
|
|
7
|
+
"ci",
|
|
8
|
+
"demo",
|
|
9
|
+
"example",
|
|
10
|
+
];
|
|
11
|
+
const n = String(projectName).toLowerCase();
|
|
12
|
+
|
|
13
|
+
if (!projectName) {
|
|
14
|
+
return false;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
return testTerms.some((t) => n.includes(t));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default looksLikeTestProject;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { spawn } from "child_process";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generates a Docusaurus config shim that loads Portosaur logic.
|
|
7
|
+
* @param {string} UserRoot - The user's project directory.
|
|
8
|
+
* @param {Object} portoPaths - Paths to Portosaur assets and core.
|
|
9
|
+
* @param {Object} [context={}] - Additional context for config generation.
|
|
10
|
+
* @returns {string} The path to the generated shim file.
|
|
11
|
+
*/
|
|
12
|
+
export function writeConfigShim(UserRoot, portoPaths, context = {}) {
|
|
13
|
+
const dotDir = path.join(UserRoot, ".docusaurus", "portosaur");
|
|
14
|
+
|
|
15
|
+
if (!fs.existsSync(dotDir)) {
|
|
16
|
+
fs.mkdirSync(dotDir, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Resolve config file
|
|
20
|
+
const configYaml = ["config.yaml", "config.yml"].find((file) =>
|
|
21
|
+
fs.existsSync(path.join(UserRoot, file)),
|
|
22
|
+
);
|
|
23
|
+
if (!configYaml) throw new Error("config.yml not found");
|
|
24
|
+
|
|
25
|
+
const configYamlAbsolute = path
|
|
26
|
+
.resolve(UserRoot, configYaml)
|
|
27
|
+
.replace(/\\/g, "/");
|
|
28
|
+
|
|
29
|
+
// Prepare shim template
|
|
30
|
+
const shimContent = `// Auto-generated by portosaur
|
|
31
|
+
import fs from "fs";
|
|
32
|
+
import yaml from "js-yaml";
|
|
33
|
+
|
|
34
|
+
export default async function getConfig() {
|
|
35
|
+
const { buildDocuConfig } = await import("@portosaur/core");
|
|
36
|
+
const yamlContent = fs.readFileSync("${configYamlAbsolute}", "utf8");
|
|
37
|
+
const rawConf = yaml.load(yamlContent);
|
|
38
|
+
|
|
39
|
+
return buildDocuConfig(rawConf, "${UserRoot.replace(/\\/g, "/")}", {
|
|
40
|
+
portoPaths: ${JSON.stringify(portoPaths)},
|
|
41
|
+
portoRoot: "${path.resolve(portoPaths.root).replace(/\\/g, "/")}",
|
|
42
|
+
...${JSON.stringify(context)}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
`;
|
|
46
|
+
|
|
47
|
+
// Write and return shim path
|
|
48
|
+
const shimPath = path.join(dotDir, "docusaurus.config.js");
|
|
49
|
+
fs.writeFileSync(shimPath, shimContent);
|
|
50
|
+
|
|
51
|
+
return shimPath;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Validate that the current directory is a Portosaur project.
|
|
56
|
+
* @param {string} UserRoot - The directory to validate.
|
|
57
|
+
* @throws {Error} If config.yml is missing.
|
|
58
|
+
*/
|
|
59
|
+
export function validateProject(UserRoot) {
|
|
60
|
+
const configPath = ["config.yaml", "config.yml"].find((file) =>
|
|
61
|
+
fs.existsSync(path.join(UserRoot, file)),
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
if (!configPath) {
|
|
65
|
+
throw new Error(
|
|
66
|
+
"config.yml/config.yaml not found. Are you in a Portosaur project directory?",
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Ensure essential user content directories exist.
|
|
73
|
+
* @param {string} UserRoot - The project root directory.
|
|
74
|
+
*/
|
|
75
|
+
export function ensureContentDirs(UserRoot) {
|
|
76
|
+
for (const dir of ["notes", "blog", "static"]) {
|
|
77
|
+
if (!fs.existsSync(path.join(UserRoot, dir))) {
|
|
78
|
+
fs.mkdirSync(path.join(UserRoot, dir), { recursive: true });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Print deployment tips defined in registry.yml
|
|
85
|
+
* @param {Object} hostingPlatforms - Hosting platforms registry.
|
|
86
|
+
* @param {string} hostingPlatformKey - The selected platform key.
|
|
87
|
+
* @param {Object} logger - The Portosaur logger instance.
|
|
88
|
+
* @param {Object} [vars={}] - Variables for replacement in tips.
|
|
89
|
+
*/
|
|
90
|
+
export function printWorkflowTips(
|
|
91
|
+
hostingPlatforms,
|
|
92
|
+
hostingPlatformKey,
|
|
93
|
+
logger,
|
|
94
|
+
vars = {},
|
|
95
|
+
) {
|
|
96
|
+
const config = hostingPlatforms[hostingPlatformKey];
|
|
97
|
+
if (config && config.post_setup_tips) {
|
|
98
|
+
for (let tip of config.post_setup_tips) {
|
|
99
|
+
if (vars.projectName)
|
|
100
|
+
tip = tip.replace(/\{\{projectName\}\}/g, vars.projectName);
|
|
101
|
+
if (vars.userName) tip = tip.replace(/\{\{user\}\}/g, vars.userName);
|
|
102
|
+
logger.tip(tip);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Warn the user if their project name does not match the expected platform repository pattern.
|
|
109
|
+
* @param {Object} hostingPlatforms - Hosting platforms registry.
|
|
110
|
+
* @param {string} hostingPlatformKey - The selected platform key.
|
|
111
|
+
* @param {string} projectName - Current project name.
|
|
112
|
+
* @param {string} userName - VCS username.
|
|
113
|
+
* @param {Object} logger - The Portosaur logger instance.
|
|
114
|
+
* @param {string} [heading] - Warning heading.
|
|
115
|
+
*/
|
|
116
|
+
export function warnIfRepoNameMismatch(
|
|
117
|
+
hostingPlatforms,
|
|
118
|
+
hostingPlatformKey,
|
|
119
|
+
projectName,
|
|
120
|
+
userName,
|
|
121
|
+
logger,
|
|
122
|
+
heading = "Project Name Mismatch",
|
|
123
|
+
) {
|
|
124
|
+
const config = hostingPlatforms[hostingPlatformKey];
|
|
125
|
+
if (!config || !config.repo || !config.repo.ideal_name) return;
|
|
126
|
+
|
|
127
|
+
const expectedRepo = config.repo.ideal_name
|
|
128
|
+
.replace("{{user}}", userName || "yourusername")
|
|
129
|
+
.replace("{{domain}}", config.domain || "");
|
|
130
|
+
|
|
131
|
+
if (projectName === expectedRepo || !config.repo.mismatch_msg) return;
|
|
132
|
+
|
|
133
|
+
const projectNameForMsg = projectName || "(custom name)";
|
|
134
|
+
|
|
135
|
+
const customMsg = config.repo.mismatch_msg
|
|
136
|
+
.replace(/\{\{user\}\}/g, userName || "yourusername")
|
|
137
|
+
.replace(/\{\{projectName\}\}/g, projectNameForMsg)
|
|
138
|
+
.replace(/\{\{domain\}\}/g, config.domain || "")
|
|
139
|
+
.replace(/\{\{idealName\}\}/g, expectedRepo);
|
|
140
|
+
|
|
141
|
+
logger.warn(`[${heading}] ${customMsg}`);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Executes a Docusaurus command.
|
|
146
|
+
* @param {string} command - Docusaurus command to run (e.g., 'start', 'build').
|
|
147
|
+
* @param {string} UserRoot - The project directory.
|
|
148
|
+
* @param {string} configPath - Path to the docusaurus.config.js shim.
|
|
149
|
+
* @param {string[]} [extraArgs=[]] - Additional CLI arguments.
|
|
150
|
+
* @returns {Promise<void>}
|
|
151
|
+
*/
|
|
152
|
+
export async function runDocusaurus(
|
|
153
|
+
command,
|
|
154
|
+
UserRoot,
|
|
155
|
+
configPath,
|
|
156
|
+
extraArgs = [],
|
|
157
|
+
) {
|
|
158
|
+
const args = [
|
|
159
|
+
"run",
|
|
160
|
+
"--bun",
|
|
161
|
+
"docusaurus",
|
|
162
|
+
command,
|
|
163
|
+
UserRoot,
|
|
164
|
+
"--config",
|
|
165
|
+
configPath,
|
|
166
|
+
...extraArgs,
|
|
167
|
+
];
|
|
168
|
+
|
|
169
|
+
// Skip actual execution in test mode
|
|
170
|
+
if (process.env.PORTO_TEST_MODE === "true") {
|
|
171
|
+
console.log(`[TEST_MODE] Would run docusaurus ${command} in ${UserRoot}`);
|
|
172
|
+
return Promise.resolve();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const child = spawn("bun", args, {
|
|
176
|
+
stdio: "inherit",
|
|
177
|
+
cwd: UserRoot,
|
|
178
|
+
env: { ...process.env, FORCE_COLOR: "true" },
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
return new Promise((resolve, reject) => {
|
|
182
|
+
child.on("error", reject);
|
|
183
|
+
|
|
184
|
+
child.on("close", (code) => {
|
|
185
|
+
if (code === 0) {
|
|
186
|
+
resolve();
|
|
187
|
+
} else {
|
|
188
|
+
reject(new Error(`Docusaurus exited with code ${code}`));
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
}
|