Skip to content
Merged
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
106 changes: 64 additions & 42 deletions index.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,50 @@
import ts = require("typescript");

function isJsxOpeningLike(node: ts.Node): node is ts.JsxOpeningLikeElement {
function isDefineMessages(el: ts.Declaration, tagName: string): el is ts.VariableDeclaration {
return (
node.kind === ts.SyntaxKind.JsxOpeningElement ||
node.kind === ts.SyntaxKind.JsxSelfClosingElement
);
}

function isDefineMessages(el, tagName) {
return (
el.kind === ts.SyntaxKind.VariableDeclaration &&
ts.isVariableDeclaration(el) &&
el.initializer &&
ts.isCallExpression(el.initializer) &&
el.initializer.expression &&
ts.isIdentifier(el.initializer.expression) &&
el.initializer.expression.text === tagName
);
}

function findProps(node) {
var res = [];
// Should be pretty fast: https://stackoverflow.com/a/34491287/14379
function emptyObject(obj: any) {
for (var x in obj) {
return false;
}
return true;
}

interface LooseObject {
[key: string]: any
}

function findProps(node: ts.Node): LooseObject[] {
var res: LooseObject[] = [];
find(node);
function find(node) {
function find(node: ts.Node): LooseObject[] {
if (!node) {
return undefined;
}
if (node.kind === ts.SyntaxKind.ObjectLiteralExpression) {
if (ts.isObjectLiteralExpression(node)) {
node.properties.forEach(p => {
var props = {};
var prop = {};
if (p.initializer.properties) {
var prop: LooseObject = {};
if (
ts.isPropertyAssignment(p) &&
ts.isObjectLiteralExpression(p.initializer) &&
p.initializer.properties
) {
p.initializer.properties.forEach(ip => {
prop[ip.name.text] = ip.initializer.text;
if (ts.isIdentifier(ip.name)) {
let name = ip.name.text
if (ts.isPropertyAssignment(ip) && ts.isStringLiteral(ip.initializer)) {
prop[name] = ip.initializer.text;
}
}
});
res.push(prop);
}
Expand All @@ -41,43 +56,51 @@ function findProps(node) {
return res;
}

function forAllVarDecls(node: ts.Node, cb: (el: ts.VariableDeclaration) => void) {
if (ts.isVariableDeclaration(node)) {
cb(node)
} else {
ts.forEachChild(node, n => forAllVarDecls(n, cb))
}
}

function findFirstJsxOpeningLikeElementWithName(
node,
node: ts.SourceFile,
tagName: string,
dm?: boolean
) {
var res = [];
var res: LooseObject[] = [];
find(node);

function find(node) {
function find(node: ts.Node | ts.SourceFile): undefined {
if (!node) {
return undefined;
}
if (dm && node.getNamedDeclarations) {
var nd = node.getNamedDeclarations();
nd.forEach(element => {
element.forEach(el => {
if (isDefineMessages(el, tagName)) {
if (
el.initializer.kind === ts.SyntaxKind.CallExpression &&
el.initializer.arguments.length
) {
var nodeProps = el.initializer.arguments[0];
var props = findProps(nodeProps);
res = res.concat(props);
}
if (dm && ts.isSourceFile(node)) {
// getNamedDeclarations is not currently public
forAllVarDecls(node, (el: ts.Declaration) => {
if (isDefineMessages(el, tagName)) {
if (
ts.isCallExpression(el.initializer) &&
el.initializer.arguments.length
) {
var nodeProps = el.initializer.arguments[0];
var props = findProps(nodeProps);
// props is an array of LooseObject
res = res.concat(props);
}
});
});
}
})
} else {
// Is this a JsxElement with an identifier name?
if (
isJsxOpeningLike(node) &&
node.tagName.kind === ts.SyntaxKind.Identifier
ts.isJsxOpeningLikeElement(node) &&
ts.isIdentifier(node.tagName)
) {
// Does the tag name match what we're looking for?
const childTagName = node.tagName as any;
const childTagName = node.tagName;
if (childTagName.text === tagName) {
// node is a JsxOpeningLikeElement
res.push(node);
}
}
Expand All @@ -95,7 +118,7 @@ function findFirstJsxOpeningLikeElementWithName(
* @param {string} contents
* @returns {array}
*/
function main(contents: string) {
function main(contents: string): {}[] {
var sourceFile = ts.createSourceFile(
"file.ts",
contents,
Expand All @@ -114,13 +137,12 @@ function main(contents: string) {
true
);

const emptyObject = o => JSON.stringify(o) === "{}";
var res = elements
.map(element => {
var msg = {};
var msg: LooseObject = {};
debugger;
element.attributes &&
element.attributes.properties.forEach(attr => {
element.attributes.properties.forEach((attr: LooseObject) => {
// found nothing
if (!attr.name || !attr.initializer) return;
msg[attr.name.text] =
Expand Down
62 changes: 38 additions & 24 deletions lib/index.js

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

2 changes: 1 addition & 1 deletion lib/index.js.map

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
},
"homepage": "https://github.com/bang88/typescript-react-intl#readme",
"dependencies": {
"typescript": "^2.3.4"
"typescript": "^2.6.2"
},
"devDependencies": {
"@types/react": "^15.0.27",
Expand Down
8 changes: 8 additions & 0 deletions test/app/defineMessages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,12 @@ const lang = defineMessages({
},
})

// NB not a variable declaration; should be ignored
defineMessages({
ignored: {
id: 'ignored.title',
defaultMessage: "Ignore me"
},
})

export default lang
3 changes: 3 additions & 0 deletions test/app/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ export class App extends React.Component<any, any>{
return <div>
<FormattedMessage id="app" defaultMessage="the defualt message" />
<FormattedMessage {...messages.intro} />
<FormattedMessage id="expr" defaultMessage={"a jsx expression"} />

{/* Not currently supported: <FormattedMessage id="concat" defaultMessage={"concatenated " + "strings"} /> */}
</div>
}
}
Expand Down
10 changes: 9 additions & 1 deletion test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@ test("<FormattedMessage/>", t => {
{
id: "app",
defaultMessage: "the defualt message"
},
{
id: "expr",
defaultMessage: "a jsx expression"
}
// {
// id: "concat",
// defaultMessage: "concatenated strings"
// }
];

t.is(res.length, 1);
t.is(res.length, 2);

t.deepEqual(res, expected);
});
Expand Down
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"noEmitOnError": false,
"target": "es5",
"declaration": true,
"noImplicitAny": false,
"noImplicitAny": true,
"removeComments": true,
"rootDir": "."
},
Expand Down
6 changes: 3 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2693,9 +2693,9 @@ tweetnacl@^0.14.3, tweetnacl@~0.14.0:
version "0.14.5"
resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64"

typescript@^2.3.4:
version "2.3.4"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.3.4.tgz#3d38321828231e434f287514959c37a82b629f42"
typescript@^2.6.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.6.2.tgz#3c5b6fd7f6de0914269027f03c0946758f7673a4"

uid-number@~0.0.6:
version "0.0.6"
Expand Down