Skip to content
Open
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
7 changes: 0 additions & 7 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@
"@babel/plugin-transform-class-properties": "^7.25.0",
"@babel/traverse": "^7.26.0",
"babel-project-relative-import": "^2.0.1",
"lodash": "^4.17.21",
"randchinese": "^1.0.0"
},
"jest": {
Expand Down
6 changes: 3 additions & 3 deletions src/frozen-asts.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
const babylon = require('@babel/parser');
const parser = require('@babel/parser');

// TODO: Use line numbers to move the two imports to separate lines
const i18nextImportStatement = babylon.parse('import i18n from \'i18next\';\n', { sourceType: 'module' }).program.body[0];
const kImportStatement = babylon.parse('import k from \'~/i18n/keys\';\n', { sourceType: 'module' }).program.body[0];
const i18nextImportStatement = parser.parse('import i18n from \'i18next\';\n', { sourceType: 'module' }).program.body[0];
const kImportStatement = parser.parse('import k from \'~/i18n/keys\';\n', { sourceType: 'module' }).program.body[0];

module.exports = {
i18nextImportStatement,
Expand Down
31 changes: 15 additions & 16 deletions src/plugin-helpers.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
const babel = require('@babel/core');
const _ = require('lodash');
const { types: t } = require('@babel/core');

const {
getUniqueKeyFromFreeText,
} = require('./lut');

const BLACKLISTED_JSX_ATTRIBUTES = [
// React router
'path', 'from', 'to', 'href', 'as',
// Inline style
'style', 'className', 'color',
// Code
'dangerouslySetInnerHTML', 'src',
];

// Dont extract value for Literals under this attribute
const isBlacklistedForJsxAttribute = (path) => {
const blacklistedJsxAttributes = [
// React router
'path', 'from', 'to', 'href', 'as',
// Inline style
'style', 'className', 'color',
// Code
'dangerouslySetInnerHTML', 'src',
];
const jsxAttributeParent = path.findParent(p => p.isJSXAttribute());
if (!jsxAttributeParent) return false;
const name = _.get(jsxAttributeParent, 'node.name.name');
if (blacklistedJsxAttributes.includes(name)) return true;
return false;
const name = jsxAttributeParent.node.name && jsxAttributeParent.node.name.name;
return BLACKLISTED_JSX_ATTRIBUTES.includes(name);
};


Expand All @@ -34,13 +33,12 @@ const handleConditionalExpressions = (path) => {
// Check for blacklist
if (isBlacklistedForJsxAttribute(path)) return;

const coreValue = _.get(path, 'node.value', '').trim();
const coreValue = (path.node.value || '').trim();
if (!coreValue.length) return;
const kValue = getUniqueKeyFromFreeText(coreValue);
// TODO: OPTIMIZATION: Use quasi quotes to optimize this

const srcString = `i18n.t(k.${kValue})`;
if (babel.types.isJSXAttribute(path.parent)) {
if (t.isJSXAttribute(path.parent)) {
// TODO: The next line does not parse
// path.replaceWithSourceString(`{${srcString}}`);
} else {
Expand All @@ -51,4 +49,5 @@ const handleConditionalExpressions = (path) => {
module.exports = {
isBlacklistedForJsxAttribute,
handleConditionalExpressions,
BLACKLISTED_JSX_ATTRIBUTES,
};
27 changes: 11 additions & 16 deletions src/plugin.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
const _ = require('lodash');

const {
i18nextImportStatement,
kImportStatement,
Expand All @@ -15,6 +13,8 @@ const {
handleConditionalExpressions,
} = require('./plugin-helpers');

const cloneDeep = (obj) => JSON.parse(JSON.stringify(obj));

const handleStringLiteral = (path, table, key) => {
const { value } = path.node;
if (!table[key]) table[key] = {};
Expand Down Expand Up @@ -46,7 +46,6 @@ module.exports = ({ types: t }) => ({
Object.keys(this.state).forEach((key) => {
if (this.state[key].valid && this.state[key].pairs) {
this.state[key].pairs.forEach(({ path, value }) => {
// TODO: OPTIMIZATION: Use quasi quotes to optimize this
const kValue = getUniqueKeyFromFreeText(value);
path.replaceWithSourceString(`i18n.t(k.${kValue})`);
});
Expand All @@ -55,10 +54,10 @@ module.exports = ({ types: t }) => ({
// Do not add imports if there is no replaceable text
// in this file
if (LutManager.getUniqueKeyFromFreeTextNumCalls > 0) {
if (!this.alreadyImportedK) programPath.node.body.unshift(_.cloneDeep(kImportStatement));
if (!this.alreadyImportedK) programPath.node.body.unshift(cloneDeep(kImportStatement));
if (!this.alreadyImportedi18n) {
programPath.node.body
.unshift(_.cloneDeep(i18nextImportStatement));
.unshift(cloneDeep(i18nextImportStatement));
}
}
},
Expand All @@ -79,7 +78,7 @@ module.exports = ({ types: t }) => ({
// Only extract the value of identifiers
// who are children of some JSX element
if (path.findParent(p => p.isJSXElement())) {
this.state[path.node.name] = _.merge(this.state[path.node.name], { valid: true });
this.state[path.node.name] = { ...this.state[path.node.name], valid: true };
}
},
},
Expand All @@ -91,21 +90,20 @@ module.exports = ({ types: t }) => ({
if (!firstJsxParent) return;

// Ignore CSS strings
if (_.get(firstJsxParent, 'node.openingElement.name.name') === 'style') return;
if (firstJsxParent.node.openingElement?.name?.name === 'style') return;

if (isBlacklistedForJsxAttribute(path)) return;

const { expressions, quasis } = path.node;
expressions.forEach((expression) => {
const key = expression.name;
this.state[key] = _.merge(this.state[key], { valid: true });
this.state[key] = { ...this.state[key], valid: true };
});
quasis.forEach((templateElement, index) => {
const coreValue = templateElement.value.raw.trim();
if (coreValue.length) {
const qPath = path.get('quasis')[index];
const kValue = getUniqueKeyFromFreeText(coreValue);
// TODO: OPTIMIZATION: Use quasi quotes to optimize this
// TODO: Replace the path instead of modifying the raw
qPath.node.value.raw = qPath.node.value.raw.replace(coreValue, `\${i18n.t(k.${kValue})}`);
qPath.node.value.cooked = qPath.node.value.cooked.replace(coreValue, `\${i18n.t(k.${kValue})}`);
Expand All @@ -115,15 +113,14 @@ module.exports = ({ types: t }) => ({
},
AssignmentExpression: {
enter(path) {
// TODO: Explore the reason behind crash
const key = _.get(path, 'node.left.name', _.get(path, 'node.left.property.name'));
const key = path.node.left.name || (path.node.left.property && path.node.left.property.name);
if (!key) return;
extractValueAndUpdateTable(t, this.state, path.get('right'), key);
},
},
ObjectProperty: {
enter(path) {
const key = _.get(path, 'node.key.name');
const key = path.node.key && path.node.key.name;
if (!key) return;

// Check for blacklist
Expand All @@ -134,8 +131,7 @@ module.exports = ({ types: t }) => ({
},
VariableDeclarator: {
enter(path) {
// TODO: Explore the reason behind crash
const key = _.get(path, 'node.id.name');
const key = path.node.id && path.node.id.name;
if (!key) return;

// Check for blacklist
Expand All @@ -146,10 +142,9 @@ module.exports = ({ types: t }) => ({
},
JSXText: {
enter(path) {
const coreValue = _.get(path, 'node.value', '').trim();
const coreValue = (path.node.value || '').trim();
if (!coreValue.length) return;
const kValue = getUniqueKeyFromFreeText(coreValue);
// TODO: OPTIMIZATION: Use quasi quotes to optimize this
path.node.value = path.node.value.replace(coreValue, `{i18n.t(k.${kValue})}`);
},
},
Expand Down
2 changes: 1 addition & 1 deletion src/used-plugins.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const parserPlugins = [
'jsx',
'classProperties', // '@babel/plugin-transform-class-properties',
'classProperties',
'flow',
];

Expand Down
Loading