@metricinsights/pp-dev 0.3.3 → 0.5.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/README.md +60 -6
- package/dist/cjs/cli.js +596 -39
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/{index-ab672d81.js → index-44d9499e.js} +50 -33
- package/dist/cjs/index-44d9499e.js.map +1 -0
- package/dist/cjs/index.js +9 -9
- package/dist/cjs/package.json +12 -1
- package/dist/cjs/{plugin-24dd0a43.js → plugin-34b9bc89.js} +250 -27
- package/dist/cjs/plugin-34b9bc89.js.map +1 -0
- package/dist/cjs/plugin.js +8 -2
- package/dist/cjs/plugin.js.map +1 -1
- package/dist/esm/cli.js +582 -28
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/{index-d2c8ad2e.js → index-4361a6ba.js} +49 -31
- package/dist/esm/index-4361a6ba.js.map +1 -0
- package/dist/esm/index.js +9 -8
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/package.json +12 -1
- package/dist/esm/{plugin-ecb1c54a.js → plugin-78cbd0b1.js} +247 -27
- package/dist/esm/plugin-78cbd0b1.js.map +1 -0
- package/dist/esm/plugin.js +4 -1
- package/dist/esm/plugin.js.map +1 -1
- package/dist/types/index.d.ts +9 -3
- package/dist/types/plugin.d.ts +68 -2
- package/package.json +12 -1
- package/dist/cjs/index-ab672d81.js.map +0 -1
- package/dist/cjs/plugin-24dd0a43.js.map +0 -1
- package/dist/esm/index-d2c8ad2e.js.map +0 -1
- package/dist/esm/plugin-ecb1c54a.js.map +0 -1
package/dist/esm/cli.js
CHANGED
|
@@ -2,27 +2,31 @@ import * as path from 'path';
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import { performance } from 'node:perf_hooks';
|
|
4
4
|
import { cac } from 'cac';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { loadConfigFromFile, mergeConfig,
|
|
5
|
+
import { g as getViteConfig, V as VERSION } from './index-4361a6ba.js';
|
|
6
|
+
import { c as createLogger, a as colors, i as initPPRedirect, M as MiAPI, b as initProxyCache, d as initProxy, e as initLoadPPData, f as initRewriteResponse, u as urlReplacer, g as cutUrlParams } from './plugin-78cbd0b1.js';
|
|
7
|
+
import { loadConfigFromFile, mergeConfig, build, resolveConfig, optimizeDeps, preview } from 'vite';
|
|
8
8
|
import { URL as URL$1, parse } from 'url';
|
|
9
|
-
import { i as initPPRedirect, M as MiAPI, a as initProxyCache, b as initProxy, c as initLoadPPData, d as initRewriteResponse, u as urlReplacer, e as cutUrlParams } from './plugin-ecb1c54a.js';
|
|
10
9
|
import * as express from 'express';
|
|
11
10
|
import * as winston from 'winston';
|
|
12
|
-
import '
|
|
13
|
-
import '
|
|
11
|
+
import * as dirCompare from 'dir-compare';
|
|
12
|
+
import DiffMatchPatch from 'diff-match-patch';
|
|
13
|
+
import { isBinaryFile } from 'isbinaryfile';
|
|
14
|
+
import * as os from 'os';
|
|
15
|
+
import * as crypto from 'crypto';
|
|
16
|
+
import extractZip from 'extract-zip';
|
|
14
17
|
import 'ejs';
|
|
18
|
+
import 'esbuild';
|
|
15
19
|
import 'http-proxy-middleware';
|
|
20
|
+
import 'picocolors';
|
|
16
21
|
import 'axios';
|
|
17
22
|
import 'jsdom';
|
|
23
|
+
import 'https';
|
|
18
24
|
import 'memory-cache';
|
|
19
|
-
import 'crypto';
|
|
20
25
|
import 'process';
|
|
21
26
|
import 'child_process';
|
|
22
27
|
import 'console';
|
|
23
28
|
import 'zlib';
|
|
24
29
|
|
|
25
|
-
const colors$2 = createColors();
|
|
26
30
|
function isDefined(value) {
|
|
27
31
|
return value != null;
|
|
28
32
|
}
|
|
@@ -31,8 +35,9 @@ function bindShortcuts(server, opts) {
|
|
|
31
35
|
return;
|
|
32
36
|
}
|
|
33
37
|
server._shortcutsOptions = opts;
|
|
38
|
+
const logger = createLogger();
|
|
34
39
|
if (opts.print) {
|
|
35
|
-
|
|
40
|
+
logger.info(colors.dim(colors.green(' ➜')) + colors.dim(' press ') + colors.bold('h') + colors.dim(' to show help'));
|
|
36
41
|
}
|
|
37
42
|
const shortcuts = (opts.customShortcuts ?? []).filter(isDefined).concat(BASE_SHORTCUTS);
|
|
38
43
|
let actionRunning = false;
|
|
@@ -46,10 +51,10 @@ function bindShortcuts(server, opts) {
|
|
|
46
51
|
return;
|
|
47
52
|
}
|
|
48
53
|
if (input === 'h') {
|
|
49
|
-
|
|
54
|
+
logger.info([
|
|
50
55
|
'',
|
|
51
|
-
colors
|
|
52
|
-
...shortcuts.map((shortcut) => colors
|
|
56
|
+
colors.bold(' Shortcuts'),
|
|
57
|
+
...shortcuts.map((shortcut) => colors.dim(' press ') + colors.bold(shortcut.key) + colors.dim(` to ${shortcut.description}`)),
|
|
53
58
|
].join('\n'));
|
|
54
59
|
}
|
|
55
60
|
const shortcut = shortcuts.find((shortcut) => shortcut.key === input);
|
|
@@ -115,7 +120,6 @@ const BASE_SHORTCUTS = [
|
|
|
115
120
|
},
|
|
116
121
|
];
|
|
117
122
|
|
|
118
|
-
const colors$1 = createColors();
|
|
119
123
|
function createDevServer(logLevel = 'info') {
|
|
120
124
|
const server = express.default();
|
|
121
125
|
const logger = winston.createLogger({
|
|
@@ -135,24 +139,492 @@ function createDevServer(logLevel = 'info') {
|
|
|
135
139
|
if (!listener) {
|
|
136
140
|
throw new Error('Server is not listening');
|
|
137
141
|
}
|
|
138
|
-
const colorUrl = (url) => colors
|
|
142
|
+
const colorUrl = (url) => colors.cyan(url.replace(/:(\d+)\//, (_, port) => `:${colors.bold(port)}/`));
|
|
139
143
|
const address = listener.address();
|
|
140
144
|
if (address && typeof address === 'object') {
|
|
141
145
|
if (address.address === '::') {
|
|
142
146
|
const url = new URL$1(base || '', `http://localhost:${address.port}`);
|
|
143
|
-
logger.info(` ${colors
|
|
147
|
+
logger.info(` ${colors.green('➜')} ${colors.bold('Local')}: ${colorUrl(url.toString())}`);
|
|
144
148
|
}
|
|
145
149
|
else {
|
|
146
150
|
const url = new URL$1(base || '', `http://[${address.address}]:${address.port}`);
|
|
147
|
-
logger.info(` ${colors
|
|
151
|
+
logger.info(` ${colors.green('➜')} ${colors.bold('Local')}: ${colorUrl(url.toString())}`);
|
|
148
152
|
}
|
|
149
153
|
}
|
|
150
154
|
};
|
|
151
155
|
return server;
|
|
152
156
|
}
|
|
153
157
|
|
|
158
|
+
const changelogTemplate = /* HTML */ `<!DOCTYPE html>
|
|
159
|
+
<html lang="en">
|
|
160
|
+
<head>
|
|
161
|
+
<meta charset="UTF-8" />
|
|
162
|
+
<title>Changelog Diff</title>
|
|
163
|
+
<style>
|
|
164
|
+
tr,
|
|
165
|
+
td {
|
|
166
|
+
padding: 0;
|
|
167
|
+
}
|
|
168
|
+
.diff-file {
|
|
169
|
+
margin-top: 20px;
|
|
170
|
+
border: 1px solid #e1e4e8;
|
|
171
|
+
border-radius: 6px;
|
|
172
|
+
}
|
|
173
|
+
.diff-file-title {
|
|
174
|
+
padding: 10px 20px;
|
|
175
|
+
background-color: #f6f8fa;
|
|
176
|
+
border-bottom: 1px solid #e1e4e8;
|
|
177
|
+
border-radius: 6px 6px 0 0;
|
|
178
|
+
font-weight: bold;
|
|
179
|
+
}
|
|
180
|
+
.diff-file-title .renamed {
|
|
181
|
+
font-weight: normal;
|
|
182
|
+
}
|
|
183
|
+
.diff-file-title .renamed .from {
|
|
184
|
+
color: #cb2431;
|
|
185
|
+
}
|
|
186
|
+
.diff-file-title .renamed .to {
|
|
187
|
+
color: #22863a;
|
|
188
|
+
}
|
|
189
|
+
.diff-file-title .added {
|
|
190
|
+
color: #22863a;
|
|
191
|
+
}
|
|
192
|
+
.diff-file-title .deleted {
|
|
193
|
+
color: #cb2431;
|
|
194
|
+
}
|
|
195
|
+
.diff-file-content {
|
|
196
|
+
}
|
|
197
|
+
.diff-table {
|
|
198
|
+
tab-size: 8;
|
|
199
|
+
width: 100%;
|
|
200
|
+
border-collapse: separate;
|
|
201
|
+
border-spacing: 0;
|
|
202
|
+
}
|
|
203
|
+
.blob-num {
|
|
204
|
+
position: relative;
|
|
205
|
+
color: #1f2328;
|
|
206
|
+
width: 1%;
|
|
207
|
+
min-width: 50px;
|
|
208
|
+
padding: 0 10px;
|
|
209
|
+
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
|
|
210
|
+
font-size: 12px;
|
|
211
|
+
line-height: 20px;
|
|
212
|
+
text-align: right;
|
|
213
|
+
white-space: nowrap;
|
|
214
|
+
vertical-align: top;
|
|
215
|
+
cursor: pointer;
|
|
216
|
+
-webkit-user-select: none;
|
|
217
|
+
user-select: none;
|
|
218
|
+
}
|
|
219
|
+
.blob-num.addition {
|
|
220
|
+
background-color: #ccffd8;
|
|
221
|
+
border-color: #1f883e;
|
|
222
|
+
}
|
|
223
|
+
.blob-num.deletion {
|
|
224
|
+
background-color: #ffd7d5;
|
|
225
|
+
border-color: #cf222e;
|
|
226
|
+
}
|
|
227
|
+
.blob-num::before {
|
|
228
|
+
content: attr(data-line-number);
|
|
229
|
+
}
|
|
230
|
+
.blob-code {
|
|
231
|
+
position: relative;
|
|
232
|
+
padding: 0 10px 0 22px;
|
|
233
|
+
vertical-align: top;
|
|
234
|
+
color: #1f2329;
|
|
235
|
+
}
|
|
236
|
+
.blob-code.code-addition {
|
|
237
|
+
background-color: #e6ffec;
|
|
238
|
+
}
|
|
239
|
+
.blob-code.code-deletion {
|
|
240
|
+
background-color: #ffebe9;
|
|
241
|
+
}
|
|
242
|
+
.blob-code.skip,
|
|
243
|
+
.blob-code.message {
|
|
244
|
+
text-align: center;
|
|
245
|
+
}
|
|
246
|
+
.blob-code.skip .blob-code-inner,
|
|
247
|
+
.blob-code.message .blob-code-inner {
|
|
248
|
+
font-weight: bold;
|
|
249
|
+
color: #6a737d;
|
|
250
|
+
padding: 10px 0;
|
|
251
|
+
}
|
|
252
|
+
.blob-code-inner {
|
|
253
|
+
display: table-cell;
|
|
254
|
+
overflow: visible;
|
|
255
|
+
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
|
|
256
|
+
font-size: 12px;
|
|
257
|
+
word-wrap: anywhere;
|
|
258
|
+
white-space: pre-wrap;
|
|
259
|
+
}
|
|
260
|
+
.blob-code-inner::before {
|
|
261
|
+
content: attr(data-code-prefix);
|
|
262
|
+
position: absolute;
|
|
263
|
+
top: 1px;
|
|
264
|
+
left: 8px;
|
|
265
|
+
padding-right: 8px;
|
|
266
|
+
}
|
|
267
|
+
</style>
|
|
268
|
+
</head>
|
|
269
|
+
<body>
|
|
270
|
+
<h1>Changelog Diff</h1>
|
|
271
|
+
|
|
272
|
+
%FILES%
|
|
273
|
+
</body>
|
|
274
|
+
</html>`;
|
|
275
|
+
const LINE_ADDED = 1;
|
|
276
|
+
const LINE_REMOVED = -1;
|
|
277
|
+
const LINE_CONTEXT = 0;
|
|
278
|
+
/**
|
|
279
|
+
* Escape HTML sequence
|
|
280
|
+
* @param str
|
|
281
|
+
*/
|
|
282
|
+
function escapeHTMLSequence(str) {
|
|
283
|
+
return str.replace(/[\u00A0-\u9999<>&]/g, (i) => '&#' + i.charCodeAt(0) + ';');
|
|
284
|
+
}
|
|
285
|
+
const fileNameWithHashRegEx = /(.+)(-[a-f0-9]{6,20})(\.[a-z0-9]+)$/i;
|
|
286
|
+
/**
|
|
287
|
+
* Changelog generator class
|
|
288
|
+
* @class
|
|
289
|
+
* @classdesc
|
|
290
|
+
* Generates a changelog file based on the differences between two asset folders or zip files
|
|
291
|
+
* @example
|
|
292
|
+
* const changelogGenerator = new ChangelogGenerator({
|
|
293
|
+
* previousAssetsPath: './dist-zip/previous',
|
|
294
|
+
* currentAssetsPath: './dist-zip/current',
|
|
295
|
+
* destinationPath: './dist-zip',
|
|
296
|
+
* changelogFilename: 'CHANGELOG.html',
|
|
297
|
+
* });
|
|
298
|
+
* changelogGenerator.generateChangelog();
|
|
299
|
+
*/
|
|
300
|
+
class ChangelogGenerator {
|
|
301
|
+
oldAssetsPath;
|
|
302
|
+
newAssetsPath;
|
|
303
|
+
destinationPath;
|
|
304
|
+
changelogFilename;
|
|
305
|
+
changelogTemplate = changelogTemplate;
|
|
306
|
+
diffFileTemplateHandler;
|
|
307
|
+
diffLineTemplateHandler;
|
|
308
|
+
contextLines = 3;
|
|
309
|
+
logger;
|
|
310
|
+
/**
|
|
311
|
+
* Changelog generator class constructor
|
|
312
|
+
* @param opts
|
|
313
|
+
*/
|
|
314
|
+
constructor(opts) {
|
|
315
|
+
const { oldAssetsPath, newAssetsPath, destinationPath, changelogTemplate, diffFileTemplateHandler, diffLineTemplateHandler, changelogFilename, contextLines, } = opts;
|
|
316
|
+
this.logger = createLogger();
|
|
317
|
+
if (!oldAssetsPath || !newAssetsPath || !destinationPath) {
|
|
318
|
+
throw new Error('Previous assets path, current assets path and destination path are required');
|
|
319
|
+
}
|
|
320
|
+
if (oldAssetsPath === newAssetsPath) {
|
|
321
|
+
throw new Error('Previous and current assets paths must be different');
|
|
322
|
+
}
|
|
323
|
+
if (!this.isExists(oldAssetsPath)) {
|
|
324
|
+
throw new Error(`Previous assets path ${oldAssetsPath} does not exist`);
|
|
325
|
+
}
|
|
326
|
+
if (!this.isExists(newAssetsPath)) {
|
|
327
|
+
throw new Error(`Current assets path ${newAssetsPath} does not exist`);
|
|
328
|
+
}
|
|
329
|
+
if (this.isZipFile(oldAssetsPath)) {
|
|
330
|
+
const unzipDestinationPath = path.resolve(os.tmpdir(), crypto.createHash('md5').update(oldAssetsPath).digest('hex'));
|
|
331
|
+
this.oldAssetsPath = this.unzipFile(oldAssetsPath, unzipDestinationPath)
|
|
332
|
+
.then(() => {
|
|
333
|
+
return this.normalizeAssetFolderPath(unzipDestinationPath);
|
|
334
|
+
})
|
|
335
|
+
.then((path) => {
|
|
336
|
+
if (this.isEmptyFolder(path)) {
|
|
337
|
+
throw new Error(`Previous assets path ${path} is empty`);
|
|
338
|
+
}
|
|
339
|
+
return path;
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
else if (this.isFolder(oldAssetsPath)) {
|
|
343
|
+
const path = this.normalizeAssetFolderPath(oldAssetsPath);
|
|
344
|
+
if (this.isEmptyFolder(path)) {
|
|
345
|
+
throw new Error(`Previous assets path ${path} is empty`);
|
|
346
|
+
}
|
|
347
|
+
this.oldAssetsPath = Promise.resolve(path);
|
|
348
|
+
}
|
|
349
|
+
else {
|
|
350
|
+
throw new Error(`Invalid previous assets path ${oldAssetsPath}. It must be a folder or a zip file`);
|
|
351
|
+
}
|
|
352
|
+
if (this.isZipFile(newAssetsPath)) {
|
|
353
|
+
const unzipDestinationPath = path.resolve(os.tmpdir(), crypto.createHash('md5').update(newAssetsPath).digest('hex'));
|
|
354
|
+
this.newAssetsPath = this.unzipFile(newAssetsPath, unzipDestinationPath)
|
|
355
|
+
.then(() => {
|
|
356
|
+
return this.normalizeAssetFolderPath(unzipDestinationPath);
|
|
357
|
+
})
|
|
358
|
+
.then((path) => {
|
|
359
|
+
if (this.isEmptyFolder(path)) {
|
|
360
|
+
throw new Error(`Current assets path ${path} is empty`);
|
|
361
|
+
}
|
|
362
|
+
return path;
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
else if (this.isFolder(newAssetsPath)) {
|
|
366
|
+
const path = this.normalizeAssetFolderPath(newAssetsPath);
|
|
367
|
+
if (this.isEmptyFolder(path)) {
|
|
368
|
+
throw new Error(`Current assets path ${newAssetsPath} is empty`);
|
|
369
|
+
}
|
|
370
|
+
this.newAssetsPath = Promise.resolve(path);
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
throw new Error(`Invalid current assets path ${newAssetsPath}. It must be a folder or a zip file`);
|
|
374
|
+
}
|
|
375
|
+
this.destinationPath = destinationPath;
|
|
376
|
+
this.mkdirpSync(this.destinationPath);
|
|
377
|
+
if (changelogFilename) {
|
|
378
|
+
this.changelogFilename = changelogFilename;
|
|
379
|
+
}
|
|
380
|
+
else {
|
|
381
|
+
this.changelogFilename = 'CHANGELOG.html';
|
|
382
|
+
}
|
|
383
|
+
if (changelogTemplate) {
|
|
384
|
+
if (this.templateIsValid(changelogTemplate)) {
|
|
385
|
+
this.changelogTemplate = changelogTemplate;
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
this.logger.warn(colors.yellow('Invalid changelog template, using default'));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
if (typeof diffFileTemplateHandler === 'function') {
|
|
392
|
+
this.diffFileTemplateHandler = diffFileTemplateHandler;
|
|
393
|
+
}
|
|
394
|
+
if (typeof diffLineTemplateHandler === 'function') {
|
|
395
|
+
this.diffLineTemplateHandler = diffLineTemplateHandler;
|
|
396
|
+
}
|
|
397
|
+
if (contextLines) {
|
|
398
|
+
this.contextLines = contextLines;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
templateIsValid(template) {
|
|
402
|
+
return template.includes('%FILES%');
|
|
403
|
+
}
|
|
404
|
+
isExists(assetPath) {
|
|
405
|
+
return fs.existsSync(assetPath);
|
|
406
|
+
}
|
|
407
|
+
isZipFile(assetPath) {
|
|
408
|
+
return assetPath.endsWith('.zip');
|
|
409
|
+
}
|
|
410
|
+
isFolder(assetPath) {
|
|
411
|
+
return fs.lstatSync(assetPath).isDirectory();
|
|
412
|
+
}
|
|
413
|
+
isEmptyFolder(assetPath) {
|
|
414
|
+
return fs.readdirSync(assetPath, { withFileTypes: true }).length === 0;
|
|
415
|
+
}
|
|
416
|
+
mkdirpSync(dir) {
|
|
417
|
+
if (!fs.existsSync(dir)) {
|
|
418
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
async unzipFile(assetPath, destinationPath) {
|
|
422
|
+
fs.rmSync(destinationPath, { force: true, recursive: true });
|
|
423
|
+
return extractZip(assetPath, { dir: destinationPath });
|
|
424
|
+
}
|
|
425
|
+
normalizeAssetFolderPath(assetPath) {
|
|
426
|
+
const folderContent = fs.readdirSync(assetPath);
|
|
427
|
+
if (folderContent.length === 1 && fs.lstatSync(path.join(assetPath, folderContent[0])).isDirectory()) {
|
|
428
|
+
return this.normalizeAssetFolderPath(path.join(assetPath, folderContent[0]));
|
|
429
|
+
}
|
|
430
|
+
return assetPath;
|
|
431
|
+
}
|
|
432
|
+
pathToPosix(path) {
|
|
433
|
+
return path.replace(/\\/g, '/');
|
|
434
|
+
}
|
|
435
|
+
diffTableTemplate(diffLinesHTML) {
|
|
436
|
+
return /* HTML */ `<table class="diff-table">
|
|
437
|
+
<tbody>
|
|
438
|
+
${diffLinesHTML}
|
|
439
|
+
</tbody>
|
|
440
|
+
</table>`;
|
|
441
|
+
}
|
|
442
|
+
getDiffTableHTML(diffLines) {
|
|
443
|
+
const diffLinesHTML = diffLines
|
|
444
|
+
.filter((value, index, array) => {
|
|
445
|
+
if (value.lineType === LINE_CONTEXT) {
|
|
446
|
+
return ((index >= 0 && index < this.contextLines) ||
|
|
447
|
+
(index > array.length - (this.contextLines + 1) && index <= array.length - 1) ||
|
|
448
|
+
array
|
|
449
|
+
.slice(index - this.contextLines, index + this.contextLines + 1)
|
|
450
|
+
.some((line) => line.lineType !== LINE_CONTEXT));
|
|
451
|
+
}
|
|
452
|
+
return true;
|
|
453
|
+
})
|
|
454
|
+
.map((line, index, array) => {
|
|
455
|
+
if (index > 0) {
|
|
456
|
+
const prevLine = array[index - 1];
|
|
457
|
+
if (line.lineNumber - prevLine.lineNumber > 1) {
|
|
458
|
+
return [{ lineNumber: -1, lineContent: '', lineType: LINE_CONTEXT }, line];
|
|
459
|
+
}
|
|
460
|
+
}
|
|
461
|
+
return [line];
|
|
462
|
+
})
|
|
463
|
+
.flat()
|
|
464
|
+
.map((line) => this.diffLineHTML(line))
|
|
465
|
+
.join('');
|
|
466
|
+
return this.diffTableTemplate(diffLinesHTML);
|
|
467
|
+
}
|
|
468
|
+
diffLineMessageTemplate(message) {
|
|
469
|
+
return /* HTML */ `<tr>
|
|
470
|
+
<td class="blob-num"></td>
|
|
471
|
+
<td class="blob-num"></td>
|
|
472
|
+
<td class="blob-code message">
|
|
473
|
+
<span class="blob-code-inner">${message}</span>
|
|
474
|
+
</td>
|
|
475
|
+
</tr>`;
|
|
476
|
+
}
|
|
477
|
+
diffLineSkipTemplate() {
|
|
478
|
+
return /* HTML */ `<tr>
|
|
479
|
+
<td class="blob-num"></td>
|
|
480
|
+
<td class="blob-num"></td>
|
|
481
|
+
<td class="blob-code skip">
|
|
482
|
+
<span class="blob-code-inner">Skip</span>
|
|
483
|
+
</td>
|
|
484
|
+
</tr>`;
|
|
485
|
+
}
|
|
486
|
+
diffLineTemplate(diffLine) {
|
|
487
|
+
const { lineNumber, lineContent, lineType } = diffLine;
|
|
488
|
+
const numClass = lineType === LINE_ADDED ? 'addition' : lineType === LINE_REMOVED ? 'deletion' : '';
|
|
489
|
+
const codeClass = lineType === LINE_ADDED ? 'code-addition' : lineType === LINE_REMOVED ? 'code-deletion' : '';
|
|
490
|
+
const prefix = lineType === LINE_ADDED ? '+' : lineType === LINE_REMOVED ? '-' : ' ';
|
|
491
|
+
return /* HTML */ `<tr>
|
|
492
|
+
<td
|
|
493
|
+
class="blob-num ${numClass}${numClass === 'addition' ? ' empty' : ''}"
|
|
494
|
+
${numClass !== 'addition' ? ` data-line-number="${lineNumber}"` : ''}
|
|
495
|
+
></td>
|
|
496
|
+
<td
|
|
497
|
+
class="blob-num ${numClass}${numClass === 'deletion' ? ' empty' : ''}"
|
|
498
|
+
${numClass !== 'deletion' ? ` data-line-number="${lineNumber}"` : ''}
|
|
499
|
+
></td>
|
|
500
|
+
<td class="blob-code ${codeClass}">
|
|
501
|
+
<span class="blob-code-inner" data-code-prefix="${prefix}">${escapeHTMLSequence(lineContent ?? '')}</span>
|
|
502
|
+
</td>
|
|
503
|
+
</tr>`;
|
|
504
|
+
}
|
|
505
|
+
diffLineHTML(diffLine) {
|
|
506
|
+
if (this.diffLineTemplateHandler) {
|
|
507
|
+
return this.diffLineTemplateHandler(diffLine);
|
|
508
|
+
}
|
|
509
|
+
const { lineNumber } = diffLine;
|
|
510
|
+
if (lineNumber === -1) {
|
|
511
|
+
return this.diffLineSkipTemplate();
|
|
512
|
+
}
|
|
513
|
+
return this.diffLineTemplate(diffLine);
|
|
514
|
+
}
|
|
515
|
+
diffFileTemplate(filename, htmlDiff) {
|
|
516
|
+
if (this.diffFileTemplateHandler) {
|
|
517
|
+
return this.diffFileTemplateHandler(filename, htmlDiff);
|
|
518
|
+
}
|
|
519
|
+
return /* HTML */ `<div class="diff-file">
|
|
520
|
+
<div class="diff-file-title">${filename}</div>
|
|
521
|
+
<div class="diff-file-content">${htmlDiff}</div>
|
|
522
|
+
</div>`;
|
|
523
|
+
}
|
|
524
|
+
async generateAssetFoldersDiff() {
|
|
525
|
+
this.logger.info(colors.blue(`Comparing asset folders ${await this.oldAssetsPath} and ${await this.newAssetsPath}`));
|
|
526
|
+
const dirDiff = await dirCompare.compare(await this.oldAssetsPath, await this.newAssetsPath, {
|
|
527
|
+
compareContent: true,
|
|
528
|
+
skipSymlinks: true,
|
|
529
|
+
compareSize: true,
|
|
530
|
+
compareDate: false,
|
|
531
|
+
compareNameHandler: (name1, name2) => {
|
|
532
|
+
if (fileNameWithHashRegEx.test(name1)) {
|
|
533
|
+
name1 = name1.replace(fileNameWithHashRegEx, '$1$3');
|
|
534
|
+
}
|
|
535
|
+
if (fileNameWithHashRegEx.test(name2)) {
|
|
536
|
+
name2 = name2.replace(fileNameWithHashRegEx, '$1$3');
|
|
537
|
+
}
|
|
538
|
+
if (name1.localeCompare(name2) === 0) {
|
|
539
|
+
return 0;
|
|
540
|
+
}
|
|
541
|
+
return name1.localeCompare(name2) > 0 ? 1 : -1;
|
|
542
|
+
},
|
|
543
|
+
});
|
|
544
|
+
return dirDiff.diffSet?.filter((d) => d.state !== 'equal' || d.name1 !== d.name2) || [];
|
|
545
|
+
}
|
|
546
|
+
async generateAssetFilesDiff(oldFileString, newFileString) {
|
|
547
|
+
const dmp = new DiffMatchPatch();
|
|
548
|
+
const linesChars = dmp.diff_linesToChars_(oldFileString, newFileString);
|
|
549
|
+
const linesDiff = dmp.diff_main(linesChars.chars1, linesChars.chars2, false);
|
|
550
|
+
dmp.diff_charsToLines_(linesDiff, linesChars.lineArray);
|
|
551
|
+
let lineNumber = 0;
|
|
552
|
+
return linesDiff
|
|
553
|
+
.map((diffLine) => {
|
|
554
|
+
const [lineType, lineContent] = diffLine;
|
|
555
|
+
const linesCount = lineContent.endsWith('\n')
|
|
556
|
+
? lineContent.split('\n').length - 1
|
|
557
|
+
: lineContent.split('\n').length;
|
|
558
|
+
const lines = lineContent
|
|
559
|
+
.split('\n')
|
|
560
|
+
.map((line, index) => {
|
|
561
|
+
return { lineContent: line, lineNumber: lineNumber + index + 1, lineType };
|
|
562
|
+
})
|
|
563
|
+
.slice(0, linesCount);
|
|
564
|
+
if (lineType !== LINE_REMOVED) {
|
|
565
|
+
lineNumber += linesCount;
|
|
566
|
+
}
|
|
567
|
+
return lines;
|
|
568
|
+
})
|
|
569
|
+
.flat();
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Generate diff for a file
|
|
573
|
+
* @param difference
|
|
574
|
+
* @private
|
|
575
|
+
*/
|
|
576
|
+
async generateFilesDiff(difference) {
|
|
577
|
+
if (difference.state === 'equal') {
|
|
578
|
+
const filepath1 = this.pathToPosix(path.join('.', difference.relativePath, difference.name1 ?? ''));
|
|
579
|
+
const filepath2 = this.pathToPosix(path.join('.', difference.relativePath, difference.name2 ?? ''));
|
|
580
|
+
return this.diffFileTemplate(
|
|
581
|
+
/* HTML */ `<span class="renamed"
|
|
582
|
+
>Renamed <span class="from">${filepath1}</span> -> <span class="to">${filepath2}</span></span
|
|
583
|
+
>`, this.diffTableTemplate(this.diffLineMessageTemplate('No changes')));
|
|
584
|
+
}
|
|
585
|
+
const assetFile1Path = difference.path1 && difference.name1 ? path.join(difference.path1, difference.name1) : null;
|
|
586
|
+
const assetFile2Path = difference.path2 && difference.name2 ? path.join(difference.path2, difference.name2) : null;
|
|
587
|
+
const filepath = this.pathToPosix(path.join('.', difference.relativePath, (difference.name1 || difference.name2) ?? ''));
|
|
588
|
+
if (difference.state === 'left' && assetFile1Path) {
|
|
589
|
+
return this.diffFileTemplate(
|
|
590
|
+
/* HTML */ `<span class="removed">Removed ${filepath}</span>`, this.diffTableTemplate(this.diffLineMessageTemplate('File removed')));
|
|
591
|
+
}
|
|
592
|
+
if (difference.state === 'right' && assetFile2Path) {
|
|
593
|
+
return this.diffFileTemplate(
|
|
594
|
+
/* HTML */ `<span class="added">Added ${filepath}</span>`, this.diffTableTemplate(this.diffLineMessageTemplate('File added')));
|
|
595
|
+
}
|
|
596
|
+
if (difference.state === 'distinct' && assetFile1Path && assetFile2Path) {
|
|
597
|
+
const filepath1 = this.pathToPosix(path.join('.', difference.relativePath, difference.name1 ?? ''));
|
|
598
|
+
const filepath2 = this.pathToPosix(path.join('.', difference.relativePath, difference.name2 ?? ''));
|
|
599
|
+
const filenameTitle = difference.name1 !== difference.name2
|
|
600
|
+
? /* HTML */ `<span class="renamed"
|
|
601
|
+
>Renamed <span class="from">${filepath1}</span> -> <span class="to">${filepath2}</span></span
|
|
602
|
+
>`
|
|
603
|
+
: filepath;
|
|
604
|
+
return this.diffFileTemplate(filenameTitle, (await isBinaryFile(assetFile1Path)) || (await isBinaryFile(assetFile2Path))
|
|
605
|
+
? this.diffTableTemplate(this.diffLineMessageTemplate('Binary file'))
|
|
606
|
+
: this.getDiffTableHTML(await this.generateAssetFilesDiff(fs.readFileSync(assetFile1Path, 'utf-8'), fs.readFileSync(assetFile2Path, 'utf-8'))));
|
|
607
|
+
}
|
|
608
|
+
return '';
|
|
609
|
+
}
|
|
610
|
+
/**
|
|
611
|
+
* Generate changelog file based on the differences between two asset folders or zip files
|
|
612
|
+
* and write it to the destination folder
|
|
613
|
+
*/
|
|
614
|
+
async generateChangelog() {
|
|
615
|
+
this.logger.info(colors.green('Generating changelog'));
|
|
616
|
+
const diffSet = await this.generateAssetFoldersDiff();
|
|
617
|
+
const htmlDiff = (await Promise.all(diffSet.map((difference) => this.generateFilesDiff(difference)))).join('');
|
|
618
|
+
this.logger.info(colors.green('Writing changelog file'));
|
|
619
|
+
const templateArray = this.changelogTemplate.split('%FILES%');
|
|
620
|
+
templateArray.splice(1, 0, htmlDiff);
|
|
621
|
+
const changelogHTML = templateArray.join('');
|
|
622
|
+
fs.writeFileSync(path.join(this.destinationPath, this.changelogFilename), changelogHTML);
|
|
623
|
+
this.logger.info(colors.green(`Changelog file written to ${path.join(this.destinationPath, this.changelogFilename)}`));
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
154
627
|
const cli = cac('pp-dev');
|
|
155
|
-
const colors = createColors();
|
|
156
628
|
let profileSession = global.__pp_dev_profile_session;
|
|
157
629
|
let profileCount = 0;
|
|
158
630
|
const stopProfiler = (log) => {
|
|
@@ -243,19 +715,21 @@ cli
|
|
|
243
715
|
clearScreen: options.clearScreen,
|
|
244
716
|
optimizeDeps: { force: options.force },
|
|
245
717
|
server: cleanOptions(options),
|
|
718
|
+
customLogger: createLogger(options.logLevel),
|
|
246
719
|
}, true));
|
|
720
|
+
if (!server.config.base || server.config.base === '/') {
|
|
721
|
+
throw new Error('base cannot be equal to "/" or empty string');
|
|
722
|
+
}
|
|
247
723
|
if (!server.httpServer) {
|
|
248
724
|
throw new Error('HTTP server not available');
|
|
249
725
|
}
|
|
250
726
|
await server.listen();
|
|
251
|
-
const
|
|
727
|
+
const logger = createLogger(options.logLevel);
|
|
252
728
|
const ppDevStartTime = global.__pp_dev_start_time ?? false;
|
|
253
729
|
const startupDurationString = ppDevStartTime
|
|
254
730
|
? colors.dim(`ready in ${colors.reset(colors.bold(Math.ceil(performance.now() - ppDevStartTime)))} ms`)
|
|
255
731
|
: '';
|
|
256
|
-
info(`\n ${colors.green(`${colors.bold('PP-DEV')} v${VERSION}`)} ${startupDurationString}\n
|
|
257
|
-
clear: !server.config.logger.hasWarned,
|
|
258
|
-
});
|
|
732
|
+
logger.info(`\n ${colors.green(`${colors.bold('PP-DEV')} v${VERSION}`)} ${startupDurationString}\n`);
|
|
259
733
|
server.printUrls();
|
|
260
734
|
bindShortcuts(server, {
|
|
261
735
|
print: true,
|
|
@@ -265,7 +739,7 @@ cli
|
|
|
265
739
|
description: 'start/stop the profiler',
|
|
266
740
|
async action(server) {
|
|
267
741
|
if (profileSession) {
|
|
268
|
-
await stopProfiler(
|
|
742
|
+
await stopProfiler(logger.info);
|
|
269
743
|
}
|
|
270
744
|
else {
|
|
271
745
|
const inspector = await import('node:inspector').then((r) => r.default);
|
|
@@ -274,7 +748,7 @@ cli
|
|
|
274
748
|
profileSession.connect();
|
|
275
749
|
profileSession.post('Profiler.enable', () => {
|
|
276
750
|
profileSession?.post('Profiler.start', () => {
|
|
277
|
-
|
|
751
|
+
logger.info('Profiler started');
|
|
278
752
|
res();
|
|
279
753
|
});
|
|
280
754
|
});
|
|
@@ -330,10 +804,13 @@ cli
|
|
|
330
804
|
if (!base.endsWith('/')) {
|
|
331
805
|
base += '/';
|
|
332
806
|
}
|
|
807
|
+
if (base === '/') {
|
|
808
|
+
throw new Error('basePath cannot be equal to "/" or empty string');
|
|
809
|
+
}
|
|
333
810
|
const baseWithoutTrailingSlash = base.substring(0, base.lastIndexOf('/'));
|
|
334
811
|
const templateName = nextServer.nextConfig.serverRuntimeConfig.templateName;
|
|
335
812
|
const ppDevConfig = nextServer.nextConfig.serverRuntimeConfig.ppDevConfig;
|
|
336
|
-
const { backendBaseURL, portalPageId, templateLess = true, enableProxyCache = true, miHudLess = true, proxyCacheTTL = 10 * 60 * 1000, } = ppDevConfig;
|
|
813
|
+
const { backendBaseURL, portalPageId, templateLess = true, enableProxyCache = true, miHudLess = true, proxyCacheTTL = 10 * 60 * 1000, disableSSLValidation = false, } = ppDevConfig;
|
|
337
814
|
server.use(initPPRedirect(base, templateName));
|
|
338
815
|
if (backendBaseURL) {
|
|
339
816
|
const baseUrlHost = new URL(backendBaseURL).host;
|
|
@@ -345,6 +822,7 @@ cli
|
|
|
345
822
|
},
|
|
346
823
|
portalPageId,
|
|
347
824
|
templateLess,
|
|
825
|
+
disableSSLValidation,
|
|
348
826
|
});
|
|
349
827
|
if (enableProxyCache) {
|
|
350
828
|
let ttl = +proxyCacheTTL;
|
|
@@ -357,6 +835,7 @@ cli
|
|
|
357
835
|
devServer: server,
|
|
358
836
|
baseURL: backendBaseURL,
|
|
359
837
|
proxyIgnore: ['/@vite', '/@metricinsights', '/@', baseWithoutTrailingSlash],
|
|
838
|
+
disableSSLValidation,
|
|
360
839
|
}));
|
|
361
840
|
const isIndexRegExp = new RegExp(`^((${base})|/)$`);
|
|
362
841
|
// Get portal page variables from the backend (also, redirect magic)
|
|
@@ -384,12 +863,12 @@ cli
|
|
|
384
863
|
}));
|
|
385
864
|
}
|
|
386
865
|
});
|
|
387
|
-
const
|
|
866
|
+
const logger = createLogger();
|
|
388
867
|
const ppDevStartTime = global.__pp_dev_start_time ?? false;
|
|
389
868
|
const startupDurationString = ppDevStartTime
|
|
390
869
|
? colors.dim(`ready in ${colors.reset(colors.bold(Math.ceil(performance.now() - ppDevStartTime)))} ms`)
|
|
391
870
|
: '';
|
|
392
|
-
info(`\n ${colors.green(`${colors.bold('PP-DEV')} v${VERSION}`)} ${startupDurationString}\n`, {
|
|
871
|
+
logger.info(`\n ${colors.green(`${colors.bold('PP-DEV')} v${VERSION}`)} ${startupDurationString}\n`, {
|
|
393
872
|
clear: true,
|
|
394
873
|
});
|
|
395
874
|
server.printUrls(base);
|
|
@@ -455,6 +934,7 @@ cli
|
|
|
455
934
|
.option('--force', `[boolean] force the optimizer to ignore the cache and re-bundle (experimental)`)
|
|
456
935
|
.option('--emptyOutDir', `[boolean] force empty outDir when it's outside of root`)
|
|
457
936
|
.option('-w, --watch', `[boolean] rebuilds when modules have changed on disk`)
|
|
937
|
+
.option('--changelog [assetsFile]', `[boolean | string] generate changelog between assetsFile and current build (default: false)`)
|
|
458
938
|
.action(async (root, options) => {
|
|
459
939
|
filterDuplicateOptions(options);
|
|
460
940
|
const buildOptions = cleanOptions(options);
|
|
@@ -465,7 +945,7 @@ cli
|
|
|
465
945
|
const { plugins, ...fileConfig } = configFromFile.config;
|
|
466
946
|
config = mergeConfig(config, fileConfig);
|
|
467
947
|
}
|
|
468
|
-
|
|
948
|
+
const buildConfig = mergeConfig(config, {
|
|
469
949
|
root,
|
|
470
950
|
base: options.base,
|
|
471
951
|
mode: options.mode,
|
|
@@ -474,7 +954,55 @@ cli
|
|
|
474
954
|
clearScreen: options.clearScreen,
|
|
475
955
|
optimizeDeps: { force: options.force },
|
|
476
956
|
build: buildOptions,
|
|
477
|
-
}, true)
|
|
957
|
+
}, true);
|
|
958
|
+
await build(buildConfig);
|
|
959
|
+
if (buildOptions.changelog) {
|
|
960
|
+
const executionRoot = root || process.cwd();
|
|
961
|
+
const outDir = buildConfig.build?.outDir || 'dist';
|
|
962
|
+
let oldAssetsPath = '';
|
|
963
|
+
if (typeof buildOptions.changelog === 'string') {
|
|
964
|
+
oldAssetsPath = path.resolve(executionRoot, buildOptions.changelog);
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
const backupsDirPath = path.resolve(executionRoot, buildConfig.ppDevConfig?.syncBackupsDir || 'backups');
|
|
968
|
+
if (!fs.existsSync(backupsDirPath)) {
|
|
969
|
+
createLogger(options.logLevel).warn(colors.yellow(`backups directory not found, skipping changelog generation`));
|
|
970
|
+
return;
|
|
971
|
+
}
|
|
972
|
+
const backups = fs.readdirSync(backupsDirPath, { withFileTypes: true });
|
|
973
|
+
if (!backups.length) {
|
|
974
|
+
createLogger(options.logLevel).warn(colors.yellow(`no backups found, skipping changelog generation`));
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
const latestBackup = backups
|
|
978
|
+
.filter((value) => {
|
|
979
|
+
return value.isFile() && value.name.endsWith('.zip');
|
|
980
|
+
})
|
|
981
|
+
.reduce((latest, current) => {
|
|
982
|
+
const latestTime = fs.statSync(path.resolve(backupsDirPath, latest.name)).mtimeMs;
|
|
983
|
+
const currentTime = fs.statSync(path.resolve(backupsDirPath, current.name)).mtimeMs;
|
|
984
|
+
return latestTime > currentTime ? latest : current;
|
|
985
|
+
}, backups[0]).name;
|
|
986
|
+
oldAssetsPath = path.resolve(backupsDirPath, latestBackup);
|
|
987
|
+
}
|
|
988
|
+
const currentAssetFilePath = path.resolve(executionRoot, outDir);
|
|
989
|
+
let changelogDestination = 'dist-zip';
|
|
990
|
+
if (buildConfig.ppDevConfig) {
|
|
991
|
+
if (buildConfig.ppDevConfig.distZip === false) {
|
|
992
|
+
changelogDestination = buildConfig.build?.outDir || 'dist';
|
|
993
|
+
}
|
|
994
|
+
else if (typeof buildConfig.ppDevConfig.distZip === 'object' &&
|
|
995
|
+
typeof buildConfig.ppDevConfig.distZip.outDir === 'string') {
|
|
996
|
+
changelogDestination = buildConfig.ppDevConfig.distZip.outDir;
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
const changelogGenerator = new ChangelogGenerator({
|
|
1000
|
+
oldAssetsPath,
|
|
1001
|
+
newAssetsPath: currentAssetFilePath,
|
|
1002
|
+
destinationPath: path.resolve(executionRoot, changelogDestination),
|
|
1003
|
+
});
|
|
1004
|
+
await changelogGenerator.generateChangelog();
|
|
1005
|
+
}
|
|
478
1006
|
}
|
|
479
1007
|
catch (e) {
|
|
480
1008
|
createLogger(options.logLevel).error(colors.red(`error during build:\n${e.stack}`), { error: e });
|
|
@@ -484,6 +1012,32 @@ cli
|
|
|
484
1012
|
stopProfiler((message) => createLogger(options.logLevel).info(message));
|
|
485
1013
|
}
|
|
486
1014
|
});
|
|
1015
|
+
// changelog
|
|
1016
|
+
cli
|
|
1017
|
+
.command('changelog [oldAssetPath] [newAssetPath]', 'generate changelog between two assets files/folders')
|
|
1018
|
+
.option('--oldAssetsPath <oldAssetsPath>', `[string] path to the old assets zip file or folder`)
|
|
1019
|
+
.option('--newAssetsPath <newAssetsPath>', `[string] path to the new assets zip file or folder`)
|
|
1020
|
+
.option('--destination <destination>', `[string] destination folder for the changelog (default: .)`)
|
|
1021
|
+
.option('--filename <filename>', `[string] filename for the changelog (default: CHANGELOG.html)`)
|
|
1022
|
+
.action(async (oldAssetPath, newAssetPath, options) => {
|
|
1023
|
+
filterDuplicateOptions(options);
|
|
1024
|
+
const { oldAssetsPath: oldPath = oldAssetPath, newAssetsPath: newPath = newAssetPath, destination = '.', filename = 'CHANGELOG.html', logLevel, } = options;
|
|
1025
|
+
const root = process.cwd();
|
|
1026
|
+
if (!oldPath || !newPath) {
|
|
1027
|
+
createLogger(logLevel).error(colors.red(`error during changelog generation: oldAssetPath and newAssetPath are required`));
|
|
1028
|
+
process.exit(1);
|
|
1029
|
+
}
|
|
1030
|
+
const fullOldPath = path.resolve(root, oldPath);
|
|
1031
|
+
const fullNewPath = path.resolve(root, newPath);
|
|
1032
|
+
const fullDestination = path.resolve(root, destination);
|
|
1033
|
+
const changelogGenerator = new ChangelogGenerator({
|
|
1034
|
+
oldAssetsPath: fullOldPath,
|
|
1035
|
+
newAssetsPath: fullNewPath,
|
|
1036
|
+
destinationPath: fullDestination,
|
|
1037
|
+
changelogFilename: filename,
|
|
1038
|
+
});
|
|
1039
|
+
await changelogGenerator.generateChangelog();
|
|
1040
|
+
});
|
|
487
1041
|
// optimize
|
|
488
1042
|
cli
|
|
489
1043
|
.command('optimize [root]', 'pre-bundle dependencies')
|