Skip to content

Commit

Permalink
Merge pull request #828 from preactjs/htm-comments
Browse files Browse the repository at this point in the history
  • Loading branch information
marvinhagemeister authored Sep 2, 2021
2 parents 9804380 + 221c8fe commit 685d4ee
Show file tree
Hide file tree
Showing 5 changed files with 169 additions and 1 deletion.
5 changes: 5 additions & 0 deletions .changeset/weak-coats-peel.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'wmr': patch
---

Fix missing props when there were comments between attributes in JSX
109 changes: 109 additions & 0 deletions packages/wmr/src/lib/transform-jsx-to-htm-lite.js
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,41 @@ export default function transformJsxToHtmLite({ types: t }, options = {}) {
name.appendString('}');
}

// Remove all JS-style comments in between JSXAttributes.
// This includes both single line comments and multi line ones.
// <A /* comment */ foo="a" /* other comment */ />
// <div
// // comment
// id="foo"
// // other comment
// />
//
// Result:
// <A foo="a" />
// <div
// id="foo"
// />
const attrs = path.get('attributes');
if (Array.isArray(attrs.node) && attrs.node.length > 0) {
let ms = path.ctx.out;

let i = name.end;
for (const attr of attrs.node) {
const comments = parseMaybeComments(ms.original, i);
for (const comment of comments) {
ms.remove(comment.start, comment.end);
}

i = attr.end;
}

// Remove potential comment before closing the opening tag
const comments = parseMaybeComments(ms.original, i);
for (const comment of comments) {
ms.remove(comment.start, comment.end);
}
}

if (isRootElement(path)) {
path.prependString(tagString + '`');

Expand Down Expand Up @@ -149,3 +184,77 @@ export default function transformJsxToHtmLite({ types: t }, options = {}) {
}
};
}

/**
* Parse comments and return the start and end offset of all comments
* that were found. This includes sibling comments.
* @param {string} code
* @param {number} start
*/
function parseMaybeComments(code, start) {
/** @type {Array<{start: number, end: number}>} */
const out = [];

const NONE = 0;
const SINGLE = 1;
const MULTI = 2;

let commentStart = 0;
let lineStart = start;

let type = NONE;
for (let i = start; i < code.length; i++) {
let char = code.charAt(i);

if (type === SINGLE) {
if (char === '\n' || char === '\r') {
if (lineStart < commentStart) {
const leading = code.slice(lineStart, commentStart).match(/^\s+/);
if (leading) {
commentStart = lineStart;
}
}

out.push({ start: commentStart, end: i });
lineStart = i;
type = NONE;
} else {
continue;
}
} else if (type === MULTI) {
if (char === '*' && code.charAt(i + 1) === '/') {
if (lineStart < commentStart) {
const leading = code.slice(lineStart, commentStart).match(/^\n\s+/);
if (leading) {
commentStart = lineStart;
}
}

let end = i + 2;
out.push({ start: commentStart, end });
type = NONE;
i = end;
} else {
continue;
}
} else if (char === '/') {
if (code.charAt(i + 1) === '/') {
type = SINGLE;
commentStart = i;
i++;
} else if (code.charAt(i + 1) === '*') {
commentStart = i;
type = MULTI;
i++;
}
} else if (char === '\n') {
lineStart = i;
} else if (/\s/.test(char)) {
commentStart++;
} else {
break;
}
}

return out;
}
19 changes: 19 additions & 0 deletions packages/wmr/test/fixtures/transformations/jsx-comment.expected.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { html as $$html } from '/@npm/htm/preact';
// prettier-ignore
export const a = $$html`<div
id="foo"
disabled
/>`

// prettier-ignore
export const b = $$html`<div
id="foo"
/>`

const Foo = () => null;
// prettier-ignore
export const c = $$html`<div
id="foo"
>
<${Foo} foo=${2} />
</div>`
28 changes: 28 additions & 0 deletions packages/wmr/test/fixtures/transformations/jsx-comment.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// prettier-ignore
export const a = <div
// comment 1
id="foo"
// comment 2
disabled
// comment 3
/>

// prettier-ignore
export const b = <div
// comment 1
// comment 2
id="foo"
// comment 3
// comment 3
/>

const Foo = () => null;
// prettier-ignore
export const c = <div
/* a */
id="foo"
/* b */
/* c */
>
<Foo /*asd*/ foo={2} /*asd*//>
</div>
9 changes: 8 additions & 1 deletion packages/wmr/test/transformations.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import path from 'path';
import { promises as fs } from 'fs';
import { setupTest, teardown, runWmr, loadFixture, get } from './test-helpers.js';
import { setupTest, teardown, runWmr, loadFixture, get, withLog } from './test-helpers.js';
import { modularizeCss } from '../src/plugins/wmr/styles/css-modules.js';

const runWmrFast = (cwd, ...args) => runWmr(cwd, '--no-optimize', '--no-compress', ...args);
Expand Down Expand Up @@ -49,6 +49,13 @@ describe('transformations', () => {
const expected = await readFile(env, 'jsx-self-closed.expected.js');
expect((await get(instance, 'jsx-self-closed.js')).body).toEqual(expected);
});

it('should remove single line comments between props', async () => {
const expected = await readFile(env, 'jsx-comment.expected.js');
await withLog(instance.output, async () => {
expect((await get(instance, 'jsx-comment.js')).body).toEqual(expected);
});
});
});

describe('css', () => {
Expand Down

0 comments on commit 685d4ee

Please sign in to comment.