browser-extension-utils 0.2.1 → 0.3.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/README.md +1 -3
- package/lib/add-element.ts +38 -0
- package/lib/create-element.ts +11 -0
- package/lib/dom-utils.ts +232 -0
- package/lib/global.d.ts +154 -0
- package/lib/index.ts +358 -0
- package/lib/set-attributes.ts +42 -0
- package/lib/userscript.ts +94 -0
- package/package.json +20 -31
- package/lib/index.d.ts +0 -207
- package/lib/index.js +0 -548
- package/lib/userscript.js +0 -86
package/lib/index.ts
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
import {
|
|
2
|
+
doc,
|
|
3
|
+
win,
|
|
4
|
+
addEventListener,
|
|
5
|
+
removeEventListener,
|
|
6
|
+
getRootElement,
|
|
7
|
+
} from './dom-utils'
|
|
8
|
+
import { setAttributes } from './set-attributes'
|
|
9
|
+
import { createElement } from './create-element'
|
|
10
|
+
import { addElement } from './add-element'
|
|
11
|
+
|
|
12
|
+
export * from './dom-utils'
|
|
13
|
+
export * from './set-attributes'
|
|
14
|
+
export * from './create-element'
|
|
15
|
+
export * from './add-element'
|
|
16
|
+
|
|
17
|
+
export const uniq = <T>(array: T[]): T[] => [...new Set(array)]
|
|
18
|
+
|
|
19
|
+
export const $ = (
|
|
20
|
+
selector: string,
|
|
21
|
+
context: Element | Document = doc
|
|
22
|
+
): HTMLElement | null => context.querySelector(selector)!
|
|
23
|
+
|
|
24
|
+
export const $$ = (
|
|
25
|
+
selector: string,
|
|
26
|
+
context: Element | Document = doc
|
|
27
|
+
): HTMLElement[] =>
|
|
28
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
[...context.querySelectorAll(selector)] as HTMLElement[]
|
|
31
|
+
|
|
32
|
+
export const addStyle = (
|
|
33
|
+
style: string,
|
|
34
|
+
attributes?: Record<string, unknown>
|
|
35
|
+
): HTMLStyleElement => {
|
|
36
|
+
const element = createElement('style', {
|
|
37
|
+
textContent: style,
|
|
38
|
+
...attributes,
|
|
39
|
+
}) as HTMLStyleElement
|
|
40
|
+
getRootElement(1).append(element)
|
|
41
|
+
return element
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type MenuCallback = (event?: MouseEvent | KeyboardEvent) => void
|
|
45
|
+
|
|
46
|
+
export const registerMenuCommand = (
|
|
47
|
+
_name?: string,
|
|
48
|
+
_callback?: MenuCallback,
|
|
49
|
+
_options?: Parameters<typeof GM_registerMenuCommand>[2]
|
|
50
|
+
): Promise<string | number | undefined> | undefined => undefined
|
|
51
|
+
|
|
52
|
+
export const extendHistoryApi = (): void => {
|
|
53
|
+
// https://dirask.com/posts/JavaScript-on-location-changed-event-on-url-changed-event-DKeyZj
|
|
54
|
+
const pushState = history.pushState
|
|
55
|
+
const replaceState = history.replaceState
|
|
56
|
+
|
|
57
|
+
history.pushState = function (...args) {
|
|
58
|
+
pushState.apply(history, args)
|
|
59
|
+
globalThis.dispatchEvent(new Event('pushstate'))
|
|
60
|
+
globalThis.dispatchEvent(new Event('locationchange'))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
history.replaceState = function (...args) {
|
|
64
|
+
replaceState.apply(history, args)
|
|
65
|
+
globalThis.dispatchEvent(new Event('replacestate'))
|
|
66
|
+
globalThis.dispatchEvent(new Event('locationchange'))
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
globalThis.addEventListener('popstate', () => {
|
|
70
|
+
globalThis.dispatchEvent(new Event('locationchange'))
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
// Usage example:
|
|
74
|
+
// window.addEventListener("locationchange", function () {
|
|
75
|
+
// console.log("onlocationchange event occurred!")
|
|
76
|
+
// })
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// eslint-disable-next-line no-script-url
|
|
80
|
+
export const actionHref = 'javascript:;'
|
|
81
|
+
|
|
82
|
+
export const getOffsetPosition = (
|
|
83
|
+
element: HTMLElement | undefined,
|
|
84
|
+
referElement?: HTMLElement
|
|
85
|
+
): {
|
|
86
|
+
top: number
|
|
87
|
+
left: number
|
|
88
|
+
} => {
|
|
89
|
+
const position = { top: 0, left: 0 }
|
|
90
|
+
referElement = referElement || doc.body
|
|
91
|
+
|
|
92
|
+
while (element && element !== referElement) {
|
|
93
|
+
position.top += element.offsetTop
|
|
94
|
+
position.left += element.offsetLeft
|
|
95
|
+
element = element.offsetParent as HTMLElement
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return position
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const runOnceCache: Record<string, any> = {}
|
|
102
|
+
export const runOnce = async (
|
|
103
|
+
key: string,
|
|
104
|
+
func: () => Promise<any> | any
|
|
105
|
+
): Promise<any> => {
|
|
106
|
+
if (Object.hasOwn(runOnceCache, key)) {
|
|
107
|
+
return runOnceCache[key]
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
111
|
+
const result = await func()
|
|
112
|
+
|
|
113
|
+
if (key) {
|
|
114
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
115
|
+
runOnceCache[key] = result
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return result
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const cacheStore: Record<string, any> = {}
|
|
122
|
+
const makeKey = (key: string | any[]) =>
|
|
123
|
+
Array.isArray(key) ? key.join(':') : key
|
|
124
|
+
|
|
125
|
+
export type Cache = {
|
|
126
|
+
get: (key: string | any[]) => any
|
|
127
|
+
add: (key: string | any[], value: any) => void
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export const cache: Cache = {
|
|
131
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
|
132
|
+
get: (key: string | any[]) => cacheStore[makeKey(key)],
|
|
133
|
+
add(key: string | any[], value: any) {
|
|
134
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
135
|
+
cacheStore[makeKey(key)] = value
|
|
136
|
+
},
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
export const sleep = async (time: number): Promise<any> =>
|
|
140
|
+
new Promise((resolve) => {
|
|
141
|
+
setTimeout(() => {
|
|
142
|
+
resolve(1)
|
|
143
|
+
}, time)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
export const parseInt10 = (
|
|
147
|
+
number: string | number | undefined,
|
|
148
|
+
defaultValue?: number
|
|
149
|
+
): number => {
|
|
150
|
+
if (typeof number === 'number' && !Number.isNaN(number)) {
|
|
151
|
+
return number
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (typeof defaultValue !== 'number') {
|
|
155
|
+
defaultValue = Number.NaN
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (!number) {
|
|
159
|
+
return defaultValue
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const result = Number.parseInt(String(number), 10)
|
|
163
|
+
return Number.isNaN(result) ? defaultValue : result
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const rootFuncArray: Array<() => void> = []
|
|
167
|
+
const headFuncArray: Array<() => void> = []
|
|
168
|
+
const bodyFuncArray: Array<() => void> = []
|
|
169
|
+
let headBodyObserver: MutationObserver
|
|
170
|
+
|
|
171
|
+
const startObserveHeadBodyExists = () => {
|
|
172
|
+
if (headBodyObserver) {
|
|
173
|
+
return
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
headBodyObserver = new MutationObserver(() => {
|
|
177
|
+
if (doc.head && doc.body) {
|
|
178
|
+
headBodyObserver.disconnect()
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
if (doc.documentElement && rootFuncArray.length > 0) {
|
|
182
|
+
for (const func of rootFuncArray) {
|
|
183
|
+
func()
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
rootFuncArray.length = 0
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (doc.head && headFuncArray.length > 0) {
|
|
190
|
+
for (const func of headFuncArray) {
|
|
191
|
+
func()
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
headFuncArray.length = 0
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (doc.body && bodyFuncArray.length > 0) {
|
|
198
|
+
for (const func of bodyFuncArray) {
|
|
199
|
+
func()
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
bodyFuncArray.length = 0
|
|
203
|
+
}
|
|
204
|
+
})
|
|
205
|
+
|
|
206
|
+
headBodyObserver.observe(doc, {
|
|
207
|
+
childList: true,
|
|
208
|
+
subtree: true,
|
|
209
|
+
})
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Run function when document.documentElement exsits.
|
|
214
|
+
*/
|
|
215
|
+
export const runWhenRootExists = (func: () => void): void => {
|
|
216
|
+
if (!doc.documentElement) {
|
|
217
|
+
rootFuncArray.push(func)
|
|
218
|
+
startObserveHeadBodyExists()
|
|
219
|
+
return
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
func()
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Run function when document.head exsits.
|
|
227
|
+
*/
|
|
228
|
+
export const runWhenHeadExists = (func: () => void): void => {
|
|
229
|
+
if (!doc.head) {
|
|
230
|
+
headFuncArray.push(func)
|
|
231
|
+
startObserveHeadBodyExists()
|
|
232
|
+
return
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
func()
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Run function when document.body exsits. The function executed before DOMContentLoaded.
|
|
240
|
+
*/
|
|
241
|
+
export const runWhenBodyExists = (func: () => void): void => {
|
|
242
|
+
if (!doc.body) {
|
|
243
|
+
bodyFuncArray.push(func)
|
|
244
|
+
startObserveHeadBodyExists()
|
|
245
|
+
return
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
func()
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Equals to jQuery.domready
|
|
253
|
+
*/
|
|
254
|
+
export const runWhenDomReady = (func: () => void): void => {
|
|
255
|
+
if (doc.readyState === 'interactive' || doc.readyState === 'complete') {
|
|
256
|
+
func()
|
|
257
|
+
return
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const handler = () => {
|
|
261
|
+
if (doc.readyState === 'interactive' || doc.readyState === 'complete') {
|
|
262
|
+
func()
|
|
263
|
+
removeEventListener(doc, 'readystatechange', handler)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
addEventListener(doc, 'readystatechange', handler)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export const isVisible = (element: HTMLElement): boolean => {
|
|
271
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
272
|
+
const el = element as any
|
|
273
|
+
|
|
274
|
+
if (typeof el.checkVisibility === 'function') {
|
|
275
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
276
|
+
return el.checkVisibility() as boolean
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return element.offsetParent !== null
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
export const isTouchScreen = (): boolean => 'ontouchstart' in win
|
|
283
|
+
|
|
284
|
+
export const isUrl = (text: string): boolean => /^https?:\/\//.test(text)
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
*
|
|
288
|
+
* @param { function } func
|
|
289
|
+
* @param { number } interval
|
|
290
|
+
* @returns
|
|
291
|
+
*/
|
|
292
|
+
export const throttle = (
|
|
293
|
+
func: (...args: any[]) => any,
|
|
294
|
+
interval: number
|
|
295
|
+
): ((...args: any[]) => void) => {
|
|
296
|
+
let timeoutId: any = null
|
|
297
|
+
let next = false
|
|
298
|
+
const handler = (...args: any[]) => {
|
|
299
|
+
if (timeoutId) {
|
|
300
|
+
next = true
|
|
301
|
+
} else {
|
|
302
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
303
|
+
// @ts-ignore
|
|
304
|
+
|
|
305
|
+
func.apply(this, args)
|
|
306
|
+
timeoutId = setTimeout(() => {
|
|
307
|
+
timeoutId = null
|
|
308
|
+
if (next) {
|
|
309
|
+
next = false
|
|
310
|
+
handler()
|
|
311
|
+
}
|
|
312
|
+
}, interval)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
return handler
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Compare two semantic version strings
|
|
321
|
+
* @param {string} v1 - First version string (e.g., "1.2.0")
|
|
322
|
+
* @param {string} v2 - Second version string (e.g., "1.1.5")
|
|
323
|
+
* @returns {number} - Returns 1 if v1 > v2, -1 if v1 < v2, 0 if equal
|
|
324
|
+
* @throws {Error} - Throws error for invalid version strings
|
|
325
|
+
*/
|
|
326
|
+
export function compareVersions(v1: string, v2: string): number {
|
|
327
|
+
// Input validation
|
|
328
|
+
if (typeof v1 !== 'string' || typeof v2 !== 'string') {
|
|
329
|
+
throw new TypeError('Version strings must be of type string')
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (!v1.trim() || !v2.trim()) {
|
|
333
|
+
throw new Error('Version strings cannot be empty')
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
// Validate version format (basic semantic versioning)
|
|
337
|
+
const versionRegex = /^\d+(\.\d+)*$/
|
|
338
|
+
if (!versionRegex.test(v1) || !versionRegex.test(v2)) {
|
|
339
|
+
throw new Error(
|
|
340
|
+
"Invalid version format. Use semantic versioning (e.g., '1.2.3')"
|
|
341
|
+
)
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const v1Parts = v1.split('.').map(Number)
|
|
345
|
+
const v2Parts = v2.split('.').map(Number)
|
|
346
|
+
const maxLength = Math.max(v1Parts.length, v2Parts.length)
|
|
347
|
+
|
|
348
|
+
for (let i = 0; i < maxLength; i++) {
|
|
349
|
+
const num1 = v1Parts[i] || 0 // Use logical OR for cleaner default assignment
|
|
350
|
+
const num2 = v2Parts[i] || 0
|
|
351
|
+
|
|
352
|
+
if (num1 !== num2) {
|
|
353
|
+
return num1 > num2 ? 1 : -1 // Simplified comparison
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
return 0 // Versions are equal
|
|
358
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {
|
|
2
|
+
setStyle,
|
|
3
|
+
createHTML,
|
|
4
|
+
addEventListener,
|
|
5
|
+
setAttribute,
|
|
6
|
+
} from './dom-utils'
|
|
7
|
+
|
|
8
|
+
export const setAttributes = (
|
|
9
|
+
element: HTMLElement | null | undefined,
|
|
10
|
+
attributes?: Record<string, unknown>
|
|
11
|
+
): HTMLElement | null | undefined => {
|
|
12
|
+
if (element && attributes) {
|
|
13
|
+
for (const name in attributes) {
|
|
14
|
+
if (Object.hasOwn(attributes, name)) {
|
|
15
|
+
const value = attributes[name]
|
|
16
|
+
if (value === undefined) {
|
|
17
|
+
continue
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (/^(value|textContent|innerText)$/.test(name)) {
|
|
21
|
+
;(element as any)[name] = value
|
|
22
|
+
} else if (/^(innerHTML)$/.test(name)) {
|
|
23
|
+
element.innerHTML = createHTML(value as string)
|
|
24
|
+
} else if (name === 'style') {
|
|
25
|
+
setStyle(element, value as string | Record<string, any>, true)
|
|
26
|
+
} else if (/on\w+/.test(name)) {
|
|
27
|
+
const type = name.slice(2)
|
|
28
|
+
addEventListener(
|
|
29
|
+
element,
|
|
30
|
+
type,
|
|
31
|
+
value as EventListenerOrEventListenerObject
|
|
32
|
+
)
|
|
33
|
+
} else {
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-base-to-string
|
|
35
|
+
setAttribute(element, name, String(value))
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return element
|
|
42
|
+
}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getRootElement,
|
|
3
|
+
setAttributes,
|
|
4
|
+
addElement as _addElement,
|
|
5
|
+
} from './index.js'
|
|
6
|
+
|
|
7
|
+
export * from './index.js'
|
|
8
|
+
|
|
9
|
+
export const addElement =
|
|
10
|
+
typeof GM_addElement === 'function'
|
|
11
|
+
? (
|
|
12
|
+
parentNode: HTMLElement | string | null | undefined,
|
|
13
|
+
tagName: string | HTMLElement,
|
|
14
|
+
attributes?: Record<string, unknown>
|
|
15
|
+
): HTMLElement | undefined => {
|
|
16
|
+
if (typeof parentNode === 'string') {
|
|
17
|
+
return addElement(
|
|
18
|
+
null,
|
|
19
|
+
parentNode,
|
|
20
|
+
tagName as unknown as Record<string, unknown>
|
|
21
|
+
)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (!tagName) {
|
|
25
|
+
return undefined
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (!parentNode) {
|
|
29
|
+
parentNode = /^(script|link|style|meta)$/.test(tagName as string)
|
|
30
|
+
? getRootElement(1)
|
|
31
|
+
: getRootElement(2)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (typeof tagName === 'string') {
|
|
35
|
+
let attributes1: Record<string, string> | undefined
|
|
36
|
+
let attributes2: Record<string, unknown> | undefined
|
|
37
|
+
if (attributes) {
|
|
38
|
+
const entries1: Array<[string, unknown]> = []
|
|
39
|
+
const entries2: Array<[string, unknown]> = []
|
|
40
|
+
for (const entry of Object.entries(attributes)) {
|
|
41
|
+
// Some userscript managers do not support innerHTML
|
|
42
|
+
// Stay do not support multiple classes: GM_addElement('div', {"class": "a b"}). Remove `|class` when it is supported
|
|
43
|
+
if (/^(on\w+|innerHTML|class)$/.test(entry[0])) {
|
|
44
|
+
entries2.push(entry)
|
|
45
|
+
} else {
|
|
46
|
+
entries1.push(entry)
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
attributes1 = Object.fromEntries(entries1) as Record<string, string>
|
|
51
|
+
attributes2 = Object.fromEntries(entries2) as Record<
|
|
52
|
+
string,
|
|
53
|
+
unknown
|
|
54
|
+
>
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const element = GM_addElement(tagName, attributes1)
|
|
59
|
+
setAttributes(element, attributes2)
|
|
60
|
+
parentNode.append(element)
|
|
61
|
+
return element
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.error('GM_addElement error:', error)
|
|
64
|
+
return _addElement(parentNode, tagName, attributes)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// tagName: HTMLElement
|
|
69
|
+
setAttributes(tagName, attributes)
|
|
70
|
+
parentNode.append(tagName)
|
|
71
|
+
return tagName
|
|
72
|
+
}
|
|
73
|
+
: _addElement
|
|
74
|
+
|
|
75
|
+
export const addStyle = (styleText: string): HTMLElement | undefined =>
|
|
76
|
+
addElement(null, 'style', { textContent: styleText })
|
|
77
|
+
|
|
78
|
+
// Only register menu on top frame
|
|
79
|
+
export const registerMenuCommand = (
|
|
80
|
+
name: string,
|
|
81
|
+
callback: (event?: any) => void,
|
|
82
|
+
options?: Parameters<typeof GM_registerMenuCommand>[2]
|
|
83
|
+
): any => {
|
|
84
|
+
if (globalThis.self !== globalThis.top) {
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (typeof GM.registerMenuCommand !== 'function') {
|
|
89
|
+
console.warn('Do not support GM.registerMenuCommand!')
|
|
90
|
+
return
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return GM.registerMenuCommand(name, callback, options)
|
|
94
|
+
}
|
package/package.json
CHANGED
|
@@ -1,17 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "browser-extension-utils",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Utilities for developing browser extensions and userscripts",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./lib/index.
|
|
6
|
+
"main": "./lib/index.ts",
|
|
7
7
|
"exports": {
|
|
8
|
-
".": "./lib/index.
|
|
9
|
-
"./userscript": "./lib/userscript.
|
|
8
|
+
".": "./lib/index.ts",
|
|
9
|
+
"./userscript": "./lib/userscript.ts"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
12
|
"p": "prettier --write .",
|
|
13
|
-
"
|
|
14
|
-
"
|
|
13
|
+
"xo": "xo",
|
|
14
|
+
"lint": "prettier --write . && xo --fix && tsc --noemit",
|
|
15
|
+
"lint:type": "tsc --noemit",
|
|
16
|
+
"test": "vitest run",
|
|
17
|
+
"test:watch": "vitest watch",
|
|
18
|
+
"test:ui": "vitest --ui --coverage.enabled=true",
|
|
19
|
+
"test:coverage": "vitest --coverage"
|
|
15
20
|
},
|
|
16
21
|
"repository": {
|
|
17
22
|
"type": "git",
|
|
@@ -29,8 +34,14 @@
|
|
|
29
34
|
},
|
|
30
35
|
"homepage": "https://github.com/utags/browser-extension-utils#readme",
|
|
31
36
|
"devDependencies": {
|
|
32
|
-
"
|
|
33
|
-
"
|
|
37
|
+
"@types/chrome": "^0.1.32",
|
|
38
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
39
|
+
"@vitest/ui": "^4.0.16",
|
|
40
|
+
"happy-dom": "^20.0.11",
|
|
41
|
+
"prettier": "^3.7.4",
|
|
42
|
+
"typescript": "^5.9.3",
|
|
43
|
+
"vitest": "^4.0.16",
|
|
44
|
+
"xo": "^1.2.3"
|
|
34
45
|
},
|
|
35
46
|
"files": [
|
|
36
47
|
"lib/",
|
|
@@ -38,28 +49,6 @@
|
|
|
38
49
|
"README.md"
|
|
39
50
|
],
|
|
40
51
|
"engines": {
|
|
41
|
-
"node": ">=
|
|
42
|
-
},
|
|
43
|
-
"xo": {
|
|
44
|
-
"space": 2,
|
|
45
|
-
"prettier": true,
|
|
46
|
-
"globals": [
|
|
47
|
-
"GM",
|
|
48
|
-
"GM_addElement",
|
|
49
|
-
"GM_addStyle",
|
|
50
|
-
"GM_registerMenuCommand",
|
|
51
|
-
"trustedTypes",
|
|
52
|
-
"MutationObserver",
|
|
53
|
-
"history",
|
|
54
|
-
"window",
|
|
55
|
-
"top",
|
|
56
|
-
"document"
|
|
57
|
-
],
|
|
58
|
-
"rules": {
|
|
59
|
-
"logical-assignment-operators": 0,
|
|
60
|
-
"prefer-destructuring": 0,
|
|
61
|
-
"unicorn/prevent-abbreviations": 0,
|
|
62
|
-
"capitalized-comments": 0
|
|
63
|
-
}
|
|
52
|
+
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
|
|
64
53
|
}
|
|
65
54
|
}
|