htm-transform 0.1.4 → 0.1.5

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 (4) hide show
  1. package/index.js +21 -7
  2. package/package.json +2 -2
  3. package/test.js +258 -308
  4. package/tsconfig.json +1 -1
package/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as acorn from "acorn";
2
2
  import { generate } from "astring";
3
- import { simple } from "acorn-walk";
3
+ import { attachComments, makeTraveler } from "astravel";
4
4
 
5
5
  /**
6
6
  * @typedef {Object} ElementNode
@@ -46,20 +46,22 @@ import { simple } from "acorn-walk";
46
46
  export default function transform(code, options = {}) {
47
47
  const { pragma = "h", tag: tagName = "html", import: importConfig } = options;
48
48
 
49
+ /** @type {Array<import('acorn').Comment>} */
50
+ const comments = [];
51
+
49
52
  const ast = /** @type {import('acorn').Program} */ (
50
53
  acorn.parse(code, {
51
54
  ecmaVersion: "latest",
52
55
  sourceType: "module",
56
+ locations: true,
57
+ onComment: comments,
53
58
  })
54
59
  );
55
60
 
56
61
  let hasTransformation = false;
57
62
 
58
- simple(ast, {
59
- /**
60
- * @param {import('acorn').TaggedTemplateExpression} node
61
- */
62
- TaggedTemplateExpression(node) {
63
+ const traveler = makeTraveler({
64
+ TaggedTemplateExpression(node, state) {
63
65
  if (node.tag.type === "Identifier" && node.tag.name === tagName) {
64
66
  hasTransformation = true;
65
67
  const transformed = transformTaggedTemplate(node, pragma);
@@ -70,16 +72,28 @@ export default function transform(code, options = {}) {
70
72
  delete mutableNode[key];
71
73
  }
72
74
  Object.assign(node, transformed);
75
+ // Continue traversing into the transformed node to handle nested templates
76
+ this.go(node, state);
77
+ return;
73
78
  }
79
+ // Continue traversal for non-matching nodes
80
+ this.super.TaggedTemplateExpression.call(this, node, state);
74
81
  },
75
82
  });
76
83
 
84
+ traveler.go(ast);
85
+
77
86
  // Add import statement if specified and transformation occurred
78
87
  if (hasTransformation && importConfig?.from && importConfig?.name) {
79
88
  addImportDeclaration(ast, importConfig.from, importConfig.name);
80
89
  }
81
90
 
82
- return generate(ast);
91
+ // Attach comments to AST nodes
92
+ if (comments.length > 0) {
93
+ attachComments(ast, comments);
94
+ }
95
+
96
+ return generate(ast, { comments: true });
83
97
  }
84
98
 
85
99
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "htm-transform",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Transform htm tagged templates into h function calls using acorn",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -26,7 +26,7 @@
26
26
  "license": "MPL-2.0",
27
27
  "dependencies": {
28
28
  "acorn": "^8.11.3",
29
- "acorn-walk": "^8.3.2",
29
+ "astravel": "^0.6.1",
30
30
  "astring": "^1.8.6"
31
31
  },
32
32
  "devDependencies": {
package/test.js CHANGED
@@ -7,449 +7,399 @@ function normalize(str) {
7
7
  return str.replace(/\s+/g, " ").trim();
8
8
  }
9
9
 
10
- describe("htm-transform", () => {
11
- test("transforms simple element", () => {
12
- const input = `const result = html\`<h1 id=hello>Hello world!</h1>\`;`;
13
- const output = transform(input);
14
- const expected = `const result = h("h1", {
10
+ test("transforms simple element", () => {
11
+ const input = `const result = html\`<h1 id=hello>Hello world!</h1>\`;`;
12
+ const output = transform(input);
13
+ const expected = `const result = h("h1", {
15
14
  id: "hello"
16
15
  }, "Hello world!");`;
17
16
 
18
- assert.strictEqual(normalize(output), normalize(expected));
19
- });
17
+ assert.strictEqual(normalize(output), normalize(expected));
18
+ });
20
19
 
21
- test("transforms element with dynamic class and content", () => {
22
- const input = `const result = html\`<div class=\${className}>\${content}</div>\`;`;
23
- const output = transform(input);
24
- const expected = `const result = h("div", {
20
+ test("transforms element with dynamic class and content", () => {
21
+ const input = `const result = html\`<div class=\${className}>\${content}</div>\`;`;
22
+ const output = transform(input);
23
+ const expected = `const result = h("div", {
25
24
  class: className
26
25
  }, content);`;
27
26
 
28
- assert.strictEqual(normalize(output), normalize(expected));
29
- });
27
+ assert.strictEqual(normalize(output), normalize(expected));
28
+ });
30
29
 
31
- test("transforms component with props", () => {
32
- const input = `const result = html\`<\${Header} name="ToDo's" />\`;`;
33
- const output = transform(input);
34
- const expected = `const result = h(Header, {
30
+ test("transforms component with props", () => {
31
+ const input = `const result = html\`<\${Header} name="ToDo's" />\`;`;
32
+ const output = transform(input);
33
+ const expected = `const result = h(Header, {
35
34
  name: "ToDo's"
36
35
  });`;
37
36
 
38
- assert.strictEqual(normalize(output), normalize(expected));
39
- });
37
+ assert.strictEqual(normalize(output), normalize(expected));
38
+ });
40
39
 
41
- test("transforms multiple root elements into array", () => {
42
- const input = `const result = html\`
40
+ test("transforms multiple root elements into array", () => {
41
+ const input = `const result = html\`
43
42
  <h1 id=hello>Hello</h1>
44
43
  <div class=world>World!</div>
45
44
  \`;`;
46
- const output = transform(input);
47
- const expected = `const result = [h("h1", {
45
+ const output = transform(input);
46
+ const expected = `const result = [h("h1", {
48
47
  id: "hello"
49
48
  }, "Hello"), h("div", {
50
49
  class: "world"
51
50
  }, "World!")];`;
52
51
 
53
- assert.strictEqual(normalize(output), normalize(expected));
54
- });
52
+ assert.strictEqual(normalize(output), normalize(expected));
53
+ });
55
54
 
56
- test("transforms nested elements", () => {
57
- const input = `const result = html\`<div><p>Hello</p><p>World</p></div>\`;`;
58
- const output = transform(input);
59
- const expected = `const result = h("div", null, h("p", null, "Hello"), h("p", null, "World"));`;
55
+ test("transforms nested elements", () => {
56
+ const input = `const result = html\`<div><p>Hello</p><p>World</p></div>\`;`;
57
+ const output = transform(input);
58
+ const expected = `const result = h("div", null, h("p", null, "Hello"), h("p", null, "World"));`;
60
59
 
61
- assert.strictEqual(normalize(output), normalize(expected));
62
- });
60
+ assert.strictEqual(normalize(output), normalize(expected));
61
+ });
63
62
 
64
- test("transforms spread props", () => {
65
- const input = `const result = html\`<div ...\${props}>Content</div>\`;`;
66
- const output = transform(input);
67
- const expected = `const result = h("div", props, "Content");`;
63
+ test("transforms spread props", () => {
64
+ const input = `const result = html\`<div ...\${props}>Content</div>\`;`;
65
+ const output = transform(input);
66
+ const expected = `const result = h("div", props, "Content");`;
68
67
 
69
- assert.strictEqual(normalize(output), normalize(expected));
70
- });
68
+ assert.strictEqual(normalize(output), normalize(expected));
69
+ });
71
70
 
72
- test("transforms mixed props and spread", () => {
73
- const input = `const result = html\`<div class="test" ...\${props}>Content</div>\`;`;
74
- const output = transform(input);
75
- const expected = `const result = h("div", {
71
+ test("transforms mixed props and spread", () => {
72
+ const input = `const result = html\`<div class="test" ...\${props}>Content</div>\`;`;
73
+ const output = transform(input);
74
+ const expected = `const result = h("div", {
76
75
  class: "test",
77
76
  ...props
78
77
  }, "Content");`;
79
78
 
80
- assert.strictEqual(normalize(output), normalize(expected));
81
- });
79
+ assert.strictEqual(normalize(output), normalize(expected));
80
+ });
82
81
 
83
- test("transforms self-closing tags", () => {
84
- const input = `const result = html\`<img src="test.jpg" alt="Test" />\`;`;
85
- const output = transform(input);
86
- const expected = `const result = h("img", {
82
+ test("transforms self-closing tags", () => {
83
+ const input = `const result = html\`<img src="test.jpg" alt="Test" />\`;`;
84
+ const output = transform(input);
85
+ const expected = `const result = h("img", {
87
86
  src: "test.jpg",
88
87
  alt: "Test"
89
88
  });`;
90
89
 
91
- assert.strictEqual(normalize(output), normalize(expected));
92
- });
90
+ assert.strictEqual(normalize(output), normalize(expected));
91
+ });
93
92
 
94
- test("transforms elements with boolean attributes", () => {
95
- const input = `const result = html\`<input type="checkbox" checked disabled />\`;`;
96
- const output = transform(input);
97
- const expected = `const result = h("input", {
93
+ test("transforms elements with boolean attributes", () => {
94
+ const input = `const result = html\`<input type="checkbox" checked disabled />\`;`;
95
+ const output = transform(input);
96
+ const expected = `const result = h("input", {
98
97
  type: "checkbox",
99
98
  checked: true,
100
99
  disabled: true
101
100
  });`;
102
101
 
103
- assert.strictEqual(normalize(output), normalize(expected));
104
- });
102
+ assert.strictEqual(normalize(output), normalize(expected));
103
+ });
105
104
 
106
- test("preserves other code unchanged", () => {
107
- const input = `const foo = 'bar';\nconst result = html\`<div>Test</div>\`;\nconsole.log(foo);`;
108
- const output = transform(input);
109
- const expected = `const foo = 'bar';
105
+ test("preserves other code unchanged", () => {
106
+ const input = `const foo = 'bar';\nconst result = html\`<div>Test</div>\`;\nconsole.log(foo);`;
107
+ const output = transform(input);
108
+ const expected = `const foo = 'bar';
110
109
  const result = h("div", null, "Test");
111
110
  console.log(foo);`;
112
111
 
113
- assert.strictEqual(normalize(output), normalize(expected));
114
- });
115
-
116
- test("handles custom pragma option", () => {
117
- const input = `const result = html\`<div>Test</div>\`;`;
118
- const output = transform(input, { pragma: "React.createElement" });
119
- const expected = `const result = React.createElement("div", null, "Test");`;
112
+ assert.strictEqual(normalize(output), normalize(expected));
113
+ });
120
114
 
121
- assert.strictEqual(normalize(output), normalize(expected));
122
- });
115
+ test("handles custom pragma option", () => {
116
+ const input = `const result = html\`<div>Test</div>\`;`;
117
+ const output = transform(input, { pragma: "React.createElement" });
118
+ const expected = `const result = React.createElement("div", null, "Test");`;
123
119
 
124
- test("handles custom tag name option", () => {
125
- const input = `const result = htm\`<div>Test</div>\`;`;
126
- const output = transform(input, { tag: "htm" });
127
- const expected = `const result = h("div", null, "Test");`;
120
+ assert.strictEqual(normalize(output), normalize(expected));
121
+ });
128
122
 
129
- assert.strictEqual(normalize(output), normalize(expected));
130
- });
123
+ test("handles custom tag name option", () => {
124
+ const input = `const result = htm\`<div>Test</div>\`;`;
125
+ const output = transform(input, { tag: "htm" });
126
+ const expected = `const result = h("div", null, "Test");`;
131
127
 
132
- test("transforms element with multiple dynamic children", () => {
133
- const input = `const result = html\`<ul>\${items.map(i => html\`<li>\${i}</li>\`)}</ul>\`;`;
134
- const output = transform(input);
135
- const expected = `const result = h("ul", null, items.map(i => h("li", null, i)));`;
128
+ assert.strictEqual(normalize(output), normalize(expected));
129
+ });
136
130
 
137
- assert.strictEqual(normalize(output), normalize(expected));
138
- });
131
+ test("transforms element with multiple dynamic children", () => {
132
+ const input = `const result = html\`<ul>\${items.map(i => html\`<li>\${i}</li>\`)}</ul>\`;`;
133
+ const output = transform(input);
134
+ const expected = `const result = h("ul", null, items.map(i => h("li", null, i)));`;
139
135
 
140
- test("transforms component with closing tag", () => {
141
- const input = `const result = html\`<\${Wrapper}>Content</\${Wrapper}>\`;`;
142
- const output = transform(input);
143
- const expected = `const result = h(Wrapper, null, "Content");`;
136
+ assert.strictEqual(normalize(output), normalize(expected));
137
+ });
144
138
 
145
- assert.strictEqual(normalize(output), normalize(expected));
146
- });
139
+ test("transforms component with closing tag", () => {
140
+ const input = `const result = html\`<\${Wrapper}>Content</\${Wrapper}>\`;`;
141
+ const output = transform(input);
142
+ const expected = `const result = h(Wrapper, null, "Content");`;
147
143
 
148
- test("transforms empty element", () => {
149
- const input = `const result = html\`<div></div>\`;`;
150
- const output = transform(input);
151
- const expected = `const result = h("div", null);`;
144
+ assert.strictEqual(normalize(output), normalize(expected));
145
+ });
152
146
 
153
- assert.strictEqual(normalize(output), normalize(expected));
154
- });
147
+ test("transforms empty element", () => {
148
+ const input = `const result = html\`<div></div>\`;`;
149
+ const output = transform(input);
150
+ const expected = `const result = h("div", null);`;
155
151
 
156
- test("does not add import when import option is omitted", () => {
157
- const input = `const result = html\`<div>Test</div>\`;`;
158
- const output = transform(input);
159
- const expected = `const result = h("div", null, "Test");`;
152
+ assert.strictEqual(normalize(output), normalize(expected));
153
+ });
160
154
 
161
- assert.strictEqual(normalize(output), normalize(expected));
162
- });
155
+ test("does not add import when import option is omitted", () => {
156
+ const input = `const result = html\`<div>Test</div>\`;`;
157
+ const output = transform(input);
158
+ const expected = `const result = h("div", null, "Test");`;
163
159
 
164
- test("adds import statement when import config is specified", () => {
165
- const input = `const result = html\`<div>Test</div>\`;`;
166
- const output = transform(input, {
167
- import: { from: "preact", name: "h" },
168
- });
169
- const expected = `import {h} from "preact";
170
- const result = h("div", null, "Test");`;
160
+ assert.strictEqual(normalize(output), normalize(expected));
161
+ });
171
162
 
172
- assert.strictEqual(normalize(output), normalize(expected));
163
+ test("adds import statement when import config is specified", () => {
164
+ const input = `const result = html\`<div>Test</div>\`;`;
165
+ const output = transform(input, {
166
+ import: { from: "preact", name: "h" },
173
167
  });
168
+ const expected = `import {h} from "preact";
169
+ const result = h("div", null, "Test");`;
174
170
 
175
- test("adds import for React.createElement", () => {
176
- const input = `const result = html\`<div>Test</div>\`;`;
177
- const output = transform(input, {
178
- pragma: "createElement",
179
- import: { from: "react", name: "createElement" },
180
- });
181
- const expected = `import {createElement} from "react";
182
- const result = createElement("div", null, "Test");`;
171
+ assert.strictEqual(normalize(output), normalize(expected));
172
+ });
183
173
 
184
- assert.strictEqual(normalize(output), normalize(expected));
174
+ test("adds import for React.createElement", () => {
175
+ const input = `const result = html\`<div>Test</div>\`;`;
176
+ const output = transform(input, {
177
+ pragma: "createElement",
178
+ import: { from: "react", name: "createElement" },
185
179
  });
180
+ const expected = `import {createElement} from "react";
181
+ const result = createElement("div", null, "Test");`;
186
182
 
187
- test("does not add import when only from is specified", () => {
188
- const input = `const result = html\`<div>Test</div>\`;`;
189
- const output = transform(input, {
190
- import: { from: "preact" },
191
- });
192
- const expected = `const result = h("div", null, "Test");`;
183
+ assert.strictEqual(normalize(output), normalize(expected));
184
+ });
193
185
 
194
- assert.strictEqual(normalize(output), normalize(expected));
186
+ test("does not add import when only from is specified", () => {
187
+ const input = `const result = html\`<div>Test</div>\`;`;
188
+ const output = transform(input, {
189
+ import: { from: "preact" },
195
190
  });
191
+ const expected = `const result = h("div", null, "Test");`;
196
192
 
197
- test("does not add import when only name is specified", () => {
198
- const input = `const result = html\`<div>Test</div>\`;`;
199
- const output = transform(input, {
200
- import: { name: "h" },
201
- });
202
- const expected = `const result = h("div", null, "Test");`;
193
+ assert.strictEqual(normalize(output), normalize(expected));
194
+ });
203
195
 
204
- assert.strictEqual(normalize(output), normalize(expected));
196
+ test("does not add import when only name is specified", () => {
197
+ const input = `const result = html\`<div>Test</div>\`;`;
198
+ const output = transform(input, {
199
+ import: { name: "h" },
205
200
  });
201
+ const expected = `const result = h("div", null, "Test");`;
202
+
203
+ assert.strictEqual(normalize(output), normalize(expected));
204
+ });
206
205
 
207
- test("does not duplicate existing import", () => {
208
- const input = `import { h } from 'preact';
206
+ test("does not duplicate existing import", () => {
207
+ const input = `import { h } from 'preact';
209
208
  const result = html\`<div>Test</div>\`;`;
210
- const output = transform(input, {
211
- import: { from: "preact", name: "h" },
212
- });
213
- const expected = `import {h} from 'preact';
209
+ const output = transform(input, {
210
+ import: { from: "preact", name: "h" },
211
+ });
212
+ const expected = `import {h} from 'preact';
214
213
  const result = h("div", null, "Test");`;
215
214
 
216
- assert.strictEqual(normalize(output), normalize(expected));
217
- });
215
+ assert.strictEqual(normalize(output), normalize(expected));
216
+ });
218
217
 
219
- test("adds import before existing imports", () => {
220
- const input = `import { useState } from 'preact/hooks';
218
+ test("adds import before existing imports", () => {
219
+ const input = `import { useState } from 'preact/hooks';
221
220
  const result = html\`<div>Test</div>\`;`;
222
- const output = transform(input, {
223
- import: { from: "preact", name: "h" },
224
- });
225
- const expected = `import {h} from "preact";
221
+ const output = transform(input, {
222
+ import: { from: "preact", name: "h" },
223
+ });
224
+ const expected = `import {h} from "preact";
226
225
  import {useState} from 'preact/hooks';
227
226
  const result = h("div", null, "Test");`;
228
227
 
229
- assert.strictEqual(normalize(output), normalize(expected));
230
- });
228
+ assert.strictEqual(normalize(output), normalize(expected));
229
+ });
231
230
 
232
- test("adds import before code when no imports exist", () => {
233
- const input = `const foo = 'bar';
231
+ test("adds import before code when no imports exist", () => {
232
+ const input = `const foo = 'bar';
234
233
  const result = html\`<div>Test</div>\`;`;
235
- const output = transform(input, {
236
- import: { from: "preact", name: "h" },
237
- });
238
- const expected = `import {h} from "preact";
234
+ const output = transform(input, {
235
+ import: { from: "preact", name: "h" },
236
+ });
237
+ const expected = `import {h} from "preact";
239
238
  const foo = 'bar';
240
239
  const result = h("div", null, "Test");`;
241
240
 
242
- assert.strictEqual(normalize(output), normalize(expected));
243
- });
241
+ assert.strictEqual(normalize(output), normalize(expected));
242
+ });
244
243
 
245
- test("does not duplicate import when importing same export from same module", () => {
246
- const input = `import { h } from 'preact';
244
+ test("does not duplicate import when importing same export from same module", () => {
245
+ const input = `import { h } from 'preact';
247
246
  import { render } from 'preact';
248
247
  const result = html\`<div>Test</div>\`;`;
249
- const output = transform(input, {
250
- import: { from: "preact", name: "h" },
251
- });
252
- const expected = `import {h} from 'preact';
248
+ const output = transform(input, {
249
+ import: { from: "preact", name: "h" },
250
+ });
251
+ const expected = `import {h} from 'preact';
253
252
  import {render} from 'preact';
254
253
  const result = h("div", null, "Test");`;
255
254
 
256
- assert.strictEqual(normalize(output), normalize(expected));
257
- });
255
+ assert.strictEqual(normalize(output), normalize(expected));
256
+ });
258
257
 
259
- test("adds import even if same module is imported with different exports", () => {
260
- const input = `import { render } from 'preact';
258
+ test("adds import even if same module is imported with different exports", () => {
259
+ const input = `import { render } from 'preact';
261
260
  const result = html\`<div>Test</div>\`;`;
262
- const output = transform(input, {
263
- import: { from: "preact", name: "h" },
264
- });
265
- const expected = `import {h} from "preact";
261
+ const output = transform(input, {
262
+ import: { from: "preact", name: "h" },
263
+ });
264
+ const expected = `import {h} from "preact";
266
265
  import {render} from 'preact';
267
266
  const result = h("div", null, "Test");`;
268
267
 
269
- assert.strictEqual(normalize(output), normalize(expected));
270
- });
268
+ assert.strictEqual(normalize(output), normalize(expected));
269
+ });
271
270
 
272
- test("handles multiple htm transformations with import", () => {
273
- const input = `const a = html\`<div>A</div>\`;
271
+ test("handles multiple htm transformations with import", () => {
272
+ const input = `const a = html\`<div>A</div>\`;
274
273
  const b = html\`<span>B</span>\`;`;
275
- const output = transform(input, {
276
- import: { from: "preact", name: "h" },
277
- });
278
- const expected = `import {h} from "preact";
274
+ const output = transform(input, {
275
+ import: { from: "preact", name: "h" },
276
+ });
277
+ const expected = `import {h} from "preact";
279
278
  const a = h("div", null, "A");
280
279
  const b = h("span", null, "B");`;
281
280
 
282
- assert.strictEqual(normalize(output), normalize(expected));
283
- });
284
-
285
- test("works with different module paths", () => {
286
- const input = `const result = html\`<div>Test</div>\`;`;
287
- const output = transform(input, {
288
- import: { from: "preact/compat", name: "h" },
289
- });
290
- const expected = `import {h} from "preact/compat";
291
- const result = h("div", null, "Test");`;
281
+ assert.strictEqual(normalize(output), normalize(expected));
282
+ });
292
283
 
293
- assert.strictEqual(normalize(output), normalize(expected));
284
+ test("works with different module paths", () => {
285
+ const input = `const result = html\`<div>Test</div>\`;`;
286
+ const output = transform(input, {
287
+ import: { from: "preact/compat", name: "h" },
294
288
  });
295
-
296
- test("works with scoped packages", () => {
297
- const input = `const result = html\`<div>Test</div>\`;`;
298
- const output = transform(input, {
299
- import: { from: "@preact/signals", name: "h" },
300
- });
301
- const expected = `import {h} from "@preact/signals";
289
+ const expected = `import {h} from "preact/compat";
302
290
  const result = h("div", null, "Test");`;
303
291
 
304
- assert.strictEqual(normalize(output), normalize(expected));
292
+ assert.strictEqual(normalize(output), normalize(expected));
293
+ });
294
+
295
+ test("works with scoped packages", () => {
296
+ const input = `const result = html\`<div>Test</div>\`;`;
297
+ const output = transform(input, {
298
+ import: { from: "@preact/signals", name: "h" },
305
299
  });
300
+ const expected = `import {h} from "@preact/signals";
301
+ const result = h("div", null, "Test");`;
306
302
 
307
- test("adds import with custom pragma matching name", () => {
308
- const input = `const result = html\`<div>Test</div>\`;`;
309
- const output = transform(input, {
310
- pragma: "myH",
311
- import: { from: "my-library", name: "myH" },
312
- });
313
- const expected = `import {myH} from "my-library";
314
- const result = myH("div", null, "Test");`;
303
+ assert.strictEqual(normalize(output), normalize(expected));
304
+ });
315
305
 
316
- assert.strictEqual(normalize(output), normalize(expected));
306
+ test("adds import with custom pragma matching name", () => {
307
+ const input = `const result = html\`<div>Test</div>\`;`;
308
+ const output = transform(input, {
309
+ pragma: "myH",
310
+ import: { from: "my-library", name: "myH" },
317
311
  });
312
+ const expected = `import {myH} from "my-library";
313
+ const result = myH("div", null, "Test");`;
318
314
 
319
- test("handles empty file with import config", () => {
320
- const input = ``;
321
- const output = transform(input, {
322
- import: { from: "preact", name: "h" },
323
- });
324
- const expected = ``;
315
+ assert.strictEqual(normalize(output), normalize(expected));
316
+ });
325
317
 
326
- assert.strictEqual(normalize(output), normalize(expected));
318
+ test("handles empty file with import config", () => {
319
+ const input = ``;
320
+ const output = transform(input, {
321
+ import: { from: "preact", name: "h" },
327
322
  });
323
+ const expected = ``;
328
324
 
329
- test("handles file with only imports", () => {
330
- const input = `import { useState } from 'preact/hooks';`;
331
- const output = transform(input, {
332
- import: { from: "preact", name: "h" },
333
- });
334
- const expected = `import {useState} from 'preact/hooks';`;
325
+ assert.strictEqual(normalize(output), normalize(expected));
326
+ });
335
327
 
336
- assert.strictEqual(normalize(output), normalize(expected));
328
+ test("handles file with only imports", () => {
329
+ const input = `import { useState } from 'preact/hooks';`;
330
+ const output = transform(input, {
331
+ import: { from: "preact", name: "h" },
337
332
  });
333
+ const expected = `import {useState} from 'preact/hooks';`;
338
334
 
339
- test("spread at beginning means later props override", () => {
340
- const input = `const result = html\`<div ...\${props} class="test">Content</div>\`;`;
341
- const output = transform(input);
342
- const expected = `const result = h("div", {
335
+ assert.strictEqual(normalize(output), normalize(expected));
336
+ });
337
+
338
+ test("spread at beginning means later props override", () => {
339
+ const input = `const result = html\`<div ...\${props} class="test">Content</div>\`;`;
340
+ const output = transform(input);
341
+ const expected = `const result = h("div", {
343
342
  ...props,
344
343
  class: "test"
345
344
  }, "Content");`;
346
345
 
347
- assert.strictEqual(normalize(output), normalize(expected));
348
- });
346
+ assert.strictEqual(normalize(output), normalize(expected));
347
+ });
349
348
 
350
- test("spread between props preserves order", () => {
351
- const input = `const result = html\`<div id="foo" ...\${props} class="test">Content</div>\`;`;
352
- const output = transform(input);
353
- const expected = `const result = h("div", {
349
+ test("spread between props preserves order", () => {
350
+ const input = `const result = html\`<div id="foo" ...\${props} class="test">Content</div>\`;`;
351
+ const output = transform(input);
352
+ const expected = `const result = h("div", {
354
353
  id: "foo",
355
354
  ...props,
356
355
  class: "test"
357
356
  }, "Content");`;
358
357
 
359
- assert.strictEqual(normalize(output), normalize(expected));
360
- });
358
+ assert.strictEqual(normalize(output), normalize(expected));
359
+ });
361
360
 
362
- test("handles attributes with hyphens", () => {
363
- const input = `const result = html\`<div data-value="test" aria-label="label">Content</div>\`;`;
364
- const output = transform(input);
365
- const expected = `const result = h("div", {
361
+ test("handles attributes with hyphens", () => {
362
+ const input = `const result = html\`<div data-value="test" aria-label="label">Content</div>\`;`;
363
+ const output = transform(input);
364
+ const expected = `const result = h("div", {
366
365
  "data-value": "test",
367
366
  "aria-label": "label"
368
367
  }, "Content");`;
369
368
 
370
- assert.strictEqual(normalize(output), normalize(expected));
371
- });
369
+ assert.strictEqual(normalize(output), normalize(expected));
370
+ });
372
371
 
373
- test("handles dynamic attributes with hyphens", () => {
374
- const input = `const result = html\`<div data-id=\${id} aria-label=\${label}>Content</div>\`;`;
375
- const output = transform(input);
376
- const expected = `const result = h("div", {
372
+ test("handles dynamic attributes with hyphens", () => {
373
+ const input = `const result = html\`<div data-id=\${id} aria-label=\${label}>Content</div>\`;`;
374
+ const output = transform(input);
375
+ const expected = `const result = h("div", {
377
376
  "data-id": id,
378
377
  "aria-label": label
379
378
  }, "Content");`;
380
379
 
381
- assert.strictEqual(normalize(output), normalize(expected));
382
- });
380
+ assert.strictEqual(normalize(output), normalize(expected));
381
+ });
383
382
 
384
- test("handles template literals in attribute values", () => {
385
- const input = `const result = html\`<div class="\${css.foo} \${css.bar}">Test</div>\`;`;
386
- const output = transform(input);
387
- const expected = `const result = h("div", {
383
+ test("handles template literals in attribute values", () => {
384
+ const input = `const result = html\`<div class="\${css.foo} \${css.bar}">Test</div>\`;`;
385
+ const output = transform(input);
386
+ const expected = `const result = h("div", {
388
387
  class: \`\${css.foo} \${css.bar}\`
389
388
  }, "Test");`;
390
389
 
391
- assert.strictEqual(normalize(output), normalize(expected));
392
- });
390
+ assert.strictEqual(normalize(output), normalize(expected));
391
+ });
393
392
 
394
- test("todo", () => {
395
- const input = `import html from "~/util/preact/html.js";
396
- /** @import { JSX } from "preact" */
397
-
398
- import button from "~/style/button.js";
399
- import Icon from "./Icon.js";
400
-
401
- import { adopt } from "~/util/css.js";
402
- import sheet from "./Button.module.css" with { type: "css" };
403
- const css = adopt(sheet);
404
-
405
- /**
406
- * @typedef {object} Props
407
- * @property {"primary" | "secondary" | "tertiary"} [level = "secondary"]
408
- * @property {"neutral" | "positive" | "negative"} [kind = "neutral"]
409
- * @property {"small" | "base" | "large"} [size = "base"]
410
- * @property {boolean} [loading]
411
- * @property {string} [popovertarget]
412
- * @property {(el: HTMLButtonElement) => void} [innerRef]
413
- */
414
-
415
- /** @param {Props & JSX.ButtonHTMLAttributes<HTMLButtonElement>} props */
416
- export default function Button(props) {
417
- const {
418
- level,
419
- kind = "neutral",
420
- size = "base",
421
- loading,
422
- disabled,
423
- type = "button",
424
- children,
425
- innerRef: ref,
426
- ...rest
427
- } = props;
428
-
429
- return html\`
430
- <button
431
- class="\${css.wrapper} \${button.button}"
432
- data-level=\${level}
433
- data-kind=\${kind}
434
- data-size=\${size}
435
- type=\${type}
436
- disabled=\${disabled || loading}
437
- ref=\${ref}
438
- ...\${rest}
439
- >
440
- <span class="\${css.layer} \${css.contents}">\${children}</span>
441
- \${loading
442
- ? html\`
443
- <span class="\${css.layer} \${css.loader}">
444
- <\${Icon} class="\${css.loader}" name="loader" />
445
- </span>
446
- \`
447
- : null}
448
- </button>
449
- \`;
450
- }
451
- `;
452
-
453
- const output = transform(input);
454
- });
393
+ test("preserves comments", () => {
394
+ const input = `// This is a comment
395
+ const x = html\`<div>Hello</div>\`;
396
+ // Another comment
397
+ const y = 42;`;
398
+ const output = transform(input);
399
+ const expected = `// This is a comment
400
+ const x = h("div", null, "Hello");
401
+ // Another comment
402
+ const y = 42;`;
403
+
404
+ assert.strictEqual(normalize(output), normalize(expected));
455
405
  });
package/tsconfig.json CHANGED
@@ -8,7 +8,7 @@
8
8
  "emitDeclarationOnly": true,
9
9
  "allowJs": true,
10
10
  "checkJs": true,
11
- "skipLibCheck": true,
11
+ "removeComments": false,
12
12
  },
13
13
  "include": ["index.js"],
14
14
  "exclude": ["node_modules", "test.js"],