@coze-arch/cli 0.0.1-alpha.c186ff → 0.0.1-alpha.c199b3
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/README.md +1 -0
- package/lib/__templates__/expo/.cozeproj/scripts/dev_run.sh +25 -16
- package/lib/__templates__/expo/.cozeproj/scripts/server_dev_run.sh +9 -8
- package/lib/__templates__/expo/README.md +4 -2
- package/lib/__templates__/expo/_npmrc +1 -0
- package/lib/__templates__/expo/client/app/+not-found.tsx +30 -0
- package/lib/__templates__/expo/client/app/_layout.tsx +11 -8
- package/lib/__templates__/expo/client/app.config.ts +2 -2
- package/lib/__templates__/expo/client/components/Screen.tsx +3 -19
- package/lib/__templates__/expo/client/components/ThemedView.tsx +1 -2
- package/lib/__templates__/expo/client/constants/theme.ts +21 -698
- package/lib/__templates__/expo/client/eslint.config.mjs +40 -10
- package/lib/__templates__/expo/client/hooks/{useColorScheme.ts → useColorScheme.tsx} +20 -6
- package/lib/__templates__/expo/client/hooks/useSafeRouter.ts +152 -0
- package/lib/__templates__/expo/client/hooks/useTheme.ts +26 -6
- package/lib/__templates__/expo/client/metro.config.js +3 -0
- package/lib/__templates__/expo/client/package.json +36 -34
- package/lib/__templates__/expo/client/screens/demo/index.tsx +3 -3
- package/lib/__templates__/expo/client/scripts/install-missing-deps.js +10 -10
- package/lib/__templates__/expo/eslint-plugins/fontawesome6/index.js +9 -0
- package/lib/__templates__/expo/eslint-plugins/fontawesome6/names.js +1889 -0
- package/lib/__templates__/expo/eslint-plugins/fontawesome6/rule.js +174 -0
- package/lib/__templates__/expo/eslint-plugins/fontawesome6/v5-only-names.js +388 -0
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/index.js +9 -0
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/rule.js +112 -0
- package/lib/__templates__/expo/eslint-plugins/forbid-emoji/tech.md +94 -0
- package/lib/__templates__/expo/eslint-plugins/react-native/index.js +9 -0
- package/lib/__templates__/expo/eslint-plugins/react-native/rule.js +64 -0
- package/lib/__templates__/expo/eslint-plugins/reanimated/index.js +9 -0
- package/lib/__templates__/expo/eslint-plugins/reanimated/rule.js +88 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/index.js +9 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/rule.js +120 -0
- package/lib/__templates__/expo/eslint-plugins/restrict-linear-gradient/tech.md +58 -0
- package/lib/__templates__/expo/package.json +3 -0
- package/lib/__templates__/expo/patches/expo@54.0.33.patch +45 -0
- package/lib/__templates__/expo/pnpm-lock.yaml +1318 -2636
- package/lib/__templates__/expo/server/package.json +9 -7
- package/lib/__templates__/expo/server/src/index.ts +3 -1
- package/lib/__templates__/expo/template.config.js +56 -0
- package/lib/__templates__/native-static/.coze +11 -0
- package/lib/__templates__/native-static/index.html +33 -0
- package/lib/__templates__/native-static/styles/main.css +136 -0
- package/lib/__templates__/native-static/template.config.js +22 -0
- package/lib/__templates__/nextjs/.babelrc +15 -0
- package/lib/__templates__/nextjs/AGENTS.md +54 -0
- package/lib/__templates__/nextjs/README.md +5 -0
- package/lib/__templates__/nextjs/eslint.config.mjs +5 -0
- package/lib/__templates__/nextjs/next.config.ts +1 -2
- package/lib/__templates__/nextjs/package.json +8 -1
- package/lib/__templates__/nextjs/pnpm-lock.yaml +3472 -1561
- package/lib/__templates__/nextjs/scripts/build.sh +4 -1
- package/lib/__templates__/nextjs/scripts/dev.sh +8 -2
- package/lib/__templates__/nextjs/scripts/start.sh +7 -1
- package/lib/__templates__/nextjs/src/app/layout.tsx +5 -3
- package/lib/__templates__/nextjs/src/app/page.tsx +17 -60
- package/lib/__templates__/nextjs/src/server.ts +35 -0
- package/lib/__templates__/nextjs/template.config.js +47 -12
- package/lib/__templates__/nextjs/tsconfig.json +1 -1
- package/lib/__templates__/nuxt-vue/.coze +12 -0
- package/lib/__templates__/nuxt-vue/AGENTS.md +42 -0
- package/lib/__templates__/nuxt-vue/README.md +73 -0
- package/lib/__templates__/nuxt-vue/_gitignore +24 -0
- package/lib/__templates__/nuxt-vue/_npmrc +23 -0
- package/lib/__templates__/nuxt-vue/app/app.vue +6 -0
- package/lib/__templates__/nuxt-vue/app/pages/index.vue +23 -0
- package/lib/__templates__/nuxt-vue/assets/css/main.css +24 -0
- package/lib/__templates__/nuxt-vue/nuxt.config.ts +116 -0
- package/lib/__templates__/nuxt-vue/package.json +35 -0
- package/lib/__templates__/nuxt-vue/pnpm-lock.yaml +8759 -0
- package/lib/__templates__/nuxt-vue/postcss.config.mjs +8 -0
- package/lib/__templates__/nuxt-vue/public/favicon.ico +0 -0
- package/lib/__templates__/nuxt-vue/public/robots.txt +2 -0
- package/lib/__templates__/nuxt-vue/scripts/build.sh +14 -0
- package/lib/__templates__/nuxt-vue/scripts/dev.sh +39 -0
- package/lib/__templates__/nuxt-vue/scripts/prepare.sh +14 -0
- package/lib/__templates__/nuxt-vue/scripts/start.sh +21 -0
- package/lib/__templates__/nuxt-vue/server/api/hello.ts +10 -0
- package/lib/__templates__/nuxt-vue/server/middleware/logger.ts +10 -0
- package/lib/__templates__/nuxt-vue/server/routes/health.ts +10 -0
- package/lib/__templates__/nuxt-vue/tailwind.config.js +13 -0
- package/lib/__templates__/nuxt-vue/template.config.js +87 -0
- package/lib/__templates__/nuxt-vue/tsconfig.json +18 -0
- package/lib/__templates__/taro/.coze +14 -0
- package/lib/__templates__/taro/.cozeproj/scripts/deploy_build.sh +19 -0
- package/lib/__templates__/taro/.cozeproj/scripts/deploy_run.sh +14 -0
- package/lib/__templates__/taro/.cozeproj/scripts/dev_build.sh +2 -0
- package/lib/__templates__/taro/.cozeproj/scripts/dev_run.sh +151 -0
- package/lib/__templates__/taro/.cozeproj/scripts/init_env.sh +5 -0
- package/lib/__templates__/taro/.cozeproj/scripts/pack.sh +24 -0
- package/lib/__templates__/taro/README.md +763 -0
- package/lib/__templates__/taro/_gitignore +40 -0
- package/lib/__templates__/taro/_npmrc +18 -0
- package/lib/__templates__/taro/babel.config.js +12 -0
- package/lib/__templates__/taro/config/dev.ts +9 -0
- package/lib/__templates__/taro/config/index.ts +238 -0
- package/lib/__templates__/taro/config/prod.ts +34 -0
- package/lib/__templates__/taro/eslint.config.mjs +135 -0
- package/lib/__templates__/taro/key/private.appid.key +0 -0
- package/lib/__templates__/taro/package.json +112 -0
- package/lib/__templates__/taro/patches/@tarojs__plugin-mini-ci@4.1.9.patch +30 -0
- package/lib/__templates__/taro/pnpm-lock.yaml +23412 -0
- package/lib/__templates__/taro/pnpm-workspace.yaml +2 -0
- package/lib/__templates__/taro/project.config.json +15 -0
- package/lib/__templates__/taro/server/nest-cli.json +10 -0
- package/lib/__templates__/taro/server/package.json +40 -0
- package/lib/__templates__/taro/server/src/app.controller.ts +23 -0
- package/lib/__templates__/taro/server/src/app.module.ts +10 -0
- package/lib/__templates__/taro/server/src/app.service.ts +8 -0
- package/lib/__templates__/taro/server/src/interceptors/http-status.interceptor.ts +23 -0
- package/lib/__templates__/taro/server/src/main.ts +49 -0
- package/lib/__templates__/taro/server/tsconfig.json +24 -0
- package/lib/__templates__/taro/src/app.config.ts +11 -0
- package/lib/__templates__/taro/src/app.css +156 -0
- package/lib/__templates__/taro/src/app.tsx +9 -0
- package/lib/__templates__/taro/src/components/ui/accordion.tsx +159 -0
- package/lib/__templates__/taro/src/components/ui/alert-dialog.tsx +260 -0
- package/lib/__templates__/taro/src/components/ui/alert.tsx +60 -0
- package/lib/__templates__/taro/src/components/ui/aspect-ratio.tsx +36 -0
- package/lib/__templates__/taro/src/components/ui/avatar.tsx +84 -0
- package/lib/__templates__/taro/src/components/ui/badge.tsx +37 -0
- package/lib/__templates__/taro/src/components/ui/breadcrumb.tsx +117 -0
- package/lib/__templates__/taro/src/components/ui/button-group.tsx +83 -0
- package/lib/__templates__/taro/src/components/ui/button.tsx +67 -0
- package/lib/__templates__/taro/src/components/ui/calendar.tsx +394 -0
- package/lib/__templates__/taro/src/components/ui/card.tsx +108 -0
- package/lib/__templates__/taro/src/components/ui/carousel.tsx +228 -0
- package/lib/__templates__/taro/src/components/ui/checkbox.tsx +58 -0
- package/lib/__templates__/taro/src/components/ui/code-block.tsx +169 -0
- package/lib/__templates__/taro/src/components/ui/collapsible.tsx +71 -0
- package/lib/__templates__/taro/src/components/ui/command.tsx +385 -0
- package/lib/__templates__/taro/src/components/ui/context-menu.tsx +614 -0
- package/lib/__templates__/taro/src/components/ui/dialog.tsx +256 -0
- package/lib/__templates__/taro/src/components/ui/drawer.tsx +192 -0
- package/lib/__templates__/taro/src/components/ui/dropdown-menu.tsx +561 -0
- package/lib/__templates__/taro/src/components/ui/field.tsx +228 -0
- package/lib/__templates__/taro/src/components/ui/hover-card.tsx +282 -0
- package/lib/__templates__/taro/src/components/ui/input-group.tsx +197 -0
- package/lib/__templates__/taro/src/components/ui/input-otp.tsx +136 -0
- package/lib/__templates__/taro/src/components/ui/input.tsx +56 -0
- package/lib/__templates__/taro/src/components/ui/label.tsx +24 -0
- package/lib/__templates__/taro/src/components/ui/menubar.tsx +595 -0
- package/lib/__templates__/taro/src/components/ui/navigation-menu.tsx +264 -0
- package/lib/__templates__/taro/src/components/ui/pagination.tsx +118 -0
- package/lib/__templates__/taro/src/components/ui/popover.tsx +291 -0
- package/lib/__templates__/taro/src/components/ui/portal.tsx +19 -0
- package/lib/__templates__/taro/src/components/ui/progress.tsx +28 -0
- package/lib/__templates__/taro/src/components/ui/radio-group.tsx +64 -0
- package/lib/__templates__/taro/src/components/ui/resizable.tsx +346 -0
- package/lib/__templates__/taro/src/components/ui/scroll-area.tsx +34 -0
- package/lib/__templates__/taro/src/components/ui/select.tsx +438 -0
- package/lib/__templates__/taro/src/components/ui/separator.tsx +30 -0
- package/lib/__templates__/taro/src/components/ui/sheet.tsx +262 -0
- package/lib/__templates__/taro/src/components/ui/skeleton.tsx +17 -0
- package/lib/__templates__/taro/src/components/ui/slider.tsx +203 -0
- package/lib/__templates__/taro/src/components/ui/sonner.tsx +1 -0
- package/lib/__templates__/taro/src/components/ui/switch.tsx +55 -0
- package/lib/__templates__/taro/src/components/ui/table.tsx +142 -0
- package/lib/__templates__/taro/src/components/ui/tabs.tsx +114 -0
- package/lib/__templates__/taro/src/components/ui/textarea.tsx +54 -0
- package/lib/__templates__/taro/src/components/ui/toast.tsx +517 -0
- package/lib/__templates__/taro/src/components/ui/toggle-group.tsx +120 -0
- package/lib/__templates__/taro/src/components/ui/toggle.tsx +77 -0
- package/lib/__templates__/taro/src/components/ui/tooltip.tsx +455 -0
- package/lib/__templates__/taro/src/index.html +39 -0
- package/lib/__templates__/taro/src/lib/hooks/use-keyboard-offset.ts +37 -0
- package/lib/__templates__/taro/src/lib/measure.ts +115 -0
- package/lib/__templates__/taro/src/lib/platform.ts +12 -0
- package/lib/__templates__/taro/src/lib/utils.ts +6 -0
- package/lib/__templates__/taro/src/network.ts +39 -0
- package/lib/__templates__/taro/src/pages/index/index.config.ts +3 -0
- package/lib/__templates__/taro/src/pages/index/index.css +1 -0
- package/lib/__templates__/taro/src/pages/index/index.tsx +33 -0
- package/lib/__templates__/taro/src/presets/dev-debug.ts +23 -0
- package/lib/__templates__/taro/src/presets/h5-container.tsx +15 -0
- package/lib/__templates__/taro/src/presets/h5-navbar.tsx +238 -0
- package/lib/__templates__/taro/src/presets/h5-styles.ts +220 -0
- package/lib/__templates__/taro/src/presets/index.tsx +18 -0
- package/lib/__templates__/taro/stylelint.config.mjs +4 -0
- package/lib/__templates__/taro/template.config.js +68 -0
- package/lib/__templates__/taro/tsconfig.json +29 -0
- package/lib/__templates__/taro/types/global.d.ts +32 -0
- package/lib/__templates__/templates.json +75 -0
- package/lib/__templates__/vite/AGENTS.md +41 -0
- package/lib/__templates__/vite/README.md +190 -11
- package/lib/__templates__/vite/_gitignore +1 -0
- package/lib/__templates__/vite/eslint.config.mjs +6 -1
- package/lib/__templates__/vite/package.json +18 -2
- package/lib/__templates__/vite/pnpm-lock.yaml +1079 -2330
- package/lib/__templates__/vite/scripts/build.sh +4 -1
- package/lib/__templates__/vite/scripts/dev.sh +9 -2
- package/lib/__templates__/vite/scripts/start.sh +9 -3
- package/lib/__templates__/vite/server/routes/index.ts +31 -0
- package/lib/__templates__/vite/server/server.ts +65 -0
- package/lib/__templates__/vite/server/vite.ts +67 -0
- package/lib/__templates__/vite/src/main.ts +17 -47
- package/lib/__templates__/vite/template.config.js +47 -10
- package/lib/__templates__/vite/tsconfig.json +4 -3
- package/lib/__templates__/vite/vite.config.ts +5 -0
- package/lib/cli.js +3603 -407
- package/package.json +10 -3
package/lib/cli.js
CHANGED
|
@@ -4,12 +4,17 @@
|
|
|
4
4
|
var commander = require('commander');
|
|
5
5
|
var path = require('path');
|
|
6
6
|
var fs = require('fs');
|
|
7
|
+
var debug = require('debug');
|
|
8
|
+
var node_http = require('node:http');
|
|
9
|
+
var node_https = require('node:https');
|
|
10
|
+
var node_path = require('node:path');
|
|
11
|
+
var node_fs = require('node:fs');
|
|
7
12
|
var shelljs = require('shelljs');
|
|
8
13
|
var perf_hooks = require('perf_hooks');
|
|
9
14
|
var fs$1 = require('fs/promises');
|
|
10
15
|
var os = require('os');
|
|
11
|
-
var toml = require('@iarna/toml');
|
|
12
16
|
var jsYaml = require('js-yaml');
|
|
17
|
+
var toml = require('@iarna/toml');
|
|
13
18
|
var child_process = require('child_process');
|
|
14
19
|
var addFormats = require('ajv-formats');
|
|
15
20
|
var Ajv = require('ajv');
|
|
@@ -84,48 +89,2203 @@ const formatParam = (paramName, param) => {
|
|
|
84
89
|
};
|
|
85
90
|
|
|
86
91
|
/**
|
|
87
|
-
* 生成模板帮助文本
|
|
92
|
+
* 生成模板帮助文本
|
|
93
|
+
*/
|
|
94
|
+
const generateTemplatesHelpText = () => {
|
|
95
|
+
const config = loadTemplatesConfig$1();
|
|
96
|
+
|
|
97
|
+
if (!config || config.templates.length === 0) {
|
|
98
|
+
return '';
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const lines = [];
|
|
102
|
+
|
|
103
|
+
lines.push('\n📦 Available Templates:\n');
|
|
104
|
+
lines.push(` ${'='.repeat(76)}`);
|
|
105
|
+
|
|
106
|
+
config.templates.forEach((template, index) => {
|
|
107
|
+
if (index > 0) {
|
|
108
|
+
lines.push(` ${'-'.repeat(76)}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
lines.push(`\n Template: ${template.name}`);
|
|
112
|
+
lines.push(` Description: ${template.description}`);
|
|
113
|
+
|
|
114
|
+
const params = Object.entries(template.paramsSchema);
|
|
115
|
+
if (params.length > 0) {
|
|
116
|
+
lines.push(' Parameters:');
|
|
117
|
+
params.forEach(([paramName, param]) => {
|
|
118
|
+
lines.push(formatParam(paramName, param));
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
lines.push(`\n ${'='.repeat(76)}`);
|
|
124
|
+
lines.push('\n Usage:');
|
|
125
|
+
lines.push(
|
|
126
|
+
' coze init <directory> -t <template-name> [--param value ...]',
|
|
127
|
+
);
|
|
128
|
+
lines.push('\n Example:');
|
|
129
|
+
lines.push(' coze init my-app -t nextjs --appName my-app --port 3000');
|
|
130
|
+
lines.push('');
|
|
131
|
+
|
|
132
|
+
return lines.join('\n');
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
var DEFAULT_SIZE = 10;
|
|
136
|
+
var DEFAULT_WAIT = 1000;
|
|
137
|
+
var stringifyBatch = function (list) {
|
|
138
|
+
return JSON.stringify({
|
|
139
|
+
ev_type: 'batch',
|
|
140
|
+
list: list,
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
function createBatchSender(config) {
|
|
144
|
+
var transport = config.transport;
|
|
145
|
+
var endpoint = config.endpoint, _a = config.size, size = _a === void 0 ? DEFAULT_SIZE : _a, _b = config.wait, wait = _b === void 0 ? DEFAULT_WAIT : _b;
|
|
146
|
+
var batch = [];
|
|
147
|
+
var tid = 0;
|
|
148
|
+
var fail;
|
|
149
|
+
var success;
|
|
150
|
+
var sender = {
|
|
151
|
+
getSize: function () {
|
|
152
|
+
return size;
|
|
153
|
+
},
|
|
154
|
+
getWait: function () {
|
|
155
|
+
return wait;
|
|
156
|
+
},
|
|
157
|
+
setSize: function (v) {
|
|
158
|
+
size = v;
|
|
159
|
+
},
|
|
160
|
+
setWait: function (v) {
|
|
161
|
+
wait = v;
|
|
162
|
+
},
|
|
163
|
+
getEndpoint: function () {
|
|
164
|
+
return endpoint;
|
|
165
|
+
},
|
|
166
|
+
setEndpoint: function (v) {
|
|
167
|
+
endpoint = v;
|
|
168
|
+
},
|
|
169
|
+
send: function (e) {
|
|
170
|
+
batch.push(e);
|
|
171
|
+
if (batch.length >= size) {
|
|
172
|
+
sendBatch.call(this);
|
|
173
|
+
}
|
|
174
|
+
clearTimeout(tid);
|
|
175
|
+
tid = setTimeout(sendBatch.bind(this), wait);
|
|
176
|
+
},
|
|
177
|
+
flush: function () {
|
|
178
|
+
clearTimeout(tid);
|
|
179
|
+
sendBatch.call(this);
|
|
180
|
+
},
|
|
181
|
+
getBatchData: function () {
|
|
182
|
+
return batch.length ? stringifyBatch(batch) : '';
|
|
183
|
+
},
|
|
184
|
+
clear: function () {
|
|
185
|
+
clearTimeout(tid);
|
|
186
|
+
batch = [];
|
|
187
|
+
},
|
|
188
|
+
fail: function (cb) {
|
|
189
|
+
fail = cb;
|
|
190
|
+
},
|
|
191
|
+
success: function (cb) {
|
|
192
|
+
success = cb;
|
|
193
|
+
},
|
|
194
|
+
};
|
|
195
|
+
function sendBatch() {
|
|
196
|
+
if (!batch.length) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
var data = this.getBatchData();
|
|
200
|
+
transport.post({
|
|
201
|
+
url: endpoint,
|
|
202
|
+
data: data,
|
|
203
|
+
fail: function (err) {
|
|
204
|
+
fail && fail(err, data);
|
|
205
|
+
},
|
|
206
|
+
success: function () {
|
|
207
|
+
success && success(data);
|
|
208
|
+
},
|
|
209
|
+
});
|
|
210
|
+
batch = [];
|
|
211
|
+
}
|
|
212
|
+
return sender;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/*! *****************************************************************************
|
|
216
|
+
Copyright (c) Microsoft Corporation.
|
|
217
|
+
|
|
218
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
219
|
+
purpose with or without fee is hereby granted.
|
|
220
|
+
|
|
221
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
222
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
223
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
224
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
225
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
226
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
227
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
228
|
+
***************************************************************************** */
|
|
229
|
+
|
|
230
|
+
var __assign = function() {
|
|
231
|
+
__assign = Object.assign || function __assign(t) {
|
|
232
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
233
|
+
s = arguments[i];
|
|
234
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p)) t[p] = s[p];
|
|
235
|
+
}
|
|
236
|
+
return t;
|
|
237
|
+
};
|
|
238
|
+
return __assign.apply(this, arguments);
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
function __values(o) {
|
|
242
|
+
var s = typeof Symbol === "function" && Symbol.iterator, m = s && o[s], i = 0;
|
|
243
|
+
if (m) return m.call(o);
|
|
244
|
+
if (o && typeof o.length === "number") return {
|
|
245
|
+
next: function () {
|
|
246
|
+
if (o && i >= o.length) o = void 0;
|
|
247
|
+
return { value: o && o[i++], done: !o };
|
|
248
|
+
}
|
|
249
|
+
};
|
|
250
|
+
throw new TypeError(s ? "Object is not iterable." : "Symbol.iterator is not defined.");
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function __read(o, n) {
|
|
254
|
+
var m = typeof Symbol === "function" && o[Symbol.iterator];
|
|
255
|
+
if (!m) return o;
|
|
256
|
+
var i = m.call(o), r, ar = [], e;
|
|
257
|
+
try {
|
|
258
|
+
while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
|
|
259
|
+
}
|
|
260
|
+
catch (error) { e = { error: error }; }
|
|
261
|
+
finally {
|
|
262
|
+
try {
|
|
263
|
+
if (r && !r.done && (m = i["return"])) m.call(i);
|
|
264
|
+
}
|
|
265
|
+
finally { if (e) throw e.error; }
|
|
266
|
+
}
|
|
267
|
+
return ar;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function __spreadArray(to, from, pack) {
|
|
271
|
+
if (arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
272
|
+
if (ar || !(i in from)) {
|
|
273
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
274
|
+
ar[i] = from[i];
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
var noop$1 = function () { return ({}); };
|
|
281
|
+
function id(v) {
|
|
282
|
+
return v;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
286
|
+
function isObject(o) {
|
|
287
|
+
return typeof o === 'object' && o !== null;
|
|
288
|
+
}
|
|
289
|
+
var objProto = Object.prototype;
|
|
290
|
+
function isArray(o) {
|
|
291
|
+
return objProto.toString.call(o) === '[object Array]';
|
|
292
|
+
}
|
|
293
|
+
function isBoolean(o) {
|
|
294
|
+
return typeof o === 'boolean';
|
|
295
|
+
}
|
|
296
|
+
function isNumber(o) {
|
|
297
|
+
return typeof o === 'number';
|
|
298
|
+
}
|
|
299
|
+
function isString(o) {
|
|
300
|
+
return typeof o === 'string';
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// 检查数组中是否有元素
|
|
304
|
+
function arrayIncludes(array, value) {
|
|
305
|
+
if (!isArray(array)) {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
308
|
+
if (array.length === 0) {
|
|
309
|
+
return false;
|
|
310
|
+
}
|
|
311
|
+
var k = 0;
|
|
312
|
+
while (k < array.length) {
|
|
313
|
+
if (array[k] === value) {
|
|
314
|
+
return true;
|
|
315
|
+
}
|
|
316
|
+
k++;
|
|
317
|
+
}
|
|
318
|
+
return false;
|
|
319
|
+
}
|
|
320
|
+
var arrayRemove = function (arr, e) {
|
|
321
|
+
if (!isArray(arr)) {
|
|
322
|
+
return arr;
|
|
323
|
+
}
|
|
324
|
+
var i = arr.indexOf(e);
|
|
325
|
+
if (i >= 0) {
|
|
326
|
+
var arr_ = arr.slice();
|
|
327
|
+
arr_.splice(i, 1);
|
|
328
|
+
return arr_;
|
|
329
|
+
}
|
|
330
|
+
return arr;
|
|
331
|
+
};
|
|
332
|
+
/**
|
|
333
|
+
* 按路径访问对象属性
|
|
334
|
+
* @param target 待访问对象
|
|
335
|
+
* @param property 访问属性路径
|
|
336
|
+
* @param { (target: any, property: string): any } visitor 访问器
|
|
337
|
+
*/
|
|
338
|
+
var safeVisit = function (target, path, visitor) {
|
|
339
|
+
var _a, _b;
|
|
340
|
+
var paths = path.split('.');
|
|
341
|
+
var _c = __read(paths), method = _c[0], rest = _c.slice(1);
|
|
342
|
+
while (target && rest.length > 0) {
|
|
343
|
+
target = target[method];
|
|
344
|
+
_a = rest, _b = __read(_a), method = _b[0], rest = _b.slice(1);
|
|
345
|
+
}
|
|
346
|
+
if (!target) {
|
|
347
|
+
return undefined;
|
|
348
|
+
}
|
|
349
|
+
return visitor(target, method);
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
function safeStringify(a) {
|
|
353
|
+
try {
|
|
354
|
+
return isString(a) ? a : JSON.stringify(a);
|
|
355
|
+
}
|
|
356
|
+
catch (err) {
|
|
357
|
+
return '[FAILED_TO_STRINGIFY]:' + String(err);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function createContextAgent() {
|
|
362
|
+
var context = {};
|
|
363
|
+
var stringified = {};
|
|
364
|
+
var contextAgent = {
|
|
365
|
+
set: function (k, v) {
|
|
366
|
+
context[k] = v;
|
|
367
|
+
stringified[k] = safeStringify(v);
|
|
368
|
+
return contextAgent;
|
|
369
|
+
},
|
|
370
|
+
merge: function (ctx) {
|
|
371
|
+
context = __assign(__assign({}, context), ctx);
|
|
372
|
+
Object.keys(ctx).forEach(function (key) {
|
|
373
|
+
stringified[key] = safeStringify(ctx[key]);
|
|
374
|
+
});
|
|
375
|
+
return contextAgent;
|
|
376
|
+
},
|
|
377
|
+
delete: function (k) {
|
|
378
|
+
delete context[k];
|
|
379
|
+
delete stringified[k];
|
|
380
|
+
return contextAgent;
|
|
381
|
+
},
|
|
382
|
+
clear: function () {
|
|
383
|
+
context = {};
|
|
384
|
+
stringified = {};
|
|
385
|
+
return contextAgent;
|
|
386
|
+
},
|
|
387
|
+
get: function (k) {
|
|
388
|
+
return stringified[k];
|
|
389
|
+
},
|
|
390
|
+
toString: function () {
|
|
391
|
+
return __assign({}, stringified);
|
|
392
|
+
},
|
|
393
|
+
};
|
|
394
|
+
return contextAgent;
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
var getPrintString = function () {
|
|
398
|
+
// @ts-expect-error
|
|
399
|
+
if (''.padStart) {
|
|
400
|
+
return function (str, prefixLength) {
|
|
401
|
+
if (prefixLength === void 0) { prefixLength = 8; }
|
|
402
|
+
return str.padStart(prefixLength, ' ');
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
return function (str) { return str; };
|
|
406
|
+
};
|
|
407
|
+
var printString = getPrintString();
|
|
408
|
+
var errCount = 0;
|
|
409
|
+
var error = function () {
|
|
410
|
+
var args = [];
|
|
411
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
412
|
+
args[_i] = arguments[_i];
|
|
413
|
+
}
|
|
414
|
+
// eslint-disable-next-line no-console
|
|
415
|
+
console.error.apply(console, __spreadArray(['[SDK]', Date.now(), printString("" + errCount++)], __read(args), false));
|
|
416
|
+
};
|
|
417
|
+
var warnCount = 0;
|
|
418
|
+
var warn = function () {
|
|
419
|
+
var args = [];
|
|
420
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
421
|
+
args[_i] = arguments[_i];
|
|
422
|
+
}
|
|
423
|
+
// eslint-disable-next-line no-console
|
|
424
|
+
console.warn.apply(console, __spreadArray(['[SDK]', Date.now(), printString("" + warnCount++)], __read(args), false));
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
var isHitBySampleRate = function (sampleRate) {
|
|
428
|
+
if (Math.random() < Number(sampleRate)) {
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
431
|
+
return false;
|
|
432
|
+
};
|
|
433
|
+
var isHitByRandom = function (random, sampleRate) {
|
|
434
|
+
if (random < Number(sampleRate)) {
|
|
435
|
+
return true;
|
|
436
|
+
}
|
|
437
|
+
return false;
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
var runProcessors = function (fns) {
|
|
441
|
+
return function (e) {
|
|
442
|
+
var r = e;
|
|
443
|
+
for (var i = 0; i < fns.length; i++) {
|
|
444
|
+
if (r) {
|
|
445
|
+
try {
|
|
446
|
+
r = fns[i](r);
|
|
447
|
+
}
|
|
448
|
+
catch (err) {
|
|
449
|
+
error(err);
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
break;
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
return r;
|
|
457
|
+
};
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* 生成uuid
|
|
462
|
+
* stolen from https://github.com/kelektiv/node-uuid#readme uuid/v4
|
|
463
|
+
*
|
|
464
|
+
* @returns
|
|
465
|
+
*/
|
|
466
|
+
function mathRNG() {
|
|
467
|
+
var rnds = new Array(16);
|
|
468
|
+
var r = 0;
|
|
469
|
+
for (var i = 0; i < 16; i++) {
|
|
470
|
+
if ((i & 0x03) === 0) {
|
|
471
|
+
r = Math.random() * 0x100000000;
|
|
472
|
+
}
|
|
473
|
+
rnds[i] = (r >>> ((i & 0x03) << 3)) & 0xff;
|
|
474
|
+
}
|
|
475
|
+
return rnds;
|
|
476
|
+
}
|
|
477
|
+
function bytesToUuid(buf) {
|
|
478
|
+
var byteToHex = [];
|
|
479
|
+
for (var index = 0; index < 256; ++index) {
|
|
480
|
+
byteToHex[index] = (index + 0x100).toString(16).substr(1);
|
|
481
|
+
}
|
|
482
|
+
var i = 0;
|
|
483
|
+
var bth = byteToHex;
|
|
484
|
+
// join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
|
|
485
|
+
return [
|
|
486
|
+
bth[buf[i++]],
|
|
487
|
+
bth[buf[i++]],
|
|
488
|
+
bth[buf[i++]],
|
|
489
|
+
bth[buf[i++]],
|
|
490
|
+
'-',
|
|
491
|
+
bth[buf[i++]],
|
|
492
|
+
bth[buf[i++]],
|
|
493
|
+
'-',
|
|
494
|
+
bth[buf[i++]],
|
|
495
|
+
bth[buf[i++]],
|
|
496
|
+
'-',
|
|
497
|
+
bth[buf[i++]],
|
|
498
|
+
bth[buf[i++]],
|
|
499
|
+
'-',
|
|
500
|
+
bth[buf[i++]],
|
|
501
|
+
bth[buf[i++]],
|
|
502
|
+
bth[buf[i++]],
|
|
503
|
+
bth[buf[i++]],
|
|
504
|
+
bth[buf[i++]],
|
|
505
|
+
bth[buf[i++]],
|
|
506
|
+
].join('');
|
|
507
|
+
}
|
|
508
|
+
function uuid() {
|
|
509
|
+
var rnds = mathRNG();
|
|
510
|
+
// Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
|
|
511
|
+
rnds[6] = (rnds[6] & 0x0f) | 0x40;
|
|
512
|
+
rnds[8] = (rnds[8] & 0x3f) | 0x80;
|
|
513
|
+
return bytesToUuid(rnds);
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
function createDestroyAgent() {
|
|
517
|
+
var destroyed = false;
|
|
518
|
+
var data = {};
|
|
519
|
+
var removeTearDownGroup = function (tearDownGroup) {
|
|
520
|
+
tearDownGroup.length &&
|
|
521
|
+
tearDownGroup.forEach(function (v) {
|
|
522
|
+
try {
|
|
523
|
+
v();
|
|
524
|
+
// eslint-disable-next-line no-empty
|
|
525
|
+
}
|
|
526
|
+
catch (_a) { }
|
|
527
|
+
});
|
|
528
|
+
tearDownGroup.length = 0;
|
|
529
|
+
};
|
|
530
|
+
var removeByPluginName = function (pluginName) {
|
|
531
|
+
data[pluginName] &&
|
|
532
|
+
data[pluginName].forEach(function (e) {
|
|
533
|
+
removeTearDownGroup(e[1]);
|
|
534
|
+
});
|
|
535
|
+
data[pluginName] = undefined;
|
|
536
|
+
};
|
|
537
|
+
var removeByEvType = function (evType) {
|
|
538
|
+
Object.keys(data).forEach(function (k) {
|
|
539
|
+
data[k] &&
|
|
540
|
+
data[k].forEach(function (e) {
|
|
541
|
+
if (e[0] === evType) {
|
|
542
|
+
removeTearDownGroup(e[1]);
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
});
|
|
546
|
+
};
|
|
547
|
+
return {
|
|
548
|
+
/**
|
|
549
|
+
* register tearDownGroup for a single plugin.
|
|
550
|
+
*/
|
|
551
|
+
set: function (pluginName, evType, tearDownGroup) {
|
|
552
|
+
if (data[pluginName])
|
|
553
|
+
data[pluginName].push([evType, tearDownGroup]);
|
|
554
|
+
else
|
|
555
|
+
data[pluginName] = [[evType, tearDownGroup]];
|
|
556
|
+
// auto remove tearDownGroup if destroyed
|
|
557
|
+
destroyed && removeTearDownGroup(tearDownGroup);
|
|
558
|
+
},
|
|
559
|
+
has: function (pluginName) {
|
|
560
|
+
return !!data[pluginName];
|
|
561
|
+
},
|
|
562
|
+
/**
|
|
563
|
+
* remove tearDownGroup for a single plugin.
|
|
564
|
+
*/
|
|
565
|
+
remove: removeByPluginName,
|
|
566
|
+
/**
|
|
567
|
+
* remove tearDownGroup by event type
|
|
568
|
+
*/
|
|
569
|
+
removeByEvType: removeByEvType,
|
|
570
|
+
/**
|
|
571
|
+
* clear all tearDownGroup
|
|
572
|
+
*/
|
|
573
|
+
clear: function () {
|
|
574
|
+
destroyed = true;
|
|
575
|
+
Object.keys(data).forEach(function (k) {
|
|
576
|
+
removeByPluginName(k);
|
|
577
|
+
});
|
|
578
|
+
},
|
|
579
|
+
};
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// max size of preStartQueue
|
|
583
|
+
var PRESTART_QUEUE_MAX_SIZE = 500;
|
|
584
|
+
// slice size to operate preStartQueue
|
|
585
|
+
var PRESTART_QUEUE_OP_SLICE_SIZE = 50;
|
|
586
|
+
function cachePreStartData(client, preStartQueue, processed) {
|
|
587
|
+
preStartQueue.push(processed);
|
|
588
|
+
if (preStartQueue.length < PRESTART_QUEUE_MAX_SIZE) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
// delete some data to prevent OOM
|
|
592
|
+
var deleteData = preStartQueue.splice(0, PRESTART_QUEUE_OP_SLICE_SIZE);
|
|
593
|
+
if (client.savePreStartDataToDb) {
|
|
594
|
+
void client.savePreStartDataToDb(deleteData);
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
function consumePreStartData(client) {
|
|
598
|
+
var preStartQueue = client.getPreStartQueue();
|
|
599
|
+
preStartQueue.forEach(function (e) { return client.build(e); });
|
|
600
|
+
preStartQueue.length = 0;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
var EVENTS = [
|
|
604
|
+
'init',
|
|
605
|
+
'start',
|
|
606
|
+
'config',
|
|
607
|
+
'beforeDestroy',
|
|
608
|
+
'provide',
|
|
609
|
+
'beforeReport',
|
|
610
|
+
'report',
|
|
611
|
+
'beforeBuild',
|
|
612
|
+
'build',
|
|
613
|
+
'beforeSend',
|
|
614
|
+
'send',
|
|
615
|
+
'beforeConfig',
|
|
616
|
+
];
|
|
617
|
+
|
|
618
|
+
function createClient(creationConfig) {
|
|
619
|
+
var builder = creationConfig.builder, createSender = creationConfig.createSender, createDefaultConfig = creationConfig.createDefaultConfig, createConfigManager = creationConfig.createConfigManager, userConfigNormalizer = creationConfig.userConfigNormalizer, initConfigNormalizer = creationConfig.initConfigNormalizer, validateInitConfig = creationConfig.validateInitConfig;
|
|
620
|
+
var sender;
|
|
621
|
+
var configManager;
|
|
622
|
+
var handlers = {};
|
|
623
|
+
EVENTS.forEach(function (e) { return (handlers[e] = []); });
|
|
624
|
+
var inited = false;
|
|
625
|
+
var started = false;
|
|
626
|
+
var destroyed = false;
|
|
627
|
+
// 缓存 start 之前 build 的事件
|
|
628
|
+
var preStartQueue = [];
|
|
629
|
+
// 禁止通过 provide 挂载的字段名
|
|
630
|
+
var reservedNames = [];
|
|
631
|
+
var destroyAgent = createDestroyAgent();
|
|
632
|
+
var client = {
|
|
633
|
+
getBuilder: function () { return builder; },
|
|
634
|
+
getSender: function () { return sender; },
|
|
635
|
+
getPreStartQueue: function () { return preStartQueue; },
|
|
636
|
+
init: function (c) {
|
|
637
|
+
if (inited) {
|
|
638
|
+
warn('already inited');
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
if (c && isObject(c) && validateInitConfig(c)) {
|
|
642
|
+
var defaultConfig = createDefaultConfig(c);
|
|
643
|
+
if (!defaultConfig) {
|
|
644
|
+
throw new Error('defaultConfig missing');
|
|
645
|
+
}
|
|
646
|
+
var initConfig = initConfigNormalizer(c);
|
|
647
|
+
configManager = createConfigManager(defaultConfig);
|
|
648
|
+
configManager.setConfig(initConfig);
|
|
649
|
+
configManager.onChange(function () {
|
|
650
|
+
handle('config');
|
|
651
|
+
});
|
|
652
|
+
sender = createSender(configManager.getConfig());
|
|
653
|
+
if (!sender) {
|
|
654
|
+
throw new Error('sender missing');
|
|
655
|
+
}
|
|
656
|
+
inited = true;
|
|
657
|
+
handle('init', true);
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
throw new Error('invalid InitConfig, init failed');
|
|
661
|
+
}
|
|
662
|
+
},
|
|
663
|
+
set: function (c) {
|
|
664
|
+
if (!inited) {
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
if (c && isObject(c)) {
|
|
668
|
+
handle('beforeConfig', false, c);
|
|
669
|
+
configManager === null || configManager === void 0 ? void 0 : configManager.setConfig(c);
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
config: function (c) {
|
|
673
|
+
if (!inited) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
if (c && isObject(c)) {
|
|
677
|
+
handle('beforeConfig', false, c);
|
|
678
|
+
configManager === null || configManager === void 0 ? void 0 : configManager.setConfig(userConfigNormalizer(c));
|
|
679
|
+
}
|
|
680
|
+
return configManager === null || configManager === void 0 ? void 0 : configManager.getConfig();
|
|
681
|
+
},
|
|
682
|
+
provide: function (name, value) {
|
|
683
|
+
if (arrayIncludes(reservedNames, name)) {
|
|
684
|
+
warn("cannot provide " + name + ", reserved");
|
|
685
|
+
return;
|
|
686
|
+
}
|
|
687
|
+
client[name] = value;
|
|
688
|
+
handle('provide', false, name);
|
|
689
|
+
},
|
|
690
|
+
start: function () {
|
|
691
|
+
if (!inited) {
|
|
692
|
+
return;
|
|
693
|
+
}
|
|
694
|
+
if (started) {
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
configManager === null || configManager === void 0 ? void 0 : configManager.onReady(function () {
|
|
698
|
+
started = true;
|
|
699
|
+
handle('start', true);
|
|
700
|
+
consumePreStartData(client);
|
|
701
|
+
});
|
|
702
|
+
},
|
|
703
|
+
report: function (data) {
|
|
704
|
+
if (!data) {
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
var preReport = runProcessors(handlers['beforeReport'])(data);
|
|
708
|
+
if (!preReport) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
var processed = runProcessors(handlers['report'])(preReport);
|
|
712
|
+
if (!processed) {
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (started) {
|
|
716
|
+
this.build(processed);
|
|
717
|
+
}
|
|
718
|
+
else {
|
|
719
|
+
cachePreStartData(client, preStartQueue, processed);
|
|
720
|
+
}
|
|
721
|
+
},
|
|
722
|
+
build: function (data) {
|
|
723
|
+
if (!started) {
|
|
724
|
+
return;
|
|
725
|
+
}
|
|
726
|
+
var preBuild = runProcessors(handlers['beforeBuild'])(data);
|
|
727
|
+
if (!preBuild) {
|
|
728
|
+
return;
|
|
729
|
+
}
|
|
730
|
+
var built = builder.build(preBuild);
|
|
731
|
+
if (!built) {
|
|
732
|
+
return;
|
|
733
|
+
}
|
|
734
|
+
var processed = runProcessors(handlers['build'])(built);
|
|
735
|
+
if (!processed) {
|
|
736
|
+
return;
|
|
737
|
+
}
|
|
738
|
+
this.send(processed);
|
|
739
|
+
},
|
|
740
|
+
send: function (data) {
|
|
741
|
+
if (!started) {
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
var processed = runProcessors(handlers['beforeSend'])(data);
|
|
745
|
+
if (processed) {
|
|
746
|
+
sender.send(processed);
|
|
747
|
+
handle('send', false, processed);
|
|
748
|
+
}
|
|
749
|
+
},
|
|
750
|
+
destroy: function () {
|
|
751
|
+
destroyAgent.clear();
|
|
752
|
+
destroyed = true;
|
|
753
|
+
preStartQueue.length = 0;
|
|
754
|
+
handle('beforeDestroy', true);
|
|
755
|
+
},
|
|
756
|
+
on: function (ev, handler) {
|
|
757
|
+
if ((ev === 'init' && inited) || (ev === 'start' && started) || (ev === 'beforeDestroy' && destroyed)) {
|
|
758
|
+
try {
|
|
759
|
+
;
|
|
760
|
+
handler();
|
|
761
|
+
}
|
|
762
|
+
catch (_err) {
|
|
763
|
+
// ignore
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
else if (handlers[ev]) {
|
|
767
|
+
handlers[ev].push(handler);
|
|
768
|
+
}
|
|
769
|
+
},
|
|
770
|
+
off: function (ev, handler) {
|
|
771
|
+
if (handlers[ev])
|
|
772
|
+
handlers[ev] = arrayRemove(handlers[ev], handler);
|
|
773
|
+
},
|
|
774
|
+
destroyAgent: destroyAgent,
|
|
775
|
+
};
|
|
776
|
+
reservedNames = Object.keys(client);
|
|
777
|
+
return client;
|
|
778
|
+
function handle(ev, once) {
|
|
779
|
+
if (once === void 0) { once = false; }
|
|
780
|
+
var args = [];
|
|
781
|
+
for (var _i = 2; _i < arguments.length; _i++) {
|
|
782
|
+
args[_i - 2] = arguments[_i];
|
|
783
|
+
}
|
|
784
|
+
handlers[ev].forEach(function (f) {
|
|
785
|
+
try {
|
|
786
|
+
f.apply(void 0, __spreadArray([], __read(args), false));
|
|
787
|
+
}
|
|
788
|
+
catch (_err) {
|
|
789
|
+
// ignore
|
|
790
|
+
}
|
|
791
|
+
});
|
|
792
|
+
if (once) {
|
|
793
|
+
handlers[ev].length = 0;
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
var ContextPlugin = function (client) {
|
|
799
|
+
var contextAgent = createContextAgent();
|
|
800
|
+
client.provide('context', contextAgent);
|
|
801
|
+
client.on('report', function (ev) {
|
|
802
|
+
if (!ev.extra) {
|
|
803
|
+
ev.extra = {};
|
|
804
|
+
}
|
|
805
|
+
ev.extra.context = contextAgent.toString();
|
|
806
|
+
return ev;
|
|
807
|
+
});
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
function IntegrationPlugin(client, runAfterSetup) {
|
|
811
|
+
client.on('init', function () {
|
|
812
|
+
var nameList = [];
|
|
813
|
+
var applyIntegrations = function (integrations) {
|
|
814
|
+
integrations.forEach(function (integration) {
|
|
815
|
+
var integrationName = integration.name;
|
|
816
|
+
if (!arrayIncludes(nameList, integrationName)) {
|
|
817
|
+
nameList.push(integrationName);
|
|
818
|
+
integration.setup(client);
|
|
819
|
+
client.destroyAgent.set(integrationName, integrationName, [
|
|
820
|
+
function () {
|
|
821
|
+
nameList = arrayRemove(nameList, integrationName);
|
|
822
|
+
integration.tearDown && integration.tearDown();
|
|
823
|
+
},
|
|
824
|
+
]);
|
|
825
|
+
}
|
|
826
|
+
});
|
|
827
|
+
};
|
|
828
|
+
client.provide('applyIntegrations', applyIntegrations);
|
|
829
|
+
var config = client.config();
|
|
830
|
+
if (config && config.integrations) {
|
|
831
|
+
applyIntegrations(config.integrations);
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
var DevtoolsPlugin = function (client) {
|
|
837
|
+
try {
|
|
838
|
+
if (typeof window === 'object' && isObject(window) && window.__SLARDAR_DEVTOOLS_GLOBAL_HOOK__) {
|
|
839
|
+
window.__SLARDAR_DEVTOOLS_GLOBAL_HOOK__.push(client);
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
catch (e) {
|
|
843
|
+
// ignore
|
|
844
|
+
}
|
|
845
|
+
};
|
|
846
|
+
|
|
847
|
+
var now = function () { return Date.now(); };
|
|
848
|
+
|
|
849
|
+
function getDefaultBrowser() {
|
|
850
|
+
if (typeof window === 'object' && isObject(window))
|
|
851
|
+
return window;
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// 获取全局注册表
|
|
855
|
+
var getGlobalRegistry = function (global) {
|
|
856
|
+
if (!global)
|
|
857
|
+
return;
|
|
858
|
+
if (!global.__SLARDAR_REGISTRY__) {
|
|
859
|
+
global.__SLARDAR_REGISTRY__ = {
|
|
860
|
+
Slardar: {
|
|
861
|
+
plugins: [],
|
|
862
|
+
errors: [],
|
|
863
|
+
subject: {},
|
|
864
|
+
},
|
|
865
|
+
};
|
|
866
|
+
}
|
|
867
|
+
return global.__SLARDAR_REGISTRY__.Slardar;
|
|
868
|
+
};
|
|
869
|
+
var reportSelfError = function () {
|
|
870
|
+
var errorInfo = [];
|
|
871
|
+
for (var _i = 0; _i < arguments.length; _i++) {
|
|
872
|
+
errorInfo[_i] = arguments[_i];
|
|
873
|
+
}
|
|
874
|
+
var registry = getGlobalRegistry(getDefaultBrowser());
|
|
875
|
+
if (!registry)
|
|
876
|
+
return;
|
|
877
|
+
if (!registry.errors) {
|
|
878
|
+
registry.errors = [];
|
|
879
|
+
}
|
|
880
|
+
registry.errors.push(errorInfo);
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
/**
|
|
884
|
+
* Special support in Perfsee for analyzing call stacks of custom events and custom metrics.
|
|
885
|
+
*/
|
|
886
|
+
// eslint-disable-next-line
|
|
887
|
+
var getStacksOnPerfsee = function (constructor) {
|
|
888
|
+
var _a;
|
|
889
|
+
// @ts-expect-error
|
|
890
|
+
if (typeof window !== 'object' || !window.__perfsee__) {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
var obj = {};
|
|
894
|
+
(_a = Error.captureStackTrace) === null || _a === void 0 ? void 0 : _a.call(Error, obj, constructor);
|
|
895
|
+
return obj.stack;
|
|
896
|
+
};
|
|
897
|
+
|
|
898
|
+
var CUSTOM_EV_TYPE = 'custom';
|
|
899
|
+
|
|
900
|
+
var CUSTOM_EVENT_TYPE = 'event';
|
|
901
|
+
var CUSTOM_LOG_TYPE = 'log';
|
|
902
|
+
var normalizeCustomEventData = function (raw) {
|
|
903
|
+
if (!raw || !isObject(raw)) {
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
// name is required
|
|
907
|
+
if (!raw['name'] || !isString(raw['name'])) {
|
|
908
|
+
return;
|
|
909
|
+
}
|
|
910
|
+
var res = {
|
|
911
|
+
name: raw['name'],
|
|
912
|
+
type: CUSTOM_EVENT_TYPE,
|
|
913
|
+
};
|
|
914
|
+
if ('metrics' in raw && isObject(raw['metrics'])) {
|
|
915
|
+
var rMetrics = raw['metrics'];
|
|
916
|
+
var metrics = {};
|
|
917
|
+
for (var k in rMetrics) {
|
|
918
|
+
if (isNumber(rMetrics[k])) {
|
|
919
|
+
metrics[k] = rMetrics[k];
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
res.metrics = metrics;
|
|
923
|
+
}
|
|
924
|
+
if ('categories' in raw && isObject(raw['categories'])) {
|
|
925
|
+
var rCategories = raw['categories'];
|
|
926
|
+
var categories = {};
|
|
927
|
+
for (var k in rCategories) {
|
|
928
|
+
categories[k] = safeStringify(rCategories[k]);
|
|
929
|
+
}
|
|
930
|
+
res.categories = categories;
|
|
931
|
+
}
|
|
932
|
+
if ('attached_log' in raw && isString(raw['attached_log'])) {
|
|
933
|
+
res.attached_log = raw['attached_log'];
|
|
934
|
+
}
|
|
935
|
+
return res;
|
|
936
|
+
};
|
|
937
|
+
var normalizeCustomLogData = function (raw) {
|
|
938
|
+
if (!raw || !isObject(raw)) {
|
|
939
|
+
return;
|
|
940
|
+
}
|
|
941
|
+
// content is required
|
|
942
|
+
if (!raw['content'] || !isString(raw['content'])) {
|
|
943
|
+
return;
|
|
944
|
+
}
|
|
945
|
+
var rContent = raw['content'];
|
|
946
|
+
var res = {
|
|
947
|
+
content: safeStringify(rContent),
|
|
948
|
+
type: CUSTOM_LOG_TYPE,
|
|
949
|
+
level: 'info',
|
|
950
|
+
};
|
|
951
|
+
if ('level' in raw) {
|
|
952
|
+
res.level = raw['level'];
|
|
953
|
+
}
|
|
954
|
+
if ('extra' in raw && isObject(raw['extra'])) {
|
|
955
|
+
var rExtra = raw['extra'];
|
|
956
|
+
var metrics = {};
|
|
957
|
+
var categories = {};
|
|
958
|
+
for (var k in rExtra) {
|
|
959
|
+
if (isNumber(rExtra[k])) {
|
|
960
|
+
metrics[k] = rExtra[k];
|
|
961
|
+
}
|
|
962
|
+
else {
|
|
963
|
+
categories[k] = safeStringify(rExtra[k]);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
res.metrics = metrics;
|
|
967
|
+
res.categories = categories;
|
|
968
|
+
}
|
|
969
|
+
if ('attached_log' in raw && isString(raw['attached_log'])) {
|
|
970
|
+
res.attached_log = raw['attached_log'];
|
|
971
|
+
}
|
|
972
|
+
return res;
|
|
973
|
+
};
|
|
974
|
+
var CustomPlugin = function (client) {
|
|
975
|
+
var sendEvent = function (data) {
|
|
976
|
+
var normalized = normalizeCustomEventData(data);
|
|
977
|
+
if (normalized) {
|
|
978
|
+
var stacks = getStacksOnPerfsee(sendEvent);
|
|
979
|
+
if (stacks) {
|
|
980
|
+
// @ts-expect-error
|
|
981
|
+
normalized.stacks = stacks;
|
|
982
|
+
}
|
|
983
|
+
client.report({
|
|
984
|
+
ev_type: CUSTOM_EV_TYPE,
|
|
985
|
+
payload: normalized,
|
|
986
|
+
extra: {
|
|
987
|
+
timestamp: now(),
|
|
988
|
+
},
|
|
989
|
+
});
|
|
990
|
+
}
|
|
991
|
+
};
|
|
992
|
+
var sendLog = function (data) {
|
|
993
|
+
var normalized = normalizeCustomLogData(data);
|
|
994
|
+
if (normalized) {
|
|
995
|
+
client.report({
|
|
996
|
+
ev_type: CUSTOM_EV_TYPE,
|
|
997
|
+
payload: normalized,
|
|
998
|
+
extra: {
|
|
999
|
+
timestamp: now(),
|
|
1000
|
+
},
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
};
|
|
1004
|
+
client.provide('sendEvent', sendEvent);
|
|
1005
|
+
client.provide('sendLog', sendLog);
|
|
1006
|
+
};
|
|
1007
|
+
|
|
1008
|
+
/* eslint-disable @typescript-eslint/prefer-for-of */
|
|
1009
|
+
var withSampleRate = function (ev, sampleRate) {
|
|
1010
|
+
var common = ev.common || {};
|
|
1011
|
+
common.sample_rate = sampleRate;
|
|
1012
|
+
ev.common = common;
|
|
1013
|
+
return ev;
|
|
1014
|
+
};
|
|
1015
|
+
var hitFnWithRandom = function (preCalc, sampleRate, isHitBySampleRate, random, isHitByRandom) {
|
|
1016
|
+
return preCalc
|
|
1017
|
+
? (function (h) { return function () {
|
|
1018
|
+
return h;
|
|
1019
|
+
}; })(isHitByRandom(random, sampleRate))
|
|
1020
|
+
: function () { return isHitBySampleRate(sampleRate); };
|
|
1021
|
+
};
|
|
1022
|
+
var parseValues = function (values, type) {
|
|
1023
|
+
return values.map(function (v) {
|
|
1024
|
+
switch (type) {
|
|
1025
|
+
case 'number':
|
|
1026
|
+
return Number(v);
|
|
1027
|
+
case 'boolean':
|
|
1028
|
+
return v === '1';
|
|
1029
|
+
case 'string': // default to string
|
|
1030
|
+
default:
|
|
1031
|
+
return String(v);
|
|
1032
|
+
}
|
|
1033
|
+
});
|
|
1034
|
+
};
|
|
1035
|
+
var checkVal = function (val, values, op) {
|
|
1036
|
+
switch (op) {
|
|
1037
|
+
case 'eq':
|
|
1038
|
+
return arrayIncludes(values, val);
|
|
1039
|
+
case 'neq':
|
|
1040
|
+
return !arrayIncludes(values, val);
|
|
1041
|
+
case 'gt':
|
|
1042
|
+
return val > values[0];
|
|
1043
|
+
case 'gte':
|
|
1044
|
+
return val >= values[0];
|
|
1045
|
+
case 'lt':
|
|
1046
|
+
return val < values[0];
|
|
1047
|
+
case 'lte':
|
|
1048
|
+
return val <= values[0];
|
|
1049
|
+
case 'regex':
|
|
1050
|
+
return Boolean(val.match(new RegExp(values.join('|'))));
|
|
1051
|
+
case 'not_regex':
|
|
1052
|
+
return !val.match(new RegExp(values.join('|')));
|
|
1053
|
+
default: {
|
|
1054
|
+
// unknown op
|
|
1055
|
+
return false;
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
};
|
|
1059
|
+
var checkFilter = function (ev, field, op, values) {
|
|
1060
|
+
var val = safeVisit(ev, field, function (t, p) {
|
|
1061
|
+
return t[p];
|
|
1062
|
+
});
|
|
1063
|
+
if (val === undefined) {
|
|
1064
|
+
return false;
|
|
1065
|
+
}
|
|
1066
|
+
var field_type = isBoolean(val) ? 'bool' : isNumber(val) ? 'number' : 'string';
|
|
1067
|
+
return checkVal(val, parseValues(values, field_type), op);
|
|
1068
|
+
};
|
|
1069
|
+
var matchFilter = function (ev, filter) {
|
|
1070
|
+
try {
|
|
1071
|
+
return filter.type === 'rule'
|
|
1072
|
+
? checkFilter(ev, filter.field, filter.op, filter.values)
|
|
1073
|
+
: filter.type === 'and'
|
|
1074
|
+
? filter.children.every(function (f) { return matchFilter(ev, f); })
|
|
1075
|
+
: filter.children.some(function (f) { return matchFilter(ev, f); });
|
|
1076
|
+
}
|
|
1077
|
+
catch (e) {
|
|
1078
|
+
reportSelfError(e);
|
|
1079
|
+
return false;
|
|
1080
|
+
}
|
|
1081
|
+
};
|
|
1082
|
+
var getHitMap = function (rules, preCalcHit, baseRate, isHitBySampleRate, random, isHitByRandom) {
|
|
1083
|
+
var hitMap = {};
|
|
1084
|
+
Object.keys(rules).forEach(function (name) {
|
|
1085
|
+
var _a = rules[name], enable = _a.enable, sample_rate = _a.sample_rate, conditional_sample_rules = _a.conditional_sample_rules;
|
|
1086
|
+
if (enable) {
|
|
1087
|
+
hitMap[name] = {
|
|
1088
|
+
enable: enable,
|
|
1089
|
+
sample_rate: sample_rate,
|
|
1090
|
+
effectiveSampleRate: sample_rate * baseRate,
|
|
1091
|
+
hit: hitFnWithRandom(preCalcHit, sample_rate, isHitBySampleRate, random, isHitByRandom),
|
|
1092
|
+
};
|
|
1093
|
+
if (conditional_sample_rules) {
|
|
1094
|
+
hitMap[name].conditional_hit_rules = conditional_sample_rules.map(function (_a) {
|
|
1095
|
+
var s = _a.sample_rate, filter = _a.filter;
|
|
1096
|
+
return ({
|
|
1097
|
+
sample_rate: s,
|
|
1098
|
+
hit: hitFnWithRandom(preCalcHit, s, isHitBySampleRate, random, isHitByRandom),
|
|
1099
|
+
effectiveSampleRate: s * baseRate,
|
|
1100
|
+
filter: filter,
|
|
1101
|
+
});
|
|
1102
|
+
});
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
else {
|
|
1106
|
+
hitMap[name] = {
|
|
1107
|
+
enable: enable,
|
|
1108
|
+
hit: function () {
|
|
1109
|
+
/* istanbul ignore next */
|
|
1110
|
+
return false;
|
|
1111
|
+
},
|
|
1112
|
+
sample_rate: 0,
|
|
1113
|
+
effectiveSampleRate: 0,
|
|
1114
|
+
};
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
return hitMap;
|
|
1118
|
+
};
|
|
1119
|
+
var getSampler = function (userId, config, isHitBySampleRate, isHitByRandom, destroyFns) {
|
|
1120
|
+
if (!config)
|
|
1121
|
+
return id;
|
|
1122
|
+
// r的设计是为了允许外部传入随机数,用于彻底实现按用户采样
|
|
1123
|
+
var baseRate = config.sample_rate, include_users = config.include_users, sample_granularity = config.sample_granularity, rules = config.rules, _a = config.r, random = _a === void 0 ? Math.random() : _a;
|
|
1124
|
+
// 用户名单采样
|
|
1125
|
+
var userHit = arrayIncludes(include_users, userId);
|
|
1126
|
+
if (userHit) {
|
|
1127
|
+
return function (ev) { return withSampleRate(ev, 1); };
|
|
1128
|
+
}
|
|
1129
|
+
// should pre calculate hit
|
|
1130
|
+
var preCalcHit = sample_granularity === 'session';
|
|
1131
|
+
var baseHit = hitFnWithRandom(preCalcHit, baseRate, isHitBySampleRate, random, isHitByRandom);
|
|
1132
|
+
var hitMap = getHitMap(rules, preCalcHit, baseRate, isHitBySampleRate, random, isHitByRandom);
|
|
1133
|
+
return function (ev) {
|
|
1134
|
+
var _a;
|
|
1135
|
+
// 总采样必须命中才有后续
|
|
1136
|
+
if (!baseHit()) {
|
|
1137
|
+
preCalcHit && destroyFns[0]();
|
|
1138
|
+
return false;
|
|
1139
|
+
}
|
|
1140
|
+
// 未配置的事件类型
|
|
1141
|
+
if (!(ev.ev_type in hitMap)) {
|
|
1142
|
+
return withSampleRate(ev, baseRate);
|
|
1143
|
+
}
|
|
1144
|
+
// 忽略未开启的事件类型
|
|
1145
|
+
if (!hitMap[ev.ev_type].enable) {
|
|
1146
|
+
preCalcHit && destroyFns[1](ev.ev_type);
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
// 跳过采样配置
|
|
1150
|
+
if ((_a = ev.common) === null || _a === void 0 ? void 0 : _a.sample_rate) {
|
|
1151
|
+
return ev;
|
|
1152
|
+
}
|
|
1153
|
+
var hitConfig = hitMap[ev.ev_type];
|
|
1154
|
+
var conditions = hitConfig.conditional_hit_rules;
|
|
1155
|
+
if (conditions) {
|
|
1156
|
+
// 先判断条件采样
|
|
1157
|
+
for (var i = 0; i < conditions.length; i++) {
|
|
1158
|
+
if (matchFilter(ev, conditions[i].filter)) {
|
|
1159
|
+
if (conditions[i].hit()) {
|
|
1160
|
+
return withSampleRate(ev, conditions[i].effectiveSampleRate);
|
|
1161
|
+
}
|
|
1162
|
+
// 条件匹配后不再搜索
|
|
1163
|
+
return false;
|
|
1164
|
+
}
|
|
1165
|
+
}
|
|
1166
|
+
}
|
|
1167
|
+
// 事件类型采样
|
|
1168
|
+
if (!hitConfig.hit()) {
|
|
1169
|
+
// not hit ev_type and no condition, destroy side effect
|
|
1170
|
+
!(conditions && conditions.length) && preCalcHit && destroyFns[1](ev.ev_type);
|
|
1171
|
+
return false;
|
|
1172
|
+
}
|
|
1173
|
+
// 事件类型默认采样已经命中
|
|
1174
|
+
return withSampleRate(ev, hitConfig.effectiveSampleRate);
|
|
1175
|
+
};
|
|
1176
|
+
};
|
|
1177
|
+
var SamplePlugin = function (client) {
|
|
1178
|
+
client.on('start', function () {
|
|
1179
|
+
var _a = client.config(), userId = _a.userId, sample = _a.sample;
|
|
1180
|
+
var destroyFns = [
|
|
1181
|
+
function () {
|
|
1182
|
+
client.destroy();
|
|
1183
|
+
},
|
|
1184
|
+
function (ev_type) {
|
|
1185
|
+
client.destroyAgent.removeByEvType(ev_type);
|
|
1186
|
+
},
|
|
1187
|
+
];
|
|
1188
|
+
var sampler = getSampler(userId, sample, isHitBySampleRate, isHitByRandom, destroyFns);
|
|
1189
|
+
client.on('build', sampler);
|
|
1190
|
+
});
|
|
1191
|
+
};
|
|
1192
|
+
|
|
1193
|
+
var builder = {
|
|
1194
|
+
build: function (e) {
|
|
1195
|
+
return {
|
|
1196
|
+
ev_type: e.ev_type,
|
|
1197
|
+
payload: e.payload,
|
|
1198
|
+
common: __assign(__assign({}, (e.extra || {})), (e.overrides || {})),
|
|
1199
|
+
};
|
|
1200
|
+
},
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
var REPORT_DOMAIN = "mon.zijieapi.com";
|
|
1204
|
+
var SDK_VERSION = "2.1.8" ;
|
|
1205
|
+
var SDK_NAME = 'SDK_BASE';
|
|
1206
|
+
var SETTINGS_PATH = '/monitor_web/settings/browser-settings';
|
|
1207
|
+
var BATCH_REPORT_PATH = "/monitor_browser/collect/batch/";
|
|
1208
|
+
var DEFAULT_SAMPLE_CONFIG = {
|
|
1209
|
+
sample_rate: 1,
|
|
1210
|
+
include_users: [],
|
|
1211
|
+
sample_granularity: 'session',
|
|
1212
|
+
rules: {},
|
|
1213
|
+
};
|
|
1214
|
+
var DEFAULT_SENDER_SIZE = 20;
|
|
1215
|
+
var DEFAULT_SAMPLE_GRANULARITY = 'session';
|
|
1216
|
+
|
|
1217
|
+
function normalizeStrictFields(config) {
|
|
1218
|
+
var e_1, _a;
|
|
1219
|
+
var strictFields = ['userId', 'deviceId', 'sessionId', 'env'];
|
|
1220
|
+
try {
|
|
1221
|
+
for (var strictFields_1 = __values(strictFields), strictFields_1_1 = strictFields_1.next(); !strictFields_1_1.done; strictFields_1_1 = strictFields_1.next()) {
|
|
1222
|
+
var k = strictFields_1_1.value;
|
|
1223
|
+
if (!config[k]) {
|
|
1224
|
+
delete config[k];
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
1229
|
+
finally {
|
|
1230
|
+
try {
|
|
1231
|
+
if (strictFields_1_1 && !strictFields_1_1.done && (_a = strictFields_1.return)) _a.call(strictFields_1);
|
|
1232
|
+
}
|
|
1233
|
+
finally { if (e_1) throw e_1.error; }
|
|
1234
|
+
}
|
|
1235
|
+
return config;
|
|
1236
|
+
}
|
|
1237
|
+
function normalizeInitConfig(config) {
|
|
1238
|
+
return normalizeStrictFields(__assign({}, config));
|
|
1239
|
+
}
|
|
1240
|
+
function validateInitConfig(config) {
|
|
1241
|
+
return isObject(config) && 'bid' in config && 'transport' in config;
|
|
1242
|
+
}
|
|
1243
|
+
function normalizeUserConfig(config) {
|
|
1244
|
+
return normalizeStrictFields(__assign({}, config));
|
|
1245
|
+
}
|
|
1246
|
+
function parseServerConfig(serverConfig) {
|
|
1247
|
+
if (!serverConfig) {
|
|
1248
|
+
return {};
|
|
1249
|
+
}
|
|
1250
|
+
var sample = serverConfig.sample, timestamp = serverConfig.timestamp, _a = serverConfig.quota_rate, quota_rate = _a === void 0 ? 1 : _a;
|
|
1251
|
+
if (!sample) {
|
|
1252
|
+
return {};
|
|
1253
|
+
}
|
|
1254
|
+
var sample_rate = sample.sample_rate, _b = sample.sample_granularity, sample_granularity = _b === void 0 ? DEFAULT_SAMPLE_GRANULARITY : _b, include_users = sample.include_users, _c = sample.rules, rules = _c === void 0 ? [] : _c;
|
|
1255
|
+
return {
|
|
1256
|
+
sample: {
|
|
1257
|
+
include_users: include_users,
|
|
1258
|
+
sample_rate: sample_rate * quota_rate,
|
|
1259
|
+
sample_granularity: sample_granularity,
|
|
1260
|
+
rules: rules.reduce(function (prev, cur) {
|
|
1261
|
+
var name = cur.name, enable = cur.enable, sample_rate = cur.sample_rate, conditional_sample_rules = cur.conditional_sample_rules;
|
|
1262
|
+
prev[name] = {
|
|
1263
|
+
enable: enable,
|
|
1264
|
+
sample_rate: sample_rate,
|
|
1265
|
+
conditional_sample_rules: conditional_sample_rules,
|
|
1266
|
+
};
|
|
1267
|
+
return prev;
|
|
1268
|
+
}, {}),
|
|
1269
|
+
},
|
|
1270
|
+
serverTimestamp: timestamp,
|
|
1271
|
+
};
|
|
1272
|
+
}
|
|
1273
|
+
|
|
1274
|
+
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
|
|
1275
|
+
var getReportUrl = function (domain, path) {
|
|
1276
|
+
if (path === void 0) { path = BATCH_REPORT_PATH; }
|
|
1277
|
+
return "" + (domain && domain.indexOf('//') >= 0 ? '' : 'https://') + domain + path;
|
|
1278
|
+
};
|
|
1279
|
+
var getSettingsUrl = function (domain, path) {
|
|
1280
|
+
if (path === void 0) { path = SETTINGS_PATH; }
|
|
1281
|
+
return "" + (domain && domain.indexOf('//') >= 0 ? '' : 'https://') + domain + path;
|
|
1282
|
+
};
|
|
1283
|
+
var getDefaultUserId = function () {
|
|
1284
|
+
return uuid();
|
|
1285
|
+
};
|
|
1286
|
+
var getDefaultDeviceId = function () {
|
|
1287
|
+
return uuid();
|
|
1288
|
+
};
|
|
1289
|
+
var getViewId = function (pid) { return pid + "_" + Date.now(); };
|
|
1290
|
+
var getDefaultSessionId = function () {
|
|
1291
|
+
return uuid();
|
|
1292
|
+
};
|
|
1293
|
+
|
|
1294
|
+
var createConfigManager = function (defaultConfig) {
|
|
1295
|
+
// the merged config
|
|
1296
|
+
var config = defaultConfig;
|
|
1297
|
+
// save it so we know when initConfig is set
|
|
1298
|
+
var initConfig;
|
|
1299
|
+
// save UserConfig so we can merge with priority
|
|
1300
|
+
var userConfig = {};
|
|
1301
|
+
// save the original server config, from get_setting response
|
|
1302
|
+
var serverConfig;
|
|
1303
|
+
// cache the parsed ServerConfig, used in merge
|
|
1304
|
+
var parsedServerConfig;
|
|
1305
|
+
var onReady = noop$1;
|
|
1306
|
+
// call when config changed
|
|
1307
|
+
var onChange = noop$1;
|
|
1308
|
+
return {
|
|
1309
|
+
getConfig: function () {
|
|
1310
|
+
return config;
|
|
1311
|
+
},
|
|
1312
|
+
setConfig: function (c) {
|
|
1313
|
+
userConfig = __assign(__assign({}, userConfig), (c || {}));
|
|
1314
|
+
updateConfig();
|
|
1315
|
+
if (!initConfig) {
|
|
1316
|
+
// handle init
|
|
1317
|
+
initConfig = c;
|
|
1318
|
+
if (config.useLocalConfig || !config.bid) {
|
|
1319
|
+
parsedServerConfig = {};
|
|
1320
|
+
onReady();
|
|
1321
|
+
}
|
|
1322
|
+
else {
|
|
1323
|
+
getServerConfig(config.transport, config.domain, config.bid, function (res) {
|
|
1324
|
+
serverConfig = res;
|
|
1325
|
+
handleServerConfig();
|
|
1326
|
+
});
|
|
1327
|
+
}
|
|
1328
|
+
}
|
|
1329
|
+
return config;
|
|
1330
|
+
},
|
|
1331
|
+
onChange: function (fn) {
|
|
1332
|
+
onChange = fn;
|
|
1333
|
+
},
|
|
1334
|
+
onReady: function (fn) {
|
|
1335
|
+
onReady = fn;
|
|
1336
|
+
if (parsedServerConfig) {
|
|
1337
|
+
onReady();
|
|
1338
|
+
}
|
|
1339
|
+
},
|
|
1340
|
+
};
|
|
1341
|
+
function updateConfig() {
|
|
1342
|
+
var newConfig = __assign(__assign(__assign({}, defaultConfig), (parsedServerConfig || {})), userConfig);
|
|
1343
|
+
newConfig.sample = mergeSampleConfig(mergeSampleConfig(defaultConfig.sample, parsedServerConfig === null || parsedServerConfig === void 0 ? void 0 : parsedServerConfig.sample), userConfig.sample);
|
|
1344
|
+
config = newConfig;
|
|
1345
|
+
onChange();
|
|
1346
|
+
}
|
|
1347
|
+
function handleServerConfig() {
|
|
1348
|
+
parsedServerConfig = parseServerConfig(serverConfig);
|
|
1349
|
+
updateConfig();
|
|
1350
|
+
onReady();
|
|
1351
|
+
}
|
|
1352
|
+
};
|
|
1353
|
+
function getServerConfig(transport, domain, bid, cb) {
|
|
1354
|
+
if (!transport.get) {
|
|
1355
|
+
return cb({});
|
|
1356
|
+
}
|
|
1357
|
+
transport.get({
|
|
1358
|
+
withCredentials: true,
|
|
1359
|
+
url: getSettingsUrl(domain) + "?bid=" + bid + "&store=1",
|
|
1360
|
+
success: function (res) {
|
|
1361
|
+
cb(res.data || {});
|
|
1362
|
+
},
|
|
1363
|
+
fail: function () {
|
|
1364
|
+
cb({ sample: { sample_rate: 0.001 } });
|
|
1365
|
+
},
|
|
1366
|
+
});
|
|
1367
|
+
}
|
|
1368
|
+
function mergeSampleConfig(a, b) {
|
|
1369
|
+
if (!a || !b)
|
|
1370
|
+
return a || b;
|
|
1371
|
+
var res = __assign(__assign({}, a), b);
|
|
1372
|
+
res.include_users = __spreadArray(__spreadArray([], __read((a.include_users || [])), false), __read((b.include_users || [])), false);
|
|
1373
|
+
res.rules = __spreadArray(__spreadArray([], __read(Object.keys(a.rules || {})), false), __read(Object.keys(b.rules || {})), false).reduce(function (obj, key) {
|
|
1374
|
+
var _a, _b;
|
|
1375
|
+
if (!(key in obj)) {
|
|
1376
|
+
if (key in (a.rules || {}) && key in (b.rules || {})) {
|
|
1377
|
+
obj[key] = __assign(__assign({}, a.rules[key]), b.rules[key]);
|
|
1378
|
+
obj[key].conditional_sample_rules = __spreadArray(__spreadArray([], __read((a.rules[key].conditional_sample_rules || [])), false), __read((b.rules[key].conditional_sample_rules || [])), false);
|
|
1379
|
+
}
|
|
1380
|
+
else {
|
|
1381
|
+
obj[key] = ((_a = a.rules) === null || _a === void 0 ? void 0 : _a[key]) || ((_b = b.rules) === null || _b === void 0 ? void 0 : _b[key]);
|
|
1382
|
+
}
|
|
1383
|
+
}
|
|
1384
|
+
return obj;
|
|
1385
|
+
}, {});
|
|
1386
|
+
return res;
|
|
1387
|
+
}
|
|
1388
|
+
|
|
1389
|
+
var addEnvToSendEvent = function (ev, config) {
|
|
1390
|
+
var _a = config || {}, version = _a.version, name = _a.name;
|
|
1391
|
+
var extra = {
|
|
1392
|
+
url: '',
|
|
1393
|
+
protocol: '',
|
|
1394
|
+
domain: '',
|
|
1395
|
+
path: '',
|
|
1396
|
+
query: '',
|
|
1397
|
+
timestamp: Date.now(),
|
|
1398
|
+
sdk_version: version || SDK_VERSION,
|
|
1399
|
+
sdk_name: name || SDK_NAME,
|
|
1400
|
+
};
|
|
1401
|
+
return __assign(__assign({}, ev), { extra: __assign(__assign({}, extra), (ev.extra || {})) });
|
|
1402
|
+
};
|
|
1403
|
+
var InjectEnvPlugin = function (client) {
|
|
1404
|
+
client.on('report', function (ev) {
|
|
1405
|
+
return addEnvToSendEvent(ev, client.config());
|
|
1406
|
+
});
|
|
1407
|
+
};
|
|
1408
|
+
|
|
1409
|
+
var addConfigToReportEvent = function (ev, config) {
|
|
1410
|
+
var extra = {};
|
|
1411
|
+
extra.bid = config.bid;
|
|
1412
|
+
extra.pid = config.pid;
|
|
1413
|
+
extra.view_id = config.viewId;
|
|
1414
|
+
extra.user_id = config.userId;
|
|
1415
|
+
extra.device_id = config.deviceId;
|
|
1416
|
+
extra.session_id = config.sessionId;
|
|
1417
|
+
extra.release = config.release;
|
|
1418
|
+
extra.env = config.env;
|
|
1419
|
+
return __assign(__assign({}, ev), { extra: __assign(__assign({}, extra), (ev.extra || {})) });
|
|
1420
|
+
};
|
|
1421
|
+
var InjectConfigPlugin = function (client) {
|
|
1422
|
+
client.on('beforeBuild', function (ev) {
|
|
1423
|
+
return addConfigToReportEvent(ev, client.config());
|
|
1424
|
+
});
|
|
1425
|
+
};
|
|
1426
|
+
|
|
1427
|
+
// createSender has side effects(register onClose behaviour)
|
|
1428
|
+
// so it must be create lazily
|
|
1429
|
+
function createSender(config) {
|
|
1430
|
+
return createBatchSender(config);
|
|
1431
|
+
}
|
|
1432
|
+
|
|
1433
|
+
/* eslint-disable @typescript-eslint/prefer-nullish-coalescing */
|
|
1434
|
+
var getDefaultConfig = function (_c) { return ({
|
|
1435
|
+
bid: '',
|
|
1436
|
+
pid: '',
|
|
1437
|
+
viewId: getViewId('_'),
|
|
1438
|
+
userId: getDefaultUserId(),
|
|
1439
|
+
deviceId: getDefaultDeviceId(),
|
|
1440
|
+
sessionId: getDefaultSessionId(),
|
|
1441
|
+
domain: REPORT_DOMAIN,
|
|
1442
|
+
release: '',
|
|
1443
|
+
env: 'production',
|
|
1444
|
+
sample: DEFAULT_SAMPLE_CONFIG,
|
|
1445
|
+
plugins: {},
|
|
1446
|
+
transport: {
|
|
1447
|
+
get: noop$1,
|
|
1448
|
+
post: noop$1,
|
|
1449
|
+
},
|
|
1450
|
+
useLocalConfig: false,
|
|
1451
|
+
}); };
|
|
1452
|
+
var createMinimalClient = function (_a) {
|
|
1453
|
+
var _b = _a === void 0 ? {} : _a, _d = _b.createSender, createSender$1 = _d === void 0 ? function (config) {
|
|
1454
|
+
return createSender({
|
|
1455
|
+
size: DEFAULT_SENDER_SIZE,
|
|
1456
|
+
endpoint: getReportUrl(config.domain),
|
|
1457
|
+
transport: config.transport,
|
|
1458
|
+
});
|
|
1459
|
+
} : _d, _e = _b.builder, builder$1 = _e === void 0 ? builder : _e, _f = _b.createDefaultConfig, createDefaultConfig = _f === void 0 ? getDefaultConfig : _f;
|
|
1460
|
+
var client = createClient({
|
|
1461
|
+
validateInitConfig: validateInitConfig,
|
|
1462
|
+
initConfigNormalizer: normalizeInitConfig,
|
|
1463
|
+
userConfigNormalizer: normalizeUserConfig,
|
|
1464
|
+
createSender: createSender$1,
|
|
1465
|
+
builder: builder$1,
|
|
1466
|
+
createDefaultConfig: createDefaultConfig,
|
|
1467
|
+
createConfigManager: createConfigManager,
|
|
1468
|
+
});
|
|
1469
|
+
ContextPlugin(client);
|
|
1470
|
+
InjectConfigPlugin(client);
|
|
1471
|
+
InjectEnvPlugin(client);
|
|
1472
|
+
IntegrationPlugin(client);
|
|
1473
|
+
DevtoolsPlugin(client);
|
|
1474
|
+
return client;
|
|
1475
|
+
};
|
|
1476
|
+
var createBaseClient = function (config) {
|
|
1477
|
+
if (config === void 0) { config = {}; }
|
|
1478
|
+
var client = createMinimalClient(config);
|
|
1479
|
+
SamplePlugin(client);
|
|
1480
|
+
CustomPlugin(client);
|
|
1481
|
+
return client;
|
|
1482
|
+
};
|
|
1483
|
+
|
|
1484
|
+
var browserClient = createBaseClient();
|
|
1485
|
+
|
|
1486
|
+
/**
|
|
1487
|
+
* Node.js 环境的 HTTP Transport 实现
|
|
1488
|
+
* 使用原生 http/https 模块实现网络请求
|
|
1489
|
+
*/
|
|
1490
|
+
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
const log$2 = debug('slardar:transport');
|
|
1494
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
1495
|
+
const noop = () => {};
|
|
1496
|
+
|
|
1497
|
+
|
|
1498
|
+
|
|
1499
|
+
|
|
1500
|
+
|
|
1501
|
+
|
|
1502
|
+
|
|
1503
|
+
|
|
1504
|
+
|
|
1505
|
+
|
|
1506
|
+
|
|
1507
|
+
/**
|
|
1508
|
+
* 执行 HTTP/HTTPS 请求
|
|
1509
|
+
*/
|
|
1510
|
+
function makeRequest(options) {
|
|
1511
|
+
const { url, method, data, success = noop, fail = noop, getResponseText = noop } = options;
|
|
1512
|
+
|
|
1513
|
+
log$2('Making %s request to %s', method, url);
|
|
1514
|
+
if (data) {
|
|
1515
|
+
log$2('Request data: %s', data.slice(0, 200));
|
|
1516
|
+
}
|
|
1517
|
+
|
|
1518
|
+
// 检查 URL 是否有效
|
|
1519
|
+
if (!url || typeof url !== 'string') {
|
|
1520
|
+
const error = new Error(`Invalid URL: ${url}`);
|
|
1521
|
+
log$2('Invalid URL provided: %o', url);
|
|
1522
|
+
fail(error);
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
|
|
1526
|
+
try {
|
|
1527
|
+
const urlObj = new URL(url);
|
|
1528
|
+
log$2('Parsed URL - protocol: %s, hostname: %s, port: %s, path: %s',
|
|
1529
|
+
urlObj.protocol, urlObj.hostname, urlObj.port, urlObj.pathname);
|
|
1530
|
+
|
|
1531
|
+
const isHttps = urlObj.protocol === 'https:';
|
|
1532
|
+
const request = isHttps ? node_https.request : node_http.request;
|
|
1533
|
+
|
|
1534
|
+
const req = request(
|
|
1535
|
+
{
|
|
1536
|
+
hostname: urlObj.hostname,
|
|
1537
|
+
port: urlObj.port,
|
|
1538
|
+
path: urlObj.pathname + urlObj.search,
|
|
1539
|
+
method,
|
|
1540
|
+
headers: {
|
|
1541
|
+
'Content-Type': 'application/json',
|
|
1542
|
+
...(data && { 'Content-Length': Buffer.byteLength(data) }),
|
|
1543
|
+
},
|
|
1544
|
+
},
|
|
1545
|
+
res => {
|
|
1546
|
+
log$2('Response callback triggered: status=%s', res.statusCode);
|
|
1547
|
+
let responseText = '';
|
|
1548
|
+
|
|
1549
|
+
res.on('data', chunk => {
|
|
1550
|
+
log$2('Response data chunk received: %s bytes', chunk.length);
|
|
1551
|
+
responseText += chunk.toString();
|
|
1552
|
+
});
|
|
1553
|
+
|
|
1554
|
+
res.on('end', () => {
|
|
1555
|
+
log$2('Response end event fired');
|
|
1556
|
+
log$2('Response received: status=%s, body=%s', res.statusCode, responseText.slice(0, 200));
|
|
1557
|
+
getResponseText(responseText);
|
|
1558
|
+
|
|
1559
|
+
try {
|
|
1560
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
1561
|
+
log$2('Request failed with status %s: %s', res.statusCode, responseText);
|
|
1562
|
+
fail(new Error(responseText || res.statusMessage || 'Request failed'));
|
|
1563
|
+
} else if (responseText) {
|
|
1564
|
+
// eslint-disable-next-line no-restricted-syntax -- Parsing trusted Slardar API responses
|
|
1565
|
+
const result = JSON.parse(responseText);
|
|
1566
|
+
log$2('Request succeeded');
|
|
1567
|
+
success(result);
|
|
1568
|
+
} else {
|
|
1569
|
+
log$2('Request succeeded with empty response');
|
|
1570
|
+
success({});
|
|
1571
|
+
}
|
|
1572
|
+
} catch (e) {
|
|
1573
|
+
log$2('Failed to parse response: %s', (e ).message);
|
|
1574
|
+
fail(e );
|
|
1575
|
+
}
|
|
1576
|
+
});
|
|
1577
|
+
},
|
|
1578
|
+
);
|
|
1579
|
+
|
|
1580
|
+
req.on('error', err => {
|
|
1581
|
+
log$2('Request error: %s', err.message);
|
|
1582
|
+
fail(err);
|
|
1583
|
+
});
|
|
1584
|
+
|
|
1585
|
+
req.on('timeout', () => {
|
|
1586
|
+
log$2('Request timeout');
|
|
1587
|
+
req.destroy();
|
|
1588
|
+
fail(new Error('Request timeout'));
|
|
1589
|
+
});
|
|
1590
|
+
|
|
1591
|
+
if (data) {
|
|
1592
|
+
req.write(data);
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
req.end();
|
|
1596
|
+
} catch (e) {
|
|
1597
|
+
log$2('Exception during request: %s', (e ).message);
|
|
1598
|
+
fail(e );
|
|
1599
|
+
}
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
/**
|
|
1603
|
+
* 创建 Node.js 环境的 Transport
|
|
1604
|
+
*/
|
|
1605
|
+
function createNodeTransport() {
|
|
1606
|
+
return {
|
|
1607
|
+
get(options) {
|
|
1608
|
+
log$2('Transport GET called: %s', options.url);
|
|
1609
|
+
makeRequest({
|
|
1610
|
+
method: 'GET',
|
|
1611
|
+
...options,
|
|
1612
|
+
});
|
|
1613
|
+
},
|
|
1614
|
+
post({ url, data }) {
|
|
1615
|
+
log$2('Transport POST called: %s', url);
|
|
1616
|
+
makeRequest({
|
|
1617
|
+
method: 'POST',
|
|
1618
|
+
url,
|
|
1619
|
+
data: typeof data === 'string' ? data : JSON.stringify(data),
|
|
1620
|
+
});
|
|
1621
|
+
},
|
|
1622
|
+
};
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
function _nullishCoalesce$3(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain$6(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; }/**
|
|
1626
|
+
* Slardar CLI Reporter 主类
|
|
1627
|
+
* 封装 @slardar/base 的初始化和上报逻辑
|
|
1628
|
+
*/
|
|
1629
|
+
|
|
1630
|
+
|
|
1631
|
+
|
|
1632
|
+
|
|
1633
|
+
|
|
1634
|
+
|
|
1635
|
+
// 创建 debug 实例
|
|
1636
|
+
// 使用方式: DEBUG=slardar:* your-cli-command
|
|
1637
|
+
const log$1 = debug('slardar:reporter');
|
|
1638
|
+
|
|
1639
|
+
/**
|
|
1640
|
+
* Slardar CLI Reporter
|
|
1641
|
+
* 提供简洁的 API 用于 CLI 工具的监控数据上报
|
|
1642
|
+
*/
|
|
1643
|
+
class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.call(this);SlardarCLIReporter.prototype.__init2.call(this); }
|
|
1644
|
+
__init() {this.initialized = false;}
|
|
1645
|
+
__init2() {this.client = browserClient;}
|
|
1646
|
+
|
|
1647
|
+
/**
|
|
1648
|
+
* 初始化 Slardar Reporter
|
|
1649
|
+
* @param config 初始化配置
|
|
1650
|
+
*
|
|
1651
|
+
* @example
|
|
1652
|
+
* ```typescript
|
|
1653
|
+
* reporter.setup({
|
|
1654
|
+
* bid: 'my_cli_app',
|
|
1655
|
+
* release: '1.0.0',
|
|
1656
|
+
* env: 'production',
|
|
1657
|
+
* });
|
|
1658
|
+
* ```
|
|
1659
|
+
*/
|
|
1660
|
+
setup(config) {
|
|
1661
|
+
if (this.initialized) {
|
|
1662
|
+
log$1('Already initialized, skipping setup');
|
|
1663
|
+
return;
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
try {
|
|
1667
|
+
log$1('Initializing Slardar with config:', {
|
|
1668
|
+
bid: config.bid,
|
|
1669
|
+
release: config.release,
|
|
1670
|
+
env: config.env,
|
|
1671
|
+
userId: config.userId,
|
|
1672
|
+
});
|
|
1673
|
+
|
|
1674
|
+
const transport = createNodeTransport();
|
|
1675
|
+
|
|
1676
|
+
this.client.init({
|
|
1677
|
+
bid: config.bid,
|
|
1678
|
+
transport,
|
|
1679
|
+
userId: config.userId,
|
|
1680
|
+
deviceId: config.deviceId,
|
|
1681
|
+
sessionId: config.sessionId,
|
|
1682
|
+
release: config.release,
|
|
1683
|
+
env: config.env,
|
|
1684
|
+
name: config.name,
|
|
1685
|
+
useLocalConfig: _nullishCoalesce$3(config.useLocalConfig, () => ( false)), // 默认使用服务端配置
|
|
1686
|
+
domain: config.domain,
|
|
1687
|
+
// 设置本地采样率,确保事件不被过滤
|
|
1688
|
+
sample: {
|
|
1689
|
+
event: 1, // 100% 采样自定义事件
|
|
1690
|
+
},
|
|
1691
|
+
});
|
|
1692
|
+
|
|
1693
|
+
// 添加 beforeSend 钩子来追踪事件是否被过滤
|
|
1694
|
+
this.client.on('beforeSend', (ev) => {
|
|
1695
|
+
log$1('beforeSend hook called for event: %s', (_optionalChain$6([ev, 'optionalAccess', _ => _.ev_type]) ) || 'unknown');
|
|
1696
|
+
return ev; // 必须返回 ev 才能继续上报
|
|
1697
|
+
});
|
|
1698
|
+
|
|
1699
|
+
this.client.start();
|
|
1700
|
+
this.initialized = true;
|
|
1701
|
+
log$1('Slardar initialized successfully');
|
|
1702
|
+
} catch (error) {
|
|
1703
|
+
log$1('Failed to initialize:', error);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
|
|
1707
|
+
/**
|
|
1708
|
+
* 检查是否已初始化
|
|
1709
|
+
*/
|
|
1710
|
+
ensureInitialized() {
|
|
1711
|
+
if (!this.initialized) {
|
|
1712
|
+
log$1('Not initialized, call setup() first');
|
|
1713
|
+
return false;
|
|
1714
|
+
}
|
|
1715
|
+
return true;
|
|
1716
|
+
}
|
|
1717
|
+
|
|
1718
|
+
/**
|
|
1719
|
+
* 上报自定义事件
|
|
1720
|
+
* @param name 事件名称
|
|
1721
|
+
* @param metrics 指标数据(数值类型)
|
|
1722
|
+
* @param categories 分类维度(字符串类型)
|
|
1723
|
+
*/
|
|
1724
|
+
sendEvent(
|
|
1725
|
+
name,
|
|
1726
|
+
metrics,
|
|
1727
|
+
categories,
|
|
1728
|
+
) {
|
|
1729
|
+
if (!this.ensureInitialized()) {
|
|
1730
|
+
return;
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
try {
|
|
1734
|
+
log$1('Sending event:', { name, metrics, categories });
|
|
1735
|
+
this.client.sendEvent({
|
|
1736
|
+
name,
|
|
1737
|
+
metrics,
|
|
1738
|
+
categories,
|
|
1739
|
+
});
|
|
1740
|
+
} catch (error) {
|
|
1741
|
+
log$1('Failed to send event:', error);
|
|
1742
|
+
}
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
/**
|
|
1746
|
+
* 上报 JS 错误(会显示在 Slardar JS 错误总览页面)
|
|
1747
|
+
* @param error Error 对象
|
|
1748
|
+
* @param extra 额外的上下文信息
|
|
1749
|
+
* @param source 错误来源信息
|
|
1750
|
+
*/
|
|
1751
|
+
reportError(
|
|
1752
|
+
error,
|
|
1753
|
+
extra,
|
|
1754
|
+
source,
|
|
1755
|
+
) {
|
|
1756
|
+
if (!this.ensureInitialized()) {
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
try {
|
|
1761
|
+
const errorEvent = {
|
|
1762
|
+
ev_type: 'js_error' ,
|
|
1763
|
+
payload: {
|
|
1764
|
+
error: {
|
|
1765
|
+
name: error.name,
|
|
1766
|
+
message: error.message,
|
|
1767
|
+
stack: error.stack,
|
|
1768
|
+
},
|
|
1769
|
+
breadcrumbs: [], // CLI 环境暂不收集面包屑
|
|
1770
|
+
extra,
|
|
1771
|
+
source: source || { type: 'manual' }, // 默认为手动捕获
|
|
1772
|
+
},
|
|
1773
|
+
};
|
|
1774
|
+
|
|
1775
|
+
log$1('Reporting JS error:', {
|
|
1776
|
+
name: error.name,
|
|
1777
|
+
message: error.message.slice(0, 100),
|
|
1778
|
+
stack: _optionalChain$6([error, 'access', _2 => _2.stack, 'optionalAccess', _3 => _3.slice, 'call', _4 => _4(0, 200)]),
|
|
1779
|
+
extra,
|
|
1780
|
+
source,
|
|
1781
|
+
});
|
|
1782
|
+
|
|
1783
|
+
// 使用 Slardar 的 js_error 事件类型,这样会显示在 JS 错误总览页面
|
|
1784
|
+
this.client.report(errorEvent);
|
|
1785
|
+
log$1('JS error reported successfully');
|
|
1786
|
+
} catch (err) {
|
|
1787
|
+
log$1('Failed to report error:', err);
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
|
|
1791
|
+
|
|
1792
|
+
/**
|
|
1793
|
+
* 设置全局上下文
|
|
1794
|
+
*/
|
|
1795
|
+
setContext(key, value) {
|
|
1796
|
+
if (!this.ensureInitialized()) {
|
|
1797
|
+
return;
|
|
1798
|
+
}
|
|
1799
|
+
this.client.context.set(key, value);
|
|
1800
|
+
}
|
|
1801
|
+
|
|
1802
|
+
/**
|
|
1803
|
+
* 批量设置全局上下文
|
|
1804
|
+
*/
|
|
1805
|
+
mergeContext(context) {
|
|
1806
|
+
if (!this.ensureInitialized()) {
|
|
1807
|
+
return;
|
|
1808
|
+
}
|
|
1809
|
+
log$1('Merging context:', context);
|
|
1810
|
+
this.client.context.merge(context);
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
/**
|
|
1814
|
+
* 删除全局上下文
|
|
1815
|
+
*/
|
|
1816
|
+
deleteContext(key) {
|
|
1817
|
+
if (!this.ensureInitialized()) {
|
|
1818
|
+
return;
|
|
1819
|
+
}
|
|
1820
|
+
this.client.context.delete(key);
|
|
1821
|
+
}
|
|
1822
|
+
|
|
1823
|
+
/**
|
|
1824
|
+
* 清空全局上下文
|
|
1825
|
+
*/
|
|
1826
|
+
clearContext() {
|
|
1827
|
+
if (!this.ensureInitialized()) {
|
|
1828
|
+
return;
|
|
1829
|
+
}
|
|
1830
|
+
this.client.context.clear();
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
/**
|
|
1834
|
+
* 更新用户配置
|
|
1835
|
+
*/
|
|
1836
|
+
updateConfig(
|
|
1837
|
+
config
|
|
1838
|
+
|
|
1839
|
+
,
|
|
1840
|
+
) {
|
|
1841
|
+
if (!this.ensureInitialized()) {
|
|
1842
|
+
return;
|
|
1843
|
+
}
|
|
1844
|
+
|
|
1845
|
+
try {
|
|
1846
|
+
log$1('Updating config:', config);
|
|
1847
|
+
this.client.config({
|
|
1848
|
+
userId: config.userId,
|
|
1849
|
+
release: config.release,
|
|
1850
|
+
sessionId: config.sessionId,
|
|
1851
|
+
env: config.env,
|
|
1852
|
+
});
|
|
1853
|
+
} catch (error) {
|
|
1854
|
+
log$1('Failed to update config:', error);
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
/**
|
|
1859
|
+
* 立即上报(不等待队列)
|
|
1860
|
+
* @param waitMs 等待时间(毫秒),默认 1500ms,用于确保设置请求完成和事件发送
|
|
1861
|
+
*/
|
|
1862
|
+
async flush(waitMs = 1500) {
|
|
1863
|
+
if (!this.ensureInitialized()) {
|
|
1864
|
+
return;
|
|
1865
|
+
}
|
|
1866
|
+
log$1('Flushing Slardar data...');
|
|
1867
|
+
this.client.getSender().flush();
|
|
1868
|
+
log$1('Waiting %dms for events to be sent...', waitMs);
|
|
1869
|
+
await new Promise(resolve => setTimeout(resolve, waitMs));
|
|
1870
|
+
log$1('Slardar data flushed');
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
/**
|
|
1874
|
+
* 获取原始 Slardar client(用于高级用法)
|
|
1875
|
+
*/
|
|
1876
|
+
getRawClient() {
|
|
1877
|
+
return this.client;
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
/**
|
|
1882
|
+
* 默认导出的单例实例
|
|
1883
|
+
*/
|
|
1884
|
+
const reporter = new SlardarCLIReporter();
|
|
1885
|
+
|
|
1886
|
+
function _optionalChain$5(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; }
|
|
1887
|
+
|
|
1888
|
+
|
|
1889
|
+
|
|
1890
|
+
|
|
1891
|
+
|
|
1892
|
+
/**
|
|
1893
|
+
* CLI 通用事件类型常量
|
|
1894
|
+
* 仅包含通用的 CLI 工具事件,不包含具体业务逻辑
|
|
1895
|
+
*/
|
|
1896
|
+
const CLI_EVENTS = {
|
|
1897
|
+
/** CLI 启动 */
|
|
1898
|
+
CLI_START: 'cli_start',
|
|
1899
|
+
/** CLI 命令执行 */
|
|
1900
|
+
CLI_COMMAND: 'cli_command',
|
|
1901
|
+
/** CLI 命令完成 */
|
|
1902
|
+
CLI_COMMAND_COMPLETE: 'cli_command_complete',
|
|
1903
|
+
/** CLI 错误 */
|
|
1904
|
+
CLI_ERROR: 'cli_error',
|
|
1905
|
+
/** 网络请求 */
|
|
1906
|
+
NETWORK_REQUEST: 'network_request',
|
|
1907
|
+
/** 文件操作 */
|
|
1908
|
+
FILE_OPERATION: 'file_operation',
|
|
1909
|
+
} ;
|
|
1910
|
+
|
|
1911
|
+
|
|
1912
|
+
|
|
1913
|
+
/**
|
|
1914
|
+
* 事件构建器 - 提供更友好的事件创建接口
|
|
1915
|
+
*/
|
|
1916
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
1917
|
+
const EventBuilder = {
|
|
1918
|
+
/**
|
|
1919
|
+
* 构建 CLI 启动事件
|
|
1920
|
+
*/
|
|
1921
|
+
cliStart(metrics) {
|
|
1922
|
+
return {
|
|
1923
|
+
name: CLI_EVENTS.CLI_START,
|
|
1924
|
+
metrics: {
|
|
1925
|
+
duration: 0,
|
|
1926
|
+
...metrics,
|
|
1927
|
+
},
|
|
1928
|
+
};
|
|
1929
|
+
},
|
|
1930
|
+
|
|
1931
|
+
/**
|
|
1932
|
+
* 构建 CLI 命令执行事件
|
|
1933
|
+
*/
|
|
1934
|
+
cliCommand(
|
|
1935
|
+
command,
|
|
1936
|
+
options
|
|
1937
|
+
|
|
1938
|
+
|
|
1939
|
+
|
|
1940
|
+
,
|
|
1941
|
+
) {
|
|
1942
|
+
return {
|
|
1943
|
+
name: CLI_EVENTS.CLI_COMMAND,
|
|
1944
|
+
categories: {
|
|
1945
|
+
command,
|
|
1946
|
+
args: _optionalChain$5([options, 'optionalAccess', _ => _.args]),
|
|
1947
|
+
status: 'running' ,
|
|
1948
|
+
..._optionalChain$5([options, 'optionalAccess', _2 => _2.categories]),
|
|
1949
|
+
},
|
|
1950
|
+
metrics: _optionalChain$5([options, 'optionalAccess', _3 => _3.metrics]),
|
|
1951
|
+
};
|
|
1952
|
+
},
|
|
1953
|
+
|
|
1954
|
+
/**
|
|
1955
|
+
* 构建 CLI 命令完成事件
|
|
1956
|
+
*/
|
|
1957
|
+
cliCommandComplete(
|
|
1958
|
+
command,
|
|
1959
|
+
success,
|
|
1960
|
+
duration,
|
|
1961
|
+
options
|
|
1962
|
+
|
|
1963
|
+
|
|
1964
|
+
|
|
1965
|
+
,
|
|
1966
|
+
) {
|
|
1967
|
+
return {
|
|
1968
|
+
name: CLI_EVENTS.CLI_COMMAND_COMPLETE,
|
|
1969
|
+
categories: {
|
|
1970
|
+
command,
|
|
1971
|
+
args: _optionalChain$5([options, 'optionalAccess', _4 => _4.args]),
|
|
1972
|
+
status: success ? ('success' ) : ('fail' ),
|
|
1973
|
+
..._optionalChain$5([options, 'optionalAccess', _5 => _5.categories]),
|
|
1974
|
+
},
|
|
1975
|
+
metrics: {
|
|
1976
|
+
duration,
|
|
1977
|
+
...(_optionalChain$5([options, 'optionalAccess', _6 => _6.errorCode]) && { errorCode: options.errorCode }),
|
|
1978
|
+
},
|
|
1979
|
+
};
|
|
1980
|
+
},
|
|
1981
|
+
|
|
1982
|
+
/**
|
|
1983
|
+
* 构建网络请求事件
|
|
1984
|
+
*/
|
|
1985
|
+
networkRequest(
|
|
1986
|
+
url,
|
|
1987
|
+
options
|
|
1988
|
+
|
|
1989
|
+
|
|
1990
|
+
|
|
1991
|
+
|
|
1992
|
+
,
|
|
1993
|
+
) {
|
|
1994
|
+
return {
|
|
1995
|
+
name: CLI_EVENTS.NETWORK_REQUEST,
|
|
1996
|
+
categories: {
|
|
1997
|
+
url,
|
|
1998
|
+
method: _optionalChain$5([options, 'optionalAccess', _7 => _7.method]) || 'GET',
|
|
1999
|
+
statusCode: _optionalChain$5([options, 'optionalAccess', _8 => _8.statusCode, 'optionalAccess', _9 => _9.toString, 'call', _10 => _10()]),
|
|
2000
|
+
status: _optionalChain$5([options, 'optionalAccess', _11 => _11.success]) ? ('success' ) : ('fail' ),
|
|
2001
|
+
},
|
|
2002
|
+
metrics: {
|
|
2003
|
+
duration: _optionalChain$5([options, 'optionalAccess', _12 => _12.duration]),
|
|
2004
|
+
},
|
|
2005
|
+
};
|
|
2006
|
+
},
|
|
2007
|
+
|
|
2008
|
+
/**
|
|
2009
|
+
* 构建文件操作事件
|
|
2010
|
+
*/
|
|
2011
|
+
fileOperation(
|
|
2012
|
+
operation,
|
|
2013
|
+
filePath,
|
|
2014
|
+
options
|
|
2015
|
+
|
|
2016
|
+
|
|
2017
|
+
|
|
2018
|
+
,
|
|
2019
|
+
) {
|
|
2020
|
+
return {
|
|
2021
|
+
name: CLI_EVENTS.FILE_OPERATION,
|
|
2022
|
+
categories: {
|
|
2023
|
+
operation,
|
|
2024
|
+
filePath,
|
|
2025
|
+
status: _optionalChain$5([options, 'optionalAccess', _13 => _13.success]) ? ('success' ) : ('fail' ),
|
|
2026
|
+
},
|
|
2027
|
+
metrics: {
|
|
2028
|
+
duration: _optionalChain$5([options, 'optionalAccess', _14 => _14.duration]),
|
|
2029
|
+
fileSize: _optionalChain$5([options, 'optionalAccess', _15 => _15.fileSize]),
|
|
2030
|
+
},
|
|
2031
|
+
};
|
|
2032
|
+
},
|
|
2033
|
+
|
|
2034
|
+
/**
|
|
2035
|
+
* 构建错误事件
|
|
2036
|
+
*/
|
|
2037
|
+
cliError(
|
|
2038
|
+
error,
|
|
2039
|
+
context,
|
|
2040
|
+
) {
|
|
2041
|
+
return {
|
|
2042
|
+
name: CLI_EVENTS.CLI_ERROR,
|
|
2043
|
+
categories: {
|
|
2044
|
+
errorName: error.name,
|
|
2045
|
+
errorMessage: error.message,
|
|
2046
|
+
stack: error.stack || '',
|
|
2047
|
+
...context,
|
|
2048
|
+
},
|
|
2049
|
+
metrics: {
|
|
2050
|
+
errorCode: 1,
|
|
2051
|
+
},
|
|
2052
|
+
};
|
|
2053
|
+
},
|
|
2054
|
+
};
|
|
2055
|
+
|
|
2056
|
+
var name = "@coze-arch/cli";
|
|
2057
|
+
var version = "0.0.1-alpha.c199b3";
|
|
2058
|
+
var description = "coze coding devtools cli";
|
|
2059
|
+
var license = "MIT";
|
|
2060
|
+
var author = "fanwenjie.fe@bytedance.com";
|
|
2061
|
+
var maintainers = [
|
|
2062
|
+
];
|
|
2063
|
+
var bin = {
|
|
2064
|
+
coze: "bin/main"
|
|
2065
|
+
};
|
|
2066
|
+
var files = [
|
|
2067
|
+
"lib",
|
|
2068
|
+
"bin",
|
|
2069
|
+
"lib/**/.npmrc",
|
|
2070
|
+
"!**/*.tsbuildinfo",
|
|
2071
|
+
"!**/*.map"
|
|
2072
|
+
];
|
|
2073
|
+
var scripts = {
|
|
2074
|
+
prebuild: "tsx scripts/prebuild.ts",
|
|
2075
|
+
build: "tsx scripts/build.ts",
|
|
2076
|
+
create: "tsx scripts/create-template.ts",
|
|
2077
|
+
lint: "eslint ./ --cache",
|
|
2078
|
+
postpublish: "bash scripts/sync-npmmirror.sh",
|
|
2079
|
+
test: "vitest --run --passWithNoTests",
|
|
2080
|
+
"test:all": "bash scripts/test-coverage.sh",
|
|
2081
|
+
"test:cov": "vitest --run --passWithNoTests --coverage",
|
|
2082
|
+
"test:e2e": "NODE_ENV=test bash scripts/e2e.sh",
|
|
2083
|
+
"test:perf": "vitest bench --run --config vitest.perf.config.ts",
|
|
2084
|
+
"test:perf:compare": "bash scripts/compare-perf.sh",
|
|
2085
|
+
"test:perf:save": "bash scripts/run-perf-with-output.sh"
|
|
2086
|
+
};
|
|
2087
|
+
var dependencies = {
|
|
2088
|
+
"@iarna/toml": "^2.2.5",
|
|
2089
|
+
ajv: "^8.17.1",
|
|
2090
|
+
"ajv-formats": "^3.0.1",
|
|
2091
|
+
"change-case": "^5.4.4",
|
|
2092
|
+
commander: "~12.1.0",
|
|
2093
|
+
debug: "^4.3.7",
|
|
2094
|
+
ejs: "^3.1.10",
|
|
2095
|
+
"js-yaml": "^4.1.0",
|
|
2096
|
+
minimist: "^1.2.5",
|
|
2097
|
+
shelljs: "^0.10.0"
|
|
2098
|
+
};
|
|
2099
|
+
var devDependencies = {
|
|
2100
|
+
"@coze-arch/cli-logger": "workspace:*",
|
|
2101
|
+
"@coze-arch/cli-slardar": "workspace:*",
|
|
2102
|
+
"@coze-arch/eslint-config": "workspace:*",
|
|
2103
|
+
"@coze-arch/monorepo-kits": "workspace:*",
|
|
2104
|
+
"@coze-arch/rollup-config": "workspace:*",
|
|
2105
|
+
"@coze-arch/ts-config": "workspace:*",
|
|
2106
|
+
"@coze-arch/vitest-config": "workspace:*",
|
|
2107
|
+
"@coze-coding/lambda": "workspace:*",
|
|
2108
|
+
"@inquirer/prompts": "^3.2.0",
|
|
2109
|
+
"@playwright/test": "~1.55.0",
|
|
2110
|
+
"@types/debug": "^4.1.12",
|
|
2111
|
+
"@types/ejs": "^3.1.5",
|
|
2112
|
+
"@types/iarna__toml": "^2.0.5",
|
|
2113
|
+
"@types/js-yaml": "^4.0.9",
|
|
2114
|
+
"@types/minimatch": "^5.1.2",
|
|
2115
|
+
"@types/minimist": "^1.2.5",
|
|
2116
|
+
"@types/node": "^24",
|
|
2117
|
+
"@types/shelljs": "^0.10.0",
|
|
2118
|
+
"@vitest/coverage-v8": "~4.0.18",
|
|
2119
|
+
"json-schema-to-typescript": "^15.0.3",
|
|
2120
|
+
minimatch: "^10.0.1",
|
|
2121
|
+
playwright: "~1.55.0",
|
|
2122
|
+
rollup: "^4.41.1",
|
|
2123
|
+
sucrase: "^3.35.0",
|
|
2124
|
+
"tree-kill": "^1.2.2",
|
|
2125
|
+
tsx: "^4.20.6",
|
|
2126
|
+
"vite-tsconfig-paths": "^4.2.1",
|
|
2127
|
+
vitest: "~4.0.18"
|
|
2128
|
+
};
|
|
2129
|
+
var publishConfig = {
|
|
2130
|
+
access: "public",
|
|
2131
|
+
registry: "https://registry.npmjs.org"
|
|
2132
|
+
};
|
|
2133
|
+
var cozePublishConfig = {
|
|
2134
|
+
bin: {
|
|
2135
|
+
coze: "bin/main"
|
|
2136
|
+
}
|
|
2137
|
+
};
|
|
2138
|
+
var packageJson = {
|
|
2139
|
+
name: name,
|
|
2140
|
+
version: version,
|
|
2141
|
+
"private": false,
|
|
2142
|
+
description: description,
|
|
2143
|
+
license: license,
|
|
2144
|
+
author: author,
|
|
2145
|
+
maintainers: maintainers,
|
|
2146
|
+
bin: bin,
|
|
2147
|
+
files: files,
|
|
2148
|
+
scripts: scripts,
|
|
2149
|
+
dependencies: dependencies,
|
|
2150
|
+
devDependencies: devDependencies,
|
|
2151
|
+
publishConfig: publishConfig,
|
|
2152
|
+
cozePublishConfig: cozePublishConfig
|
|
2153
|
+
};
|
|
2154
|
+
|
|
2155
|
+
function _optionalChain$4(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; }/**
|
|
2156
|
+
* Slardar 监控初始化和上报
|
|
2157
|
+
*/
|
|
2158
|
+
|
|
2159
|
+
const log = debug('slardar:cli');
|
|
2160
|
+
|
|
2161
|
+
/**
|
|
2162
|
+
* 安全执行函数包装器
|
|
2163
|
+
* 捕获并静默处理所有错误,确保 Slardar 上报失败不影响 CLI 正常运行
|
|
2164
|
+
* 支持同步和异步函数
|
|
2165
|
+
*/
|
|
2166
|
+
function safeRun(
|
|
2167
|
+
name,
|
|
2168
|
+
fn,
|
|
2169
|
+
) {
|
|
2170
|
+
return ((...args) => {
|
|
2171
|
+
try {
|
|
2172
|
+
log('Calling Slardar function: %s', name);
|
|
2173
|
+
const result = fn(...args);
|
|
2174
|
+
|
|
2175
|
+
// 如果是 Promise,处理异步错误
|
|
2176
|
+
if (result instanceof Promise) {
|
|
2177
|
+
return result
|
|
2178
|
+
.then(res => {
|
|
2179
|
+
log('Slardar function %s completed', name);
|
|
2180
|
+
return res;
|
|
2181
|
+
})
|
|
2182
|
+
.catch(error => {
|
|
2183
|
+
log(
|
|
2184
|
+
'Slardar function %s failed: %s',
|
|
2185
|
+
name,
|
|
2186
|
+
(error ).message,
|
|
2187
|
+
);
|
|
2188
|
+
void error;
|
|
2189
|
+
});
|
|
2190
|
+
}
|
|
2191
|
+
|
|
2192
|
+
log('Slardar function %s completed', name);
|
|
2193
|
+
return result;
|
|
2194
|
+
} catch (error) {
|
|
2195
|
+
// Slardar 上报失败不应影响 CLI 正常运行,但要记录错误
|
|
2196
|
+
log('Slardar function %s failed: %s', name, (error ).message);
|
|
2197
|
+
}
|
|
2198
|
+
}) ;
|
|
2199
|
+
}
|
|
2200
|
+
|
|
2201
|
+
/**
|
|
2202
|
+
* 初始化 Slardar Reporter
|
|
2203
|
+
*/
|
|
2204
|
+
const initSlardar = safeRun('initSlardar', () => {
|
|
2205
|
+
reporter.setup({
|
|
2206
|
+
bid: 'coze_codign_cli',
|
|
2207
|
+
release: packageJson.version,
|
|
2208
|
+
env: process.env.NODE_ENV || 'production',
|
|
2209
|
+
userId: process.env.USER,
|
|
2210
|
+
projectId: process.env.COZE_PROJECT_ID || '',
|
|
2211
|
+
useLocalConfig: false, // 启用服务端采样率配置
|
|
2212
|
+
domain: 'mon.zijieapi.com', // Node.js 环境上报域名
|
|
2213
|
+
});
|
|
2214
|
+
|
|
2215
|
+
// 设置全局上下文
|
|
2216
|
+
reporter.mergeContext({
|
|
2217
|
+
platform: process.platform,
|
|
2218
|
+
nodeVersion: process.version,
|
|
2219
|
+
cliVersion: packageJson.version,
|
|
2220
|
+
});
|
|
2221
|
+
});
|
|
2222
|
+
|
|
2223
|
+
/**
|
|
2224
|
+
* 上报命令执行
|
|
88
2225
|
*/
|
|
89
|
-
const
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
2226
|
+
const reportCommandStart = safeRun(
|
|
2227
|
+
'reportCommandStart',
|
|
2228
|
+
(
|
|
2229
|
+
command,
|
|
2230
|
+
args,
|
|
2231
|
+
extraCategories,
|
|
2232
|
+
) => {
|
|
2233
|
+
const event = EventBuilder.cliCommand(command, {
|
|
2234
|
+
args,
|
|
2235
|
+
categories: extraCategories,
|
|
2236
|
+
});
|
|
2237
|
+
reporter.sendEvent(event.name, event.metrics, event.categories);
|
|
2238
|
+
},
|
|
2239
|
+
);
|
|
95
2240
|
|
|
96
|
-
|
|
2241
|
+
/**
|
|
2242
|
+
* 上报命令完成
|
|
2243
|
+
*/
|
|
2244
|
+
const reportCommandComplete = safeRun(
|
|
2245
|
+
'reportCommandComplete',
|
|
2246
|
+
(
|
|
2247
|
+
command,
|
|
2248
|
+
success,
|
|
2249
|
+
duration,
|
|
2250
|
+
options
|
|
97
2251
|
|
|
98
|
-
lines.push('\n📦 Available Templates:\n');
|
|
99
|
-
lines.push(` ${'='.repeat(76)}`);
|
|
100
2252
|
|
|
101
|
-
config.templates.forEach((template, index) => {
|
|
102
|
-
if (index > 0) {
|
|
103
|
-
lines.push(` ${'-'.repeat(76)}`);
|
|
104
|
-
}
|
|
105
2253
|
|
|
106
|
-
lines.push(`\n Template: ${template.name}`);
|
|
107
|
-
lines.push(` Description: ${template.description}`);
|
|
108
2254
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
2255
|
+
,
|
|
2256
|
+
) => {
|
|
2257
|
+
const event = EventBuilder.cliCommandComplete(command, success, duration, {
|
|
2258
|
+
args: _optionalChain$4([options, 'optionalAccess', _ => _.args]),
|
|
2259
|
+
errorCode: _optionalChain$4([options, 'optionalAccess', _2 => _2.errorCode]),
|
|
2260
|
+
categories: {
|
|
2261
|
+
...(_optionalChain$4([options, 'optionalAccess', _3 => _3.errorMessage]) && { errorMessage: options.errorMessage }),
|
|
2262
|
+
..._optionalChain$4([options, 'optionalAccess', _4 => _4.categories]),
|
|
2263
|
+
},
|
|
2264
|
+
});
|
|
2265
|
+
reporter.sendEvent(event.name, event.metrics, event.categories);
|
|
2266
|
+
},
|
|
2267
|
+
);
|
|
117
2268
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
2269
|
+
/**
|
|
2270
|
+
* 上报错误(使用 JS 错误上报,会显示在 Slardar JS 错误总览页面)
|
|
2271
|
+
*/
|
|
2272
|
+
const reportError = safeRun(
|
|
2273
|
+
'reportError',
|
|
2274
|
+
(error, context) => {
|
|
2275
|
+
reporter.reportError(error, context, {
|
|
2276
|
+
type: 'cli',
|
|
2277
|
+
data: context,
|
|
2278
|
+
});
|
|
2279
|
+
},
|
|
2280
|
+
);
|
|
126
2281
|
|
|
127
|
-
|
|
128
|
-
|
|
2282
|
+
/**
|
|
2283
|
+
* 立即上报(在 CLI 退出前调用)
|
|
2284
|
+
* 等待 500ms 确保设置请求完成和事件发送
|
|
2285
|
+
*/
|
|
2286
|
+
const flushSlardar = safeRun('flushSlardar', async () => {
|
|
2287
|
+
await reporter.flush();
|
|
2288
|
+
});
|
|
129
2289
|
|
|
130
2290
|
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) {
|
|
131
2291
|
const ERROR = 0; LogLevel[LogLevel["ERROR"] = ERROR] = "ERROR";
|
|
@@ -439,159 +2599,506 @@ const findTemplate = (
|
|
|
439
2599
|
);
|
|
440
2600
|
}
|
|
441
2601
|
|
|
442
|
-
return template;
|
|
2602
|
+
return template;
|
|
2603
|
+
};
|
|
2604
|
+
|
|
2605
|
+
/**
|
|
2606
|
+
* 获取模板的完整路径
|
|
2607
|
+
*
|
|
2608
|
+
* @param basePath - 模板目录(templates.json 所在目录)
|
|
2609
|
+
* @param templateMetadata - 模板元信息
|
|
2610
|
+
* @returns 模板完整路径
|
|
2611
|
+
*/
|
|
2612
|
+
const getTemplatePath = async (
|
|
2613
|
+
basePath,
|
|
2614
|
+
templateMetadata,
|
|
2615
|
+
) => {
|
|
2616
|
+
logger.verbose('Resolving template path:');
|
|
2617
|
+
logger.verbose(` - Base path: ${basePath}`);
|
|
2618
|
+
logger.verbose(` - Template location: ${templateMetadata.location}`);
|
|
2619
|
+
|
|
2620
|
+
// location 是相对于 templates.json 文件的路径
|
|
2621
|
+
const templatePath = path.join(basePath, templateMetadata.location);
|
|
2622
|
+
|
|
2623
|
+
logger.verbose(` - Resolved path: ${templatePath}`);
|
|
2624
|
+
|
|
2625
|
+
try {
|
|
2626
|
+
await fs$1.access(templatePath);
|
|
2627
|
+
logger.verbose(' - Template directory exists: ✓');
|
|
2628
|
+
// eslint-disable-next-line @coze-arch/use-error-in-catch -- Error handling done in throw statement
|
|
2629
|
+
} catch (e) {
|
|
2630
|
+
logger.error(' - Template directory does not exist: ✗');
|
|
2631
|
+
throw new Error(`Template directory not found: ${templatePath}`);
|
|
2632
|
+
}
|
|
2633
|
+
|
|
2634
|
+
return templatePath;
|
|
2635
|
+
};
|
|
2636
|
+
|
|
2637
|
+
/**
|
|
2638
|
+
* 对单个模板执行 pnpm install
|
|
2639
|
+
*/
|
|
2640
|
+
const warmupTemplate = (templatePath, templateName) => {
|
|
2641
|
+
logger.info(`\nWarming up template: ${templateName}`);
|
|
2642
|
+
logger.info(` Path: ${templatePath}`);
|
|
2643
|
+
|
|
2644
|
+
// 检查是否存在 package.json
|
|
2645
|
+
const packageJsonPath = node_path.join(templatePath, 'package.json');
|
|
2646
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename
|
|
2647
|
+
if (!node_fs.existsSync(packageJsonPath)) {
|
|
2648
|
+
logger.info(` ⊘ Skipping ${templateName} (no package.json found)`);
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
const result = shelljs.exec('pnpm install', {
|
|
2653
|
+
cwd: templatePath,
|
|
2654
|
+
silent: true,
|
|
2655
|
+
});
|
|
2656
|
+
|
|
2657
|
+
// 输出 stdout
|
|
2658
|
+
if (result.stdout) {
|
|
2659
|
+
process.stdout.write(result.stdout);
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
// 输出 stderr
|
|
2663
|
+
if (result.stderr) {
|
|
2664
|
+
process.stderr.write(result.stderr);
|
|
2665
|
+
}
|
|
2666
|
+
|
|
2667
|
+
if (result.code === 0) {
|
|
2668
|
+
logger.success(` ✓ ${templateName} warmed up successfully`);
|
|
2669
|
+
} else {
|
|
2670
|
+
const errorMessage = [
|
|
2671
|
+
`pnpm install failed for ${templateName} with exit code ${result.code}`,
|
|
2672
|
+
result.stderr ? `\nStderr:\n${result.stderr}` : '',
|
|
2673
|
+
result.stdout ? `\nStdout:\n${result.stdout}` : '',
|
|
2674
|
+
]
|
|
2675
|
+
.filter(Boolean)
|
|
2676
|
+
.join('');
|
|
2677
|
+
|
|
2678
|
+
throw new Error(errorMessage);
|
|
2679
|
+
}
|
|
2680
|
+
};
|
|
2681
|
+
|
|
2682
|
+
/**
|
|
2683
|
+
* 执行 warmup 命令的内部实现
|
|
2684
|
+
*/
|
|
2685
|
+
const executeWarmup = async (options) => {
|
|
2686
|
+
const timer = new TimeTracker();
|
|
2687
|
+
|
|
2688
|
+
try {
|
|
2689
|
+
const { template: templateFilter } = options;
|
|
2690
|
+
|
|
2691
|
+
logger.info('Starting template warmup...');
|
|
2692
|
+
timer.logPhase('Initialization');
|
|
2693
|
+
|
|
2694
|
+
// 加载模板配置
|
|
2695
|
+
const configPath = getTemplatesConfigPath();
|
|
2696
|
+
const templatesConfig = await loadTemplatesConfig(configPath);
|
|
2697
|
+
|
|
2698
|
+
logger.verbose(
|
|
2699
|
+
`Found ${templatesConfig.templates.length} templates in config`,
|
|
2700
|
+
);
|
|
2701
|
+
|
|
2702
|
+
// 过滤模板
|
|
2703
|
+
const templatesToWarmup = templateFilter
|
|
2704
|
+
? templatesConfig.templates.filter(t => t.name === templateFilter)
|
|
2705
|
+
: templatesConfig.templates;
|
|
2706
|
+
|
|
2707
|
+
if (templatesToWarmup.length === 0) {
|
|
2708
|
+
if (templateFilter) {
|
|
2709
|
+
logger.warn(`Template "${templateFilter}" not found`);
|
|
2710
|
+
logger.info(
|
|
2711
|
+
`Available templates: ${templatesConfig.templates.map(t => t.name).join(', ')}`,
|
|
2712
|
+
);
|
|
2713
|
+
} else {
|
|
2714
|
+
logger.warn('No templates found');
|
|
2715
|
+
}
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2718
|
+
|
|
2719
|
+
logger.info(
|
|
2720
|
+
`\nWill warm up ${templatesToWarmup.length} template(s): ${templatesToWarmup.map(t => t.name).join(', ')}`,
|
|
2721
|
+
);
|
|
2722
|
+
|
|
2723
|
+
// 获取模板基础路径
|
|
2724
|
+
const basePath = configPath.replace(/\/templates\.json$/, '');
|
|
2725
|
+
|
|
2726
|
+
// 对每个模板执行 pnpm install
|
|
2727
|
+
for (const templateMetadata of templatesToWarmup) {
|
|
2728
|
+
const templatePath = await getTemplatePath(basePath, templateMetadata);
|
|
2729
|
+
warmupTemplate(templatePath, templateMetadata.name);
|
|
2730
|
+
timer.logPhase(`Warmup ${templateMetadata.name}`);
|
|
2731
|
+
}
|
|
2732
|
+
|
|
2733
|
+
logger.success('\n✅ All templates warmed up successfully!');
|
|
2734
|
+
logger.info(
|
|
2735
|
+
'\nNext time you run `coze init`, it will be much faster as node_modules are pre-installed.',
|
|
2736
|
+
);
|
|
2737
|
+
|
|
2738
|
+
timer.logTotal();
|
|
2739
|
+
} catch (error) {
|
|
2740
|
+
timer.logTotal();
|
|
2741
|
+
logger.error('Failed to warmup templates:');
|
|
2742
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
2743
|
+
process.exit(1);
|
|
2744
|
+
}
|
|
2745
|
+
};
|
|
2746
|
+
|
|
2747
|
+
/**
|
|
2748
|
+
* 注册 warmup 命令到 program
|
|
2749
|
+
*/
|
|
2750
|
+
const registerCommand$4 = program => {
|
|
2751
|
+
program
|
|
2752
|
+
.command('warmup')
|
|
2753
|
+
.description('Pre-install dependencies for templates to speed up init')
|
|
2754
|
+
.option('-t, --template <name>', 'Warmup a specific template only')
|
|
2755
|
+
.action(async options => {
|
|
2756
|
+
await executeWarmup(options);
|
|
2757
|
+
});
|
|
2758
|
+
};
|
|
2759
|
+
|
|
2760
|
+
// ABOUTME: This file implements the update command for coze CLI
|
|
2761
|
+
// ABOUTME: It wraps pnpm update/install to update package dependencies with logging support
|
|
2762
|
+
|
|
2763
|
+
|
|
2764
|
+
|
|
2765
|
+
/**
|
|
2766
|
+
* 日志文件名常量
|
|
2767
|
+
*/
|
|
2768
|
+
const LOG_FILE_NAME$1 = 'update.log';
|
|
2769
|
+
|
|
2770
|
+
/**
|
|
2771
|
+
* 获取日志目录
|
|
2772
|
+
* 优先使用环境变量 COZE_LOG_DIR,否则使用 ~/.coze-logs
|
|
2773
|
+
*/
|
|
2774
|
+
const getLogDir$1 = () =>
|
|
2775
|
+
process.env.COZE_LOG_DIR || path.join(os.homedir(), '.coze-logs');
|
|
2776
|
+
|
|
2777
|
+
/**
|
|
2778
|
+
* 解析日志文件路径
|
|
2779
|
+
* - 如果是绝对路径,直接使用
|
|
2780
|
+
* - 如果是相对路径,基于 getLogDir() + 相对路径
|
|
2781
|
+
* - 如果为空,使用 getLogDir() + LOG_FILE_NAME
|
|
2782
|
+
*/
|
|
2783
|
+
const resolveLogFilePath$1 = (logFile) => {
|
|
2784
|
+
if (!logFile) {
|
|
2785
|
+
return path.join(getLogDir$1(), LOG_FILE_NAME$1);
|
|
2786
|
+
}
|
|
2787
|
+
|
|
2788
|
+
if (path.isAbsolute(logFile)) {
|
|
2789
|
+
return logFile;
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2792
|
+
return path.join(getLogDir$1(), logFile);
|
|
2793
|
+
};
|
|
2794
|
+
|
|
2795
|
+
/**
|
|
2796
|
+
* 创建日志写入流
|
|
2797
|
+
*/
|
|
2798
|
+
const createLogStream$1 = (logFilePath) => {
|
|
2799
|
+
const logDir = path.dirname(logFilePath);
|
|
2800
|
+
|
|
2801
|
+
// 确保日志目录存在
|
|
2802
|
+
if (!fs.existsSync(logDir)) {
|
|
2803
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
2804
|
+
}
|
|
2805
|
+
|
|
2806
|
+
// 使用 'w' 标志覆盖之前的日志
|
|
2807
|
+
return fs.createWriteStream(logFilePath, { flags: 'w' });
|
|
2808
|
+
};
|
|
2809
|
+
|
|
2810
|
+
/**
|
|
2811
|
+
* 格式化时间戳
|
|
2812
|
+
*/
|
|
2813
|
+
const formatTimestamp = () => {
|
|
2814
|
+
const now = new Date();
|
|
2815
|
+
return now.toISOString();
|
|
2816
|
+
};
|
|
2817
|
+
|
|
2818
|
+
/**
|
|
2819
|
+
* 写入带时间戳的日志
|
|
2820
|
+
*/
|
|
2821
|
+
const writeLogWithTimestamp = (stream, message) => {
|
|
2822
|
+
const timestamp = formatTimestamp();
|
|
2823
|
+
const lines = message.split('\n');
|
|
2824
|
+
lines.forEach(line => {
|
|
2825
|
+
if (line) {
|
|
2826
|
+
stream.write(`[${timestamp}] ${line}\n`);
|
|
2827
|
+
} else {
|
|
2828
|
+
stream.write('\n');
|
|
2829
|
+
}
|
|
2830
|
+
});
|
|
2831
|
+
// 确保数据写入磁盘
|
|
2832
|
+
stream.uncork();
|
|
2833
|
+
};
|
|
2834
|
+
|
|
2835
|
+
/**
|
|
2836
|
+
* 同时输出到控制台和日志文件
|
|
2837
|
+
*/
|
|
2838
|
+
const logWithFile = (
|
|
2839
|
+
stream,
|
|
2840
|
+
level,
|
|
2841
|
+
message,
|
|
2842
|
+
) => {
|
|
2843
|
+
// 输出到控制台
|
|
2844
|
+
switch (level) {
|
|
2845
|
+
case 'info':
|
|
2846
|
+
logger.info(message);
|
|
2847
|
+
break;
|
|
2848
|
+
case 'success':
|
|
2849
|
+
logger.success(message);
|
|
2850
|
+
break;
|
|
2851
|
+
case 'error':
|
|
2852
|
+
logger.error(message);
|
|
2853
|
+
break;
|
|
2854
|
+
default:
|
|
2855
|
+
logger.info(message);
|
|
2856
|
+
break;
|
|
2857
|
+
}
|
|
2858
|
+
|
|
2859
|
+
// 写入日志文件(带时间戳)
|
|
2860
|
+
writeLogWithTimestamp(stream, `[${level.toUpperCase()}] ${message}`);
|
|
443
2861
|
};
|
|
444
2862
|
|
|
2863
|
+
// start_aigc
|
|
445
2864
|
/**
|
|
446
|
-
*
|
|
447
|
-
*
|
|
448
|
-
* @param basePath - 模板目录(templates.json 所在目录)
|
|
449
|
-
* @param templateMetadata - 模板元信息
|
|
450
|
-
* @returns 模板完整路径
|
|
2865
|
+
* 构建 pnpm add 命令
|
|
451
2866
|
*/
|
|
452
|
-
const
|
|
453
|
-
|
|
454
|
-
|
|
2867
|
+
const buildPnpmCommand = (
|
|
2868
|
+
packageName,
|
|
2869
|
+
options
|
|
2870
|
+
|
|
2871
|
+
|
|
2872
|
+
|
|
2873
|
+
|
|
2874
|
+
,
|
|
455
2875
|
) => {
|
|
456
|
-
|
|
457
|
-
logger.verbose(` - Base path: ${basePath}`);
|
|
458
|
-
logger.verbose(` - Template location: ${templateMetadata.location}`);
|
|
2876
|
+
const { global, version, registry, extraArgs } = options;
|
|
459
2877
|
|
|
460
|
-
|
|
461
|
-
const templatePath = path.join(basePath, templateMetadata.location);
|
|
2878
|
+
const parts = ['pnpm', 'add'];
|
|
462
2879
|
|
|
463
|
-
|
|
2880
|
+
// 添加全局标记
|
|
2881
|
+
if (global) {
|
|
2882
|
+
parts.push('-g');
|
|
2883
|
+
}
|
|
464
2884
|
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
logger.error(' - Template directory does not exist: ✗');
|
|
471
|
-
throw new Error(`Template directory not found: ${templatePath}`);
|
|
2885
|
+
// 添加包名和版本
|
|
2886
|
+
if (version && version !== 'latest') {
|
|
2887
|
+
parts.push(`${packageName}@${version}`);
|
|
2888
|
+
} else {
|
|
2889
|
+
parts.push(`${packageName}@latest`);
|
|
472
2890
|
}
|
|
473
2891
|
|
|
474
|
-
|
|
2892
|
+
// 添加 registry
|
|
2893
|
+
if (registry) {
|
|
2894
|
+
parts.push(`--registry=${registry}`);
|
|
2895
|
+
}
|
|
2896
|
+
|
|
2897
|
+
// 添加额外参数
|
|
2898
|
+
if (extraArgs.length > 0) {
|
|
2899
|
+
parts.push(...extraArgs);
|
|
2900
|
+
}
|
|
2901
|
+
|
|
2902
|
+
return parts.join(' ');
|
|
475
2903
|
};
|
|
2904
|
+
// end_aigc
|
|
476
2905
|
|
|
477
2906
|
/**
|
|
478
|
-
*
|
|
2907
|
+
* 处理更新失败的错误
|
|
479
2908
|
*/
|
|
480
|
-
const
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
2909
|
+
const handleUpdateError = (
|
|
2910
|
+
error,
|
|
2911
|
+
packageName,
|
|
2912
|
+
options,
|
|
2913
|
+
cmdStartTime,
|
|
2914
|
+
logStream,
|
|
2915
|
+
) => {
|
|
2916
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
2917
|
+
logger.error('Failed to update package:');
|
|
2918
|
+
logger.error(err.message);
|
|
2919
|
+
|
|
2920
|
+
// 上报错误
|
|
2921
|
+
reportError(err, {
|
|
2922
|
+
command: 'update',
|
|
2923
|
+
packageName,
|
|
2924
|
+
type: 'execution_error',
|
|
2925
|
+
});
|
|
2926
|
+
reportCommandComplete('update', false, Date.now() - cmdStartTime, {
|
|
2927
|
+
args: JSON.stringify({ packageName, ...options }),
|
|
2928
|
+
errorCode: 1,
|
|
2929
|
+
errorMessage: err.message,
|
|
487
2930
|
});
|
|
488
2931
|
|
|
489
|
-
//
|
|
490
|
-
if (
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
if (result.code === 0) {
|
|
500
|
-
logger.success(` ✓ ${templateName} warmed up successfully`);
|
|
2932
|
+
// 写入错误到日志文件
|
|
2933
|
+
if (logStream) {
|
|
2934
|
+
writeLogWithTimestamp(logStream, `[ERROR] ${err.message}`);
|
|
2935
|
+
// 等待流关闭后再退出
|
|
2936
|
+
logStream.end(() => {
|
|
2937
|
+
flushSlardar().then(() => {
|
|
2938
|
+
process.exit(1);
|
|
2939
|
+
});
|
|
2940
|
+
});
|
|
501
2941
|
} else {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
result.stdout ? `\nStdout:\n${result.stdout}` : '',
|
|
506
|
-
]
|
|
507
|
-
.filter(Boolean)
|
|
508
|
-
.join('');
|
|
509
|
-
|
|
510
|
-
throw new Error(errorMessage);
|
|
2942
|
+
flushSlardar().then(() => {
|
|
2943
|
+
process.exit(1);
|
|
2944
|
+
});
|
|
511
2945
|
}
|
|
512
2946
|
};
|
|
513
2947
|
|
|
2948
|
+
// start_aigc
|
|
514
2949
|
/**
|
|
515
|
-
* 执行
|
|
2950
|
+
* 执行 update 命令的内部实现
|
|
516
2951
|
*/
|
|
517
|
-
const
|
|
2952
|
+
const executeUpdate = (
|
|
2953
|
+
packageName,
|
|
518
2954
|
options
|
|
519
2955
|
|
|
520
|
-
,
|
|
521
2956
|
|
|
522
|
-
|
|
2957
|
+
|
|
2958
|
+
|
|
2959
|
+
|
|
2960
|
+
|
|
2961
|
+
,
|
|
523
2962
|
) => {
|
|
524
|
-
const
|
|
2963
|
+
const cmdStartTime = Date.now();
|
|
2964
|
+
let logStream = null;
|
|
525
2965
|
|
|
526
2966
|
try {
|
|
527
|
-
const {
|
|
2967
|
+
const { global, cwd, version, registry, logFile, extraArgs } = options;
|
|
528
2968
|
|
|
529
|
-
|
|
530
|
-
|
|
2969
|
+
// 上报命令开始
|
|
2970
|
+
reportCommandStart('update', JSON.stringify({ packageName, ...options }));
|
|
531
2971
|
|
|
532
|
-
//
|
|
533
|
-
const
|
|
534
|
-
const templatesConfig = await loadTemplatesConfig(configPath);
|
|
2972
|
+
// 准备日志
|
|
2973
|
+
const logFilePath = resolveLogFilePath$1(logFile);
|
|
535
2974
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
);
|
|
2975
|
+
// 调试:确认日志路径
|
|
2976
|
+
logger.info(`Log file path resolved to: ${logFilePath}`);
|
|
539
2977
|
|
|
540
|
-
|
|
541
|
-
const templatesToWarmup = templateFilter
|
|
542
|
-
? templatesConfig.templates.filter(t => t.name === templateFilter)
|
|
543
|
-
: templatesConfig.templates;
|
|
2978
|
+
logStream = createLogStream$1(logFilePath);
|
|
544
2979
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
logger.warn(`Template "${templateFilter}" not found`);
|
|
548
|
-
logger.info(
|
|
549
|
-
`Available templates: ${templatesConfig.templates.map(t => t.name).join(', ')}`,
|
|
550
|
-
);
|
|
551
|
-
} else {
|
|
552
|
-
logger.warn('No templates found');
|
|
553
|
-
}
|
|
554
|
-
return;
|
|
555
|
-
}
|
|
2980
|
+
// 调试:确认流已创建
|
|
2981
|
+
logger.info('Log stream created successfully');
|
|
556
2982
|
|
|
557
|
-
|
|
558
|
-
`\nWill warm up ${templatesToWarmup.length} template(s): ${templatesToWarmup.map(t => t.name).join(', ')}`,
|
|
559
|
-
);
|
|
2983
|
+
logWithFile(logStream, 'info', `Updating package: ${packageName}`);
|
|
560
2984
|
|
|
561
|
-
//
|
|
562
|
-
const
|
|
2985
|
+
// 构建命令
|
|
2986
|
+
const command = buildPnpmCommand(packageName, {
|
|
2987
|
+
global,
|
|
2988
|
+
version,
|
|
2989
|
+
registry,
|
|
2990
|
+
extraArgs,
|
|
2991
|
+
});
|
|
563
2992
|
|
|
564
|
-
//
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
2993
|
+
// 确定工作目录
|
|
2994
|
+
const workingDir = cwd
|
|
2995
|
+
? path.isAbsolute(cwd)
|
|
2996
|
+
? cwd
|
|
2997
|
+
: path.join(process.cwd(), cwd)
|
|
2998
|
+
: process.cwd();
|
|
2999
|
+
|
|
3000
|
+
logWithFile(logStream, 'info', `Executing: ${command}`);
|
|
3001
|
+
logWithFile(logStream, 'info', `Working directory: ${workingDir}`);
|
|
3002
|
+
logWithFile(logStream, 'info', `Log file: ${logFilePath}`);
|
|
3003
|
+
|
|
3004
|
+
// 记录命令开始时间
|
|
3005
|
+
writeLogWithTimestamp(logStream, '--- Command execution started ---');
|
|
3006
|
+
|
|
3007
|
+
// 同步执行命令
|
|
3008
|
+
const result = shelljs.exec(command, {
|
|
3009
|
+
cwd: workingDir,
|
|
3010
|
+
silent: true, // 使用 silent 来捕获输出
|
|
3011
|
+
});
|
|
3012
|
+
|
|
3013
|
+
// 将输出写入控制台和日志文件(带时间戳)
|
|
3014
|
+
if (result.stdout) {
|
|
3015
|
+
process.stdout.write(result.stdout);
|
|
3016
|
+
writeLogWithTimestamp(logStream, result.stdout.trim());
|
|
569
3017
|
}
|
|
570
3018
|
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
3019
|
+
if (result.stderr) {
|
|
3020
|
+
process.stderr.write(result.stderr);
|
|
3021
|
+
writeLogWithTimestamp(logStream, result.stderr.trim());
|
|
3022
|
+
}
|
|
575
3023
|
|
|
576
|
-
|
|
3024
|
+
// 记录命令结束时间
|
|
3025
|
+
writeLogWithTimestamp(logStream, '--- Command execution ended ---');
|
|
3026
|
+
|
|
3027
|
+
// 检查执行结果并记录到日志
|
|
3028
|
+
if (result.code === 0) {
|
|
3029
|
+
logWithFile(logStream, 'success', 'Package updated successfully');
|
|
3030
|
+
logWithFile(logStream, 'info', `Log file: ${logFilePath}`);
|
|
3031
|
+
|
|
3032
|
+
// 上报命令成功
|
|
3033
|
+
reportCommandComplete('update', true, Date.now() - cmdStartTime, {
|
|
3034
|
+
args: JSON.stringify({ packageName, ...options }),
|
|
3035
|
+
});
|
|
3036
|
+
// flush 由 main 函数统一处理
|
|
3037
|
+
} else {
|
|
3038
|
+
const errorMessage = `Command exited with code ${result.code}`;
|
|
3039
|
+
logWithFile(logStream, 'error', errorMessage);
|
|
3040
|
+
logWithFile(
|
|
3041
|
+
logStream,
|
|
3042
|
+
'error',
|
|
3043
|
+
`Check log file for details: ${logFilePath}`,
|
|
3044
|
+
);
|
|
3045
|
+
|
|
3046
|
+
// 上报命令失败
|
|
3047
|
+
reportError(new Error(errorMessage), {
|
|
3048
|
+
command: 'update',
|
|
3049
|
+
packageName,
|
|
3050
|
+
exitCode: String(result.code),
|
|
3051
|
+
});
|
|
3052
|
+
reportCommandComplete('update', false, Date.now() - cmdStartTime, {
|
|
3053
|
+
args: JSON.stringify({ packageName, ...options }),
|
|
3054
|
+
errorCode: result.code || 1,
|
|
3055
|
+
errorMessage,
|
|
3056
|
+
});
|
|
3057
|
+
}
|
|
3058
|
+
|
|
3059
|
+
// 关闭日志流并等待写入完成
|
|
3060
|
+
logStream.end(() => {
|
|
3061
|
+
// 流关闭后再退出进程
|
|
3062
|
+
if (result.code !== 0) {
|
|
3063
|
+
// flush 后再退出
|
|
3064
|
+
flushSlardar().then(() => {
|
|
3065
|
+
process.exit(result.code || 1);
|
|
3066
|
+
});
|
|
3067
|
+
}
|
|
3068
|
+
// 成功时 flush 由 main 函数统一处理
|
|
3069
|
+
});
|
|
577
3070
|
} catch (error) {
|
|
578
|
-
|
|
579
|
-
logger.error('Failed to warmup templates:');
|
|
580
|
-
logger.error(error instanceof Error ? error.message : String(error));
|
|
581
|
-
process.exit(1);
|
|
3071
|
+
handleUpdateError(error, packageName, options, cmdStartTime, logStream);
|
|
582
3072
|
}
|
|
583
3073
|
};
|
|
3074
|
+
// end_aigc
|
|
584
3075
|
|
|
585
3076
|
/**
|
|
586
|
-
* 注册
|
|
3077
|
+
* 注册 update 命令到 program
|
|
587
3078
|
*/
|
|
588
|
-
const registerCommand$
|
|
3079
|
+
const registerCommand$3 = program => {
|
|
589
3080
|
program
|
|
590
|
-
.command('
|
|
591
|
-
.description('
|
|
592
|
-
.option('-
|
|
593
|
-
.
|
|
594
|
-
|
|
3081
|
+
.command('update <package>')
|
|
3082
|
+
.description('Update a package dependency')
|
|
3083
|
+
.option('-g, --global', 'Update package globally', false)
|
|
3084
|
+
.option('-c, --cwd <path>', 'Working directory for the update')
|
|
3085
|
+
.option(
|
|
3086
|
+
'--to <version>',
|
|
3087
|
+
'Version to update to (default: latest)',
|
|
3088
|
+
'latest',
|
|
3089
|
+
)
|
|
3090
|
+
.option('--registry <url>', 'Registry URL to use for the update')
|
|
3091
|
+
.option('--log-file <path>', 'Log file path')
|
|
3092
|
+
.allowUnknownOption() // 允许透传参数给 pnpm
|
|
3093
|
+
.action((packageName, options, command) => {
|
|
3094
|
+
// 收集所有未知选项作为额外参数
|
|
3095
|
+
const extraArgs = command.args.slice(1);
|
|
3096
|
+
|
|
3097
|
+
executeUpdate(packageName, {
|
|
3098
|
+
...options,
|
|
3099
|
+
version: options.to, // 将 --to 映射到 version
|
|
3100
|
+
extraArgs,
|
|
3101
|
+
});
|
|
595
3102
|
});
|
|
596
3103
|
};
|
|
597
3104
|
|
|
@@ -727,7 +3234,7 @@ const parseConfigContent = (content) => {
|
|
|
727
3234
|
return config ;
|
|
728
3235
|
} catch (error) {
|
|
729
3236
|
// TOML 解析失败,继续尝试其他格式
|
|
730
|
-
|
|
3237
|
+
|
|
731
3238
|
console.debug('TOML parse failed:', error);
|
|
732
3239
|
}
|
|
733
3240
|
|
|
@@ -739,7 +3246,7 @@ const parseConfigContent = (content) => {
|
|
|
739
3246
|
}
|
|
740
3247
|
} catch (error) {
|
|
741
3248
|
// YAML 解析失败,继续尝试其他格式
|
|
742
|
-
|
|
3249
|
+
|
|
743
3250
|
console.debug('YAML parse failed:', error);
|
|
744
3251
|
}
|
|
745
3252
|
|
|
@@ -774,57 +3281,283 @@ const loadCozeConfig = async (
|
|
|
774
3281
|
);
|
|
775
3282
|
}
|
|
776
3283
|
|
|
777
|
-
const config = parseConfigContent(content);
|
|
778
|
-
if (!config) {
|
|
779
|
-
throw new Error(
|
|
780
|
-
'Failed to parse .coze config file.\n' +
|
|
781
|
-
'Please ensure the file is in valid TOML, YAML, or JSON format.',
|
|
3284
|
+
const config = parseConfigContent(content);
|
|
3285
|
+
if (!config) {
|
|
3286
|
+
throw new Error(
|
|
3287
|
+
'Failed to parse .coze config file.\n' +
|
|
3288
|
+
'Please ensure the file is in valid TOML, YAML, or JSON format.',
|
|
3289
|
+
);
|
|
3290
|
+
}
|
|
3291
|
+
|
|
3292
|
+
return config;
|
|
3293
|
+
};
|
|
3294
|
+
|
|
3295
|
+
/**
|
|
3296
|
+
* 获取指定命令的配置
|
|
3297
|
+
*
|
|
3298
|
+
* @param config - .coze 配置对象
|
|
3299
|
+
* @param commandName - 命令名称 (dev/build/start)
|
|
3300
|
+
* @returns 命令配置数组
|
|
3301
|
+
*/
|
|
3302
|
+
const getCommandConfig = (
|
|
3303
|
+
config,
|
|
3304
|
+
commandName,
|
|
3305
|
+
) => {
|
|
3306
|
+
let commandConfig;
|
|
3307
|
+
|
|
3308
|
+
// 根据命令名称映射到配置路径
|
|
3309
|
+
switch (commandName) {
|
|
3310
|
+
case 'dev':
|
|
3311
|
+
commandConfig = _optionalChain$1([config, 'access', _ => _.dev, 'optionalAccess', _2 => _2.run]);
|
|
3312
|
+
break;
|
|
3313
|
+
case 'build':
|
|
3314
|
+
commandConfig = _optionalChain$1([config, 'access', _3 => _3.deploy, 'optionalAccess', _4 => _4.build]);
|
|
3315
|
+
break;
|
|
3316
|
+
case 'start':
|
|
3317
|
+
commandConfig = _optionalChain$1([config, 'access', _5 => _5.deploy, 'optionalAccess', _6 => _6.run]);
|
|
3318
|
+
break;
|
|
3319
|
+
default:
|
|
3320
|
+
throw new Error(`Unknown command: ${commandName}`);
|
|
3321
|
+
}
|
|
3322
|
+
|
|
3323
|
+
if (!commandConfig || commandConfig.length === 0) {
|
|
3324
|
+
throw new Error(
|
|
3325
|
+
`Command '${commandName}' is not configured in .coze file.\n` +
|
|
3326
|
+
'Please add the corresponding configuration to your .coze file.',
|
|
3327
|
+
);
|
|
3328
|
+
}
|
|
3329
|
+
|
|
3330
|
+
return commandConfig;
|
|
3331
|
+
};
|
|
3332
|
+
|
|
3333
|
+
// ABOUTME: Fix rule to comment out problematic outputFileTracingRoot config in Next.js projects
|
|
3334
|
+
// ABOUTME: This config can cause issues in monorepo environments and should be removed
|
|
3335
|
+
|
|
3336
|
+
|
|
3337
|
+
|
|
3338
|
+
|
|
3339
|
+
/**
|
|
3340
|
+
* 检查是否为 Next.js 项目
|
|
3341
|
+
*/
|
|
3342
|
+
const isNextProject = (projectFolder) => {
|
|
3343
|
+
const packageJsonPath = path.join(projectFolder, 'package.json');
|
|
3344
|
+
|
|
3345
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
3346
|
+
return false;
|
|
3347
|
+
}
|
|
3348
|
+
|
|
3349
|
+
try {
|
|
3350
|
+
// eslint-disable-next-line no-restricted-syntax
|
|
3351
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
3352
|
+
const deps = {
|
|
3353
|
+
...packageJson.dependencies,
|
|
3354
|
+
...packageJson.devDependencies,
|
|
3355
|
+
};
|
|
3356
|
+
|
|
3357
|
+
return 'next' in deps;
|
|
3358
|
+
} catch (e) {
|
|
3359
|
+
return false;
|
|
3360
|
+
}
|
|
3361
|
+
};
|
|
3362
|
+
|
|
3363
|
+
/**
|
|
3364
|
+
* 查找 Next.js 配置文件
|
|
3365
|
+
*/
|
|
3366
|
+
const findNextConfigFile = (projectFolder) => {
|
|
3367
|
+
const possibleConfigs = [
|
|
3368
|
+
'next.config.ts',
|
|
3369
|
+
'next.config.js',
|
|
3370
|
+
'next.config.mjs',
|
|
3371
|
+
];
|
|
3372
|
+
|
|
3373
|
+
for (const configFile of possibleConfigs) {
|
|
3374
|
+
const configPath = path.join(projectFolder, configFile);
|
|
3375
|
+
if (fs.existsSync(configPath)) {
|
|
3376
|
+
return configPath;
|
|
3377
|
+
}
|
|
3378
|
+
}
|
|
3379
|
+
|
|
3380
|
+
return null;
|
|
3381
|
+
};
|
|
3382
|
+
|
|
3383
|
+
/**
|
|
3384
|
+
* 注释掉 outputFileTracingRoot 配置
|
|
3385
|
+
*/
|
|
3386
|
+
const commentOutOutputTracingRoot = (
|
|
3387
|
+
configPath,
|
|
3388
|
+
) => {
|
|
3389
|
+
let content = fs.readFileSync(configPath, 'utf-8');
|
|
3390
|
+
let modified = false;
|
|
3391
|
+
let originalLine = null;
|
|
3392
|
+
|
|
3393
|
+
// 匹配包含 outputFileTracingRoot 的行(尚未被注释的)
|
|
3394
|
+
// 支持 path.resolve(...) 后面有空格,然后可选逗号
|
|
3395
|
+
// 只匹配单行配置,不匹配跨多行的配置
|
|
3396
|
+
const pattern =
|
|
3397
|
+
/^(\s*)(outputFileTracingRoot:\s*path\.resolve\([^\n\r)]+\)\s*,?)\s*$/gm;
|
|
3398
|
+
|
|
3399
|
+
const matches = content.match(pattern);
|
|
3400
|
+
|
|
3401
|
+
if (matches && matches.length > 0) {
|
|
3402
|
+
originalLine = matches[0].trim();
|
|
3403
|
+
|
|
3404
|
+
// 在匹配的行前添加 // 注释
|
|
3405
|
+
content = content.replace(pattern, '$1// $2');
|
|
3406
|
+
modified = true;
|
|
3407
|
+
|
|
3408
|
+
fs.writeFileSync(configPath, content, 'utf-8');
|
|
3409
|
+
}
|
|
3410
|
+
|
|
3411
|
+
return { modified, originalLine };
|
|
3412
|
+
};
|
|
3413
|
+
|
|
3414
|
+
// start_aigc
|
|
3415
|
+
/**
|
|
3416
|
+
* Fix 规则:注释掉 Next.js 项目中的 outputFileTracingRoot 配置
|
|
3417
|
+
* 这个配置在 monorepo 环境中可能会导致问题
|
|
3418
|
+
*/
|
|
3419
|
+
const fixNextOutputTracingRoot = context => {
|
|
3420
|
+
const ruleName = 'next-output-tracing-root';
|
|
3421
|
+
|
|
3422
|
+
// 1. 检查是否为 Next.js 项目
|
|
3423
|
+
if (!isNextProject(context.projectFolder)) {
|
|
3424
|
+
return {
|
|
3425
|
+
ruleName,
|
|
3426
|
+
applied: false,
|
|
3427
|
+
message: 'Not a Next.js project, skipping',
|
|
3428
|
+
};
|
|
3429
|
+
}
|
|
3430
|
+
|
|
3431
|
+
// 2. 查找 Next.js 配置文件
|
|
3432
|
+
const configPath = findNextConfigFile(context.projectFolder);
|
|
3433
|
+
|
|
3434
|
+
if (!configPath) {
|
|
3435
|
+
return {
|
|
3436
|
+
ruleName,
|
|
3437
|
+
applied: false,
|
|
3438
|
+
message: 'Next.js config file not found, skipping',
|
|
3439
|
+
};
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3442
|
+
// 3. 注释掉 outputFileTracingRoot 配置
|
|
3443
|
+
const { modified, originalLine } = commentOutOutputTracingRoot(configPath);
|
|
3444
|
+
|
|
3445
|
+
if (modified && originalLine) {
|
|
3446
|
+
logger.success(
|
|
3447
|
+
`Commented out outputFileTracingRoot in ${configPath.split('/').pop()}`,
|
|
782
3448
|
);
|
|
3449
|
+
logger.info(` Original: ${originalLine}`);
|
|
3450
|
+
|
|
3451
|
+
return {
|
|
3452
|
+
ruleName,
|
|
3453
|
+
applied: true,
|
|
3454
|
+
message: `Successfully commented out: ${originalLine}`,
|
|
3455
|
+
};
|
|
783
3456
|
}
|
|
784
3457
|
|
|
785
|
-
return
|
|
3458
|
+
return {
|
|
3459
|
+
ruleName,
|
|
3460
|
+
applied: false,
|
|
3461
|
+
message: 'No outputFileTracingRoot config found, skipping',
|
|
3462
|
+
};
|
|
786
3463
|
};
|
|
3464
|
+
// end_aigc
|
|
787
3465
|
|
|
788
3466
|
/**
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
* @param config - .coze 配置对象
|
|
792
|
-
* @param commandName - 命令名称 (dev/build/start)
|
|
793
|
-
* @returns 命令配置数组
|
|
3467
|
+
* 所有修复规则的数组
|
|
3468
|
+
* 按顺序执行,新增规则直接添加到数组中
|
|
794
3469
|
*/
|
|
795
|
-
const
|
|
796
|
-
|
|
797
|
-
|
|
3470
|
+
const rules = [
|
|
3471
|
+
// Next.js related fixes
|
|
3472
|
+
fixNextOutputTracingRoot,
|
|
3473
|
+
|
|
3474
|
+
// Add more rules here
|
|
3475
|
+
] ;
|
|
3476
|
+
|
|
3477
|
+
// ABOUTME: Fix command for resolving legacy issues from previous project versions
|
|
3478
|
+
// ABOUTME: Applies a series of fix rules defined in the fix-rules directory
|
|
3479
|
+
|
|
3480
|
+
|
|
3481
|
+
// start_aigc
|
|
3482
|
+
/**
|
|
3483
|
+
* 执行 fix 命令的内部实现
|
|
3484
|
+
*/
|
|
3485
|
+
const executeFix = async (
|
|
3486
|
+
options = {},
|
|
798
3487
|
) => {
|
|
799
|
-
|
|
3488
|
+
try {
|
|
3489
|
+
const cwd = process.cwd();
|
|
3490
|
+
const projectFolder = options.directory
|
|
3491
|
+
? path.resolve(cwd, options.directory)
|
|
3492
|
+
: cwd;
|
|
800
3493
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
case 'dev':
|
|
804
|
-
commandConfig = _optionalChain$1([config, 'access', _ => _.dev, 'optionalAccess', _2 => _2.run]);
|
|
805
|
-
break;
|
|
806
|
-
case 'build':
|
|
807
|
-
commandConfig = _optionalChain$1([config, 'access', _3 => _3.deploy, 'optionalAccess', _4 => _4.build]);
|
|
808
|
-
break;
|
|
809
|
-
case 'start':
|
|
810
|
-
commandConfig = _optionalChain$1([config, 'access', _5 => _5.deploy, 'optionalAccess', _6 => _6.run]);
|
|
811
|
-
break;
|
|
812
|
-
default:
|
|
813
|
-
throw new Error(`Unknown command: ${commandName}`);
|
|
814
|
-
}
|
|
3494
|
+
logger.info(`Running fix command on: ${projectFolder}`);
|
|
3495
|
+
logger.info(`Found ${rules.length} fix rule(s) to apply\n`);
|
|
815
3496
|
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
3497
|
+
const context = {
|
|
3498
|
+
cwd,
|
|
3499
|
+
projectFolder,
|
|
3500
|
+
};
|
|
3501
|
+
|
|
3502
|
+
let appliedCount = 0;
|
|
3503
|
+
let skippedCount = 0;
|
|
3504
|
+
|
|
3505
|
+
// 依次执行所有修复规则
|
|
3506
|
+
for (const rule of rules) {
|
|
3507
|
+
try {
|
|
3508
|
+
const result = await Promise.resolve(rule(context));
|
|
3509
|
+
|
|
3510
|
+
if (result.applied) {
|
|
3511
|
+
appliedCount++;
|
|
3512
|
+
logger.success(`✓ ${result.ruleName}: ${result.message}`);
|
|
3513
|
+
} else {
|
|
3514
|
+
skippedCount++;
|
|
3515
|
+
logger.info(`○ ${result.ruleName}: ${result.message}`);
|
|
3516
|
+
}
|
|
3517
|
+
} catch (error) {
|
|
3518
|
+
logger.error(
|
|
3519
|
+
`✗ Rule execution failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
3520
|
+
);
|
|
3521
|
+
}
|
|
3522
|
+
}
|
|
3523
|
+
|
|
3524
|
+
// 输出汇总信息
|
|
3525
|
+
logger.info(
|
|
3526
|
+
`\nSummary: ${appliedCount} fixed, ${skippedCount} skipped, ${rules.length} total`,
|
|
820
3527
|
);
|
|
3528
|
+
|
|
3529
|
+
if (appliedCount > 0) {
|
|
3530
|
+
logger.success('\nFixes applied successfully!');
|
|
3531
|
+
} else {
|
|
3532
|
+
logger.info('\nNo fixes needed');
|
|
3533
|
+
}
|
|
3534
|
+
} catch (error) {
|
|
3535
|
+
logger.error('Failed to run fix command:');
|
|
3536
|
+
logger.error(error instanceof Error ? error.message : String(error));
|
|
3537
|
+
process.exit(1);
|
|
821
3538
|
}
|
|
3539
|
+
};
|
|
3540
|
+
// end_aigc
|
|
822
3541
|
|
|
823
|
-
|
|
3542
|
+
/**
|
|
3543
|
+
* 注册 fix 命令到 program
|
|
3544
|
+
*/
|
|
3545
|
+
const registerCommand$2 = program => {
|
|
3546
|
+
program
|
|
3547
|
+
.command('fix')
|
|
3548
|
+
.description(
|
|
3549
|
+
'Fix legacy issues from previous versions (e.g., problematic configs)',
|
|
3550
|
+
)
|
|
3551
|
+
.argument(
|
|
3552
|
+
'[directory]',
|
|
3553
|
+
'Target directory to fix (defaults to current directory)',
|
|
3554
|
+
)
|
|
3555
|
+
.action(async (directory) => {
|
|
3556
|
+
await executeFix({ directory });
|
|
3557
|
+
});
|
|
824
3558
|
};
|
|
825
3559
|
|
|
826
3560
|
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; }
|
|
827
|
-
|
|
828
3561
|
/**
|
|
829
3562
|
* 日志文件名常量
|
|
830
3563
|
*/
|
|
@@ -877,18 +3610,35 @@ const executeRun = async (
|
|
|
877
3610
|
commandName,
|
|
878
3611
|
options = {},
|
|
879
3612
|
) => {
|
|
3613
|
+
const cmdStartTime = Date.now();
|
|
3614
|
+
|
|
880
3615
|
try {
|
|
881
3616
|
logger.info(`Running ${commandName} command...`);
|
|
882
3617
|
|
|
883
|
-
//
|
|
3618
|
+
// 上报命令开始
|
|
3619
|
+
reportCommandStart(commandName, JSON.stringify(options));
|
|
3620
|
+
|
|
3621
|
+
// 1. 对于 build 命令,先执行 fix 以确保项目配置正确
|
|
3622
|
+
if (['dev', 'build'].includes(commandName)) {
|
|
3623
|
+
logger.info('\n🔧 Running fix command before build...\n');
|
|
3624
|
+
try {
|
|
3625
|
+
await executeFix();
|
|
3626
|
+
// eslint-disable-next-line @coze-arch/no-empty-catch
|
|
3627
|
+
} catch (e) {
|
|
3628
|
+
// just ignore
|
|
3629
|
+
}
|
|
3630
|
+
logger.info('');
|
|
3631
|
+
}
|
|
3632
|
+
|
|
3633
|
+
// 2. 加载 .coze 配置
|
|
884
3634
|
const config = await loadCozeConfig();
|
|
885
3635
|
const commandArgs = getCommandConfig(config, commandName);
|
|
886
3636
|
|
|
887
|
-
//
|
|
3637
|
+
// 3. 准备日志
|
|
888
3638
|
const logFilePath = resolveLogFilePath(options.logFile);
|
|
889
3639
|
const logStream = createLogStream(logFilePath);
|
|
890
3640
|
|
|
891
|
-
//
|
|
3641
|
+
// 4. 执行命令
|
|
892
3642
|
const commandString = commandArgs.join(' ');
|
|
893
3643
|
|
|
894
3644
|
logger.info(`Executing: ${commandString}`);
|
|
@@ -919,14 +3669,34 @@ const executeRun = async (
|
|
|
919
3669
|
logStream.end();
|
|
920
3670
|
|
|
921
3671
|
if (code !== 0) {
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
);
|
|
3672
|
+
const errorMessage = `Command exited with code ${_nullishCoalesce$1(code, () => ( 'unknown'))}${signal ? ` and signal ${signal}` : ''}`;
|
|
3673
|
+
logger.error(errorMessage);
|
|
925
3674
|
logger.error(`Check log file for details: ${logFilePath}`);
|
|
926
|
-
|
|
3675
|
+
|
|
3676
|
+
// 上报命令失败
|
|
3677
|
+
reportError(new Error(errorMessage), {
|
|
3678
|
+
command: commandName,
|
|
3679
|
+
exitCode: String(_nullishCoalesce$1(code, () => ( 'unknown'))),
|
|
3680
|
+
signal: _nullishCoalesce$1(signal, () => ( 'none')),
|
|
3681
|
+
logFile: logFilePath,
|
|
3682
|
+
});
|
|
3683
|
+
reportCommandComplete(commandName, false, Date.now() - cmdStartTime, {
|
|
3684
|
+
args: JSON.stringify(options),
|
|
3685
|
+
errorCode: _nullishCoalesce$1(code, () => ( 1)),
|
|
3686
|
+
errorMessage,
|
|
3687
|
+
});
|
|
3688
|
+
flushSlardar().then(() => {
|
|
3689
|
+
process.exit(code || 1);
|
|
3690
|
+
});
|
|
927
3691
|
} else {
|
|
928
3692
|
logger.success('Command completed successfully');
|
|
929
3693
|
logger.info(`Log file: ${logFilePath}`);
|
|
3694
|
+
|
|
3695
|
+
// 上报命令成功
|
|
3696
|
+
reportCommandComplete(commandName, true, Date.now() - cmdStartTime, {
|
|
3697
|
+
args: JSON.stringify(options),
|
|
3698
|
+
});
|
|
3699
|
+
// flush 由 main 函数统一处理
|
|
930
3700
|
}
|
|
931
3701
|
});
|
|
932
3702
|
|
|
@@ -937,12 +3707,39 @@ const executeRun = async (
|
|
|
937
3707
|
logger.error(`Stack trace:\n${error.stack}`);
|
|
938
3708
|
}
|
|
939
3709
|
logStream.end();
|
|
940
|
-
|
|
3710
|
+
|
|
3711
|
+
// 上报错误
|
|
3712
|
+
reportError(error, {
|
|
3713
|
+
command: commandName,
|
|
3714
|
+
type: 'child_process_error',
|
|
3715
|
+
});
|
|
3716
|
+
reportCommandComplete(commandName, false, Date.now() - cmdStartTime, {
|
|
3717
|
+
args: JSON.stringify(options),
|
|
3718
|
+
errorCode: 1,
|
|
3719
|
+
errorMessage: error.message,
|
|
3720
|
+
});
|
|
3721
|
+
flushSlardar().then(() => {
|
|
3722
|
+
process.exit(1);
|
|
3723
|
+
});
|
|
941
3724
|
});
|
|
942
3725
|
} catch (error) {
|
|
3726
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
943
3727
|
logger.error(`Failed to run ${commandName} command:`);
|
|
944
|
-
logger.error(
|
|
945
|
-
|
|
3728
|
+
logger.error(err.message);
|
|
3729
|
+
|
|
3730
|
+
// 上报错误
|
|
3731
|
+
reportError(err, {
|
|
3732
|
+
command: commandName,
|
|
3733
|
+
type: 'execution_error',
|
|
3734
|
+
});
|
|
3735
|
+
reportCommandComplete(commandName, false, Date.now() - cmdStartTime, {
|
|
3736
|
+
args: JSON.stringify(options),
|
|
3737
|
+
errorCode: 1,
|
|
3738
|
+
errorMessage: err.message,
|
|
3739
|
+
});
|
|
3740
|
+
flushSlardar().then(() => {
|
|
3741
|
+
process.exit(1);
|
|
3742
|
+
});
|
|
946
3743
|
}
|
|
947
3744
|
};
|
|
948
3745
|
|
|
@@ -1201,6 +3998,11 @@ const shouldIgnoreFile = (filePath) => {
|
|
|
1201
3998
|
return directoryPatterns.some(dir => pathParts.includes(dir));
|
|
1202
3999
|
};
|
|
1203
4000
|
|
|
4001
|
+
// ABOUTME: File system utilities for template file processing
|
|
4002
|
+
// ABOUTME: Provides directory scanning, file path conversion, and node_modules copying
|
|
4003
|
+
|
|
4004
|
+
|
|
4005
|
+
// start_aigc
|
|
1204
4006
|
/**
|
|
1205
4007
|
* 递归获取目录中的所有文件
|
|
1206
4008
|
*
|
|
@@ -1270,7 +4072,13 @@ const convertDotfileName = (filePath) => {
|
|
|
1270
4072
|
|
|
1271
4073
|
return filePath;
|
|
1272
4074
|
};
|
|
4075
|
+
// end_aigc
|
|
1273
4076
|
|
|
4077
|
+
// ABOUTME: File rendering utilities for template processing
|
|
4078
|
+
// ABOUTME: Handles file content rendering, hook execution, and file writing
|
|
4079
|
+
|
|
4080
|
+
|
|
4081
|
+
// start_aigc
|
|
1274
4082
|
/**
|
|
1275
4083
|
* 执行文件渲染钩子
|
|
1276
4084
|
*
|
|
@@ -1316,22 +4124,25 @@ const executeFileRenderHook = async (
|
|
|
1316
4124
|
};
|
|
1317
4125
|
|
|
1318
4126
|
/**
|
|
1319
|
-
*
|
|
4127
|
+
* 准备单个文件的渲染信息(不实际写入)
|
|
4128
|
+
* 用于 dry-run 阶段,收集所有将要写入的文件信息
|
|
4129
|
+
*
|
|
4130
|
+
* @param options - 准备选项
|
|
4131
|
+
* @returns 文件渲染信息,或 null 表示该文件被跳过
|
|
1320
4132
|
*/
|
|
1321
|
-
const
|
|
1322
|
-
|
|
4133
|
+
const prepareFileInfo = async (options
|
|
1323
4134
|
|
|
1324
4135
|
|
|
1325
4136
|
|
|
1326
4137
|
|
|
1327
4138
|
) => {
|
|
1328
|
-
const { file, templatePath,
|
|
4139
|
+
const { file, templatePath, context, templateConfig } = options;
|
|
1329
4140
|
|
|
1330
4141
|
const srcPath = path.join(templatePath, file);
|
|
1331
4142
|
const destFile = convertDotfileName(file);
|
|
1332
4143
|
|
|
1333
4144
|
logger.verbose(
|
|
1334
|
-
` -
|
|
4145
|
+
` - Preparing: ${file}${destFile !== file ? ` -> ${destFile}` : ''}`,
|
|
1335
4146
|
);
|
|
1336
4147
|
|
|
1337
4148
|
// 判断是否为二进制文件
|
|
@@ -1365,51 +4176,203 @@ const processSingleFile = async (options
|
|
|
1365
4176
|
context,
|
|
1366
4177
|
);
|
|
1367
4178
|
|
|
1368
|
-
// 如果返回 null
|
|
4179
|
+
// 如果返回 null,表示该文件被 hook 跳过
|
|
1369
4180
|
if (processedFileInfo === null) {
|
|
1370
4181
|
logger.verbose(' ⊘ Skipped by onFileRender hook');
|
|
1371
|
-
return;
|
|
4182
|
+
return null;
|
|
1372
4183
|
}
|
|
1373
4184
|
|
|
1374
|
-
|
|
1375
|
-
|
|
4185
|
+
return processedFileInfo;
|
|
4186
|
+
};
|
|
4187
|
+
|
|
4188
|
+
/**
|
|
4189
|
+
* 写入渲染后的文件到目标路径
|
|
4190
|
+
*
|
|
4191
|
+
* @param options - 写入选项
|
|
4192
|
+
*/
|
|
4193
|
+
const writeRenderedFile = async (options
|
|
4194
|
+
|
|
4195
|
+
|
|
4196
|
+
|
|
4197
|
+
) => {
|
|
4198
|
+
const { fileInfo, srcPath, destPath } = options;
|
|
4199
|
+
|
|
4200
|
+
logger.verbose(` - Writing: ${fileInfo.destPath}`);
|
|
1376
4201
|
|
|
1377
4202
|
// 确保目标目录存在
|
|
1378
|
-
await ensureDir(path.dirname(
|
|
4203
|
+
await ensureDir(path.dirname(destPath));
|
|
1379
4204
|
|
|
1380
4205
|
// 写入文件
|
|
1381
|
-
if (
|
|
1382
|
-
//
|
|
1383
|
-
|
|
1384
|
-
|
|
4206
|
+
if (fileInfo.isBinary) {
|
|
4207
|
+
// 二进制文件:如果内容是原始 base64(未被 hook 修改),直接复制;否则从 base64 解码写入
|
|
4208
|
+
const buffer = await fs$1.readFile(srcPath);
|
|
4209
|
+
const originalContent = buffer.toString('base64');
|
|
4210
|
+
|
|
4211
|
+
if (fileInfo.content === originalContent) {
|
|
4212
|
+
await fs$1.copyFile(srcPath, destPath);
|
|
1385
4213
|
logger.verbose(' ✓ Copied (binary)');
|
|
1386
4214
|
} else {
|
|
1387
|
-
const
|
|
1388
|
-
await fs$1.writeFile(
|
|
4215
|
+
const modifiedBuffer = Buffer.from(fileInfo.content, 'base64');
|
|
4216
|
+
await fs$1.writeFile(destPath, modifiedBuffer);
|
|
1389
4217
|
logger.verbose(' ✓ Written (binary, modified by hook)');
|
|
1390
4218
|
}
|
|
1391
4219
|
} else {
|
|
1392
4220
|
// 文本文件
|
|
1393
|
-
await fs$1.writeFile(
|
|
4221
|
+
await fs$1.writeFile(destPath, fileInfo.content, 'utf-8');
|
|
1394
4222
|
logger.verbose(' ✓ Rendered and written');
|
|
1395
4223
|
}
|
|
1396
4224
|
};
|
|
4225
|
+
// end_aigc
|
|
4226
|
+
|
|
4227
|
+
// ABOUTME: File conflict detection utilities for template processing
|
|
4228
|
+
// ABOUTME: Provides dry-run file collection and conflict checking
|
|
4229
|
+
|
|
4230
|
+
|
|
4231
|
+
// start_aigc
|
|
4232
|
+
/**
|
|
4233
|
+
* 收集所有将要写入的文件路径(dry-run 阶段)
|
|
4234
|
+
*
|
|
4235
|
+
* @param options - 收集选项
|
|
4236
|
+
* @returns 将要写入的文件路径列表
|
|
4237
|
+
*/
|
|
4238
|
+
const collectFilesToRender = async (options
|
|
4239
|
+
|
|
4240
|
+
|
|
4241
|
+
|
|
4242
|
+
|
|
4243
|
+
) => {
|
|
4244
|
+
const { files, templatePath, context, templateConfig } = options;
|
|
4245
|
+
|
|
4246
|
+
logger.verbose('\nDry-run: Collecting files to render...');
|
|
4247
|
+
|
|
4248
|
+
const fileInfos = await Promise.all(
|
|
4249
|
+
files.map(file =>
|
|
4250
|
+
prepareFileInfo({
|
|
4251
|
+
file,
|
|
4252
|
+
templatePath,
|
|
4253
|
+
context,
|
|
4254
|
+
templateConfig,
|
|
4255
|
+
}),
|
|
4256
|
+
),
|
|
4257
|
+
);
|
|
4258
|
+
|
|
4259
|
+
// 过滤掉被 hook 跳过的文件,收集 destPath
|
|
4260
|
+
const filesToWrite = fileInfos
|
|
4261
|
+
.filter((info) => info !== null)
|
|
4262
|
+
.map(info => info.destPath);
|
|
4263
|
+
|
|
4264
|
+
logger.verbose(` - ${filesToWrite.length} files will be written`);
|
|
4265
|
+
logger.verbose(
|
|
4266
|
+
` - ${fileInfos.length - filesToWrite.length} files skipped by hooks`,
|
|
4267
|
+
);
|
|
4268
|
+
|
|
4269
|
+
return filesToWrite;
|
|
4270
|
+
};
|
|
4271
|
+
|
|
4272
|
+
/**
|
|
4273
|
+
* 检测文件冲突
|
|
4274
|
+
*
|
|
4275
|
+
* @param outputPath - 输出目录路径
|
|
4276
|
+
* @param filesToWrite - 将要写入的文件路径列表
|
|
4277
|
+
* @returns 冲突的文件路径列表
|
|
4278
|
+
*/
|
|
4279
|
+
const detectFileConflicts = (
|
|
4280
|
+
outputPath,
|
|
4281
|
+
filesToWrite,
|
|
4282
|
+
) => {
|
|
4283
|
+
logger.verbose('\nChecking for file conflicts...');
|
|
4284
|
+
|
|
4285
|
+
const conflicts = [];
|
|
4286
|
+
|
|
4287
|
+
for (const file of filesToWrite) {
|
|
4288
|
+
const fullPath = path.join(outputPath, file);
|
|
4289
|
+
if (fs.existsSync(fullPath)) {
|
|
4290
|
+
conflicts.push(file);
|
|
4291
|
+
logger.verbose(` ⚠ Conflict detected: ${file}`);
|
|
4292
|
+
}
|
|
4293
|
+
}
|
|
4294
|
+
|
|
4295
|
+
if (conflicts.length === 0) {
|
|
4296
|
+
logger.verbose(' ✓ No conflicts detected');
|
|
4297
|
+
} else {
|
|
4298
|
+
logger.verbose(` ⚠ ${conflicts.length} conflicts detected`);
|
|
4299
|
+
}
|
|
4300
|
+
|
|
4301
|
+
return conflicts;
|
|
4302
|
+
};
|
|
4303
|
+
// end_aigc
|
|
4304
|
+
|
|
4305
|
+
// ABOUTME: Main file processing orchestration for template rendering
|
|
4306
|
+
// ABOUTME: Coordinates file system, rendering, and conflict detection layers
|
|
4307
|
+
|
|
4308
|
+
|
|
4309
|
+
// start_aigc
|
|
4310
|
+
/**
|
|
4311
|
+
* 处理单个文件(准备 + 写入)
|
|
4312
|
+
*
|
|
4313
|
+
* @param options - 处理选项
|
|
4314
|
+
*/
|
|
4315
|
+
const processSingleFile = async (options
|
|
4316
|
+
|
|
4317
|
+
|
|
4318
|
+
|
|
4319
|
+
|
|
4320
|
+
|
|
4321
|
+
) => {
|
|
4322
|
+
const { file, templatePath, outputPath, context, templateConfig } = options;
|
|
4323
|
+
|
|
4324
|
+
const srcPath = path.join(templatePath, file);
|
|
4325
|
+
|
|
4326
|
+
// 准备文件信息
|
|
4327
|
+
const processedFileInfo = await prepareFileInfo({
|
|
4328
|
+
file,
|
|
4329
|
+
templatePath,
|
|
4330
|
+
context,
|
|
4331
|
+
templateConfig,
|
|
4332
|
+
});
|
|
4333
|
+
|
|
4334
|
+
// 如果返回 null,跳过该文件
|
|
4335
|
+
if (processedFileInfo === null) {
|
|
4336
|
+
return;
|
|
4337
|
+
}
|
|
4338
|
+
|
|
4339
|
+
// 使用处理后的目标路径
|
|
4340
|
+
const finalDestPath = path.join(outputPath, processedFileInfo.destPath);
|
|
4341
|
+
|
|
4342
|
+
// 写入文件
|
|
4343
|
+
await writeRenderedFile({
|
|
4344
|
+
fileInfo: processedFileInfo,
|
|
4345
|
+
srcPath,
|
|
4346
|
+
destPath: finalDestPath,
|
|
4347
|
+
});
|
|
4348
|
+
};
|
|
1397
4349
|
|
|
1398
4350
|
/**
|
|
1399
4351
|
* 复制并处理模板文件到目标目录
|
|
4352
|
+
*
|
|
4353
|
+
* 流程:
|
|
4354
|
+
* 1. 验证模板目录
|
|
4355
|
+
* 2. 扫描所有模板文件
|
|
4356
|
+
* 3. Dry-run:收集将要写入的文件列表(考虑 hooks 影响)
|
|
4357
|
+
* 4. 冲突检测:检查是否有文件会被覆盖(可通过 force 跳过)
|
|
4358
|
+
* 5. 实际写入:渲染并写入所有文件
|
|
4359
|
+
* 6. 复制 node_modules(如果存在)
|
|
4360
|
+
*
|
|
4361
|
+
* @param options - 处理选项
|
|
1400
4362
|
*/
|
|
1401
4363
|
const processTemplateFiles = async (options
|
|
1402
4364
|
|
|
1403
4365
|
|
|
1404
4366
|
|
|
1405
4367
|
|
|
4368
|
+
|
|
1406
4369
|
) => {
|
|
1407
|
-
const { templatePath, outputPath, context, templateConfig } = options;
|
|
4370
|
+
const { templatePath, outputPath, context, templateConfig, force } = options;
|
|
1408
4371
|
logger.verbose('Processing template files:');
|
|
1409
4372
|
logger.verbose(` - Template path: ${templatePath}`);
|
|
1410
4373
|
logger.verbose(` - Output path: ${outputPath}`);
|
|
1411
4374
|
|
|
1412
|
-
// 验证模板目录是否存在
|
|
4375
|
+
// 阶段 0: 验证模板目录是否存在
|
|
1413
4376
|
try {
|
|
1414
4377
|
const stat = await fs$1.stat(templatePath);
|
|
1415
4378
|
logger.verbose(
|
|
@@ -1425,6 +4388,7 @@ const processTemplateFiles = async (options
|
|
|
1425
4388
|
throw error;
|
|
1426
4389
|
}
|
|
1427
4390
|
|
|
4391
|
+
// 阶段 1: 扫描所有模板文件
|
|
1428
4392
|
const files = await getAllFiles(templatePath);
|
|
1429
4393
|
|
|
1430
4394
|
logger.verbose(` - Found ${files.length} files to process`);
|
|
@@ -1434,88 +4398,53 @@ const processTemplateFiles = async (options
|
|
|
1434
4398
|
return;
|
|
1435
4399
|
}
|
|
1436
4400
|
|
|
4401
|
+
// 阶段 2: Dry-run - 收集所有将要写入的文件
|
|
4402
|
+
const filesToWrite = await collectFilesToRender({
|
|
4403
|
+
files,
|
|
4404
|
+
templatePath,
|
|
4405
|
+
context,
|
|
4406
|
+
templateConfig,
|
|
4407
|
+
});
|
|
4408
|
+
|
|
4409
|
+
// 阶段 3: 冲突检测(force 为 true 时跳过)
|
|
4410
|
+
if (!force) {
|
|
4411
|
+
const conflicts = detectFileConflicts(outputPath, filesToWrite);
|
|
4412
|
+
|
|
4413
|
+
if (conflicts.length > 0) {
|
|
4414
|
+
// 有冲突,抛出详细的错误信息
|
|
4415
|
+
const conflictList = conflicts.map(f => ` - ${f}`).join('\n');
|
|
4416
|
+
throw new Error(
|
|
4417
|
+
`File conflicts detected in output directory: ${outputPath}\n\n` +
|
|
4418
|
+
`The following files already exist and would be overwritten:\n${conflictList}\n\n` +
|
|
4419
|
+
'Please remove these files or use a different output directory.\n' +
|
|
4420
|
+
'Or use --force to overwrite existing files.',
|
|
4421
|
+
);
|
|
4422
|
+
}
|
|
4423
|
+
} else {
|
|
4424
|
+
logger.verbose(
|
|
4425
|
+
' - Force mode enabled, skipping conflict detection. Existing files will be overwritten.',
|
|
4426
|
+
);
|
|
4427
|
+
}
|
|
4428
|
+
|
|
4429
|
+
// 阶段 4: 实际写入文件
|
|
4430
|
+
logger.verbose('\nWriting files...');
|
|
1437
4431
|
await Promise.all(
|
|
1438
4432
|
files.map(file =>
|
|
1439
4433
|
processSingleFile({
|
|
1440
4434
|
file,
|
|
1441
4435
|
templatePath,
|
|
1442
4436
|
outputPath,
|
|
1443
|
-
context,
|
|
1444
|
-
templateConfig,
|
|
1445
|
-
}),
|
|
1446
|
-
),
|
|
1447
|
-
);
|
|
1448
|
-
|
|
1449
|
-
logger.verbose('✓ All files processed successfully');
|
|
1450
|
-
|
|
1451
|
-
// 单独处理 node_modules 目录(如果存在)
|
|
1452
|
-
const sourceNodeModules = path.join(templatePath, 'node_modules');
|
|
1453
|
-
const targetNodeModules = path.join(outputPath, 'node_modules');
|
|
1454
|
-
|
|
1455
|
-
if (fs.existsSync(sourceNodeModules)) {
|
|
1456
|
-
logger.info('\nCopying node_modules from pre-warmed template...');
|
|
1457
|
-
logger.verbose(` From: ${sourceNodeModules}`);
|
|
1458
|
-
logger.verbose(` To: ${targetNodeModules}`);
|
|
1459
|
-
|
|
1460
|
-
const result = shelljs.exec(`cp -R "${sourceNodeModules}" "${targetNodeModules}"`, {
|
|
1461
|
-
silent: true,
|
|
1462
|
-
});
|
|
1463
|
-
|
|
1464
|
-
if (result.stdout) {
|
|
1465
|
-
process.stdout.write(result.stdout);
|
|
1466
|
-
}
|
|
1467
|
-
|
|
1468
|
-
if (result.stderr) {
|
|
1469
|
-
process.stderr.write(result.stderr);
|
|
1470
|
-
}
|
|
1471
|
-
|
|
1472
|
-
if (result.code === 0) {
|
|
1473
|
-
logger.success('✓ node_modules copied successfully');
|
|
1474
|
-
} else {
|
|
1475
|
-
logger.warn(
|
|
1476
|
-
`Failed to copy node_modules: ${result.stderr || 'unknown error'}`,
|
|
1477
|
-
);
|
|
1478
|
-
logger.info('Will need to run pnpm install manually');
|
|
1479
|
-
}
|
|
1480
|
-
}
|
|
1481
|
-
};
|
|
1482
|
-
|
|
1483
|
-
/**
|
|
1484
|
-
* 检查输出目录是否为空
|
|
1485
|
-
* 注意:.git 目录会被忽略,允许在已初始化 git 的目录中创建项目
|
|
1486
|
-
*
|
|
1487
|
-
* @param outputPath - 输出目录路径
|
|
1488
|
-
* @returns 是否为空
|
|
1489
|
-
*/
|
|
1490
|
-
const isOutputDirEmpty = async (
|
|
1491
|
-
outputPath,
|
|
1492
|
-
) => {
|
|
1493
|
-
try {
|
|
1494
|
-
const entries = await fs$1.readdir(outputPath);
|
|
1495
|
-
// 过滤掉 .git 目录,允许在已初始化 git 的目录中创建项目
|
|
1496
|
-
const filteredEntries = entries.filter(entry => entry !== '.git');
|
|
1497
|
-
return filteredEntries.length === 0;
|
|
1498
|
-
} catch (e) {
|
|
1499
|
-
// 目录不存在,视为空
|
|
1500
|
-
return true;
|
|
1501
|
-
}
|
|
1502
|
-
};
|
|
4437
|
+
context,
|
|
4438
|
+
templateConfig,
|
|
4439
|
+
}),
|
|
4440
|
+
),
|
|
4441
|
+
);
|
|
1503
4442
|
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
* @param outputPath - 输出目录路径
|
|
1508
|
-
* @throws 如果目录不为空则抛出错误
|
|
1509
|
-
*/
|
|
1510
|
-
const validateOutputDir = async (outputPath) => {
|
|
1511
|
-
const isEmpty = await isOutputDirEmpty(outputPath);
|
|
1512
|
-
if (!isEmpty) {
|
|
1513
|
-
throw new Error(
|
|
1514
|
-
`Output directory is not empty: ${outputPath}\n` +
|
|
1515
|
-
'Please use an empty directory or remove existing files.',
|
|
1516
|
-
);
|
|
1517
|
-
}
|
|
4443
|
+
logger.verbose('✓ All files processed successfully');
|
|
4444
|
+
|
|
4445
|
+
// node_modules 将由 pnpm install 处理(利用缓存和硬链接机制)
|
|
1518
4446
|
};
|
|
4447
|
+
// end_aigc
|
|
1519
4448
|
|
|
1520
4449
|
/**
|
|
1521
4450
|
* 模板引擎执行选项
|
|
@@ -1526,6 +4455,7 @@ const validateOutputDir = async (outputPath) => {
|
|
|
1526
4455
|
|
|
1527
4456
|
|
|
1528
4457
|
|
|
4458
|
+
|
|
1529
4459
|
/**
|
|
1530
4460
|
* 加载模板元数据和路径
|
|
1531
4461
|
*/
|
|
@@ -1595,22 +4525,47 @@ const executeAfterRenderHook = async (
|
|
|
1595
4525
|
}
|
|
1596
4526
|
};
|
|
1597
4527
|
|
|
4528
|
+
/**
|
|
4529
|
+
* 执行完成钩子
|
|
4530
|
+
*/
|
|
4531
|
+
const executeCompleteHook = async (
|
|
4532
|
+
templateConfig,
|
|
4533
|
+
context,
|
|
4534
|
+
outputPath,
|
|
4535
|
+
) => {
|
|
4536
|
+
if (templateConfig.onComplete) {
|
|
4537
|
+
await templateConfig.onComplete(context, outputPath);
|
|
4538
|
+
}
|
|
4539
|
+
};
|
|
4540
|
+
|
|
1598
4541
|
/**
|
|
1599
4542
|
* 准备输出目录
|
|
1600
4543
|
*/
|
|
1601
|
-
const prepareOutputDirectory =
|
|
4544
|
+
const prepareOutputDirectory = (outputPath) => {
|
|
1602
4545
|
const absolutePath = path.resolve(process.cwd(), outputPath);
|
|
1603
|
-
|
|
4546
|
+
// 不再在这里验证目录是否为空,冲突检测已移至 processTemplateFiles 中
|
|
1604
4547
|
return absolutePath;
|
|
1605
4548
|
};
|
|
1606
4549
|
|
|
4550
|
+
/**
|
|
4551
|
+
* 模板引擎执行结果
|
|
4552
|
+
*/
|
|
4553
|
+
|
|
4554
|
+
|
|
4555
|
+
|
|
4556
|
+
|
|
4557
|
+
|
|
4558
|
+
|
|
4559
|
+
|
|
4560
|
+
|
|
4561
|
+
|
|
1607
4562
|
/**
|
|
1608
4563
|
* 执行完整的模板渲染流程
|
|
1609
4564
|
*/
|
|
1610
4565
|
const execute = async (
|
|
1611
4566
|
options,
|
|
1612
4567
|
) => {
|
|
1613
|
-
const { templateName, outputPath, command } = options;
|
|
4568
|
+
const { templateName, outputPath, command, force } = options;
|
|
1614
4569
|
|
|
1615
4570
|
// 1. 加载模板
|
|
1616
4571
|
const { templatePath } = await loadTemplateMetadata(templateName);
|
|
@@ -1627,7 +4582,7 @@ const execute = async (
|
|
|
1627
4582
|
});
|
|
1628
4583
|
|
|
1629
4584
|
// 5. 准备输出目录
|
|
1630
|
-
const absoluteOutputPath =
|
|
4585
|
+
const absoluteOutputPath = prepareOutputDirectory(outputPath);
|
|
1631
4586
|
|
|
1632
4587
|
// 6. 处理模板文件
|
|
1633
4588
|
await processTemplateFiles({
|
|
@@ -1635,12 +4590,17 @@ const execute = async (
|
|
|
1635
4590
|
outputPath: absoluteOutputPath,
|
|
1636
4591
|
context,
|
|
1637
4592
|
templateConfig,
|
|
4593
|
+
force,
|
|
1638
4594
|
});
|
|
1639
4595
|
|
|
1640
4596
|
// 7. 执行 onAfterRender 钩子
|
|
1641
4597
|
await executeAfterRenderHook(templateConfig, context, absoluteOutputPath);
|
|
1642
4598
|
|
|
1643
|
-
return
|
|
4599
|
+
return {
|
|
4600
|
+
outputPath: absoluteOutputPath,
|
|
4601
|
+
templateConfig,
|
|
4602
|
+
context,
|
|
4603
|
+
};
|
|
1644
4604
|
};
|
|
1645
4605
|
|
|
1646
4606
|
function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
|
|
@@ -1682,12 +4642,46 @@ const runPnpmInstall = (projectPath) => {
|
|
|
1682
4642
|
};
|
|
1683
4643
|
|
|
1684
4644
|
/**
|
|
1685
|
-
*
|
|
4645
|
+
* 运行 git 命令的辅助函数
|
|
4646
|
+
*/
|
|
4647
|
+
const runGitCommand = (command, projectPath) => {
|
|
4648
|
+
logger.info(`Executing: ${command}`);
|
|
4649
|
+
|
|
4650
|
+
const result = shelljs.exec(command, {
|
|
4651
|
+
cwd: projectPath,
|
|
4652
|
+
silent: true,
|
|
4653
|
+
});
|
|
4654
|
+
|
|
4655
|
+
// 输出命令的结果
|
|
4656
|
+
if (result.stdout) {
|
|
4657
|
+
process.stdout.write(result.stdout);
|
|
4658
|
+
}
|
|
4659
|
+
|
|
4660
|
+
if (result.stderr) {
|
|
4661
|
+
process.stderr.write(result.stderr);
|
|
4662
|
+
}
|
|
4663
|
+
|
|
4664
|
+
if (result.code !== 0) {
|
|
4665
|
+
const errorMessage = [
|
|
4666
|
+
`${command} failed with exit code ${result.code}`,
|
|
4667
|
+
result.stderr ? `\nStderr:\n${result.stderr}` : '',
|
|
4668
|
+
result.stdout ? `\nStdout:\n${result.stdout}` : '',
|
|
4669
|
+
]
|
|
4670
|
+
.filter(Boolean)
|
|
4671
|
+
.join('');
|
|
4672
|
+
|
|
4673
|
+
throw new Error(errorMessage);
|
|
4674
|
+
}
|
|
4675
|
+
};
|
|
4676
|
+
|
|
4677
|
+
/**
|
|
4678
|
+
* 初始化 git 仓库
|
|
1686
4679
|
* 如果目录中已存在 .git,则跳过初始化
|
|
1687
4680
|
*/
|
|
1688
4681
|
const runGitInit = (projectPath) => {
|
|
1689
4682
|
// 检查是否已存在 .git 目录
|
|
1690
4683
|
const gitDir = path.join(projectPath, '.git');
|
|
4684
|
+
|
|
1691
4685
|
if (fs.existsSync(gitDir)) {
|
|
1692
4686
|
logger.info(
|
|
1693
4687
|
'\n💡 Git repository already exists, skipping git initialization',
|
|
@@ -1695,53 +4689,43 @@ const runGitInit = (projectPath) => {
|
|
|
1695
4689
|
return;
|
|
1696
4690
|
}
|
|
1697
4691
|
|
|
1698
|
-
const runGitCommand = (command) => {
|
|
1699
|
-
logger.info(`Executing: ${command}`);
|
|
1700
|
-
|
|
1701
|
-
const result = shelljs.exec(command, {
|
|
1702
|
-
cwd: projectPath,
|
|
1703
|
-
silent: true,
|
|
1704
|
-
});
|
|
1705
|
-
|
|
1706
|
-
// 输出命令的结果
|
|
1707
|
-
if (result.stdout) {
|
|
1708
|
-
process.stdout.write(result.stdout);
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
if (result.stderr) {
|
|
1712
|
-
process.stderr.write(result.stderr);
|
|
1713
|
-
}
|
|
1714
|
-
|
|
1715
|
-
if (result.code !== 0) {
|
|
1716
|
-
const errorMessage = [
|
|
1717
|
-
`${command} failed with exit code ${result.code}`,
|
|
1718
|
-
result.stderr ? `\nStderr:\n${result.stderr}` : '',
|
|
1719
|
-
result.stdout ? `\nStdout:\n${result.stdout}` : '',
|
|
1720
|
-
]
|
|
1721
|
-
.filter(Boolean)
|
|
1722
|
-
.join('');
|
|
1723
|
-
|
|
1724
|
-
throw new Error(errorMessage);
|
|
1725
|
-
}
|
|
1726
|
-
};
|
|
1727
|
-
|
|
1728
4692
|
try {
|
|
1729
4693
|
logger.info('\nInitializing git repository...');
|
|
1730
|
-
runGitCommand('git init');
|
|
1731
|
-
|
|
1732
|
-
logger.info('Adding files to git...');
|
|
1733
|
-
runGitCommand('git add .');
|
|
1734
|
-
|
|
1735
|
-
logger.info('Creating initial commit...');
|
|
1736
|
-
runGitCommand('git commit -m "chore: initial commit"');
|
|
1737
|
-
|
|
4694
|
+
runGitCommand('git init', projectPath);
|
|
1738
4695
|
logger.success('Git repository initialized successfully!');
|
|
1739
4696
|
} catch (error) {
|
|
1740
4697
|
// Git 初始化失败不应该导致整个流程失败
|
|
4698
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4699
|
+
logger.warn(`Git initialization failed: ${errorMessage}`);
|
|
4700
|
+
logger.info('You can manually initialize git later with: git init');
|
|
4701
|
+
}
|
|
4702
|
+
};
|
|
4703
|
+
|
|
4704
|
+
/**
|
|
4705
|
+
* 提交初始化生成的所有文件
|
|
4706
|
+
*/
|
|
4707
|
+
const commitChanges = (projectPath) => {
|
|
4708
|
+
// 检查是否存在 .git 目录
|
|
4709
|
+
const gitDir = path.join(projectPath, '.git');
|
|
4710
|
+
if (!fs.existsSync(gitDir)) {
|
|
1741
4711
|
logger.warn(
|
|
1742
|
-
|
|
4712
|
+
'\n⚠️ Git repository does not exist, skipping commit. Run git init first.',
|
|
4713
|
+
);
|
|
4714
|
+
return;
|
|
4715
|
+
}
|
|
4716
|
+
|
|
4717
|
+
try {
|
|
4718
|
+
logger.info('\nCommitting initialized files...');
|
|
4719
|
+
runGitCommand('git add --all', projectPath);
|
|
4720
|
+
runGitCommand('git commit -m "chore: init env"', projectPath);
|
|
4721
|
+
logger.success('Changes committed successfully!');
|
|
4722
|
+
} catch (error) {
|
|
4723
|
+
// Commit 失败不应该导致整个流程失败
|
|
4724
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4725
|
+
logger.warn(`Git commit failed: ${errorMessage}`);
|
|
4726
|
+
logger.info(
|
|
4727
|
+
'You can manually commit later with: git add --all && git commit -m "chore: init env"',
|
|
1743
4728
|
);
|
|
1744
|
-
logger.info('You can manually initialize git later with: git init');
|
|
1745
4729
|
}
|
|
1746
4730
|
};
|
|
1747
4731
|
|
|
@@ -1759,23 +4743,213 @@ const runDev = (projectPath) => {
|
|
|
1759
4743
|
|
|
1760
4744
|
logger.info(`Executing: ${cliPath} dev in ${projectPath}`);
|
|
1761
4745
|
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
4746
|
+
try {
|
|
4747
|
+
// 使用通用的后台执行函数启动开发服务器
|
|
4748
|
+
// 调用 CLI 自己的 dev 命令
|
|
4749
|
+
const pid = spawnDetached(process.argv[0], [cliPath, 'dev'], {
|
|
4750
|
+
cwd: projectPath,
|
|
4751
|
+
verbose: false, // 不输出额外的进程信息,由 logger 统一处理
|
|
4752
|
+
});
|
|
4753
|
+
|
|
4754
|
+
if (pid) {
|
|
4755
|
+
logger.success('Development server started in background!');
|
|
4756
|
+
logger.info(`Process ID: ${pid}`);
|
|
4757
|
+
logger.info(
|
|
4758
|
+
'\nThe dev server is running independently. You can close this terminal.',
|
|
4759
|
+
);
|
|
4760
|
+
logger.info(`To stop the server later, use: kill ${pid}`);
|
|
4761
|
+
} else {
|
|
4762
|
+
logger.warn('Failed to get process ID for dev server');
|
|
4763
|
+
}
|
|
4764
|
+
} catch (error) {
|
|
4765
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
4766
|
+
logger.error(`Failed to start dev server: ${errorMessage}`);
|
|
4767
|
+
}
|
|
4768
|
+
};
|
|
4769
|
+
|
|
4770
|
+
/**
|
|
4771
|
+
* Init 命令执行上下文
|
|
4772
|
+
*/
|
|
4773
|
+
|
|
4774
|
+
|
|
4775
|
+
|
|
4776
|
+
|
|
4777
|
+
|
|
4778
|
+
|
|
4779
|
+
|
|
4780
|
+
|
|
4781
|
+
|
|
4782
|
+
|
|
4783
|
+
|
|
4784
|
+
|
|
4785
|
+
|
|
4786
|
+
|
|
4787
|
+
|
|
4788
|
+
|
|
4789
|
+
|
|
4790
|
+
|
|
4791
|
+
|
|
4792
|
+
|
|
4793
|
+
|
|
4794
|
+
|
|
4795
|
+
|
|
4796
|
+
|
|
4797
|
+
|
|
4798
|
+
|
|
4799
|
+
|
|
4800
|
+
|
|
4801
|
+
|
|
4802
|
+
|
|
4803
|
+
|
|
4804
|
+
/**
|
|
4805
|
+
* 步骤1: 执行模板引擎
|
|
4806
|
+
*/
|
|
4807
|
+
const executeTemplateEngineStep = async ctx => {
|
|
4808
|
+
logger.info(`Initializing project with template: ${ctx.templateName}`);
|
|
4809
|
+
ctx.timer.logPhase('Initialization');
|
|
4810
|
+
|
|
4811
|
+
const result = await execute({
|
|
4812
|
+
templateName: ctx.templateName,
|
|
4813
|
+
outputPath: ctx.outputPath,
|
|
4814
|
+
command: ctx.command,
|
|
4815
|
+
force: _nullishCoalesce(ctx.options.force, () => ( false)),
|
|
1767
4816
|
});
|
|
1768
4817
|
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
4818
|
+
// 保存结果到上下文
|
|
4819
|
+
ctx.absoluteOutputPath = result.outputPath;
|
|
4820
|
+
ctx.templateConfig = result.templateConfig;
|
|
4821
|
+
ctx.context = result.context;
|
|
4822
|
+
|
|
4823
|
+
ctx.timer.logPhase('Template engine execution');
|
|
4824
|
+
logger.success('Project created successfully!');
|
|
4825
|
+
};
|
|
4826
|
+
|
|
4827
|
+
/**
|
|
4828
|
+
* 步骤2: 检查 package.json 是否存在
|
|
4829
|
+
*/
|
|
4830
|
+
const checkPackageJsonStep = ctx => {
|
|
4831
|
+
if (!ctx.absoluteOutputPath) {
|
|
4832
|
+
return;
|
|
4833
|
+
}
|
|
4834
|
+
|
|
4835
|
+
const packageJsonPath = path.join(ctx.absoluteOutputPath, 'package.json');
|
|
4836
|
+
ctx.hasPackageJson = fs.existsSync(packageJsonPath);
|
|
4837
|
+
};
|
|
4838
|
+
|
|
4839
|
+
/**
|
|
4840
|
+
* 步骤3: 安装依赖
|
|
4841
|
+
*/
|
|
4842
|
+
const installDependenciesStep = ctx => {
|
|
4843
|
+
if (!ctx.absoluteOutputPath) {
|
|
4844
|
+
return;
|
|
4845
|
+
}
|
|
4846
|
+
|
|
4847
|
+
const { skipInstall } = ctx.options;
|
|
4848
|
+
|
|
4849
|
+
if (!skipInstall) {
|
|
4850
|
+
if (ctx.hasPackageJson) {
|
|
4851
|
+
runPnpmInstall(ctx.absoluteOutputPath);
|
|
4852
|
+
ctx.timer.logPhase('Dependencies installation');
|
|
4853
|
+
} else {
|
|
4854
|
+
logger.info(
|
|
4855
|
+
'\n💡 No package.json found, skipping dependency installation',
|
|
4856
|
+
);
|
|
4857
|
+
}
|
|
4858
|
+
}
|
|
4859
|
+
};
|
|
4860
|
+
|
|
4861
|
+
/**
|
|
4862
|
+
* 步骤4: 执行完成钩子
|
|
4863
|
+
*/
|
|
4864
|
+
const executeCompleteHookStep = async ctx => {
|
|
4865
|
+
if (!ctx.absoluteOutputPath || !ctx.templateConfig || !ctx.context) {
|
|
4866
|
+
return;
|
|
4867
|
+
}
|
|
4868
|
+
|
|
4869
|
+
await executeCompleteHook(
|
|
4870
|
+
ctx.templateConfig,
|
|
4871
|
+
ctx.context,
|
|
4872
|
+
ctx.absoluteOutputPath,
|
|
4873
|
+
);
|
|
4874
|
+
|
|
4875
|
+
ctx.timer.logPhase('Complete hook execution');
|
|
4876
|
+
};
|
|
4877
|
+
|
|
4878
|
+
/**
|
|
4879
|
+
* 步骤5: 初始化 Git 仓库
|
|
4880
|
+
*/
|
|
4881
|
+
const gitInitStep = ctx => {
|
|
4882
|
+
if (!ctx.absoluteOutputPath) {
|
|
4883
|
+
return;
|
|
4884
|
+
}
|
|
4885
|
+
|
|
4886
|
+
const { skipGit } = ctx.options;
|
|
4887
|
+
|
|
4888
|
+
if (!skipGit) {
|
|
4889
|
+
runGitInit(ctx.absoluteOutputPath);
|
|
4890
|
+
ctx.timer.logPhase('Git initialization');
|
|
4891
|
+
}
|
|
4892
|
+
};
|
|
4893
|
+
|
|
4894
|
+
/**
|
|
4895
|
+
* 步骤6: 提交 Git 变更
|
|
4896
|
+
*/
|
|
4897
|
+
const gitCommitStep = ctx => {
|
|
4898
|
+
if (!ctx.absoluteOutputPath) {
|
|
4899
|
+
return;
|
|
4900
|
+
}
|
|
4901
|
+
|
|
4902
|
+
const { skipCommit } = ctx.options;
|
|
4903
|
+
|
|
4904
|
+
if (!skipCommit) {
|
|
4905
|
+
commitChanges(ctx.absoluteOutputPath);
|
|
4906
|
+
ctx.timer.logPhase('Git commit');
|
|
4907
|
+
}
|
|
4908
|
+
};
|
|
4909
|
+
|
|
4910
|
+
/**
|
|
4911
|
+
* 步骤7: 启动开发服务器
|
|
4912
|
+
*/
|
|
4913
|
+
const startDevServerStep = ctx => {
|
|
4914
|
+
if (!ctx.absoluteOutputPath) {
|
|
4915
|
+
return;
|
|
4916
|
+
}
|
|
4917
|
+
|
|
4918
|
+
const { skipDev, skipInstall, skipGit, skipCommit } = ctx.options;
|
|
4919
|
+
|
|
4920
|
+
if (!skipDev) {
|
|
4921
|
+
runDev(ctx.absoluteOutputPath);
|
|
4922
|
+
ctx.timer.logPhase('Dev server startup');
|
|
4923
|
+
} else {
|
|
4924
|
+
// 只有跳过 dev 时才显示 Next steps
|
|
4925
|
+
logger.info('\nNext steps:');
|
|
4926
|
+
logger.info(` cd ${ctx.outputPath}`);
|
|
4927
|
+
if (skipInstall && ctx.hasPackageJson) {
|
|
4928
|
+
logger.info(' pnpm install');
|
|
4929
|
+
}
|
|
4930
|
+
if (skipGit) {
|
|
4931
|
+
logger.info(' git init');
|
|
4932
|
+
}
|
|
4933
|
+
if (skipCommit) {
|
|
4934
|
+
logger.info(' git add --all && git commit -m "chore: init env"');
|
|
4935
|
+
}
|
|
4936
|
+
logger.info(' coze dev');
|
|
1776
4937
|
}
|
|
1777
4938
|
};
|
|
1778
4939
|
|
|
4940
|
+
/**
|
|
4941
|
+
* Init 命令的所有执行步骤
|
|
4942
|
+
*/
|
|
4943
|
+
const initSteps = [
|
|
4944
|
+
executeTemplateEngineStep,
|
|
4945
|
+
checkPackageJsonStep,
|
|
4946
|
+
installDependenciesStep,
|
|
4947
|
+
executeCompleteHookStep,
|
|
4948
|
+
gitInitStep,
|
|
4949
|
+
gitCommitStep,
|
|
4950
|
+
startDevServerStep,
|
|
4951
|
+
];
|
|
4952
|
+
|
|
1779
4953
|
/**
|
|
1780
4954
|
* 执行 init 命令的内部实现
|
|
1781
4955
|
*/
|
|
@@ -1786,80 +4960,68 @@ const executeInit = async (
|
|
|
1786
4960
|
|
|
1787
4961
|
|
|
1788
4962
|
|
|
4963
|
+
|
|
4964
|
+
|
|
1789
4965
|
,
|
|
1790
4966
|
command,
|
|
1791
4967
|
) => {
|
|
1792
4968
|
const timer = new TimeTracker();
|
|
4969
|
+
const cmdStartTime = Date.now();
|
|
4970
|
+
|
|
4971
|
+
// 初始化执行上下文
|
|
4972
|
+
const ctx = {
|
|
4973
|
+
options,
|
|
4974
|
+
command,
|
|
4975
|
+
timer,
|
|
4976
|
+
cmdStartTime,
|
|
4977
|
+
templateName: options.template,
|
|
4978
|
+
outputPath: options.output,
|
|
4979
|
+
};
|
|
1793
4980
|
|
|
1794
4981
|
try {
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
} = options;
|
|
1802
|
-
|
|
1803
|
-
logger.info(`Initializing project with template: ${templateName}`);
|
|
1804
|
-
timer.logPhase('Initialization');
|
|
1805
|
-
|
|
1806
|
-
// 执行模板引擎,返回绝对路径
|
|
1807
|
-
const absoluteOutputPath = await execute({
|
|
1808
|
-
templateName,
|
|
1809
|
-
outputPath,
|
|
1810
|
-
command,
|
|
1811
|
-
});
|
|
1812
|
-
|
|
1813
|
-
timer.logPhase('Template engine execution');
|
|
1814
|
-
logger.success('Project created successfully!');
|
|
1815
|
-
|
|
1816
|
-
// 如果没有跳过安装,检查是否需要运行 pnpm install
|
|
1817
|
-
if (!skipInstall) {
|
|
1818
|
-
const nodeModulesPath = path.join(absoluteOutputPath, 'node_modules');
|
|
1819
|
-
const hasNodeModules = fs.existsSync(nodeModulesPath);
|
|
1820
|
-
|
|
1821
|
-
if (hasNodeModules) {
|
|
1822
|
-
logger.info(
|
|
1823
|
-
'\n💡 Using pre-warmed node_modules, skipping pnpm install',
|
|
1824
|
-
);
|
|
1825
|
-
timer.logPhase('Node modules (pre-warmed)');
|
|
1826
|
-
} else {
|
|
1827
|
-
runPnpmInstall(absoluteOutputPath);
|
|
1828
|
-
timer.logPhase('Dependencies installation');
|
|
1829
|
-
}
|
|
1830
|
-
}
|
|
1831
|
-
|
|
1832
|
-
// 如果没有跳过 git,则初始化 git 仓库
|
|
1833
|
-
if (!skipGit) {
|
|
1834
|
-
runGitInit(absoluteOutputPath);
|
|
1835
|
-
timer.logPhase('Git initialization');
|
|
1836
|
-
}
|
|
4982
|
+
// 上报命令开始
|
|
4983
|
+
reportCommandStart(
|
|
4984
|
+
'init',
|
|
4985
|
+
JSON.stringify({ template: ctx.templateName, output: ctx.outputPath }),
|
|
4986
|
+
{ template: ctx.templateName },
|
|
4987
|
+
);
|
|
1837
4988
|
|
|
1838
|
-
//
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
timer.logPhase('Dev server startup');
|
|
1842
|
-
} else {
|
|
1843
|
-
// 只有跳过 dev 时才显示 Next steps
|
|
1844
|
-
logger.info('\nNext steps:');
|
|
1845
|
-
logger.info(` cd ${outputPath}`);
|
|
1846
|
-
if (skipInstall) {
|
|
1847
|
-
logger.info(' pnpm install');
|
|
1848
|
-
}
|
|
1849
|
-
if (skipGit) {
|
|
1850
|
-
logger.info(
|
|
1851
|
-
' git init && git add . && git commit -m "initial commit"',
|
|
1852
|
-
);
|
|
1853
|
-
}
|
|
1854
|
-
logger.info(' coze dev');
|
|
4989
|
+
// 逐个执行步骤
|
|
4990
|
+
for (const step of initSteps) {
|
|
4991
|
+
await step(ctx);
|
|
1855
4992
|
}
|
|
1856
4993
|
|
|
1857
4994
|
// 输出总耗时
|
|
1858
4995
|
timer.logTotal();
|
|
4996
|
+
|
|
4997
|
+
// 上报命令成功
|
|
4998
|
+
reportCommandComplete('init', true, Date.now() - cmdStartTime, {
|
|
4999
|
+
args: JSON.stringify(options),
|
|
5000
|
+
categories: { template: ctx.templateName },
|
|
5001
|
+
});
|
|
5002
|
+
// flush 由 main 函数统一处理
|
|
1859
5003
|
} catch (error) {
|
|
1860
5004
|
timer.logTotal();
|
|
5005
|
+
|
|
5006
|
+
// 上报错误
|
|
5007
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
5008
|
+
reportError(err, {
|
|
5009
|
+
command: 'init',
|
|
5010
|
+
template: options.template,
|
|
5011
|
+
output: options.output,
|
|
5012
|
+
});
|
|
5013
|
+
|
|
5014
|
+
// 上报命令失败
|
|
5015
|
+
reportCommandComplete('init', false, Date.now() - cmdStartTime, {
|
|
5016
|
+
args: JSON.stringify(options),
|
|
5017
|
+
errorCode: 1,
|
|
5018
|
+
errorMessage: err.message,
|
|
5019
|
+
categories: { template: options.template },
|
|
5020
|
+
});
|
|
5021
|
+
await flushSlardar();
|
|
5022
|
+
|
|
1861
5023
|
logger.error('Failed to initialize project:');
|
|
1862
|
-
logger.error(
|
|
5024
|
+
logger.error(err.message);
|
|
1863
5025
|
process.exit(1);
|
|
1864
5026
|
}
|
|
1865
5027
|
};
|
|
@@ -1876,26 +5038,55 @@ const registerCommand = program => {
|
|
|
1876
5038
|
.option('-o, --output <path>', 'Output directory', process.cwd())
|
|
1877
5039
|
.option('--skip-install', 'Skip automatic pnpm install', false)
|
|
1878
5040
|
.option('--skip-git', 'Skip automatic git initialization', false)
|
|
5041
|
+
.option(
|
|
5042
|
+
'--skip-commit',
|
|
5043
|
+
'Skip automatic git commit after initialization',
|
|
5044
|
+
false,
|
|
5045
|
+
)
|
|
1879
5046
|
.option('--skip-dev', 'Skip automatic dev server start', false)
|
|
1880
5047
|
.allowUnknownOption() // 允许透传参数
|
|
1881
5048
|
.action(async (directory, options, command) => {
|
|
1882
5049
|
// 位置参数优先级高于 --output 选项
|
|
1883
5050
|
const outputPath = _nullishCoalesce(directory, () => ( options.output));
|
|
1884
|
-
|
|
5051
|
+
// Always use force mode - overwrite existing files without conflict check
|
|
5052
|
+
const force = true;
|
|
5053
|
+
await executeInit({ ...options, output: outputPath, force }, command);
|
|
1885
5054
|
});
|
|
1886
5055
|
};
|
|
1887
5056
|
|
|
1888
|
-
var version = "0.0.1-alpha.c186ff";
|
|
1889
|
-
var packageJson = {
|
|
1890
|
-
version: version};
|
|
1891
|
-
|
|
1892
5057
|
const commands = [
|
|
1893
5058
|
registerCommand,
|
|
1894
5059
|
registerCommand$1,
|
|
5060
|
+
registerCommand$4,
|
|
1895
5061
|
registerCommand$2,
|
|
5062
|
+
registerCommand$3,
|
|
1896
5063
|
];
|
|
1897
5064
|
|
|
1898
|
-
const main = () => {
|
|
5065
|
+
const main = async () => {
|
|
5066
|
+
// 初始化 Slardar 监控
|
|
5067
|
+
initSlardar();
|
|
5068
|
+
|
|
5069
|
+
// 监听未捕获的异常
|
|
5070
|
+
process.on('uncaughtException', async (error) => {
|
|
5071
|
+
reportError(error, {
|
|
5072
|
+
type: 'uncaughtException',
|
|
5073
|
+
context: 'global',
|
|
5074
|
+
});
|
|
5075
|
+
await flushSlardar();
|
|
5076
|
+
process.exit(1);
|
|
5077
|
+
});
|
|
5078
|
+
|
|
5079
|
+
// 监听未处理的 Promise rejection
|
|
5080
|
+
process.on('unhandledRejection', async (reason) => {
|
|
5081
|
+
const error = reason instanceof Error ? reason : new Error(String(reason));
|
|
5082
|
+
reportError(error, {
|
|
5083
|
+
type: 'unhandledRejection',
|
|
5084
|
+
context: 'global',
|
|
5085
|
+
});
|
|
5086
|
+
await flushSlardar();
|
|
5087
|
+
process.exit(1);
|
|
5088
|
+
});
|
|
5089
|
+
|
|
1899
5090
|
const program = new commander.Command();
|
|
1900
5091
|
|
|
1901
5092
|
program
|
|
@@ -1910,7 +5101,12 @@ const main = () => {
|
|
|
1910
5101
|
// 在 help 输出中添加模板信息
|
|
1911
5102
|
program.addHelpText('after', generateTemplatesHelpText());
|
|
1912
5103
|
|
|
1913
|
-
|
|
5104
|
+
// 使用 parseAsync 等待命令执行完成
|
|
5105
|
+
await program.parseAsync(process.argv);
|
|
5106
|
+
|
|
5107
|
+
// 统一在命令执行完成后 flush
|
|
5108
|
+
// 注意:如果命令中调用了 process.exit(),不会执行到这里
|
|
5109
|
+
await flushSlardar();
|
|
1914
5110
|
};
|
|
1915
5111
|
|
|
1916
5112
|
main();
|