jtlt 0.3.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 (40) hide show
  1. package/CHANGES.md +9 -0
  2. package/README.md +46 -78
  3. package/demo/codemirror.esm.js +28242 -0
  4. package/demo/codemirror.js +94 -0
  5. package/demo/index.css +7 -0
  6. package/demo/index.html +11 -14
  7. package/demo/index.js +206 -26
  8. package/demo/vendor/jamilih/dist/jml.mjs +2341 -0
  9. package/demo/vendor/jhtml/src/SAJJ/SAJJ.ObjectArrayDelegator.js +356 -0
  10. package/demo/vendor/jhtml/src/SAJJ/SAJJ.Stringifier.js +186 -0
  11. package/demo/vendor/jhtml/src/SAJJ/SAJJ.js +746 -0
  12. package/demo/vendor/jhtml/src/SAJJ/testing/SAJJ.html +33 -0
  13. package/demo/vendor/jhtml/src/SAJJ/testing/SAJJ.testing.js +25 -0
  14. package/demo/vendor/jhtml/src/jhtml-browser.js +5 -0
  15. package/demo/vendor/jhtml/src/jhtml-node.cts +3 -0
  16. package/demo/vendor/jhtml/src/jhtml-node.js +8 -0
  17. package/demo/vendor/jhtml/src/jhtml-node.mts +1 -0
  18. package/demo/vendor/jhtml/src/jhtml.cts +3 -0
  19. package/demo/vendor/jhtml/src/jhtml.js +602 -0
  20. package/demo/vendor/jhtml/src/jhtml.mts +1 -0
  21. package/demo/vendor/jsonpath-plus/dist/index-browser-esm.js +2158 -0
  22. package/demo/vendor/simple-get-json/dist/index-es.js +151 -0
  23. package/dist/JSONPathTransformerContext.d.ts +88 -4
  24. package/dist/JSONPathTransformerContext.d.ts.map +1 -1
  25. package/dist/XPathTransformer.d.ts +2 -2
  26. package/dist/XPathTransformerContext.d.ts +92 -8
  27. package/dist/XPathTransformerContext.d.ts.map +1 -1
  28. package/dist/index.d.ts +4 -4
  29. package/dist/index.d.ts.map +1 -1
  30. package/docs/API.expanded.md +5 -3
  31. package/docs/API.md +1 -1
  32. package/docs/TO-DO.md +55 -31
  33. package/eslint.config.js +3 -1
  34. package/package.json +24 -4
  35. package/rollup.config.js +13 -0
  36. package/src/JSONPathTransformerContext.js +422 -4
  37. package/src/XPathTransformer.js +1 -1
  38. package/src/XPathTransformerContext.js +462 -11
  39. package/src/index.js +9 -6
  40. package/tsconfig.json +5 -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
+ }
package/demo/index.html CHANGED
@@ -4,28 +4,25 @@
4
4
  <meta charset="utf-8" />
5
5
  <title>JTLT Demo</title>
6
6
  <link rel="icon" href="data:image/png;base64,iVBORw0KGgo=" />
7
- <link rel="stylesheet" href="../node_modules/mocha/mocha.css" />
7
+ <link rel="stylesheet" href="index.css" />
8
8
  <script type="importmap">
9
9
  {
10
10
  "imports": {
11
- "mocha": "../node_modules/mocha/mocha.js",
12
- "chai": "../node_modules/chai/index.js",
13
- "simple-get-json": "../node_modules/simple-get-json/dist/index-es.js",
14
- "jhtml": "../node_modules/jhtml/src/jhtml-browser.js",
15
- "jamilih": "../node_modules/jamilih/dist/jml.mjs",
16
- "jsonpath-plus" :"../node_modules/jsonpath-plus/dist/index-browser-esm.js",
17
- "xpath2.js": "./xpath2-placeholder.js"
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"
18
21
  }
19
22
  }
20
23
  </script>
21
- <script src="../node_modules/mocha/mocha.js"></script>
22
- <script>
23
- /* globals mocha -- Needs ESM */
24
- mocha.setup('bdd');
25
- </script>
26
24
  <script type="module" src="index.js"></script>
27
25
  </head>
28
26
  <body>
29
- <div id="mocha"></div>
30
27
  </body>
31
28
  </html>
package/demo/index.js CHANGED
@@ -1,30 +1,210 @@
1
- /* globals mocha, describe, it -- Should be ESM */
2
- import {expect} from 'chai';
3
-
1
+ import {nbsp, jml, body} from 'jamilih';
4
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
+ }
5
74
 
6
- describe('jtlt', () => {
7
- it('performs a string transformation', async () => {
8
- const result = await jtlt({
9
- data: new DOMParser().parseFromString(
10
- `<div type="questions-answers">
11
- <p n="1">Some text</p>
12
- <p n="2">More text</p>
13
- </div>`,
14
- 'text/xml'
15
- ),
16
- engineType: 'xpath',
17
- outputType: 'string',
18
- templates: [{
19
- path: '//*[@type="questions-answers"]/p',
20
- template (p) {
21
- this.valueOf('@n');
22
- this.valueOf('./text()');
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();
23
186
  }
24
- }]
25
- });
26
- expect(result).to.equal('1Some text2More text');
27
- });
28
- });
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);
29
204
 
30
- mocha.run();
205
+ editorFromTextArea($t('#jtltTemplates'), {
206
+ javascript: {typescript: true}
207
+ }, templateProcessor);
208
+ editorFromTextArea($t('#source'), {
209
+ xml: {}
210
+ }, templateProcessor);