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

Hoist state to file level #96

Merged
merged 1 commit into from
Jan 25, 2017
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
6 changes: 0 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,6 @@ If a message descriptor has a `description`, it'll be removed from the source af

- **`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.

### Via CLI

```sh
$ babel --plugins react-intl script.js
```

### Via Node API

The extract message descriptors are available via the `metadata` property on the object returned from Babel's `transform()` API:
Expand Down
97 changes: 49 additions & 48 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,16 @@ const FUNCTION_NAMES = [

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

const EXTRACTED_TAG = Symbol('ReactIntlExtracted');
const EXTRACTED = Symbol('ReactIntlExtracted');
const MESSAGES = Symbol('ReactIntlMessages');

export default function ({types: t}) {
function getModuleSourceName(opts) {
return opts.moduleSourceName || 'react-intl';
}

function evaluatePath(path) {
let evaluated = path.evaluate();
const evaluated = path.evaluate();
if (evaluated.confident) {
return evaluated.value;
}
Expand Down Expand Up @@ -62,7 +63,7 @@ export default function ({types: t}) {
}

function getICUMessageValue(messagePath, {isJSXSource = false} = {}) {
let message = getMessageDescriptorValue(messagePath);
const message = getMessageDescriptorValue(messagePath);

try {
return printICUMessage(message);
Expand Down Expand Up @@ -90,7 +91,7 @@ export default function ({types: t}) {

function createMessageDescriptor(propPaths) {
return propPaths.reduce((hash, [keyPath, valuePath]) => {
let key = getMessageDescriptorKey(keyPath);
const key = getMessageDescriptorKey(keyPath);

if (DESCRIPTOR_PROPS.has(key)) {
hash[key] = valuePath;
Expand All @@ -102,7 +103,7 @@ export default function ({types: t}) {

function evaluateMessageDescriptor({...descriptor}, {isJSXSource = false} = {}) {
Object.keys(descriptor).forEach((key) => {
let valuePath = descriptor[key];
const valuePath = descriptor[key];

if (key === 'defaultMessage') {
descriptor[key] = getICUMessageValue(valuePath, {isJSXSource});
Expand All @@ -115,16 +116,17 @@ export default function ({types: t}) {
}

function storeMessage({id, description, defaultMessage}, path, state) {
const {file, opts, reactIntl} = state;
const {file, opts} = state;

if (!(id && defaultMessage)) {
throw path.buildCodeFrameError(
'[React Intl] Message Descriptors require an `id` and `defaultMessage`.'
);
}

if (reactIntl.messages.has(id)) {
let existing = reactIntl.messages.get(id);
const messages = file.get(MESSAGES);
if (messages.has(id)) {
const existing = messages.get(id);

if (description !== existing.description ||
defaultMessage !== existing.defaultMessage) {
Expand Down Expand Up @@ -155,7 +157,7 @@ export default function ({types: t}) {
};
}

reactIntl.messages.set(id, {id, description, defaultMessage, ...loc});
messages.set(id, {id, description, defaultMessage, ...loc});
}

function referencesImport(path, mod, importedNames) {
Expand All @@ -167,51 +169,50 @@ export default function ({types: t}) {
}

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

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

return {
visitor: {
Program: {
enter(path, state) {
state.reactIntl = {
messages: new Map(),
};
},

exit(path, state) {
const {file, opts, reactIntl} = state;
const {basename, filename} = file.opts;

let descriptors = [...reactIntl.messages.values()];
file.metadata['react-intl'] = {messages: descriptors};

if (opts.messagesDir && descriptors.length > 0) {
// Make sure the relative path is "absolute" before
// joining it with the `messagesDir`.
let relativePath = p.join(
p.sep,
p.relative(process.cwd(), filename)
);
pre(file) {
if (!file.has(MESSAGES)) {
file.set(MESSAGES, new Map());
}
},

let messagesFilename = p.join(
opts.messagesDir,
p.dirname(relativePath),
basename + '.json'
);
post(file) {
const {opts} = this;
const {basename, filename} = file.opts;

let messagesFile = JSON.stringify(descriptors, null, 2);
const messages = file.get(MESSAGES);
const descriptors = [...messages.values()];
file.metadata['react-intl'] = {messages: descriptors};

mkdirpSync(p.dirname(messagesFilename));
writeFileSync(messagesFilename, messagesFile);
}
},
},
if (opts.messagesDir && descriptors.length > 0) {
// Make sure the relative path is "absolute" before
// joining it with the `messagesDir`.
const relativePath = p.join(
p.sep,
p.relative(process.cwd(), filename)
);

const messagesFilename = p.join(
opts.messagesDir,
p.dirname(relativePath),
basename + '.json'
);

const messagesFile = JSON.stringify(descriptors, null, 2);

mkdirpSync(p.dirname(messagesFilename));
writeFileSync(messagesFilename, messagesFile);
}
},

visitor: {
JSXOpeningElement(path, state) {
if (wasExtracted(path)) {
return;
Expand All @@ -232,7 +233,7 @@ export default function ({types: t}) {
}

if (referencesImport(name, moduleSourceName, COMPONENT_NAMES)) {
let attributes = path.get('attributes')
const attributes = path.get('attributes')
.filter((attr) => attr.isJSXAttribute());

let descriptor = createMessageDescriptor(
Expand Down Expand Up @@ -260,7 +261,7 @@ export default function ({types: t}) {

// Remove description since it's not used at runtime.
attributes.some((attr) => {
let ketPath = attr.get('name');
const ketPath = attr.get('name');
if (getMessageDescriptorKey(ketPath) === 'description') {
attr.remove();
return true;
Expand Down Expand Up @@ -295,7 +296,7 @@ export default function ({types: t}) {
return;
}

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

let descriptor = createMessageDescriptor(
properties.map((prop) => [
Expand Down Expand Up @@ -325,7 +326,7 @@ export default function ({types: t}) {
}

if (referencesImport(callee, moduleSourceName, FUNCTION_NAMES)) {
let messagesObj = path.get('arguments')[0];
const messagesObj = path.get('arguments')[0];

assertObjectExpression(messagesObj);

Expand Down