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

Commit 0d00e71

Browse files
authored
Merge pull request #73 from yahoo/src-meta
Add extractSourceLocation option
2 parents e491de9 + 4f9f8fc commit 0d00e71

File tree

7 files changed

+119
-9
lines changed

7 files changed

+119
-9
lines changed

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ The default message descriptors for the app's default language will be extracted
2424
{
2525
"plugins": [
2626
["react-intl", {
27-
"messagesDir": "./build/messages/",
28-
"enforceDescriptions": true
27+
"messagesDir": "./build/messages/"
2928
}]
3029
]
3130
}
@@ -35,7 +34,9 @@ The default message descriptors for the app's default language will be extracted
3534

3635
- **`messagesDir`**: The target location where the plugin will output a `.json` file corresponding to each component from which React Intl messages were extracted. If not provided, the extracted message descriptors will only be accessible via Babel's API.
3736

38-
- **`enforceDescriptions`**: Whether or not message declarations _must_ contain a `description` to provide context to translators. Defaults to: `false`.
37+
- **`enforceDescriptions`**: Whether message declarations _must_ contain a `description` to provide context to translators. Defaults to: `false`.
38+
39+
- **`extractSourceLocation`**: Whether the metadata about the location of the message in the source file should be extracted. If `true`, then `file`, `start`, and `end` fields will exist for each extracted message descriptors. Defaults to `false`.
3940

4041
- **`moduleSourceName`**: The ES6 module source name of the React Intl package. Defaults to: `"react-intl"`, but can be changed to another name/path to React Intl.
4142

scripts/build-fixtures.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ const baseDir = p.resolve(`${__dirname}/../test/fixtures`);
77

88
const fixtures = [
99
'defineMessages',
10+
['extractSourceLocation', {
11+
extractSourceLocation: true,
12+
}],
1013
'FormattedHTMLMessage',
1114
'FormattedMessage',
1215
['moduleSourceName', {

src/index.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ export default function () {
107107
}
108108

109109
function storeMessage({id, description, defaultMessage}, path, state) {
110-
const {opts, reactIntl} = state;
110+
const {file, opts, reactIntl} = state;
111111

112112
if (!(id && defaultMessage)) {
113113
throw path.buildCodeFrameError(
@@ -134,7 +134,15 @@ export default function () {
134134
);
135135
}
136136

137-
reactIntl.messages.set(id, {id, description, defaultMessage});
137+
let loc;
138+
if (opts.extractSourceLocation) {
139+
loc = {
140+
file: p.relative(process.cwd(), file.opts.filename),
141+
...path.node.loc,
142+
};
143+
}
144+
145+
reactIntl.messages.set(id, {id, description, defaultMessage, ...loc});
138146
}
139147

140148
function referencesImport(path, mod, importedNames) {
@@ -257,7 +265,7 @@ export default function () {
257265

258266
// Evaluate the Message Descriptor values, then store it.
259267
descriptor = evaluateMessageDescriptor(descriptor);
260-
storeMessage(descriptor, path, state);
268+
storeMessage(descriptor, messageObj, state);
261269
}
262270

263271
if (referencesImport(callee, moduleSourceName, FUNCTION_NAMES)) {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import React, {Component} from 'react';
2+
import {FormattedMessage} from 'react-intl';
3+
4+
export default class Foo extends Component {
5+
render() {
6+
return (
7+
<FormattedMessage
8+
id='foo.bar.baz'
9+
defaultMessage='Hello World!'
10+
/>
11+
);
12+
}
13+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
3+
Object.defineProperty(exports, "__esModule", {
4+
value: true
5+
});
6+
7+
var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
8+
9+
var _react = require('react');
10+
11+
var _react2 = _interopRequireDefault(_react);
12+
13+
var _reactIntl = require('react-intl');
14+
15+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16+
17+
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
18+
19+
function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
20+
21+
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
22+
23+
var Foo = function (_Component) {
24+
_inherits(Foo, _Component);
25+
26+
function Foo() {
27+
_classCallCheck(this, Foo);
28+
29+
return _possibleConstructorReturn(this, (Foo.__proto__ || Object.getPrototypeOf(Foo)).apply(this, arguments));
30+
}
31+
32+
_createClass(Foo, [{
33+
key: 'render',
34+
value: function render() {
35+
return _react2.default.createElement(_reactIntl.FormattedMessage, {
36+
id: 'foo.bar.baz',
37+
defaultMessage: 'Hello World!'
38+
});
39+
}
40+
}]);
41+
42+
return Foo;
43+
}(_react.Component);
44+
45+
exports.default = Foo;
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[
2+
{
3+
"id": "foo.bar.baz",
4+
"defaultMessage": "Hello World!",
5+
"file": "test/fixtures/extractSourceLocation/actual.js",
6+
"start": {
7+
"line": 7,
8+
"column": 12
9+
},
10+
"end": {
11+
"line": 10,
12+
"column": 14
13+
}
14+
}
15+
]

test/index.js

Lines changed: 28 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ const skipTests = [
1212
'.babelrc',
1313
'.DS_Store',
1414
'enforceDescriptions',
15+
'extractSourceLocation',
1516
'moduleSourceName',
1617
'icuSyntax',
1718
];
@@ -49,7 +50,9 @@ describe('options', () => {
4950
const fixtureDir = path.join(fixturesDir, 'enforceDescriptions');
5051

5152
try {
52-
transform(path.join(fixtureDir, 'actual.js'));
53+
transform(path.join(fixtureDir, 'actual.js'), {
54+
enforceDescriptions: true,
55+
});
5356
assert(false);
5457
} catch (e) {
5558
assert(e);
@@ -83,6 +86,30 @@ describe('options', () => {
8386
console.error(e);
8487
assert(false);
8588
}
89+
90+
// Check message output
91+
const expectedMessages = fs.readFileSync(path.join(fixtureDir, 'expected.json'));
92+
const actualMessages = fs.readFileSync(path.join(fixtureDir, 'actual.json'));
93+
assert.equal(trim(actualMessages), trim(expectedMessages));
94+
});
95+
96+
it('respects extractSourceLocation', () => {
97+
const fixtureDir = path.join(fixturesDir, 'extractSourceLocation');
98+
99+
try {
100+
transform(path.join(fixtureDir, 'actual.js'), {
101+
extractSourceLocation: true,
102+
});
103+
assert(true);
104+
} catch (e) {
105+
console.error(e);
106+
assert(false);
107+
}
108+
109+
// Check message output
110+
const expectedMessages = fs.readFileSync(path.join(fixtureDir, 'expected.json'));
111+
const actualMessages = fs.readFileSync(path.join(fixtureDir, 'actual.json'));
112+
assert.equal(trim(actualMessages), trim(expectedMessages));
86113
});
87114
});
88115

@@ -99,13 +126,11 @@ describe('errors', () => {
99126
assert(/Expected .* but "\." found/.test(e.message));
100127
}
101128
});
102-
103129
});
104130

105131

106132
const BASE_OPTIONS = {
107133
messagesDir: baseDir,
108-
enforceDescriptions: true,
109134
};
110135

111136
function transform(filePath, options = {}) {

0 commit comments

Comments
 (0)