jtlt 0.2.0 → 0.4.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 (70) hide show
  1. package/CHANGES.md +27 -0
  2. package/README.md +61 -164
  3. package/demo/calltemplate-params-demo.js +138 -0
  4. package/demo/codemirror.esm.js +28242 -0
  5. package/demo/codemirror.js +94 -0
  6. package/demo/index.css +7 -0
  7. package/demo/index.html +28 -0
  8. package/demo/index.js +210 -0
  9. package/demo/vendor/jamilih/dist/jml.mjs +2341 -0
  10. package/demo/vendor/jhtml/src/SAJJ/SAJJ.ObjectArrayDelegator.js +356 -0
  11. package/demo/vendor/jhtml/src/SAJJ/SAJJ.Stringifier.js +186 -0
  12. package/demo/vendor/jhtml/src/SAJJ/SAJJ.js +746 -0
  13. package/demo/vendor/jhtml/src/SAJJ/testing/SAJJ.html +33 -0
  14. package/demo/vendor/jhtml/src/SAJJ/testing/SAJJ.testing.js +25 -0
  15. package/demo/vendor/jhtml/src/jhtml-browser.js +5 -0
  16. package/demo/vendor/jhtml/src/jhtml-node.cts +3 -0
  17. package/demo/vendor/jhtml/src/jhtml-node.js +8 -0
  18. package/demo/vendor/jhtml/src/jhtml-node.mts +1 -0
  19. package/demo/vendor/jhtml/src/jhtml.cts +3 -0
  20. package/demo/vendor/jhtml/src/jhtml.js +602 -0
  21. package/demo/vendor/jhtml/src/jhtml.mts +1 -0
  22. package/demo/vendor/jsonpath-plus/dist/index-browser-esm.js +2158 -0
  23. package/demo/vendor/simple-get-json/dist/index-es.js +151 -0
  24. package/demo/xpath2-placeholder.js +1 -0
  25. package/dist/AbstractJoiningTransformer.d.ts +83 -9
  26. package/dist/AbstractJoiningTransformer.d.ts.map +1 -1
  27. package/dist/DOMJoiningTransformer.d.ts +85 -25
  28. package/dist/DOMJoiningTransformer.d.ts.map +1 -1
  29. package/dist/JSONJoiningTransformer.d.ts +159 -51
  30. package/dist/JSONJoiningTransformer.d.ts.map +1 -1
  31. package/dist/JSONPathTransformer.d.ts +37 -38
  32. package/dist/JSONPathTransformer.d.ts.map +1 -1
  33. package/dist/JSONPathTransformerContext.d.ts +334 -124
  34. package/dist/JSONPathTransformerContext.d.ts.map +1 -1
  35. package/dist/StringJoiningTransformer.d.ts +132 -41
  36. package/dist/StringJoiningTransformer.d.ts.map +1 -1
  37. package/dist/XPathTransformer.d.ts +35 -20
  38. package/dist/XPathTransformer.d.ts.map +1 -1
  39. package/dist/XPathTransformerContext.d.ts +281 -105
  40. package/dist/XPathTransformerContext.d.ts.map +1 -1
  41. package/dist/index-browser.d.ts +4 -0
  42. package/dist/index-browser.d.ts.map +1 -0
  43. package/dist/index-node.d.ts +4 -0
  44. package/dist/index-node.d.ts.map +1 -0
  45. package/dist/index.d.ts +330 -57
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/types.d.ts +204 -0
  48. package/dist/types.d.ts.map +1 -0
  49. package/docs/API.expanded.md +172 -5
  50. package/docs/API.md +92 -2
  51. package/docs/TO-DO.md +168 -0
  52. package/docs/calltemplate-params.md +251 -0
  53. package/eslint.config.js +11 -5
  54. package/package.json +35 -9
  55. package/pnpm-workspace.yaml +1 -0
  56. package/rollup.config.js +13 -0
  57. package/src/AbstractJoiningTransformer.js +54 -15
  58. package/src/DOMJoiningTransformer.js +275 -28
  59. package/src/JSONJoiningTransformer.js +351 -70
  60. package/src/JSONPathTransformer.js +48 -30
  61. package/src/JSONPathTransformerContext.js +729 -107
  62. package/src/StringJoiningTransformer.js +311 -57
  63. package/src/XPathTransformer.js +27 -12
  64. package/src/XPathTransformerContext.js +928 -99
  65. package/src/index-browser.js +5 -0
  66. package/src/index-node.js +7 -0
  67. package/src/index.js +502 -98
  68. package/tsconfig.json +5 -2
  69. package/typings/xpath2-js.d.ts +40 -1
  70. package/src/types/xpath2-js.d.ts +0 -2
@@ -0,0 +1,94 @@
1
+ import {javascript} from '@codemirror/lang-javascript';
2
+ import {EditorView, basicSetup} from 'codemirror';
3
+ import {xml} from '@codemirror/lang-xml';
4
+ import {json} from '@codemirror/lang-json';
5
+
6
+ /**
7
+ * @typedef {HTMLTextAreaElement & {
8
+ * $setValue: (insert: string) => void,
9
+ * $getValue: () => string
10
+ * }} EnhancedTextArea
11
+ */
12
+
13
+ /** @type {Map<HTMLTextAreaElement, EditorView>} */
14
+ const textareaToViewMap = new Map();
15
+
16
+ /**
17
+ * @param {HTMLTextAreaElement} textarea
18
+ * @param {any} extensions
19
+ * @param {(content: string) => void} inputHandler
20
+ * @returns {EditorView}
21
+ */
22
+ export function editorFromTextArea (textarea, extensions, inputHandler) {
23
+ /** @type {EditorView} */
24
+ let view = /** @type {EditorView} */ (textareaToViewMap.get(textarea));
25
+
26
+ /**
27
+ * @param {string} [val]
28
+ */
29
+ const createView = (val) => {
30
+ view = new EditorView({
31
+ doc: val ?? textarea.value,
32
+ extensions: [
33
+ basicSetup,
34
+ EditorView.updateListener.of((viewUpdate) => {
35
+ // Check if the document content has changed
36
+ if (viewUpdate.docChanged) {
37
+ const newContent = viewUpdate.state.doc.toString();
38
+ inputHandler(newContent);
39
+ // You can perform actions here based on the new content
40
+ }
41
+ }),
42
+ ...(extensions.javascript ? [javascript(extensions.javascript)] : []),
43
+ ...(extensions.xml ? [xml(extensions.xml)] : []),
44
+ ...(extensions.json ? [json()] : [])
45
+ ]
46
+ });
47
+
48
+ textareaToViewMap.set(textarea, view);
49
+ textarea.parentNode?.insertBefore(view.dom, textarea);
50
+ textarea.style.display = 'none';
51
+ };
52
+
53
+ if (textarea.previousElementSibling?.matches('.cm-editor')) {
54
+ const prevLang = /** @type {HTMLElement} */ (
55
+ textarea.previousElementSibling.querySelector('.cm-content')
56
+ ).dataset.language;
57
+ if (prevLang && prevLang in extensions) {
58
+ // console.log('same language');
59
+ return view;
60
+ }
61
+
62
+ const val = /** @type {EnhancedTextArea} */ (textarea).$getValue();
63
+ textarea.previousElementSibling.remove();
64
+ view.destroy();
65
+ createView(val);
66
+ // console.log('different language');
67
+ } else {
68
+ createView();
69
+ // console.log('new setup');
70
+ }
71
+
72
+ /** @type {EnhancedTextArea} */
73
+ (textarea).$setValue = (insert) => {
74
+ view.dispatch({
75
+ changes: {
76
+ from: 0,
77
+ to: view.state.doc.length,
78
+ insert
79
+ }
80
+ });
81
+ };
82
+
83
+ /** @type {EnhancedTextArea} */
84
+ (textarea).$getValue = () => {
85
+ return view.state.doc.toString();
86
+ };
87
+
88
+ if (textarea.form) {
89
+ textarea.form.addEventListener('submit', () => {
90
+ textarea.value = view.state.doc.toString();
91
+ });
92
+ }
93
+ return view;
94
+ }
package/demo/index.css ADDED
@@ -0,0 +1,7 @@
1
+ .cm-editor {
2
+ margin-top: 20px;
3
+ margin-bottom: 20px;
4
+ }
5
+ select {
6
+ min-width: 300px;
7
+ }
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <title>JTLT Demo</title>
6
+ <link rel="icon" href="data:image/png;base64,iVBORw0KGgo=" />
7
+ <link rel="stylesheet" href="index.css" />
8
+ <script type="importmap">
9
+ {
10
+ "imports": {
11
+ "simple-get-json": "./vendor/simple-get-json/dist/index-es.js",
12
+ "jhtml": "./vendor/jhtml/src/jhtml-browser.js",
13
+ "jamilih": "./vendor/jamilih/dist/jml.mjs",
14
+ "jsonpath-plus" :"./vendor/jsonpath-plus/dist/index-browser-esm.js",
15
+ "xpath2.js": "./xpath2-placeholder.js",
16
+ "fontoxpath": "../../node_modules/fontoxpath/dist/fontoxpath.esm.js",
17
+ "xspattern": "../../node_modules/xspattern/dist/xspattern.esm.js",
18
+ "prsc": "../../node_modules/prsc/dist/prsc.esm.js",
19
+ "whynot": "../../node_modules/whynot/dist/whynot.esm.js",
20
+ "./codemirror.js": "./codemirror.esm.js"
21
+ }
22
+ }
23
+ </script>
24
+ <script type="module" src="index.js"></script>
25
+ </head>
26
+ <body>
27
+ </body>
28
+ </html>
package/demo/index.js ADDED
@@ -0,0 +1,210 @@
1
+ import {nbsp, jml, body} from 'jamilih';
2
+ import {jtlt} from '../src/index-browser.js';
3
+ import {editorFromTextArea} from './codemirror.js';
4
+
5
+ /**
6
+ * @returns {Promise<void>}
7
+ */
8
+ async function templateProcessor () {
9
+ await processTemplates();
10
+ }
11
+ /**
12
+ * @param {string} sel
13
+ * @returns {HTMLInputElement}
14
+ */
15
+ const $i = (sel) => {
16
+ return /** @type {HTMLInputElement} */ (document.querySelector(sel));
17
+ };
18
+ /**
19
+ * @param {string} sel
20
+ * @returns {import('./codemirror.js').EnhancedTextArea}
21
+ */
22
+ const $t = (sel) => {
23
+ return /** @type {import('./codemirror.js').EnhancedTextArea} */ (
24
+ document.querySelector(sel)
25
+ );
26
+ };
27
+
28
+ /**
29
+ * @returns {Promise<void>}
30
+ */
31
+ async function processTemplates () {
32
+ $t('#output').value = '';
33
+ const source = $t('#source').$getValue();
34
+
35
+ let data;
36
+ let isJson = false;
37
+ data = new DOMParser().parseFromString(source, 'text/xml');
38
+
39
+ if (data.documentElement.localName === 'parsererror' &&
40
+ data.documentElement.namespaceURI ===
41
+ // eslint-disable-next-line sonarjs/no-clear-text-protocols -- Namespace
42
+ 'http://www.mozilla.org/newlayout/xml/parsererror.xml'
43
+ ) {
44
+ try {
45
+ data = JSON.parse(source);
46
+ editorFromTextArea($t('#source'), {
47
+ json: {}
48
+ }, templateProcessor);
49
+ } catch (err) {
50
+ $t('#output').value = 'Error parsing source as either XML or JSON\n\n' +
51
+ new XMLSerializer().serializeToString(data) + '\n\n' +
52
+ /** @type {Error} */ (err).message;
53
+ return;
54
+ }
55
+ isJson = true;
56
+ } else {
57
+ editorFromTextArea($t('#source'), {
58
+ xml: {}
59
+ }, templateProcessor);
60
+ }
61
+
62
+ let templates;
63
+ try {
64
+ // eslint-disable-next-line no-eval -- Todo: input to jsep?
65
+ templates = eval($t('#jtltTemplates').$getValue());
66
+ if (!templates || !templates.length) {
67
+ throw new Error('Bad templates');
68
+ }
69
+ } catch {
70
+ $t('#output').value =
71
+ 'Error parsing jtlt templates; must be an array of templates.';
72
+ return;
73
+ }
74
+
75
+ let result;
76
+
77
+ if ($i('#forQuery').checked) {
78
+ try {
79
+ result = isJson
80
+ ? await jtlt({
81
+ data,
82
+ engineType: 'jsonpath',
83
+ outputType: 'string',
84
+ forQuery: templates
85
+ })
86
+ : await jtlt({
87
+ data,
88
+ engineType: 'xpath',
89
+ xpathVersion: 3.1,
90
+ outputType: 'string',
91
+ forQuery: templates
92
+ });
93
+ } catch (err) {
94
+ $t('#output').value = 'Error executing jtlt()\n\n' +
95
+ /** @type {Error} */ (err).message;
96
+ return;
97
+ }
98
+ } else {
99
+ try {
100
+ result = isJson
101
+ ? await jtlt({
102
+ data,
103
+ engineType: 'jsonpath',
104
+ outputType: 'string',
105
+ templates
106
+ })
107
+ : await jtlt({
108
+ data,
109
+ engineType: 'xpath',
110
+ xpathVersion: 3.1,
111
+ outputType: 'string',
112
+ templates
113
+ });
114
+ } catch (err) {
115
+ $t('#output').value = 'Error executing jtlt()\n\n' +
116
+ /** @type {Error} */ (err).message;
117
+ return;
118
+ }
119
+ }
120
+
121
+ $t('#output').value = result;
122
+ }
123
+
124
+ jml('section', [
125
+ ['select', {$on: {
126
+ click () {
127
+ if (/** @type {HTMLSelectElement} */ (this).value === 'xml') {
128
+ $t('#source').$setValue(`<root></root>`);
129
+ $t('#jtltTemplates').$setValue($i('#forQuery').checked
130
+ ? `['//*', function () {
131
+ this.string('test123');
132
+ }]`
133
+ : `[
134
+ ['//*', function () {
135
+ this.string('test123');
136
+ }]
137
+ ]`);
138
+ } else if (/** @type {HTMLSelectElement} */ (this).value === 'json') {
139
+ $t('#source').$setValue(`{
140
+ "a": 5,
141
+ "b": {
142
+ "c": 7
143
+ }
144
+ }`);
145
+ $t('#jtltTemplates').$setValue($i('#forQuery').checked
146
+ ? `['$.b', function (o) {
147
+ this.string(o.c);
148
+ }]`
149
+ : `[
150
+ {
151
+ path: '$',
152
+ template () {
153
+ this.applyTemplates('$.b');
154
+ }
155
+ },
156
+ ['$.b', function (o) {
157
+ this.string(o.c);
158
+ }]
159
+ ]`);
160
+ }
161
+ }
162
+ }}, [
163
+ ['option', [
164
+ '(Populate...)'
165
+ ]],
166
+ ['option', {value: 'xml'}, [
167
+ 'XML/XPath example 1'
168
+ ]],
169
+ ['option', {value: 'json'}, [
170
+ 'JSON/JSONPath example 1'
171
+ ]]
172
+ ]],
173
+ nbsp.repeat(2),
174
+ ['label', [
175
+ ['input', {
176
+ id: 'forQuery', type: 'checkbox',
177
+ checked: Boolean(localStorage.getItem('forQuery')),
178
+ $on: {
179
+ async click (e) {
180
+ if (/** @type {HTMLInputElement} */ (e.target).checked) {
181
+ localStorage.setItem('forQuery', 'true');
182
+ } else {
183
+ localStorage.removeItem('forQuery');
184
+ }
185
+ await processTemplates();
186
+ }
187
+ }
188
+ }],
189
+ 'Use `forQuery` instead of templates'
190
+ ]],
191
+ ['br'], ['br'],
192
+ ['textarea', {
193
+ id: 'source',
194
+ placeholder: 'Put XML or JSON source here...'
195
+ }],
196
+
197
+ ['textarea', {
198
+ id: 'jtltTemplates',
199
+ placeholder: 'Put jtlt templates array here...'
200
+ }],
201
+
202
+ ['textarea', {id: 'output', placeholder: 'Loading...'}]
203
+ ], body);
204
+
205
+ editorFromTextArea($t('#jtltTemplates'), {
206
+ javascript: {typescript: true}
207
+ }, templateProcessor);
208
+ editorFromTextArea($t('#source'), {
209
+ xml: {}
210
+ }, templateProcessor);