@blcklab/yuirinx 0.1.1 → 0.1.2

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 (85) hide show
  1. package/README.md +47 -64
  2. package/dist/cjs/core/index.cjs +36 -7
  3. package/dist/cjs/index.cjs +37 -8
  4. package/dist/cjs/languages/c.cjs +1 -1
  5. package/dist/cjs/languages/cpp.cjs +1 -1
  6. package/dist/cjs/languages/csharp.cjs +1 -1
  7. package/dist/cjs/languages/css.cjs +2 -2
  8. package/dist/cjs/languages/dart.cjs +1 -1
  9. package/dist/cjs/languages/go.cjs +1 -1
  10. package/dist/cjs/languages/java.cjs +1 -1
  11. package/dist/cjs/languages/javascript.cjs +65 -6
  12. package/dist/cjs/languages/jsx.cjs +65 -6
  13. package/dist/cjs/languages/kotlin.cjs +1 -1
  14. package/dist/cjs/languages/markdown.cjs +30 -6
  15. package/dist/cjs/languages/php.cjs +1 -1
  16. package/dist/cjs/languages/python.cjs +1 -1
  17. package/dist/cjs/languages/ruby.cjs +1 -1
  18. package/dist/cjs/languages/rust.cjs +1 -1
  19. package/dist/cjs/languages/scss.cjs +2 -2
  20. package/dist/cjs/languages/sql.cjs +1 -1
  21. package/dist/cjs/languages/swift.cjs +1 -1
  22. package/dist/cjs/languages/tsx.cjs +65 -6
  23. package/dist/cjs/languages/typescript.cjs +65 -6
  24. package/dist/esm/core/create-highlighter.js +132 -0
  25. package/dist/esm/core/errors.js +10 -0
  26. package/dist/esm/core/escape.js +17 -0
  27. package/dist/esm/core/grammar.js +103 -0
  28. package/dist/esm/core/index.js +4 -536
  29. package/dist/esm/core/renderer.js +215 -0
  30. package/dist/esm/core/tokenizer.js +107 -0
  31. package/dist/esm/core/types.js +0 -0
  32. package/dist/esm/index.js +8 -558
  33. package/dist/esm/languages/bash.js +9 -21
  34. package/dist/esm/languages/c.js +2 -82
  35. package/dist/esm/languages/cpp.js +2 -82
  36. package/dist/esm/languages/csharp.js +2 -82
  37. package/dist/esm/languages/css.js +2 -64
  38. package/dist/esm/languages/dart.js +2 -82
  39. package/dist/esm/languages/dockerfile.js +3 -12
  40. package/dist/esm/languages/go.js +2 -82
  41. package/dist/esm/languages/graphql.js +8 -20
  42. package/dist/esm/languages/html.js +2 -44
  43. package/dist/esm/languages/ini.js +2 -5
  44. package/dist/esm/languages/java.js +2 -82
  45. package/dist/esm/languages/javascript.js +2 -229
  46. package/dist/esm/languages/json.js +2 -5
  47. package/dist/esm/languages/jsx.js +2 -229
  48. package/dist/esm/languages/kotlin.js +2 -82
  49. package/dist/esm/languages/markdown.js +32 -11
  50. package/dist/esm/languages/nginx.js +2 -5
  51. package/dist/esm/languages/php.js +3 -83
  52. package/dist/esm/languages/plaintext.js +1 -2
  53. package/dist/esm/languages/python.js +10 -25
  54. package/dist/esm/languages/ruby.js +9 -24
  55. package/dist/esm/languages/rust.js +2 -82
  56. package/dist/esm/languages/scss.js +2 -64
  57. package/dist/esm/languages/shared/c-family.js +55 -0
  58. package/dist/esm/languages/shared/css.js +48 -0
  59. package/dist/esm/languages/shared/ecmascript.js +261 -0
  60. package/dist/esm/languages/shared/helpers.js +52 -0
  61. package/dist/esm/languages/shared/markup.js +41 -0
  62. package/dist/esm/languages/shell.js +1 -115
  63. package/dist/esm/languages/sql.js +11 -32
  64. package/dist/esm/languages/svelte.js +2 -44
  65. package/dist/esm/languages/swift.js +2 -82
  66. package/dist/esm/languages/toml.js +2 -5
  67. package/dist/esm/languages/tsx.js +2 -229
  68. package/dist/esm/languages/typescript.js +2 -229
  69. package/dist/esm/languages/vue.js +2 -44
  70. package/dist/esm/languages/xml.js +2 -44
  71. package/dist/esm/languages/yaml.js +2 -5
  72. package/dist/esm/singleton.js +24 -0
  73. package/dist/esm/themes/index.js +3 -188
  74. package/dist/esm/themes/yuirinx-aurora.js +1 -2
  75. package/dist/esm/themes/yuirinx-noir.js +1 -2
  76. package/dist/esm/themes/yuirinx-pearl.js +1 -2
  77. package/dist/types/core/errors.d.cts +1 -1
  78. package/dist/types/core/errors.d.ts +1 -1
  79. package/dist/types/core/index.d.cts +1 -1
  80. package/dist/types/core/index.d.ts +1 -1
  81. package/dist/types/core/types.d.cts +5 -1
  82. package/dist/types/core/types.d.ts +5 -1
  83. package/dist/types/singleton.d.cts +1 -1
  84. package/dist/types/singleton.d.ts +1 -1
  85. package/package.json +4 -4
@@ -38,7 +38,7 @@ var punctuationRule = {
38
38
  };
39
39
  var operatorRule = {
40
40
  type: "operator",
41
- pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])+/
41
+ pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])/
42
42
  };
43
43
  var numberRule = {
44
44
  type: "number",
@@ -163,6 +163,18 @@ var BUILTINS = [
163
163
  "process",
164
164
  "window"
165
165
  ];
166
+ function canStartRegex(source, offset) {
167
+ const before = source.slice(0, offset).trimEnd();
168
+ if (!before) return true;
169
+ const previous = before[before.length - 1] ?? "";
170
+ if (/[([{,:;=!?&|+\-*%^~<>]/.test(previous)) return true;
171
+ const word = /[A-Za-z_$][\w$]*$/.exec(before)?.[0];
172
+ return Boolean(
173
+ word && /^(?:await|case|delete|do|else|in|instanceof|new|of|return|throw|typeof|void|yield)$/.test(
174
+ word
175
+ )
176
+ );
177
+ }
166
178
  function createEcmaGrammar(options) {
167
179
  const keywordValues = options.typescript ? [...JS_KEYWORDS, ...TS_KEYWORDS] : JS_KEYWORDS;
168
180
  const rootRules = [
@@ -173,16 +185,23 @@ function createEcmaGrammar(options) {
173
185
  { type: "string", pattern: /`/, push: "template" }
174
186
  ];
175
187
  if (options.jsx) {
188
+ if (options.typescript) {
189
+ rootRules.push({
190
+ type: "operator",
191
+ pattern: /<(?=[A-Za-z_$][\w$]*(?:\s+extends\s+[^>]+)?,?>\s*\()/
192
+ });
193
+ }
176
194
  rootRules.push({
177
195
  type: "tag.punctuation",
178
- pattern: /<\/?/,
179
- push: "jsxTag"
196
+ pattern: /<(?=[A-Za-z_$>])/,
197
+ push: "jsxTagName"
180
198
  });
181
199
  }
182
200
  rootRules.push(
183
201
  {
184
202
  type: "regex",
185
- pattern: /\/(?![/*\s])(?:\\.|\[(?:\\.|[^\]\\])*\]|[^/\\\r\n])+\/[dgimsuvy]*/
203
+ pattern: /\/(?![/*\s])(?:\\.|\[(?:\\.|[^\]\\])*\]|[^/\\\r\n])+\/[dgimsuvy]*/,
204
+ when: (_match, context) => canStartRegex(context.source, context.offset)
186
205
  },
187
206
  numberRule,
188
207
  { type: "boolean", pattern: /\b(?:true|false)\b/ },
@@ -228,14 +247,54 @@ function createEcmaGrammar(options) {
228
247
  { include: "root" }
229
248
  ]
230
249
  },
231
- jsxTag: {
250
+ jsxTagName: {
251
+ rules: [
252
+ { type: "tag.punctuation", pattern: />/, next: "jsxChildren" },
253
+ { type: "tag.punctuation", pattern: /\/>/, pop: true },
254
+ whitespaceRule,
255
+ {
256
+ type: "tag",
257
+ pattern: /[A-Za-z_$][\w$.-]*/,
258
+ next: "jsxAttributes"
259
+ },
260
+ punctuationRule
261
+ ]
262
+ },
263
+ jsxAttributes: {
232
264
  rules: [
233
- { type: "tag.punctuation", pattern: /\/?>/, pop: true },
265
+ { type: "tag.punctuation", pattern: /\/>/, pop: true },
266
+ { type: "tag.punctuation", pattern: />/, next: "jsxChildren" },
234
267
  whitespaceRule,
235
268
  { type: "attribute.value", pattern: /"(?:\\.|[^"\\])*"?/ },
236
269
  { type: "attribute.value", pattern: /'(?:\\.|[^'\\])*'?/ },
237
270
  { type: "punctuation", pattern: /{/, push: "jsxExpression" },
238
271
  { type: "operator", pattern: /=/ },
272
+ { type: "attribute", pattern: /[A-Za-z_$][\w$.-]*/ },
273
+ punctuationRule
274
+ ]
275
+ },
276
+ jsxChildren: {
277
+ fallbackType: "plain",
278
+ rules: [
279
+ {
280
+ type: "tag.punctuation",
281
+ pattern: /<\/(?=[A-Za-z_$>])/,
282
+ push: "jsxClosingTag"
283
+ },
284
+ {
285
+ type: "tag.punctuation",
286
+ pattern: /<(?=[A-Za-z_$>])/,
287
+ push: "jsxTagName"
288
+ },
289
+ { type: "punctuation", pattern: /{/, push: "jsxExpression" },
290
+ { type: "plain", pattern: /[^<{]+/ },
291
+ { type: "plain", pattern: /[<{]/ }
292
+ ]
293
+ },
294
+ jsxClosingTag: {
295
+ rules: [
296
+ { type: "tag.punctuation", pattern: />/, pop: 2 },
297
+ whitespaceRule,
239
298
  { type: "tag", pattern: /[A-Za-z_$][\w$.-]*/ },
240
299
  punctuationRule
241
300
  ]
@@ -38,7 +38,7 @@ var punctuationRule = {
38
38
  };
39
39
  var operatorRule = {
40
40
  type: "operator",
41
- pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])+/
41
+ pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])/
42
42
  };
43
43
  var numberRule = {
44
44
  type: "number",
@@ -28,20 +28,39 @@ module.exports = __toCommonJS(markdown_exports);
28
28
  var whitespaceRule = { type: "plain", pattern: /\s+/ };
29
29
 
30
30
  // src/languages/markdown.ts
31
+ var atLineStart = (_match, context) => context.offset === 0 || /[\r\n]/.test(context.source[context.offset - 1] ?? "");
31
32
  var markdown = {
32
33
  id: "markdown",
33
34
  aliases: ["md", "mdown"],
34
35
  states: {
35
36
  root: {
36
37
  rules: [
37
- { type: "meta", pattern: /```[^\r\n]*(?:\r?\n|$)/, push: "fence" },
38
- { type: "heading", pattern: /#{1,6}[ \t]+[^\r\n]*/ },
38
+ {
39
+ type: "meta",
40
+ pattern: / {0,3}```[^\r\n]*(?:\r?\n|$)/,
41
+ when: atLineStart,
42
+ push: "fence"
43
+ },
44
+ {
45
+ type: "heading",
46
+ pattern: / {0,3}#{1,6}[ \t]+[^\r\n]*/,
47
+ when: atLineStart
48
+ },
39
49
  {
40
50
  type: "heading",
41
- pattern: /[^\r\n]+\r?\n(?:===+|---+)[ \t]*(?:\r?\n|$)/
51
+ pattern: /[^\r\n]+\r?\n {0,3}(?:===+|---+)[ \t]*(?:\r?\n|$)/,
52
+ when: atLineStart
53
+ },
54
+ {
55
+ type: "quote",
56
+ pattern: / {0,3}>[ \t]?[^\r\n]*/,
57
+ when: atLineStart
58
+ },
59
+ {
60
+ type: "list",
61
+ pattern: / {0,3}(?:[-+*]|\d+[.)])[ \t]+/,
62
+ when: atLineStart
42
63
  },
43
- { type: "quote", pattern: />[ \t]?[^\r\n]*/ },
44
- { type: "list", pattern: /(?:[-+*]|\d+[.)])[ \t]+/ },
45
64
  { type: "important", pattern: /!\[[^\]\r\n]*\]\([^\r\n)]*\)/ },
46
65
  { type: "link", pattern: /\[[^\]\r\n]+\]\([^\r\n)]*\)/ },
47
66
  { type: "url", pattern: /https?:\/\/[^\s<>()]+/ },
@@ -57,7 +76,12 @@ var markdown = {
57
76
  fence: {
58
77
  fallbackType: "code",
59
78
  rules: [
60
- { type: "meta", pattern: /```[ \t]*(?:\r?\n|$)/, pop: true },
79
+ {
80
+ type: "meta",
81
+ pattern: / {0,3}```[ \t]*(?:\r?\n|$)/,
82
+ when: atLineStart,
83
+ pop: true
84
+ },
61
85
  { type: "code", pattern: /[^`]+/ },
62
86
  { type: "code", pattern: /`/ }
63
87
  ]
@@ -38,7 +38,7 @@ var punctuationRule = {
38
38
  };
39
39
  var operatorRule = {
40
40
  type: "operator",
41
- pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])+/
41
+ pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])/
42
42
  };
43
43
  var numberRule = {
44
44
  type: "number",
@@ -38,7 +38,7 @@ var punctuationRule = {
38
38
  };
39
39
  var operatorRule = {
40
40
  type: "operator",
41
- pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])+/
41
+ pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])/
42
42
  };
43
43
  var numberRule = {
44
44
  type: "number",
@@ -38,7 +38,7 @@ var punctuationRule = {
38
38
  };
39
39
  var operatorRule = {
40
40
  type: "operator",
41
- pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])+/
41
+ pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])/
42
42
  };
43
43
  var numberRule = {
44
44
  type: "number",
@@ -38,7 +38,7 @@ var punctuationRule = {
38
38
  };
39
39
  var operatorRule = {
40
40
  type: "operator",
41
- pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])+/
41
+ pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])/
42
42
  };
43
43
  var numberRule = {
44
44
  type: "number",
@@ -32,7 +32,7 @@ var punctuationRule = {
32
32
  };
33
33
  var operatorRule = {
34
34
  type: "operator",
35
- pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])+/
35
+ pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])/
36
36
  };
37
37
  var doubleStringRule = {
38
38
  type: "string",
@@ -61,7 +61,7 @@ function createCssGrammar(options) {
61
61
  { type: "number", pattern: /(?:\b\d*\.\d+|\b\d+)(?:e[+-]?\d+)?/i },
62
62
  {
63
63
  type: "unit",
64
- pattern: /(?:%|px|r?em|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc|s|ms|deg|rad|turn)\b/i
64
+ pattern: /(?:%(?![\w-])|(?:px|r?em|vh|vw|vmin|vmax|ch|ex|cm|mm|in|pt|pc|s|ms|deg|rad|turn)\b)/i
65
65
  },
66
66
  {
67
67
  type: "selector",
@@ -38,7 +38,7 @@ var punctuationRule = {
38
38
  };
39
39
  var operatorRule = {
40
40
  type: "operator",
41
- pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])+/
41
+ pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])/
42
42
  };
43
43
  var numberRule = {
44
44
  type: "number",
@@ -38,7 +38,7 @@ var punctuationRule = {
38
38
  };
39
39
  var operatorRule = {
40
40
  type: "operator",
41
- pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])+/
41
+ pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])/
42
42
  };
43
43
  var numberRule = {
44
44
  type: "number",
@@ -38,7 +38,7 @@ var punctuationRule = {
38
38
  };
39
39
  var operatorRule = {
40
40
  type: "operator",
41
- pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])+/
41
+ pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])/
42
42
  };
43
43
  var numberRule = {
44
44
  type: "number",
@@ -163,6 +163,18 @@ var BUILTINS = [
163
163
  "process",
164
164
  "window"
165
165
  ];
166
+ function canStartRegex(source, offset) {
167
+ const before = source.slice(0, offset).trimEnd();
168
+ if (!before) return true;
169
+ const previous = before[before.length - 1] ?? "";
170
+ if (/[([{,:;=!?&|+\-*%^~<>]/.test(previous)) return true;
171
+ const word = /[A-Za-z_$][\w$]*$/.exec(before)?.[0];
172
+ return Boolean(
173
+ word && /^(?:await|case|delete|do|else|in|instanceof|new|of|return|throw|typeof|void|yield)$/.test(
174
+ word
175
+ )
176
+ );
177
+ }
166
178
  function createEcmaGrammar(options) {
167
179
  const keywordValues = options.typescript ? [...JS_KEYWORDS, ...TS_KEYWORDS] : JS_KEYWORDS;
168
180
  const rootRules = [
@@ -173,16 +185,23 @@ function createEcmaGrammar(options) {
173
185
  { type: "string", pattern: /`/, push: "template" }
174
186
  ];
175
187
  if (options.jsx) {
188
+ if (options.typescript) {
189
+ rootRules.push({
190
+ type: "operator",
191
+ pattern: /<(?=[A-Za-z_$][\w$]*(?:\s+extends\s+[^>]+)?,?>\s*\()/
192
+ });
193
+ }
176
194
  rootRules.push({
177
195
  type: "tag.punctuation",
178
- pattern: /<\/?/,
179
- push: "jsxTag"
196
+ pattern: /<(?=[A-Za-z_$>])/,
197
+ push: "jsxTagName"
180
198
  });
181
199
  }
182
200
  rootRules.push(
183
201
  {
184
202
  type: "regex",
185
- pattern: /\/(?![/*\s])(?:\\.|\[(?:\\.|[^\]\\])*\]|[^/\\\r\n])+\/[dgimsuvy]*/
203
+ pattern: /\/(?![/*\s])(?:\\.|\[(?:\\.|[^\]\\])*\]|[^/\\\r\n])+\/[dgimsuvy]*/,
204
+ when: (_match, context) => canStartRegex(context.source, context.offset)
186
205
  },
187
206
  numberRule,
188
207
  { type: "boolean", pattern: /\b(?:true|false)\b/ },
@@ -228,14 +247,54 @@ function createEcmaGrammar(options) {
228
247
  { include: "root" }
229
248
  ]
230
249
  },
231
- jsxTag: {
250
+ jsxTagName: {
251
+ rules: [
252
+ { type: "tag.punctuation", pattern: />/, next: "jsxChildren" },
253
+ { type: "tag.punctuation", pattern: /\/>/, pop: true },
254
+ whitespaceRule,
255
+ {
256
+ type: "tag",
257
+ pattern: /[A-Za-z_$][\w$.-]*/,
258
+ next: "jsxAttributes"
259
+ },
260
+ punctuationRule
261
+ ]
262
+ },
263
+ jsxAttributes: {
232
264
  rules: [
233
- { type: "tag.punctuation", pattern: /\/?>/, pop: true },
265
+ { type: "tag.punctuation", pattern: /\/>/, pop: true },
266
+ { type: "tag.punctuation", pattern: />/, next: "jsxChildren" },
234
267
  whitespaceRule,
235
268
  { type: "attribute.value", pattern: /"(?:\\.|[^"\\])*"?/ },
236
269
  { type: "attribute.value", pattern: /'(?:\\.|[^'\\])*'?/ },
237
270
  { type: "punctuation", pattern: /{/, push: "jsxExpression" },
238
271
  { type: "operator", pattern: /=/ },
272
+ { type: "attribute", pattern: /[A-Za-z_$][\w$.-]*/ },
273
+ punctuationRule
274
+ ]
275
+ },
276
+ jsxChildren: {
277
+ fallbackType: "plain",
278
+ rules: [
279
+ {
280
+ type: "tag.punctuation",
281
+ pattern: /<\/(?=[A-Za-z_$>])/,
282
+ push: "jsxClosingTag"
283
+ },
284
+ {
285
+ type: "tag.punctuation",
286
+ pattern: /<(?=[A-Za-z_$>])/,
287
+ push: "jsxTagName"
288
+ },
289
+ { type: "punctuation", pattern: /{/, push: "jsxExpression" },
290
+ { type: "plain", pattern: /[^<{]+/ },
291
+ { type: "plain", pattern: /[<{]/ }
292
+ ]
293
+ },
294
+ jsxClosingTag: {
295
+ rules: [
296
+ { type: "tag.punctuation", pattern: />/, pop: 2 },
297
+ whitespaceRule,
239
298
  { type: "tag", pattern: /[A-Za-z_$][\w$.-]*/ },
240
299
  punctuationRule
241
300
  ]
@@ -38,7 +38,7 @@ var punctuationRule = {
38
38
  };
39
39
  var operatorRule = {
40
40
  type: "operator",
41
- pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])+/
41
+ pattern: /(?:===|!==|>>>|<<=|>>=|=>|==|!=|<=|>=|&&|\|\||\?\?|\?\.|\+\+|--|\+=|-=|\*=|\/=|%=|\*\*|<<|>>|[+\-*/%=&|!<>^~?])/
42
42
  };
43
43
  var numberRule = {
44
44
  type: "number",
@@ -163,6 +163,18 @@ var BUILTINS = [
163
163
  "process",
164
164
  "window"
165
165
  ];
166
+ function canStartRegex(source, offset) {
167
+ const before = source.slice(0, offset).trimEnd();
168
+ if (!before) return true;
169
+ const previous = before[before.length - 1] ?? "";
170
+ if (/[([{,:;=!?&|+\-*%^~<>]/.test(previous)) return true;
171
+ const word = /[A-Za-z_$][\w$]*$/.exec(before)?.[0];
172
+ return Boolean(
173
+ word && /^(?:await|case|delete|do|else|in|instanceof|new|of|return|throw|typeof|void|yield)$/.test(
174
+ word
175
+ )
176
+ );
177
+ }
166
178
  function createEcmaGrammar(options) {
167
179
  const keywordValues = options.typescript ? [...JS_KEYWORDS, ...TS_KEYWORDS] : JS_KEYWORDS;
168
180
  const rootRules = [
@@ -173,16 +185,23 @@ function createEcmaGrammar(options) {
173
185
  { type: "string", pattern: /`/, push: "template" }
174
186
  ];
175
187
  if (options.jsx) {
188
+ if (options.typescript) {
189
+ rootRules.push({
190
+ type: "operator",
191
+ pattern: /<(?=[A-Za-z_$][\w$]*(?:\s+extends\s+[^>]+)?,?>\s*\()/
192
+ });
193
+ }
176
194
  rootRules.push({
177
195
  type: "tag.punctuation",
178
- pattern: /<\/?/,
179
- push: "jsxTag"
196
+ pattern: /<(?=[A-Za-z_$>])/,
197
+ push: "jsxTagName"
180
198
  });
181
199
  }
182
200
  rootRules.push(
183
201
  {
184
202
  type: "regex",
185
- pattern: /\/(?![/*\s])(?:\\.|\[(?:\\.|[^\]\\])*\]|[^/\\\r\n])+\/[dgimsuvy]*/
203
+ pattern: /\/(?![/*\s])(?:\\.|\[(?:\\.|[^\]\\])*\]|[^/\\\r\n])+\/[dgimsuvy]*/,
204
+ when: (_match, context) => canStartRegex(context.source, context.offset)
186
205
  },
187
206
  numberRule,
188
207
  { type: "boolean", pattern: /\b(?:true|false)\b/ },
@@ -228,14 +247,54 @@ function createEcmaGrammar(options) {
228
247
  { include: "root" }
229
248
  ]
230
249
  },
231
- jsxTag: {
250
+ jsxTagName: {
251
+ rules: [
252
+ { type: "tag.punctuation", pattern: />/, next: "jsxChildren" },
253
+ { type: "tag.punctuation", pattern: /\/>/, pop: true },
254
+ whitespaceRule,
255
+ {
256
+ type: "tag",
257
+ pattern: /[A-Za-z_$][\w$.-]*/,
258
+ next: "jsxAttributes"
259
+ },
260
+ punctuationRule
261
+ ]
262
+ },
263
+ jsxAttributes: {
232
264
  rules: [
233
- { type: "tag.punctuation", pattern: /\/?>/, pop: true },
265
+ { type: "tag.punctuation", pattern: /\/>/, pop: true },
266
+ { type: "tag.punctuation", pattern: />/, next: "jsxChildren" },
234
267
  whitespaceRule,
235
268
  { type: "attribute.value", pattern: /"(?:\\.|[^"\\])*"?/ },
236
269
  { type: "attribute.value", pattern: /'(?:\\.|[^'\\])*'?/ },
237
270
  { type: "punctuation", pattern: /{/, push: "jsxExpression" },
238
271
  { type: "operator", pattern: /=/ },
272
+ { type: "attribute", pattern: /[A-Za-z_$][\w$.-]*/ },
273
+ punctuationRule
274
+ ]
275
+ },
276
+ jsxChildren: {
277
+ fallbackType: "plain",
278
+ rules: [
279
+ {
280
+ type: "tag.punctuation",
281
+ pattern: /<\/(?=[A-Za-z_$>])/,
282
+ push: "jsxClosingTag"
283
+ },
284
+ {
285
+ type: "tag.punctuation",
286
+ pattern: /<(?=[A-Za-z_$>])/,
287
+ push: "jsxTagName"
288
+ },
289
+ { type: "punctuation", pattern: /{/, push: "jsxExpression" },
290
+ { type: "plain", pattern: /[^<{]+/ },
291
+ { type: "plain", pattern: /[<{]/ }
292
+ ]
293
+ },
294
+ jsxClosingTag: {
295
+ rules: [
296
+ { type: "tag.punctuation", pattern: />/, pop: 2 },
297
+ whitespaceRule,
239
298
  { type: "tag", pattern: /[A-Za-z_$][\w$.-]*/ },
240
299
  punctuationRule
241
300
  ]
@@ -0,0 +1,132 @@
1
+ import { compileGrammar, normalizeId } from "./grammar.js";
2
+ import { YuirinxError } from "./errors.js";
3
+ import { renderColorLines, renderTokens } from "./renderer.js";
4
+ import { tokenizeCompiled } from "./tokenizer.js";
5
+ const plaintextGrammar = {
6
+ id: "plaintext",
7
+ aliases: ["text", "txt", "plain"],
8
+ states: {
9
+ root: {
10
+ rules: [{ type: "plain", pattern: /[\s\S]+/ }]
11
+ }
12
+ }
13
+ };
14
+ function themeId(theme) {
15
+ return normalizeId(theme.id);
16
+ }
17
+ function createHighlighter(options = {}) {
18
+ const grammars = /* @__PURE__ */ new Map();
19
+ const canonicalIds = /* @__PURE__ */ new Set();
20
+ const themes = /* @__PURE__ */ new Map();
21
+ const requestedMaxStateDepth = options.maxStateDepth ?? 64;
22
+ if (!Number.isFinite(requestedMaxStateDepth) || !Number.isInteger(requestedMaxStateDepth) || requestedMaxStateDepth < 2) {
23
+ throw new YuirinxError(
24
+ "YUIRINX_INVALID_OPTION",
25
+ "maxStateDepth must be a finite integer greater than or equal to 2."
26
+ );
27
+ }
28
+ const maxStateDepth = requestedMaxStateDepth;
29
+ const compiledPlaintext = compileGrammar(plaintextGrammar);
30
+ const plaintextIds = /* @__PURE__ */ new Set([
31
+ normalizeId(plaintextGrammar.id),
32
+ ...(plaintextGrammar.aliases ?? []).map(normalizeId)
33
+ ]);
34
+ const registerLanguage = (grammar) => {
35
+ const compiled = compileGrammar(grammar);
36
+ const id = compiled.id;
37
+ if (grammars.has(id)) {
38
+ throw new YuirinxError(
39
+ "YUIRINX_DUPLICATE_LANGUAGE",
40
+ `Language "${id}" is already registered.`
41
+ );
42
+ }
43
+ const aliases = (grammar.aliases ?? []).map(normalizeId).filter(Boolean);
44
+ for (const alias of aliases) {
45
+ if (alias === id || grammars.has(alias)) {
46
+ throw new YuirinxError(
47
+ "YUIRINX_DUPLICATE_ALIAS",
48
+ `Alias "${alias}" for language "${id}" is already registered.`
49
+ );
50
+ }
51
+ }
52
+ grammars.set(id, compiled);
53
+ canonicalIds.add(id);
54
+ for (const alias of aliases) grammars.set(alias, compiled);
55
+ };
56
+ const registerTheme = (theme) => {
57
+ const id = themeId(theme);
58
+ if (!id)
59
+ throw new YuirinxError(
60
+ "YUIRINX_DUPLICATE_THEME",
61
+ "A theme must have a non-empty id."
62
+ );
63
+ if (themes.has(id)) {
64
+ throw new YuirinxError(
65
+ "YUIRINX_DUPLICATE_THEME",
66
+ `Theme "${id}" is already registered.`
67
+ );
68
+ }
69
+ themes.set(id, theme);
70
+ };
71
+ for (const grammar of options.languages ?? []) registerLanguage(grammar);
72
+ for (const theme of options.themes ?? []) registerTheme(theme);
73
+ const resolveGrammar = (language) => {
74
+ if (typeof language !== "string") {
75
+ return { grammar: compileGrammar(language), unavailable: false };
76
+ }
77
+ const id = normalizeId(language);
78
+ const registered = grammars.get(id);
79
+ if (registered) return { grammar: registered, unavailable: false };
80
+ return {
81
+ grammar: compiledPlaintext,
82
+ unavailable: !plaintextIds.has(id)
83
+ };
84
+ };
85
+ const resolveTheme = (theme) => {
86
+ if (!theme)
87
+ return options.defaultTheme ? themes.get(normalizeId(options.defaultTheme)) : void 0;
88
+ return typeof theme === "string" ? themes.get(normalizeId(theme)) : theme;
89
+ };
90
+ const tokenize = (code, language) => tokenizeCompiled(code, resolveGrammar(language).grammar, maxStateDepth);
91
+ const render = (tokens, renderOptions = {}) => renderTokens(tokens, renderOptions, resolveTheme(renderOptions.theme));
92
+ const highlight = (code, highlightOptions = {}) => {
93
+ const language = highlightOptions.lang ?? options.defaultLanguage ?? "plaintext";
94
+ const resolved = resolveGrammar(language);
95
+ const theme = resolveTheme(highlightOptions.theme);
96
+ const renderOptions = {
97
+ ...highlightOptions,
98
+ language: typeof language === "string" ? language : language.id
99
+ };
100
+ const fallback = highlightOptions.fallback ?? options.defaultFallback ?? "plaintext";
101
+ if (resolved.unavailable && fallback === "color-lines") {
102
+ return renderColorLines(code, renderOptions, theme);
103
+ }
104
+ const tokens = tokenizeCompiled(code, resolved.grammar, maxStateDepth);
105
+ return renderTokens(tokens, renderOptions, theme);
106
+ };
107
+ return {
108
+ highlight,
109
+ tokenize,
110
+ render,
111
+ registerLanguage,
112
+ registerTheme,
113
+ hasLanguage: (language) => {
114
+ const id = normalizeId(language);
115
+ return plaintextIds.has(id) || grammars.has(id);
116
+ },
117
+ hasTheme: (theme) => themes.has(normalizeId(theme)),
118
+ getLanguage: (language) => {
119
+ const id = normalizeId(language);
120
+ return grammars.get(id)?.source ?? (plaintextIds.has(id) ? plaintextGrammar : void 0);
121
+ },
122
+ getTheme: (theme) => themes.get(normalizeId(theme)),
123
+ listLanguages: () => Object.freeze([
124
+ "plaintext",
125
+ ...[...canonicalIds].filter((id) => id !== "plaintext")
126
+ ]),
127
+ listThemes: () => Object.freeze([...themes.keys()])
128
+ };
129
+ }
130
+ export {
131
+ createHighlighter
132
+ };
@@ -0,0 +1,10 @@
1
+ class YuirinxError extends Error {
2
+ constructor(code, message) {
3
+ super(message);
4
+ this.name = "YuirinxError";
5
+ this.code = code;
6
+ }
7
+ }
8
+ export {
9
+ YuirinxError
10
+ };
@@ -0,0 +1,17 @@
1
+ const HTML_ESCAPE_RE = /[&<>"']/g;
2
+ const HTML_ENTITIES = {
3
+ "&": "&amp;",
4
+ "<": "&lt;",
5
+ ">": "&gt;",
6
+ '"': "&quot;",
7
+ "'": "&#39;"
8
+ };
9
+ function escapeHtml(value) {
10
+ return value.replace(
11
+ HTML_ESCAPE_RE,
12
+ (character) => HTML_ENTITIES[character] ?? character
13
+ );
14
+ }
15
+ export {
16
+ escapeHtml
17
+ };