@playcraft/devkit 1.0.18 → 1.0.20

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.
Files changed (50) hide show
  1. package/cli/bin/playable-scripts.js +1 -44
  2. package/cli/commands/build.js +18 -130
  3. package/cli/commands/builds.js +2 -13
  4. package/cli/commands/dev.js +1 -7
  5. package/cli/commands/pack.js +39 -308
  6. package/cli/commands/vite-dev.js +1 -7
  7. package/core/batch-build.js +125 -1091
  8. package/core/constants.js +6 -0
  9. package/core/cos-uploader.js +20 -264
  10. package/core/index.d.ts +9 -0
  11. package/core/index.js +7 -71
  12. package/core/loaders/gltf-loader.js +28 -74
  13. package/core/options.js +15 -310
  14. package/core/plugins/AdikteevInjectorPlugin.js +4 -26
  15. package/core/plugins/BigoAdsInjectorPlugin.js +4 -21
  16. package/core/plugins/DAPIInjectorPlugin.js +2 -25
  17. package/core/plugins/DebuggerInjectionPlugin.js +3 -31
  18. package/core/plugins/ExitAPIInjectorPlugin.js +3 -57
  19. package/core/plugins/FflateCompressionPlugin.js +37 -225
  20. package/core/plugins/FontInjectionWebpackPlugin.js +4 -46
  21. package/core/plugins/LiftoffInjectorPlugin.js +4 -26
  22. package/core/plugins/MRAIDInjectorPlugin.js +2 -24
  23. package/core/plugins/MintegralInjectorPlugin.js +2 -25
  24. package/core/plugins/PangleInjectorPlugin.js +2 -24
  25. package/core/plugins/SnapchatInjectorPlugin.js +4 -26
  26. package/core/plugins/TikTokInjectorPlugin.js +2 -24
  27. package/core/plugins/UnityInjectorPlugin.js +8 -37
  28. package/core/plugins/ZipPlugin.js +19 -138
  29. package/core/utils/buildDefines.js +2 -29
  30. package/core/utils/buildTemplateString.js +5 -40
  31. package/core/utils/date.js +1 -16
  32. package/core/utils/fontRegistry.js +13 -81
  33. package/core/utils/fontResolver.js +37 -335
  34. package/core/utils/generateAdikteevHtmlWebpackPluginConfig.js +7 -42
  35. package/core/utils/generateBigabidHtmlWebpackPluginConfig.js +3 -30
  36. package/core/utils/generateInMobiHtmlWebpackPluginConfig.js +7 -43
  37. package/core/utils/injectSDKPlugins.js +11 -58
  38. package/core/utils/logOptions.js +4 -37
  39. package/core/utils/mergeOptions.js +2 -19
  40. package/core/utils/parseArgvOptions.js +5 -110
  41. package/core/utils/resolveChannelFold2Zip.js +3 -15
  42. package/core/utils/validateThemeData.js +25 -220
  43. package/core/validators/pre-build-checker.js +21 -201
  44. package/core/validators/tracking-validator.js +40 -358
  45. package/core/vite.dev.js +17 -326
  46. package/core/webpack.build.js +56 -489
  47. package/core/webpack.common.js +11 -177
  48. package/core/webpack.dev.js +5 -82
  49. package/package.json +2 -3
  50. package/README.md +0 -259
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ "use strict";/**
3
+ * 全局常量配置
4
+ *
5
+ * 集中管理服务端 API 地址、构建参数等常量,方便切换环境。
6
+ *//** 服务端 API 基础地址 */const API_BASE_URL="http://9.135.99.22";module.exports={API_BASE_URL};
@@ -1,86 +1,17 @@
1
1
  #!/usr/bin/env node
2
-
3
- 'use strict';
4
-
5
- /**
2
+ "use strict";/**
6
3
  * COS 上传模块
7
4
  *
8
5
  * 功能:上传文件到腾讯云 COS,并刷新 CDN 缓存
9
- */
10
-
11
- const COS = require('cos-nodejs-sdk-v5');
12
- const fs = require('fs');
13
- const path = require('path');
14
-
15
- /**
6
+ */const COS=require("cos-nodejs-sdk-v5");const fs=require("fs");const path=require("path");const http=require("http");const{API_BASE_URL}=require("./constants");/**
16
7
  * 从远程 API 获取临时凭证
17
- */
18
- async function fetchTemporaryCredentials() {
19
- const https = require('https');
20
- const credentialsUrl = 'https://wd-act.woa.com/aix/playable/api/cos/credentials';
21
-
22
- return new Promise((resolve, reject) => {
23
- https.get(credentialsUrl, (response) => {
24
- if (response.statusCode !== 200) {
25
- reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));
26
- return;
27
- }
28
-
29
- let data = '';
30
- response.on('data', chunk => data += chunk);
31
- response.on('end', () => {
32
- try {
33
- const result = JSON.parse(data);
34
- if (result.code === 'SUCCESS') {
35
- resolve(result.data);
36
- } else {
37
- reject(new Error(result.message || 'Failed to get credentials'));
38
- }
39
- } catch (error) {
40
- reject(new Error(`Failed to parse response: ${error.message}`));
41
- }
42
- });
43
- }).on('error', reject)
44
- .setTimeout(30000, function() {
45
- this.destroy();
46
- reject(new Error('Request timeout'));
47
- });
48
- });
49
- }
50
-
51
- /**
8
+ */async function fetchTemporaryCredentials(){const credentialsUrl=`${API_BASE_URL}/aix/playable/api/cos/credentials`;return new Promise((resolve,reject)=>{http.get(credentialsUrl,response=>{if(response.statusCode!==200){reject(new Error(`HTTP ${response.statusCode}: ${response.statusMessage}`));return}let data="";response.on("data",chunk=>data+=chunk);response.on("end",()=>{try{const result=JSON.parse(data);if(result.code==="SUCCESS"){resolve(result.data)}else{reject(new Error(result.message||"Failed to get credentials"))}}catch(error){reject(new Error(`Failed to parse response: ${error.message}`))}})}).on("error",reject).setTimeout(30000,function(){this.destroy();reject(new Error("Request timeout"))})})}/**
52
9
  * 创建 COS 客户端(使用临时凭证)
53
10
  * @param {Object} cosConfig - COS 配置
54
11
  * @param {Object} credentials - 已获取的临时凭证(可选,避免重复请求)
55
- */
56
- async function createCOSClient(cosConfig, credentials = null) {
57
- // 如果配置中提供了 SecretId 和 SecretKey,使用传统方式
58
- if (cosConfig && cosConfig.SecretId && cosConfig.SecretKey) {
59
- return new COS({
60
- SecretId: cosConfig.SecretId,
61
- SecretKey: cosConfig.SecretKey,
62
- });
63
- }
64
-
65
- // 使用已传入的凭证,或重新获取
66
- if (!credentials) {
67
- credentials = await fetchTemporaryCredentials();
68
- }
69
-
70
- return new COS({
71
- getAuthorization: (options, callback) => {
72
- callback({
73
- TmpSecretId: credentials.credentials.tmpSecretId,
74
- TmpSecretKey: credentials.credentials.tmpSecretKey,
75
- SecurityToken: credentials.credentials.sessionToken,
76
- StartTime: credentials.startTime,
77
- ExpiredTime: credentials.expiredTime,
78
- });
79
- },
80
- });
81
- }
82
-
83
- /**
12
+ */async function createCOSClient(cosConfig,credentials=null){// 如果配置中提供了 SecretId 和 SecretKey,使用传统方式
13
+ if(cosConfig&&cosConfig.SecretId&&cosConfig.SecretKey){return new COS({SecretId:cosConfig.SecretId,SecretKey:cosConfig.SecretKey})}// 使用已传入的凭证,或重新获取
14
+ if(!credentials){credentials=await fetchTemporaryCredentials()}return new COS({getAuthorization:(options,callback)=>{callback({TmpSecretId:credentials.credentials.tmpSecretId,TmpSecretKey:credentials.credentials.tmpSecretKey,SecurityToken:credentials.credentials.sessionToken,StartTime:credentials.startTime,ExpiredTime:credentials.expiredTime})}})}/**
84
15
  * 刷新 CDN 缓存
85
16
  * 通过服务端 API 代理执行(STS 临时凭证无法携带 CDN 权限,需由服务端使用主账号凭证刷新)
86
17
  *
@@ -88,82 +19,9 @@ async function createCOSClient(cosConfig, credentials = null) {
88
19
  * @param {Array<string>} urls - 需要刷新的 URL 列表
89
20
  * @param {Object} credentials - 未使用,保留参数兼容性
90
21
  * @returns {Promise<void>}
91
- */
92
- async function purgeCDNCache(cosConfig, urls, credentials = null) {
93
- if (!urls || urls.length === 0) {
94
- return;
95
- }
96
-
97
- const area = cosConfig?.CdnArea || 'overseas';
98
-
99
- // 如果配置中有 SecretId/SecretKey,本地直接刷新
100
- if (cosConfig && cosConfig.SecretId && cosConfig.SecretKey) {
101
- try {
102
- const tencentcloud = require('tencentcloud-sdk-nodejs-cdn');
103
- const client = new tencentcloud.cdn.v20180606.Client({
104
- credential: { secretId: cosConfig.SecretId, secretKey: cosConfig.SecretKey },
105
- region: '',
106
- profile: { httpProfile: { endpoint: 'cdn.tencentcloudapi.com' } },
107
- });
108
- const batchSize = 1000;
109
- for (let i = 0; i < urls.length; i += batchSize) {
110
- const batch = urls.slice(i, i + batchSize);
111
- await client.PurgeUrlsCache({ Urls: batch, Area: area });
112
- console.log(`✓ CDN 缓存已刷新 ${batch.length} 个 URL (区域: ${area})`);
113
- }
114
- } catch (error) {
115
- if (error.code === 'MODULE_NOT_FOUND') {
116
- console.warn('⚠️ 未安装 tencentcloud-sdk-nodejs-cdn,跳过 CDN 刷新');
117
- } else {
118
- throw error;
119
- }
120
- }
121
- return;
122
- }
123
-
124
- // 否则通过服务端 API 代理刷新
125
- const https = require('https');
126
- const purgeUrl = 'https://wd-act.woa.com/aix/playable/api/cdn/purge';
127
-
128
- const postData = JSON.stringify({ urls, area });
129
-
130
- return new Promise((resolve, reject) => {
131
- const req = https.request(purgeUrl, {
132
- method: 'POST',
133
- headers: {
134
- 'Content-Type': 'application/json',
135
- 'Content-Length': Buffer.byteLength(postData),
136
- },
137
- }, (response) => {
138
- let data = '';
139
- response.on('data', chunk => data += chunk);
140
- response.on('end', () => {
141
- try {
142
- const result = JSON.parse(data);
143
- if (result.code === 'SUCCESS') {
144
- console.log(`✓ CDN 缓存已刷新 ${urls.length} 个 URL (区域: ${area})`);
145
- resolve();
146
- } else {
147
- reject(new Error(result.message || 'CDN purge failed'));
148
- }
149
- } catch (error) {
150
- reject(new Error(`Failed to parse CDN purge response: ${error.message}`));
151
- }
152
- });
153
- });
154
-
155
- req.on('error', reject);
156
- req.setTimeout(30000, function() {
157
- this.destroy();
158
- reject(new Error('CDN purge request timeout'));
159
- });
160
-
161
- req.write(postData);
162
- req.end();
163
- });
164
- }
165
-
166
- /**
22
+ */async function purgeCDNCache(cosConfig,urls,credentials=null){if(!urls||urls.length===0){return}const area=(cosConfig===null||cosConfig===void 0?void 0:cosConfig.CdnArea)||"overseas";// 如果配置中有 SecretId/SecretKey,本地直接刷新
23
+ if(cosConfig&&cosConfig.SecretId&&cosConfig.SecretKey){try{const tencentcloud=require("tencentcloud-sdk-nodejs-cdn");const client=new tencentcloud.cdn.v20180606.Client({credential:{secretId:cosConfig.SecretId,secretKey:cosConfig.SecretKey},region:"",profile:{httpProfile:{endpoint:"cdn.tencentcloudapi.com"}}});const batchSize=1000;for(let i=0;i<urls.length;i+=batchSize){const batch=urls.slice(i,i+batchSize);await client.PurgeUrlsCache({Urls:batch,Area:area});console.log(`✓ CDN 缓存已刷新 ${batch.length} 个 URL (区域: ${area})`)}}catch(error){if(error.code==="MODULE_NOT_FOUND"){console.warn("\u26A0\uFE0F \u672A\u5B89\u88C5 tencentcloud-sdk-nodejs-cdn\uFF0C\u8DF3\u8FC7 CDN \u5237\u65B0")}else{throw error}}return}// 否则通过服务端 API 代理刷新
24
+ const purgeUrl=`${API_BASE_URL}/aix/playable/api/cdn/purge`;const postData=JSON.stringify({urls,area});return new Promise((resolve,reject)=>{const req=http.request(purgeUrl,{method:"POST",headers:{"Content-Type":"application/json","Content-Length":Buffer.byteLength(postData)}},response=>{let data="";response.on("data",chunk=>data+=chunk);response.on("end",()=>{try{const result=JSON.parse(data);if(result.code==="SUCCESS"){console.log(`✓ CDN 缓存已刷新 ${urls.length} 个 URL (区域: ${area})`);resolve()}else{reject(new Error(result.message||"CDN purge failed"))}}catch(error){reject(new Error(`Failed to parse CDN purge response: ${error.message}`))}})});req.on("error",reject);req.setTimeout(30000,function(){this.destroy();reject(new Error("CDN purge request timeout"))});req.write(postData);req.end()})}/**
167
25
  * 上传单个文件到 COS
168
26
  *
169
27
  * @param {Object} cosConfig - COS 配置
@@ -171,58 +29,12 @@ async function purgeCDNCache(cosConfig, urls, credentials = null) {
171
29
  * @param {string} cosKey - COS 对象键(远程路径)
172
30
  * @param {Object} credentials - 可选的凭证对象
173
31
  * @returns {Promise<string>} - 上传成功返回访问链接
174
- */
175
- async function uploadFileToCOS(cosConfig, filePath, cosKey, credentials = null) {
176
- if (!fs.existsSync(filePath)) {
177
- throw new Error(`文件不存在: ${filePath}`);
178
- }
179
-
180
- // 获取临时凭证和配置信息
181
- let bucket, region, cdnDomain;
182
-
183
- if (cosConfig && cosConfig.Bucket && cosConfig.Region) {
184
- // 使用配置文件中的信息
185
- bucket = cosConfig.Bucket;
186
- region = cosConfig.Region;
187
- cdnDomain = cosConfig.CdnDomain || 'static.aix.intlgame.com';
188
- } else {
189
- // 从临时凭证 API 获取配置(优先使用传入的凭证,否则重新获取)
190
- if (!credentials) {
191
- credentials = await fetchTemporaryCredentials();
192
- }
193
- bucket = credentials.bucket;
194
- region = credentials.region;
195
- cdnDomain = 'static.aix.intlgame.com';
196
- }
197
-
198
- const cos = await createCOSClient(cosConfig || null, credentials);
199
-
200
- return new Promise((resolve, reject) => {
201
- cos.putObject(
202
- {
203
- Bucket: bucket,
204
- Region: region,
205
- Key: cosKey,
206
- Body: fs.createReadStream(filePath),
207
- },
208
- (err, data) => {
209
- if (err) {
210
- reject(new Error(`上传失败: ${err.message}`));
211
- } else {
212
- // 生成访问链接
213
- // 对路径的每个部分分别进行 URL 编码
214
- const pathParts = cosKey.split('/');
215
- const encodedPath = pathParts.map(part => encodeURIComponent(part)).join('/');
216
-
217
- const url = `https://${cdnDomain}/${encodedPath}`;
218
- resolve(url);
219
- }
220
- }
221
- );
222
- });
223
- }
224
-
225
- /**
32
+ */async function uploadFileToCOS(cosConfig,filePath,cosKey,credentials=null){if(!fs.existsSync(filePath)){throw new Error(`文件不存在: ${filePath}`)}// 获取临时凭证和配置信息
33
+ let bucket,region,cdnDomain;if(cosConfig&&cosConfig.Bucket&&cosConfig.Region){// 使用配置文件中的信息
34
+ bucket=cosConfig.Bucket;region=cosConfig.Region;cdnDomain=cosConfig.CdnDomain||"static.aix.intlgame.com"}else{// 从临时凭证 API 获取配置(优先使用传入的凭证,否则重新获取)
35
+ if(!credentials){credentials=await fetchTemporaryCredentials()}bucket=credentials.bucket;region=credentials.region;cdnDomain="static.aix.intlgame.com"}const cos=await createCOSClient(cosConfig||null,credentials);return new Promise((resolve,reject)=>{cos.putObject({Bucket:bucket,Region:region,Key:cosKey,Body:fs.createReadStream(filePath)},(err,data)=>{if(err){reject(new Error(`上传失败: ${err.message}`))}else{// 生成访问链接
36
+ // 对路径的每个部分分别进行 URL 编码
37
+ const pathParts=cosKey.split("/");const encodedPath=pathParts.map(part=>encodeURIComponent(part)).join("/");const url=`https://${cdnDomain}/${encodedPath}`;resolve(url)}})})}/**
226
38
  * 上传目录下的所有文件到 COS
227
39
  *
228
40
  * @param {Object} cosConfig - COS 配置
@@ -230,64 +42,8 @@ async function uploadFileToCOS(cosConfig, filePath, cosKey, credentials = null)
230
42
  * @param {string} cosPrefix - COS 路径前缀
231
43
  * @param {Object} credentials - 可选的凭证对象
232
44
  * @returns {Promise<Array>} - 上传成功返回文件列表
233
- */
234
- async function uploadDirectoryToCOS(cosConfig, localDir, cosPrefix, credentials = null) {
235
- if (!fs.existsSync(localDir)) {
236
- throw new Error(`目录不存在: ${localDir}`);
237
- }
238
-
239
- // 如果没有传入凭证且没有配置,获取一次凭证用于所有文件上传
240
- if (!credentials && (!cosConfig || !cosConfig.Bucket || !cosConfig.Region)) {
241
- try {
242
- credentials = await fetchTemporaryCredentials();
243
- console.log('✓ 获取临时凭证成功');
244
- } catch (error) {
245
- throw new Error(`获取临时凭证失败: ${error.message}`);
246
- }
247
- }
248
-
249
- const files = fs.readdirSync(localDir);
250
- const results = [];
251
- const uploadedUrls = [];
252
-
253
- for (const file of files) {
254
- const filePath = path.join(localDir, file);
255
- const stat = fs.statSync(filePath);
256
-
257
- if (stat.isFile()) {
258
- // 去掉文件名中的 -preview 或 _preview 后缀
259
- let cleanFileName = file;
260
- cleanFileName = cleanFileName.replace(/-preview\./i, '.');
261
- cleanFileName = cleanFileName.replace(/_preview\./i, '.');
262
-
263
- const cosKey = cosPrefix ? `${cosPrefix}/${cleanFileName}` : cleanFileName;
264
- // 传入凭证,避免重复获取
265
- const url = await uploadFileToCOS(cosConfig, filePath, cosKey, credentials);
266
- results.push({
267
- file: cleanFileName,
268
- url,
269
- });
270
- uploadedUrls.push(url);
271
- }
272
- }
273
-
274
- // 上传完成后刷新 CDN 缓存,传入凭证
275
- if (uploadedUrls.length > 0) {
276
- try {
277
- await purgeCDNCache(cosConfig, uploadedUrls, credentials);
278
- console.log(`✓ CDN 缓存刷新成功 (${uploadedUrls.length} 个文件)`);
279
- } catch (error) {
280
- console.warn(`⚠️ CDN 刷新失败: ${error.message}`);
281
- }
282
- }
283
-
284
- return results;
285
- }
286
-
287
- module.exports = {
288
- fetchTemporaryCredentials,
289
- createCOSClient,
290
- uploadFileToCOS,
291
- uploadDirectoryToCOS,
292
- purgeCDNCache,
293
- };
45
+ */async function uploadDirectoryToCOS(cosConfig,localDir,cosPrefix,credentials=null){if(!fs.existsSync(localDir)){throw new Error(`目录不存在: ${localDir}`)}// 如果没有传入凭证且没有配置,获取一次凭证用于所有文件上传
46
+ if(!credentials&&(!cosConfig||!cosConfig.Bucket||!cosConfig.Region)){try{credentials=await fetchTemporaryCredentials();console.log("\u2713 \u83B7\u53D6\u4E34\u65F6\u51ED\u8BC1\u6210\u529F")}catch(error){throw new Error(`获取临时凭证失败: ${error.message}`)}}const files=fs.readdirSync(localDir);const results=[];const uploadedUrls=[];for(const file of files){const filePath=path.join(localDir,file);const stat=fs.statSync(filePath);if(stat.isFile()){// 去掉文件名中的 -preview 或 _preview 后缀
47
+ let cleanFileName=file;cleanFileName=cleanFileName.replace(/-preview\./i,".");cleanFileName=cleanFileName.replace(/_preview\./i,".");const cosKey=cosPrefix?`${cosPrefix}/${cleanFileName}`:cleanFileName;// 传入凭证,避免重复获取
48
+ const url=await uploadFileToCOS(cosConfig,filePath,cosKey,credentials);results.push({file:cleanFileName,url});uploadedUrls.push(url)}}// 上传完成后刷新 CDN 缓存,传入凭证
49
+ if(uploadedUrls.length>0){try{await purgeCDNCache(cosConfig,uploadedUrls,credentials);console.log(`✓ CDN 缓存刷新成功 (${uploadedUrls.length} 个文件)`)}catch(error){console.warn(`⚠️ CDN 刷新失败: ${error.message}`)}}return results}module.exports={fetchTemporaryCredentials,createCOSClient,uploadFileToCOS,uploadDirectoryToCOS,purgeCDNCache};
package/core/index.d.ts CHANGED
@@ -183,3 +183,12 @@ export declare function runViteDev(
183
183
  customDefines?: Record<string, any>,
184
184
  viteCustomConfig?: Partial<ViteUserConfig>
185
185
  ): Promise<void>;
186
+
187
+ /** Vite plugin for .json5 file imports */
188
+ export declare function json5Plugin(): import('vite').Plugin;
189
+
190
+ /** Vite plugin for asset inlining (.atlas, .fnt, .xml) */
191
+ export declare function assetInlinePlugin(): import('vite').Plugin;
192
+
193
+ /** Vite plugin for font injection (base64 @font-face in <head>) */
194
+ export declare function fontFamilyPlugin(): import('vite').Plugin;
package/core/index.js CHANGED
@@ -1,71 +1,7 @@
1
- const { allowedExtensions, webpackCommonConfig } = require('./webpack.common.js');
2
- const { parseArgvOptions } = require('./utils/parseArgvOptions.js');
3
- const { mergeOptions } = require('./utils/mergeOptions.js');
4
- const { options } = require('./options.js');
5
- const { DAPIInjectorPlugin } = require('./plugins/DAPIInjectorPlugin.js');
6
- const { DebuggerInjectionPlugin } = require('./plugins/DebuggerInjectionPlugin.js');
7
- const { ExitAPIInjectorPlugin } = require('./plugins/ExitAPIInjectorPlugin.js');
8
- const { runBuild, makeWebpackBuildConfig } = require('./webpack.build.js');
9
- const { runDev, makeWebpackDevConfig } = require('./webpack.dev.js');
10
- const { runViteDev, makeViteDevConfig } = require('./vite.dev.js');
11
-
12
- // 字体注入
13
- const { FontInjectionWebpackPlugin } = require('./plugins/FontInjectionWebpackPlugin.js');
14
- const { FONT_REGISTRY, getRegisteredFontKeys, isBuiltin: isBuiltinFont, getFontEntry } = require('./utils/fontRegistry.js');
15
- const { getOrGenerateFontCSS } = require('./utils/fontResolver.js');
16
-
17
- // 验证器
18
- const {
19
- runPreBuildChecks,
20
- runTrackingCheck,
21
- registerChecker,
22
- toggleChecker,
23
- listCheckers
24
- } = require('./validators/pre-build-checker.js');
25
-
26
- const {
27
- validateTracking,
28
- printValidationResult,
29
- TRACKING_METHODS,
30
- SDK_TO_TRACKING_MAP
31
- } = require('./validators/tracking-validator.js');
32
-
33
- exports.mergeOptions = mergeOptions;
34
- exports.parseArgvOptions = parseArgvOptions;
35
-
36
- exports.options = options;
37
-
38
- exports.allowedExtensions = allowedExtensions;
39
- exports.webpackCommonConfig = webpackCommonConfig;
40
- exports.makeWebpackDevConfig = makeWebpackDevConfig;
41
- exports.makeWebpackBuildConfig = makeWebpackBuildConfig;
42
- exports.runDev = runDev;
43
- exports.runBuild = runBuild;
44
-
45
- exports.DAPIInjectorPlugin = DAPIInjectorPlugin;
46
- exports.ExitAPIInjectorPlugin = ExitAPIInjectorPlugin;
47
- exports.DebuggerInjectionPlugin = DebuggerInjectionPlugin;
48
-
49
- exports.makeViteDevConfig = makeViteDevConfig;
50
- exports.runViteDev = runViteDev;
51
-
52
- // 编译前检查器导出
53
- exports.runPreBuildChecks = runPreBuildChecks;
54
- exports.runTrackingCheck = runTrackingCheck;
55
- exports.registerChecker = registerChecker;
56
- exports.toggleChecker = toggleChecker;
57
- exports.listCheckers = listCheckers;
58
-
59
- // 埋点验证器导出
60
- exports.validateTracking = validateTracking;
61
- exports.printValidationResult = printValidationResult;
62
- exports.TRACKING_METHODS = TRACKING_METHODS;
63
- exports.SDK_TO_TRACKING_MAP = SDK_TO_TRACKING_MAP;
64
-
65
- // 字体注入导出
66
- exports.FontInjectionWebpackPlugin = FontInjectionWebpackPlugin;
67
- exports.FONT_REGISTRY = FONT_REGISTRY;
68
- exports.getRegisteredFontKeys = getRegisteredFontKeys;
69
- exports.isBuiltinFont = isBuiltinFont;
70
- exports.getFontEntry = getFontEntry;
71
- exports.getOrGenerateFontCSS = getOrGenerateFontCSS;
1
+ "use strict";const{allowedExtensions,webpackCommonConfig}=require("./webpack.common.js");const{parseArgvOptions}=require("./utils/parseArgvOptions.js");const{mergeOptions}=require("./utils/mergeOptions.js");const{options}=require("./options.js");const{DAPIInjectorPlugin}=require("./plugins/DAPIInjectorPlugin.js");const{DebuggerInjectionPlugin}=require("./plugins/DebuggerInjectionPlugin.js");const{ExitAPIInjectorPlugin}=require("./plugins/ExitAPIInjectorPlugin.js");const{runBuild,makeWebpackBuildConfig}=require("./webpack.build.js");const{runDev,makeWebpackDevConfig}=require("./webpack.dev.js");const{runViteDev,makeViteDevConfig,json5Plugin,assetInlinePlugin,fontFamilyPlugin}=require("./vite.dev.js");// 字体注入
2
+ const{FontInjectionWebpackPlugin}=require("./plugins/FontInjectionWebpackPlugin.js");const{FONT_REGISTRY,getRegisteredFontKeys,isBuiltin:isBuiltinFont,getFontEntry}=require("./utils/fontRegistry.js");const{getOrGenerateFontCSS}=require("./utils/fontResolver.js");// 验证器
3
+ const{runPreBuildChecks,runTrackingCheck,registerChecker,toggleChecker,listCheckers}=require("./validators/pre-build-checker.js");const{validateTracking,printValidationResult,TRACKING_METHODS,SDK_TO_TRACKING_MAP}=require("./validators/tracking-validator.js");exports.mergeOptions=mergeOptions;exports.parseArgvOptions=parseArgvOptions;exports.options=options;exports.allowedExtensions=allowedExtensions;exports.webpackCommonConfig=webpackCommonConfig;exports.makeWebpackDevConfig=makeWebpackDevConfig;exports.makeWebpackBuildConfig=makeWebpackBuildConfig;exports.runDev=runDev;exports.runBuild=runBuild;exports.DAPIInjectorPlugin=DAPIInjectorPlugin;exports.ExitAPIInjectorPlugin=ExitAPIInjectorPlugin;exports.DebuggerInjectionPlugin=DebuggerInjectionPlugin;exports.makeViteDevConfig=makeViteDevConfig;exports.runViteDev=runViteDev;// 公共 Vite 插件(供项目 vite.config.ts 直接引用)
4
+ exports.json5Plugin=json5Plugin;exports.assetInlinePlugin=assetInlinePlugin;exports.fontFamilyPlugin=fontFamilyPlugin;// 编译前检查器导出
5
+ exports.runPreBuildChecks=runPreBuildChecks;exports.runTrackingCheck=runTrackingCheck;exports.registerChecker=registerChecker;exports.toggleChecker=toggleChecker;exports.listCheckers=listCheckers;// 埋点验证器导出
6
+ exports.validateTracking=validateTracking;exports.printValidationResult=printValidationResult;exports.TRACKING_METHODS=TRACKING_METHODS;exports.SDK_TO_TRACKING_MAP=SDK_TO_TRACKING_MAP;// 字体注入导出
7
+ exports.FontInjectionWebpackPlugin=FontInjectionWebpackPlugin;exports.FONT_REGISTRY=FONT_REGISTRY;exports.getRegisteredFontKeys=getRegisteredFontKeys;exports.isBuiltinFont=isBuiltinFont;exports.getFontEntry=getFontEntry;exports.getOrGenerateFontCSS=getOrGenerateFontCSS;
@@ -1,11 +1,4 @@
1
- const path = require('path');
2
- const loaderUtils = require('loader-utils');
3
- const fs = require('fs');
4
-
5
- const isObject = (value) => typeof value === 'object' && value !== null;
6
- const isDataURI = (value) => /^data:/i.test(value);
7
-
8
- /**
1
+ "use strict";const path=require("path");const loaderUtils=require("loader-utils");const fs=require("fs");const isObject=value=>typeof value==="object"&&value!==null;const isDataURI=value=>/^data:/i.test(value);/**
9
2
  * A utility function that allows mapping the value for a given key in an object
10
3
  * [in place](https://en.wikipedia.org/wiki/In-place_algorithm).
11
4
  *
@@ -20,72 +13,33 @@ const isDataURI = (value) => /^data:/i.test(value);
20
13
  * @param {(value: unknown) => Promise<any>} callback
21
14
  * The callback that either returns the mapped value, or returns a Promise
22
15
  * that resolves to the new value.
23
- */
24
- async function mapDeep(obj, key, callback) {
25
- if (Array.isArray(obj)) {
26
- for (const item of obj) {
27
- await mapDeep(item, key, callback);
28
- }
29
- } else if (isObject(obj)) {
30
- for (const [k, v] of Object.entries(obj)) {
31
- if (k === key) {
32
- obj[k] = await callback(v);
33
- } else {
34
- await mapDeep(v, key, callback);
35
- }
36
- }
37
- }
38
- }
39
-
40
- /**
16
+ */async function mapDeep(obj,key,callback){if(Array.isArray(obj)){for(const item of obj){await mapDeep(item,key,callback)}}else if(isObject(obj)){for(const[k,v]of Object.entries(obj)){if(k===key){obj[k]=await callback(v)}else{await mapDeep(v,key,callback)}}}}/**
41
17
  * @typedef {import(".").GLTFLoaderDefinition} GLTFLoader
42
18
  * @this {ThisParameterType<GLTFLoader>}
43
19
  * @type {GLTFLoader}
44
- */
45
- async function gltfLoader(content) {
46
- // Destructure loader options and set default values:
47
- const {
48
- // inline = true,
49
- // useRelativePaths = true,
50
- // uriResolver = (module) => String(module.default ?? module),
51
- // fileName = '[name].[hash:8].[ext]',
52
- // filePath = '/static/media',
53
- // publicPath = this._compilation?.outputOptions.publicPath ?? '/',
54
- context = this.context
55
- } = this.getOptions();
56
-
57
- // Parse the glTF data:
58
-
59
- // console.log(content);
60
- const data = JSON.parse(content);
61
- // console.log(data);
62
- // Iterate over the object and map any URIs:
63
- await mapDeep(data, 'uri', async (uri) => {
64
- // Resolve early if the URI cannot be imported as a module:
65
- if (!loaderUtils.isUrlRequest(uri)) return uri;
66
- // If the URI is a data URI, print a warning and resolve with the original value:
67
- if (isDataURI(uri)) {
68
- return uri;
69
- }
70
-
71
- const requestPath = path.join(context, uri);
72
- // console.log(requestPath);
73
- const binContent = fs.readFileSync(requestPath, { encoding: 'base64' });
74
- return `data:application/octet-stream;base64,${binContent}`;
75
- });
76
- // Stringify the updated data:
77
- const updatedContent = JSON.stringify(data);
78
- // Interpolate any file name tokens:
79
- // const interpolatedName = loaderUtils.interpolateName(this, fileName, { content: updatedContent });
80
- // Join the file path and interpolated name:
81
- // const interpolatedPath = path.join(filePath, interpolatedName);
82
- // Emit the file:
83
- // this.emitFile(interpolatedPath, updatedContent, null);
84
- // Join all paths together:
85
- // const fullPath = path.join(publicPath, interpolatedPath);
86
- // Lastly, resolve with either the JSON data or the full output path:
87
- // return `export default ${inline ? updatedContent : `"${fullPath}"`}`;
88
- return `export default ${updatedContent}`;
89
- }
90
-
91
- module.exports = gltfLoader;
20
+ */async function gltfLoader(content){// Destructure loader options and set default values:
21
+ const{// inline = true,
22
+ // useRelativePaths = true,
23
+ // uriResolver = (module) => String(module.default ?? module),
24
+ // fileName = '[name].[hash:8].[ext]',
25
+ // filePath = '/static/media',
26
+ // publicPath = this._compilation?.outputOptions.publicPath ?? '/',
27
+ context=this.context}=this.getOptions();// Parse the glTF data:
28
+ // console.log(content);
29
+ const data=JSON.parse(content);// console.log(data);
30
+ // Iterate over the object and map any URIs:
31
+ await mapDeep(data,"uri",async uri=>{// Resolve early if the URI cannot be imported as a module:
32
+ if(!loaderUtils.isUrlRequest(uri))return uri;// If the URI is a data URI, print a warning and resolve with the original value:
33
+ if(isDataURI(uri)){return uri}const requestPath=path.join(context,uri);// console.log(requestPath);
34
+ const binContent=fs.readFileSync(requestPath,{encoding:"base64"});return`data:application/octet-stream;base64,${binContent}`});// Stringify the updated data:
35
+ const updatedContent=JSON.stringify(data);// Interpolate any file name tokens:
36
+ // const interpolatedName = loaderUtils.interpolateName(this, fileName, { content: updatedContent });
37
+ // Join the file path and interpolated name:
38
+ // const interpolatedPath = path.join(filePath, interpolatedName);
39
+ // Emit the file:
40
+ // this.emitFile(interpolatedPath, updatedContent, null);
41
+ // Join all paths together:
42
+ // const fullPath = path.join(publicPath, interpolatedPath);
43
+ // Lastly, resolve with either the JSON data or the full output path:
44
+ // return `export default ${inline ? updatedContent : `"${fullPath}"`}`;
45
+ return`export default ${updatedContent}`}module.exports=gltfLoader;