@playcraft/devkit 1.0.8 → 1.0.9
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/core/options.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";const{parseArgvOptions,allowedAdProtocols,allowedAdNetworks,allowedOrientations,allowedLanguages}=require("./utils/parseArgvOptions");const path=require("path");const fs=require("fs");const{name,version}=require("../package.json");const possibleOptions=[{name:"out-dir",alias:"outDir",defaultValue:"dist",hasValue:true,description:"Output directory for build files"},{name:"build-config",alias:"buildConfig",defaultValue:"build.json",hasValue:true,description:"Path to build.json configuration file"},{name:"ts-config",alias:"tsConfig",defaultValue:"tsconfig.json",hasValue:true,description:"For TypeScript projects, path to tsconfig.json file"},{name:"js-config",alias:"jsConfig",defaultValue:"jsconfig.json",hasValue:true,description:"For JavaScript projects, path to jsconfig.json file"},{name:"port",hasValue:true,defaultValue:3000,description:"Development server port number",parser:function(rawValue){const value=+rawValue;if(isNaN(value))throw new Error("--port should be a number");return value}},{name:"open",defaultValue:false,hasValue:false,description:"Open browser automatically when server starts"},{name:"protocol",hasValue:true,defaultValue:"none",description:"Ad protocol to use (none, mraid, or dapi)",parser:function(rawValue){if(!allowedAdProtocols.includes(rawValue)){throw new Error(`--protocol should have one of the value: ${allowedAdProtocols.join(", ")}`)}return rawValue}},{name:"network",defaultValue:"preview",hasValue:true,description:"Target Ad network",parser:function(rawValue){if(!allowedAdNetworks.includes(rawValue)){throw new Error(`--network should have one of the value: ${allowedAdNetworks.join(", ")}`)}return rawValue}},{name:"zip",defaultValue:false,hasValue:false,description:"Should the build be zipped? (only for some ad networks)"},{name:"is-channel-fold2zip",defaultValue:false,hasValue:false,description:"\u5F53\u542F\u7528\u65F6\uFF0C\u6784\u5EFA\u4EA7\u7269\u683C\u5F0F\u4E3A\u300C\u4E3B\u9898\u6587\u4EF6\u540D/\u6E20\u9053.zip\u300D"},{name:"dev",hasValue:true,description:"Enable development mode (true/false)",parser:function(rawValue){if(!(rawValue==="true"||rawValue==="false"))throw new Error("--dev should have either true or false value");return rawValue==="true"}},{name:"filename",defaultValue:"{app}_{name}_{version}_{date}_{language}_{network}",hasValue:true,description:"Specifies the build filename template"},{name:"app",defaultValue:"AppName",hasValue:true,description:"Specifies the application name used in build filename and APP define"},{name:"name",defaultValue:"ConceptName",hasValue:true,description:"Specifies the concept name used in build filename and NAME define"},{name:"version",defaultValue:"v1",hasValue:true,description:"Specifies the version name used in build filename and VERSION define"},{name:"language",defaultValue:"en",hasValue:true,description:"Specifies the language of the build used in LANGUAGE define",parser:function(rawValue){if(!allowedLanguages.includes(rawValue)){throw new Error(`--platform should have one of the value: ${allowedLanguages.join(", ")}`)}return rawValue}},{name:"orientation",defaultValue:"both",hasValue:true,description:"Specifies the ad orientation used in ORIENTATION define and some network specific configurations",parser:function(rawValue){if(!allowedOrientations.includes(rawValue)){throw new Error(`--orientation should have one of the value: ${allowedOrientations.join(", ")}`)}return rawValue}},{name:"google-play-url",alias:"googlePlayUrl",defaultValue:"https://play.google.com/store/games",hasValue:true,description:"Google Play Store URL for the app"},{name:"app-store-url",alias:"appStoreUrl",defaultValue:"https://www.apple.com/app-store/",hasValue:true,description:"App Store URL for the app"},{name:"skip-recommended-meta",alias:"skipRecommendedMeta",hasValue:false,description:"Don't inject recommended for playable ads META tags"},{name:"debugger",hasValue:true,description:"URL of debugger script to inject into code"},{name:"obfuscate-level",alias:"obfuscateLevel",defaultValue:4,hasValue:true,description:"Code obfuscation level: 1=terser only, 2=obfuscator basic(default), 3=obfuscator+dead code, 4=maximum obfuscation",parser:function(rawValue){const value=+rawValue;if(isNaN(value)||value<1||value>4){throw new Error("--obfuscate-level should be a number between 1 and 4")}return value}},{name:"fflate-compression",alias:"fflateCompression",defaultValue:false,hasValue:true,description:"Enable fflate compression for HTML output (level 9, production only)",parser:function(rawValue){// 支持 true/false 字符串和布尔值
|
|
2
|
-
if(rawValue==="true"||rawValue===true||rawValue==="1")return true;if(rawValue==="false"||rawValue===false||rawValue==="0")return false;return Boolean(rawValue)}},{name:"exclude-compress-dirs",alias:"excludeCompressDirs",defaultValue:[],hasValue:true,description:"Directories to exclude from compression (comma-separated list of paths)",parser:function(rawValue){if(!rawValue)return[];return rawValue.split(",").map(dir=>dir.trim()).filter(Boolean)}}];/** @type {import('./index').CLIOptions} */const options=parseArgvOptions(possibleOptions)
|
|
2
|
+
if(rawValue==="true"||rawValue===true||rawValue==="1")return true;if(rawValue==="false"||rawValue===false||rawValue==="0")return false;return Boolean(rawValue)}},{name:"exclude-compress-dirs",alias:"excludeCompressDirs",defaultValue:[],hasValue:true,description:"Directories to exclude from compression (comma-separated list of paths)",parser:function(rawValue){if(!rawValue)return[];return rawValue.split(",").map(dir=>dir.trim()).filter(Boolean)}}];/** @type {import('./index').CLIOptions} */const options=parseArgvOptions(possibleOptions);// Extract CLI explicit keys set, then remove it from options
|
|
3
|
+
const cliExplicitKeys=options._cliExplicitKeys||new Set;delete options._cliExplicitKeys;options.defines={};options.compilation={};options.adNetworkNames={};// ==================== 配置文件加载 ====================
|
|
3
4
|
//
|
|
4
5
|
// 优先级(从低到高):
|
|
5
6
|
// 1. builds.config.js (新配置文件,优先读取)
|
|
@@ -11,15 +12,15 @@ if(rawValue==="true"||rawValue===true||rawValue==="1")return true;if(rawValue===
|
|
|
11
12
|
// - build.json: google_play_url → googlePlayUrl, app_store_url → appStoreUrl
|
|
12
13
|
// 1️⃣ 先尝试读取 builds.config.js
|
|
13
14
|
const buildsConfigPath=path.resolve("builds.config.js");if(fs.existsSync(buildsConfigPath)){try{// 清除 require 缓存,确保读取最新配置
|
|
14
|
-
delete require.cache[buildsConfigPath];const buildsConfig=require(buildsConfigPath);//
|
|
15
|
-
if(buildsConfig.googlePlayUrl){options.googlePlayUrl=buildsConfig.googlePlayUrl}if(buildsConfig.appStoreUrl){options.appStoreUrl=buildsConfig.appStoreUrl}// 读取其他特殊配置
|
|
15
|
+
delete require.cache[buildsConfigPath];const buildsConfig=require(buildsConfigPath);// 读取商店链接配置(respect CLI args priority)
|
|
16
|
+
if(buildsConfig.googlePlayUrl&&!cliExplicitKeys.has("googlePlayUrl")){options.googlePlayUrl=buildsConfig.googlePlayUrl}if(buildsConfig.appStoreUrl&&!cliExplicitKeys.has("appStoreUrl")){options.appStoreUrl=buildsConfig.appStoreUrl}// 读取其他特殊配置
|
|
16
17
|
if(buildsConfig.defines)Object.assign(options.defines,buildsConfig.defines);if(buildsConfig.compilation)Object.assign(options.compilation,buildsConfig.compilation);if(buildsConfig.adNetworkNames)Object.assign(options.adNetworkNames,buildsConfig.adNetworkNames);// 读取所有通用配置字段(如果在 builds.config.js 中定义了)
|
|
17
18
|
for(const key in buildsConfig){// 跳过已处理的特殊字段和批量构建专用字段
|
|
18
19
|
if(["googlePlayUrl","appStoreUrl","defines","compilation","adNetworkNames","title","appName","projectCode","channels","themes","naming","build","hooks","cosConfig"].includes(key)){continue}// 查找对应的配置项定义
|
|
19
|
-
const possibleOption=possibleOptions.find(e=>e.alias===key||e.name===key);if(possibleOption&&buildsConfig[key]!==undefined){
|
|
20
|
-
|
|
21
|
-
else if(key==="app_store_url")options.appStoreUrl=customOptions.app_store_url
|
|
22
|
-
|
|
20
|
+
const possibleOption=possibleOptions.find(e=>e.alias===key||e.name===key);if(possibleOption&&buildsConfig[key]!==undefined){// Only override if not explicitly set by CLI args
|
|
21
|
+
const optionKey=possibleOption.alias||possibleOption.name;if(!cliExplicitKeys.has(optionKey)){options[optionKey]=buildsConfig[key]}}}}catch(err){console.log("\x1B[33m\u26A0\uFE0F \u8BFB\u53D6 builds.config.js \u5931\u8D25: "+err.message+"\x1B[0m")}}// 2️⃣ 再尝试读取 build.json(会覆盖 builds.config.js 中的相同字段)
|
|
22
|
+
try{const fileData=fs.readFileSync(path.resolve(options["buildConfig"]),"utf8");try{const customOptions=JSON.parse(fileData);for(let key in customOptions){if(key==="defines")Object.assign(options.defines,customOptions[key]);else if(key==="compilation")Object.assign(options.compilation,customOptions[key]);else if(key==="adNetworkNames")Object.assign(options.adNetworkNames,customOptions[key]);else if(key==="google_play_url"&&!cliExplicitKeys.has("googlePlayUrl"))options.googlePlayUrl=customOptions.google_play_url;else if(key==="app_store_url"&&!cliExplicitKeys.has("appStoreUrl"))options.appStoreUrl=customOptions.app_store_url;else{const possibleOption=possibleOptions.find(e=>e.alias===key||e.name===key);if(possibleOption){const optionKey=possibleOption.alias||possibleOption.name;// Only override if not explicitly set by CLI args
|
|
23
|
+
if(!cliExplicitKeys.has(optionKey)){options[optionKey]=customOptions[key]}}}}}catch(err){console.log("\x1B[31mBuild config parsing error: "+err.message+"\x1B[0m")}}catch(err){// build.json 不存在是正常的(使用 builds.config.js)
|
|
23
24
|
}// 环境变量覆盖(用于批量构建时避免竞态条件)
|
|
24
25
|
// 批量构建时通过 PLAYABLE_FILENAME 环境变量传递 filename,优先级最高
|
|
25
26
|
if(process.env.PLAYABLE_FILENAME){options.filename=process.env.PLAYABLE_FILENAME}console.log(`${name} v${version}`);console.log("Options:",options);exports.options=options;
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* Parses command line arguments based on provided options configuration
|
|
3
3
|
* @param {import('../index').CLIOptionConfig[]} posiibleOptions - Array of possible options to parse
|
|
4
4
|
* @returns {Record<string, any>} Parsed options object with values from command line arguments
|
|
5
|
-
*/exports.parseArgvOptions=function parseArgvOptions(posiibleOptions){const argvOptions={}
|
|
5
|
+
*/exports.parseArgvOptions=function parseArgvOptions(posiibleOptions){const argvOptions={};// Track which options were explicitly set via CLI args
|
|
6
|
+
const cliExplicitKeys=new Set;for(let possibleOption of posiibleOptions){if(possibleOption.defaultValue!==undefined)argvOptions[possibleOption.alias||possibleOption.name]=possibleOption.defaultValue}let adNetwork=null;for(let i=2;i<process.argv.length;i++){let key=process.argv[i];let inlineValue=null;// 支持 --key=value 格式
|
|
6
7
|
if(key.includes("=")){const eqIndex=key.indexOf("=");inlineValue=key.slice(eqIndex+1);key=key.slice(0,eqIndex)}const allowedOption=posiibleOptions.find(e=>"--"+e.name===key);if(allowedOption){let value=true;if(allowedOption.hasValue){// 优先使用内联值(--key=value),否则使用下一个参数
|
|
7
|
-
value=inlineValue!==null?inlineValue:process.argv[++i];if(allowedOption.parser)value=allowedOption.parser(value)}
|
|
8
|
+
value=inlineValue!==null?inlineValue:process.argv[++i];if(allowedOption.parser)value=allowedOption.parser(value)}const optionKey=allowedOption.alias||allowedOption.name;argvOptions[optionKey]=value;cliExplicitKeys.add(optionKey)}else if(!adNetwork)adNetwork=key}if(posiibleOptions.find(e=>e.name==="network")){if(adNetwork&&allowedAdNetworks.includes(adNetwork)){argvOptions["network"]=adNetwork}if(posiibleOptions.find(e=>e.name==="protocol")){if(mraidPartners.includes(argvOptions["network"])&&argvOptions["protocol"]==="none"){argvOptions["protocol"]="mraid"}}}argvOptions._cliExplicitKeys=cliExplicitKeys;return argvOptions};exports.allowedAdNetworks=allowedAdNetworks;exports.allowedAdProtocols=allowedAdProtocols;exports.mraidPartners=mraidPartners;exports.allowedLanguages=allowedLanguages;exports.allowedOrientations=allowedOrientations;
|