@hpcc-js/esbuild-plugins 1.8.7 → 1.8.9
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 +43 -43
- package/README.md +45 -45
- package/dist/index.js +3 -3
- package/dist/index.js.map +3 -3
- package/dist/sfx-wrapper.js +5 -25
- package/dist/sfx-wrapper.js.map +3 -3
- package/package.json +5 -5
- package/src/build.ts +177 -177
- package/src/exclude-sourcemap.ts +20 -20
- package/src/index.ts +8 -8
- package/src/inline-css.ts +44 -44
- package/src/package-version-plugin.ts +87 -87
- package/src/problem-matcher.ts +24 -24
- package/src/rebuild-logger.ts +24 -24
- package/src/remove-strict.ts +21 -21
- package/src/sfx-wrapper.ts +152 -152
- package/src/vite-utils.ts +260 -259
- package/types/vite-utils.d.ts +1 -1
|
@@ -1,87 +1,87 @@
|
|
|
1
|
-
import type { Plugin } from "vite";
|
|
2
|
-
|
|
3
|
-
export interface PackageVersionPluginOptions {
|
|
4
|
-
/**
|
|
5
|
-
* Package.json object containing name and version
|
|
6
|
-
*/
|
|
7
|
-
pkg: {
|
|
8
|
-
name: string;
|
|
9
|
-
version: string;
|
|
10
|
-
};
|
|
11
|
-
/**
|
|
12
|
-
* Build version (typically from root package.json)
|
|
13
|
-
* If not provided, defaults to pkg.version
|
|
14
|
-
*/
|
|
15
|
-
buildVersion?: string;
|
|
16
|
-
/**
|
|
17
|
-
* Placeholder for package name (default: "__PACKAGE_NAME__")
|
|
18
|
-
*/
|
|
19
|
-
namePlaceholder?: string;
|
|
20
|
-
/**
|
|
21
|
-
* Placeholder for package version (default: "__PACKAGE_VERSION__")
|
|
22
|
-
*/
|
|
23
|
-
versionPlaceholder?: string;
|
|
24
|
-
/**
|
|
25
|
-
* Placeholder for build version (default: "__BUILD_VERSION__")
|
|
26
|
-
*/
|
|
27
|
-
buildVersionPlaceholder?: string;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Vite plugin to replace package version placeholders during build
|
|
32
|
-
* This allows keeping version information in source files as placeholders
|
|
33
|
-
* that get replaced with actual values from package.json during the build
|
|
34
|
-
*/
|
|
35
|
-
export function packageVersionPlugin(options: PackageVersionPluginOptions): Plugin {
|
|
36
|
-
const {
|
|
37
|
-
pkg,
|
|
38
|
-
buildVersion = pkg.version,
|
|
39
|
-
namePlaceholder = "__PACKAGE_NAME__",
|
|
40
|
-
versionPlaceholder = "__PACKAGE_VERSION__",
|
|
41
|
-
buildVersionPlaceholder = "__BUILD_VERSION__"
|
|
42
|
-
} = options;
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
name: "hpcc-package-version-plugin",
|
|
46
|
-
enforce: "pre",
|
|
47
|
-
transform(code: string, id: string) {
|
|
48
|
-
// Only process TypeScript/JavaScript files
|
|
49
|
-
if (!id.endsWith(".ts") && !id.endsWith(".js")) {
|
|
50
|
-
return null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
// Check if the file contains any placeholders
|
|
54
|
-
if (!code.includes(namePlaceholder) &&
|
|
55
|
-
!code.includes(versionPlaceholder) &&
|
|
56
|
-
!code.includes(buildVersionPlaceholder)) {
|
|
57
|
-
return null;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Replace placeholders with actual values
|
|
61
|
-
let transformedCode = code;
|
|
62
|
-
if (code.includes(namePlaceholder)) {
|
|
63
|
-
transformedCode = transformedCode.replace(
|
|
64
|
-
new RegExp(namePlaceholder, "g"),
|
|
65
|
-
pkg.name
|
|
66
|
-
);
|
|
67
|
-
}
|
|
68
|
-
if (code.includes(versionPlaceholder)) {
|
|
69
|
-
transformedCode = transformedCode.replace(
|
|
70
|
-
new RegExp(versionPlaceholder, "g"),
|
|
71
|
-
pkg.version
|
|
72
|
-
);
|
|
73
|
-
}
|
|
74
|
-
if (code.includes(buildVersionPlaceholder)) {
|
|
75
|
-
transformedCode = transformedCode.replace(
|
|
76
|
-
new RegExp(buildVersionPlaceholder, "g"),
|
|
77
|
-
buildVersion
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
return {
|
|
82
|
-
code: transformedCode,
|
|
83
|
-
map: null // Could generate source map if needed
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
}
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
export interface PackageVersionPluginOptions {
|
|
4
|
+
/**
|
|
5
|
+
* Package.json object containing name and version
|
|
6
|
+
*/
|
|
7
|
+
pkg: {
|
|
8
|
+
name: string;
|
|
9
|
+
version: string;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Build version (typically from root package.json)
|
|
13
|
+
* If not provided, defaults to pkg.version
|
|
14
|
+
*/
|
|
15
|
+
buildVersion?: string;
|
|
16
|
+
/**
|
|
17
|
+
* Placeholder for package name (default: "__PACKAGE_NAME__")
|
|
18
|
+
*/
|
|
19
|
+
namePlaceholder?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Placeholder for package version (default: "__PACKAGE_VERSION__")
|
|
22
|
+
*/
|
|
23
|
+
versionPlaceholder?: string;
|
|
24
|
+
/**
|
|
25
|
+
* Placeholder for build version (default: "__BUILD_VERSION__")
|
|
26
|
+
*/
|
|
27
|
+
buildVersionPlaceholder?: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Vite plugin to replace package version placeholders during build
|
|
32
|
+
* This allows keeping version information in source files as placeholders
|
|
33
|
+
* that get replaced with actual values from package.json during the build
|
|
34
|
+
*/
|
|
35
|
+
export function packageVersionPlugin(options: PackageVersionPluginOptions): Plugin {
|
|
36
|
+
const {
|
|
37
|
+
pkg,
|
|
38
|
+
buildVersion = pkg.version,
|
|
39
|
+
namePlaceholder = "__PACKAGE_NAME__",
|
|
40
|
+
versionPlaceholder = "__PACKAGE_VERSION__",
|
|
41
|
+
buildVersionPlaceholder = "__BUILD_VERSION__"
|
|
42
|
+
} = options;
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
name: "hpcc-package-version-plugin",
|
|
46
|
+
enforce: "pre",
|
|
47
|
+
transform(code: string, id: string) {
|
|
48
|
+
// Only process TypeScript/JavaScript files
|
|
49
|
+
if (!id.endsWith(".ts") && !id.endsWith(".js")) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Check if the file contains any placeholders
|
|
54
|
+
if (!code.includes(namePlaceholder) &&
|
|
55
|
+
!code.includes(versionPlaceholder) &&
|
|
56
|
+
!code.includes(buildVersionPlaceholder)) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Replace placeholders with actual values
|
|
61
|
+
let transformedCode = code;
|
|
62
|
+
if (code.includes(namePlaceholder)) {
|
|
63
|
+
transformedCode = transformedCode.replace(
|
|
64
|
+
new RegExp(namePlaceholder, "g"),
|
|
65
|
+
pkg.name
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
if (code.includes(versionPlaceholder)) {
|
|
69
|
+
transformedCode = transformedCode.replace(
|
|
70
|
+
new RegExp(versionPlaceholder, "g"),
|
|
71
|
+
pkg.version
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
if (code.includes(buildVersionPlaceholder)) {
|
|
75
|
+
transformedCode = transformedCode.replace(
|
|
76
|
+
new RegExp(buildVersionPlaceholder, "g"),
|
|
77
|
+
buildVersion
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return {
|
|
82
|
+
code: transformedCode,
|
|
83
|
+
map: null // Could generate source map if needed
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}
|
package/src/problem-matcher.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import type { PluginBuild, Plugin } from "esbuild";
|
|
2
|
-
|
|
3
|
-
export function problemMatcher(): Plugin {
|
|
4
|
-
return {
|
|
5
|
-
name: "problem-matcher",
|
|
6
|
-
|
|
7
|
-
setup(build: PluginBuild) {
|
|
8
|
-
|
|
9
|
-
build.onStart(() => {
|
|
10
|
-
// eslint-disable-next-line no-console
|
|
11
|
-
console.log("[watch] build started");
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
build.onEnd((result) => {
|
|
15
|
-
result.errors.forEach(({ text, location }) => {
|
|
16
|
-
console.error(`✘ [ERROR] ${text}`);
|
|
17
|
-
console.error(` ${location?.file}:${location?.line}:${location?.column}:`);
|
|
18
|
-
});
|
|
19
|
-
// eslint-disable-next-line no-console
|
|
20
|
-
console.log("[watch] build finished");
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
}
|
|
1
|
+
import type { PluginBuild, Plugin } from "esbuild";
|
|
2
|
+
|
|
3
|
+
export function problemMatcher(): Plugin {
|
|
4
|
+
return {
|
|
5
|
+
name: "problem-matcher",
|
|
6
|
+
|
|
7
|
+
setup(build: PluginBuild) {
|
|
8
|
+
|
|
9
|
+
build.onStart(() => {
|
|
10
|
+
// eslint-disable-next-line no-console
|
|
11
|
+
console.log("[watch] build started");
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
build.onEnd((result) => {
|
|
15
|
+
result.errors.forEach(({ text, location }) => {
|
|
16
|
+
console.error(`✘ [ERROR] ${text}`);
|
|
17
|
+
console.error(` ${location?.file}:${location?.line}:${location?.column}:`);
|
|
18
|
+
});
|
|
19
|
+
// eslint-disable-next-line no-console
|
|
20
|
+
console.log("[watch] build finished");
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
package/src/rebuild-logger.ts
CHANGED
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import type { PluginBuild, Plugin } from "esbuild";
|
|
2
|
-
|
|
3
|
-
export interface RebuildLoggerOptions {
|
|
4
|
-
outfile?: string;
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export function rebuildLogger(opts: RebuildLoggerOptions): Plugin {
|
|
8
|
-
return {
|
|
9
|
-
name: "rebuild-logger",
|
|
10
|
-
|
|
11
|
-
setup(build: PluginBuild) {
|
|
12
|
-
|
|
13
|
-
build.onStart(() => {
|
|
14
|
-
// eslint-disable-next-line no-console
|
|
15
|
-
console.log("[watch] build started");
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
build.onEnd(() => {
|
|
19
|
-
// eslint-disable-next-line no-console
|
|
20
|
-
console.log(`Rebuilt ${opts.outfile ?? "Unknown"}`);
|
|
21
|
-
});
|
|
22
|
-
}
|
|
23
|
-
};
|
|
24
|
-
}
|
|
1
|
+
import type { PluginBuild, Plugin } from "esbuild";
|
|
2
|
+
|
|
3
|
+
export interface RebuildLoggerOptions {
|
|
4
|
+
outfile?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function rebuildLogger(opts: RebuildLoggerOptions): Plugin {
|
|
8
|
+
return {
|
|
9
|
+
name: "rebuild-logger",
|
|
10
|
+
|
|
11
|
+
setup(build: PluginBuild) {
|
|
12
|
+
|
|
13
|
+
build.onStart(() => {
|
|
14
|
+
// eslint-disable-next-line no-console
|
|
15
|
+
console.log("[watch] build started");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
build.onEnd(() => {
|
|
19
|
+
// eslint-disable-next-line no-console
|
|
20
|
+
console.log(`Rebuilt ${opts.outfile ?? "Unknown"}`);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
}
|
package/src/remove-strict.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import { writeFileSync } from "node:fs";
|
|
2
|
-
import type { PluginBuild, Plugin } from "esbuild";
|
|
3
|
-
|
|
4
|
-
export function removeStrict(): Plugin {
|
|
5
|
-
return {
|
|
6
|
-
name: "remove-strict",
|
|
7
|
-
setup(build: PluginBuild) {
|
|
8
|
-
build.initialOptions.write = false;
|
|
9
|
-
build.onEnd((result) => {
|
|
10
|
-
result?.outputFiles?.forEach(file => {
|
|
11
|
-
if (file.path.endsWith(".js")) {
|
|
12
|
-
const contents = file.text.replace(/"use strict";/g, "");
|
|
13
|
-
writeFileSync(file.path, contents, { encoding: "utf8" });
|
|
14
|
-
} else {
|
|
15
|
-
writeFileSync(file.path, file.contents, { encoding: "binary" });
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
}
|
|
1
|
+
import { writeFileSync } from "node:fs";
|
|
2
|
+
import type { PluginBuild, Plugin } from "esbuild";
|
|
3
|
+
|
|
4
|
+
export function removeStrict(): Plugin {
|
|
5
|
+
return {
|
|
6
|
+
name: "remove-strict",
|
|
7
|
+
setup(build: PluginBuild) {
|
|
8
|
+
build.initialOptions.write = false;
|
|
9
|
+
build.onEnd((result) => {
|
|
10
|
+
result?.outputFiles?.forEach(file => {
|
|
11
|
+
if (file.path.endsWith(".js")) {
|
|
12
|
+
const contents = file.text.replace(/"use strict";/g, "");
|
|
13
|
+
writeFileSync(file.path, contents, { encoding: "utf8" });
|
|
14
|
+
} else {
|
|
15
|
+
writeFileSync(file.path, file.contents, { encoding: "binary" });
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
package/src/sfx-wrapper.ts
CHANGED
|
@@ -1,152 +1,152 @@
|
|
|
1
|
-
import { readFile } from "node:fs/promises";
|
|
2
|
-
import { existsSync } from "node:fs";
|
|
3
|
-
import { Base91 } from "@hpcc-js/wasm-base91";
|
|
4
|
-
import { Zstd } from "@hpcc-js/wasm-zstd";
|
|
5
|
-
import type { Plugin, PluginBuild } from "esbuild";
|
|
6
|
-
|
|
7
|
-
const table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_\`{|}~"';
|
|
8
|
-
const DECODE_TABLE = (() => {
|
|
9
|
-
const t = new Int8Array(128).fill(-1);
|
|
10
|
-
for (let i = 0; i < table.length; ++i) {
|
|
11
|
-
t[table.charCodeAt(i)] = i;
|
|
12
|
-
}
|
|
13
|
-
return t;
|
|
14
|
-
})();
|
|
15
|
-
|
|
16
|
-
function tpl(wasmJsPath: string, base91Wasm: string, base91CompressedWasm: string, wasmSize: number, compressedSize: number) {
|
|
17
|
-
|
|
18
|
-
const compressed = (base91CompressedWasm.length + 8 * 1024) <= base91Wasm.length;
|
|
19
|
-
const wasmJsExists = existsSync(wasmJsPath);
|
|
20
|
-
|
|
21
|
-
return `\
|
|
22
|
-
${compressed ? 'import { decompress } from "fzstd";' : ""}
|
|
23
|
-
${wasmJsExists ? `import wrapper from "${wasmJsPath}";` : ""}
|
|
24
|
-
|
|
25
|
-
const D = new Int8Array([${DECODE_TABLE.join(",")}]);
|
|
26
|
-
|
|
27
|
-
function decode(s: string): Uint8Array {
|
|
28
|
-
const out = new Uint8Array(${compressed ? compressedSize : wasmSize});
|
|
29
|
-
let pos = 0, b = 0, n = 0, v = -1;
|
|
30
|
-
|
|
31
|
-
for (let i = 0, len = s.length; i < len; i++) {
|
|
32
|
-
const c = s.charCodeAt(i);
|
|
33
|
-
if (c > 127) continue;
|
|
34
|
-
const p = D[c];
|
|
35
|
-
if (p < 0) continue;
|
|
36
|
-
if (v < 0) {
|
|
37
|
-
v = p;
|
|
38
|
-
} else {
|
|
39
|
-
v += p * 91;
|
|
40
|
-
b |= v << n;
|
|
41
|
-
n += (v & 8191) > 88 ? 13 : 14;
|
|
42
|
-
do {
|
|
43
|
-
out[pos++] = b;
|
|
44
|
-
b >>>= 8;
|
|
45
|
-
n -= 8;
|
|
46
|
-
} while (n > 7);
|
|
47
|
-
v = -1;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
if (v >= 0) out[pos++] = (b | v << n) & 0xff;
|
|
52
|
-
|
|
53
|
-
return out;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const blobStr = '${compressed ? base91CompressedWasm : base91Wasm}';
|
|
57
|
-
|
|
58
|
-
let g_wasmBinary: Uint8Array | undefined;
|
|
59
|
-
export default function() {
|
|
60
|
-
g_wasmBinary ??= ${compressed ? "decompress(decode(blobStr))" : "decode(blobStr)"};
|
|
61
|
-
${!wasmJsExists ? `\
|
|
62
|
-
return g_wasmBinary;
|
|
63
|
-
`: `\
|
|
64
|
-
return wrapper({
|
|
65
|
-
wasmBinary: g_wasmBinary,
|
|
66
|
-
locateFile: () => ""
|
|
67
|
-
});
|
|
68
|
-
`}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function reset() {
|
|
72
|
-
g_wasmBinary = undefined;
|
|
73
|
-
} `.trim();
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
let base91Promise: Promise<Base91> | undefined;
|
|
77
|
-
let zstdPromise: Promise<Zstd> | undefined;
|
|
78
|
-
function getBase91() {
|
|
79
|
-
base91Promise ??= Base91.load();
|
|
80
|
-
return base91Promise;
|
|
81
|
-
}
|
|
82
|
-
function getZstd() {
|
|
83
|
-
zstdPromise ??= Zstd.load();
|
|
84
|
-
return zstdPromise;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
export async function wrap(path: string) {
|
|
88
|
-
console.info(`Wrapping: ${path}`);
|
|
89
|
-
const [base91, zstd] = await Promise.all([getBase91(), getZstd()]);
|
|
90
|
-
|
|
91
|
-
console.info(" Reading WASM file...");
|
|
92
|
-
const wasm = await readFile(path);
|
|
93
|
-
const wasmJsPath = path.replace(/\.wasm$/, ".js");
|
|
94
|
-
const CHUNK_SIZE = 64 * 1024 * 1024;
|
|
95
|
-
|
|
96
|
-
const base91Parts: string[] = [];
|
|
97
|
-
base91.reset();
|
|
98
|
-
for (let offset = 0; offset < wasm.length; offset += CHUNK_SIZE) {
|
|
99
|
-
const chunk = wasm.subarray(offset, Math.min(offset + CHUNK_SIZE, wasm.length));
|
|
100
|
-
base91Parts.push(base91.encodeChunk(chunk));
|
|
101
|
-
console.info(` Encoded ${Math.min(offset + CHUNK_SIZE, wasm.length)} / ${wasm.length} bytes`);
|
|
102
|
-
}
|
|
103
|
-
base91Parts.push(base91.encodeChunkEnd());
|
|
104
|
-
const base91Wasm = base91Parts.join("");
|
|
105
|
-
|
|
106
|
-
console.info(" Compressing...");
|
|
107
|
-
const compressedChunks: Uint8Array[] = [];
|
|
108
|
-
zstd.reset();
|
|
109
|
-
for (let offset = 0; offset < wasm.length; offset += CHUNK_SIZE) {
|
|
110
|
-
const chunk = wasm.subarray(offset, Math.min(offset + CHUNK_SIZE, wasm.length));
|
|
111
|
-
compressedChunks.push(zstd.compressChunk(chunk));
|
|
112
|
-
console.info(` Compressed ${Math.min(offset + CHUNK_SIZE, wasm.length)} / ${wasm.length} bytes`);
|
|
113
|
-
}
|
|
114
|
-
compressedChunks.push(zstd.compressEnd());
|
|
115
|
-
const totalLength = compressedChunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
116
|
-
const compressedWasm = new Uint8Array(totalLength);
|
|
117
|
-
let compressionOffset = 0;
|
|
118
|
-
for (const chunk of compressedChunks) {
|
|
119
|
-
compressedWasm.set(chunk, compressionOffset);
|
|
120
|
-
compressionOffset += chunk.length;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
console.info(" Encoding compressed...");
|
|
124
|
-
const base91CompressedParts: string[] = [];
|
|
125
|
-
base91.reset();
|
|
126
|
-
for (let offset = 0; offset < compressedWasm.length; offset += CHUNK_SIZE) {
|
|
127
|
-
const chunk = compressedWasm.subarray(offset, Math.min(offset + CHUNK_SIZE, compressedWasm.length));
|
|
128
|
-
base91CompressedParts.push(base91.encodeChunk(chunk));
|
|
129
|
-
console.info(` Encoded ${Math.min(offset + CHUNK_SIZE, compressedWasm.length)} / ${compressedWasm.length} bytes (compressed)`);
|
|
130
|
-
}
|
|
131
|
-
base91CompressedParts.push(base91.encodeChunkEnd());
|
|
132
|
-
const base91CompressedWasm = base91CompressedParts.join("");
|
|
133
|
-
|
|
134
|
-
console.info(" Creating wrapper...");
|
|
135
|
-
return tpl(wasmJsPath, base91Wasm, base91CompressedWasm, wasm.length, compressedWasm.length);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function sfxWasm(): Plugin {
|
|
139
|
-
return {
|
|
140
|
-
name: "sfx-wasm",
|
|
141
|
-
|
|
142
|
-
setup(build: PluginBuild) {
|
|
143
|
-
|
|
144
|
-
build.onLoad({ filter: /\.wasm$/ }, async args => {
|
|
145
|
-
return {
|
|
146
|
-
contents: await wrap(args.path),
|
|
147
|
-
loader: "ts",
|
|
148
|
-
};
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
};
|
|
152
|
-
}
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { Base91 } from "@hpcc-js/wasm-base91";
|
|
4
|
+
import { Zstd } from "@hpcc-js/wasm-zstd";
|
|
5
|
+
import type { Plugin, PluginBuild } from "esbuild";
|
|
6
|
+
|
|
7
|
+
const table = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!#$%&()*+,./:;<=>?@[]^_\`{|}~"';
|
|
8
|
+
const DECODE_TABLE = (() => {
|
|
9
|
+
const t = new Int8Array(128).fill(-1);
|
|
10
|
+
for (let i = 0; i < table.length; ++i) {
|
|
11
|
+
t[table.charCodeAt(i)] = i;
|
|
12
|
+
}
|
|
13
|
+
return t;
|
|
14
|
+
})();
|
|
15
|
+
|
|
16
|
+
function tpl(wasmJsPath: string, base91Wasm: string, base91CompressedWasm: string, wasmSize: number, compressedSize: number) {
|
|
17
|
+
|
|
18
|
+
const compressed = (base91CompressedWasm.length + 8 * 1024) <= base91Wasm.length;
|
|
19
|
+
const wasmJsExists = existsSync(wasmJsPath);
|
|
20
|
+
|
|
21
|
+
return `\
|
|
22
|
+
${compressed ? 'import { decompress } from "fzstd";' : ""}
|
|
23
|
+
${wasmJsExists ? `import wrapper from "${wasmJsPath}";` : ""}
|
|
24
|
+
|
|
25
|
+
const D = new Int8Array([${DECODE_TABLE.join(",")}]);
|
|
26
|
+
|
|
27
|
+
function decode(s: string): Uint8Array {
|
|
28
|
+
const out = new Uint8Array(${compressed ? compressedSize : wasmSize});
|
|
29
|
+
let pos = 0, b = 0, n = 0, v = -1;
|
|
30
|
+
|
|
31
|
+
for (let i = 0, len = s.length; i < len; i++) {
|
|
32
|
+
const c = s.charCodeAt(i);
|
|
33
|
+
if (c > 127) continue;
|
|
34
|
+
const p = D[c];
|
|
35
|
+
if (p < 0) continue;
|
|
36
|
+
if (v < 0) {
|
|
37
|
+
v = p;
|
|
38
|
+
} else {
|
|
39
|
+
v += p * 91;
|
|
40
|
+
b |= v << n;
|
|
41
|
+
n += (v & 8191) > 88 ? 13 : 14;
|
|
42
|
+
do {
|
|
43
|
+
out[pos++] = b;
|
|
44
|
+
b >>>= 8;
|
|
45
|
+
n -= 8;
|
|
46
|
+
} while (n > 7);
|
|
47
|
+
v = -1;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (v >= 0) out[pos++] = (b | v << n) & 0xff;
|
|
52
|
+
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const blobStr = '${compressed ? base91CompressedWasm : base91Wasm}';
|
|
57
|
+
|
|
58
|
+
let g_wasmBinary: Uint8Array | undefined;
|
|
59
|
+
export default function() {
|
|
60
|
+
g_wasmBinary ??= ${compressed ? "decompress(decode(blobStr))" : "decode(blobStr)"};
|
|
61
|
+
${!wasmJsExists ? `\
|
|
62
|
+
return g_wasmBinary;
|
|
63
|
+
`: `\
|
|
64
|
+
return wrapper({
|
|
65
|
+
wasmBinary: g_wasmBinary,
|
|
66
|
+
locateFile: () => ""
|
|
67
|
+
});
|
|
68
|
+
`}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function reset() {
|
|
72
|
+
g_wasmBinary = undefined;
|
|
73
|
+
} `.trim();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
let base91Promise: Promise<Base91> | undefined;
|
|
77
|
+
let zstdPromise: Promise<Zstd> | undefined;
|
|
78
|
+
function getBase91() {
|
|
79
|
+
base91Promise ??= Base91.load();
|
|
80
|
+
return base91Promise;
|
|
81
|
+
}
|
|
82
|
+
function getZstd() {
|
|
83
|
+
zstdPromise ??= Zstd.load();
|
|
84
|
+
return zstdPromise;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export async function wrap(path: string) {
|
|
88
|
+
console.info(`Wrapping: ${path}`);
|
|
89
|
+
const [base91, zstd] = await Promise.all([getBase91(), getZstd()]);
|
|
90
|
+
|
|
91
|
+
console.info(" Reading WASM file...");
|
|
92
|
+
const wasm = await readFile(path);
|
|
93
|
+
const wasmJsPath = path.replace(/\.wasm$/, ".js");
|
|
94
|
+
const CHUNK_SIZE = 64 * 1024 * 1024;
|
|
95
|
+
|
|
96
|
+
const base91Parts: string[] = [];
|
|
97
|
+
base91.reset();
|
|
98
|
+
for (let offset = 0; offset < wasm.length; offset += CHUNK_SIZE) {
|
|
99
|
+
const chunk = wasm.subarray(offset, Math.min(offset + CHUNK_SIZE, wasm.length));
|
|
100
|
+
base91Parts.push(base91.encodeChunk(chunk));
|
|
101
|
+
console.info(` Encoded ${Math.min(offset + CHUNK_SIZE, wasm.length)} / ${wasm.length} bytes`);
|
|
102
|
+
}
|
|
103
|
+
base91Parts.push(base91.encodeChunkEnd());
|
|
104
|
+
const base91Wasm = base91Parts.join("");
|
|
105
|
+
|
|
106
|
+
console.info(" Compressing...");
|
|
107
|
+
const compressedChunks: Uint8Array[] = [];
|
|
108
|
+
zstd.reset();
|
|
109
|
+
for (let offset = 0; offset < wasm.length; offset += CHUNK_SIZE) {
|
|
110
|
+
const chunk = wasm.subarray(offset, Math.min(offset + CHUNK_SIZE, wasm.length));
|
|
111
|
+
compressedChunks.push(zstd.compressChunk(chunk));
|
|
112
|
+
console.info(` Compressed ${Math.min(offset + CHUNK_SIZE, wasm.length)} / ${wasm.length} bytes`);
|
|
113
|
+
}
|
|
114
|
+
compressedChunks.push(zstd.compressEnd());
|
|
115
|
+
const totalLength = compressedChunks.reduce((sum, chunk) => sum + chunk.length, 0);
|
|
116
|
+
const compressedWasm = new Uint8Array(totalLength);
|
|
117
|
+
let compressionOffset = 0;
|
|
118
|
+
for (const chunk of compressedChunks) {
|
|
119
|
+
compressedWasm.set(chunk, compressionOffset);
|
|
120
|
+
compressionOffset += chunk.length;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
console.info(" Encoding compressed...");
|
|
124
|
+
const base91CompressedParts: string[] = [];
|
|
125
|
+
base91.reset();
|
|
126
|
+
for (let offset = 0; offset < compressedWasm.length; offset += CHUNK_SIZE) {
|
|
127
|
+
const chunk = compressedWasm.subarray(offset, Math.min(offset + CHUNK_SIZE, compressedWasm.length));
|
|
128
|
+
base91CompressedParts.push(base91.encodeChunk(chunk));
|
|
129
|
+
console.info(` Encoded ${Math.min(offset + CHUNK_SIZE, compressedWasm.length)} / ${compressedWasm.length} bytes (compressed)`);
|
|
130
|
+
}
|
|
131
|
+
base91CompressedParts.push(base91.encodeChunkEnd());
|
|
132
|
+
const base91CompressedWasm = base91CompressedParts.join("");
|
|
133
|
+
|
|
134
|
+
console.info(" Creating wrapper...");
|
|
135
|
+
return tpl(wasmJsPath, base91Wasm, base91CompressedWasm, wasm.length, compressedWasm.length);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function sfxWasm(): Plugin {
|
|
139
|
+
return {
|
|
140
|
+
name: "sfx-wasm",
|
|
141
|
+
|
|
142
|
+
setup(build: PluginBuild) {
|
|
143
|
+
|
|
144
|
+
build.onLoad({ filter: /\.wasm$/ }, async args => {
|
|
145
|
+
return {
|
|
146
|
+
contents: await wrap(args.path),
|
|
147
|
+
loader: "ts",
|
|
148
|
+
};
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|