@pfmcodes/caret 0.1.1 → 0.1.3

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.
@@ -0,0 +1,428 @@
1
+ /**
2
+ * Caret Editor - Language Utilities
3
+ * Helper types and utilities for working with language registration
4
+ */
5
+
6
+ import type { LanguageFn } from 'highlight.js';
7
+ import type { PreRegisteredLanguage, LanguagesModule } from './languages';
8
+
9
+ /**
10
+ * Language information object
11
+ */
12
+ export interface LanguageInfo {
13
+ /**
14
+ * Primary language identifier
15
+ */
16
+ name: string;
17
+
18
+ /**
19
+ * Display name for the language
20
+ */
21
+ displayName: string;
22
+
23
+ /**
24
+ * File extensions associated with this language
25
+ */
26
+ extensions: string[];
27
+
28
+ /**
29
+ * Common aliases for the language
30
+ */
31
+ aliases: string[];
32
+
33
+ /**
34
+ * Whether this language is pre-registered
35
+ */
36
+ isPreRegistered: boolean;
37
+ }
38
+
39
+ /**
40
+ * Map of language information for all pre-registered languages
41
+ */
42
+ export const LANGUAGE_INFO: Record<PreRegisteredLanguage, LanguageInfo> = {
43
+ javascript: {
44
+ name: 'javascript',
45
+ displayName: 'JavaScript',
46
+ extensions: ['.js', '.mjs', '.cjs'],
47
+ aliases: ['js'],
48
+ isPreRegistered: true
49
+ },
50
+ js: {
51
+ name: 'javascript',
52
+ displayName: 'JavaScript',
53
+ extensions: ['.js', '.mjs', '.cjs'],
54
+ aliases: ['javascript'],
55
+ isPreRegistered: true
56
+ },
57
+ typescript: {
58
+ name: 'typescript',
59
+ displayName: 'TypeScript',
60
+ extensions: ['.ts', '.tsx'],
61
+ aliases: ['ts'],
62
+ isPreRegistered: true
63
+ },
64
+ python: {
65
+ name: 'python',
66
+ displayName: 'Python',
67
+ extensions: ['.py', '.pyw'],
68
+ aliases: ['py'],
69
+ isPreRegistered: true
70
+ },
71
+ java: {
72
+ name: 'java',
73
+ displayName: 'Java',
74
+ extensions: ['.java'],
75
+ aliases: [],
76
+ isPreRegistered: true
77
+ },
78
+ csharp: {
79
+ name: 'csharp',
80
+ displayName: 'C#',
81
+ extensions: ['.cs'],
82
+ aliases: ['cs', 'c#'],
83
+ isPreRegistered: true
84
+ },
85
+ cpp: {
86
+ name: 'cpp',
87
+ displayName: 'C++',
88
+ extensions: ['.cpp', '.cc', '.cxx', '.hpp', '.h'],
89
+ aliases: ['c++'],
90
+ isPreRegistered: true
91
+ },
92
+ c: {
93
+ name: 'c',
94
+ displayName: 'C',
95
+ extensions: ['.c', '.h'],
96
+ aliases: [],
97
+ isPreRegistered: true
98
+ },
99
+ ruby: {
100
+ name: 'ruby',
101
+ displayName: 'Ruby',
102
+ extensions: ['.rb'],
103
+ aliases: ['rb'],
104
+ isPreRegistered: true
105
+ },
106
+ php: {
107
+ name: 'php',
108
+ displayName: 'PHP',
109
+ extensions: ['.php'],
110
+ aliases: [],
111
+ isPreRegistered: true
112
+ },
113
+ go: {
114
+ name: 'go',
115
+ displayName: 'Go',
116
+ extensions: ['.go'],
117
+ aliases: ['golang'],
118
+ isPreRegistered: true
119
+ },
120
+ rust: {
121
+ name: 'rust',
122
+ displayName: 'Rust',
123
+ extensions: ['.rs'],
124
+ aliases: ['rs'],
125
+ isPreRegistered: true
126
+ },
127
+ kotlin: {
128
+ name: 'kotlin',
129
+ displayName: 'Kotlin',
130
+ extensions: ['.kt', '.kts'],
131
+ aliases: ['kt'],
132
+ isPreRegistered: true
133
+ },
134
+ swift: {
135
+ name: 'swift',
136
+ displayName: 'Swift',
137
+ extensions: ['.swift'],
138
+ aliases: [],
139
+ isPreRegistered: true
140
+ },
141
+ xml: {
142
+ name: 'xml',
143
+ displayName: 'XML',
144
+ extensions: ['.xml'],
145
+ aliases: [],
146
+ isPreRegistered: true
147
+ },
148
+ html: {
149
+ name: 'html',
150
+ displayName: 'HTML',
151
+ extensions: ['.html', '.htm'],
152
+ aliases: [],
153
+ isPreRegistered: true
154
+ },
155
+ svg: {
156
+ name: 'svg',
157
+ displayName: 'SVG',
158
+ extensions: ['.svg'],
159
+ aliases: [],
160
+ isPreRegistered: true
161
+ },
162
+ css: {
163
+ name: 'css',
164
+ displayName: 'CSS',
165
+ extensions: ['.css'],
166
+ aliases: [],
167
+ isPreRegistered: true
168
+ },
169
+ json: {
170
+ name: 'json',
171
+ displayName: 'JSON',
172
+ extensions: ['.json'],
173
+ aliases: [],
174
+ isPreRegistered: true
175
+ },
176
+ bash: {
177
+ name: 'bash',
178
+ displayName: 'Bash',
179
+ extensions: ['.sh', '.bash'],
180
+ aliases: ['shell', 'sh'],
181
+ isPreRegistered: true
182
+ },
183
+ shell: {
184
+ name: 'shell',
185
+ displayName: 'Shell',
186
+ extensions: ['.sh'],
187
+ aliases: ['bash', 'sh'],
188
+ isPreRegistered: true
189
+ },
190
+ sh: {
191
+ name: 'sh',
192
+ displayName: 'Shell',
193
+ extensions: ['.sh'],
194
+ aliases: ['bash', 'shell'],
195
+ isPreRegistered: true
196
+ },
197
+ plaintext: {
198
+ name: 'plaintext',
199
+ displayName: 'Plain Text',
200
+ extensions: ['.txt'],
201
+ aliases: ['text', 'plain'],
202
+ isPreRegistered: true
203
+ }
204
+ };
205
+
206
+ /**
207
+ * Type guard to check if a language is pre-registered
208
+ */
209
+ export function isPreRegisteredLanguage(language: string): language is PreRegisteredLanguage {
210
+ const preRegistered: PreRegisteredLanguage[] = [
211
+ 'javascript', 'js', 'xml', 'html', 'svg', 'css',
212
+ 'python', 'java', 'csharp', 'cpp', 'ruby', 'php',
213
+ 'go', 'c', 'rust', 'kotlin', 'swift', 'typescript',
214
+ 'json', 'bash', 'shell', 'sh', 'plaintext'
215
+ ];
216
+ return preRegistered.includes(language as PreRegisteredLanguage);
217
+ }
218
+
219
+ /**
220
+ * Check if a language is currently registered
221
+ */
222
+ export function isLanguageRegistered(
223
+ languages: LanguagesModule,
224
+ language: string
225
+ ): boolean {
226
+ return languages.registeredLanguages.includes(language);
227
+ }
228
+
229
+ /**
230
+ * Get language information by name or alias
231
+ */
232
+ export function getLanguageInfo(language: string): LanguageInfo | null {
233
+ // Check if it's a direct match
234
+ if (isPreRegisteredLanguage(language)) {
235
+ return LANGUAGE_INFO[language];
236
+ }
237
+
238
+ // Check if it's an alias
239
+ for (const [key, info] of Object.entries(LANGUAGE_INFO)) {
240
+ if (info.aliases.includes(language)) {
241
+ return info;
242
+ }
243
+ }
244
+
245
+ return null;
246
+ }
247
+
248
+ /**
249
+ * Get language by file extension
250
+ */
251
+ export function getLanguageByExtension(extension: string): string | null {
252
+ const normalizedExt = extension.startsWith('.') ? extension : `.${extension}`;
253
+
254
+ for (const [key, info] of Object.entries(LANGUAGE_INFO)) {
255
+ if (info.extensions.includes(normalizedExt)) {
256
+ return info.name;
257
+ }
258
+ }
259
+
260
+ return null;
261
+ }
262
+
263
+ /**
264
+ * Get all available language names (primary names only)
265
+ */
266
+ export function getAllLanguageNames(): string[] {
267
+ const uniqueNames = new Set<string>();
268
+
269
+ for (const info of Object.values(LANGUAGE_INFO)) {
270
+ uniqueNames.add(info.name);
271
+ }
272
+
273
+ return Array.from(uniqueNames);
274
+ }
275
+
276
+ /**
277
+ * Language registration helper class
278
+ */
279
+ export class LanguageRegistry {
280
+ private languages: LanguagesModule;
281
+
282
+ constructor(languagesModule: LanguagesModule) {
283
+ this.languages = languagesModule;
284
+ }
285
+
286
+ /**
287
+ * Register a language if it's not already registered
288
+ */
289
+ registerIfNeeded(name: string, definition: LanguageFn): boolean {
290
+ if (this.isRegistered(name)) {
291
+ return false;
292
+ }
293
+
294
+ this.languages.registerLanguage(name, definition);
295
+ return true;
296
+ }
297
+
298
+ /**
299
+ * Check if a language is registered
300
+ */
301
+ isRegistered(name: string): boolean {
302
+ return isLanguageRegistered(this.languages, name);
303
+ }
304
+
305
+ /**
306
+ * Get all registered languages
307
+ */
308
+ getRegistered(): string[] {
309
+ return [...this.languages.registeredLanguages];
310
+ }
311
+
312
+ /**
313
+ * Get language info
314
+ */
315
+ getInfo(name: string): LanguageInfo | null {
316
+ return getLanguageInfo(name);
317
+ }
318
+
319
+ /**
320
+ * Register multiple languages at once
321
+ */
322
+ registerBulk(definitions: Record<string, LanguageFn>): void {
323
+ for (const [name, definition] of Object.entries(definitions)) {
324
+ this.registerIfNeeded(name, definition);
325
+ }
326
+ }
327
+ }
328
+
329
+ /**
330
+ * Options for lazy language loading
331
+ */
332
+ export interface LazyLanguageOptions {
333
+ /**
334
+ * Base path for language modules
335
+ * @default "../highlight.js/es/languages/"
336
+ */
337
+ basePath?: string;
338
+
339
+ /**
340
+ * Whether to cache loaded languages
341
+ * @default true
342
+ */
343
+ cache?: boolean;
344
+ }
345
+
346
+ /**
347
+ * Lazy language loader for dynamic imports
348
+ */
349
+ export class LazyLanguageLoader {
350
+ private languages: LanguagesModule;
351
+ private basePath: string;
352
+ private cache: boolean;
353
+ private loadedLanguages: Set<string> = new Set();
354
+
355
+ constructor(
356
+ languagesModule: LanguagesModule,
357
+ options: LazyLanguageOptions = {}
358
+ ) {
359
+ this.languages = languagesModule;
360
+ this.basePath = options.basePath || '../highlight.js/es/languages/';
361
+ this.cache = options.cache !== false;
362
+ }
363
+
364
+ /**
365
+ * Load a language dynamically
366
+ */
367
+ async loadLanguage(name: string): Promise<void> {
368
+ // Check if already loaded (if caching is enabled)
369
+ if (this.cache && this.loadedLanguages.has(name)) {
370
+ return;
371
+ }
372
+
373
+ // Check if pre-registered
374
+ if (isLanguageRegistered(this.languages, name)) {
375
+ this.loadedLanguages.add(name);
376
+ return;
377
+ }
378
+
379
+ try {
380
+ const module = await import(
381
+ `${this.basePath}${name}.js`
382
+ );
383
+
384
+ const definition = module.default;
385
+ this.languages.registerLanguage(name, definition);
386
+
387
+ if (this.cache) {
388
+ this.loadedLanguages.add(name);
389
+ }
390
+ } catch (error) {
391
+ throw new Error(
392
+ `Failed to load language "${name}": ${(error as Error).message}`
393
+ );
394
+ }
395
+ }
396
+
397
+ /**
398
+ * Load multiple languages
399
+ */
400
+ async loadLanguages(names: string[]): Promise<void> {
401
+ await Promise.all(names.map(name => this.loadLanguage(name)));
402
+ }
403
+
404
+ /**
405
+ * Check if a language has been loaded
406
+ */
407
+ isLoaded(name: string): boolean {
408
+ return this.loadedLanguages.has(name);
409
+ }
410
+
411
+ /**
412
+ * Clear the cache
413
+ */
414
+ clearCache(): void {
415
+ this.loadedLanguages.clear();
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Export utility functions
421
+ */
422
+ export const LanguageUtils = {
423
+ isPreRegisteredLanguage,
424
+ isLanguageRegistered,
425
+ getLanguageInfo,
426
+ getLanguageByExtension,
427
+ getAllLanguageNames
428
+ };
@@ -1,28 +1,143 @@
1
- import type hljs from './highlight.js/es/core.js';
1
+ /**
2
+ * Caret Editor - Languages Module Type Definitions
3
+ * Handles language registration and management for syntax highlighting
4
+ */
5
+
6
+ import type { HLJSApi, LanguageFn } from 'highlight.js';
7
+
8
+ /**
9
+ * Supported languages that are pre-registered by default
10
+ */
11
+ export type PreRegisteredLanguage =
12
+ | 'javascript'
13
+ | 'js'
14
+ | 'xml'
15
+ | 'html'
16
+ | 'svg'
17
+ | 'css'
18
+ | 'python'
19
+ | 'java'
20
+ | 'csharp'
21
+ | 'cpp'
22
+ | 'ruby'
23
+ | 'php'
24
+ | 'go'
25
+ | 'c'
26
+ | 'rust'
27
+ | 'kotlin'
28
+ | 'swift'
29
+ | 'typescript'
30
+ | 'json'
31
+ | 'bash'
32
+ | 'shell'
33
+ | 'sh'
34
+ | 'plaintext';
35
+
36
+ /**
37
+ * Language registration status
38
+ */
39
+ export interface LanguageRegistration {
40
+ /**
41
+ * Language identifier/name
42
+ */
43
+ name: string;
2
44
 
3
- export function init(): void;
45
+ /**
46
+ * Whether the language is currently registered
47
+ */
48
+ isRegistered: boolean;
49
+
50
+ /**
51
+ * Aliases for this language
52
+ */
53
+ aliases?: string[];
54
+ }
4
55
 
5
56
  /**
6
- * Caret language API
57
+ * Languages module interface
7
58
  */
8
- declare const languages: {
59
+ export interface LanguagesModule {
9
60
  /**
10
- * Register all built-in languages
61
+ * Initialize and register all default languages
62
+ * This should be called once before using the editor
63
+ *
64
+ * @example
65
+ * ```typescript
66
+ * import languages from './languages.js';
67
+ * languages.init();
68
+ * ```
11
69
  */
12
70
  init(): void;
13
71
 
14
72
  /**
15
- * Register a custom Highlight.js language
73
+ * Array of currently registered language identifiers
74
+ * This includes both the primary language names and their aliases
75
+ *
76
+ * @example
77
+ * ```typescript
78
+ * console.log(languages.registeredLanguages);
79
+ * // ['javascript', 'js', 'xml', 'html', 'python', ...]
80
+ * ```
81
+ */
82
+ registeredLanguages: string[];
83
+
84
+ /**
85
+ * Register a new language for syntax highlighting
86
+ * If the language is already registered, this will update its definition
87
+ *
88
+ * @param name - Language identifier (e.g., "rust", "go", "python")
89
+ * @param definition - Highlight.js language definition function
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * import languages from './languages.js';
94
+ * import scala from 'highlight.js/es/languages/scala';
95
+ *
96
+ * languages.registerLanguage('scala', scala);
97
+ * ```
16
98
  */
17
- registerLanguage(
18
- name: string,
19
- language: (hljs: typeof import('./highlight.js/es/core.js')) => any
20
- ): void;
99
+ registerLanguage(name: string, definition: LanguageFn): void;
21
100
 
22
101
  /**
23
- * Exposed Highlight.js core instance
102
+ * Reference to the Highlight.js core instance
103
+ * Provides direct access to all Highlight.js methods
24
104
  */
25
- hljs: typeof import('./highlight.js/es/core.js');
26
- };
105
+ hljs: HLJSApi;
106
+ }
107
+
108
+ /**
109
+ * Default export - Languages module instance
110
+ */
111
+ declare const languages: LanguagesModule;
27
112
 
28
113
  export default languages;
114
+
115
+ /**
116
+ * Helper type to check if a language is registered
117
+ */
118
+ export type IsLanguageRegistered<T extends string> = T extends PreRegisteredLanguage
119
+ ? true
120
+ : boolean;
121
+
122
+ /**
123
+ * Language definition map for custom language registration
124
+ */
125
+ export interface LanguageDefinitionMap {
126
+ [languageName: string]: LanguageFn;
127
+ }
128
+
129
+ /**
130
+ * Configuration for bulk language registration
131
+ */
132
+ export interface BulkLanguageConfig {
133
+ /**
134
+ * Map of language names to their definition functions
135
+ */
136
+ languages: LanguageDefinitionMap;
137
+
138
+ /**
139
+ * Whether to skip already registered languages
140
+ * @default true
141
+ */
142
+ skipIfRegistered?: boolean;
143
+ }