mirror of
https://github.com/yume-chan/ya-webadb.git
synced 2025-10-04 18:29:23 +02:00
refactor(struct): tweak type plugin system
This commit is contained in:
parent
b601f7a7f2
commit
9f3d480523
22 changed files with 862 additions and 530 deletions
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
|
@ -24,6 +24,7 @@
|
|||
"hhmm",
|
||||
"hisi",
|
||||
"jmuxer",
|
||||
"keyof",
|
||||
"killforward",
|
||||
"laggy",
|
||||
"lapo",
|
||||
|
@ -46,8 +47,10 @@
|
|||
"tcpip",
|
||||
"tinyh",
|
||||
"tsbuildinfo",
|
||||
"typeof",
|
||||
"uifabric",
|
||||
"webadb",
|
||||
"webpackbar",
|
||||
"websockify",
|
||||
"webusb",
|
||||
"wifi",
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"name": "@yume-chan/adb-backend-web",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"description": "Backend for `@yume-chan/adb` using Web technologies.",
|
||||
"keywords": [
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"name": "@yume-chan/adb",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"description": "TypeScript implementation of Android Debug Bridge (ADB) protocol.",
|
||||
"keywords": [
|
||||
|
@ -34,7 +35,7 @@
|
|||
"dependencies": {
|
||||
"@yume-chan/async-operation-manager": "2.1.3",
|
||||
"@yume-chan/event": "^0.0.1",
|
||||
"@yume-chan/struct": "^0.0.1",
|
||||
"@yume-chan/struct": "^0.0.2",
|
||||
"tslib": "2.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
173
packages/demo/package-lock.json
generated
173
packages/demo/package-lock.json
generated
|
@ -861,6 +861,15 @@
|
|||
"integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-escapes": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz",
|
||||
"integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-fest": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"ansi-html": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
|
||||
|
@ -1232,6 +1241,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"ci-info": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
|
||||
"integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
|
||||
"dev": true
|
||||
},
|
||||
"class-utils": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
|
||||
|
@ -1407,6 +1422,12 @@
|
|||
"integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==",
|
||||
"dev": true
|
||||
},
|
||||
"consola": {
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/consola/-/consola-2.15.0.tgz",
|
||||
"integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ==",
|
||||
"dev": true
|
||||
},
|
||||
"content-disposition": {
|
||||
"version": "0.5.3",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
|
||||
|
@ -2249,6 +2270,15 @@
|
|||
"websocket-driver": ">=0.5.1"
|
||||
}
|
||||
},
|
||||
"figures": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
||||
"integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"escape-string-regexp": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"file-loader": {
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
|
||||
|
@ -4348,6 +4378,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"pretty-time": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz",
|
||||
"integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==",
|
||||
"dev": true
|
||||
},
|
||||
"process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
|
@ -5232,6 +5268,15 @@
|
|||
"integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=",
|
||||
"dev": true
|
||||
},
|
||||
"std-env": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-2.2.1.tgz",
|
||||
"integrity": "sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ci-info": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"streamsaver": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/streamsaver/-/streamsaver-2.0.5.tgz",
|
||||
|
@ -5402,6 +5447,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"text-table": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
|
||||
"dev": true
|
||||
},
|
||||
"thunky": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/thunky/-/thunky-1.1.0.tgz",
|
||||
|
@ -5516,6 +5567,12 @@
|
|||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz",
|
||||
"integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ=="
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz",
|
||||
"integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"type-is": {
|
||||
"version": "1.6.18",
|
||||
"resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
|
||||
|
@ -6454,6 +6511,122 @@
|
|||
"source-map": "~0.6.1"
|
||||
}
|
||||
},
|
||||
"webpackbar": {
|
||||
"version": "5.0.0-3",
|
||||
"resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.0-3.tgz",
|
||||
"integrity": "sha512-viW6KCYjMb0NPoDrw2jAmLXU2dEOhRrtku28KmOfeE1vxbfwCYuTbTaMhnkrCZLFAFyY9Q49Z/jzYO80Dw5b8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-escapes": "^4.3.1",
|
||||
"chalk": "^4.1.0",
|
||||
"consola": "^2.15.0",
|
||||
"figures": "^3.2.0",
|
||||
"pretty-time": "^1.1.0",
|
||||
"std-env": "^2.2.1",
|
||||
"text-table": "^0.2.0",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"websocket-driver": {
|
||||
"version": "0.6.5",
|
||||
"resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.6.5.tgz",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "demo",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"description": "Demo of `@yume-chan/adb` and `@yume-chan/adb-backend-web`.",
|
||||
"author": "Simon Chan <cnsimonchan@live.com>",
|
||||
"homepage": "https://github.com/yume-chan/ya-webadb#readme",
|
||||
|
@ -37,6 +37,7 @@
|
|||
"webpack-bundle-analyzer": "4.3.0",
|
||||
"webpack-cli": "4.2.0",
|
||||
"webpack-dev-server": "3.11.0",
|
||||
"webpackbar": "5.0.0-3",
|
||||
"worker-loader": "3.0.7"
|
||||
},
|
||||
"dependencies": {
|
||||
|
@ -47,7 +48,7 @@
|
|||
"@yume-chan/adb-backend-web": "^0.0.1",
|
||||
"@yume-chan/async-operation-manager": "2.1.3",
|
||||
"@yume-chan/event": "^0.0.1",
|
||||
"@yume-chan/struct": "^0.0.1",
|
||||
"@yume-chan/struct": "^0.0.2",
|
||||
"jmuxer": "2.0.0",
|
||||
"path-browserify": "1.0.1",
|
||||
"react": "17.0.1",
|
||||
|
|
|
@ -12,6 +12,18 @@ import { createTinyH264Decoder, TinyH264Decoder } from './tinyh264';
|
|||
|
||||
const DeviceServerPath = '/data/local/tmp/scrcpy-server.jar';
|
||||
|
||||
function clamp(value: number, min: number, max: number): number {
|
||||
if (value < min) {
|
||||
return min;
|
||||
}
|
||||
|
||||
if (value > max) {
|
||||
return max;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
export const Scrcpy = withDisplayName('Scrcpy')(({
|
||||
device
|
||||
}: RouteProps): JSX.Element | null => {
|
||||
|
@ -283,14 +295,11 @@ export const Scrcpy = withDisplayName('Scrcpy')(({
|
|||
action: AndroidMotionEventAction,
|
||||
e: React.PointerEvent<HTMLCanvasElement>
|
||||
) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const view = e.currentTarget.getBoundingClientRect();
|
||||
const view = canvasRef.current!.getBoundingClientRect();
|
||||
const pointerViewX = e.clientX - view.x;
|
||||
const pointerViewY = e.clientY - view.y;
|
||||
const pointerScreenX = pointerViewX / view.width * width;
|
||||
const pointerScreenY = pointerViewY / view.height * height;
|
||||
const pointerScreenX = clamp(pointerViewX / view.width, 0, 1) * width;
|
||||
const pointerScreenY = clamp(pointerViewY / view.height, 0, 1) * height;
|
||||
|
||||
scrcpyClientRef.current?.injectTouch({
|
||||
action,
|
||||
|
@ -307,6 +316,7 @@ export const Scrcpy = withDisplayName('Scrcpy')(({
|
|||
return;
|
||||
}
|
||||
canvasRef.current!.focus();
|
||||
e.currentTarget.setPointerCapture(e.pointerId);
|
||||
injectTouch(AndroidMotionEventAction.Down, e);
|
||||
}, [injectTouch]);
|
||||
|
||||
|
@ -321,6 +331,7 @@ export const Scrcpy = withDisplayName('Scrcpy')(({
|
|||
if (e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
e.currentTarget.releasePointerCapture(e.pointerId);
|
||||
injectTouch(AndroidMotionEventAction.Up, e);
|
||||
}, [injectTouch]);
|
||||
|
||||
|
@ -406,6 +417,7 @@ export const Scrcpy = withDisplayName('Scrcpy')(({
|
|||
onPointerDown={handlePointerDown}
|
||||
onPointerMove={handlePointerMove}
|
||||
onPointerUp={handlePointerUp}
|
||||
onPointerCancel={handlePointerUp}
|
||||
onKeyDown={handleKeyDown}
|
||||
/>
|
||||
</DeviceView>
|
||||
|
|
|
@ -6,9 +6,10 @@ var html_webpack_plugin_1 = tslib_1.__importDefault(require("html-webpack-plugin
|
|||
var mini_css_extract_plugin_1 = tslib_1.__importDefault(require("mini-css-extract-plugin"));
|
||||
var path_1 = tslib_1.__importDefault(require("path"));
|
||||
var webpack_bundle_analyzer_1 = require("webpack-bundle-analyzer");
|
||||
var webpackbar_1 = tslib_1.__importDefault(require("webpackbar"));
|
||||
var context = path_1.default.resolve(process.cwd());
|
||||
var plugins = [
|
||||
new clean_webpack_plugin_1.CleanWebpackPlugin(),
|
||||
new webpackbar_1.default({}),
|
||||
new mini_css_extract_plugin_1.default({
|
||||
filename: '[name].[contenthash].css',
|
||||
}),
|
||||
|
@ -29,34 +30,40 @@ var plugins = [
|
|||
if (process.env.ANALYZE) {
|
||||
plugins.push(new webpack_bundle_analyzer_1.BundleAnalyzerPlugin());
|
||||
}
|
||||
var config = function (env, argv) { return ({
|
||||
mode: 'development',
|
||||
devtool: argv.mode === 'production' ? 'source-map' : 'eval-source-map',
|
||||
context: context,
|
||||
target: 'web',
|
||||
entry: {
|
||||
index: './src/index.tsx',
|
||||
},
|
||||
output: {
|
||||
path: path_1.default.resolve(context, 'lib'),
|
||||
filename: '[name].[contenthash].js',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js'],
|
||||
fallback: { "path": require.resolve("path-browserify") },
|
||||
},
|
||||
plugins: plugins,
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.js$/, enforce: 'pre', use: ['source-map-loader'], },
|
||||
{ test: /\.css$/i, use: [mini_css_extract_plugin_1.default.loader, 'css-loader'] },
|
||||
{ test: /\.asset$/, use: { loader: "file-loader" } },
|
||||
{ test: /\.tsx?$/i, loader: 'ts-loader', options: { projectReferences: true } },
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
contentBase: path_1.default.resolve(context, 'lib'),
|
||||
port: 9000,
|
||||
},
|
||||
}); };
|
||||
var config = function (env, argv) {
|
||||
if (argv.mode !== 'production') {
|
||||
plugins.unshift(new clean_webpack_plugin_1.CleanWebpackPlugin());
|
||||
}
|
||||
return {
|
||||
mode: 'development',
|
||||
devtool: argv.mode === 'production' ? 'source-map' : 'eval-source-map',
|
||||
context: context,
|
||||
target: 'web',
|
||||
entry: {
|
||||
index: './src/index.tsx',
|
||||
},
|
||||
output: {
|
||||
path: path_1.default.resolve(context, 'lib'),
|
||||
filename: '[name].[contenthash].js',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js'],
|
||||
// @ts-expect-error typing is not up to date
|
||||
fallback: { "path": require.resolve("path-browserify") },
|
||||
},
|
||||
plugins: plugins,
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.js$/, enforce: 'pre', use: ['source-map-loader'], },
|
||||
{ test: /\.css$/i, use: [mini_css_extract_plugin_1.default.loader, 'css-loader'] },
|
||||
{ test: /\.asset$/, use: { loader: "file-loader" } },
|
||||
{ test: /\.tsx?$/i, loader: 'ts-loader', options: { projectReferences: true } },
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
contentBase: path_1.default.resolve(context, 'lib'),
|
||||
port: 9000,
|
||||
},
|
||||
};
|
||||
};
|
||||
module.exports = config;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"name": "@yume-chan/event",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"description": "Event/EventEmitter",
|
||||
"keywords": [
|
||||
|
|
|
@ -4,30 +4,42 @@ C-style structure serializer and deserializer.
|
|||
|
||||
Fully compatible with TypeScript.
|
||||
|
||||
- [Compatibility](#compatibility)
|
||||
- [Quick Start](#quick-start)
|
||||
- [Compatibility](#compatibility)
|
||||
- [API](#api)
|
||||
- [`placeholder` method](#placeholder-method)
|
||||
- [`Struct` constructor](#struct-constructor)
|
||||
- [`Struct#uint8`/`uint16`/`uint32`/`int32` methods](#structuint8uint16uint32int32-methods)
|
||||
- [`Struct#uint64`/`int64` method](#structuint64int64-method)
|
||||
- [`Struct#fields` method](#structfields-method)
|
||||
- [`Struct#uint8`/`uint16`/`int32`/`uint32` methods](#structuint8uint16int32uint32-methods)
|
||||
- [`Struct#int64`/`uint64` methods](#structint64uint64-methods)
|
||||
- [`extra` function](#extra-function)
|
||||
- [`afterParsed` method](#afterparsed-method)
|
||||
- [`postDeserialize` method](#postdeserialize-method)
|
||||
- [`deserialize` method](#deserialize-method)
|
||||
- [`serialize` method](#serialize-method)
|
||||
- [Extend types](#extend-types)
|
||||
- [Backing Field](#backing-field)
|
||||
- [`FieldDescriptorBase` interface](#fielddescriptorbase-interface)
|
||||
- [`field` method](#field-method)
|
||||
- [`FieldTypeDefinition` interface](#fieldtypedefinition-interface)
|
||||
- [`deserialize` method](#deserialize-method-1)
|
||||
- [`getSize` method](#getsize-method)
|
||||
- [`getDynamicSize` method](#getdynamicsize-method)
|
||||
- [`initialize` method](#initialize-method)
|
||||
- [`serialize` method](#serialize-method-1)
|
||||
- [Array type](#array-type)
|
||||
- [`registerFieldTypeDefinition` method](#registerfieldtypedefinition-method)
|
||||
- [Data flow](#data-flow)
|
||||
- [Custom types](#custom-types)
|
||||
- [`Struct#field` method](#structfield-method)
|
||||
- [FieldDefinition](#fielddefinition)
|
||||
- [`FieldDefinition#getSize` method](#fielddefinitiongetsize-method)
|
||||
- [`FieldDefinition#deserialize` method](#fielddefinitiondeserialize-method)
|
||||
- [`FieldDefinition#createValue` method](#fielddefinitioncreatevalue-method)
|
||||
- [FieldRuntimeValue](#fieldruntimevalue)
|
||||
|
||||
## Quick Start
|
||||
|
||||
```ts
|
||||
import Struct from '@yume-chan/struct';
|
||||
|
||||
const MyStruct =
|
||||
new Struct({ littleEndian: true })
|
||||
.int32('foo')
|
||||
.int32('bar');
|
||||
|
||||
const value = await MyStruct.deserialize(someStream);
|
||||
// TypeScript can infer type of the result object.
|
||||
const { foo, bar } = value;
|
||||
|
||||
const buffer = MyStruct.serialize({ foo, bar });
|
||||
```
|
||||
|
||||
## Compatibility
|
||||
|
||||
|
@ -47,7 +59,7 @@ Basic usage requires [`Promise`][MDN_Promise], [`ArrayBuffer`][MDN_ArrayBuffer],
|
|||
| **Safari** | 8 | |
|
||||
| **Node.js** | 0.12 | |
|
||||
|
||||
Usage of `uint64` requires [`BigInt`][MDN_BigInt] (**can't** be polyfilled), [`DataView#getBigUint64`][MDN_DataView_getBigUint64] and [`DataView#setBigUint64`][MDN_DataView_setBigUint64] (can be polyfilled).
|
||||
Usage of `int64`/`uint64` requires [`BigInt`][MDN_BigInt] (**can't** be polyfilled), [`DataView#getBigUint64`][MDN_DataView_getBigUint64] and [`DataView#setBigUint64`][MDN_DataView_setBigUint64] (can be polyfilled).
|
||||
|
||||
[MDN_BigInt]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
|
||||
[MDN_DataView_getBigUint64]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DataView/getBigUint64
|
||||
|
@ -62,21 +74,6 @@ Usage of `uint64` requires [`BigInt`][MDN_BigInt] (**can't** be polyfilled), [`D
|
|||
| **Safari** | 14 | Requires polyfills for `DataView#getBigUint64`/`DataView#setBigUint64` |
|
||||
| **Node.js** | 10.4.0 | |
|
||||
|
||||
## Quick Start
|
||||
|
||||
```ts
|
||||
import Struct from '@yume-chan/struct';
|
||||
|
||||
const MyStruct =
|
||||
new Struct({ littleEndian: true })
|
||||
.int32('foo')
|
||||
.int32('bar');
|
||||
|
||||
const value = MyStruct.deserialize(someStream);
|
||||
// TypeScript can infer type of the result object.
|
||||
const { foo, bar } = value;
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
### `placeholder` method
|
||||
|
@ -89,20 +86,18 @@ export function placeholder<T>(): T {
|
|||
|
||||
Return a (fake) value of the given type.
|
||||
|
||||
Because TypeScript only supports supply all or none type arguments, this library allows all type parameters to be inferred from arguments.
|
||||
|
||||
This method can be used where an argument is only used to infer a type parameter.
|
||||
Because TypeScript only supports supply all or none type arguments, this method allows all type parameters to be inferred from arguments.
|
||||
|
||||
**While all following APIs heavily rely on generic, DO NOT PASS ANY GENERIC ARGUMENTS MANUALLY!**
|
||||
|
||||
### `Struct` constructor
|
||||
|
||||
```ts
|
||||
export default class Struct<
|
||||
TResult extends object = {},
|
||||
class Struct<
|
||||
TValue extends object = {},
|
||||
TInit extends object = {},
|
||||
TExtra extends object = {},
|
||||
TAfterParsed = undefined,
|
||||
TPostDeserialized = undefined
|
||||
> {
|
||||
public constructor(options: Partial<StructOptions> = StructDefaultOptions);
|
||||
}
|
||||
|
@ -112,10 +107,10 @@ Creates a new structure definition.
|
|||
|
||||
**Generic Parameters**
|
||||
|
||||
1. `TResult`: Type of the result object.
|
||||
2. `TInit`: Type requirement to create such a structure. (Because some fields may implies other fields)
|
||||
1. `TValue`: Type of the Struct instance.
|
||||
2. `TInit`: Type requirement to create such a structure. (May not be same as `TValue` because some fields can implies others)
|
||||
3. `TExtra`: Type of extra fields.
|
||||
4. `TAfterParsed`: State of the `afterParsed` function.
|
||||
4. `TPostDeserialized`: State of the `postDeserialize` function.
|
||||
|
||||
**DO NOT PASS ANY GENERIC ARGUMENTS MANUALLY!**
|
||||
|
||||
|
@ -124,52 +119,90 @@ These are considered "internal state" of the `Struct` and will be taken care of
|
|||
**Parameters**
|
||||
|
||||
1. `options`:
|
||||
* `littleEndian:boolean = false`: Whether all multi-byte fields are [little-endian encoded][Wikipeida_Endianess].
|
||||
* `littleEndian:boolean = false`: Whether all multi-byte fields in this struct are [little-endian encoded][Wikipeida_Endianess].
|
||||
|
||||
[Wikipeida_Endianess]: https://en.wikipedia.org/wiki/Endianness
|
||||
|
||||
### `Struct#uint8`/`uint16`/`uint32`/`int32` methods
|
||||
### `Struct#fields` method
|
||||
|
||||
```ts
|
||||
public int32<
|
||||
TName extends string,
|
||||
TTypeScriptType = number
|
||||
fields<
|
||||
TOther extends Struct<any, any, any, any>
|
||||
>(
|
||||
name: TName,
|
||||
options: FieldDescriptorBaseOptions = {},
|
||||
_typescriptType?: TTypeScriptType,
|
||||
struct: TOther
|
||||
): Struct<
|
||||
TResult & Record<TName, TTypeScriptType>,
|
||||
TInit & Record<TName, TTypeScriptType>,
|
||||
TExtra,
|
||||
TAfterParsed,
|
||||
>;
|
||||
|
||||
public uint32<
|
||||
TName extends string,
|
||||
TTypeScriptType = number
|
||||
>(
|
||||
name: TName,
|
||||
options: {} = {},
|
||||
_typescriptType?: TTypeScriptType,
|
||||
): Struct<
|
||||
TResult & Record<TName, TTypeScriptType>,
|
||||
TInit & Record<TName, TTypeScriptType>,
|
||||
TExtra,
|
||||
TAfterParsed,
|
||||
TValue & TOther['valueType'],
|
||||
TInit & TOther['initType'],
|
||||
TExtra & TOther['extraType'],
|
||||
TPostDeserialized
|
||||
>;
|
||||
```
|
||||
|
||||
Return a new `Struct` instance with an `int32`/`uint32` field appended to the end.
|
||||
Merges (flats) another `Struct`'s fields and extra fields into this one.
|
||||
|
||||
The original `Struct` instance will not be changed.
|
||||
**Examples**
|
||||
|
||||
TypeScript will also append a `name: TTypeScriptType` field into the result object and the init object.
|
||||
1. Extending another `Struct`
|
||||
|
||||
```ts
|
||||
const MyStructV1 =
|
||||
new Struct()
|
||||
.int32('field1');
|
||||
|
||||
const MyStructV2 =
|
||||
new Struct()
|
||||
.fields(MyStructV1)
|
||||
.int32('field2');
|
||||
|
||||
const structV2 = await MyStructV2.deserialize(context);
|
||||
structV2.field1; // number
|
||||
structV2.field2; // number
|
||||
// Same result, but serialize/deserialize order is reversed
|
||||
```
|
||||
|
||||
2. Also possible in any order
|
||||
|
||||
```ts
|
||||
const MyStructV1 =
|
||||
new Struct()
|
||||
.int32('field1');
|
||||
|
||||
const MyStructV2 =
|
||||
new Struct()
|
||||
.int32('field2')
|
||||
.fields(MyStructV1);
|
||||
|
||||
const structV2 = await MyStructV2.deserialize(context);
|
||||
structV2.field1; // number
|
||||
structV2.field2; // number
|
||||
// Fields are flatten
|
||||
```
|
||||
|
||||
### `Struct#uint8`/`uint16`/`int32`/`uint32` methods
|
||||
|
||||
```ts
|
||||
int32<
|
||||
TName extends PropertyKey,
|
||||
TTypeScriptType = number
|
||||
>(
|
||||
name: TName,
|
||||
_typescriptType?: TTypeScriptType
|
||||
): Struct<
|
||||
Evaluate<TValue & Record<TName, TTypeScriptType>>,
|
||||
Evaluate<TInit & Record<TName, TTypeScriptType>>,
|
||||
TExtra,
|
||||
TPostDeserialized
|
||||
>;
|
||||
```
|
||||
|
||||
(All method signatures are same)
|
||||
|
||||
Appends an `uint8`/`uint16`/`int32`/`uint32` field to the `Struct`
|
||||
|
||||
**Generic Parameters**
|
||||
|
||||
1. `TName`: Literal type of the field's name.
|
||||
2. `TTypeScriptType = number`: Type of the field in the result object.
|
||||
2. `TTypeScriptType = number`: Type of the field in the result object. For example you can declare it as a number literal type, or some enum type.
|
||||
|
||||
**DO NOT PASS ANY GENERIC ARGUMENTS MANUALLY!**
|
||||
|
||||
|
@ -178,7 +211,6 @@ TypeScript will infer them from arguments. See examples below.
|
|||
**Parameters**
|
||||
|
||||
1. `name`: (Required) Field name. Should be a string literal to make types work.
|
||||
1. `options`: currently unused.
|
||||
2. `_typescriptType`: Set field's type. See examples below.
|
||||
|
||||
**Note**
|
||||
|
@ -226,67 +258,62 @@ But obviously, it's a bad idea.
|
|||
struct.create({ foo: MyEnum.a, bar: MyEnum.b }); // ok
|
||||
```
|
||||
|
||||
3. Create a new struct by extending existing one
|
||||
|
||||
```ts
|
||||
const struct1 = new Struct()
|
||||
.int32('foo');
|
||||
|
||||
const struct2 = struct1
|
||||
.int32('bar');
|
||||
|
||||
assert(struct2 !== struct1);
|
||||
// `struct1` will not be changed
|
||||
```
|
||||
|
||||
### `Struct#uint64`/`int64` method
|
||||
### `Struct#int64`/`uint64` methods
|
||||
|
||||
```ts
|
||||
public uint64<
|
||||
TName extends string,
|
||||
int64<
|
||||
TName extends PropertyKey,
|
||||
TTypeScriptType = bigint
|
||||
>(
|
||||
name: TName,
|
||||
options: FieldDescriptorBaseOptions = {},
|
||||
_typescriptType?: TTypeScriptType,
|
||||
_typescriptType?: TTypeScriptType
|
||||
): Struct<
|
||||
TResult & Record<TName, TTypeScriptType>,
|
||||
TInit & Record<TName, TTypeScriptType>,
|
||||
Evaluate<TValue & Record<TName, TTypeScriptType>>,
|
||||
Evaluate<TInit & Record<TName, TTypeScriptType>>,
|
||||
TExtra,
|
||||
TAfterParsed,
|
||||
TPostDeserialized
|
||||
>;
|
||||
```
|
||||
|
||||
Return a new `Struct` instance with an `uint64` field appended to the end.
|
||||
Appends an `int64`/`uint64` field to the `Struct`.
|
||||
|
||||
The original `Struct` instance will not be changed.
|
||||
|
||||
TypeScript will also append a `name: TTypeScriptType` field into the result object and the init object.
|
||||
|
||||
Require native `BigInt` support in runtime. See [compatibility](#compatibility).
|
||||
Requires native `BigInt` support of runtime. See [compatibility](#compatibility).
|
||||
|
||||
### `extra` function
|
||||
|
||||
```ts
|
||||
public extra<TValue extends object>(
|
||||
value: TValue & ThisType<WithBackingField<Overwrite<Overwrite<TExtra, TValue>, TResult>>>
|
||||
extra<
|
||||
T extends Record<
|
||||
Exclude<
|
||||
keyof T,
|
||||
Exclude<
|
||||
keyof T,
|
||||
keyof TValue
|
||||
>
|
||||
>,
|
||||
never
|
||||
>
|
||||
>(
|
||||
value: T & ThisType<Overwrite<Overwrite<TExtra, T>, TValue>>
|
||||
): Struct<
|
||||
TResult,
|
||||
TValue,
|
||||
TInit,
|
||||
Overwrite<TExtra, TValue>,
|
||||
TAfterParsed
|
||||
Overwrite<TExtra, T>,
|
||||
TPostDeserialized
|
||||
>;
|
||||
```
|
||||
|
||||
Return a new `Struct` instance adding some extra fields.
|
||||
Adds some extra fields into every Struct instance.
|
||||
|
||||
The original `Struct` instance will not be changed.
|
||||
Extra fields will not affect serialize or deserialize process.
|
||||
|
||||
TypeScript will also append all extra fields into the result object (if not already exited).
|
||||
Multiple calls to `extra` will merge all values together.
|
||||
|
||||
See examples below.
|
||||
|
||||
**Generic Parameters**
|
||||
|
||||
1. `TValue`: Type of the extra fields.
|
||||
1. `T`: Type of the extra fields. The scary looking generic constraint is used to forbid overwriting any already existed fields.
|
||||
|
||||
**DO NOT PASS ANY GENERIC ARGUMENTS MANUALLY!**
|
||||
|
||||
|
@ -299,8 +326,6 @@ TypeScript will infer them from arguments. See examples below.
|
|||
**Note**
|
||||
|
||||
1. If the current `Struct` already has some extra fields, it will be merged with `value`, with `value` taking precedence.
|
||||
2. Extra fields will not be serialized.
|
||||
3. Extra fields will be ignored if it has the same name with some defined fields.
|
||||
|
||||
**Examples**
|
||||
|
||||
|
@ -343,31 +368,30 @@ TypeScript will infer them from arguments. See examples below.
|
|||
value.logBar();
|
||||
```
|
||||
|
||||
### `afterParsed` method
|
||||
### `postDeserialize` method
|
||||
|
||||
```ts
|
||||
public afterParsed(
|
||||
callback?: StructAfterParsed<TResult, void>
|
||||
): Struct<TResult, TInit, TExtra, undefined>;
|
||||
postDeserialize(callback: StructPostDeserialized<TValue, never>): Struct<TValue, TInit, TExtra, never>;
|
||||
postDeserialize(callback?: StructPostDeserialized<TValue, void>): Struct<TValue, TInit, TExtra, undefined>;
|
||||
```
|
||||
|
||||
Return a new `Struct` instance, registering (or replacing) a custom callback to be run after deserialized.
|
||||
Registers (or replaces) a custom callback to be run after deserialized.
|
||||
|
||||
The original `Struct` instance will not be changed.
|
||||
A callback returning `never` (always throw an error) will also change the return type of `deserialize` to `never`.
|
||||
|
||||
A callback returning `void` means it modify the result object in-place (or doesn't modify it at all), so `deserialize` will still return the result object.
|
||||
|
||||
```ts
|
||||
public afterParsed<TAfterParsed>(
|
||||
callback?: StructAfterParsed<TResult, TAfterParsed>
|
||||
): Struct<TResult, TInit, TExtra, TAfterParsed>;
|
||||
postDeserialize<TPostSerialize>(callback?: StructPostDeserialized<TValue, TPostSerialize>): Struct<TValue, TInit, TExtra, TPostSerialize>;
|
||||
```
|
||||
|
||||
Return a new `Struct` instance, registering (or replacing) a custom callback to be run after deserialized, and replacing the result object with the returned value.
|
||||
Registers (or replaces) a custom callback to be run after deserialized.
|
||||
|
||||
The original `Struct` instance will not be changed.
|
||||
A callback returning anything other than `undefined` will `deserialize` to return that object instead.
|
||||
|
||||
**Generic Parameters**
|
||||
|
||||
1. `TAfterParsed`: Type of the new result object.
|
||||
1. `TPostSerialize`: Type of the new result object.
|
||||
|
||||
**DO NOT PASS ANY GENERIC ARGUMENTS MANUALLY!**
|
||||
|
||||
|
@ -389,7 +413,7 @@ TypeScript will infer them from arguments. See examples below.
|
|||
const struct = new Struct()
|
||||
.int32('messageLength')
|
||||
.string('message', { lengthField: 'messageLength' })
|
||||
.afterParsed(value => {
|
||||
.postDeserialize(value => {
|
||||
throw new Error(value.message);
|
||||
});
|
||||
```
|
||||
|
@ -400,28 +424,12 @@ TypeScript will infer them from arguments. See examples below.
|
|||
// I think this one doesn't need any code example
|
||||
```
|
||||
|
||||
3. Clear a previously set `afterParsed` callback
|
||||
|
||||
```ts
|
||||
// Most used with extending structures
|
||||
|
||||
const struct1 = new Struct()
|
||||
.int32('foo')
|
||||
.afterParsed(value => {
|
||||
// do something
|
||||
});
|
||||
|
||||
const struct2 = struct1
|
||||
.afterParsed() // don't inherit `struct1`'s `afterParsed`
|
||||
.int32('bar');
|
||||
```
|
||||
|
||||
4. Replace result object
|
||||
3. Replace result object
|
||||
|
||||
```ts
|
||||
const struct1 = new Struct()
|
||||
.int32('foo')
|
||||
.afterParsed(value => {
|
||||
.postDeserialize(value => {
|
||||
return {
|
||||
bar: value.foo,
|
||||
};
|
||||
|
@ -434,14 +442,12 @@ TypeScript will infer them from arguments. See examples below.
|
|||
### `deserialize` method
|
||||
|
||||
```ts
|
||||
public async deserialize(
|
||||
context: StructDeserializationContext
|
||||
): Promise<TAfterParsed extends undefined ? Overwrite<TExtra, TResult> : TAfterParsed>;
|
||||
deserialize(context: StructDeserializationContext): Promise<TPostDeserialized extends undefined ? Overwrite<TExtra, TValue> : TPostDeserialized>;
|
||||
```
|
||||
|
||||
Deserialize one structure from the `context`.
|
||||
Deserialize a Struct instance from `context`.
|
||||
|
||||
As you can see, if your `afterParsed` callback returns a value, that value will be returned by `deserialize`. Or the result object will be returned.
|
||||
As you can see, if your `postDeserialize` callback returns something, that value will be returned by `deserialize`.
|
||||
|
||||
### `serialize` method
|
||||
|
||||
|
@ -449,214 +455,97 @@ As you can see, if your `afterParsed` callback returns a value, that value will
|
|||
public serialize(init: TInit, context: StructSerializationContext): ArrayBuffer;
|
||||
```
|
||||
|
||||
Serialize a value as the structure.
|
||||
Serialize a Struct instance into an `ArrayBuffer`.
|
||||
|
||||
## Extend types
|
||||
## Custom types
|
||||
|
||||
The library also supports adding custom types.
|
||||
It also supports adding custom types.
|
||||
|
||||
There are two concepts around the type plugin system.
|
||||
|
||||
### Backing Field
|
||||
|
||||
The result object has a hidden backing field, containing implementation details of each field.
|
||||
### `Struct#field` method
|
||||
|
||||
```ts
|
||||
import { getBackingField, setBackingField } from '@yume-chan/struct';
|
||||
|
||||
const value = getBackingField<number>(resultObject, 'foo');
|
||||
setBackingField(resultObject, 'foo', value);
|
||||
field<
|
||||
TName extends PropertyKey,
|
||||
TDefinition extends FieldDefinition<any, any, any>
|
||||
>(
|
||||
name: TName,
|
||||
definition: TDefinition
|
||||
): Struct<
|
||||
Evaluate<TValue & Record<TFieldName, TDefinition['valueType']>>,
|
||||
Evaluate<Omit<TInit, TDefinition['removeFields']> & Record<TFieldName, TDefinition['valueType']>>,
|
||||
TExtra,
|
||||
TPostDeserialized
|
||||
>;
|
||||
```
|
||||
|
||||
It's possible to access other fields' data if you know the type. But it's not recommended to modify them.
|
||||
Appends a `FieldDefinition` to the `Struct.
|
||||
|
||||
### `FieldDescriptorBase` interface
|
||||
All above built-in methods are all alias to this method.
|
||||
|
||||
This interface describes one field, and will be stored in `Struct` class.
|
||||
|
||||
**Generic Parameters**
|
||||
|
||||
* `TName extends string = string`: Name of the field. Although `FieldDescriptorBase` doesn't need it to be generic, derived types will need it. So marking this way helps TypeScript infer the type.
|
||||
* `TResultObject = {}`: Type that will be merged into the result object (`TResult`). Any key that has `never` type will be removed.
|
||||
* `TInitObject = {}`: Type that will be merged into the init object (`TInit`). Any key that has `never` type will be removed. Normally you only need to add the current field into `TInit`, but sometimes one field will imply other fields, so you may want to also remove those implied fields from `TInit`.
|
||||
* `TOptions extends FieldDescriptorBaseOptions = FieldDescriptorBaseOptions`: Type of the `options`. currently `FieldDescriptorBaseOptions` is empty but maybe something will happen later.
|
||||
|
||||
When declaring your own field descriptor, you need to extend `FieldDescriptorBase`, and correctly pass all generic arguments.
|
||||
|
||||
**Fields**
|
||||
|
||||
* `type: string`: The unique identifier of the type.
|
||||
* `name: TName`: Field name in the result object.
|
||||
* `options: TOptions`: You can store your options here.
|
||||
* `resultObject?: TResultObject`: Make it possible for TypeScript to infer `TResultObject`. DO NOT TOUCH.
|
||||
* `initObject?: TInitObject`: Make it possible for TypeScript to infer `TInitObject`. DO NOT TOUCH.
|
||||
|
||||
When declaring your own field descriptor, you can also add more fields to hold your data.
|
||||
|
||||
### `field` method
|
||||
|
||||
`Struct` class also has a `field` method to add a custom field descriptor.
|
||||
|
||||
Due to the limitation of TypeScript, you can't extend `Struct` class while keeping the fluent style API working.
|
||||
|
||||
So for type safety you should provide a function to generate your own field descriptor, then let the user call the `field` method.
|
||||
|
||||
### `FieldTypeDefinition` interface
|
||||
|
||||
This interface defines how to serialize and deserialize a type. You need to implement this interface for your type and register it.
|
||||
|
||||
**Generic Parameters**
|
||||
|
||||
* `TDescriptor extends FieldDescriptorBase = FieldDescriptorBase`: Type of the field descriptor. Just pass in your own field descriptor type.
|
||||
|
||||
**Fields**
|
||||
|
||||
* `type: string`: The unique identifier of the type. Make sure it's same as in `FieldDescriptor`.
|
||||
|
||||
#### `deserialize` method
|
||||
### FieldDefinition
|
||||
|
||||
```ts
|
||||
deserialize(options: {
|
||||
context: StructDeserializationContext;
|
||||
field: TDescriptor;
|
||||
object: any;
|
||||
options: StructOptions;
|
||||
}): Promise<void>;
|
||||
```
|
||||
abstract class FieldDefinition<TOptions = void, TValueType = unknown, TRemoveFields = never> {
|
||||
readonly options: TOptions;
|
||||
|
||||
Defines how to deserialize the field.
|
||||
|
||||
You should `read` data from the `context` according to your `field` descriptor, and set appropriate values onto `object` ("appropriate" means "same as `TDescriptor`'s `TResultObject`").
|
||||
|
||||
If you also defined `initialize` method, the result data shape of `object` should be same as the result of `initialize`.
|
||||
|
||||
#### `getSize` method
|
||||
|
||||
```ts
|
||||
getSize(options: {
|
||||
field: TDescriptor;
|
||||
options: StructOptions;
|
||||
}): number;
|
||||
```
|
||||
|
||||
Get the size (in bytes) of the field.
|
||||
|
||||
If the size is (partially or fully) dynamic, returns the minimal size.
|
||||
|
||||
It's just a hint for how much data should be ready before parsing, not that important.
|
||||
|
||||
#### `getDynamicSize` method
|
||||
|
||||
```ts
|
||||
getDynamicSize?(options: {
|
||||
context: StructSerializationContext,
|
||||
field: TDescriptor,
|
||||
object: any,
|
||||
options: StructOptions,
|
||||
}): number;
|
||||
```
|
||||
|
||||
Similar to `getSize`, but also has access to `object` and `context` so the actual size can be calculated.
|
||||
|
||||
This method will be called just before `serialize`, so you can also prepare your field to be serialized in it.
|
||||
|
||||
You can also modify `object` to store your lazily evaluated values so next serialization can reuse them. But make sure you have also defined a setter in `initialize` method to invalidate the cache.
|
||||
|
||||
#### `initialize` method
|
||||
|
||||
```ts
|
||||
initialize?(options: {
|
||||
context: StructSerializationContext;
|
||||
field: TDescriptor;
|
||||
init: any;
|
||||
object: any;
|
||||
options: StructOptions;
|
||||
}): void;
|
||||
```
|
||||
|
||||
When creating or serializing an object, you can fine tune how to map fields from `init` object onto the result `object`.
|
||||
|
||||
You can modify the `object` as your wish, but a common practice is storing actual data on the backing field and define getter/setter on `object` to access them. Because fields may be overwritten by `extra` fields, where data on the backing field is still useful.
|
||||
|
||||
```ts
|
||||
initialize({ field, init, object }) {
|
||||
object[BackingField][field.name] = {
|
||||
value: init[field.name],
|
||||
...extraData,
|
||||
};
|
||||
|
||||
Object.defineProperty(object, field.name, {
|
||||
configurable: true,
|
||||
enumerable: true,
|
||||
get() { return object[BackingField][field.name].value; }
|
||||
set(value) {
|
||||
object[BackingField][field.name].value = value;
|
||||
// set some other data
|
||||
}
|
||||
});
|
||||
constructor(options: TOptions);
|
||||
}
|
||||
```
|
||||
|
||||
If omitted, value from `init` will be set into the backing field and a pair of simple getter/setter will be defined on `object`.
|
||||
A `FieldDefinition` defines its type, size, etc.
|
||||
|
||||
Some possible usages:
|
||||
It's an `abstract` class, means it lacks some method implementations, so it shouldn't be constructed.
|
||||
|
||||
1. Do some calculations and then set it onto `object`.
|
||||
2. Define getter/setter onto `object` to intercept read/write.
|
||||
3. Maybe one field implies others, so you can define multiple fields onto `object` for a single `field`.
|
||||
To create a custom type, one should create its own derived classes of `FieldDefinition` and `FieldRuntimeValue`.
|
||||
|
||||
#### `serialize` method
|
||||
The custom `FieldDefinition` then can be passed to `Struct#field` method to append such a custom type field.
|
||||
|
||||
### `FieldDefinition#getSize` method
|
||||
|
||||
```ts
|
||||
serialize(options: {
|
||||
context: StructSerializationContext;
|
||||
dataView: DataView;
|
||||
field: TDescriptor;
|
||||
object: any;
|
||||
offset: number;
|
||||
options: StructOptions;
|
||||
}): void;
|
||||
abstract getSize(): number;
|
||||
```
|
||||
|
||||
Defines how to serialize the field.
|
||||
Returns the size (or minimal size if it's dynamic) of this field.
|
||||
|
||||
You should serialize your `field`'s value on `object`, and write it to `dataView` at `offset`.
|
||||
Actual size should been returned from `FieldRuntimeValue#getSize`
|
||||
|
||||
You must not write more data than `getSize`/`getDynamicSize` returned. Or an Error will be thrown.
|
||||
### `FieldDefinition#deserialize` method
|
||||
|
||||
### Array type
|
||||
```ts
|
||||
abstract deserialize(
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructDeserializationContext,
|
||||
object: any,
|
||||
): ValueOrPromise<FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TRemoveInitFields>>>;
|
||||
```
|
||||
|
||||
Instead of true "Array", the current array types (`arraybuffer` and `string`) are more like buffers.
|
||||
Defines how to deserialize a value from `context`. Can also return a `Promise`.
|
||||
|
||||
### `registerFieldTypeDefinition` method
|
||||
Usually implementations should be:
|
||||
|
||||
This library exports the `registerFieldTypeDefinition` method to register your custom type definitions.
|
||||
1. Some how parse the value from `context`
|
||||
2. Pass the value into `FieldDefinition#createValue`
|
||||
|
||||
Pass the `undefined as unknown as YourTypeDescriptor` as the first argument to make TypeScript infers the type.
|
||||
Sometimes, some metadata is present when deserializing, but need to be calculated when serializing, for example a UTF-8 encoded string may have different length between itself (character count) and serialized form (byte length). So `deserialize` and save those metadata on the `FieldRuntimeValue` instance.
|
||||
|
||||
### Data flow
|
||||
### `FieldDefinition#createValue` method
|
||||
|
||||
Here are lists of methods calling order to help you understand how this library works.
|
||||
```ts
|
||||
abstract createValue(
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructSerializationContext,
|
||||
object: any,
|
||||
value: TValueType,
|
||||
): FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TRemoveInitFields>>;
|
||||
```
|
||||
|
||||
| Method | Description |
|
||||
| ----------------------------- | -------------------------- |
|
||||
| `Struct#field` | Add a field descriptor |
|
||||
| `FieldTypeDefinition#getSize` | Add up struct's total size |
|
||||
Similar to `deserialize`, creates a `FieldRuntimeValue` for this instance.
|
||||
|
||||
| Method | Description |
|
||||
| --------------------------------- | ------------------------------------ |
|
||||
| `Struct#deserialize` | Start deserializing from a `context` |
|
||||
| `FieldTypeDefinition#deserialize` | Deserialize each field |
|
||||
The difference is `createValue` will be called when a init value was provided to create a Struct instance.
|
||||
|
||||
| Method | Description |
|
||||
| -------------------------------- | ---------------------------------------------------- |
|
||||
| `Struct#create` | Validate and create a value of the current structure |
|
||||
| `FieldTypeDefinition#initialize` | Initialize each field |
|
||||
### FieldRuntimeValue
|
||||
|
||||
| Method | Description |
|
||||
| ------------------------------------ | ---------------------------------------------------- |
|
||||
| `Struct#serialize` | Serialize a value into a buffer |
|
||||
| `Struct#create` | Validate and create a value of the current structure |
|
||||
| `FieldTypeDefinition#initialize` | Initialize each field |
|
||||
| `FieldTypeDefinition#getDynamicSize` | Get actual sizes of each field |
|
||||
| `FieldTypeDefinition#serialize` | Write each field into the allocated buffer |
|
||||
One `FieldDefinition` instance represents one field declaration, and one `FieldRuntimeValue` instance represents one value.
|
||||
|
||||
It defines how to get, set, and serialize a value.
|
||||
|
|
|
@ -28,7 +28,7 @@ export interface StructDeserializationContext extends StructSerializationContext
|
|||
|
||||
export interface StructOptions {
|
||||
/**
|
||||
* Whether multi-byte fields in this struct are in little endian
|
||||
* Whether all multi-byte fields in this struct are little-endian encoded.
|
||||
*
|
||||
* Default to `false`
|
||||
*/
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import type { StructOptions, StructSerializationContext } from './context';
|
||||
import type { StructDeserializationContext, StructOptions, StructSerializationContext } from './context';
|
||||
import type { FieldRuntimeValue } from './runtime-value';
|
||||
|
||||
type ValueOrPromise<T> = T | Promise<T>;
|
||||
|
||||
/**
|
||||
* A field definition is a bridge between its type and its runtime value.
|
||||
*
|
||||
|
@ -14,44 +16,54 @@ import type { FieldRuntimeValue } from './runtime-value';
|
|||
*
|
||||
* @template TOptions TypeScript type of this definition's `options`.
|
||||
* @template TValueType TypeScript type of this field.
|
||||
* @template TRemoveFields Optional remove keys from current `Struct`. Should be a union of string literal types.
|
||||
* @template TRemoveInitFields Optional remove some field from the `TInit` type. Should be a union of string literal types.
|
||||
*/
|
||||
export abstract class FieldDefinition<
|
||||
TOptions = void,
|
||||
TValueType = unknown,
|
||||
TRemoveFields = never,
|
||||
TRemoveInitFields = never,
|
||||
> {
|
||||
public readonly options: TOptions;
|
||||
|
||||
/**
|
||||
* When `T` is a type initiated `FieldDefinition`,
|
||||
* use `T['valueType']` to retrieve its `TValueType` type parameter
|
||||
* use `T['valueType']` to retrieve its `TValueType` type parameter.
|
||||
*/
|
||||
public readonly valueType!: TValueType;
|
||||
|
||||
/**
|
||||
* When `T` is a type initiated `FieldDefinition`,
|
||||
* use `T['removeFields']` to retrieve its `TRemoveFields` type parameter .
|
||||
* use `T['removeInitFields']` to retrieve its `TRemoveInitFields` type parameter.
|
||||
*/
|
||||
public readonly removeFields!: TRemoveFields;
|
||||
public readonly removeInitFields!: TRemoveInitFields;
|
||||
|
||||
public constructor(options: TOptions) {
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* When implemented in derived classes, returns the static size (or smallest size) of this field.
|
||||
* When implemented in derived classes, returns the size (or minimal size if it's dynamic) of this field.
|
||||
*
|
||||
* Actual size can be retrieved from `FieldRuntimeValue#getSize`
|
||||
*/
|
||||
public abstract getSize(): number;
|
||||
|
||||
/**
|
||||
* When implemented in derived classes, creates a `FieldRuntimeValue` for the current field definition.
|
||||
* When implemented in derived classes, creates a `FieldRuntimeValue` by parsing the `context`.
|
||||
*/
|
||||
public abstract deserialize(
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructDeserializationContext,
|
||||
object: any,
|
||||
): ValueOrPromise<FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TRemoveInitFields>>>;
|
||||
|
||||
/**
|
||||
* When implemented in derived classes, creates a `FieldRuntimeValue` from a given `value`.
|
||||
*/
|
||||
public abstract createValue(
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructSerializationContext,
|
||||
object: any
|
||||
): FieldRuntimeValue;
|
||||
object: any,
|
||||
value: TValueType,
|
||||
): FieldRuntimeValue<FieldDefinition<TOptions, TValueType, TRemoveInitFields>>;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { StructDeserializationContext, StructOptions, StructSerializationContext } from './context';
|
||||
import type { StructOptions, StructSerializationContext } from './context';
|
||||
import type { FieldDefinition } from './definition';
|
||||
|
||||
/**
|
||||
|
@ -22,23 +22,22 @@ export abstract class FieldRuntimeValue<
|
|||
/** Gets the associated `Struct` instance */
|
||||
public readonly object: any;
|
||||
|
||||
protected value: TDefinition['valueType'];
|
||||
|
||||
public constructor(
|
||||
definition: TDefinition,
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructSerializationContext,
|
||||
object: any,
|
||||
value: TDefinition['valueType'],
|
||||
) {
|
||||
this.definition = definition;
|
||||
this.options = options;
|
||||
this.context = context;
|
||||
this.object = object;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/** When implemented in derived classes, deserialize this field from the specified `context` */
|
||||
public abstract deserialize(
|
||||
context: StructDeserializationContext
|
||||
): void | Promise<void>;
|
||||
|
||||
/**
|
||||
* Gets the actual size of this field. By default, the return value of its `definition.getSize()`
|
||||
*
|
||||
|
@ -51,12 +50,16 @@ export abstract class FieldRuntimeValue<
|
|||
/**
|
||||
* When implemented in derived classes, returns the current value of this field
|
||||
*/
|
||||
public abstract get(): unknown;
|
||||
public get(): TDefinition['valueType'] {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
/**
|
||||
* When implemented in derived classes, update the current value of this field
|
||||
*/
|
||||
public abstract set(value: unknown): void;
|
||||
public set(value: TDefinition['valueType']): void {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* When implemented in derived classes, serializes this field into `dataView` at `offset`
|
||||
|
|
|
@ -31,7 +31,7 @@ type AddFieldDescriptor<
|
|||
Evaluate<TValue & Record<TFieldName, TDefinition['valueType']>>,
|
||||
// There is no `Evaluate` here, because otherwise the type of a `Struct` with many fields
|
||||
// can become too complex for TypeScript to compute
|
||||
Evaluate<Omit<TInit, TDefinition['removeFields']> & Record<TFieldName, TDefinition['valueType']>>,
|
||||
Evaluate<Omit<TInit, TDefinition['removeInitFields']> & Record<TFieldName, TDefinition['valueType']>>,
|
||||
TExtra,
|
||||
TPostDeserialized
|
||||
>>;
|
||||
|
@ -171,7 +171,7 @@ export default class Struct<
|
|||
|
||||
private _size = 0;
|
||||
/**
|
||||
* Get the static size (exclude fields that can change size at runtime)
|
||||
* Gets the static size (exclude fields that can change size at runtime)
|
||||
*/
|
||||
public get size() { return this._size; }
|
||||
|
||||
|
@ -185,6 +185,9 @@ export default class Struct<
|
|||
this.options = { ...StructDefaultOptions, ...options };
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends a `FieldDefinition` to the `Struct
|
||||
*/
|
||||
public field<
|
||||
TName extends PropertyKey,
|
||||
TDefinition extends FieldDefinition<any, any, any>
|
||||
|
@ -208,6 +211,9 @@ export default class Struct<
|
|||
return this as any;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merges (flats) another `Struct`'s fields and extra fields into this one.
|
||||
*/
|
||||
public fields<TOther extends Struct<any, any, any, any>>(
|
||||
struct: TOther
|
||||
): Struct<
|
||||
|
@ -239,6 +245,9 @@ export default class Struct<
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an `uint8` field to the `Struct`
|
||||
*/
|
||||
public uint8<
|
||||
TName extends PropertyKey,
|
||||
TTypeScriptType = (typeof NumberFieldType)['Uint8']['valueType']
|
||||
|
@ -253,6 +262,9 @@ export default class Struct<
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an `uint16` field to the `Struct`
|
||||
*/
|
||||
public uint16<
|
||||
TName extends PropertyKey,
|
||||
TTypeScriptType = (typeof NumberFieldType)['Uint16']['valueType']
|
||||
|
@ -267,6 +279,9 @@ export default class Struct<
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an `int32` field to the `Struct`
|
||||
*/
|
||||
public int32<
|
||||
TName extends PropertyKey,
|
||||
TTypeScriptType = (typeof NumberFieldType)['Int32']['valueType']
|
||||
|
@ -281,6 +296,9 @@ export default class Struct<
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an `uint32` field to the `Struct`
|
||||
*/
|
||||
public uint32<
|
||||
TName extends PropertyKey,
|
||||
TTypeScriptType = (typeof NumberFieldType)['Uint32']['valueType']
|
||||
|
@ -295,6 +313,11 @@ export default class Struct<
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an `int64` field to the `Struct`
|
||||
*
|
||||
* Requires native `BigInt` support
|
||||
*/
|
||||
public int64<
|
||||
TName extends PropertyKey,
|
||||
TTypeScriptType = (typeof NumberFieldType)['Int64']['valueType']
|
||||
|
@ -309,6 +332,11 @@ export default class Struct<
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an `uint64` field to the `Struct`
|
||||
*
|
||||
* Requires native `BigInt` support
|
||||
*/
|
||||
public uint64<
|
||||
TName extends PropertyKey,
|
||||
TTypeScriptType = (typeof NumberFieldType)['Uint64']['valueType']
|
||||
|
@ -380,6 +408,13 @@ export default class Struct<
|
|||
return this.arrayBufferLike(name, StringFieldType.instance, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Adds some extra fields into every Struct instance.
|
||||
*
|
||||
* Extra fields will not affect serialize or deserialize process.
|
||||
*
|
||||
* Multiple calls to `extra` will merge all values together.
|
||||
*/
|
||||
public extra<T extends Record<
|
||||
// This trick disallows any keys that are already in `TValue`
|
||||
Exclude<
|
||||
|
@ -400,14 +435,29 @@ export default class Struct<
|
|||
}
|
||||
|
||||
/**
|
||||
* Registers (or replaces) a custom callback to be run after deserialized.
|
||||
*
|
||||
* A callback returning `never` (always throw an error)
|
||||
* will also change the return type of `deserialize` to `never`.
|
||||
*/
|
||||
public postDeserialize(
|
||||
callback: StructPostDeserialized<TValue, never>
|
||||
): Struct<TValue, TInit, TExtra, never>;
|
||||
/**
|
||||
* Registers (or replaces) a custom callback to be run after deserialized.
|
||||
*
|
||||
* A callback returning `void` means it modify the result object in-place
|
||||
* (or doesn't modify it at all), so `deserialize` will still return the result object.
|
||||
*/
|
||||
public postDeserialize(
|
||||
callback?: StructPostDeserialized<TValue, void>
|
||||
): Struct<TValue, TInit, TExtra, undefined>;
|
||||
/**
|
||||
* Registers (or replaces) a custom callback to be run after deserialized.
|
||||
*
|
||||
* A callback returning anything other than `undefined`
|
||||
* will `deserialize` to return that object instead.
|
||||
*/
|
||||
public postDeserialize<TPostSerialize>(
|
||||
callback?: StructPostDeserialized<TValue, TPostSerialize>
|
||||
): Struct<TValue, TInit, TExtra, TPostSerialize>;
|
||||
|
@ -418,24 +468,18 @@ export default class Struct<
|
|||
return this as any;
|
||||
}
|
||||
|
||||
private initializeObject(context: StructSerializationContext) {
|
||||
private initializeObject() {
|
||||
const object = createRuntimeObject();
|
||||
Object.defineProperties(object, this._extra);
|
||||
|
||||
for (const [name, definition] of this._fields) {
|
||||
const runtimeValue = definition.createValue(this.options, context, object);
|
||||
setRuntimeValue(object, name, runtimeValue);
|
||||
}
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
public create(init: TInit, context: StructSerializationContext): Overwrite<TExtra, TValue> {
|
||||
const object = this.initializeObject(context);
|
||||
const object = this.initializeObject();
|
||||
|
||||
for (const [name] of this._fields) {
|
||||
const runtimeValue = getRuntimeValue(object, name);
|
||||
runtimeValue.set((init as any)[name]);
|
||||
for (const [name, definition] of this._fields) {
|
||||
const runtimeValue = definition.createValue(this.options, context, object, (init as any)[name]);
|
||||
setRuntimeValue(object, name, runtimeValue);
|
||||
}
|
||||
|
||||
return object as any;
|
||||
|
@ -444,11 +488,11 @@ export default class Struct<
|
|||
public async deserialize(
|
||||
context: StructDeserializationContext
|
||||
): Promise<TPostDeserialized extends undefined ? Overwrite<TExtra, TValue> : TPostDeserialized> {
|
||||
const object = this.initializeObject(context);
|
||||
const object = this.initializeObject();
|
||||
|
||||
for (const [name] of this._fields) {
|
||||
const runtimeValue = getRuntimeValue(object, name);
|
||||
await runtimeValue.deserialize(context);
|
||||
for (const [name, definition] of this._fields) {
|
||||
const runtimeValue = await definition.deserialize(this.options, context, object);
|
||||
setRuntimeValue(object, name, runtimeValue);
|
||||
}
|
||||
|
||||
if (this._postDeserialized) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { FieldDefinition, FieldRuntimeValue, StructDeserializationContext, StructSerializationContext } from '../basic';
|
||||
import { FieldDefinition, FieldRuntimeValue, StructDeserializationContext, StructOptions, StructSerializationContext } from '../basic';
|
||||
|
||||
/**
|
||||
* Base class for all types that
|
||||
|
@ -97,14 +97,16 @@ export class StringFieldType<TTypeScriptType = string>
|
|||
}
|
||||
}
|
||||
|
||||
const EmptyArrayBuffer = new ArrayBuffer(0);
|
||||
|
||||
export abstract class ArrayBufferLikeFieldDefinition<
|
||||
TType extends ArrayBufferLikeFieldType = ArrayBufferLikeFieldType,
|
||||
TOptions = void,
|
||||
TRemoveFields = never,
|
||||
TRemoveInitFields = never,
|
||||
> extends FieldDefinition<
|
||||
TOptions,
|
||||
TType['valueType'],
|
||||
TRemoveFields
|
||||
TRemoveInitFields
|
||||
>{
|
||||
public readonly type: TType;
|
||||
|
||||
|
@ -112,48 +114,55 @@ export abstract class ArrayBufferLikeFieldDefinition<
|
|||
super(options);
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
|
||||
const EmptyArrayBuffer = new ArrayBuffer(0);
|
||||
|
||||
export abstract class ArrayBufferLikeFieldRuntimeValue<
|
||||
TDefinition extends ArrayBufferLikeFieldDefinition<any, any, any>,
|
||||
> extends FieldRuntimeValue<TDefinition> {
|
||||
protected arrayBuffer: ArrayBuffer | undefined;
|
||||
|
||||
protected typedValue: unknown;
|
||||
|
||||
protected getDeserializeSize(): number {
|
||||
protected getDeserializeSize(object: any): number {
|
||||
return this.getSize();
|
||||
}
|
||||
|
||||
public async deserialize(context: StructDeserializationContext): Promise<void> {
|
||||
const size = this.getDeserializeSize();
|
||||
|
||||
this.arrayBuffer = undefined;
|
||||
this.typedValue = undefined;
|
||||
public async deserialize(
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructDeserializationContext,
|
||||
object: any,
|
||||
): Promise<ArrayBufferLikeFieldRuntimeValue<ArrayBufferLikeFieldDefinition<TType, TOptions, TRemoveInitFields>>> {
|
||||
const size = this.getDeserializeSize(object);
|
||||
|
||||
let arrayBuffer: ArrayBuffer;
|
||||
if (size === 0) {
|
||||
this.arrayBuffer = EmptyArrayBuffer;
|
||||
arrayBuffer = EmptyArrayBuffer;
|
||||
} else {
|
||||
this.arrayBuffer = await context.read(size);
|
||||
arrayBuffer = await context.read(size);
|
||||
}
|
||||
|
||||
this.typedValue = this.definition.type.fromArrayBuffer(this.arrayBuffer, context);
|
||||
const value = this.type.fromArrayBuffer(arrayBuffer, context);
|
||||
const runtimeValue = this.createValue(options, context, object, value);
|
||||
runtimeValue.arrayBuffer = arrayBuffer;
|
||||
return runtimeValue;
|
||||
}
|
||||
|
||||
public get(): unknown {
|
||||
return this.typedValue;
|
||||
}
|
||||
/**
|
||||
* When implemented in derived classes, creates a `FieldRuntimeValue` for the current field definition.
|
||||
*/
|
||||
public abstract createValue(
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructSerializationContext,
|
||||
object: any,
|
||||
value: TType['valueType'],
|
||||
): ArrayBufferLikeFieldRuntimeValue<ArrayBufferLikeFieldDefinition<TType, TOptions, TRemoveInitFields>>;
|
||||
}
|
||||
|
||||
public set(value: unknown): void {
|
||||
this.typedValue = value;
|
||||
export class ArrayBufferLikeFieldRuntimeValue<
|
||||
TDefinition extends ArrayBufferLikeFieldDefinition<any, any, any>,
|
||||
> extends FieldRuntimeValue<TDefinition> {
|
||||
public arrayBuffer: ArrayBuffer | undefined;
|
||||
|
||||
public set(value: TDefinition['valueType']): void {
|
||||
super.set(value);
|
||||
this.arrayBuffer = undefined;
|
||||
}
|
||||
|
||||
public serialize(dataView: DataView, offset: number, context: StructSerializationContext): void {
|
||||
if (!this.arrayBuffer) {
|
||||
this.arrayBuffer = this.definition.type.toArrayBuffer(this.typedValue, context);
|
||||
this.arrayBuffer = this.definition.type.toArrayBuffer(this.value, context);
|
||||
}
|
||||
|
||||
new Uint8Array(dataView.buffer)
|
||||
|
|
|
@ -19,15 +19,9 @@ export class FixedLengthArrayBufferLikeFieldDefinition<
|
|||
public createValue(
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructSerializationContext,
|
||||
object: any
|
||||
): FixedLengthArrayBufferFieldRuntimeValue {
|
||||
return new FixedLengthArrayBufferFieldRuntimeValue(this, options, context, object);
|
||||
object: any,
|
||||
value: TType['valueType']
|
||||
): ArrayBufferLikeFieldRuntimeValue<FixedLengthArrayBufferLikeFieldDefinition<TType, TOptions>> {
|
||||
return new ArrayBufferLikeFieldRuntimeValue(this, options, context, object, value);
|
||||
}
|
||||
};
|
||||
|
||||
class FixedLengthArrayBufferFieldRuntimeValue
|
||||
extends ArrayBufferLikeFieldRuntimeValue<FixedLengthArrayBufferLikeFieldDefinition>{
|
||||
public static getSize(descriptor: FixedLengthArrayBufferLikeFieldDefinition) {
|
||||
return descriptor.options.length;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,12 +57,27 @@ export class NumberFieldDefinition<
|
|||
return this.type.size;
|
||||
}
|
||||
|
||||
public async deserialize(
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructDeserializationContext,
|
||||
object: any,
|
||||
): Promise<NumberFieldRuntimeValue<TType, TTypeScriptType>> {
|
||||
const buffer = await context.read(this.getSize());
|
||||
const view = new DataView(buffer);
|
||||
const value = view[this.type.dataViewGetter](
|
||||
0,
|
||||
options.littleEndian
|
||||
) as any;
|
||||
return this.createValue(options, context, object, value);
|
||||
}
|
||||
|
||||
public createValue(
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructSerializationContext,
|
||||
object: any
|
||||
object: any,
|
||||
value: TTypeScriptType,
|
||||
): NumberFieldRuntimeValue<TType, TTypeScriptType> {
|
||||
return new NumberFieldRuntimeValue(this, options, context, object);
|
||||
return new NumberFieldRuntimeValue(this, options, context, object, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -70,25 +85,6 @@ export class NumberFieldRuntimeValue<
|
|||
TType extends NumberFieldType = NumberFieldType,
|
||||
TTypeScriptType = TType['valueType'],
|
||||
> extends FieldRuntimeValue<NumberFieldDefinition<TType, TTypeScriptType>> {
|
||||
protected value: number | bigint | undefined;
|
||||
|
||||
public async deserialize(context: StructDeserializationContext): Promise<void> {
|
||||
const buffer = await context.read(this.getSize());
|
||||
const view = new DataView(buffer);
|
||||
this.value = view[this.definition.type.dataViewGetter](
|
||||
0,
|
||||
this.options.littleEndian
|
||||
);
|
||||
}
|
||||
|
||||
public get(): unknown {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
public set(value: unknown): void {
|
||||
this.value = value as number | bigint;
|
||||
}
|
||||
|
||||
public serialize(dataView: DataView, offset: number): void {
|
||||
// `setBigInt64` requires a `bigint` while others require `number`
|
||||
// So `dataView[DataViewSetters]` requires `bigint & number`
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import { getRuntimeValue, setRuntimeValue, StructOptions, StructSerializationContext } from '../basic';
|
||||
import { FieldRuntimeValue, getRuntimeValue, setRuntimeValue, StructOptions, StructSerializationContext } from '../basic';
|
||||
import { ArrayBufferLikeFieldDefinition, ArrayBufferLikeFieldRuntimeValue, ArrayBufferLikeFieldType } from './array-buffer';
|
||||
import { NumberFieldDefinition, NumberFieldRuntimeValue } from './number';
|
||||
import { KeysOfType } from './utils';
|
||||
|
||||
export interface VariableLengthArrayBufferLikeFieldOptions<
|
||||
|
@ -22,47 +21,59 @@ export class VariableLengthArrayBufferLikeFieldDefinition<
|
|||
return 0;
|
||||
}
|
||||
|
||||
protected getDeserializeSize(object: any) {
|
||||
let value = object[this.options.lengthField] as number | string;
|
||||
if (typeof value === 'string') {
|
||||
value = Number.parseInt(value, 10);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public createValue(
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructSerializationContext,
|
||||
object: any
|
||||
): VariableLengthArrayBufferLikeFieldRuntimeValue {
|
||||
return new VariableLengthArrayBufferLikeFieldRuntimeValue(this, options, context, object);
|
||||
object: any,
|
||||
value: TType['valueType'],
|
||||
): VariableLengthArrayBufferLikeFieldRuntimeValue<TType, TOptions> {
|
||||
return new VariableLengthArrayBufferLikeFieldRuntimeValue(this, options, context, object, value);
|
||||
}
|
||||
}
|
||||
|
||||
class VariableLengthArrayBufferLikeLengthFieldRuntimeValue extends NumberFieldRuntimeValue {
|
||||
class VariableLengthArrayBufferLikeLengthFieldRuntimeValue extends FieldRuntimeValue {
|
||||
protected originalValue: FieldRuntimeValue;
|
||||
|
||||
protected arrayBufferValue: VariableLengthArrayBufferLikeFieldRuntimeValue;
|
||||
|
||||
public constructor(
|
||||
definition: NumberFieldDefinition,
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructSerializationContext,
|
||||
object: any,
|
||||
originalValue: FieldRuntimeValue,
|
||||
arrayBufferValue: VariableLengthArrayBufferLikeFieldRuntimeValue,
|
||||
) {
|
||||
super(definition, options, context, object);
|
||||
super(originalValue.definition, originalValue.options, originalValue.context, originalValue.object, 0);
|
||||
this.originalValue = originalValue;
|
||||
this.arrayBufferValue = arrayBufferValue;
|
||||
}
|
||||
|
||||
getDeserializeSize() {
|
||||
return this.value;
|
||||
public getSize() {
|
||||
return this.originalValue.getSize();
|
||||
}
|
||||
|
||||
get() {
|
||||
// TODO: originalValue might be a `string` type, now it always returns `number`.
|
||||
return this.arrayBufferValue.getSize();
|
||||
}
|
||||
|
||||
set() { }
|
||||
|
||||
serialize(dataView: DataView, offset: number) {
|
||||
this.value = this.get();
|
||||
super.serialize(dataView, offset);
|
||||
serialize(dataView: DataView, offset: number, context: StructSerializationContext) {
|
||||
this.originalValue.set(this.get());
|
||||
this.originalValue.serialize(dataView, offset, context);
|
||||
}
|
||||
}
|
||||
|
||||
class VariableLengthArrayBufferLikeFieldRuntimeValue
|
||||
extends ArrayBufferLikeFieldRuntimeValue<VariableLengthArrayBufferLikeFieldDefinition> {
|
||||
class VariableLengthArrayBufferLikeFieldRuntimeValue<
|
||||
TType extends ArrayBufferLikeFieldType = ArrayBufferLikeFieldType,
|
||||
TOptions extends VariableLengthArrayBufferLikeFieldOptions = VariableLengthArrayBufferLikeFieldOptions
|
||||
> extends ArrayBufferLikeFieldRuntimeValue<VariableLengthArrayBufferLikeFieldDefinition<TType, TOptions>> {
|
||||
public static getSize() {
|
||||
return 0;
|
||||
}
|
||||
|
@ -72,38 +83,29 @@ class VariableLengthArrayBufferLikeFieldRuntimeValue
|
|||
protected lengthFieldValue: VariableLengthArrayBufferLikeLengthFieldRuntimeValue;
|
||||
|
||||
public constructor(
|
||||
descriptor: VariableLengthArrayBufferLikeFieldDefinition,
|
||||
definition: VariableLengthArrayBufferLikeFieldDefinition<TType, TOptions>,
|
||||
options: Readonly<StructOptions>,
|
||||
context: StructSerializationContext,
|
||||
object: any
|
||||
object: any,
|
||||
value: TType['valueType'],
|
||||
) {
|
||||
super(descriptor, options, context, object);
|
||||
super(definition, options, context, object, value);
|
||||
|
||||
// Patch the associated length field.
|
||||
const lengthField = this.definition.options.lengthField;
|
||||
const oldValue = getRuntimeValue(object, lengthField) as NumberFieldRuntimeValue;
|
||||
this.lengthFieldValue = new VariableLengthArrayBufferLikeLengthFieldRuntimeValue(
|
||||
oldValue.definition,
|
||||
this.options,
|
||||
this.context,
|
||||
object,
|
||||
this
|
||||
);
|
||||
const originalValue = getRuntimeValue(object, lengthField);
|
||||
this.lengthFieldValue = new VariableLengthArrayBufferLikeLengthFieldRuntimeValue(originalValue, this);
|
||||
setRuntimeValue(object, lengthField, this.lengthFieldValue);
|
||||
}
|
||||
|
||||
protected getDeserializeSize() {
|
||||
const value = this.lengthFieldValue.getDeserializeSize() as number;
|
||||
return value;
|
||||
}
|
||||
|
||||
public getSize() {
|
||||
if (this.length === undefined) {
|
||||
if (this.arrayBuffer !== undefined) {
|
||||
this.length = this.arrayBuffer.byteLength;
|
||||
} else {
|
||||
this.length = this.definition.type.getSize(this.typedValue);
|
||||
this.length = this.definition.type.getSize(this.value);
|
||||
if (this.length === -1) {
|
||||
this.arrayBuffer = this.definition.type.toArrayBuffer(this.typedValue, this.context);
|
||||
this.arrayBuffer = this.definition.type.toArrayBuffer(this.value, this.context);
|
||||
this.length = this.arrayBuffer.byteLength;
|
||||
}
|
||||
}
|
||||
|
|
177
packages/webpack-config/package-lock.json
generated
177
packages/webpack-config/package-lock.json
generated
|
@ -263,6 +263,30 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"ansi-escapes": {
|
||||
"version": "4.3.1",
|
||||
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.1.tgz",
|
||||
"integrity": "sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-fest": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"ansi-regex": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz",
|
||||
"integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==",
|
||||
"dev": true
|
||||
},
|
||||
"ansi-styles": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
|
||||
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-convert": "^2.0.1"
|
||||
}
|
||||
},
|
||||
"array-union": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/array-union/-/array-union-1.0.2.tgz",
|
||||
|
@ -294,6 +318,22 @@
|
|||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"chalk": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
|
||||
"integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.1.0",
|
||||
"supports-color": "^7.1.0"
|
||||
}
|
||||
},
|
||||
"ci-info": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
|
||||
"integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==",
|
||||
"dev": true
|
||||
},
|
||||
"clean-webpack-plugin": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/clean-webpack-plugin/-/clean-webpack-plugin-3.0.0.tgz",
|
||||
|
@ -304,12 +344,33 @@
|
|||
"del": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"color-convert": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
|
||||
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"color-name": "~1.1.4"
|
||||
}
|
||||
},
|
||||
"color-name": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"concat-map": {
|
||||
"version": "0.0.1",
|
||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
|
||||
"dev": true
|
||||
},
|
||||
"consola": {
|
||||
"version": "2.15.0",
|
||||
"resolved": "https://registry.npmjs.org/consola/-/consola-2.15.0.tgz",
|
||||
"integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ==",
|
||||
"dev": true
|
||||
},
|
||||
"del": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/del/-/del-4.1.1.tgz",
|
||||
|
@ -325,6 +386,27 @@
|
|||
"rimraf": "^2.6.3"
|
||||
}
|
||||
},
|
||||
"emoji-regex": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
|
||||
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
|
||||
"dev": true
|
||||
},
|
||||
"escape-string-regexp": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
|
||||
"integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
|
||||
"dev": true
|
||||
},
|
||||
"figures": {
|
||||
"version": "3.2.0",
|
||||
"resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz",
|
||||
"integrity": "sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"escape-string-regexp": "^1.0.5"
|
||||
}
|
||||
},
|
||||
"fs.realpath": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||
|
@ -366,6 +448,12 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"has-flag": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
|
||||
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
|
||||
"dev": true
|
||||
},
|
||||
"inflight": {
|
||||
"version": "1.0.6",
|
||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||
|
@ -382,6 +470,12 @@
|
|||
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
|
||||
"dev": true
|
||||
},
|
||||
"is-fullwidth-code-point": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
|
||||
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
|
||||
"dev": true
|
||||
},
|
||||
"is-path-cwd": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/is-path-cwd/-/is-path-cwd-2.2.0.tgz",
|
||||
|
@ -469,6 +563,12 @@
|
|||
"pinkie": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"pretty-time": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/pretty-time/-/pretty-time-1.1.0.tgz",
|
||||
"integrity": "sha512-28iF6xPQrP8Oa6uxE6a1biz+lWeTOAPKggvjB8HAs6nVMKZwf5bG++632Dx614hIWgUPkgivRfG+a8uAXGTIbA==",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
|
@ -484,12 +584,89 @@
|
|||
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
|
||||
"dev": true
|
||||
},
|
||||
"std-env": {
|
||||
"version": "2.2.1",
|
||||
"resolved": "https://registry.npmjs.org/std-env/-/std-env-2.2.1.tgz",
|
||||
"integrity": "sha512-IjYQUinA3lg5re/YMlwlfhqNRTzMZMqE+pezevdcTaHceqx8ngEi1alX9nNCk9Sc81fy1fLDeQoaCzeiW1yBOQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ci-info": "^1.6.0"
|
||||
}
|
||||
},
|
||||
"string-width": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.0.tgz",
|
||||
"integrity": "sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"emoji-regex": "^8.0.0",
|
||||
"is-fullwidth-code-point": "^3.0.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"strip-ansi": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
|
||||
"integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-regex": "^5.0.0"
|
||||
}
|
||||
},
|
||||
"supports-color": {
|
||||
"version": "7.2.0",
|
||||
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
|
||||
"integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"has-flag": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"text-table": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
|
||||
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
|
||||
"dev": true
|
||||
},
|
||||
"type-fest": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.11.0.tgz",
|
||||
"integrity": "sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==",
|
||||
"dev": true
|
||||
},
|
||||
"typescript": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz",
|
||||
"integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==",
|
||||
"dev": true
|
||||
},
|
||||
"webpackbar": {
|
||||
"version": "5.0.0-3",
|
||||
"resolved": "https://registry.npmjs.org/webpackbar/-/webpackbar-5.0.0-3.tgz",
|
||||
"integrity": "sha512-viW6KCYjMb0NPoDrw2jAmLXU2dEOhRrtku28KmOfeE1vxbfwCYuTbTaMhnkrCZLFAFyY9Q49Z/jzYO80Dw5b8g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-escapes": "^4.3.1",
|
||||
"chalk": "^4.1.0",
|
||||
"consola": "^2.15.0",
|
||||
"figures": "^3.2.0",
|
||||
"pretty-time": "^1.1.0",
|
||||
"std-env": "^2.2.1",
|
||||
"text-table": "^0.2.0",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"wrap-ansi": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
|
||||
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ansi-styles": "^4.0.0",
|
||||
"string-width": "^4.1.0",
|
||||
"strip-ansi": "^6.0.0"
|
||||
}
|
||||
},
|
||||
"wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "webpack-config",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"description": "Webpack config for `demo` project in TypeScript",
|
||||
"author": "Simon Chan <cnsimonchan@live.com>",
|
||||
"homepage": "https://github.com/yume-chan/ya-webadb#readme",
|
||||
|
@ -25,6 +25,7 @@
|
|||
"@types/webpack-bundle-analyzer": "3.9.0",
|
||||
"@types/webpack-dev-server": "3.11.1",
|
||||
"clean-webpack-plugin": "3.0.0",
|
||||
"typescript": "4.1.3"
|
||||
"typescript": "4.1.3",
|
||||
"webpackbar": "5.0.0-3"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,11 +5,12 @@ import MiniCssExtractPlugin from 'mini-css-extract-plugin';
|
|||
import path from 'path';
|
||||
import webpack from 'webpack';
|
||||
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
|
||||
import WebpackBar from 'webpackbar';
|
||||
|
||||
const context = path.resolve(process.cwd());
|
||||
|
||||
const plugins: webpack.Plugin[] = [
|
||||
new CleanWebpackPlugin(),
|
||||
new WebpackBar({}),
|
||||
new MiniCssExtractPlugin({
|
||||
filename: '[name].[contenthash].css',
|
||||
}),
|
||||
|
@ -35,36 +36,42 @@ if (process.env.ANALYZE) {
|
|||
const config: webpack.ConfigurationFactory = (
|
||||
env: unknown,
|
||||
argv: webpack.CliConfigOptions
|
||||
): webpack.Configuration => ({
|
||||
mode: 'development',
|
||||
devtool: argv.mode === 'production' ? 'source-map' : 'eval-source-map',
|
||||
context,
|
||||
target: 'web',
|
||||
entry: {
|
||||
index: './src/index.tsx',
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(context, 'lib'),
|
||||
filename: '[name].[contenthash].js',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js'],
|
||||
// @ts-expect-error typing is not up to date
|
||||
fallback: { "path": require.resolve("path-browserify") },
|
||||
},
|
||||
plugins,
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.js$/, enforce: 'pre', use: ['source-map-loader'], },
|
||||
{ test: /\.css$/i, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
|
||||
{ test: /\.asset$/, use: { loader: "file-loader" } },
|
||||
{ test: /\.tsx?$/i, loader: 'ts-loader', options: { projectReferences: true } },
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
contentBase: path.resolve(context, 'lib'),
|
||||
port: 9000,
|
||||
},
|
||||
});
|
||||
): webpack.Configuration => {
|
||||
if (argv.mode !== 'production') {
|
||||
plugins.unshift(new CleanWebpackPlugin());
|
||||
}
|
||||
|
||||
return {
|
||||
mode: 'development',
|
||||
devtool: argv.mode === 'production' ? 'source-map' : 'eval-source-map',
|
||||
context,
|
||||
target: 'web',
|
||||
entry: {
|
||||
index: './src/index.tsx',
|
||||
},
|
||||
output: {
|
||||
path: path.resolve(context, 'lib'),
|
||||
filename: '[name].[contenthash].js',
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.ts', '.tsx', '.js'],
|
||||
// @ts-expect-error typing is not up to date
|
||||
fallback: { "path": require.resolve("path-browserify") },
|
||||
},
|
||||
plugins,
|
||||
module: {
|
||||
rules: [
|
||||
{ test: /\.js$/, enforce: 'pre', use: ['source-map-loader'], },
|
||||
{ test: /\.css$/i, use: [MiniCssExtractPlugin.loader, 'css-loader'] },
|
||||
{ test: /\.asset$/, use: { loader: "file-loader" } },
|
||||
{ test: /\.tsx?$/i, loader: 'ts-loader', options: { projectReferences: true } },
|
||||
],
|
||||
},
|
||||
devServer: {
|
||||
contentBase: path.resolve(context, 'lib'),
|
||||
port: 9000,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export = config;
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
"ESNext"
|
||||
],
|
||||
"declaration": false, // /* Generates corresponding '.d.ts' file. */
|
||||
"declarationDir": null,
|
||||
"declarationMap": false, // /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
"sourceMap": false, // /* Generates corresponding '.map' file. */
|
||||
"outDir": "../demo", // /* Redirect output structure to the directory. */
|
||||
"rootDir": "./src", // /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
"composite": false, // /* Enable project compilation */
|
||||
"tsBuildInfoFile": "./tsconfig.tsbuildinfo", /* Specify file to store incremental compilation information */
|
||||
},
|
||||
"include": [
|
||||
"src"
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
"rootDir": "src",
|
||||
"outDir": "esm",
|
||||
"sourceMap": true,
|
||||
"removeComments": true,
|
||||
"declaration": true,
|
||||
"declarationDir": "dts",
|
||||
"declarationMap": true,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue