@changerawr/markdown 1.1.5 → 1.1.6
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.
- package/README.md +94 -9
- package/dist/index.d.mts +129 -2
- package/dist/index.d.ts +129 -2
- package/dist/index.js +306 -23
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +303 -23
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +55 -1
- package/dist/react/index.d.ts +55 -1
- package/dist/react/index.js +282 -22
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +282 -22
- package/dist/react/index.mjs.map +1 -1
- package/dist/standalone.browser.js +304 -22
- package/dist/standalone.d.mts +55 -1
- package/dist/standalone.d.ts +55 -1
- package/dist/standalone.js +282 -22
- package/dist/standalone.js.map +1 -1
- package/dist/standalone.mjs +282 -22
- package/dist/standalone.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// src/parser.ts
|
|
2
2
|
var MarkdownParser = class {
|
|
3
|
+
// Cache compiled regexes
|
|
3
4
|
constructor(config) {
|
|
4
5
|
this.rules = [];
|
|
5
6
|
this.warnings = [];
|
|
7
|
+
this.compiledPatterns = /* @__PURE__ */ new Map();
|
|
6
8
|
this.config = {
|
|
7
9
|
debugMode: false,
|
|
8
10
|
maxIterations: 1e4,
|
|
@@ -12,6 +14,10 @@ var MarkdownParser = class {
|
|
|
12
14
|
}
|
|
13
15
|
addRule(rule) {
|
|
14
16
|
this.rules.push(rule);
|
|
17
|
+
this.compiledPatterns.set(
|
|
18
|
+
rule,
|
|
19
|
+
new RegExp(rule.pattern.source, rule.pattern.flags.replace("g", ""))
|
|
20
|
+
);
|
|
15
21
|
this.rules.sort((a, b) => {
|
|
16
22
|
const aFeatureExtension = ["alert", "button", "embed"].includes(a.name);
|
|
17
23
|
const bFeatureExtension = ["alert", "button", "embed"].includes(b.name);
|
|
@@ -57,12 +63,20 @@ var MarkdownParser = class {
|
|
|
57
63
|
iterationCount++;
|
|
58
64
|
let matched = false;
|
|
59
65
|
let bestMatch = null;
|
|
66
|
+
let nextBestMatchIndex = null;
|
|
60
67
|
for (const rule of this.rules) {
|
|
61
68
|
try {
|
|
62
|
-
const pattern =
|
|
69
|
+
const pattern = this.compiledPatterns.get(rule);
|
|
63
70
|
const match = remaining.match(pattern);
|
|
64
71
|
if (match && match.index !== void 0) {
|
|
65
|
-
|
|
72
|
+
if (match.index === 0) {
|
|
73
|
+
bestMatch = { rule, match, priority: 1e3 };
|
|
74
|
+
break;
|
|
75
|
+
}
|
|
76
|
+
if (nextBestMatchIndex === null || match.index < nextBestMatchIndex) {
|
|
77
|
+
nextBestMatchIndex = match.index;
|
|
78
|
+
}
|
|
79
|
+
const priority = 1e3 - match.index;
|
|
66
80
|
if (!bestMatch || priority > bestMatch.priority || priority === bestMatch.priority && match.index < (bestMatch.match.index || 0)) {
|
|
67
81
|
bestMatch = { rule, match, priority };
|
|
68
82
|
}
|
|
@@ -109,15 +123,14 @@ var MarkdownParser = class {
|
|
|
109
123
|
}
|
|
110
124
|
}
|
|
111
125
|
if (!matched) {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}
|
|
126
|
+
const chunkSize = nextBestMatchIndex !== null ? nextBestMatchIndex : Math.min(remaining.length, 1e3);
|
|
127
|
+
const textChunk = remaining.slice(0, chunkSize);
|
|
128
|
+
tokens.push({
|
|
129
|
+
type: "text",
|
|
130
|
+
content: textChunk,
|
|
131
|
+
raw: textChunk
|
|
132
|
+
});
|
|
133
|
+
remaining = remaining.slice(chunkSize);
|
|
121
134
|
}
|
|
122
135
|
}
|
|
123
136
|
if (iterationCount >= maxIterations) {
|
|
@@ -615,6 +628,171 @@ var MarkdownRenderer = class {
|
|
|
615
628
|
}
|
|
616
629
|
};
|
|
617
630
|
|
|
631
|
+
// src/cache.ts
|
|
632
|
+
var LRUCache = class {
|
|
633
|
+
constructor(capacity = 100) {
|
|
634
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
635
|
+
this.hits = 0;
|
|
636
|
+
this.misses = 0;
|
|
637
|
+
this.evictions = 0;
|
|
638
|
+
if (capacity <= 0) {
|
|
639
|
+
throw new Error("Cache capacity must be greater than 0");
|
|
640
|
+
}
|
|
641
|
+
this.capacity = capacity;
|
|
642
|
+
}
|
|
643
|
+
/**
|
|
644
|
+
* Get a value from the cache
|
|
645
|
+
*/
|
|
646
|
+
get(key) {
|
|
647
|
+
const entry = this.cache.get(key);
|
|
648
|
+
if (entry) {
|
|
649
|
+
entry.timestamp = Date.now();
|
|
650
|
+
entry.accessCount++;
|
|
651
|
+
this.hits++;
|
|
652
|
+
this.cache.delete(key);
|
|
653
|
+
this.cache.set(key, entry);
|
|
654
|
+
return entry.value;
|
|
655
|
+
}
|
|
656
|
+
this.misses++;
|
|
657
|
+
return void 0;
|
|
658
|
+
}
|
|
659
|
+
/**
|
|
660
|
+
* Set a value in the cache
|
|
661
|
+
*/
|
|
662
|
+
set(key, value) {
|
|
663
|
+
if (this.cache.has(key)) {
|
|
664
|
+
this.cache.delete(key);
|
|
665
|
+
} else if (this.cache.size >= this.capacity) {
|
|
666
|
+
this.evictLRU();
|
|
667
|
+
}
|
|
668
|
+
this.cache.set(key, {
|
|
669
|
+
value,
|
|
670
|
+
timestamp: Date.now(),
|
|
671
|
+
accessCount: 0
|
|
672
|
+
});
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Check if a key exists in the cache
|
|
676
|
+
*/
|
|
677
|
+
has(key) {
|
|
678
|
+
return this.cache.has(key);
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Delete a specific key from the cache
|
|
682
|
+
*/
|
|
683
|
+
delete(key) {
|
|
684
|
+
return this.cache.delete(key);
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Clear all entries from the cache
|
|
688
|
+
*/
|
|
689
|
+
clear() {
|
|
690
|
+
this.cache.clear();
|
|
691
|
+
this.hits = 0;
|
|
692
|
+
this.misses = 0;
|
|
693
|
+
this.evictions = 0;
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Get the current size of the cache
|
|
697
|
+
*/
|
|
698
|
+
get size() {
|
|
699
|
+
return this.cache.size;
|
|
700
|
+
}
|
|
701
|
+
/**
|
|
702
|
+
* Get cache statistics
|
|
703
|
+
*/
|
|
704
|
+
getStats() {
|
|
705
|
+
const totalRequests = this.hits + this.misses;
|
|
706
|
+
return {
|
|
707
|
+
size: this.cache.size,
|
|
708
|
+
capacity: this.capacity,
|
|
709
|
+
hits: this.hits,
|
|
710
|
+
misses: this.misses,
|
|
711
|
+
hitRate: totalRequests > 0 ? this.hits / totalRequests : 0,
|
|
712
|
+
evictions: this.evictions
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
/**
|
|
716
|
+
* Reset cache statistics
|
|
717
|
+
*/
|
|
718
|
+
resetStats() {
|
|
719
|
+
this.hits = 0;
|
|
720
|
+
this.misses = 0;
|
|
721
|
+
this.evictions = 0;
|
|
722
|
+
}
|
|
723
|
+
/**
|
|
724
|
+
* Get all keys in the cache
|
|
725
|
+
*/
|
|
726
|
+
keys() {
|
|
727
|
+
return Array.from(this.cache.keys());
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* Get all values in the cache
|
|
731
|
+
*/
|
|
732
|
+
values() {
|
|
733
|
+
return Array.from(this.cache.values()).map((entry) => entry.value);
|
|
734
|
+
}
|
|
735
|
+
/**
|
|
736
|
+
* Update cache capacity and evict if necessary
|
|
737
|
+
*/
|
|
738
|
+
setCapacity(newCapacity) {
|
|
739
|
+
if (newCapacity <= 0) {
|
|
740
|
+
throw new Error("Cache capacity must be greater than 0");
|
|
741
|
+
}
|
|
742
|
+
this.capacity = newCapacity;
|
|
743
|
+
while (this.cache.size > this.capacity) {
|
|
744
|
+
this.evictLRU();
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
/**
|
|
748
|
+
* Evict the least recently used entry
|
|
749
|
+
*/
|
|
750
|
+
evictLRU() {
|
|
751
|
+
const firstKey = this.cache.keys().next().value;
|
|
752
|
+
if (firstKey !== void 0) {
|
|
753
|
+
this.cache.delete(firstKey);
|
|
754
|
+
this.evictions++;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
function hashContent(content) {
|
|
759
|
+
if (content.length > 1e4) {
|
|
760
|
+
const start = content.slice(0, 1e3);
|
|
761
|
+
const middle = content.slice(Math.floor(content.length / 2) - 500, Math.floor(content.length / 2) + 500);
|
|
762
|
+
const end = content.slice(-1e3);
|
|
763
|
+
const sample = content.length + "|" + start + middle + end;
|
|
764
|
+
let hash2 = 2166136261;
|
|
765
|
+
for (let i = 0; i < sample.length; i++) {
|
|
766
|
+
hash2 ^= sample.charCodeAt(i);
|
|
767
|
+
hash2 += (hash2 << 1) + (hash2 << 4) + (hash2 << 7) + (hash2 << 8) + (hash2 << 24);
|
|
768
|
+
}
|
|
769
|
+
return (hash2 >>> 0).toString(36);
|
|
770
|
+
}
|
|
771
|
+
let hash = 2166136261;
|
|
772
|
+
for (let i = 0; i < content.length; i++) {
|
|
773
|
+
hash ^= content.charCodeAt(i);
|
|
774
|
+
hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24);
|
|
775
|
+
}
|
|
776
|
+
return (hash >>> 0).toString(36);
|
|
777
|
+
}
|
|
778
|
+
function memoize(fn, options = {}) {
|
|
779
|
+
const cache = options.cache || new LRUCache(options.maxSize || 100);
|
|
780
|
+
const keyGenerator = options.keyGenerator || ((...args) => JSON.stringify(args));
|
|
781
|
+
const memoized = function(...args) {
|
|
782
|
+
const key = keyGenerator(...args);
|
|
783
|
+
const cached = cache.get(key);
|
|
784
|
+
if (cached !== void 0) {
|
|
785
|
+
return cached;
|
|
786
|
+
}
|
|
787
|
+
const result = fn.apply(this, args);
|
|
788
|
+
cache.set(key, result);
|
|
789
|
+
return result;
|
|
790
|
+
};
|
|
791
|
+
memoized.cache = cache;
|
|
792
|
+
memoized.clearCache = () => cache.clear();
|
|
793
|
+
return memoized;
|
|
794
|
+
}
|
|
795
|
+
|
|
618
796
|
// src/extensions/core/blockquote.ts
|
|
619
797
|
var BlockquoteExtension = {
|
|
620
798
|
name: "blockquote",
|
|
@@ -1587,8 +1765,13 @@ function extractVimeoId(url) {
|
|
|
1587
1765
|
var ChangerawrMarkdown = class {
|
|
1588
1766
|
constructor(config) {
|
|
1589
1767
|
this.extensions = /* @__PURE__ */ new Map();
|
|
1768
|
+
this.parseTime = 0;
|
|
1769
|
+
this.renderTime = 0;
|
|
1770
|
+
this.lastTokenCount = 0;
|
|
1590
1771
|
this.parser = new MarkdownParser(config?.parser);
|
|
1591
1772
|
this.renderer = new MarkdownRenderer(config?.renderer);
|
|
1773
|
+
this.parseCache = new LRUCache(100);
|
|
1774
|
+
this.renderCache = new LRUCache(100);
|
|
1592
1775
|
this.registerCoreExtensions();
|
|
1593
1776
|
this.registerFeatureExtensions();
|
|
1594
1777
|
if (config?.extensions) {
|
|
@@ -1671,14 +1854,83 @@ var ChangerawrMarkdown = class {
|
|
|
1671
1854
|
}
|
|
1672
1855
|
}
|
|
1673
1856
|
parse(markdown3) {
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1857
|
+
const cacheKey = hashContent(markdown3);
|
|
1858
|
+
const cached = this.parseCache.get(cacheKey);
|
|
1859
|
+
if (cached) {
|
|
1860
|
+
this.lastTokenCount = cached.length;
|
|
1861
|
+
return cached;
|
|
1862
|
+
}
|
|
1863
|
+
const startTime = performance.now();
|
|
1864
|
+
const tokens = this.parser.parse(markdown3);
|
|
1865
|
+
this.parseTime = performance.now() - startTime;
|
|
1866
|
+
this.lastTokenCount = tokens.length;
|
|
1867
|
+
this.parseCache.set(cacheKey, tokens);
|
|
1868
|
+
return tokens;
|
|
1869
|
+
}
|
|
1870
|
+
render(tokens, cacheKey) {
|
|
1871
|
+
if (cacheKey) {
|
|
1872
|
+
const cached = this.renderCache.get(cacheKey);
|
|
1873
|
+
if (cached) {
|
|
1874
|
+
return cached;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
const startTime = performance.now();
|
|
1878
|
+
const html = this.renderer.render(tokens);
|
|
1879
|
+
this.renderTime = performance.now() - startTime;
|
|
1880
|
+
if (cacheKey) {
|
|
1881
|
+
this.renderCache.set(cacheKey, html);
|
|
1882
|
+
}
|
|
1883
|
+
return html;
|
|
1678
1884
|
}
|
|
1679
1885
|
toHtml(markdown3) {
|
|
1886
|
+
const cacheKey = hashContent(markdown3);
|
|
1887
|
+
const cachedHtml = this.renderCache.get(cacheKey);
|
|
1888
|
+
if (cachedHtml) {
|
|
1889
|
+
return cachedHtml;
|
|
1890
|
+
}
|
|
1891
|
+
const tokens = this.parse(markdown3);
|
|
1892
|
+
return this.render(tokens, cacheKey);
|
|
1893
|
+
}
|
|
1894
|
+
/**
|
|
1895
|
+
* Render markdown with performance metrics
|
|
1896
|
+
*/
|
|
1897
|
+
toHtmlWithMetrics(markdown3) {
|
|
1898
|
+
const startTotal = performance.now();
|
|
1899
|
+
const parseCacheKey = hashContent(markdown3);
|
|
1900
|
+
const parseCacheHit = this.parseCache.has(parseCacheKey);
|
|
1901
|
+
const html = this.toHtml(markdown3);
|
|
1902
|
+
const totalTime = performance.now() - startTotal;
|
|
1903
|
+
const metrics = {
|
|
1904
|
+
inputSize: markdown3.length,
|
|
1905
|
+
parseTime: this.parseTime,
|
|
1906
|
+
renderTime: this.renderTime,
|
|
1907
|
+
totalTime,
|
|
1908
|
+
tokenCount: this.lastTokenCount,
|
|
1909
|
+
cacheHit: parseCacheHit
|
|
1910
|
+
};
|
|
1911
|
+
return { html, metrics };
|
|
1912
|
+
}
|
|
1913
|
+
/**
|
|
1914
|
+
* Stream-render large documents in chunks for better performance
|
|
1915
|
+
*/
|
|
1916
|
+
async toHtmlStreamed(markdown3, options = {}) {
|
|
1917
|
+
const chunkSize = options.chunkSize || 50;
|
|
1680
1918
|
const tokens = this.parse(markdown3);
|
|
1681
|
-
|
|
1919
|
+
const totalTokens = tokens.length;
|
|
1920
|
+
const chunks = [];
|
|
1921
|
+
for (let i = 0; i < tokens.length; i += chunkSize) {
|
|
1922
|
+
const chunkTokens = tokens.slice(i, Math.min(i + chunkSize, tokens.length));
|
|
1923
|
+
const chunkHtml = this.render(chunkTokens);
|
|
1924
|
+
chunks.push(chunkHtml);
|
|
1925
|
+
if (options.onChunk) {
|
|
1926
|
+
options.onChunk({
|
|
1927
|
+
html: chunkHtml,
|
|
1928
|
+
progress: Math.min(i + chunkSize, tokens.length) / totalTokens
|
|
1929
|
+
});
|
|
1930
|
+
}
|
|
1931
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
1932
|
+
}
|
|
1933
|
+
return chunks.join("");
|
|
1682
1934
|
}
|
|
1683
1935
|
getExtensions() {
|
|
1684
1936
|
return Array.from(this.extensions.keys());
|
|
@@ -1692,25 +1944,50 @@ var ChangerawrMarkdown = class {
|
|
|
1692
1944
|
getDebugInfo() {
|
|
1693
1945
|
return {
|
|
1694
1946
|
warnings: this.getWarnings(),
|
|
1695
|
-
parseTime:
|
|
1696
|
-
renderTime:
|
|
1697
|
-
tokenCount:
|
|
1947
|
+
parseTime: this.parseTime,
|
|
1948
|
+
renderTime: this.renderTime,
|
|
1949
|
+
tokenCount: this.lastTokenCount,
|
|
1698
1950
|
iterationCount: 0
|
|
1699
1951
|
};
|
|
1700
1952
|
}
|
|
1701
1953
|
getPerformanceMetrics() {
|
|
1702
1954
|
return {
|
|
1703
|
-
parseTime:
|
|
1704
|
-
renderTime:
|
|
1705
|
-
totalTime:
|
|
1706
|
-
tokenCount:
|
|
1955
|
+
parseTime: this.parseTime,
|
|
1956
|
+
renderTime: this.renderTime,
|
|
1957
|
+
totalTime: this.parseTime + this.renderTime,
|
|
1958
|
+
tokenCount: this.lastTokenCount
|
|
1707
1959
|
};
|
|
1708
1960
|
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Get cache statistics
|
|
1963
|
+
*/
|
|
1964
|
+
getCacheStats() {
|
|
1965
|
+
return {
|
|
1966
|
+
parse: this.parseCache.getStats(),
|
|
1967
|
+
render: this.renderCache.getStats()
|
|
1968
|
+
};
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Clear all caches
|
|
1972
|
+
*/
|
|
1973
|
+
clearCaches() {
|
|
1974
|
+
this.parseCache.clear();
|
|
1975
|
+
this.renderCache.clear();
|
|
1976
|
+
}
|
|
1977
|
+
/**
|
|
1978
|
+
* Update cache capacity
|
|
1979
|
+
*/
|
|
1980
|
+
setCacheSize(size) {
|
|
1981
|
+
this.parseCache.setCapacity(size);
|
|
1982
|
+
this.renderCache.setCapacity(size);
|
|
1983
|
+
}
|
|
1709
1984
|
rebuildParserAndRenderer() {
|
|
1710
1985
|
const parserConfig = this.parser.getConfig();
|
|
1711
1986
|
const rendererConfig = this.renderer.getConfig();
|
|
1712
1987
|
this.parser = new MarkdownParser(parserConfig);
|
|
1713
1988
|
this.renderer = new MarkdownRenderer(rendererConfig);
|
|
1989
|
+
this.parseCache.clear();
|
|
1990
|
+
this.renderCache.clear();
|
|
1714
1991
|
const extensionsToRegister = Array.from(this.extensions.values());
|
|
1715
1992
|
const featureExtensions = extensionsToRegister.filter(
|
|
1716
1993
|
(ext) => ["alert", "button", "embed"].includes(ext.name)
|
|
@@ -2355,6 +2632,7 @@ export {
|
|
|
2355
2632
|
ImageExtension,
|
|
2356
2633
|
InlineCodeExtension,
|
|
2357
2634
|
ItalicExtension,
|
|
2635
|
+
LRUCache,
|
|
2358
2636
|
LineBreakExtension,
|
|
2359
2637
|
LinkExtension,
|
|
2360
2638
|
ListExtension,
|
|
@@ -2387,10 +2665,12 @@ export {
|
|
|
2387
2665
|
generateId,
|
|
2388
2666
|
getASTStats,
|
|
2389
2667
|
getTokenStats,
|
|
2668
|
+
hashContent,
|
|
2390
2669
|
isBrowser,
|
|
2391
2670
|
isNode,
|
|
2392
2671
|
isValidUrl,
|
|
2393
2672
|
markdown2 as markdown,
|
|
2673
|
+
memoize,
|
|
2394
2674
|
minimalClasses,
|
|
2395
2675
|
parseASTFromJSON,
|
|
2396
2676
|
parseCum,
|