Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
97e8937709 | ||
|
eb7f3f603e | ||
|
ab2b78eaec | ||
|
75ee2bdfda | ||
|
4fe3c43624 | ||
|
41bd679fcd | ||
|
24ebd8f428 | ||
|
e1bb5bfd02 | ||
|
1323c2e6a1 | ||
|
0f465014df | ||
|
5d5b07ba2c | ||
|
7eefd6c8d3 | ||
|
50fd3ae9e2 | ||
|
a5b8f6e6c6 |
12
README.md
12
README.md
@@ -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: " "]
|
||||
@@ -105,5 +103,9 @@ Options:
|
||||
[boolean] [default: false]
|
||||
--clean, -c Remove obsolete strings when merging
|
||||
[boolean] [default: false]
|
||||
--key-as-default-value, -k Use key as default value for translations
|
||||
[boolean] [default: false]
|
||||
--key-as-default-value, -k Use key as default value for translations
|
||||
[boolean] [default: false]
|
||||
--null-as-default-value, -n Use null as default value for translations
|
||||
[boolean] [default: false]
|
||||
|
||||
Arguments key-as-default-value and null-as-default-value are mutually exclusive
|
||||
|
202
package-lock.json
generated
202
package-lock.json
generated
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"name": "@biesbjerg/ngx-translate-extract",
|
||||
"version": "2.3.4",
|
||||
"version": "4.0.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@angular/compiler": {
|
||||
"version": "8.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@angular/compiler/-/compiler-8.1.3.tgz",
|
||||
"integrity": "sha512-mKeRkpPy/iBPGBCVQIPF9x4f1S68ilEYaQTTfHoLR0OfivEQsyGuf2GEegwbTosEBX3JF+0JHfCNvsAE1zI5Og==",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"@babel/code-frame": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz",
|
||||
"integrity": "sha512-OfC2uemaknXr87bdLUkWog7nYuliM9Ij5HUcajsVcMCpQrcLmtxRbVFTIqmcSkSeYRBFBRxs2FiUqFJDLdiebA==",
|
||||
"version": "7.5.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
|
||||
"integrity": "sha512-27d4lZoomVyo51VegxI20xZPuSHusqbQag/ztrBC7wegWoQ1nLREPVSKSW8byhTlzTKyNE4ifaTA6lCp7JjpFw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/highlight": "^7.0.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.1.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.1.7.tgz",
|
||||
"integrity": "sha512-2Y8uPt0/jwjhQ6EiluT0XCri1Dbplr0ZxfFXUz+ye13gaqE8u5gL5ppao1JrUYr9cIip5S6MvQzBS7Kke7U9VA==",
|
||||
"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,15 +91,15 @@
|
||||
"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": {
|
||||
"version": "13.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.1.tgz",
|
||||
"integrity": "sha512-7X+FaCpRQ1scFQnWUY0TnVuuNaP5ajN45IGmwHVkAbsH1g4txz4hNb6sjJeR/wLaqUdNoInOwLk7oRL5M4zVFA==",
|
||||
"version": "13.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-13.0.2.tgz",
|
||||
"integrity": "sha512-lwwgizwk/bIIU+3ELORkyuOgDjCh7zuWDFqRtPPhhVgq9N1F7CvLNKg1TX4f2duwtKQ0p044Au9r1PLIXHrIzQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/yargs-parser": "*"
|
||||
@@ -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,10 +493,23 @@
|
||||
"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.2",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
|
||||
"integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=",
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
|
||||
"integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
|
||||
"dev": true
|
||||
},
|
||||
"execa": {
|
||||
@@ -512,9 +536,8 @@
|
||||
}
|
||||
},
|
||||
"flat": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
|
||||
"integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
|
||||
"version": "github:lenchvolodymyr/flat#ffe77efe8c33bc80ffb2f7a465537610dea4f611",
|
||||
"from": "github:lenchvolodymyr/flat#ffe77ef",
|
||||
"requires": {
|
||||
"is-buffer": "~2.0.3"
|
||||
}
|
||||
@@ -551,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",
|
||||
@@ -1091,9 +1114,9 @@
|
||||
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg=="
|
||||
},
|
||||
"resolve": {
|
||||
"version": "1.11.1",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
|
||||
"integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==",
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.12.0.tgz",
|
||||
"integrity": "sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"path-parse": "^1.0.6"
|
||||
@@ -1148,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",
|
||||
@@ -1174,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": {
|
||||
@@ -1218,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"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -1239,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",
|
||||
@@ -1269,21 +1293,21 @@
|
||||
}
|
||||
},
|
||||
"tslib": {
|
||||
"version": "1.9.3",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
|
||||
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
|
||||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
|
||||
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
|
||||
},
|
||||
"tslint": {
|
||||
"version": "5.18.0",
|
||||
"resolved": "https://registry.npmjs.org/tslint/-/tslint-5.18.0.tgz",
|
||||
"integrity": "sha512-Q3kXkuDEijQ37nXZZLKErssQVnwCV/+23gFEMROi8IlbaBG6tXqLPQJ5Wjcyt/yHPKBC+hD5SzuGaMora+ZS6w==",
|
||||
"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",
|
||||
@@ -1292,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": {
|
||||
@@ -1343,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",
|
||||
@@ -1491,11 +1523,12 @@
|
||||
"integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w=="
|
||||
},
|
||||
"yargs": {
|
||||
"version": "13.3.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-13.3.0.tgz",
|
||||
"integrity": "sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA==",
|
||||
"version": "14.0.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-14.0.0.tgz",
|
||||
"integrity": "sha512-ssa5JuRjMeZEUjg7bEL99AwpitxU/zWGAGpdj0di41pOEmJti8NR6kyUIJBkR78DTYNPZOU08luUo0GTHuB+ow==",
|
||||
"requires": {
|
||||
"cliui": "^5.0.0",
|
||||
"decamelize": "^1.2.0",
|
||||
"find-up": "^3.0.0",
|
||||
"get-caller-file": "^2.0.1",
|
||||
"require-directory": "^2.1.1",
|
||||
@@ -1582,6 +1615,15 @@
|
||||
"yargs": "^12.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"flat": {
|
||||
"version": "4.1.0",
|
||||
"resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz",
|
||||
"integrity": "sha512-Px/TiLIznH7gEDlPXcUD4KnBusa6kR6ayRUVcnEAbreRIuhkqow/mun59BuRXwoYk7ZQOLW1ZM05ilIvK38hFw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-buffer": "~2.0.3"
|
||||
}
|
||||
},
|
||||
"get-caller-file": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz",
|
||||
@@ -1627,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
|
||||
}
|
||||
}
|
||||
|
29
package.json
29
package.json
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@biesbjerg/ngx-translate-extract",
|
||||
"version": "3.0.0",
|
||||
"version": "4.1.0",
|
||||
"description": "Extract strings from projects using ngx-translate",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
@@ -40,33 +40,38 @@
|
||||
},
|
||||
"homepage": "https://github.com/biesbjerg/ngx-translate-extract",
|
||||
"engines": {
|
||||
"node": ">=4.3.2"
|
||||
"node": ">=8"
|
||||
},
|
||||
"config": {},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.7",
|
||||
"@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/yargs": "^13.0.1",
|
||||
"@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.18.0",
|
||||
"ts-node": "^8.4.1",
|
||||
"tslint": "^5.20.0",
|
||||
"tslint-eslint-rules": "^5.4.0"
|
||||
},
|
||||
"bundledDependencies": [
|
||||
"flat"
|
||||
],
|
||||
"dependencies": {
|
||||
"@angular/compiler": "^8.1.3",
|
||||
"@angular/compiler": "^8.2.6",
|
||||
"@phenomnomnominal/tsquery": "^3.0.0",
|
||||
"boxen": "^4.1.0",
|
||||
"colorette": "^1.1.0",
|
||||
"flat": "^4.1.0",
|
||||
"gettext-parser": "^4.0.1",
|
||||
"flat": "github:lenchvolodymyr/flat#ffe77ef",
|
||||
"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",
|
||||
"yargs": "^13.3.0"
|
||||
"terminal-link": "^2.0.0",
|
||||
"typescript": "^3.6.3",
|
||||
"yargs": "^14.0.0"
|
||||
}
|
||||
}
|
||||
|
@@ -6,10 +6,11 @@ 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';
|
||||
import { NullAsDefaultValuePostProcessor } from '../post-processors/null-as-default-value.post-processor';
|
||||
import { PurgeObsoleteKeysPostProcessor } from '../post-processors/purge-obsolete-keys.post-processor';
|
||||
import { CompilerInterface } from '../compilers/compiler.interface';
|
||||
import { CompilerFactory } from '../compilers/compiler.factory';
|
||||
@@ -29,7 +30,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 +51,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',
|
||||
@@ -93,10 +88,17 @@ export const cli = yargs
|
||||
default: false,
|
||||
type: 'boolean'
|
||||
})
|
||||
.option('null-as-default-value', {
|
||||
alias: 'n',
|
||||
describe: 'Use null as default value for translations',
|
||||
default: false,
|
||||
type: 'boolean'
|
||||
})
|
||||
.conflicts('key-as-default-value', 'null-as-default-value')
|
||||
.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 +107,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
|
||||
@@ -121,6 +119,8 @@ if (cli.clean) {
|
||||
}
|
||||
if (cli.keyAsDefaultValue) {
|
||||
postProcessors.push(new KeyAsDefaultValuePostProcessor());
|
||||
} else if (cli.nullAsDefaultValue) {
|
||||
postProcessors.push(new NullAsDefaultValuePostProcessor());
|
||||
}
|
||||
if (cli.sort) {
|
||||
postProcessors.push(new SortByKeyPostProcessor());
|
||||
|
@@ -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,43 +0,0 @@
|
||||
import {
|
||||
createSourceFile,
|
||||
SourceFile,
|
||||
CallExpression,
|
||||
Node,
|
||||
SyntaxKind,
|
||||
StringLiteral
|
||||
} 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)
|
||||
.map((node: StringLiteral) => node.text);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all child nodes of a kind
|
||||
*/
|
||||
protected findNodes(node: Node, kind: SyntaxKind): Node[] {
|
||||
const childrenNodes: Node[] = node.getChildren(this.sourceFile);
|
||||
const initialValue: Node[] = node.kind === kind ? [node] : [];
|
||||
|
||||
return childrenNodes.reduce((result: Node[], childNode: Node) => {
|
||||
return result.concat(this.findNodes(childNode, kind));
|
||||
}, 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 | null {
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@@ -2,6 +2,6 @@ import { TranslationCollection } from '../utils/translation.collection';
|
||||
|
||||
export interface ParserInterface {
|
||||
|
||||
extract(template: string, path: string): TranslationCollection;
|
||||
extract(source: string, filePath: string): TranslationCollection | null;
|
||||
|
||||
}
|
||||
|
@@ -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, findMethodCallExpressions, 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 | null {
|
||||
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 = findMethodCallExpressions(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;
|
||||
}
|
||||
|
||||
}
|
||||
|
12
src/post-processors/null-as-default-value.post-processor.ts
Normal file
12
src/post-processors/null-as-default-value.post-processor.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { TranslationCollection } from '../utils/translation.collection';
|
||||
import { PostProcessorInterface } from './post-processor.interface';
|
||||
|
||||
export class NullAsDefaultValuePostProcessor implements PostProcessorInterface {
|
||||
|
||||
public name: string = 'NullAsDefaultValue';
|
||||
|
||||
public process(draft: TranslationCollection, extracted: TranslationCollection, existing: TranslationCollection): TranslationCollection {
|
||||
return draft.map((key, val) => existing.get(key) === undefined ? null : val);
|
||||
}
|
||||
|
||||
}
|
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 findMethodCallExpressions(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 [];
|
||||
}
|
@@ -3,17 +3,15 @@ import * as boxen from 'boxen';
|
||||
import * as terminalLink from 'terminal-link';
|
||||
|
||||
const url = 'https://donate.biesbjerg.com';
|
||||
const text = `
|
||||
If this tool saves you time, please consider making a
|
||||
donation towards the continued maintainence and development:
|
||||
const link = terminalLink(url, url);
|
||||
const message = `
|
||||
If this tool saves you or your company time, please consider making a
|
||||
donation to support my work and the continued maintainence and development:
|
||||
|
||||
${yellow(terminalLink(url, url))}
|
||||
`;
|
||||
${yellow(link)}`;
|
||||
|
||||
export const donateMessage = boxen(text.trim(), {
|
||||
export const donateMessage = boxen(message.trim(), {
|
||||
padding: 1,
|
||||
margin: 0,
|
||||
borderColor: 'yellow',
|
||||
backgroundColor: 'black',
|
||||
dimBorder: true
|
||||
});
|
||||
|
@@ -57,4 +57,13 @@ describe('NamespacedJsonCompiler', () => {
|
||||
expect(result).to.equal('{\n "NAMESPACE": {\n "KEY": {\n "FIRST_KEY": "",\n "SECOND_KEY": "VALUE"\n }\n }\n}');
|
||||
});
|
||||
|
||||
it('should not reorder keys when compiled', () => {
|
||||
const collection = new TranslationCollection({
|
||||
'BROWSE': '',
|
||||
'LOGIN': ''
|
||||
});
|
||||
const result: string = compiler.compile(collection);
|
||||
expect(result).to.equal('{\n\t"BROWSE": "",\n\t"LOGIN": ""\n}');
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
@@ -27,4 +27,19 @@ describe('FunctionParser', () => {
|
||||
expect(keys).to.deep.equal(['Hello world', 'I', 'am', 'extracted', 'binary expression', 'conditional operator', 'FOO.bar']);
|
||||
});
|
||||
|
||||
it('should extract split strings', () => {
|
||||
const contents = `
|
||||
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
|
||||
_('Hello ' + 'world');
|
||||
_('This is a ' + 'very ' + 'very ' + 'very ' + 'very ' + 'long line.');
|
||||
_('Mix ' + \`of \` + 'different ' + \`types\`);
|
||||
`;
|
||||
const keys = parser.extract(contents, componentFilename).keys();
|
||||
expect(keys).to.deep.equal([
|
||||
'Hello world',
|
||||
'This is a very very very very long line.',
|
||||
'Mix of different types'
|
||||
]);
|
||||
});
|
||||
|
||||
});
|
@@ -122,6 +122,19 @@ describe('ServiceParser', () => {
|
||||
expect(key).to.deep.equal(['Hello', 'World']);
|
||||
});
|
||||
|
||||
it('should extract string arrays encapsulated in backticks', () => {
|
||||
const contents = `
|
||||
@Component({ })
|
||||
export class AppComponent {
|
||||
public constructor(protected _translateService: TranslateService) { }
|
||||
public test() {
|
||||
this._translateService.get([\`Hello\`, \`World\`]);
|
||||
}
|
||||
`;
|
||||
const keys = parser.extract(contents, componentFilename).keys();
|
||||
expect(keys).to.deep.equal(['Hello', 'World']);
|
||||
});
|
||||
|
||||
it('should not extract strings in get()/instant()/stream() methods of other services', () => {
|
||||
const contents = `
|
||||
@Component({ })
|
||||
@@ -211,6 +224,7 @@ describe('ServiceParser', () => {
|
||||
console.log(translations[variable]);
|
||||
});
|
||||
}
|
||||
}
|
||||
`;
|
||||
const keys = parser.extract(contents, componentFilename).keys();
|
||||
expect(keys).to.deep.equal(['yes']);
|
||||
@@ -250,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']);
|
||||
});
|
||||
|
||||
});
|
||||
|
@@ -0,0 +1,42 @@
|
||||
import { expect } from 'chai';
|
||||
|
||||
import { PostProcessorInterface } from '../../src/post-processors/post-processor.interface';
|
||||
import { NullAsDefaultValuePostProcessor } from '../../src/post-processors/null-as-default-value.post-processor';
|
||||
import { TranslationCollection } from '../../src/utils/translation.collection';
|
||||
|
||||
describe('NullAsDefaultValuePostProcessor', () => {
|
||||
|
||||
let processor: PostProcessorInterface;
|
||||
|
||||
beforeEach(() => {
|
||||
processor = new NullAsDefaultValuePostProcessor();
|
||||
});
|
||||
|
||||
it('should use null as default value', () => {
|
||||
const draft = new TranslationCollection({ 'String A': '' });
|
||||
const extracted = new TranslationCollection({ 'String A': '' });
|
||||
const existing = new TranslationCollection();
|
||||
expect(processor.process(draft, extracted, existing).values).to.deep.equal({
|
||||
'String A': null
|
||||
});
|
||||
});
|
||||
|
||||
it('should keep existing value even if it is an empty string', () => {
|
||||
const draft = new TranslationCollection({ 'String A': '' });
|
||||
const extracted = new TranslationCollection({ 'String A': '' });
|
||||
const existing = new TranslationCollection({ 'String A': '' });
|
||||
expect(processor.process(draft, extracted, existing).values).to.deep.equal({
|
||||
'String A': ''
|
||||
});
|
||||
});
|
||||
|
||||
it('should keep existing value', () => {
|
||||
const draft = new TranslationCollection({ 'String A': 'Streng A' });
|
||||
const extracted = new TranslationCollection({ 'String A': 'Streng A' });
|
||||
const existing = new TranslationCollection({ 'String A': 'Streng A' });
|
||||
expect(processor.process(draft, extracted, existing).values).to.deep.equal({
|
||||
'String A': 'Streng A'
|
||||
});
|
||||
});
|
||||
|
||||
});
|
@@ -5,10 +5,10 @@
|
||||
"noImplicitAny": true,
|
||||
"removeComments": true,
|
||||
"declaration": true,
|
||||
"target": "es6",
|
||||
"target": "es2015",
|
||||
"lib": [
|
||||
"dom",
|
||||
"es2015"
|
||||
"es2018"
|
||||
],
|
||||
"module": "commonjs",
|
||||
"outDir": "./dist/",
|
||||
|
Reference in New Issue
Block a user