Skip to content

Commit

Permalink
feat(babel-plugin-formatjs): support preserving whitespace and newlines
Browse files Browse the repository at this point in the history
  • Loading branch information
skoging authored and longlho committed Feb 12, 2021
1 parent a1b31bd commit 0172f46
Show file tree
Hide file tree
Showing 6 changed files with 225 additions and 11 deletions.
28 changes: 18 additions & 10 deletions packages/babel-plugin-formatjs/index.ts
Expand Up @@ -91,14 +91,17 @@ interface State {

function getICUMessageValue(
messagePath?: NodePath<StringLiteral> | NodePath<TemplateLiteral>,
{isJSXSource = false} = {}
{isJSXSource = false} = {},
preserveWhitespace?: OptionsSchema['preserveWhitespace']
) {
if (!messagePath) {
return '';
}
const message = getMessageDescriptorValue(messagePath)
.trim()
.replace(/\s+/gm, ' ');
let message = getMessageDescriptorValue(messagePath);

if (!preserveWhitespace) {
message = message.trim().replace(/\s+/gm, ' ');
}

try {
parse(message);
Expand Down Expand Up @@ -193,12 +196,13 @@ function evaluateMessageDescriptor(
isJSXSource = false,
filename: string,
idInterpolationPattern = '[contenthash:5]',
overrideIdFn?: OptionsSchema['overrideIdFn']
overrideIdFn?: OptionsSchema['overrideIdFn'],
preserveWhitespace?: OptionsSchema['preserveWhitespace']
) {
let id = getMessageDescriptorValue(descriptorPath.id);
const defaultMessage = getICUMessageValue(descriptorPath.defaultMessage, {
isJSXSource,
});
}, preserveWhitespace);
const description = getMessageDescriptorValue(descriptorPath.description);

if (overrideIdFn) {
Expand Down Expand Up @@ -398,7 +402,8 @@ export default declare((api: any, options: OptionsSchema) => {
removeDefaultMessage,
idInterpolationPattern,
overrideIdFn,
ast,
ast,
preserveWhitespace,
} = opts;
if (wasExtracted(path)) {
return;
Expand Down Expand Up @@ -447,7 +452,8 @@ export default declare((api: any, options: OptionsSchema) => {
true,
filename,
idInterpolationPattern,
overrideIdFn
overrideIdFn,
preserveWhitespace
);

storeMessage(
Expand Down Expand Up @@ -542,7 +548,8 @@ export default declare((api: any, options: OptionsSchema) => {
idInterpolationPattern,
removeDefaultMessage,
additionalFunctionNames = [],
ast,
ast,
preserveWhitespace,
} = opts;
const callee = path.get('callee');

Expand Down Expand Up @@ -579,7 +586,8 @@ export default declare((api: any, options: OptionsSchema) => {
false,
filename,
idInterpolationPattern,
overrideIdFn
overrideIdFn,
preserveWhitespace
);
storeMessage(descriptor, messageDescriptor, opts, filename, messages);

Expand Down
3 changes: 2 additions & 1 deletion packages/babel-plugin-formatjs/options.schema.json
Expand Up @@ -11,7 +11,8 @@
"additionalFunctionNames": {"type": "array", "items": {"type": "string"}},
"pragma": {"type": "string"},
"extractSourceLocation": {"type": "boolean"},
"ast": {"type": "boolean"}
"ast": {"type": "boolean"},
"preserveWhitespace": {"type": "boolean"}
},
"additionalProperties": false
}
1 change: 1 addition & 0 deletions packages/babel-plugin-formatjs/options.ts
Expand Up @@ -14,4 +14,5 @@ export interface OptionsSchema {
pragma?: string;
extractSourceLocation?: boolean;
ast?: boolean;
preserveWhitespace?: boolean;
}
122 changes: 122 additions & 0 deletions packages/babel-plugin-formatjs/tests/__snapshots__/index.test.ts.snap
Expand Up @@ -323,6 +323,128 @@ export default class Foo extends Component {
}
`;

exports[`emit asserts for: defineMessagesPreserveWhitespace 1`] = `
Object {
"code": "// @react-intl project:amazing
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 {😭} one {# kitten} other {# kittens}}\\"
},
trailingWhitespace: {
\\"id\\": \\"trailing.ws\\",
\\"defaultMessage\\": \\" Some whitespace \\"
},
escaped: {
\\"id\\": \\"escaped.apostrophe\\",
\\"defaultMessage\\": \\"A quoted value ''{value}'\\"
},
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} />
</p>
<p>
<FormattedMessage id=\\"inline.linebreak\\" defaultMessage=\\"formatted message
with linebreak\\" />
</p>
</div>;
}
}",
"data": Object {
"messages": 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": "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
with linebreak",
"description": "foo
bar",
"id": "inline.linebreak",
},
],
"meta": Object {
"project": "amazing",
},
},
}
`;

exports[`emit asserts for: descriptionsAsObjects 1`] = `
Object {
"code": "import React, { Component } from 'react';
Expand Down
@@ -0,0 +1,79 @@
// @react-intl project:amazing
import React, {Component} from 'react';
import {defineMessages, FormattedMessage} 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}'",
},
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`,
},
});

export default class Foo extends Component {
render() {
return (
<div>
<h1>
<FormattedMessage {...msgs.header} />
</h1>
<p>
<FormattedMessage {...msgs.content} />
</p>
<p>
<FormattedMessage {...msgs.kittens} />
</p>
<p>
<FormattedMessage
id="inline.linebreak"
defaultMessage="formatted message
with linebreak"
description="foo
bar"
/>
</p>
</div>
);
}
}
3 changes: 3 additions & 0 deletions packages/babel-plugin-formatjs/tests/index.test.ts
Expand Up @@ -44,6 +44,9 @@ const TESTS: Record<string, OptionsSchema> = {
removeDefaultMessage: {
removeDefaultMessage: true,
},
defineMessagesPreserveWhitespace: {
preserveWhitespace: true,
},
};

describe('emit asserts for: ', () => {
Expand Down

0 comments on commit 0172f46

Please sign in to comment.