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