jtsx-loader 0.1.2 → 0.1.3

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.
@@ -0,0 +1,17 @@
1
+ // @ts-nocheck
2
+ import fs from 'node:fs';
3
+
4
+ // TODO: типы для тайпскрипта
5
+ interface Props {
6
+ children: any
7
+ lang: string
8
+ }
9
+
10
+ export default ({ children, renderCount, lang }: Props) => {
11
+ const { version } = JSON.parse(fs.readFileSync('package.json').toString());
12
+
13
+ return <div class="lang">
14
+ <h1>JTSX Loader <small>v{version}</small></h1>
15
+ <a href="/ru" class={lang === 'ru' && 'is-active'}>Russian</a> / <a href="/" class={lang === 'en' && 'is-active'}>English</a>
16
+ </div>
17
+ }
@@ -1,5 +1,6 @@
1
1
  import ComponentInner from '../components/Inner.tsx';
2
2
  import Layout from '../layouts/Layout.jsx';
3
+ import Header from '../components/Header.tsx';
3
4
 
4
5
  // const requestOnImport = await fetch(process.env.URL + '/api/401.json').then(r => r.json()).catch(e => e.message);
5
6
  // console.log('Build time request when component imported', requestOnImport);
@@ -10,153 +11,152 @@ export default async ({ data }) => {
10
11
  const inlineRequest = await fetch(process.env.URL + '/api/200.json').then(r => r.json()).catch(e => e.message);
11
12
 
12
13
  return <Layout title="JTSX Loader">
13
- <script __raw={`window.addEventListener('load', () => {
14
+ <script __raw={ `window.addEventListener('load', () => {
14
15
  console.log('LOADED INLINE');
15
16
  });`}>
16
17
  </script>
17
18
 
18
- <script __raw={`
19
+ <script __raw={ `
19
20
  window.addEventListener('load', () => {
20
21
  console.log('Self Closing INLINE');
21
22
  })
22
- `}/>
23
+ `} />
23
24
 
24
25
  <section>
25
- <div class="lang">
26
- <h1>JTSX Loader <small>v0.1.0</small></h1>
27
- <a href="/" class="is-active">Russian</a> / <a href="/en">English</a>
28
- </div>
26
+ <Header lang="en" />
29
27
 
30
- <h5>* Документация в стадии разработки</h5>
28
+ <h5>* Documentation under development</h5>
31
29
 
32
- <p><b>J</b>avaScript <b>T</b>ype<b>S</b>cript <b>X</b>ml — загрузчик <code>.jsx</code> и <code>.tsx</code> файлов для <b>Node.js</b></p>
33
- <p>Позволяет писать <b>JTSX</b> код и компилировать в нативный HTML</p>
34
- <p>Цель разработки данного загрузчика в том, чтобы решить проблему комфортного использования JSX как шаблонизатора, без привязки к React</p>
35
- <p>Транспиляция JTSX файлов происходит с помощью <a href="https://esbuild.github.io/" target="_blank">esbuild</a> - надежный и очень быстрый транспайлер</p>
36
- <p>Всего одна зависимость — esbuild</p>
37
- <p>Для конвертации используется <code>jsxFactory</code> функция, которая преобразует теги, аттрибуты и дочерние элементы в стандартный HTML</p>
38
- <p>Загрузчик позволяет расширить преобразования JTSX файлов через конфигурации и самостоятельно написанные функции</p>
39
- <p>Так же загрузчик позволяет предотвратить кеширование при импорте компонентов, что дает возможность комфортно использовать перезагрузку страницы при изменениях и не перезапускать сервер, которые рендерит компонент</p>
40
- <p>ВАЖНО: Данный загрузчик это НЕ замена React, Vue и их производных. Если вам нужна реактивность и толстые клиенты, то лучше использовать соответствующие инструменты</p>
30
+ <p><b>J</b>avaScript <b>T</b>ype<b>S</b>cript <b>X</b>ml — <code>.jsx</code> and <code>.tsx</code> file loader for <b>Node.js</b></p>
31
+ <p>Allows you to write <b>JTSX</b> code and compile it into native HTML</p>
32
+ <p>The purpose of developing this loader is to solve the problem of comfortable use of JSX as a template engine, without being tied to React</p>
33
+ <p>JTSX files are transpiled using <a href="https://esbuild.github.io/" target="_blank">esbuild</a> a reliable and very fast transpiler</p>
34
+ <p>Only one dependency — esbuild</p>
35
+ <p>For conversion, the <code>jsxFactory</code> function is used, which converts tag attributes, attributes and child elements into standard HTML</p>
36
+ <p>The loader allows you to extend the transformation of JTSX files through configurations and self-written functions</p>
37
+ <p>The loader also allows you to prevent caching when importing components, which makes it possible to comfortably use page reloading when changes are made and not restart the server that renders the component</p>
38
+ <p>IMPORTANT: This project is NOT a replacement for React, Vue and their derivatives. If you need reactivity and thick clients, it is better to use the appropriate tools</p>
41
39
 
42
40
  <p><a href="https://github.com/dergachevm/jtsx-loader">Github</a></p>
41
+ <p><a href="https://github.com/dergachevm/jtsx-loader/blob/master/jtsx.config.example.js">Config file description</a></p>
43
42
  </section>
44
43
 
45
44
  <section>
46
- <h2>Установка, использование, требования</h2>
47
- <h3>Установка</h3>
45
+ <h2>Installation, usage, requirements</h2>
46
+ <h3>Installation</h3>
48
47
  <pre>npm install jtsx-loader</pre>
49
48
 
50
- <h3>Использование</h3>
51
- <p>Проекты необходимо запускать через команду --import</p>
49
+ <h3>Usage</h3>
50
+ <p>Projects must be run via the --import command</p>
52
51
  <pre>node --import jtsx-loader ./server.js</pre>
53
52
 
54
- <h3>Как отрендерить файл</h3>
55
- <p>Напишем простой компонент страницы index.jsx:</p>
53
+ <h3>How to render a file</h3>
54
+ <p>Let's write a simple index.jsx page component:</p>
56
55
  <pre __escape={`
57
- export default async ({ title }) => <html lang="ru">
56
+ export default async ({ title }) => <html lang="en">
58
57
  <head>
59
58
  <title>{title}</title>
60
59
  </head>
61
60
  <body>
62
61
  <h1>{title}</h1>
63
62
  <p>
64
- Body content with array:
63
+ Body content with array:
65
64
  {[0, 1, 2].map(n => <code>{n}</code>)}
66
65
  </p>
67
66
  </body>
68
67
  </html>;
69
68
  `}></pre>
70
- <p>Импортировать компонент через <code>import()</code>, ?reload параметр опционален, используется для предотвращения кеширования</p>
69
+ <p>Import the component via <code>import()</code>, the ?reload parameter is optional, used to prevent caching</p>
71
70
  <pre>
72
71
  const JSXComp = (await import('./index.jsx?reload')).default;
73
72
  </pre>
74
- <p>Вызвать импортированный компонент как функцию передав объект данных и вывести результат в консоль</p>
73
+ <p>Call the imported component as a function passing the data object and output the result to the console</p>
75
74
  <pre>
76
75
  {`const rendered = await JSXComp({ data: renderCount });\nconsole.log(rendered);`}
77
76
  </pre>
78
77
 
79
- <p>Подробный пример с сохранением результат в файл <a href="https://github.com/dergachevm/jtsx-loader/tree/master/example">здесь</a></p>
78
+ <p>A detailed example with saving the result to a file <a href="https://github.com/dergachevm/jtsx-loader/tree/master/example">here</a></p>
80
79
 
81
- <h3>Требования</h3>
82
- <p>Минимальная версия Node.js - 20.16.0</p>
80
+ <h3>Requirements</h3>
81
+ <p>Minimum Node.js version - 20.16.0</p>
83
82
  </section>
84
83
 
85
84
  <section>
86
- <h2>Базовые примеры</h2>
85
+ <h2>Basic Examples</h2>
87
86
 
88
87
  <div class="examples">
89
88
  <div class="item">
90
- <h3>Нативные HTML аттрибуты</h3>
91
- <p>Допускается использование только нативных HTML аттрибутов</p>
89
+ <h3>Native HTML Attributes</h3>
90
+ <p>Only native HTML attributes are allowed</p>
92
91
 
93
- <p>React аттрибуты и инлайн функции в тегах не поддерживаются по дизайну и никогда не должны использоваться</p>
92
+ <p>React attributes are not supported by design and should never be used</p>
94
93
 
95
- <p случае использования React аттрибутов, как например <code>className</code>,<br />
96
- в консоль будет выведено предупреждение, а аттрибут отрендерится как есть</p>
94
+ <p>If you use React attributes, such as <code>className</code>,<br />
95
+ a warning will be printed to the console, and the attribute will be rendered as is</p>
97
96
 
98
- <p>Другие отличия от стандартного JSX:</p>
97
+ <p>Other differences from standard JSX:</p>
99
98
 
100
99
  <ul>
101
- <li>Аттрибут <code>__escape</code>: используется для вывода любого экранированного контента</li>
102
- <li>Аттрибут <code>__raw</code>: рендерит строку в содержимое тега как есть, например полезно для тегов <code>script</code> и <code>style</code></li>
103
- <li JTSX файл передается объект <code>_jsxUtils</code>, которая содержит в себе полезные утилиты. Вместо данного объекта можно передать свою любую функцию и использовать ее для расширения стандартного преобразования</li>
100
+ <li><code>__escape</code> attribute: used to output any escaped content</li>
101
+ <li><code>__raw</code> attribute: renders a string into the tag content as is, for example useful for <code>script</code> tags and <code>style</code></li>
102
+ <li>The JTSX file is passed the <code>_jsxUtils</code> object, which contains useful utilities. Instead of this object, you can pass any function you want and use it to extend the standard transformation</li>
104
103
  </ul>
105
104
  </div>
106
105
 
107
106
  <div class="item">
108
- <h3>Доступны все возможности JavaScript, которые используются в обычном JSX</h3>
109
- <p>ВАЖНО: весь код выполняется в среде Node.js, поэтому здесь нет браузерного DOM</p>
110
- <h4>Вычисления</h4>
111
- <pre __escape={'{(1 + 1 + 2 * 2) / 3}'}></pre>
112
- <p>Результат: <code>{(1 + 1 + 2 * 2) / 3}</code></p>
113
-
114
- <h4>Стандартная библиотека Node.js</h4>
115
- <pre __escape={'{Math.pow(9, 2)}'}></pre>
116
- <p>Результат: <code>{Math.pow(9, 2)}</code></p>
117
-
118
- <h4>Тернарные выражения</h4>
119
- <pre __escape={`1 > 2 ? <strong>Больше</strong> : 'Меньше'`}></pre>
120
- <p>Результат: <code>{1 > 2 ? <strong>Больше</strong> : 'Меньше'}</code></p>
121
- <pre __escape={`2 > 1 ? <strong>Больше</strong> : 'Меньше'`}></pre>
122
- <p>Результат: <code>{2 > 1 ? <strong>Больше</strong> : 'Меньше'}</code></p>
123
-
124
- <h4>Манипуляции c массивами и рендеринг компонентов</h4>
125
- <p>Рендерит компоненты/теги с данными из массива</p>
126
- <pre __escape={'[0, 1, 2].map(num => <code>{num}</code>)'}></pre>
127
- <p>Результат: {[0, 1, 2].map(el => <code>{el}</code>)}</p>
107
+ <h3>All JavaScript capabilities that are used in regular JSX are available</h3>
108
+ <p>IMPORTANT: all code is executed in the Node.js environment, so there is no browser DOM</p>
109
+ <h4>Calculations</h4>
110
+ <pre __escape={ '{(1 + 1 + 2 * 2) / 3}' }></pre>
111
+ <p>Result: <code>{ (1 + 1 + 2 * 2) / 3 }</code></p>
112
+
113
+ <h4>Node.js Standard Library</h4>
114
+ <pre __escape={ '{Math.pow(9, 2)}' }></pre>
115
+ <p>Result: <code>{ Math.pow(9, 2) }</code></p>
116
+
117
+ <h4>Ternary expressions</h4>
118
+ <pre __escape={ `1 > 2 ? <strong>Greater</strong> : 'Less'` }></pre>
119
+ <p>Result: <code>{ 1 > 2 ? <strong>Greater</strong> : 'Less' }</code></p>
120
+ <pre __escape={ `2 > 1 ? <strong>Greater</strong> : 'Less'` }></pre>
121
+ <p>Result: <code>{ 2 > 1 ? <strong>Greater</strong> : 'Less' }</code></p>
122
+
123
+ <h4>Array manipulation and component rendering</h4>
124
+ <p>Renders components/tags with array data</p>
125
+ <pre __escape={ '[0, 1, 2].map(num => <code>{num}</code>)' }></pre>
126
+ <p>Result: { [0, 1, 2].map(el => <code>{ el }</code>) }</p>
128
127
 
129
128
  <h4>__escape</h4>
130
- <p>Вывод любого экранированного контента</p>
131
- <pre __escape={'<code __escape={\'<div>escaped</div>\'}></code>'}></pre>
132
- <p>Результат: <code __escape={'<div>escaped</div>'}></code></p>
129
+ <p>Output any escaped content</p>
130
+ <pre __escape={ '<code __escape={\'<div>escaped</div>\'}></code>' }></pre>
131
+ <p>Result: <code __escape={ '<div>escaped</div>' }></code></p>
133
132
  </div>
134
133
 
135
134
  <div class="item">
136
- <h3>Запрос внутри компонента через <code>fetch</code></h3>
137
- <p><code __escape={`const inlineRequest = await fetch(process.env.URL + '/api/200.json').then(r => r.json()).catch(e => e.message);`}></code></p>
138
- <p>Вызывается при каждом рендеринге</p>
139
- <p>Если вызывается внутренний сервер, то он должен быть запущен до запуска рендеринга</p>
135
+ <h3>Request inside the component via <code>fetch</code></h3>
136
+ <p><code __escape={ `const inlineRequest = await fetch(process.env.URL + '/api/200.json').then(r => r.json()).catch(e => e.message);` }></code></p>
137
+ <p>Called on every render</p>
138
+ <p>If the internal server is called, it must be running before the render starts</p>
140
139
  <pre>
141
- Ответ: {JSON.stringify(inlineRequest, null, 4)}
140
+ Response: { JSON.stringify(inlineRequest, null, 4) }
142
141
  </pre>
143
142
  </div>
144
143
 
145
144
  <div class="item">
146
- <h3>Импортированный компонент</h3>
145
+ <h3>Imported component</h3>
147
146
  <p></p>
148
147
  <div class="component-container">
149
- <ComponentInner renderCount={data} >
150
- <p><i special="CHILD 0">Дочерний компонент 0</i></p>
151
- <p><i special="CHILD 1">Дочерний компонент 1</i></p>
148
+ <ComponentInner lang="en" renderCount={ data } >
149
+ <p><i special="CHILD 0">Child component 0</i></p>
150
+ <p><i special="CHILD 1">Child component 1</i></p>
152
151
  </ComponentInner>
153
152
  </div>
154
153
  </div>
155
154
 
156
155
  <div class="item">
157
- <h3>Пользовательские аттрибуты и их обработка</h3>
158
- <p><code>{_jsxUtils.escapeHtml(`<input type="text" ac:custom="input" />`)}</code> трансформируется в <code>data-ac-custom="input"</code></p>
159
- <p>Обработчик пишется в jtsx.config.js</p>
156
+ <h3>Custom attributes and their processing</h3>
157
+ <p>{ _jsxUtils.escapeHtml(`<input type="text" ac:custom="input"`) } is transformed into <code>data-ac-custom="input"</code></p>
158
+ <p>The handler is written in jtsx.config.js</p>
159
+
160
160
  <div>
161
161
  <input type="text" ac:custom="input" value="ac:custom='input'" />
162
162
  </div>
@@ -164,24 +164,17 @@ export default async ({ title }) => <html lang="ru">
164
164
  </div>
165
165
  </section>
166
166
 
167
-
168
167
  <section>
169
- <h2>Авторы</h2>
170
- <p><a href="mailto:dergachev.mihail@gmail.com">Дергачев Михаил</a></p>
171
- <p><a href="https://ancros.dev">https://ancros.dev</a></p>
168
+ <h2>Authors</h2>
169
+ <p><a href="mailto:dergachev.mihail@gmail.com">Dergachev Mikhail</a></p>
170
+ <p><a href="https: //ancros.dev">https://ancros.dev</a></p>
172
171
  </section>
173
-
172
+
174
173
  <section>
175
- <h2>Референсы</h2>
176
- <p><a href="https://esbuild.github.io/api/#jsx-factory">https://esbuild.github.io/api/#jsx-factory</a></p>
177
- <p><a href="https://lwebapp.com/en/post/custom-jsx">https://lwebapp.com/en/post/custom-jsx</a></p>
174
+ <h2>References</h2>
175
+ <p><a href="https://esbuild.github.io/api/#jsx-factory">https://esbuild.github.io/api/# jsx-factory</a></p>
176
+ <p><a href="https://lwebapp.com/en/post/custom-jsx">https://lwebapp.com/en/post/custom- jsx</a></p>
178
177
  <p><a href="https://nakedjsx.org/">https://nakedjsx.org/</a></p>
179
178
  </section>
180
-
181
- {/*
182
- Inline functions not allowed
183
- <form action="/api" onSubmit={async () => {
184
- await console.log('ONSUBMIT');
185
- }} novalidate></form> */}
186
179
  </Layout>
187
180
  }
@@ -0,0 +1,187 @@
1
+ import ComponentInner from '../components/Inner.tsx';
2
+ import Header from '../components/Header.tsx';
3
+ import Layout from '../layouts/Layout.jsx';
4
+ import fs from 'node:fs';
5
+
6
+ // const requestOnImport = await fetch(process.env.URL + '/api/401.json').then(r => r.json()).catch(e => e.message);
7
+ // console.log('Build time request when component imported', requestOnImport);
8
+
9
+ export default async ({ data }) => {
10
+ // inline server request example
11
+ // possible only if current server already running
12
+ const inlineRequest = await fetch(process.env.URL + '/api/200.json').then(r => r.json()).catch(e => e.message);
13
+
14
+ return <Layout title="JTSX Loader">
15
+ <script __raw={`window.addEventListener('load', () => {
16
+ console.log('LOADED INLINE');
17
+ });`}>
18
+ </script>
19
+
20
+ <script __raw={`
21
+ window.addEventListener('load', () => {
22
+ console.log('Self Closing INLINE');
23
+ })
24
+ `}/>
25
+
26
+ <section>
27
+ <Header lang="ru" />
28
+
29
+ <h5>* Документация в стадии разработки</h5>
30
+
31
+ <p><b>J</b>avaScript <b>T</b>ype<b>S</b>cript <b>X</b>ml — загрузчик <code>.jsx</code> и <code>.tsx</code> файлов для <b>Node.js</b></p>
32
+ <p>Позволяет писать <b>JTSX</b> код и компилировать в нативный HTML</p>
33
+ <p>Цель разработки данного загрузчика в том, чтобы решить проблему комфортного использования JSX как шаблонизатора, без привязки к React</p>
34
+ <p>Транспиляция JTSX файлов происходит с помощью <a href="https://esbuild.github.io/" target="_blank">esbuild</a> - надежный и очень быстрый транспайлер</p>
35
+ <p>Всего одна зависимость — esbuild</p>
36
+ <p>Для конвертации используется <code>jsxFactory</code> функция, которая преобразует теги, аттрибуты и дочерние элементы в стандартный HTML</p>
37
+ <p>Загрузчик позволяет расширить преобразования JTSX файлов через конфигурации и самостоятельно написанные функции</p>
38
+ <p>Так же загрузчик позволяет предотвратить кеширование при импорте компонентов, что дает возможность комфортно использовать перезагрузку страницы при изменениях и не перезапускать сервер, которые рендерит компонент</p>
39
+ <p>ВАЖНО: Данный загрузчик это НЕ замена React, Vue и их производных. Если вам нужна реактивность и толстые клиенты, то лучше использовать соответствующие инструменты</p>
40
+
41
+ <p><a href="https://github.com/dergachevm/jtsx-loader">Github</a></p>
42
+ <p><a href="https://github.com/dergachevm/jtsx-loader/blob/master/jtsx.config.example.js">Описание файла конфигурации</a></p>
43
+ </section>
44
+
45
+ <section>
46
+ <h2>Установка, использование, требования</h2>
47
+ <h3>Установка</h3>
48
+ <pre>npm install jtsx-loader</pre>
49
+
50
+ <h3>Использование</h3>
51
+ <p>Проекты необходимо запускать через команду --import</p>
52
+ <pre>node --import jtsx-loader ./server.js</pre>
53
+
54
+ <h3>Как отрендерить файл</h3>
55
+ <p>Напишем простой компонент страницы index.jsx:</p>
56
+ <pre __escape={`
57
+ export default async ({ title }) => <html lang="ru">
58
+ <head>
59
+ <title>{title}</title>
60
+ </head>
61
+ <body>
62
+ <h1>{title}</h1>
63
+ <p>
64
+ Body content with array:
65
+ {[0, 1, 2].map(n => <code>{n}</code>)}
66
+ </p>
67
+ </body>
68
+ </html>;
69
+ `}></pre>
70
+ <p>Импортировать компонент через <code>import()</code>, ?reload параметр опционален, используется для предотвращения кеширования</p>
71
+ <pre>
72
+ const JSXComp = (await import('./index.jsx?reload')).default;
73
+ </pre>
74
+ <p>Вызвать импортированный компонент как функцию передав объект данных и вывести результат в консоль</p>
75
+ <pre>
76
+ {`const rendered = await JSXComp({ data: renderCount });\nconsole.log(rendered);`}
77
+ </pre>
78
+
79
+ <p>Подробный пример с сохранением результат в файл <a href="https://github.com/dergachevm/jtsx-loader/tree/master/example">здесь</a></p>
80
+
81
+ <h3>Требования</h3>
82
+ <p>Минимальная версия Node.js - 20.16.0</p>
83
+ </section>
84
+
85
+ <section>
86
+ <h2>Базовые примеры</h2>
87
+
88
+ <div class="examples">
89
+ <div class="item">
90
+ <h3>Нативные HTML аттрибуты</h3>
91
+ <p>Допускается использование только нативных HTML аттрибутов</p>
92
+
93
+ <p>React аттрибуты и инлайн функции в тегах не поддерживаются по дизайну и никогда не должны использоваться</p>
94
+
95
+ <p>В случае использования React аттрибутов, как например <code>className</code>,<br />
96
+ в консоль будет выведено предупреждение, а аттрибут отрендерится как есть</p>
97
+
98
+ <p>Другие отличия от стандартного JSX:</p>
99
+
100
+ <ul>
101
+ <li>Аттрибут <code>__escape</code>: используется для вывода любого экранированного контента</li>
102
+ <li>Аттрибут <code>__raw</code>: рендерит строку в содержимое тега как есть, например полезно для тегов <code>script</code> и <code>style</code></li>
103
+ <li>В JTSX файл передается объект <code>_jsxUtils</code>, которая содержит в себе полезные утилиты. Вместо данного объекта можно передать свою любую функцию и использовать ее для расширения стандартного преобразования</li>
104
+ </ul>
105
+ </div>
106
+
107
+ <div class="item">
108
+ <h3>Доступны все возможности JavaScript, которые используются в обычном JSX</h3>
109
+ <p>ВАЖНО: весь код выполняется в среде Node.js, поэтому здесь нет браузерного DOM</p>
110
+ <h4>Вычисления</h4>
111
+ <pre __escape={'{(1 + 1 + 2 * 2) / 3}'}></pre>
112
+ <p>Результат: <code>{(1 + 1 + 2 * 2) / 3}</code></p>
113
+
114
+ <h4>Стандартная библиотека Node.js</h4>
115
+ <pre __escape={'{Math.pow(9, 2)}'}></pre>
116
+ <p>Результат: <code>{Math.pow(9, 2)}</code></p>
117
+
118
+ <h4>Тернарные выражения</h4>
119
+ <pre __escape={`1 > 2 ? <strong>Больше</strong> : 'Меньше'`}></pre>
120
+ <p>Результат: <code>{1 > 2 ? <strong>Больше</strong> : 'Меньше'}</code></p>
121
+ <pre __escape={`2 > 1 ? <strong>Больше</strong> : 'Меньше'`}></pre>
122
+ <p>Результат: <code>{2 > 1 ? <strong>Больше</strong> : 'Меньше'}</code></p>
123
+
124
+ <h4>Манипуляции c массивами и рендеринг компонентов</h4>
125
+ <p>Рендерит компоненты/теги с данными из массива</p>
126
+ <pre __escape={'[0, 1, 2].map(num => <code>{num}</code>)'}></pre>
127
+ <p>Результат: {[0, 1, 2].map(el => <code>{el}</code>)}</p>
128
+
129
+ <h4>__escape</h4>
130
+ <p>Вывод любого экранированного контента</p>
131
+ <pre __escape={'<code __escape={\'<div>escaped</div>\'}></code>'}></pre>
132
+ <p>Результат: <code __escape={'<div>escaped</div>'}></code></p>
133
+ </div>
134
+
135
+ <div class="item">
136
+ <h3>Запрос внутри компонента через <code>fetch</code></h3>
137
+ <p><code __escape={`const inlineRequest = await fetch(process.env.URL + '/api/200.json').then(r => r.json()).catch(e => e.message);`}></code></p>
138
+ <p>Вызывается при каждом рендеринге</p>
139
+ <p>Если вызывается внутренний сервер, то он должен быть запущен до запуска рендеринга</p>
140
+ <pre>
141
+ Ответ: {JSON.stringify(inlineRequest, null, 4)}
142
+ </pre>
143
+ </div>
144
+
145
+ <div class="item">
146
+ <h3>Импортированный компонент</h3>
147
+ <p></p>
148
+ <div class="component-container">
149
+ <ComponentInner renderCount={data} >
150
+ <p><i special="CHILD 0">Дочерний компонент 0</i></p>
151
+ <p><i special="CHILD 1">Дочерний компонент 1</i></p>
152
+ </ComponentInner>
153
+ </div>
154
+ </div>
155
+
156
+ <div class="item">
157
+ <h3>Пользовательские аттрибуты и их обработка</h3>
158
+ <p><code>{_jsxUtils.escapeHtml(`<input type="text" ac:custom="input" />`)}</code> трансформируется в <code>data-ac-custom="input"</code></p>
159
+ <p>Обработчик пишется в jtsx.config.js</p>
160
+ <div>
161
+ <input type="text" ac:custom="input" value="ac:custom='input'" />
162
+ </div>
163
+ </div>
164
+ </div>
165
+ </section>
166
+
167
+
168
+ <section>
169
+ <h2>Авторы</h2>
170
+ <p><a href="mailto:dergachev.mihail@gmail.com">Дергачев Михаил</a></p>
171
+ <p><a href="https://ancros.dev">https://ancros.dev</a></p>
172
+ </section>
173
+
174
+ <section>
175
+ <h2>Референсы</h2>
176
+ <p><a href="https://esbuild.github.io/api/#jsx-factory">https://esbuild.github.io/api/#jsx-factory</a></p>
177
+ <p><a href="https://lwebapp.com/en/post/custom-jsx">https://lwebapp.com/en/post/custom-jsx</a></p>
178
+ <p><a href="https://nakedjsx.org/">https://nakedjsx.org/</a></p>
179
+ </section>
180
+
181
+ {/*
182
+ Inline functions not allowed
183
+ <form action="/api" onSubmit={async () => {
184
+ await console.log('ONSUBMIT');
185
+ }} novalidate></form> */}
186
+ </Layout>
187
+ }
package/example/server.js CHANGED
@@ -58,6 +58,7 @@ router.get('/', async (req, res) => {
58
58
  });
59
59
 
60
60
  router.get('/:page', async (req, res) => {
61
+ if (req.params.page.includes('.')) return res.status(404);
61
62
  renderCount++;
62
63
  return res.send(await render(req.params.page));
63
64
  });
@@ -228,4 +228,25 @@ main {
228
228
  border-top: none;
229
229
  border-bottom: none;
230
230
  border-radius: 8px;
231
+ }
232
+
233
+ @media screen and (max-width: 768px) {
234
+ body {
235
+ padding: 20px;
236
+ }
237
+
238
+ main {
239
+ /* overflow-x: hidden; */
240
+ }
241
+
242
+ pre {
243
+ display: block;
244
+ overflow-x: auto;
245
+ max-width: 90vw;
246
+ padding: 20px;
247
+ }
248
+
249
+ h5 {
250
+ margin-top: 20px;
251
+ }
231
252
  }
@@ -0,0 +1,114 @@
1
+ /***
2
+ The new CSS reset - version 1.11.2 (last updated 15.11.2023)
3
+ GitHub page: https://github.com/elad2412/the-new-css-reset
4
+ ***/
5
+
6
+ /*
7
+ Remove all the styles of the "User-Agent-Stylesheet", except for the 'display' property
8
+ - The "symbol *" part is to solve Firefox SVG sprite bug
9
+ - The "html" element is excluded, otherwise a bug in Chrome breaks the CSS hyphens property (https://github.com/elad2412/the-new-css-reset/issues/36)
10
+ */
11
+ *:where(:not(html, iframe, canvas, img, svg, video, audio):not(svg *, symbol *)) {
12
+ all: unset;
13
+ display: revert;
14
+ }
15
+
16
+ /* Preferred box-sizing value */
17
+ *,
18
+ *::before,
19
+ *::after {
20
+ box-sizing: border-box;
21
+ }
22
+
23
+ /* Fix mobile Safari increase font-size on landscape mode */
24
+ html {
25
+ -moz-text-size-adjust: none;
26
+ -webkit-text-size-adjust: none;
27
+ text-size-adjust: none;
28
+ }
29
+
30
+ /* Reapply the pointer cursor for anchor tags */
31
+ a,
32
+ button {
33
+ cursor: revert;
34
+ }
35
+
36
+ /* Remove list styles (bullets/numbers) */
37
+ ol,
38
+ ul,
39
+ menu,
40
+ summary {
41
+ list-style: none;
42
+ }
43
+
44
+ /* For images to not be able to exceed their container */
45
+ img {
46
+ max-inline-size: 100%;
47
+ max-block-size: 100%;
48
+ }
49
+
50
+ /* removes spacing between cells in tables */
51
+ table {
52
+ border-collapse: collapse;
53
+ }
54
+
55
+ /* Safari - solving issue when using user-select:none on the <body> text input doesn't working */
56
+ input,
57
+ textarea {
58
+ -webkit-user-select: auto;
59
+ }
60
+
61
+ /* revert the 'white-space' property for textarea elements on Safari */
62
+ textarea {
63
+ white-space: revert;
64
+ }
65
+
66
+ /* minimum style to allow to style meter element */
67
+ meter {
68
+ -webkit-appearance: revert;
69
+ appearance: revert;
70
+ }
71
+
72
+ /* preformatted text - use only for this feature */
73
+ :where(pre) {
74
+ all: revert;
75
+ box-sizing: border-box;
76
+ }
77
+
78
+ /* reset default text opacity of input placeholder */
79
+ ::placeholder {
80
+ color: unset;
81
+ }
82
+
83
+ /* fix the feature of 'hidden' attribute.
84
+ display:revert; revert to element instead of attribute */
85
+ :where([hidden]) {
86
+ display: none;
87
+ }
88
+
89
+ /* revert for bug in Chromium browsers
90
+ - fix for the content editable attribute will work properly.
91
+ - webkit-user-select: auto; added for Safari in case of using user-select:none on wrapper element*/
92
+ :where([contenteditable]:not([contenteditable="false"])) {
93
+ -moz-user-modify: read-write;
94
+ -webkit-user-modify: read-write;
95
+ overflow-wrap: break-word;
96
+ -webkit-line-break: after-white-space;
97
+ -webkit-user-select: auto;
98
+ }
99
+
100
+ /* apply back the draggable feature - exist only in Chromium and Safari */
101
+ :where([draggable="true"]) {
102
+ -webkit-user-drag: element;
103
+ }
104
+
105
+ /* Revert Modal native behavior */
106
+ :where(dialog:modal) {
107
+ all: revert;
108
+ box-sizing: border-box;
109
+ }
110
+
111
+ /* Remove details summary webkit styles */
112
+ ::-webkit-details-marker {
113
+ display: none;
114
+ }
@@ -1 +1,2 @@
1
+ @import url('./reset.css');
1
2
  @import url('./default.css');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jtsx-loader",
3
- "version": "0.1.2",
3
+ "version": "0.1.3",
4
4
  "main": "./loader/register.mjs",
5
5
  "type": "module",
6
6
  "imports": {
@@ -1,181 +0,0 @@
1
- import ComponentInner from '../components/Inner.tsx';
2
- import Layout from '../layouts/Layout.jsx';
3
-
4
- // const requestOnImport = await fetch(process.env.URL + '/api/401.json').then(r => r.json()).catch(e => e.message);
5
- // console.log('Build time request when component imported', requestOnImport);
6
-
7
- export default async ({ data }) => {
8
- // inline server request example
9
- // possible only if current server already running
10
- const inlineRequest = await fetch(process.env.URL + '/api/200.json').then(r => r.json()).catch(e => e.message);
11
-
12
- return <Layout title="JTSX Loader">
13
- <script __raw={ `window.addEventListener('load', () => {
14
- console.log('LOADED INLINE');
15
- });`}>
16
- </script>
17
-
18
- <script __raw={ `
19
- window.addEventListener('load', () => {
20
- console.log('Self Closing INLINE');
21
- })
22
- `} />
23
-
24
- <section>
25
- <div class="lang">
26
- <h1>JTSX Loader <small>v0.1.0</small></h1>
27
- <a href="/">Russian</a> / <a href="/en" class="is-active">English</a>
28
- </div>
29
-
30
- <h5>* Documentation under development</h5>
31
-
32
- <p><b>J</b>avaScript <b>T</b>ype<b>S</b>cript <b>X</b>ml — <code>.jsx</code> and <code>.tsx</code> file loader for <b>Node.js</b></p>
33
- <p>Allows you to write <b>JTSX</b> code and compile it into native HTML</p>
34
- <p>The purpose of developing this loader is to solve the problem of comfortable use of JSX as a template engine, without being tied to React</p>
35
- <p>JTSX files are transpiled using <a href="https://esbuild.github.io/" target="_blank">esbuild</a> — a reliable and very fast transpiler</p>
36
- <p>Only one dependency — esbuild</p>
37
- <p>For conversion, the <code>jsxFactory</code> function is used, which converts tag attributes, attributes and child elements into standard HTML</p>
38
- <p>The loader allows you to extend the transformation of JTSX files through configurations and self-written functions</p>
39
- <p>The loader also allows you to prevent caching when importing components, which makes it possible to comfortably use page reloading when changes are made and not restart the server that renders the component</p>
40
- <p>IMPORTANT: This project is NOT a replacement for React, Vue and their derivatives. If you need reactivity and thick clients, it is better to use the appropriate tools</p>
41
-
42
- <p><a href="https://github.com/dergachevm/jtsx-loader">Github</a></p>
43
- </section>
44
-
45
- <section>
46
- <h2>Installation, usage, requirements</h2>
47
- <h3>Installation</h3>
48
- <pre>npm install jtsx-loader</pre>
49
-
50
- <h3>Usage</h3>
51
- <p>Projects must be run via the --import command</p>
52
- <pre>node --import jtsx-loader ./server.js</pre>
53
-
54
- <h3>How to render a file</h3>
55
- <p>Let's write a simple index.jsx page component:</p>
56
- <pre __escape={`
57
- export default async ({ title }) => <html lang="en">
58
- <head>
59
- <title>{title}</title>
60
- </head>
61
- <body>
62
- <h1>{title}</h1>
63
- <p>
64
- Body content with array:
65
- {[0, 1, 2].map(n => <code>{n}</code>)}
66
- </p>
67
- </body>
68
- </html>;
69
- `}></pre>
70
- <p>Import the component via <code>import()</code>, the ?reload parameter is optional, used to prevent caching</p>
71
- <pre>
72
- const JSXComp = (await import('./index.jsx?reload')).default;
73
- </pre>
74
- <p>Call the imported component as a function passing the data object and output the result to the console</p>
75
- <pre>
76
- {`const rendered = await JSXComp({ data: renderCount });\nconsole.log(rendered);`}
77
- </pre>
78
-
79
- <p>A detailed example with saving the result to a file <a href="https://github.com/dergachevm/jtsx-loader/tree/master/example">here</a></p>
80
-
81
- <h3>Requirements</h3>
82
- <p>Minimum Node.js version - 20.16.0</p>
83
- </section>
84
-
85
- <section>
86
- <h2>Basic Examples</h2>
87
-
88
- <div class="examples">
89
- <div class="item">
90
- <h3>Native HTML Attributes</h3>
91
- <p>Only native HTML attributes are allowed</p>
92
-
93
- <p>React attributes are not supported by design and should never be used</p>
94
-
95
- <p>If you use React attributes, such as <code>className</code>,<br />
96
- a warning will be printed to the console, and the attribute will be rendered as is</p>
97
-
98
- <p>Other differences from standard JSX:</p>
99
-
100
- <ul>
101
- <li><code>__escape</code> attribute: used to output any escaped content</li>
102
- <li><code>__raw</code> attribute: renders a string into the tag content as is, for example useful for <code>script</code> tags and <code>style</code></li>
103
- <li>The JTSX file is passed the <code>_jsxUtils</code> object, which contains useful utilities. Instead of this object, you can pass any function you want and use it to extend the standard transformation</li>
104
- </ul>
105
- </div>
106
-
107
- <div class="item">
108
- <h3>All JavaScript capabilities that are used in regular JSX are available</h3>
109
- <p>IMPORTANT: all code is executed in the Node.js environment, so there is no browser DOM</p>
110
- <h4>Calculations</h4>
111
- <pre __escape={ '{(1 + 1 + 2 * 2) / 3}' }></pre>
112
- <p>Result: <code>{ (1 + 1 + 2 * 2) / 3 }</code></p>
113
-
114
- <h4>Node.js Standard Library</h4>
115
- <pre __escape={ '{Math.pow(9, 2)}' }></pre>
116
- <p>Result: <code>{ Math.pow(9, 2) }</code></p>
117
-
118
- <h4>Ternary expressions</h4>
119
- <pre __escape={ `1 > 2 ? <strong>Greater</strong> : 'Less'` }></pre>
120
- <p>Result: <code>{ 1 > 2 ? <strong>Greater</strong> : 'Less' }</code></p>
121
- <pre __escape={ `2 > 1 ? <strong>Greater</strong> : 'Less'` }></pre>
122
- <p>Result: <code>{ 2 > 1 ? <strong>Greater</strong> : 'Less' }</code></p>
123
-
124
- <h4>Array manipulation and component rendering</h4>
125
- <p>Renders components/tags with array data</p>
126
- <pre __escape={ '[0, 1, 2].map(num => <code>{num}</code>)' }></pre>
127
- <p>Result: { [0, 1, 2].map(el => <code>{ el }</code>) }</p>
128
-
129
- <h4>__escape</h4>
130
- <p>Output any escaped content</p>
131
- <pre __escape={ '<code __escape={\'<div>escaped</div>\'}></code>' }></pre>
132
- <p>Result: <code __escape={ '<div>escaped</div>' }></code></p>
133
- </div>
134
-
135
- <div class="item">
136
- <h3>Request inside the component via <code>fetch</code></h3>
137
- <p><code __escape={ `const inlineRequest = await fetch(process.env.URL + '/api/200.json').then(r => r.json()).catch(e => e.message);` }></code></p>
138
- <p>Called on every render</p>
139
- <p>If the internal server is called, it must be running before the render starts</p>
140
- <pre>
141
- Response: { JSON.stringify(inlineRequest, null, 4) }
142
- </pre>
143
- </div>
144
-
145
- <div class="item">
146
- <h3>Imported component</h3>
147
- <p></p>
148
- <div class="component-container">
149
- <ComponentInner lang="en" renderCount={ data } >
150
- <p><i special="CHILD 0">Child component 0</i></p>
151
- <p><i special="CHILD 1">Child component 1</i></p>
152
- </ComponentInner>
153
- </div>
154
- </div>
155
-
156
- <div class="item">
157
- <h3>Custom attributes and their processing</h3>
158
- <p>{ _jsxUtils.escapeHtml(`<input type="text" ac:custom="input"`) } is transformed into <code>data-ac-custom="input"</code></p>
159
- <p>The handler is written in jtsx.config.js</p>
160
-
161
- <div>
162
- <input type="text" ac:custom="input" value="ac:custom='input'" />
163
- </div>
164
- </div>
165
- </div>
166
- </section>
167
-
168
- <section>
169
- <h2>Authors</h2>
170
- <p><a href="mailto:dergachev.mihail@gmail.com">Dergachev Mikhail</a></p>
171
- <p><a href="https: //ancros.dev">https://ancros.dev</a></p>
172
- </section>
173
-
174
- <section>
175
- <h2>References</h2>
176
- <p><a href="https://esbuild.github.io/api/#jsx-factory">https://esbuild.github.io/api/# jsx-factory</a></p>
177
- <p><a href="https://lwebapp.com/en/post/custom-jsx">https://lwebapp.com/en/post/custom- jsx</a></p>
178
- <p><a href="https://nakedjsx.org/">https://nakedjsx.org/</a></p>
179
- </section>
180
- </Layout>
181
- }