@flowtools/uplot 0.0.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Mentalhouse Oy - Open Source
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,48 @@
1
+ # Vue Typescript Bundle Template
2
+
3
+ ![Vue Typescript Bundle Template](https://github.com/productdevbookcom/assets/blob/main/vue-ts-bundle-template.jpg?raw=true)
4
+
5
+
6
+ This is a template for creating a Typescript bundle. It is based on the [Typescript](https://www.typescriptlang.org/) compiler with the [Vite](https://vitejs.dev/) bundler.
7
+
8
+ ## Features
9
+
10
+ - [x] [Typescript](https://www.typescriptlang.org/)
11
+ - [x] [Vite](https://vitejs.dev/)
12
+ - [x] [Vue](https://vuejs.org/)
13
+ - [x] [Vue Macros](https://github.com/sxzz/unplugin-vue-macros)
14
+ - [x] [ESLint](https://eslint.org/) with [Antfu's ESLint Config](https://github.com/antfu/eslint-config)
15
+ - [x] [Bumpp](https://github.com/antfu/bumpp) github changelog generator
16
+ - [x] [Vitest](https://vitest.dev/)
17
+ - [x] [Pnpm](https://pnpm.io/)
18
+ - [x] [GitHub Actions]()
19
+ - [x] [NPM Local Registry]()
20
+ - [x] [Renovate]()
21
+
22
+
23
+ ## Usage
24
+
25
+ 1. To use this template, click the "Use this template" button above.
26
+ 2. Clone the repository to your local machine.
27
+ 3. Run `pnpm install` to install the dependencies.
28
+ 4. Run `pnpm build` to build the bundle.
29
+ 5. Run `pnpm start` to start the bundle.
30
+ 6. Run `pnpm lint` to lint the code. (You can also run `pnpm lint:fix` to fix the linting errors.)
31
+ 7. Run `pnpm test` to run the tests. (You can also run `pnpm test:watch` to run the tests in watch mode.)
32
+ 8. Run `pnpm release` to bump the version. Terminal will ask you to select the version type. And then it will automatically commit and push the changes. GitHub Actions will automatically publish git tags. NPM local registry will automatically publish the package.
33
+
34
+ ## Configuration
35
+
36
+ ### Github Secrets
37
+
38
+ [Github Token](https://github.com/settings/tokens) is required github changelog generator. You can create a token. Select the `repo` scope. Then add the token to the repository secrets.
39
+
40
+ `REPOCHANGELOG_TOKEN` - add the token to the repository secrets.
41
+
42
+ ### Renovate
43
+
44
+ [Setup Github App](https://github.com/apps/renovate) for Renovate.
45
+
46
+ ## License
47
+
48
+ This project is licensed under the [MIT License](LICENSE).
@@ -0,0 +1 @@
1
+ export { default as Uplot } from './components/Uplot.vue';
@@ -0,0 +1,176 @@
1
+ import { defineComponent as E, ref as U, onMounted as V, watch as b, nextTick as $, openBlock as p, createElementBlock as u, renderSlot as O, unref as _, createElementVNode as g, createTextVNode as B, toDisplayString as h, createCommentVNode as A, Fragment as P, renderList as N, normalizeClass as L, normalizeStyle as k } from "vue";
2
+ import M from "uplot";
3
+ import { useElementSize as F, useVModel as C } from "@vueuse/core";
4
+ const j = (a) => {
5
+ if (typeof a == "object" && a !== null) {
6
+ if (typeof Object.getPrototypeOf == "function") {
7
+ const r = Object.getPrototypeOf(a);
8
+ return r === Object.prototype || r === null;
9
+ }
10
+ return Object.prototype.toString.call(a) === "[object Object]";
11
+ }
12
+ return !1;
13
+ }, c = (...a) => a.reduce((r, l) => {
14
+ if (Array.isArray(l))
15
+ throw new TypeError(
16
+ "Arguments provided to ts-deepmerge must be objects, not arrays."
17
+ );
18
+ return l && Object.keys(l).forEach((o) => {
19
+ ["__proto__", "constructor", "prototype"].includes(o) || (Array.isArray(r[o]) && Array.isArray(l[o]) ? r[o] = c.options.mergeArrays ? Array.from(new Set(r[o].concat(l[o]))) : l[o] : j(r[o]) && j(l[o]) ? r[o] = c(r[o], l[o]) : r[o] = l[o]);
20
+ }), r;
21
+ }, {}), T = {
22
+ mergeArrays: !0
23
+ };
24
+ c.options = T;
25
+ c.withOptions = (a, ...r) => {
26
+ c.options = {
27
+ mergeArrays: !0,
28
+ ...a
29
+ };
30
+ const l = c(...r);
31
+ return c.options = T, l;
32
+ };
33
+ const q = { class: "__uplot-root" }, G = {
34
+ key: 0,
35
+ class: "extra-info"
36
+ }, H = /* @__PURE__ */ g("br", null, null, -1), I = { style: { "text-align": "left" } }, J = { class: "__uplot-legend" }, K = ["onClick"], Q = {
37
+ key: 1,
38
+ class: "__uplot-legend-value"
39
+ }, R = {
40
+ key: 2,
41
+ class: "__uplot-legend-value"
42
+ }, ee = /* @__PURE__ */ E({
43
+ __name: "Uplot",
44
+ props: {
45
+ options: null,
46
+ data: null,
47
+ resetScale: { type: Boolean, default: !1 },
48
+ showDebug: { type: Boolean },
49
+ zoom: { default: () => [null, null] },
50
+ series: null
51
+ },
52
+ emits: ["select", "cursor", "update:zoom"],
53
+ setup(a, { expose: r, emit: l }) {
54
+ const o = a, v = U(), { width: y, height: w } = F(v), x = C(o, "zoom", l, { passive: !0 }), i = C(o, "series", l, { passive: !0 }), D = {
55
+ title: void 0,
56
+ legend: {
57
+ show: !1
58
+ },
59
+ hooks: {
60
+ init: [
61
+ (e) => {
62
+ console.log("init", e), i.value = e.series.map((t, s) => ({
63
+ label: t.label,
64
+ stroke: typeof t.stroke == "function" ? t.stroke(e, s) : null,
65
+ value: null,
66
+ data: null,
67
+ show: t.show
68
+ // ...s,
69
+ }));
70
+ }
71
+ ],
72
+ setCursor: [
73
+ (e) => {
74
+ l("cursor", e.cursor), i.value = e.series.map((t, s) => {
75
+ var d, f;
76
+ return {
77
+ label: t.label,
78
+ stroke: typeof t.stroke == "function" ? t.stroke(e, s) : null,
79
+ value: e.cursor.idx && e.data[s][e.cursor.idx] ? typeof t.value == "function" ? t.value(e, e.data[s][e.cursor.idx], s, e.cursor.idx) : e.data[s][e.cursor.idx] : null,
80
+ data: (d = e.cursor) != null && d.idx ? e.data[s][(f = e.cursor) == null ? void 0 : f.idx] : null,
81
+ show: t.show
82
+ // ...s,
83
+ };
84
+ });
85
+ }
86
+ ],
87
+ setSelect: [
88
+ (e) => {
89
+ l("select", e.select), console.log("setSelect", e.select), x.value = [
90
+ e.posToVal(e.select.left, "x"),
91
+ e.posToVal(e.select.left + e.select.width, "x")
92
+ ];
93
+ }
94
+ ],
95
+ setScale: [
96
+ (e) => {
97
+ x.value = [
98
+ e.scales.x.min || null,
99
+ e.scales.x.max || null
100
+ ];
101
+ }
102
+ ]
103
+ }
104
+ };
105
+ let n;
106
+ function S() {
107
+ n && n.destroy(), n = new M({ width: 100, height: 100, ...c(o.options, D) }, o.data, v.value), o.zoom[0] !== null && o.zoom[1] !== null && n.setScale("x", { min: o.zoom[0], max: o.zoom[1] }), setTimeout(() => {
108
+ z();
109
+ }, 0);
110
+ }
111
+ V(() => {
112
+ S();
113
+ }), b([y, w], () => {
114
+ $(() => {
115
+ z();
116
+ });
117
+ }), b(o.data, (e) => {
118
+ if (o.resetScale) {
119
+ n.setData(e);
120
+ return;
121
+ }
122
+ n.setData(e, !0), n.redraw();
123
+ }), b(o.options, (e, t) => {
124
+ console.log("watch options", e, t), S();
125
+ });
126
+ function z() {
127
+ n.setSize({ width: y.value, height: w.value }), console.log("resize", n);
128
+ }
129
+ function m(e) {
130
+ var t;
131
+ n.setSeries(e, { show: !n.series[e].show }), (t = i.value) != null && t[e] && (i.value[e].show = n.series[e].show);
132
+ }
133
+ return r({ toggleShow: m }), (e, t) => (p(), u("div", q, [
134
+ O(e.$slots, "header", {
135
+ series: _(i),
136
+ toggleShow: m
137
+ }),
138
+ g("div", {
139
+ ref_key: "el",
140
+ ref: v,
141
+ class: "__uplot"
142
+ }, null, 512),
143
+ a.showDebug ? (p(), u("div", G, [
144
+ B(h(_(y)) + " x " + h(_(w)) + " ", 1),
145
+ H,
146
+ g("pre", I, h(_(i)), 1)
147
+ ])) : A("", !0),
148
+ O(e.$slots, "footer", {
149
+ series: _(i),
150
+ toggleShow: m
151
+ }, () => [
152
+ g("div", J, [
153
+ (p(!0), u(P, null, N(_(i), (s, d) => {
154
+ var f;
155
+ return p(), u("div", {
156
+ key: s.label,
157
+ class: L(["__uplot-legend-serie", [`__uplot-${(f = s.label) == null ? void 0 : f.toLowerCase()}`, `__uplot-i-${d}`]]),
158
+ style: k({ backgroundColor: s.show ? "" : "lightgrey" }),
159
+ onClick: (W) => m(d)
160
+ }, [
161
+ d !== 0 ? (p(), u("span", {
162
+ key: 0,
163
+ style: k({ color: s.stroke || "black" }),
164
+ class: "__uplot-legend-label"
165
+ }, h(s.label), 5)) : A("", !0),
166
+ s.value ? (p(), u("span", Q, h(s.value), 1)) : (p(), u("span", R, "--"))
167
+ ], 14, K);
168
+ }), 128))
169
+ ])
170
+ ])
171
+ ]));
172
+ }
173
+ });
174
+ export {
175
+ ee as Uplot
176
+ };
@@ -0,0 +1 @@
1
+ (function(i,e){typeof exports=="object"&&typeof module<"u"?e(exports,require("vue"),require("uplot"),require("@vueuse/core")):typeof define=="function"&&define.amd?define(["exports","vue","uplot","@vueuse/core"],e):(i=typeof globalThis<"u"?globalThis:i||self,e(i["change-name"]={},i.vue,i.Uplot,i.core))})(this,function(i,e,B,u){"use strict";const P="",w=c=>{if(typeof c=="object"&&c!==null){if(typeof Object.getPrototypeOf=="function"){const r=Object.getPrototypeOf(c);return r===Object.prototype||r===null}return Object.prototype.toString.call(c)==="[object Object]"}return!1},p=(...c)=>c.reduce((r,s)=>{if(Array.isArray(s))throw new TypeError("Arguments provided to ts-deepmerge must be objects, not arrays.");return s&&Object.keys(s).forEach(o=>{["__proto__","constructor","prototype"].includes(o)||(Array.isArray(r[o])&&Array.isArray(s[o])?r[o]=p.options.mergeArrays?Array.from(new Set(r[o].concat(s[o]))):s[o]:w(r[o])&&w(s[o])?r[o]=p(r[o],s[o]):r[o]=s[o])}),r},{}),S={mergeArrays:!0};p.options=S,p.withOptions=(c,...r)=>{p.options={mergeArrays:!0,...c};const s=p(...r);return p.options=S,s};const z={class:"__uplot-root"},E={key:0,class:"extra-info"},O=e.createElementVNode("br",null,null,-1),A={style:{"text-align":"left"}},V={class:"__uplot-legend"},j=["onClick"],C={key:1,class:"__uplot-legend-value"},D={key:2,class:"__uplot-legend-value"},T=e.defineComponent({__name:"Uplot",props:{options:null,data:null,resetScale:{type:Boolean,default:!1},showDebug:{type:Boolean},zoom:{default:()=>[null,null]},series:null},emits:["select","cursor","update:zoom"],setup(c,{expose:r,emit:s}){const o=c,h=e.ref(),{width:g,height:y}=u.useElementSize(h),b=u.useVModel(o,"zoom",s,{passive:!0}),d=u.useVModel(o,"series",s,{passive:!0}),N={title:void 0,legend:{show:!1},hooks:{init:[t=>{console.log("init",t),d.value=t.series.map((l,n)=>({label:l.label,stroke:typeof l.stroke=="function"?l.stroke(t,n):null,value:null,data:null,show:l.show}))}],setCursor:[t=>{s("cursor",t.cursor),d.value=t.series.map((l,n)=>{var f,m;return{label:l.label,stroke:typeof l.stroke=="function"?l.stroke(t,n):null,value:t.cursor.idx&&t.data[n][t.cursor.idx]?typeof l.value=="function"?l.value(t,t.data[n][t.cursor.idx],n,t.cursor.idx):t.data[n][t.cursor.idx]:null,data:(f=t.cursor)!=null&&f.idx?t.data[n][(m=t.cursor)==null?void 0:m.idx]:null,show:l.show}})}],setSelect:[t=>{s("select",t.select),console.log("setSelect",t.select),b.value=[t.posToVal(t.select.left,"x"),t.posToVal(t.select.left+t.select.width,"x")]}],setScale:[t=>{b.value=[t.scales.x.min||null,t.scales.x.max||null]}]}};let a;function k(){a&&a.destroy(),a=new B({width:100,height:100,...p(o.options,N)},o.data,h.value),o.zoom[0]!==null&&o.zoom[1]!==null&&a.setScale("x",{min:o.zoom[0],max:o.zoom[1]}),setTimeout(()=>{x()},0)}e.onMounted(()=>{k()}),e.watch([g,y],()=>{e.nextTick(()=>{x()})}),e.watch(o.data,t=>{if(o.resetScale){a.setData(t);return}a.setData(t,!0),a.redraw()}),e.watch(o.options,(t,l)=>{console.log("watch options",t,l),k()});function x(){a.setSize({width:g.value,height:y.value}),console.log("resize",a)}function _(t){var l;a.setSeries(t,{show:!a.series[t].show}),(l=d.value)!=null&&l[t]&&(d.value[t].show=a.series[t].show)}return r({toggleShow:_}),(t,l)=>(e.openBlock(),e.createElementBlock("div",z,[e.renderSlot(t.$slots,"header",{series:e.unref(d),toggleShow:_}),e.createElementVNode("div",{ref_key:"el",ref:h,class:"__uplot"},null,512),c.showDebug?(e.openBlock(),e.createElementBlock("div",E,[e.createTextVNode(e.toDisplayString(e.unref(g))+" x "+e.toDisplayString(e.unref(y))+" ",1),O,e.createElementVNode("pre",A,e.toDisplayString(e.unref(d)),1)])):e.createCommentVNode("",!0),e.renderSlot(t.$slots,"footer",{series:e.unref(d),toggleShow:_},()=>[e.createElementVNode("div",V,[(e.openBlock(!0),e.createElementBlock(e.Fragment,null,e.renderList(e.unref(d),(n,f)=>{var m;return e.openBlock(),e.createElementBlock("div",{key:n.label,class:e.normalizeClass(["__uplot-legend-serie",[`__uplot-${(m=n.label)==null?void 0:m.toLowerCase()}`,`__uplot-i-${f}`]]),style:e.normalizeStyle({backgroundColor:n.show?"":"lightgrey"}),onClick:$=>_(f)},[f!==0?(e.openBlock(),e.createElementBlock("span",{key:0,style:e.normalizeStyle({color:n.stroke||"black"}),class:"__uplot-legend-label"},e.toDisplayString(n.label),5)):e.createCommentVNode("",!0),n.value?(e.openBlock(),e.createElementBlock("span",C,e.toDisplayString(n.value),1)):(e.openBlock(),e.createElementBlock("span",D,"--"))],14,j)}),128))])])]))}}),U="";i.Uplot=T,Object.defineProperty(i,Symbol.toStringTag,{value:"Module"})});
package/dist/style.css ADDED
@@ -0,0 +1 @@
1
+ .uplot,.uplot *,.uplot *:before,.uplot *:after{box-sizing:border-box}.uplot{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,"Apple Color Emoji","Segoe UI Emoji",Segoe UI Symbol,"Noto Color Emoji";line-height:1.5;width:min-content}.u-title{text-align:center;font-size:18px;font-weight:700}.u-wrap{position:relative;user-select:none}.u-over,.u-under{position:absolute}.u-under{overflow:hidden}.uplot canvas{display:block;position:relative;width:100%;height:100%}.u-axis{position:absolute}.u-legend{font-size:14px;margin:auto;text-align:center}.u-inline{display:block}.u-inline *{display:inline-block}.u-inline tr{margin-right:16px}.u-legend th{font-weight:600}.u-legend th>*{vertical-align:middle;display:inline-block}.u-legend .u-marker{width:1em;height:1em;margin-right:4px;background-clip:padding-box!important}.u-inline.u-live th:after{content:":";vertical-align:middle}.u-inline:not(.u-live) .u-value{display:none}.u-series>*{padding:4px}.u-series th{cursor:pointer}.u-legend .u-off>*{opacity:.3}.u-select{background:rgba(0,0,0,.07);position:absolute;pointer-events:none}.u-cursor-x,.u-cursor-y{position:absolute;left:0;top:0;pointer-events:none;will-change:transform;z-index:100}.u-hz .u-cursor-x,.u-vt .u-cursor-y{height:100%;border-right:1px dashed #607D8B}.u-hz .u-cursor-y,.u-vt .u-cursor-x{width:100%;border-bottom:1px dashed #607D8B}.u-cursor-pt{position:absolute;top:0;left:0;border-radius:50%;border:0 solid;pointer-events:none;will-change:transform;z-index:100;background-clip:padding-box!important}.u-axis.u-off,.u-select.u-off,.u-cursor-x.u-off,.u-cursor-y.u-off,.u-cursor-pt.u-off{display:none}.__uplot{flex:1 1 auto;min-height:70px;position:relative}.__uplot-root{display:flex;flex-direction:column;width:100%;height:100%}.__uplot .uplot{position:absolute;top:0;left:0;right:0;bottom:0}.__uplot-legend{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;align-content:center;padding:.5em;gap:.25em;font-size:10px}.__uplot-legend-serie{display:flex;flex-direction:column;flex-wrap:nowrap;justify-content:space-between;align-items:center;align-content:center;padding:5px;background-color:#fff;color:#000;border:1px solid lightgray;border-radius:5px;min-width:6em;min-height:4em}.__uplot-i-0{min-width:12em;justify-content:end}.extra-info{position:absolute;bottom:0;left:0;background-color:#fff;color:#000;padding:5px;border:1px solid black}
@@ -0,0 +1,24 @@
1
+ type TAllKeys<T> = T extends any ? keyof T : never;
2
+ type TIndexValue<T, K extends PropertyKey, D = never> = T extends any ? K extends keyof T ? T[K] : D : never;
3
+ type TPartialKeys<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>> extends infer O ? {
4
+ [P in keyof O]: O[P];
5
+ } : never;
6
+ type TFunction = (...a: any[]) => any;
7
+ type TPrimitives = string | number | boolean | bigint | symbol | Date | TFunction;
8
+ type TMerged<T> = [T] extends [Array<any>] ? {
9
+ [K in keyof T]: TMerged<T[K]>;
10
+ } : [T] extends [TPrimitives] ? T : [T] extends [object] ? TPartialKeys<{
11
+ [K in TAllKeys<T>]: TMerged<TIndexValue<T, K>>;
12
+ }, never> : T;
13
+ interface IObject {
14
+ [key: string]: any;
15
+ }
16
+ declare const merge: {
17
+ <T extends IObject[]>(...objects: T): TMerged<T[number]>;
18
+ options: IOptions;
19
+ withOptions<T_1 extends IObject[]>(options: Partial<IOptions>, ...objects: T_1): TMerged<T_1[number]>;
20
+ };
21
+ interface IOptions {
22
+ mergeArrays: boolean;
23
+ }
24
+ export default merge;
package/package.json ADDED
@@ -0,0 +1,69 @@
1
+ {
2
+ "name": "@flowtools/uplot",
3
+ "version": "0.0.1",
4
+ "description": "Modern vue component for uplot",
5
+ "keywords": [
6
+ "add keywords"
7
+ ],
8
+ "homepage": "https://github.com/flow-tools/uplot",
9
+ "bugs": "https://github.com/flow-tools/uplot",
10
+ "author": "add author",
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/flow-tools/uplot.git"
14
+ },
15
+ "funding": "https://github.com/sponsors/flow-tools",
16
+ "engines": {
17
+ "node": ">=16"
18
+ },
19
+ "packageManager": "pnpm@7.28.0",
20
+ "exports": {
21
+ ".": {
22
+ "import": "./dist/index.es.js",
23
+ "types": "./dist/index.d.ts"
24
+ }
25
+ },
26
+ "main": "./dist/index.es.js",
27
+ "types": "./dist/index.d.ts",
28
+ "files": [
29
+ "dist",
30
+ "LICENSE",
31
+ "README.md"
32
+ ],
33
+ "license": "MIT",
34
+ "peerDependencies": {
35
+ "@vueuse/core": "^9.13.0",
36
+ "uplot": "^1.6.24",
37
+ "vue": "=>3.2.47"
38
+ },
39
+ "devDependencies": {
40
+ "@antfu/eslint-config": "^0.36.0",
41
+ "@vitejs/plugin-vue": "^4.0.0",
42
+ "@vitest/coverage-c8": "^0.29.2",
43
+ "@vue/test-utils": "^2.3.1",
44
+ "@vueuse/core": "^9.13.0",
45
+ "bumpp": "^9.0.0",
46
+ "eslint": "^8.35.0",
47
+ "jsdom": "^21.1.0",
48
+ "typescript": "^4.9.5",
49
+ "uplot": "^1.6.24",
50
+ "vi-canvas-mock": "^1.0.0",
51
+ "vite": "^4.1.4",
52
+ "vite-plugin-dts": "^2.1.0",
53
+ "vitest": "^0.29.2",
54
+ "vue": "^3.2.47"
55
+ },
56
+ "publishConfig": {
57
+ "access": "public"
58
+ },
59
+ "scripts": {
60
+ "build": "vite build --mode production",
61
+ "dev": "vite",
62
+ "release": "pnpm build && bumpp --commit --push --tag && pnpm publish",
63
+ "lint": "eslint --ext .ts,.tsx,.js,.jsx .",
64
+ "lint:fix": "eslint --ext .ts,.tsx,.js,.jsx . --fix",
65
+ "test": "vitest",
66
+ "test:watch": "vitest --watch",
67
+ "coverage": "vitest run --coverage"
68
+ }
69
+ }