@fecommunity/reactpress 4.0.0-beta.12 → 4.0.0-beta.14
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 +49 -11
- package/admin-dist/apple-touch-icon.png +0 -0
- package/admin-dist/assets/abap-08VXUWAP.js +1 -0
- package/admin-dist/assets/admin-BifGpl6Y.css +1 -0
- package/admin-dist/assets/admin-CrL6Xc9b.js +1 -0
- package/admin-dist/assets/admin-ZWBL0q2y.css +1 -0
- package/admin-dist/assets/admin-bamoE2Tn.js +1 -0
- package/admin-dist/assets/apex-BWPQTe0t.js +1 -0
- package/admin-dist/assets/azcli-Bc_sGQ0U.js +1 -0
- package/admin-dist/assets/bat-i0X4ZdIN.js +1 -0
- package/admin-dist/assets/bicep-B5-_aFwp.js +2 -0
- package/admin-dist/assets/cameligo-DMUM7wLl.js +1 -0
- package/admin-dist/assets/clojure-Cm7r79vr.js +1 -0
- package/admin-dist/assets/codicon-DCmgc-ay.ttf +0 -0
- package/admin-dist/assets/coffee-Ba7i2nA0.js +1 -0
- package/admin-dist/assets/cpp-C7h46wYY.js +1 -0
- package/admin-dist/assets/csharp-BKxtCVv1.js +1 -0
- package/admin-dist/assets/csp-bTuwJoIa.js +1 -0
- package/admin-dist/assets/css-DIMkf-bt.js +3 -0
- package/admin-dist/assets/cssMode-J2cfgFkq.js +4 -0
- package/admin-dist/assets/cypher-CVaqCwHa.js +1 -0
- package/admin-dist/assets/dart-onAF5SnQ.js +1 -0
- package/admin-dist/assets/dashicons-DDlWl2tg.eot +0 -0
- package/admin-dist/assets/dashicons-MbROV69l.ttf +0 -0
- package/admin-dist/assets/dockerfile-DZFCIeNp.js +1 -0
- package/admin-dist/assets/ecl-D05T4iGw.js +1 -0
- package/admin-dist/assets/elixir-6RTg0lbw.js +1 -0
- package/admin-dist/assets/flow9-C5_-GSwl.js +1 -0
- package/admin-dist/assets/freemarker2-L_gr_Wq4.js +3 -0
- package/admin-dist/assets/fsharp-C8Ef5oNN.js +1 -0
- package/admin-dist/assets/github-dark.min-Z5exjX_n.css +1 -0
- package/admin-dist/assets/github.min-D2BCvnWf.css +1 -0
- package/admin-dist/assets/go-C-y9NEjX.js +1 -0
- package/admin-dist/assets/graphql-fmXr3nnJ.js +1 -0
- package/admin-dist/assets/handlebars-D81kzUDC.js +1 -0
- package/admin-dist/assets/hcl-CpzslTdj.js +1 -0
- package/admin-dist/assets/html-Cung-Yr0.js +1 -0
- package/admin-dist/assets/htmlMode-C6QemWl2.js +4 -0
- package/admin-dist/assets/index-BNrD3O4x.css +1 -0
- package/admin-dist/assets/index-DXTkTQ21.js +1184 -0
- package/admin-dist/assets/ini-sBoK_t0W.js +1 -0
- package/admin-dist/assets/java-BEtHBSE6.js +1 -0
- package/admin-dist/assets/javascript-WUcGtCKt.js +1 -0
- package/admin-dist/assets/jsonMode-Dp0R9P1F.js +10 -0
- package/admin-dist/assets/julia-Bri6UV-V.js +1 -0
- package/admin-dist/assets/kotlin-B0qVs-E9.js +1 -0
- package/admin-dist/assets/less-B9JPFI3C.js +2 -0
- package/admin-dist/assets/lexon-CfSJPG6W.js +1 -0
- package/admin-dist/assets/liquid-OFb9c-6B.js +1 -0
- package/admin-dist/assets/lua-CsQS60Ue.js +1 -0
- package/admin-dist/assets/m3-D-oSqn_W.js +1 -0
- package/admin-dist/assets/markdown-Cimd5fb3.js +1 -0
- package/admin-dist/assets/mdx-CUfjLxA3.js +1 -0
- package/admin-dist/assets/mips-CIPQ_RoX.js +1 -0
- package/admin-dist/assets/msdax-DauUninz.js +1 -0
- package/admin-dist/assets/mysql-SOo6toE5.js +1 -0
- package/admin-dist/assets/objective-c-FvmIjYaQ.js +1 -0
- package/admin-dist/assets/pascal-DrH0SRf2.js +1 -0
- package/admin-dist/assets/pascaligo-D-ptJ9y-.js +1 -0
- package/admin-dist/assets/perl-oz_6vUea.js +1 -0
- package/admin-dist/assets/pgsql-DTj74zXo.js +1 -0
- package/admin-dist/assets/php-nr791fC2.js +1 -0
- package/admin-dist/assets/pla-CopQ2nXW.js +1 -0
- package/admin-dist/assets/postiats-43DmfD33.js +1 -0
- package/admin-dist/assets/powerquery-D3hlyOfw.js +1 -0
- package/admin-dist/assets/powershell-DmHpPYUd.js +1 -0
- package/admin-dist/assets/protobuf-C531GsRP.js +2 -0
- package/admin-dist/assets/pug-Z5eAx3Zn.js +1 -0
- package/admin-dist/assets/python-zxEYY77U.js +1 -0
- package/admin-dist/assets/qsharp-DkqhCAOL.js +1 -0
- package/admin-dist/assets/r-BwWrilGY.js +1 -0
- package/admin-dist/assets/razor-DJrSr4ly.js +1 -0
- package/admin-dist/assets/redis-ClamHrr6.js +1 -0
- package/admin-dist/assets/redshift-DT7zqm-g.js +1 -0
- package/admin-dist/assets/restructuredtext-BYgofb2h.js +1 -0
- package/admin-dist/assets/rolldown-runtime-Cyuzqnbw.js +1 -0
- package/admin-dist/assets/ruby-DezsRK8O.js +1 -0
- package/admin-dist/assets/rust-DdL9SqIa.js +1 -0
- package/admin-dist/assets/sb-CcwsVR0C.js +1 -0
- package/admin-dist/assets/scala-DHpiXF5c.js +1 -0
- package/admin-dist/assets/scheme-BeGwcela.js +1 -0
- package/admin-dist/assets/scss-gp-XZpBa.js +3 -0
- package/admin-dist/assets/shell-CC2rA5mh.js +1 -0
- package/admin-dist/assets/solidity-BEEn4gHE.js +1 -0
- package/admin-dist/assets/sophia-CRfGWb83.js +1 -0
- package/admin-dist/assets/sparql-D_Lu-MrJ.js +1 -0
- package/admin-dist/assets/sql-NEE52Syq.js +1 -0
- package/admin-dist/assets/st-DbInun42.js +1 -0
- package/admin-dist/assets/swift-Bxkupp3x.js +1 -0
- package/admin-dist/assets/systemverilog-Bz4Y3fRF.js +1 -0
- package/admin-dist/assets/tcl-DISqw1ZD.js +1 -0
- package/admin-dist/assets/tsMode-Dtxx1cRn.js +11 -0
- package/admin-dist/assets/twig-De2hgUGE.js +1 -0
- package/admin-dist/assets/typescript-DT11gl4K.js +1 -0
- package/admin-dist/assets/typespec-B8J7ngcE.js +1 -0
- package/admin-dist/assets/vb-DV3o63ZY.js +1 -0
- package/admin-dist/assets/vendor-antd-854Wnkhp.js +334 -0
- package/admin-dist/assets/vendor-tanstack-CUgEbs6-.js +1 -0
- package/admin-dist/assets/vendor-ui-BosneugM.js +1 -0
- package/admin-dist/assets/wgsl-DpFanUEy.js +298 -0
- package/admin-dist/assets/xml-BIbLvdwx.js +1 -0
- package/admin-dist/assets/yaml-BEeUZskE.js +1 -0
- package/admin-dist/favicon-16.png +0 -0
- package/admin-dist/favicon-32.png +0 -0
- package/admin-dist/favicon-48.png +0 -0
- package/admin-dist/favicon.ico +0 -0
- package/admin-dist/favicon.png +0 -0
- package/admin-dist/icon-192.png +0 -0
- package/admin-dist/icon-512.png +0 -0
- package/admin-dist/index.html +26 -0
- package/admin-dist/logo-200.png +0 -0
- package/admin-dist/logo-400.png +0 -0
- package/admin-dist/logo-wordmark.svg +10 -0
- package/admin-dist/logo.png +0 -0
- package/admin-dist/logo.svg +7 -0
- package/admin-dist/mockServiceWorker.js +336 -0
- package/admin-dist/showcase/undraw_around_the_world.svg +1 -0
- package/admin-dist/showcase/undraw_docusaurus_mountain.svg +54 -0
- package/admin-dist/showcase/undraw_docusaurus_react.svg +51 -0
- package/admin-dist/showcase/undraw_docusaurus_tree.svg +40 -0
- package/admin-dist/showcase/undraw_react.svg +1 -0
- package/admin-dist/showcase/undraw_typewriter.svg +1 -0
- package/admin-dist/showcase/undraw_version_control.svg +1 -0
- package/out/bin/reactpress-theme-client.js +97 -64
- package/out/bin/reactpress.js +50 -29
- package/out/core/services/admin-static.js +136 -0
- package/out/core/services/local-site.js +3 -2
- package/out/lib/client-lifecycle.js +88 -0
- package/out/lib/i18n/strings.js +56 -14
- package/out/lib/lifecycle.js +96 -41
- package/out/lib/paths.js +4 -0
- package/out/lib/pm2-runtime.js +95 -0
- package/out/lib/process.js +25 -5
- package/out/lib/project-logs.js +1 -1
- package/out/lib/publish.js +3 -3
- package/out/lib/theme-api-proxy-patch.js +58 -0
- package/out/lib/theme-install.js +2 -0
- package/package.json +19 -3
- package/server/package-lock.json +5 -5
- package/server/package.json +18 -7
- package/toolkit/package.json +1 -1
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.resolveBundledAdminDistDir = resolveBundledAdminDistDir;
|
|
7
|
+
exports.resolveWebPackageRoot = resolveWebPackageRoot;
|
|
8
|
+
exports.ensureAdminRewritesInNextConfig = ensureAdminRewritesInNextConfig;
|
|
9
|
+
exports.ensureAdminStaticForTheme = ensureAdminStaticForTheme;
|
|
10
|
+
exports.shouldRebuildThemeAfterAdminSync = shouldRebuildThemeAfterAdminSync;
|
|
11
|
+
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
12
|
+
const node_child_process_1 = require("node:child_process");
|
|
13
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
14
|
+
const ADMIN_REWRITES_MARKER = 'reactpress-admin-rewrites';
|
|
15
|
+
function getCliPackageRoot() {
|
|
16
|
+
return node_path_1.default.join(__dirname, '..', '..', '..');
|
|
17
|
+
}
|
|
18
|
+
function resolveBundledAdminDistDir() {
|
|
19
|
+
const bundled = node_path_1.default.join(getCliPackageRoot(), 'admin-dist', 'index.html');
|
|
20
|
+
return fs_extra_1.default.existsSync(bundled) ? node_path_1.default.dirname(bundled) : null;
|
|
21
|
+
}
|
|
22
|
+
function resolveWebPackageRoot(projectRoot) {
|
|
23
|
+
for (const candidate of [node_path_1.default.join(projectRoot, 'web'), node_path_1.default.join(projectRoot, '..', 'web')]) {
|
|
24
|
+
if (fs_extra_1.default.existsSync(node_path_1.default.join(candidate, 'package.json')))
|
|
25
|
+
return candidate;
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
function syncDirToPublicAdmin(sourceDist, themeDir) {
|
|
30
|
+
const target = node_path_1.default.join(themeDir, 'public', 'admin');
|
|
31
|
+
fs_extra_1.default.removeSync(target);
|
|
32
|
+
fs_extra_1.default.mkdirSync(node_path_1.default.dirname(target), { recursive: true });
|
|
33
|
+
fs_extra_1.default.copySync(sourceDist, target);
|
|
34
|
+
return target;
|
|
35
|
+
}
|
|
36
|
+
function removeReactpressAdminMiddleware(themeDir) {
|
|
37
|
+
for (const name of ['middleware.js', 'middleware.mjs']) {
|
|
38
|
+
const filePath = node_path_1.default.join(themeDir, name);
|
|
39
|
+
if (!fs_extra_1.default.existsSync(filePath))
|
|
40
|
+
continue;
|
|
41
|
+
const content = fs_extra_1.default.readFileSync(filePath, 'utf8');
|
|
42
|
+
if (content.includes('reactpress-admin-middleware')) {
|
|
43
|
+
fs_extra_1.default.removeSync(filePath);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
/** Patch theme next.config.js with afterFiles admin SPA rewrites. */
|
|
48
|
+
function ensureAdminRewritesInNextConfig(themeDir) {
|
|
49
|
+
const configCandidates = ['next.config.js', 'next.config.mjs', 'next.config.ts'];
|
|
50
|
+
const configPath = configCandidates
|
|
51
|
+
.map((name) => node_path_1.default.join(themeDir, name))
|
|
52
|
+
.find((candidate) => fs_extra_1.default.existsSync(candidate));
|
|
53
|
+
if (!configPath)
|
|
54
|
+
return false;
|
|
55
|
+
let src = fs_extra_1.default.readFileSync(configPath, 'utf8');
|
|
56
|
+
if (src.includes(ADMIN_REWRITES_MARKER))
|
|
57
|
+
return false;
|
|
58
|
+
const arrayReturn = /async rewrites\(\)\s*\{\s*return (\[[\s\S]*?\])\s*\}/m.exec(src);
|
|
59
|
+
if (!arrayReturn) {
|
|
60
|
+
console.warn('[ReactPress Client] Could not patch next.config rewrites() for admin SPA');
|
|
61
|
+
return false;
|
|
62
|
+
}
|
|
63
|
+
const replacement = `async rewrites() {
|
|
64
|
+
// ${ADMIN_REWRITES_MARKER}
|
|
65
|
+
return {
|
|
66
|
+
beforeFiles: ${arrayReturn[1]},
|
|
67
|
+
afterFiles: [
|
|
68
|
+
{ source: '/admin', destination: '/admin/index.html' },
|
|
69
|
+
{ source: '/admin/:path*', destination: '/admin/index.html' },
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
}`;
|
|
73
|
+
src = src.replace(arrayReturn[0], replacement);
|
|
74
|
+
fs_extra_1.default.writeFileSync(configPath, src, 'utf8');
|
|
75
|
+
removeReactpressAdminMiddleware(themeDir);
|
|
76
|
+
return true;
|
|
77
|
+
}
|
|
78
|
+
function finalizeAdminThemeSetup(themeDir) {
|
|
79
|
+
return ensureAdminRewritesInNextConfig(themeDir);
|
|
80
|
+
}
|
|
81
|
+
/** Returns true when admin static or rewrites were newly applied (theme rebuild recommended). */
|
|
82
|
+
function ensureAdminStaticForTheme(projectRoot, themeDir) {
|
|
83
|
+
const adminIndex = node_path_1.default.join(themeDir, 'public', 'admin', 'index.html');
|
|
84
|
+
if (fs_extra_1.default.existsSync(adminIndex)) {
|
|
85
|
+
return finalizeAdminThemeSetup(themeDir);
|
|
86
|
+
}
|
|
87
|
+
const webRoot = resolveWebPackageRoot(projectRoot);
|
|
88
|
+
if (webRoot) {
|
|
89
|
+
const webDist = node_path_1.default.join(webRoot, 'dist', 'index.html');
|
|
90
|
+
if (!fs_extra_1.default.existsSync(webDist)) {
|
|
91
|
+
console.log('[ReactPress Client] Building admin SPA (web/)…');
|
|
92
|
+
const buildWeb = (0, node_child_process_1.spawnSync)('pnpm', ['run', 'build'], { cwd: webRoot, stdio: 'inherit' });
|
|
93
|
+
if (buildWeb.status !== 0)
|
|
94
|
+
process.exit(buildWeb.status || 1);
|
|
95
|
+
}
|
|
96
|
+
const syncScript = node_path_1.default.join(webRoot, 'bin', 'reactpress-web.js');
|
|
97
|
+
const publicAdmin = node_path_1.default.join(themeDir, 'public', 'admin');
|
|
98
|
+
console.log('[ReactPress Client] Syncing admin static → theme public/admin…');
|
|
99
|
+
const sync = (0, node_child_process_1.spawnSync)(process.execPath, [syncScript, 'sync-public', publicAdmin], {
|
|
100
|
+
stdio: 'inherit',
|
|
101
|
+
cwd: webRoot,
|
|
102
|
+
});
|
|
103
|
+
if (sync.status !== 0)
|
|
104
|
+
process.exit(sync.status || 1);
|
|
105
|
+
return finalizeAdminThemeSetup(themeDir) || true;
|
|
106
|
+
}
|
|
107
|
+
const bundledDist = resolveBundledAdminDistDir();
|
|
108
|
+
if (!bundledDist) {
|
|
109
|
+
console.warn('[ReactPress Client] Admin static missing and bundled admin-dist not found');
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
console.log('[ReactPress Client] Syncing bundled admin static → theme public/admin…');
|
|
113
|
+
syncDirToPublicAdmin(bundledDist, themeDir);
|
|
114
|
+
return finalizeAdminThemeSetup(themeDir) || true;
|
|
115
|
+
}
|
|
116
|
+
function shouldRebuildThemeAfterAdminSync(themeDir) {
|
|
117
|
+
const adminIndex = node_path_1.default.join(themeDir, 'public', 'admin', 'index.html');
|
|
118
|
+
const nextDir = node_path_1.default.join(themeDir, '.next');
|
|
119
|
+
const configPath = node_path_1.default.join(themeDir, 'next.config.js');
|
|
120
|
+
if (!fs_extra_1.default.existsSync(adminIndex))
|
|
121
|
+
return false;
|
|
122
|
+
if (!fs_extra_1.default.existsSync(nextDir))
|
|
123
|
+
return true;
|
|
124
|
+
const adminMtime = fs_extra_1.default.statSync(adminIndex).mtimeMs;
|
|
125
|
+
const nextMtime = fs_extra_1.default.statSync(nextDir).mtimeMs;
|
|
126
|
+
if (adminMtime > nextMtime)
|
|
127
|
+
return true;
|
|
128
|
+
if (fs_extra_1.default.existsSync(configPath)) {
|
|
129
|
+
const configMtime = fs_extra_1.default.statSync(configPath).mtimeMs;
|
|
130
|
+
if (configMtime > nextMtime && fs_extra_1.default.readFileSync(configPath, 'utf8').includes(ADMIN_REWRITES_MARKER)) {
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=admin-static.js.map
|
|
@@ -123,10 +123,11 @@ async function initLocalProject(projectRoot, options = {}) {
|
|
|
123
123
|
const paths = (0, paths_1.getProjectPaths)(projectRoot);
|
|
124
124
|
const port = options.port ?? 3002;
|
|
125
125
|
if ((await fs_extra_1.default.pathExists(paths.configPath)) && !options.force) {
|
|
126
|
+
const { t } = require('../../lib/i18n');
|
|
126
127
|
return {
|
|
127
|
-
ok:
|
|
128
|
+
ok: true,
|
|
128
129
|
projectRoot,
|
|
129
|
-
message: '
|
|
130
|
+
message: t('init.alreadyInitialized'),
|
|
130
131
|
};
|
|
131
132
|
}
|
|
132
133
|
await fs_extra_1.default.ensureDir(projectRoot);
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
// @ts-nocheck
|
|
4
|
+
const ora = require('ora');
|
|
5
|
+
const { getThemeBin } = require('./paths');
|
|
6
|
+
const { loadClientSiteUrl } = require('./http');
|
|
7
|
+
const { runNodeScript } = require('./spawn');
|
|
8
|
+
const { readClientPid, isProcessRunning, clearClientPidFile } = require('./process');
|
|
9
|
+
const { PM2_CLIENT_APP, isPm2RuntimeAvailable, isPm2AppOnline, getPm2AppPid, } = require('./pm2-runtime');
|
|
10
|
+
const { t } = require('./i18n');
|
|
11
|
+
function stopClient(projectRoot) {
|
|
12
|
+
const pid = readClientPid(projectRoot);
|
|
13
|
+
if (pid && isProcessRunning(pid)) {
|
|
14
|
+
try {
|
|
15
|
+
process.kill(pid, 'SIGTERM');
|
|
16
|
+
console.log(t('lifecycle.clientStopped', { pid }));
|
|
17
|
+
}
|
|
18
|
+
catch (err) {
|
|
19
|
+
console.warn(t('lifecycle.stopClientPidFailed', { pid }), err.message);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
clearClientPidFile(projectRoot);
|
|
23
|
+
}
|
|
24
|
+
async function startClientInBackground(projectRoot) {
|
|
25
|
+
const preferPm2 = isPm2RuntimeAvailable(projectRoot);
|
|
26
|
+
if (preferPm2 && isPm2AppOnline(projectRoot, PM2_CLIENT_APP)) {
|
|
27
|
+
const pid = getPm2AppPid(projectRoot, PM2_CLIENT_APP);
|
|
28
|
+
console.log(t('lifecycle.clientAlreadyRunning', { pid: pid ?? PM2_CLIENT_APP }));
|
|
29
|
+
return 0;
|
|
30
|
+
}
|
|
31
|
+
const existing = readClientPid(projectRoot);
|
|
32
|
+
if (!preferPm2 && existing && isProcessRunning(existing)) {
|
|
33
|
+
console.log(t('lifecycle.clientAlreadyRunning', { pid: existing }));
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
clearClientPidFile(projectRoot);
|
|
37
|
+
const themeBin = getThemeBin(projectRoot);
|
|
38
|
+
const { resolveThemeDirectory, readActiveThemeManifest } = require('./theme-runtime');
|
|
39
|
+
const { activeTheme } = readActiveThemeManifest(projectRoot);
|
|
40
|
+
const themeDir = resolveThemeDirectory(projectRoot, activeTheme);
|
|
41
|
+
const bgFlag = preferPm2 ? '--pm2-bg' : '--bg';
|
|
42
|
+
await runNodeScript(themeBin, [bgFlag], {
|
|
43
|
+
cwd: projectRoot,
|
|
44
|
+
env: themeDir ? { REACTPRESS_THEME_DIR: themeDir } : undefined,
|
|
45
|
+
});
|
|
46
|
+
const supervised = isPm2AppOnline(projectRoot, PM2_CLIENT_APP);
|
|
47
|
+
const pid = supervised ? getPm2AppPid(projectRoot, PM2_CLIENT_APP) : readClientPid(projectRoot);
|
|
48
|
+
if (!supervised && !pid) {
|
|
49
|
+
console.error(t('lifecycle.clientPidMissing'));
|
|
50
|
+
return 1;
|
|
51
|
+
}
|
|
52
|
+
const clientUrl = loadClientSiteUrl(projectRoot);
|
|
53
|
+
const spinner = ora({
|
|
54
|
+
text: t('lifecycle.waitingClient', { url: clientUrl }),
|
|
55
|
+
color: 'magenta',
|
|
56
|
+
spinner: 'dots',
|
|
57
|
+
}).start();
|
|
58
|
+
const deadline = Date.now() + 180_000;
|
|
59
|
+
let ready = false;
|
|
60
|
+
while (Date.now() < deadline) {
|
|
61
|
+
const alive = supervised
|
|
62
|
+
? isPm2AppOnline(projectRoot, PM2_CLIENT_APP)
|
|
63
|
+
: pid
|
|
64
|
+
? isProcessRunning(pid)
|
|
65
|
+
: false;
|
|
66
|
+
if (!alive) {
|
|
67
|
+
spinner.fail(t('lifecycle.clientExitedEarly', { pid: pid ?? '—' }));
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
if (await require('./http').isHttpResponding(clientUrl)) {
|
|
71
|
+
ready = true;
|
|
72
|
+
break;
|
|
73
|
+
}
|
|
74
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
75
|
+
}
|
|
76
|
+
if (!ready) {
|
|
77
|
+
spinner.fail(t('lifecycle.clientTimeout', { url: clientUrl }));
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
const readyPid = supervised ? getPm2AppPid(projectRoot, PM2_CLIENT_APP) ?? PM2_CLIENT_APP : pid;
|
|
81
|
+
spinner.succeed(t('lifecycle.clientReady', { url: clientUrl, pid: readyPid }));
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
module.exports = {
|
|
85
|
+
startClientInBackground,
|
|
86
|
+
stopClient,
|
|
87
|
+
};
|
|
88
|
+
//# sourceMappingURL=client-lifecycle.js.map
|
package/out/lib/i18n/strings.js
CHANGED
|
@@ -76,6 +76,13 @@ const STRINGS = {
|
|
|
76
76
|
'cli.doctor.logs.sourceOption': 'Log source: error, request, response, or all',
|
|
77
77
|
'cli.doctor.logs.grepOption': 'Filter log lines by regex',
|
|
78
78
|
'cli.doctor.logs.listOption': 'List available log files only',
|
|
79
|
+
'cli.logs.description': 'View recent API logs (error, request, response)',
|
|
80
|
+
'cli.logs.descriptionAlias': 'View API logs (alias of reactpress logs)',
|
|
81
|
+
'cli.logs.tailOption': 'Number of lines to show per log file',
|
|
82
|
+
'cli.logs.sourceOption': 'Log source: error, request, response, or all',
|
|
83
|
+
'cli.logs.grepOption': 'Filter log lines by regex',
|
|
84
|
+
'cli.logs.listOption': 'List available log files only',
|
|
85
|
+
'cli.stop.description': 'Stop API and site services started by init',
|
|
79
86
|
'cli.db.description': 'Database operations',
|
|
80
87
|
'cli.db.backup': 'Backup current project database with mysqldump',
|
|
81
88
|
'cli.db.backup.output': 'Output SQL file path',
|
|
@@ -84,9 +91,11 @@ const STRINGS = {
|
|
|
84
91
|
'cli.publish.publish': 'Publish core packages to npm',
|
|
85
92
|
'cli.start.description': 'Start ReactPress (SQLite API + admin + theme)',
|
|
86
93
|
'cli.help.examples': 'Usage:',
|
|
87
|
-
'cli.help.init': ' reactpress init
|
|
94
|
+
'cli.help.init': ' reactpress init [dir] Initialize project',
|
|
88
95
|
'cli.help.doctor': ' reactpress doctor Diagnose environment issues',
|
|
89
|
-
'cli.help.
|
|
96
|
+
'cli.help.logs': ' reactpress logs Tail API logs for quick debugging',
|
|
97
|
+
'cli.help.stop': ' reactpress stop Stop the background API',
|
|
98
|
+
'cli.help.doctorLogs': ' reactpress doctor logs Alias of reactpress logs',
|
|
90
99
|
'cli.help.zeroDep': ' Zero deps: Node.js 18+ only — no Docker, nginx, or MySQL',
|
|
91
100
|
'cli.plugin.description': 'Manage ReactPress plugins',
|
|
92
101
|
'cli.plugin.install.description': 'Install a local plugin into .reactpress/plugins',
|
|
@@ -112,10 +121,11 @@ const STRINGS = {
|
|
|
112
121
|
'init.label.site': 'Site',
|
|
113
122
|
'init.label.admin': 'Admin',
|
|
114
123
|
'init.label.api': 'API',
|
|
115
|
-
'init.hint': '
|
|
124
|
+
'init.hint': 'Login: admin / admin · Services run in background · Stop: reactpress stop · Logs: reactpress logs',
|
|
125
|
+
'init.alreadyInitialized': 'Project already initialized — ensuring services are running',
|
|
116
126
|
'init.apiOnlyHint': 'API is running. Install a theme in Admin → Appearance to enable the public site.',
|
|
117
127
|
'init.unknownCommand': 'Unknown command: {cmd}',
|
|
118
|
-
'init.useInitOnly': 'ReactPress 4
|
|
128
|
+
'init.useInitOnly': 'ReactPress 4: reactpress init · doctor · logs · stop',
|
|
119
129
|
'banner.subtitle': '· Publishing platform · v4.0',
|
|
120
130
|
'banner.tagline': 'Zero deps · SQLite · ship in ~60s',
|
|
121
131
|
/** Left label for the decorative pulse bar (not a URL — the repo link
|
|
@@ -415,8 +425,10 @@ const STRINGS = {
|
|
|
415
425
|
'doctor.checking': 'Checking {name}…',
|
|
416
426
|
'doctor.summary': '{passed} passed · {failed} failed · {total} total',
|
|
417
427
|
'doctor.fixesHeader': 'Suggested fixes',
|
|
418
|
-
'doctor.logsHint': 'Tip: run reactpress
|
|
419
|
-
'
|
|
428
|
+
'doctor.logsHint': 'Tip: run reactpress logs --tail 100 to inspect API error logs',
|
|
429
|
+
'logs.title': 'ReactPress Logs',
|
|
430
|
+
'logs.subtitle': 'API log viewer',
|
|
431
|
+
'doctor.logs.title': 'ReactPress Logs',
|
|
420
432
|
'doctor.logs.subtitle': 'API log viewer',
|
|
421
433
|
'doctor.logs.dir': 'Log dir {path}',
|
|
422
434
|
'doctor.logs.empty': 'No API log files found yet.',
|
|
@@ -428,7 +440,7 @@ const STRINGS = {
|
|
|
428
440
|
'doctor.logs.noPid': 'No API pid file at {path}',
|
|
429
441
|
'doctor.logs.pidRunning': 'API pid {pid} (recorded in {path})',
|
|
430
442
|
'doctor.logs.pidStale': 'Stale pid {pid} in {path} — process not running',
|
|
431
|
-
'doctor.logs.moreHint': 'Use --tail, --source, --grep, or --list for more control (see reactpress
|
|
443
|
+
'doctor.logs.moreHint': 'Use --tail, --source, --grep, or --list for more control (see reactpress logs --help)',
|
|
432
444
|
'status.title': 'ReactPress project status',
|
|
433
445
|
'status.dir': 'Project {path}',
|
|
434
446
|
'status.apiSource': 'API source {source}',
|
|
@@ -489,6 +501,14 @@ const STRINGS = {
|
|
|
489
501
|
'lifecycle.apiTimeout120': '[reactpress] API not ready within 120s: {url}',
|
|
490
502
|
'lifecycle.apiExitedEarly': '[reactpress] API process exited early (pid {pid})',
|
|
491
503
|
'lifecycle.apiReady': '[reactpress] API ready: {url}',
|
|
504
|
+
'lifecycle.clientStopped': '[reactpress] Site process stopped (pid {pid})',
|
|
505
|
+
'lifecycle.stopClientPidFailed': '[reactpress] Failed to stop site pid {pid}:',
|
|
506
|
+
'lifecycle.clientAlreadyRunning': '[reactpress] Site already running (pid {pid})',
|
|
507
|
+
'lifecycle.clientPidMissing': '[reactpress] Site failed to start (no pid recorded)',
|
|
508
|
+
'lifecycle.waitingClient': 'Waiting for site at {url}…',
|
|
509
|
+
'lifecycle.clientTimeout': '[reactpress] Site not ready within 180s: {url}',
|
|
510
|
+
'lifecycle.clientExitedEarly': '[reactpress] Site process exited early (pid {pid})',
|
|
511
|
+
'lifecycle.clientReady': '[reactpress] Site ready: {url} (pid {pid})',
|
|
492
512
|
'lifecycle.apiStatusTitle': '[reactpress] API status',
|
|
493
513
|
'lifecycle.source': ' Source: {source}',
|
|
494
514
|
'lifecycle.source.monorepo': 'monorepo server/',
|
|
@@ -500,6 +520,7 @@ const STRINGS = {
|
|
|
500
520
|
'lifecycle.httpReachable': 'reachable',
|
|
501
521
|
'lifecycle.httpUnreachable': 'unreachable',
|
|
502
522
|
'lifecycle.unknownCommand': 'Unknown lifecycle command: {command}',
|
|
523
|
+
'lifecycle.processSupervisorFallback': '[reactpress] Process supervisor unavailable — falling back to background node start',
|
|
503
524
|
'docker.stopping': '[reactpress] Stopping Docker services…',
|
|
504
525
|
'docker.stopped': '[reactpress] Docker services stopped.',
|
|
505
526
|
'docker.stopFailed': '[reactpress] Failed to stop Docker:',
|
|
@@ -730,6 +751,13 @@ const STRINGS = {
|
|
|
730
751
|
'cli.doctor.logs.sourceOption': '日志类型:error、request、response 或 all',
|
|
731
752
|
'cli.doctor.logs.grepOption': '用正则过滤日志行',
|
|
732
753
|
'cli.doctor.logs.listOption': '仅列出可用日志文件',
|
|
754
|
+
'cli.logs.description': '查看 API 日志(error / request / response)',
|
|
755
|
+
'cli.logs.descriptionAlias': '查看 API 日志(同 reactpress logs)',
|
|
756
|
+
'cli.logs.tailOption': '每个日志文件显示的行数',
|
|
757
|
+
'cli.logs.sourceOption': '日志类型:error、request、response 或 all',
|
|
758
|
+
'cli.logs.grepOption': '用正则过滤日志行',
|
|
759
|
+
'cli.logs.listOption': '仅列出可用日志文件',
|
|
760
|
+
'cli.stop.description': '停止 init 启动的 API 与站点服务',
|
|
733
761
|
'cli.db.description': '数据库运维',
|
|
734
762
|
'cli.db.backup': '使用 mysqldump 备份当前项目数据库',
|
|
735
763
|
'cli.db.backup.output': '输出 SQL 文件路径',
|
|
@@ -738,9 +766,11 @@ const STRINGS = {
|
|
|
738
766
|
'cli.publish.publish': '发布核心 npm 包',
|
|
739
767
|
'cli.start.description': '启动 ReactPress(SQLite API + 管理后台 + 主题)',
|
|
740
768
|
'cli.help.examples': '用法:',
|
|
741
|
-
'cli.help.init': ' reactpress init
|
|
769
|
+
'cli.help.init': ' reactpress init [dir] 初始化项目',
|
|
742
770
|
'cli.help.doctor': ' reactpress doctor 诊断环境问题',
|
|
743
|
-
'cli.help.
|
|
771
|
+
'cli.help.logs': ' reactpress logs 查看 API 日志,快速定位问题',
|
|
772
|
+
'cli.help.stop': ' reactpress stop 停止后台 API',
|
|
773
|
+
'cli.help.doctorLogs': ' reactpress doctor logs reactpress logs 的别名',
|
|
744
774
|
'cli.help.zeroDep': ' 零依赖:仅需 Node.js 18+,无需 Docker、nginx 或 MySQL',
|
|
745
775
|
'cli.plugin.description': '管理 ReactPress 插件',
|
|
746
776
|
'cli.plugin.install.description': '安装本地插件到 .reactpress/plugins',
|
|
@@ -766,10 +796,11 @@ const STRINGS = {
|
|
|
766
796
|
'init.label.site': '站点',
|
|
767
797
|
'init.label.admin': '后台',
|
|
768
798
|
'init.label.api': 'API',
|
|
769
|
-
'init.hint': '
|
|
799
|
+
'init.hint': '账号 admin / admin · 服务已在后台运行 · 停止:reactpress stop · 日志:reactpress logs',
|
|
800
|
+
'init.alreadyInitialized': '项目已初始化 — 正在确保服务运行',
|
|
770
801
|
'init.apiOnlyHint': 'API 已运行。请在管理后台「外观 → 主题」安装主题以启用访客站。',
|
|
771
802
|
'init.unknownCommand': '未知命令:{cmd}',
|
|
772
|
-
'init.useInitOnly': 'ReactPress 4 支持:reactpress init ·
|
|
803
|
+
'init.useInitOnly': 'ReactPress 4 支持:reactpress init · doctor · logs · stop',
|
|
773
804
|
'banner.subtitle': '· 发布平台 · v4.0',
|
|
774
805
|
'banner.tagline': '零依赖 · SQLite · 约 60 秒上线',
|
|
775
806
|
/** 左侧装饰性进度条标签(不是网址;仓库地址放在卡片顶部 Title 正下方)。 */
|
|
@@ -1068,8 +1099,10 @@ const STRINGS = {
|
|
|
1068
1099
|
'doctor.checking': '正在检查 {name}…',
|
|
1069
1100
|
'doctor.summary': '通过 {passed} · 失败 {failed} · 共 {total} 项',
|
|
1070
1101
|
'doctor.fixesHeader': '修复建议',
|
|
1071
|
-
'doctor.logsHint': '提示:运行 reactpress
|
|
1072
|
-
'
|
|
1102
|
+
'doctor.logsHint': '提示:运行 reactpress logs --tail 100 查看 API 错误日志',
|
|
1103
|
+
'logs.title': 'ReactPress 日志',
|
|
1104
|
+
'logs.subtitle': 'API 日志查看',
|
|
1105
|
+
'doctor.logs.title': 'ReactPress 日志',
|
|
1073
1106
|
'doctor.logs.subtitle': 'API 日志查看',
|
|
1074
1107
|
'doctor.logs.dir': '日志目录 {path}',
|
|
1075
1108
|
'doctor.logs.empty': '尚未找到 API 日志文件。',
|
|
@@ -1081,7 +1114,7 @@ const STRINGS = {
|
|
|
1081
1114
|
'doctor.logs.noPid': '未找到 API pid 文件:{path}',
|
|
1082
1115
|
'doctor.logs.pidRunning': 'API 进程 pid {pid}(记录在 {path})',
|
|
1083
1116
|
'doctor.logs.pidStale': 'pid 文件 {path} 中的 {pid} 已失效(进程未运行)',
|
|
1084
|
-
'doctor.logs.moreHint': '可用 --tail、--source、--grep、--list 进一步筛选(见 reactpress
|
|
1117
|
+
'doctor.logs.moreHint': '可用 --tail、--source、--grep、--list 进一步筛选(见 reactpress logs --help)',
|
|
1085
1118
|
'status.title': 'ReactPress 项目状态',
|
|
1086
1119
|
'status.dir': '项目目录 {path}',
|
|
1087
1120
|
'status.apiSource': 'API 来源 {source}',
|
|
@@ -1142,6 +1175,14 @@ const STRINGS = {
|
|
|
1142
1175
|
'lifecycle.apiTimeout120': '[reactpress] API 在 120s 内未就绪: {url}',
|
|
1143
1176
|
'lifecycle.apiExitedEarly': '[reactpress] API 进程已提前退出 (pid {pid})',
|
|
1144
1177
|
'lifecycle.apiReady': '[reactpress] API 已就绪: {url}',
|
|
1178
|
+
'lifecycle.clientStopped': '[reactpress] 已停止站点进程 (pid {pid})',
|
|
1179
|
+
'lifecycle.stopClientPidFailed': '[reactpress] 停止站点 pid {pid} 失败:',
|
|
1180
|
+
'lifecycle.clientAlreadyRunning': '[reactpress] 站点已在运行 (pid {pid})',
|
|
1181
|
+
'lifecycle.clientPidMissing': '[reactpress] 站点启动失败(未记录 pid)',
|
|
1182
|
+
'lifecycle.waitingClient': '等待站点就绪 {url}…',
|
|
1183
|
+
'lifecycle.clientTimeout': '[reactpress] 站点在 180s 内未就绪: {url}',
|
|
1184
|
+
'lifecycle.clientExitedEarly': '[reactpress] 站点进程已提前退出 (pid {pid})',
|
|
1185
|
+
'lifecycle.clientReady': '[reactpress] 站点已就绪: {url} (pid {pid})',
|
|
1145
1186
|
'lifecycle.apiStatusTitle': '[reactpress] API 状态',
|
|
1146
1187
|
'lifecycle.source': ' 来源: {source}',
|
|
1147
1188
|
'lifecycle.source.monorepo': 'monorepo server/',
|
|
@@ -1153,6 +1194,7 @@ const STRINGS = {
|
|
|
1153
1194
|
'lifecycle.httpReachable': '可访问',
|
|
1154
1195
|
'lifecycle.httpUnreachable': '不可访问',
|
|
1155
1196
|
'lifecycle.unknownCommand': '未知 lifecycle 命令: {command}',
|
|
1197
|
+
'lifecycle.processSupervisorFallback': '[reactpress] 进程守护不可用 — 已回退到后台 Node 启动',
|
|
1156
1198
|
'docker.stopping': '[reactpress] 正在停止 Docker 服务…',
|
|
1157
1199
|
'docker.stopped': '[reactpress] Docker 服务已停止。',
|
|
1158
1200
|
'docker.stopFailed': '[reactpress] 停止 Docker 失败:',
|
package/out/lib/lifecycle.js
CHANGED
|
@@ -10,9 +10,12 @@ const { loadServerSiteUrl, waitForHttp } = require('./http');
|
|
|
10
10
|
const { getServerBin, getServerDir, isUsingMonorepoServer, canStartLocalApi, getPidFile, } = require('./paths');
|
|
11
11
|
const net = require('net');
|
|
12
12
|
const { readPid, isProcessRunning, clearPidFile, writePid } = require('./process');
|
|
13
|
+
const { stopClient } = require('./client-lifecycle');
|
|
13
14
|
const { ensureOriginalCwd } = require('./root');
|
|
14
15
|
const { t } = require('./i18n');
|
|
16
|
+
const { getPm2ServerMemoryRestart } = require('./prod-memory');
|
|
15
17
|
const { ensureBundledServerDeps } = require('./server-bundle');
|
|
18
|
+
const { PM2_API_APP, PM2_CLIENT_APP, isPm2RuntimeAvailable, isPm2AppOnline, getPm2AppPid, runPm2, stopPm2Apps, } = require('./pm2-runtime');
|
|
16
19
|
function parseServerPort(projectRoot) {
|
|
17
20
|
try {
|
|
18
21
|
const url = new URL(loadServerSiteUrl(projectRoot));
|
|
@@ -67,51 +70,45 @@ function stopApi(projectRoot) {
|
|
|
67
70
|
}
|
|
68
71
|
clearPidFile(projectRoot);
|
|
69
72
|
}
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
clearPidFile(projectRoot);
|
|
80
|
-
if (!canStartLocalApi(projectRoot)) {
|
|
81
|
-
console.error(t('lifecycle.noServerAvailable'));
|
|
82
|
-
return 1;
|
|
83
|
-
}
|
|
84
|
-
if (!isUsingMonorepoServer(projectRoot)) {
|
|
85
|
-
const bundled = await ensureBundledServerDeps(projectRoot);
|
|
86
|
-
if (!bundled.ok) {
|
|
87
|
-
console.error(bundled.message || t('bundle.serverBundle.notBuilt'));
|
|
88
|
-
return 1;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
if (isUsingMonorepoServer(projectRoot)) {
|
|
92
|
-
console.log(t('lifecycle.startingLocalApi'));
|
|
93
|
-
}
|
|
94
|
-
else {
|
|
95
|
-
console.log(t('lifecycle.startingBundledApi'));
|
|
96
|
-
}
|
|
73
|
+
function stopAll(projectRoot) {
|
|
74
|
+
stopPm2Apps(projectRoot, [PM2_API_APP, PM2_CLIENT_APP]);
|
|
75
|
+
stopClient(projectRoot);
|
|
76
|
+
stopApi(projectRoot);
|
|
77
|
+
}
|
|
78
|
+
async function startApiWithPm2(projectRoot, { wait = true } = {}) {
|
|
79
|
+
const serverBin = getServerBin(projectRoot);
|
|
80
|
+
const serverDir = getServerDir(projectRoot);
|
|
97
81
|
const serverLogDir = path.join(projectRoot, '.reactpress', 'logs', 'server');
|
|
98
82
|
fs.mkdirSync(serverLogDir, { recursive: true });
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
83
|
+
const started = runPm2(projectRoot, [
|
|
84
|
+
'start',
|
|
85
|
+
serverBin,
|
|
86
|
+
'--name',
|
|
87
|
+
PM2_API_APP,
|
|
88
|
+
'--cwd',
|
|
89
|
+
serverDir,
|
|
90
|
+
'--max-memory-restart',
|
|
91
|
+
getPm2ServerMemoryRestart(),
|
|
92
|
+
'--update-env',
|
|
93
|
+
'-f',
|
|
94
|
+
], {
|
|
103
95
|
env: {
|
|
104
|
-
...process.env,
|
|
105
96
|
REACTPRESS_ORIGINAL_CWD: projectRoot,
|
|
106
97
|
REACTPRESS_SERVER_LOG_DIR: serverLogDir,
|
|
107
98
|
},
|
|
99
|
+
stdio: 'ignore',
|
|
108
100
|
});
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (!wait) {
|
|
113
|
-
return 0;
|
|
101
|
+
if (!started.ok) {
|
|
102
|
+
console.warn(t('lifecycle.processSupervisorFallback'));
|
|
103
|
+
return startApiDetached(projectRoot, { wait });
|
|
114
104
|
}
|
|
105
|
+
const pid = getPm2AppPid(projectRoot, PM2_API_APP);
|
|
106
|
+
console.log(t('lifecycle.apiStartedBg', { pid: pid ?? PM2_API_APP }));
|
|
107
|
+
if (!wait)
|
|
108
|
+
return 0;
|
|
109
|
+
return waitForApiReady(projectRoot, pid);
|
|
110
|
+
}
|
|
111
|
+
async function waitForApiReady(projectRoot, pid) {
|
|
115
112
|
const serverUrl = loadServerSiteUrl(projectRoot);
|
|
116
113
|
const spinner = ora({
|
|
117
114
|
text: t('dev.waitingApi', { url: serverUrl }),
|
|
@@ -121,8 +118,8 @@ async function startApi(projectRoot, { wait = true } = {}) {
|
|
|
121
118
|
const deadline = Date.now() + 120_000;
|
|
122
119
|
let ready = false;
|
|
123
120
|
while (Date.now() < deadline) {
|
|
124
|
-
if (!isProcessRunning(
|
|
125
|
-
spinner.fail(t('lifecycle.apiExitedEarly', { pid
|
|
121
|
+
if (pid && !isProcessRunning(pid) && !isPm2AppOnline(projectRoot, PM2_API_APP)) {
|
|
122
|
+
spinner.fail(t('lifecycle.apiExitedEarly', { pid }));
|
|
126
123
|
return 1;
|
|
127
124
|
}
|
|
128
125
|
if (await require('./http').isHttpResponding(serverUrl)) {
|
|
@@ -138,6 +135,63 @@ async function startApi(projectRoot, { wait = true } = {}) {
|
|
|
138
135
|
spinner.succeed(t('lifecycle.apiReady', { url: serverUrl }));
|
|
139
136
|
return 0;
|
|
140
137
|
}
|
|
138
|
+
async function startApiDetached(projectRoot, { wait = true } = {}) {
|
|
139
|
+
const serverLogDir = path.join(projectRoot, '.reactpress', 'logs', 'server');
|
|
140
|
+
fs.mkdirSync(serverLogDir, { recursive: true });
|
|
141
|
+
const child = spawn(process.execPath, [getServerBin(projectRoot)], {
|
|
142
|
+
cwd: getServerDir(projectRoot),
|
|
143
|
+
detached: true,
|
|
144
|
+
stdio: 'ignore',
|
|
145
|
+
env: {
|
|
146
|
+
...process.env,
|
|
147
|
+
REACTPRESS_ORIGINAL_CWD: projectRoot,
|
|
148
|
+
REACTPRESS_SERVER_LOG_DIR: serverLogDir,
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
child.unref();
|
|
152
|
+
writePid(projectRoot, child.pid);
|
|
153
|
+
console.log(t('lifecycle.apiStartedBg', { pid: child.pid }));
|
|
154
|
+
if (!wait)
|
|
155
|
+
return 0;
|
|
156
|
+
return waitForApiReady(projectRoot, child.pid);
|
|
157
|
+
}
|
|
158
|
+
async function startApi(projectRoot, { wait = true } = {}) {
|
|
159
|
+
if (!(await ensureConfig(projectRoot))) {
|
|
160
|
+
return 1;
|
|
161
|
+
}
|
|
162
|
+
if (isPm2AppOnline(projectRoot, PM2_API_APP)) {
|
|
163
|
+
const pid = getPm2AppPid(projectRoot, PM2_API_APP);
|
|
164
|
+
console.log(t('lifecycle.apiAlreadyRunning', { pid: pid ?? PM2_API_APP }));
|
|
165
|
+
return 0;
|
|
166
|
+
}
|
|
167
|
+
const existing = readPid(projectRoot);
|
|
168
|
+
if (existing && isProcessRunning(existing)) {
|
|
169
|
+
console.log(t('lifecycle.apiAlreadyRunning', { pid: existing }));
|
|
170
|
+
return 0;
|
|
171
|
+
}
|
|
172
|
+
clearPidFile(projectRoot);
|
|
173
|
+
if (!canStartLocalApi(projectRoot)) {
|
|
174
|
+
console.error(t('lifecycle.noServerAvailable'));
|
|
175
|
+
return 1;
|
|
176
|
+
}
|
|
177
|
+
if (!isUsingMonorepoServer(projectRoot)) {
|
|
178
|
+
const bundled = await ensureBundledServerDeps(projectRoot);
|
|
179
|
+
if (!bundled.ok) {
|
|
180
|
+
console.error(bundled.message || t('bundle.serverBundle.notBuilt'));
|
|
181
|
+
return 1;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
if (isUsingMonorepoServer(projectRoot)) {
|
|
185
|
+
console.log(t('lifecycle.startingLocalApi'));
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
console.log(t('lifecycle.startingBundledApi'));
|
|
189
|
+
}
|
|
190
|
+
if (isPm2RuntimeAvailable(projectRoot)) {
|
|
191
|
+
return startApiWithPm2(projectRoot, { wait });
|
|
192
|
+
}
|
|
193
|
+
return startApiDetached(projectRoot, { wait });
|
|
194
|
+
}
|
|
141
195
|
async function statusApi(projectRoot) {
|
|
142
196
|
const pid = readPid(projectRoot);
|
|
143
197
|
const serverUrl = loadServerSiteUrl(projectRoot);
|
|
@@ -171,10 +225,10 @@ async function runLifecycleCommand(command, projectRoot = ensureOriginalCwd()) {
|
|
|
171
225
|
case 'start:bg':
|
|
172
226
|
return startApi(projectRoot, { wait: false });
|
|
173
227
|
case 'stop':
|
|
174
|
-
|
|
228
|
+
stopAll(projectRoot);
|
|
175
229
|
return 0;
|
|
176
230
|
case 'restart':
|
|
177
|
-
|
|
231
|
+
stopAll(projectRoot);
|
|
178
232
|
await waitForPortFree(parseServerPort(projectRoot));
|
|
179
233
|
await new Promise((r) => setTimeout(r, 400));
|
|
180
234
|
return startApi(projectRoot, { wait: true });
|
|
@@ -188,6 +242,7 @@ async function runLifecycleCommand(command, projectRoot = ensureOriginalCwd()) {
|
|
|
188
242
|
module.exports = {
|
|
189
243
|
startApi,
|
|
190
244
|
stopApi,
|
|
245
|
+
stopAll,
|
|
191
246
|
statusApi,
|
|
192
247
|
runLifecycleCommand,
|
|
193
248
|
};
|
package/out/lib/paths.js
CHANGED
|
@@ -95,6 +95,9 @@ function getClientBin(projectRoot) {
|
|
|
95
95
|
function getPidFile(projectRoot) {
|
|
96
96
|
return path.join(resolveProjectRoot(projectRoot), '.reactpress', 'server.pid');
|
|
97
97
|
}
|
|
98
|
+
function getClientPidFile(projectRoot) {
|
|
99
|
+
return path.join(resolveProjectRoot(projectRoot), '.reactpress', 'client.pid');
|
|
100
|
+
}
|
|
98
101
|
module.exports = {
|
|
99
102
|
getMonorepoRoot,
|
|
100
103
|
resolveProjectRoot,
|
|
@@ -113,5 +116,6 @@ module.exports = {
|
|
|
113
116
|
getThemeBin,
|
|
114
117
|
getClientBin,
|
|
115
118
|
getPidFile,
|
|
119
|
+
getClientPidFile,
|
|
116
120
|
};
|
|
117
121
|
//# sourceMappingURL=paths.js.map
|