Skip to content
This repository was archived by the owner on Jun 8, 2019. It is now read-only.

Remove description after extracting Message Descriptors #74

Merged
merged 2 commits into from
Sep 3, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ $ npm install babel-plugin-react-intl

The default message descriptors for the app's default language will be extracted from: `defineMessages()`, `<FormattedMessage>`, and `<FormattedHTMLMessage>`; all of which are named exports of the React Intl package.

If a message descriptor has a `description`, it'll be removed from the source after it's extracted to save bytes since it isn't used at runtime.

### Via `.babelrc` (Recommended)

**.babelrc**
Expand Down
53 changes: 49 additions & 4 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@ const FUNCTION_NAMES = [

const DESCRIPTOR_PROPS = new Set(['id', 'description', 'defaultMessage']);

export default function () {
const EXTRACTED_TAG = Symbol('ReactIntlExtracted');

export default function ({types: t}) {
function getModuleSourceName(opts) {
return opts.moduleSourceName || 'react-intl';
}
Expand Down Expand Up @@ -153,6 +155,14 @@ export default function () {
return importedNames.some((name) => path.referencesImport(mod, name));
}

function tagAsExtracted(path) {
path.node[EXTRACTED_TAG] = true;
}

function wasExtracted(path) {
return !!path.node[EXTRACTED_TAG];
}

return {
visitor: {
Program: {
Expand Down Expand Up @@ -192,10 +202,13 @@ export default function () {
},

JSXOpeningElement(path, state) {
const {file, opts} = state;
const moduleSourceName = getModuleSourceName(opts);
if (wasExtracted(path)) {
return;
}

let name = path.get('name');
const {file, opts} = state;
const moduleSourceName = getModuleSourceName(opts);
const name = path.get('name');

if (name.referencesImport(moduleSourceName, 'FormattedPlural')) {
file.log.warn(
Expand Down Expand Up @@ -231,7 +244,20 @@ export default function () {
descriptor = evaluateMessageDescriptor(descriptor, {
isJSXSource: true,
});

storeMessage(descriptor, path, state);

// Remove description since it's not used at runtime.
attributes.some((attr) => {
let ketPath = attr.get('name');
if (getMessageDescriptorKey(ketPath) === 'description') {
attr.remove();
return true;
}
});

// Tag the AST node so we don't try to extract it twice.
tagAsExtracted(path);
}
}
},
Expand All @@ -254,6 +280,10 @@ export default function () {
function processMessageObject(messageObj) {
assertObjectExpression(messageObj);

if (wasExtracted(messageObj)) {
return;
}

let properties = messageObj.get('properties');

let descriptor = createMessageDescriptor(
Expand All @@ -266,6 +296,21 @@ export default function () {
// Evaluate the Message Descriptor values, then store it.
descriptor = evaluateMessageDescriptor(descriptor);
storeMessage(descriptor, messageObj, state);

// Remove description since it's not used at runtime.
messageObj.replaceWith(t.objectExpression([
t.objectProperty(
t.stringLiteral('id'),
t.stringLiteral(descriptor.id)
),
t.objectProperty(
t.stringLiteral('defaultMessage'),
t.stringLiteral(descriptor.defaultMessage)
),
]));

// Tag the AST node so we don't try to extract it twice.
tagAsExtracted(messageObj);
}

if (referencesImport(callee, moduleSourceName, FUNCTION_NAMES)) {
Expand Down
3 changes: 1 addition & 2 deletions test/fixtures/FormattedHTMLMessage/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ var Foo = function (_Component) {
value: function render() {
return _react2.default.createElement(_reactIntl.FormattedHTMLMessage, {
id: 'foo.bar.baz',
defaultMessage: '<h1>Hello World!</h1>',
description: 'The default message.'
defaultMessage: '<h1>Hello World!</h1>'
});
}
}]);
Expand Down
3 changes: 1 addition & 2 deletions test/fixtures/FormattedMessage/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ var Foo = function (_Component) {
value: function render() {
return _react2.default.createElement(_reactIntl.FormattedMessage, {
id: 'foo.bar.baz',
defaultMessage: 'Hello World!',
description: 'The default message.'
defaultMessage: 'Hello World!'
});
}
}]);
Expand Down
10 changes: 4 additions & 6 deletions test/fixtures/defineMessages/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ function _inherits(subClass, superClass) { if (typeof superClass !== "function"

var msgs = (0, _reactIntl.defineMessages)({
header: {
id: 'foo.bar.baz',
defaultMessage: 'Hello World!',
description: 'The default message'
'id': 'foo.bar.baz',
'defaultMessage': 'Hello World!'
},
content: {
id: 'foo.bar.biff',
defaultMessage: 'Hello Nurse!',
description: 'Another message'
'id': 'foo.bar.biff',
'defaultMessage': 'Hello Nurse!'
}
});

Expand Down
3 changes: 1 addition & 2 deletions test/fixtures/moduleSourceName/expected.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,7 @@ var Foo = function (_Component) {
null,
_react2.default.createElement(_reactI18n.FormattedMessage, {
id: 'foo.bar.baz',
defaultMessage: 'Hello World!',
description: 'The default message.'
defaultMessage: 'Hello World!'
}),
msgs
);
Expand Down
22 changes: 22 additions & 0 deletions test/fixtures/removeDescriptions/actual.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import React, {Component} from 'react';
import {defineMessages, FormattedMessage} from 'react-intl';

const messages = defineMessages({
foo: {
id: 'greeting-user',
description: 'Greeting the user',
defaultMessage: 'Hello, {name}',
},
});

export default class Foo extends Component {
render() {
return (
<FormattedMessage
id='greeting-world'
description='Greeting to the world'
defaultMessage='Hello World!'
/>
);
}
}
36 changes: 29 additions & 7 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const skipTests = [
'extractSourceLocation',
'moduleSourceName',
'icuSyntax',
'removeDescriptions',
];

const fixturesDir = path.join(__dirname, 'fixtures');
Expand Down Expand Up @@ -74,6 +75,22 @@ describe('options', () => {
}
});

it('removes descriptions when plugin is applied more than once', () => {
const fixtureDir = path.join(fixturesDir, 'removeDescriptions');

try {
transform(path.join(fixtureDir, 'actual.js'), {
enforceDescriptions: true,
}, {
multiplePasses: true,
});
assert(true);
} catch (e) {
console.error(e);
assert(false);
}
});

it('respects moduleSourceName', () => {
const fixtureDir = path.join(fixturesDir, 'moduleSourceName');

Expand Down Expand Up @@ -133,13 +150,18 @@ const BASE_OPTIONS = {
messagesDir: baseDir,
};

function transform(filePath, options = {}) {
function transform(filePath, options = {}, {multiplePasses = false} = {}) {
function getPluginConfig() {
return [plugin, {
...BASE_OPTIONS,
...options,
}];
}

return babel.transformFileSync(filePath, {
plugins: [
[plugin, {
...BASE_OPTIONS,
...options,
}],
],
plugins: multiplePasses ? [
getPluginConfig(),
getPluginConfig(),
] : [getPluginConfig()],
}).code;
}