@coze-arch/cli 0.0.1-alpha.6a5120 → 0.0.1-alpha.6d3e84
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/lib/__templates__/expo/.coze +7 -2
- package/lib/__templates__/expo/.cozeproj/scripts/dev_build.sh +46 -0
- package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +220 -0
- package/lib/__templates__/expo/.cozeproj/scripts/prod_build.sh +47 -0
- package/lib/__templates__/expo/.cozeproj/scripts/prod_run.sh +34 -0
- package/lib/__templates__/expo/.cozeproj/scripts/server_dev_run.sh +45 -0
- package/lib/__templates__/expo/README.md +66 -7
- package/lib/__templates__/expo/_gitignore +1 -1
- package/lib/__templates__/expo/_npmrc +2 -4
- package/lib/__templates__/expo/client/app/_layout.tsx +14 -14
- package/lib/__templates__/expo/client/app/index.tsx +1 -0
- package/lib/__templates__/expo/client/app.config.ts +76 -0
- package/lib/__templates__/expo/client/components/ThemedText.tsx +33 -0
- package/lib/__templates__/expo/client/components/ThemedView.tsx +38 -0
- package/lib/__templates__/expo/client/constants/theme.ts +786 -50
- package/lib/__templates__/expo/client/declarations.d.ts +5 -0
- package/lib/__templates__/expo/client/hooks/useColorScheme.ts +34 -1
- package/lib/__templates__/expo/client/hooks/useTheme.ts +1 -1
- package/lib/__templates__/expo/client/metro.config.js +121 -0
- package/lib/__templates__/expo/client/package.json +93 -0
- package/lib/__templates__/expo/client/screens/demo/index.tsx +25 -0
- package/lib/__templates__/expo/client/screens/demo/styles.ts +28 -0
- package/lib/__templates__/expo/client/scripts/install-missing-deps.js +1 -0
- package/lib/__templates__/expo/client/tsconfig.json +24 -0
- package/lib/__templates__/expo/client/utils/index.ts +22 -0
- package/lib/__templates__/expo/package.json +13 -103
- package/lib/__templates__/expo/pnpm-lock.yaml +451 -867
- package/lib/__templates__/expo/pnpm-workspace.yaml +3 -0
- package/lib/__templates__/expo/server/build.js +21 -0
- package/lib/__templates__/expo/server/package.json +32 -0
- package/lib/__templates__/expo/{src → server/src}/index.ts +8 -2
- package/lib/__templates__/expo/server/tsconfig.json +24 -0
- package/lib/__templates__/expo/template.config.js +1 -0
- package/lib/__templates__/expo/tsconfig.json +1 -24
- package/lib/__templates__/nextjs/.coze +1 -0
- package/lib/__templates__/nextjs/_npmrc +1 -0
- package/lib/__templates__/nextjs/next.config.ts +12 -0
- package/lib/__templates__/nextjs/package.json +3 -2
- package/lib/__templates__/nextjs/pnpm-lock.yaml +13 -5
- package/lib/__templates__/nextjs/scripts/dev.sh +7 -26
- package/lib/__templates__/nextjs/scripts/prepare.sh +9 -0
- package/lib/__templates__/nextjs/src/app/globals.css +109 -89
- package/lib/__templates__/nextjs/src/app/layout.tsx +19 -30
- package/lib/__templates__/nextjs/src/app/page.tsx +35 -23
- package/lib/__templates__/nextjs/src/components/ui/resizable.tsx +29 -22
- package/lib/__templates__/nextjs/src/components/ui/sidebar.tsx +228 -230
- package/lib/__templates__/nextjs/template.config.js +30 -0
- package/lib/__templates__/templates.json +61 -36
- package/lib/__templates__/vite/.coze +1 -0
- package/lib/__templates__/vite/_npmrc +1 -0
- package/lib/__templates__/vite/eslint.config.mjs +9 -0
- package/lib/__templates__/vite/package.json +6 -2
- package/lib/__templates__/vite/pnpm-lock.yaml +3486 -19
- package/lib/__templates__/vite/scripts/dev.sh +7 -26
- package/lib/__templates__/vite/scripts/prepare.sh +9 -0
- package/lib/__templates__/vite/src/main.ts +1 -2
- package/lib/__templates__/vite/template.config.js +39 -6
- package/lib/__templates__/vite/vite.config.ts +3 -3
- package/lib/cli.js +269 -103
- package/package.json +8 -3
- package/lib/__templates__/expo/.cozeproj/scripts/deploy_build.sh +0 -116
- package/lib/__templates__/expo/.cozeproj/scripts/deploy_run.sh +0 -239
- package/lib/__templates__/expo/app.json +0 -63
- package/lib/__templates__/expo/babel.config.js +0 -9
- package/lib/__templates__/expo/client/app/(tabs)/_layout.tsx +0 -43
- package/lib/__templates__/expo/client/app/(tabs)/home.tsx +0 -1
- package/lib/__templates__/expo/client/app/(tabs)/index.tsx +0 -7
- package/lib/__templates__/expo/client/app/+not-found.tsx +0 -79
- package/lib/__templates__/expo/client/index.js +0 -12
- package/lib/__templates__/expo/client/screens/home/index.tsx +0 -51
- package/lib/__templates__/expo/client/screens/home/styles.ts +0 -60
- package/lib/__templates__/expo/metro.config.js +0 -53
- package/lib/__templates__/nextjs/.vscode/settings.json +0 -121
- package/lib/__templates__/vite/.vscode/settings.json +0 -7
- /package/lib/__templates__/expo/{eslint-formatter-simple.mjs → client/eslint-formatter-simple.mjs} +0 -0
- /package/lib/__templates__/expo/{eslint.config.mjs → client/eslint.config.mjs} +0 -0
package/lib/cli.js
CHANGED
|
@@ -7,8 +7,10 @@ var fs = require('fs');
|
|
|
7
7
|
var shelljs = require('shelljs');
|
|
8
8
|
var perf_hooks = require('perf_hooks');
|
|
9
9
|
var fs$1 = require('fs/promises');
|
|
10
|
+
var os = require('os');
|
|
10
11
|
var toml = require('@iarna/toml');
|
|
11
12
|
var jsYaml = require('js-yaml');
|
|
13
|
+
var child_process = require('child_process');
|
|
12
14
|
var addFormats = require('ajv-formats');
|
|
13
15
|
var Ajv = require('ajv');
|
|
14
16
|
var minimist = require('minimist');
|
|
@@ -125,7 +127,7 @@ const generateTemplatesHelpText = () => {
|
|
|
125
127
|
return lines.join('\n');
|
|
126
128
|
};
|
|
127
129
|
|
|
128
|
-
function _nullishCoalesce$2(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain$
|
|
130
|
+
function _nullishCoalesce$2(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain$3(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var LogLevel; (function (LogLevel) {
|
|
129
131
|
const ERROR = 0; LogLevel[LogLevel["ERROR"] = ERROR] = "ERROR";
|
|
130
132
|
const WARN = 1; LogLevel[LogLevel["WARN"] = WARN] = "WARN";
|
|
131
133
|
const SUCCESS = 2; LogLevel[LogLevel["SUCCESS"] = SUCCESS] = "SUCCESS";
|
|
@@ -172,7 +174,7 @@ class Logger {
|
|
|
172
174
|
return level;
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
const envLevel = _optionalChain$
|
|
177
|
+
const envLevel = _optionalChain$3([process, 'access', _ => _.env, 'access', _2 => _2.LOG_LEVEL, 'optionalAccess', _3 => _3.toLowerCase, 'call', _4 => _4()]);
|
|
176
178
|
if (envLevel && envLevel in LOG_LEVEL_MAP) {
|
|
177
179
|
return LOG_LEVEL_MAP[envLevel];
|
|
178
180
|
}
|
|
@@ -184,7 +186,7 @@ class Logger {
|
|
|
184
186
|
// 简单检测:Node.js 环境且支持 TTY
|
|
185
187
|
return (
|
|
186
188
|
typeof process !== 'undefined' &&
|
|
187
|
-
_optionalChain$
|
|
189
|
+
_optionalChain$3([process, 'access', _5 => _5.stdout, 'optionalAccess', _6 => _6.isTTY]) === true &&
|
|
188
190
|
process.env.NO_COLOR === undefined
|
|
189
191
|
);
|
|
190
192
|
}
|
|
@@ -593,7 +595,7 @@ const registerCommand$2 = program => {
|
|
|
593
595
|
});
|
|
594
596
|
};
|
|
595
597
|
|
|
596
|
-
function _optionalChain$
|
|
598
|
+
function _optionalChain$2(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
597
599
|
// Safe JSON parsing utilities with type safety and error handling
|
|
598
600
|
// Provides fallback values, validation, and error monitoring capabilities
|
|
599
601
|
|
|
@@ -684,12 +686,12 @@ function safeJsonParse(
|
|
|
684
686
|
const parsed = JSON.parse(String(input));
|
|
685
687
|
|
|
686
688
|
// Optional validation
|
|
687
|
-
if (_optionalChain$
|
|
689
|
+
if (_optionalChain$2([options, 'optionalAccess', _ => _.validate])) {
|
|
688
690
|
if (options.validate(parsed)) {
|
|
689
691
|
return parsed;
|
|
690
692
|
} else {
|
|
691
693
|
const validationError = new Error('JSON validation failed');
|
|
692
|
-
_optionalChain$
|
|
694
|
+
_optionalChain$2([options, 'access', _2 => _2.onError, 'optionalCall', _3 => _3(validationError, input)]);
|
|
693
695
|
|
|
694
696
|
if (options.throwOnValidationError) {
|
|
695
697
|
throw validationError;
|
|
@@ -701,15 +703,15 @@ function safeJsonParse(
|
|
|
701
703
|
return parsed;
|
|
702
704
|
} catch (error) {
|
|
703
705
|
// Re-throw validation errors when throwOnValidationError is true
|
|
704
|
-
if (error instanceof Error && error.message === 'JSON validation failed' && _optionalChain$
|
|
706
|
+
if (error instanceof Error && error.message === 'JSON validation failed' && _optionalChain$2([options, 'optionalAccess', _4 => _4.throwOnValidationError])) {
|
|
705
707
|
throw error;
|
|
706
708
|
}
|
|
707
|
-
_optionalChain$
|
|
709
|
+
_optionalChain$2([options, 'optionalAccess', _5 => _5.onError, 'optionalCall', _6 => _6(error , input)]);
|
|
708
710
|
return defaultValue;
|
|
709
711
|
}
|
|
710
712
|
}
|
|
711
713
|
|
|
712
|
-
function _optionalChain$
|
|
714
|
+
function _optionalChain$1(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
713
715
|
|
|
714
716
|
|
|
715
717
|
/**
|
|
@@ -799,13 +801,13 @@ const getCommandConfig = (
|
|
|
799
801
|
// 根据命令名称映射到配置路径
|
|
800
802
|
switch (commandName) {
|
|
801
803
|
case 'dev':
|
|
802
|
-
commandConfig = _optionalChain$
|
|
804
|
+
commandConfig = _optionalChain$1([config, 'access', _ => _.dev, 'optionalAccess', _2 => _2.run]);
|
|
803
805
|
break;
|
|
804
806
|
case 'build':
|
|
805
|
-
commandConfig = _optionalChain$
|
|
807
|
+
commandConfig = _optionalChain$1([config, 'access', _3 => _3.deploy, 'optionalAccess', _4 => _4.build]);
|
|
806
808
|
break;
|
|
807
809
|
case 'start':
|
|
808
|
-
commandConfig = _optionalChain$
|
|
810
|
+
commandConfig = _optionalChain$1([config, 'access', _5 => _5.deploy, 'optionalAccess', _6 => _6.run]);
|
|
809
811
|
break;
|
|
810
812
|
default:
|
|
811
813
|
throw new Error(`Unknown command: ${commandName}`);
|
|
@@ -821,26 +823,51 @@ const getCommandConfig = (
|
|
|
821
823
|
return commandConfig;
|
|
822
824
|
};
|
|
823
825
|
|
|
824
|
-
function _nullishCoalesce$1(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain
|
|
826
|
+
function _nullishCoalesce$1(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
|
|
825
827
|
|
|
826
828
|
/**
|
|
827
|
-
*
|
|
829
|
+
* 日志文件名常量
|
|
828
830
|
*/
|
|
829
|
-
const
|
|
830
|
-
const ensureLogDir = () => {
|
|
831
|
-
if (!fs.existsSync(logDir)) {
|
|
832
|
-
fs.mkdirSync(logDir, { recursive: true });
|
|
833
|
-
}
|
|
834
|
-
};
|
|
831
|
+
const LOG_FILE_NAME = 'dev.log';
|
|
835
832
|
|
|
836
|
-
|
|
833
|
+
/**
|
|
834
|
+
* 获取日志目录
|
|
835
|
+
* 优先使用环境变量 COZE_LOG_DIR,否则使用 ~/.coze-logs
|
|
836
|
+
*/
|
|
837
|
+
const getLogDir = () =>
|
|
838
|
+
process.env.COZE_LOG_DIR || path.join(os.homedir(), '.coze-logs');
|
|
837
839
|
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
840
|
+
/**
|
|
841
|
+
* 解析日志文件路径
|
|
842
|
+
* - 如果是绝对路径,直接使用
|
|
843
|
+
* - 如果是相对路径,基于 getLogDir() + 相对路径
|
|
844
|
+
* - 如果为空,使用 getLogDir() + LOG_FILE_NAME
|
|
845
|
+
*/
|
|
846
|
+
const resolveLogFilePath = (logFile) => {
|
|
847
|
+
if (!logFile) {
|
|
848
|
+
return path.join(getLogDir(), LOG_FILE_NAME);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
if (path.isAbsolute(logFile)) {
|
|
852
|
+
return logFile;
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
return path.join(getLogDir(), logFile);
|
|
856
|
+
};
|
|
857
|
+
|
|
858
|
+
/**
|
|
859
|
+
* 创建日志写入流
|
|
860
|
+
*/
|
|
861
|
+
const createLogStream = (logFilePath) => {
|
|
862
|
+
const logDir = path.dirname(logFilePath);
|
|
863
|
+
|
|
864
|
+
// 确保日志目录存在
|
|
865
|
+
if (!fs.existsSync(logDir)) {
|
|
866
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// 使用 'w' 标志覆盖之前的日志
|
|
870
|
+
return fs.createWriteStream(logFilePath, { flags: 'w' });
|
|
844
871
|
};
|
|
845
872
|
|
|
846
873
|
/**
|
|
@@ -858,16 +885,15 @@ const executeRun = async (
|
|
|
858
885
|
const commandArgs = getCommandConfig(config, commandName);
|
|
859
886
|
|
|
860
887
|
// 2. 准备日志
|
|
861
|
-
const
|
|
862
|
-
const
|
|
863
|
-
const logStream = logManager.createWriteStream(logFile);
|
|
888
|
+
const logFilePath = resolveLogFilePath(options.logFile);
|
|
889
|
+
const logStream = createLogStream(logFilePath);
|
|
864
890
|
|
|
865
891
|
// 3. 执行命令
|
|
866
892
|
const commandString = commandArgs.join(' ');
|
|
867
893
|
|
|
868
894
|
logger.info(`Executing: ${commandString}`);
|
|
869
895
|
logger.info(`Working directory: ${process.cwd()}`);
|
|
870
|
-
logger.info(`Log file: ${
|
|
896
|
+
logger.info(`Log file: ${logFilePath}`);
|
|
871
897
|
|
|
872
898
|
const childProcess = shelljs.exec(commandString, {
|
|
873
899
|
async: true,
|
|
@@ -879,12 +905,12 @@ const executeRun = async (
|
|
|
879
905
|
}
|
|
880
906
|
|
|
881
907
|
// 将输出同时写入控制台和日志文件
|
|
882
|
-
_optionalChain
|
|
908
|
+
_optionalChain([childProcess, 'access', _ => _.stdout, 'optionalAccess', _2 => _2.on, 'call', _3 => _3('data', (data) => {
|
|
883
909
|
process.stdout.write(data);
|
|
884
910
|
logStream.write(data);
|
|
885
911
|
})]);
|
|
886
912
|
|
|
887
|
-
_optionalChain
|
|
913
|
+
_optionalChain([childProcess, 'access', _4 => _4.stderr, 'optionalAccess', _5 => _5.on, 'call', _6 => _6('data', (data) => {
|
|
888
914
|
process.stderr.write(data);
|
|
889
915
|
logStream.write(data);
|
|
890
916
|
})]);
|
|
@@ -896,11 +922,11 @@ const executeRun = async (
|
|
|
896
922
|
logger.error(
|
|
897
923
|
`Command exited with code ${_nullishCoalesce$1(code, () => ( 'unknown'))}${signal ? ` and signal ${signal}` : ''}`,
|
|
898
924
|
);
|
|
899
|
-
logger.error(`Check log file for details: ${
|
|
925
|
+
logger.error(`Check log file for details: ${logFilePath}`);
|
|
900
926
|
process.exit(code || 1);
|
|
901
927
|
} else {
|
|
902
928
|
logger.success('Command completed successfully');
|
|
903
|
-
logger.info(`Log file: ${
|
|
929
|
+
logger.info(`Log file: ${logFilePath}`);
|
|
904
930
|
}
|
|
905
931
|
});
|
|
906
932
|
|
|
@@ -952,6 +978,45 @@ const registerCommand$1 = program => {
|
|
|
952
978
|
});
|
|
953
979
|
};
|
|
954
980
|
|
|
981
|
+
/**
|
|
982
|
+
* 在后台启动一个独立的子进程
|
|
983
|
+
* 类似于 `setsid command args >/dev/null 2>&1 &`
|
|
984
|
+
*
|
|
985
|
+
* @param command - 要执行的命令 (例如: 'npm', 'node', 'bash')
|
|
986
|
+
* @param args - 命令参数数组 (例如: ['run', 'dev'])
|
|
987
|
+
* @param options - 配置选项
|
|
988
|
+
* @returns 子进程的 PID
|
|
989
|
+
*/
|
|
990
|
+
function spawnDetached(
|
|
991
|
+
command,
|
|
992
|
+
args,
|
|
993
|
+
options,
|
|
994
|
+
) {
|
|
995
|
+
const { cwd, verbose = true } = options;
|
|
996
|
+
const isWindows = os.platform() === 'win32';
|
|
997
|
+
|
|
998
|
+
if (verbose) {
|
|
999
|
+
console.log(`Spawning detached process: ${command} ${args.join(' ')}`);
|
|
1000
|
+
console.log(`Working directory: ${cwd}`);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
// 使用 spawn 创建后台子进程
|
|
1004
|
+
const child = child_process.spawn(command, args, {
|
|
1005
|
+
cwd,
|
|
1006
|
+
detached: !isWindows, // Windows 不完全支持 detached,但仍可以使用
|
|
1007
|
+
stdio: 'ignore', // 忽略所有输入输出,让进程完全独立运行
|
|
1008
|
+
});
|
|
1009
|
+
|
|
1010
|
+
// 分离父子进程引用,允许父进程退出而不等待子进程
|
|
1011
|
+
child.unref();
|
|
1012
|
+
|
|
1013
|
+
if (verbose && child.pid) {
|
|
1014
|
+
console.log(`Process started with PID: ${child.pid}`);
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
return child.pid;
|
|
1018
|
+
}
|
|
1019
|
+
|
|
955
1020
|
/**
|
|
956
1021
|
* 创建 AJV 验证器实例
|
|
957
1022
|
*/
|
|
@@ -1207,17 +1272,139 @@ const convertDotfileName = (filePath) => {
|
|
|
1207
1272
|
};
|
|
1208
1273
|
|
|
1209
1274
|
/**
|
|
1210
|
-
*
|
|
1275
|
+
* 执行文件渲染钩子
|
|
1211
1276
|
*
|
|
1212
|
-
* @param
|
|
1213
|
-
* @param
|
|
1277
|
+
* @param templateConfig - 模板配置
|
|
1278
|
+
* @param fileInfo - 文件渲染信息
|
|
1214
1279
|
* @param context - 模板上下文
|
|
1280
|
+
* @returns 处理后的文件信息,或 null 表示跳过该文件
|
|
1215
1281
|
*/
|
|
1216
|
-
const
|
|
1217
|
-
|
|
1218
|
-
|
|
1282
|
+
const executeFileRenderHook = async (
|
|
1283
|
+
templateConfig,
|
|
1284
|
+
fileInfo,
|
|
1219
1285
|
context,
|
|
1220
1286
|
) => {
|
|
1287
|
+
if (!templateConfig.onFileRender) {
|
|
1288
|
+
return fileInfo;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
const result = await templateConfig.onFileRender(
|
|
1292
|
+
fileInfo,
|
|
1293
|
+
context,
|
|
1294
|
+
);
|
|
1295
|
+
|
|
1296
|
+
// false: 跳过文件
|
|
1297
|
+
if (result === false) {
|
|
1298
|
+
return null;
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
// undefined/void: 使用默认内容
|
|
1302
|
+
if (result === undefined || result === null) {
|
|
1303
|
+
return fileInfo;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
// string: 作为 content,其他不变
|
|
1307
|
+
if (typeof result === 'string') {
|
|
1308
|
+
return {
|
|
1309
|
+
...fileInfo,
|
|
1310
|
+
content: result,
|
|
1311
|
+
};
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// FileRenderInfo: 使用新对象的信息
|
|
1315
|
+
return result;
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
/**
|
|
1319
|
+
* 处理单个文件
|
|
1320
|
+
*/
|
|
1321
|
+
const processSingleFile = async (options
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
|
|
1325
|
+
|
|
1326
|
+
|
|
1327
|
+
) => {
|
|
1328
|
+
const { file, templatePath, outputPath, context, templateConfig } = options;
|
|
1329
|
+
|
|
1330
|
+
const srcPath = path.join(templatePath, file);
|
|
1331
|
+
const destFile = convertDotfileName(file);
|
|
1332
|
+
|
|
1333
|
+
logger.verbose(
|
|
1334
|
+
` - Processing: ${file}${destFile !== file ? ` -> ${destFile}` : ''}`,
|
|
1335
|
+
);
|
|
1336
|
+
|
|
1337
|
+
// 判断是否为二进制文件
|
|
1338
|
+
const isBinary = !shouldRenderFile(srcPath);
|
|
1339
|
+
let content;
|
|
1340
|
+
let wasRendered = false;
|
|
1341
|
+
|
|
1342
|
+
if (isBinary) {
|
|
1343
|
+
// 二进制文件,读取为 buffer 然后转为 base64
|
|
1344
|
+
const buffer = await fs$1.readFile(srcPath);
|
|
1345
|
+
content = buffer.toString('base64');
|
|
1346
|
+
} else {
|
|
1347
|
+
// 文本文件,渲染后的内容
|
|
1348
|
+
content = await renderTemplate(srcPath, context);
|
|
1349
|
+
wasRendered = true;
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// 构造文件信息对象
|
|
1353
|
+
const fileInfo = {
|
|
1354
|
+
path: file,
|
|
1355
|
+
destPath: destFile,
|
|
1356
|
+
content,
|
|
1357
|
+
isBinary,
|
|
1358
|
+
wasRendered,
|
|
1359
|
+
};
|
|
1360
|
+
|
|
1361
|
+
// 执行文件渲染钩子
|
|
1362
|
+
const processedFileInfo = await executeFileRenderHook(
|
|
1363
|
+
templateConfig,
|
|
1364
|
+
fileInfo,
|
|
1365
|
+
context,
|
|
1366
|
+
);
|
|
1367
|
+
|
|
1368
|
+
// 如果返回 null,跳过该文件
|
|
1369
|
+
if (processedFileInfo === null) {
|
|
1370
|
+
logger.verbose(' ⊘ Skipped by onFileRender hook');
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
// 使用处理后的目标路径
|
|
1375
|
+
const finalDestPath = path.join(outputPath, processedFileInfo.destPath);
|
|
1376
|
+
|
|
1377
|
+
// 确保目标目录存在
|
|
1378
|
+
await ensureDir(path.dirname(finalDestPath));
|
|
1379
|
+
|
|
1380
|
+
// 写入文件
|
|
1381
|
+
if (processedFileInfo.isBinary) {
|
|
1382
|
+
// 二进制文件:如果内容没变,直接复制;否则从 base64 解码写入
|
|
1383
|
+
if (processedFileInfo.content === content) {
|
|
1384
|
+
await fs$1.copyFile(srcPath, finalDestPath);
|
|
1385
|
+
logger.verbose(' ✓ Copied (binary)');
|
|
1386
|
+
} else {
|
|
1387
|
+
const buffer = Buffer.from(processedFileInfo.content, 'base64');
|
|
1388
|
+
await fs$1.writeFile(finalDestPath, buffer);
|
|
1389
|
+
logger.verbose(' ✓ Written (binary, modified by hook)');
|
|
1390
|
+
}
|
|
1391
|
+
} else {
|
|
1392
|
+
// 文本文件
|
|
1393
|
+
await fs$1.writeFile(finalDestPath, processedFileInfo.content, 'utf-8');
|
|
1394
|
+
logger.verbose(' ✓ Rendered and written');
|
|
1395
|
+
}
|
|
1396
|
+
};
|
|
1397
|
+
|
|
1398
|
+
/**
|
|
1399
|
+
* 复制并处理模板文件到目标目录
|
|
1400
|
+
*/
|
|
1401
|
+
const processTemplateFiles = async (options
|
|
1402
|
+
|
|
1403
|
+
|
|
1404
|
+
|
|
1405
|
+
|
|
1406
|
+
) => {
|
|
1407
|
+
const { templatePath, outputPath, context, templateConfig } = options;
|
|
1221
1408
|
logger.verbose('Processing template files:');
|
|
1222
1409
|
logger.verbose(` - Template path: ${templatePath}`);
|
|
1223
1410
|
logger.verbose(` - Output path: ${outputPath}`);
|
|
@@ -1248,29 +1435,15 @@ const processTemplateFiles = async (
|
|
|
1248
1435
|
}
|
|
1249
1436
|
|
|
1250
1437
|
await Promise.all(
|
|
1251
|
-
files.map(
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
)
|
|
1259
|
-
|
|
1260
|
-
// 确保目标目录存在
|
|
1261
|
-
await ensureDir(path.dirname(destPath));
|
|
1262
|
-
|
|
1263
|
-
if (shouldRenderFile(srcPath)) {
|
|
1264
|
-
// 渲染文本文件
|
|
1265
|
-
const rendered = await renderTemplate(srcPath, context);
|
|
1266
|
-
await fs$1.writeFile(destPath, rendered, 'utf-8');
|
|
1267
|
-
logger.verbose(' ✓ Rendered and written');
|
|
1268
|
-
} else {
|
|
1269
|
-
// 直接复制二进制文件
|
|
1270
|
-
await fs$1.copyFile(srcPath, destPath);
|
|
1271
|
-
logger.verbose(' ✓ Copied');
|
|
1272
|
-
}
|
|
1273
|
-
}),
|
|
1438
|
+
files.map(file =>
|
|
1439
|
+
processSingleFile({
|
|
1440
|
+
file,
|
|
1441
|
+
templatePath,
|
|
1442
|
+
outputPath,
|
|
1443
|
+
context,
|
|
1444
|
+
templateConfig,
|
|
1445
|
+
}),
|
|
1446
|
+
),
|
|
1274
1447
|
);
|
|
1275
1448
|
|
|
1276
1449
|
logger.verbose('✓ All files processed successfully');
|
|
@@ -1457,7 +1630,12 @@ const execute = async (
|
|
|
1457
1630
|
const absoluteOutputPath = await prepareOutputDirectory(outputPath);
|
|
1458
1631
|
|
|
1459
1632
|
// 6. 处理模板文件
|
|
1460
|
-
await processTemplateFiles(
|
|
1633
|
+
await processTemplateFiles({
|
|
1634
|
+
templatePath,
|
|
1635
|
+
outputPath: absoluteOutputPath,
|
|
1636
|
+
context,
|
|
1637
|
+
templateConfig,
|
|
1638
|
+
});
|
|
1461
1639
|
|
|
1462
1640
|
// 7. 执行 onAfterRender 钩子
|
|
1463
1641
|
await executeAfterRenderHook(templateConfig, context, absoluteOutputPath);
|
|
@@ -1465,7 +1643,7 @@ const execute = async (
|
|
|
1465
1643
|
return absoluteOutputPath;
|
|
1466
1644
|
};
|
|
1467
1645
|
|
|
1468
|
-
function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
|
|
1646
|
+
function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
|
|
1469
1647
|
/**
|
|
1470
1648
|
* 运行 pnpm install
|
|
1471
1649
|
*/
|
|
@@ -1568,45 +1746,33 @@ const runGitInit = (projectPath) => {
|
|
|
1568
1746
|
};
|
|
1569
1747
|
|
|
1570
1748
|
/**
|
|
1571
|
-
* 运行开发服务器
|
|
1749
|
+
* 运行开发服务器(后台模式)
|
|
1750
|
+
* 启动后台子进程运行开发服务器,父进程可以直接退出
|
|
1751
|
+
* 使用 CLI 自己的 dev 命令(定义在 run.ts)而不是直接运行 npm run dev
|
|
1572
1752
|
*/
|
|
1573
|
-
const
|
|
1574
|
-
logger.info('\nStarting development server...');
|
|
1575
|
-
logger.info(`Executing: npm run dev in ${projectPath}`);
|
|
1576
|
-
logger.info('Press Ctrl+C to stop the server\n');
|
|
1753
|
+
const runDev = (projectPath) => {
|
|
1754
|
+
logger.info('\nStarting development server in background...');
|
|
1577
1755
|
|
|
1578
|
-
//
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
async: true,
|
|
1582
|
-
silent: true, // 手动处理输出以便显示详细信息
|
|
1583
|
-
});
|
|
1756
|
+
// 获取当前 CLI 的可执行文件路径
|
|
1757
|
+
// process.argv[0] 是 node,process.argv[1] 是 CLI 入口文件
|
|
1758
|
+
const cliPath = process.argv[1];
|
|
1584
1759
|
|
|
1585
|
-
|
|
1586
|
-
// 输出 stdout
|
|
1587
|
-
_optionalChain([child, 'access', _ => _.stdout, 'optionalAccess', _2 => _2.on, 'call', _3 => _3('data', (data) => {
|
|
1588
|
-
process.stdout.write(data);
|
|
1589
|
-
})]);
|
|
1760
|
+
logger.info(`Executing: ${cliPath} dev in ${projectPath}`);
|
|
1590
1761
|
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
child.on('error', (error) => {
|
|
1598
|
-
logger.error(`Failed to start dev server: ${error.message}`);
|
|
1599
|
-
logger.error(`Error stack: ${error.stack}`);
|
|
1600
|
-
});
|
|
1762
|
+
// 使用通用的后台执行函数启动开发服务器
|
|
1763
|
+
// 调用 CLI 自己的 dev 命令
|
|
1764
|
+
const pid = spawnDetached(process.argv[0], [cliPath, 'dev'], {
|
|
1765
|
+
cwd: projectPath,
|
|
1766
|
+
verbose: false, // 不输出额外的进程信息,由 logger 统一处理
|
|
1767
|
+
});
|
|
1601
1768
|
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
});
|
|
1769
|
+
logger.success('Development server started in background!');
|
|
1770
|
+
if (pid) {
|
|
1771
|
+
logger.info(`Process ID: ${pid}`);
|
|
1772
|
+
logger.info(
|
|
1773
|
+
'\nThe dev server is running independently. You can close this terminal.',
|
|
1774
|
+
);
|
|
1775
|
+
logger.info(`To stop the server later, use: kill ${pid}`);
|
|
1610
1776
|
}
|
|
1611
1777
|
};
|
|
1612
1778
|
|
|
@@ -1671,7 +1837,7 @@ const executeInit = async (
|
|
|
1671
1837
|
|
|
1672
1838
|
// 如果没有跳过 dev,则启动开发服务器
|
|
1673
1839
|
if (!skipDev) {
|
|
1674
|
-
|
|
1840
|
+
runDev(absoluteOutputPath);
|
|
1675
1841
|
timer.logPhase('Dev server startup');
|
|
1676
1842
|
} else {
|
|
1677
1843
|
// 只有跳过 dev 时才显示 Next steps
|
|
@@ -1685,7 +1851,7 @@ const executeInit = async (
|
|
|
1685
1851
|
' git init && git add . && git commit -m "initial commit"',
|
|
1686
1852
|
);
|
|
1687
1853
|
}
|
|
1688
|
-
logger.info('
|
|
1854
|
+
logger.info(' coze dev');
|
|
1689
1855
|
}
|
|
1690
1856
|
|
|
1691
1857
|
// 输出总耗时
|
|
@@ -1719,7 +1885,7 @@ const registerCommand = program => {
|
|
|
1719
1885
|
});
|
|
1720
1886
|
};
|
|
1721
1887
|
|
|
1722
|
-
var version = "0.0.1-alpha.
|
|
1888
|
+
var version = "0.0.1-alpha.6d3e84";
|
|
1723
1889
|
var packageJson = {
|
|
1724
1890
|
version: version};
|
|
1725
1891
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@coze-arch/cli",
|
|
3
|
-
"version": "0.0.1-alpha.
|
|
3
|
+
"version": "0.0.1-alpha.6d3e84",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "coze coding devtools cli",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,12 +19,13 @@
|
|
|
19
19
|
"scripts": {
|
|
20
20
|
"prebuild": "tsx scripts/prebuild.ts",
|
|
21
21
|
"build": "tsx scripts/build.ts",
|
|
22
|
+
"create": "tsx scripts/create-template.ts",
|
|
22
23
|
"lint": "eslint ./ --cache",
|
|
23
24
|
"postpublish": "bash scripts/sync-npmmirror.sh",
|
|
24
25
|
"test": "vitest --run --passWithNoTests",
|
|
25
26
|
"test:all": "bash scripts/test-coverage.sh",
|
|
26
27
|
"test:cov": "vitest --run --passWithNoTests --coverage",
|
|
27
|
-
"test:e2e": "bash scripts/e2e.sh",
|
|
28
|
+
"test:e2e": "NODE_ENV=test bash scripts/e2e.sh",
|
|
28
29
|
"test:perf": "vitest bench --run --config vitest.perf.config.ts",
|
|
29
30
|
"test:perf:compare": "bash scripts/compare-perf.sh",
|
|
30
31
|
"test:perf:save": "bash scripts/run-perf-with-output.sh"
|
|
@@ -48,21 +49,25 @@
|
|
|
48
49
|
"@coze-arch/ts-config": "workspace:*",
|
|
49
50
|
"@coze-arch/vitest-config": "workspace:*",
|
|
50
51
|
"@coze-coding/lambda": "workspace:*",
|
|
52
|
+
"@inquirer/prompts": "^3.2.0",
|
|
51
53
|
"@types/ejs": "^3.1.5",
|
|
52
54
|
"@types/iarna__toml": "^2.0.5",
|
|
53
55
|
"@types/js-yaml": "^4.0.9",
|
|
56
|
+
"@types/minimatch": "^5.1.2",
|
|
54
57
|
"@types/minimist": "^1.2.5",
|
|
55
58
|
"@types/node": "^24",
|
|
56
59
|
"@types/shelljs": "^0.10.0",
|
|
57
60
|
"@vitest/coverage-v8": "~4.0.16",
|
|
58
61
|
"json-schema-to-typescript": "^15.0.3",
|
|
62
|
+
"minimatch": "^10.0.1",
|
|
59
63
|
"rollup": "^4.41.1",
|
|
60
64
|
"sucrase": "^3.35.0",
|
|
61
65
|
"tsx": "^4.20.6",
|
|
62
66
|
"vitest": "~4.0.16"
|
|
63
67
|
},
|
|
64
68
|
"publishConfig": {
|
|
65
|
-
"access": "public"
|
|
69
|
+
"access": "public",
|
|
70
|
+
"registry": "https://registry.npmjs.org"
|
|
66
71
|
},
|
|
67
72
|
"cozePublishConfig": {
|
|
68
73
|
"bin": {
|