unicode-input-toolconverter 0.2.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.
Files changed (123) hide show
  1. package/.babelrc.json +3 -0
  2. package/.editorconfig +16 -0
  3. package/.nojekyll +0 -0
  4. package/.nyc_output/out.json +173804 -0
  5. package/.nyc_output/processinfo/index.json +1 -0
  6. package/CHANGES.md +11 -0
  7. package/COPYING.LESSER.txt +180 -0
  8. package/COPYING.txt +687 -0
  9. package/README.md +65 -0
  10. package/_locales/en-US/messages.json +3126 -0
  11. package/_locales/hu-HU/messages.json +3126 -0
  12. package/_locales/pt-BR/messages.json +3126 -0
  13. package/_locales/sv-SE/messages.json +3126 -0
  14. package/babel.config.json +5 -0
  15. package/browser_action/characterSelection.js +91 -0
  16. package/browser_action/charrefConverters.js +389 -0
  17. package/browser_action/chartBuild.js +155 -0
  18. package/browser_action/encodingBehaviors.js +72 -0
  19. package/browser_action/encodings.js +97 -0
  20. package/browser_action/entities.js +12 -0
  21. package/browser_action/entityBehaviors.js +96 -0
  22. package/browser_action/index-es.html +15 -0
  23. package/browser_action/index-instrumented.html +16 -0
  24. package/browser_action/index-pages.html +16 -0
  25. package/browser_action/index.html +16 -0
  26. package/browser_action/index.iife.min.js +2 -0
  27. package/browser_action/index.iife.min.js.map +1 -0
  28. package/browser_action/index.instrumented.iife.min.js +2 -0
  29. package/browser_action/index.instrumented.iife.min.js.map +1 -0
  30. package/browser_action/index.js +95 -0
  31. package/browser_action/preferences/prefDefaults.js +52 -0
  32. package/browser_action/service-worker/sw-activateCallback.js +53 -0
  33. package/browser_action/service-worker/sw-locales.json +6 -0
  34. package/browser_action/service-worker/sw-resources.json +73 -0
  35. package/browser_action/service-worker/sw-unicode-data.json +3 -0
  36. package/browser_action/styles/unicode-dialog.css +391 -0
  37. package/browser_action/templateUtils/elements.js +12 -0
  38. package/browser_action/templateUtils/fill.js +3 -0
  39. package/browser_action/templateUtils/validation.js +6 -0
  40. package/browser_action/templates/chartBuild.js +185 -0
  41. package/browser_action/templates/index.js +1304 -0
  42. package/browser_action/templatesElementCustomization/widgets.js +56 -0
  43. package/browser_action/unicode/UnicodeConverter.js +793 -0
  44. package/browser_action/unicode/charrefunicodeDb.js +258 -0
  45. package/browser_action/unicode/getScriptInfoForCodePoint.js +646 -0
  46. package/browser_action/unicode/hangul.js +253 -0
  47. package/browser_action/unicode/lastScriptNames.json +5 -0
  48. package/browser_action/unicode/parseUnihanFromTextFileStrings.js +56 -0
  49. package/browser_action/unicode/unicodeFieldInfo.js +120 -0
  50. package/browser_action/unicode/unicodeScripts.js +4912 -0
  51. package/browser_action/unicode/unihan.js +129 -0
  52. package/browser_action/unicode/unihanDbPopulate.js +26 -0
  53. package/browser_action/unicodecharref.js +1480 -0
  54. package/browser_action/utils/DOMUtils.js +122 -0
  55. package/browser_action/utils/FetchUtils.js +19 -0
  56. package/browser_action/utils/TextUtils.js +28 -0
  57. package/browser_action/utils/TypedArrayUtils.js +21 -0
  58. package/browser_action/utils/semicolonSeparatedToArray.js +12 -0
  59. package/browser_action/utils/setupServiceWorker.js +193 -0
  60. package/cypress.config.js +21 -0
  61. package/download/entities/copyright-software-20021231.html +70 -0
  62. package/download/entities/html5-uppercase.ent +54 -0
  63. package/download/entities/htmlmathml-f.ent +2164 -0
  64. package/download/entities/isoamsa.ent +201 -0
  65. package/download/entities/isoamsb.ent +177 -0
  66. package/download/entities/isoamsc.ent +77 -0
  67. package/download/entities/isoamsn.ent +148 -0
  68. package/download/entities/isoamso.ent +107 -0
  69. package/download/entities/isoamsr.ent +238 -0
  70. package/download/entities/isobox.ent +95 -0
  71. package/download/entities/isocyr1.ent +122 -0
  72. package/download/entities/isocyr2.ent +81 -0
  73. package/download/entities/isodia.ent +69 -0
  74. package/download/entities/isogrk1.ent +104 -0
  75. package/download/entities/isogrk2.ent +75 -0
  76. package/download/entities/isogrk3.ent +98 -0
  77. package/download/entities/isogrk4.ent +98 -0
  78. package/download/entities/isolat1.ent +117 -0
  79. package/download/entities/isolat2.ent +176 -0
  80. package/download/entities/isomfrk.ent +107 -0
  81. package/download/entities/isomopf.ent +81 -0
  82. package/download/entities/isomscr.ent +107 -0
  83. package/download/entities/isonum.ent +131 -0
  84. package/download/entities/isopub.ent +140 -0
  85. package/download/entities/isotech.ent +216 -0
  86. package/download/entities/mmlalias.ent +598 -0
  87. package/download/entities/mmlextra.ent +154 -0
  88. package/download/entities/predefined.ent +52 -0
  89. package/download/entities/w3centities-f.ent +2276 -0
  90. package/download/entities/xhtml1-lat1.ent +143 -0
  91. package/download/entities/xhtml1-special.ent +78 -0
  92. package/download/entities/xhtml1-symbol.ent +171 -0
  93. package/eslint.config.js +66 -0
  94. package/icons/openWindow16.png +0 -0
  95. package/icons/openWindow24.png +0 -0
  96. package/lgtm.yml +5 -0
  97. package/lib/background.html +11 -0
  98. package/lib/background.js +13 -0
  99. package/lib/overlay.css +6 -0
  100. package/manifest.json +37 -0
  101. package/package.json +121 -0
  102. package/polyfills/browser-polyfill.min.js +8 -0
  103. package/polyfills/browser-polyfill.min.js.map +1 -0
  104. package/server.js +61 -0
  105. package/sw.js +259 -0
  106. package/tools/entities-import.js +62 -0
  107. package/tools/findEsResources.js +30 -0
  108. package/tools/list-locales.js +17 -0
  109. package/tools/parseUnicodeCharts.js +428 -0
  110. package/tools/ucd-import.js +24 -0
  111. package/tools/unicode-charts.html +596 -0
  112. package/tools/unihan-import.js +72 -0
  113. package/unicode_copyright.txt +46 -0
  114. package/vendor/camelcase/index.js +110 -0
  115. package/vendor/fflate/esm/browser.js +2665 -0
  116. package/vendor/intl-dom/dist/index.esm.js +2962 -0
  117. package/vendor/jamilih/dist/jml-es.js +2359 -0
  118. package/vendor/jquery/dist/jquery.js +10716 -0
  119. package/vendor/json-6/dist/index.mjs +1783 -0
  120. package/vendor/miller-columns/dist/index-es.min.js +1 -0
  121. package/vendor/miller-columns/miller-columns.css +75 -0
  122. package/vendor/simple-prefs/dist/index.esm.js +286 -0
  123. package/web-ext-config.cjs +13 -0
@@ -0,0 +1,62 @@
1
+ // As the Unicode server does not have CORS enabled, we can't adapt
2
+ // this file for the browser, but we're keeping this file for demonstrating
3
+ // an approach for live-obtaining the live version.
4
+
5
+ import fs from 'fs/promises';
6
+
7
+ // eslint-disable-next-line no-shadow -- Clearer
8
+ import fetch from 'node-fetch';
9
+
10
+ import {load} from 'cheerio';
11
+
12
+ const args = process.argv.slice(2);
13
+
14
+ const targetDir = `${process.cwd()}/download/entities`;
15
+ const entityBasePath = 'https://www.w3.org/2003/entities/2007/';
16
+
17
+ const getText = async (file) => {
18
+ return await (await fetch(
19
+ file
20
+ )).text();
21
+ };
22
+
23
+ // const targetDir = `${process.cwd()}/download/entities`;
24
+
25
+ if (args.includes('download')) {
26
+ const htmlText = await getText(entityBasePath);
27
+ const $ = load(htmlText);
28
+ const elems = $('a[href$=".ent"]');
29
+ const entityFiles = [];
30
+ // eslint-disable-next-line unicorn/no-for-loop -- Not iterable
31
+ for (let i = 0; i < elems.length; i++) {
32
+ entityFiles.push(elems[i].attribs.href);
33
+ }
34
+ const entityFileTexts = await Promise.all(
35
+ entityFiles.map(async (entityFile) => {
36
+ if ([
37
+ // We don't really need these as express parameter entities
38
+ 'htmlmathml.ent',
39
+ 'w3centities.ent'
40
+ ].includes(entityFile)) {
41
+ return {};
42
+ }
43
+ return {
44
+ file: `${targetDir}/${entityFile}`,
45
+ text: await getText(`${entityBasePath}${entityFile}`)
46
+ };
47
+ })
48
+ );
49
+
50
+ // eslint-disable-next-line no-console -- CLI
51
+ console.log('entity file file count', entityFileTexts.length);
52
+
53
+ await Promise.all(entityFileTexts.map(({file, text}) => {
54
+ if (!file) {
55
+ return undefined;
56
+ }
57
+ return fs.writeFile(file, text);
58
+ }));
59
+
60
+ // eslint-disable-next-line no-console -- CLI
61
+ console.log('Complete!');
62
+ }
@@ -0,0 +1,30 @@
1
+ const queryModule = {
2
+ [
3
+ // Entity files
4
+ 'ArrayExpression > Literal[value="optgroup"] + ObjectExpression + ' +
5
+ 'ArrayExpression:has(CallExpression > Literal[value=/^ent_/]) ' +
6
+ 'ObjectExpression Literal'
7
+ ] (node) {
8
+ return [
9
+ `/download/entities/${node.value}.ent`
10
+ ];
11
+ },
12
+ // Dynamically imported stylesheets
13
+ 'Property[key.name="stylesheets"]' (node) {
14
+ return (node.value.elements || []).flatMap((element) => {
15
+ if (element.type === 'Literal') {
16
+ return element.value;
17
+ }
18
+ // if (element.type === 'ArrayExpression') {
19
+ return element.elements.flatMap((arrayItem) => {
20
+ if (arrayItem.type === 'Literal') {
21
+ return arrayItem.value;
22
+ }
23
+ // Ignore
24
+ return [];
25
+ });
26
+ });
27
+ }
28
+ };
29
+
30
+ export default queryModule;
@@ -0,0 +1,17 @@
1
+ import fs from 'fs/promises';
2
+
3
+ const dirs = await fs.readdir('_locales');
4
+
5
+ const json = dirs.filter((dir) => {
6
+ return dir !== '.DS_Store';
7
+ }).map((dir) => {
8
+ return `/_locales/${dir}/messages.json`;
9
+ }).sort();
10
+
11
+ // eslint-disable-next-line no-console -- CLI
12
+ console.log('json', json);
13
+
14
+ fs.writeFile(
15
+ 'browser_action/service-worker/sw-locales.json',
16
+ JSON.stringify(json, null, 2) + '\n'
17
+ );
@@ -0,0 +1,428 @@
1
+ import fs from 'fs/promises';
2
+ import path from 'path';
3
+
4
+ // eslint-disable-next-line no-shadow -- Remove?
5
+ import fetch from 'node-fetch';
6
+ import jsdom from 'jsdom';
7
+
8
+ // For `localize` `true`, need to provide strings for all
9
+ // possible; could auto-add to locale files if missing
10
+ const localize = true;
11
+
12
+ const {JSDOM} = jsdom;
13
+
14
+ /**
15
+ * @param {string} key
16
+ * @returns {string}
17
+ */
18
+ function getChromeSafeLocaleKey (key) {
19
+ return key.replaceAll(
20
+ String.raw`\n`, ''
21
+ ).replaceAll(/\s+/gu, ' '). // Getting some extra WS
22
+ replaceAll(/\W/gu, '_');
23
+ }
24
+
25
+ let text;
26
+ const lastScriptNamesFile = new URL(
27
+ '../browser_action/unicode/lastScriptNames.json', import.meta.url
28
+ );
29
+ const chartsURL = 'https://unicode.org/charts/';
30
+ const chartsFile = new URL('unicode-charts.html', import.meta.url);
31
+ if (process.argv[2] === 'retrieve') {
32
+ const resp = await fetch(chartsURL);
33
+ text = await resp.text();
34
+ // eslint-disable-next-line no-console -- CLI
35
+ console.log('retrieved', text);
36
+ await fs.writeFile(chartsFile, text);
37
+ // eslint-disable-next-line no-console -- CLI
38
+ console.log('saved file');
39
+ } else {
40
+ text = await fs.readFile(chartsFile, 'utf8');
41
+ }
42
+
43
+ const doc = new JSDOM(text).window.document;
44
+ const scriptMaps = [...doc.querySelectorAll('table.map')];
45
+
46
+ const uniqueTextPlaceholder = localize
47
+ ? '___placeholder___'
48
+ : ''; // We know it is not present inside Unicode script names!
49
+
50
+ const cleanupText = (txt) => {
51
+ return txt.trim().
52
+ replace(/(?<initWS>\s)\s+/u, '$<initWS>').
53
+ replace(/\s*\((?:ASCII|Odia)\)$/u, '').
54
+ replace(/\s\(\d\.?\d*MB\)$/u, ''); // Remove MB size
55
+ };
56
+
57
+ const scriptsAndStartRanges = [];
58
+
59
+ const jamilih = scriptMaps.map((scriptMap) => {
60
+ const majorHeading = cleanupText(
61
+ scriptMap.previousElementSibling.textContent
62
+ );
63
+ // const scriptGroups = [...scriptMap.querySelectorAll('table td p')];
64
+
65
+ const scriptGroups = [...scriptMap.querySelectorAll('table td p.sg')];
66
+ // sg, mb, pb/sb
67
+ let lastChildren;
68
+ return ['li', [
69
+ majorHeading,
70
+ ['ul', scriptGroups.map((scriptGroup) => {
71
+ return ['li', [
72
+ ['b', [
73
+ uniqueTextPlaceholder + cleanupText(scriptGroup.textContent)
74
+ ]],
75
+ (() => {
76
+ const lists = [];
77
+ do {
78
+ const a = scriptGroup.querySelector('a');
79
+ const title = a && a.title;
80
+ let cleanedText;
81
+ if (scriptGroup.matches('.mb')) {
82
+ cleanedText = cleanupText(scriptGroup.textContent);
83
+ const children = [uniqueTextPlaceholder + cleanedText];
84
+ lastChildren = children;
85
+ lists.push(
86
+ ['li', {title}, children]
87
+ );
88
+ } else if (scriptGroup.matches('.pb,.sb')) {
89
+ cleanedText = cleanupText(scriptGroup.textContent);
90
+ const children = [
91
+ ['i', [
92
+ uniqueTextPlaceholder + cleanedText
93
+ ]]
94
+ ];
95
+ if (!lastChildren) { // A few rare cases to handle, e.g., "Other"
96
+ lists.push(
97
+ ['li', {title}, children]
98
+ );
99
+ } else {
100
+ if (!lastChildren[1]) {
101
+ lastChildren[1] = ['ul', []];
102
+ }
103
+ lastChildren[1][1].push(
104
+ ['li', {title}, children]
105
+ );
106
+ }
107
+ }
108
+ if (cleanedText && title) {
109
+ scriptsAndStartRanges.push({
110
+ script: getChromeSafeLocaleKey(cleanedText),
111
+ startRange: title.replace(/-.*$/u, '')
112
+ });
113
+ }
114
+ scriptGroup = scriptGroup.nextElementSibling;
115
+ } while (scriptGroup && !scriptGroup.matches('p.sg'));
116
+ lastChildren = null;
117
+ if (!lists.length) { // Just be safe
118
+ return '';
119
+ }
120
+ return ['ul', lists];
121
+ })()
122
+ ]];
123
+ })]
124
+ ]];
125
+ });
126
+ // console.log('m', majorHeading, scriptGroups);
127
+
128
+ /**
129
+ * @typedef {JSON} LocaleFileContents
130
+ * @todo Indicate more precise format
131
+ */
132
+
133
+ /**
134
+ * @param {string[]} localeFiles
135
+ * @param {LocaleFileContents} localeFileContents
136
+ * @returns {Promise<void>}
137
+ */
138
+ async function saveLocalesWithoutDupes (localeFiles, localeFileContents) {
139
+ return await Promise.all(
140
+ localeFiles.map((localeFile, i) => {
141
+ return fs.writeFile(
142
+ localeFile, JSON.stringify(localeFileContents[i], null, 2) + '\n'
143
+ );
144
+ })
145
+ );
146
+ }
147
+
148
+ /**
149
+ * @param {string[]} newScriptNames
150
+ * @param {LocaleFileContents} localeFileContents
151
+ * @returns {Promise<void>}
152
+ */
153
+ async function deleteUnusedScriptNames (newScriptNames, localeFileContents) {
154
+ const {lastScriptNames} = JSON.parse(
155
+ await fs.readFile(lastScriptNamesFile, 'utf8')
156
+ );
157
+ lastScriptNames.forEach((lastScriptName) => {
158
+ if (!newScriptNames.includes(lastScriptName)) {
159
+ localeFileContents.forEach((lfc) => {
160
+ delete lfc.body[lastScriptName];
161
+ });
162
+ }
163
+ });
164
+ }
165
+
166
+ // We only actually need the ESLint `indent` rule for the localized
167
+ // version, but add anyway
168
+ await fs.writeFile(
169
+ 'browser_action/unicode/unicodeScripts.js',
170
+ `/* eslint-disable @stylistic/comma-spacing, quotes, indent */
171
+ // Do not edit this file; this is an auto-generated file used to
172
+ // build a hierarchy of script names. It is built by
173
+ // \`parseUnicodeCharts.js\` using ${chartsURL}.
174
+ function unicodeScripts (_) {
175
+ return ` + (localize
176
+ ? await (async () => {
177
+ const localesDir = '_locales';
178
+ const dirs = (
179
+ await fs.readdir(localesDir)
180
+ ).filter((f) => !f.includes('.'));
181
+ const localeFiles = dirs.map(
182
+ (dir) => path.join(localesDir, dir, 'messages.json')
183
+ );
184
+ const localeFileContents = (
185
+ await Promise.all(localeFiles.map((localeFile) => {
186
+ return fs.readFile(localeFile, 'utf8');
187
+ }))
188
+ ).map((fileContents) => {
189
+ return JSON.parse(fileContents);
190
+ });
191
+
192
+ const dirResults = (
193
+ await recurseDirectory({directory: 'browser_action'})
194
+ ).filter(({keys}) => {
195
+ return keys.length;
196
+ });
197
+ // console.log('dirResults', JSON.stringify(dirResults, null, 2));
198
+ const keyMap = {};
199
+ dirResults.forEach(({path: pth, keys}) => {
200
+ keys.forEach((key) => {
201
+ localeFileContents.forEach((lfc, i) => {
202
+ if (!(key in lfc.body)) {
203
+ if (!keyMap[key]) {
204
+ keyMap[key] = {paths: [], locales: []};
205
+ }
206
+ if (!keyMap[key].paths.includes(pth)) {
207
+ keyMap[key].paths.push(pth);
208
+ }
209
+ if (!keyMap[key].locales.includes(localeFiles[i])) {
210
+ keyMap[key].locales.push(localeFiles[i]);
211
+ }
212
+ }
213
+ });
214
+ });
215
+ });
216
+
217
+ /*
218
+ Object.entries(keyMap).forEach(([key, {paths, locales}]) => {
219
+ // Working:
220
+ // console.log(
221
+ // `key "${key}" found in paths [${paths.join(', ')}] is ` +
222
+ // `missing in locales: [${locales.join(', ')}]\n`
223
+ // );
224
+ });
225
+ */
226
+
227
+ localeFileContents.forEach((lfc, i) => {
228
+ if (i !== 0) { // Just test one for now
229
+ return;
230
+ }
231
+ Object.keys(lfc.body).forEach((localeKey) => {
232
+ if (!dirResults.some(({
233
+ keys
234
+ // path: pth
235
+ }) => {
236
+ // console.log('keys', keys);
237
+ return keys.includes(localeKey);
238
+ })) {
239
+ // Working:
240
+ // console.log(`Locale key "${localeKey}" not present in keys
241
+ // used in any of the found file paths.`);
242
+ }
243
+ });
244
+ });
245
+
246
+ const newScriptNames = [];
247
+ const ret = JSON.stringify(['ul', jamilih], null, 2).replaceAll(
248
+ new RegExp(
249
+ String.raw`^(\s*)"` + uniqueTextPlaceholder + '(.*)"(,)?$',
250
+ 'gum'
251
+ ),
252
+ (_, initialWS, key, possibleComma = '') => {
253
+ const chromeSafeLocaleKey = getChromeSafeLocaleKey(key);
254
+
255
+ newScriptNames.push(chromeSafeLocaleKey);
256
+ // Insert `chromeSafeLocaleKey` if not present;
257
+ // Todo: Hard-code-test each locale to make sure no other
258
+ // l10n errors for missing keys, script or otherwise
259
+ localeFileContents.forEach((lfc) => {
260
+ /*
261
+ // Works
262
+ localeFileContents.forEach((lfc2, j) => {
263
+ if (i === j) {
264
+ return;
265
+ }
266
+ Object.entries(lfc.body).forEach(([key, val]) => {
267
+ if (!(key in lfc2)) {
268
+ console.log(`key, ${key}, not present in ${localeFiles[j]}`);
269
+ }
270
+ });
271
+ });
272
+ */
273
+ if (lfc.body.langCode.message !== 'hu-HU') {
274
+ // return;
275
+ }
276
+ if (!(chromeSafeLocaleKey in lfc.body)) {
277
+ lfc.body[chromeSafeLocaleKey] = {
278
+ message: key
279
+ };
280
+ // console.log(1, chromeSafeLocaleKey, key);
281
+ // fs.writeFile(localeFiles[i], JSON.stringify(lfc, null, 2));
282
+ }
283
+ });
284
+ /*
285
+ // Find change in *values*
286
+ if (
287
+ lfc.body[chromeSafeLocaleKey] &&
288
+ lfc.body[chromeSafeLocaleKey].message !== key
289
+ ) {
290
+ console.log(key, lfc.body[chromeSafeLocaleKey].message);
291
+ }
292
+ */
293
+ return `${initialWS}_("${chromeSafeLocaleKey}")${possibleComma}`;
294
+ }
295
+ );
296
+ await deleteUnusedScriptNames(newScriptNames, localeFileContents);
297
+ await saveLocalesWithoutDupes(localeFiles, localeFileContents);
298
+
299
+ /* eslint-disable @stylistic/max-len -- JSON */
300
+ await fs.writeFile(lastScriptNamesFile, `
301
+ {
302
+ "$comment": "Do not edit this file; this is an auto-generated file used to track script names, some of which may end up needing to be deleted from locale files if no longer in use",
303
+ "lastScriptNames": ${JSON.stringify(newScriptNames)}
304
+ }
305
+ `);
306
+ /* eslint-enable @stylistic/max-len -- JSON */
307
+ return ret;
308
+ })()
309
+ : JSON.stringify(['ul', jamilih], null, 2)
310
+ ) +
311
+ `;
312
+ }
313
+ export default unicodeScripts;
314
+ `
315
+ );
316
+
317
+ scriptsAndStartRanges.sort((
318
+ {startRange: aStartRange}, {startRange: bStartRange}
319
+ ) => {
320
+ return Number.parseInt(aStartRange, 16) >
321
+ Number.parseInt(bStartRange, 16)
322
+ ? 1
323
+ : -1;
324
+ });
325
+
326
+ let ifElseBlocks = '';
327
+ let lastStartRange = '0000';
328
+ let lastScript = 'null';
329
+ scriptsAndStartRanges.forEach(({script, startRange}) => {
330
+ if (startRange === '0000' || !lastScript) {
331
+ return;
332
+ }
333
+ // Todo: Add plane, privateuse, surrogate info
334
+ ifElseBlocks += `
335
+ } else if (num < 0x${startRange}) {
336
+ codePointStart = '${lastStartRange}';
337
+ script = _('${lastScript}');${
338
+ // Not in chart:
339
+ // startRange === 'DB80' ? "surrogate = _('High_Private_Use_Surrogate')" :
340
+ lastStartRange === 'D800'
341
+ ? `
342
+ surrogate = _('High_Surrogate');`
343
+ : lastStartRange === 'DC00'
344
+ ? `
345
+ surrogate = _('Low_Surrogate');`
346
+ : ['E000', 'F0000', '100000'].includes(lastStartRange)
347
+ ? `
348
+ privateuse = true;`
349
+ : ''
350
+ }`;
351
+ lastStartRange = startRange;
352
+ lastScript = script;
353
+ });
354
+
355
+ /**
356
+ * @param {PlainObject} cfg
357
+ * @param {string} cfg.directory
358
+ * @param {string} [cfg.basePath]
359
+ * @param {{path, keys}} cfg.results
360
+ * @returns {Promise<{path, keys}|void>}
361
+ */
362
+ async function recurseDirectory ({directory, basePath = './', results}) {
363
+ const dirPath = path.join(basePath, directory);
364
+ const filesAndDirs = await fs.readdir(dirPath);
365
+ let finalPromise = false;
366
+ if (!results) {
367
+ finalPromise = true;
368
+ results = [];
369
+ }
370
+ await Promise.all(
371
+ filesAndDirs.filter((fileOrDir) => {
372
+ // Is a directory or JavaScript file
373
+ return !fileOrDir.includes('.') || (/\.js$/u).test(fileOrDir);
374
+ }).map(async (fileOrDir) => {
375
+ if (!fileOrDir.includes('.')) { // Should check `isDirectory()` or such
376
+ return recurseDirectory({
377
+ directory: fileOrDir,
378
+ basePath: dirPath,
379
+ results
380
+ });
381
+ }
382
+ const pth = path.join(dirPath, fileOrDir);
383
+ const contents = await fs.readFile(pth, 'utf8');
384
+
385
+ const i18nRegex = // /_\((?:['"]([^'"]*)['"])\)/g;
386
+ // Todo: Works to find variable forms:
387
+ /_\((?:['"](?<key1>[^'"]*)['"]|(?<key2>[^"')][^{)]*|[^{)]*[^"')]))\)/gu;
388
+
389
+ const keys = [];
390
+ let result;
391
+ while ((result = (i18nRegex.exec(contents))) !== null) {
392
+ keys.push(result.groups.key1 || result.groups.key2);
393
+ }
394
+ results.push({path: pth, keys});
395
+ return undefined;
396
+ })
397
+ );
398
+ if (finalPromise) {
399
+ return results;
400
+ }
401
+ return undefined;
402
+ }
403
+
404
+ // console.log('scriptsAndStartRanges', scriptsAndStartRanges);
405
+
406
+ const output = `
407
+ // Todo: Auto-generate this function
408
+
409
+ /**
410
+ * @param {PositiveInteger} num
411
+ * @param {IntlDom} _
412
+ * @returns {{
413
+ * codePointStart: string, script: string, plane: PositiveInteger,
414
+ * privateuse: boolean, surrogate: boolean|string
415
+ * }}
416
+ */
417
+ export default function getScriptInfoForCodePoint (num, _) {
418
+ let privateuse = false, surrogate = false;
419
+ let plane = num >= 0x10000 && num <= 0x1FFFF ? 1 : 0;
420
+ let script = '', codePointStart = '';
421
+ ${ifElseBlocks.slice('} else '.length) + '\n }'}
422
+
423
+ return {codePointStart, script, plane, privateuse, surrogate};
424
+ }
425
+ `;
426
+
427
+ // eslint-disable-next-line no-console -- Debugging
428
+ console.log('output', output);
@@ -0,0 +1,24 @@
1
+ // As the Unicode server does not have CORS enabled, we can't adapt
2
+ // this file for the browser, but we're keeping this file for demonstrating
3
+ // an approach for live-obtaining the live version.
4
+
5
+ import fs from 'fs/promises';
6
+
7
+ import download from 'download';
8
+ import extract from 'extract-zip';
9
+
10
+ const args = process.argv.slice(2);
11
+
12
+ const targetDir = `${process.cwd()}/download/UCD`;
13
+ const ucdZip = `${targetDir}/UCD.zip`;
14
+
15
+ if (args.includes('download')) {
16
+ await download(
17
+ 'https://www.unicode.org/Public/UCD/latest/ucd/UCD.zip', targetDir
18
+ );
19
+ }
20
+
21
+ if (args.includes('extract') || args.includes('download')) {
22
+ await extract(ucdZip, {dir: targetDir});
23
+ await fs.unlink(ucdZip);
24
+ }