Skip to content

Commit 0b864de

Browse files
committed
support pretty mode
1 parent 79a9c25 commit 0b864de

File tree

8 files changed

+99
-55
lines changed

8 files changed

+99
-55
lines changed

jsx.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ interface Options {
88
functions?: boolean;
99
functionNames?: boolean;
1010
skipFalseAttributes?: boolean;
11+
attributeHook?: (name: string) => string;
1112
}
1213

1314
export default function renderToStringPretty(

src/index.d.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,25 @@
11
import { VNode } from 'preact';
22

3-
interface RenderOptions {
4-
attrHook?: (name: string) => string;
3+
interface Options {
4+
attributeHook?: (name: string) => string;
55
}
66

77
export default function renderToString(
88
vnode: VNode,
99
context?: any,
10-
renderOpts?: RenderOptions
10+
options?: Options
1111
): string;
1212

13-
export function render(
14-
vnode: VNode,
15-
context?: any,
16-
renderOpts?: RenderOptions
17-
): string;
13+
export function render(vnode: VNode, context?: any, options?: Options): string;
14+
1815
export function renderToString(
1916
vnode: VNode,
2017
context?: any,
21-
renderOpts?: RenderOptions
18+
options?: Options
2219
): string;
20+
2321
export function renderToStaticMarkup(
2422
vnode: VNode,
2523
context?: any,
26-
renderOpts?: RenderOptions
24+
options?: Options
2725
): string;

src/index.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,15 @@ const isArray = Array.isArray;
2121
const assign = Object.assign;
2222

2323
// Global state for the current render pass
24-
let beforeDiff, afterDiff, renderHook, ummountHook, attrHook;
24+
let beforeDiff, afterDiff, renderHook, ummountHook, attributeHook;
2525

2626
/**
2727
* Render Preact JSX + Components to an HTML string.
2828
* @param {VNode} vnode JSX Element / VNode to render
2929
* @param {Object} [context={}] Initial root context object
3030
* @returns {string} serialized HTML
3131
*/
32-
export function renderToString(vnode, context, renderOpts) {
32+
export function renderToString(vnode, context, opts) {
3333
// Performance optimization: `renderToString` is synchronous and we
3434
// therefore don't execute any effects. To do that we pass an empty
3535
// array to `options._commit` (`__c`). But we can go one step further
@@ -43,7 +43,7 @@ export function renderToString(vnode, context, renderOpts) {
4343
afterDiff = options[DIFFED];
4444
renderHook = options[RENDER];
4545
ummountHook = options.unmount;
46-
attrHook = renderOpts?.attrHook;
46+
attributeHook = opts && opts.attributeHook;
4747

4848
const parent = h(Fragment, null);
4949
parent[CHILDREN] = [vnode];
@@ -400,7 +400,7 @@ function _renderToString(vnode, context, isSvgMode, selectValue, parent) {
400400
}
401401
}
402402

403-
if (attrHook) name = attrHook(name);
403+
if (attributeHook) name = attributeHook(name);
404404

405405
// write this attribute to the buffer
406406
if (v != null && v !== false && typeof v !== 'function') {

src/jsx.d.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,15 @@ interface Options {
88
functions?: boolean;
99
functionNames?: boolean;
1010
skipFalseAttributes?: boolean;
11+
attributeHook?: (name: string) => string;
1112
}
1213

1314
export default function renderToStringPretty(
1415
vnode: VNode,
1516
context?: any,
1617
options?: Options
1718
): string;
19+
1820
export function render(vnode: VNode, context?: any, options?: Options): string;
1921

2022
export function shallowRender(

src/jsx.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ let prettyFormatOpts = {
2525
plugins: [preactPlugin]
2626
};
2727

28-
function attributeHook(name, value, context, opts, isComponent) {
28+
function jsxAttributeHook(name, value, context, opts, isComponent) {
2929
let type = typeof value;
3030

3131
// Use render-to-string's built-in handling for these properties
@@ -60,7 +60,7 @@ function attributeHook(name, value, context, opts, isComponent) {
6060
}
6161

6262
let defaultOpts = {
63-
attributeHook,
63+
jsxAttributeHook,
6464
jsx: true,
6565
xml: false,
6666
functions: true,
@@ -83,7 +83,7 @@ let defaultOpts = {
8383
*/
8484
export default function renderToStringPretty(vnode, context, options) {
8585
const opts = Object.assign({}, defaultOpts, options || {});
86-
if (!opts.jsx) opts.attributeHook = null;
86+
if (!opts.jsx || opts.attributeHook) opts.jsxAttributeHook = null;
8787
return renderToString(vnode, context, opts);
8888
}
8989
export { renderToStringPretty as render };

src/pretty.js

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,8 @@ function _renderToStringPretty(
209209
propChildren,
210210
html;
211211

212+
const attributeHook = opts && opts.attributeHook;
213+
212214
if (props) {
213215
let attrs = Object.keys(props);
214216

@@ -263,8 +265,8 @@ function _renderToStringPretty(
263265
}
264266

265267
let hooked =
266-
opts.attributeHook &&
267-
opts.attributeHook(name, v, context, opts, isComponent);
268+
opts.jsxAttributeHook &&
269+
opts.jsxAttributeHook(name, v, context, opts, isComponent);
268270
if (hooked || hooked === '') {
269271
s = s + hooked;
270272
continue;
@@ -280,6 +282,7 @@ function _renderToStringPretty(
280282
v = name;
281283
// in non-xml mode, allow boolean attributes
282284
if (!opts || !opts.xml) {
285+
if (attributeHook) name = attributeHook(name);
283286
s = s + ' ' + name;
284287
continue;
285288
}
@@ -299,6 +302,7 @@ function _renderToStringPretty(
299302
s = s + ` selected`;
300303
}
301304
}
305+
if (attributeHook) name = attributeHook(name);
302306
s = s + ` ${name}="${encodeEntities(v + '')}"`;
303307
}
304308
}

test/pretty.test.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,4 +196,44 @@ describe('pretty', () => {
196196
it('should not render function children', () => {
197197
expect(prettyRender(<div>{() => {}}</div>)).to.equal('<div></div>');
198198
});
199+
200+
it('transforms attributes with custom attributeHook option', () => {
201+
function attributeHook(name) {
202+
const DASHED_ATTRS = /^(acceptC|httpE|(clip|color|fill|font|glyph|marker|stop|stroke|text|vert)[A-Z])/;
203+
const CAMEL_ATTRS = /^(isP|viewB)/;
204+
const COLON_ATTRS = /^(xlink|xml|xmlns)([A-Z])/;
205+
const CAPITAL_REGEXP = /([A-Z])/g;
206+
if (CAMEL_ATTRS.test(name)) return name;
207+
if (DASHED_ATTRS.test(name))
208+
return name.replace(CAPITAL_REGEXP, '-$1').toLowerCase();
209+
if (COLON_ATTRS.test(name))
210+
return name.replace(CAPITAL_REGEXP, ':$1').toLowerCase();
211+
return name.toLowerCase();
212+
}
213+
214+
const content = (
215+
<html>
216+
<head>
217+
<meta charSet="utf=8" />
218+
<meta httpEquiv="refresh" />
219+
<link rel="preconnect" href="https://foo.com" crossOrigin />
220+
<link rel="preconnect" href="https://bar.com" crossOrigin={false} />
221+
</head>
222+
<body>
223+
<img srcSet="foo.png, foo2.png 2x" />
224+
<svg xmlSpace="preserve" viewBox="0 0 10 10" fillRule="nonzero">
225+
<foreignObject>
226+
<div xlinkHref="#" />
227+
</foreignObject>
228+
</svg>
229+
</body>
230+
</html>
231+
);
232+
233+
const expected =
234+
'<html>\n\t<head>\n\t\t<meta charSet="utf=8" />\n\t\t<meta httpEquiv="refresh" />\n\t\t<link\n\t\t\trel="preconnect"\n\t\t\thref="https://foo.com"\n\t\t\tcrossOrigin={true}\n\t\t />\n\t\t<link\n\t\t\trel="preconnect"\n\t\t\thref="https://bar.com"\n\t\t />\n\t</head>\n\t<body>\n\t\t<img srcSet="foo.png, foo2.png 2x" />\n\t\t<svg\n\t\t\txmlSpace="preserve"\n\t\t\tviewBox="0 0 10 10"\n\t\t\tfillRule="nonzero"\n\t\t>\n\t\t\t<foreignObject>\n\t\t\t\t<div xlinkHref="#"></div>\n\t\t\t</foreignObject>\n\t\t</svg>\n\t</body>\n</html>';
235+
236+
const rendered = prettyRender(content, {}, { attributeHook });
237+
expect(rendered).to.equal(expected);
238+
});
199239
});

test/render.test.js

Lines changed: 35 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1600,45 +1600,44 @@ describe('render', () => {
16001600
});
16011601

16021602
describe('Render Options', () => {
1603-
describe('Attribute Hook', () => {
1604-
it('Transforms attributes with custom attrHook option', () => {
1605-
function attrHook(name) {
1606-
const DASHED_ATTRS = /^(acceptC|httpE|(clip|color|fill|font|glyph|marker|stop|stroke|text|vert)[A-Z])/;
1607-
const CAMEL_ATTRS = /^(isP|viewB)/;
1608-
const COLON_ATTRS = /^(xlink|xml|xmlns)([A-Z])/;
1609-
const CAPITAL_REGEXP = /([A-Z])/g;
1610-
if (CAMEL_ATTRS.test(name)) return name;
1611-
if (DASHED_ATTRS.test(name))
1612-
return name.replace(CAPITAL_REGEXP, '-$1').toLowerCase();
1613-
if (COLON_ATTRS.test(name))
1614-
return name.replace(CAPITAL_REGEXP, ':$1').toLowerCase();
1615-
return name.toLowerCase();
1616-
}
1603+
it('transforms attributes with custom attributeHook option', () => {
1604+
function attributeHook(name) {
1605+
const DASHED_ATTRS = /^(acceptC|httpE|(clip|color|fill|font|glyph|marker|stop|stroke|text|vert)[A-Z])/;
1606+
const CAMEL_ATTRS = /^(isP|viewB)/;
1607+
const COLON_ATTRS = /^(xlink|xml|xmlns)([A-Z])/;
1608+
const CAPITAL_REGEXP = /([A-Z])/g;
1609+
if (CAMEL_ATTRS.test(name)) return name;
1610+
if (DASHED_ATTRS.test(name))
1611+
return name.replace(CAPITAL_REGEXP, '-$1').toLowerCase();
1612+
if (COLON_ATTRS.test(name))
1613+
return name.replace(CAPITAL_REGEXP, ':$1').toLowerCase();
1614+
return name.toLowerCase();
1615+
}
16171616

1618-
const content = (
1619-
<html>
1620-
<head>
1621-
<meta charSet="utf=8" />
1622-
<meta httpEquiv="refresh" />
1623-
<link rel="preconnect" href="https://foo.com" crossOrigin />
1624-
</head>
1625-
<body>
1626-
<img srcSet="foo.png, foo2.png 2x" />
1627-
<svg xmlSpace="preserve" viewBox="0 0 10 10" fillRule="nonzero">
1628-
<foreignObject>
1629-
<div xlinkHref="#" />
1630-
</foreignObject>
1631-
</svg>
1632-
</body>
1633-
</html>
1634-
);
1617+
const content = (
1618+
<html>
1619+
<head>
1620+
<meta charSet="utf=8" />
1621+
<meta httpEquiv="refresh" />
1622+
<link rel="preconnect" href="https://foo.com" crossOrigin />
1623+
<link rel="preconnect" href="https://bar.com" crossOrigin={false} />
1624+
</head>
1625+
<body>
1626+
<img srcSet="foo.png, foo2.png 2x" />
1627+
<svg xmlSpace="preserve" viewBox="0 0 10 10" fillRule="nonzero">
1628+
<foreignObject>
1629+
<div xlinkHref="#" />
1630+
</foreignObject>
1631+
</svg>
1632+
</body>
1633+
</html>
1634+
);
16351635

1636-
const expected =
1637-
'<html><head><meta charset="utf=8"/><meta http-equiv="refresh"/><link rel="preconnect" href="https://foo.com" crossorigin/></head><body><img srcset="foo.png, foo2.png 2x"/><svg xml:space="preserve" viewBox="0 0 10 10" fill-rule="nonzero"><foreignObject><div xlink:href="#"></div></foreignObject></svg></body></html>';
1636+
const expected =
1637+
'<html><head><meta charset="utf=8"/><meta http-equiv="refresh"/><link rel="preconnect" href="https://foo.com" crossorigin/><link rel="preconnect" href="https://bar.com"/></head><body><img srcset="foo.png, foo2.png 2x"/><svg xml:space="preserve" viewBox="0 0 10 10" fill-rule="nonzero"><foreignObject><div xlink:href="#"></div></foreignObject></svg></body></html>';
16381638

1639-
const rendered = render(content, {}, { attrHook });
1640-
expect(rendered).to.equal(expected);
1641-
});
1639+
const rendered = render(content, {}, { attributeHook });
1640+
expect(rendered).to.equal(expected);
16421641
});
16431642
});
16441643
});

0 commit comments

Comments
 (0)