@shikijs/core 1.6.5 → 1.8.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.
@@ -1005,6 +1005,11 @@ interface HighlighterCoreOptions {
1005
1005
  * Load wasm file from a custom path or using a custom function.
1006
1006
  */
1007
1007
  loadWasm?: LoadWasmOptions;
1008
+ /**
1009
+ * Emit console warnings to alert users of potential issues.
1010
+ * @default true
1011
+ */
1012
+ warnings?: boolean;
1008
1013
  }
1009
1014
  interface BundledHighlighterOptions<L extends string, T extends string> {
1010
1015
  /**
package/dist/index.mjs CHANGED
@@ -4651,6 +4651,50 @@ async function main(init) {
4651
4651
  }
4652
4652
  return false;
4653
4653
  }
4654
+ const UTF8Decoder = typeof TextDecoder != 'undefined' ? new TextDecoder('utf8') : undefined;
4655
+ function UTF8ArrayToString(heapOrArray, idx, maxBytesToRead = 1024) {
4656
+ const endIdx = idx + maxBytesToRead;
4657
+ let endPtr = idx;
4658
+ while (heapOrArray[endPtr] && !(endPtr >= endIdx))
4659
+ ++endPtr;
4660
+ if (endPtr - idx > 16 && heapOrArray.buffer && UTF8Decoder) {
4661
+ return UTF8Decoder.decode(heapOrArray.subarray(idx, endPtr));
4662
+ }
4663
+ let str = '';
4664
+ while (idx < endPtr) {
4665
+ let u0 = heapOrArray[idx++];
4666
+ if (!(u0 & 128)) {
4667
+ str += String.fromCharCode(u0);
4668
+ continue;
4669
+ }
4670
+ const u1 = heapOrArray[idx++] & 63;
4671
+ if ((u0 & 224) === 192) {
4672
+ str += String.fromCharCode(((u0 & 31) << 6) | u1);
4673
+ continue;
4674
+ }
4675
+ const u2 = heapOrArray[idx++] & 63;
4676
+ if ((u0 & 240) === 224) {
4677
+ u0 = ((u0 & 15) << 12) | (u1 << 6) | u2;
4678
+ }
4679
+ else {
4680
+ u0 = ((u0 & 7) << 18)
4681
+ | (u1 << 12)
4682
+ | (u2 << 6)
4683
+ | (heapOrArray[idx++] & 63);
4684
+ }
4685
+ if (u0 < 65536) {
4686
+ str += String.fromCharCode(u0);
4687
+ }
4688
+ else {
4689
+ const ch = u0 - 65536;
4690
+ str += String.fromCharCode(55296 | (ch >> 10), 56320 | (ch & 1023));
4691
+ }
4692
+ }
4693
+ return str;
4694
+ }
4695
+ function UTF8ToString(ptr, maxBytesToRead) {
4696
+ return ptr ? UTF8ArrayToString(binding.HEAPU8, ptr, maxBytesToRead) : '';
4697
+ }
4654
4698
  const asmLibraryArg = {
4655
4699
  emscripten_get_now: _emscripten_get_now,
4656
4700
  emscripten_memcpy_big: _emscripten_memcpy_big,
@@ -4666,6 +4710,7 @@ async function main(init) {
4666
4710
  wasmMemory = exports.memory;
4667
4711
  updateGlobalBufferAndViews(wasmMemory.buffer);
4668
4712
  Object.assign(binding, exports);
4713
+ binding.UTF8ToString = UTF8ToString;
4669
4714
  }
4670
4715
  await createWasm();
4671
4716
  return binding;
@@ -5154,9 +5199,9 @@ class Registry extends Registry$1 {
5154
5199
  _themes;
5155
5200
  _langs;
5156
5201
  _alias;
5157
- _resolvedThemes = {};
5158
- _resolvedGrammars = {};
5159
- _langMap = {};
5202
+ _resolvedThemes = new Map();
5203
+ _resolvedGrammars = new Map();
5204
+ _langMap = new Map();
5160
5205
  _langGraph = new Map();
5161
5206
  _textmateThemeCache = new WeakMap();
5162
5207
  _loadedThemesCache = null;
@@ -5172,14 +5217,14 @@ class Registry extends Registry$1 {
5172
5217
  }
5173
5218
  getTheme(theme) {
5174
5219
  if (typeof theme === 'string')
5175
- return this._resolvedThemes[theme];
5220
+ return this._resolvedThemes.get(theme);
5176
5221
  else
5177
5222
  return this.loadTheme(theme);
5178
5223
  }
5179
5224
  loadTheme(theme) {
5180
5225
  const _theme = normalizeTheme(theme);
5181
5226
  if (_theme.name) {
5182
- this._resolvedThemes[_theme.name] = _theme;
5227
+ this._resolvedThemes.set(_theme.name, _theme);
5183
5228
  // Reset cache
5184
5229
  this._loadedThemesCache = null;
5185
5230
  }
@@ -5187,7 +5232,7 @@ class Registry extends Registry$1 {
5187
5232
  }
5188
5233
  getLoadedThemes() {
5189
5234
  if (!this._loadedThemesCache)
5190
- this._loadedThemesCache = Object.keys(this._resolvedThemes);
5235
+ this._loadedThemesCache = [...this._resolvedThemes.keys()];
5191
5236
  return this._loadedThemesCache;
5192
5237
  }
5193
5238
  // Override and re-implement this method to cache the textmate themes as `TextMateTheme.createFromRawTheme`
@@ -5214,12 +5259,13 @@ class Registry extends Registry$1 {
5214
5259
  resolved.add(name);
5215
5260
  }
5216
5261
  }
5217
- return this._resolvedGrammars[name];
5262
+ return this._resolvedGrammars.get(name);
5218
5263
  }
5219
5264
  async loadLanguage(lang) {
5220
5265
  if (this.getGrammar(lang.name))
5221
5266
  return;
5222
- const embeddedLazilyBy = new Set(Object.values(this._langMap).filter(i => i.embeddedLangsLazy?.includes(lang.name)));
5267
+ const embeddedLazilyBy = new Set([...this._langMap.values()]
5268
+ .filter(i => i.embeddedLangsLazy?.includes(lang.name)));
5223
5269
  this._resolver.addLanguage(lang);
5224
5270
  const grammarConfig = {
5225
5271
  balancedBracketSelectors: lang.balancedBracketSelectors || ['*'],
@@ -5228,7 +5274,7 @@ class Registry extends Registry$1 {
5228
5274
  // @ts-expect-error Private members, set this to override the previous grammar (that can be a stub)
5229
5275
  this._syncRegistry._rawGrammars.set(lang.scopeName, lang);
5230
5276
  const g = await this.loadGrammarWithConfiguration(lang.scopeName, 1, grammarConfig);
5231
- this._resolvedGrammars[lang.name] = g;
5277
+ this._resolvedGrammars.set(lang.name, g);
5232
5278
  if (lang.aliases) {
5233
5279
  lang.aliases.forEach((alias) => {
5234
5280
  this._alias[alias] = lang.name;
@@ -5239,14 +5285,14 @@ class Registry extends Registry$1 {
5239
5285
  // If there is a language that embeds this language lazily, we need to reload it
5240
5286
  if (embeddedLazilyBy.size) {
5241
5287
  for (const e of embeddedLazilyBy) {
5242
- delete this._resolvedGrammars[e.name];
5288
+ this._resolvedGrammars.delete(e.name);
5243
5289
  // Reset cache
5244
5290
  this._loadedLanguagesCache = null;
5245
5291
  // @ts-expect-error clear cache
5246
5292
  this._syncRegistry?._injectionGrammars?.delete(e.scopeName);
5247
5293
  // @ts-expect-error clear cache
5248
5294
  this._syncRegistry?._grammars?.delete(e.scopeName);
5249
- await this.loadLanguage(this._langMap[e.name]);
5295
+ await this.loadLanguage(this._langMap.get(e.name));
5250
5296
  }
5251
5297
  }
5252
5298
  }
@@ -5254,6 +5300,14 @@ class Registry extends Registry$1 {
5254
5300
  this._themes.map(t => this.loadTheme(t));
5255
5301
  await this.loadLanguages(this._langs);
5256
5302
  }
5303
+ dispose() {
5304
+ super.dispose();
5305
+ this._resolvedThemes.clear();
5306
+ this._resolvedGrammars.clear();
5307
+ this._langMap.clear();
5308
+ this._langGraph.clear();
5309
+ this._loadedThemesCache = null;
5310
+ }
5257
5311
  async loadLanguages(langs) {
5258
5312
  for (const lang of langs)
5259
5313
  this.resolveEmbeddedLanguages(lang);
@@ -5271,16 +5325,19 @@ class Registry extends Registry$1 {
5271
5325
  await this.loadLanguage(lang);
5272
5326
  }
5273
5327
  getLoadedLanguages() {
5274
- if (!this._loadedLanguagesCache)
5275
- this._loadedLanguagesCache = Object.keys({ ...this._resolvedGrammars, ...this._alias });
5328
+ if (!this._loadedLanguagesCache) {
5329
+ this._loadedLanguagesCache = [
5330
+ ...new Set([...this._resolvedGrammars.keys(), ...Object.keys(this._alias)]),
5331
+ ];
5332
+ }
5276
5333
  return this._loadedLanguagesCache;
5277
5334
  }
5278
5335
  resolveEmbeddedLanguages(lang) {
5279
- this._langMap[lang.name] = lang;
5336
+ this._langMap.set(lang.name, lang);
5280
5337
  this._langGraph.set(lang.name, lang);
5281
5338
  if (lang.embeddedLangs) {
5282
5339
  for (const embeddedLang of lang.embeddedLangs)
5283
- this._langGraph.set(embeddedLang, this._langMap[embeddedLang]);
5340
+ this._langGraph.set(embeddedLang, this._langMap.get(embeddedLang));
5284
5341
  }
5285
5342
  }
5286
5343
  }
@@ -5338,10 +5395,15 @@ let _defaultWasmLoader;
5338
5395
  function setDefaultWasmLoader(_loader) {
5339
5396
  _defaultWasmLoader = _loader;
5340
5397
  }
5398
+ let instancesCount = 0;
5341
5399
  /**
5342
5400
  * Get the minimal shiki context for rendering.
5343
5401
  */
5344
5402
  async function getShikiInternal(options = {}) {
5403
+ instancesCount += 1;
5404
+ if (options.warnings !== false && instancesCount >= 10 && instancesCount % 10 === 0)
5405
+ console.warn(`[Shiki] ${instancesCount} instances have been created. Shiki is supposed to be used as a singleton, consider refactoring your code to cache your highlighter instance; Or call \`highlighter.dispose()\` to release unused instances.`);
5406
+ let isDisposed = false;
5345
5407
  async function normalizeGetter(p) {
5346
5408
  return Promise.resolve(typeof p === 'function' ? p() : p).then(r => r.default || r);
5347
5409
  }
@@ -5368,6 +5430,7 @@ async function getShikiInternal(options = {}) {
5368
5430
  await _registry.init();
5369
5431
  let _lastTheme;
5370
5432
  function getLanguage(name) {
5433
+ ensureNotDisposed();
5371
5434
  const _lang = _registry.getGrammar(typeof name === 'string' ? name : name.name);
5372
5435
  if (!_lang)
5373
5436
  throw new ShikiError(`Language \`${name}\` not found, you may need to load it first`);
@@ -5376,12 +5439,14 @@ async function getShikiInternal(options = {}) {
5376
5439
  function getTheme(name) {
5377
5440
  if (name === 'none')
5378
5441
  return { bg: '', fg: '', name: 'none', settings: [], type: 'dark' };
5442
+ ensureNotDisposed();
5379
5443
  const _theme = _registry.getTheme(name);
5380
5444
  if (!_theme)
5381
5445
  throw new ShikiError(`Theme \`${name}\` not found, you may need to load it first`);
5382
5446
  return _theme;
5383
5447
  }
5384
5448
  function setTheme(name) {
5449
+ ensureNotDisposed();
5385
5450
  const theme = getTheme(name);
5386
5451
  if (_lastTheme !== name) {
5387
5452
  _registry.setTheme(theme);
@@ -5394,19 +5459,34 @@ async function getShikiInternal(options = {}) {
5394
5459
  };
5395
5460
  }
5396
5461
  function getLoadedThemes() {
5462
+ ensureNotDisposed();
5397
5463
  return _registry.getLoadedThemes();
5398
5464
  }
5399
5465
  function getLoadedLanguages() {
5466
+ ensureNotDisposed();
5400
5467
  return _registry.getLoadedLanguages();
5401
5468
  }
5402
5469
  async function loadLanguage(...langs) {
5470
+ ensureNotDisposed();
5403
5471
  await _registry.loadLanguages(await resolveLangs(langs));
5404
5472
  }
5405
5473
  async function loadTheme(...themes) {
5474
+ ensureNotDisposed();
5406
5475
  await Promise.all(themes.map(async (theme) => isSpecialTheme(theme)
5407
5476
  ? null
5408
5477
  : _registry.loadTheme(await normalizeGetter(theme))));
5409
5478
  }
5479
+ function ensureNotDisposed() {
5480
+ if (isDisposed)
5481
+ throw new ShikiError('Shiki instance has been disposed');
5482
+ }
5483
+ function dispose() {
5484
+ if (isDisposed)
5485
+ return;
5486
+ isDisposed = true;
5487
+ _registry.dispose();
5488
+ instancesCount -= 1;
5489
+ }
5410
5490
  return {
5411
5491
  setTheme,
5412
5492
  getTheme,
@@ -5415,6 +5495,8 @@ async function getShikiInternal(options = {}) {
5415
5495
  getLoadedLanguages,
5416
5496
  loadLanguage,
5417
5497
  loadTheme,
5498
+ dispose,
5499
+ [Symbol.dispose]: dispose,
5418
5500
  };
5419
5501
  }
5420
5502
 
package/dist/types.d.mts CHANGED
@@ -42,6 +42,14 @@ interface ShikiInternal<BundledLangKeys extends string = never, BundledThemeKeys
42
42
  * Special-handled themes like `none` are not included.
43
43
  */
44
44
  getLoadedThemes: () => string[];
45
+ /**
46
+ * Dispose the internal registry and release resources
47
+ */
48
+ dispose: () => void;
49
+ /**
50
+ * Dispose the internal registry and release resources
51
+ */
52
+ [Symbol.dispose]: () => void;
45
53
  }
46
54
  /**
47
55
  * Generic instance interface of Shiki
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@shikijs/core",
3
3
  "type": "module",
4
- "version": "1.6.5",
4
+ "version": "1.8.0",
5
5
  "description": "Core of Shiki",
6
6
  "author": "Pine Wu <octref@gmail.com>; Anthony Fu <anthonyfu117@hotmail.com>",
7
7
  "license": "MIT",