@lark-project/cli
Version:
飞书项目插件开发工具
85 lines (84 loc) • 4.23 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.preflightLocalConfig = void 0;
const fs_extra_1 = require("fs-extra");
const validate_point_schema_1 = require("./validate-point-schema");
const write_local_point_config_1 = require("./write-local-point-config");
const get_plugin_app_type_1 = require("./get-plugin-app-type");
const validate_point_type_allowed_1 = require("./validate-point-type-allowed");
/**
* Pre-build / pre-start gate: validate `point.config.local.json` against
* the plugin schema before any expensive work (webpack compile, dev server
* bootstrap, remote upload) starts.
*
* Why here, given `local-config set` already validates: that gate only fires
* when the config flows through the proper plan→set workflow. When AI (or a
* developer) directly edits `point.config.local.json` and runs `lpm start`/
* `lpm build`/`lpm release`, the schema check is skipped — webpack happily
* compiles, the dev page boots, and the broken schema only surfaces when
* release pushes to backend (or worse, silently produces a broken plugin).
*
* Gated purely on file existence: if `point.config.local.json` is in the
* project, treat it as authoritative for local-mode runs and validate it.
* `source_type` lives in `plugin.config.json` and is not a reliable trigger —
* the CLI flag isn't always passed, and the file's mere presence already
* signals intent to use local config.
*
* Why we write directly to process.stderr instead of going through `logger`:
* the project's log4js console appender caches `process.stdout.write` /
* `process.stderr.write` references at first flush, so test harnesses that
* temporarily replace those writes (jest `IOCapture`) can't reliably observe
* logger output. Preflight diagnostics are agent-readable error lists, so
* stderr-direct keeps them deterministically capturable in tests + still
* lands them on the correct sink for humans/CI.
*
* Exits the process with code 1 on validation errors so build/start fail fast
* with a clear AI/developer-facing error list.
*/
async function preflightLocalConfig() {
const filePath = (0, write_local_point_config_1.getLocalConfigPath)();
if (!(0, fs_extra_1.existsSync)(filePath))
return;
let config;
try {
config = JSON.parse((0, fs_extra_1.readFileSync)(filePath, 'utf8'));
}
catch (e) {
process.stderr.write(`point.config.local.json is not valid JSON: ${e.message}\n`);
process.exit(1);
}
let result;
try {
result = await (0, validate_point_schema_1.validatePointConfig)(config);
}
catch (e) {
// Schema unreachable (no auth, no fallback yaml, etc.) — warn but don't
// block. We don't want a flaky network to break local builds.
process.stderr.write(`Skip point.config.local.json schema check: ${e.message}\n`);
return;
}
if (result.valid) {
// schema 通过后再跑 app_type 硬卡。schema 只能保证形态,"AI 插件只能配专属点位"
// 是 schema 之外的语义约束,必须在 preflight 也跑一次(防止用户绕过 local-config set
// 直接写 point.config.local.json)。
const appType = (0, get_plugin_app_type_1.getPluginAppType)();
const allowed = (0, validate_point_type_allowed_1.validatePointTypeAllowed)(config, appType);
if (!allowed.ok) {
process.stderr.write('point.config.local.json violates plugin app_type constraints:\n');
for (const line of allowed.errors) {
process.stderr.write(` ${line}\n`);
}
process.exit(1);
}
return;
}
process.stderr.write('point.config.local.json failed schema validation:\n');
const { lines } = (0, validate_point_schema_1.formatValidationErrors)(result, 'text');
for (const line of lines) {
process.stderr.write(` ${line}\n`);
}
process.stderr.write('\nFix the errors above, or run "lpm ai validate --format json" for a structured listing.\n' +
'If you no longer need the local override, delete point.config.local.json.\n');
process.exit(1);
}
exports.preflightLocalConfig = preflightLocalConfig;