@splendidlabz/utils 1.5.0 → 1.5.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/CHANGELOG.md +71 -1
- package/dist/dom/accessibility.d.ts +28 -0
- package/dist/dom/actions/index.d.ts +3 -0
- package/dist/dom/actions/masonry.d.ts +3 -0
- package/dist/dom/actions/prefer-horizontal-scroll.d.ts +3 -0
- package/dist/dom/actions/sticky.d.ts +3 -0
- package/dist/dom/bounding-box.d.ts +39 -0
- package/dist/dom/clipboard.d.ts +5 -0
- package/dist/dom/cookie.d.ts +11 -0
- package/dist/dom/css-vars.d.ts +4 -0
- package/dist/dom/events.d.ts +18 -0
- package/dist/dom/focusable.d.ts +76 -0
- package/dist/dom/font-size.d.ts +5 -0
- package/dist/dom/get-element.d.ts +100 -0
- package/dist/dom/hash.d.ts +1 -0
- package/dist/dom/index.d.ts +22 -0
- package/dist/dom/keyboard.d.ts +30 -0
- package/dist/dom/local-store.d.ts +29 -0
- package/dist/dom/media.d.ts +3 -0
- package/dist/dom/observers/index.d.ts +3 -0
- package/dist/dom/observers/intersection-observer.d.ts +8 -0
- package/dist/dom/observers/mutation-observer.d.ts +7 -0
- package/dist/dom/observers/observer.d.ts +1 -0
- package/dist/dom/observers/resize-observer.d.ts +38 -0
- package/dist/dom/pkce.d.ts +6 -0
- package/dist/dom/query-params.d.ts +4 -0
- package/dist/dom/random-string.d.ts +7 -0
- package/dist/dom/sanitize.d.ts +8 -0
- package/dist/dom/session-store.d.ts +5 -0
- package/dist/dom/trap-focus.d.ts +28 -0
- package/dist/dom/ui/aria-current.d.ts +5 -0
- package/dist/dom/ui/inconsistent-button-fix.d.ts +12 -0
- package/dist/dom/ui/index.d.ts +4 -0
- package/dist/dom/ui/scroll-container.d.ts +1 -0
- package/dist/dom/ui/traverse-and-scramble.d.ts +19 -0
- package/dist/lib/arrays/index.d.ts +5 -0
- package/dist/lib/arrays/item-position.d.ts +14 -0
- package/dist/lib/arrays/join.d.ts +13 -0
- package/dist/lib/arrays/shuffle.d.ts +1 -0
- package/dist/lib/arrays/sort.d.ts +30 -0
- package/dist/lib/arrays/unique.d.ts +1 -0
- package/dist/lib/auth/index.d.ts +1 -0
- package/dist/lib/auth/route-manager.d.ts +25 -0
- package/dist/lib/checks.d.ts +29 -0
- package/dist/lib/date/days.d.ts +5 -0
- package/dist/lib/date/index.d.ts +3 -0
- package/dist/lib/date/months.d.ts +9 -0
- package/dist/lib/date/time.d.ts +8 -0
- package/dist/lib/form/form-data.d.ts +7 -0
- package/dist/lib/form/index.d.ts +2 -0
- package/dist/lib/form/sanitize.d.ts +58 -0
- package/dist/lib/functions/debounce.d.ts +1 -0
- package/dist/lib/functions/env.d.ts +1 -0
- package/dist/lib/functions/functional.d.ts +4 -0
- package/dist/lib/functions/index.d.ts +5 -0
- package/dist/lib/functions/throttle.d.ts +1 -0
- package/dist/lib/functions/timeout.d.ts +3 -0
- package/dist/lib/index.d.ts +13 -0
- package/dist/lib/numbers/index.d.ts +2 -0
- package/dist/lib/numbers/math.d.ts +2 -0
- package/dist/lib/objects/camelcase-keys.d.ts +1 -0
- package/dist/lib/objects/empty.d.ts +2 -0
- package/dist/lib/objects/equal.d.ts +2 -0
- package/dist/lib/objects/extend.d.ts +4 -0
- package/dist/lib/objects/flatten.d.ts +4 -0
- package/dist/lib/objects/index.d.ts +13 -0
- package/dist/lib/objects/json.d.ts +1 -0
- package/dist/lib/objects/loop.d.ts +2 -0
- package/dist/lib/objects/mix/mix.d.ts +1 -0
- package/dist/lib/objects/nested-property.d.ts +22 -0
- package/dist/lib/objects/normalize-object.d.ts +1 -0
- package/dist/lib/objects/omit-empty.d.ts +1 -0
- package/dist/lib/objects/size.d.ts +1 -0
- package/dist/lib/objects/split.d.ts +10 -0
- package/dist/lib/promises/index.d.ts +1 -0
- package/dist/lib/promises/reject.d.ts +3 -0
- package/dist/lib/sse.d.ts +16 -0
- package/dist/lib/strings/convert-case/convert-case.d.ts +8 -0
- package/dist/lib/strings/index.d.ts +4 -0
- package/dist/lib/strings/markdown.d.ts +23 -0
- package/dist/lib/strings/pluralize.d.ts +1 -0
- package/dist/lib/strings/query-string.d.ts +8 -0
- package/dist/lib/style/index.d.ts +1 -0
- package/dist/lib/symbols/index.d.ts +1 -0
- package/dist/lib/symbols/symbols.d.ts +2 -0
- package/dist/node/common.d.ts +1 -0
- package/dist/node/dirname.d.ts +7 -0
- package/dist/node/file-cache.d.ts +54 -0
- package/dist/node/file.d.ts +2 -0
- package/dist/node/hash.d.ts +1 -0
- package/dist/node/index.d.ts +7 -0
- package/dist/node/pkce.d.ts +6 -0
- package/dist/node/random-string.d.ts +2 -0
- package/dist/node/sanitize.d.ts +8 -0
- package/dom/accessibility.js +46 -13
- package/{actions → dom/actions}/index.js +0 -2
- package/dom/actions/masonry.js +42 -0
- package/{actions → dom/actions}/prefer-horizontal-scroll.js +8 -8
- package/{actions → dom/actions}/sticky.js +3 -6
- package/dom/bounding-box.js +35 -7
- package/dom/css-vars.js +8 -0
- package/dom/events.js +2 -1
- package/dom/focusable.js +119 -10
- package/dom/font-size.js +21 -10
- package/dom/get-element.js +112 -5
- package/dom/index.js +3 -0
- package/dom/keyboard.js +13 -8
- package/dom/local-store.js +37 -0
- package/dom/media.js +1 -0
- package/dom/observers/index.js +3 -0
- package/dom/observers/intersection-observer.js +43 -0
- package/dom/observers/mutation-observer.js +51 -0
- package/dom/observers/observer.js +18 -0
- package/dom/observers/resize-observer.js +64 -0
- package/dom/sanitize.js +16 -0
- package/dom/trap-focus.js +18 -3
- package/dom/ui/inconsistent-button-fix.js +14 -2
- package/dom/ui/traverse-and-scramble.js +29 -8
- package/eslint.config.js +2 -0
- package/lib/arrays/index.js +5 -62
- package/lib/arrays/item-position.js +23 -0
- package/lib/arrays/join.js +20 -0
- package/lib/arrays/shuffle.js +21 -0
- package/lib/arrays/sort.js +88 -0
- package/lib/arrays/sort.spec.js +123 -0
- package/lib/arrays/unique.js +3 -0
- package/lib/checks.js +26 -0
- package/lib/date/index.js +2 -1
- package/lib/date/time.js +50 -0
- package/lib/form/sanitize.js +68 -35
- package/lib/form/sanitize.spec.js +126 -0
- package/lib/index.js +1 -0
- package/lib/numbers/index.js +2 -0
- package/lib/numbers/math.js +9 -0
- package/lib/objects/equal.js +9 -0
- package/lib/objects/index.js +1 -0
- package/lib/objects/mix/mix.js +2 -0
- package/lib/objects/mix/mix.test.js +16 -0
- package/lib/objects/nested-property.js +5 -0
- package/lib/sse.js +61 -0
- package/lib/strings/markdown.js +29 -10
- package/node/dirname.js +1 -1
- package/node/file-cache.js +46 -7
- package/node/index.js +1 -0
- package/node/sanitize.js +13 -0
- package/node/sanitize.spec.js +142 -0
- package/package.json +38 -13
- package/tsconfig.json +23 -0
- package/.eslintrc.cjs +0 -3
- package/.turbo/turbo-lint.log +0 -10
- package/.turbo/turbo-test.log +0 -10
- package/actions/intersection-observer.js +0 -37
- package/actions/masonry.js +0 -75
- package/actions/mutation-observer.js +0 -42
- package/actions/resize-observer.js +0 -33
- package/lib/index.test.js +0 -9
package/node/file-cache.js
CHANGED
|
@@ -3,7 +3,18 @@ import glob from 'glob-promise'
|
|
|
3
3
|
import path from 'node:path'
|
|
4
4
|
import { sort } from '../lib/arrays/index.js'
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
/**
|
|
7
|
+
* Creates a file cache instance for managing cached files in a directory
|
|
8
|
+
* @param {string} dir - Relative path to cache directory
|
|
9
|
+
* @returns {Promise<object>} Cache instance
|
|
10
|
+
* @property {function(string): Promise<number>} cacheAt - Returns the cached file's modified timestamp, or directory's last modified time if no file specified
|
|
11
|
+
* @property {function(string): string} getAbsPath - Returns absolute path for a file in cache
|
|
12
|
+
* @property {function(string): Promise<boolean>} hasFile - Checks if a file exists in cache
|
|
13
|
+
* @property {function(string): Promise<Buffer|undefined>} loadFile - Loads file contents from cache
|
|
14
|
+
* @property {function(string, string|Buffer): Promise<void>} saveFile - Saves data to cache file
|
|
15
|
+
* @property {function(string): Promise<object>} loadJSON - Loads and parses JSON from cache file
|
|
16
|
+
* @property {function(string, object): Promise<void>} saveJSON - Saves object as JSON to cache file
|
|
17
|
+
*/
|
|
7
18
|
export async function createCache(dir) {
|
|
8
19
|
const absDirpath = path.join(process.cwd(), dir)
|
|
9
20
|
await fs.mkdir(absDirpath, { recursive: true })
|
|
@@ -56,7 +67,7 @@ export async function createCache(dir) {
|
|
|
56
67
|
async loadJSON(file) {
|
|
57
68
|
const data = await this.loadFile(file)
|
|
58
69
|
try {
|
|
59
|
-
return JSON.parse(data)
|
|
70
|
+
return JSON.parse(data.toString())
|
|
60
71
|
} catch (err) {
|
|
61
72
|
console.warn(`Error parsing JSON: ${file}`)
|
|
62
73
|
return {}
|
|
@@ -69,7 +80,19 @@ export async function createCache(dir) {
|
|
|
69
80
|
}
|
|
70
81
|
}
|
|
71
82
|
|
|
72
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Legacy file cache API for managing a single cached file
|
|
85
|
+
* @param {Object} options
|
|
86
|
+
* @param {string} options.dir - Relative path to cache directory
|
|
87
|
+
* @param {string} options.file - Filename to cache
|
|
88
|
+
* @returns {Promise<{
|
|
89
|
+
* modifiedAt: number,
|
|
90
|
+
* load: () => Promise<Buffer>,
|
|
91
|
+
* save: (data: string|Buffer) => Promise<void>,
|
|
92
|
+
* loadJSON: () => Promise<object>,
|
|
93
|
+
* saveJSON: (data: object) => Promise<void>
|
|
94
|
+
* }>}
|
|
95
|
+
*/
|
|
73
96
|
export async function fileCache({ dir, file }) {
|
|
74
97
|
// Makes the path if it doesn't exist
|
|
75
98
|
await fs.mkdir(path.join(process.cwd(), dir), { recursive: true })
|
|
@@ -94,7 +117,8 @@ export async function fileCache({ dir, file }) {
|
|
|
94
117
|
},
|
|
95
118
|
|
|
96
119
|
async loadJSON() {
|
|
97
|
-
|
|
120
|
+
const data = await fs.readFile(cachePath)
|
|
121
|
+
return JSON.parse(data.toString())
|
|
98
122
|
},
|
|
99
123
|
async saveJSON(data) {
|
|
100
124
|
await fs.writeFile(cachePath, JSON.stringify(data))
|
|
@@ -102,6 +126,11 @@ export async function fileCache({ dir, file }) {
|
|
|
102
126
|
}
|
|
103
127
|
}
|
|
104
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Gets the most recent modification time of files matching the glob pattern
|
|
131
|
+
* @param {string|string[]} globPath - Glob pattern(s) to match files
|
|
132
|
+
* @returns {Promise<number>} Timestamp of most recently modified file, or 0 if no files found
|
|
133
|
+
*/
|
|
105
134
|
export async function getLastModifiedTime(globPath) {
|
|
106
135
|
let files
|
|
107
136
|
|
|
@@ -116,13 +145,18 @@ export async function getLastModifiedTime(globPath) {
|
|
|
116
145
|
files = a.flat()
|
|
117
146
|
}
|
|
118
147
|
|
|
119
|
-
|
|
120
|
-
|
|
148
|
+
const stats = await Promise.all(files.map(file => fs.stat(file)))
|
|
149
|
+
const timestamps = stats.map(stat => Number(stat.mtimeMs))
|
|
121
150
|
|
|
122
|
-
const sorted = sort(
|
|
151
|
+
const sorted = sort(timestamps, { props: 'mtimeMs', reverse: true })
|
|
123
152
|
return sorted[0] || 0
|
|
124
153
|
}
|
|
125
154
|
|
|
155
|
+
/**
|
|
156
|
+
* @deprecated Use getLastModifiedTime instead
|
|
157
|
+
* @param {string|string[]} globPath - Glob pattern(s) to match files
|
|
158
|
+
* @returns {Promise<number>} Timestamp of most recently modified file, or 0 if no files found
|
|
159
|
+
*/
|
|
126
160
|
export async function getLatestModifiedTime(globPath) {
|
|
127
161
|
console.warn(
|
|
128
162
|
'`getLatestModifiedTime` has been renamed to `getLastModifiedTime` and will be deprecated in future versions. Please use `getLastModifiedTime` instead.',
|
|
@@ -130,6 +164,11 @@ export async function getLatestModifiedTime(globPath) {
|
|
|
130
164
|
return getLastModifiedTime(globPath)
|
|
131
165
|
}
|
|
132
166
|
|
|
167
|
+
/**
|
|
168
|
+
* Removes leading slash from a path string
|
|
169
|
+
* @param {string} x - Path string to process
|
|
170
|
+
* @returns {string} Path without leading slash
|
|
171
|
+
*/
|
|
133
172
|
export function removeFirstSlash(x) {
|
|
134
173
|
return x.startsWith('/') ? x.slice(1) : x
|
|
135
174
|
}
|
package/node/index.js
CHANGED
package/node/sanitize.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import sanitizeHtml from 'sanitize-html'
|
|
2
|
+
import { sanitize as sanitizeLib } from '../lib/form/sanitize.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Sanitizes values using sanitize-html
|
|
6
|
+
* @param {*} value - Value to sanitize (string, array, or object)
|
|
7
|
+
* @param {Object} [options={}] - sanitize-html options
|
|
8
|
+
* @returns {*} Sanitized value
|
|
9
|
+
* @throws {Error} If input is a string but sanitize-html fails
|
|
10
|
+
*/
|
|
11
|
+
export function sanitize(value, options = {}) {
|
|
12
|
+
return sanitizeLib(value, { sanitizer: sanitizeHtml, ...options })
|
|
13
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest'
|
|
2
|
+
import { sanitize } from './sanitize.js'
|
|
3
|
+
|
|
4
|
+
describe('node sanitize', () => {
|
|
5
|
+
describe('sanitize', () => {
|
|
6
|
+
it('removes script tags', () => {
|
|
7
|
+
const input = '<script>alert("xss")</script>Hello'
|
|
8
|
+
expect(sanitize(input)).toBe('Hello')
|
|
9
|
+
})
|
|
10
|
+
|
|
11
|
+
it('allows safe HTML tags', () => {
|
|
12
|
+
const input = '<p>Hello <b>World</b></p>'
|
|
13
|
+
expect(sanitize(input, { allowedTags: ['b'] })).toBe('Hello <b>World</b>')
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
it('sanitizes unsafe attributes', () => {
|
|
17
|
+
const input = '<a href="javascript:alert(1)">Click me</a>'
|
|
18
|
+
expect(
|
|
19
|
+
sanitize(input, {
|
|
20
|
+
allowedTags: ['a'],
|
|
21
|
+
allowedAttributes: { a: ['href'] },
|
|
22
|
+
}),
|
|
23
|
+
).toBe('<a>Click me</a>')
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
it('allows safe attributes', () => {
|
|
27
|
+
const input = '<a href="https://example.com">Safe link</a>'
|
|
28
|
+
expect(
|
|
29
|
+
sanitize(input, {
|
|
30
|
+
allowedTags: ['a'],
|
|
31
|
+
allowedAttributes: { a: ['href'] },
|
|
32
|
+
}),
|
|
33
|
+
).toBe('<a href="https://example.com">Safe link</a>')
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('sanitizes HTML in string values', () => {
|
|
37
|
+
const input = {
|
|
38
|
+
name: '<script>alert("xss")</script>John',
|
|
39
|
+
bio: '<p>Hello <b>World</b> <script>alert("xss")</script></p>',
|
|
40
|
+
link: '<a href="javascript:alert(1)">Click me</a>',
|
|
41
|
+
safeLink: '<a href="https://example.com">Safe link</a>',
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const expected = {
|
|
45
|
+
name: 'John',
|
|
46
|
+
bio: 'Hello <b>World</b> ',
|
|
47
|
+
link: '<a>Click me</a>',
|
|
48
|
+
safeLink: '<a href="https://example.com">Safe link</a>',
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
expect(
|
|
52
|
+
sanitize(input, {
|
|
53
|
+
allowedTags: ['b', 'i', 'em', 'strong', 'a'],
|
|
54
|
+
allowedAttributes: { a: ['href'] },
|
|
55
|
+
}),
|
|
56
|
+
).toEqual(expected)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
it('sanitizes nested objects with HTML', () => {
|
|
60
|
+
const input = {
|
|
61
|
+
user: {
|
|
62
|
+
name: '<script>alert("xss")</script>John',
|
|
63
|
+
profile: {
|
|
64
|
+
bio: '<p>Hello <b>World</b></p>',
|
|
65
|
+
links: [
|
|
66
|
+
'<a href="javascript:alert(1)">Bad</a>',
|
|
67
|
+
'<a href="https://good.com">Good</a>',
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const expected = {
|
|
74
|
+
user: {
|
|
75
|
+
name: 'John',
|
|
76
|
+
profile: {
|
|
77
|
+
bio: 'Hello <b>World</b>',
|
|
78
|
+
links: ['<a>Bad</a>', '<a href="https://good.com">Good</a>'],
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
expect(
|
|
84
|
+
sanitize(input, {
|
|
85
|
+
allowedTags: ['b', 'i', 'em', 'strong', 'a'],
|
|
86
|
+
allowedAttributes: { a: ['href'] },
|
|
87
|
+
}),
|
|
88
|
+
).toEqual(expected)
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
it('preserves non-string values and sanitizes arrays correctly', () => {
|
|
92
|
+
const input = {
|
|
93
|
+
name: '<script>alert("xss")</script>John',
|
|
94
|
+
age: 25,
|
|
95
|
+
active: true,
|
|
96
|
+
email: null,
|
|
97
|
+
role: undefined,
|
|
98
|
+
tags: ['<script>alert(1)</script>admin', 'user'],
|
|
99
|
+
numbers: [1, 2, 3],
|
|
100
|
+
mixed: ['<b>bold</b>', 42, null, '<script>alert(1)</script>text'],
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const expected = {
|
|
104
|
+
name: 'John',
|
|
105
|
+
age: 25,
|
|
106
|
+
active: true,
|
|
107
|
+
email: null,
|
|
108
|
+
role: undefined,
|
|
109
|
+
tags: ['admin', 'user'],
|
|
110
|
+
numbers: [1, 2, 3],
|
|
111
|
+
mixed: ['<b>bold</b>', 42, null, 'text'],
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
expect(
|
|
115
|
+
sanitize(input, {
|
|
116
|
+
allowedTags: ['b', 'i', 'em', 'strong', 'a'],
|
|
117
|
+
allowedAttributes: { a: ['href'] },
|
|
118
|
+
}),
|
|
119
|
+
).toEqual(expected)
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
it('allows custom sanitizer options', () => {
|
|
123
|
+
const input = {
|
|
124
|
+
content: '<p>Hello <span>World</span> <script>alert(1)</script></p>',
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Test with custom allowed tags
|
|
128
|
+
const withSpan = sanitize(input, {
|
|
129
|
+
allowedTags: ['b', 'i', 'em', 'strong', 'a', 'span'],
|
|
130
|
+
allowedAttributes: { a: ['href'] },
|
|
131
|
+
})
|
|
132
|
+
expect(withSpan.content).toBe('Hello <span>World</span> ')
|
|
133
|
+
|
|
134
|
+
// Test with no allowed tags
|
|
135
|
+
const noTags = sanitize(input, {
|
|
136
|
+
allowedTags: [],
|
|
137
|
+
allowedAttributes: { a: ['href'] },
|
|
138
|
+
})
|
|
139
|
+
expect(noTags.content).toBe('Hello World ')
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
})
|
package/package.json
CHANGED
|
@@ -1,35 +1,60 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@splendidlabz/utils",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.1",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"exports": {
|
|
8
|
-
".":
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
"./
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
8
|
+
".": {
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"import": "./lib/index.js"
|
|
11
|
+
},
|
|
12
|
+
"./lib/*": {
|
|
13
|
+
"types": "./dist/lib/*.d.ts",
|
|
14
|
+
"import": "./lib/*.js"
|
|
15
|
+
},
|
|
16
|
+
"./dom": {
|
|
17
|
+
"types": "./dist/dom/index.d.ts",
|
|
18
|
+
"import": "./dom/index.js"
|
|
19
|
+
},
|
|
20
|
+
"./dom/*": {
|
|
21
|
+
"types": "./dist/dom/*.d.ts",
|
|
22
|
+
"import": "./dom/*.js"
|
|
23
|
+
},
|
|
24
|
+
"./node": {
|
|
25
|
+
"types": "./dist/node/index.d.ts",
|
|
26
|
+
"import": "./node/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./node/*": {
|
|
29
|
+
"types": "./dist/node/*.d.ts",
|
|
30
|
+
"import": "./node/*.js"
|
|
31
|
+
}
|
|
16
32
|
},
|
|
17
33
|
"scripts": {
|
|
18
34
|
"lint": "eslint . --fix",
|
|
19
|
-
"test": "vitest run",
|
|
20
|
-
"test:watch": "vitest"
|
|
35
|
+
"test": "npm run type-check && vitest run",
|
|
36
|
+
"test:watch": "vitest",
|
|
37
|
+
"type-check": "tsc --noEmit",
|
|
38
|
+
"build": "tsc",
|
|
39
|
+
"prepublishOnly": "npm run build"
|
|
21
40
|
},
|
|
22
41
|
"author": "Zell Liew <zellwk@gmail.com>",
|
|
23
42
|
"dependencies": {
|
|
43
|
+
"deep-eql": "^5.0.2",
|
|
24
44
|
"dompurify": "^3.2.4",
|
|
25
45
|
"glob-promise": "^6.0.7",
|
|
46
|
+
"marked": "^15.0.7",
|
|
47
|
+
"marked-gfm-heading-id": "^4.1.1",
|
|
48
|
+
"marked-mangle": "^1.1.10",
|
|
26
49
|
"pluralize": "^8.0.0",
|
|
50
|
+
"sanitize-html": "^2.17.0",
|
|
27
51
|
"statuses": "^2.0.1"
|
|
28
52
|
},
|
|
29
53
|
"devDependencies": {
|
|
30
|
-
"@splendidlabz/eslint-config": "
|
|
54
|
+
"@splendidlabz/eslint-config": "2.0.0",
|
|
31
55
|
"jsdom": "^26.0.0",
|
|
32
56
|
"np": "^10.2.0",
|
|
33
|
-
"
|
|
57
|
+
"typescript": "^5.4.2",
|
|
58
|
+
"vitest": "^3.1.1"
|
|
34
59
|
}
|
|
35
60
|
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"allowJs": true,
|
|
7
|
+
"checkJs": true,
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"emitDeclarationOnly": true,
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"strict": false,
|
|
12
|
+
"noImplicitAny": false,
|
|
13
|
+
"skipLibCheck": true,
|
|
14
|
+
"forceConsistentCasingInFileNames": true,
|
|
15
|
+
"resolveJsonModule": true,
|
|
16
|
+
"isolatedModules": true,
|
|
17
|
+
"noEmit": false,
|
|
18
|
+
"esModuleInterop": true,
|
|
19
|
+
"allowSyntheticDefaultImports": true
|
|
20
|
+
},
|
|
21
|
+
"include": ["lib/**/*", "dom/**/*", "node/**/*"],
|
|
22
|
+
"exclude": ["node_modules", "**/*.test.js", "**/*.spec.js"]
|
|
23
|
+
}
|
package/.eslintrc.cjs
DELETED
package/.turbo/turbo-lint.log
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @splendidlabs/utils@1.0.3 lint
|
|
3
|
-
> eslint . --fix
|
|
4
|
-
|
|
5
|
-
[0m[0m
|
|
6
|
-
[0m[4m/Users/zellwk/projects/@splendidlabs/packages/utils/lib/index.test.js[24m[0m
|
|
7
|
-
[0m [2m4:10[22m [31merror[39m 'expect' is defined but never used [2mno-unused-vars[22m[0m
|
|
8
|
-
[0m[0m
|
|
9
|
-
[0m[31m[1m✖ 1 problem (1 error, 0 warnings)[22m[39m[0m
|
|
10
|
-
[0m[31m[1m[22m[39m[0m
|
package/.turbo/turbo-test.log
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
> @splendidlabs/utils@1.0.3 test
|
|
3
|
-
> vitest run
|
|
4
|
-
|
|
5
|
-
sh: vitest: command not found
|
|
6
|
-
[37;40mnpm[0m [0m[31;40mERR![0m [0m[35mLifecycle script `test` failed with error:[0m
|
|
7
|
-
[0m[37;40mnpm[0m [0m[31;40mERR![0m [0m[35mError: command failed[0m
|
|
8
|
-
[0m[37;40mnpm[0m [0m[31;40mERR![0m [0m[35m in workspace: @splendidlabs/utils@1.0.3[0m
|
|
9
|
-
[0m[37;40mnpm[0m [0m[31;40mERR![0m [0m[35m at location: /Users/zellwk/projects/@splendidlabs/packages/utils[0m
|
|
10
|
-
[0m
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
// See https://developer.mozilla.org/en-US/docs/Web/API/IntersectionObserver/IntersectionObserver#options for options
|
|
2
|
-
const options = {}
|
|
3
|
-
|
|
4
|
-
export function intersectionObserver(node, options) {
|
|
5
|
-
const { callback, opts } = options
|
|
6
|
-
const observer = new IntersectionObserver(observerFn, opts)
|
|
7
|
-
|
|
8
|
-
function observerFn(entries, observer) {
|
|
9
|
-
entries.forEach(entry => {
|
|
10
|
-
for (const entry of entries) {
|
|
11
|
-
// Needs testing to see what's in entry
|
|
12
|
-
if (callback) callback({ node, entry, observer })
|
|
13
|
-
else {
|
|
14
|
-
node.dispatchEvent(
|
|
15
|
-
new CustomEvent('intersect', {
|
|
16
|
-
detail: { node, entry, observer },
|
|
17
|
-
}),
|
|
18
|
-
)
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
})
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
observer.observe(node)
|
|
25
|
-
|
|
26
|
-
return {
|
|
27
|
-
observer,
|
|
28
|
-
disconnect() {
|
|
29
|
-
observer.disconnect()
|
|
30
|
-
},
|
|
31
|
-
|
|
32
|
-
// Destroy is used for Svelte actions
|
|
33
|
-
destroy() {
|
|
34
|
-
observer.disconnect()
|
|
35
|
-
},
|
|
36
|
-
}
|
|
37
|
-
}
|
package/actions/masonry.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { getChildrenElements, mediaLoaded } from '../dom'
|
|
2
|
-
|
|
3
|
-
import { resizeObserver } from './resize-observer'
|
|
4
|
-
|
|
5
|
-
export async function masonry(node) {
|
|
6
|
-
if (nativeMasonrySupport(node)) return
|
|
7
|
-
|
|
8
|
-
const colGap = parseFloat(getComputedStyle(node).columnGap)
|
|
9
|
-
const items = getChildrenElements(node)
|
|
10
|
-
|
|
11
|
-
node.style.setProperty('row-gap', '1px', 'important')
|
|
12
|
-
node.style.gridAutoRows = '0px'
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
await mediaLoaded(node)
|
|
16
|
-
} catch (e) {}
|
|
17
|
-
|
|
18
|
-
layout({ colGap, items })
|
|
19
|
-
|
|
20
|
-
const obs = resizeObserver(node, {
|
|
21
|
-
async callback(opts) {
|
|
22
|
-
layout({ colGap, items })
|
|
23
|
-
},
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
async function layout({ colGap, items }) {
|
|
27
|
-
items.forEach(item => {
|
|
28
|
-
const ib = item.getBoundingClientRect()
|
|
29
|
-
item.style.gridRowEnd = `span ${Math.round(ib.height + colGap)}`
|
|
30
|
-
})
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
destroy() {
|
|
35
|
-
obs.disconnect()
|
|
36
|
-
},
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// export async function masonry(node) {
|
|
41
|
-
// if (typeof window === 'undefined') return
|
|
42
|
-
// if (nativeMasonrySupport(node)) return
|
|
43
|
-
|
|
44
|
-
// let items
|
|
45
|
-
// let gap
|
|
46
|
-
|
|
47
|
-
// node.style.setProperty('row-gap', '1px', 'important')
|
|
48
|
-
// node.style.gridAutoRows = '0px'
|
|
49
|
-
// // await mediaLoaded(node)
|
|
50
|
-
|
|
51
|
-
// const obs = resizeObserver(node, {
|
|
52
|
-
// async callback(opts) {
|
|
53
|
-
// items = getChildrenElements(node)
|
|
54
|
-
// gap = parseFloat(getComputedStyle(node).columnGap)
|
|
55
|
-
// layout(node)
|
|
56
|
-
// },
|
|
57
|
-
// })
|
|
58
|
-
|
|
59
|
-
// async function layout() {
|
|
60
|
-
// items.forEach(item => {
|
|
61
|
-
// const itemBox = item.getBoundingClientRect()
|
|
62
|
-
// // item.style.gridRowEnd = `span ${Math.round(itemBox.height + gap)}`
|
|
63
|
-
// })
|
|
64
|
-
// }
|
|
65
|
-
|
|
66
|
-
// return {
|
|
67
|
-
// destroy() {
|
|
68
|
-
// obs.disconnect()
|
|
69
|
-
// },
|
|
70
|
-
// }
|
|
71
|
-
// }
|
|
72
|
-
|
|
73
|
-
function nativeMasonrySupport(node) {
|
|
74
|
-
return getComputedStyle(node).gridTemplateRows === 'masonry'
|
|
75
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
/* eslint-env browser */
|
|
2
|
-
// https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/observe
|
|
3
|
-
|
|
4
|
-
// These are simply options that will often be used. Check the docs for other options.
|
|
5
|
-
const defaultOptions = {
|
|
6
|
-
attributes: false,
|
|
7
|
-
childList: false,
|
|
8
|
-
subtree: false,
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export function mutationObserver(node, options) {
|
|
12
|
-
options = { ...defaultOptions, ...options }
|
|
13
|
-
const { callback, ...opts } = options
|
|
14
|
-
const observer = new MutationObserver(observerFn)
|
|
15
|
-
|
|
16
|
-
function observerFn(entries) {
|
|
17
|
-
for (const entry of entries) {
|
|
18
|
-
if (callback) callback({ node, entry, observer })
|
|
19
|
-
else {
|
|
20
|
-
node.dispatchEvent(
|
|
21
|
-
new CustomEvent('mutate', {
|
|
22
|
-
detail: { node, entry, observer },
|
|
23
|
-
}),
|
|
24
|
-
)
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (node === window) node = document.body
|
|
30
|
-
observer.observe(node, opts)
|
|
31
|
-
|
|
32
|
-
return {
|
|
33
|
-
disconnect() {
|
|
34
|
-
observer.disconnect()
|
|
35
|
-
},
|
|
36
|
-
|
|
37
|
-
// Destroy is used for Svelte actions
|
|
38
|
-
destroy() {
|
|
39
|
-
observer.disconnect()
|
|
40
|
-
},
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/* eslint-env browser */
|
|
2
|
-
export function resizeObserver(node, options) {
|
|
3
|
-
// We set the option here to prevent observer from being created unless necessary.
|
|
4
|
-
options = { observe: true, ...options }
|
|
5
|
-
if (!options.observe) return
|
|
6
|
-
|
|
7
|
-
const observer = new ResizeObserver(observerFn)
|
|
8
|
-
|
|
9
|
-
function observerFn(entries) {
|
|
10
|
-
for (const entry of entries) {
|
|
11
|
-
if (options.callback) options.callback({ node, entry, entries, observer })
|
|
12
|
-
else {
|
|
13
|
-
node.dispatchEvent(
|
|
14
|
-
new CustomEvent('resize-obs', {
|
|
15
|
-
detail: { node, entry, entries, observer },
|
|
16
|
-
}),
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
observer.observe(node)
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
disconnect() {
|
|
26
|
-
observer.unobserve(node)
|
|
27
|
-
},
|
|
28
|
-
|
|
29
|
-
destroy() {
|
|
30
|
-
observer.unobserve(node)
|
|
31
|
-
},
|
|
32
|
-
}
|
|
33
|
-
}
|