From 0172f46e3e4b554c25579bb7c3563b2879522d91 Mon Sep 17 00:00:00 2001 From: Tore Hammervoll Date: Thu, 11 Feb 2021 16:42:07 +0100 Subject: [PATCH] feat(babel-plugin-formatjs): support preserving whitespace and newlines --- packages/babel-plugin-formatjs/index.ts | 28 ++-- .../babel-plugin-formatjs/options.schema.json | 3 +- packages/babel-plugin-formatjs/options.ts | 1 + .../tests/__snapshots__/index.test.ts.snap | 122 ++++++++++++++++++ .../actual.js | 79 ++++++++++++ .../babel-plugin-formatjs/tests/index.test.ts | 3 + 6 files changed, 225 insertions(+), 11 deletions(-) create mode 100644 packages/babel-plugin-formatjs/tests/fixtures/defineMessagesPreserveWhitespace/actual.js diff --git a/packages/babel-plugin-formatjs/index.ts b/packages/babel-plugin-formatjs/index.ts index 6b26694ca8..b8ab8b8c76 100644 --- a/packages/babel-plugin-formatjs/index.ts +++ b/packages/babel-plugin-formatjs/index.ts @@ -91,14 +91,17 @@ interface State { function getICUMessageValue( messagePath?: NodePath | NodePath, - {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); @@ -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) { @@ -398,7 +402,8 @@ export default declare((api: any, options: OptionsSchema) => { removeDefaultMessage, idInterpolationPattern, overrideIdFn, - ast, + ast, + preserveWhitespace, } = opts; if (wasExtracted(path)) { return; @@ -447,7 +452,8 @@ export default declare((api: any, options: OptionsSchema) => { true, filename, idInterpolationPattern, - overrideIdFn + overrideIdFn, + preserveWhitespace ); storeMessage( @@ -542,7 +548,8 @@ export default declare((api: any, options: OptionsSchema) => { idInterpolationPattern, removeDefaultMessage, additionalFunctionNames = [], - ast, + ast, + preserveWhitespace, } = opts; const callee = path.get('callee'); @@ -579,7 +586,8 @@ export default declare((api: any, options: OptionsSchema) => { false, filename, idInterpolationPattern, - overrideIdFn + overrideIdFn, + preserveWhitespace ); storeMessage(descriptor, messageDescriptor, opts, filename, messages); diff --git a/packages/babel-plugin-formatjs/options.schema.json b/packages/babel-plugin-formatjs/options.schema.json index 154818c2bb..b939c1de0b 100644 --- a/packages/babel-plugin-formatjs/options.schema.json +++ b/packages/babel-plugin-formatjs/options.schema.json @@ -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 } diff --git a/packages/babel-plugin-formatjs/options.ts b/packages/babel-plugin-formatjs/options.ts index 4b5dcf2652..6a9695ffb3 100644 --- a/packages/babel-plugin-formatjs/options.ts +++ b/packages/babel-plugin-formatjs/options.ts @@ -14,4 +14,5 @@ export interface OptionsSchema { pragma?: string; extractSourceLocation?: boolean; ast?: boolean; + preserveWhitespace?: boolean; } diff --git a/packages/babel-plugin-formatjs/tests/__snapshots__/index.test.ts.snap b/packages/babel-plugin-formatjs/tests/__snapshots__/index.test.ts.snap index 781b0614f0..9af093fdfe 100644 --- a/packages/babel-plugin-formatjs/tests/__snapshots__/index.test.ts.snap +++ b/packages/babel-plugin-formatjs/tests/__snapshots__/index.test.ts.snap @@ -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
+

+ +

+

+ +

+

+ +

+

+ +

+
; + } + +}", + "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'; diff --git a/packages/babel-plugin-formatjs/tests/fixtures/defineMessagesPreserveWhitespace/actual.js b/packages/babel-plugin-formatjs/tests/fixtures/defineMessagesPreserveWhitespace/actual.js new file mode 100644 index 0000000000..a48b611d82 --- /dev/null +++ b/packages/babel-plugin-formatjs/tests/fixtures/defineMessagesPreserveWhitespace/actual.js @@ -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 ( +
+

+ +

+

+ +

+

+ +

+

+ +

+
+ ); + } +} diff --git a/packages/babel-plugin-formatjs/tests/index.test.ts b/packages/babel-plugin-formatjs/tests/index.test.ts index 9bc5557cd0..12fcf3616f 100644 --- a/packages/babel-plugin-formatjs/tests/index.test.ts +++ b/packages/babel-plugin-formatjs/tests/index.test.ts @@ -44,6 +44,9 @@ const TESTS: Record = { removeDefaultMessage: { removeDefaultMessage: true, }, + defineMessagesPreserveWhitespace: { + preserveWhitespace: true, + }, }; describe('emit asserts for: ', () => {