@jslint-org/jslint 2021.10.20 → 2021.11.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.
package/jslint_ci.sh DELETED
@@ -1,2749 +0,0 @@
1
- #!/bin/sh
2
-
3
- # POSIX reference
4
- # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/test.html
5
- # http://www.gnu.org/software/bash/manual/html_node/Shell-Parameter-Expansion.html
6
-
7
- # sh one-liner
8
- #
9
- # git branch -d -r origin/aa
10
- # git config --global diff.algorithm histogram
11
- # git fetch origin alpha beta master && git fetch upstream alpha beta master
12
- # git fetch origin alpha beta master --tags
13
- # git fetch upstream "refs/tags/*:refs/tags/*"
14
- # git ls-files --stage | sort
15
- # git ls-remote --heads origin
16
- # git update-index --chmod=+x aa.js
17
- # head CHANGELOG.md -n50
18
- # ln -f jslint.mjs ~/jslint.mjs
19
- # openssl rand -base64 32 # random key
20
- # sh jslint_ci.sh shCiBranchPromote origin alpha beta
21
- # sh jslint_ci.sh shRunWithScreenshotTxt .artifact/screenshot_changelog.svg head -n50 CHANGELOG.md
22
- # vim rgx-lowercase \L\1\e
23
-
24
- shBashrcDebianInit() {
25
- # this function will init debian:stable /etc/skel/.bashrc
26
- # https://sources.debian.org/src/bash/4.4-5/debian/skel.bashrc/
27
- # ~/.bashrc: executed by bash(1) for non-login shells.
28
- # see /usr/share/doc/bash/examples/startup-files (in the package bash-doc)
29
- # for examples
30
-
31
- # If not running interactively, don't do anything
32
- case $- in
33
- *i*) ;;
34
- *) return;;
35
- esac
36
-
37
- # don't put duplicate lines or lines starting with space in the history.
38
- # See bash(1) for more options
39
- HISTCONTROL=ignoreboth
40
-
41
- # append to the history file, don't overwrite it
42
- shopt -s histappend
43
-
44
- # for setting history length see HISTSIZE and HISTFILESIZE in bash(1)
45
- HISTSIZE=1000
46
- HISTFILESIZE=2000
47
-
48
- # check the window size after each command and, if necessary,
49
- # update the values of LINES and COLUMNS.
50
- shopt -s checkwinsize
51
-
52
- # If set, the pattern "**" used in a pathname expansion context will
53
- # match all files and zero or more directories and subdirectories.
54
- #shopt -s globstar
55
-
56
- # make less more friendly for non-text input files, see lesspipe(1)
57
- [ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)"
58
-
59
- # set variable identifying the chroot you work in (used in the prompt below)
60
- if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then
61
- debian_chroot=$(cat /etc/debian_chroot)
62
- fi
63
-
64
- # set a fancy prompt (non-color, unless we know we "want" color)
65
- case "$TERM" in
66
- xterm-color|*-256color) color_prompt=yes;;
67
- esac
68
-
69
- # uncomment for a colored prompt, if the terminal has the capability; turned
70
- # off by default to not distract the user: the focus in a terminal window
71
- # should be on the output of commands, not on the prompt
72
- #force_color_prompt=yes
73
-
74
- if [ -n "$force_color_prompt" ]; then
75
- if [ -x /usr/bin/tput ] && tput setaf 1 >&/dev/null; then
76
- # We have color support; assume it's compliant with Ecma-48
77
- # (ISO/IEC-6429). (Lack of such support is extremely rare, and such
78
- # a case would tend to support setf rather than setaf.)
79
- color_prompt=yes
80
- else
81
- color_prompt=
82
- fi
83
- fi
84
-
85
- if [ "$color_prompt" = yes ]; then
86
- PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\h\[\033[00m\]:\[\033[01;34m\]\w\[\033[00m\]\$ '
87
- else
88
- PS1='${debian_chroot:+($debian_chroot)}\u@\h:\w\$ '
89
- fi
90
- unset color_prompt force_color_prompt
91
-
92
- # If this is an xterm set the title to user@host:dir
93
- case "$TERM" in
94
- xterm*|rxvt*)
95
- PS1="\[\e]0;${debian_chroot:+($debian_chroot)}\u@\h: \w\a\]$PS1"
96
- ;;
97
- *)
98
- ;;
99
- esac
100
-
101
- # enable color support of ls and also add handy aliases
102
- if [ -x /usr/bin/dircolors ]; then
103
- test -r ~/.dircolors && eval "$(dircolors -b ~/.dircolors)" || eval "$(dircolors -b)"
104
- alias ls='ls --color=auto'
105
- #alias dir='dir --color=auto'
106
- #alias vdir='vdir --color=auto'
107
-
108
- alias grep='grep --color=auto'
109
- #alias fgrep='fgrep --color=auto'
110
- #alias egrep='egrep --color=auto'
111
- fi
112
-
113
- # colored GCC warnings and errors
114
- #export GCC_COLORS='error=01;31:warning=01;35:note=01;36:caret=01;32:locus=01:quote=01'
115
-
116
- # some more ls aliases
117
- alias ll='ls -alF'
118
- #alias la='ls -A'
119
- #alias l='ls -CF'
120
-
121
- # Alias definitions.
122
- # You may want to put all your additions into a separate file like
123
- # ~/.bash_aliases, instead of adding them here directly.
124
- # See /usr/share/doc/bash-doc/examples in the bash-doc package.
125
-
126
- if [ -f ~/.bash_aliases ]; then
127
- . ~/.bash_aliases
128
- fi
129
-
130
- # enable programmable completion features (you don't need to enable
131
- # this, if it's already enabled in /etc/bash.bashrc and /etc/profile
132
- # sources /etc/bash.bashrc).
133
- if ! shopt -oq posix; then
134
- if [ -f /usr/share/bash-completion/bash_completion ]; then
135
- . /usr/share/bash-completion/bash_completion
136
- elif [ -f /etc/bash_completion ]; then
137
- . /etc/bash_completion
138
- fi
139
- fi
140
- }
141
-
142
- shBrowserScreenshot() {(set -e
143
- # this function will run headless-chrome to screenshot url $1 with
144
- # window-size $2
145
- node --input-type=module -e '
146
- import moduleChildProcess from "child_process";
147
- import modulePath from "path";
148
- import moduleUrl from "url";
149
- // init debugInline
150
- (function () {
151
- let consoleError = console.error;
152
- globalThis.debugInline = globalThis.debugInline || function (...argList) {
153
-
154
- // this function will both print <argList> to stderr and return <argList>[0]
155
-
156
- consoleError("\n\ndebugInline");
157
- consoleError(...argList);
158
- consoleError("\n");
159
- return argList[0];
160
- };
161
- }());
162
- (async function () {
163
- let child;
164
- let exitCode;
165
- let file;
166
- let timeStart;
167
- let url;
168
- if (process.platform !== "linux") {
169
- return;
170
- }
171
- timeStart = Date.now();
172
- url = process.argv[1];
173
- if (!(
174
- /^\w+?:/
175
- ).test(url)) {
176
- url = modulePath.resolve(url);
177
- }
178
- file = moduleUrl.parse(url).pathname;
179
- // remove prefix $PWD from file
180
- if (String(file + "/").startsWith(process.cwd() + "/")) {
181
- file = file.replace(process.cwd(), "");
182
- }
183
- file = ".artifact/screenshot_browser_" + encodeURIComponent(file).replace((
184
- /%/g
185
- ), "_").toLowerCase() + ".png";
186
- child = moduleChildProcess.spawn(
187
- (
188
- process.platform === "darwin"
189
- ? "/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
190
- : process.platform === "win32"
191
- ? "C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe"
192
- : "/usr/bin/google-chrome-stable"
193
- ),
194
- [
195
- "--headless",
196
- "--ignore-certificate-errors",
197
- "--incognito",
198
- "--screenshot",
199
- "--timeout=30000",
200
- "--user-data-dir=/dev/null",
201
- "--window-size=800x600",
202
- "-screenshot=" + file,
203
- (
204
- (process.getuid && process.getuid() === 0)
205
- ? "--no-sandbox"
206
- : ""
207
- ),
208
- url
209
- ].concat(process.argv.filter(function (elem) {
210
- return elem.startsWith("-");
211
- })).filter(function (elem) {
212
- return elem;
213
- }),
214
- {
215
- stdio: [
216
- "ignore", 1, 2
217
- ]
218
- }
219
- );
220
- exitCode = await new Promise(function (resolve) {
221
- child.on("exit", resolve);
222
- });
223
- console.error(
224
- "shBrowserScreenshot"
225
- + "\n - url - " + url
226
- + "\n - wrote - " + file
227
- + "\n - timeElapsed - " + (Date.now() - timeStart) + " ms"
228
- + "\n - EXIT_CODE=" + exitCode
229
- );
230
- }());
231
- ' "$@" # '
232
- )}
233
-
234
- shCiArtifactUpload() {(set -e
235
- # this function will upload build-artifacts to branch-gh-pages
236
- local BRANCH
237
- local FILE
238
- node --input-type=module -e '
239
- process.exit(Number(
240
- `${process.version.split(".")[0]}.${process.arch}.${process.platform}`
241
- !== process.env.CI_NODE_VERSION_ARCH_PLATFORM
242
- ));
243
- ' || return 0
244
- # init .git/config
245
- git config --local user.email "github-actions@users.noreply.github.com"
246
- git config --local user.name "github-actions"
247
- # init $BRANCH
248
- BRANCH="$(git rev-parse --abbrev-ref HEAD)"
249
- git pull --unshallow origin "$BRANCH"
250
- # init $UPSTREAM_OWNER
251
- export UPSTREAM_OWNER="${UPSTREAM_OWNER:-jslint-org}"
252
- # init $UPSTREAM_REPO
253
- export UPSTREAM_REPO="${UPSTREAM_REPO:-jslint}"
254
- # screenshot changelog and files
255
- node --input-type=module -e '
256
- import moduleChildProcess from "child_process";
257
- (function () {
258
- [
259
- // parallel-task - screenshot changelog
260
- [
261
- "jslint_ci.sh",
262
- "shRunWithScreenshotTxt",
263
- ".artifact/screenshot_changelog.svg",
264
- "head",
265
- "-n50",
266
- "CHANGELOG.md"
267
- ],
268
- // parallel-task - screenshot files
269
- [
270
- "jslint_ci.sh",
271
- "shRunWithScreenshotTxt",
272
- ".artifact/screenshot_package_listing.svg",
273
- "shGitLsTree"
274
- ]
275
- ].forEach(function (argList) {
276
- moduleChildProcess.spawn("sh", argList, {
277
- stdio: [
278
- "ignore", 1, 2
279
- ]
280
- }).on("exit", function (exitCode) {
281
- if (exitCode) {
282
- process.exit(exitCode);
283
- }
284
- });
285
- });
286
- }());
287
- ' "$@" # '
288
- shCiArtifactUploadCustom
289
- # 1px-border around browser-screenshot
290
- if (ls .artifact/screenshot_browser_*.png 2>/dev/null \
291
- && mogrify -version 2>&1 | grep -i imagemagick)
292
- then
293
- mogrify -shave 1x1 -bordercolor black -border 1 \
294
- .artifact/screenshot_browser_*.png
295
- fi
296
- # add dir .artifact
297
- git add -f .artifact
298
- git rm --cached -r .artifact/tmp 2>/dev/null || true
299
- git commit -am "add dir .artifact"
300
- # checkout branch-gh-pages
301
- git fetch origin gh-pages
302
- git checkout -b gh-pages origin/gh-pages
303
- # update dir branch-$BRANCH
304
- rm -rf "branch-$BRANCH"
305
- mkdir -p "branch-$BRANCH"
306
- (set -e
307
- cd "branch-$BRANCH"
308
- git init -b branch1
309
- git pull --depth=1 .. "$BRANCH"
310
- rm -rf .git
311
- git add -f .
312
- )
313
- # update root-dir with branch-beta
314
- if [ "$BRANCH" = beta ]
315
- then
316
- rm -rf .artifact
317
- git checkout beta .
318
- # update apidoc.html
319
- for FILE in apidoc.html
320
- do
321
- if [ -f ".artifact/$FILE" ]
322
- then
323
- cp -a ".artifact/$FILE" .
324
- git add -f "$FILE"
325
- fi
326
- done
327
- fi
328
- # update README.md with branch-$BRANCH and $GITHUB_REPOSITORY
329
- sed -i \
330
- -e "s|/branch-[0-9A-Z_a-z]*/|/branch-$BRANCH/|g" \
331
- -e "s|\b$UPSTREAM_OWNER/$UPSTREAM_REPO\b|$GITHUB_REPOSITORY|g" \
332
- -e "s|\b$UPSTREAM_OWNER\.github\.io/$UPSTREAM_REPO\b|$(
333
- printf "$GITHUB_REPOSITORY" | sed -e "s|/|.github.io/|"
334
- )|g" \
335
- "branch-$BRANCH/README.md"
336
- git status
337
- git commit -am "update dir branch-$BRANCH" || true
338
- # if branch-gh-pages has more than 50 commits,
339
- # then backup and squash commits
340
- if [ "$(git rev-list --count gh-pages)" -gt 50 ]
341
- then
342
- # backup
343
- shGitCmdWithGithubToken push origin -f gh-pages:gh-pages-backup
344
- # squash commits
345
- git checkout --orphan squash1
346
- git commit --quiet -am squash || true
347
- # reset branch-gh-pages to squashed-commit
348
- git push . -f squash1:gh-pages
349
- git checkout gh-pages
350
- # force-push squashed-commit
351
- shGitCmdWithGithubToken push origin -f gh-pages
352
- fi
353
- # list files
354
- shGitLsTree
355
- # push branch-gh-pages
356
- shGitCmdWithGithubToken push origin gh-pages
357
- # validate http-links
358
- (set -e
359
- cd "branch-$BRANCH"
360
- sleep 15
361
- shDirHttplinkValidate
362
- )
363
- )}
364
-
365
- shCiArtifactUploadCustom() {(set -e
366
- return
367
- )}
368
-
369
- shCiBase() {(set -e
370
- # this function will run base-ci
371
- # update table-of-contents in README.md
372
- node --input-type=module -e '
373
- import moduleFs from "fs";
374
- (async function () {
375
- let data = await moduleFs.promises.readFile("README.md", "utf8");
376
- data = data.replace((
377
- /\n# Table of Contents$[\S\s]*?\n\n\n/m
378
- ), function () {
379
- let ii = -1;
380
- let toc = "\n# Table of Contents\n";
381
- data.replace((
382
- /(\n\n\n#|\n###) (.*)/g
383
- ), function (ignore, level, title) {
384
- if (title === "Table of Contents") {
385
- ii += 1;
386
- return "";
387
- }
388
- if (ii < 0) {
389
- return "";
390
- }
391
- switch (level.trim()) {
392
- case "#":
393
- ii += 1;
394
- toc += "\n" + ii + ". [" + title + "](#";
395
- break;
396
- case "###":
397
- toc += " - [" + title + "](#";
398
- break;
399
- }
400
- toc += title.toLowerCase().replace((
401
- /[^ \-0-9A-Z_a-z]/g
402
- ), "").replace((
403
- / /g
404
- ), "-") + ")\n";
405
- return "";
406
- });
407
- toc += "\n\n";
408
- return toc;
409
- });
410
- await moduleFs.promises.writeFile("README.md", data);
411
- }());
412
- ' "$@" # '
413
- shCiBaseCustom
414
- git diff
415
- )}
416
-
417
- shCiBaseCustom() {(set -e
418
- return
419
- )}
420
-
421
- shCiBranchPromote() {(set -e
422
- # this function will promote branch $REMOTE/$BRANCH1 to branch $REMOTE/$BRANCH2
423
- local BRANCH1
424
- local BRANCH2
425
- local REMOTE
426
- REMOTE="$1"
427
- shift
428
- BRANCH1="$1"
429
- shift
430
- BRANCH2="$1"
431
- shift
432
- git fetch "$REMOTE" "$BRANCH1"
433
- git push "$REMOTE" "$REMOTE/$BRANCH1:$BRANCH2" "$@"
434
- )}
435
-
436
- shDirHttplinkValidate() {(set -e
437
- # this function will validate http-links embedded in .html and .md files
438
- node --input-type=module -e '
439
- import moduleFs from "fs";
440
- import moduleHttps from "https";
441
- import moduleUrl from "url";
442
- (async function () {
443
- let dict = {};
444
- Array.from(
445
- await moduleFs.promises.readdir(".")
446
- ).forEach(async function (file) {
447
- let data;
448
- if (file === "CHANGELOG.md" || !(
449
- /.\.html$|.\.md$/m
450
- ).test(file)) {
451
- return;
452
- }
453
- data = await moduleFs.promises.readFile(file, "utf8");
454
- data.replace((
455
- /\bhttps?:\/\/.*?(?:[\s")\]]|\W?$)/gm
456
- ), function (url) {
457
- let req;
458
- url = url.slice(0, -1).replace((
459
- /[\u0022\u0027]/g
460
- ), "").replace((
461
- /\/branch-\w+?\//g
462
- ), "/branch-alpha/").replace((
463
- /\bjslint-org\/jslint\b/g
464
- ), process.env.GITHUB_REPOSITORY || "jslint-org/jslint").replace((
465
- /\bjslint-org\.github\.io\/jslint\b/g
466
- ), String(
467
- process.env.GITHUB_REPOSITORY || "jslint-org/jslint"
468
- ).replace("/", ".github.io/"));
469
- if (url.startsWith("http://")) {
470
- throw new Error("shDirHttplinkValidate - insecure link " + url);
471
- }
472
- // ignore duplicate-link
473
- if (dict.hasOwnProperty(url)) {
474
- return "";
475
- }
476
- dict[url] = true;
477
- req = moduleHttps.request(moduleUrl.parse(
478
- url
479
- ), function (res) {
480
- console.error(
481
- "shDirHttplinkValidate " + res.statusCode + " " + url
482
- );
483
- if (!(res.statusCode < 400)) {
484
- throw new Error(
485
- "shDirHttplinkValidate - " + file
486
- + " - unreachable link " + url
487
- );
488
- }
489
- req.abort();
490
- res.destroy();
491
- });
492
- req.setTimeout(30000);
493
- req.end();
494
- return "";
495
- });
496
- data.replace((
497
- /(\bhref=|\bsrc=|\burl\(|\[[^]*?\]\()("?.*?)(?:[")\]]|$)/gm
498
- ), function (ignore, linkType, url) {
499
- if (!linkType.startsWith("[")) {
500
- url = url.slice(1);
501
- }
502
- if (url.length === 0 || url.startsWith("data:")) {
503
- return;
504
- }
505
- // ignore duplicate-link
506
- if (dict.hasOwnProperty(url)) {
507
- return "";
508
- }
509
- dict[url] = true;
510
- if (!(
511
- /^https?|^mailto:|^[#\/]/m
512
- ).test(url)) {
513
- moduleFs.stat(url.split("?")[0], function (ignore, exists) {
514
- console.error(
515
- "shDirHttplinkValidate " + Boolean(exists) + " " + url
516
- );
517
- if (!exists) {
518
- throw new Error(
519
- "shDirHttplinkValidate - " + file
520
- + " - unreachable link " + url
521
- );
522
- }
523
- });
524
- }
525
- return "";
526
- });
527
- });
528
- }());
529
- ' "$@" # '
530
- )}
531
-
532
- shDuList() {(set -e
533
- # this function will du $1 and sort its subdir by size
534
- du -md1 "$1" | sort -nr
535
- )}
536
-
537
- shGitCmdWithGithubToken() {(set -e
538
- # this function will run git $CMD with $GITHUB_TOKEN
539
- local CMD
540
- local EXIT_CODE
541
- local REMOTE
542
- local URL
543
- printf "shGitCmdWithGithubToken $*\n"
544
- CMD="$1"
545
- shift
546
- REMOTE="$1"
547
- shift
548
- URL="$(
549
- git config "remote.$REMOTE.url" \
550
- | sed -e "s|https://|https://x-access-token:$GITHUB_TOKEN@|"
551
- )"
552
- EXIT_CODE=0
553
- # hide $GITHUB_TOKEN in case of err
554
- git "$CMD" "$URL" "$@" 2>/dev/null || EXIT_CODE="$?"
555
- printf "shGitCmdWithGithubToken - EXIT_CODE=$EXIT_CODE\n" 1>&2
556
- return "$EXIT_CODE"
557
- )}
558
-
559
- shGitGc() {(set -e
560
- # this function will gc unreachable .git objects
561
- # http://stackoverflow.com/questions/3797907/how-to-remove-unused-objects-from-a-git-repository
562
- git \
563
- -c gc.reflogExpire=0 \
564
- -c gc.reflogExpireUnreachable=0 \
565
- -c gc.rerereresolved=0 \
566
- -c gc.rerereunresolved=0 \
567
- -c gc.pruneExpire=now \
568
- gc
569
- )}
570
-
571
- shGitInitBase() {(set -e
572
- # this function will git init && create basic git-template from jslint-org/base
573
- local BRANCH
574
- git init
575
- git config core.autocrlf input
576
- git remote remove base 2>/dev/null || true
577
- git remote add base https://github.com/jslint-org/base
578
- git fetch base base
579
- for BRANCH in base alpha
580
- do
581
- git branch -D "$BRANCH" 2>/dev/null || true
582
- git checkout -b "$BRANCH" base/base
583
- done
584
- sed -i.bak "s|owner/repo|${1:-owner/repo}|" .gitconfig
585
- rm .gitconfig.bak
586
- cp .gitconfig .git/config
587
- git commit -am "update owner/repo to $1" || true
588
- )}
589
-
590
-
591
- shGitLsTree() {(set -e
592
- # this function will "git ls-tree" all files committed in HEAD
593
- # example use:
594
- # shGitLsTree | sort -rk3 # sort by date
595
- # shGitLsTree | sort -rk4 # sort by size
596
- node --input-type=module -e '
597
- import moduleChildProcess from "child_process";
598
- (async function () {
599
- let result;
600
- // get file, mode, size
601
- result = await new Promise(function (resolve) {
602
- result = "";
603
- moduleChildProcess.spawn("git", [
604
- "ls-tree", "-lr", "HEAD"
605
- ], {
606
- encoding: "utf8",
607
- stdio: [
608
- "ignore", "pipe", 2
609
- ]
610
- }).on("exit", function () {
611
- resolve(result);
612
- }).stdout.on("data", function (chunk) {
613
- result += chunk;
614
- }).setEncoding("utf8");
615
- });
616
- result = Array.from(result.matchAll(
617
- /^(\S+?) +?\S+? +?\S+? +?(\S+?)\t(\S+?)$/gm
618
- )).map(function ([
619
- ignore, mode, size, file
620
- ]) {
621
- return {
622
- file,
623
- mode: mode.slice(-3),
624
- size: Number(size)
625
- };
626
- });
627
- result = result.sort(function (aa, bb) {
628
- return aa.file > bb.file || -1;
629
- });
630
- result = result.slice(0, 1000);
631
- result.unshift({
632
- file: ".",
633
- mode: "755",
634
- size: 0
635
- });
636
- // get date
637
- result.forEach(function (elem) {
638
- result[0].size += elem.size;
639
- moduleChildProcess.spawn("git", [
640
- "log", "--max-count=1", "--format=%at", elem.file
641
- ], {
642
- stdio: [
643
- "ignore", "pipe", 2
644
- ]
645
- }).stdout.on("data", function (chunk) {
646
- elem.date = new Date(
647
- Number(chunk) * 1000
648
- ).toISOString().slice(0, 19) + "Z";
649
- });
650
- });
651
- process.on("exit", function () {
652
- let iiPad;
653
- let sizePad;
654
- iiPad = String(result.length).length + 1;
655
- sizePad = String(Math.ceil(result[0].size / 1024)).length;
656
- process.stdout.write(result.map(function (elem, ii) {
657
- return (
658
- String(ii + ".").padStart(iiPad, " ")
659
- + " " + elem.mode
660
- + " " + elem.date
661
- + " " + String(
662
- Math.ceil(elem.size / 1024)
663
- ).padStart(sizePad, " ") + " KB"
664
- + " " + elem.file
665
- + "\n"
666
- );
667
- }).join(""));
668
- });
669
- }());
670
- ' "$@" # '
671
- )}
672
-
673
- shGitSquashPop() {(set -e
674
- # this function will squash HEAD to given $COMMIT
675
- # http://stackoverflow.com/questions/5189560
676
- # /how-can-i-squash-my-last-x-commits-together-using-git
677
- COMMIT="$1"
678
- MESSAGE="$2"
679
- # reset git to previous $COMMIT
680
- git reset "$COMMIT"
681
- git add .
682
- # commit HEAD immediately after previous $COMMIT
683
- git commit -am "$MESSAGE" || true
684
- )}
685
-
686
- shGrep() {(set -e
687
- # this function will recursively grep . for $REGEXP
688
- REGEXP="$1"
689
- shift
690
- FILE_FILTER="\
691
- /\\.|~$|/(obj|release)/|(\\b|_)(\\.\\d|\
692
- archive|artifact|\
693
- bower_component|build|\
694
- coverage|\
695
- doc|\
696
- external|\
697
- fixture|\
698
- git_module|\
699
- jquery|\
700
- log|\
701
- min|misc|mock|\
702
- node_module|\
703
- old|\
704
- raw|\rollup|\
705
- swp|\
706
- tmp|\
707
- vendor)s{0,1}(\\b|_)\
708
- "
709
- find . -type f |
710
- grep -v -E "$FILE_FILTER" |
711
- tr "\n" "\000" |
712
- xargs -0 grep -HIin -E "$REGEXP" "$@" |
713
- tee /tmp/shGrep.txt || true
714
- )}
715
-
716
- shGrepReplace() {(set -e
717
- # this function will inline grep-and-replace /tmp/shGrep.txt
718
- node --input-type=module -e '
719
- import moduleFs from "fs";
720
- import moduleOs from "os";
721
- import modulePath from "path";
722
- (async function () {
723
- "use strict";
724
- let data;
725
- let dict = {};
726
- data = await moduleFs.promises.readFile((
727
- moduleOs.tmpdir() + "/shGrep.txt"
728
- ), "utf8");
729
- data = data.replace((
730
- /^(.+?):(\d+?):(.*?)$/gm
731
- ), function (ignore, file, lineno, str) {
732
- dict[file] = dict[file] || moduleFs.readFileSync( //jslint-quiet
733
- modulePath.resolve(file),
734
- "utf8"
735
- ).split("\n");
736
- dict[file][lineno - 1] = str;
737
- return "";
738
- });
739
- Object.entries(dict).forEach(function ([
740
- file, data
741
- ]) {
742
- moduleFs.promises.writeFile(file, data.join("\n"));
743
- });
744
- }());
745
- ' "$@" # '
746
- )}
747
-
748
- shHttpFileServer() {(set -e
749
- # this function will run simple node http-file-server on port $PORT
750
- if [ ! "$npm_config_mode_auto_restart" ]
751
- then
752
- local EXIT_CODE
753
- EXIT_CODE=0
754
- export npm_config_mode_auto_restart=1
755
- while true
756
- do
757
- printf "\n"
758
- git diff --color 2>/dev/null | cat || true
759
- printf "\nshHttpFileServer - (re)starting $*\n"
760
- (shHttpFileServer "$@") || EXIT_CODE="$?"
761
- printf "process exited with code $EXIT_CODE\n"
762
- # if $EXIT_CODE != 77, then exit process
763
- # http://en.wikipedia.org/wiki/Unix_signal
764
- if [ "$EXIT_CODE" != 77 ]
765
- then
766
- break
767
- fi
768
- # else restart process after 1 second
769
- sleep 1
770
- done
771
- return
772
- fi
773
- node --input-type=module -e '
774
- import moduleChildProcess from "child_process";
775
- import moduleFs from "fs";
776
- import moduleHttp from "http";
777
- import modulePath from "path";
778
- import moduleRepl from "repl";
779
- import moduleUrl from "url";
780
- // init debugInline
781
- (function () {
782
- let consoleError = console.error;
783
- globalThis.debugInline = globalThis.debugInline || function (...argList) {
784
-
785
- // this function will both print <argList> to stderr and return <argList>[0]
786
-
787
- consoleError("\n\ndebugInline");
788
- consoleError(...argList);
789
- consoleError("\n");
790
- return argList[0];
791
- };
792
- }());
793
- (async function httpFileServer() {
794
-
795
- // this function will start http-file-server
796
-
797
- let contentTypeDict = {
798
- ".bmp": "image/bmp",
799
- ".cjs": "application/javascript; charset=utf-8",
800
- ".css": "text/css; charset=utf-8",
801
- ".gif": "image/gif",
802
- ".htm": "text/html; charset=utf-8",
803
- ".html": "text/html; charset=utf-8",
804
- ".jpe": "image/jpeg",
805
- ".jpeg": "image/jpeg",
806
- ".jpg": "image/jpeg",
807
- ".js": "application/javascript; charset=utf-8",
808
- ".json": "application/json; charset=utf-8",
809
- ".md": "text/markdown; charset=utf-8",
810
- ".mjs": "application/javascript; charset=utf-8",
811
- ".pdf": "application/pdf",
812
- ".png": "image/png",
813
- ".svg": "image/svg+xml; charset=utf-8",
814
- ".txt": "text/plain; charset=utf-8",
815
- ".wasm": "application/wasm",
816
- ".woff": "font/woff",
817
- ".woff2": "font/woff2",
818
- ".xml": "application/xml; charset=utf-8",
819
- "/": "text/html; charset=utf-8"
820
- };
821
- if (process.argv[1]) {
822
- await import("file://" + modulePath.resolve(process.argv[1]));
823
- }
824
- process.env.PORT = process.env.PORT || "8080";
825
- console.error("http-file-server listening on port " + process.env.PORT);
826
- moduleHttp.createServer(function (req, res) {
827
- let file;
828
- let pathname;
829
- let timeStart;
830
- // init timeStart
831
- timeStart = Date.now();
832
- // init pathname
833
- pathname = moduleUrl.parse(req.url).pathname;
834
- // debug - serverLog
835
- res.on("close", function () {
836
- if (pathname === "/favicon.ico") {
837
- return;
838
- }
839
- console.error(
840
- "serverLog - "
841
- + new Date(timeStart).toISOString() + " - "
842
- + (Date.now() - timeStart) + "ms - "
843
- + (res.statusCode || 0) + " " + req.method + " " + pathname
844
- );
845
- });
846
- // debug - echo request
847
- if (pathname === "/echo") {
848
- res.write(JSON.stringify(req.headers, undefined, 4) + "\n");
849
- req.pipe(res);
850
- return;
851
- }
852
- // replace trailing "/" with "/index.html"
853
- file = pathname.slice(1).replace((
854
- /\/$/
855
- ), "/index.html");
856
- // resolve file
857
- file = modulePath.resolve(file);
858
- // security - disable parent-directory lookup
859
- if (!file.startsWith(process.cwd() + modulePath.sep)) {
860
- res.statusCode = 404;
861
- res.end();
862
- return;
863
- }
864
- moduleFs.readFile(file, function (err, data) {
865
- let contentType;
866
- if (err) {
867
- res.statusCode = 404;
868
- res.end();
869
- return;
870
- }
871
- contentType = contentTypeDict[(
872
- /^\/$|\.[^.]*?$|$/m
873
- ).exec(file)[0]];
874
- if (contentType) {
875
- res.setHeader("content-type", contentType);
876
- }
877
- res.end(data);
878
- });
879
- }).listen(process.env.PORT);
880
- }());
881
- (function jslintDir() {
882
-
883
- // this function will jslint current-directory
884
-
885
- moduleFs.stat((
886
- process.env.HOME + "/jslint.mjs"
887
- ), function (ignore, exists) {
888
- if (exists) {
889
- moduleChildProcess.spawn("node", [
890
- process.env.HOME + "/jslint.mjs", "."
891
- ], {
892
- stdio: [
893
- "ignore", 1, 2
894
- ]
895
- });
896
- }
897
- });
898
- }());
899
- (function replStart() {
900
-
901
- // this function will start repl-debugger
902
-
903
- let that;
904
- // start repl
905
- that = moduleRepl.start({
906
- useGlobal: true
907
- });
908
- // init history
909
- that.setupHistory(modulePath.resolve(
910
- process.env.HOME + "/.node_repl_history"
911
- ), function () {
912
- return;
913
- });
914
- // save eval-function
915
- that.evalDefault = that.eval;
916
- // hook custom-eval-function
917
- that.eval = function (script, context, file, onError) {
918
- script.replace((
919
- /^(\S+) (.*?)\n/
920
- ), function (ignore, match1, match2) {
921
- switch (match1) {
922
- // syntax-sugar - run shell-cmd
923
- case "$":
924
- switch (match2.split(" ").slice(0, 2).join(" ")) {
925
- // syntax-sugar - run git diff
926
- case "git diff":
927
- match2 += " --color";
928
- break;
929
- // syntax-sugar - run git log
930
- case "git log":
931
- match2 += " -n 10";
932
- break;
933
- // syntax-sugar - run ll
934
- case "ll":
935
- match2 = "ls -Fal";
936
- break;
937
- }
938
- match2 = match2.replace((
939
- /^git /
940
- ), "git --no-pager ");
941
- // run shell-cmd
942
- console.error("$ " + match2);
943
- moduleChildProcess.spawn(match2, {
944
- shell: true,
945
- stdio: [
946
- "ignore", 1, 2
947
- ]
948
- // print exitCode
949
- }).on("exit", function (exitCode) {
950
- console.error("$ EXIT_CODE=" + exitCode);
951
- that.evalDefault("\n", context, file, onError);
952
- });
953
- script = "\n";
954
- break;
955
- // syntax-sugar - map text with charCodeAt
956
- case "charCode":
957
- console.error(
958
- match2.split("").map(function (chr) {
959
- return (
960
- "\\u"
961
- + chr.charCodeAt(0).toString(16).padStart(4, 0)
962
- );
963
- }).join("")
964
- );
965
- script = "\n";
966
- break;
967
- // syntax-sugar - sort chr
968
- case "charSort":
969
- console.error(JSON.stringify(match2.split("").sort().join("")));
970
- script = "\n";
971
- break;
972
- // syntax-sugar - list obj-keys, sorted by item-type
973
- // console.error(Object.keys(global).map(function(key){return(typeof global[key]===\u0027object\u0027&&global[key]&&global[key]===global[key]?\u0027global\u0027:typeof global[key])+\u0027 \u0027+key;}).sort().join(\u0027\n\u0027)) //jslint-quiet
974
- case "keys":
975
- script = (
976
- "console.error(Object.keys(" + match2
977
- + ").map(function(key){return("
978
- + "typeof " + match2 + "[key]===\u0027object\u0027&&"
979
- + match2 + "[key]&&"
980
- + match2 + "[key]===global[key]"
981
- + "?\u0027global\u0027"
982
- + ":typeof " + match2 + "[key]"
983
- + ")+\u0027 \u0027+key;"
984
- + "}).sort().join(\u0027\\n\u0027))\n"
985
- );
986
- break;
987
- // syntax-sugar - print String(val)
988
- case "print":
989
- script = "console.error(String(" + match2 + "))\n";
990
- break;
991
- }
992
- });
993
- // eval script
994
- that.evalDefault(script, context, file, onError);
995
- };
996
- }());
997
- (function watchDir() {
998
-
999
- // this function will watch current-directory for changes
1000
-
1001
- moduleFs.readdir(".", function (ignore, fileList) {
1002
- fileList.forEach(function (file) {
1003
- if (file[0] === ".") {
1004
- return;
1005
- }
1006
- moduleFs.stat(file, function (ignore, stats) {
1007
- if (!(stats && stats.isFile())) {
1008
- return;
1009
- }
1010
- moduleFs.watchFile(file, {
1011
- interval: 1000,
1012
- persistent: false
1013
- }, function () {
1014
- console.error("watchFile - modified - " + file);
1015
- setTimeout(process.exit.bind(undefined, 77), 1000);
1016
- });
1017
- });
1018
- });
1019
- });
1020
- }());
1021
- ' "$@" # '
1022
- )}
1023
-
1024
- shImageLogoCreate() {(set -e
1025
- # this function will create .png logo
1026
- local SIZE
1027
- echo '
1028
- <!DOCTYPE html>
1029
- <html lang="en">
1030
- <head>
1031
- <title>logo</title>
1032
- <style>
1033
- /* sh jslint_ci.sh shBrowserScreenshot asset_image_logo.html --window-size=512x512 */
1034
- /* csslint box-model:false */
1035
- /* csslint ignore:start */
1036
- *,
1037
- *:after,
1038
- *:before {
1039
- box-sizing: border-box;
1040
- }
1041
- @font-face {
1042
- font-family: Daley;
1043
- font-weight: bold;
1044
- src: url("asset_font_daley_bold.woff2") format("woff2");
1045
- }
1046
- /* csslint ignore:end */
1047
- body,
1048
- div {
1049
- margin: 0;
1050
- }
1051
- .container1 {
1052
- background: antiquewhite;
1053
- border: 24px solid darkslategray;
1054
- border-radius: 96px;
1055
- color: darkslategray;
1056
- font-family: Daley;
1057
- height: 512px;
1058
- margin: 0;
1059
- position: relative;
1060
- width: 512px;
1061
- zoom: 100%;
1062
- /*
1063
- background: transparent;
1064
- border: 24px solid black;
1065
- color: black;
1066
- */
1067
- }
1068
- .text1 {
1069
- font-size: 256px;
1070
- left: 44px;
1071
- position: absolute;
1072
- top: 32px;
1073
- }
1074
- .text2 {
1075
- bottom: 8px;
1076
- font-size: 192px;
1077
- left: 44px;
1078
- position: absolute;
1079
- }
1080
- </style>
1081
- </head>
1082
- <body>
1083
- <div class="container1">
1084
- <div class="text1">JS</div>
1085
- <div class="text2">Lint</div>
1086
- </div>
1087
- </body>
1088
- </html>
1089
- ' > .artifact/asset_image_logo_512.html
1090
- cp asset_font_daley_bold.woff2 .artifact || true
1091
- # screenshot asset_image_logo_512.png
1092
- shBrowserScreenshot .artifact/asset_image_logo_512.html \
1093
- --window-size=512x512 \
1094
- -screenshot=.artifact/asset_image_logo_512.png
1095
- # create various smaller thumbnails
1096
- for SIZE in 32 64 128 256
1097
- do
1098
- convert -resize "${SIZE}x${SIZE}" .artifact/asset_image_logo_512.png \
1099
- ".artifact/asset_image_logo_$SIZE.png"
1100
- printf \
1101
- "shImageLogoCreate - wrote - .artifact/asset_image_logo_$SIZE.png\n" 1>&2
1102
- done
1103
- # convert to svg @ https://convertio.co/png-svg/
1104
- )}
1105
-
1106
- shImageToDataUri() {(set -e
1107
- # this function will convert image $1 to data-uri string
1108
- node --input-type=module -e '
1109
- import moduleFs from "fs";
1110
- import moduleHttps from "https";
1111
- (async function () {
1112
- let file;
1113
- let result;
1114
- file = process.argv[1];
1115
- if ((
1116
- /^https:\/\//
1117
- ).test(file)) {
1118
- result = await new Promise(function (resolve) {
1119
- moduleHttps.get(file, function (res) {
1120
- let chunkList;
1121
- chunkList = [];
1122
- res.on("data", function (chunk) {
1123
- chunkList.push(chunk);
1124
- }).on("end", function () {
1125
- resolve(Buffer.concat(chunkList));
1126
- });
1127
- });
1128
- });
1129
- } else {
1130
- result = await moduleFs.promises.readFile(file);
1131
- }
1132
- result = String(
1133
- "data:image/" + file.match(
1134
- /\.[^.]*?$|$/m
1135
- )[0].slice(1) + ";base64," + result.toString("base64")
1136
- ).replace((
1137
- /.{72}/g
1138
- ), "$&\\\n");
1139
- console.log(result);
1140
- }());
1141
- ' "$@" # '
1142
- )}
1143
-
1144
- shJsonNormalize() {(set -e
1145
- # this function will
1146
- # 1. read json-data from file $1
1147
- # 2. normalize json-data
1148
- # 3. write normalized json-data back to file $1
1149
- node --input-type=module -e '
1150
- import moduleFs from "fs";
1151
- (async function () {
1152
- function noop(val) {
1153
-
1154
- // this function will do nothing except return <val>
1155
-
1156
- return val;
1157
- }
1158
- function objectDeepCopyWithKeysSorted(obj) {
1159
-
1160
- // this function will recursively deep-copy <obj> with keys sorted
1161
-
1162
- let sorted;
1163
- if (typeof obj !== "object" || !obj) {
1164
- return obj;
1165
- }
1166
-
1167
- // recursively deep-copy list with child-keys sorted
1168
-
1169
- if (Array.isArray(obj)) {
1170
- return obj.map(objectDeepCopyWithKeysSorted);
1171
- }
1172
-
1173
- // recursively deep-copy obj with keys sorted
1174
-
1175
- sorted = {};
1176
- Object.keys(obj).sort().forEach(function (key) {
1177
- sorted[key] = objectDeepCopyWithKeysSorted(obj[key]);
1178
- });
1179
- return sorted;
1180
- }
1181
- console.error("shJsonNormalize - " + process.argv[1]);
1182
- moduleFs.promises.writeFile(
1183
- process.argv[1],
1184
- JSON.stringify(
1185
- objectDeepCopyWithKeysSorted(
1186
- JSON.parse(
1187
- noop(
1188
- await moduleFs.promises.readFile(
1189
- process.argv[1],
1190
- "utf8"
1191
- )
1192
- ).replace((
1193
- /^\ufeff/
1194
- ), "")
1195
- )
1196
- ),
1197
- undefined,
1198
- 4
1199
- ) + "\n"
1200
- );
1201
- }());
1202
- ' "$@" # '
1203
- )}
1204
-
1205
- shNpmPublishV0() {(set -e
1206
- # this function will npm-publish name $1 with bare package.json
1207
- local DIR
1208
- DIR=/tmp/shNpmPublishV0
1209
- rm -rf "$DIR" && mkdir -p "$DIR" && cd "$DIR"
1210
- printf "{\"name\":\"$1\",\"version\":\"0.0.1\"}\n" > package.json
1211
- shift
1212
- npm publish "$@"
1213
- )}
1214
-
1215
- shRawLibFetch() {(set -e
1216
- # this function will fetch raw-lib from $1
1217
- node --input-type=module -e '
1218
- import moduleChildProcess from "child_process";
1219
- import moduleFs from "fs";
1220
- import moduleHttps from "https";
1221
- import modulePath from "path";
1222
- // init debugInline
1223
- (function () {
1224
- let consoleError = console.error;
1225
- globalThis.debugInline = globalThis.debugInline || function (...argList) {
1226
-
1227
- // this function will both print <argList> to stderr and return <argList>[0]
1228
-
1229
- consoleError("\n\ndebugInline");
1230
- consoleError(...argList);
1231
- consoleError("\n");
1232
- return argList[0];
1233
- };
1234
- }());
1235
- (async function () {
1236
- let fetchList;
1237
- let matchObj;
1238
- let replaceList;
1239
- let repoDict;
1240
- function pipeToBuffer(res, dict, key) {
1241
-
1242
- // This function will concat data from <res> to <dict>[<key>].
1243
-
1244
- let data;
1245
- data = [];
1246
- res.on("data", function (chunk) {
1247
- data.push(chunk);
1248
- }).on("end", function () {
1249
- dict[key] = Buffer.concat(data);
1250
- });
1251
- }
1252
- // init matchObj
1253
- matchObj = (
1254
- /^\/\*jslint-disable\*\/\n\/\*\nshRawLibFetch\n(\{\n[\S\s]*?\n\})([\S\s]*?)\n\*\/\n/m
1255
- ).exec(await moduleFs.promises.readFile(process.argv[1], "utf8"));
1256
- // JSON.parse match1 with comment
1257
- fetchList = JSON.parse(matchObj[1]).fetchList;
1258
- replaceList = JSON.parse(matchObj[1]).replaceList || [];
1259
- // init repoDict, fetchList
1260
- repoDict = {};
1261
- fetchList.forEach(function (elem) {
1262
- if (!elem.url) {
1263
- return;
1264
- }
1265
- elem.prefix = elem.url.split("/").slice(0, 7).join("/");
1266
- // fetch dateCommitted
1267
- if (!repoDict.hasOwnProperty(elem.prefix)) {
1268
- repoDict[elem.prefix] = true;
1269
- moduleHttps.request(elem.prefix.replace(
1270
- "/blob/",
1271
- "/commits/"
1272
- ), function (res) {
1273
- pipeToBuffer(res, elem, "dateCommitted");
1274
- }).end();
1275
- }
1276
- // fetch file
1277
- if (elem.node) {
1278
- pipeToBuffer(moduleChildProcess.spawn("node", [
1279
- "-e", elem.node
1280
- ], {
1281
- stdio: [
1282
- "ignore", "pipe", 2
1283
- ]
1284
- }).stdout, elem, "data");
1285
- return;
1286
- }
1287
- if (elem.sh) {
1288
- pipeToBuffer(moduleChildProcess.spawn(elem.sh, {
1289
- shell: true,
1290
- stdio: [
1291
- "ignore", "pipe", 2
1292
- ]
1293
- }).stdout, elem, "data");
1294
- return;
1295
- }
1296
- moduleHttps.get(elem.url2 || elem.url.replace(
1297
- "https://github.com/",
1298
- "https://raw.githubusercontent.com/"
1299
- ).replace("/blob/", "/"), function (res) {
1300
- // http-redirect
1301
- if (res.statusCode === 302) {
1302
- moduleHttps.get(res.headers.location, function (res) {
1303
- pipeToBuffer(res, elem, "data");
1304
- });
1305
- return;
1306
- }
1307
- pipeToBuffer(res, elem, "data");
1308
- });
1309
- });
1310
- // parse fetched data
1311
- process.on("exit", function () {
1312
- let header;
1313
- let result;
1314
- let result0;
1315
- result = "";
1316
- fetchList.forEach(function (elem, ii, list) {
1317
- let prefix;
1318
- if (!elem.url) {
1319
- return;
1320
- }
1321
- // init prefix
1322
- prefix = "exports_" + modulePath.dirname(elem.url).replace(
1323
- "https://github.com/",
1324
- ""
1325
- ).replace((
1326
- /\/blob\/[^\/]*/
1327
- ), "/").replace((
1328
- /\W/g
1329
- ), "_").replace((
1330
- /(_)_+|_+$/g
1331
- ), "$1");
1332
- list[ii].exports = prefix + "_" + modulePath.basename(
1333
- elem.url
1334
- ).replace((
1335
- /\.js$/
1336
- ), "").replace((
1337
- /\W/g
1338
- ), "_");
1339
- if (elem.dataUriType) {
1340
- return;
1341
- }
1342
- if (elem.dateCommitted) {
1343
- result += (
1344
- "\n\n\n/*\n"
1345
- + "repo " + elem.prefix.replace("/blob/", "/tree/") + "\n"
1346
- + "committed " + (
1347
- /\b\d\d\d\d-\d\d-\d\dT\d\d:\d\d:\d\dZ\b|$/
1348
- ).exec(elem.dateCommitted.toString())[0] + "\n"
1349
- + "*/"
1350
- );
1351
- }
1352
- // comment /*...*/
1353
- if (elem.comment) {
1354
- elem.data = "/*\n" + elem.data.toString().trim().replace((
1355
- /\/\*/g
1356
- ), "/\\*").replace((
1357
- /\*\//g
1358
- ), "*\\/") + "\n*/";
1359
- }
1360
- // init header and footer
1361
- result += (
1362
- "\n\n\n/*\nfile " + elem.url + "\n*/\n"
1363
- + (elem.header || "")
1364
- + elem.data.toString().trim()
1365
- + (elem.footer || "")
1366
- );
1367
- });
1368
- result = (
1369
- "\n" + result.trim()
1370
- + "\n\n\n/*\nfile none\n*/\n/*jslint-enable*/\n"
1371
- );
1372
- // comment #!
1373
- result = result.replace((
1374
- /^#!/gm
1375
- ), "// $&");
1376
- // normalize newline
1377
- result = result.replace((
1378
- /\r\n|\r/g
1379
- ), "\n");
1380
- // remove trailing-whitespace
1381
- result = result.replace((
1382
- /[\t ]+$/gm
1383
- ), "");
1384
- // remove leading-newline before ket
1385
- result = result.replace((
1386
- /\n+?(\n *?\})/g
1387
- ), "$1");
1388
- // eslint - no-multiple-empty-lines
1389
- // https://github.com/eslint/eslint/blob/v7.2.0/docs/rules/no-multiple-empty-lines.md //jslint-quiet
1390
- result = result.replace((
1391
- /\n{4,}/g
1392
- ), "\n\n\n");
1393
- // replace from replaceList
1394
- replaceList.forEach(function ({
1395
- aa,
1396
- bb,
1397
- flags
1398
- }) {
1399
- result0 = result;
1400
- result = result.replace(new RegExp(aa, flags), bb);
1401
- if (result0 === result) {
1402
- throw new Error(
1403
- "shRawLibFetch - cannot find-and-replace snippet "
1404
- + JSON.stringify(aa)
1405
- );
1406
- }
1407
- });
1408
- // init header
1409
- header = (
1410
- matchObj.input.slice(0, matchObj.index)
1411
- + "/*jslint-disable*/\n/*\nshRawLibFetch\n"
1412
- + JSON.stringify(JSON.parse(matchObj[1]), undefined, 4) + "\n"
1413
- + matchObj[2].split("\n\n").filter(function (elem) {
1414
- return elem.trim();
1415
- }).map(function (elem) {
1416
- return elem.trim().replace((
1417
- /\*\//g
1418
- ), "*\\\\/").replace((
1419
- /\/\*/g
1420
- ), "/\\\\*") + "\n";
1421
- }).sort().join("\n") + "*/\n\n"
1422
- );
1423
- // replace from header-diff
1424
- header.replace((
1425
- /((?:^-.*?\n)+?)((?:^\+.*?\n)+)/gm
1426
- ), function (ignore, aa, bb) {
1427
- aa = "\n" + aa.replace((
1428
- /^-/gm
1429
- ), "").replace((
1430
- /\*\\\\\//g
1431
- ), "*/").replace((
1432
- /\/\\\\\*/g
1433
- ), "/*");
1434
- bb = "\n" + bb.replace((
1435
- /^\+/gm
1436
- ), "").replace((
1437
- /\*\\\\\//g
1438
- ), "*/").replace((
1439
- /\/\\\\\*/g
1440
- ), "/*");
1441
- result0 = result;
1442
- // disable $-escape in replacement-string
1443
- result = result.replace(aa, function () {
1444
- return bb;
1445
- });
1446
- if (result0 === result) {
1447
- throw new Error(
1448
- "shRawLibFetch - cannot find-and-replace snippet "
1449
- + JSON.stringify(aa)
1450
- );
1451
- }
1452
- return "";
1453
- });
1454
- // inline dataUri
1455
- fetchList.forEach(function ({
1456
- data,
1457
- dataUriType,
1458
- exports
1459
- }) {
1460
- if (!dataUriType) {
1461
- return;
1462
- }
1463
- data = (
1464
- "data:" + dataUriType + ";base64,"
1465
- + data.toString("base64")
1466
- );
1467
- result0 = result;
1468
- result = result.replace(
1469
- new RegExp("^" + exports + "$", "gm"),
1470
- // disable $-escape in replacement-string
1471
- function () {
1472
- return data;
1473
- }
1474
- );
1475
- if (result0 === result) {
1476
- throw new Error(
1477
- "shRawLibFetch - cannot find-and-replace snippet "
1478
- + JSON.stringify(exports)
1479
- );
1480
- }
1481
- });
1482
- // init footer
1483
- result = header + result;
1484
- matchObj.input.replace((
1485
- /\n\/\*\nfile none\n\*\/\n\/\*jslint-enable\*\/\n([\S\s]+)/
1486
- ), function (ignore, match1) {
1487
- result += "\n\n" + match1.trim() + "\n";
1488
- });
1489
- // write to file
1490
- moduleFs.writeFileSync(process.argv[1], result); //jslint-quiet
1491
- });
1492
- }());
1493
- ' "$@" # '
1494
- git diff
1495
- )}
1496
-
1497
- shRmDsStore() {(set -e
1498
- # this function will recursively rm .DS_Store from current-dir
1499
- # http://stackoverflow.com/questions/2016844/bash-recursively-remove-files
1500
- local NAME
1501
- for NAME in "._*" ".DS_Store" "desktop.ini" "npm-debug.log" "*~"
1502
- do
1503
- find . -iname "$NAME" -print0 | xargs -0 rm -f || true
1504
- done
1505
- )}
1506
-
1507
- shRunWithCoverage() {(set -e
1508
- # this function will run nodejs command $@ with v8-coverage
1509
- # and create coverage-report .artifact/coverage/index.html
1510
- node --input-type=module -e '
1511
- /*jslint indent2*/
1512
- let moduleChildProcess;
1513
- let moduleFs;
1514
- let moduleFsInitResolveList;
1515
- let modulePath;
1516
- let moduleUrl;
1517
- function assertOrThrow(condition, message) {
1518
- if (!condition) {
1519
- throw (
1520
- (!message || typeof message === "string")
1521
- ? new Error(String(message).slice(0, 2048))
1522
- : message
1523
- );
1524
- }
1525
- }
1526
- async function fsWriteFileWithParents(pathname, data) {
1527
- await moduleFsInit();
1528
- try {
1529
- await moduleFs.promises.writeFile(pathname, data);
1530
- } catch (ignore) {
1531
- await moduleFs.promises.mkdir(modulePath.dirname(pathname), {
1532
- recursive: true
1533
- });
1534
- await moduleFs.promises.writeFile(pathname, data);
1535
- }
1536
- console.error("wrote file " + pathname);
1537
- }
1538
- function htmlEscape(str) {
1539
- return String(str).replace((
1540
- /&/g
1541
- ), "&amp;").replace((
1542
- /</g
1543
- ), "&lt;").replace((
1544
- />/g
1545
- ), "&gt;");
1546
- }
1547
- async function moduleFsInit() {
1548
-
1549
- if (moduleFs !== undefined) {
1550
- return;
1551
- }
1552
- if (moduleFsInitResolveList !== undefined) {
1553
- return new Promise(function (resolve) {
1554
- moduleFsInitResolveList.push(resolve);
1555
- });
1556
- }
1557
- moduleFsInitResolveList = [];
1558
- [
1559
- moduleChildProcess,
1560
- moduleFs,
1561
- modulePath,
1562
- moduleUrl
1563
- ] = await Promise.all([
1564
- import("child_process"),
1565
- import("fs"),
1566
- import("path"),
1567
- import("url")
1568
- ]);
1569
- while (moduleFsInitResolveList.length > 0) {
1570
- moduleFsInitResolveList.shift()();
1571
- }
1572
- }
1573
- function v8CoverageListMerge(processCovs) {
1574
- let resultMerged = []; // List of merged scripts from processCovs.
1575
- let urlToScriptDict = new Map(); // Map scriptCov.url to scriptCovs.
1576
-
1577
- function compareRangeList(aa, bb) {
1578
- if (aa.startOffset !== bb.startOffset) {
1579
- return aa.startOffset - bb.startOffset;
1580
- }
1581
- return bb.endOffset - aa.endOffset;
1582
- }
1583
-
1584
- function dictKeyValueAppend(dict, key, val) {
1585
- let list = dict.get(key);
1586
- if (list === undefined) {
1587
- list = [];
1588
- dict.set(key, list);
1589
- }
1590
- list.push(val);
1591
- }
1592
-
1593
- function mergeTreeList(parentTrees) {
1594
- if (parentTrees.length <= 1) {
1595
- return parentTrees[0];
1596
- }
1597
- return {
1598
- children: mergeTreeListToChildren(parentTrees),
1599
- delta: parentTrees.reduce(function (aa, bb) {
1600
- return aa + bb.delta;
1601
- }, 0),
1602
- end: parentTrees[0].end,
1603
- start: parentTrees[0].start
1604
- };
1605
- }
1606
-
1607
- function mergeTreeListToChildren(parentTrees) {
1608
- let openRange;
1609
- let parentToChildDict = new Map(); // Map parent to child.
1610
- let queueList;
1611
- let queueListIi = 0;
1612
- let queueOffset;
1613
- let queueTrees;
1614
- let resultChildren = [];
1615
- let startToTreeDict = new Map(); // Map tree.start to tree.
1616
- function nextXxx() {
1617
- let [
1618
- nextOffset, nextTrees
1619
- ] = queueList[queueListIi] || [];
1620
- let openRangeEnd;
1621
- if (queueTrees === undefined) {
1622
- queueListIi += 1;
1623
- } else if (nextOffset === undefined || nextOffset > queueOffset) {
1624
- nextOffset = queueOffset;
1625
- nextTrees = queueTrees;
1626
- queueTrees = undefined;
1627
- } else {
1628
- if (nextOffset === queueOffset) {
1629
- queueTrees.forEach(function (tree) {
1630
- nextTrees.push(tree);
1631
- });
1632
- queueTrees = undefined;
1633
- }
1634
- queueListIi += 1;
1635
- }
1636
- if (nextOffset === undefined) {
1637
- if (openRange !== undefined) {
1638
- resultAppendNextChild();
1639
- }
1640
- return true;
1641
- }
1642
- if (openRange !== undefined && openRange.end <= nextOffset) {
1643
- resultAppendNextChild();
1644
- openRange = undefined;
1645
- }
1646
- if (openRange === undefined) {
1647
- openRangeEnd = nextOffset + 1;
1648
- nextTrees.forEach(function ({
1649
- parentIi,
1650
- tree
1651
- }) {
1652
- openRangeEnd = Math.max(openRangeEnd, tree.end);
1653
- dictKeyValueAppend(parentToChildDict, parentIi, tree);
1654
- });
1655
- queueOffset = openRangeEnd;
1656
- openRange = {
1657
- end: openRangeEnd,
1658
- start: nextOffset
1659
- };
1660
- } else {
1661
- nextTrees.forEach(function ({
1662
- parentIi,
1663
- tree
1664
- }) {
1665
- let right;
1666
- if (tree.end > openRange.end) {
1667
- right = treeSplit(tree, openRange.end);
1668
- if (queueTrees === undefined) {
1669
- queueTrees = [];
1670
- }
1671
- queueTrees.push({
1672
- parentIi,
1673
- tree: right
1674
- });
1675
- }
1676
- dictKeyValueAppend(parentToChildDict, parentIi, tree);
1677
- });
1678
- }
1679
- }
1680
- function resultAppendNextChild() {
1681
- let treesMatching = [];
1682
- parentToChildDict.forEach(function (nested) {
1683
- if (
1684
- nested.length === 1
1685
- && nested[0].start === openRange.start
1686
- && nested[0].end === openRange.end
1687
- ) {
1688
- treesMatching.push(nested[0]);
1689
- } else {
1690
- treesMatching.push({
1691
- children: nested,
1692
- delta: 0,
1693
- end: openRange.end,
1694
- start: openRange.start
1695
- });
1696
- }
1697
- });
1698
- parentToChildDict.clear();
1699
- resultChildren.push(mergeTreeList(treesMatching));
1700
- }
1701
- function treeSplit(tree, offset) {
1702
- let child;
1703
- let ii = 0;
1704
- let leftChildLen = tree.children.length;
1705
- let mid;
1706
- let resultTree;
1707
- let rightChildren;
1708
- while (ii < tree.children.length) {
1709
- child = tree.children[ii];
1710
- if (child.start < offset && offset < child.end) {
1711
- mid = treeSplit(child, offset);
1712
- leftChildLen = ii + 1;
1713
- break;
1714
- }
1715
- if (child.start >= offset) {
1716
- leftChildLen = ii;
1717
- break;
1718
- }
1719
- ii += 1;
1720
- }
1721
- rightChildren = tree.children.splice(
1722
- leftChildLen,
1723
- tree.children.length - leftChildLen
1724
- );
1725
- if (mid !== undefined) {
1726
- rightChildren.unshift(mid);
1727
- }
1728
- resultTree = {
1729
- children: rightChildren,
1730
- delta: tree.delta,
1731
- end: tree.end,
1732
- start: offset
1733
- };
1734
- tree.end = offset;
1735
- return resultTree;
1736
- }
1737
- parentTrees.forEach(function (parentTree, parentIi) {
1738
- parentTree.children.forEach(function (child) {
1739
- dictKeyValueAppend(startToTreeDict, child.start, {
1740
- parentIi,
1741
- tree: child
1742
- });
1743
- });
1744
- });
1745
- queueList = Array.from(startToTreeDict).map(function ([
1746
- startOffset, trees
1747
- ]) {
1748
- return [
1749
- startOffset, trees
1750
- ];
1751
- }).sort(function (aa, bb) {
1752
- return aa[0] - bb[0];
1753
- });
1754
- while (true) {
1755
- if (nextXxx()) {
1756
- break;
1757
- }
1758
- }
1759
- return resultChildren;
1760
- }
1761
-
1762
- function sortFunc(funcCov) {
1763
- funcCov.ranges = treeToRanges(treeFromSortedRanges(
1764
- funcCov.ranges.sort(compareRangeList)
1765
- ));
1766
- return funcCov;
1767
- }
1768
-
1769
- function sortProcess(processCov) {
1770
- Object.entries(processCov.result.sort(function (aa, bb) {
1771
- return (
1772
- aa.url < bb.url
1773
- ? -1
1774
- : aa.url > bb.url
1775
- ? 1
1776
- : 0
1777
- );
1778
- })).forEach(function ([
1779
- scriptId, scriptCov
1780
- ]) {
1781
- scriptCov.scriptId = scriptId.toString(10);
1782
- });
1783
- return processCov;
1784
- }
1785
-
1786
- function sortScript(scriptCov) {
1787
-
1788
- scriptCov.functions.forEach(function (funcCov) {
1789
- sortFunc(funcCov);
1790
- });
1791
- scriptCov.functions.sort(function (aa, bb) {
1792
- return compareRangeList(aa.ranges[0], bb.ranges[0]);
1793
- });
1794
- return scriptCov;
1795
- }
1796
-
1797
- function treeFromSortedRanges(ranges) {
1798
- let root;
1799
- let stack = []; // Stack of parent trees and parent counts.
1800
- ranges.forEach(function (range) {
1801
- let node = {
1802
- children: [],
1803
- delta: range.count,
1804
- end: range.endOffset,
1805
- start: range.startOffset
1806
- };
1807
- let parent;
1808
- let parentCount;
1809
- if (root === undefined) {
1810
- root = node;
1811
- stack.push([
1812
- node, range.count
1813
- ]);
1814
- return;
1815
- }
1816
- while (true) {
1817
- [
1818
- parent, parentCount
1819
- ] = stack[stack.length - 1];
1820
- if (range.startOffset < parent.end) {
1821
- break;
1822
- }
1823
- stack.pop();
1824
- }
1825
- node.delta -= parentCount;
1826
- parent.children.push(node);
1827
- stack.push([
1828
- node, range.count
1829
- ]);
1830
- });
1831
- return root;
1832
- }
1833
-
1834
- function treeToRanges(tree) {
1835
- let count;
1836
- let cur;
1837
- let ii;
1838
- let parentCount;
1839
- let ranges = [];
1840
- let stack = [ // Stack of parent trees and counts.
1841
- [
1842
- tree, 0
1843
- ]
1844
- ];
1845
- function normalizeRange(tree) {
1846
- let children = [];
1847
- let curEnd;
1848
- let head;
1849
- let tail = [];
1850
- function endChain() {
1851
- if (tail.length !== 0) {
1852
- head.end = tail[tail.length - 1].end;
1853
- tail.forEach(function (tailTree) {
1854
- tailTree.children.forEach(function (subChild) {
1855
- subChild.delta += tailTree.delta - head.delta;
1856
- head.children.push(subChild);
1857
- });
1858
- });
1859
- tail.length = 0;
1860
- }
1861
- normalizeRange(head);
1862
- children.push(head);
1863
- }
1864
- tree.children.forEach(function (child) {
1865
- if (head === undefined) {
1866
- head = child;
1867
- } else if (
1868
- child.delta === head.delta && child.start === curEnd
1869
- ) {
1870
- tail.push(child);
1871
- } else {
1872
- endChain();
1873
- head = child;
1874
- }
1875
- curEnd = child.end;
1876
- });
1877
- if (head !== undefined) {
1878
- endChain();
1879
- }
1880
- if (children.length === 1) {
1881
- if (
1882
- children[0].start === tree.start
1883
- && children[0].end === tree.end
1884
- ) {
1885
- tree.delta += children[0].delta;
1886
- tree.children = children[0].children;
1887
- return;
1888
- }
1889
- }
1890
- tree.children = children;
1891
- }
1892
- normalizeRange(tree);
1893
- while (stack.length > 0) {
1894
- [
1895
- cur, parentCount
1896
- ] = stack.pop();
1897
- count = parentCount + cur.delta;
1898
- ranges.push({
1899
- count,
1900
- endOffset: cur.end,
1901
- startOffset: cur.start
1902
- });
1903
- ii = cur.children.length - 1;
1904
- while (ii >= 0) {
1905
- stack.push([
1906
- cur.children[ii], count
1907
- ]);
1908
- ii -= 1;
1909
- }
1910
- }
1911
- return ranges;
1912
- }
1913
-
1914
- if (processCovs.length === 0) {
1915
- return {
1916
- result: []
1917
- };
1918
- }
1919
- if (processCovs.length === 1) {
1920
- processCovs[0].result.forEach(function (scriptCov) {
1921
- sortScript(scriptCov);
1922
- });
1923
- return sortProcess(processCovs[0]);
1924
- }
1925
- processCovs.forEach(function ({
1926
- result
1927
- }) {
1928
- result.forEach(function (scriptCov) {
1929
- dictKeyValueAppend(urlToScriptDict, scriptCov.url, scriptCov);
1930
- });
1931
- });
1932
- urlToScriptDict.forEach(function (scriptCovs) {
1933
-
1934
- let functions = [];
1935
- let rangeToFuncDict = new Map();
1936
- if (scriptCovs.length === 1) {
1937
- resultMerged.push(sortScript(scriptCovs[0]));
1938
- return;
1939
- }
1940
- scriptCovs.forEach(function ({
1941
- functions
1942
- }) {
1943
- functions.forEach(function (funcCov) {
1944
- dictKeyValueAppend(
1945
- rangeToFuncDict,
1946
- (
1947
- funcCov.ranges[0].startOffset
1948
- + ";" + funcCov.ranges[0].endOffset
1949
- ),
1950
- funcCov
1951
- );
1952
- });
1953
- });
1954
- rangeToFuncDict.forEach(function (funcCovs) {
1955
-
1956
- let count = 0;
1957
- let isBlockCoverage;
1958
- let merged;
1959
- let ranges;
1960
- let trees = [];
1961
- if (funcCovs.length === 1) {
1962
- functions.push(sortFunc(funcCovs[0]));
1963
- return;
1964
- }
1965
- funcCovs.forEach(function (funcCov) {
1966
- count += (
1967
- funcCov.count !== undefined
1968
- ? funcCov.count
1969
- : funcCov.ranges[0].count
1970
- );
1971
- if (funcCov.isBlockCoverage) {
1972
- trees.push(treeFromSortedRanges(funcCov.ranges));
1973
- }
1974
- });
1975
- if (trees.length > 0) {
1976
- isBlockCoverage = true;
1977
- ranges = treeToRanges(mergeTreeList(trees));
1978
- } else {
1979
- isBlockCoverage = false;
1980
- ranges = [
1981
- {
1982
- count,
1983
- endOffset: funcCovs[0].ranges[0].endOffset,
1984
- startOffset: funcCovs[0].ranges[0].startOffset
1985
- }
1986
- ];
1987
- }
1988
- merged = {
1989
- functionName: funcCovs[0].functionName,
1990
- isBlockCoverage,
1991
- ranges
1992
- };
1993
- if (count !== ranges[0].count) {
1994
- merged.count = count;
1995
- }
1996
- functions.push(merged);
1997
- });
1998
- resultMerged.push(sortScript({
1999
- functions,
2000
- scriptId: scriptCovs[0].scriptId,
2001
- url: scriptCovs[0].url
2002
- }));
2003
- });
2004
- return sortProcess({
2005
- result: resultMerged
2006
- });
2007
- }
2008
- async function v8CoverageReportCreate({
2009
- consoleError,
2010
- coverageDir,
2011
- processArgv = []
2012
- }) {
2013
- let cwd;
2014
- let exitCode = 0;
2015
- let fileDict;
2016
- let promiseList = [];
2017
- let v8CoverageObj;
2018
-
2019
- function htmlRender({
2020
- fileList,
2021
- lineList,
2022
- modeIndex,
2023
- pathname
2024
- }) {
2025
- let html;
2026
- let padLines;
2027
- let padPathname;
2028
- let txt;
2029
- let txtBorder;
2030
- html = "";
2031
- html += String(`
2032
- <!DOCTYPE html>
2033
- <html lang="en">
2034
- <head>
2035
- <title>V8 Coverage Report</title>
2036
- <style>
2037
- /* jslint utility2:true */
2038
- /*csslint ignore:start*/
2039
- * {
2040
- box-sizing: border-box;
2041
- font-family: consolas, menlo, monospace;
2042
- }
2043
- /*csslint ignore:end*/
2044
- body {
2045
- margin: 0;
2046
- }
2047
- .coverage pre {
2048
- margin: 5px 0;
2049
- }
2050
- .coverage table {
2051
- border-collapse: collapse;
2052
- }
2053
- .coverage td,
2054
- .coverage th {
2055
- border: 1px solid #777;
2056
- margin: 0;
2057
- padding: 5px;
2058
- }
2059
- .coverage td span {
2060
- display: inline-block;
2061
- width: 100%;
2062
- }
2063
- .coverage .content {
2064
- padding: 0 5px;
2065
- }
2066
- .coverage .content a {
2067
- text-decoration: none;
2068
- }
2069
- .coverage .count {
2070
- margin: 0 5px;
2071
- padding: 0 5px;
2072
- }
2073
- .coverage .footer,
2074
- .coverage .header {
2075
- padding: 20px;
2076
- }
2077
- .coverage .footer {
2078
- text-align: center;
2079
- }
2080
- .coverage .percentbar {
2081
- height: 12px;
2082
- margin: 2px 0;
2083
- min-width: 200px;
2084
- position: relative;
2085
- width: 100%;
2086
- }
2087
- .coverage .percentbar div {
2088
- height: 100%;
2089
- position: absolute;
2090
- }
2091
- .coverage .title {
2092
- font-size: large;
2093
- font-weight: bold;
2094
- margin-bottom: 10px;
2095
- }
2096
-
2097
- .coverage td,
2098
- .coverage th {
2099
- background: #fff;
2100
- }
2101
- .coverage .count {
2102
- background: #9d9;
2103
- color: #777;
2104
- }
2105
- .coverage .coverageHigh{
2106
- background: #9d9;
2107
- }
2108
- .coverage .coverageIgnore{
2109
- background: #ccc;
2110
- }
2111
- .coverage .coverageLow{
2112
- background: #ebb;
2113
- }
2114
- .coverage .coverageMedium{
2115
- background: #fd7;
2116
- }
2117
- .coverage .footer,
2118
- .coverage .header {
2119
- background: #ddd;
2120
- }
2121
- .coverage .lineno {
2122
- background: #ddd;
2123
- }
2124
- .coverage .percentbar {
2125
- background: #999;
2126
- }
2127
- .coverage .percentbar div {
2128
- background: #666;
2129
- }
2130
- .coverage .uncovered {
2131
- background: #dbb;
2132
- }
2133
-
2134
- .coverage pre:hover span,
2135
- .coverage tr:hover td {
2136
- background: #7d7;
2137
- }
2138
- .coverage pre:hover span.uncovered,
2139
- .coverage tr:hover td.coverageLow {
2140
- background: #d99;
2141
- }
2142
- </style>
2143
- </head>
2144
- <body class="coverage">
2145
- <!-- header start -->
2146
- <div class="header">
2147
- <div class="title">V8 Coverage Report</div>
2148
- <table>
2149
- <thead>
2150
- <tr>
2151
- <th>Files covered</th>
2152
- <th>Lines</th>
2153
- </tr>
2154
- </thead>
2155
- <tbody>
2156
- `).trim() + "\n";
2157
- if (modeIndex) {
2158
- padLines = String("(ignore) 100.00 %").length;
2159
- padPathname = 32;
2160
- fileList.unshift({
2161
- linesCovered: 0,
2162
- linesTotal: 0,
2163
- modeCoverageIgnoreFile: "",
2164
- pathname: "./"
2165
- });
2166
- fileList.slice(1).forEach(function ({
2167
- linesCovered,
2168
- linesTotal,
2169
- modeCoverageIgnoreFile,
2170
- pathname
2171
- }) {
2172
- if (!modeCoverageIgnoreFile) {
2173
- fileList[0].linesCovered += linesCovered;
2174
- fileList[0].linesTotal += linesTotal;
2175
- }
2176
- padPathname = Math.max(padPathname, pathname.length + 2);
2177
- padLines = Math.max(
2178
- padLines,
2179
- String(linesCovered + " / " + linesTotal).length
2180
- );
2181
- });
2182
- }
2183
- txtBorder = (
2184
- "+" + "-".repeat(padPathname + 2) + "+"
2185
- + "-".repeat(padLines + 2) + "+\n"
2186
- );
2187
- txt = "";
2188
- txt += "V8 Coverage Report\n";
2189
- txt += txtBorder;
2190
- txt += (
2191
- "| " + String("Files covered").padEnd(padPathname, " ") + " | "
2192
- + String("Lines").padStart(padLines, " ") + " |\n"
2193
- );
2194
- txt += txtBorder;
2195
- fileList.forEach(function ({
2196
- linesCovered,
2197
- linesTotal,
2198
- modeCoverageIgnoreFile,
2199
- pathname
2200
- }, ii) {
2201
- let coverageLevel;
2202
- let coveragePct;
2203
- let fill;
2204
- let str1;
2205
- let str2;
2206
- let xx1;
2207
- let xx2;
2208
- coveragePct = Math.floor(10000 * linesCovered / linesTotal || 0);
2209
- coverageLevel = (
2210
- modeCoverageIgnoreFile
2211
- ? "coverageIgnore"
2212
- : coveragePct >= 8000
2213
- ? "coverageHigh"
2214
- : coveragePct >= 5000
2215
- ? "coverageMedium"
2216
- : "coverageLow"
2217
- );
2218
- coveragePct = String(coveragePct).replace((
2219
- /..$/m
2220
- ), ".$&");
2221
- if (modeIndex && ii === 0) {
2222
- fill = (
2223
- "#" + Math.round(
2224
- (100 - Number(coveragePct)) * 2.21
2225
- ).toString(16).padStart(2, "0")
2226
- + Math.round(
2227
- Number(coveragePct) * 2.21
2228
- ).toString(16).padStart(2, "0")
2229
- + "00"
2230
- );
2231
- str1 = "coverage";
2232
- str2 = coveragePct + " %";
2233
- xx1 = 6 * str1.length + 20;
2234
- xx2 = 6 * str2.length + 20;
2235
- promiseList.push(fsWriteFileWithParents((
2236
- coverageDir + "coverage_badge.svg"
2237
- ), String(`
2238
- <svg height="20" width="${xx1 + xx2}" xmlns="http://www.w3.org/2000/svg">
2239
- <rect fill="#555" height="20" width="${xx1 + xx2}"/>
2240
- <rect fill="${fill}" height="20" width="${xx2}" x="${xx1}"/>
2241
- <g
2242
- fill="#fff"
2243
- font-family="dejavu sans, verdana, geneva, sans-serif"
2244
- font-size="11"
2245
- font-weight="bold"
2246
- text-anchor="middle"
2247
- >
2248
- <text x="${0.5 * xx1}" y="14">${str1}</text>
2249
- <text x="${xx1 + 0.5 * xx2}" y="14">${str2}</text>
2250
- </g>
2251
- </svg>
2252
- `).trim() + "\n"));
2253
- pathname = "";
2254
- }
2255
- txt += (
2256
- "| "
2257
- + String("./" + pathname).padEnd(padPathname, " ") + " | "
2258
- + String(
2259
- modeCoverageIgnoreFile + " " + coveragePct + " %"
2260
- ).padStart(padLines, " ") + " |\n"
2261
- );
2262
- txt += (
2263
- "| " + "*".repeat(
2264
- Math.round(0.01 * coveragePct * padPathname)
2265
- ).padEnd(padPathname, "_") + " | "
2266
- + String(
2267
- linesCovered + " / " + linesTotal
2268
- ).padStart(padLines, " ") + " |\n"
2269
- );
2270
- txt += txtBorder;
2271
- pathname = htmlEscape(pathname);
2272
- html += String(`
2273
- <tr>
2274
- <td class="${coverageLevel}">
2275
- ${(
2276
- modeIndex
2277
- ? (
2278
- "<a href=\"" + (pathname || "index") + ".html\">. / "
2279
- + pathname + "</a><br>"
2280
- )
2281
- : (
2282
- "<a href=\""
2283
- + "../".repeat(pathname.split("/").length - 1)
2284
- + "index.html\">. / </a>"
2285
- + pathname + "<br>"
2286
- )
2287
- )}
2288
- <div class="percentbar">
2289
- <div style="width: ${coveragePct}%;"></div>
2290
- </div>
2291
- </td>
2292
- <td style="text-align: right;">
2293
- ${modeCoverageIgnoreFile} ${coveragePct} %<br>
2294
- ${linesCovered} / ${linesTotal}
2295
- </td>
2296
- </tr>
2297
- `).trim() + "\n";
2298
- });
2299
- html += String(`
2300
- </tbody>
2301
- </table>
2302
- </div>
2303
- <!-- header end -->
2304
- `).trim() + "\n";
2305
- if (!modeIndex) {
2306
- html += String(`
2307
- <!-- content start -->
2308
- <div class="content">
2309
- `).trim() + "\n";
2310
- lineList.forEach(function ({
2311
- count,
2312
- holeList,
2313
- line,
2314
- startOffset
2315
- }, ii) {
2316
- let chunk;
2317
- let inHole;
2318
- let lineHtml;
2319
- let lineId;
2320
- lineHtml = "";
2321
- lineId = "line_" + (ii + 1);
2322
- switch (count) {
2323
- case 0:
2324
- if (holeList.length === 0) {
2325
- lineHtml += "</span>";
2326
- lineHtml += "<span class=\"uncovered\">";
2327
- lineHtml += htmlEscape(line);
2328
- break;
2329
- }
2330
- line = line.split("").map(function (char) {
2331
- return {
2332
- char,
2333
- isHole: undefined
2334
- };
2335
- });
2336
- holeList.forEach(function ([
2337
- aa, bb
2338
- ]) {
2339
- aa = Math.max(aa - startOffset, 0);
2340
- bb = Math.min(bb - startOffset, line.length);
2341
- while (aa < bb) {
2342
- line[aa].isHole = true;
2343
- aa += 1;
2344
- }
2345
- });
2346
- chunk = "";
2347
- line.forEach(function ({
2348
- char,
2349
- isHole
2350
- }) {
2351
- if (inHole !== isHole) {
2352
- lineHtml += htmlEscape(chunk);
2353
- lineHtml += (
2354
- isHole
2355
- ? "</span><span class=\"uncovered\">"
2356
- : "</span><span>"
2357
- );
2358
- chunk = "";
2359
- inHole = isHole;
2360
- }
2361
- chunk += char;
2362
- });
2363
- lineHtml += htmlEscape(chunk);
2364
- break;
2365
- default:
2366
- lineHtml += htmlEscape(line);
2367
- }
2368
- html += String(`
2369
- <pre>
2370
- <span class="lineno">
2371
- <a href="#${lineId}" id="${lineId}">${String(ii + 1).padStart(5, " ")}.</a>
2372
- </span>
2373
- <span class="count
2374
- ${(
2375
- count <= 0
2376
- ? "uncovered"
2377
- : ""
2378
- )}"
2379
- >
2380
- ${String(count).padStart(7, " ")}
2381
- </span>
2382
- <span>${lineHtml}</span>
2383
- </pre>
2384
- `).replace((
2385
- /\n/g
2386
- ), "").trim() + "\n";
2387
- });
2388
- html += String(`
2389
- </div>
2390
- <!-- content end -->
2391
- `).trim() + "\n";
2392
- }
2393
- html += String(`
2394
- <div class="footer">
2395
- [
2396
- This document was created with
2397
- <a href="https://github.com/jslint-org/jslint">JSLint</a>
2398
- ]
2399
- </div>
2400
- </body>
2401
- </html>
2402
- `).trim() + "\n";
2403
- promiseList.push(fsWriteFileWithParents(pathname + ".html", html));
2404
- if (!modeIndex) {
2405
- return;
2406
- }
2407
- consoleError("\n" + txt);
2408
- promiseList.push(fsWriteFileWithParents((
2409
- coverageDir + "coverage_report.txt"
2410
- ), txt));
2411
- }
2412
-
2413
- function pathnameRelativeCwd(pathname) {
2414
- pathname = modulePath.resolve(pathname).replace((
2415
- /\\/g
2416
- ), "/");
2417
- if (!pathname.startsWith(cwd)) {
2418
- return;
2419
- }
2420
- pathname = pathname.slice(cwd.length);
2421
- return pathname;
2422
- }
2423
-
2424
- /*
2425
- function sentinel() {}
2426
- */
2427
-
2428
- await moduleFsInit();
2429
- consoleError = consoleError || console.error;
2430
- cwd = process.cwd().replace((
2431
- /\\/g
2432
- ), "/") + "/";
2433
- assertOrThrow(coverageDir, "invalid coverageDir " + coverageDir);
2434
- coverageDir = modulePath.resolve(coverageDir).replace((
2435
- /\\/g
2436
- ), "/") + "/";
2437
- if (processArgv.length > 0) {
2438
- await fsWriteFileWithParents(coverageDir + "/touch.txt", "");
2439
- await Promise.all(Array.from(
2440
- await moduleFs.promises.readdir(coverageDir)
2441
- ).map(async function (file) {
2442
- if ((
2443
- /^coverage-\d+?-\d+?-\d+?\.json$/
2444
- ).test(file)) {
2445
- console.error("rm file " + coverageDir + file);
2446
- await moduleFs.promises.unlink(coverageDir + file);
2447
- }
2448
- }));
2449
- exitCode = await new Promise(function (resolve) {
2450
- moduleChildProcess.spawn((
2451
- processArgv[0] === "npm"
2452
- ? process.platform.replace("win32", "npm.cmd").replace(
2453
- process.platform,
2454
- "npm"
2455
- )
2456
- : processArgv[0]
2457
- ), processArgv.slice(1), {
2458
- env: Object.assign({}, process.env, {
2459
- NODE_V8_COVERAGE: coverageDir
2460
- }),
2461
- stdio: [
2462
- "ignore", 1, 2
2463
- ]
2464
- }).on("exit", resolve);
2465
- });
2466
- }
2467
- v8CoverageObj = await moduleFs.promises.readdir(coverageDir);
2468
- v8CoverageObj = v8CoverageObj.filter(function (file) {
2469
- return (
2470
- /^coverage-\d+?-\d+?-\d+?\.json$/
2471
- ).test(file);
2472
- });
2473
- v8CoverageObj = await Promise.all(v8CoverageObj.map(async function (file) {
2474
- let data = await moduleFs.promises.readFile(coverageDir + file, "utf8");
2475
- data = JSON.parse(data);
2476
- data.result = data.result.filter(function (scriptCov) {
2477
- let pathname = scriptCov.url;
2478
- if (!pathname.startsWith("file:///")) {
2479
- return;
2480
- }
2481
- pathname = pathnameRelativeCwd(moduleUrl.fileURLToPath(pathname));
2482
- if (
2483
- !pathname
2484
- || pathname.startsWith("[")
2485
- || (
2486
- process.env.npm_config_mode_coverage !== "all"
2487
- && (
2488
- /(?:^|\/)node_modules\//m
2489
- ).test(pathname)
2490
- )
2491
- ) {
2492
- return;
2493
- }
2494
- scriptCov.url = pathname;
2495
- return true;
2496
- });
2497
- return data;
2498
- }));
2499
- v8CoverageObj = v8CoverageListMerge(v8CoverageObj);
2500
- await fsWriteFileWithParents(
2501
- coverageDir + "v8_coverage_merged.json",
2502
- JSON.stringify(v8CoverageObj)
2503
- );
2504
- fileDict = {};
2505
- await Promise.all(v8CoverageObj.result.map(async function ({
2506
- functions,
2507
- url: pathname
2508
- }) {
2509
- let lineList;
2510
- let linesCovered;
2511
- let linesTotal;
2512
- let source;
2513
- source = await moduleFs.promises.readFile(pathname, "utf8");
2514
- lineList = [{}];
2515
- source.replace((
2516
- /^.*$/gm
2517
- ), function (line, startOffset) {
2518
- lineList[lineList.length - 1].endOffset = startOffset - 1;
2519
- lineList.push({
2520
- count: -1,
2521
- endOffset: 0,
2522
- holeList: [],
2523
- line,
2524
- startOffset
2525
- });
2526
- return "";
2527
- });
2528
- lineList.shift();
2529
- lineList[lineList.length - 1].endOffset = source.length;
2530
- functions.reverse().forEach(function ({
2531
- ranges
2532
- }) {
2533
- ranges.reverse().forEach(function ({
2534
- count,
2535
- endOffset,
2536
- startOffset
2537
- }, ii, list) {
2538
- lineList.forEach(function (elem) {
2539
- if (!(
2540
- (
2541
- elem.startOffset <= startOffset
2542
- && startOffset <= elem.endOffset
2543
- ) || (
2544
- elem.startOffset <= endOffset
2545
- && endOffset <= elem.endOffset
2546
- ) || (
2547
- startOffset <= elem.startOffset
2548
- && elem.endOffset <= endOffset
2549
- )
2550
- )) {
2551
- return;
2552
- }
2553
- if (ii + 1 === list.length) {
2554
- if (elem.count === -1) {
2555
- elem.count = count;
2556
- }
2557
- return;
2558
- }
2559
- if (elem.count !== 0) {
2560
- elem.count = Math.max(count, elem.count);
2561
- }
2562
- if (count === 0) {
2563
- elem.count = 0;
2564
- elem.holeList.push([
2565
- startOffset, endOffset
2566
- ]);
2567
- }
2568
- });
2569
- });
2570
- });
2571
- linesTotal = lineList.length;
2572
- linesCovered = lineList.filter(function ({
2573
- count
2574
- }) {
2575
- return count > 0;
2576
- }).length;
2577
- await moduleFs.promises.mkdir((
2578
- modulePath.dirname(coverageDir + pathname)
2579
- ), {
2580
- recursive: true
2581
- });
2582
- fileDict[pathname] = {
2583
- lineList,
2584
- linesCovered,
2585
- linesTotal,
2586
- modeCoverageIgnoreFile: (
2587
- (
2588
- /^\/\*mode-coverage-ignore-file\*\/$/m
2589
- ).test(source.slice(0, 65536))
2590
- ? "(ignore)"
2591
- : ""
2592
- ),
2593
- pathname
2594
- };
2595
- htmlRender({
2596
- fileList: [
2597
- fileDict[pathname]
2598
- ],
2599
- lineList,
2600
- pathname: coverageDir + pathname
2601
- });
2602
- }));
2603
- htmlRender({
2604
- fileList: Object.keys(fileDict).sort().map(function (pathname) {
2605
- return fileDict[pathname];
2606
- }),
2607
- modeIndex: true,
2608
- pathname: coverageDir + "index"
2609
- });
2610
- await Promise.all(promiseList);
2611
- assertOrThrow(
2612
- exitCode === 0,
2613
- "v8CoverageReportCreate - nonzero exitCode " + exitCode
2614
- );
2615
- }
2616
- v8CoverageReportCreate({
2617
- coverageDir: ".artifact/coverage",
2618
- process_argv: process.argv.slice(1)
2619
- });
2620
- ' "$@" # '
2621
- )}
2622
-
2623
- shRunWithScreenshotTxt() {(set -e
2624
- # this function will run cmd $@ and screenshot text-output
2625
- # https://www.cnx-software.com/2011/09/22/how-to-convert-a-command-line-result-into-an-image-in-linux/
2626
- local EXIT_CODE
2627
- EXIT_CODE=0
2628
- export SCREENSHOT_SVG="$1"
2629
- shift
2630
- printf "0\n" > "$SCREENSHOT_SVG.exit_code"
2631
- printf "shRunWithScreenshotTxt - ($* 2>&1)\n" 1>&2
2632
- # run "$@" with screenshot
2633
- (set -e
2634
- "$@" 2>&1 || printf "$?\n" > "$SCREENSHOT_SVG.exit_code"
2635
- ) | tee "$SCREENSHOT_SVG.txt"
2636
- EXIT_CODE="$(cat "$SCREENSHOT_SVG.exit_code")"
2637
- printf "shRunWithScreenshotTxt - EXIT_CODE=$EXIT_CODE - $SCREENSHOT_SVG\n" \
2638
- 1>&2
2639
- # format text-output
2640
- node --input-type=module -e '
2641
- import moduleFs from "fs";
2642
- (async function () {
2643
- let result = await moduleFs.promises.readFile(
2644
- process.argv[1] + ".txt",
2645
- "utf8"
2646
- );
2647
- let yy = 10;
2648
- // remove ansi escape-code
2649
- result = result.replace((
2650
- /\u001b.*?m/g
2651
- ), "");
2652
- /*
2653
- // format unicode
2654
- result = result.replace((
2655
- /\\u[0-9a-f]{4}/g
2656
- ), function (match0) {
2657
- return String.fromCharCode("0x" + match0.slice(-4));
2658
- });
2659
- */
2660
- // normalize "\r\n"
2661
- result = result.replace((
2662
- /\r\n?/
2663
- ), "\n").trimEnd();
2664
- // 96-column wordwrap
2665
- result = result.split("\n").map(function (line) {
2666
- let wordwrap = line.slice(0, 96).padEnd(96, " ");
2667
- line = line.slice(96);
2668
- while (line) {
2669
- wordwrap += "\\\n " + line.slice(0, 96 - 2).padEnd(96 - 2, " ");
2670
- line = line.slice(96 - 2);
2671
- }
2672
- return wordwrap + " ";
2673
- }).join("\n");
2674
- // html-escape
2675
- result = result.replace((
2676
- /&/g
2677
- ), "&amp;").replace((
2678
- /</g
2679
- ), "&lt;").replace((
2680
- />/g
2681
- ), "&gt;");
2682
- // convert text to svg-tspan
2683
- result = result.split("\n").map(function (line) {
2684
- yy += 22;
2685
- return `<tspan
2686
- lengthAdjust="spacingAndGlyphs"
2687
- textLength="${96 * 8}"
2688
- x="10"
2689
- y="${yy}"
2690
- >${line}</tspan>\n`;
2691
- }).join("");
2692
- result = String(`
2693
- <svg height="${yy + 20}" width="800" xmlns="http://www.w3.org/2000/svg">
2694
- <rect height="${yy + 20}" fill="#222" width="800"></rect>
2695
- <text
2696
- fill="#7f7"
2697
- font-family="consolas, menlo, monospace"
2698
- font-size="14"
2699
- xml:space="preserve"
2700
- >
2701
- ${result}
2702
- </text>
2703
- </svg>
2704
- `).trim() + "\n";
2705
- moduleFs.promises.writeFile(process.argv[1], result);
2706
- }());
2707
- ' "$SCREENSHOT_SVG" # '
2708
- printf "shRunWithScreenshotTxt - wrote - $SCREENSHOT_SVG\n"
2709
- # cleanup
2710
- rm "$SCREENSHOT_SVG.exit_code" "$SCREENSHOT_SVG.txt"
2711
- return "$EXIT_CODE"
2712
- )}
2713
-
2714
- shCiMain() {(set -e
2715
- # this function will run $@
2716
- if [ "$1" = "" ]
2717
- then
2718
- return
2719
- fi
2720
- # run "$@" with winpty
2721
- export CI_UNAME="${CI_UNAME:-$(uname)}"
2722
- case "$CI_UNAME" in
2723
- MSYS*)
2724
- if [ ! "$CI_WINPTY" ] && [ "$1" != shHttpFileServer ]
2725
- then
2726
- export CI_WINPTY=1
2727
- winpty -Xallow-non-tty -Xplain sh "$0" "$@"
2728
- return
2729
- fi
2730
- ;;
2731
- esac
2732
- # run "$@"
2733
- export NODE_OPTIONS="--unhandled-rejections=strict"
2734
- if [ -f ./.ci.sh ]
2735
- then
2736
- . ./.ci.sh "$@"
2737
- fi
2738
- if [ "$npm_config_mode_coverage" ] && [ "$1" = "node" ]
2739
- then
2740
- shRunWithCoverage "$@"
2741
- return
2742
- fi
2743
- "$@"
2744
- )}
2745
-
2746
- # init ubuntu .bashrc
2747
- shBashrcDebianInit || return "$?"
2748
-
2749
- shCiMain "$@"