UNPKG

@lark-project/cli

Version:

飞书项目插件开发工具

128 lines (127 loc) 5.97 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.archiveFile = exports.cleanWorkspace = exports.ensureGitignore = exports.ensureWorkspace = exports.assertPluginRoot = exports.workspacePaths = exports.WORKSPACE_DIR = void 0; const path_1 = __importDefault(require("path")); const fs_extra_1 = require("fs-extra"); const get_project_directory_1 = require("../../utils/get-project-directory"); const logger_1 = require("../../utils/logger"); exports.WORKSPACE_DIR = '.lpm-cache'; const workspacePaths = (projectDirOverride) => { const projectDir = projectDirOverride !== null && projectDirOverride !== void 0 ? projectDirOverride : (0, get_project_directory_1.getProjectDirectory)(); const root = path_1.default.join(projectDir, exports.WORKSPACE_DIR); const schemaDir = path_1.default.join(root, 'schema'); const configDir = path_1.default.join(root, 'config'); const mcpDir = path_1.default.join(root, 'mcp'); return { root, state: path_1.default.join(root, 'state.json'), schema: path_1.default.join(schemaDir, 'point-schema.json'), schemaDir, configRemote: path_1.default.join(configDir, 'remote.json'), configLastSet: path_1.default.join(configDir, 'last-set.json'), configDir, historyDir: path_1.default.join(root, 'history'), mcpDir, configDraft: (timestamp) => path_1.default.join(configDir, `draft-${timestamp}.json`), mcp: (slug) => path_1.default.join(mcpDir, `${slug}.md`), relative: (absolute) => path_1.default.relative(projectDir, absolute), }; }; exports.workspacePaths = workspacePaths; /** * 守卫:projectDir 必须是 plugin 工程根(含 plugin.config.json 或 manifest.json),否则 throw。 * 任何会创建 .lpm-cache 子内容的入口都应先调用此守卫,防错误目录污染(ISS-O)。 */ const assertPluginRoot = (projectDir) => { const hasPluginConfig = (0, fs_extra_1.existsSync)(path_1.default.join(projectDir, 'plugin.config.json')) || (0, fs_extra_1.existsSync)(path_1.default.join(projectDir, 'manifest.json')); if (!hasPluginConfig) { throw new Error(`Refusing to create ${exports.WORKSPACE_DIR}: ${projectDir} is not a plugin project root.\n` + 'Hint: cd into a plugin directory (containing plugin.config.json or manifest.json), or pass --cwd <plugin_dir>.'); } }; exports.assertPluginRoot = assertPluginRoot; const ensureWorkspace = (projectDirOverride) => { const projectDir = projectDirOverride !== null && projectDirOverride !== void 0 ? projectDirOverride : (0, get_project_directory_1.getProjectDirectory)(); (0, exports.assertPluginRoot)(projectDir); const p = (0, exports.workspacePaths)(projectDir); (0, fs_extra_1.ensureDirSync)(p.root); (0, fs_extra_1.ensureDirSync)(p.schemaDir); (0, fs_extra_1.ensureDirSync)(p.configDir); (0, fs_extra_1.ensureDirSync)(p.mcpDir); }; exports.ensureWorkspace = ensureWorkspace; const ensureGitignore = (projectDirOverride) => { const projectDir = projectDirOverride !== null && projectDirOverride !== void 0 ? projectDirOverride : (0, get_project_directory_1.getProjectDirectory)(); const gitignorePath = path_1.default.join(projectDir, '.gitignore'); const line = `${exports.WORKSPACE_DIR}/`; const block = `\n# lpm cli workspace (schema snapshots, config drafts, mcp cache)\n${line}\n`; if (!(0, fs_extra_1.existsSync)(gitignorePath)) { (0, fs_extra_1.writeFileSync)(gitignorePath, block.trimStart()); return; } const current = (0, fs_extra_1.readFileSync)(gitignorePath, 'utf8'); if (current.split('\n').some(l => l.trim() === line || l.trim() === exports.WORKSPACE_DIR)) { return; } (0, fs_extra_1.appendFileSync)(gitignorePath, block); }; exports.ensureGitignore = ensureGitignore; const cleanWorkspace = ({ scope = 'all', keepState = true, silent = false } = {}) => { const p = (0, exports.workspacePaths)(); if (!(0, fs_extra_1.existsSync)(p.root)) return; const report = (target) => { if (!silent) logger_1.logger.info(`Cleaned ${p.relative(target)}`); }; if (scope === 'schema') { (0, fs_extra_1.removeSync)(p.schemaDir); report(p.schemaDir); return; } if (scope === 'config') { (0, fs_extra_1.removeSync)(p.configDir); report(p.configDir); return; } if (scope === 'mcp') { (0, fs_extra_1.removeSync)(p.mcpDir); report(p.mcpDir); return; } // scope === 'all' let preservedState = null; if (keepState && (0, fs_extra_1.existsSync)(p.state)) { preservedState = (0, fs_extra_1.readFileSync)(p.state); } (0, fs_extra_1.removeSync)(p.root); if (preservedState) { (0, fs_extra_1.ensureDirSync)(p.root); (0, fs_extra_1.writeFileSync)(p.state, preservedState); } report(p.root); }; exports.cleanWorkspace = cleanWorkspace; /** * 把已消费的中间产物移出活跃目录、归档到 historyDir(保留可追溯,不硬删)。 * 文件名前缀 timestamp 避免多次 set 撞名。归档失败时兜底仍清掉源文件—— * 守住"活跃目录里不留陈旧基线/草稿"这条不变量(remote.json 留原地会坑后续 diff/删除闸口)。 */ const archiveFile = (absolutePath, historyDir) => { if (!(0, fs_extra_1.existsSync)(absolutePath)) return; try { (0, fs_extra_1.ensureDirSync)(historyDir); (0, fs_extra_1.moveSync)(absolutePath, path_1.default.join(historyDir, `${Date.now()}-${path_1.default.basename(absolutePath)}`), { overwrite: true, }); } catch (_a) { (0, fs_extra_1.removeSync)(absolutePath); } }; exports.archiveFile = archiveFile;