PluginProbe
Code Snippets / 3.10.0
Code Snippets v3.10.0
3.10.2 3.10.1 3.10.0 3.10.0-beta.2 3.10.0-beta.1 4.0.0-beta.1 3.9.6 trunk 2.10.0 2.10.1 2.12.0 2.12.1 2.13.0 2.13.1 2.13.2 2.13.3 2.14.0 2.14.1 2.14.2 2.14.3 2.14.4 2.14.5 2.14.6 3.0.0 3.0.1 All 64 releases
code-snippets / js / utils / files.ts

files.ts in Code Snippets 3.10.0, at js/utils/files.ts

143 lines 4.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 import { getSnippetType } from './snippets/snippets'
2 import type { SnippetsExport } from '../types/schema/SnippetsExport'
3 import type { Snippet } from '../types/Snippet'
4
5 const SECOND_IN_MS = 1000
6 const TIMEOUT_SECONDS = 40
7 const JSON_INDENT_SPACES = 2
8 const EXPORT_FILENAME = 'snippets'
9 const EXPORT_GENERATOR = 'Code Snippets'
10 const DEFAULT_PRIORITY = 10
11 const EXPORT_DATE_LENGTH = 16
12
13 const MIME_INFO = <const> {
14 php: ['php', 'text/php'],
15 html: ['php', 'text/php'],
16 css: ['css', 'text/css'],
17 js: ['js', 'text/javascript'],
18 cond: ['json', 'application/json'],
19 json: ['json', 'application/json']
20 }
21
22 export const downloadAsFile = (content: BlobPart, filename: string, type: string) => {
23 const link = document.createElement('a')
24 link.download = filename
25 link.href = URL.createObjectURL(new Blob([content], { type }))
26
27 setTimeout(() => URL.revokeObjectURL(link.href), TIMEOUT_SECONDS * SECOND_IN_MS)
28
29 // Some browsers (notably headless Chromium) can ignore programmatic clicks on detached anchors.
30 // Appending the link to the DOM before clicking improves reliability.
31 link.style.display = 'none'
32 document.body.appendChild(link)
33
34 setTimeout(() => {
35 link.click()
36 link.remove()
37 }, 0)
38 }
39
40 export const downloadSnippetExportFile = (
41 content: SnippetsExport | string,
42 { id, name, scope }: Snippet,
43 type?: keyof typeof MIME_INFO
44 ) => {
45 const sanitizedName = name.toLowerCase().replace(/[^\w-]+/g, '-').trim()
46 const title = '' === sanitizedName ? `snippet-${id}` : sanitizedName
47
48 if ('string' === typeof content) {
49 const [ext, mimeType] = MIME_INFO[type ?? getSnippetType({ scope })]
50 const filename = `${title}.code-snippets.${ext}`
51 downloadAsFile(content, filename, mimeType)
52 } else {
53 const filename = `${title}.code-snippets.json`
54 downloadAsFile(JSON.stringify(content, undefined, JSON_INDENT_SPACES), filename, 'application/json')
55 }
56 }
57
58 type DefaultExportValueCheck = (value: unknown) => boolean
59
60 const isNullish = (value: unknown): value is undefined | null => undefined === value || null === value
61
62 const DEFAULT_EXPORT_VALUE_CHECKS: Record<string, DefaultExportValueCheck> = {
63 id: value => 0 === value,
64 desc: value => '' === value,
65 name: value => '' === value,
66 code: value => '' === value,
67 tags: value => Array.isArray(value) && 0 === value.length,
68 scope: value => 'global' === value,
69 condition_id: value => 0 === value,
70 active: value => false === value,
71 locked: value => false === value,
72 trashed: value => false === value,
73 priority: value => DEFAULT_PRIORITY === value,
74 modified: value => '' === value || isNullish(value),
75 last_active: value => 0 === value || isNullish(value),
76 network: value => null === value || false === value,
77 shared_network: value => null === value || false === value,
78 code_error: value => null === value || false === value,
79 code_error_trace: value => null === value || false === value
80 }
81
82 const isDefaultExportValue = (field: string, value: unknown): boolean =>
83 (DEFAULT_EXPORT_VALUE_CHECKS[field] ?? isNullish)(value)
84
85 const buildExportSnippet = ({
86 id,
87 name,
88 desc,
89 code,
90 tags,
91 scope,
92 active,
93 locked,
94 trashed,
95 network,
96 shared_network,
97 modified,
98 priority,
99 conditionId,
100 lastActive,
101 code_error,
102 code_error_trace
103 }: Snippet): SnippetsExport['snippets'][number] =>
104 Object.fromEntries(
105 Object.entries({
106 id,
107 name,
108 desc,
109 code,
110 tags,
111 scope,
112 active,
113 locked,
114 trashed,
115 network,
116 shared_network,
117 modified,
118 priority,
119 condition_id: conditionId,
120 last_active: lastActive,
121 code_error,
122 code_error_trace
123 }).filter(([field, value]) => !isDefaultExportValue(field, value))
124 )
125
126 export const downloadBulkSnippetExportFile = (snippets: readonly Snippet[]) => {
127 if (0 === snippets.length) {
128 return
129 }
130
131 const content: SnippetsExport = {
132 generator: EXPORT_GENERATOR,
133 date_created: new Date().toISOString().slice(0, EXPORT_DATE_LENGTH).replace('T', ' '),
134 snippets: snippets.map(buildExportSnippet)
135 }
136
137 downloadAsFile(
138 JSON.stringify(content, undefined, JSON_INDENT_SPACES),
139 `${EXPORT_FILENAME}.code-snippets.json`,
140 'application/json'
141 )
142 }
143