@shijiu/jsview 2.2.201 → 2.2.426-test.0
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/dom/bin/DroidSansFallback.ttf +0 -0
- package/dom/bin/NotoColorEmoji.ttf +0 -0
- package/dom/bin/jsview-dom-browser-engine-core.min.js +2 -0
- package/dom/bin/jsview-dom-browser-engine-modules.min.js +2 -0
- package/dom/bin/jsview-dom-browser-forge.1385.f80e.wasm +0 -0
- package/dom/bin/jsview-dom-browser-forge.min.js +1 -0
- package/dom/bin/jsview-dom-browser-forge.worker.min.js +1 -0
- package/dom/bin/jsview-dom-browser.min.js +1 -1
- package/dom/bin/jsview-dom-native.min.js +1 -1
- package/dom/bin/jsview-engine-js-browser.min.js +1 -22
- package/dom/bin/jsview-engine-js-native.min.js +2 -0
- package/dom/bin/jsview-forge-define.min.js +1 -1
- package/dom/index.mjs +38 -14
- package/dom/target_core_revision.mjs +10 -5
- package/loader/jsv-core-api/jsview-core-api-glue.js +74 -0
- package/loader/jsv-core-api/wasm/backgroundtask.js +66 -0
- package/loader/jsv-core-api/wasm/core-api.js +206 -0
- package/loader/jsview-config.js +22 -0
- package/loader/jsview-loader.js +74 -85
- package/loader/jsview-main.mjs +8 -14
- package/loader/jsview.config.default.js +2 -2
- package/package.json +1 -1
- package/patches/node_modules/vite/dist/node/jsview-vite-extension.js +67 -6
- package/shijiu-jsview-0.0.0.tgz +0 -0
- package/tools/https-server-config/cert.pem +61 -0
- package/tools/https-server-config/key.pem +27 -0
- package/tools/https-server-config/serve.config.json +16 -0
- package/tools/jsview-common.mjs +87 -3
- package/tools/jsview-post-build.mjs +248 -70
- package/tools/jsview-post-install.mjs +7 -4
- package/tools/jsview-retrieve-sourcemap.mjs +64 -49
- package/tools/jsview-run-tool.mjs +53 -3
- package/tools/jsview-vue-devtools.mjs +2 -0
package/tools/jsview-common.mjs
CHANGED
|
@@ -179,6 +179,7 @@ function parseArguments(requiredUsages = {},
|
|
|
179
179
|
/**************************************************
|
|
180
180
|
* jsview-test-project
|
|
181
181
|
* ├── dist
|
|
182
|
+
* │ ├── assets
|
|
182
183
|
* │ ├── debug
|
|
183
184
|
* │ │ └── map
|
|
184
185
|
* │ └── js
|
|
@@ -209,6 +210,7 @@ function getOptions(framework)
|
|
|
209
210
|
options.modulesDir = path.resolve(options.projectDir, 'node_modules');
|
|
210
211
|
options.jsviewDir = path.resolve(options.modulesDir, '@shijiu', 'jsview');
|
|
211
212
|
options.jsviewDomDir = path.resolve(options.jsviewDir, 'dom');
|
|
213
|
+
options.jsviewDomBinDir = path.resolve(options.jsviewDomDir, 'bin');
|
|
212
214
|
options.jsviewPatchesDir = path.resolve(options.jsviewDir, 'patches');
|
|
213
215
|
options.jsviewPatchModulesDir = path.resolve(options.jsviewPatchesDir, 'node_modules');
|
|
214
216
|
options.jsviewToolsDir = path.resolve(options.jsviewDir, 'tools');
|
|
@@ -225,6 +227,7 @@ function getOptions(framework)
|
|
|
225
227
|
options.appJsviewConfigFile = path.resolve(options.appConfigDir, 'jsview.config.mjs');
|
|
226
228
|
options.appPrivKeyFile = path.resolve(options.appConfigDir, 'app_sign_private_key.crt');
|
|
227
229
|
options.appPubKeyFile = path.resolve(options.appConfigDir, 'app_sign_public_key.pem');
|
|
230
|
+
options.appConfigSVKeysDir = path.resolve(options.appConfigDir, 'signVerifyKeys');
|
|
228
231
|
|
|
229
232
|
options.distDir = path.resolve(options.projectDir, 'dist');
|
|
230
233
|
if (framework == 'vue') {
|
|
@@ -240,6 +243,7 @@ function getOptions(framework)
|
|
|
240
243
|
options.distJsvListFile = path.resolve(options.distDir, 'jsv-list.json');
|
|
241
244
|
options.distJsvInfoFile = path.resolve(options.distDir, 'jsv-info.json');
|
|
242
245
|
options.distJsIndexFile = path.resolve(options.distDir, 'index.html');
|
|
246
|
+
options.distAssetsDir = path.resolve(options.distDir, 'assets');
|
|
243
247
|
options.distJsDir = path.resolve(options.distDir, 'js');
|
|
244
248
|
options.distDebugDir = path.resolve(options.distDir, 'debug');
|
|
245
249
|
options.distDebugMapDir = path.resolve(options.distDebugDir, 'map');
|
|
@@ -325,6 +329,18 @@ function symlinkSync(workDir, targetPath, toPath)
|
|
|
325
329
|
fs.symlinkSync(relativePath, toPath, 'junction');
|
|
326
330
|
}
|
|
327
331
|
|
|
332
|
+
function isCommandAvailable(command) {
|
|
333
|
+
const isWindows = process.platform === 'win32';
|
|
334
|
+
const cmd = isWindows ? 'where' : 'which';
|
|
335
|
+
|
|
336
|
+
const result = childProcess.spawnSync(cmd, [command], {
|
|
337
|
+
stdio: 'ignore',
|
|
338
|
+
shell: true
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
return result.status === 0;
|
|
342
|
+
}
|
|
343
|
+
|
|
328
344
|
function isSymlinkSync(filePath)
|
|
329
345
|
{
|
|
330
346
|
const stats = fs.lstatSync(filePath);
|
|
@@ -362,6 +378,54 @@ function execCommandAsync(cmdline)
|
|
|
362
378
|
});
|
|
363
379
|
}
|
|
364
380
|
|
|
381
|
+
function execCommandPromise(cmdline)
|
|
382
|
+
{
|
|
383
|
+
let log = '';
|
|
384
|
+
|
|
385
|
+
const promise = new Promise((resolve, reject) => {
|
|
386
|
+
const child = childProcess.spawn(cmdline, null, { shell: true });
|
|
387
|
+
child.stdout.on('data', (data) => {
|
|
388
|
+
log += data;
|
|
389
|
+
});
|
|
390
|
+
child.stderr.on('data', (data) => {
|
|
391
|
+
log += data;
|
|
392
|
+
});
|
|
393
|
+
child.on('close', (code) => {
|
|
394
|
+
if (code !== 0) {
|
|
395
|
+
reject(log);
|
|
396
|
+
} else {
|
|
397
|
+
resolve(log);
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
return promise;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function execCommandWithLiveLog(cmdline)
|
|
406
|
+
{
|
|
407
|
+
Logger.Info('Run [' + cmdline + ']... ');
|
|
408
|
+
const promise = new Promise((resolve, reject) => {
|
|
409
|
+
const child = childProcess.spawn(cmdline, null, { shell: true });
|
|
410
|
+
child.stdout.on('data', (data) => {
|
|
411
|
+
process.stdout.write(data);
|
|
412
|
+
});
|
|
413
|
+
child.stderr.on('data', (data) => {
|
|
414
|
+
process.stderr.write(data);
|
|
415
|
+
});
|
|
416
|
+
child.on('close', (code) => {
|
|
417
|
+
if (code !== 0) {
|
|
418
|
+
process.exit(code);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
resolve();
|
|
422
|
+
});
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
return promise;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
|
|
365
429
|
function checkNodeVersion(minMajorVersion = 16)
|
|
366
430
|
{
|
|
367
431
|
const versions = process.version.replace('v', '').split('.');
|
|
@@ -463,7 +527,7 @@ function downloadFileAsync(url, localPath, workDir)
|
|
|
463
527
|
|
|
464
528
|
function unzipFile(zipFilePath, toDir, workDir)
|
|
465
529
|
{
|
|
466
|
-
|
|
530
|
+
let relativePath = path.relative(workDir, toDir);
|
|
467
531
|
if(!relativePath) {
|
|
468
532
|
relativePath = './'
|
|
469
533
|
}
|
|
@@ -473,6 +537,21 @@ function unzipFile(zipFilePath, toDir, workDir)
|
|
|
473
537
|
execCommand(cmdline);
|
|
474
538
|
}
|
|
475
539
|
|
|
540
|
+
function getExportPrefix()
|
|
541
|
+
{
|
|
542
|
+
let exportPrefix = 'export';
|
|
543
|
+
if(process.platform == 'win32') {
|
|
544
|
+
exportPrefix = 'set'
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return exportPrefix;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function getJsViewBurdenDefaultBaseUrl()
|
|
551
|
+
{
|
|
552
|
+
return 'https://cdn.release.qcast.cn/JsViewTest/wasm/resources';
|
|
553
|
+
}
|
|
554
|
+
|
|
476
555
|
function getNpmRegistry()
|
|
477
556
|
{
|
|
478
557
|
return 'https://nexus.cluster.qcast.cn/repository/npm-public';
|
|
@@ -484,8 +563,8 @@ function getNetworkIpv4Addresses() {
|
|
|
484
563
|
for (const devName in interfaces) {
|
|
485
564
|
const iface = interfaces[devName];
|
|
486
565
|
|
|
487
|
-
for (
|
|
488
|
-
|
|
566
|
+
for (let idx = 0; idx < iface.length; idx++) {
|
|
567
|
+
let alias = iface[idx];
|
|
489
568
|
if (alias.family === 'IPv4' && alias.address !== '127.0.0.1' && !alias.internal) {
|
|
490
569
|
addresses.push(alias.address);
|
|
491
570
|
}
|
|
@@ -503,9 +582,14 @@ export {
|
|
|
503
582
|
downloadFileAsync,
|
|
504
583
|
execCommand,
|
|
505
584
|
execCommandAsync,
|
|
585
|
+
execCommandPromise,
|
|
586
|
+
execCommandWithLiveLog,
|
|
587
|
+
getExportPrefix,
|
|
588
|
+
getJsViewBurdenDefaultBaseUrl,
|
|
506
589
|
getNetworkIpv4Addresses,
|
|
507
590
|
getNpmRegistry,
|
|
508
591
|
getOptions,
|
|
592
|
+
isCommandAvailable,
|
|
509
593
|
isSymlinkSync,
|
|
510
594
|
loadPackageObject,
|
|
511
595
|
makeZip,
|
|
@@ -7,11 +7,13 @@ import path from 'node:path';
|
|
|
7
7
|
import url from 'node:url'
|
|
8
8
|
import {
|
|
9
9
|
checkNodeVersion,
|
|
10
|
+
getJsViewBurdenDefaultBaseUrl,
|
|
10
11
|
getOptions,
|
|
11
12
|
loadPackageObject,
|
|
12
13
|
parseArguments,
|
|
13
14
|
Logger,
|
|
14
15
|
} from './jsview-common.mjs';
|
|
16
|
+
import { assert } from 'node:console';
|
|
15
17
|
|
|
16
18
|
// npm run build 时检查jsview-dom是否处于Debug模式。
|
|
17
19
|
async function checkDomDebugDisabled(options) {
|
|
@@ -24,15 +26,52 @@ async function checkDomDebugDisabled(options) {
|
|
|
24
26
|
}
|
|
25
27
|
}
|
|
26
28
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
+
function getEntryFilePath(options)
|
|
30
|
+
{
|
|
31
|
+
let jsEntryFilePath;
|
|
32
|
+
const jsFileNames = fs.readdirSync(options.distJsDir);
|
|
33
|
+
for(const fileName of jsFileNames) {
|
|
34
|
+
if (fileName.startsWith('main.jsv') && fileName.endsWith('.js')) {
|
|
35
|
+
jsEntryFilePath = path.resolve(options.distJsDir, fileName);
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
return jsEntryFilePath;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getEncryptBase64(appPrivKey, appPubKey, plainText)
|
|
44
|
+
{
|
|
45
|
+
// 编码md5值
|
|
46
|
+
const encryptBase64 = crypto.privateEncrypt({
|
|
47
|
+
key: appPrivKey,
|
|
48
|
+
padding: crypto.constants.RSA_PKCS1_PADDING
|
|
49
|
+
},
|
|
50
|
+
Buffer.from(plainText)
|
|
51
|
+
).toString('base64');
|
|
52
|
+
|
|
53
|
+
// 校验publicKey是否是配对的
|
|
54
|
+
const decryptText = crypto.publicDecrypt({
|
|
55
|
+
key: appPubKey,
|
|
56
|
+
padding: crypto.constants.RSA_PKCS1_PADDING
|
|
57
|
+
},
|
|
58
|
+
Buffer.from(encryptBase64, 'base64')
|
|
59
|
+
).toString();
|
|
60
|
+
if (decryptText !== plainText) {
|
|
61
|
+
Logger.ErrorAndExit('public key dismath to private key!');
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return encryptBase64;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function updateOptions(options)
|
|
29
68
|
{
|
|
30
69
|
// 加载私钥文件
|
|
31
70
|
if (!fs.existsSync(options.appPrivKeyFile)) {
|
|
32
71
|
Logger.ErrorAndExit('Failed to open private key file from ' + options.appPrivKeyFile);
|
|
33
72
|
}
|
|
34
|
-
const
|
|
35
|
-
if (
|
|
73
|
+
const privKey = fs.readFileSync(options.appPrivKeyFile);
|
|
74
|
+
if (privKey.indexOf('-----BEGIN PRIVATE KEY-----') < 0) {
|
|
36
75
|
Logger.ErrorAndExit('Get private from src/appConfig/app_sign_private_key.crt failed.');
|
|
37
76
|
}
|
|
38
77
|
|
|
@@ -40,40 +79,62 @@ async function prepareMainAppData(options, fileMd5)
|
|
|
40
79
|
if (!fs.existsSync(options.appPubKeyFile)) {
|
|
41
80
|
Logger.ErrorAndExit('Failed to open public key file from ' + options.appPubKeyFile);
|
|
42
81
|
}
|
|
43
|
-
const
|
|
44
|
-
if (
|
|
82
|
+
const pubKey = fs.readFileSync(options.appPubKeyFile);
|
|
83
|
+
if (pubKey.indexOf('-----BEGIN PUBLIC KEY-----') < 0) {
|
|
45
84
|
Logger.ErrorAndExit('Get private from src/appConfig/app_sign_public_key.pem failed.');
|
|
46
85
|
}
|
|
47
|
-
// 公钥格式转化pem -> der,因为java中的解码处理只识别der格式
|
|
48
|
-
let publicKeyDerText = publicKeyText.toString();
|
|
49
|
-
if (publicKeyDerText.indexOf('\r') >= 0) {
|
|
50
|
-
publicKeyDerText = publicKeyDerText.replace(/\r/g, '');
|
|
51
|
-
}
|
|
52
|
-
if (publicKeyDerText.indexOf('\n') >= 0) {
|
|
53
|
-
publicKeyDerText = publicKeyDerText.replace(/\n/g, '');
|
|
54
|
-
}
|
|
55
|
-
publicKeyDerText = publicKeyDerText.replace('-----BEGIN PUBLIC KEY-----', '');
|
|
56
|
-
publicKeyDerText = publicKeyDerText.replace('-----END PUBLIC KEY-----', '');
|
|
57
|
-
|
|
58
|
-
// 编码md5值
|
|
59
|
-
const encryptCodeBase64 = crypto.privateEncrypt({
|
|
60
|
-
key: privateKeyText,
|
|
61
|
-
padding: crypto.constants.RSA_PKCS1_PADDING
|
|
62
|
-
},
|
|
63
|
-
Buffer.from(fileMd5)
|
|
64
|
-
).toString('base64');
|
|
65
86
|
|
|
66
87
|
// 校验publicKey是否是配对的
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
).
|
|
73
|
-
|
|
74
|
-
|
|
88
|
+
const encryptBase64 = getEncryptBase64(privKey, pubKey, 'crypto-test');
|
|
89
|
+
assert(encryptBase64)
|
|
90
|
+
|
|
91
|
+
const appEntryFilePath = getEntryFilePath(options);
|
|
92
|
+
const appEntryContent = fs.readFileSync(appEntryFilePath, 'utf8');
|
|
93
|
+
const appEntryMd5 = crypto.createHash('md5').update(appEntryContent).digest('hex');
|
|
94
|
+
|
|
95
|
+
options.appPrivKey = privKey;
|
|
96
|
+
options.appPubKey = pubKey;
|
|
97
|
+
options.appEntryMd5 = appEntryMd5;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function getOtherSignVerifyKeys(options)
|
|
101
|
+
{
|
|
102
|
+
const signVerifyKeys = {};
|
|
103
|
+
const signVerifyKeyFiles = fs.readdirSync(options.appConfigSVKeysDir);
|
|
104
|
+
for (const keyName of signVerifyKeyFiles) {
|
|
105
|
+
const filePath = path.resolve(options.appConfigSVKeysDir, keyName);
|
|
106
|
+
if (keyName == '.gitkeep') {
|
|
107
|
+
continue;
|
|
108
|
+
} else if (!keyName.endsWith('.pem')) {
|
|
109
|
+
Logger.Warn(`Ignore to append invalid signature verification key: ${filePath}`);
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const keyValue = fs.readFileSync(filePath, 'utf8');
|
|
114
|
+
signVerifyKeys[keyName] = keyValue;
|
|
75
115
|
}
|
|
76
116
|
|
|
117
|
+
return signVerifyKeys;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function publicPemToDer(pubKeyPem)
|
|
121
|
+
{
|
|
122
|
+
let pubKeyDer = pubKeyPem.toString();
|
|
123
|
+
if (pubKeyDer.indexOf('\r') >= 0) {
|
|
124
|
+
pubKeyDer = pubKeyDer.replace(/\r/g, '');
|
|
125
|
+
}
|
|
126
|
+
if (pubKeyDer.indexOf('\n') >= 0) {
|
|
127
|
+
pubKeyDer = pubKeyDer.replace(/\n/g, '');
|
|
128
|
+
}
|
|
129
|
+
pubKeyDer = pubKeyDer.replace('-----BEGIN PUBLIC KEY-----', '');
|
|
130
|
+
pubKeyDer = pubKeyDer.replace('-----END PUBLIC KEY-----', '');
|
|
131
|
+
|
|
132
|
+
return pubKeyDer;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// main.js处理AppData信息
|
|
136
|
+
async function prepareMainAppData(options)
|
|
137
|
+
{
|
|
77
138
|
// 获取AppData信息
|
|
78
139
|
const appConfigFilePath = path.resolve(options.appConfigDir, 'app.config.mjs');
|
|
79
140
|
if (!fs.existsSync(appConfigFilePath)) {
|
|
@@ -82,29 +143,47 @@ async function prepareMainAppData(options, fileMd5)
|
|
|
82
143
|
const appConfigFileUrl = url.pathToFileURL(appConfigFilePath);
|
|
83
144
|
const { default: appConfig } = await import(appConfigFileUrl);
|
|
84
145
|
|
|
85
|
-
// 获取
|
|
86
|
-
const
|
|
146
|
+
// 获取PublickKey数组
|
|
147
|
+
const publicKeys = [publicPemToDer(options.appPubKey)];
|
|
148
|
+
// TODO: 验签PubKey的安全性尚需考虑
|
|
149
|
+
// const signVerifyKeys = getOtherSignVerifyKeys(options);
|
|
150
|
+
// for(const [keyName, keyValue] of Object.entries(signVerifyKeys)) {
|
|
151
|
+
// Logger.Info(` -> Append ${keyName} to jsvapp.`)
|
|
152
|
+
// publicKeys.push(publicPemToDer(keyValue));
|
|
153
|
+
// }
|
|
154
|
+
|
|
155
|
+
// 获取密文
|
|
156
|
+
const encryptBase64 = getEncryptBase64(options.appPrivKey, options.appPubKey, options.appEntryMd5);
|
|
157
|
+
|
|
158
|
+
// 获取 预加载的文件名
|
|
159
|
+
const filterKeys = [
|
|
160
|
+
'domNativePath',
|
|
161
|
+
'NativePlatformDomBridge',
|
|
162
|
+
'ForgeExtension',
|
|
163
|
+
'CodeRevision',
|
|
164
|
+
'__vue_app__',
|
|
165
|
+
'mount("#app")',
|
|
166
|
+
];
|
|
167
|
+
const preloadChunks = [];
|
|
87
168
|
const jsFileNames = fs.readdirSync(options.distJsDir);
|
|
88
169
|
for(const fileName of jsFileNames) {
|
|
89
170
|
if (!fileName.startsWith('chunk.jsv') || !fileName.endsWith('.js')) {
|
|
90
171
|
continue;
|
|
91
172
|
}
|
|
92
173
|
|
|
93
|
-
const hash = fileName.replace(/.*chunk\.jsv.([a-fA-F0-9].*).js/, '$1');
|
|
94
|
-
|
|
95
174
|
const filePath = path.resolve(options.distJsDir, fileName);
|
|
96
175
|
const sourceContent = fs.readFileSync(filePath, 'utf8');
|
|
97
|
-
|
|
176
|
+
if(filterKeys.some(it => sourceContent.includes(it)) == false) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
98
179
|
|
|
99
|
-
|
|
180
|
+
preloadChunks.push(fileName);
|
|
100
181
|
}
|
|
101
182
|
|
|
102
183
|
// 组装AppData
|
|
103
|
-
appConfig.PublicKeys =
|
|
104
|
-
appConfig.EncryptCodes = [
|
|
105
|
-
appConfig.
|
|
106
|
-
|
|
107
|
-
|
|
184
|
+
appConfig.PublicKeys = publicKeys; // 使用签名数组,支持后续追加签名
|
|
185
|
+
appConfig.EncryptCodes = [encryptBase64]; // 同样使用数组,支持后续追加
|
|
186
|
+
appConfig.PreloadChunks = preloadChunks;
|
|
108
187
|
|
|
109
188
|
return JSON.stringify(appConfig);
|
|
110
189
|
}
|
|
@@ -146,24 +225,94 @@ async function signApp(options)
|
|
|
146
225
|
|
|
147
226
|
var sourceContent = fs.readFileSync(filePath, 'utf8');
|
|
148
227
|
|
|
149
|
-
|
|
228
|
+
const jsvMd5 = crypto.createHash('md5').update(sourceContent).digest('hex');
|
|
229
|
+
|
|
230
|
+
// 获取签名并验签
|
|
231
|
+
const cryptoSigner = crypto.createSign('sha256').update(jsvMd5).end();
|
|
232
|
+
const cryptoVerifier = crypto.createVerify('sha256').update(jsvMd5).end();
|
|
233
|
+
const jsvSign = cryptoSigner.sign(options.appPrivKey, 'hex');
|
|
234
|
+
const verified = cryptoVerifier.verify(options.appPubKey, jsvSign, 'hex');
|
|
235
|
+
if(!verified) {
|
|
236
|
+
Logger.ErrorAndExit('Failed to create signature verification.');
|
|
237
|
+
}
|
|
150
238
|
|
|
151
|
-
const jsvAppMd5 = crypto.createHash('md5').update(sourceContent).digest('hex');
|
|
152
239
|
let appDataInfo = '';
|
|
153
240
|
if (fileName.indexOf('main.jsv.') >= 0) {
|
|
154
241
|
// 对main文件加入应用头信息
|
|
155
|
-
appDataInfo = await prepareMainAppData(options,
|
|
242
|
+
appDataInfo = await prepareMainAppData(options, jsvMd5);
|
|
156
243
|
|
|
157
244
|
// 格式化jsvapp信息 /*jsvapp:内容长度:{内容}*/
|
|
158
245
|
// 使用TextEncoder解决中文长度问题
|
|
159
246
|
const infoLen = new TextEncoder().encode(appDataInfo).length;
|
|
160
247
|
appDataInfo = '/*jsvapp:' + infoLen + ':' + appDataInfo + ':' + infoLen + ':jsvapp*/';
|
|
161
248
|
}
|
|
162
|
-
sourceContent = sourceContent + appDataInfo
|
|
249
|
+
sourceContent = sourceContent + appDataInfo;
|
|
250
|
+
|
|
251
|
+
sourceContent = sourceContent + '/*jsvsign:' + jsvSign + '*/';
|
|
252
|
+
|
|
163
253
|
fs.writeFileSync(filePath, sourceContent, 'utf8');
|
|
164
254
|
}
|
|
165
255
|
}
|
|
166
256
|
|
|
257
|
+
function redirectBurdenResource(options)
|
|
258
|
+
{
|
|
259
|
+
if(process.env['JSVIEW_BURDEN_LOCAL']) {
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
let burdenBaseUrl = process.env['JSVIEW_BURDEN_BASEURL'];
|
|
264
|
+
if(!burdenBaseUrl) {
|
|
265
|
+
burdenBaseUrl = getJsViewBurdenDefaultBaseUrl();
|
|
266
|
+
}
|
|
267
|
+
if(!burdenBaseUrl.endsWith('/')) {
|
|
268
|
+
burdenBaseUrl += '/';
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const burdenFilePathList = [];
|
|
272
|
+
|
|
273
|
+
const assetFileNames = fs.readdirSync(options.distAssetsDir);
|
|
274
|
+
for(const fileName of assetFileNames) {
|
|
275
|
+
if (!fileName.endsWith('.ttf')) {
|
|
276
|
+
continue;
|
|
277
|
+
}
|
|
278
|
+
burdenFilePathList.push(path.resolve(options.distAssetsDir, fileName));
|
|
279
|
+
}
|
|
280
|
+
const jsFileNames = fs.readdirSync(options.distJsDir);
|
|
281
|
+
for(const fileName of jsFileNames) {
|
|
282
|
+
if (!fileName.endsWith('.wasm')) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
burdenFilePathList.push(path.resolve(options.distJsDir, fileName));
|
|
286
|
+
}
|
|
287
|
+
for(const filePath of burdenFilePathList) {
|
|
288
|
+
fs.rmSync(filePath);
|
|
289
|
+
Logger.Info('Config burden file: ' + path.relative(options.projectDir, filePath));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
for(const fileName of jsFileNames) {
|
|
293
|
+
if (!fileName.endsWith('.js') && !fileName.endsWith('.js.map')) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const filePath = path.resolve(options.distJsDir, fileName);
|
|
298
|
+
let sourceContent = fs.readFileSync(filePath, 'utf8');
|
|
299
|
+
|
|
300
|
+
for(const burdenFilePath of burdenFilePathList) {
|
|
301
|
+
const relativeBurdenFilePath = path.relative(options.distJsDir, burdenFilePath);
|
|
302
|
+
if(!sourceContent.includes(relativeBurdenFilePath)) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const burdenFileUrl = burdenBaseUrl + path.basename(burdenFilePath);
|
|
307
|
+
|
|
308
|
+
sourceContent = sourceContent.replaceAll(relativeBurdenFilePath, burdenFileUrl);
|
|
309
|
+
Logger.Info('Burdenting ' + path.relative(options.projectDir, filePath) + ' for [' + relativeBurdenFilePath + ']');
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
fs.writeFileSync(filePath, sourceContent, 'utf8');
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
167
316
|
function redirectSourceMappingURL(options)
|
|
168
317
|
{
|
|
169
318
|
const jsFileNames = fs.readdirSync(options.distJsDir);
|
|
@@ -182,6 +331,9 @@ function redirectSourceMappingURL(options)
|
|
|
182
331
|
'# sourceMappingURL=', '# sourceMappingURL=http://localhost:57245/map/');
|
|
183
332
|
sourceContent = sourceContent.substring(0, mapUrlOffset) + mapUrlStr;
|
|
184
333
|
|
|
334
|
+
//添加一个回车,后续把\n归入算进md5计算中
|
|
335
|
+
sourceContent += '\n';
|
|
336
|
+
|
|
185
337
|
fs.writeFileSync(filePath, sourceContent, 'utf8');
|
|
186
338
|
};
|
|
187
339
|
}
|
|
@@ -192,41 +344,57 @@ function makeMainJsvMjs(options, framework)
|
|
|
192
344
|
return;
|
|
193
345
|
}
|
|
194
346
|
|
|
195
|
-
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
if (fileName.startsWith('main.jsv') && fileName.endsWith('.js')) {
|
|
199
|
-
jsEntryFilePath = path.resolve(options.distJsDir, fileName);
|
|
200
|
-
break;
|
|
201
|
-
}
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const sourceContent = fs.readFileSync(jsEntryFilePath, 'utf8');
|
|
205
|
-
const md5 = sourceContent.replace(/\n/g, '').replace(/.*\/\*jsvmd5:([a-fA-F0-9]{8})[a-fA-F0-9]*\*\/.*/, '$1');
|
|
206
|
-
const newEntryFileName = `main.jsv.${md5}.js`;
|
|
347
|
+
// 更新entry hash
|
|
348
|
+
const jsEntryFilePath = getEntryFilePath(options);
|
|
349
|
+
const newEntryFileName = `main.jsv.${options.appEntryMd5.slice(0, 8)}.js`;
|
|
207
350
|
const newEntryFilePath = path.resolve(path.dirname(jsEntryFilePath), newEntryFileName);
|
|
208
351
|
Logger.Info(' -> ' + path.relative(options.projectDir, newEntryFilePath));
|
|
209
352
|
fs.renameSync(jsEntryFilePath, newEntryFilePath);
|
|
210
353
|
|
|
354
|
+
// 更新index.html hash
|
|
211
355
|
var indexContent = fs.readFileSync(options.distJsIndexFile, 'utf8');
|
|
212
356
|
indexContent = indexContent.replace(path.basename(jsEntryFilePath), newEntryFileName);
|
|
213
357
|
Logger.Info(' -> ' + path.relative(options.projectDir, options.distJsIndexFile));
|
|
214
358
|
fs.writeFileSync(options.distJsIndexFile, indexContent, 'utf8');
|
|
215
359
|
|
|
360
|
+
// 创建JsView 加载用的 mjs entry
|
|
216
361
|
const moduleEntryFilePath = newEntryFilePath.replace(/\.js$/, '.mjs');
|
|
217
362
|
fs.copyFileSync(newEntryFilePath, moduleEntryFilePath);
|
|
363
|
+
|
|
364
|
+
// 创建JsView Debug用的 mjs entry
|
|
365
|
+
const debugEntryFilePath = path.resolve(options.distJsDir, 'main.jsv.mjs');
|
|
366
|
+
Logger.Info(' -> ' + path.relative(options.projectDir, debugEntryFilePath));
|
|
367
|
+
fs.copyFileSync(newEntryFilePath, debugEntryFilePath);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
function getWasmMapChunkName(options, wasmMapName)
|
|
371
|
+
{
|
|
372
|
+
let chunkName = wasmMapName;
|
|
373
|
+
|
|
374
|
+
const regex = new RegExp(`sourceMappingURL.${wasmMapName}`);
|
|
375
|
+
const jsFileNames = fs.readdirSync(options.distJsDir);
|
|
376
|
+
for(const fileName of jsFileNames) {
|
|
377
|
+
if (!fileName.endsWith('.wasm')) {
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const filePath = path.resolve(options.distJsDir, fileName);
|
|
382
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
383
|
+
if(regex.test(content)) {
|
|
384
|
+
chunkName = fileName + '.map';
|
|
385
|
+
break;
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
|
|
389
|
+
return chunkName;
|
|
218
390
|
}
|
|
219
391
|
|
|
220
392
|
function makeDebugMap(options, framework)
|
|
221
393
|
{
|
|
222
394
|
fs.mkdirSync(options.distDebugMapDir, { recursive: true });
|
|
223
395
|
|
|
224
|
-
let jsEntryFilePath;
|
|
225
396
|
const jsFileNames = fs.readdirSync(options.distJsDir);
|
|
226
397
|
for(const fileName of jsFileNames) {
|
|
227
|
-
if (fileName.startsWith('main.jsv') && fileName.endsWith('.mjs')) {
|
|
228
|
-
jsEntryFilePath = path.resolve(options.distJsDir, fileName);
|
|
229
|
-
}
|
|
230
398
|
if (!fileName.endsWith('.map')) {
|
|
231
399
|
continue;
|
|
232
400
|
}
|
|
@@ -237,15 +405,18 @@ function makeDebugMap(options, framework)
|
|
|
237
405
|
fs.renameSync(from, to);
|
|
238
406
|
};
|
|
239
407
|
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (
|
|
243
|
-
|
|
408
|
+
const wasmFileNames = fs.readdirSync(options.jsviewDomBinDir);
|
|
409
|
+
for(const fileName of wasmFileNames) {
|
|
410
|
+
if (!fileName.endsWith('.wasm.map')) {
|
|
411
|
+
continue;
|
|
244
412
|
}
|
|
245
|
-
|
|
413
|
+
|
|
414
|
+
const chunkName = getWasmMapChunkName(options, fileName);
|
|
415
|
+
const from = path.resolve(options.jsviewDomBinDir, fileName);
|
|
416
|
+
const to = path.resolve(options.distDebugMapDir, chunkName);
|
|
246
417
|
Logger.Info(' -> ' + path.relative(options.projectDir, to));
|
|
247
|
-
fs.copyFileSync(
|
|
248
|
-
}
|
|
418
|
+
fs.copyFileSync(from, to);
|
|
419
|
+
};
|
|
249
420
|
|
|
250
421
|
const jsmapServeName = 'jsview-jsmap-serve.mjs';
|
|
251
422
|
const jsmapServePath = path.resolve(options.jsviewToolsDir, jsmapServeName);
|
|
@@ -331,10 +502,17 @@ async function main(argv)
|
|
|
331
502
|
await checkDomDebugDisabled(options);
|
|
332
503
|
|
|
333
504
|
Logger.Info();
|
|
505
|
+
Logger.Info('Redirecting JsView burden resource...');
|
|
506
|
+
redirectBurdenResource(options)
|
|
507
|
+
Logger.Info('Redirected JsView burden resource...');
|
|
508
|
+
|
|
334
509
|
Logger.Info('Redirecting JsView source map...');
|
|
335
510
|
redirectSourceMappingURL(options)
|
|
336
511
|
Logger.Info('Redirected JsView source map...');
|
|
337
512
|
|
|
513
|
+
// EntryMd5 要放在sourcemap更新之后
|
|
514
|
+
updateOptions(options);
|
|
515
|
+
|
|
338
516
|
Logger.Info('Checking JsView app config...');
|
|
339
517
|
await checkAppConfig(options);
|
|
340
518
|
Logger.Info('Checked JsView app config...');
|
|
@@ -245,7 +245,7 @@ async function printRevision(options)
|
|
|
245
245
|
Logger.Info('**************************************************');
|
|
246
246
|
}
|
|
247
247
|
|
|
248
|
-
function doPostInstall(framework, pkgNeedPatch, skipCheckVersion)
|
|
248
|
+
function doPostInstall(framework, pkgNeedPatch, skipCheckVersion, skipCheckNpmcmd)
|
|
249
249
|
{
|
|
250
250
|
const options = getOptions(framework);
|
|
251
251
|
options.projectDir = process.cwd();
|
|
@@ -260,7 +260,9 @@ function doPostInstall(framework, pkgNeedPatch, skipCheckVersion)
|
|
|
260
260
|
'@shijiu/jsview-react-extra-samples',
|
|
261
261
|
];
|
|
262
262
|
|
|
263
|
-
|
|
263
|
+
if(!skipCheckNpmcmd) {
|
|
264
|
+
checkNpmCommand();
|
|
265
|
+
}
|
|
264
266
|
|
|
265
267
|
checkNpmLinkForDebug(options, linkablePkgNames);
|
|
266
268
|
|
|
@@ -304,14 +306,15 @@ function main(argv)
|
|
|
304
306
|
Logger.ErrorAndExit('Failed to support framework: ' + framework);
|
|
305
307
|
}
|
|
306
308
|
|
|
307
|
-
doPostInstall(argv.framework, pkgNeedPatch, argv.skipCheckVersion);
|
|
309
|
+
doPostInstall(argv.framework, pkgNeedPatch, argv.skipCheckVersion, argv.skipCheckNpmcmd);
|
|
308
310
|
}
|
|
309
311
|
|
|
310
312
|
const requiredUsages = {
|
|
311
313
|
'--framework': 'Select from [vue|react]',
|
|
312
314
|
};
|
|
313
315
|
const optionalUsages = {
|
|
314
|
-
'--skip-check-version': 'Skip check patches version.'
|
|
316
|
+
'--skip-check-version': 'Skip check patches version.',
|
|
317
|
+
'--skip-check-npmcmd': 'Skip check npm command.'
|
|
315
318
|
};
|
|
316
319
|
const argv = parseArguments(requiredUsages, optionalUsages);
|
|
317
320
|
main(argv);
|