@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/.npmignore +6 -0
- package/CHANGELOG.md +14 -4
- package/README.md +69 -22
- package/jslint.mjs +264 -201
- package/package.json +13 -2
- package/.gitconfig +0 -25
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -23
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -18
- package/.github/ISSUE_TEMPLATE/website_issue.md +0 -12
- package/.github/workflows/ci.yml +0 -68
- package/.github/workflows/on_pull_request.yml +0 -39
- package/.github/workflows/publish.yml +0 -68
- package/.gitignore +0 -18
- package/asset_codemirror_rollup.js +0 -11329
- package/asset_font_daley_bold.woff2 +0 -0
- package/asset_image_folder_open_solid.svg +0 -1
- package/asset_image_github_brands.svg +0 -1
- package/asset_image_jslint_vim_plugin.png +0 -0
- package/asset_image_json_160.svg +0 -104
- package/asset_image_logo_512.svg +0 -39
- package/help.html +0 -1232
- package/index.html +0 -1512
- package/jslint_ci.sh +0 -2749
- package/test.mjs +0 -937
- package/test_coverage_merge_data.json +0 -17877
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
|
-
), "&").replace((
|
|
1542
|
-
/</g
|
|
1543
|
-
), "<").replace((
|
|
1544
|
-
/>/g
|
|
1545
|
-
), ">");
|
|
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
|
-
), "&").replace((
|
|
2678
|
-
/</g
|
|
2679
|
-
), "<").replace((
|
|
2680
|
-
/>/g
|
|
2681
|
-
), ">");
|
|
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 "$@"
|