js-dev-tool 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +150 -0
- package/basic-types.d.ts +279 -0
- package/common/index.d.ts +43 -0
- package/common/index.js +101 -0
- package/extras/tiny-progress.d.ts +14 -0
- package/extras/tiny-progress.js +74 -0
- package/index.d.ts +27 -0
- package/index.js +15 -0
- package/lib/unzip.min.js +812 -0
- package/lib/zip.min.js +1036 -0
- package/lib/zlibjs.d.ts +34 -0
- package/package.json +51 -0
- package/progress/index.d.ts +92 -0
- package/progress/index.js +305 -0
- package/progress/progress-extras.js +90 -0
- package/progress/rnd-spinner.js +55 -0
- package/progress/spinners.d.ts +74 -0
- package/progress/spinners.json +805 -0
- package/progress/test.js +94 -0
- package/regex.d.ts +154 -0
- package/scripts/publish-version.json +3 -0
- package/scripts/unzip.js +30 -0
- package/scripts/zip.js +138 -0
- package/tool-lib/cjbm.js +239 -0
- package/tool-lib/cjmb-regex-latest.svg +366 -0
- package/tool-lib/cjmb-regex.svg +90 -0
- package/tool-lib/cmt-trick.js +125 -0
- package/tool-lib/ps.js +158 -0
- package/tool-lib/rws.js +181 -0
- package/tool-lib/tools.d.ts +245 -0
- package/tool-lib/zip-task.js +34 -0
- package/tools.d.ts +7 -0
- package/tools.js +450 -0
- package/tsconfig.json +25 -0
- package/utils.d.ts +194 -0
- package/utils.js +643 -0
package/progress/test.js
ADDED
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
3
|
+
Copyright (C) 2022 jeffy-g <hirotom1107@gmail.com>
|
|
4
|
+
Released under the MIT license
|
|
5
|
+
https://opensource.org/licenses/mit-license.php
|
|
6
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
7
|
+
*/
|
|
8
|
+
/** [usage]
|
|
9
|
+
> node -i
|
|
10
|
+
let t = require("./progress/test");
|
|
11
|
+
let p = t.init();
|
|
12
|
+
p.run();
|
|
13
|
+
p.stop();
|
|
14
|
+
p = t.init();
|
|
15
|
+
p.run();
|
|
16
|
+
p.stop();
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
const p = require("./index.js");
|
|
20
|
+
const rndspinner = require("./rnd-spinner.js");
|
|
21
|
+
|
|
22
|
+
let fired = 0;
|
|
23
|
+
let pending = 0;
|
|
24
|
+
|
|
25
|
+
/** @type {() => { fired: number; pending: number; errors?: number; }} */
|
|
26
|
+
const cb = () => {
|
|
27
|
+
return {
|
|
28
|
+
fired: ++fired,
|
|
29
|
+
pending: ++pending,
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
let tag = "progress test";
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* progress callback
|
|
37
|
+
* @type {() => string}
|
|
38
|
+
*/
|
|
39
|
+
const pcb = () => {
|
|
40
|
+
if (cb) {
|
|
41
|
+
const { fired, pending, errors } = cb();
|
|
42
|
+
return `${tag} | error: ${(errors + "").padEnd(3)} | send: ${(fired + "").padStart(3)}, pending: ${(pending + "").padEnd(5)}`;
|
|
43
|
+
} else {
|
|
44
|
+
// error
|
|
45
|
+
return "- - error - -";
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
/** @type {ReturnType<typeof p.createProgressObject>} */
|
|
50
|
+
let progress;
|
|
51
|
+
/**
|
|
52
|
+
* @param {string} frameName
|
|
53
|
+
*/
|
|
54
|
+
const getFormatOpt = (frameName) => {
|
|
55
|
+
return {
|
|
56
|
+
fmt: "{frameName}: {msg} | {tick}",
|
|
57
|
+
payload: { frameName },
|
|
58
|
+
};
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* create progress options as randomly
|
|
62
|
+
*/
|
|
63
|
+
const createRandomlyOptions = () => {
|
|
64
|
+
const { name, frames } = rndspinner.getRandomFrame();
|
|
65
|
+
return {
|
|
66
|
+
frames,
|
|
67
|
+
formatOpt: getFormatOpt(name.padStart(rndspinner.MAX_PAD)),
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* @param {number} fps
|
|
72
|
+
*/
|
|
73
|
+
const init = (fps = 30) => {
|
|
74
|
+
/**
|
|
75
|
+
* @typedef {ReturnType<typeof createRandomlyOptions>} TcreateRandomlyOptionsReturn
|
|
76
|
+
*/
|
|
77
|
+
// /** @type {string[]} */
|
|
78
|
+
// let frames;
|
|
79
|
+
// /** @type {TcreateRandomlyOptionsReturn["formatOpt"]} */
|
|
80
|
+
// let formatOpt;
|
|
81
|
+
// // TIP: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Object_destructuring
|
|
82
|
+
// (
|
|
83
|
+
// { frames, formatOpt } = createRandomlyOptions()
|
|
84
|
+
// );
|
|
85
|
+
const { frames, formatOpt } = createRandomlyOptions();
|
|
86
|
+
progress = p.createProgressObject(frames, formatOpt, pcb);
|
|
87
|
+
progress.setFPS(fps);
|
|
88
|
+
|
|
89
|
+
return progress;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
module.exports = {
|
|
93
|
+
init,
|
|
94
|
+
};
|
package/regex.d.ts
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
3
|
+
// Copyright (C) 2025 jeffy-g <hirotom1107@gmail.com>
|
|
4
|
+
// Released under the MIT license
|
|
5
|
+
// https://opensource.org/licenses/mit-license.php
|
|
6
|
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* @file js-dev-scripts/regex.d.ts
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
13
|
+
// Basics
|
|
14
|
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
15
|
+
/**
|
|
16
|
+
* Extracts the literal type from the source property of a RegExp.
|
|
17
|
+
* @template R - A RegExp type.
|
|
18
|
+
*/
|
|
19
|
+
export type ReSource<R extends RegExp> = R extends RegExp & { readonly source: infer T } ? T : never;
|
|
20
|
+
export declare function createRegExp<P extends string>(pattern: P, flags?: string): RegExp & {
|
|
21
|
+
readonly source: P;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
25
|
+
// Named Capture Groups
|
|
26
|
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
27
|
+
type FirstChar<S extends string> = S extends `${infer F}${infer _}` ? F : never;
|
|
28
|
+
// Modified ExtractGroupNames:
|
|
29
|
+
// First, extract the part of S after "(?<" into Rest
|
|
30
|
+
// If the first character of Rest is "=" or "!", it is judged to be Lookbehind and recurses without treating it as a capture
|
|
31
|
+
// Otherwise, split Rest into the group name (GroupName) and the ">" separator and extract it,
|
|
32
|
+
// Apply the same process to the remaining After part
|
|
33
|
+
/**
|
|
34
|
+
* Recursively extracts parts that match the format (?<GroupName>...) from a string pattern,
|
|
35
|
+
* without considering nesting, and unions them.
|
|
36
|
+
* @template S - A string pattern.
|
|
37
|
+
*/
|
|
38
|
+
export type ExtractGroupNames<S extends string> =
|
|
39
|
+
S extends `${infer _Before}(?<${infer Rest}`
|
|
40
|
+
? FirstChar<Rest> extends "=" | "!"
|
|
41
|
+
? ExtractGroupNames<Rest>
|
|
42
|
+
: Rest extends `${infer GroupName}>${infer After}`
|
|
43
|
+
? GroupName | ExtractGroupNames<After>
|
|
44
|
+
: never
|
|
45
|
+
: never;
|
|
46
|
+
/**
|
|
47
|
+
* Creates an object type with keys as the extracted group names and values as strings.
|
|
48
|
+
* If no groups are found, it results in an empty object.
|
|
49
|
+
* @template R - A RegExp type.
|
|
50
|
+
*/
|
|
51
|
+
export type ReGroups<R extends RegExp> = {
|
|
52
|
+
[K in ExtractGroupNames<ReSource<R>>]?: string;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
56
|
+
// Capture Groups
|
|
57
|
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
58
|
+
// CountCaptureGroups type: Get the exact number of capture groups
|
|
59
|
+
/**
|
|
60
|
+
* Counts the exact number of capture groups in a string pattern.
|
|
61
|
+
* @template S - A string pattern.
|
|
62
|
+
* @template Counter - An array used to count the capture groups.
|
|
63
|
+
*
|
|
64
|
+
* @todo FIXME: 多く count してしまう場合あり. 2025/3/18 15:47:57
|
|
65
|
+
* @deprecated text base の parse には限界があり cost が見合わないので、この type は将来削除予定!
|
|
66
|
+
*/
|
|
67
|
+
export type CountCaptureGroups<S extends string, Counter extends unknown[] = []> =
|
|
68
|
+
// // まず、エスケープされた左括弧 \(
|
|
69
|
+
// S extends `${infer _Before}\\(${infer Rest}`
|
|
70
|
+
// ? CountCaptureGroups<Rest, Counter> :
|
|
71
|
+
// 次に、通常の左括弧 ( を検出
|
|
72
|
+
S extends `${infer _Before}(${infer Rest}`
|
|
73
|
+
// 非キャプチャグループ (?:...) をスキップ
|
|
74
|
+
? Rest extends `?:${infer After}`
|
|
75
|
+
? CountCaptureGroups<After, Counter>
|
|
76
|
+
// Lookahead/Lookbehind (?=...), (?!...), (?<=...), (?<!...) をスキップ
|
|
77
|
+
: Rest extends `?=${infer After}`
|
|
78
|
+
| `?!${infer After}`
|
|
79
|
+
| `?<=${infer After}`
|
|
80
|
+
| `?<!${infer After}`
|
|
81
|
+
? CountCaptureGroups<After, Counter>
|
|
82
|
+
// それ以外は通常のキャプチャグループとしてカウント
|
|
83
|
+
: CountCaptureGroups<Rest, [...Counter, unknown]>
|
|
84
|
+
: Counter['length'];
|
|
85
|
+
|
|
86
|
+
// Fixed RegExpExecArray type
|
|
87
|
+
/**
|
|
88
|
+
* Represents a fixed version of RegExpExecArray that includes the matched string,
|
|
89
|
+
* captures, and optionally named groups.
|
|
90
|
+
* @template R - A RegExp type.
|
|
91
|
+
* @template S - The source string of the RegExp.
|
|
92
|
+
* @template GroupCount - The number of capture groups.
|
|
93
|
+
*/
|
|
94
|
+
export type RegExpExecArrayFixed<
|
|
95
|
+
R extends RegExp,
|
|
96
|
+
S extends ReSource<R> = ReSource<R>,
|
|
97
|
+
GroupCount extends number = CountCaptureGroups<S>
|
|
98
|
+
> = [match: string, ...ExtractCaptures<GroupCount>] & {
|
|
99
|
+
groups?: ReGroups<R>;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Helper type: Generates an array according to the number of capture groups
|
|
103
|
+
/**
|
|
104
|
+
* Generates an array type with a length corresponding to the number of capture groups.
|
|
105
|
+
* @template Count - The number of capture groups.
|
|
106
|
+
* @template Result - The resulting array type.
|
|
107
|
+
*/
|
|
108
|
+
export type ExtractCaptures<Count extends number, Result extends unknown[] = []> =
|
|
109
|
+
Result['length'] extends Count
|
|
110
|
+
? Result : ExtractCaptures<Count, [...Result, string]>;
|
|
111
|
+
|
|
112
|
+
/* ctt
|
|
113
|
+
type Dummy = ExtractCaptures<4>;
|
|
114
|
+
// [string, string, string, string]
|
|
115
|
+
/*/
|
|
116
|
+
//*/
|
|
117
|
+
|
|
118
|
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
119
|
+
// The Examples
|
|
120
|
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
121
|
+
// Example 1: Anonymous capture groups
|
|
122
|
+
const re1 = createRegExp("(test)(group)", "g");
|
|
123
|
+
type ExecResult1 = RegExpExecArrayFixed<typeof re1>;
|
|
124
|
+
// ExecResult1 is [string, string, string] & { groups?: Record<string, string | undefined> };
|
|
125
|
+
|
|
126
|
+
// Example 2: Named capture groups
|
|
127
|
+
const re2 = createRegExp("(?<name1>test)(?<name2>group)", "g");
|
|
128
|
+
type ExecResult2 = RegExpExecArrayFixed<typeof re2>;
|
|
129
|
+
// ExecResult2 is [string, string, string] & { groups?: { name1: string; name2: string } };
|
|
130
|
+
|
|
131
|
+
// Verify that the exec result matches the type
|
|
132
|
+
const result = re2.exec("test group") as RegExpExecArrayFixed<typeof re2>;
|
|
133
|
+
if (result) {
|
|
134
|
+
console.log(result[1]); // "test"
|
|
135
|
+
console.log(result.groups?.name1); // "test"
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export as namespace XRegex;
|
|
139
|
+
/* ctt sample code
|
|
140
|
+
// 補助関数 createRegExp を使って型レベルの情報を保持する RegExp を生成
|
|
141
|
+
function createRegExp<P extends string>(
|
|
142
|
+
pattern: P,
|
|
143
|
+
flags?: string
|
|
144
|
+
): RegExp & { readonly source: P } {
|
|
145
|
+
return new RegExp(pattern, flags) as RegExp & { readonly source: P };
|
|
146
|
+
}
|
|
147
|
+
const re = createRegExp("(?<token>test)", "ig");
|
|
148
|
+
// type a は "token" というグループ名から導出された型になりますが、
|
|
149
|
+
// groups オブジェクトとしては { token: string } になる
|
|
150
|
+
type Groups = ReGroups<typeof re>; // → { token: string }
|
|
151
|
+
const m = re.exec("this is test")!;
|
|
152
|
+
console.log(m, m.groups);
|
|
153
|
+
/*/
|
|
154
|
+
//*/
|
package/scripts/unzip.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
const fs = require("fs");
|
|
3
|
+
const fflate = require("fflate");
|
|
4
|
+
const { unzip } = fflate;
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} fileName
|
|
7
|
+
*/
|
|
8
|
+
const decompress = (fileName) => {
|
|
9
|
+
fs.readFile(fileName, null, (err, data) => {
|
|
10
|
+
if (err) {
|
|
11
|
+
console.error(err);
|
|
12
|
+
} else {
|
|
13
|
+
console.time(fileName);
|
|
14
|
+
unzip(data, (err, unzipped) => {
|
|
15
|
+
// [2022/5/10 3:05:43] webpack.zip: 159.833ms
|
|
16
|
+
console.timeEnd(fileName);
|
|
17
|
+
if (err) {
|
|
18
|
+
console.error(err);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
Object.entries(unzipped).forEach(([filename, data]) => {
|
|
22
|
+
fs.writeFile(filename, data, null, () => {
|
|
23
|
+
console.log("write:", filename);
|
|
24
|
+
});
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
};
|
|
30
|
+
module.exports = decompress;
|
package/scripts/zip.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
const fs = require("fs");
|
|
2
|
+
const path = require("path");
|
|
3
|
+
const fflate = require("fflate");
|
|
4
|
+
const { TextEncoder } = require("util");
|
|
5
|
+
// const b = require("node:buffer");
|
|
6
|
+
|
|
7
|
+
// fflate's ZIP API is asynchronous and parallelized (multithreaded)
|
|
8
|
+
const ALREADY_COMPRESSED = [
|
|
9
|
+
"zip",
|
|
10
|
+
"gz",
|
|
11
|
+
"png",
|
|
12
|
+
"jpg",
|
|
13
|
+
"jpeg",
|
|
14
|
+
"pdf",
|
|
15
|
+
"doc",
|
|
16
|
+
"docx",
|
|
17
|
+
"ppt",
|
|
18
|
+
"pptx",
|
|
19
|
+
"xls",
|
|
20
|
+
"xlsx",
|
|
21
|
+
"heic",
|
|
22
|
+
"heif",
|
|
23
|
+
"7z",
|
|
24
|
+
"bz2",
|
|
25
|
+
"rar",
|
|
26
|
+
"gif",
|
|
27
|
+
"webp",
|
|
28
|
+
"webm",
|
|
29
|
+
"mp4",
|
|
30
|
+
"mov",
|
|
31
|
+
"mp3",
|
|
32
|
+
"aifc",
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param {string} fileName
|
|
38
|
+
* @param {(out: Uint8Array | NodeJS.ErrnoException) => void} cb
|
|
39
|
+
*/
|
|
40
|
+
const fileToU8 = (fileName, cb /* : (out: Uint8Array) => void */) => {
|
|
41
|
+
fs.readFile(fileName, "utf8", (err, data) => {
|
|
42
|
+
if (err) {
|
|
43
|
+
cb(err);
|
|
44
|
+
} else {
|
|
45
|
+
cb(new TextEncoder().encode(data));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// Yet again, this is necessary for parallelization.
|
|
51
|
+
/**
|
|
52
|
+
* @param {path.ParsedPath} parsedPath
|
|
53
|
+
* @param {(out: Uint8Array | null) => void} callback
|
|
54
|
+
* @param {string} [comment]
|
|
55
|
+
*/
|
|
56
|
+
const processFile = function (parsedPath, callback, comment) {
|
|
57
|
+
const filePath = `${parsedPath.dir}/${parsedPath.base}`;
|
|
58
|
+
fileToU8(filePath, function (data) {
|
|
59
|
+
if (data instanceof Uint8Array) {
|
|
60
|
+
/** @type {fflate.AsyncZippable} */
|
|
61
|
+
const zipObj = {
|
|
62
|
+
[parsedPath.base]: [
|
|
63
|
+
data, {
|
|
64
|
+
// With fflate, we can choose which files we want to compress
|
|
65
|
+
level: ALREADY_COMPRESSED.indexOf(parsedPath.ext) === -1 ? 6 : 0,
|
|
66
|
+
comment,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
// If we didn't want to specify options:
|
|
72
|
+
// zipObj[fileName] = buf;
|
|
73
|
+
console.time(filePath);
|
|
74
|
+
fflate.zip(
|
|
75
|
+
zipObj,
|
|
76
|
+
{
|
|
77
|
+
// If you want to control options for every file, you can do so here
|
|
78
|
+
// They are merged with the per-file options (if they exist)
|
|
79
|
+
// mem: 9
|
|
80
|
+
},
|
|
81
|
+
function (err, out) {
|
|
82
|
+
if (err) {
|
|
83
|
+
console.log(err);
|
|
84
|
+
callback(null);
|
|
85
|
+
console.timeEnd(filePath);
|
|
86
|
+
} else {
|
|
87
|
+
// [2022/5/10 3:06:16] webpack.js: 399.832ms
|
|
88
|
+
console.timeEnd(filePath);
|
|
89
|
+
// You may want to try downloading to see that fflate actually works:
|
|
90
|
+
// download(out, 'fflate-demo.zip');
|
|
91
|
+
callback(out);
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
);
|
|
95
|
+
} else {
|
|
96
|
+
console.error(data);
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* @param {string} inputPath
|
|
103
|
+
* @param {string} [comment]
|
|
104
|
+
* @param {string} [dest]
|
|
105
|
+
*/
|
|
106
|
+
module.exports = function zip(inputPath, comment, dest) {
|
|
107
|
+
let actualOutput = dest;
|
|
108
|
+
const pp = path.parse(inputPath);
|
|
109
|
+
processFile(
|
|
110
|
+
pp,
|
|
111
|
+
(out) => {
|
|
112
|
+
if (out) {
|
|
113
|
+
if (!actualOutput) {
|
|
114
|
+
actualOutput = `${pp.dir ? pp.dir + "/" : ""}${pp.name}.zip`;
|
|
115
|
+
}
|
|
116
|
+
fs.writeFile(actualOutput, out, "binary", () => {
|
|
117
|
+
console.log("done!!");
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
comment,
|
|
122
|
+
);
|
|
123
|
+
};
|
|
124
|
+
/*
|
|
125
|
+
node -i
|
|
126
|
+
const zip = require("js-dev-scripts/scripts/zip");
|
|
127
|
+
const comment = "sha384-XSSUboqeWltJhmqrtg5XP9Svrg5NFArrGX0Nm4+c6NUtaRXOiidVCaWFbSQXdVFC";
|
|
128
|
+
zip("./lib/webpack.js", comment);
|
|
129
|
+
const comment2 = "sha384-Y7FclS+/O1tb8AfLynrvaZRzlgEfzNlqOB2iX/tQ/BQBu6lK3MsxBlDDeqtFd+2X";
|
|
130
|
+
zip("./lib/typeid-map.js", comment2);
|
|
131
|
+
|
|
132
|
+
[2022/5/11 10:07:34]
|
|
133
|
+
b64=$(echo -n "sha384-XSSUboqeWltJhmqrtg5XP9Svrg5NFArrGX0Nm4+c6NUtaRXOiidVCaWFbSQXdVFC" | base64)
|
|
134
|
+
echo "$b64"
|
|
135
|
+
echo -n "$b64" | base64 -d # OK
|
|
136
|
+
|
|
137
|
+
echo -n "XSSUboqeWltJhmqrtg5XP9Svrg5NFArrGX0Nm4+c6NUtaRXOiidVCaWFbSQXdVFC" | base64 -d # binary data
|
|
138
|
+
*/
|
package/tool-lib/cjbm.js
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
3
|
+
Copyright (C) 2023 jeffy-g <hirotom1107@gmail.com>
|
|
4
|
+
Released under the MIT license
|
|
5
|
+
https://opensource.org/licenses/mit-license.php
|
|
6
|
+
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
|
7
|
+
*/
|
|
8
|
+
/// <reference path="./tools.d.ts"/>
|
|
9
|
+
/// <reference path="../regex.d.ts"/>
|
|
10
|
+
// @ts-check
|
|
11
|
+
/**
|
|
12
|
+
* @file (C)onvert (J)S to (B)rowser (M)odule
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @import * as Regex from "../regex.d.ts";
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
// [2023-10-30] from https://regex101.com/r/EpuQLT/37
|
|
20
|
+
// CAUTION: PCRE style では、js では使える assertion が使えない場合があるので、適宜書き換えないといけない.
|
|
21
|
+
// 詳細は、 https://regex101.com/r/EpuQLT/38 の regex string と以下を見比べて確認
|
|
22
|
+
// const RE_TEXT = `(?<!\\/\\/\\s*)(?:import|export) # comments
|
|
23
|
+
// \\s*
|
|
24
|
+
// (?:
|
|
25
|
+
// "(?=[.\\/]+)| # character ["] \\x22 e.g - import "../global/index"
|
|
26
|
+
// (?:
|
|
27
|
+
// [\\w_]+\\s+| # default import, e.g - import fs from "./file";
|
|
28
|
+
// (?:[\\w_]+\\s*,\\s*)?\\{[^}]+\\}\\s*| # e.g - import View, { TitiledSphereMesh } from "./view";
|
|
29
|
+
// \\*\\s*as\\s+[\\w_]+\\s+| # e.g - export * as OO from "./rateLimiter";
|
|
30
|
+
// \\*\\s* # e.g - export * from "./rateLimiter";
|
|
31
|
+
// )from\\s*"(?:[.\\/]+) # Positive Lookahead (./|../|../../) ...
|
|
32
|
+
// )
|
|
33
|
+
// (?:[^"]*?)(?:\\.((?:c|m)?js))? # extenstion replaceable
|
|
34
|
+
// (?="\\s*;?)`;
|
|
35
|
+
|
|
36
|
+
/* TODO: 2025/2/15 19:39:29 - regex group name を抽出する utility type
|
|
37
|
+
(?<!\\/\\/|\\/\\/\\s)(?:import|export)
|
|
38
|
+
\\s*
|
|
39
|
+
(?:
|
|
40
|
+
"(?!https?:)(?:
|
|
41
|
+
(?<globalName>(?:[.\\/]+)?[^"]+?)(?:\\.(?<globalExt>(?:c|m)?jsx?))?
|
|
42
|
+
)"|
|
|
43
|
+
\\(\\s*
|
|
44
|
+
"(?<dynamicName>(?:[.\\/]+)?[^"]+?)(?:\\.(?<dynamicExt>(?:c|m)?jsx?))?"
|
|
45
|
+
\\s*\\)|
|
|
46
|
+
(?:(?<clause>.+?|(?:\\w+\\s*,)?\\s*\\{[^}]+\\})\\s*from)\\s*"(?!https?:)
|
|
47
|
+
(?:
|
|
48
|
+
(?<moduleName>(?:[.\\/]+)?[^"]+?)(?:\\.(?<moduleExt>(?:c|m)?jsx?))?
|
|
49
|
+
)"
|
|
50
|
+
)
|
|
51
|
+
\\s*;?
|
|
52
|
+
*/
|
|
53
|
+
// /**
|
|
54
|
+
// * @date 2025/2/15 20:11:22
|
|
55
|
+
// * @see https://regex101.com/r/uMMsD4/22
|
|
56
|
+
// */
|
|
57
|
+
const RE_TEXT = String.raw`
|
|
58
|
+
# NOTE: PCRE での検証時は line comment assertion を (?<!\/\/|\/\/\s) とすること (regex101.com)
|
|
59
|
+
(?<!\/\/+\s*)(?:import|export)
|
|
60
|
+
\s*
|
|
61
|
+
(?:
|
|
62
|
+
"(?!https?:)(?=[.\/]+)(?:
|
|
63
|
+
(?<globalName>(?:[.\/]+)?[^"]+?)(?:\.(?<globalExt>(?:c|m)?jsx?))?
|
|
64
|
+
)"|
|
|
65
|
+
\(\s*
|
|
66
|
+
"(?!https?:)(?=[.\/]+)(?<dynamicName>(?:[.\/]+)?[^"]+?)(?:\.(?<dynamicExt>(?:c|m)?jsx?))?"
|
|
67
|
+
\s*\)|
|
|
68
|
+
(?:(?<clause>.+?|(?:\w+\s*,)?\s*\{[^}]+\})\s*from)\s*"(?!https?:)(?=[.\/]+)
|
|
69
|
+
(?:
|
|
70
|
+
(?<moduleName>(?:[.\/]+)?[^"]+?)(?:\.(?<moduleExt>(?:c|m)?jsx?))?
|
|
71
|
+
)"
|
|
72
|
+
)
|
|
73
|
+
(?=\s*;)?
|
|
74
|
+
`;
|
|
75
|
+
// const RE_TEXT = `
|
|
76
|
+
// # NOTE: PCRE での検証時は line comment assertion を (?<!\\/\\/|\\/\\/\\s) とすること
|
|
77
|
+
// (?<!\\/\\/+\\s*)(?:import|export) # line comment でない import/export statement
|
|
78
|
+
// \\s*
|
|
79
|
+
// (?:
|
|
80
|
+
// "(?!https?:)(?=[.\\/]+)(?:
|
|
81
|
+
// (?<globalName>(?:[.\\/]+)?[^"]+?)(?:\\.(?<globalExt>(?:c|m)?jsx?))?
|
|
82
|
+
// )"|
|
|
83
|
+
// \\(\\s*
|
|
84
|
+
// "(?!https?:)(?=[.\\/]+)(?<dynamicName>(?:[.\\/]+)?[^"]+?)(?:\\.(?<dynamicExt>(?:c|m)?jsx?))?"
|
|
85
|
+
// \\s*\\)|
|
|
86
|
+
// (?:(?<clause>.+?|(?:\\w+\\s*,)?\\s*\\{[^}]+\\})\\s*from)\\s*"(?!https?:)(?=[.\\/]+)
|
|
87
|
+
// (?:
|
|
88
|
+
// (?<moduleName>(?:[.\\/]+)?[^"]+?)(?:\\.(?<moduleExt>(?:c|m)?jsx?))?
|
|
89
|
+
// )"
|
|
90
|
+
// )
|
|
91
|
+
// (?=\\s*;)?
|
|
92
|
+
// `;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* @date 2025/2/15 20:11:22
|
|
96
|
+
* @see https://regex101.com/r/uMMsD4/22
|
|
97
|
+
*/
|
|
98
|
+
/* ctt
|
|
99
|
+
const reImportExportDetection = createRegExp(
|
|
100
|
+
`(?<!\\/\\/+\\s*)(?:import|export)\\s*(?:"(?!https?:)(?=[.\\/]+)\
|
|
101
|
+
(?:(?<globalName>(?:[.\\/]+)?[^"]+?)(?:\\.(?<globalExt>(?:c|m)?jsx?))?)"|\
|
|
102
|
+
\\(\\s*"(?!https?:)(?=[.\\/]+)(?<dynamicName>(?:[.\\/]+)?[^"]+?)(?:\\.(?<dynamicExt>(?:c|m)?jsx?))?"\\s*\\)|\
|
|
103
|
+
(?:(?<clause>.+?|(?:\\w+\\s*,)?\\s*\\{[^}]+\\})\\s*from)\\s*"(?!https?:)(?=[.\\/]+)(?:(?<moduleName>(?:[.\\/]+)?[^"]+?)\
|
|
104
|
+
(?:\\.(?<moduleExt>(?:c|m)?jsx?))?)")(?=\\s*;)?`, "g"
|
|
105
|
+
);
|
|
106
|
+
/*/
|
|
107
|
+
const reImportExportDetection = new RegExp(
|
|
108
|
+
RE_TEXT.replace(/\s*\(\?\#.*\)\s*$|(?<!\\)\#\s*.*$|\s+/gm, ""), "g",
|
|
109
|
+
);
|
|
110
|
+
//*/
|
|
111
|
+
|
|
112
|
+
// /**
|
|
113
|
+
// * @type {typeof XRegex["createRegExp"]}
|
|
114
|
+
// */
|
|
115
|
+
// function createRegExp(pattern, flags) {
|
|
116
|
+
// return /** @type {RegExp & { readonly source: typeof pattern }} */(new RegExp(pattern, flags));
|
|
117
|
+
// }
|
|
118
|
+
/* ctt 2025/3/18 13:46:03 OK, works
|
|
119
|
+
// @ts-expect-error
|
|
120
|
+
let gg = /** @type {XRegex.RegExpExecArrayFixed<typeof reImportExportDetection>} * /(reImportExportDetection.exec("test"));
|
|
121
|
+
gg.groups?.dynamicExt
|
|
122
|
+
/*/
|
|
123
|
+
//*/
|
|
124
|
+
// function createRegExp<P extends string>(
|
|
125
|
+
// pattern: P,
|
|
126
|
+
// flags?: string
|
|
127
|
+
// ): RegExp & { readonly source: P } {
|
|
128
|
+
// return new RegExp(pattern, flags) as RegExp & { readonly source: P };
|
|
129
|
+
// }
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Generates a replacer function to update import/export statements with a new file extension.
|
|
133
|
+
*
|
|
134
|
+
* @param {string} ext - The new file extension to use.
|
|
135
|
+
* @returns {TRegexImportExportDetectorReplacer} A function to update import/export statements with the specified file extension.
|
|
136
|
+
* @date 2025/2/16 18:38:39
|
|
137
|
+
*/
|
|
138
|
+
function getReplacer(ext) {
|
|
139
|
+
/**
|
|
140
|
+
* NOTE: Replacement is only performed with the syntax "$` is $1".
|
|
141
|
+
* String concatenation is not allowed.
|
|
142
|
+
*/
|
|
143
|
+
// before match: $` NOTE: "$` is $1" のような使い方でないと置換されない (string concatnation is not allowed)
|
|
144
|
+
// after match: $'
|
|
145
|
+
// TIP: debug on chrome, snippet: detect-import-export-regex.js
|
|
146
|
+
/** @type {TRegexImportExportDetectorReplacer} */
|
|
147
|
+
const replacer = (
|
|
148
|
+
$0, _1, _2, _3, _4, _5, _6, _7, _o, _s, { clause, dynamicName, globalName, moduleName }
|
|
149
|
+
// $0, $1, $2, $3, $4, $5, $6, $7, offset, source, groups
|
|
150
|
+
) => {
|
|
151
|
+
// const {
|
|
152
|
+
// clause, // dynamicExt,
|
|
153
|
+
// dynamicName,// globalExt,
|
|
154
|
+
// globalName, // moduleExt,
|
|
155
|
+
// moduleName,
|
|
156
|
+
// } = groups;
|
|
157
|
+
const kw = $0.startsWith("import") ? "import" : "export";
|
|
158
|
+
let whichName = /** @type {string} */ (
|
|
159
|
+
globalName || moduleName || dynamicName
|
|
160
|
+
);
|
|
161
|
+
if (whichName[whichName.length - 1] === "/") {
|
|
162
|
+
whichName += "index";
|
|
163
|
+
}
|
|
164
|
+
return `${kw}${globalName ? ` "${whichName}.${ext}"` : clause ? ` ${clause} from "${whichName}.${ext}"` : `("${whichName}.${ext}")`}`;
|
|
165
|
+
};
|
|
166
|
+
return replacer;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* @returns {TJSToolEntry}
|
|
171
|
+
*/
|
|
172
|
+
module.exports = () => {
|
|
173
|
+
return {
|
|
174
|
+
taskName: "(C)onvert (J)S to (B)rowser (M)odule",
|
|
175
|
+
/**
|
|
176
|
+
* about regex: see [[tools.js] regex - (C)onvert (J)S to (B)rowser (M)odule](https://regex101.com/r/EpuQLT/37)
|
|
177
|
+
*/
|
|
178
|
+
fn() {
|
|
179
|
+
// DEVNOTE: 2020/5/27 12:11:11 - https://regex101.com/r/EpuQLT/21
|
|
180
|
+
// DEVNOTE: 2022/4/19 7:28:28 - https://regex101.com/r/EpuQLT/23
|
|
181
|
+
// DEVNOTE: 2023/10/30 - https://regex101.com/r/EpuQLT/24
|
|
182
|
+
// -> supports [import*as e from"../"] etc
|
|
183
|
+
// DEVNOTE: 2023/11/06 - https://regex101.com/r/EpuQLT/32
|
|
184
|
+
// -> fix unstable detection
|
|
185
|
+
// const bp = params.basePath;
|
|
186
|
+
// const bases = Array.isArray(bp)? bp: typeof bp === "string" && bp.length ? [bp] : void 0;
|
|
187
|
+
const bases = getBasePaths();
|
|
188
|
+
const ext = params.ext || "js";
|
|
189
|
+
/** ### regex summary
|
|
190
|
+
* ```perl
|
|
191
|
+
* (?:import|export) # comments
|
|
192
|
+
* \s*
|
|
193
|
+
* (?:
|
|
194
|
+
* "(?=[.\/]+)| # character ["] \x22 e.g - import "../global/index"
|
|
195
|
+
* (?:
|
|
196
|
+
* [\w_]+\s+| # default import, e.g - import fs from "./file";
|
|
197
|
+
* (?:[\w_]+\s*,\s*)?\{[^}]+\}\s*| # e.g - import View, { TitiledSphereMesh } from "./view";
|
|
198
|
+
* \*\s*as\s+[\w_]+\s+| # e.g - export * as OO from "./rateLimiter";
|
|
199
|
+
* \*\s* # e.g - export * from "./rateLimiter";
|
|
200
|
+
* )from\s*"(?:[.\/]+) # Positive Lookahead (./|../|../../) ...
|
|
201
|
+
* )
|
|
202
|
+
* (?:[^"]*)
|
|
203
|
+
* (?<!\.js) # Negative Lookbehind not(/\.(?:c|m)?js/)
|
|
204
|
+
* (?="\s*;?)
|
|
205
|
+
* ```
|
|
206
|
+
* @see regex details see {@link https://regex101.com/r/EpuQLT/32 regex101.com}
|
|
207
|
+
*/
|
|
208
|
+
// const replacer = (/** @type {string} */$0, /** @type {string} */$1) => {
|
|
209
|
+
// if ($0[$0.length - 1] === "/") {
|
|
210
|
+
// return `${$0}index.${ext}`;
|
|
211
|
+
// }
|
|
212
|
+
// return $1? $0.replace(`.${$1}`, `.${ext}`): `${$0}.${ext}`;
|
|
213
|
+
// };
|
|
214
|
+
// const bs = isArray(params.basePath)? params.basePath[0]: params.basePath;
|
|
215
|
+
processSources(
|
|
216
|
+
/** @type {string} */ (this.taskName), (data) => {
|
|
217
|
+
reImportExportDetection.lastIndex = 0;
|
|
218
|
+
return data.replace(reImportExportDetection, getReplacer(ext));
|
|
219
|
+
}, {
|
|
220
|
+
bases,
|
|
221
|
+
// base: bs || ""
|
|
222
|
+
},
|
|
223
|
+
);
|
|
224
|
+
},
|
|
225
|
+
get help() {
|
|
226
|
+
return `${this.taskName}
|
|
227
|
+
ex - jstool -cmd cjbm [-root ./build | -basePath "./dist/esm,extra-tests/mini-semaphore"] [-ext js] [-targets "['core.js', 'object.js']"]
|
|
228
|
+
note:
|
|
229
|
+
root - Recursively searches for files.
|
|
230
|
+
This option is useful, but if there are directories that need to be avoided, use the 'basePath' option
|
|
231
|
+
basePath - can be "<path>,<path>,..." (array type arg)
|
|
232
|
+
ext - specifies the module extension for import clauses. default is "js"
|
|
233
|
+
targets - specify this if you want to apply it only to a specific file.
|
|
234
|
+
the file specified here should be directly under \`basePath\`!
|
|
235
|
+
value must be array type arg, "['<path>', '<path>',...]" or "<path>,<path>,..."
|
|
236
|
+
`;
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
};
|