presently 0.1.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.
Files changed (215) hide show
  1. checksums.yaml +7 -0
  2. data/bin/presently +13 -0
  3. data/lib/presently/application.rb +104 -0
  4. data/lib/presently/clock.rb +77 -0
  5. data/lib/presently/display_view.rb +73 -0
  6. data/lib/presently/environment/application.rb +62 -0
  7. data/lib/presently/page.rb +38 -0
  8. data/lib/presently/page.xrb +31 -0
  9. data/lib/presently/presentation.rb +72 -0
  10. data/lib/presently/presentation_controller.rb +181 -0
  11. data/lib/presently/presenter_view.rb +264 -0
  12. data/lib/presently/slide.rb +148 -0
  13. data/lib/presently/slide_view.rb +92 -0
  14. data/lib/presently/state.rb +66 -0
  15. data/lib/presently/version.rb +9 -0
  16. data/lib/presently.rb +9 -0
  17. data/license.md +21 -0
  18. data/public/_components/@socketry/syntax/Syntax/CodeElement.js +337 -0
  19. data/public/_components/@socketry/syntax/Syntax/Errors.js +52 -0
  20. data/public/_components/@socketry/syntax/Syntax/Language/apache.js +49 -0
  21. data/public/_components/@socketry/syntax/Syntax/Language/applescript.js +157 -0
  22. data/public/_components/@socketry/syntax/Syntax/Language/assembly.js +42 -0
  23. data/public/_components/@socketry/syntax/Syntax/Language/bash-script.js +108 -0
  24. data/public/_components/@socketry/syntax/Syntax/Language/bash.js +32 -0
  25. data/public/_components/@socketry/syntax/Syntax/Language/basic.js +232 -0
  26. data/public/_components/@socketry/syntax/Syntax/Language/c++.js +1 -0
  27. data/public/_components/@socketry/syntax/Syntax/Language/c.js +1 -0
  28. data/public/_components/@socketry/syntax/Syntax/Language/clang.js +201 -0
  29. data/public/_components/@socketry/syntax/Syntax/Language/cpp.js +1 -0
  30. data/public/_components/@socketry/syntax/Syntax/Language/csharp.js +166 -0
  31. data/public/_components/@socketry/syntax/Syntax/Language/css.js +244 -0
  32. data/public/_components/@socketry/syntax/Syntax/Language/diff.js +24 -0
  33. data/public/_components/@socketry/syntax/Syntax/Language/go.js +135 -0
  34. data/public/_components/@socketry/syntax/Syntax/Language/haskell.js +110 -0
  35. data/public/_components/@socketry/syntax/Syntax/Language/html.js +69 -0
  36. data/public/_components/@socketry/syntax/Syntax/Language/io.js +68 -0
  37. data/public/_components/@socketry/syntax/Syntax/Language/java.js +134 -0
  38. data/public/_components/@socketry/syntax/Syntax/Language/javascript.js +89 -0
  39. data/public/_components/@socketry/syntax/Syntax/Language/json.js +36 -0
  40. data/public/_components/@socketry/syntax/Syntax/Language/lisp.js +38 -0
  41. data/public/_components/@socketry/syntax/Syntax/Language/lua.js +87 -0
  42. data/public/_components/@socketry/syntax/Syntax/Language/markdown.js +112 -0
  43. data/public/_components/@socketry/syntax/Syntax/Language/nginx.js +37 -0
  44. data/public/_components/@socketry/syntax/Syntax/Language/objective-c.js +1 -0
  45. data/public/_components/@socketry/syntax/Syntax/Language/ocaml.js +225 -0
  46. data/public/_components/@socketry/syntax/Syntax/Language/pascal.js +166 -0
  47. data/public/_components/@socketry/syntax/Syntax/Language/patch.js +2 -0
  48. data/public/_components/@socketry/syntax/Syntax/Language/perl5.js +317 -0
  49. data/public/_components/@socketry/syntax/Syntax/Language/php-script.js +112 -0
  50. data/public/_components/@socketry/syntax/Syntax/Language/php.js +18 -0
  51. data/public/_components/@socketry/syntax/Syntax/Language/plain.js +20 -0
  52. data/public/_components/@socketry/syntax/Syntax/Language/protobuf.js +77 -0
  53. data/public/_components/@socketry/syntax/Syntax/Language/python.js +208 -0
  54. data/public/_components/@socketry/syntax/Syntax/Language/ruby.js +124 -0
  55. data/public/_components/@socketry/syntax/Syntax/Language/scala.js +81 -0
  56. data/public/_components/@socketry/syntax/Syntax/Language/smalltalk.js +30 -0
  57. data/public/_components/@socketry/syntax/Syntax/Language/sql.js +865 -0
  58. data/public/_components/@socketry/syntax/Syntax/Language/super-collider.js +70 -0
  59. data/public/_components/@socketry/syntax/Syntax/Language/swift.js +176 -0
  60. data/public/_components/@socketry/syntax/Syntax/Language/xml.js +76 -0
  61. data/public/_components/@socketry/syntax/Syntax/Language/xrb.js +33 -0
  62. data/public/_components/@socketry/syntax/Syntax/Language/yaml.js +29 -0
  63. data/public/_components/@socketry/syntax/Syntax/Language.js +276 -0
  64. data/public/_components/@socketry/syntax/Syntax/Loader.js +78 -0
  65. data/public/_components/@socketry/syntax/Syntax/Match.js +546 -0
  66. data/public/_components/@socketry/syntax/Syntax/Rule.js +306 -0
  67. data/public/_components/@socketry/syntax/Syntax.js +356 -0
  68. data/public/_components/@socketry/syntax/bin/syntax-ast.js +42 -0
  69. data/public/_components/@socketry/syntax/examples/_template.html +53 -0
  70. data/public/_components/@socketry/syntax/examples/apache.html +72 -0
  71. data/public/_components/@socketry/syntax/examples/applescript.html +72 -0
  72. data/public/_components/@socketry/syntax/examples/assembly.html +74 -0
  73. data/public/_components/@socketry/syntax/examples/bash.html +90 -0
  74. data/public/_components/@socketry/syntax/examples/basic.html +87 -0
  75. data/public/_components/@socketry/syntax/examples/c.html +141 -0
  76. data/public/_components/@socketry/syntax/examples/clang.html +202 -0
  77. data/public/_components/@socketry/syntax/examples/csharp.html +110 -0
  78. data/public/_components/@socketry/syntax/examples/css-colors.html +179 -0
  79. data/public/_components/@socketry/syntax/examples/custom-theme.html +155 -0
  80. data/public/_components/@socketry/syntax/examples/diff.html +142 -0
  81. data/public/_components/@socketry/syntax/examples/examples.css +216 -0
  82. data/public/_components/@socketry/syntax/examples/go.html +413 -0
  83. data/public/_components/@socketry/syntax/examples/haskell.html +373 -0
  84. data/public/_components/@socketry/syntax/examples/html.html +316 -0
  85. data/public/_components/@socketry/syntax/examples/index.html +97 -0
  86. data/public/_components/@socketry/syntax/examples/io.html +552 -0
  87. data/public/_components/@socketry/syntax/examples/java.html +786 -0
  88. data/public/_components/@socketry/syntax/examples/javascript.html +199 -0
  89. data/public/_components/@socketry/syntax/examples/json.html +150 -0
  90. data/public/_components/@socketry/syntax/examples/lisp.html +476 -0
  91. data/public/_components/@socketry/syntax/examples/lua.html +737 -0
  92. data/public/_components/@socketry/syntax/examples/markdown.html +121 -0
  93. data/public/_components/@socketry/syntax/examples/mixed.html +306 -0
  94. data/public/_components/@socketry/syntax/examples/nginx.html +554 -0
  95. data/public/_components/@socketry/syntax/examples/ocaml.html +596 -0
  96. data/public/_components/@socketry/syntax/examples/pascal.html +762 -0
  97. data/public/_components/@socketry/syntax/examples/perl5.html +488 -0
  98. data/public/_components/@socketry/syntax/examples/php-script.html +142 -0
  99. data/public/_components/@socketry/syntax/examples/php.html +95 -0
  100. data/public/_components/@socketry/syntax/examples/plain.html +222 -0
  101. data/public/_components/@socketry/syntax/examples/protobuf.html +405 -0
  102. data/public/_components/@socketry/syntax/examples/python.html +82 -0
  103. data/public/_components/@socketry/syntax/examples/readme.md +79 -0
  104. data/public/_components/@socketry/syntax/examples/ruby.html +58 -0
  105. data/public/_components/@socketry/syntax/examples/scala.html +41 -0
  106. data/public/_components/@socketry/syntax/examples/smalltalk.html +436 -0
  107. data/public/_components/@socketry/syntax/examples/sql.html +373 -0
  108. data/public/_components/@socketry/syntax/examples/super-collider.html +55 -0
  109. data/public/_components/@socketry/syntax/examples/swift.html +176 -0
  110. data/public/_components/@socketry/syntax/examples/wrap-demo.html +103 -0
  111. data/public/_components/@socketry/syntax/examples/xml.html +112 -0
  112. data/public/_components/@socketry/syntax/examples/xrb.html +37 -0
  113. data/public/_components/@socketry/syntax/examples/yaml.html +72 -0
  114. data/public/_components/@socketry/syntax/license.md +21 -0
  115. data/public/_components/@socketry/syntax/package-lock.json +834 -0
  116. data/public/_components/@socketry/syntax/package.json +43 -0
  117. data/public/_components/@socketry/syntax/readme.md +162 -0
  118. data/public/_components/@socketry/syntax/test/Syntax/CodeElement.js +306 -0
  119. data/public/_components/@socketry/syntax/test/Syntax/ErrorHandling.js +85 -0
  120. data/public/_components/@socketry/syntax/test/Syntax/Language/apache.js +153 -0
  121. data/public/_components/@socketry/syntax/test/Syntax/Language/applescript.js +198 -0
  122. data/public/_components/@socketry/syntax/test/Syntax/Language/assembly.js +209 -0
  123. data/public/_components/@socketry/syntax/test/Syntax/Language/bash-script.js +225 -0
  124. data/public/_components/@socketry/syntax/test/Syntax/Language/bash.js +162 -0
  125. data/public/_components/@socketry/syntax/test/Syntax/Language/basic.js +265 -0
  126. data/public/_components/@socketry/syntax/test/Syntax/Language/clang.js +390 -0
  127. data/public/_components/@socketry/syntax/test/Syntax/Language/csharp.js +436 -0
  128. data/public/_components/@socketry/syntax/test/Syntax/Language/css.js +431 -0
  129. data/public/_components/@socketry/syntax/test/Syntax/Language/diff.js +206 -0
  130. data/public/_components/@socketry/syntax/test/Syntax/Language/go.js +386 -0
  131. data/public/_components/@socketry/syntax/test/Syntax/Language/haskell.js +454 -0
  132. data/public/_components/@socketry/syntax/test/Syntax/Language/html.js +111 -0
  133. data/public/_components/@socketry/syntax/test/Syntax/Language/io.js +229 -0
  134. data/public/_components/@socketry/syntax/test/Syntax/Language/java.js +362 -0
  135. data/public/_components/@socketry/syntax/test/Syntax/Language/javascript.js +101 -0
  136. data/public/_components/@socketry/syntax/test/Syntax/Language/json.js +101 -0
  137. data/public/_components/@socketry/syntax/test/Syntax/Language/lisp.js +224 -0
  138. data/public/_components/@socketry/syntax/test/Syntax/Language/lua.js +307 -0
  139. data/public/_components/@socketry/syntax/test/Syntax/Language/markdown.js +163 -0
  140. data/public/_components/@socketry/syntax/test/Syntax/Language/nginx.js +267 -0
  141. data/public/_components/@socketry/syntax/test/Syntax/Language/ocaml.js +299 -0
  142. data/public/_components/@socketry/syntax/test/Syntax/Language/pascal.js +311 -0
  143. data/public/_components/@socketry/syntax/test/Syntax/Language/perl5.js +333 -0
  144. data/public/_components/@socketry/syntax/test/Syntax/Language/php-script.js +197 -0
  145. data/public/_components/@socketry/syntax/test/Syntax/Language/php.js +92 -0
  146. data/public/_components/@socketry/syntax/test/Syntax/Language/plain.js +327 -0
  147. data/public/_components/@socketry/syntax/test/Syntax/Language/protobuf.js +294 -0
  148. data/public/_components/@socketry/syntax/test/Syntax/Language/python.js +213 -0
  149. data/public/_components/@socketry/syntax/test/Syntax/Language/ruby.js +70 -0
  150. data/public/_components/@socketry/syntax/test/Syntax/Language/scala.js +75 -0
  151. data/public/_components/@socketry/syntax/test/Syntax/Language/smalltalk.js +223 -0
  152. data/public/_components/@socketry/syntax/test/Syntax/Language/sql.js +281 -0
  153. data/public/_components/@socketry/syntax/test/Syntax/Language/super-collider.js +66 -0
  154. data/public/_components/@socketry/syntax/test/Syntax/Language/swift.js +71 -0
  155. data/public/_components/@socketry/syntax/test/Syntax/Language/xml.js +170 -0
  156. data/public/_components/@socketry/syntax/test/Syntax/Language/xrb.js +57 -0
  157. data/public/_components/@socketry/syntax/test/Syntax/Language/yaml.js +123 -0
  158. data/public/_components/@socketry/syntax/test/Syntax/Language.js +62 -0
  159. data/public/_components/@socketry/syntax/test/Syntax/Match.js +40 -0
  160. data/public/_components/@socketry/syntax/test/Syntax/Rule.js +251 -0
  161. data/public/_components/@socketry/syntax/test/Syntax.js +38 -0
  162. data/public/_components/@socketry/syntax/test/helpers/ast-matcher.js +90 -0
  163. data/public/_components/@socketry/syntax/themes/base/apache.css +1 -0
  164. data/public/_components/@socketry/syntax/themes/base/applescript.css +1 -0
  165. data/public/_components/@socketry/syntax/themes/base/assembly.css +1 -0
  166. data/public/_components/@socketry/syntax/themes/base/bash.css +1 -0
  167. data/public/_components/@socketry/syntax/themes/base/basic.css +1 -0
  168. data/public/_components/@socketry/syntax/themes/base/c.css +1 -0
  169. data/public/_components/@socketry/syntax/themes/base/clang.css +0 -0
  170. data/public/_components/@socketry/syntax/themes/base/csharp.css +1 -0
  171. data/public/_components/@socketry/syntax/themes/base/css.css +22 -0
  172. data/public/_components/@socketry/syntax/themes/base/diff.css +48 -0
  173. data/public/_components/@socketry/syntax/themes/base/go.css +1 -0
  174. data/public/_components/@socketry/syntax/themes/base/haskell.css +1 -0
  175. data/public/_components/@socketry/syntax/themes/base/html.css +1 -0
  176. data/public/_components/@socketry/syntax/themes/base/io.css +1 -0
  177. data/public/_components/@socketry/syntax/themes/base/java.css +1 -0
  178. data/public/_components/@socketry/syntax/themes/base/javascript.css +1 -0
  179. data/public/_components/@socketry/syntax/themes/base/json.css +41 -0
  180. data/public/_components/@socketry/syntax/themes/base/lisp.css +1 -0
  181. data/public/_components/@socketry/syntax/themes/base/lua.css +1 -0
  182. data/public/_components/@socketry/syntax/themes/base/markdown.css +16 -0
  183. data/public/_components/@socketry/syntax/themes/base/nginx.css +1 -0
  184. data/public/_components/@socketry/syntax/themes/base/ocaml.css +1 -0
  185. data/public/_components/@socketry/syntax/themes/base/pascal.css +1 -0
  186. data/public/_components/@socketry/syntax/themes/base/perl5.css +1 -0
  187. data/public/_components/@socketry/syntax/themes/base/php-script.css +1 -0
  188. data/public/_components/@socketry/syntax/themes/base/php.css +1 -0
  189. data/public/_components/@socketry/syntax/themes/base/plain.css +1 -0
  190. data/public/_components/@socketry/syntax/themes/base/protobuf.css +1 -0
  191. data/public/_components/@socketry/syntax/themes/base/python.css +1 -0
  192. data/public/_components/@socketry/syntax/themes/base/ruby.css +23 -0
  193. data/public/_components/@socketry/syntax/themes/base/scala.css +3 -0
  194. data/public/_components/@socketry/syntax/themes/base/smalltalk.css +1 -0
  195. data/public/_components/@socketry/syntax/themes/base/sql.css +1 -0
  196. data/public/_components/@socketry/syntax/themes/base/super-collider.css +33 -0
  197. data/public/_components/@socketry/syntax/themes/base/swift.css +1 -0
  198. data/public/_components/@socketry/syntax/themes/base/syntax.css +63 -0
  199. data/public/_components/@socketry/syntax/themes/base/xml.css +1 -0
  200. data/public/_components/@socketry/syntax/themes/base/xrb.css +29 -0
  201. data/public/_components/@socketry/syntax/themes/base/yaml.css +1 -0
  202. data/public/_components/@socketry/syntax/themes/theming.md +233 -0
  203. data/public/_components/@socketry/syntax/update-examples.js +135 -0
  204. data/public/_static/index.css +593 -0
  205. data/public/application.js +147 -0
  206. data/readme.md +69 -0
  207. data/releases.md +3 -0
  208. data/templates/code.xrb +12 -0
  209. data/templates/default.xrb +5 -0
  210. data/templates/image.xrb +8 -0
  211. data/templates/section.xrb +5 -0
  212. data/templates/title.xrb +8 -0
  213. data/templates/translation.xrb +8 -0
  214. data/templates/two_column.xrb +8 -0
  215. metadata +280 -0
@@ -0,0 +1,43 @@
1
+ {
2
+ "name": "@socketry/syntax",
3
+ "version": "0.3.2",
4
+ "description": "A modern, framework-agnostic syntax highlighter using Web Components",
5
+ "type": "module",
6
+ "main": "Syntax.js",
7
+ "exports": {
8
+ ".": "./Syntax.js",
9
+ "./Match": "./Syntax/Match.js",
10
+ "./Language": "./Syntax/Language.js",
11
+ "./Loader": "./Syntax/Loader.js",
12
+ "./Errors": "./Syntax/Errors.js",
13
+ "./Language/*": "./Syntax/Language/*.js"
14
+ },
15
+ "files": [
16
+ "Syntax.js",
17
+ "Syntax/",
18
+ "styles/",
19
+ "themes/"
20
+ ],
21
+ "scripts": {
22
+ "test": "node --test 'test/**/*.js'",
23
+ "format": "prettier --write '**/*.{js,json,md}'",
24
+ "format:check": "prettier --check '**/*.{js,json,md}'"
25
+ },
26
+ "keywords": [
27
+ "syntax",
28
+ "highlighting",
29
+ "web-components",
30
+ "code",
31
+ "highlighter"
32
+ ],
33
+ "author": "Samuel G. D. Williams",
34
+ "license": "MIT",
35
+ "repository": {
36
+ "type": "git",
37
+ "url": "https://github.com/socketry/syntax-js.git"
38
+ },
39
+ "devDependencies": {
40
+ "jsdom": "^25.0.0",
41
+ "prettier": "^3.6.2"
42
+ }
43
+ }
@@ -0,0 +1,162 @@
1
+ # @socketry/syntax
2
+
3
+ A modern, framework-agnostic syntax highlighter using Web Components. This is a reimplementation of [jQuery.Syntax](https://github.com/ioquatix/jquery-syntax) without jQuery dependencies.
4
+
5
+ ## Features
6
+
7
+ - 🎨 **Modern Web Components** - Uses autonomous custom elements
8
+ - 📦 **Dynamic Loading** - Loads language definitions on-demand
9
+ - 🔒 **No Dependencies** - Pure JavaScript, no jQuery required
10
+ - 🎯 **Framework Agnostic** - Works with React, Vue, vanilla JS, etc.
11
+ - 🌲 **Clean Architecture** - Well-structured classes with clear responsibilities
12
+ - 🔐 **Private Fields** - Modern JavaScript with proper encapsulation
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ npm install @socketry/syntax
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ### Automatic Upgrade
23
+
24
+ The easiest way to use Syntax is to let it automatically upgrade existing `<code>` elements:
25
+
26
+ ```html
27
+ <script type="module">
28
+ import Syntax from '@socketry/syntax';
29
+
30
+ // Automatically upgrade all code blocks with language classes
31
+ document.addEventListener('DOMContentLoaded', async function() {
32
+ await Syntax.highlight();
33
+ });
34
+ </script>
35
+
36
+ <!-- Standard code blocks - will be automatically upgraded -->
37
+ <pre><code class="language-javascript">
38
+ const hello = "world";
39
+ console.log(hello);
40
+ </code></pre>
41
+
42
+ <p>Inline code: <code class="language-javascript">const x = 1;</code></p>
43
+ ```
44
+
45
+ By default, `Syntax.highlight()` will:
46
+ - Find all `<code>` elements with class names matching `language-*` e.g., `language-javascript`, `language-python`.
47
+ - Replace them with `<syntax-code>` web components.
48
+ - Automatically detect if they're inside `<pre>` tags for proper wrapping behavior.
49
+
50
+ You can customize the selector:
51
+
52
+ ```javascript
53
+ // Only upgrade specific elements:
54
+ await Syntax.highlight({
55
+ selector: 'code.highlight'
56
+ });
57
+ ```
58
+
59
+ ### Manual Web Components
60
+
61
+ You can also use the `<syntax-code>` web component directly:
62
+
63
+ ```html
64
+ <script type="module">
65
+ import {Syntax} from '@socketry/syntax';
66
+
67
+ // Disable automatic upgrade, just register the component:
68
+ await Syntax.highlight({upgradeAll: false});
69
+ </script>
70
+
71
+ <!-- Use the web component directly -->
72
+ <syntax-code language="javascript">
73
+ const hello = "world"; console.log(hello);
74
+ </syntax-code>
75
+
76
+ <!-- Inside a <pre> tag for block display with line wrapping -->
77
+ <pre><syntax-code language="python" wrap>
78
+ def greet(name):
79
+ print(f"Hello, {name}!")
80
+ </syntax-code></pre>
81
+ ```
82
+
83
+ ### Programmatic Usage
84
+
85
+ ```javascript
86
+ import Syntax from '@socketry/syntax';
87
+ import registerJavaScript from '@socketry/syntax/Language/javascript.js';
88
+
89
+ // Create a Syntax instance
90
+ const syntax = new Syntax();
91
+ registerJavaScript(syntax);
92
+
93
+ // Get the JavaScript language and process code:
94
+ const language = await syntax.getLanguage('javascript');
95
+ const code = 'const x = 10;';
96
+ const html = await language.process(code);
97
+ document.body.appendChild(html);
98
+ ```
99
+
100
+ ### The `wrap` Attribute
101
+
102
+ The `<syntax-code>` element automatically detects whether it's inside a `<pre>` tag:
103
+
104
+ - **Inside `<pre>`**: Sets `wrap` attribute, enables line wrapping with proper indentation.
105
+ - **Standalone**: No `wrap` attribute, uses horizontal scrolling for long lines.
106
+
107
+ You can manually control this:
108
+
109
+ ```html
110
+ <!-- Force wrapping even outside <pre> -->
111
+ <syntax-code language="javascript" wrap>
112
+ const reallyLongLine = "This will wrap instead of scroll";
113
+ </syntax-code>
114
+
115
+ <!-- Disable wrapping even inside <pre> -->
116
+ <pre><syntax-code language="javascript">
117
+ const code = "This will scroll horizontally";
118
+ </syntax-code></pre>
119
+ ```
120
+
121
+ ## Command Line Tool
122
+
123
+ A simple CLI tool is included to inspect the AST (Abstract Syntax Tree) of parsed code:
124
+
125
+ ```bash
126
+ node bin/syntax-ast.js <language> <code>
127
+ ```
128
+
129
+ Examples:
130
+
131
+ ```bash
132
+ # Parse JavaScript
133
+ node bin/syntax-ast.js javascript "const x = 1;"
134
+
135
+ # Parse Markdown
136
+ node bin/syntax-ast.js markdown '`inline code`'
137
+
138
+ # Parse Python
139
+ node bin/syntax-ast.js python "def foo(): pass"
140
+ ```
141
+
142
+ Output shows all matched tokens with their type, position, length, and text:
143
+
144
+ ```
145
+ Language: javascript
146
+ Code: "const x = 1;"
147
+
148
+ Matches: 3
149
+ ────────────────────────────────────────────────────────────────────────────────
150
+ [keyword] @0..5 (5 chars)
151
+ Text: "const"
152
+ [operator] @8..9 (1 chars)
153
+ Text: "="
154
+ [constant] @10..11 (1 chars)
155
+ Text: "1"
156
+ ```
157
+
158
+ This is useful for:
159
+ - Debugging language definitions
160
+ - Understanding how code is tokenized
161
+ - Testing pattern matching
162
+ - Developing new language support
@@ -0,0 +1,306 @@
1
+ /**
2
+ * CodeElement Integration Tests
3
+ *
4
+ * Tests the <syntax-code> web component with auto-loading
5
+ *
6
+ * Note: These tests focus on the integration between Syntax.js and Language classes
7
+ * without requiring a full browser environment. Full end-to-end tests with stylesheets
8
+ * and DOM rendering are better suited for browser-based testing.
9
+ */
10
+
11
+ import {test} from 'node:test';
12
+ import assert from 'node:assert/strict';
13
+ import {JSDOM} from 'jsdom';
14
+ import Syntax from '../../Syntax.js';
15
+
16
+ // Set up JSDOM environment
17
+ const dom = new JSDOM('<!DOCTYPE html><html><body></body></html>', {
18
+ url: 'http://localhost:8000/'
19
+ });
20
+
21
+ global.window = dom.window;
22
+ global.document = dom.window.document;
23
+ global.HTMLElement = dom.window.HTMLElement;
24
+ global.customElements = dom.window.customElements;
25
+ global.CustomEvent = dom.window.CustomEvent;
26
+
27
+ test('Syntax.highlight() registers the web component', async () => {
28
+ // This should register the <syntax-code> element
29
+ await Syntax.highlight({upgradeAll: false});
30
+
31
+ // Verify the element is registered
32
+ const constructor = customElements.get('syntax-code');
33
+ assert.ok(constructor, 'syntax-code element should be registered');
34
+
35
+ // Verify we can create an instance
36
+ const element = new constructor();
37
+ assert.ok(element instanceof HTMLElement);
38
+ });
39
+
40
+ test('Auto-loading mechanism loads YAML language on demand', async () => {
41
+ const syntax = Syntax.default;
42
+
43
+ // YAML should not be loaded yet
44
+ assert.ok(!syntax.hasLanguage('yaml'), 'YAML should not be loaded initially');
45
+
46
+ // Request the YAML language
47
+ const language = await syntax.getResource('yaml');
48
+
49
+ // Verify it was loaded and cached
50
+ assert.ok(language, 'Language should be loaded');
51
+ assert.equal(language.name, 'yaml', 'Language name should be yaml');
52
+ assert.ok(syntax.hasLanguage('yaml'), 'YAML should now be cached');
53
+
54
+ // Requesting again should return the same instance
55
+ const language2 = await syntax.getResource('yaml');
56
+ assert.equal(language2, language, 'Should return cached instance');
57
+ });
58
+
59
+ test('Auto-loading deduplicates concurrent requests', async () => {
60
+ const syntax = new Syntax();
61
+
62
+ // Make multiple concurrent requests for the same language
63
+ const [lang1, lang2, lang3] = await Promise.all([
64
+ syntax.getResource('python'),
65
+ syntax.getResource('python'),
66
+ syntax.getResource('python')
67
+ ]);
68
+
69
+ // All should return the same instance
70
+ assert.equal(
71
+ lang1,
72
+ lang2,
73
+ 'Concurrent request 1 and 2 should return same instance'
74
+ );
75
+ assert.equal(
76
+ lang2,
77
+ lang3,
78
+ 'Concurrent request 2 and 3 should return same instance'
79
+ );
80
+ assert.equal(lang1.name, 'python');
81
+ });
82
+
83
+ test('Language registration works correctly', async () => {
84
+ const syntax = new Syntax();
85
+
86
+ // Load CSS which uses the register() pattern
87
+ const language = await syntax.getResource('css');
88
+
89
+ // Verify it's registered under the correct name
90
+ assert.ok(syntax.hasLanguage('css'));
91
+ assert.equal(language.name, 'css');
92
+
93
+ // Verify we can process code with it
94
+ const html = await language.process(syntax, 'body { color: red; }');
95
+ assert.ok(html instanceof HTMLElement);
96
+ assert.ok(html.outerHTML.includes('color'));
97
+ assert.ok(html.outerHTML.includes('red'));
98
+ });
99
+
100
+ test('Language.process() with multiple languages', async () => {
101
+ const syntax = Syntax.default;
102
+
103
+ // Load multiple languages
104
+ const python = await syntax.getResource('python');
105
+ const yaml = await syntax.getResource('yaml');
106
+ const css = await syntax.getResource('css');
107
+
108
+ // Process code in each language
109
+ const pythonHtml = await python.process(syntax, 'def hello(): pass');
110
+ const yamlHtml = await yaml.process(syntax, 'key: value');
111
+ const cssHtml = await css.process(syntax, 'a { color: blue; }');
112
+
113
+ // Verify all returned HTML elements
114
+ assert.ok(pythonHtml instanceof HTMLElement);
115
+ assert.ok(yamlHtml instanceof HTMLElement);
116
+ assert.ok(cssHtml instanceof HTMLElement);
117
+
118
+ // Verify they contain the expected content
119
+ assert.ok(pythonHtml.textContent.includes('def'));
120
+ assert.ok(yamlHtml.textContent.includes('key'));
121
+ assert.ok(cssHtml.textContent.includes('color'));
122
+ });
123
+
124
+ test('Error handling for non-existent language', async () => {
125
+ const syntax = new Syntax();
126
+
127
+ // Try to load a language that doesn't exist
128
+ await assert.rejects(
129
+ async () => await syntax.getResource('nonexistent-language-xyz'),
130
+ {
131
+ name: 'LanguageLoadError',
132
+ message: /Failed to load language 'nonexistent-language-xyz'/
133
+ },
134
+ 'Should throw LanguageLoadError for non-existent language'
135
+ );
136
+ });
137
+
138
+ test('Aliases work correctly', async () => {
139
+ const syntax = new Syntax();
140
+
141
+ // Load bash-script language and check it registered bash-statement too
142
+ const bashScript = await syntax.getResource('bash-script');
143
+
144
+ // bash-statement should also be available (registered by bash-script)
145
+ assert.ok(syntax.hasLanguage('bash-statement'));
146
+
147
+ // Load clang which has multiple aliases
148
+ const clang = await syntax.getResource('clang');
149
+
150
+ // Check aliases
151
+ assert.ok(syntax.hasLanguage('c'));
152
+ assert.ok(syntax.hasLanguage('cpp'));
153
+ assert.ok(syntax.hasLanguage('c++'));
154
+ assert.ok(syntax.hasLanguage('objective-c'));
155
+
156
+ // All aliases should resolve to the same language
157
+ const c = await syntax.getResource('c');
158
+ const cpp = await syntax.getResource('cpp');
159
+ assert.equal(c, clang);
160
+ assert.equal(cpp, clang);
161
+ });
162
+
163
+ test('upgradeAll handles <pre><code> blocks without double-nesting', async () => {
164
+ // Import the upgradeAll function
165
+ const {upgradeAll} = await import('../../Syntax/CodeElement.js');
166
+
167
+ // Create a typical code block structure: <pre><code>
168
+ document.body.innerHTML = `
169
+ <div id="test-container">
170
+ <pre><code class="language-javascript">const x = 1;</code></pre>
171
+ </div>
172
+ `;
173
+
174
+ // Run upgradeAll
175
+ upgradeAll('code[class*="language-"]');
176
+
177
+ const container = document.getElementById('test-container');
178
+
179
+ // Check that the syntax-code element was created
180
+ const syntaxCode = container.querySelector('syntax-code');
181
+ assert.ok(syntaxCode, 'syntax-code element should be created');
182
+
183
+ // Check that syntax-code is INSIDE the <pre> (replacing <code>)
184
+ const pre = container.querySelector('pre');
185
+ assert.ok(pre, '<pre> should still exist in the container');
186
+ assert.equal(
187
+ syntaxCode.parentElement,
188
+ pre,
189
+ 'syntax-code should be child of <pre>'
190
+ );
191
+
192
+ // Verify the language was set correctly
193
+ assert.equal(
194
+ syntaxCode.getAttribute('language'),
195
+ 'javascript',
196
+ 'Language should be set from class name'
197
+ );
198
+
199
+ // Verify the code content is preserved
200
+ assert.equal(
201
+ syntaxCode.textContent.trim(),
202
+ 'const x = 1;',
203
+ 'Code content should be preserved'
204
+ );
205
+
206
+ // Verify wrap attribute is set (because it's inside <pre>)
207
+ // Note: This happens in connectedCallback, which may not fire in JSDOM
208
+ // We'll just verify structure for now
209
+ });
210
+
211
+ test('upgradeAll can handle standalone <code> blocks with custom selector', async () => {
212
+ const {upgradeAll} = await import('../../Syntax/CodeElement.js');
213
+
214
+ // Create a standalone code block (no <pre> wrapper)
215
+ document.body.innerHTML = `
216
+ <div id="test-container">
217
+ <code class="language-python">print("hello")</code>
218
+ </div>
219
+ `;
220
+
221
+ // For standalone code blocks, use a selector without 'pre >'
222
+ upgradeAll('code[class*="language-"]');
223
+
224
+ const container = document.getElementById('test-container');
225
+ const syntaxCode = container.querySelector('syntax-code');
226
+
227
+ assert.ok(syntaxCode, 'syntax-code element should be created');
228
+ assert.equal(
229
+ syntaxCode.parentElement.tagName,
230
+ 'DIV',
231
+ 'syntax-code should be child of DIV'
232
+ );
233
+ assert.equal(
234
+ syntaxCode.getAttribute('language'),
235
+ 'python',
236
+ 'Language should be set'
237
+ );
238
+ assert.equal(
239
+ syntaxCode.textContent.trim(),
240
+ 'print("hello")',
241
+ 'Code content should be preserved'
242
+ );
243
+ });
244
+
245
+ test('syntax-code behaves semantically like <code> when inline', async () => {
246
+ const {CodeElement} = await import('../../Syntax/CodeElement.js');
247
+
248
+ // Create an inline syntax-code element
249
+ document.body.innerHTML = `
250
+ <p id="test-container">
251
+ Some text <syntax-code language="javascript">const x = 1;</syntax-code> more text.
252
+ </p>
253
+ `;
254
+
255
+ const element = document.querySelector('syntax-code');
256
+
257
+ // Wait for rendering to complete
258
+ await new Promise(resolve => setTimeout(resolve, 100));
259
+
260
+ // Verify it's inline (like <code>) by checking the shadow DOM structure
261
+ const shadowRoot = element.shadowRoot;
262
+ assert.ok(shadowRoot, 'Shadow root should exist');
263
+
264
+ // Should contain a <code> element, not wrapped in <pre>
265
+ const codeElement = shadowRoot.querySelector('code');
266
+ assert.ok(codeElement, 'Shadow DOM should contain <code> element');
267
+
268
+ const preElement = shadowRoot.querySelector('pre');
269
+ assert.equal(preElement, null, 'Shadow DOM should NOT contain <pre> wrapper for inline usage');
270
+
271
+ // The code element should be a direct child of shadow root
272
+ assert.ok(
273
+ Array.from(shadowRoot.children).includes(codeElement),
274
+ '<code> should be direct child of shadow root'
275
+ );
276
+ });
277
+
278
+ test('syntax-code behaves as block when inside <pre>', async () => {
279
+ const {CodeElement} = await import('../../Syntax/CodeElement.js');
280
+
281
+ // Create a block syntax-code element inside <pre>
282
+ document.body.innerHTML = `
283
+ <div id="test-container">
284
+ <pre><syntax-code language="javascript">const x = 1;</syntax-code></pre>
285
+ </div>
286
+ `;
287
+
288
+ const element = document.querySelector('syntax-code');
289
+
290
+ // Wait for rendering to complete
291
+ await new Promise(resolve => setTimeout(resolve, 100));
292
+
293
+ // Check that wrap attribute was set (because it's inside <pre>)
294
+ assert.ok(element.hasAttribute('wrap'), 'wrap attribute should be set when inside <pre>');
295
+
296
+ // After rendering, light DOM is cleared, so check shadow DOM
297
+ const shadowRoot = element.shadowRoot;
298
+ const codeElement = shadowRoot.querySelector('code');
299
+ assert.ok(codeElement, 'Shadow DOM should contain <code> element');
300
+
301
+ // The <code> should be a direct child of shadow root (no <pre> wrapper needed inside)
302
+ assert.ok(
303
+ Array.from(shadowRoot.children).includes(codeElement),
304
+ '<code> should be direct child of shadow root'
305
+ );
306
+ });
@@ -0,0 +1,85 @@
1
+ import {test} from 'node:test';
2
+ import assert from 'node:assert';
3
+
4
+ import Syntax from '../../Syntax.js';
5
+ import {Language} from '../../Syntax/Language.js';
6
+ import {
7
+ LanguageNotFoundError,
8
+ RuleApplyError,
9
+ StyleSheetLoadError
10
+ } from '../../Syntax/Errors.js';
11
+
12
+ test('Language.buildTree throws LanguageNotFoundError in strict mode for missing embedded language', async () => {
13
+ const syntax = new Syntax();
14
+ syntax.defaultOptions.strict = true;
15
+
16
+ const lang = new Language('dummy');
17
+ // Rule that embeds a non-existent language
18
+ lang.push({pattern: /x+/, language: 'no-such-language'});
19
+
20
+ await assert.rejects(
21
+ async () => {
22
+ await lang.buildTree(syntax, 'xxx', 0);
23
+ },
24
+ err => err instanceof LanguageNotFoundError
25
+ );
26
+ });
27
+
28
+ test('RuleApplyError thrown in strict mode when rule.apply throws', async () => {
29
+ const syntax = new Syntax();
30
+ syntax.defaultOptions.strict = true;
31
+
32
+ const lang = new Language('dummy');
33
+ // Custom rule that throws from apply
34
+ lang.push({
35
+ pattern: /x+/,
36
+ apply() {
37
+ throw new Error('boom');
38
+ }
39
+ });
40
+
41
+ await assert.rejects(
42
+ async () => {
43
+ await lang.getMatches(syntax, 'xxx');
44
+ },
45
+ err => err instanceof RuleApplyError
46
+ );
47
+ });
48
+
49
+ test('Rule apply failure is ignored (warn) in non-strict mode', async () => {
50
+ const syntax = new Syntax();
51
+ syntax.defaultOptions.strict = false;
52
+
53
+ const lang = new Language('dummy');
54
+ lang.push({
55
+ pattern: /x+/,
56
+ apply() {
57
+ throw new Error('boom');
58
+ }
59
+ });
60
+
61
+ const matches = await lang.getMatches(syntax, 'xxx');
62
+ assert.ok(Array.isArray(matches));
63
+ assert.strictEqual(matches.length, 0);
64
+ });
65
+
66
+ test('Stylesheet loader throws StyleSheetLoadError on non-200 response', async () => {
67
+ const syntax = new Syntax();
68
+ // Mock fetch to return 404
69
+ const originalFetch = globalThis.fetch;
70
+ globalThis.fetch = async () => ({
71
+ ok: false,
72
+ status: 404,
73
+ text: async () => ''
74
+ });
75
+ try {
76
+ await assert.rejects(
77
+ async () => {
78
+ await syntax.getStyleSheet('https://example.invalid/missing.css');
79
+ },
80
+ err => err instanceof StyleSheetLoadError
81
+ );
82
+ } finally {
83
+ globalThis.fetch = originalFetch;
84
+ }
85
+ });