@fecommunity/reactpress 4.0.0-beta.15 → 4.0.0-beta.18
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/admin-dist/assets/{admin-bamoE2Tn.js → admin-CF4LIXsB.js} +1 -1
- package/admin-dist/assets/{admin-CrL6Xc9b.js → admin-DYBZ60Ac.js} +1 -1
- package/admin-dist/assets/{cssMode-J2cfgFkq.js → cssMode-BJqbvgLG.js} +1 -1
- package/admin-dist/assets/{freemarker2-L_gr_Wq4.js → freemarker2-CYGOmtYQ.js} +1 -1
- package/admin-dist/assets/{handlebars-D81kzUDC.js → handlebars-qhE4jgvZ.js} +1 -1
- package/admin-dist/assets/{html-Cung-Yr0.js → html-U5gI8Ly8.js} +1 -1
- package/admin-dist/assets/{htmlMode-C6QemWl2.js → htmlMode-DQd5Eb2D.js} +1 -1
- package/admin-dist/assets/{index-DXTkTQ21.js → index-BKNoDtfb.js} +178 -178
- package/admin-dist/assets/{javascript-WUcGtCKt.js → javascript-DU3Ebk7L.js} +1 -1
- package/admin-dist/assets/{jsonMode-Dp0R9P1F.js → jsonMode-D1w8Vhjb.js} +1 -1
- package/admin-dist/assets/{liquid-OFb9c-6B.js → liquid-BPVbnl_K.js} +1 -1
- package/admin-dist/assets/{mdx-CUfjLxA3.js → mdx-CMSYE9vh.js} +1 -1
- package/admin-dist/assets/{python-zxEYY77U.js → python-Dbpms-Xp.js} +1 -1
- package/admin-dist/assets/{razor-DJrSr4ly.js → razor-CWQdYrqP.js} +1 -1
- package/admin-dist/assets/{tsMode-Dtxx1cRn.js → tsMode-DAsZhwU2.js} +1 -1
- package/admin-dist/assets/{typescript-DT11gl4K.js → typescript-7mrVr_47.js} +1 -1
- package/admin-dist/assets/{xml-BIbLvdwx.js → xml-DbPOrhNN.js} +1 -1
- package/admin-dist/assets/{yaml-BEeUZskE.js → yaml-6hDyKyrt.js} +1 -1
- package/admin-dist/index.html +1 -1
- package/out/bin/reactpress-theme-client.js +4 -0
- package/out/bin/reactpress.js +7 -2
- package/out/core/services/admin-static.js +79 -26
- package/out/core/services/local-site.js +68 -0
- package/out/lib/bootstrap.js +9 -0
- package/out/lib/i18n/strings.js +20 -8
- package/out/lib/lifecycle.js +9 -0
- package/out/lib/pm2-runtime.js +15 -0
- package/out/lib/project-logs.js +318 -12
- package/package.json +2 -1
- package/plugins/hello-world/README.md +104 -0
- package/plugins/hello-world/dist/config.d.ts +4 -0
- package/plugins/hello-world/dist/config.js +17 -0
- package/plugins/hello-world/dist/index.d.ts +10 -0
- package/plugins/hello-world/dist/index.js +15 -0
- package/plugins/hello-world/dist/summary.d.ts +3 -0
- package/plugins/hello-world/dist/summary.js +33 -0
- package/plugins/hello-world/dist/types.d.ts +16 -0
- package/plugins/hello-world/dist/types.js +2 -0
- package/plugins/hello-world/locales/en.json +24 -0
- package/plugins/hello-world/locales/zh.json +24 -0
- package/plugins/hello-world/package.json +20 -0
- package/plugins/hello-world/plugin.json +61 -0
- package/plugins/hello-world/tsconfig.json +16 -0
- package/plugins/image-optimizer/README.md +43 -0
- package/plugins/image-optimizer/admin/OptimizeDashboard.tsx +249 -0
- package/plugins/image-optimizer/admin/api.ts +133 -0
- package/plugins/image-optimizer/admin/index.ts +16 -0
- package/plugins/image-optimizer/admin/locale.ts +68 -0
- package/plugins/image-optimizer/admin/optimize-dashboard.module.css +80 -0
- package/plugins/image-optimizer/dist/config.d.ts +8 -0
- package/plugins/image-optimizer/dist/config.js +13 -0
- package/plugins/image-optimizer/dist/index.d.ts +10 -0
- package/plugins/image-optimizer/dist/index.js +13 -0
- package/plugins/image-optimizer/locales/en.json +51 -0
- package/plugins/image-optimizer/locales/zh.json +53 -0
- package/plugins/image-optimizer/package.json +20 -0
- package/plugins/image-optimizer/plugin.json +58 -0
- package/plugins/image-optimizer/tsconfig.json +16 -0
- package/plugins/package.json +14 -0
- package/plugins/seo/README.md +60 -0
- package/plugins/seo/admin/ArticleEditorSeoPanel.tsx +67 -0
- package/plugins/seo/admin/article-editor-seo-panel.module.css +74 -0
- package/plugins/seo/admin/index.ts +18 -0
- package/plugins/seo/dist/config.d.ts +4 -0
- package/plugins/seo/dist/config.js +19 -0
- package/plugins/seo/dist/index.d.ts +10 -0
- package/plugins/seo/dist/index.js +23 -0
- package/plugins/seo/dist/seo.d.ts +3 -0
- package/plugins/seo/dist/seo.js +79 -0
- package/plugins/seo/dist/slug.d.ts +2 -0
- package/plugins/seo/dist/slug.js +13 -0
- package/plugins/seo/dist/types.d.ts +23 -0
- package/plugins/seo/dist/types.js +2 -0
- package/plugins/seo/locales/en.json +32 -0
- package/plugins/seo/locales/zh.json +32 -0
- package/plugins/seo/package.json +20 -0
- package/plugins/seo/plugin.json +76 -0
- package/plugins/seo/tsconfig.json +16 -0
- package/server/bin/reactpress-server.js +17 -0
- package/server/package-lock.json +3 -3
- package/server/package.json +1 -1
- package/toolkit/package.json +1 -1
|
@@ -5,6 +5,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.getLocalSitePaths = getLocalSitePaths;
|
|
7
7
|
exports.ensureLocalSite = ensureLocalSite;
|
|
8
|
+
exports.ensureBundledPlugins = ensureBundledPlugins;
|
|
8
9
|
exports.initLocalProject = initLocalProject;
|
|
9
10
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
10
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -44,6 +45,7 @@ function ensureLocalSite(siteRoot, port, options = {}) {
|
|
|
44
45
|
if (options.monorepoRoot) {
|
|
45
46
|
seedBundledAssets(siteRoot, options.monorepoRoot);
|
|
46
47
|
}
|
|
48
|
+
ensureBundledPlugins(siteRoot);
|
|
47
49
|
const configPath = node_path_1.default.join(paths.reactpressDir, 'config.json');
|
|
48
50
|
if (!fs_extra_1.default.existsSync(configPath)) {
|
|
49
51
|
const config = {
|
|
@@ -119,6 +121,72 @@ function seedRuntimeThemes(siteRoot, monorepoRoot) {
|
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
123
|
}
|
|
124
|
+
const PLUGIN_COPY_SKIP_DIRS = new Set(['node_modules', '.git', '.turbo', 'src', 'coverage']);
|
|
125
|
+
function hasUsablePluginsRegistry(pluginsDir) {
|
|
126
|
+
const pkgPath = node_path_1.default.join(pluginsDir, 'package.json');
|
|
127
|
+
if (!fs_extra_1.default.existsSync(pkgPath))
|
|
128
|
+
return false;
|
|
129
|
+
try {
|
|
130
|
+
const meta = JSON.parse(fs_extra_1.default.readFileSync(pkgPath, 'utf8'));
|
|
131
|
+
const local = Array.isArray(meta.reactpress?.local) ? meta.reactpress.local : [];
|
|
132
|
+
return local.some((id) => typeof id === 'string' &&
|
|
133
|
+
id.trim() &&
|
|
134
|
+
fs_extra_1.default.existsSync(node_path_1.default.join(pluginsDir, id.trim(), 'plugin.json')));
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function copyPluginTree(sourceDir, targetDir) {
|
|
141
|
+
fs_extra_1.default.mkdirSync(targetDir, { recursive: true });
|
|
142
|
+
for (const entry of fs_extra_1.default.readdirSync(sourceDir, { withFileTypes: true })) {
|
|
143
|
+
const sourcePath = node_path_1.default.join(sourceDir, entry.name);
|
|
144
|
+
const targetPath = node_path_1.default.join(targetDir, entry.name);
|
|
145
|
+
if (entry.isDirectory()) {
|
|
146
|
+
if (PLUGIN_COPY_SKIP_DIRS.has(entry.name))
|
|
147
|
+
continue;
|
|
148
|
+
copyPluginTree(sourcePath, targetPath);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
fs_extra_1.default.copyFileSync(sourcePath, targetPath);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
function copyBundledPlugins(sourceDir, targetDir) {
|
|
155
|
+
const sourcePackageJson = node_path_1.default.join(sourceDir, 'package.json');
|
|
156
|
+
if (!fs_extra_1.default.existsSync(sourcePackageJson))
|
|
157
|
+
return;
|
|
158
|
+
fs_extra_1.default.mkdirSync(targetDir, { recursive: true });
|
|
159
|
+
fs_extra_1.default.copyFileSync(sourcePackageJson, node_path_1.default.join(targetDir, 'package.json'));
|
|
160
|
+
let meta = {};
|
|
161
|
+
try {
|
|
162
|
+
meta = JSON.parse(fs_extra_1.default.readFileSync(sourcePackageJson, 'utf8'));
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
const local = Array.isArray(meta.reactpress?.local) ? meta.reactpress.local : [];
|
|
168
|
+
for (const id of local) {
|
|
169
|
+
if (typeof id !== 'string' || !id.trim())
|
|
170
|
+
continue;
|
|
171
|
+
const sourcePath = node_path_1.default.join(sourceDir, id.trim());
|
|
172
|
+
const targetPath = node_path_1.default.join(targetDir, id.trim());
|
|
173
|
+
if (!fs_extra_1.default.existsSync(node_path_1.default.join(sourcePath, 'plugin.json')) || fs_extra_1.default.existsSync(targetPath))
|
|
174
|
+
continue;
|
|
175
|
+
copyPluginTree(sourcePath, targetPath);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** Seed default plugins from the CLI bundle when the site has no plugins registry yet. */
|
|
179
|
+
function ensureBundledPlugins(siteRoot) {
|
|
180
|
+
const targetDir = node_path_1.default.join(siteRoot, 'plugins');
|
|
181
|
+
if (hasUsablePluginsRegistry(targetDir))
|
|
182
|
+
return false;
|
|
183
|
+
const bundledDir = node_path_1.default.join((0, paths_1.getPackageRoot)(), 'plugins');
|
|
184
|
+
if (fs_extra_1.default.existsSync(node_path_1.default.join(bundledDir, 'package.json'))) {
|
|
185
|
+
copyBundledPlugins(bundledDir, targetDir);
|
|
186
|
+
return hasUsablePluginsRegistry(targetDir);
|
|
187
|
+
}
|
|
188
|
+
return false;
|
|
189
|
+
}
|
|
122
190
|
async function initLocalProject(projectRoot, options = {}) {
|
|
123
191
|
const paths = (0, paths_1.getProjectPaths)(projectRoot);
|
|
124
192
|
const port = options.port ?? 3002;
|
package/out/lib/bootstrap.js
CHANGED
|
@@ -43,6 +43,15 @@ async function ensureProjectEnvironment(projectRoot = (0, root_1.ensureOriginalC
|
|
|
43
43
|
applyZeroDependencyDefaults();
|
|
44
44
|
const root = node_path_1.default.resolve(projectRoot);
|
|
45
45
|
(0, cli_context_1.setProjectCwd)(root);
|
|
46
|
+
try {
|
|
47
|
+
const { ensureBundledPlugins } = require('../core/services/local-site');
|
|
48
|
+
if (ensureBundledPlugins(root)) {
|
|
49
|
+
console.log(`[reactpress] ${(0, i18n_1.t)('init.pluginsSeeded')}`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// bundled plugins are optional; ignore when CLI core is unavailable
|
|
54
|
+
}
|
|
46
55
|
if (!(await (0, config_1.isReactPressProject)(root))) {
|
|
47
56
|
const result = await (0, init_1.initProject)({ directory: root, force: false });
|
|
48
57
|
if (!result.ok) {
|
package/out/lib/i18n/strings.js
CHANGED
|
@@ -76,12 +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
|
|
79
|
+
'cli.logs.description': 'View API logs (PM2 process output or structured error/request/response)',
|
|
80
80
|
'cli.logs.descriptionAlias': 'View API logs (alias of reactpress logs)',
|
|
81
81
|
'cli.logs.tailOption': 'Number of lines to show per log file',
|
|
82
|
-
'cli.logs.sourceOption': 'Log source: error, request, response, or all',
|
|
82
|
+
'cli.logs.sourceOption': 'Log source: process (PM2), error, request, response, or all',
|
|
83
83
|
'cli.logs.grepOption': 'Filter log lines by regex',
|
|
84
84
|
'cli.logs.listOption': 'List available log files only',
|
|
85
|
+
'cli.logs.followOption': 'Stream logs continuously (PM2 when supervised, otherwise tail -f)',
|
|
85
86
|
'cli.stop.description': 'Stop API and site services started by init',
|
|
86
87
|
'cli.db.description': 'Database operations',
|
|
87
88
|
'cli.db.backup': 'Backup current project database with mysqldump',
|
|
@@ -124,6 +125,7 @@ const STRINGS = {
|
|
|
124
125
|
'init.hint': 'Login: admin / admin · Services run in background · Stop: reactpress stop · Logs: reactpress logs',
|
|
125
126
|
'init.alreadyInitialized': 'Project already initialized — ensuring services are running',
|
|
126
127
|
'init.apiOnlyHint': 'API is running. Install a theme in Admin → Appearance to enable the public site.',
|
|
128
|
+
'init.pluginsSeeded': 'Seeded bundled plugins (hello-world, seo, image-optimizer).',
|
|
127
129
|
'init.unknownCommand': 'Unknown command: {cmd}',
|
|
128
130
|
'init.useInitOnly': 'ReactPress 4: reactpress init · doctor · logs · stop',
|
|
129
131
|
'banner.subtitle': '· Publishing platform · v4.0',
|
|
@@ -432,15 +434,19 @@ const STRINGS = {
|
|
|
432
434
|
'doctor.logs.subtitle': 'API log viewer',
|
|
433
435
|
'doctor.logs.dir': 'Log dir {path}',
|
|
434
436
|
'doctor.logs.empty': 'No API log files found yet.',
|
|
435
|
-
'doctor.logs.emptyHint': 'Start the API with reactpress init — logs are written to .reactpress/logs/server/',
|
|
437
|
+
'doctor.logs.emptyHint': 'Start the API with reactpress init — logs are written to .reactpress/logs/server/ (PM2 output: pm2-out.log / pm2-error.log)',
|
|
436
438
|
'doctor.logs.listHeader': 'Available log files',
|
|
437
439
|
'doctor.logs.fileHeader': '{file} (showing {count} line(s))',
|
|
438
440
|
'doctor.logs.noMatchingLines': 'No matching log lines.',
|
|
439
441
|
'doctor.logs.badPattern': 'Invalid grep pattern: {pattern}',
|
|
440
442
|
'doctor.logs.noPid': 'No API pid file at {path}',
|
|
443
|
+
'doctor.logs.activePid': 'API listening on port {port} (pid {pid})',
|
|
444
|
+
'doctor.logs.pm2Supervised': 'API supervised by PM2 (pid {pid})',
|
|
445
|
+
'doctor.logs.pm2Unavailable': 'PM2 is not available for live log streaming.',
|
|
446
|
+
'doctor.logs.followWindows': 'Live follow is not supported here. Open: {path}',
|
|
441
447
|
'doctor.logs.pidRunning': 'API pid {pid} (recorded in {path})',
|
|
442
448
|
'doctor.logs.pidStale': 'Stale pid {pid} in {path} — process not running',
|
|
443
|
-
'doctor.logs.moreHint': 'Use --tail, --source, --grep, or --list for more control (see reactpress logs --help)',
|
|
449
|
+
'doctor.logs.moreHint': 'Use --tail, --source, --grep, --follow, or --list for more control (see reactpress logs --help)',
|
|
444
450
|
'status.title': 'ReactPress project status',
|
|
445
451
|
'status.dir': 'Project {path}',
|
|
446
452
|
'status.apiSource': 'API source {source}',
|
|
@@ -751,12 +757,13 @@ const STRINGS = {
|
|
|
751
757
|
'cli.doctor.logs.sourceOption': '日志类型:error、request、response 或 all',
|
|
752
758
|
'cli.doctor.logs.grepOption': '用正则过滤日志行',
|
|
753
759
|
'cli.doctor.logs.listOption': '仅列出可用日志文件',
|
|
754
|
-
'cli.logs.description': '查看 API 日志(error
|
|
760
|
+
'cli.logs.description': '查看 API 日志(PM2 进程输出或结构化 error/request/response)',
|
|
755
761
|
'cli.logs.descriptionAlias': '查看 API 日志(同 reactpress logs)',
|
|
756
762
|
'cli.logs.tailOption': '每个日志文件显示的行数',
|
|
757
|
-
'cli.logs.sourceOption': '日志类型:error、request、response 或 all',
|
|
763
|
+
'cli.logs.sourceOption': '日志类型:process(PM2)、error、request、response 或 all',
|
|
758
764
|
'cli.logs.grepOption': '用正则过滤日志行',
|
|
759
765
|
'cli.logs.listOption': '仅列出可用日志文件',
|
|
766
|
+
'cli.logs.followOption': '持续输出日志(PM2 托管时用 pm2 logs,否则 tail -f)',
|
|
760
767
|
'cli.stop.description': '停止 init 启动的 API 与站点服务',
|
|
761
768
|
'cli.db.description': '数据库运维',
|
|
762
769
|
'cli.db.backup': '使用 mysqldump 备份当前项目数据库',
|
|
@@ -799,6 +806,7 @@ const STRINGS = {
|
|
|
799
806
|
'init.hint': '账号 admin / admin · 服务已在后台运行 · 停止:reactpress stop · 日志:reactpress logs',
|
|
800
807
|
'init.alreadyInitialized': '项目已初始化 — 正在确保服务运行',
|
|
801
808
|
'init.apiOnlyHint': 'API 已运行。请在管理后台「外观 → 主题」安装主题以启用访客站。',
|
|
809
|
+
'init.pluginsSeeded': '已写入内置插件(hello-world、seo、image-optimizer)。',
|
|
802
810
|
'init.unknownCommand': '未知命令:{cmd}',
|
|
803
811
|
'init.useInitOnly': 'ReactPress 4 支持:reactpress init · doctor · logs · stop',
|
|
804
812
|
'banner.subtitle': '· 发布平台 · v4.0',
|
|
@@ -1106,15 +1114,19 @@ const STRINGS = {
|
|
|
1106
1114
|
'doctor.logs.subtitle': 'API 日志查看',
|
|
1107
1115
|
'doctor.logs.dir': '日志目录 {path}',
|
|
1108
1116
|
'doctor.logs.empty': '尚未找到 API 日志文件。',
|
|
1109
|
-
'doctor.logs.emptyHint': '运行 reactpress init 启动 API
|
|
1117
|
+
'doctor.logs.emptyHint': '运行 reactpress init 启动 API 后,日志写入 .reactpress/logs/server/(PM2 输出:pm2-out.log / pm2-error.log)',
|
|
1110
1118
|
'doctor.logs.listHeader': '可用日志文件',
|
|
1111
1119
|
'doctor.logs.fileHeader': '{file}(显示 {count} 行)',
|
|
1112
1120
|
'doctor.logs.noMatchingLines': '没有匹配的日志行。',
|
|
1113
1121
|
'doctor.logs.badPattern': 'grep 正则无效:{pattern}',
|
|
1114
1122
|
'doctor.logs.noPid': '未找到 API pid 文件:{path}',
|
|
1123
|
+
'doctor.logs.activePid': 'API 正在监听端口 {port}(pid {pid})',
|
|
1124
|
+
'doctor.logs.pm2Supervised': 'API 由 PM2 托管(pid {pid})',
|
|
1125
|
+
'doctor.logs.pm2Unavailable': '无法使用 PM2 实时输出日志。',
|
|
1126
|
+
'doctor.logs.followWindows': '当前环境不支持持续跟踪,请直接打开:{path}',
|
|
1115
1127
|
'doctor.logs.pidRunning': 'API 进程 pid {pid}(记录在 {path})',
|
|
1116
1128
|
'doctor.logs.pidStale': 'pid 文件 {path} 中的 {pid} 已失效(进程未运行)',
|
|
1117
|
-
'doctor.logs.moreHint': '可用 --tail、--source、--grep、--list 进一步筛选(见 reactpress logs --help)',
|
|
1129
|
+
'doctor.logs.moreHint': '可用 --tail、--source、--grep、--follow、--list 进一步筛选(见 reactpress logs --help)',
|
|
1118
1130
|
'status.title': 'ReactPress 项目状态',
|
|
1119
1131
|
'status.dir': '项目目录 {path}',
|
|
1120
1132
|
'status.apiSource': 'API 来源 {source}',
|
package/out/lib/lifecycle.js
CHANGED
|
@@ -80,6 +80,8 @@ async function startApiWithPm2(projectRoot, { wait = true } = {}) {
|
|
|
80
80
|
const serverDir = getServerDir(projectRoot);
|
|
81
81
|
const serverLogDir = path.join(projectRoot, '.reactpress', 'logs', 'server');
|
|
82
82
|
fs.mkdirSync(serverLogDir, { recursive: true });
|
|
83
|
+
const pm2OutLog = path.join(serverLogDir, 'pm2-out.log');
|
|
84
|
+
const pm2ErrLog = path.join(serverLogDir, 'pm2-error.log');
|
|
83
85
|
const started = runPm2(projectRoot, [
|
|
84
86
|
'start',
|
|
85
87
|
serverBin,
|
|
@@ -87,6 +89,10 @@ async function startApiWithPm2(projectRoot, { wait = true } = {}) {
|
|
|
87
89
|
PM2_API_APP,
|
|
88
90
|
'--cwd',
|
|
89
91
|
serverDir,
|
|
92
|
+
'--output',
|
|
93
|
+
pm2OutLog,
|
|
94
|
+
'--error',
|
|
95
|
+
pm2ErrLog,
|
|
90
96
|
'--max-memory-restart',
|
|
91
97
|
getPm2ServerMemoryRestart(),
|
|
92
98
|
'--update-env',
|
|
@@ -103,6 +109,9 @@ async function startApiWithPm2(projectRoot, { wait = true } = {}) {
|
|
|
103
109
|
return startApiDetached(projectRoot, { wait });
|
|
104
110
|
}
|
|
105
111
|
const pid = getPm2AppPid(projectRoot, PM2_API_APP);
|
|
112
|
+
if (pid) {
|
|
113
|
+
writePid(projectRoot, pid);
|
|
114
|
+
}
|
|
106
115
|
console.log(t('lifecycle.apiStartedBg', { pid: pid ?? PM2_API_APP }));
|
|
107
116
|
if (!wait)
|
|
108
117
|
return 0;
|
package/out/lib/pm2-runtime.js
CHANGED
|
@@ -69,6 +69,20 @@ function getPm2AppPid(projectRoot, name) {
|
|
|
69
69
|
const app = getPm2App(projectRoot, name);
|
|
70
70
|
return typeof app?.pid === 'number' ? app.pid : null;
|
|
71
71
|
}
|
|
72
|
+
function getPm2AppLogPaths(projectRoot, name = PM2_API_APP) {
|
|
73
|
+
const serverLogDir = path.join(projectRoot, '.reactpress', 'logs', 'server');
|
|
74
|
+
const defaults = {
|
|
75
|
+
out: path.join(serverLogDir, 'pm2-out.log'),
|
|
76
|
+
err: path.join(serverLogDir, 'pm2-error.log'),
|
|
77
|
+
};
|
|
78
|
+
const app = getPm2App(projectRoot, name);
|
|
79
|
+
if (!app)
|
|
80
|
+
return defaults;
|
|
81
|
+
return {
|
|
82
|
+
out: app.pm2_env?.pm_out_log_path || defaults.out,
|
|
83
|
+
err: app.pm2_env?.pm_err_log_path || defaults.err,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
72
86
|
function stopPm2Apps(projectRoot, names) {
|
|
73
87
|
const bin = resolvePm2Bin(projectRoot);
|
|
74
88
|
if (!bin)
|
|
@@ -90,6 +104,7 @@ module.exports = {
|
|
|
90
104
|
getPm2App,
|
|
91
105
|
isPm2AppOnline,
|
|
92
106
|
getPm2AppPid,
|
|
107
|
+
getPm2AppLogPaths,
|
|
93
108
|
stopPm2Apps,
|
|
94
109
|
};
|
|
95
110
|
//# sourceMappingURL=pm2-runtime.js.map
|
package/out/lib/project-logs.js
CHANGED
|
@@ -3,35 +3,259 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
// @ts-nocheck
|
|
4
4
|
const fs = require('fs');
|
|
5
5
|
const path = require('path');
|
|
6
|
+
const { spawnSync } = require('child_process');
|
|
6
7
|
const { brand, divider, gradientText, palette, sectionHeader } = require('../ui/theme');
|
|
7
8
|
const { getServerDir, getPidFile } = require('./paths');
|
|
8
9
|
const { readPid, isProcessRunning } = require('./process');
|
|
10
|
+
const { loadServerSiteUrl } = require('./http');
|
|
11
|
+
const { getPm2App, PM2_API_APP, isPm2AppOnline, getPm2AppLogPaths, resolvePm2Bin, } = require('./pm2-runtime');
|
|
9
12
|
const { t } = require('./i18n');
|
|
10
13
|
const LOG_SOURCES = ['error', 'request', 'response'];
|
|
14
|
+
const PROCESS_LOG_SOURCES = ['pm2-out', 'pm2-error'];
|
|
15
|
+
const PM2_LOG_FILES = {
|
|
16
|
+
'pm2-out': 'pm2-out.log',
|
|
17
|
+
'pm2-error': 'pm2-error.log',
|
|
18
|
+
};
|
|
19
|
+
function measureLogDir(logDir) {
|
|
20
|
+
if (!fs.existsSync(logDir))
|
|
21
|
+
return -1;
|
|
22
|
+
let total = listLogFiles(logDir, 'all').reduce((sum, file) => sum + file.size, 0);
|
|
23
|
+
for (const filename of Object.values(PM2_LOG_FILES)) {
|
|
24
|
+
const filePath = path.join(logDir, filename);
|
|
25
|
+
try {
|
|
26
|
+
if (fs.existsSync(filePath)) {
|
|
27
|
+
total += fs.statSync(filePath).size;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// ignore
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return total;
|
|
35
|
+
}
|
|
36
|
+
function getPm2ServerLogDir(projectRoot) {
|
|
37
|
+
try {
|
|
38
|
+
const app = getPm2App(projectRoot, PM2_API_APP);
|
|
39
|
+
const dir = app?.pm2_env?.env?.REACTPRESS_SERVER_LOG_DIR;
|
|
40
|
+
if (typeof dir === 'string' && dir.trim()) {
|
|
41
|
+
return path.resolve(dir.trim());
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// ignore
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
function parseUrlPort(urlString) {
|
|
50
|
+
try {
|
|
51
|
+
const url = new URL(urlString);
|
|
52
|
+
const port = Number(url.port);
|
|
53
|
+
if (port > 0)
|
|
54
|
+
return port;
|
|
55
|
+
return url.protocol === 'https:' ? 443 : 80;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function resolveListeningPid(port) {
|
|
62
|
+
if (!port || process.platform === 'win32')
|
|
63
|
+
return null;
|
|
64
|
+
const result = spawnSync('lsof', ['-nP', `-iTCP:${port}`, '-sTCP:LISTEN', '-t'], {
|
|
65
|
+
encoding: 'utf8',
|
|
66
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
67
|
+
});
|
|
68
|
+
if (result.status !== 0 || !result.stdout.trim())
|
|
69
|
+
return null;
|
|
70
|
+
const pids = result.stdout
|
|
71
|
+
.trim()
|
|
72
|
+
.split('\n')
|
|
73
|
+
.map((line) => Number.parseInt(line.trim(), 10))
|
|
74
|
+
.filter((pid) => Number.isFinite(pid));
|
|
75
|
+
return pids[0] ?? null;
|
|
76
|
+
}
|
|
77
|
+
function resolveLogDirFromPid(pid) {
|
|
78
|
+
if (!pid || process.platform === 'win32')
|
|
79
|
+
return null;
|
|
80
|
+
const result = spawnSync('lsof', ['-Fn', '-p', String(pid)], {
|
|
81
|
+
encoding: 'utf8',
|
|
82
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
83
|
+
});
|
|
84
|
+
if (result.status !== 0)
|
|
85
|
+
return null;
|
|
86
|
+
for (const line of result.stdout.split('\n')) {
|
|
87
|
+
if (!line.startsWith('n') || !line.includes('.log'))
|
|
88
|
+
continue;
|
|
89
|
+
const logPath = line.slice(1);
|
|
90
|
+
const match = logPath.match(/^(.*\/logs)\/(?:error|request|response)\//);
|
|
91
|
+
if (match)
|
|
92
|
+
return match[1];
|
|
93
|
+
}
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
function resolveActiveServerLogDir(projectRoot) {
|
|
97
|
+
const port = parseUrlPort(loadServerSiteUrl(projectRoot));
|
|
98
|
+
if (!port)
|
|
99
|
+
return null;
|
|
100
|
+
const pid = resolveListeningPid(port);
|
|
101
|
+
if (!pid)
|
|
102
|
+
return null;
|
|
103
|
+
return resolveLogDirFromPid(pid);
|
|
104
|
+
}
|
|
105
|
+
function collectLogDirCandidates(projectRoot) {
|
|
106
|
+
const candidates = [
|
|
107
|
+
path.join(projectRoot, '.reactpress', 'logs', 'server'),
|
|
108
|
+
path.join(getServerDir(projectRoot), 'logs'),
|
|
109
|
+
];
|
|
110
|
+
const pm2Dir = getPm2ServerLogDir(projectRoot);
|
|
111
|
+
if (pm2Dir)
|
|
112
|
+
candidates.push(pm2Dir);
|
|
113
|
+
const activeDir = resolveActiveServerLogDir(projectRoot);
|
|
114
|
+
if (activeDir)
|
|
115
|
+
candidates.push(activeDir);
|
|
116
|
+
return [...new Set(candidates.map((dir) => path.resolve(dir)))];
|
|
117
|
+
}
|
|
11
118
|
function getProjectServerLogDir(projectRoot) {
|
|
12
119
|
const preferred = path.join(projectRoot, '.reactpress', 'logs', 'server');
|
|
13
|
-
if (fs.existsSync(preferred)) {
|
|
14
|
-
return preferred;
|
|
15
|
-
}
|
|
16
120
|
const bundled = path.join(getServerDir(projectRoot), 'logs');
|
|
17
|
-
|
|
18
|
-
|
|
121
|
+
const pm2Dir = getPm2ServerLogDir(projectRoot);
|
|
122
|
+
const activeDir = resolveActiveServerLogDir(projectRoot);
|
|
123
|
+
const localCandidates = [preferred, bundled];
|
|
124
|
+
if (pm2Dir)
|
|
125
|
+
localCandidates.push(pm2Dir);
|
|
126
|
+
let bestLocal = preferred;
|
|
127
|
+
let bestLocalScore = -1;
|
|
128
|
+
for (const dir of [...new Set(localCandidates.map((d) => path.resolve(d)))]) {
|
|
129
|
+
const score = measureLogDir(dir);
|
|
130
|
+
if (score > bestLocalScore) {
|
|
131
|
+
bestLocalScore = score;
|
|
132
|
+
bestLocal = dir;
|
|
133
|
+
}
|
|
19
134
|
}
|
|
20
|
-
|
|
135
|
+
if (bestLocalScore > 0) {
|
|
136
|
+
return bestLocal;
|
|
137
|
+
}
|
|
138
|
+
if (activeDir && fs.existsSync(activeDir) && measureLogDir(activeDir) > 0) {
|
|
139
|
+
return activeDir;
|
|
140
|
+
}
|
|
141
|
+
return bestLocal;
|
|
21
142
|
}
|
|
22
143
|
function normalizeSource(source) {
|
|
23
144
|
const value = String(source || 'error').toLowerCase();
|
|
24
145
|
if (value === 'all')
|
|
25
146
|
return 'all';
|
|
147
|
+
if (value === 'process' || value === 'pm2')
|
|
148
|
+
return 'process';
|
|
26
149
|
if (LOG_SOURCES.includes(value))
|
|
27
150
|
return value;
|
|
28
151
|
return 'error';
|
|
29
152
|
}
|
|
30
|
-
function
|
|
153
|
+
function measureStructuredLogDir(logDir) {
|
|
154
|
+
if (!fs.existsSync(logDir))
|
|
155
|
+
return 0;
|
|
156
|
+
let total = 0;
|
|
157
|
+
for (const name of LOG_SOURCES) {
|
|
158
|
+
const dir = path.join(logDir, name);
|
|
159
|
+
if (!fs.existsSync(dir))
|
|
160
|
+
continue;
|
|
161
|
+
for (const entry of fs.readdirSync(dir)) {
|
|
162
|
+
if (!entry.endsWith('.log'))
|
|
163
|
+
continue;
|
|
164
|
+
try {
|
|
165
|
+
total += fs.statSync(path.join(dir, entry)).size;
|
|
166
|
+
}
|
|
167
|
+
catch {
|
|
168
|
+
// ignore
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return total;
|
|
173
|
+
}
|
|
174
|
+
function resolveLogSource(projectRoot, sourceOption) {
|
|
175
|
+
if (sourceOption != null && String(sourceOption).trim() !== '') {
|
|
176
|
+
return normalizeSource(sourceOption);
|
|
177
|
+
}
|
|
178
|
+
const logDir = path.join(projectRoot, '.reactpress', 'logs', 'server');
|
|
179
|
+
if (measureStructuredLogDir(logDir) > 0) {
|
|
180
|
+
return 'error';
|
|
181
|
+
}
|
|
182
|
+
if (isPm2AppOnline(projectRoot, PM2_API_APP)) {
|
|
183
|
+
return 'process';
|
|
184
|
+
}
|
|
185
|
+
return 'error';
|
|
186
|
+
}
|
|
187
|
+
function listProcessLogFiles(projectRoot, logDir, source = 'process') {
|
|
188
|
+
const normalized = normalizeSource(source);
|
|
189
|
+
const names = normalized === 'all'
|
|
190
|
+
? PROCESS_LOG_SOURCES
|
|
191
|
+
: normalized === 'process'
|
|
192
|
+
? PROCESS_LOG_SOURCES
|
|
193
|
+
: PROCESS_LOG_SOURCES.includes(normalized)
|
|
194
|
+
? [normalized]
|
|
195
|
+
: [];
|
|
196
|
+
if (!names.length)
|
|
197
|
+
return [];
|
|
198
|
+
const pm2Paths = getPm2AppLogPaths(projectRoot, PM2_API_APP);
|
|
199
|
+
const pathBySource = {
|
|
200
|
+
'pm2-out': pm2Paths.out,
|
|
201
|
+
'pm2-error': pm2Paths.err,
|
|
202
|
+
};
|
|
203
|
+
const files = [];
|
|
204
|
+
for (const name of names) {
|
|
205
|
+
const localPath = path.join(logDir, PM2_LOG_FILES[name]);
|
|
206
|
+
const candidates = [];
|
|
207
|
+
if (fs.existsSync(localPath)) {
|
|
208
|
+
candidates.push(localPath);
|
|
209
|
+
}
|
|
210
|
+
const remotePath = pathBySource[name];
|
|
211
|
+
if (remotePath &&
|
|
212
|
+
path.resolve(remotePath) !== path.resolve(localPath) &&
|
|
213
|
+
fs.existsSync(remotePath)) {
|
|
214
|
+
candidates.push(remotePath);
|
|
215
|
+
}
|
|
216
|
+
const seen = new Set();
|
|
217
|
+
for (const fullPath of candidates) {
|
|
218
|
+
const resolved = path.resolve(fullPath);
|
|
219
|
+
if (seen.has(resolved))
|
|
220
|
+
continue;
|
|
221
|
+
seen.add(resolved);
|
|
222
|
+
let stat;
|
|
223
|
+
try {
|
|
224
|
+
stat = fs.statSync(resolved);
|
|
225
|
+
}
|
|
226
|
+
catch {
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (!stat.isFile())
|
|
230
|
+
continue;
|
|
231
|
+
files.push({
|
|
232
|
+
source: name,
|
|
233
|
+
path: resolved,
|
|
234
|
+
name: path.basename(resolved),
|
|
235
|
+
mtimeMs: stat.mtimeMs,
|
|
236
|
+
size: stat.size,
|
|
237
|
+
});
|
|
238
|
+
break;
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
files.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
242
|
+
return files;
|
|
243
|
+
}
|
|
244
|
+
function listLogFiles(logDir, source = 'error', projectRoot = null) {
|
|
31
245
|
const normalized = normalizeSource(source);
|
|
32
|
-
|
|
246
|
+
if (normalized === 'process' || PROCESS_LOG_SOURCES.includes(normalized)) {
|
|
247
|
+
return listProcessLogFiles(projectRoot || '', logDir, normalized);
|
|
248
|
+
}
|
|
249
|
+
const sources = normalized === 'all'
|
|
250
|
+
? [...PROCESS_LOG_SOURCES, ...LOG_SOURCES]
|
|
251
|
+
: [normalized];
|
|
33
252
|
const files = [];
|
|
253
|
+
if (normalized === 'all' && projectRoot) {
|
|
254
|
+
files.push(...listProcessLogFiles(projectRoot, logDir, 'process'));
|
|
255
|
+
}
|
|
34
256
|
for (const name of sources) {
|
|
257
|
+
if (PROCESS_LOG_SOURCES.includes(name))
|
|
258
|
+
continue;
|
|
35
259
|
const dir = path.join(logDir, name);
|
|
36
260
|
if (!fs.existsSync(dir))
|
|
37
261
|
continue;
|
|
@@ -104,9 +328,20 @@ function formatTimestamp(ms) {
|
|
|
104
328
|
return new Date(ms).toISOString().replace('T', ' ').slice(0, 19);
|
|
105
329
|
}
|
|
106
330
|
function printProcessHint(projectRoot) {
|
|
331
|
+
if (isPm2AppOnline(projectRoot, PM2_API_APP)) {
|
|
332
|
+
const pid = getPm2App(projectRoot, PM2_API_APP)?.pid;
|
|
333
|
+
console.log(` ${brand.dim(t('doctor.logs.pm2Supervised', { pid: pid ?? PM2_API_APP }))}`);
|
|
334
|
+
return;
|
|
335
|
+
}
|
|
107
336
|
const pid = readPid(projectRoot);
|
|
108
337
|
const pidFile = getPidFile(projectRoot);
|
|
109
338
|
if (!pid) {
|
|
339
|
+
const port = parseUrlPort(loadServerSiteUrl(projectRoot));
|
|
340
|
+
const activePid = port ? resolveListeningPid(port) : null;
|
|
341
|
+
if (activePid && isProcessRunning(activePid)) {
|
|
342
|
+
console.log(` ${brand.dim(t('doctor.logs.activePid', { pid: activePid, port }))}`);
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
110
345
|
console.log(` ${brand.dim(t('doctor.logs.noPid', { path: pidFile }))}`);
|
|
111
346
|
return;
|
|
112
347
|
}
|
|
@@ -115,11 +350,69 @@ function printProcessHint(projectRoot) {
|
|
|
115
350
|
? t('doctor.logs.pidRunning', { pid, path: pidFile })
|
|
116
351
|
: t('doctor.logs.pidStale', { pid, path: pidFile }))}`);
|
|
117
352
|
}
|
|
353
|
+
function pickPrimaryLogFile(files, source) {
|
|
354
|
+
if (!files.length)
|
|
355
|
+
return null;
|
|
356
|
+
if (source === 'process') {
|
|
357
|
+
return (files.find((file) => file.source === 'pm2-error') ||
|
|
358
|
+
files.find((file) => file.source === 'pm2-out') ||
|
|
359
|
+
files[0]);
|
|
360
|
+
}
|
|
361
|
+
const grouped = new Map();
|
|
362
|
+
for (const file of files) {
|
|
363
|
+
if (!grouped.has(file.source))
|
|
364
|
+
grouped.set(file.source, file);
|
|
365
|
+
}
|
|
366
|
+
const order = source === 'all' ? [...PROCESS_LOG_SOURCES, ...LOG_SOURCES] : [source];
|
|
367
|
+
for (const name of order) {
|
|
368
|
+
const file = grouped.get(name);
|
|
369
|
+
if (file)
|
|
370
|
+
return file;
|
|
371
|
+
}
|
|
372
|
+
return files[0];
|
|
373
|
+
}
|
|
374
|
+
async function runFollowLogs(projectRoot, { tail, source, logDir, files }) {
|
|
375
|
+
const usePm2Stream = source === 'process' && isPm2AppOnline(projectRoot, PM2_API_APP);
|
|
376
|
+
if (usePm2Stream) {
|
|
377
|
+
const bin = resolvePm2Bin(projectRoot);
|
|
378
|
+
if (!bin) {
|
|
379
|
+
console.error(` ${brand.warn(t('doctor.logs.pm2Unavailable'))}`);
|
|
380
|
+
return 1;
|
|
381
|
+
}
|
|
382
|
+
const { spawn } = require('child_process');
|
|
383
|
+
await new Promise((resolve) => {
|
|
384
|
+
const child = spawn(bin, ['logs', PM2_API_APP, '--lines', String(tail)], {
|
|
385
|
+
stdio: 'inherit',
|
|
386
|
+
});
|
|
387
|
+
child.on('exit', (code) => resolve(code ?? 0));
|
|
388
|
+
});
|
|
389
|
+
return 0;
|
|
390
|
+
}
|
|
391
|
+
const primary = pickPrimaryLogFile(files, source);
|
|
392
|
+
if (!primary) {
|
|
393
|
+
console.log(` ${brand.warn(t('doctor.logs.empty'))}`);
|
|
394
|
+
console.log('');
|
|
395
|
+
return 1;
|
|
396
|
+
}
|
|
397
|
+
if (process.platform === 'win32') {
|
|
398
|
+
console.log(` ${brand.dim(t('doctor.logs.followWindows', { path: primary.path }))}`);
|
|
399
|
+
console.log('');
|
|
400
|
+
return 0;
|
|
401
|
+
}
|
|
402
|
+
const { spawn } = require('child_process');
|
|
403
|
+
await new Promise((resolve) => {
|
|
404
|
+
const child = spawn('tail', ['-n', String(tail), '-f', primary.path], {
|
|
405
|
+
stdio: 'inherit',
|
|
406
|
+
});
|
|
407
|
+
child.on('exit', (code) => resolve(code ?? 0));
|
|
408
|
+
});
|
|
409
|
+
return 0;
|
|
410
|
+
}
|
|
118
411
|
async function runDoctorLogs(projectRoot, options = {}) {
|
|
119
412
|
const tail = Math.max(1, Math.min(parseInt(String(options.tail ?? '50'), 10) || 50, 5000));
|
|
120
|
-
const source =
|
|
413
|
+
const source = resolveLogSource(projectRoot, options.source);
|
|
121
414
|
const logDir = getProjectServerLogDir(projectRoot);
|
|
122
|
-
const files = listLogFiles(logDir, source);
|
|
415
|
+
const files = listLogFiles(logDir, source, projectRoot);
|
|
123
416
|
const w = 56;
|
|
124
417
|
console.log('');
|
|
125
418
|
console.log(` ${gradientText(t('logs.title'), [palette.primary, palette.accent], { bold: true })} ${brand.dim(t('logs.subtitle'))}`);
|
|
@@ -127,6 +420,9 @@ async function runDoctorLogs(projectRoot, options = {}) {
|
|
|
127
420
|
console.log(` ${brand.dim(t('doctor.logs.dir', { path: logDir }))}`);
|
|
128
421
|
printProcessHint(projectRoot);
|
|
129
422
|
console.log(` ${divider(w)}`);
|
|
423
|
+
if (options.follow) {
|
|
424
|
+
return runFollowLogs(projectRoot, { tail, source, logDir, files });
|
|
425
|
+
}
|
|
130
426
|
if (options.list) {
|
|
131
427
|
if (!files.length) {
|
|
132
428
|
console.log(` ${brand.warn(t('doctor.logs.empty'))}`);
|
|
@@ -153,8 +449,13 @@ async function runDoctorLogs(projectRoot, options = {}) {
|
|
|
153
449
|
if (!grouped.has(file.source))
|
|
154
450
|
grouped.set(file.source, file);
|
|
155
451
|
}
|
|
452
|
+
const displayOrder = source === 'all'
|
|
453
|
+
? [...PROCESS_LOG_SOURCES, ...LOG_SOURCES]
|
|
454
|
+
: source === 'process'
|
|
455
|
+
? PROCESS_LOG_SOURCES
|
|
456
|
+
: [source];
|
|
156
457
|
let printed = 0;
|
|
157
|
-
for (const name of
|
|
458
|
+
for (const name of displayOrder) {
|
|
158
459
|
const file = grouped.get(name);
|
|
159
460
|
if (!file)
|
|
160
461
|
continue;
|
|
@@ -171,7 +472,7 @@ async function runDoctorLogs(projectRoot, options = {}) {
|
|
|
171
472
|
continue;
|
|
172
473
|
}
|
|
173
474
|
for (const line of lines) {
|
|
174
|
-
const isError = /\[ERROR\]/i.test(line) || name === 'error';
|
|
475
|
+
const isError = /\[ERROR\]/i.test(line) || name === 'error' || name === 'pm2-error';
|
|
175
476
|
console.log(` ${isError ? brand.warn(line) : brand.dim(line)}`);
|
|
176
477
|
}
|
|
177
478
|
printed += lines.length;
|
|
@@ -188,7 +489,12 @@ async function runDoctorLogs(projectRoot, options = {}) {
|
|
|
188
489
|
}
|
|
189
490
|
module.exports = {
|
|
190
491
|
LOG_SOURCES,
|
|
492
|
+
PROCESS_LOG_SOURCES,
|
|
191
493
|
getProjectServerLogDir,
|
|
494
|
+
collectLogDirCandidates,
|
|
495
|
+
measureLogDir,
|
|
496
|
+
resolveActiveServerLogDir,
|
|
497
|
+
resolveLogSource,
|
|
192
498
|
listLogFiles,
|
|
193
499
|
readTailLines,
|
|
194
500
|
filterLines,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fecommunity/reactpress",
|
|
3
|
-
"version": "4.0.0-beta.
|
|
3
|
+
"version": "4.0.0-beta.18",
|
|
4
4
|
"description": "ReactPress CLI — publishing platform for modern developers. CMS, Admin, API, and Next.js themes in one command. Self-hosted, MIT. `npx @fecommunity/reactpress init`",
|
|
5
5
|
"author": "fecommunity",
|
|
6
6
|
"license": "MIT",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"server",
|
|
46
46
|
"toolkit",
|
|
47
47
|
"admin-dist",
|
|
48
|
+
"plugins",
|
|
48
49
|
"templates",
|
|
49
50
|
"scripts/install-bundled-runtime.mjs",
|
|
50
51
|
"README.md",
|