- (chore) update packages
- (refactor) use tsquery for querying AST - (feat) autodetect usage of marker function and remove --marker cli argument - (bugfix) extract strings when TranslateService is declared directly as a class parameter. Closes https://github.com/biesbjerg/ngx-translate-extract/issues/83 - (bugfix) handle split strings: marker('hello ' + 'world') is now extracted as a single string: 'hello world'
This commit is contained in:
		| @@ -52,7 +52,7 @@ If you want to use spaces instead, you can do the following: | ||||
| `ngx-translate-extract --input ./src --output ./src/i18n/en.json --format-indentation '  '` | ||||
|  | ||||
| ## Mark strings for extraction using a marker function | ||||
| If, for some reason, you want to extract strings not passed directly to `TranslateService`'s `get()` or `instant()` methods, you can wrap them in a custom marker function to let `ngx-translate-extract` know you want to extract them. | ||||
| If, for some reason, you want to extract strings not passed directly to `TranslateService`'s `get()` or `instant()` methods, you can wrap them in a marker function to let `ngx-translate-extract` know you want to extract them. | ||||
|  | ||||
| Install marker function: | ||||
| `npm install @biesbjerg/ngx-translate-extract-marker` | ||||
| @@ -94,8 +94,6 @@ Options: | ||||
|   --output, -o                Paths where you would like to save extracted | ||||
|                               strings. You can use path expansion, glob patterns | ||||
|                               and multiple paths              [array] [required] | ||||
|   --marker, -m                Extract strings passed to a marker function | ||||
|                                                        [string] [default: false] | ||||
|   --format, -f                Output format | ||||
|           [string] [choices: "json", "namespaced-json", "pot"] [default: "json"] | ||||
|   --format-indentation, --fi  Output format indentation   [string] [default: "	"] | ||||
|   | ||||
							
								
								
									
										151
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										151
									
								
								package-lock.json
									
									
									
										generated
									
									
									
								
							| @@ -1,13 +1,13 @@ | ||||
| { | ||||
|   "name": "@biesbjerg/ngx-translate-extract", | ||||
|   "version": "3.0.4", | ||||
|   "version": "4.0.0", | ||||
|   "lockfileVersion": 1, | ||||
|   "requires": true, | ||||
|   "dependencies": { | ||||
|     "@angular/compiler": { | ||||
|       "version": "8.2.2", | ||||
|       "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-8.2.2.tgz", | ||||
|       "integrity": "sha512-UMhOQehvi9u1r4u48Ymwm5JkdOKoH057ImCo26WqRqJBUgA44xwmUsKLFAmSg1JqzWCO5pBDyA3RaNBscD8ZzQ==", | ||||
|       "version": "8.2.6", | ||||
|       "resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-8.2.6.tgz", | ||||
|       "integrity": "sha512-NdTY2n0XNRmKixbKDWB++9tEDLFwN0/Bp/1lXJ4qF/8V5Wju6IJ/UZZKjR5C4uiKtF17T3GzubhXgghumt5UVA==", | ||||
|       "requires": { | ||||
|         "tslib": "^1.9.0" | ||||
|       } | ||||
| @@ -32,10 +32,18 @@ | ||||
|         "js-tokens": "^4.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "@phenomnomnominal/tsquery": { | ||||
|       "version": "3.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/@phenomnomnominal/tsquery/-/tsquery-3.0.0.tgz", | ||||
|       "integrity": "sha512-SW8lKitBHWJ9fAYkJ9kJivuctwNYCh3BUxLdH0+XiR1GPBiu+7qiZzh8p8jqlj1LgVC1TbvfNFroaEsmYlL8Iw==", | ||||
|       "requires": { | ||||
|         "esquery": "^1.0.1" | ||||
|       } | ||||
|     }, | ||||
|     "@types/chai": { | ||||
|       "version": "4.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.0.tgz", | ||||
|       "integrity": "sha512-zw8UvoBEImn392tLjxoavuonblX/4Yb9ha4KBU10FirCfwgzhKO0dvyJSF9ByxV1xK1r2AgnAi/tvQaLgxQqxA==", | ||||
|       "version": "4.2.2", | ||||
|       "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.2.tgz", | ||||
|       "integrity": "sha512-8V2aCcPM3WLuJvJpF6N60uUvdZb7NHjpjQlLk1QmZbTP4XZET/FX0c3TJ3K7qt4L98FS1Pifa8BVofTVuJFWVA==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "@types/events": { | ||||
| @@ -83,9 +91,9 @@ | ||||
|       "dev": true | ||||
|     }, | ||||
|     "@types/node": { | ||||
|       "version": "7.0.8", | ||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-7.0.8.tgz", | ||||
|       "integrity": "sha1-JeTdgEtjDJFq5nEjPm1x9s4YEko=", | ||||
|       "version": "12.7.5", | ||||
|       "resolved": "https://registry.npmjs.org/@types/node/-/node-12.7.5.tgz", | ||||
|       "integrity": "sha512-9fq4jZVhPNW8r+UYKnxF1e2HkDWOWKM5bC2/7c9wPV835I0aOrVbS/Hw/pWPk2uKrNXQqg9Z959Kz+IYDd5p3w==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "@types/yargs": { | ||||
| @@ -143,9 +151,12 @@ | ||||
|       "dev": true | ||||
|     }, | ||||
|     "ansi-escapes": { | ||||
|       "version": "3.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", | ||||
|       "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==" | ||||
|       "version": "4.2.1", | ||||
|       "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.2.1.tgz", | ||||
|       "integrity": "sha512-Cg3ymMAdN10wOk/VYfLV7KCQyv7EDirJ64500sU7n9UlmioEtDuU5Gd+hj73hXSU/ex7tHJSssmyftDdkMLO8Q==", | ||||
|       "requires": { | ||||
|         "type-fest": "^0.5.2" | ||||
|       } | ||||
|     }, | ||||
|     "ansi-regex": { | ||||
|       "version": "3.0.0", | ||||
| @@ -162,9 +173,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "arg": { | ||||
|       "version": "4.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", | ||||
|       "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", | ||||
|       "version": "4.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.1.tgz", | ||||
|       "integrity": "sha512-SlmP3fEA88MBv0PypnXZ8ZfJhwmDeIE3SP71j37AiXQBXYosPV0x6uISAaHYSlSVhmHOVkomen0tbGk6Anlebw==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "argparse": { | ||||
| @@ -482,6 +493,19 @@ | ||||
|       "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", | ||||
|       "dev": true | ||||
|     }, | ||||
|     "esquery": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", | ||||
|       "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==", | ||||
|       "requires": { | ||||
|         "estraverse": "^4.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "estraverse": { | ||||
|       "version": "4.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", | ||||
|       "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==" | ||||
|     }, | ||||
|     "esutils": { | ||||
|       "version": "2.0.3", | ||||
|       "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", | ||||
| @@ -550,9 +574,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "gettext-parser": { | ||||
|       "version": "4.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-4.0.1.tgz", | ||||
|       "integrity": "sha512-ny1f9saN1xnhto5UzDOp7djJy7NbK6ebDAmOFXwp0DDu5KkQ5u3WF6giFU3BXHVqkS+3bxjXS1AmSUbX64fblA==", | ||||
|       "version": "4.0.2", | ||||
|       "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-4.0.2.tgz", | ||||
|       "integrity": "sha512-JPCBpGzm01te+nTenJwWqKDzixYPY4pInedixpcMl4GPEJeia/cH2TJCh32IggDrrLYrzqA8OitXZLpBdrx4Gg==", | ||||
|       "requires": { | ||||
|         "content-type": "^1.0.4", | ||||
|         "encoding": "^0.1.12", | ||||
| @@ -1147,9 +1171,9 @@ | ||||
|       "dev": true | ||||
|     }, | ||||
|     "source-map-support": { | ||||
|       "version": "0.5.12", | ||||
|       "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.12.tgz", | ||||
|       "integrity": "sha512-4h2Pbvyy15EE02G+JOZpUCmqWJuqrs+sEkzewTm++BPi7Hvn/HwcqLAcNxYAyI0x13CpPPn+kMjl+hplXMHITQ==", | ||||
|       "version": "0.5.13", | ||||
|       "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", | ||||
|       "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "buffer-from": "^1.0.0", | ||||
| @@ -1173,18 +1197,11 @@ | ||||
|       } | ||||
|     }, | ||||
|     "string_decoder": { | ||||
|       "version": "1.2.0", | ||||
|       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.2.0.tgz", | ||||
|       "integrity": "sha512-6YqyX6ZWEYguAxgZzHGL7SsCeGx3V2TtOTqZz1xSTSWnqsbWwbptafNyvf/ACquZUXV3DANr5BDIwNYe1mN42w==", | ||||
|       "version": "1.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", | ||||
|       "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", | ||||
|       "requires": { | ||||
|         "safe-buffer": "~5.1.0" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "safe-buffer": { | ||||
|           "version": "5.1.2", | ||||
|           "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", | ||||
|           "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" | ||||
|         } | ||||
|         "safe-buffer": "~5.2.0" | ||||
|       } | ||||
|     }, | ||||
|     "strip-ansi": { | ||||
| @@ -1217,18 +1234,26 @@ | ||||
|       } | ||||
|     }, | ||||
|     "supports-hyperlinks": { | ||||
|       "version": "1.0.1", | ||||
|       "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-1.0.1.tgz", | ||||
|       "integrity": "sha512-HHi5kVSefKaJkGYXbDuKbUGRVxqnWGn3J2e39CYcNJEfWciGq2zYtOhXLTlvrOZW1QU7VX67w7fMmWafHX9Pfw==", | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.0.0.tgz", | ||||
|       "integrity": "sha512-bFhn0MQ8qefLyJ3K7PpHiPUTuTVPWw6RXfaMeV6xgJLXtBbszyboz1bvGTVv4R0YpQm2DqlXXn0fFHhxUHVE5w==", | ||||
|       "requires": { | ||||
|         "has-flag": "^2.0.0", | ||||
|         "supports-color": "^5.0.0" | ||||
|         "has-flag": "^4.0.0", | ||||
|         "supports-color": "^7.0.0" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "has-flag": { | ||||
|           "version": "2.0.0", | ||||
|           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-2.0.0.tgz", | ||||
|           "integrity": "sha1-6CB68cx7MNRGzHC3NLXovhj4jVE=" | ||||
|           "version": "4.0.0", | ||||
|           "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", | ||||
|           "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" | ||||
|         }, | ||||
|         "supports-color": { | ||||
|           "version": "7.0.0", | ||||
|           "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.0.0.tgz", | ||||
|           "integrity": "sha512-WRt32iTpYEZWYOpcetGm0NPeSvaebccx7hhS/5M6sAiqnhedtFCHFxkjzZlJvFNCPowiKSFGiZk5USQDFy83vQ==", | ||||
|           "requires": { | ||||
|             "has-flag": "^4.0.0" | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
| @@ -1238,18 +1263,18 @@ | ||||
|       "integrity": "sha512-I42EWhJ+2aeNQawGx1VtpO0DFI9YcfuvAMNIdKyf/6sRbHJ4P+ZQ/zIT87tE+ln1ymAGcCJds4dolfSAS0AcNg==" | ||||
|     }, | ||||
|     "terminal-link": { | ||||
|       "version": "1.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-1.3.0.tgz", | ||||
|       "integrity": "sha512-nFaWG/gs3brGi3opgWU2+dyFGbQ7tueSRYOBOD8URdDXCbAGqDEZzuskCc+okCClYcJFDPwn8e2mbv4FqAnWFA==", | ||||
|       "version": "2.0.0", | ||||
|       "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.0.0.tgz", | ||||
|       "integrity": "sha512-rdBAY35jUvVapqCuhehjenLbYY73cVgRQ6podD6u9EDBomBBHjCOtmq2InPgPpTysOIOsQ5PdBzwSC/sKjv6ew==", | ||||
|       "requires": { | ||||
|         "ansi-escapes": "^3.2.0", | ||||
|         "supports-hyperlinks": "^1.0.1" | ||||
|         "ansi-escapes": "^4.2.1", | ||||
|         "supports-hyperlinks": "^2.0.0" | ||||
|       } | ||||
|     }, | ||||
|     "ts-node": { | ||||
|       "version": "8.3.0", | ||||
|       "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.3.0.tgz", | ||||
|       "integrity": "sha512-dyNS/RqyVTDcmNM4NIBAeDMpsAdaQ+ojdf0GOLqE6nwJOgzEkdRNzJywhDfwnuvB10oa6NLVG1rUJQCpRN7qoQ==", | ||||
|       "version": "8.4.1", | ||||
|       "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.4.1.tgz", | ||||
|       "integrity": "sha512-5LpRN+mTiCs7lI5EtbXmF/HfMeCjzt7DH9CZwtkr6SywStrNQC723wG+aOWFiLNn7zT3kD/RnFqi3ZUfr4l5Qw==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "arg": "^4.1.0", | ||||
| @@ -1273,16 +1298,16 @@ | ||||
|       "integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==" | ||||
|     }, | ||||
|     "tslint": { | ||||
|       "version": "5.19.0", | ||||
|       "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.19.0.tgz", | ||||
|       "integrity": "sha512-1LwwtBxfRJZnUvoS9c0uj8XQtAnyhWr9KlNvDIdB+oXyT+VpsOAaEhEgKi1HrZ8rq0ki/AAnbGSv4KM6/AfVZw==", | ||||
|       "version": "5.20.0", | ||||
|       "resolved": "https://registry.npmjs.org/tslint/-/tslint-5.20.0.tgz", | ||||
|       "integrity": "sha512-2vqIvkMHbnx8acMogAERQ/IuINOq6DFqgF8/VDvhEkBqQh/x6SP0Y+OHnKth9/ZcHQSroOZwUQSN18v8KKF0/g==", | ||||
|       "dev": true, | ||||
|       "requires": { | ||||
|         "@babel/code-frame": "^7.0.0", | ||||
|         "builtin-modules": "^1.1.1", | ||||
|         "chalk": "^2.3.0", | ||||
|         "commander": "^2.12.1", | ||||
|         "diff": "^3.2.0", | ||||
|         "diff": "^4.0.1", | ||||
|         "glob": "^7.1.1", | ||||
|         "js-yaml": "^3.13.1", | ||||
|         "minimatch": "^3.0.4", | ||||
| @@ -1291,6 +1316,14 @@ | ||||
|         "semver": "^5.3.0", | ||||
|         "tslib": "^1.8.0", | ||||
|         "tsutils": "^2.29.0" | ||||
|       }, | ||||
|       "dependencies": { | ||||
|         "diff": { | ||||
|           "version": "4.0.1", | ||||
|           "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.1.tgz", | ||||
|           "integrity": "sha512-s2+XdvhPCOF01LRQBC8hf4vhbVmI2CGS5aZnxLJlT5FtdhPCDFq80q++zK2KlrVorVDdL5BOGZ/VfLrVtYNF+Q==", | ||||
|           "dev": true | ||||
|         } | ||||
|       } | ||||
|     }, | ||||
|     "tslint-eslint-rules": { | ||||
| @@ -1342,9 +1375,9 @@ | ||||
|       "integrity": "sha512-DWkS49EQKVX//Tbupb9TFa19c7+MK1XmzkrZUR8TAktmE/DizXoaoJV6TZ/tSIPXipqNiRI6CyAe7x69Jb6RSw==" | ||||
|     }, | ||||
|     "typescript": { | ||||
|       "version": "3.5.3", | ||||
|       "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.5.3.tgz", | ||||
|       "integrity": "sha512-ACzBtm/PhXBDId6a6sDJfroT2pOWt/oOnk4/dElG5G33ZL776N3Y6/6bKZJBFpd+b05F3Ct9qDjMeJmRWtE2/g==" | ||||
|       "version": "3.6.3", | ||||
|       "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.6.3.tgz", | ||||
|       "integrity": "sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw==" | ||||
|     }, | ||||
|     "util": { | ||||
|       "version": "0.10.3", | ||||
| @@ -1636,9 +1669,9 @@ | ||||
|       } | ||||
|     }, | ||||
|     "yn": { | ||||
|       "version": "3.1.0", | ||||
|       "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.0.tgz", | ||||
|       "integrity": "sha512-kKfnnYkbTfrAdd0xICNFw7Atm8nKpLcLv9AZGEt+kczL/WQVai4e2V6ZN8U/O+iI6WrNuJjNNOyu4zfhl9D3Hg==", | ||||
|       "version": "3.1.1", | ||||
|       "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", | ||||
|       "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", | ||||
|       "dev": true | ||||
|     } | ||||
|   } | ||||
|   | ||||
							
								
								
									
										20
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,6 +1,6 @@ | ||||
| { | ||||
|   "name": "@biesbjerg/ngx-translate-extract", | ||||
|   "version": "3.0.5", | ||||
|   "version": "4.0.0", | ||||
|   "description": "Extract strings from projects using ngx-translate", | ||||
|   "main": "dist/index.js", | ||||
|   "typings": "dist/index.d.ts", | ||||
| @@ -40,36 +40,38 @@ | ||||
|   }, | ||||
|   "homepage": "https://github.com/biesbjerg/ngx-translate-extract", | ||||
|   "engines": { | ||||
|     "node": ">=4.3.2" | ||||
|     "node": ">=8" | ||||
|   }, | ||||
|   "config": {}, | ||||
|   "devDependencies": { | ||||
|     "@types/chai": "^4.2.0", | ||||
|     "@types/chai": "^4.2.2", | ||||
|     "@types/flat": "^0.0.28", | ||||
|     "@types/glob": "^7.1.1", | ||||
|     "@types/mkdirp": "^0.5.2", | ||||
|     "@types/mocha": "^5.2.7", | ||||
|     "@types/node": "^12.7.5", | ||||
|     "@types/yargs": "^13.0.2", | ||||
|     "chai": "^4.2.0", | ||||
|     "mocha": "^6.2.0", | ||||
|     "ts-node": "^8.3.0", | ||||
|     "tslint": "^5.19.0", | ||||
|     "ts-node": "^8.4.1", | ||||
|     "tslint": "^5.20.0", | ||||
|     "tslint-eslint-rules": "^5.4.0" | ||||
|   }, | ||||
|   "bundledDependencies": [ | ||||
|     "flat" | ||||
|   ], | ||||
|   "dependencies": { | ||||
|     "@angular/compiler": "^8.2.2", | ||||
|     "@angular/compiler": "^8.2.6", | ||||
|     "@phenomnomnominal/tsquery": "^3.0.0", | ||||
|     "boxen": "^4.1.0", | ||||
|     "colorette": "^1.1.0", | ||||
|     "flat": "github:lenchvolodymyr/flat#ffe77ef", | ||||
|     "gettext-parser": "^4.0.1", | ||||
|     "gettext-parser": "^4.0.2", | ||||
|     "glob": "^7.1.4", | ||||
|     "mkdirp": "^0.5.1", | ||||
|     "path": "^0.12.7", | ||||
|     "terminal-link": "^1.3.0", | ||||
|     "typescript": "^3.5.3", | ||||
|     "terminal-link": "^2.0.0", | ||||
|     "typescript": "^3.6.3", | ||||
|     "yargs": "^14.0.0" | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -6,7 +6,7 @@ import { ParserInterface } from '../parsers/parser.interface'; | ||||
| import { PipeParser } from '../parsers/pipe.parser'; | ||||
| import { DirectiveParser } from '../parsers/directive.parser'; | ||||
| import { ServiceParser } from '../parsers/service.parser'; | ||||
| import { FunctionParser } from '../parsers/function.parser'; | ||||
| import { MarkerParser } from '../parsers/marker.parser'; | ||||
| import { PostProcessorInterface } from '../post-processors/post-processor.interface'; | ||||
| import { SortByKeyPostProcessor } from '../post-processors/sort-by-key.post-processor'; | ||||
| import { KeyAsDefaultValuePostProcessor } from '../post-processors/key-as-default-value.post-processor'; | ||||
| @@ -29,7 +29,7 @@ export const cli = yargs | ||||
| 		normalize: true | ||||
| 	}) | ||||
| 	.check(options => { | ||||
| 		options.input.forEach((dir: string) => { | ||||
| 		(options.input as unknown as string[]).forEach((dir: string) => { | ||||
| 			if (!fs.existsSync(dir) || !fs.statSync(dir).isDirectory()) { | ||||
| 				throw new Error(`The path you supplied was not found: '${dir}'`); | ||||
| 			} | ||||
| @@ -50,12 +50,6 @@ export const cli = yargs | ||||
| 		normalize: true, | ||||
| 		required: true | ||||
| 	}) | ||||
| 	.option('marker', { | ||||
| 		alias: 'm', | ||||
| 		describe: 'Extract strings passed to a marker function', | ||||
| 		default: false, | ||||
| 		type: 'string' | ||||
| 	}) | ||||
| 	.option('format', { | ||||
| 		alias: 'f', | ||||
| 		describe: 'Output format', | ||||
| @@ -96,7 +90,7 @@ export const cli = yargs | ||||
| 	.exitProcess(true) | ||||
| 	.parse(process.argv); | ||||
|  | ||||
| const extractTask = new ExtractTask(cli.input, cli.output, { | ||||
| const extractTask = new ExtractTask(cli.input as unknown as string[], cli.output, { | ||||
| 	replace: cli.replace, | ||||
| 	patterns: cli.patterns | ||||
| }); | ||||
| @@ -105,13 +99,9 @@ const extractTask = new ExtractTask(cli.input, cli.output, { | ||||
| const parsers: ParserInterface[] = [ | ||||
| 	new PipeParser(), | ||||
| 	new DirectiveParser(), | ||||
| 	new ServiceParser() | ||||
| 	new ServiceParser(), | ||||
| 	new MarkerParser() | ||||
| ]; | ||||
| if (cli.marker) { | ||||
| 	parsers.push(new FunctionParser({ | ||||
| 		identifier: cli.marker | ||||
| 	})); | ||||
| } | ||||
| extractTask.setParsers(parsers); | ||||
|  | ||||
| // Post processors | ||||
|   | ||||
| @@ -100,17 +100,20 @@ export class ExtractTask implements TaskInterface { | ||||
| 	 * Extract strings from specified input dirs using configured parsers | ||||
| 	 */ | ||||
| 	protected extract(): TranslationCollection { | ||||
| 		let extracted: TranslationCollection = new TranslationCollection(); | ||||
| 		let collection: TranslationCollection = new TranslationCollection(); | ||||
| 		this.inputs.forEach(dir => { | ||||
| 			this.readDir(dir, this.options.patterns).forEach(path => { | ||||
| 				this.out(dim('- %s'), path); | ||||
| 				const contents: string = fs.readFileSync(path, 'utf-8'); | ||||
| 				this.parsers.forEach(parser => { | ||||
| 					extracted = extracted.union(parser.extract(contents, path)); | ||||
| 					const extracted = parser.extract(contents, path); | ||||
| 					if (extracted) { | ||||
| 						collection = collection.union(extracted); | ||||
| 					} | ||||
| 				}); | ||||
| 			}); | ||||
| 		}); | ||||
| 		return extracted; | ||||
| 		return collection; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
|   | ||||
| @@ -6,11 +6,10 @@ export * from './cli/tasks/task.interface'; | ||||
| export * from './cli/tasks/extract.task'; | ||||
|  | ||||
| export * from './parsers/parser.interface'; | ||||
| export * from './parsers/abstract-ast.parser'; | ||||
| export * from './parsers/directive.parser'; | ||||
| export * from './parsers/pipe.parser'; | ||||
| export * from './parsers/service.parser'; | ||||
| export * from './parsers/function.parser'; | ||||
| export * from './parsers/marker.parser'; | ||||
|  | ||||
| export * from './compilers/compiler.interface'; | ||||
| export * from './compilers/compiler.factory'; | ||||
|   | ||||
| @@ -1,48 +0,0 @@ | ||||
| import { | ||||
| 	createSourceFile, | ||||
| 	SourceFile, | ||||
| 	CallExpression, | ||||
| 	Node, | ||||
| 	SyntaxKind, | ||||
| 	StringLiteral, | ||||
| 	NoSubstitutionTemplateLiteral | ||||
| } from 'typescript'; | ||||
|  | ||||
| export abstract class AbstractAstParser { | ||||
|  | ||||
| 	protected sourceFile: SourceFile; | ||||
|  | ||||
| 	protected createSourceFile(path: string, contents: string): SourceFile { | ||||
| 		return createSourceFile(path, contents, null, /*setParentNodes */ false); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Get strings from function call's first argument | ||||
| 	 */ | ||||
| 	protected getStringLiterals(callNode: CallExpression): string[] { | ||||
| 		if (!callNode.arguments.length) { | ||||
| 			return[]; | ||||
| 		} | ||||
|  | ||||
| 		const firstArg = callNode.arguments[0]; | ||||
|  | ||||
| 		return this.findNodes(firstArg, [ | ||||
| 			SyntaxKind.StringLiteral, | ||||
| 			SyntaxKind.NoSubstitutionTemplateLiteral | ||||
| 		]) | ||||
| 		.map((node: StringLiteral | NoSubstitutionTemplateLiteral) => node.text); | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Find all child nodes of a kind | ||||
| 	 */ | ||||
| 	protected findNodes(node: Node, kinds: SyntaxKind[]): Node[] { | ||||
| 		const childrenNodes: Node[] = node.getChildren(this.sourceFile); | ||||
| 		const initialValue: Node[] = kinds.includes(node.kind) ? [node] : []; | ||||
|  | ||||
| 		return childrenNodes.reduce((result: Node[], childNode: Node) => { | ||||
| 			return result.concat(this.findNodes(childNode, kinds)); | ||||
| 		}, initialValue); | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -1,60 +0,0 @@ | ||||
| import { Node, CallExpression, SyntaxKind, Identifier } from 'typescript'; | ||||
|  | ||||
| import { ParserInterface } from './parser.interface'; | ||||
| import { AbstractAstParser } from './abstract-ast.parser'; | ||||
| import { TranslationCollection } from '../utils/translation.collection'; | ||||
|  | ||||
| export class FunctionParser extends AbstractAstParser implements ParserInterface { | ||||
|  | ||||
| 	protected functionIdentifier: string = 'marker'; | ||||
|  | ||||
| 	public constructor(options?: any) { | ||||
| 		super(); | ||||
| 		if (options && typeof options.identifier !== 'undefined') { | ||||
| 			this.functionIdentifier = options.identifier; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	public extract(template: string, path: string): TranslationCollection { | ||||
| 		let collection: TranslationCollection = new TranslationCollection(); | ||||
|  | ||||
| 		this.sourceFile = this.createSourceFile(path, template); | ||||
|  | ||||
| 		const callNodes = this.findCallNodes(); | ||||
| 		callNodes.forEach(callNode => { | ||||
| 			const keys: string[] = this.getStringLiterals(callNode); | ||||
| 			if (keys && keys.length) { | ||||
| 				collection = collection.addKeys(keys); | ||||
| 			} | ||||
| 		}); | ||||
| 		return collection; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Find all calls to marker function | ||||
| 	 */ | ||||
| 	protected findCallNodes(node?: Node): CallExpression[] { | ||||
| 		if (!node) { | ||||
| 			node = this.sourceFile; | ||||
| 		} | ||||
|  | ||||
| 		let callNodes = this.findNodes(node, [SyntaxKind.CallExpression]) as CallExpression[]; | ||||
| 		callNodes = callNodes | ||||
| 			.filter(callNode => { | ||||
| 				// Only call expressions with arguments | ||||
| 				if (callNode.arguments.length < 1) { | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				const identifier = (callNode.getChildAt(0) as Identifier).text; | ||||
| 				if (identifier !== this.functionIdentifier) { | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				return true; | ||||
| 			}); | ||||
|  | ||||
| 		return callNodes; | ||||
| 	} | ||||
|  | ||||
| } | ||||
							
								
								
									
										34
									
								
								src/parsers/marker.parser.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								src/parsers/marker.parser.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,34 @@ | ||||
| import { tsquery } from '@phenomnomnominal/tsquery'; | ||||
|  | ||||
| import { ParserInterface } from './parser.interface'; | ||||
| import { TranslationCollection } from '../utils/translation.collection'; | ||||
| import { getNamedImportAlias, findFunctionCallExpressions, getStringsFromExpression } from '../utils/ast-helpers'; | ||||
|  | ||||
| const MARKER_PACKAGE_MODULE_NAME = '@biesbjerg/ngx-translate-extract-marker'; | ||||
| const MARKER_PACKAGE_IMPORT_NAME = 'marker'; | ||||
|  | ||||
| export class MarkerParser implements ParserInterface { | ||||
|  | ||||
| 	public extract(contents: string, filePath: string): TranslationCollection { | ||||
| 		const sourceFile = tsquery.ast(contents, filePath); | ||||
|  | ||||
| 		const markerFnName = getNamedImportAlias(sourceFile, MARKER_PACKAGE_MODULE_NAME, MARKER_PACKAGE_IMPORT_NAME); | ||||
| 		if (!markerFnName) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 		let collection: TranslationCollection = new TranslationCollection(); | ||||
|  | ||||
| 		const callNodes = findFunctionCallExpressions(sourceFile, markerFnName); | ||||
| 		callNodes.forEach(callNode => { | ||||
| 			const [firstArgNode] = callNode.arguments; | ||||
| 			if (!firstArgNode) { | ||||
| 				return; | ||||
| 			} | ||||
| 			const strings = getStringsFromExpression(firstArgNode); | ||||
| 			collection = collection.addKeys(strings); | ||||
| 		}); | ||||
| 		return collection; | ||||
| 	} | ||||
|  | ||||
| } | ||||
| @@ -1,142 +1,41 @@ | ||||
| import { | ||||
| 	SourceFile, | ||||
| 	Node, | ||||
| 	ConstructorDeclaration, | ||||
| 	Identifier, | ||||
| 	TypeReferenceNode, | ||||
| 	ClassDeclaration, | ||||
| 	SyntaxKind, | ||||
| 	CallExpression, | ||||
| 	PropertyAccessExpression, | ||||
| 	isPropertyAccessExpression | ||||
| } from 'typescript'; | ||||
| import { tsquery } from '@phenomnomnominal/tsquery'; | ||||
|  | ||||
| import { ParserInterface } from './parser.interface'; | ||||
| import { AbstractAstParser } from './abstract-ast.parser'; | ||||
| import { TranslationCollection } from '../utils/translation.collection'; | ||||
| import { findClasses, findClassPropertyByType, findMethodCallExpression, getStringsFromExpression } from '../utils/ast-helpers'; | ||||
|  | ||||
| export class ServiceParser extends AbstractAstParser implements ParserInterface { | ||||
| const TRANSLATE_SERVICE_TYPE_REFERENCE = 'TranslateService'; | ||||
| const TRANSLATE_SERVICE_METHOD_NAMES = ['get', 'instant', 'stream']; | ||||
|  | ||||
| 	protected sourceFile: SourceFile; | ||||
| export class ServiceParser implements ParserInterface { | ||||
|  | ||||
| 	public extract(source: string, filePath: string): TranslationCollection { | ||||
| 		const sourceFile = tsquery.ast(source, filePath); | ||||
|  | ||||
| 		const classNodes = findClasses(sourceFile); | ||||
| 		if (!classNodes) { | ||||
| 			return; | ||||
| 		} | ||||
|  | ||||
| 	public extract(template: string, path: string): TranslationCollection { | ||||
| 		let collection: TranslationCollection = new TranslationCollection(); | ||||
|  | ||||
| 		this.sourceFile = this.createSourceFile(path, template); | ||||
| 		const classNodes = this.findClassNodes(this.sourceFile); | ||||
| 		classNodes.forEach(classNode => { | ||||
| 			const constructorNode = this.findConstructorNode(classNode); | ||||
| 			if (!constructorNode) { | ||||
| 			const propName: string = findClassPropertyByType(classNode, TRANSLATE_SERVICE_TYPE_REFERENCE); | ||||
| 			if (!propName) { | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			const propertyName: string = this.findTranslateServicePropertyName(constructorNode); | ||||
| 			if (!propertyName) { | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			const callNodes = this.findCallNodes(classNode, propertyName); | ||||
| 			const callNodes = findMethodCallExpression(classNode, propName, TRANSLATE_SERVICE_METHOD_NAMES); | ||||
| 			callNodes.forEach(callNode => { | ||||
| 				const keys: string[] = this.getStringLiterals(callNode); | ||||
| 				if (keys && keys.length) { | ||||
| 					collection = collection.addKeys(keys); | ||||
| 				const [firstArgNode] = callNode.arguments; | ||||
| 				if (!firstArgNode) { | ||||
| 					return; | ||||
| 				} | ||||
| 				const strings = getStringsFromExpression(firstArgNode); | ||||
| 				collection = collection.addKeys(strings); | ||||
| 			}); | ||||
| 		}); | ||||
|  | ||||
| 		return collection; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Detect what the TranslateService instance property | ||||
| 	 * is called by inspecting constructor arguments | ||||
| 	 */ | ||||
| 	protected findTranslateServicePropertyName(constructorNode: ConstructorDeclaration): string { | ||||
| 		if (!constructorNode) { | ||||
| 			return null; | ||||
| 		} | ||||
|  | ||||
| 		const result = constructorNode.parameters.find(parameter => { | ||||
| 			// Skip if visibility modifier is not present (we want it set as an instance property) | ||||
| 			if (!parameter.modifiers) { | ||||
| 				return false; | ||||
| 			} | ||||
|  | ||||
| 			// Parameter has no type | ||||
| 			if (!parameter.type) { | ||||
| 				return false; | ||||
| 			} | ||||
|  | ||||
| 			// Make sure className is of the correct type | ||||
| 			const parameterType: Identifier = (parameter.type as TypeReferenceNode).typeName as Identifier; | ||||
| 			if (!parameterType) { | ||||
| 				return false; | ||||
| 			} | ||||
| 			const className: string = parameterType.text; | ||||
| 			if (className !== 'TranslateService') { | ||||
| 				return false; | ||||
| 			} | ||||
|  | ||||
| 			return true; | ||||
| 		}); | ||||
|  | ||||
| 		if (result) { | ||||
| 			return (result.name as Identifier).text; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Find class nodes | ||||
| 	 */ | ||||
| 	protected findClassNodes(node: Node): ClassDeclaration[] { | ||||
| 		return this.findNodes(node, [SyntaxKind.ClassDeclaration]) as ClassDeclaration[]; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Find constructor | ||||
| 	 */ | ||||
| 	protected findConstructorNode(node: ClassDeclaration): ConstructorDeclaration { | ||||
| 		const constructorNodes = this.findNodes(node, [SyntaxKind.Constructor]) as ConstructorDeclaration[]; | ||||
| 		if (constructorNodes) { | ||||
| 			return constructorNodes[0]; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	 * Find all calls to TranslateService methods | ||||
| 	 */ | ||||
| 	protected findCallNodes(node: Node, propertyIdentifier: string): CallExpression[] { | ||||
| 		let callNodes = this.findNodes(node, [SyntaxKind.CallExpression]) as CallExpression[]; | ||||
| 		callNodes = callNodes | ||||
| 			.filter(callNode => { | ||||
| 				// Only call expressions with arguments | ||||
| 				if (callNode.arguments.length < 1) { | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				const propAccess = callNode.getChildAt(0).getChildAt(0) as PropertyAccessExpression; | ||||
| 				if (!propAccess || !isPropertyAccessExpression(propAccess)) { | ||||
| 					return false; | ||||
| 				} | ||||
| 				if (!propAccess.getFirstToken() || propAccess.getFirstToken().kind !== SyntaxKind.ThisKeyword) { | ||||
| 					return false; | ||||
| 				} | ||||
| 				if (propAccess.name.text !== propertyIdentifier) { | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				const methodAccess = callNode.getChildAt(0) as PropertyAccessExpression; | ||||
| 				if (!methodAccess || methodAccess.kind !== SyntaxKind.PropertyAccessExpression) { | ||||
| 					return false; | ||||
| 				} | ||||
| 				if (!methodAccess.name || (methodAccess.name.text !== 'get' && methodAccess.name.text !== 'instant' && methodAccess.name.text !== 'stream')) { | ||||
| 					return false; | ||||
| 				} | ||||
|  | ||||
| 				return true; | ||||
| 			}); | ||||
|  | ||||
| 		return callNodes; | ||||
| 	} | ||||
|  | ||||
| } | ||||
|   | ||||
							
								
								
									
										135
									
								
								src/utils/ast-helpers.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										135
									
								
								src/utils/ast-helpers.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,135 @@ | ||||
| import { tsquery } from '@phenomnomnominal/tsquery'; | ||||
| import { | ||||
| 	Node, | ||||
| 	NamedImports, | ||||
| 	Identifier, | ||||
| 	ClassDeclaration, | ||||
| 	CallExpression, | ||||
| 	isStringLiteralLike, | ||||
| 	isArrayLiteralExpression, | ||||
| 	Expression, | ||||
| 	isBinaryExpression, | ||||
| 	SyntaxKind, | ||||
| 	isConditionalExpression, | ||||
| 	PropertyAccessExpression | ||||
| } from 'typescript'; | ||||
|  | ||||
| export function getNamedImports(node: Node, moduleName: string): NamedImports[] { | ||||
| 	const query = `ImportDeclaration[moduleSpecifier.text="${moduleName}"] NamedImports`; | ||||
| 	return tsquery<NamedImports>(node, query); | ||||
| } | ||||
|  | ||||
| export function getNamedImportAlias(node: Node, moduleName: string, importName: string): string | null { | ||||
| 	const [namedImportNode] = getNamedImports(node, moduleName); | ||||
| 	if (!namedImportNode) { | ||||
| 		return; | ||||
| 	} | ||||
|  | ||||
| 	const query = `ImportSpecifier:has(Identifier[name="${importName}"]) > Identifier`; | ||||
| 	const identifiers = tsquery<Identifier>(namedImportNode, query); | ||||
| 	if (identifiers.length === 1) { | ||||
| 		return identifiers[0].text; | ||||
| 	} | ||||
| 	if (identifiers.length > 1) { | ||||
| 		return identifiers[identifiers.length - 1].text; | ||||
| 	} | ||||
| 	return null; | ||||
| } | ||||
|  | ||||
| export function findClasses(node: Node): ClassDeclaration[] { | ||||
| 	const query = 'ClassDeclaration'; | ||||
| 	return tsquery<ClassDeclaration>(node, query); | ||||
| } | ||||
|  | ||||
| export function findClassPropertyByType(node: ClassDeclaration, type: string): string | null { | ||||
| 	return findClassPropertyConstructorParameterByType(node, type) || findClassPropertyDeclarationByType(node, type); | ||||
| } | ||||
|  | ||||
| export function findClassPropertyConstructorParameterByType(node: ClassDeclaration, type: string): string | null { | ||||
| 	const query = `Constructor Parameter:has(TypeReference > Identifier[name="${type}"]):has(PublicKeyword,ProtectedKeyword,PrivateKeyword) > Identifier`; | ||||
| 	const [result] = tsquery<Identifier>(node, query); | ||||
| 	if (result) { | ||||
| 		return result.text; | ||||
| 	} | ||||
| 	return null; | ||||
| } | ||||
|  | ||||
| export function findClassPropertyDeclarationByType(node: ClassDeclaration, type: string): string | null { | ||||
| 	const query = `PropertyDeclaration:has(TypeReference > Identifier[name="${type}"]) > Identifier`; | ||||
| 	const [result] = tsquery<Identifier>(node, query); | ||||
| 	if (result) { | ||||
| 		return result.text; | ||||
| 	} | ||||
| 	return null; | ||||
| } | ||||
|  | ||||
| export function findFunctionCallExpressions(node: Node, fnName: string | string[]): CallExpression[] { | ||||
| 	if (Array.isArray(fnName)) { | ||||
| 		fnName = fnName.join('|'); | ||||
| 	} | ||||
| 	const query = `CallExpression:has(Identifier[name="${fnName}"]):not(:has(PropertyAccessExpression))`; | ||||
| 	const nodes = tsquery<CallExpression>(node, query); | ||||
| 	return nodes; | ||||
| } | ||||
|  | ||||
| export function findMethodCallExpression(node: Node, prop: string, fnName: string | string[]): CallExpression[] { | ||||
| 	if (Array.isArray(fnName)) { | ||||
| 		fnName = fnName.join('|'); | ||||
| 	} | ||||
| 	const query = `CallExpression > PropertyAccessExpression:has(Identifier[name=/^(${fnName})$/]):has(PropertyAccessExpression:has(Identifier[name="${prop}"]):has(ThisKeyword))`; | ||||
| 	let nodes = tsquery<PropertyAccessExpression>(node, query).map(node => node.parent as CallExpression); | ||||
| 	return nodes; | ||||
| } | ||||
|  | ||||
| export function getStringsFromExpression(expression: Expression): string[] { | ||||
| 	if (isStringLiteralLike(expression)) { | ||||
| 		return [expression.text]; | ||||
| 	} | ||||
|  | ||||
| 	if (isArrayLiteralExpression(expression)) { | ||||
| 		return expression.elements.reduce((result: string[], element: Expression) => { | ||||
| 			const strings = this.getStringsFromExpression(element); | ||||
| 			return [ | ||||
| 				...result, | ||||
| 				...strings | ||||
| 			]; | ||||
| 		}, []); | ||||
| 	} | ||||
|  | ||||
| 	if (isBinaryExpression(expression)) { | ||||
| 		const [left] = this.getStringsFromExpression(expression.left); | ||||
| 		const [right] = this.getStringsFromExpression(expression.right); | ||||
|  | ||||
| 		if (expression.operatorToken.kind === SyntaxKind.PlusToken) { | ||||
| 			if (typeof left === 'string' && typeof right === 'string') { | ||||
| 				return [left + right]; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (expression.operatorToken.kind === SyntaxKind.BarBarToken) { | ||||
| 			const result = []; | ||||
| 			if (typeof left === 'string') { | ||||
| 				result.push(left); | ||||
| 			} | ||||
| 			if (typeof right === 'string') { | ||||
| 				result.push(right); | ||||
| 			} | ||||
| 			return result; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if (isConditionalExpression(expression)) { | ||||
| 		const [whenTrue] = this.getStringsFromExpression(expression.whenTrue); | ||||
| 		const [whenFalse] = this.getStringsFromExpression(expression.whenFalse); | ||||
|  | ||||
| 		const result = []; | ||||
| 		if (typeof whenTrue === 'string') { | ||||
| 			result.push(whenTrue); | ||||
| 		} | ||||
| 		if (typeof whenFalse === 'string') { | ||||
| 			result.push(whenFalse); | ||||
| 		} | ||||
| 		return result; | ||||
| 	} | ||||
| 	return []; | ||||
| } | ||||
| @@ -1,15 +1,15 @@ | ||||
| import { expect } from 'chai'; | ||||
| 
 | ||||
| import { FunctionParser } from '../../src/parsers/function.parser'; | ||||
| import { MarkerParser } from '../../src/parsers/marker.parser'; | ||||
| 
 | ||||
| describe('FunctionParser', () => { | ||||
| describe('MarkerParser', () => { | ||||
| 
 | ||||
| 	const componentFilename: string = 'test.component.ts'; | ||||
| 
 | ||||
| 	let parser: FunctionParser; | ||||
| 	let parser: MarkerParser; | ||||
| 
 | ||||
| 	beforeEach(() => { | ||||
| 		parser = new FunctionParser(); | ||||
| 		parser = new MarkerParser(); | ||||
| 	}); | ||||
| 
 | ||||
| 
 | ||||
| @@ -224,6 +224,7 @@ describe('ServiceParser', () => { | ||||
| 						console.log(translations[variable]); | ||||
| 					}); | ||||
| 				} | ||||
| 			} | ||||
| 		`; | ||||
| 		const keys = parser.extract(contents, componentFilename).keys(); | ||||
| 		expect(keys).to.deep.equal(['yes']); | ||||
| @@ -263,4 +264,39 @@ describe('ServiceParser', () => { | ||||
| 		expect(keys).to.deep.equal(['Extract me!', 'Hello!']); | ||||
| 	}); | ||||
|  | ||||
| 	it('should extract strings when TranslateService is declared as a property', () => { | ||||
| 		const contents = ` | ||||
| 			export class MyComponent { | ||||
| 				protected translateService: TranslateService; | ||||
| 				public constructor() { | ||||
| 					this.translateService = new TranslateService(); | ||||
| 				} | ||||
| 				public test() { | ||||
| 					this.translateService.instant('Hello World'); | ||||
| 				} | ||||
| 		`; | ||||
| 		const keys = parser.extract(contents, componentFilename).keys(); | ||||
| 		expect(keys).to.deep.equal(['Hello World']); | ||||
| 	}); | ||||
|  | ||||
| 	it('should extract strings passed to TranslateServices methods only', () => { | ||||
| 		const contents = ` | ||||
| 			export class AppComponent implements OnInit { | ||||
| 				constructor(protected config: Config, protected translateService: TranslateService) {} | ||||
|  | ||||
| 				public ngOnInit(): void { | ||||
| 					this.localizeBackButton(); | ||||
| 				} | ||||
|  | ||||
| 				protected localizeBackButton(): void { | ||||
| 					this.translateService.onLangChange.subscribe((event: LangChangeEvent) => { | ||||
| 						this.config.set('backButtonText', this.translateService.instant('Back')); | ||||
| 					}); | ||||
| 				} | ||||
| 			} | ||||
| 		`; | ||||
| 		const keys = parser.extract(contents, componentFilename).keys(); | ||||
| 		expect(keys).to.deep.equal(['Back']); | ||||
| 	}); | ||||
|  | ||||
| }); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user