@blcklab/yuirinx 0.1.1
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/LICENSE +21 -0
- package/README.md +249 -0
- package/dist/cjs/core/index.cjs +572 -0
- package/dist/cjs/index.cjs +600 -0
- package/dist/cjs/languages/bash.cjs +145 -0
- package/dist/cjs/languages/c.cjs +177 -0
- package/dist/cjs/languages/cpp.cjs +235 -0
- package/dist/cjs/languages/csharp.cjs +223 -0
- package/dist/cjs/languages/css.cjs +94 -0
- package/dist/cjs/languages/dart.cjs +204 -0
- package/dist/cjs/languages/dockerfile.cjs +80 -0
- package/dist/cjs/languages/go.cjs +185 -0
- package/dist/cjs/languages/graphql.cjs +91 -0
- package/dist/cjs/languages/html.cjs +74 -0
- package/dist/cjs/languages/ini.cjs +54 -0
- package/dist/cjs/languages/java.cjs +202 -0
- package/dist/cjs/languages/javascript.cjs +262 -0
- package/dist/cjs/languages/json.cjs +60 -0
- package/dist/cjs/languages/jsx.cjs +263 -0
- package/dist/cjs/languages/kotlin.cjs +211 -0
- package/dist/cjs/languages/markdown.cjs +70 -0
- package/dist/cjs/languages/nginx.cjs +57 -0
- package/dist/cjs/languages/php.cjs +194 -0
- package/dist/cjs/languages/plaintext.cjs +34 -0
- package/dist/cjs/languages/python.cjs +200 -0
- package/dist/cjs/languages/ruby.cjs +117 -0
- package/dist/cjs/languages/rust.cjs +179 -0
- package/dist/cjs/languages/scss.cjs +98 -0
- package/dist/cjs/languages/shell.cjs +145 -0
- package/dist/cjs/languages/sql.cjs +150 -0
- package/dist/cjs/languages/svelte.cjs +74 -0
- package/dist/cjs/languages/swift.cjs +216 -0
- package/dist/cjs/languages/toml.cjs +63 -0
- package/dist/cjs/languages/tsx.cjs +264 -0
- package/dist/cjs/languages/typescript.cjs +263 -0
- package/dist/cjs/languages/vue.cjs +74 -0
- package/dist/cjs/languages/xml.cjs +77 -0
- package/dist/cjs/languages/yaml.cjs +65 -0
- package/dist/cjs/themes/index.cjs +222 -0
- package/dist/cjs/themes/yuirinx-aurora.cjs +90 -0
- package/dist/cjs/themes/yuirinx-noir.cjs +90 -0
- package/dist/cjs/themes/yuirinx-pearl.cjs +90 -0
- package/dist/esm/core/index.js +542 -0
- package/dist/esm/index.js +565 -0
- package/dist/esm/languages/bash.js +118 -0
- package/dist/esm/languages/c.js +150 -0
- package/dist/esm/languages/cpp.js +208 -0
- package/dist/esm/languages/csharp.js +196 -0
- package/dist/esm/languages/css.js +67 -0
- package/dist/esm/languages/dart.js +177 -0
- package/dist/esm/languages/dockerfile.js +53 -0
- package/dist/esm/languages/go.js +158 -0
- package/dist/esm/languages/graphql.js +64 -0
- package/dist/esm/languages/html.js +47 -0
- package/dist/esm/languages/ini.js +27 -0
- package/dist/esm/languages/java.js +175 -0
- package/dist/esm/languages/javascript.js +235 -0
- package/dist/esm/languages/json.js +33 -0
- package/dist/esm/languages/jsx.js +236 -0
- package/dist/esm/languages/kotlin.js +184 -0
- package/dist/esm/languages/markdown.js +43 -0
- package/dist/esm/languages/nginx.js +30 -0
- package/dist/esm/languages/php.js +167 -0
- package/dist/esm/languages/plaintext.js +9 -0
- package/dist/esm/languages/python.js +173 -0
- package/dist/esm/languages/ruby.js +90 -0
- package/dist/esm/languages/rust.js +152 -0
- package/dist/esm/languages/scss.js +71 -0
- package/dist/esm/languages/shell.js +118 -0
- package/dist/esm/languages/sql.js +123 -0
- package/dist/esm/languages/svelte.js +47 -0
- package/dist/esm/languages/swift.js +189 -0
- package/dist/esm/languages/toml.js +36 -0
- package/dist/esm/languages/tsx.js +237 -0
- package/dist/esm/languages/typescript.js +236 -0
- package/dist/esm/languages/vue.js +47 -0
- package/dist/esm/languages/xml.js +50 -0
- package/dist/esm/languages/yaml.js +38 -0
- package/dist/esm/themes/index.js +193 -0
- package/dist/esm/themes/yuirinx-aurora.js +65 -0
- package/dist/esm/themes/yuirinx-noir.js +65 -0
- package/dist/esm/themes/yuirinx-pearl.js +65 -0
- package/dist/types/core/create-highlighter.d.cts +3 -0
- package/dist/types/core/create-highlighter.d.ts +3 -0
- package/dist/types/core/errors.d.cts +6 -0
- package/dist/types/core/errors.d.ts +6 -0
- package/dist/types/core/escape.d.cts +2 -0
- package/dist/types/core/escape.d.ts +2 -0
- package/dist/types/core/grammar.d.cts +14 -0
- package/dist/types/core/grammar.d.ts +14 -0
- package/dist/types/core/index.d.cts +6 -0
- package/dist/types/core/index.d.ts +6 -0
- package/dist/types/core/renderer.d.cts +6 -0
- package/dist/types/core/renderer.d.ts +6 -0
- package/dist/types/core/tokenizer.d.cts +3 -0
- package/dist/types/core/tokenizer.d.ts +3 -0
- package/dist/types/core/types.d.cts +134 -0
- package/dist/types/core/types.d.ts +134 -0
- package/dist/types/index.d.cts +2 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/languages/bash.d.cts +3 -0
- package/dist/types/languages/bash.d.ts +3 -0
- package/dist/types/languages/c.d.cts +2 -0
- package/dist/types/languages/c.d.ts +2 -0
- package/dist/types/languages/cpp.d.cts +2 -0
- package/dist/types/languages/cpp.d.ts +2 -0
- package/dist/types/languages/csharp.d.cts +2 -0
- package/dist/types/languages/csharp.d.ts +2 -0
- package/dist/types/languages/css.d.cts +2 -0
- package/dist/types/languages/css.d.ts +2 -0
- package/dist/types/languages/dart.d.cts +2 -0
- package/dist/types/languages/dart.d.ts +2 -0
- package/dist/types/languages/dockerfile.d.cts +3 -0
- package/dist/types/languages/dockerfile.d.ts +3 -0
- package/dist/types/languages/go.d.cts +2 -0
- package/dist/types/languages/go.d.ts +2 -0
- package/dist/types/languages/graphql.d.cts +3 -0
- package/dist/types/languages/graphql.d.ts +3 -0
- package/dist/types/languages/html.d.cts +2 -0
- package/dist/types/languages/html.d.ts +2 -0
- package/dist/types/languages/ini.d.cts +3 -0
- package/dist/types/languages/ini.d.ts +3 -0
- package/dist/types/languages/java.d.cts +2 -0
- package/dist/types/languages/java.d.ts +2 -0
- package/dist/types/languages/javascript.d.cts +2 -0
- package/dist/types/languages/javascript.d.ts +2 -0
- package/dist/types/languages/json.d.cts +3 -0
- package/dist/types/languages/json.d.ts +3 -0
- package/dist/types/languages/jsx.d.cts +2 -0
- package/dist/types/languages/jsx.d.ts +2 -0
- package/dist/types/languages/kotlin.d.cts +2 -0
- package/dist/types/languages/kotlin.d.ts +2 -0
- package/dist/types/languages/markdown.d.cts +3 -0
- package/dist/types/languages/markdown.d.ts +3 -0
- package/dist/types/languages/nginx.d.cts +3 -0
- package/dist/types/languages/nginx.d.ts +3 -0
- package/dist/types/languages/php.d.cts +3 -0
- package/dist/types/languages/php.d.ts +3 -0
- package/dist/types/languages/plaintext.d.cts +3 -0
- package/dist/types/languages/plaintext.d.ts +3 -0
- package/dist/types/languages/python.d.cts +3 -0
- package/dist/types/languages/python.d.ts +3 -0
- package/dist/types/languages/ruby.d.cts +3 -0
- package/dist/types/languages/ruby.d.ts +3 -0
- package/dist/types/languages/rust.d.cts +2 -0
- package/dist/types/languages/rust.d.ts +2 -0
- package/dist/types/languages/scss.d.cts +2 -0
- package/dist/types/languages/scss.d.ts +2 -0
- package/dist/types/languages/shell.d.cts +1 -0
- package/dist/types/languages/shell.d.ts +1 -0
- package/dist/types/languages/sql.d.cts +3 -0
- package/dist/types/languages/sql.d.ts +3 -0
- package/dist/types/languages/svelte.d.cts +2 -0
- package/dist/types/languages/svelte.d.ts +2 -0
- package/dist/types/languages/swift.d.cts +2 -0
- package/dist/types/languages/swift.d.ts +2 -0
- package/dist/types/languages/toml.d.cts +3 -0
- package/dist/types/languages/toml.d.ts +3 -0
- package/dist/types/languages/tsx.d.cts +2 -0
- package/dist/types/languages/tsx.d.ts +2 -0
- package/dist/types/languages/typescript.d.cts +2 -0
- package/dist/types/languages/typescript.d.ts +2 -0
- package/dist/types/languages/vue.d.cts +2 -0
- package/dist/types/languages/vue.d.ts +2 -0
- package/dist/types/languages/xml.d.cts +2 -0
- package/dist/types/languages/xml.d.ts +2 -0
- package/dist/types/languages/yaml.d.cts +3 -0
- package/dist/types/languages/yaml.d.ts +3 -0
- package/dist/types/singleton.d.cts +11 -0
- package/dist/types/singleton.d.ts +11 -0
- package/dist/types/themes/index.d.cts +3 -0
- package/dist/types/themes/index.d.ts +3 -0
- package/dist/types/themes/yuirinx-aurora.d.cts +3 -0
- package/dist/types/themes/yuirinx-aurora.d.ts +3 -0
- package/dist/types/themes/yuirinx-noir.d.cts +3 -0
- package/dist/types/themes/yuirinx-noir.d.ts +3 -0
- package/dist/types/themes/yuirinx-pearl.d.cts +3 -0
- package/dist/types/themes/yuirinx-pearl.d.ts +3 -0
- package/package.json +471 -0
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/index.ts
|
|
21
|
+
var index_exports = {};
|
|
22
|
+
__export(index_exports, {
|
|
23
|
+
YuirinxError: () => YuirinxError,
|
|
24
|
+
createHighlighter: () => createHighlighter,
|
|
25
|
+
escapeHtml: () => escapeHtml,
|
|
26
|
+
highlight: () => highlight,
|
|
27
|
+
registerLanguage: () => registerLanguage,
|
|
28
|
+
registerTheme: () => registerTheme,
|
|
29
|
+
render: () => render,
|
|
30
|
+
themeToCss: () => themeToCss,
|
|
31
|
+
tokenize: () => tokenize
|
|
32
|
+
});
|
|
33
|
+
module.exports = __toCommonJS(index_exports);
|
|
34
|
+
|
|
35
|
+
// src/core/errors.ts
|
|
36
|
+
var YuirinxError = class extends Error {
|
|
37
|
+
constructor(code, message) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.name = "YuirinxError";
|
|
40
|
+
this.code = code;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
// src/core/grammar.ts
|
|
45
|
+
function normalizeId(value) {
|
|
46
|
+
return value.trim().toLowerCase();
|
|
47
|
+
}
|
|
48
|
+
function isIncludeRule(rule) {
|
|
49
|
+
return "include" in rule;
|
|
50
|
+
}
|
|
51
|
+
function stickyPattern(pattern) {
|
|
52
|
+
const flags = [...pattern.flags].filter((flag) => flag !== "g" && flag !== "y").join("");
|
|
53
|
+
return new RegExp(pattern.source, `${flags}y`);
|
|
54
|
+
}
|
|
55
|
+
function canMatchEmpty(pattern) {
|
|
56
|
+
const candidates = ["", "x", " ", "\n", "0"];
|
|
57
|
+
for (const candidate of candidates) {
|
|
58
|
+
const probe = stickyPattern(pattern);
|
|
59
|
+
probe.lastIndex = 0;
|
|
60
|
+
const match = probe.exec(candidate);
|
|
61
|
+
if (match && match[0].length === 0) return true;
|
|
62
|
+
}
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
function compileGrammar(grammar) {
|
|
66
|
+
const id = normalizeId(grammar.id);
|
|
67
|
+
if (!id) {
|
|
68
|
+
throw new YuirinxError(
|
|
69
|
+
"YUIRINX_INVALID_GRAMMAR",
|
|
70
|
+
"A grammar must have a non-empty id."
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
const stateNames = Object.keys(grammar.states);
|
|
74
|
+
if (stateNames.length === 0) {
|
|
75
|
+
throw new YuirinxError(
|
|
76
|
+
"YUIRINX_INVALID_GRAMMAR",
|
|
77
|
+
`Grammar "${id}" must define at least one state.`
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
const initialState = grammar.initialState ?? "root";
|
|
81
|
+
if (!grammar.states[initialState]) {
|
|
82
|
+
throw new YuirinxError(
|
|
83
|
+
"YUIRINX_UNKNOWN_STATE",
|
|
84
|
+
`Grammar "${id}" uses missing initial state "${initialState}".`
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
const compiledStates = {};
|
|
88
|
+
const fallbackTypes = {};
|
|
89
|
+
const resolveRules = (stateName, ancestry) => {
|
|
90
|
+
const state = grammar.states[stateName];
|
|
91
|
+
if (!state) {
|
|
92
|
+
throw new YuirinxError(
|
|
93
|
+
"YUIRINX_UNKNOWN_STATE",
|
|
94
|
+
`Grammar "${id}" references missing state "${stateName}".`
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
if (ancestry.includes(stateName)) {
|
|
98
|
+
throw new YuirinxError(
|
|
99
|
+
"YUIRINX_INCLUDE_CYCLE",
|
|
100
|
+
`Grammar "${id}" has an include cycle: ${[...ancestry, stateName].join(" -> ")}.`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
const rules = [];
|
|
104
|
+
for (const [index, rule] of state.rules.entries()) {
|
|
105
|
+
if (isIncludeRule(rule)) {
|
|
106
|
+
rules.push(...resolveRules(rule.include, [...ancestry, stateName]));
|
|
107
|
+
continue;
|
|
108
|
+
}
|
|
109
|
+
if (canMatchEmpty(rule.pattern)) {
|
|
110
|
+
throw new YuirinxError(
|
|
111
|
+
"YUIRINX_EMPTY_PATTERN",
|
|
112
|
+
`Grammar "${id}", state "${stateName}", rule ${index} can match an empty string.`
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
for (const target of [rule.push, rule.next]) {
|
|
116
|
+
if (typeof target === "string" && !grammar.states[target]) {
|
|
117
|
+
throw new YuirinxError(
|
|
118
|
+
"YUIRINX_UNKNOWN_STATE",
|
|
119
|
+
`Grammar "${id}", state "${stateName}", rule ${index} references missing state "${target}".`
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
rules.push({
|
|
124
|
+
...rule,
|
|
125
|
+
pattern: stickyPattern(rule.pattern),
|
|
126
|
+
source: rule.pattern.source
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return rules;
|
|
130
|
+
};
|
|
131
|
+
for (const stateName of stateNames) {
|
|
132
|
+
compiledStates[stateName] = resolveRules(stateName, []);
|
|
133
|
+
fallbackTypes[stateName] = grammar.states[stateName]?.fallbackType ?? "plain";
|
|
134
|
+
}
|
|
135
|
+
return {
|
|
136
|
+
source: grammar,
|
|
137
|
+
id,
|
|
138
|
+
initialState,
|
|
139
|
+
states: compiledStates,
|
|
140
|
+
fallbackTypes
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// src/core/escape.ts
|
|
145
|
+
var HTML_ESCAPE_RE = /[&<>"']/g;
|
|
146
|
+
var HTML_ENTITIES = {
|
|
147
|
+
"&": "&",
|
|
148
|
+
"<": "<",
|
|
149
|
+
">": ">",
|
|
150
|
+
'"': """,
|
|
151
|
+
"'": "'"
|
|
152
|
+
};
|
|
153
|
+
function escapeHtml(value) {
|
|
154
|
+
return value.replace(
|
|
155
|
+
HTML_ESCAPE_RE,
|
|
156
|
+
(character) => HTML_ENTITIES[character] ?? character
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// src/core/renderer.ts
|
|
161
|
+
var SAFE_COLOR = /^(?:#[\da-f]{3,8}|(?:rgb|hsl)a?\([\d.%+\-,\s]+\)|[a-z]+)$/i;
|
|
162
|
+
function sanitizeColor(value) {
|
|
163
|
+
return value && SAFE_COLOR.test(value.trim()) ? value.trim() : void 0;
|
|
164
|
+
}
|
|
165
|
+
function sanitizeClassPart(value) {
|
|
166
|
+
return value.trim().replace(/[^a-zA-Z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
|
|
167
|
+
}
|
|
168
|
+
function sanitizeClassList(value) {
|
|
169
|
+
if (!value) return [];
|
|
170
|
+
return value.split(/\s+/).map(sanitizeClassPart).filter(Boolean);
|
|
171
|
+
}
|
|
172
|
+
function safeClassPrefix(prefix) {
|
|
173
|
+
return prefix.replace(/[^a-zA-Z0-9_-]+/g, "") || "tok-";
|
|
174
|
+
}
|
|
175
|
+
function classForToken(type, prefix) {
|
|
176
|
+
const safeType = sanitizeClassPart(type.replace(/\./g, "-")) || "plain";
|
|
177
|
+
return `${safeClassPrefix(prefix)}${safeType}`;
|
|
178
|
+
}
|
|
179
|
+
function classForFallbackLine(index, prefix) {
|
|
180
|
+
return `${safeClassPrefix(prefix)}fallback-line-${index}`;
|
|
181
|
+
}
|
|
182
|
+
function styleToDeclarations(style) {
|
|
183
|
+
if (!style) return "";
|
|
184
|
+
const declarations = [];
|
|
185
|
+
const color = sanitizeColor(style.color);
|
|
186
|
+
const background = sanitizeColor(style.backgroundColor);
|
|
187
|
+
if (color) declarations.push(`color:${color}`);
|
|
188
|
+
if (background) declarations.push(`background-color:${background}`);
|
|
189
|
+
if (style.fontStyle) declarations.push(`font-style:${style.fontStyle}`);
|
|
190
|
+
if (style.fontWeight) {
|
|
191
|
+
const weight = {
|
|
192
|
+
normal: "400",
|
|
193
|
+
medium: "500",
|
|
194
|
+
semibold: "600",
|
|
195
|
+
bold: "700"
|
|
196
|
+
}[style.fontWeight];
|
|
197
|
+
declarations.push(`font-weight:${weight}`);
|
|
198
|
+
}
|
|
199
|
+
if (style.textDecoration)
|
|
200
|
+
declarations.push(`text-decoration:${style.textDecoration}`);
|
|
201
|
+
return declarations.join(";");
|
|
202
|
+
}
|
|
203
|
+
function resolveFallbackPalette(theme) {
|
|
204
|
+
if (!theme) return [];
|
|
205
|
+
const explicit = (theme.fallbackPalette ?? []).map((color) => sanitizeColor(color)).filter((color) => Boolean(color));
|
|
206
|
+
if (explicit.length > 0) return [...new Set(explicit)];
|
|
207
|
+
const inferred = Object.values(theme.tokens).map((style) => sanitizeColor(style.color)).filter((color) => Boolean(color));
|
|
208
|
+
const colors = [...new Set(inferred)].slice(0, 8);
|
|
209
|
+
if (colors.length > 0) return colors;
|
|
210
|
+
const foreground = sanitizeColor(theme.foreground);
|
|
211
|
+
return foreground ? [foreground] : [];
|
|
212
|
+
}
|
|
213
|
+
function hashSource(value) {
|
|
214
|
+
let hash = 2166136261;
|
|
215
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
216
|
+
hash ^= value.charCodeAt(index);
|
|
217
|
+
hash = Math.imul(hash, 16777619);
|
|
218
|
+
}
|
|
219
|
+
return hash >>> 0;
|
|
220
|
+
}
|
|
221
|
+
function renderFallbackLine(value, lineIndex, seed, palette, mode, prefix) {
|
|
222
|
+
const paletteIndex = (seed + lineIndex) % palette.length;
|
|
223
|
+
const escaped = escapeHtml(value);
|
|
224
|
+
if (mode === "classes") {
|
|
225
|
+
const classes = [
|
|
226
|
+
"yuirinx-line",
|
|
227
|
+
classForFallbackLine(paletteIndex, prefix)
|
|
228
|
+
];
|
|
229
|
+
return `<span class="${escapeHtml(classes.join(" "))}">${escaped}</span>`;
|
|
230
|
+
}
|
|
231
|
+
const color = palette[paletteIndex] ?? "";
|
|
232
|
+
const declarations = styleToDeclarations({ color });
|
|
233
|
+
return `<span class="yuirinx-line" style="${escapeHtml(declarations)}">${escaped}</span>`;
|
|
234
|
+
}
|
|
235
|
+
function renderFallbackBody(code, options, theme) {
|
|
236
|
+
if (!code) return "";
|
|
237
|
+
const palette = resolveFallbackPalette(theme);
|
|
238
|
+
if (palette.length === 0) return escapeHtml(code);
|
|
239
|
+
const mode = options.mode ?? "inline";
|
|
240
|
+
const prefix = options.classPrefix ?? "tok-";
|
|
241
|
+
const seed = hashSource(code) % palette.length;
|
|
242
|
+
const newline = /\r\n|\r|\n/g;
|
|
243
|
+
let lineStart = 0;
|
|
244
|
+
let lineIndex = 0;
|
|
245
|
+
let body = "";
|
|
246
|
+
let match;
|
|
247
|
+
while ((match = newline.exec(code)) !== null) {
|
|
248
|
+
body += renderFallbackLine(
|
|
249
|
+
code.slice(lineStart, match.index),
|
|
250
|
+
lineIndex,
|
|
251
|
+
seed,
|
|
252
|
+
palette,
|
|
253
|
+
mode,
|
|
254
|
+
prefix
|
|
255
|
+
);
|
|
256
|
+
body += escapeHtml(match[0]);
|
|
257
|
+
lineStart = newline.lastIndex;
|
|
258
|
+
lineIndex += 1;
|
|
259
|
+
}
|
|
260
|
+
if (lineStart < code.length) {
|
|
261
|
+
body += renderFallbackLine(
|
|
262
|
+
code.slice(lineStart),
|
|
263
|
+
lineIndex,
|
|
264
|
+
seed,
|
|
265
|
+
palette,
|
|
266
|
+
mode,
|
|
267
|
+
prefix
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
return body;
|
|
271
|
+
}
|
|
272
|
+
function wrapBody(body, options, theme, fallbackLines) {
|
|
273
|
+
if (options.wrap === false) return body;
|
|
274
|
+
const mode = options.mode ?? "inline";
|
|
275
|
+
const normalizedThemeId = theme ? sanitizeClassPart(normalizeId(theme.id)) : "";
|
|
276
|
+
const themeId2 = normalizedThemeId.startsWith("yuirinx-") ? normalizedThemeId.slice("yuirinx-".length) : normalizedThemeId;
|
|
277
|
+
const preClasses = [
|
|
278
|
+
"yuirinx",
|
|
279
|
+
themeId2 ? `yuirinx-theme-${themeId2}` : "",
|
|
280
|
+
fallbackLines ? "yuirinx-fallback-lines" : "",
|
|
281
|
+
...sanitizeClassList(options.preClass)
|
|
282
|
+
].filter(Boolean);
|
|
283
|
+
const language = sanitizeClassPart(normalizeId(options.language ?? "plaintext")) || "plaintext";
|
|
284
|
+
const codeClasses = [
|
|
285
|
+
`language-${language}`,
|
|
286
|
+
...sanitizeClassList(options.codeClass)
|
|
287
|
+
];
|
|
288
|
+
const containerStyle = mode === "inline" && theme ? styleToDeclarations({
|
|
289
|
+
color: theme.foreground,
|
|
290
|
+
backgroundColor: theme.background
|
|
291
|
+
}) : "";
|
|
292
|
+
return `<pre class="${escapeHtml(preClasses.join(" "))}"${containerStyle ? ` style="${escapeHtml(containerStyle)}"` : ""}><code class="${escapeHtml(codeClasses.join(" "))}">${body}</code></pre>`;
|
|
293
|
+
}
|
|
294
|
+
function resolveTokenStyle(theme, type) {
|
|
295
|
+
if (!theme) return void 0;
|
|
296
|
+
let current = type;
|
|
297
|
+
while (current) {
|
|
298
|
+
const style = theme.tokens[current];
|
|
299
|
+
if (style) return style;
|
|
300
|
+
const separator = current.lastIndexOf(".");
|
|
301
|
+
if (separator < 0) break;
|
|
302
|
+
current = current.slice(0, separator);
|
|
303
|
+
}
|
|
304
|
+
return void 0;
|
|
305
|
+
}
|
|
306
|
+
function renderTokens(tokens, options, theme) {
|
|
307
|
+
const mode = options.mode ?? "inline";
|
|
308
|
+
const prefix = options.classPrefix ?? "tok-";
|
|
309
|
+
const body = tokens.map((token) => {
|
|
310
|
+
const escaped = escapeHtml(token.value);
|
|
311
|
+
if (token.type === "plain") return escaped;
|
|
312
|
+
if (mode === "classes") {
|
|
313
|
+
return `<span class="${escapeHtml(classForToken(token.type, prefix))}">${escaped}</span>`;
|
|
314
|
+
}
|
|
315
|
+
const declarations = styleToDeclarations(
|
|
316
|
+
resolveTokenStyle(theme, token.type)
|
|
317
|
+
);
|
|
318
|
+
return declarations ? `<span style="${escapeHtml(declarations)}">${escaped}</span>` : escaped;
|
|
319
|
+
}).join("");
|
|
320
|
+
return wrapBody(body, options, theme, false);
|
|
321
|
+
}
|
|
322
|
+
function renderColorLines(code, options, theme) {
|
|
323
|
+
const colored = code.length > 0 && resolveFallbackPalette(theme).length > 0;
|
|
324
|
+
const body = renderFallbackBody(code, options, theme);
|
|
325
|
+
return wrapBody(body, options, theme, colored);
|
|
326
|
+
}
|
|
327
|
+
function themeToCss(theme, options = {}) {
|
|
328
|
+
const prefix = options.classPrefix ?? "tok-";
|
|
329
|
+
const normalizedThemeId = sanitizeClassPart(normalizeId(theme.id));
|
|
330
|
+
const themeId2 = normalizedThemeId.startsWith("yuirinx-") ? normalizedThemeId.slice("yuirinx-".length) : normalizedThemeId;
|
|
331
|
+
const rawSelector = options.selector ?? `.yuirinx-theme-${themeId2 || "default"}`;
|
|
332
|
+
const selector = rawSelector.replace(/[{}<>]/g, "").trim() || ".yuirinx";
|
|
333
|
+
const lines = [];
|
|
334
|
+
if (options.includeContainer !== false) {
|
|
335
|
+
const foreground = sanitizeColor(theme.foreground);
|
|
336
|
+
const background = sanitizeColor(theme.background);
|
|
337
|
+
const declarations = [
|
|
338
|
+
foreground ? `color:${foreground}` : "",
|
|
339
|
+
background ? `background-color:${background}` : ""
|
|
340
|
+
].filter(Boolean);
|
|
341
|
+
lines.push(`${selector}{${declarations.join(";")}}`);
|
|
342
|
+
}
|
|
343
|
+
for (const [type, style] of Object.entries(theme.tokens)) {
|
|
344
|
+
const declarations = styleToDeclarations(style);
|
|
345
|
+
if (declarations)
|
|
346
|
+
lines.push(
|
|
347
|
+
`${selector} .${classForToken(type, prefix)}{${declarations}}`
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
for (const [index, color] of resolveFallbackPalette(theme).entries()) {
|
|
351
|
+
lines.push(
|
|
352
|
+
`${selector} .${classForFallbackLine(index, prefix)}{color:${color}}`
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
return lines.join("\n");
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// src/core/tokenizer.ts
|
|
359
|
+
function resolveValue(resolver, match, context) {
|
|
360
|
+
return typeof resolver === "function" ? resolver(match, context) : resolver;
|
|
361
|
+
}
|
|
362
|
+
function appendToken(tokens, type, value, start) {
|
|
363
|
+
if (!value) return;
|
|
364
|
+
const previous = tokens[tokens.length - 1];
|
|
365
|
+
if (previous && previous.type === type && previous.end === start) {
|
|
366
|
+
tokens[tokens.length - 1] = {
|
|
367
|
+
type,
|
|
368
|
+
value: previous.value + value,
|
|
369
|
+
start: previous.start,
|
|
370
|
+
end: start + value.length
|
|
371
|
+
};
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
tokens.push({ type, value, start, end: start + value.length });
|
|
375
|
+
}
|
|
376
|
+
function publicStack(grammarId, stack) {
|
|
377
|
+
return stack.map((name) => ({ name, language: grammarId }));
|
|
378
|
+
}
|
|
379
|
+
function applyTransition(rule, match, context, stack, maxStateDepth) {
|
|
380
|
+
const popCount = rule.pop === true ? 1 : typeof rule.pop === "number" ? rule.pop : 0;
|
|
381
|
+
if (popCount > 0) {
|
|
382
|
+
const removable = Math.min(popCount, Math.max(0, stack.length - 1));
|
|
383
|
+
stack.splice(stack.length - removable, removable);
|
|
384
|
+
}
|
|
385
|
+
const next = resolveValue(
|
|
386
|
+
rule.next,
|
|
387
|
+
match,
|
|
388
|
+
context
|
|
389
|
+
);
|
|
390
|
+
if (next) stack[stack.length - 1] = next;
|
|
391
|
+
const push = resolveValue(
|
|
392
|
+
rule.push,
|
|
393
|
+
match,
|
|
394
|
+
context
|
|
395
|
+
);
|
|
396
|
+
if (push) {
|
|
397
|
+
if (stack.length >= maxStateDepth) {
|
|
398
|
+
throw new YuirinxError(
|
|
399
|
+
"YUIRINX_STATE_DEPTH_EXCEEDED",
|
|
400
|
+
`Tokenizer state depth exceeded the configured limit of ${maxStateDepth}.`
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
stack.push(push);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
function tokenizeCompiled(code, grammar, maxStateDepth) {
|
|
407
|
+
if (!code) return [];
|
|
408
|
+
const tokens = [];
|
|
409
|
+
const stack = [grammar.initialState];
|
|
410
|
+
let offset = 0;
|
|
411
|
+
while (offset < code.length) {
|
|
412
|
+
const stateName = stack[stack.length - 1] ?? grammar.initialState;
|
|
413
|
+
const rules = grammar.states[stateName];
|
|
414
|
+
if (!rules) {
|
|
415
|
+
throw new YuirinxError(
|
|
416
|
+
"YUIRINX_UNKNOWN_STATE",
|
|
417
|
+
`Grammar "${grammar.id}" entered missing state "${stateName}".`
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
let matched = false;
|
|
421
|
+
for (const rule of rules) {
|
|
422
|
+
rule.pattern.lastIndex = offset;
|
|
423
|
+
const match = rule.pattern.exec(code);
|
|
424
|
+
if (!match) continue;
|
|
425
|
+
if (match[0].length === 0) {
|
|
426
|
+
throw new YuirinxError(
|
|
427
|
+
"YUIRINX_EMPTY_PATTERN",
|
|
428
|
+
`Grammar "${grammar.id}" produced an empty match in state "${stateName}" from /${rule.source}/.`
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
const context = {
|
|
432
|
+
language: grammar.id,
|
|
433
|
+
state: stateName,
|
|
434
|
+
stack: publicStack(grammar.id, stack),
|
|
435
|
+
offset
|
|
436
|
+
};
|
|
437
|
+
const tokenType = resolveValue(
|
|
438
|
+
rule.type,
|
|
439
|
+
match,
|
|
440
|
+
context
|
|
441
|
+
) ?? grammar.fallbackTypes[stateName] ?? "plain";
|
|
442
|
+
appendToken(tokens, tokenType, match[0], offset);
|
|
443
|
+
applyTransition(rule, match, context, stack, maxStateDepth);
|
|
444
|
+
offset += match[0].length;
|
|
445
|
+
matched = true;
|
|
446
|
+
break;
|
|
447
|
+
}
|
|
448
|
+
if (!matched) {
|
|
449
|
+
appendToken(
|
|
450
|
+
tokens,
|
|
451
|
+
grammar.fallbackTypes[stateName] ?? "plain",
|
|
452
|
+
code[offset] ?? "",
|
|
453
|
+
offset
|
|
454
|
+
);
|
|
455
|
+
offset += 1;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return tokens;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
// src/core/create-highlighter.ts
|
|
462
|
+
var plaintextGrammar = {
|
|
463
|
+
id: "plaintext",
|
|
464
|
+
aliases: ["text", "txt", "plain"],
|
|
465
|
+
states: {
|
|
466
|
+
root: {
|
|
467
|
+
rules: [{ type: "plain", pattern: /[\s\S]+/ }]
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
function themeId(theme) {
|
|
472
|
+
return normalizeId(theme.id);
|
|
473
|
+
}
|
|
474
|
+
function createHighlighter(options = {}) {
|
|
475
|
+
const grammars = /* @__PURE__ */ new Map();
|
|
476
|
+
const canonicalIds = /* @__PURE__ */ new Set();
|
|
477
|
+
const themes = /* @__PURE__ */ new Map();
|
|
478
|
+
const maxStateDepth = Math.max(2, options.maxStateDepth ?? 64);
|
|
479
|
+
const compiledPlaintext = compileGrammar(plaintextGrammar);
|
|
480
|
+
const plaintextIds = /* @__PURE__ */ new Set([
|
|
481
|
+
normalizeId(plaintextGrammar.id),
|
|
482
|
+
...(plaintextGrammar.aliases ?? []).map(normalizeId)
|
|
483
|
+
]);
|
|
484
|
+
const registerLanguage2 = (grammar) => {
|
|
485
|
+
const compiled = compileGrammar(grammar);
|
|
486
|
+
const id = compiled.id;
|
|
487
|
+
if (grammars.has(id)) {
|
|
488
|
+
throw new YuirinxError(
|
|
489
|
+
"YUIRINX_DUPLICATE_LANGUAGE",
|
|
490
|
+
`Language "${id}" is already registered.`
|
|
491
|
+
);
|
|
492
|
+
}
|
|
493
|
+
const aliases = (grammar.aliases ?? []).map(normalizeId).filter(Boolean);
|
|
494
|
+
for (const alias of aliases) {
|
|
495
|
+
if (alias === id || grammars.has(alias)) {
|
|
496
|
+
throw new YuirinxError(
|
|
497
|
+
"YUIRINX_DUPLICATE_ALIAS",
|
|
498
|
+
`Alias "${alias}" for language "${id}" is already registered.`
|
|
499
|
+
);
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
grammars.set(id, compiled);
|
|
503
|
+
canonicalIds.add(id);
|
|
504
|
+
for (const alias of aliases) grammars.set(alias, compiled);
|
|
505
|
+
};
|
|
506
|
+
const registerTheme2 = (theme) => {
|
|
507
|
+
const id = themeId(theme);
|
|
508
|
+
if (!id)
|
|
509
|
+
throw new YuirinxError(
|
|
510
|
+
"YUIRINX_DUPLICATE_THEME",
|
|
511
|
+
"A theme must have a non-empty id."
|
|
512
|
+
);
|
|
513
|
+
if (themes.has(id)) {
|
|
514
|
+
throw new YuirinxError(
|
|
515
|
+
"YUIRINX_DUPLICATE_THEME",
|
|
516
|
+
`Theme "${id}" is already registered.`
|
|
517
|
+
);
|
|
518
|
+
}
|
|
519
|
+
themes.set(id, theme);
|
|
520
|
+
};
|
|
521
|
+
for (const grammar of options.languages ?? []) registerLanguage2(grammar);
|
|
522
|
+
for (const theme of options.themes ?? []) registerTheme2(theme);
|
|
523
|
+
const resolveGrammar = (language) => {
|
|
524
|
+
if (typeof language !== "string") {
|
|
525
|
+
return { grammar: compileGrammar(language), unavailable: false };
|
|
526
|
+
}
|
|
527
|
+
const id = normalizeId(language);
|
|
528
|
+
const registered = grammars.get(id);
|
|
529
|
+
if (registered) return { grammar: registered, unavailable: false };
|
|
530
|
+
return {
|
|
531
|
+
grammar: compiledPlaintext,
|
|
532
|
+
unavailable: !plaintextIds.has(id)
|
|
533
|
+
};
|
|
534
|
+
};
|
|
535
|
+
const resolveTheme = (theme) => {
|
|
536
|
+
if (!theme)
|
|
537
|
+
return options.defaultTheme ? themes.get(normalizeId(options.defaultTheme)) : void 0;
|
|
538
|
+
return typeof theme === "string" ? themes.get(normalizeId(theme)) : theme;
|
|
539
|
+
};
|
|
540
|
+
const tokenize2 = (code, language) => tokenizeCompiled(code, resolveGrammar(language).grammar, maxStateDepth);
|
|
541
|
+
const render2 = (tokens, renderOptions = {}) => renderTokens(tokens, renderOptions, resolveTheme(renderOptions.theme));
|
|
542
|
+
const highlight2 = (code, highlightOptions) => {
|
|
543
|
+
const language = highlightOptions.lang ?? options.defaultLanguage ?? "plaintext";
|
|
544
|
+
const resolved = resolveGrammar(language);
|
|
545
|
+
const theme = resolveTheme(highlightOptions.theme);
|
|
546
|
+
const renderOptions = {
|
|
547
|
+
...highlightOptions,
|
|
548
|
+
language: typeof language === "string" ? language : language.id
|
|
549
|
+
};
|
|
550
|
+
const fallback = highlightOptions.fallback ?? options.defaultFallback ?? "plaintext";
|
|
551
|
+
if (resolved.unavailable && fallback === "color-lines") {
|
|
552
|
+
return renderColorLines(code, renderOptions, theme);
|
|
553
|
+
}
|
|
554
|
+
const tokens = tokenizeCompiled(code, resolved.grammar, maxStateDepth);
|
|
555
|
+
return renderTokens(tokens, renderOptions, theme);
|
|
556
|
+
};
|
|
557
|
+
return {
|
|
558
|
+
highlight: highlight2,
|
|
559
|
+
tokenize: tokenize2,
|
|
560
|
+
render: render2,
|
|
561
|
+
registerLanguage: registerLanguage2,
|
|
562
|
+
registerTheme: registerTheme2,
|
|
563
|
+
hasLanguage: (language) => grammars.has(normalizeId(language)),
|
|
564
|
+
hasTheme: (theme) => themes.has(normalizeId(theme)),
|
|
565
|
+
getLanguage: (language) => grammars.get(normalizeId(language))?.source,
|
|
566
|
+
getTheme: (theme) => themes.get(normalizeId(theme)),
|
|
567
|
+
listLanguages: () => Object.freeze([...canonicalIds]),
|
|
568
|
+
listThemes: () => Object.freeze([...themes.keys()])
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
// src/singleton.ts
|
|
573
|
+
var defaultHighlighter = createHighlighter();
|
|
574
|
+
function highlight(code, options) {
|
|
575
|
+
return defaultHighlighter.highlight(code, options);
|
|
576
|
+
}
|
|
577
|
+
function tokenize(code, language) {
|
|
578
|
+
return defaultHighlighter.tokenize(code, language);
|
|
579
|
+
}
|
|
580
|
+
function render(tokens, options) {
|
|
581
|
+
return defaultHighlighter.render(tokens, options);
|
|
582
|
+
}
|
|
583
|
+
function registerLanguage(grammar) {
|
|
584
|
+
defaultHighlighter.registerLanguage(grammar);
|
|
585
|
+
}
|
|
586
|
+
function registerTheme(theme) {
|
|
587
|
+
defaultHighlighter.registerTheme(theme);
|
|
588
|
+
}
|
|
589
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
590
|
+
0 && (module.exports = {
|
|
591
|
+
YuirinxError,
|
|
592
|
+
createHighlighter,
|
|
593
|
+
escapeHtml,
|
|
594
|
+
highlight,
|
|
595
|
+
registerLanguage,
|
|
596
|
+
registerTheme,
|
|
597
|
+
render,
|
|
598
|
+
themeToCss,
|
|
599
|
+
tokenize
|
|
600
|
+
});
|