babel-plugin-i18next-extract
Advanced tools
Comparing version 0.1.0-alpha.2 to 0.1.0-alpha.3
@@ -34,23 +34,24 @@ import i18next from 'i18next'; | ||
/** | ||
* Given a Babel BaseComment, try to extract a comment hint. | ||
* Given a Babel BaseComment, extract base comment hints. | ||
* @param baseComment babel comment | ||
* @returns A comment hint without line interval information. | ||
* @yields Comment hint without line interval information. | ||
*/ | ||
function extractCommentHint(baseComment) { | ||
const trimmedValue = baseComment.value.trim(); | ||
const keyword = trimmedValue.split(/\s+/)[0]; | ||
const value = trimmedValue.split(/\s+(.+)/)[1] || ''; | ||
for (let [commentHintType, commentHintKeywords] of Object.entries(COMMENT_HINTS_KEYWORDS)) { | ||
for (let [commentHintScope, commentHintKeyword] of Object.entries(commentHintKeywords)) { | ||
if (keyword === commentHintKeyword) { | ||
return { | ||
type: commentHintType, | ||
scope: commentHintScope, | ||
value, | ||
baseComment: baseComment, | ||
}; | ||
function* extractCommentHintFromBaseComment(baseComment) { | ||
for (const line of baseComment.value.split(/\r?\n/)) { | ||
const trimmedValue = line.trim(); | ||
const keyword = trimmedValue.split(/\s+/)[0]; | ||
const value = trimmedValue.split(/\s+(.+)/)[1] || ''; | ||
for (let [commentHintType, commentHintKeywords] of Object.entries(COMMENT_HINTS_KEYWORDS)) { | ||
for (let [commentHintScope, commentHintKeyword] of Object.entries(commentHintKeywords)) { | ||
if (keyword === commentHintKeyword) { | ||
yield { | ||
type: commentHintType, | ||
scope: commentHintScope, | ||
value, | ||
baseComment: baseComment, | ||
}; | ||
} | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
@@ -74,4 +75,4 @@ /** | ||
result.push({ | ||
startLine: commentHint.baseComment.loc.start.line + 1, | ||
stopLine: commentHint.baseComment.loc.start.line + 1, | ||
startLine: commentHint.baseComment.loc.end.line + 1, | ||
stopLine: commentHint.baseComment.loc.end.line + 1, | ||
...commentHint, | ||
@@ -104,11 +105,7 @@ }); | ||
function parseCommentHints(baseComments) { | ||
const result = Array(); | ||
const baseCommentHints = Array(); | ||
for (const baseComment of baseComments) { | ||
const commentHint = extractCommentHint(baseComment); | ||
if (commentHint === null) { | ||
continue; | ||
} | ||
result.push(commentHint); | ||
baseCommentHints.push(...extractCommentHintFromBaseComment(baseComment)); | ||
} | ||
return computeCommentHintsIntervals(result); | ||
return computeCommentHintsIntervals(baseCommentHints); | ||
} | ||
@@ -940,2 +937,216 @@ /** | ||
/** | ||
* Find whether a given function or class is wrapped with "withTranslation" HOC | ||
* somewhere. | ||
* @param path Function or class declaration node path. | ||
* @returns "withTranslation()()" call expression if found. Else null. | ||
*/ | ||
function findWithTranslationHOCCallExpression(path) { | ||
let functionIdentifier = path.get('id'); | ||
if (!Array.isArray(functionIdentifier) && | ||
!functionIdentifier.isIdentifier() && | ||
path.parentPath.isVariableDeclarator()) { | ||
// It doesn't look like "function MyComponent(…)" | ||
// but like be "const MyComponent = (…) => …" or "const MyComponent = function(…) { … }" | ||
functionIdentifier = path.parentPath.get('id'); | ||
} | ||
if (Array.isArray(functionIdentifier) || !functionIdentifier.isIdentifier()) | ||
return null; | ||
// Try to find a withTranslation() call in parent scope | ||
for (const refPath of path.parentPath.scope.bindings[functionIdentifier.node.name].referencePaths) { | ||
const callExpr = refPath.findParent(parentPath => { | ||
if (!parentPath.isCallExpression()) | ||
return false; | ||
const callee = parentPath.get('callee'); | ||
return (callee.isCallExpression() && | ||
referencesImport(callee.get('callee'), 'react-i18next', 'withTranslation')); | ||
}); | ||
if (callExpr !== null) { | ||
return callExpr.get('callee'); | ||
} | ||
} | ||
return null; | ||
} | ||
/** | ||
* Try to find "t" in an object spread. Useful when looking for the "t" key | ||
* in a spread object. e.g. const {t} = props; | ||
* | ||
* @param path object pattern | ||
* @returns t identifier or null of it was not found in the object pattern. | ||
*/ | ||
function findTFunctionIdentifierInObjectPattern(path) { | ||
const props = path.get('properties'); | ||
for (const prop of props) { | ||
if (prop.isObjectProperty()) { | ||
const key = prop.get('key'); | ||
if (!Array.isArray(key) && key.isIdentifier() && key.node.name === 't') { | ||
return key; | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
/** | ||
* Find T function calls from a props assignment. Prop assignment can occur | ||
* in function parameters (i.e. "function Component(props)" or | ||
* "function Component({t})") or in a variable declarator (i.e. | ||
* "const props = …" or "const {t} = props"). | ||
* | ||
* @param propsId identifier for the prop assignment. e.g. "props" or "{t}" | ||
* @returns Call expressions to t function. | ||
*/ | ||
function findTFunctionCallsFromPropsAssignment(propsId) { | ||
const tReferences = Array(); | ||
if (propsId.isObjectPattern()) { | ||
// got "function MyComponent({t, other, props})" | ||
// or "const {t, other, props} = this.props" | ||
// we want to find references to "t" | ||
const tFunctionIdentifier = findTFunctionIdentifierInObjectPattern(propsId); | ||
if (tFunctionIdentifier === null) | ||
return []; | ||
const tBinding = tFunctionIdentifier.scope.bindings[tFunctionIdentifier.node.name]; | ||
if (!tBinding) | ||
return []; | ||
tReferences.push(...tBinding.referencePaths); | ||
} | ||
else if (propsId.isIdentifier()) { | ||
// got "function MyComponent(props)" | ||
// or "const props = this.props" | ||
// we want to find references to props.t | ||
const references = propsId.scope.bindings[propsId.node.name].referencePaths; | ||
for (const reference of references) { | ||
if (reference.parentPath.isMemberExpression()) { | ||
const prop = reference.parentPath.get('property'); | ||
if (!Array.isArray(prop) && prop.node.name === 't') { | ||
tReferences.push(reference.parentPath); | ||
} | ||
} | ||
} | ||
} | ||
// We have candidates. Let's see if t references are actual calls to the t | ||
// function | ||
const tCalls = Array(); | ||
for (const tCall of tReferences) { | ||
if (tCall.parentPath.isCallExpression()) { | ||
tCalls.push(tCall.parentPath); | ||
} | ||
} | ||
return tCalls; | ||
} | ||
/** | ||
* Find all t function calls in a class component. | ||
* @param path node path to the class component. | ||
*/ | ||
function findTFunctionCallsInClassComponent(path) { | ||
const result = Array(); | ||
const thisVisitor = { | ||
ThisExpression(path) { | ||
if (!path.parentPath.isMemberExpression()) | ||
return; | ||
const propProperty = path.parentPath.get('property'); | ||
if (Array.isArray(propProperty) || !propProperty.isIdentifier()) | ||
return; | ||
if (propProperty.node.name !== 'props') | ||
return; | ||
// Ok, this is interesting, we have something with "this.props" | ||
if (path.parentPath.parentPath.isMemberExpression()) { | ||
// We have something in the form "this.props.xxxx". | ||
const tIdentifier = path.parentPath.parentPath.get('property'); | ||
if (Array.isArray(tIdentifier) || !tIdentifier.isIdentifier()) | ||
return; | ||
if (tIdentifier.node.name !== 't') | ||
return; | ||
// We have something in the form "this.props.t". Let's see if it's an | ||
// actual function call or an assignment. | ||
const tExpression = path.parentPath.parentPath.parentPath; | ||
if (tExpression.isCallExpression()) { | ||
// Simple case. Direct call to "this.props.t()" | ||
result.push(tExpression); | ||
} | ||
else if (tExpression.isVariableDeclarator()) { | ||
// Hard case. const t = this.props.t; | ||
// Let's loop through all references to t. | ||
const id = tExpression.get('id'); | ||
if (!id.isIdentifier()) | ||
return; | ||
for (const reference of id.scope.bindings[id.node.name] | ||
.referencePaths) { | ||
if (reference.parentPath.isCallExpression()) { | ||
result.push(reference.parentPath); | ||
} | ||
} | ||
} | ||
} | ||
else if (path.parentPath.parentPath.isVariableDeclarator()) { | ||
// We have something in the form "const props = this.props" | ||
// Or "const {t} = this.props" | ||
const id = path.parentPath.parentPath.get('id'); | ||
result.push(...findTFunctionCallsFromPropsAssignment(id)); | ||
} | ||
}, | ||
}; | ||
path.traverse(thisVisitor); | ||
return result; | ||
} | ||
/** | ||
* Find t function calls in a function component. | ||
* @param path node path to the function component. | ||
*/ | ||
function findTFunctionCallsInFunctionComponent(path) { | ||
const propsParam = path.get('params')[0]; | ||
if (propsParam === undefined) | ||
return []; | ||
return findTFunctionCallsFromPropsAssignment(propsParam); | ||
} | ||
/** | ||
* Parse function or class declaration (likely components) to find whether | ||
* they are wrapped with "withTranslation()" HOC, and if so, extract all the | ||
* translations that come from the "t" function injected in the component | ||
* properties. | ||
* | ||
* @param path node path to the component | ||
* @param config plugin configuration | ||
* @param commentHints parsed comment hints | ||
*/ | ||
function extractWithTranslationHOC(path, config, commentHints = []) { | ||
// Detect if this component is wrapped with withTranslation() somewhere | ||
const withTranslationCallExpression = findWithTranslationHOCCallExpression(path); | ||
if (withTranslationCallExpression === null) | ||
return []; | ||
let tCalls; | ||
if (path.isClassDeclaration()) { | ||
tCalls = findTFunctionCallsInClassComponent(path); | ||
} | ||
else { | ||
tCalls = findTFunctionCallsInFunctionComponent(path); | ||
} | ||
// Extract namespace | ||
let ns; | ||
const nsCommentHint = getCommentHintForPath(withTranslationCallExpression, 'NAMESPACE', commentHints); | ||
if (nsCommentHint) { | ||
// We got a comment hint, take its value as namespace. | ||
ns = nsCommentHint.value; | ||
} | ||
else { | ||
// Otherwise, try to get namespace from arguments. | ||
const namespaceArgument = withTranslationCallExpression.get('arguments')[0]; | ||
ns = getFirstOrNull(evaluateIfConfident(namespaceArgument)); | ||
} | ||
let keys = Array(); | ||
for (const tCall of tCalls) { | ||
keys = [ | ||
...keys, | ||
...extractTFunction(tCall, config, commentHints, true).map(k => ({ | ||
// Add namespace if it was not explicitely set in t() call. | ||
...k, | ||
parsedOptions: { | ||
...k.parsedOptions, | ||
ns: k.parsedOptions.ns || ns, | ||
}, | ||
})), | ||
]; | ||
} | ||
return keys; | ||
} | ||
// We have to store which nodes were extracted because the plugin might be called multiple times | ||
@@ -971,10 +1182,8 @@ // by Babel and the state would be lost across calls. | ||
catch (err) { | ||
if (err instanceof ExtractionError) { | ||
// eslint-disable-next-line no-console | ||
console.warn(`${PLUGIN_NAME}: Extraction error in ${filename} at line ` + | ||
`${lineNumber}. ${err.message}`); | ||
} | ||
else { | ||
if (!(err instanceof ExtractionError)) { | ||
throw err; | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn(`${PLUGIN_NAME}: Extraction error in ${filename} at line ` + | ||
`${lineNumber}. ${err.message}`); | ||
} | ||
@@ -997,2 +1206,14 @@ } | ||
}, | ||
ClassDeclaration(path, state) { | ||
const extractState = this.I18NextExtract; | ||
handleExtraction(path, state, collect => { | ||
collect(extractWithTranslationHOC(path, extractState.config, extractState.commentHints)); | ||
}); | ||
}, | ||
Function(path, state) { | ||
const extractState = this.I18NextExtract; | ||
handleExtraction(path, state, collect => { | ||
collect(extractWithTranslationHOC(path, extractState.config, extractState.commentHints)); | ||
}); | ||
}, | ||
}; | ||
@@ -999,0 +1220,0 @@ function plugin (api) { |
283
lib/index.js
@@ -38,23 +38,24 @@ 'use strict'; | ||
/** | ||
* Given a Babel BaseComment, try to extract a comment hint. | ||
* Given a Babel BaseComment, extract base comment hints. | ||
* @param baseComment babel comment | ||
* @returns A comment hint without line interval information. | ||
* @yields Comment hint without line interval information. | ||
*/ | ||
function extractCommentHint(baseComment) { | ||
const trimmedValue = baseComment.value.trim(); | ||
const keyword = trimmedValue.split(/\s+/)[0]; | ||
const value = trimmedValue.split(/\s+(.+)/)[1] || ''; | ||
for (let [commentHintType, commentHintKeywords] of Object.entries(COMMENT_HINTS_KEYWORDS)) { | ||
for (let [commentHintScope, commentHintKeyword] of Object.entries(commentHintKeywords)) { | ||
if (keyword === commentHintKeyword) { | ||
return { | ||
type: commentHintType, | ||
scope: commentHintScope, | ||
value, | ||
baseComment: baseComment, | ||
}; | ||
function* extractCommentHintFromBaseComment(baseComment) { | ||
for (const line of baseComment.value.split(/\r?\n/)) { | ||
const trimmedValue = line.trim(); | ||
const keyword = trimmedValue.split(/\s+/)[0]; | ||
const value = trimmedValue.split(/\s+(.+)/)[1] || ''; | ||
for (let [commentHintType, commentHintKeywords] of Object.entries(COMMENT_HINTS_KEYWORDS)) { | ||
for (let [commentHintScope, commentHintKeyword] of Object.entries(commentHintKeywords)) { | ||
if (keyword === commentHintKeyword) { | ||
yield { | ||
type: commentHintType, | ||
scope: commentHintScope, | ||
value, | ||
baseComment: baseComment, | ||
}; | ||
} | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
@@ -78,4 +79,4 @@ /** | ||
result.push({ | ||
startLine: commentHint.baseComment.loc.start.line + 1, | ||
stopLine: commentHint.baseComment.loc.start.line + 1, | ||
startLine: commentHint.baseComment.loc.end.line + 1, | ||
stopLine: commentHint.baseComment.loc.end.line + 1, | ||
...commentHint, | ||
@@ -108,11 +109,7 @@ }); | ||
function parseCommentHints(baseComments) { | ||
const result = Array(); | ||
const baseCommentHints = Array(); | ||
for (const baseComment of baseComments) { | ||
const commentHint = extractCommentHint(baseComment); | ||
if (commentHint === null) { | ||
continue; | ||
} | ||
result.push(commentHint); | ||
baseCommentHints.push(...extractCommentHintFromBaseComment(baseComment)); | ||
} | ||
return computeCommentHintsIntervals(result); | ||
return computeCommentHintsIntervals(baseCommentHints); | ||
} | ||
@@ -944,2 +941,216 @@ /** | ||
/** | ||
* Find whether a given function or class is wrapped with "withTranslation" HOC | ||
* somewhere. | ||
* @param path Function or class declaration node path. | ||
* @returns "withTranslation()()" call expression if found. Else null. | ||
*/ | ||
function findWithTranslationHOCCallExpression(path) { | ||
let functionIdentifier = path.get('id'); | ||
if (!Array.isArray(functionIdentifier) && | ||
!functionIdentifier.isIdentifier() && | ||
path.parentPath.isVariableDeclarator()) { | ||
// It doesn't look like "function MyComponent(…)" | ||
// but like be "const MyComponent = (…) => …" or "const MyComponent = function(…) { … }" | ||
functionIdentifier = path.parentPath.get('id'); | ||
} | ||
if (Array.isArray(functionIdentifier) || !functionIdentifier.isIdentifier()) | ||
return null; | ||
// Try to find a withTranslation() call in parent scope | ||
for (const refPath of path.parentPath.scope.bindings[functionIdentifier.node.name].referencePaths) { | ||
const callExpr = refPath.findParent(parentPath => { | ||
if (!parentPath.isCallExpression()) | ||
return false; | ||
const callee = parentPath.get('callee'); | ||
return (callee.isCallExpression() && | ||
referencesImport(callee.get('callee'), 'react-i18next', 'withTranslation')); | ||
}); | ||
if (callExpr !== null) { | ||
return callExpr.get('callee'); | ||
} | ||
} | ||
return null; | ||
} | ||
/** | ||
* Try to find "t" in an object spread. Useful when looking for the "t" key | ||
* in a spread object. e.g. const {t} = props; | ||
* | ||
* @param path object pattern | ||
* @returns t identifier or null of it was not found in the object pattern. | ||
*/ | ||
function findTFunctionIdentifierInObjectPattern(path) { | ||
const props = path.get('properties'); | ||
for (const prop of props) { | ||
if (prop.isObjectProperty()) { | ||
const key = prop.get('key'); | ||
if (!Array.isArray(key) && key.isIdentifier() && key.node.name === 't') { | ||
return key; | ||
} | ||
} | ||
} | ||
return null; | ||
} | ||
/** | ||
* Find T function calls from a props assignment. Prop assignment can occur | ||
* in function parameters (i.e. "function Component(props)" or | ||
* "function Component({t})") or in a variable declarator (i.e. | ||
* "const props = …" or "const {t} = props"). | ||
* | ||
* @param propsId identifier for the prop assignment. e.g. "props" or "{t}" | ||
* @returns Call expressions to t function. | ||
*/ | ||
function findTFunctionCallsFromPropsAssignment(propsId) { | ||
const tReferences = Array(); | ||
if (propsId.isObjectPattern()) { | ||
// got "function MyComponent({t, other, props})" | ||
// or "const {t, other, props} = this.props" | ||
// we want to find references to "t" | ||
const tFunctionIdentifier = findTFunctionIdentifierInObjectPattern(propsId); | ||
if (tFunctionIdentifier === null) | ||
return []; | ||
const tBinding = tFunctionIdentifier.scope.bindings[tFunctionIdentifier.node.name]; | ||
if (!tBinding) | ||
return []; | ||
tReferences.push(...tBinding.referencePaths); | ||
} | ||
else if (propsId.isIdentifier()) { | ||
// got "function MyComponent(props)" | ||
// or "const props = this.props" | ||
// we want to find references to props.t | ||
const references = propsId.scope.bindings[propsId.node.name].referencePaths; | ||
for (const reference of references) { | ||
if (reference.parentPath.isMemberExpression()) { | ||
const prop = reference.parentPath.get('property'); | ||
if (!Array.isArray(prop) && prop.node.name === 't') { | ||
tReferences.push(reference.parentPath); | ||
} | ||
} | ||
} | ||
} | ||
// We have candidates. Let's see if t references are actual calls to the t | ||
// function | ||
const tCalls = Array(); | ||
for (const tCall of tReferences) { | ||
if (tCall.parentPath.isCallExpression()) { | ||
tCalls.push(tCall.parentPath); | ||
} | ||
} | ||
return tCalls; | ||
} | ||
/** | ||
* Find all t function calls in a class component. | ||
* @param path node path to the class component. | ||
*/ | ||
function findTFunctionCallsInClassComponent(path) { | ||
const result = Array(); | ||
const thisVisitor = { | ||
ThisExpression(path) { | ||
if (!path.parentPath.isMemberExpression()) | ||
return; | ||
const propProperty = path.parentPath.get('property'); | ||
if (Array.isArray(propProperty) || !propProperty.isIdentifier()) | ||
return; | ||
if (propProperty.node.name !== 'props') | ||
return; | ||
// Ok, this is interesting, we have something with "this.props" | ||
if (path.parentPath.parentPath.isMemberExpression()) { | ||
// We have something in the form "this.props.xxxx". | ||
const tIdentifier = path.parentPath.parentPath.get('property'); | ||
if (Array.isArray(tIdentifier) || !tIdentifier.isIdentifier()) | ||
return; | ||
if (tIdentifier.node.name !== 't') | ||
return; | ||
// We have something in the form "this.props.t". Let's see if it's an | ||
// actual function call or an assignment. | ||
const tExpression = path.parentPath.parentPath.parentPath; | ||
if (tExpression.isCallExpression()) { | ||
// Simple case. Direct call to "this.props.t()" | ||
result.push(tExpression); | ||
} | ||
else if (tExpression.isVariableDeclarator()) { | ||
// Hard case. const t = this.props.t; | ||
// Let's loop through all references to t. | ||
const id = tExpression.get('id'); | ||
if (!id.isIdentifier()) | ||
return; | ||
for (const reference of id.scope.bindings[id.node.name] | ||
.referencePaths) { | ||
if (reference.parentPath.isCallExpression()) { | ||
result.push(reference.parentPath); | ||
} | ||
} | ||
} | ||
} | ||
else if (path.parentPath.parentPath.isVariableDeclarator()) { | ||
// We have something in the form "const props = this.props" | ||
// Or "const {t} = this.props" | ||
const id = path.parentPath.parentPath.get('id'); | ||
result.push(...findTFunctionCallsFromPropsAssignment(id)); | ||
} | ||
}, | ||
}; | ||
path.traverse(thisVisitor); | ||
return result; | ||
} | ||
/** | ||
* Find t function calls in a function component. | ||
* @param path node path to the function component. | ||
*/ | ||
function findTFunctionCallsInFunctionComponent(path) { | ||
const propsParam = path.get('params')[0]; | ||
if (propsParam === undefined) | ||
return []; | ||
return findTFunctionCallsFromPropsAssignment(propsParam); | ||
} | ||
/** | ||
* Parse function or class declaration (likely components) to find whether | ||
* they are wrapped with "withTranslation()" HOC, and if so, extract all the | ||
* translations that come from the "t" function injected in the component | ||
* properties. | ||
* | ||
* @param path node path to the component | ||
* @param config plugin configuration | ||
* @param commentHints parsed comment hints | ||
*/ | ||
function extractWithTranslationHOC(path, config, commentHints = []) { | ||
// Detect if this component is wrapped with withTranslation() somewhere | ||
const withTranslationCallExpression = findWithTranslationHOCCallExpression(path); | ||
if (withTranslationCallExpression === null) | ||
return []; | ||
let tCalls; | ||
if (path.isClassDeclaration()) { | ||
tCalls = findTFunctionCallsInClassComponent(path); | ||
} | ||
else { | ||
tCalls = findTFunctionCallsInFunctionComponent(path); | ||
} | ||
// Extract namespace | ||
let ns; | ||
const nsCommentHint = getCommentHintForPath(withTranslationCallExpression, 'NAMESPACE', commentHints); | ||
if (nsCommentHint) { | ||
// We got a comment hint, take its value as namespace. | ||
ns = nsCommentHint.value; | ||
} | ||
else { | ||
// Otherwise, try to get namespace from arguments. | ||
const namespaceArgument = withTranslationCallExpression.get('arguments')[0]; | ||
ns = getFirstOrNull(evaluateIfConfident(namespaceArgument)); | ||
} | ||
let keys = Array(); | ||
for (const tCall of tCalls) { | ||
keys = [ | ||
...keys, | ||
...extractTFunction(tCall, config, commentHints, true).map(k => ({ | ||
// Add namespace if it was not explicitely set in t() call. | ||
...k, | ||
parsedOptions: { | ||
...k.parsedOptions, | ||
ns: k.parsedOptions.ns || ns, | ||
}, | ||
})), | ||
]; | ||
} | ||
return keys; | ||
} | ||
// We have to store which nodes were extracted because the plugin might be called multiple times | ||
@@ -975,10 +1186,8 @@ // by Babel and the state would be lost across calls. | ||
catch (err) { | ||
if (err instanceof ExtractionError) { | ||
// eslint-disable-next-line no-console | ||
console.warn(`${PLUGIN_NAME}: Extraction error in ${filename} at line ` + | ||
`${lineNumber}. ${err.message}`); | ||
} | ||
else { | ||
if (!(err instanceof ExtractionError)) { | ||
throw err; | ||
} | ||
// eslint-disable-next-line no-console | ||
console.warn(`${PLUGIN_NAME}: Extraction error in ${filename} at line ` + | ||
`${lineNumber}. ${err.message}`); | ||
} | ||
@@ -1001,2 +1210,14 @@ } | ||
}, | ||
ClassDeclaration(path, state) { | ||
const extractState = this.I18NextExtract; | ||
handleExtraction(path, state, collect => { | ||
collect(extractWithTranslationHOC(path, extractState.config, extractState.commentHints)); | ||
}); | ||
}, | ||
Function(path, state) { | ||
const extractState = this.I18NextExtract; | ||
handleExtraction(path, state, collect => { | ||
collect(extractWithTranslationHOC(path, extractState.config, extractState.commentHints)); | ||
}); | ||
}, | ||
}; | ||
@@ -1003,0 +1224,0 @@ function plugin (api) { |
{ | ||
"name": "babel-plugin-i18next-extract", | ||
"version": "0.1.0-alpha.2", | ||
"version": "0.1.0-alpha.3", | ||
"description": "Statically extract translation keys from i18next application.", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -136,3 +136,3 @@ # babel-plugin-i18next-extract | ||
- [x] `Translation` render prop support (with plural forms, contexts and namespaces). | ||
- [ ] (todo) Fuzzy namespace inference from `withTranslation` HoC. | ||
- [x] Namespace inference from `withTranslation` HOC. | ||
- [x] Namespace inference: | ||
@@ -149,4 +149,4 @@ - [x] Depending on the key value. | ||
| Option | Type | Description | Default | | ||
|-----------------------|-------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------| | ||
| locales | `string[]` | All the locales your project supports. babel-plugin-i18next-extract will generate a JSON file for each locale. | `['en']` | | ||
|-|-|-|-| | ||
| locales | `string[]` | Locales your project supports. | `['en']` | | ||
| defaultNS | `string` | The default namespace that your translation use. | `'translation'` | | ||
@@ -153,0 +153,0 @@ | pluralSeparator | `string` | String you want to use to split plural from keys. See [i18next Configuration options](https://www.i18next.com/overview/configuration-options#misc) | `'_'` | |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
118015
21
2749