Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

messages-modules

Package Overview
Dependencies
Maintainers
1
Versions
91
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

messages-modules - npm Package Compare versions

Comparing version 1.0.0 to 1.1.0

2

lib/babel-plugin/index.js

@@ -11,4 +11,4 @@ "use strict";

{
function: 'getMessages',
module: 'messages-modules',
function: 'getMessages',
},

@@ -15,0 +15,0 @@ ];

@@ -5,2 +5,6 @@ import * as BabelTypes from '@babel/types';

export declare type Statement = BabelTypes.Statement;
export declare type ExportNamedDeclaration = BabelTypes.ExportNamedDeclaration;
export declare type ExportSpecifier = BabelTypes.ExportSpecifier;
export declare type ExportNamespaceSpecifier = BabelTypes.ExportNamespaceSpecifier;
export declare type ExportDefaultSpecifier = BabelTypes.ExportDefaultSpecifier;
export declare type ImportDeclaration = BabelTypes.ImportDeclaration;

@@ -12,7 +16,16 @@ export declare type ImportSpecifier = BabelTypes.ImportSpecifier;

/**
* Target to hijack.
* A target to hijack.
*
* A target to hijack (inject messages) must be identified by both a `function` and the `module` it's being
* imported from. For example:
*
* `import { getMessages } from 'messages-modules'`
*
* The function is `getMessages` and the module is `messages-modules`
*/
export declare type HijackTarget = {
/** The name of a function to hijack (e.g., `getMessages`). */
function: string;
/** The function's module used to import it. */
module: string;
function: string;
};

@@ -34,6 +47,6 @@ /**

readonly isInjected = true;
/** A collection of "key/value" objects for for all locales. */
keyValueObjectCollection: KeyValueObjectCollection;
/** The path of the source file that is invoking `useMessages`. */
readonly sourceFilePath: string;
/** A collection of "key/value" objects for for all locales. */
keyValueObjectCollection: KeyValueObjectCollection;
/**

@@ -40,0 +53,0 @@ * The injected localized messages.

"use strict";
var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
if (ar || !(i in from)) {
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
ar[i] = from[i];
}
}
return to.concat(ar || Array.prototype.slice.call(from));
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getMessages = exports.messageModulePlugin = exports.InjectedMessages = void 0;
var fs_1 = require("fs");
var path_1 = require("path");
var node_fs_1 = require("node:fs");
var node_path_1 = require("node:path");
var template_1 = require("@babel/template");
var BabelTypes = require("@babel/types");
var isExportSpecifier = BabelTypes.isExportSpecifier;
var isImportSpecifier = BabelTypes.isImportSpecifier;
var isIdentifier = BabelTypes.isIdentifier;
/**

@@ -17,3 +28,3 @@ * Escapes a regular expression string.

function escapeRegExp(regexp) {
return regexp.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string
return regexp.replace(/[$()*+.?[\\\]^{|}]/g, '\\$&'); // $& means the whole matched string
}

@@ -46,3 +57,3 @@ var InjectedMessages = /** @class */ (function () {

function getInjectedMessages(sourceFilePath, messagesFileExtension, getMessages) {
var parsedSourceFile = (0, path_1.parse)(sourceFilePath);
var parsedSourceFile = (0, node_path_1.parse)(sourceFilePath);
var sourceFileDirectoryPath = parsedSourceFile.dir;

@@ -52,3 +63,3 @@ var sourceFilename = parsedSourceFile.name;

var fileRegExp = new RegExp("^".concat(escapeRegExp(sourceFilename), ".(?<locale>[\\w-]+).").concat(messagesFileExtension, "$"));
(0, fs_1.readdirSync)(sourceFileDirectoryPath, { withFileTypes: true }).forEach(function (directoryEntry) {
(0, node_fs_1.readdirSync)(sourceFileDirectoryPath, { withFileTypes: true }).forEach(function (directoryEntry) {
if (directoryEntry.isFile()) {

@@ -59,3 +70,3 @@ var directoryEntryFilename = directoryEntry.name;

var locale = regExpMatch.groups.locale;
var messagesFilePath = sourceFileDirectoryPath.length
var messagesFilePath = sourceFileDirectoryPath.length > 0
? "".concat(sourceFileDirectoryPath, "/").concat(directoryEntryFilename)

@@ -71,3 +82,3 @@ : directoryEntryFilename;

/**
* Verify if an import declaration node matches the target module.
* Verify if an import or export statement matches the target module.
*

@@ -77,24 +88,57 @@ * @param nodePath - A node path object.

*
* @returns True is the node matches, otherwise false.
* @returns True is the module matches, otherwise false.
*/
function isMatchingModule(nodePath, hijackTarget) {
if (!nodePath.isImportDeclaration())
return false;
if (nodePath.node.source.value !== hijackTarget.module)
return false;
return true;
return !!nodePath.node.source && nodePath.node.source.value === hijackTarget.module;
}
/**
* Verify if a specifier matches the target function.
* Verify if an import or export statement matches the target function.
*
* @param specifier - A specifier object.
* @param nodePath - A node path object.
* @param hijackTarget - The target to hijack.
*
* @returns True is the specifier matches, otherwise false.
* @returns True is the function matches, otherwise false.
*/
function isMatchingModuleImportName(specifier, hijackTarget) {
return (isImportSpecifier(specifier) &&
specifier.imported.name === hijackTarget.function);
function isMatchingFunction(nodePath, hijackTarget) {
return nodePath.node.specifiers.some(function (specifier) {
return ((isImportSpecifier(specifier) && isMatchingImportFunction(specifier, hijackTarget)) ||
(isExportSpecifier(specifier) && isMatchingExportFunction(specifier, hijackTarget)));
});
}
/**
* Verify if an import specifier matches the target function.
*
* @param specifier - An import specifier object.
* @param hijackTarget - The target to hijack.
*
* @returns True is the module matches, otherwise false.
*/
function isMatchingImportFunction(specifier, hijackTarget) {
return isIdentifier(specifier.imported) && specifier.imported.name === hijackTarget.function;
}
/**
* Verify if an export specifier matches the target function.
*
* @param specifier - An export specifier object.
* @param hijackTarget - The target to hijack.
*
* @returns True is the module matches, otherwise false.
*/
function isMatchingExportFunction(specifier, hijackTarget) {
return isIdentifier(specifier.local) && specifier.local.name === hijackTarget.function;
}
/**
* Verify if a named export declaration node matches the target module and function.
*
* @param nodePath - A node path object.
* @param hijackTarget - The target to hijack.
*
* @returns True is the node matches, otherwise false.
*/
function isMatchingNamedExport(nodePath, hijackTarget) {
return (nodePath.isExportNamedDeclaration() &&
isMatchingFunction(nodePath, hijackTarget) &&
isMatchingModule(nodePath, hijackTarget));
}
/**
* Verify if an import declaration node matches the target module and function.

@@ -108,6 +152,5 @@ *

function isMatchingNamedImport(nodePath, hijackTarget) {
return (isMatchingModule(nodePath, hijackTarget) &&
nodePath.node.specifiers.some(function (specifier) {
return isMatchingModuleImportName(specifier, hijackTarget);
}));
return (nodePath.isImportDeclaration() &&
isMatchingFunction(nodePath, hijackTarget) &&
isMatchingModule(nodePath, hijackTarget));
}

@@ -128,3 +171,3 @@ var Messages = /** @class */ (function () {

this.programNodePath = programNodePath;
var leadingPathSeparatorRegExp = new RegExp("^".concat(escapeRegExp(path_1.sep)));
var leadingPathSeparatorRegExp = new RegExp("^".concat(escapeRegExp(node_path_1.sep)));
// this.sourceFilePath = (pluginPass as PluginPass).file.opts.filename

@@ -138,5 +181,5 @@ var pluginPassFilename = (_a = pluginPass.file.opts) === null || _a === void 0 ? void 0 : _a.filename;

.replace(leadingPathSeparatorRegExp, ''); // Remove leading path separator (e.g., '/') if present.
if (path_1.sep !== '/') {
if (node_path_1.sep !== '/') {
// Normalize path separators to `/`.
var separatorRegExp = new RegExp("".concat(escapeRegExp(path_1.sep)), 'g');
var separatorRegExp = new RegExp("".concat(escapeRegExp(node_path_1.sep)), 'g');
this.sourceFilePath = this.sourceFilePath.replace(separatorRegExp, '/');

@@ -191,10 +234,11 @@ }

node.specifiers.forEach(function (specifier) {
if (isMatchingModuleImportName(specifier, hijackTarget)) {
if (isImportSpecifier(specifier) && isMatchingImportFunction(specifier, hijackTarget)) {
// The current function name used in the local scope.
var currentName_1 = specifier.local.name;
// This is the scope-unique variable name that will replace all matching function bindings.
var hijackedFunction_1 = getVariableName(nodePath, hijackTarget, 'Function');
var currentName_1 = specifier.local.name;
// Rename all bindings with the the new name (this excludes the import declaration).
var binding = nodePath.scope.getBinding(currentName_1);
if (!binding) {
return; // If there is no binding, no need to hijack.
return; // If the function is unused (no binding), no need to hijack.
}

@@ -204,3 +248,3 @@ binding.referencePaths.forEach(function (referencePath) {

});
// Insert the new "hijacked" namespace variable, with the correct binding.
// Insert the new "hijacked" variable, with the correct binding.
nodePath.insertAfter(template_1.default.ast("const ".concat(hijackedFunction_1, " = ").concat(currentName_1, ".bind(").concat(messages.getVariableName(), ");")));

@@ -211,2 +255,37 @@ }

/**
* "Hijack" a named export (e.g., `export { useMessages } from`).
*
* For every named export, we will create an import statement to which we will create a new hijacked function
* that will then be re-exported using the original name. If all named exports of a statement are hijacked, the
* export statement will be removed.
*
* @param nodePath - The node path being hijacked.
* @param hijackTarget - The target to hijack.
* @param messages - The object used to conditionally inject messages.
*/
function hijackNamedExport(nodePath, hijackTarget, messages) {
var node = nodePath.node;
__spreadArray([], node.specifiers, true).reverse().forEach(function (specifier, index, specifiersCopy) {
if (isExportSpecifier(specifier) &&
isMatchingExportFunction(specifier, hijackTarget) &&
isIdentifier(specifier.exported)) {
// Remove the matching specifier from the export as we will hijack it.
node.specifiers.splice(specifiersCopy.length - 1 - index, 1);
// The current function name used when exporting.
var currentName = specifier.exported.name;
// This is the scope-unique variable name that will be used to perform the hijack.
var hijackedImport = getVariableName(nodePath, hijackTarget, 'Function');
var hijackedExport = getVariableName(nodePath, hijackTarget, 'Function');
// Insert new import/exports statement using the new "hijacked" variable, with the correct binding.
nodePath.insertAfter(template_1.default.ast("import { ".concat(hijackTarget.function, " as ").concat(hijackedImport, " } from '").concat(hijackTarget.module, "';") +
"const ".concat(hijackedExport, " = ").concat(hijackedImport, ".bind(").concat(messages.getVariableName(), ");") +
"export { ".concat(hijackedExport, " as ").concat(currentName, " };")));
}
});
// If the entire export statement was hijacked (it is now empty), we can remove it.
if (node.specifiers.length === 0) {
nodePath.remove();
}
}
/**
* Dynamically returns a plugin based on the specified parameters.

@@ -224,5 +303,10 @@ *

hijackTargets.forEach(function (hijackTarget) {
// Try to hijack matching named import statements.
if (isMatchingNamedImport(bodyNodePath, hijackTarget)) {
hijackNamedImport(bodyNodePath, hijackTarget, messages);
}
// Try to hijack matching named export statements.
if (isMatchingNamedExport(bodyNodePath, hijackTarget)) {
hijackNamedExport(bodyNodePath, hijackTarget, messages);
}
});

@@ -244,2 +328,3 @@ });

function getMessages(locale) {
var _a;
// @ts-expect-error: `this` is injected using `bind` and will trigger a false compilation error.

@@ -250,5 +335,4 @@ var injectedMessages = this;

}
var messages = injectedMessages.keyValueObjectCollection[locale.toLowerCase()];
return !messages ? {} : messages;
return (_a = injectedMessages.keyValueObjectCollection[locale.toLowerCase()]) !== null && _a !== void 0 ? _a : {};
}
exports.getMessages = getMessages;
{
"name": "messages-modules",
"version": "1.0.0",
"version": "1.1.0",
"description": "Messages (localized strings) that are scoped locally.",

@@ -24,4 +24,5 @@ "author": "Avansai (https://avansai.com)",

"scripts": {
"build": "rm -Rf ./lib && tsc && npm run lint && npm test",
"lint": "eslint --ext .js --ext .jsx --ext .ts --ext .tsx --fix .",
"build": "npm run prettier && npm run lint-fix && rm -Rf ./lib && tsc && npm test",
"lint-fix": "eslint --ext .js --ext .jsx --ext .ts --ext .tsx --fix .",
"lint-check": "eslint --ext .js --ext .jsx --ext .ts --ext .tsx .",
"lint-print-config": "eslint --print-config ./eslintrc.yaml",

@@ -46,10 +47,10 @@ "prettier": "prettier --write .",

"devDependencies": {
"@babel/cli": "^7.18.9",
"@babel/core": "^7.18.9",
"@babel/cli": "^7.18.10",
"@babel/core": "^7.18.10",
"@release-it/conventional-changelog": "^5.0.0",
"@types/babel__core": "^7.1.19",
"@types/jest": "^28.1.6",
"@types/node": "^18.6.3",
"@typescript-eslint/eslint-plugin": "^5.32.0",
"@typescript-eslint/parser": "^5.32.0",
"@types/node": "^18.6.5",
"@typescript-eslint/eslint-plugin": "^5.33.0",
"@typescript-eslint/parser": "^5.33.0",
"dotenv-cli": "^6.0.0",

@@ -61,8 +62,10 @@ "eslint": "^8.21.0",

"eslint-plugin-import": "^2.26.0",
"eslint-plugin-jest": "^26.7.0",
"eslint-plugin-jest": "^26.8.2",
"eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-unicorn": "^43.0.2",
"jest": "^28.1.3",
"prettier": "^2.7.1",
"prettier-plugin-organize-imports": "^3.0.0",
"release-it": "^15.2.0",
"prettier-plugin-organize-imports": "^3.0.3",
"prettier-plugin-sh": "^0.12.8",
"release-it": "^15.3.0",
"ts-jest": "^28.0.7",

@@ -73,4 +76,7 @@ "ts-node": "^10.9.1",

"dependencies": {
"properties-file": "^2.0.9"
"properties-file": "^2.1.0"
},
"engines": {
"node": "^14.18.1 || ^16.0.0"
}
}

@@ -66,25 +66,21 @@ # messages-modules

To keep this simple, the `message-modules` plugins only support **named imports** which means that **namespace imports**, **dynamic imports** and **require imports** are out of scope:
To keep this simple, the `message-modules` plugins only support **named imports** and **named exports**. This means that **namespace imports**, **dynamic imports** and **require imports** are out of scope:
👍 **named imports**
👍 **Supported**
```ts
// Named import
import { getMessages } from 'messages-modules'
// Named export (used for shared messages)
export { getMessages } from 'messages-modules'
```
👎 **namespace imports**
👎 **Unsupported**
```ts
// Namespace import
import * as messagesModules from 'messages-modules'
```
👎 **dynamic imports**
```ts
// Dynamic imports
const { getMessages } = await import('messages-modules')
```
👎 **require imports**
```ts
// Require imports
const messagesModules = require('messages-modules')

@@ -91,0 +87,0 @@ ```

SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc