/** 默认桌面能力;显式 feature 参数优先,不把应用参数当 Cargo 参数。 */ export function withDefaultCargoFeatures(argv, features) { // 只看第一个 `--` 之前的 Tauri 选项:runner 参数区里的 `--features` 会与这里注入的 // 默认值在 Cargo 侧合并,不能当作「用户已经指定过了」而跳过注入。 if (!features.length || tauriOptions(argv).some(hasExplicitFeatures)) { return argv; } return [`--features=${features.join(',')}`, ...argv]; } /// 第一个 `--` 之前是 Tauri 自己的选项,之后是 runner(Cargo)参数。 function tauriOptions(argv) { const separator = argv.indexOf('--'); return separator < 0 ? argv : argv.slice(0, separator); } /** * 交给 Tauri 与 Cargo 的参数区间:第一个 `--` 之前是 Tauri 选项,之后到第二个 `--` 是 * runner(Cargo)参数;第二个 `--` 之后由 Tauri 转交应用,不属于构建参数。 * dev 入口(`buildTauriArguments` 会把启动器的单个 `--` 补成 runner + 应用两段)与发布 * 入口都用这一份区间解析,两边的目标与 feature 必须来自同一个口径。 */ function cargoArguments(argv) { const separator = argv.indexOf('--'); if (separator < 0) return argv; const applicationStart = argv.indexOf('--', separator + 1); return applicationStart < 0 ? argv : argv.slice(0, applicationStart); } /// 四种拼写(`--features x` / `--features=x` / `-f x` / `-fx`)都算显式指定。 function hasExplicitFeatures(value) { return ( value === '--features' || value === '-f' || value.startsWith('--features=') || /^-f.+/u.test(value) ); } function parseFeatureNames(raw) { return String(raw) .split(/[\s,]+/u) .map((name) => name.trim()) .filter(Boolean); } export function defaultEditorFeatures(target) { return target === 'win32' || target.includes('windows') ? ['cocos-editor-execute', 'unity-editor-execute', 'godot-editor-execute'] : []; } /** * 显式 Cargo 目标解析:`--target ` / `--target=` / `-t `。 * 应用参数区(第二个 `--` 之后)不参与解析。未显式指定返回 undefined;取值缺失、 * 取到另一个选项或重复指定都失败关闭。 * dev 入口与发布入口共用这一份解析:cargo 的构建目标与随包资源 staging 目标 * 必须来自同一个显式来源,不能一方读参数、另一方回退宿主默认值。 */ export function readCargoTarget(argv = []) { const options = cargoArguments(argv); let target; for (let index = 0; index < options.length; index += 1) { const argument = String(options[index]); let value; if (argument === '--target' || argument === '-t') { value = options[index + 1]; index += 1; } else if (argument.startsWith('--target=')) { value = argument.slice('--target='.length); } else if (argument.startsWith('-t') && argument.length > 2) { value = argument.slice(2).replace(/^=/u, ''); } else { continue; } if (!value?.trim() || String(value).startsWith('-')) { throw new Error('--target 缺少有效目标'); } if (target !== undefined) throw new Error('不能重复指定 --target'); target = String(value).trim(); } return target; } /** * 单一声明式解析:本次构建实际生效的编辑器 feature 集。 * 解析顺序:命令行 `--features`(Cargo 真正收到的参数,不能被环境覆盖) * > 环境变量 `AGC_DEV_CARGO_FEATURES`(存在即以它为准,空串即关闭默认 feature) * > 目标平台默认值。 * 传入的必须是「最终交给 Tauri 的参数」:dev 入口与发布入口的 cargo 参数与随包资源 * 准备步骤都消费这里的返回值,否则会出现「cargo 开了 feature、staging 没开(或反之)」。 */ export function resolveEditorFeatures({ argv = [], target, env = process.env, } = {}) { const fromArgv = featuresFromArgv(argv); if (fromArgv) { return fromArgv; } // 变量存在本身即声明「本次 feature 集由环境决定」:空串是有意的关闭,不是未设置。 if (env.AGC_DEV_CARGO_FEATURES !== undefined) { return parseFeatureNames(env.AGC_DEV_CARGO_FEATURES); } return defaultEditorFeatures(target); } /// 汇总 cargo 参数区里出现在任何位置的 `--features` / `-f`(Cargo 会把多个 flag 合并); /// 没有任何 feature flag 时返回 null。显式但为空(如 `--features=`)返回空数组。 export function featuresFromArgv(argv = []) { const options = cargoArguments(argv); const features = []; let found = false; for (let index = 0; index < options.length; index += 1) { const value = String(options[index]); if (value.startsWith('--features=')) { found = true; features.push(...parseFeatureNames(value.slice('--features='.length))); } else if (value === '--features' || value === '-f') { found = true; while ( index + 1 < options.length && !String(options[index + 1]).startsWith('-') ) { index += 1; features.push(...parseFeatureNames(options[index])); } } else if (value.startsWith('-f') && value.length > 2) { found = true; features.push(...parseFeatureNames(value.slice(2).replace(/^=/u, ''))); } } return found ? features : null; }