vite-tampermonkey 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,34 @@
1
+ import type { ExternalResource, TampermonkeyMeta } from './types';
2
+ /**
3
+ * 元数据构造器
4
+ */
5
+ export declare class TampermonkeyMetaBuilder {
6
+ private lines;
7
+ add(key: string, value?: string | string[] | boolean): this;
8
+ addRecord(record?: Record<string, string | string[] | true>): this;
9
+ private push;
10
+ build(): string;
11
+ }
12
+ export declare class TampermonkeyBuilder {
13
+ private cssCode;
14
+ private jsCode;
15
+ private meta;
16
+ private external?;
17
+ private externalResources;
18
+ constructor(meta: TampermonkeyMeta, external?: ExternalResource);
19
+ /**
20
+ * 添加CSS代码
21
+ * @param source 源码
22
+ */
23
+ addCss(source: any): void;
24
+ /**
25
+ * 添加JS代码
26
+ * @param code 代码
27
+ */
28
+ addCode(code: string): void;
29
+ private getGrant;
30
+ private getExternalResources;
31
+ buildHeader(isDev?: boolean, filename?: string): string;
32
+ build(isDev?: boolean): string;
33
+ }
34
+ //# sourceMappingURL=builder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builder.d.ts","sourceRoot":"","sources":["../src/builder.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAClE;;GAEG;AACH,qBAAa,uBAAuB;IAClC,OAAO,CAAC,KAAK,CAAmC;IAEhD,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,OAAO;IAqBpD,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC;IAgB3D,OAAO,CAAC,IAAI;IAKZ,KAAK;CAIN;AAOD,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,OAAO,CAAc;IAC7B,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,IAAI,CAAwB;IACpC,OAAO,CAAC,QAAQ,CAAC,CAAmB;IACpC,OAAO,CAAC,iBAAiB,CAA2B;gBAExC,IAAI,EAAE,gBAAgB,EAAE,QAAQ,CAAC,EAAE,gBAAgB;IAM/D;;;OAGG;IACH,MAAM,CAAC,MAAM,EAAE,GAAG;IAOlB;;;OAGG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM;IAIpB,OAAO,CAAC,QAAQ;IAQhB,OAAO,CAAC,oBAAoB;IAkB5B,WAAW,CAAC,KAAK,GAAE,OAAe,EAAE,QAAQ,CAAC,EAAE,MAAM;IAgDrD,KAAK,CAAC,KAAK,GAAE,OAAe;CAkB7B"}
@@ -0,0 +1,156 @@
1
+ import crypto from 'crypto';
2
+ import fs from 'fs';
3
+ /**
4
+ * 元数据构造器
5
+ */
6
+ export class TampermonkeyMetaBuilder {
7
+ lines = ['// ==UserScript=='];
8
+ add(key, value) {
9
+ if (value == null)
10
+ return this;
11
+ if (key === 'icon') {
12
+ // 判断是否是本地文件
13
+ if (typeof value === 'string' && !value.startsWith('http')) {
14
+ const iconBase64 = fs.readFileSync(value, 'base64');
15
+ value = `data:image/png;base64,${iconBase64}`;
16
+ }
17
+ }
18
+ if (Array.isArray(value)) {
19
+ const set = new Set(value);
20
+ set.forEach((v) => this.push(key, v));
21
+ }
22
+ else {
23
+ this.push(key, value === true ? '' : String(value));
24
+ }
25
+ return this;
26
+ }
27
+ addRecord(record) {
28
+ if (!record)
29
+ return this;
30
+ for (const [k, v] of Object.entries(record)) {
31
+ const key = k.replace(/([A-Z])/g, '-$1').toLowerCase();
32
+ if (Array.isArray(v)) {
33
+ v.forEach((val) => this.push(key, String(val)));
34
+ }
35
+ else {
36
+ this.push(key, v === true ? '' : String(v));
37
+ }
38
+ }
39
+ return this;
40
+ }
41
+ push(key, value) {
42
+ const spacing = ' '.repeat(Math.max(1, 12 - key.length));
43
+ this.lines.push(`// @${key}${spacing}${value}`.trimEnd());
44
+ }
45
+ build() {
46
+ return [...this.lines, '// ==/UserScript==', ''].join('\n');
47
+ }
48
+ }
49
+ export class TampermonkeyBuilder {
50
+ cssCode = '';
51
+ jsCode = '';
52
+ meta = {};
53
+ external;
54
+ externalResources = [];
55
+ constructor(meta, external) {
56
+ this.meta = meta;
57
+ this.external = external;
58
+ this.externalResources = this.getExternalResources();
59
+ this.addCode('var global = window;\n');
60
+ }
61
+ /**
62
+ * 添加CSS代码
63
+ * @param source 源码
64
+ */
65
+ addCss(source) {
66
+ this.cssCode +=
67
+ typeof source === 'string'
68
+ ? source
69
+ : Buffer.from(source).toString('utf8');
70
+ }
71
+ /**
72
+ * 添加JS代码
73
+ * @param code 代码
74
+ */
75
+ addCode(code) {
76
+ this.jsCode += code;
77
+ }
78
+ getGrant() {
79
+ let grant = this.meta.grant ?? [];
80
+ if (typeof grant === 'string') {
81
+ grant = [grant];
82
+ }
83
+ return grant;
84
+ }
85
+ getExternalResources() {
86
+ const externalResources = Array.from(new Set(Object.values(this.external?.resources ?? {})));
87
+ return externalResources.map((resource) => {
88
+ // md5(resource)
89
+ const md5 = crypto
90
+ .createHash('md5')
91
+ .update(resource)
92
+ .digest('hex')
93
+ .substring(0, 8);
94
+ return {
95
+ name: md5,
96
+ url: resource,
97
+ };
98
+ });
99
+ }
100
+ buildHeader(isDev = false, filename) {
101
+ const name = isDev ? `${this.meta.name} (Dev)` : this.meta.name;
102
+ const grant = this.getGrant();
103
+ if (this.cssCode.trim().length > 0) {
104
+ grant.push('GM_addStyle');
105
+ }
106
+ if (this.externalResources.length > 0 && !isDev) {
107
+ grant.push('GM_getResourceText');
108
+ }
109
+ const requires = [
110
+ ...(this.meta.require ?? []),
111
+ ...(isDev ? [] : Object.values(this.external?.modules ?? {})),
112
+ ...(filename && isDev ? [filename] : []),
113
+ ];
114
+ // 处理外部资源
115
+ const resources = [
116
+ ...(this.meta.resource ?? []),
117
+ ...(isDev && this.externalResources.length > 0
118
+ ? []
119
+ : this.externalResources.map((resource) => `${resource.name} ${resource.url}`)),
120
+ ];
121
+ return new TampermonkeyMetaBuilder()
122
+ .add('name', name)
123
+ .add('namespace', this.meta.namespace)
124
+ .add('version', this.meta.version)
125
+ .add('description', this.meta.description)
126
+ .add('author', this.meta.author)
127
+ .add('match', this.meta.match)
128
+ .add('include', this.meta.include)
129
+ .add('grant', this.meta.grant)
130
+ .add('run-at', this.meta.runAt)
131
+ .add('icon', this.meta.icon)
132
+ .add('require', requires)
133
+ .add('resource', resources)
134
+ .addRecord(this.meta.extra)
135
+ .add('license', this.meta.license)
136
+ .add('downloadUrl', this.meta.downloadUrl)
137
+ .add('updateUrl', this.meta.updateUrl)
138
+ .build();
139
+ }
140
+ build(isDev = false) {
141
+ const header = !isDev ? this.buildHeader(isDev) : '';
142
+ if (!isDev) {
143
+ this.externalResources.forEach((resource) => {
144
+ if (resource.url.endsWith('.css')) {
145
+ this.jsCode =
146
+ 'GM_addStyle(GM_getResourceText("' +
147
+ resource.name +
148
+ '"));\n\n' +
149
+ this.jsCode;
150
+ }
151
+ });
152
+ }
153
+ const code = `${header}\nGM_addStyle(${JSON.stringify(this.cssCode)});\n${this.jsCode}`;
154
+ return code;
155
+ }
156
+ }
@@ -0,0 +1,7 @@
1
+ import type { Plugin } from 'vite';
2
+ import type { TampermonkeyPluginOptions } from './types';
3
+ /**
4
+ * 油猴插件
5
+ */
6
+ export declare function tampermonkeyPlugin(options?: TampermonkeyPluginOptions): Plugin;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,MAAM,CAAC;AAGnC,OAAO,KAAK,EAAoB,yBAAyB,EAAE,MAAM,SAAS,CAAC;AAU3E;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,OAAO,GAAE,yBAA8B,GACtC,MAAM,CA8HR"}
package/dist/index.js ADDED
@@ -0,0 +1,124 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { resolve } from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+ import { TampermonkeyBuilder } from './builder';
5
+ import { createExternalResolver, resolveMeta } from './resovler';
6
+ const DEFAULT_META = {
7
+ namespace: 'https://github.com/',
8
+ runAt: 'document-idle',
9
+ grant: [],
10
+ };
11
+ const DEFAULT_PKG_NAME = 'userscript';
12
+ /**
13
+ * 油猴插件
14
+ */
15
+ export function tampermonkeyPlugin(options = {}) {
16
+ const { root = process.cwd(), entry = 'src/main.tsx', packageJson = resolve(root, 'package.json'), meta: userMeta = {}, } = options;
17
+ // 解析package.json
18
+ const pkg = JSON.parse(readFileSync(packageJson, 'utf8'));
19
+ const pkgName = pkg.name || DEFAULT_PKG_NAME;
20
+ const baseName = pkgName.replace(/^@/, '').replace(/\//g, '-');
21
+ // 预处理外部资源
22
+ const externalResolver = createExternalResolver(options.external);
23
+ const meta = resolveMeta(pkg, { ...DEFAULT_META, ...userMeta });
24
+ // 创建构建器
25
+ const builder = new TampermonkeyBuilder(meta, options.external);
26
+ let isDev = false;
27
+ const fileName = `user.js`;
28
+ let outDirAbs = '';
29
+ return {
30
+ name: 'vite-tampermonkey-plugin',
31
+ enforce: 'post',
32
+ apply: 'build',
33
+ /**
34
+ * 配置
35
+ * @param mode 模式
36
+ * @returns 配置
37
+ */
38
+ config({ mode }) {
39
+ isDev = mode === 'development';
40
+ return {
41
+ publicDir: false,
42
+ build: {
43
+ lib: {
44
+ entry: resolve(root, entry),
45
+ name: baseName.replace(/-/g, '_'),
46
+ fileName: () => fileName,
47
+ formats: ['iife'],
48
+ },
49
+ minify: false,
50
+ cssMinify: true,
51
+ sourcemap: false,
52
+ emptyOutDir: true,
53
+ cssCodeSplit: false,
54
+ rolldownOptions: isDev ? undefined : {
55
+ external: externalResolver.isModule,
56
+ output: {
57
+ globals: externalResolver.getGlobal,
58
+ comments: false,
59
+ },
60
+ },
61
+ },
62
+ define: {
63
+ 'process.env.NODE_ENV': JSON.stringify(mode),
64
+ },
65
+ };
66
+ },
67
+ configResolved(config) {
68
+ outDirAbs = resolve(root, config.build.outDir);
69
+ },
70
+ /**
71
+ * 拦截外部资源,防止写入到输出文件,之后自定义代码注入到用户脚本中.
72
+ * @param code
73
+ * @param id
74
+ */
75
+ load(id) {
76
+ if (externalResolver.isResource(id) && !isDev) {
77
+ return {
78
+ code: '',
79
+ map: null,
80
+ };
81
+ }
82
+ return null;
83
+ },
84
+ /**
85
+ * 生成打包
86
+ * @param _options 选项
87
+ * @param bundle 打包
88
+ */
89
+ generateBundle(_, bundle) {
90
+ // 先处理一次css代码
91
+ for (const key of Object.keys(bundle)) {
92
+ const file = bundle[key];
93
+ if (file.type === 'asset' && file.fileName.endsWith('.css')) {
94
+ builder.addCss(file.source);
95
+ delete bundle[key];
96
+ }
97
+ }
98
+ // 再处理js代码
99
+ for (const key of Object.keys(bundle)) {
100
+ const file = bundle[key];
101
+ // 不是入口文件或者不是chunk,跳过
102
+ if (file.type !== 'chunk' || !file.isEntry)
103
+ continue;
104
+ builder.addCode(file.code);
105
+ if (isDev) {
106
+ const fileUrl = pathToFileURL(resolve(outDirAbs, fileName)).href;
107
+ const header = builder.buildHeader(isDev, fileUrl);
108
+ this.emitFile({
109
+ type: 'asset',
110
+ fileName: `user.dev.js`,
111
+ source: header,
112
+ });
113
+ }
114
+ file.code = builder.build(isDev);
115
+ // 产生一个meta.json文件
116
+ this.emitFile({
117
+ type: 'asset',
118
+ fileName: `meta.json`,
119
+ source: JSON.stringify(meta, null, 2),
120
+ });
121
+ }
122
+ },
123
+ };
124
+ }
@@ -0,0 +1,20 @@
1
+ import type { ExternalResource, PackageJson, TampermonkeyMeta } from './types';
2
+ /**
3
+ * 解析元数据
4
+ * @param pkg package.json
5
+ * @param userMeta 用户元数据
6
+ * @returns 元数据
7
+ */
8
+ export declare function resolveMeta(pkg: PackageJson, userMeta: TampermonkeyMeta): TampermonkeyMeta;
9
+ /**
10
+ * 创建外部资源解析器
11
+ * @param externals 外部资源
12
+ * @param globals 全局变量
13
+ * @returns 外部资源解析器
14
+ */
15
+ export declare function createExternalResolver(external?: ExternalResource): {
16
+ isModule(id: string): boolean;
17
+ getGlobal(id: string): string;
18
+ isResource(id: string): boolean;
19
+ };
20
+ //# sourceMappingURL=resovler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resovler.d.ts","sourceRoot":"","sources":["../src/resovler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC/E;;;;;GAKG;AACH,wBAAgB,WAAW,CACzB,GAAG,EAAE,WAAW,EAChB,QAAQ,EAAE,gBAAgB,GACzB,gBAAgB,CAWlB;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,QAAQ,CAAC,EAAE,gBAAgB;iBAajD,MAAM;kBAIL,MAAM;mBAGL,MAAM;EAIxB"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * 解析元数据
3
+ * @param pkg package.json
4
+ * @param userMeta 用户元数据
5
+ * @returns 元数据
6
+ */
7
+ export function resolveMeta(pkg, userMeta) {
8
+ const grants = new Set(userMeta.grant ?? []);
9
+ return {
10
+ name: pkg.name,
11
+ version: pkg.version,
12
+ description: pkg.description,
13
+ author: pkg.author,
14
+ ...userMeta,
15
+ grant: Array.from(grants),
16
+ };
17
+ }
18
+ /**
19
+ * 创建外部资源解析器
20
+ * @param externals 外部资源
21
+ * @param globals 全局变量
22
+ * @returns 外部资源解析器
23
+ */
24
+ export function createExternalResolver(external) {
25
+ if (!external)
26
+ return {
27
+ isModule: () => false,
28
+ getGlobal: () => '',
29
+ isResource: () => false,
30
+ };
31
+ const modules = Object.keys(external.modules ?? {});
32
+ const globals = external.globals ?? {};
33
+ const resources = Object.keys(external.resources ?? {});
34
+ return {
35
+ isModule(id) {
36
+ return modules.some((name) => id === name || id.startsWith(`${name}/`));
37
+ },
38
+ getGlobal(id) {
39
+ return globals[id] ?? id.replace(/[^\w$]/g, '_');
40
+ },
41
+ isResource(id) {
42
+ return resources.some((name) => id.endsWith(name));
43
+ },
44
+ };
45
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * 油猴元数据
3
+ */
4
+ export type TampermonkeyMeta = {
5
+ name?: string;
6
+ namespace?: string;
7
+ version?: string;
8
+ description?: string;
9
+ author?: string;
10
+ /** 单条或多条 @match */
11
+ match?: string | string[];
12
+ /** 单条或多条 @include */
13
+ include?: string | string[];
14
+ /** 单条或多条 @grant */
15
+ grant?: string | string[];
16
+ runAt?: 'document-start' | 'document-end' | 'document-idle';
17
+ icon?: string;
18
+ exclude?: string | string[];
19
+ require?: string | string[];
20
+ connect?: string | string[];
21
+ resource?: string | string[];
22
+ /** 其它 // @key value,如 { noframes: '' } -> // @noframes */
23
+ extra?: Record<string, string | string[] | true>;
24
+ license?: string;
25
+ downloadUrl?: string;
26
+ updateUrl?: string;
27
+ };
28
+ /**
29
+ * 外部资源
30
+ */
31
+ export type ExternalResource = {
32
+ /** 外部资源(JS/CSS 等),用于生成 @require/@resource */
33
+ resources?: Record<string, string>;
34
+ /** 外部模块(用于 rolldown external + @require) */
35
+ modules?: Record<string, string>;
36
+ /** 外部全局变量映射 */
37
+ globals?: Record<string, string>;
38
+ };
39
+ /**
40
+ * 油猴插件选项
41
+ */
42
+ export type TampermonkeyPluginOptions = {
43
+ /** 用户脚本入口,默认 `src/userscript.tsx`(相对 root) */
44
+ entry?: string;
45
+ /** 工程根目录,默认 `process.cwd()` */
46
+ root?: string;
47
+ /** package.json 路径,默认 `<root>/package.json` */
48
+ packageJson?: string;
49
+ /** 覆盖 / 补充 UserScript 元数据 */
50
+ meta?: TampermonkeyMeta;
51
+ /** 外部资源 */
52
+ external?: ExternalResource;
53
+ };
54
+ /**
55
+ * package.json需要包含的字段
56
+ */
57
+ export type PackageJson = {
58
+ name: string;
59
+ version: string;
60
+ description?: string;
61
+ author?: string;
62
+ };
63
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,qBAAqB;IACrB,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,mBAAmB;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B,KAAK,CAAC,EAAE,gBAAgB,GAAG,cAAc,GAAG,eAAe,CAAC;IAC5D,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC5B,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC7B,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,IAAI,CAAC,CAAC;IACjD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,6CAA6C;IAC7C,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnC,4CAA4C;IAC5C,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,eAAe;IACf,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClC,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,8CAA8C;IAC9C,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,+BAA+B;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,+CAA+C;IAC/C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,6BAA6B;IAC7B,IAAI,CAAC,EAAE,gBAAgB,CAAC;IACxB,WAAW;IACX,QAAQ,CAAC,EAAE,gBAAgB,CAAC;CAC7B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC"}
package/dist/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "vite-tampermonkey",
3
+ "version": "1.0.0",
4
+ "description": "Vite plugin for Tampermonkey / userscript builds",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/index.d.ts",
11
+ "import": "./dist/index.js",
12
+ "default": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc -p tsconfig.json",
20
+ "prepublishOnly": "npm run build",
21
+ "test": "echo \"Error: no test specified\" && exit 1"
22
+ },
23
+ "keywords": [
24
+ "vite",
25
+ "vite-plugin",
26
+ "tampermonkey",
27
+ "userscript"
28
+ ],
29
+ "author": "koyori",
30
+ "license": "MIT",
31
+ "peerDependencies": {
32
+ "vite": "^8.0.0"
33
+ },
34
+ "devDependencies": {
35
+ "@types/node": "^22.10.0",
36
+ "typescript": "^5.7.0",
37
+ "vite": "^8.0.0"
38
+ }
39
+ }