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

@formatjs/ts-transformer

Package Overview
Dependencies
Maintainers
3
Versions
172
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@formatjs/ts-transformer - npm Package Compare versions

Comparing version 1.1.7 to 2.0.0

18

CHANGELOG.md

@@ -6,2 +6,20 @@ # Change Log

# [2.0.0](https://github.com/formatjs/formatjs/compare/@formatjs/ts-transformer@1.1.7...@formatjs/ts-transformer@2.0.0) (2020-05-07)
### Features
* **@formatjs/ts-transformer:** rm dependency on typechecker ([46a675f](https://github.com/formatjs/formatjs/commit/46a675f572017f2bffe7a42dcc0bf68a824aaf69))
### BREAKING CHANGES
* **@formatjs/ts-transformer:** Also remove `moduleSourceName` option
Dependency on typechecker means we cannot run w/ just `transpileModule`
Typechecking is also a lot slower
## [1.1.7](https://github.com/formatjs/formatjs/compare/@formatjs/ts-transformer@1.1.6...@formatjs/ts-transformer@1.1.7) (2020-05-06)

@@ -8,0 +26,0 @@

35

compile.ts
import * as ts from 'typescript';
import {Project} from 'ts-morph';
import {transform as intlTransformer} from './src';

@@ -26,23 +25,27 @@

) {
const project = new Project({
compilerOptions,
});
project.addExistingSourceFiles(input);
const compilerHost = ts.createCompilerHost(compilerOptions);
const program = ts.createProgram([input], compilerOptions, compilerHost);
const msgs = {};
project.emit({
customTransformers: {
before: [
intlTransformer({
overrideIdFn: '[hash:base64:10]',
program: project.getProgram().compilerObject,
}),
],
},
let emitResult = program.emit(undefined, undefined, undefined, undefined, {
before: [
intlTransformer({
overrideIdFn: '[hash:base64:10]',
}),
],
});
const diagnostics = project.getPreEmitDiagnostics();
console.log(project.formatDiagnosticsWithColorAndContext(diagnostics));
let allDiagnostics = ts
.getPreEmitDiagnostics(program)
.concat(emitResult.diagnostics);
console.log(
ts.formatDiagnosticsWithColorAndContext(allDiagnostics, {
getCanonicalFileName: fileName => fileName,
getCurrentDirectory: () => process.cwd(),
getNewLine: () => ts.sys.newLine,
})
);
return msgs;
}

@@ -14,10 +14,2 @@ import * as ts from 'typescript';

/**
* 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.
*
* @type {string}
* @memberof Opts
*/
moduleSourceName?: string;
/**
* Remove `defaultMessage` field in generated js after extraction.

@@ -28,6 +20,3 @@ */

* Additional component names to extract messages from,
* e.g: `['FormattedFooBarMessage']`. **NOTE**: By default we check
* for the fact that `FormattedMessage` is
* imported from `moduleSourceName` to make sure variable alias works.
* This option does not do that so it's less safe.
* e.g: `['FormattedFooBarMessage']`.
*/

@@ -56,8 +45,4 @@ additionalComponentNames?: string[];

overrideIdFn?: InterpolateNameFn | string;
/**
* TS Compiler program
*/
program: ts.Program;
}
export declare function transform(opts: Opts): (ctx: ts.TransformationContext) => ts.Transformer<ts.SourceFile>;
export declare function transform(opts: Opts): ts.TransformerFactory<ts.SourceFile>;
//# sourceMappingURL=transform.d.ts.map

@@ -8,34 +8,7 @@ "use strict";

};
function getImportSpecifier(program, node) {
const symbol = program.getTypeChecker().getSymbolAtLocation(node);
if (!symbol) {
return;
}
const decls = symbol.getDeclarations();
if (!Array.isArray(decls) || decls.length !== 1) {
return;
}
return decls[0];
function isMultipleMessageDecl(node) {
return (ts.isIdentifier(node.expression) &&
node.expression.text === 'defineMessages');
}
function referenceImport(moduleSourceName, importSpecifier) {
return (!!importSpecifier &&
ts.isNamedImports(importSpecifier.parent) &&
ts.isImportClause(importSpecifier.parent.parent) &&
ts.isImportDeclaration(importSpecifier.parent.parent.parent) &&
importSpecifier.parent.parent.parent.moduleSpecifier
.text === moduleSourceName);
}
function isMultipleMessageDecl(node, program, moduleSourceName) {
const importSpecifier = getImportSpecifier(program, node.expression);
if (!importSpecifier) {
return false;
}
return (referenceImport(moduleSourceName, importSpecifier) &&
importSpecifier.name.text === 'defineMessages');
}
function isSingularMessageDecl(node, program, moduleSourceName, additionalComponentNames) {
const importSpecifier = getImportSpecifier(program, ts.isCallExpression(node) ? node.expression : node.tagName);
if (!importSpecifier) {
return false;
}
function isSingularMessageDecl(node, additionalComponentNames) {
const compNames = new Set([

@@ -46,6 +19,16 @@ '_',

]);
return (referenceImport(moduleSourceName, importSpecifier) &&
compNames.has(importSpecifier.name.text));
let fnName = '';
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
fnName = node.expression.text;
}
else if (ts.isJsxOpeningElement(node) && ts.isIdentifier(node.tagName)) {
fnName = node.tagName.text;
}
else if (ts.isJsxSelfClosingElement(node) &&
ts.isIdentifier(node.tagName)) {
fnName = node.tagName.text;
}
return compNames.has(fnName);
}
function extractMessageDescriptor(node, sf, { overrideIdFn, extractSourceLocation }) {
function extractMessageDescriptor(node, { overrideIdFn, extractSourceLocation }, sf) {
let properties = undefined;

@@ -67,6 +50,9 @@ if (ts.isObjectLiteralExpression(node)) {

: undefined;
if (!initializer || !ts.isStringLiteral(initializer) || !name) {
if (!initializer ||
!ts.isStringLiteral(initializer) ||
!name ||
!ts.isIdentifier(name)) {
return;
}
switch (name.getText(sf)) {
switch (name.text) {
case 'id':

@@ -102,3 +88,3 @@ msg.id = initializer.text;

if (extractSourceLocation) {
return Object.assign(Object.assign({}, msg), { file: sf.fileName, start: node.getStart(sf), end: node.getEnd() });
return Object.assign(Object.assign({}, msg), { file: sf.fileName, start: node.pos, end: node.end });
}

@@ -112,21 +98,21 @@ return msg;

*/
function isIntlFormatMessageCall(node, sf) {
function isIntlFormatMessageCall(node) {
const method = node.expression;
// Handle intl.formatMessage()
if (ts.isPropertyAccessExpression(method)) {
return ((method.name.getText(sf) === 'formatMessage' &&
return ((method.name.text === 'formatMessage' &&
ts.isIdentifier(method.expression) &&
method.expression.getText(sf) === 'intl') ||
method.expression.text === 'intl') ||
(ts.isPropertyAccessExpression(method.expression) &&
method.expression.name.getText(sf) === 'intl'));
method.expression.name.text === 'intl'));
}
// Handle formatMessage()
return ts.isIdentifier(method) && method.getText(sf) === 'formatMessage';
return ts.isIdentifier(method) && method.text === 'formatMessage';
}
function extractMessageFromJsxComponent(node, program, sf, opts) {
const { moduleSourceName = 'react-intl', onMsgExtracted } = opts;
if (!isSingularMessageDecl(node, program, moduleSourceName, opts.additionalComponentNames || [])) {
function extractMessageFromJsxComponent(node, opts, sf) {
const { onMsgExtracted } = opts;
if (!isSingularMessageDecl(node, opts.additionalComponentNames || [])) {
return;
}
const msg = extractMessageDescriptor(node, sf, opts);
const msg = extractMessageDescriptor(node, opts, sf);
if (!msg) {

@@ -160,5 +146,5 @@ return;

}
function extractMessagesFromCallExpression(node, program, sf, opts) {
const { moduleSourceName = 'react-intl', onMsgExtracted } = opts;
if (isMultipleMessageDecl(node, program, moduleSourceName)) {
function extractMessagesFromCallExpression(node, opts, sf) {
const { onMsgExtracted } = opts;
if (isMultipleMessageDecl(node)) {
const [descriptorsObj, ...restArgs] = node.arguments;

@@ -168,3 +154,3 @@ if (ts.isObjectLiteralExpression(descriptorsObj)) {

const msgs = properties
.map(prop => extractMessageDescriptor(prop.initializer, sf, opts))
.map(prop => extractMessageDescriptor(prop.initializer, opts, sf))
.filter((msg) => !!msg);

@@ -200,7 +186,7 @@ if (!msgs.length) {

}
else if (isSingularMessageDecl(node, program, moduleSourceName, opts.additionalComponentNames || []) ||
(opts.extractFromFormatMessageCall && isIntlFormatMessageCall(node, sf))) {
else if (isSingularMessageDecl(node, opts.additionalComponentNames || []) ||
(opts.extractFromFormatMessageCall && isIntlFormatMessageCall(node))) {
const [descriptorsObj, ...restArgs] = node.arguments;
if (ts.isObjectLiteralExpression(descriptorsObj)) {
const msg = extractMessageDescriptor(descriptorsObj, sf, opts);
const msg = extractMessageDescriptor(descriptorsObj, opts, sf);
if (!msg) {

@@ -228,10 +214,9 @@ return;

opts = Object.assign(Object.assign({}, DEFAULT_OPTS), opts);
const { program } = opts;
return (ctx) => {
const transformFn = ctx => {
function getVisitor(sf) {
const visitor = (node) => {
const newNode = ts.isCallExpression(node)
? extractMessagesFromCallExpression(node, program, sf, opts)
? extractMessagesFromCallExpression(node, opts, sf)
: ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)
? extractMessageFromJsxComponent(node, program, sf, opts)
? extractMessageFromJsxComponent(node, opts, sf)
: undefined;

@@ -244,4 +229,5 @@ return newNode || ts.visitEachChild(node, visitor, ctx);

};
return transformFn;
}
exports.transform = transform;
//# sourceMappingURL=transform.js.map
{
"name": "@formatjs/ts-transformer",
"version": "1.1.7",
"version": "2.0.0",
"description": "TS Compiler transformer for formatjs",

@@ -25,3 +25,3 @@ "main": "dist/index.js",

"loader-utils": "^2.0.0",
"typescript": "3.7.2"
"typescript": "3.8"
},

@@ -37,6 +37,5 @@ "author": "Long Ho <holevietlong@gmail.com>",

"jest": "^25.4.0",
"ts-jest": "^25.4.0",
"ts-morph": "6"
"ts-jest": "^25.4.0"
},
"gitHead": "31693b2c2371274e2371bcf4e82700bcad53ca74"
"gitHead": "3228121bf88b5c5cbe3d3775686a0960ca6a0395"
}

@@ -22,10 +22,2 @@ import * as ts from 'typescript';

/**
* 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.
*
* @type {string}
* @memberof Opts
*/
moduleSourceName?: string;
/**
* Remove `defaultMessage` field in generated js after extraction.

@@ -36,6 +28,3 @@ */

* Additional component names to extract messages from,
* e.g: `['FormattedFooBarMessage']`. **NOTE**: By default we check
* for the fact that `FormattedMessage` is
* imported from `moduleSourceName` to make sure variable alias works.
* This option does not do that so it's less safe.
* e.g: `['FormattedFooBarMessage']`.
*/

@@ -64,6 +53,2 @@ additionalComponentNames?: string[];

overrideIdFn?: InterpolateNameFn | string;
/**
* TS Compiler program
*/
program: ts.Program;
}

@@ -75,56 +60,13 @@

function getImportSpecifier(program: ts.Program, node: ts.Node) {
const symbol = program.getTypeChecker().getSymbolAtLocation(node);
if (!symbol) {
return;
}
const decls = symbol.getDeclarations();
if (!Array.isArray(decls) || decls.length !== 1) {
return;
}
return decls[0] as ts.ImportSpecifier;
}
function referenceImport(
moduleSourceName: string,
importSpecifier?: ts.ImportSpecifier
): boolean {
function isMultipleMessageDecl(node: ts.CallExpression) {
return (
!!importSpecifier &&
ts.isNamedImports(importSpecifier.parent) &&
ts.isImportClause(importSpecifier.parent.parent) &&
ts.isImportDeclaration(importSpecifier.parent.parent.parent) &&
(importSpecifier.parent.parent.parent.moduleSpecifier as ts.StringLiteral)
.text === moduleSourceName
ts.isIdentifier(node.expression) &&
node.expression.text === 'defineMessages'
);
}
function isMultipleMessageDecl(
node: ts.CallExpression,
program: ts.Program,
moduleSourceName: string
) {
const importSpecifier = getImportSpecifier(program, node.expression);
if (!importSpecifier) {
return false;
}
return (
referenceImport(moduleSourceName, importSpecifier) &&
importSpecifier.name.text === 'defineMessages'
);
}
function isSingularMessageDecl(
node: ts.CallExpression | ts.JsxOpeningElement | ts.JsxSelfClosingElement,
program: ts.Program,
moduleSourceName: string,
additionalComponentNames: string[]
) {
const importSpecifier = getImportSpecifier(
program,
ts.isCallExpression(node) ? node.expression : node.tagName
);
if (!importSpecifier) {
return false;
}
const compNames = new Set([

@@ -135,6 +77,14 @@ '_',

]);
return (
referenceImport(moduleSourceName, importSpecifier) &&
compNames.has(importSpecifier.name.text)
);
let fnName = '';
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {
fnName = node.expression.text;
} else if (ts.isJsxOpeningElement(node) && ts.isIdentifier(node.tagName)) {
fnName = node.tagName.text;
} else if (
ts.isJsxSelfClosingElement(node) &&
ts.isIdentifier(node.tagName)
) {
fnName = node.tagName.text;
}
return compNames.has(fnName);
}

@@ -147,4 +97,4 @@

| ts.JsxSelfClosingElement,
sf: ts.SourceFile,
{overrideIdFn, extractSourceLocation}: Opts
{overrideIdFn, extractSourceLocation}: Opts,
sf: ts.SourceFile
): MessageDescriptor | undefined {

@@ -161,2 +111,3 @@ let properties: ts.NodeArray<ts.ObjectLiteralElement> | undefined = undefined;

}
properties.forEach(prop => {

@@ -168,6 +119,11 @@ const {name} = prop;

: undefined;
if (!initializer || !ts.isStringLiteral(initializer) || !name) {
if (
!initializer ||
!ts.isStringLiteral(initializer) ||
!name ||
!ts.isIdentifier(name)
) {
return;
}
switch (name.getText(sf)) {
switch (name.text) {
case 'id':

@@ -210,4 +166,4 @@ msg.id = initializer.text;

file: sf.fileName,
start: node.getStart(sf),
end: node.getEnd(),
start: node.pos,
end: node.end,
};

@@ -223,3 +179,3 @@ }

*/
function isIntlFormatMessageCall(node: ts.CallExpression, sf: ts.SourceFile) {
function isIntlFormatMessageCall(node: ts.CallExpression) {
const method = node.expression;

@@ -230,7 +186,7 @@

return (
(method.name.getText(sf) === 'formatMessage' &&
(method.name.text === 'formatMessage' &&
ts.isIdentifier(method.expression) &&
method.expression.getText(sf) === 'intl') ||
method.expression.text === 'intl') ||
(ts.isPropertyAccessExpression(method.expression) &&
method.expression.name.getText(sf) === 'intl')
method.expression.name.text === 'intl')
);

@@ -240,3 +196,3 @@ }

// Handle formatMessage()
return ts.isIdentifier(method) && method.getText(sf) === 'formatMessage';
return ts.isIdentifier(method) && method.text === 'formatMessage';
}

@@ -246,18 +202,10 @@

node: ts.JsxOpeningElement | ts.JsxSelfClosingElement,
program: ts.Program,
sf: ts.SourceFile,
opts: Opts
opts: Opts,
sf: ts.SourceFile
): typeof node | undefined {
const {moduleSourceName = 'react-intl', onMsgExtracted} = opts;
if (
!isSingularMessageDecl(
node,
program,
moduleSourceName,
opts.additionalComponentNames || []
)
) {
const {onMsgExtracted} = opts;
if (!isSingularMessageDecl(node, opts.additionalComponentNames || [])) {
return;
}
const msg = extractMessageDescriptor(node, sf, opts);
const msg = extractMessageDescriptor(node, opts, sf);
if (!msg) {

@@ -301,8 +249,7 @@ return;

node: ts.CallExpression,
program: ts.Program,
sf: ts.SourceFile,
opts: Opts
opts: Opts,
sf: ts.SourceFile
): typeof node | undefined {
const {moduleSourceName = 'react-intl', onMsgExtracted} = opts;
if (isMultipleMessageDecl(node, program, moduleSourceName)) {
const {onMsgExtracted} = opts;
if (isMultipleMessageDecl(node)) {
const [descriptorsObj, ...restArgs] = node.arguments;

@@ -317,4 +264,4 @@ if (ts.isObjectLiteralExpression(descriptorsObj)) {

prop.initializer as ts.ObjectLiteralExpression,
sf,
opts
opts,
sf
)

@@ -355,13 +302,8 @@ )

} else if (
isSingularMessageDecl(
node,
program,
moduleSourceName,
opts.additionalComponentNames || []
) ||
(opts.extractFromFormatMessageCall && isIntlFormatMessageCall(node, sf))
isSingularMessageDecl(node, opts.additionalComponentNames || []) ||
(opts.extractFromFormatMessageCall && isIntlFormatMessageCall(node))
) {
const [descriptorsObj, ...restArgs] = node.arguments;
if (ts.isObjectLiteralExpression(descriptorsObj)) {
const msg = extractMessageDescriptor(descriptorsObj, sf, opts);
const msg = extractMessageDescriptor(descriptorsObj, opts, sf);
if (!msg) {

@@ -391,10 +333,9 @@ return;

opts = {...DEFAULT_OPTS, ...opts};
const {program} = opts;
return (ctx: ts.TransformationContext): ts.Transformer<ts.SourceFile> => {
const transformFn: ts.TransformerFactory<ts.SourceFile> = ctx => {
function getVisitor(sf: ts.SourceFile) {
const visitor: ts.Visitor = (node: ts.Node): ts.Node => {
const newNode = ts.isCallExpression(node)
? extractMessagesFromCallExpression(node, program, sf, opts)
? extractMessagesFromCallExpression(node, opts, sf)
: ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)
? extractMessageFromJsxComponent(node, program, sf, opts)
? extractMessageFromJsxComponent(node, opts, sf)
: undefined;

@@ -409,2 +350,4 @@

};
return transformFn;
}
import {join} from 'path';
import {Project} from 'ts-morph';
import {transform, Opts, MessageDescriptor} from '../src';
import * as ts from 'typescript';
import {readFile as readFileAsync} from 'fs';
import {promisify} from 'util';
const readFile = promisify(readFileAsync);
const FILES_TO_TESTS: Record<string, Partial<Opts>> = {

@@ -23,5 +26,2 @@ additionalComponentNames: {

inline: {},
moduleSourceName: {
moduleSourceName: 'react-i18n',
},
overrideIdFn: {

@@ -49,4 +49,4 @@ overrideIdFn: (id, defaultMessage, description) => {

if (fn === 'extractSourceLocation') {
it(`[special] ${fn}`, function () {
const output = compile(
it(`[special] ${fn}`, async function () {
const output = await compile(
join(FIXTURES_DIR, `${fn}.tsx`),

@@ -61,3 +61,3 @@ FILES_TO_TESTS[fn]

id: 'foo.bar.baz',
start: 155,
start: 154,
end: 222,

@@ -68,4 +68,4 @@ file: expect.stringContaining('extractSourceLocation.tsx'),

} else {
it(fn, function () {
const output = compile(
it(fn, async function () {
const output = await compile(
join(FIXTURES_DIR, `${fn}.tsx`),

@@ -80,4 +80,7 @@ FILES_TO_TESTS[fn]

function compile(filePath: string, options?: Partial<Opts>) {
const project = new Project({
async function compile(filePath: string, options?: Partial<Opts>) {
let msgs: MessageDescriptor[] = [];
const input = await readFile(filePath, 'utf8');
const output = ts.transpileModule(input, {
compilerOptions: {

@@ -98,9 +101,5 @@ experimentalDecorators: true,

},
});
project.addSourceFileAtPathIfExists(filePath);
let msgs: MessageDescriptor[] = [];
const result = project.emitToMemory({
customTransformers: {
fileName: filePath,
reportDiagnostics: true,
transformers: {
before: [

@@ -112,3 +111,2 @@ transform({

},
program: project.getProgram().compilerObject,
...(options || {}),

@@ -121,4 +119,4 @@ }),

msgs,
code: result.getFiles()[0].text,
code: output.outputText,
};
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

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