Skip to content

Commit

Permalink
Merge with Marvin's changes and tests from #828
Browse files Browse the repository at this point in the history
  • Loading branch information
developit authored Sep 2, 2021
1 parent cdedcdd commit 3c91013
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 136 deletions.
126 changes: 9 additions & 117 deletions packages/wmr/src/lib/transform-jsx-to-htm-lite.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,6 @@ export default function transformJsxToHtmLite({ types: t }, options = {}) {
const tagString = options.tag || 'html';
const tagImport = options.import || false;

const commentToHtml = (path, comment, ifStart = 0, ifEnd = Infinity) => {
if (comment.start >= ifStart && comment.end <= ifEnd) {
path.ctx.out.overwrite(comment.start, comment.start + 2, '<!--');
if (comment.type === 'Block') path.ctx.out.overwrite(comment.end - 2, comment.end, '-->');
else path.ctx.out.appendRight(comment.end, '-->');
}
};

return {
name: 'transform-jsx-to-htm-lite',
visitor: {
Expand Down Expand Up @@ -69,6 +61,14 @@ export default function transformJsxToHtmLite({ types: t }, options = {}) {
name.appendString('}');
}

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

if (path.node.selfClosing) {
path.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 */ />
Expand All @@ -83,36 +83,6 @@ export default function transformJsxToHtmLite({ types: t }, options = {}) {
// <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 + '`');

if (path.node.selfClosing) {
path.appendString('`');
}
}

// Convert Line + Block comments within JSXOpeningElement into HTML comments:
let last = name.end;
let comments = path.hub.file.ast.comments;

Expand All @@ -123,7 +93,7 @@ export default function transformJsxToHtmLite({ types: t }, options = {}) {
// Don't transform comments to HTML if they're within a JSXExpression
let inExpression = attrs.some(attr => comment.start >= attr.start && comment.end <= attr.end);
if (!inExpression) {
commentToHtml(path, comment);
path.ctx.out.remove(comment.start, comment.end);
}
}
}
Expand Down Expand Up @@ -209,81 +179,3 @@ 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 + 1;
}
}

const end = i + 1;
out.push({ start: commentStart, end });
lineStart = i;
i = end;
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 + 1;
}
}

let end = i + 2;
const next = code.charAt(i + 2);
if (next === '\n' || next === '\r') end++;
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;
}
34 changes: 17 additions & 17 deletions packages/wmr/test/acorn-traverse.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1058,7 +1058,7 @@ describe('acorn-traverse', () => {
});

describe('JSX Comments', () => {
it('should preserve leading comments in JSX attributes in development', () => {
it('should remove leading comments in JSX attributes in development', () => {
const str = transformWithPlugin(
dent`
<div
Expand All @@ -1070,13 +1070,13 @@ describe('acorn-traverse', () => {
);
expect(str).toMatchInlineSnapshot(`
"html\`<div
<!-- comment-->
id=\\"foo\\"
/>\`"
`);
});

it('should preserve trailing comments in JSX attributes in development', () => {
it('should remove trailing comments in JSX attributes in development', () => {
const str = transformWithPlugin(
dent`
<div
Expand All @@ -1089,12 +1089,12 @@ describe('acorn-traverse', () => {
expect(str).toMatchInlineSnapshot(`
"html\`<div
id=\\"foo\\"
<!-- comment-->
/>\`"
`);
});

it('should preserve commented-out JSXAttributes in development', () => {
it('should remove commented-out JSXAttributes in development', () => {
const str = transformWithPlugin(
dent`
<div
Expand All @@ -1109,16 +1109,16 @@ describe('acorn-traverse', () => {
);
expect(str).toMatchInlineSnapshot(`
"html\`<div
<!--id=\\"foo\\"-->
<!-- class=\\"bar\\"-->
<!-- style={{-->
<!-- color: 'red',-->
<!-- }}-->
/>\`"
`);
});

it('should preserve multiple comments in JSX attributes in development', () => {
it('should remove multiple comments in JSX attributes in development', () => {
const str = transformWithPlugin(
dent`
<div
Expand All @@ -1133,16 +1133,16 @@ describe('acorn-traverse', () => {
);
expect(str).toMatchInlineSnapshot(`
"html\`<div
<!-- block comment -->
id=\\"foo\\"<!--A-->
<!-- line comment-->
class=\\"bar\\"<!--B-->
<!-- additional comment-->
id=\\"foo\\"
class=\\"bar\\"
/>\`"
`);
});

it('should ignore comments within JSXExpressions', () => {
it('should remove comments within JSXExpressions', () => {
const str = transformWithPlugin(
dent`
<div
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export default function ContentRegion({ content, ...props }) {
$$html`<content-region name=${props.name} data-page-nav=${hasNav}>
${content && (
$$html`<${Markup}
markup=${content}
type="html"
trim=${false}
Expand Down
10 changes: 10 additions & 0 deletions packages/wmr/test/fixtures/transformations/jsx-comment.expected.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,29 @@
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>`
10 changes: 8 additions & 2 deletions packages/wmr/test/transformations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,20 @@ describe('transformations', () => {
it('should remove JS 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);
let actual = (await get(instance, 'jsx-comment.js')).body;
// avoid tests failing due to trimmed whitespace lines
actual = actual.replace(/^\s+?$/gm, '');
expect(actual).toEqual(expected);
});
});

it('should remove JS comments between props #2', async () => {
const expected = await readFile(env, 'jsx-comment-2.expected.js');
await withLog(instance.output, async () => {
expect((await get(instance, 'jsx-comment-2.js')).body).toEqual(expected);
let actual = (await get(instance, 'jsx-comment-2.js')).body;
// avoid tests failing due to trimmed whitespace lines
actual = actual.replace(/^\s+?$/gm, '');
expect(actual).toEqual(expected);
});
});
});
Expand Down

0 comments on commit 3c91013

Please sign in to comment.