Skip to content

Commit

Permalink
feat(@formatjs/ts-transformer): support preserving whitespace and new…
Browse files Browse the repository at this point in the history
…lines
  • Loading branch information
skoging authored and longlho committed Feb 12, 2021
1 parent 01e10d3 commit 99e3b89
Show file tree
Hide file tree
Showing 4 changed files with 193 additions and 2 deletions.
8 changes: 6 additions & 2 deletions packages/ts-transformer/src/transform.ts
Expand Up @@ -129,6 +129,10 @@ export interface Opts {
* This is no-op if `removeDefaultMessage` is `true`
*/
ast?: boolean;
/**
* Whether to preserve whitespace and newlines.
*/
preserveWhitespace?: boolean;
}

const DEFAULT_OPTS: Omit<Opts, 'program'> = {
Expand Down Expand Up @@ -197,7 +201,7 @@ function extractMessageDescriptor(
| typescript.ObjectLiteralExpression
| typescript.JsxOpeningElement
| typescript.JsxSelfClosingElement,
{overrideIdFn, extractSourceLocation}: Opts,
{overrideIdFn, extractSourceLocation, preserveWhitespace}: Opts,
sf: typescript.SourceFile
): MessageDescriptor | undefined {
let properties:
Expand Down Expand Up @@ -306,7 +310,7 @@ function extractMessageDescriptor(
return;
}

if (msg.defaultMessage) {
if (msg.defaultMessage && !preserveWhitespace) {
msg.defaultMessage = msg.defaultMessage.trim().replace(/\s+/gm, ' ');
}
if (msg.defaultMessage && overrideIdFn) {
Expand Down
96 changes: 96 additions & 0 deletions packages/ts-transformer/tests/__snapshots__/index.test.ts.snap
Expand Up @@ -246,6 +246,102 @@ export default class Foo extends Component {
}
`;
exports[`emit asserts for defineMessagesPreserveWhitespace 1`] = `
Object {
"code": "// @react-intl project:foo file:bar
import React, { Component } from 'react';
import { defineMessages, FormattedMessage } from 'react-intl';
const msgs = defineMessages({ header: { id: \\"foo.bar.baz\\", defaultMessage: \\"Hello World!\\" }, content: { id: \\"foo.bar.biff\\", defaultMessage: \\"Hello Nurse!\\" }, kittens: { id: \\"app.home.kittens\\", defaultMessage: \\"{count, plural, =0 {\\\\uD83D\\\\uDE2D} one {# kitten} other {# kittens}}\\" }, trailingWhitespace: { id: \\"trailing.ws\\", defaultMessage: \\" Some whitespace \\" }, escaped: { id: \\"escaped.apostrophe\\", defaultMessage: \\"A quoted value ''{value}'\\" }, quoted: { id: \\"escaped.apostrophe\\", defaultMessage: \\"What's going on\\" }, newline: { id: \\"newline\\", defaultMessage: \\"this is a message\\" }, linebreak: { id: \\"linebreak\\", defaultMessage: \\"this is\\\\na message\\" }, templateLinebreak: { id: \\"templateLinebreak\\", defaultMessage: \\"this is\\\\n a message\\" } });
export default class Foo extends Component {
render() {
return (<div>
<h1>
<FormattedMessage {...msgs.header}/>
</h1>
<p>
<FormattedMessage {...msgs.content}/>
</p>
<p>
<FormattedMessage {...msgs.kittens}/>
<FormattedMessage id=\\"inline\\" defaultMessage=\\"formatted message\\"/>
<FormattedMessage id=\\"inline.linebreak\\" defaultMessage=\\"formatted message&#xA;&#x9;&#x9;&#x9;&#x9;&#x9;&#x9;with linebreak\\"/>
</p>
</div>);
}
}
",
"meta": Object {
"file": "bar",
"project": "foo",
},
"msgs": Array [
Object {
"defaultMessage": "Hello World!",
"description": "The default message",
"id": "foo.bar.baz",
},
Object {
"defaultMessage": "Hello Nurse!",
"description": "Another message",
"id": "foo.bar.biff",
},
Object {
"defaultMessage": "{count, plural, =0 {😭} one {# kitten} other {# kittens}}",
"description": "Counts kittens",
"id": "app.home.kittens",
},
Object {
"defaultMessage": " Some whitespace ",
"description": "Whitespace",
"id": "trailing.ws",
},
Object {
"defaultMessage": "A quoted value ''{value}'",
"description": "Escaped apostrophe",
"id": "escaped.apostrophe",
},
Object {
"defaultMessage": "What's going on",
"description": "Escaped apostrophe",
"id": "escaped.apostrophe",
},
Object {
"defaultMessage": "this is a message",
"description": "this is a description",
"id": "newline",
},
Object {
"defaultMessage": "this is
a message",
"description": "this is
a
description",
"id": "linebreak",
},
Object {
"defaultMessage": "this is
a message",
"description": "this is
a
description",
"id": "templateLinebreak",
},
Object {
"defaultMessage": "formatted message",
"description": "foo",
"id": "inline",
},
Object {
"defaultMessage": "formatted message
with linebreak",
"description": "foo
bar",
"id": "inline.linebreak",
},
],
}
`;
exports[`emit asserts for extractFromFormatMessage 1`] = `
Object {
"code": "import React, { Component } from 'react';
Expand Down
@@ -0,0 +1,87 @@
// @react-intl project:foo file:bar
import React, {Component} from 'react';
import {defineMessages, FormattedMessage, defineMessage} from 'react-intl';

const msgs = defineMessages({
header: {
id: 'foo.bar.baz',
defaultMessage: 'Hello World!',
description: 'The default message',
},
content: {
id: 'foo.bar.biff',
defaultMessage: 'Hello Nurse!',
description: 'Another message',
},
kittens: {
id: 'app.home.kittens',
description: 'Counts kittens',
defaultMessage: '{count, plural, =0 {😭} one {# kitten} other {# kittens}}',
},
trailingWhitespace: {
id: 'trailing.ws',
description: 'Whitespace',
defaultMessage: ' Some whitespace ',
},
escaped: {
id: 'escaped.apostrophe',
description: 'Escaped apostrophe',
defaultMessage: "A quoted value ''{value}'",
},
quoted: {
id: 'escaped.apostrophe',
description: 'Escaped apostrophe',
defaultMessage: "What's going on",
},
newline: {
id: 'newline',
description: 'this is \
a \
description',
defaultMessage: 'this is \
a message',
},
linebreak: {
id: 'linebreak',
description: 'this is\na\ndescription',
defaultMessage: 'this is\na message',
},
templateLinebreak: {
id: 'templateLinebreak',
description: `this is
a
description`,
defaultMessage: `this is
a message`,
},
} as const);

export default class Foo extends Component {
render() {
return (
<div>
<h1>
<FormattedMessage {...msgs.header} />
</h1>
<p>
<FormattedMessage {...msgs.content} />
</p>
<p>
<FormattedMessage {...msgs.kittens} />
<FormattedMessage
id="inline"
defaultMessage="formatted message"
description="foo"
/>
<FormattedMessage
id="inline.linebreak"
defaultMessage="formatted message
with linebreak"
description="foo
bar"
/>
</p>
</div>
);
}
}
4 changes: 4 additions & 0 deletions packages/ts-transformer/tests/index.test.ts
Expand Up @@ -56,6 +56,10 @@ const FILES_TO_TESTS: Record<string, Partial<Opts>> = {
overrideIdFn: '[name]-[hash:base64:5]',
},
removeDescription: {},
defineMessagesPreserveWhitespace: {
pragma: 'react-intl',
preserveWhitespace: true,
},
};

const FIXTURES_DIR = join(__dirname, 'fixtures');
Expand Down

0 comments on commit 99e3b89

Please sign in to comment.