Socket
Socket
Sign inDemoInstall

@automattic/babel-plugin-i18n-calypso

Package Overview
Dependencies
64
Maintainers
30
Versions
5
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.0.3 to 1.1.0

.eslintrc.js

46

package.json
{
"name": "@automattic/babel-plugin-i18n-calypso",
"version": "1.0.3",
"description": "A Babel plugin to generate a POT file for translate calls",
"main": "src/index.js",
"dependencies": {
"@babel/runtime": "^7.0.0",
"gettext-parser": "^1.3.1",
"lodash": "^4.17.10"
},
"devDependencies": {
"@babel/core": "^7.0.0",
"@babel/traverse": "^7.0.0"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
},
"publishConfig": {
"access": "public"
},
"keywords": [],
"author": "Alex Kirk <alex.kirk@automattic.com> (https://automattic.com)",
"license": "GPL-2.0+",
"gitHead": "d63600c280795bbf8d4dc45eb0702b0a22961ca9"
"name": "@automattic/babel-plugin-i18n-calypso",
"version": "1.1.0",
"description": "A Babel plugin to generate a POT file for translate calls",
"main": "src/index.js",
"dependencies": {
"gettext-parser": "^4.0.0",
"lodash": "^4.17.14"
},
"peerDependencies": {
"@babel/core": "^7.0.0"
},
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Automattic/wp-calypso.git",
"directory": "packages/babel-plugin-i18n-calypso"
},
"keywords": [],
"author": "Automattic Inc.",
"license": "GPL-2.0-or-later",
"gitHead": "e50bdc06da220ce237bf309703102dec8673436f"
}
/**
* Extract i18n-calypso `translate` calls into a POT file.
* Extract i18n-calypso `translate` and @wordpress/i18n `__`, `_n`, `_x`, `_nx`
* calls into a POT file.
*

@@ -32,3 +33,2 @@ * Credits:

*
* @format
*/

@@ -40,3 +40,3 @@

const { po } = require( 'gettext-parser' );
const { merge, isEmpty } = require( 'lodash' );
const { merge, isEmpty, forEach } = require( 'lodash' );
const { relative, sep } = require( 'path' );

@@ -48,3 +48,3 @@ const { existsSync, mkdirSync, writeFileSync } = require( 'fs' );

*
* @type {Object}
* @type {object}
*/

@@ -64,8 +64,81 @@ const DEFAULT_HEADERS = {

/**
* The order of arguments in translate functions.
*
* @type {object}
*/
const DEFAULT_FUNCTIONS_ARGUMENTS_ORDER = {
__: [],
_n: [ 'msgid_plural' ],
_x: [ 'msgctxt' ],
_nx: [ 'msgid_plural', null, 'msgctxt' ],
translate: [ 'msgid_plural', 'options_object' ],
};
/**
* Regular expression matching translator comment value.
*
* @type {RegExp}
*/
const REGEXP_TRANSLATOR_COMMENT = /^\s*translators:\s*([\s\S]+)/im;
/**
* Returns the extracted comment for a given AST traversal path if one exists.
*
* @param {object} path Traversal path.
* @param {number} _originalNodeLine Private: In recursion, line number of
* the original node passed.
*
* @returns {?string} Extracted comment.
*/
function getExtractedComment( path, _originalNodeLine ) {
const { node, parent, parentPath } = path;
// Assign original node line so we can keep track in recursion whether a
// matched comment or parent occurs on the same or previous line
if ( ! _originalNodeLine ) {
_originalNodeLine = node.loc.start.line;
}
let comment;
forEach( node.leadingComments, commentNode => {
const { line } = commentNode.loc.end;
if ( line < _originalNodeLine - 1 || line > _originalNodeLine ) {
return;
}
const match = commentNode.value.match( REGEXP_TRANSLATOR_COMMENT );
if ( match ) {
// Extract text from matched translator prefix
comment = match[ 1 ]
.split( '\n' )
.map( text => text.trim() )
.join( ' ' );
// False return indicates to Lodash to break iteration
return false;
}
} );
if ( comment ) {
return comment;
}
if ( ! parent || ! parent.loc || ! parentPath ) {
return;
}
// Only recurse as long as parent node is on the same or previous line
const { line } = parent.loc.start;
if ( line >= _originalNodeLine - 1 && line <= _originalNodeLine ) {
return getExtractedComment( parentPath, _originalNodeLine );
}
}
/**
* Given an argument node (or recursed node), attempts to return a string
* represenation of that node's value.
*
* @param {Object} node AST node.
* @param {object} node AST node.
*
* @return {string} String value.
* @returns {string} String value.
*/

@@ -84,2 +157,7 @@ function getNodeAsString( node ) {

case 'TemplateLiteral':
return ( node.quasis || [] ).reduce( ( string, element ) => {
return ( string += element.value.cooked );
}, '' );
default:

@@ -90,9 +168,47 @@ return '';

/**
* Returns true if the specified funciton name is valid translate function name
*
* @param {string} name Function name to test.
*
* @returns {boolean} Whether function name is valid translate function name.
*/
function isValidFunctionName( name ) {
return Object.keys( DEFAULT_FUNCTIONS_ARGUMENTS_ORDER ).includes( name );
}
/**
* Returns true if the specified key of a function is valid for assignment in
* the translation object.
*
* @param {string} key Key to test.
*
* @returns {boolean} Whether key is valid for assignment.
*/
function isValidTranslationKey( key ) {
return Object.values( DEFAULT_FUNCTIONS_ARGUMENTS_ORDER ).some( args => args.includes( key ) );
}
module.exports = function() {
let strings = {},
nplurals = 2,
baseData;
baseData,
functions = { ...DEFAULT_FUNCTIONS_ARGUMENTS_ORDER };
return {
visitor: {
ImportDeclaration( path ) {
// If `translate` from `i18n-calypso` is imported with an
// alias, set the specified alias as a reference to translate.
if ( 'i18n-calypso' !== path.node.source.value ) {
return;
}
path.node.specifiers.forEach( specifier => {
if ( specifier.imported && 'translate' === specifier.imported.name && specifier.local ) {
functions[ specifier.local.name ] = functions.translate;
}
} );
},
CallExpression( path, state ) {

@@ -104,7 +220,7 @@ const { callee } = path.node;

if ( 'MemberExpression' === callee.type ) {
name = callee.property.name;
name = callee.property.loc ? callee.property.loc.identifierName : callee.property.name;
} else {
name = callee.name;
name = callee.loc ? callee.loc.identifierName : callee.name;
}
if ( 'translate' !== name ) {
if ( ! isValidFunctionName( name ) ) {
return;

@@ -155,14 +271,11 @@ }

if ( path.node.arguments.length > i ) {
const msgid_plural = getNodeAsString( path.node.arguments[ i ] );
if ( msgid_plural.length ) {
translation.msgid_plural = msgid_plural;
i++;
// For plurals, create an empty mgstr array
translation.msgstr = Array.from( Array( nplurals ) ).map( () => '' );
}
// If exists, also assign translator comment
const translator = getExtractedComment( path );
if ( translator ) {
translation.comments.extracted = translator;
}
const { filename } = this.file.opts;
const pathname = relative( '.', filename )
const base = state.opts.base || '.';
const pathname = relative( base, filename )
.split( sep )

@@ -172,21 +285,33 @@ .join( '/' );

if (
path.node.arguments.length > i &&
'ObjectExpression' === path.node.arguments[ i ].type
) {
for ( const j in path.node.arguments[ i ].properties ) {
if ( 'ObjectProperty' === path.node.arguments[ i ].properties[ j ].type ) {
switch ( path.node.arguments[ i ].properties[ j ].key.name ) {
case 'context':
translation.msgctxt = path.node.arguments[ i ].properties[ j ].value.value;
break;
case 'comment':
translation.comments.extracted =
path.node.arguments[ i ].properties[ j ].value.value;
break;
}
const functionKeys = state.opts.functions || functions[ name ];
if ( functionKeys ) {
path.node.arguments.slice( i ).forEach( ( arg, index ) => {
const key = functionKeys[ index ];
if ( 'ObjectExpression' === arg.type ) {
arg.properties.forEach( property => {
if ( 'ObjectProperty' !== property.type ) {
return;
}
if ( 'context' === property.key.name ) {
translation.msgctxt = property.value.value;
}
if ( 'comment' === property.key.name ) {
translation.comments.extracted = property.value.value;
}
} );
} else if ( isValidTranslationKey( key ) ) {
translation[ key ] = getNodeAsString( arg );
}
}
} );
}
// For plurals, create an empty mgstr array
if ( ( translation.msgid_plural || '' ).length ) {
translation.msgstr = Array.from( Array( nplurals ) ).map( () => '' );
}
// Create context grouping for translation if not yet exists

@@ -200,3 +325,7 @@ const { msgctxt = '', msgid } = translation;

strings[ msgctxt ][ msgid ] = translation;
} else {
} else if (
! strings[ msgctxt ][ msgid ].comments.reference.includes(
translation.comments.reference
)
) {
strings[ msgctxt ][ msgid ].comments.reference += '\n' + translation.comments.reference;

@@ -208,2 +337,3 @@ }

strings = {};
functions = { ...DEFAULT_FUNCTIONS_ARGUMENTS_ORDER };
},

@@ -223,3 +353,4 @@ exit( path, state ) {

const { filename } = this.file.opts;
const pathname = relative( '.', filename )
const base = state.opts.base || '.';
const pathname = relative( base, filename )
.split( sep )

@@ -226,0 +357,0 @@ .join( '-' );

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc