From 05dc657e9bdc4962acf99390ca8df5d7dff6304c Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 3 Feb 2019 16:19:32 +0100 Subject: [PATCH 01/11] Initial no-yargs implementation --- package-lock.json | 358 +------------------ package.json | 3 +- src/CommandLineParser.ts | 222 +++++++----- src/Compiler.ts | 7 +- test/unit/compiler/commandLineParser.spec.ts | 66 ++++ 5 files changed, 199 insertions(+), 457 deletions(-) create mode 100644 test/unit/compiler/commandLineParser.spec.ts diff --git a/package-lock.json b/package-lock.json index 1fdc3b499..4021109cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,7 +67,8 @@ "ansi-regex": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true }, "ansi-styles": { "version": "2.2.1", @@ -175,11 +176,6 @@ "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", "dev": true }, - "camelcase": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-4.1.0.tgz", - "integrity": "sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=" - }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -205,42 +201,12 @@ "integrity": "sha512-13YaR6kiz0kBNmIVM87Io8Hp7bWOo4r61vkEANy8iH9R9bc6avud/1FT0SBpqR1RpIQADOh/Q+yHZDA1iL6ysA==", "dev": true }, - "cliui": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", - "integrity": "sha512-4FG+RSG9DL7uEwRUZXZn3SS34DiDPfzP0VOiEwtUWlE+AR2EIg+hSyvrIgUUfhdgR/UkAeW2QHgeP+hWrXs7jQ==", - "requires": { - "string-width": "^2.1.1", - "strip-ansi": "^4.0.0", - "wrap-ansi": "^2.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", "dev": true }, - "code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" - }, "codecov": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/codecov/-/codecov-3.0.2.tgz", @@ -294,16 +260,6 @@ "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true }, - "cross-spawn": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz", - "integrity": "sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk=", - "requires": { - "lru-cache": "^4.0.1", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, "dashdash": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", @@ -313,14 +269,6 @@ "assert-plus": "^1.0.0" } }, - "decamelize": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-2.0.0.tgz", - "integrity": "sha512-Ikpp5scV3MSYxY39ymh45ZLEecsTdv/Xj2CaQfI8RLMuwi7XvjX9H/fhraiSuU+C5w5NTDu4ZU72xNiZnurBPg==", - "requires": { - "xregexp": "4.0.0" - } - }, "deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", @@ -385,20 +333,6 @@ "integrity": "sha1-LUH1Y+H+QA7Uli/hpNXGp1Od9/Y=", "dev": true }, - "execa": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-0.7.0.tgz", - "integrity": "sha1-lEvs00zEHuMqY6n68nrVpl/Fl3c=", - "requires": { - "cross-spawn": "^5.0.1", - "get-stream": "^3.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, "extend": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", @@ -448,14 +382,6 @@ } } }, - "find-up": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", - "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", - "requires": { - "locate-path": "^3.0.0" - } - }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -479,16 +405,6 @@ "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==" - }, - "get-stream": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", - "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" - }, "getpass": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", @@ -570,21 +486,6 @@ "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true }, - "invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY=" - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=" - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -597,11 +498,6 @@ "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=" - }, "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", @@ -661,46 +557,12 @@ "verror": "1.10.0" } }, - "lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=", - "requires": { - "invert-kv": "^1.0.0" - } - }, - "locate-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", - "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", - "requires": { - "p-locate": "^3.0.0", - "path-exists": "^3.0.0" - } - }, - "lru-cache": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.3.tgz", - "integrity": "sha512-fFEhvcgzuIoJVUF8fYr5KR0YqxD238zgObTps31YdADwPPAp82a4M8TrckkWyx7ekNlf9aBcVn81cFwwXngrJA==", - "requires": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, "make-error": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.4.tgz", "integrity": "sha512-0Dab5btKVPhibSalc9QGXb559ED7G7iLjFXBaj9Wq8O3vorueR5K5jaE3hkG6ZQINyhA/JgG6Qk4qdFQjsYV6g==", "dev": true }, - "mem": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/mem/-/mem-1.1.0.tgz", - "integrity": "sha1-Xt1StIXKHZAP5kiVUFOZoN+kX3Y=", - "requires": { - "mimic-fn": "^1.0.0" - } - }, "mime-db": { "version": "1.33.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.33.0.tgz", @@ -716,11 +578,6 @@ "mime-db": "~1.33.0" } }, - "mimic-fn": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", - "integrity": "sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==" - }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", @@ -759,19 +616,6 @@ "integrity": "sha1-IKMYwwy0X3H+et+/eyHJnBRy7xE=", "dev": true }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "requires": { - "path-key": "^2.0.0" - } - }, - "number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=" - }, "nyc": { "version": "11.9.0", "resolved": "https://registry.npmjs.org/nyc/-/nyc-11.9.0.tgz", @@ -3419,64 +3263,18 @@ "wrappy": "1" } }, - "os-locale": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-2.1.0.tgz", - "integrity": "sha512-3sslG3zJbEYcaC4YVAvDorjGxc7tv6KVATnLPZONiljsUncvihe9BQoVCEs0RZ1kmf4Hk9OBqlZfJZWI4GanKA==", - "requires": { - "execa": "^0.7.0", - "lcid": "^1.0.0", - "mem": "^1.1.0" - } - }, "os-tmpdir": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" - }, - "p-limit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.0.0.tgz", - "integrity": "sha512-fl5s52lI5ahKCernzzIyAP0QAZbGIovtVHGwpcu1Jr/EpzLVDI2myISHwGqK7m8uQFugVWSrbxH7XnhGtvEc+A==", - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", - "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", - "requires": { - "p-limit": "^2.0.0" - } - }, - "p-try": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz", - "integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ==" - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=" - }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=" - }, "path-parse": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.5.tgz", @@ -3495,11 +3293,6 @@ "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true }, - "pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM=" - }, "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", @@ -3568,16 +3361,6 @@ "uuid": "^3.1.0" } }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=" - }, - "require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=" - }, "resolve": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.8.1.tgz", @@ -3630,29 +3413,6 @@ "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==", "dev": true }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=" - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=" - }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -3692,30 +3452,6 @@ "tweetnacl": "~0.14.0" } }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=" - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "requires": { - "ansi-regex": "^3.0.0" - } - } - } - }, "string_decoder": { "version": "1.1.1", "resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -3729,15 +3465,11 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, "requires": { "ansi-regex": "^2.0.0" } }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=" - }, "supports-color": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", @@ -3961,102 +3693,18 @@ "extsprintf": "^1.2.0" } }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "requires": { - "isexe": "^2.0.0" - } - }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=" - }, - "wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", - "requires": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" - }, - "dependencies": { - "is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", - "requires": { - "number-is-nan": "^1.0.0" - } - }, - "string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", - "requires": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - } - } - } - }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "xregexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xregexp/-/xregexp-4.0.0.tgz", - "integrity": "sha512-PHyM+sQouu7xspQQwELlGwwd05mXUFqwFYfqPO0cC7x4fxyHnnuetmQr6CjJiafIDoH4MogHb9dOoJzR/Y4rFg==" - }, "xtend": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", "dev": true }, - "y18n": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", - "integrity": "sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==" - }, - "yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI=" - }, - "yargs": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.1.tgz", - "integrity": "sha512-B0vRAp1hRX4jgIOWFtjfNjd9OA9RWYZ6tqGA9/I/IrTMsxmKvtWy+ersM+jzpQqbC3YfLzeABPdeTgcJ9eu1qQ==", - "requires": { - "cliui": "^4.0.0", - "decamelize": "^2.0.0", - "find-up": "^3.0.0", - "get-caller-file": "^1.0.1", - "os-locale": "^2.0.0", - "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^2.0.0", - "which-module": "^2.0.0", - "y18n": "^3.2.1 || ^4.0.0", - "yargs-parser": "^10.1.0" - } - }, - "yargs-parser": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-10.1.0.tgz", - "integrity": "sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==", - "requires": { - "camelcase": "^4.1.0" - } - }, "yn": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yn/-/yn-2.0.0.tgz", diff --git a/package.json b/package.json index e23e6cfc9..2cefaf7f3 100644 --- a/package.json +++ b/package.json @@ -47,8 +47,7 @@ "node": ">=8.5.0" }, "dependencies": { - "typescript": "2.9.2", - "yargs": "^12.0.1" + "typescript": "2.9.2" }, "devDependencies": { "@types/glob": "^5.0.35", diff --git a/src/CommandLineParser.ts b/src/CommandLineParser.ts index 989abeb14..22cfec468 100644 --- a/src/CommandLineParser.ts +++ b/src/CommandLineParser.ts @@ -1,149 +1,175 @@ import * as fs from "fs"; import * as path from "path"; import * as ts from "typescript"; -import * as yargs from "yargs"; -import {CompilerOptions} from "./CompilerOptions"; +import {CompilerOptions, LuaTarget, LuaLibImportKind} from "./CompilerOptions"; interface ParsedCommandLine extends ts.ParsedCommandLine { options: CompilerOptions; } -interface YargsOptions { - [key: string]: yargs.Options; +interface BaseCLIOption { + alias: string | string[]; + describe: string; + type: string; } -export const optionDeclarations: YargsOptions = { +interface CLIOption extends BaseCLIOption { + choices: T[]; + default: T; +} + +export const optionDeclarations: {[key: string]: CLIOption} = { luaLibImport: { - choices: ["inline", "require", "none"], - default: "inline", + choices: [LuaLibImportKind.Inline, LuaLibImportKind.Require, LuaLibImportKind.Always, LuaLibImportKind.None], + default: LuaLibImportKind.Inline, describe: "Specifies how js standard features missing in lua are imported.", - type: "string", - }, + type: "enum", + } as CLIOption, luaTarget: { alias: "lt", - choices: ["JIT", "5.3", "5.2", "5.1"], - default: "JIT", + choices: [LuaTarget.LuaJIT, LuaTarget.Lua53, LuaTarget.Lua52, LuaTarget.Lua51], + default: LuaTarget.LuaJIT, describe: "Specify Lua target version.", - type: "string", - }, + type: "enum", + } as CLIOption, noHeader: { default: false, describe: "Specify if a header will be added to compiled files.", type: "boolean", - }, + } as CLIOption, }; +const helpString = + "Syntax: tstl [options] [files...]\n\n" + + "In addition to the options listed below you can also pass options\n" + + "for the typescript compiler (For a list of options use tsc -h).\n" + + "Some tsc options might have no effect."; + +const examples = [ + ["Compile files", "tstl path/to/file.ts [...]"], + ["Compile project", "tstl -p path/to/tsconfig.json"], +]; + class CLIError extends Error {} /** - * Removes defaults from the arguments. - * Returns a tuple where [0] is a copy of the options without defaults and [1] is the extracted defaults. + * Parse the supplied arguments. + * The result will include arguments supplied via CLI and arguments from tsconfig. */ -function getYargOptionsWithoutDefaults(options: YargsOptions): [YargsOptions, yargs.Arguments] { - // options is a deep object, Object.assign or {...options} still keeps the referece - const copy = JSON.parse(JSON.stringify(options)); +export function parseCommandLine(args: string[]): ParsedCommandLine +{ + const commandLine = ts.parseCommandLine(args); - const optionDefaults: yargs.Arguments = {_: undefined, $0: undefined}; - for (const optionName in copy) { - const section = copy[optionName]; + const tsConfigOptions = readTsConfig(commandLine.options); - optionDefaults[optionName] = section.default; - delete section.default; + copyOptionsIfNotSet(commandLine.options, tsConfigOptions); + + // Run diagnostics to check for invalid tsc options + runTsDiagnostics(commandLine); + + const tstlOptions = parseTSTLOptions(args); + copyOptionsIfNotSet(commandLine.options, tstlOptions); + + const tstlDefaults = getDefaultOptions(); + copyOptionsIfNotSet(commandLine.options, tstlDefaults); + + if (commandLine.options.project && !commandLine.options.rootDir) { + commandLine.options.rootDir = path.dirname(commandLine.options.project); } - return [copy, optionDefaults]; + return commandLine as ParsedCommandLine; } -/** - * Pares the supplied arguments. - * The result will include arguments supplied via CLI and arguments from tsconfig. - */ -export function parseCommandLine(args: string[]): ParsedCommandLine { - // Get a copy of the options without defaults to prevent defaults overriding project config - const [tstlOptions, tstlDefaults] = getYargOptionsWithoutDefaults(optionDeclarations); - - const parsedArgs = yargs - .usage( - "Syntax: tstl [options] [files...]\n\n" + - "In addition to the options listed below you can also pass options" + - "for the typescript compiler (For a list of options use tsc -h).\n" + - "Some tsc options might have no effect.") - .example("tstl path/to/file.ts [...]", "Compile files") - .example("tstl -p path/to/tsconfig.json", "Compile project") - .wrap(yargs.terminalWidth()) - .options(tstlOptions) - .fail((msg, err) => { - throw new CLIError(msg); - }) - .parse(args); - - let commandLine = ts.parseCommandLine(args); - - // Run diagnostics to check for invalid tsc/tstl options - runDiagnostics(commandLine); - - // Add TSTL options from CLI - addTSTLOptions(commandLine, parsedArgs); +export function getHelpString(): string { + let result = helpString + "\n\n"; + + if (examples.length > 0) { + result += "Examples:\n"; + for (const [exampleName, example] of examples) { + result += ` ${exampleName}: ${example}\n`; + } + } + + return result; +} +function readTsConfig(options: CompilerOptions): CompilerOptions { // Load config - if (commandLine.options.project) { - findConfigFile(commandLine); - const configPath = commandLine.options.project; + if (options.project) { + findConfigFile(options); + const configPath = options.project; const configContents = fs.readFileSync(configPath).toString(); const configJson = ts.parseConfigFileTextToJson(configPath, configContents); - commandLine = ts.parseJsonConfigFileContent( + return ts.parseJsonConfigFileContent( configJson.config, ts.sys, path.dirname(configPath), - commandLine.options - ); + options + ).options; } + return undefined; +} - // Add TSTL options from tsconfig - addTSTLOptions(commandLine); - - // Add TSTL defaults last - addTSTLOptions(commandLine, tstlDefaults); +function copyOptionsIfNotSet(options: CompilerOptions, optionsToCopy: CompilerOptions): void { + for (const optionName in optionsToCopy) { + if (!options[optionName]) { + options[optionName] = optionsToCopy[optionName]; + } + } +} - // Run diagnostics again to check for errors in tsconfig - runDiagnostics(commandLine); +function parseTSTLOptions(args: string[]): CompilerOptions { + const result = {}; + for (let i = 0; i < args.length; i++) { + if (args[i].startsWith("--")) { + const argumentName = args[i].substr(2); + const option = optionDeclarations[argumentName]; + if (option) { + const value = readValue(args[i + 1], option.type); + i++; // Skip the value from being considered as argument name + + if (option.choices) { + if (option.choices.indexOf(value) < 0) { + throw new CLIError(`Unknown ${argumentName} value '${value}.\n'` + + `Accepted values: ${option.choices}`); + } + } - if (commandLine.options.project && !commandLine.options.rootDir) { - commandLine.options.rootDir = path.dirname(commandLine.options.project); + result[argumentName] = value; + } + } } + return result; +} - if (!commandLine.options.rootDir) { - commandLine.options.rootDir = process.cwd(); +function readValue(valueString: string, valueType: string): any { + if (valueType === "boolean") { + return valueString === "true" || valueString === "t" + ? true + : false; + } else if (valueType === "enum") { + return valueString.toLowerCase(); + } else { + return valueString; } +} - if (!commandLine.options.outDir) { - commandLine.options.outDir = commandLine.options.rootDir; +function getDefaultOptions(): CompilerOptions { + const options: CompilerOptions = {}; + + for (const optionName in optionDeclarations) { + options[optionName] = optionDeclarations[optionName].default; } - return commandLine as ParsedCommandLine; -} + options.rootDir = process.cwd(); + options.outDir = options.rootDir; -function addTSTLOptions( - commandLine: ts.ParsedCommandLine, - additionalArgs?: yargs.Arguments, - forceOverride?: boolean -): void -{ - additionalArgs = additionalArgs ? additionalArgs : commandLine.raw; - // Add compiler options that are ignored by TS parsers - if (additionalArgs) { - for (const arg in additionalArgs) { - // dont override, this will prioritize CLI over tsconfig. - if (optionDeclarations[arg] && (!commandLine.options[arg] || forceOverride)) { - commandLine.options[arg] = additionalArgs[arg]; - } - } - } + return options; } /** Check the current state of the ParsedCommandLine for errors */ -function runDiagnostics(commandLine: ts.ParsedCommandLine): void { +function runTsDiagnostics(commandLine: ts.ParsedCommandLine): void { // Remove files that dont exist commandLine.fileNames = commandLine.fileNames.filter(file => fs.existsSync(file) || fs.existsSync(file + ".ts")); @@ -181,18 +207,18 @@ function runDiagnostics(commandLine: ts.ParsedCommandLine): void { } /** Find configFile, function from ts api seems to be broken? */ -export function findConfigFile(commandLine: ts.ParsedCommandLine): void { - if (!commandLine.options.project) { +export function findConfigFile(options: ts.CompilerOptions): void { + if (!options.project) { throw new CLIError(`error no base path provided, could not find config.`); } - let configPath = commandLine.options.project; + let configPath = options.project; // If the project path is wrapped in double quotes, remove them if (/^".*"$/.test(configPath)) { configPath = configPath.substring(1, configPath.length - 1); } /* istanbul ignore if: Testing else part is not really possible via automated tests */ if (!path.isAbsolute(configPath)) { - // TODO check if commandLine.options.project can even contain non absolute paths + // TODO check if options.project can even contain non absolute paths configPath = path.join(process.cwd(), configPath); } if (fs.statSync(configPath).isDirectory()) { @@ -210,5 +236,5 @@ export function findConfigFile(commandLine: ts.ParsedCommandLine): void { } } } - commandLine.options.project = configPath; + options.project = configPath; } diff --git a/src/Compiler.ts b/src/Compiler.ts index 112bce125..622addd49 100644 --- a/src/Compiler.ts +++ b/src/Compiler.ts @@ -2,14 +2,17 @@ import * as fs from "fs"; import * as path from "path"; import * as ts from "typescript"; -import {parseCommandLine} from "./CommandLineParser"; +import {parseCommandLine, getHelpString} from "./CommandLineParser"; import {CompilerOptions, LuaLibImportKind, LuaTarget} from "./CompilerOptions"; import {LuaTranspiler} from "./LuaTranspiler"; export function compile(argv: string[]): void { const commandLine = parseCommandLine(argv); /* istanbul ignore if: tested in test/compiler/watchmode.spec with subproccess */ - if (commandLine.options.watch) { + if (commandLine.options.help) { + console.log(getHelpString()); + } + else if (commandLine.options.watch) { watchWithOptions(commandLine.fileNames, commandLine.options); } else { compileFilesWithOptions(commandLine.fileNames, commandLine.options); diff --git a/test/unit/compiler/commandLineParser.spec.ts b/test/unit/compiler/commandLineParser.spec.ts new file mode 100644 index 000000000..7b71da888 --- /dev/null +++ b/test/unit/compiler/commandLineParser.spec.ts @@ -0,0 +1,66 @@ +import { Expect, Test, TestCase } from "alsatian"; +import { parseCommandLine } from "../../../src/CommandLineParser"; +import { LuaLibImportKind, LuaTarget } from "../../../src/CompilerOptions"; + +export class CommandLineParserTests +{ + @TestCase([""], LuaLibImportKind.Inline) + @TestCase(["--luaLibImport", "none"], LuaLibImportKind.None) + @TestCase(["--luaLibImport", "always"], LuaLibImportKind.Always) + @TestCase(["--luaLibImport", "inline"], LuaLibImportKind.Inline) + @TestCase(["--luaLibImport", "require"], LuaLibImportKind.Require) + @Test("CLI parser luaLibImportKind") + public cliParserLuaLibImportKind(args: string[], expected: LuaLibImportKind): void { + const result = parseCommandLine(args); + Expect(result.options.luaLibImport).toBe(expected); + } + + @Test("CLI parser invalid luaLibImportKind") + public cliParserInvalidLuaLibImportKind(): void { + Expect(() => parseCommandLine(["--luaLibImport", "invalid"])).toThrow(); + } + + @TestCase([""], LuaTarget.LuaJIT) + @TestCase(["--luaTarget", "5.1"], LuaTarget.Lua51) + @TestCase(["--luaTarget", "5.2"], LuaTarget.Lua52) + @TestCase(["--luaTarget", "jit"], LuaTarget.LuaJIT) + @TestCase(["--luaTarget", "JIT"], LuaTarget.LuaJIT) + @TestCase(["--luaTarget", "5.3"], LuaTarget.Lua53) + @Test("CLI parser luaTarget") + public cliParserLuaTarget(args: string[], expected: LuaTarget): void { + const result = parseCommandLine(args); + Expect(result.options.luaTarget).toBe(expected); + } + + @Test("CLI parser invalid luaTarget") + public cliParserInvalidLuaTarget(): void { + Expect(() => parseCommandLine(["--luatTarget", "invalid"])).toThrow(); + } + + @TestCase([""], false) + @TestCase(["--noHeader", "true"], true) + @TestCase(["--noHeader", "false"], false) + @Test("CLI parser noHeader") + public cliParserNoHeader(args: string[], expected: boolean): void { + const result = parseCommandLine(args); + Expect(result.options.noHeader).toBe(expected); + } + + @TestCase([""], false) + @TestCase(["--project", "tsconfig.json"], true) + @TestCase(["-p", "tsconfig.json"], true) + @Test("CLI parser project") + public cliParserProject(args: string[], expected: boolean): void { + const result = parseCommandLine(args); + Expect(result.options.project !== undefined).toBe(expected); + } + + @TestCase([""], false) + @TestCase(["--help"], true) + @TestCase(["-h"], true) + @Test("CLI parser project") + public cliParserHelp(args: string[], expected: boolean): void { + const result = parseCommandLine(args); + Expect(result.options.help === true).toBe(expected); + } +} \ No newline at end of file From bf4d5962f0ad87f51fdaaaff833448e2207ea427 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 3 Feb 2019 18:29:03 +0100 Subject: [PATCH 02/11] Fixed issue with tsconfig and some other tests --- src/CommandLineParser.ts | 56 ++++++++++++++----- test/unit/cli.spec.ts | 3 +- .../configuration/mixed/index.spec.ts | 6 +- 3 files changed, 47 insertions(+), 18 deletions(-) diff --git a/src/CommandLineParser.ts b/src/CommandLineParser.ts index 22cfec468..c229ca5bb 100644 --- a/src/CommandLineParser.ts +++ b/src/CommandLineParser.ts @@ -61,23 +61,35 @@ export function parseCommandLine(args: string[]): ParsedCommandLine { const commandLine = ts.parseCommandLine(args); - const tsConfigOptions = readTsConfig(commandLine.options); - - copyOptionsIfNotSet(commandLine.options, tsConfigOptions); - // Run diagnostics to check for invalid tsc options runTsDiagnostics(commandLine); - const tstlOptions = parseTSTLOptions(args); - copyOptionsIfNotSet(commandLine.options, tstlOptions); + const tstlCLIOptions = parseTSTLOptions(args); + copyOptionsIfNotSet(commandLine.options, tstlCLIOptions); - const tstlDefaults = getDefaultOptions(); - copyOptionsIfNotSet(commandLine.options, tstlDefaults); + const tsConfigOptions = readTsConfig(commandLine.options); + if (tsConfigOptions !== undefined) { + copyOptionsIfNotSet(commandLine.options, tsConfigOptions); + } + + // Run diagnostics to check for invalid tsconfig + runTsDiagnostics(commandLine); if (commandLine.options.project && !commandLine.options.rootDir) { commandLine.options.rootDir = path.dirname(commandLine.options.project); } + if (!commandLine.options.rootDir) { + commandLine.options.rootDir = process.cwd(); + } + + if (!commandLine.options.outDir) { + commandLine.options.outDir = commandLine.options.rootDir; + } + + const tstlDefaults = getDefaultOptions(); + copyOptionsIfNotSet(commandLine.options, tstlDefaults); + return commandLine as ParsedCommandLine; } @@ -101,12 +113,29 @@ function readTsConfig(options: CompilerOptions): CompilerOptions { const configPath = options.project; const configContents = fs.readFileSync(configPath).toString(); const configJson = ts.parseConfigFileTextToJson(configPath, configContents); - return ts.parseJsonConfigFileContent( + const parsedJsonConfig = ts.parseJsonConfigFileContent( configJson.config, ts.sys, path.dirname(configPath), options - ).options; + ); + + for (const key in parsedJsonConfig.raw) { + const option = optionDeclarations[key]; + if (option !== undefined) { + const value = readValue(parsedJsonConfig.raw[key], option.type); + if (option.choices) { + if (option.choices.indexOf(value) < 0) { + throw new CLIError(`Unknown ${key} value '${value}.\n'` + + `Accepted values: ${option.choices}`); + } + } + + parsedJsonConfig.options[key] = value; + } + } + + return parsedJsonConfig.options; } return undefined; } @@ -159,12 +188,11 @@ function getDefaultOptions(): CompilerOptions { const options: CompilerOptions = {}; for (const optionName in optionDeclarations) { - options[optionName] = optionDeclarations[optionName].default; + if (optionDeclarations[optionName].default !== undefined) { + options[optionName] = optionDeclarations[optionName].default; + } } - options.rootDir = process.cwd(); - options.outDir = options.rootDir; - return options; } diff --git a/test/unit/cli.spec.ts b/test/unit/cli.spec.ts index bfbc98dc6..a8ad0ae3d 100644 --- a/test/unit/cli.spec.ts +++ b/test/unit/cli.spec.ts @@ -1,11 +1,12 @@ import { Expect, Test, TestCase } from "alsatian"; import { findConfigFile, parseCommandLine } from "../../src/CommandLineParser"; +import { LuaTarget } from "../../src/CompilerOptions"; export class CLITests { @Test("defaultOption") - @TestCase("luaTarget", "JIT") + @TestCase("luaTarget", LuaTarget.LuaJIT) @TestCase("noHeader", false) @TestCase("luaLibImport", "inline") @TestCase("rootDir", process.cwd()) diff --git a/test/unit/compiler/configuration/mixed/index.spec.ts b/test/unit/compiler/configuration/mixed/index.spec.ts index 376cd8c55..bdd5fb2f2 100644 --- a/test/unit/compiler/configuration/mixed/index.spec.ts +++ b/test/unit/compiler/configuration/mixed/index.spec.ts @@ -6,10 +6,10 @@ import * as ts from "typescript"; import { CompilerOptions, LuaLibImportKind } from "../../../../../src/CompilerOptions"; import { optionDeclarations, parseCommandLine } from "../../../../../src/CommandLineParser"; -export class MixedConfigurationTests { - +export class MixedConfigurationTests +{ @Test("tsconfig.json mixed with cmd line args") - public tsconfigMixedWithCmdLineArgs() { + public tsconfigMixedWithCmdLineArgs(): void { const rootPath = __dirname; const tsConfigPath = path.join(rootPath, "project-tsconfig.json"); const expectedTsConfig = ts.parseJsonConfigFileContent( From c70b797c3388ebebdb8cdb504b1eaf8317abf8c1 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Sun, 3 Feb 2019 19:43:26 +0100 Subject: [PATCH 03/11] Upgraded to TS3 --- package-lock.json | 6 +++--- package.json | 2 +- src/TSHelper.ts | 19 +++++++++++++++---- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/package-lock.json b/package-lock.json index 4021109cc..29a8ae494 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3660,9 +3660,9 @@ "optional": true }, "typescript": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-2.9.2.tgz", - "integrity": "sha512-Gr4p6nFNaoufRIY4NMdpQRNmgxVIGMs4Fcu/ujdYk3nAZqk7supzBE9idmvfZIlH/Cuj//dvi+019qEue9lV0w==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.3.1.tgz", + "integrity": "sha512-cTmIDFW7O0IHbn1DPYjkiebHxwtCMU+eTy30ZtJNBPF9j2O1ITu5XH2YnBeVRKWHqF+3JQwWJv0Q0aUgX8W7IA==" }, "urlgrey": { "version": "0.4.4", diff --git a/package.json b/package.json index 2cefaf7f3..17c570f1d 100644 --- a/package.json +++ b/package.json @@ -47,7 +47,7 @@ "node": ">=8.5.0" }, "dependencies": { - "typescript": "2.9.2" + "typescript": "^3.3.1" }, "devDependencies": { "@types/glob": "^5.0.35", diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 89e57f575..4ffe1c828 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -83,14 +83,25 @@ export class TSHelper { // export statement, we only check for export statements // TODO will break in 3.x return sourceFile.statements.some( - statement => (ts.getCombinedModifierFlags(statement) & ts.ModifierFlags.Export) !== 0 - || statement.kind === ts.SyntaxKind.ExportAssignment - || statement.kind === ts.SyntaxKind.ExportDeclaration + statement => (this.isDeclaration(statement) && this.isExported(statement)) + || ts.isExportAssignment(statement) + || ts.isExportDeclaration(statement) ); } return false; } + public static isExported(declaration: ts.Declaration): boolean { + return (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Export) !== 0; + } + + public static isDeclaration(node: ts.Node): node is ts.Declaration { + return ts.isEnumDeclaration(node) || ts.isClassDeclaration(node) || ts.isExportDeclaration(node) + || ts.isImportDeclaration(node) || ts.isMethodDeclaration(node) || ts.isModuleDeclaration(node) + || ts.isFunctionDeclaration(node) || ts.isVariableDeclaration(node) || ts.isInterfaceDeclaration(node) + || ts.isTypeAliasDeclaration(node) || ts.isNamespaceExportDeclaration(node); + } + public static isInDestructingAssignment(node: ts.Node): boolean { return node.parent && ((ts.isVariableDeclaration(node.parent) && ts.isArrayBindingPattern(node.parent.name)) || (ts.isBinaryExpression(node.parent) && ts.isArrayLiteralExpression(node.parent.left))); @@ -384,7 +395,7 @@ export class TSHelper { } public static getSignatureDeclarations( - signatures: ts.Signature[], + signatures: ReadonlyArray, checker: ts.TypeChecker ): ts.SignatureDeclaration[] { From 126e48ddd6c5f8ec1adf9b0e56741e06e6450281 Mon Sep 17 00:00:00 2001 From: Lolleko Date: Wed, 6 Feb 2019 13:52:52 +0100 Subject: [PATCH 04/11] Fixed TS3 support Fixed CLI only reading compilerOptions field of tsconfig --- package-lock.json | 4 +- src/CommandLineParser.ts | 48 ++++++++++---------- src/Compiler.ts | 34 +++++++++----- src/LuaTranspiler.ts | 4 +- src/TSHelper.ts | 90 +++++++++++++++++++------------------- test/src/util.ts | 32 +------------- test/unit/tshelper.spec.ts | 3 +- 7 files changed, 99 insertions(+), 116 deletions(-) diff --git a/package-lock.json b/package-lock.json index 29a8ae494..804910d51 100644 --- a/package-lock.json +++ b/package-lock.json @@ -655,7 +655,6 @@ "version": "0.1.4", "bundled": true, "dev": true, - "optional": true, "requires": { "kind-of": "^3.0.2", "longest": "^1.0.1", @@ -1838,8 +1837,7 @@ "longest": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "loose-envify": { "version": "1.3.1", diff --git a/src/CommandLineParser.ts b/src/CommandLineParser.ts index c229ca5bb..00d6d6872 100644 --- a/src/CommandLineParser.ts +++ b/src/CommandLineParser.ts @@ -59,22 +59,20 @@ class CLIError extends Error {} */ export function parseCommandLine(args: string[]): ParsedCommandLine { - const commandLine = ts.parseCommandLine(args); + let commandLine = ts.parseCommandLine(args); // Run diagnostics to check for invalid tsc options runTsDiagnostics(commandLine); - const tstlCLIOptions = parseTSTLOptions(args); - copyOptionsIfNotSet(commandLine.options, tstlCLIOptions); - - const tsConfigOptions = readTsConfig(commandLine.options); - if (tsConfigOptions !== undefined) { - copyOptionsIfNotSet(commandLine.options, tsConfigOptions); - } + // This will add TS and TSTL options from a tsconfig + commandLine = readTsConfig(commandLine); // Run diagnostics to check for invalid tsconfig runTsDiagnostics(commandLine); + // Merge TSTL CLI options in (highest priority) will also set defaults if none specified + commandLine = parseTSTLOptions(commandLine, args); + if (commandLine.options.project && !commandLine.options.rootDir) { commandLine.options.rootDir = path.dirname(commandLine.options.project); } @@ -87,9 +85,6 @@ export function parseCommandLine(args: string[]): ParsedCommandLine commandLine.options.outDir = commandLine.options.rootDir; } - const tstlDefaults = getDefaultOptions(); - copyOptionsIfNotSet(commandLine.options, tstlDefaults); - return commandLine as ParsedCommandLine; } @@ -106,7 +101,9 @@ export function getHelpString(): string { return result; } -function readTsConfig(options: CompilerOptions): CompilerOptions { +function readTsConfig(parsedCommandLine: ts.ParsedCommandLine): ts.ParsedCommandLine { + const options = parsedCommandLine.options; + // Load config if (options.project) { findConfigFile(options); @@ -134,21 +131,12 @@ function readTsConfig(options: CompilerOptions): CompilerOptions { parsedJsonConfig.options[key] = value; } } - - return parsedJsonConfig.options; - } - return undefined; -} - -function copyOptionsIfNotSet(options: CompilerOptions, optionsToCopy: CompilerOptions): void { - for (const optionName in optionsToCopy) { - if (!options[optionName]) { - options[optionName] = optionsToCopy[optionName]; - } + return parsedJsonConfig; } + return parsedCommandLine; } -function parseTSTLOptions(args: string[]): CompilerOptions { +function parseTSTLOptions(commandLine: ts.ParsedCommandLine, args: string[]): ts.ParsedCommandLine { const result = {}; for (let i = 0; i < args.length; i++) { if (args[i].startsWith("--")) { @@ -169,7 +157,17 @@ function parseTSTLOptions(args: string[]): CompilerOptions { } } } - return result; + for (const option in result) { + commandLine.options[option] = result[option]; + } + // Add defaults if not set + const defaultOptions = getDefaultOptions(); + for (const option in defaultOptions) { + if (!commandLine.options[option]) { + commandLine.options[option] = defaultOptions[option]; + } + } + return commandLine; } function readValue(valueString: string, valueType: string): any { diff --git a/src/Compiler.ts b/src/Compiler.ts index 622addd49..d92a26e5a 100644 --- a/src/Compiler.ts +++ b/src/Compiler.ts @@ -68,19 +68,17 @@ export function compileFilesWithOptions(fileNames: string[], options: CompilerOp transpiler.emitFilesAndReportErrors(); } -const libSource = fs.readFileSync(path.join(path.dirname(require.resolve("typescript")), "lib.es6.d.ts")).toString(); +const libCache: {[key: string]: string} = {}; const defaultCompilerOptions: CompilerOptions = { luaLibImport: LuaLibImportKind.Require, luaTarget: LuaTarget.Lua53, }; -export function transpileString( - str: string, - options: CompilerOptions = defaultCompilerOptions, - ignoreDiagnostics = false -): string { - const compilerHost = { +export function createStringCompilerProgram( + input: string, options: CompilerOptions = defaultCompilerOptions): ts.Program { + + const compilerHost = { directoryExists: () => true, fileExists: (fileName): boolean => true, getCanonicalFileName: fileName => fileName, @@ -89,12 +87,16 @@ export function transpileString( getDirectories: () => [], getNewLine: () => "\n", - getSourceFile: (filename, languageVersion) => { + getSourceFile: (filename: string, languageVersion) => { if (filename === "file.ts") { - return ts.createSourceFile(filename, str, ts.ScriptTarget.Latest, false); + return ts.createSourceFile(filename, input, ts.ScriptTarget.Latest, false); } - if (filename === "lib.es6.d.ts") { - return ts.createSourceFile(filename, libSource, ts.ScriptTarget.Latest, false); + if (filename.indexOf(".d.ts") !== -1) { + if (!libCache[filename]) { + libCache[filename] = + fs.readFileSync(path.join(path.dirname(require.resolve("typescript")), filename)).toString(); + } + return ts.createSourceFile(filename, libCache[filename], ts.ScriptTarget.Latest, false); } return undefined; }, @@ -105,7 +107,15 @@ export function transpileString( // Don't write output writeFile: (name, text, writeByteOrderMark) => undefined, }; - const program = ts.createProgram(["file.ts"], options, compilerHost); + return ts.createProgram(["file.ts"], options, compilerHost); +} + +export function transpileString( + str: string, + options: CompilerOptions = defaultCompilerOptions, + ignoreDiagnostics = false +): string { + const program = createStringCompilerProgram(str, options); if (!ignoreDiagnostics) { const diagnostics = ts.getPreEmitDiagnostics(program); diff --git a/src/LuaTranspiler.ts b/src/LuaTranspiler.ts index 565685cad..13fcb272c 100644 --- a/src/LuaTranspiler.ts +++ b/src/LuaTranspiler.ts @@ -61,7 +61,9 @@ export class LuaTranspiler { return error; } - this.program.getSourceFiles().forEach(sourceFile => this.emitSourceFile(sourceFile)); + this.program.getSourceFiles().forEach(sourceFile => { + this.emitSourceFile(sourceFile); + }); // Copy lualib to target dir if (this.options.luaLibImport === LuaLibImportKind.Require diff --git a/src/TSHelper.ts b/src/TSHelper.ts index 4ffe1c828..a0cbbe265 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -62,7 +62,7 @@ export class TSHelper { for (const clause of node.heritageClauses) { if (clause.token === ts.SyntaxKind.ExtendsKeyword) { const superType = checker.getTypeAtLocation(clause.types[0]); - const decorators = this.getCustomDecorators(superType, checker); + const decorators = TSHelper.getCustomDecorators(superType, checker); if (!decorators.has(DecoratorKind.PureAbstract)) { return clause.types[0]; } @@ -73,26 +73,27 @@ export class TSHelper { } public static getExtendedType(node: ts.ClassLikeDeclarationBase, checker: ts.TypeChecker): ts.Type | undefined { - const extendedTypeNode = this.getExtendedTypeNode(node, checker); + const extendedTypeNode = TSHelper.getExtendedTypeNode(node, checker); return extendedTypeNode && checker.getTypeAtLocation(extendedTypeNode); } public static isFileModule(sourceFile: ts.SourceFile): boolean { if (sourceFile) { - // Vanilla ts flags files as external module if they have an import or - // export statement, we only check for export statements - // TODO will break in 3.x - return sourceFile.statements.some( - statement => (this.isDeclaration(statement) && this.isExported(statement)) - || ts.isExportAssignment(statement) - || ts.isExportDeclaration(statement) - ); + return sourceFile.statements.some(TSHelper.isStatementExported); } return false; } - public static isExported(declaration: ts.Declaration): boolean { - return (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Export) !== 0; + public static isStatementExported(statement: ts.Statement): boolean { + if (ts.isExportAssignment(statement) || ts.isExportDeclaration(statement)) { + return true; + } + if (ts.isVariableStatement(statement)) { + return statement.declarationList.declarations.some( + declaration => (ts.getCombinedModifierFlags(declaration) & ts.ModifierFlags.Export) !== 0); + } + return TSHelper.isDeclaration(statement) + && ((ts.getCombinedModifierFlags(statement) & ts.ModifierFlags.Export) !== 0); } public static isDeclaration(node: ts.Node): node is ts.Declaration { @@ -123,7 +124,7 @@ export class TSHelper { const superTypes = type.getBaseTypes(); if (superTypes) { for (const superType of superTypes) { - if (this.forTypeOrAnySupertype(superType, checker, predicate)) { + if (TSHelper.forTypeOrAnySupertype(superType, checker, predicate)) { return true; } } @@ -139,12 +140,12 @@ export class TSHelper { public static isArrayTypeNode(typeNode: ts.TypeNode): boolean { return typeNode.kind === ts.SyntaxKind.ArrayType || typeNode.kind === ts.SyntaxKind.TupleType || ((typeNode.kind === ts.SyntaxKind.UnionType || typeNode.kind === ts.SyntaxKind.IntersectionType) && - (typeNode as ts.UnionOrIntersectionTypeNode).types.some(this.isArrayTypeNode)); + (typeNode as ts.UnionOrIntersectionTypeNode).types.some(TSHelper.isArrayTypeNode)); } public static isExplicitArrayType(type: ts.Type, checker: ts.TypeChecker): boolean { const typeNode = checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias); - return typeNode && this.isArrayTypeNode(typeNode); + return typeNode && TSHelper.isArrayTypeNode(typeNode); } public static isFunctionType(type: ts.Type, checker: ts.TypeChecker): boolean { @@ -153,13 +154,13 @@ export class TSHelper { } public static isArrayType(type: ts.Type, checker: ts.TypeChecker): boolean { - return this.forTypeOrAnySupertype(type, checker, t => this.isExplicitArrayType(t, checker)); + return TSHelper.forTypeOrAnySupertype(type, checker, t => TSHelper.isExplicitArrayType(t, checker)); } public static isLuaIteratorCall(node: ts.Node, checker: ts.TypeChecker): boolean { if (ts.isCallExpression(node) && node.parent && ts.isForOfStatement(node.parent)) { const type = checker.getTypeAtLocation(node.expression); - return this.getCustomDecorators(type, checker).has(DecoratorKind.LuaIterator); + return TSHelper.getCustomDecorators(type, checker).has(DecoratorKind.LuaIterator); } else { return false; } @@ -169,19 +170,19 @@ export class TSHelper { if (ts.isCallExpression(node)) { const type = checker.getTypeAtLocation(node.expression); - return this.getCustomDecorators(type, checker).has(DecoratorKind.TupleReturn); + return TSHelper.getCustomDecorators(type, checker).has(DecoratorKind.TupleReturn); } else { return false; } } public static isInTupleReturnFunction(node: ts.Node, checker: ts.TypeChecker): boolean { - const declaration = this.findFirstNodeAbove( + const declaration = TSHelper.findFirstNodeAbove( node, (n): n is ts.Node => ts.isFunctionDeclaration(n) || ts.isMethodDeclaration(n) ); if (declaration) { - const decorators = this.getCustomDecorators(checker.getTypeAtLocation(declaration), checker); + const decorators = TSHelper.getCustomDecorators(checker.getTypeAtLocation(declaration), checker); return decorators.has(DecoratorKind.TupleReturn) // Lua iterators are not 'true' tupleReturn functions as they actually return a function && !decorators.has(DecoratorKind.LuaIterator); @@ -191,7 +192,7 @@ export class TSHelper { } public static getContainingFunctionReturnType(node: ts.Node, checker: ts.TypeChecker): ts.Type { - const declaration = this.findFirstNodeAbove(node, ts.isFunctionLike); + const declaration = TSHelper.findFirstNodeAbove(node, ts.isFunctionLike); if (declaration) { const signature = checker.getSignatureFromDeclaration(declaration); return checker.getReturnTypeOfSignature(signature); @@ -224,7 +225,6 @@ export class TSHelper { console.warn(`Encountered unknown decorator ${decStr}.`); } }); - symbol.getJsDocTags().forEach(tag => { if (Decorator.isValid(tag.name)) { const dec = new Decorator(tag.name, tag.text ? tag.text.split(" ") : []); @@ -236,10 +236,10 @@ export class TSHelper { public static getCustomDecorators(type: ts.Type, checker: ts.TypeChecker): Map { const decMap = new Map(); if (type.symbol) { - this.collectCustomDecorators(type.symbol, checker, decMap); + TSHelper.collectCustomDecorators(type.symbol, checker, decMap); } if (type.aliasSymbol) { - this.collectCustomDecorators(type.aliasSymbol, checker, decMap); + TSHelper.collectCustomDecorators(type.aliasSymbol, checker, decMap); } return decMap; } @@ -266,20 +266,20 @@ export class TSHelper { public static typeHasGetAccessor(type: ts.Type, name: ts.__String, checker: ts.TypeChecker): boolean | undefined { if (type.isUnion()) { - if (type.types.some(t => this.typeHasGetAccessor(t, name, checker))) { + if (type.types.some(t => TSHelper.typeHasGetAccessor(t, name, checker))) { // undefined if only a subset of types implements the accessor - return type.types.every(t => this.typeHasGetAccessor(t, name, checker)) ? true : undefined; + return type.types.every(t => TSHelper.typeHasGetAccessor(t, name, checker)) ? true : undefined; } return false; } - return this.forTypeOrAnySupertype(type, checker, t => this.hasExplicitGetAccessor(t, name)); + return TSHelper.forTypeOrAnySupertype(type, checker, t => TSHelper.hasExplicitGetAccessor(t, name)); } public static hasGetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean | undefined { if (ts.isPropertyAccessExpression(node)) { const name = node.name.escapedText; const type = checker.getTypeAtLocation(node.expression); - return this.typeHasGetAccessor(type, name, checker); + return TSHelper.typeHasGetAccessor(type, name, checker); } return false; } @@ -293,20 +293,20 @@ export class TSHelper { public static typeHasSetAccessor(type: ts.Type, name: ts.__String, checker: ts.TypeChecker): boolean | undefined { if (type.isUnion()) { - if (type.types.some(t => this.typeHasSetAccessor(t, name, checker))) { + if (type.types.some(t => TSHelper.typeHasSetAccessor(t, name, checker))) { // undefined if only a subset of types implements the accessor - return type.types.every(t => this.typeHasSetAccessor(t, name, checker)) ? true : undefined; + return type.types.every(t => TSHelper.typeHasSetAccessor(t, name, checker)) ? true : undefined; } return false; } - return this.forTypeOrAnySupertype(type, checker, t => this.hasExplicitSetAccessor(t, name)); + return TSHelper.forTypeOrAnySupertype(type, checker, t => TSHelper.hasExplicitSetAccessor(t, name)); } public static hasSetAccessor(node: ts.Node, checker: ts.TypeChecker): boolean { if (ts.isPropertyAccessExpression(node)) { const name = node.name.escapedText; const type = checker.getTypeAtLocation(node.expression); - return this.typeHasSetAccessor(type, name, checker); + return TSHelper.typeHasSetAccessor(type, name, checker); } return false; } @@ -367,10 +367,10 @@ export class TSHelper { public static isAccessExpressionWithEvaluationEffects(node: ts.Expression, checker: ts.TypeChecker): [boolean, ts.Expression, ts.Expression] { if (ts.isElementAccessExpression(node) && - (this.isExpressionWithEvaluationEffect(node.expression) - || this.isExpressionWithEvaluationEffect(node.argumentExpression))) { + (TSHelper.isExpressionWithEvaluationEffect(node.expression) + || TSHelper.isExpressionWithEvaluationEffect(node.argumentExpression))) { const type = checker.getTypeAtLocation(node.expression); - if (this.isArrayType(type, checker)) { + if (TSHelper.isArrayType(type, checker)) { // Offset arrays by one const oneLit = ts.createNumericLiteral("1"); const exp = ts.createParen(node.argumentExpression); @@ -379,7 +379,7 @@ export class TSHelper { } else { return [true, node.expression, node.argumentExpression]; } - } else if (ts.isPropertyAccessExpression(node) && this.isExpressionWithEvaluationEffect(node.expression)) { + } else if (ts.isPropertyAccessExpression(node) && TSHelper.isExpressionWithEvaluationEffect(node.expression)) { return [true, node.expression, ts.createStringLiteral(node.name.text)]; } return [false, undefined, undefined]; @@ -403,7 +403,7 @@ export class TSHelper { for (const signature of signatures) { const signatureDeclaration = signature.getDeclaration(); if ((ts.isFunctionExpression(signatureDeclaration) || ts.isArrowFunction(signatureDeclaration)) - && !this.getExplicitThisParameter(signatureDeclaration)) { + && !TSHelper.getExplicitThisParameter(signatureDeclaration)) { // Function expressions: get signatures of type being assigned to, unless 'this' was explicit let declType: ts.Type; if (ts.isCallExpression(signatureDeclaration.parent)) { @@ -417,7 +417,7 @@ export class TSHelper { } } } else if (ts.isReturnStatement(signatureDeclaration.parent)) { - declType = this.getContainingFunctionReturnType(signatureDeclaration.parent, checker); + declType = TSHelper.getContainingFunctionReturnType(signatureDeclaration.parent, checker); } else { // Function expression being assigned declType = checker.getTypeAtLocation(signatureDeclaration.parent); @@ -440,7 +440,7 @@ export class TSHelper { checker: ts.TypeChecker ): ContextType { - const thisParameter = this.getExplicitThisParameter(signatureDeclaration); + const thisParameter = TSHelper.getExplicitThisParameter(signatureDeclaration); if (thisParameter) { // Explicit 'this' return thisParameter.type && thisParameter.type.kind === ts.SyntaxKind.VoidKeyword @@ -459,7 +459,8 @@ export class TSHelper { } if (ts.isBinaryExpression(signatureDeclaration.parent)) { // Function expression: check type being assigned to - return this.getFunctionContextType(checker.getTypeAtLocation(signatureDeclaration.parent.left), checker); + return TSHelper.getFunctionContextType( + checker.getTypeAtLocation(signatureDeclaration.parent.left), checker); } return ContextType.Void; } @@ -485,15 +486,16 @@ export class TSHelper { } if (type.isUnion()) { - return this.reduceContextTypes(type.types.map(t => this.getFunctionContextType(t, checker))); + return TSHelper.reduceContextTypes(type.types.map(t => TSHelper.getFunctionContextType(t, checker))); } const signatures = checker.getSignaturesOfType(type, ts.SignatureKind.Call); if (signatures.length === 0) { return ContextType.None; } - const signatureDeclarations = this.getSignatureDeclarations(signatures, checker); - return this.reduceContextTypes(signatureDeclarations.map(s => this.getDeclarationContextType(s, checker))); + const signatureDeclarations = TSHelper.getSignatureDeclarations(signatures, checker); + return TSHelper.reduceContextTypes( + signatureDeclarations.map(s => TSHelper.getDeclarationContextType(s, checker))); } public static isDefaultArrayPropertyName(methodName: string): boolean { @@ -544,7 +546,7 @@ export class TSHelper { return true; } else if (type.isUnion()) { for (const subType of type.types) { - if (this.isFalsible(subType, strictNullChecks)) { + if (TSHelper.isFalsible(subType, strictNullChecks)) { return true; } } diff --git a/test/src/util.ts b/test/src/util.ts index 34ba1458d..6aa27a716 100644 --- a/test/src/util.ts +++ b/test/src/util.ts @@ -3,7 +3,7 @@ import * as ts from "typescript"; import { Expect } from "alsatian"; -import { transpileString as compilerTranspileString } from "../../src/Compiler"; +import { transpileString as compilerTranspileString, createStringCompilerProgram } from "../../src/Compiler"; import { CompilerOptions, LuaTarget, LuaLibImportKind } from "../../src/CompilerOptions"; import {lauxlib, lua, lualib, to_jsstring, to_luastring } from "fengari"; @@ -100,35 +100,7 @@ export function transpileAndExecute( export function parseTypeScript(typescript: string, target: LuaTarget = LuaTarget.Lua53) : [ts.SourceFile, ts.TypeChecker] { - const compilerHost = { - directoryExists: () => true, - fileExists: (fileName): boolean => true, - getCanonicalFileName: fileName => fileName, - getCurrentDirectory: () => "", - getDefaultLibFileName: () => "lib.es6.d.ts", - getDirectories: () => [], - getNewLine: () => "\n", - - getSourceFile: (filename, languageVersion) => { - if (filename === "file.ts") { - return ts.createSourceFile(filename, typescript, ts.ScriptTarget.Latest, false); - } - if (filename === "lib.es6.d.ts") { - const libPath = path.join(path.dirname(require.resolve("typescript")), "lib.es6.d.ts"); - const libSource = fs.readFileSync(libPath).toString(); - return ts.createSourceFile(filename, libSource, ts.ScriptTarget.Latest, false); - } - return undefined; - }, - - readFile: () => "", - - useCaseSensitiveFileNames: () => false, - // Don't write output - writeFile: (name, text, writeByteOrderMark) => undefined, - }; - - const program = ts.createProgram(["file.ts"], { luaTarget: target }, compilerHost); + const program = createStringCompilerProgram(typescript, { luaTarget: target }); return [program.getSourceFile("file.ts"), program.getTypeChecker()]; } diff --git a/test/unit/tshelper.spec.ts b/test/unit/tshelper.spec.ts index 6a7b76273..90311a887 100644 --- a/test/unit/tshelper.spec.ts +++ b/test/unit/tshelper.spec.ts @@ -1,4 +1,4 @@ -import { Expect, Test, TestCase } from "alsatian"; +import { Expect, Test, TestCase, FocusTest } from "alsatian"; import { TSHelper as tsHelper } from "../../src/TSHelper"; import * as ts from "typescript"; @@ -41,6 +41,7 @@ export class TSHelperTests { } @Test("GetCustomDecorators single") + @FocusTest public GetCustomDecoratorsSingle(): void { const source = `/** @compileMembersOnly */ enum TestEnum { From c3e048da6fa4aa21d4dce34b90af051871d2a477 Mon Sep 17 00:00:00 2001 From: Lolleko Date: Wed, 6 Feb 2019 15:17:20 +0100 Subject: [PATCH 05/11] Fixed decorator tests and Added test-fast script --- package.json | 7 +++---- test/src/util.ts | 3 ++- test/unit/tshelper.spec.ts | 13 ++++++------- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/package.json b/package.json index 17c570f1d..46ef87d70 100644 --- a/package.json +++ b/package.json @@ -20,7 +20,8 @@ "coverage": "nyc --source-map=true npm test && nyc report --reporter=text-lcov > coverage.lcov", "coverage-html": "nyc --source-map=true npm test && nyc report --reporter=html", "test-threaded": "npm run pretest && node ./test/threaded_runner.js && npm run posttest", - "clean": "rimraf \"src/**/*.js\" \"src/**/*.js.map\" \"test/**/*.js\" \"test/**/*.js.map\"", + "test-fast": "npm run pretest && node ./test/runner.js --ignoreDiagnostics && npm run posttest", + "clean": "rimraf \"src/**/*.js\" \"src/**/*.js.map\" \"test/**/*.js\" \"test/**/*.js.map\" \"test/compiler/testfiles/*.lua\"", "release-patch": "npm version patch", "release-minor": "npm version minor", "release-major": "npm version major", @@ -52,7 +53,6 @@ "devDependencies": { "@types/glob": "^5.0.35", "@types/node": "^9.6.23", - "@types/yargs": "^11.1.1", "alsatian": "^2.3.0", "circular-json": "^0.5.5", "codecov": "3.0.2", @@ -63,7 +63,6 @@ "rimraf": "^2.6.3", "threads": "^0.12.0", "ts-node": "^7.0.0", - "tslint": "^5.10.0", - "tslint-override": "^0.1.2" + "tslint": "^5.10.0" } } diff --git a/test/src/util.ts b/test/src/util.ts index 6aa27a716..f9380a434 100644 --- a/test/src/util.ts +++ b/test/src/util.ts @@ -91,8 +91,9 @@ export function transpileAndExecute( ${tsHeader ? tsHeader : ""} function __runTest(): any {${tsStr}}`; + const ignoreDiagnosticsOverride = process.argv[2] === "--ignoreDiagnostics"; const lua = `${luaHeader ? luaHeader : ""} - ${transpileString(wrappedTsString, compilerOptions, false)} + ${transpileString(wrappedTsString, compilerOptions, ignoreDiagnosticsOverride)} return __runTest();`; return executeLua(lua); diff --git a/test/unit/tshelper.spec.ts b/test/unit/tshelper.spec.ts index 90311a887..408443d8e 100644 --- a/test/unit/tshelper.spec.ts +++ b/test/unit/tshelper.spec.ts @@ -1,4 +1,4 @@ -import { Expect, Test, TestCase, FocusTest } from "alsatian"; +import { Expect, Test, TestCase } from "alsatian"; import { TSHelper as tsHelper } from "../../src/TSHelper"; import * as ts from "typescript"; @@ -41,7 +41,6 @@ export class TSHelperTests { } @Test("GetCustomDecorators single") - @FocusTest public GetCustomDecoratorsSingle(): void { const source = `/** @compileMembersOnly */ enum TestEnum { @@ -54,7 +53,7 @@ export class TSHelperTests { const a = TestEnum.val1;`; const [sourceFile, typeChecker] = util.parseTypeScript(source); - const identifier = util.findFirstChild(sourceFile, ts.isIdentifier); + const identifier = util.findFirstChild(sourceFile, ts.isEnumDeclaration); const enumType = typeChecker.getTypeAtLocation(identifier); const decorators = tsHelper.getCustomDecorators(enumType, typeChecker); @@ -77,7 +76,7 @@ export class TSHelperTests { const a = TestEnum.val1;`; const [sourceFile, typeChecker] = util.parseTypeScript(source); - const identifier = util.findFirstChild(sourceFile, ts.isIdentifier); + const identifier = util.findFirstChild(sourceFile, ts.isEnumDeclaration); const enumType = typeChecker.getTypeAtLocation(identifier); const decorators = tsHelper.getCustomDecorators(enumType, typeChecker); @@ -100,7 +99,7 @@ export class TSHelperTests { const a = TestEnum.val1;`; const [sourceFile, typeChecker] = util.parseTypeScript(source); - const identifier = util.findFirstChild(sourceFile, ts.isIdentifier); + const identifier = util.findFirstChild(sourceFile, ts.isEnumDeclaration); const enumType = typeChecker.getTypeAtLocation(identifier); const decorators = tsHelper.getCustomDecorators(enumType, typeChecker); @@ -123,7 +122,7 @@ export class TSHelperTests { const a = TestEnum.val1;`; const [sourceFile, typeChecker] = util.parseTypeScript(source); - const identifier = util.findFirstChild(sourceFile, ts.isIdentifier); + const identifier = util.findFirstChild(sourceFile, ts.isEnumDeclaration); const enumType = typeChecker.getTypeAtLocation(identifier); const decorators = tsHelper.getCustomDecorators(enumType, typeChecker); @@ -149,7 +148,7 @@ export class TSHelperTests { const a = TestEnum.val1;`; const [sourceFile, typeChecker] = util.parseTypeScript(source); - const identifier = util.findFirstChild(sourceFile, ts.isIdentifier); + const identifier = util.findFirstChild(sourceFile, ts.isEnumDeclaration); const enumType = typeChecker.getTypeAtLocation(identifier); const decorators = tsHelper.getCustomDecorators(enumType, typeChecker); From 8da2cc25f60a53aa01d987c6e0eddde07c638884 Mon Sep 17 00:00:00 2001 From: Lolleko Date: Wed, 6 Feb 2019 15:24:21 +0100 Subject: [PATCH 06/11] Removed tslint-override from tslint config --- package-lock.json | 12 ------------ tslint.json | 3 --- 2 files changed, 15 deletions(-) diff --git a/package-lock.json b/package-lock.json index 804910d51..02b3303d3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -33,12 +33,6 @@ "integrity": "sha512-d2SJJpwkiPudEQ3+9ysANN2Nvz4QJKUPoe/WL5zyQzI0RaEeZWH5K5xjvUIGszTItHQpFPdH+u51f6G/LkS8Cg==", "dev": true }, - "@types/yargs": { - "version": "11.1.1", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-11.1.1.tgz", - "integrity": "sha512-Awgju4dqD8kHXX3jc/B/LaryJC7MsyNfnbN62lIbFzTi0GewH64zrkh4bxo/YTgVEK6r9V3GNecxMhXTJw0+jA==", - "dev": true - }, "ajv": { "version": "5.5.2", "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", @@ -3626,12 +3620,6 @@ } } }, - "tslint-override": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/tslint-override/-/tslint-override-0.1.2.tgz", - "integrity": "sha512-6Xc6KF1NtoDDzYr+quZAXnKhtsItjyw+/n2618/kyp7iY8hdAjg2Tb1UlUABbXEVCvAzjVsLsDUyET5fLohUsg==", - "dev": true - }, "tsutils": { "version": "2.28.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz", diff --git a/tslint.json b/tslint.json index 601befe6c..c321344c9 100644 --- a/tslint.json +++ b/tslint.json @@ -1,8 +1,5 @@ { "defaultSeverity": "error", - "extends": [ - "tslint-override" - ], "rules": { "array-type": [true, "array-simple"], "arrow-parens": [true, "ban-single-arg-parens"], From a1ada418c700f70f8dfd767460912f213d44539d Mon Sep 17 00:00:00 2001 From: Lolleko Date: Wed, 6 Feb 2019 16:55:46 +0100 Subject: [PATCH 07/11] Changed array type to readonly --- src/TSHelper.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TSHelper.ts b/src/TSHelper.ts index a1c879435..086dd6812 100644 --- a/src/TSHelper.ts +++ b/src/TSHelper.ts @@ -459,7 +459,7 @@ export class TSHelper { } public static getSignatureDeclarations( - signatures: ts.Signature[], + signatures: ReadonlyArray, checker: ts.TypeChecker ): ts.SignatureDeclaration[] { From 2f117495129ab5f3d7b782cd88d7ce08b54ae706 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Wed, 6 Feb 2019 23:19:55 +0100 Subject: [PATCH 08/11] Added options and their parameters to the help string --- src/CommandLineParser.ts | 16 ++++++++++++++-- src/Compiler.ts | 6 +++--- .../compiler/configuration/mixed/index.spec.ts | 6 +++--- 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/CommandLineParser.ts b/src/CommandLineParser.ts index 98a512c4f..7f42a5244 100644 --- a/src/CommandLineParser.ts +++ b/src/CommandLineParser.ts @@ -19,7 +19,7 @@ interface CLIOption extends BaseCLIOption { default: T; } -export const optionDeclarations: {[key: string]: CLIOption} = { +const optionDeclarations: {[key: string]: CLIOption} = { luaLibImport: { choices: [LuaLibImportKind.Inline, LuaLibImportKind.Require, LuaLibImportKind.Always, LuaLibImportKind.None], default: LuaLibImportKind.Inline, @@ -96,8 +96,20 @@ export function parseCommandLine(args: string[]): ParsedCommandLine export function getHelpString(): string { let result = helpString + "\n\n"; + result += "Options:\n"; + for (const optionName in optionDeclarations) { + const option = optionDeclarations[optionName]; + const parameterDescribe = option.choices + ? option.choices.join("|") + : option.type; + + const spacing = " ".repeat(Math.max(1, 45 - optionName.length - parameterDescribe.length)); + + result += `\n --${optionName} <${parameterDescribe}>${spacing}${option.describe}\n`; + } + if (examples.length > 0) { - result += "Examples:\n"; + result += "\nExamples:\n"; for (const [exampleName, example] of examples) { result += ` ${exampleName}: ${example}\n`; } diff --git a/src/Compiler.ts b/src/Compiler.ts index 3c73ded5e..8a521079b 100644 --- a/src/Compiler.ts +++ b/src/Compiler.ts @@ -2,15 +2,15 @@ import * as fs from "fs"; import * as path from "path"; import * as ts from "typescript"; -import {parseCommandLine, getHelpString} from "./CommandLineParser"; +import * as CommandLineParser from "./CommandLineParser"; import {CompilerOptions, LuaLibImportKind, LuaTarget} from "./CompilerOptions"; import {LuaTranspiler} from "./LuaTranspiler"; export function compile(argv: string[]): void { - const commandLine = parseCommandLine(argv); + const commandLine = CommandLineParser.parseCommandLine(argv); /* istanbul ignore if: tested in test/compiler/watchmode.spec with subproccess */ if (commandLine.options.help) { - console.log(getHelpString()); + console.log(CommandLineParser.getHelpString()); } else if (commandLine.options.watch) { watchWithOptions(commandLine.fileNames, commandLine.options); diff --git a/test/unit/compiler/configuration/mixed/index.spec.ts b/test/unit/compiler/configuration/mixed/index.spec.ts index f353aa99c..acefe629d 100644 --- a/test/unit/compiler/configuration/mixed/index.spec.ts +++ b/test/unit/compiler/configuration/mixed/index.spec.ts @@ -4,7 +4,7 @@ import * as path from "path"; import * as ts from "typescript"; import { CompilerOptions, LuaLibImportKind } from "../../../../../src/CompilerOptions"; -import { optionDeclarations, parseCommandLine } from "../../../../../src/CommandLineParser"; +import { parseCommandLine } from "../../../../../src/CommandLineParser"; export class MixedConfigurationTests { @@ -33,9 +33,9 @@ export class MixedConfigurationTests // Only set in tsconfig, TSTL default is "JIT" luaTarget: "5.1", // Only present in TSTL dfaults - noHeader: optionDeclarations["noHeader"].default, + noHeader: false, project: tsConfigPath, - noHoisting: optionDeclarations["noHoisting"].default, + noHoisting: false, } as CompilerOptions); } } From 1d1b2210e92f83977d0966c3440ae5fa543620d8 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Thu, 7 Feb 2019 21:53:26 +0100 Subject: [PATCH 09/11] Added function aliases and cleaned up command line parser, merged tests --- src/CommandLineParser.ts | 142 +++++++++++---- src/Compiler.ts | 24 ++- src/LuaTranspiler.ts | 1 - test/unit/cli.spec.ts | 67 ------- test/unit/commandLineParser.spec.ts | 169 ++++++++++++++++++ test/unit/compiler/commandLineParser.spec.ts | 66 ------- .../configuration/mixed/index.spec.ts | 27 +-- test/unit/string.spec.ts | 11 +- 8 files changed, 313 insertions(+), 194 deletions(-) delete mode 100644 test/unit/cli.spec.ts create mode 100644 test/unit/commandLineParser.spec.ts delete mode 100644 test/unit/compiler/commandLineParser.spec.ts diff --git a/src/CommandLineParser.ts b/src/CommandLineParser.ts index 7f42a5244..f2bba6f67 100644 --- a/src/CommandLineParser.ts +++ b/src/CommandLineParser.ts @@ -4,12 +4,18 @@ import * as ts from "typescript"; import {CompilerOptions, LuaTarget, LuaLibImportKind} from "./CompilerOptions"; +export type CLIParseResult = ParseResult; + +type ParseResult = + { isValid: true; result: T } + | { isValid: false, errorMessage: string}; + interface ParsedCommandLine extends ts.ParsedCommandLine { options: CompilerOptions; } interface BaseCLIOption { - alias: string | string[]; + aliases: string[]; describe: string; type: string; } @@ -27,7 +33,7 @@ const optionDeclarations: {[key: string]: CLIOption} = { type: "enum", } as CLIOption, luaTarget: { - alias: "lt", + aliases: ["lt"], choices: [LuaTarget.LuaJIT, LuaTarget.Lua53, LuaTarget.Lua52, LuaTarget.Lua51], default: LuaTarget.LuaJIT, describe: "Specify Lua target version.", @@ -62,21 +68,37 @@ class CLIError extends Error {} * Parse the supplied arguments. * The result will include arguments supplied via CLI and arguments from tsconfig. */ -export function parseCommandLine(args: string[]): ParsedCommandLine +export function parseCommandLine(args: string[]): CLIParseResult { let commandLine = ts.parseCommandLine(args); // Run diagnostics to check for invalid tsc options - runTsDiagnostics(commandLine); + const diagnosticsResult = runTsDiagnostics(commandLine); + if (diagnosticsResult.isValid === false) { + return diagnosticsResult; + } // This will add TS and TSTL options from a tsconfig - commandLine = readTsConfig(commandLine); + const configResult = readTsConfig(commandLine); + if (configResult.isValid === true) { + commandLine = configResult.result; + } else { + return { isValid: false, errorMessage: configResult.errorMessage }; + } // Run diagnostics to check for invalid tsconfig - runTsDiagnostics(commandLine); + const diagnosticsResult2 = runTsDiagnostics(commandLine); + if (diagnosticsResult2.isValid === false) { + return diagnosticsResult2; + } // Merge TSTL CLI options in (highest priority) will also set defaults if none specified - commandLine = parseTSTLOptions(commandLine, args); + const tstlCLIResult = parseTSTLOptions(commandLine, args); + if (tstlCLIResult.isValid === true) { + commandLine = tstlCLIResult.result; + } else { + return { isValid: false, errorMessage: tstlCLIResult.errorMessage }; + } if (commandLine.options.project && !commandLine.options.rootDir) { commandLine.options.rootDir = path.dirname(commandLine.options.project); @@ -90,7 +112,7 @@ export function parseCommandLine(args: string[]): ParsedCommandLine commandLine.options.outDir = commandLine.options.rootDir; } - return commandLine as ParsedCommandLine; + return { isValid: true, result: commandLine as ParsedCommandLine }; } export function getHelpString(): string { @@ -118,12 +140,19 @@ export function getHelpString(): string { return result; } -function readTsConfig(parsedCommandLine: ts.ParsedCommandLine): ts.ParsedCommandLine { +function readTsConfig(parsedCommandLine: ts.ParsedCommandLine): CLIParseResult +{ const options = parsedCommandLine.options; // Load config if (options.project) { - findConfigFile(options); + const findProjectPathResult = findConfigFile(options); + if (findProjectPathResult.isValid === true) { + options.project = findProjectPathResult.result; + } else { + return { isValid: false, errorMessage: findProjectPathResult.errorMessage }; + } + const configPath = options.project; const configContents = fs.readFileSync(configPath).toString(); const configJson = ts.parseConfigFileTextToJson(configPath, configContents); @@ -137,40 +166,57 @@ function readTsConfig(parsedCommandLine: ts.ParsedCommandLine): ts.ParsedCommand for (const key in parsedJsonConfig.raw) { const option = optionDeclarations[key]; if (option !== undefined) { - const value = readValue(parsedJsonConfig.raw[key], option.type); + const value = readValue(parsedJsonConfig.raw[key], option.type, key); if (option.choices) { if (option.choices.indexOf(value) < 0) { - throw new CLIError(`Unknown ${key} value '${value}.\n'` - + `Accepted values: ${option.choices}`); + return { + isValid: false, + errorMessage: `Unknown ${key} value '${value}'.\nAccepted values: ${option.choices}`, + }; } } parsedJsonConfig.options[key] = value; } } - return parsedJsonConfig; + return { isValid: true, result: parsedJsonConfig }; } - return parsedCommandLine; + return { isValid: true, result: parsedCommandLine }; } -function parseTSTLOptions(commandLine: ts.ParsedCommandLine, args: string[]): ts.ParsedCommandLine { +function parseTSTLOptions(commandLine: ts.ParsedCommandLine, args: string[]): CLIParseResult { const result = {}; for (let i = 0; i < args.length; i++) { if (args[i].startsWith("--")) { const argumentName = args[i].substr(2); const option = optionDeclarations[argumentName]; if (option) { - const value = readValue(args[i + 1], option.type); + const argumentResult = getArgumentValue(argumentName, args[i + 1]); i++; // Skip the value from being considered as argument name - - if (option.choices) { - if (option.choices.indexOf(value) < 0) { - throw new CLIError(`Unknown ${argumentName} value '${value}.\n'` - + `Accepted values: ${option.choices}`); - } + if (argumentResult.isValid === true) { + result[argumentName] = argumentResult.result; + } else { + return { isValid: false, errorMessage: argumentResult.errorMessage }; + } + } + } else if (args[i].startsWith("-")) { + const argument = args[i].substr(1); + let argumentName: string; + for (const key in optionDeclarations) { + if (optionDeclarations[key].aliases && optionDeclarations[key].aliases.indexOf(argument) >= 0) { + argumentName = key; + break; } + } - result[argumentName] = value; + if (argumentName) { + const argumentResult = getArgumentValue(argumentName, args[i + 1]); + i++; // Skip the value from being considered as argument name + if (argumentResult.isValid === true) { + result[argumentName] = argumentResult.result; + } else { + return { isValid: false, errorMessage: argumentResult.errorMessage }; + } } } } @@ -184,10 +230,31 @@ function parseTSTLOptions(commandLine: ts.ParsedCommandLine, args: string[]): ts commandLine.options[option] = defaultOptions[option]; } } - return commandLine; + return { isValid: true, result: commandLine }; } -function readValue(valueString: string, valueType: string): any { +function getArgumentValue(argumentName: string, argument: string): ParseResult +{ + if (argument === undefined) { + return { isValid: false, errorMessage: `Missing value for parameter ${argumentName}`}; + } + + const option = optionDeclarations[argumentName]; + const value = readValue(argument, option.type, argumentName); + + if (option.choices) { + if (option.choices.indexOf(value) < 0) { + return { + isValid: false, + errorMessage: `Unknown ${argumentName} value '${value}'. Accepted values are: ${option.choices}`, + }; + } + } + + return { isValid: true, result: value }; +} + +function readValue(valueString: string, valueType: string, parameterName: string): string | boolean { if (valueType === "boolean") { return valueString === "true" || valueString === "t" ? true @@ -212,7 +279,7 @@ function getDefaultOptions(): CompilerOptions { } /** Check the current state of the ParsedCommandLine for errors */ -function runTsDiagnostics(commandLine: ts.ParsedCommandLine): void { +function runTsDiagnostics(commandLine: ts.ParsedCommandLine): ParseResult { // Remove files that dont exist commandLine.fileNames = commandLine.fileNames.filter(file => fs.existsSync(file) || fs.existsSync(file + ".ts")); @@ -222,7 +289,7 @@ function runTsDiagnostics(commandLine: ts.ParsedCommandLine): void { const optionNames: string[] = []; for (const key of Object.keys(optionDeclarations)) { optionNames.push(key); - const alias = optionDeclarations[key].alias; + const alias = optionDeclarations[key].aliases; if (alias) { if (typeof alias === "string") { optionNames.push(alias); @@ -232,27 +299,31 @@ function runTsDiagnostics(commandLine: ts.ParsedCommandLine): void { } } - commandLine.errors.forEach(err => { - let ignore = false; + for (const err of commandLine.errors) { // Ignore errors caused by tstl specific compiler options if (err.code === tsInvalidCompilerOptionErrorCode) { + let ignore = false; for (const optionName of optionNames) { if (err.messageText.toString().indexOf(optionName) !== -1) { ignore = true; + break; } } + if (!ignore) { - throw new CLIError(`error TS${err.code}: ${err.messageText}`); + return { isValid: false, errorMessage: `error TS${err.code}: ${err.messageText}`}; } } - }); + } } + + return { isValid: true, result: true }; } /** Find configFile, function from ts api seems to be broken? */ -export function findConfigFile(options: ts.CompilerOptions): void { +export function findConfigFile(options: ts.CompilerOptions): ParseResult { if (!options.project) { - throw new CLIError(`error no base path provided, could not find config.`); + return { isValid: false, errorMessage: `error no base path provided, could not find config.`}; } let configPath = options.project; // If the project path is wrapped in double quotes, remove them @@ -279,5 +350,6 @@ export function findConfigFile(options: ts.CompilerOptions): void { } } } - options.project = configPath; + + return { isValid: true, result: configPath }; } diff --git a/src/Compiler.ts b/src/Compiler.ts index 8a521079b..388b92a26 100644 --- a/src/Compiler.ts +++ b/src/Compiler.ts @@ -7,15 +7,23 @@ import {CompilerOptions, LuaLibImportKind, LuaTarget} from "./CompilerOptions"; import {LuaTranspiler} from "./LuaTranspiler"; export function compile(argv: string[]): void { - const commandLine = CommandLineParser.parseCommandLine(argv); - /* istanbul ignore if: tested in test/compiler/watchmode.spec with subproccess */ - if (commandLine.options.help) { - console.log(CommandLineParser.getHelpString()); - } - else if (commandLine.options.watch) { - watchWithOptions(commandLine.fileNames, commandLine.options); + const parseResult = CommandLineParser.parseCommandLine(argv); + + if (parseResult.isValid === true) { + + if (parseResult.result.options.help) { + console.log(CommandLineParser.getHelpString()); + return; + } + + /* istanbul ignore if: tested in test/compiler/watchmode.spec with subproccess */ + if (parseResult.result.options.watch) { + watchWithOptions(parseResult.result.fileNames, parseResult.result.options); + } else { + compileFilesWithOptions(parseResult.result.fileNames, parseResult.result.options); + } } else { - compileFilesWithOptions(commandLine.fileNames, commandLine.options); + console.error(`Invalid CLI input: ${parseResult.errorMessage}`); } } diff --git a/src/LuaTranspiler.ts b/src/LuaTranspiler.ts index fff44a26d..a4a4416a2 100644 --- a/src/LuaTranspiler.ts +++ b/src/LuaTranspiler.ts @@ -119,7 +119,6 @@ export class LuaTranspiler { // Graciously handle transpilation errors console.error("Encountered error parsing file: " + exception.message); console.error(`${sourceFile.fileName} (${1 + pos.line},${pos.character})\n${exception.stack}`); - process.exit(1); } else { throw exception; } diff --git a/test/unit/cli.spec.ts b/test/unit/cli.spec.ts deleted file mode 100644 index a8ad0ae3d..000000000 --- a/test/unit/cli.spec.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { Expect, Test, TestCase } from "alsatian"; - -import { findConfigFile, parseCommandLine } from "../../src/CommandLineParser"; -import { LuaTarget } from "../../src/CompilerOptions"; - -export class CLITests { - - @Test("defaultOption") - @TestCase("luaTarget", LuaTarget.LuaJIT) - @TestCase("noHeader", false) - @TestCase("luaLibImport", "inline") - @TestCase("rootDir", process.cwd()) - @TestCase("outDir", process.cwd()) - public defaultOptions(option: any, expected: any): void { - const parsedCommandLine = parseCommandLine([]); - - Expect(expected).toBe(parsedCommandLine.options[option]); - } - - @Test("ValidLuaTarget") - public validLuaTarget(): void { - const parsedCommandLine = parseCommandLine(['--luaTarget', '5.3']); - Expect(parsedCommandLine.options["luaTarget"]).toBe("5.3"); - } - - @Test("InvalidLuaTarget") - public invalidLuaTarget(): void { - // Don't check error message because the yargs library messes the message up. - Expect(() => parseCommandLine(['--luaTarget', '42'])).toThrow(); - } - - @Test("InvalidArgumentTSTL") - public invalidArgument(): void { - // Don't check error message because the yargs library messes the message up. - Expect(() => parseCommandLine(['--invalidTarget', 'test'])).toThrow(); - } - - @Test("outDir") - public outDir(): void { - const parsedCommandLine = parseCommandLine(['--outDir', './test']); - - Expect(parsedCommandLine.options['outDir']).toBe('./test'); - } - - @Test("rootDir") - public rootDir(): void { - const parsedCommandLine = parseCommandLine(['--rootDir', './test']); - - Expect(parsedCommandLine.options['rootDir']).toBe('./test'); - Expect(parsedCommandLine.options['outDir']).toBe('./test'); - } - - @Test("outDirAndRooDir") - public outDirAndRooDir(): void { - const parsedCommandLine = parseCommandLine(['--outDir', './testOut', '--rootDir', './testRoot']); - - Expect(parsedCommandLine.options['outDir']).toBe('./testOut'); - Expect(parsedCommandLine.options['rootDir']).toBe('./testRoot'); - } - - @Test("Find config no path") - public findConfigNoPath(): void { - Expect(() => findConfigFile({options: {}, fileNames: [], errors: []})).toThrow(); - - } - -} diff --git a/test/unit/commandLineParser.spec.ts b/test/unit/commandLineParser.spec.ts new file mode 100644 index 000000000..c8070f98b --- /dev/null +++ b/test/unit/commandLineParser.spec.ts @@ -0,0 +1,169 @@ +import { Expect, Test, TestCase } from "alsatian"; + +import { findConfigFile, parseCommandLine } from "../../src/CommandLineParser"; +import { LuaTarget, LuaLibImportKind } from "../../src/CompilerOptions"; + +export class CommandLineParserTests +{ + @TestCase([""], LuaLibImportKind.Inline) + @TestCase(["--luaLibImport", "none"], LuaLibImportKind.None) + @TestCase(["--luaLibImport", "always"], LuaLibImportKind.Always) + @TestCase(["--luaLibImport", "inline"], LuaLibImportKind.Inline) + @TestCase(["--luaLibImport", "require"], LuaLibImportKind.Require) + @Test("CLI parser luaLibImportKind") + public cliParserLuaLibImportKind(args: string[], expected: LuaLibImportKind): void { + const result = parseCommandLine(args); + if (result.isValid === true) { + Expect(result.result.options.luaLibImport).toBe(expected); + } else { + Expect(result.isValid).toBeTruthy(); + } + } + + @Test("CLI parser invalid luaLibImportKind") + public cliParserInvalidLuaLibImportKind(): void { + const result = parseCommandLine(["--luaLibImport", "invalid"]); + Expect(result.isValid).toBe(false); + } + + @TestCase([""], LuaTarget.LuaJIT) + @TestCase(["--luaTarget", "5.1"], LuaTarget.Lua51) + @TestCase(["--luaTarget", "5.2"], LuaTarget.Lua52) + @TestCase(["--luaTarget", "jit"], LuaTarget.LuaJIT) + @TestCase(["--luaTarget", "JIT"], LuaTarget.LuaJIT) + @TestCase(["--luaTarget", "5.3"], LuaTarget.Lua53) + @Test("CLI parser luaTarget") + public cliParserLuaTarget(args: string[], expected: LuaTarget): void { + const result = parseCommandLine(args); + if (result.isValid === true) { + Expect(result.result.options.luaTarget).toBe(expected); + } else { + Expect(result.isValid).toBeTruthy(); + } + } + + @Test("CLI parser invalid luaTarget") + public cliParserInvalidLuaTarget(): void { + const result = parseCommandLine(["--luatTarget", "invalid"]); + Expect(result.isValid).toBe(false); + } + + @TestCase([""], false) + @TestCase(["--noHeader", "true"], true) + @TestCase(["--noHeader", "false"], false) + @Test("CLI parser noHeader") + public cliParserNoHeader(args: string[], expected: boolean): void { + const result = parseCommandLine(args); + if (result.isValid === true) { + Expect(result.result.options.noHeader).toBe(expected); + } else { + Expect(result.isValid).toBeTruthy(); + } + } + + @TestCase([""], false) + @TestCase(["--project", "tsconfig.json"], true) + @TestCase(["-p", "tsconfig.json"], true) + @Test("CLI parser project") + public cliParserProject(args: string[], expected: boolean): void { + const result = parseCommandLine(args); + if (result.isValid === true) { + Expect(result.result.options.project !== undefined).toBe(expected); + } else { + Expect(result.isValid).toBeTruthy(); + } + } + + @TestCase([""], false) + @TestCase(["--help"], true) + @TestCase(["-h"], true) + @Test("CLI parser project") + public cliParserHelp(args: string[], expected: boolean): void { + const result = parseCommandLine(args); + if (result.isValid === true) { + Expect(result.result.options.help === true).toBe(expected); + } else { + Expect(result.isValid).toBeTruthy(); + } + } + + @Test("defaultOption") + @TestCase("luaTarget", LuaTarget.LuaJIT) + @TestCase("noHeader", false) + @TestCase("luaLibImport", "inline") + @TestCase("rootDir", process.cwd()) + @TestCase("outDir", process.cwd()) + public defaultOptions(option: any, expected: any): void { + const parsedCommandLine = parseCommandLine([]); + if (parsedCommandLine.isValid) { + Expect(expected).toBe(parsedCommandLine.result.options[option]); + } else { + Expect(parsedCommandLine.isValid).toBeTruthy(); + } + } + + @Test("ValidLuaTarget") + public validLuaTarget(): void { + const parsedCommandLine = parseCommandLine(['--luaTarget', '5.3']); + if (parsedCommandLine.isValid) { + Expect(parsedCommandLine.result.options["luaTarget"]).toBe("5.3"); + } else { + Expect(parsedCommandLine.isValid).toBeTruthy(); + } + } + + @Test("InvalidLuaTarget") + public invalidLuaTarget(): void { + // Don't check error message because the yargs library messes the message up. + const result = parseCommandLine(['--luaTarget', '42']); + Expect(result.isValid).toBe(false); + } + + @Test("InvalidArgumentTSTL") + public invalidArgument(): void { + // Don't check error message because the yargs library messes the message up. + const result = parseCommandLine(['--invalidTarget', 'test']); + Expect(result.isValid).toBe(false); + } + + @Test("outDir") + public outDir(): void { + const parsedCommandLine = parseCommandLine(['--outDir', './test']); + + if (parsedCommandLine.isValid) { + Expect(parsedCommandLine.result.options['outDir']).toBe('./test'); + } else { + Expect(parsedCommandLine.isValid).toBeTruthy(); + } + } + + @Test("rootDir") + public rootDir(): void { + const parsedCommandLine = parseCommandLine(['--rootDir', './test']); + + if (parsedCommandLine.isValid) { + Expect(parsedCommandLine.result.options['rootDir']).toBe('./test'); + Expect(parsedCommandLine.result.options['outDir']).toBe('./test'); + } else { + Expect(parsedCommandLine.isValid).toBeTruthy(); + } + } + + @Test("outDirAndRooDir") + public outDirAndRooDir(): void { + const parsedCommandLine = parseCommandLine(['--outDir', './testOut', '--rootDir', './testRoot']); + + if (parsedCommandLine.isValid) { + Expect(parsedCommandLine.result.options['outDir']).toBe('./testOut'); + Expect(parsedCommandLine.result.options['rootDir']).toBe('./testRoot'); + } else { + Expect(parsedCommandLine.isValid).toBeTruthy(); + } + } + + @Test("Find config no path") + public findConfigNoPath(): void { + const result = findConfigFile({ options: {}, fileNames: [], errors: [] }); + Expect(result.isValid).toBe(false); + } +} diff --git a/test/unit/compiler/commandLineParser.spec.ts b/test/unit/compiler/commandLineParser.spec.ts deleted file mode 100644 index 7b71da888..000000000 --- a/test/unit/compiler/commandLineParser.spec.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { Expect, Test, TestCase } from "alsatian"; -import { parseCommandLine } from "../../../src/CommandLineParser"; -import { LuaLibImportKind, LuaTarget } from "../../../src/CompilerOptions"; - -export class CommandLineParserTests -{ - @TestCase([""], LuaLibImportKind.Inline) - @TestCase(["--luaLibImport", "none"], LuaLibImportKind.None) - @TestCase(["--luaLibImport", "always"], LuaLibImportKind.Always) - @TestCase(["--luaLibImport", "inline"], LuaLibImportKind.Inline) - @TestCase(["--luaLibImport", "require"], LuaLibImportKind.Require) - @Test("CLI parser luaLibImportKind") - public cliParserLuaLibImportKind(args: string[], expected: LuaLibImportKind): void { - const result = parseCommandLine(args); - Expect(result.options.luaLibImport).toBe(expected); - } - - @Test("CLI parser invalid luaLibImportKind") - public cliParserInvalidLuaLibImportKind(): void { - Expect(() => parseCommandLine(["--luaLibImport", "invalid"])).toThrow(); - } - - @TestCase([""], LuaTarget.LuaJIT) - @TestCase(["--luaTarget", "5.1"], LuaTarget.Lua51) - @TestCase(["--luaTarget", "5.2"], LuaTarget.Lua52) - @TestCase(["--luaTarget", "jit"], LuaTarget.LuaJIT) - @TestCase(["--luaTarget", "JIT"], LuaTarget.LuaJIT) - @TestCase(["--luaTarget", "5.3"], LuaTarget.Lua53) - @Test("CLI parser luaTarget") - public cliParserLuaTarget(args: string[], expected: LuaTarget): void { - const result = parseCommandLine(args); - Expect(result.options.luaTarget).toBe(expected); - } - - @Test("CLI parser invalid luaTarget") - public cliParserInvalidLuaTarget(): void { - Expect(() => parseCommandLine(["--luatTarget", "invalid"])).toThrow(); - } - - @TestCase([""], false) - @TestCase(["--noHeader", "true"], true) - @TestCase(["--noHeader", "false"], false) - @Test("CLI parser noHeader") - public cliParserNoHeader(args: string[], expected: boolean): void { - const result = parseCommandLine(args); - Expect(result.options.noHeader).toBe(expected); - } - - @TestCase([""], false) - @TestCase(["--project", "tsconfig.json"], true) - @TestCase(["-p", "tsconfig.json"], true) - @Test("CLI parser project") - public cliParserProject(args: string[], expected: boolean): void { - const result = parseCommandLine(args); - Expect(result.options.project !== undefined).toBe(expected); - } - - @TestCase([""], false) - @TestCase(["--help"], true) - @TestCase(["-h"], true) - @Test("CLI parser project") - public cliParserHelp(args: string[], expected: boolean): void { - const result = parseCommandLine(args); - Expect(result.options.help === true).toBe(expected); - } -} \ No newline at end of file diff --git a/test/unit/compiler/configuration/mixed/index.spec.ts b/test/unit/compiler/configuration/mixed/index.spec.ts index acefe629d..179a8ac77 100644 --- a/test/unit/compiler/configuration/mixed/index.spec.ts +++ b/test/unit/compiler/configuration/mixed/index.spec.ts @@ -26,16 +26,21 @@ export class MixedConfigurationTests `${path.join(rootPath, "test.ts")}`, ]); - Expect(parsedArgs.options).toEqual({ - ...expectedTsConfig.options, - // Overridden by cmd args (set to "none" in project-tsconfig.json) - luaLibImport: LuaLibImportKind.Inline, - // Only set in tsconfig, TSTL default is "JIT" - luaTarget: "5.1", - // Only present in TSTL dfaults - noHeader: false, - project: tsConfigPath, - noHoisting: false, - } as CompilerOptions); + if (parsedArgs.isValid === true) + { + Expect(parsedArgs.result.options).toEqual({ + ...expectedTsConfig.options, + // Overridden by cmd args (set to "none" in project-tsconfig.json) + luaLibImport: LuaLibImportKind.Inline, + // Only set in tsconfig, TSTL default is "JIT" + luaTarget: "5.1", + // Only present in TSTL dfaults + noHeader: false, + project: tsConfigPath, + noHoisting: false, + } as CompilerOptions); + } else { + Expect(parsedArgs.isValid).toBeTruthy(); + } } } diff --git a/test/unit/string.spec.ts b/test/unit/string.spec.ts index 6b838c4ca..b3326ec78 100644 --- a/test/unit/string.spec.ts +++ b/test/unit/string.spec.ts @@ -88,12 +88,11 @@ export class StringTests @Test("string.concatFct") public concatFct(str: string, param: string[]): void { const paramStr = param.map(elem => `"${elem}"`).join(", "); - console.log(`return "${str}".concat(${paramStr})`); - const result = util.transpileAndExecute( - `return "${str}".concat(${paramStr})` - ); - // Assert - Expect(result).toBe(str.concat(...param)); + const result = util.transpileAndExecute( + `return "${str}".concat(${paramStr})` + ); + // Assert + Expect(result).toBe(str.concat(...param)); } @TestCase("hello test", "") @TestCase("hello test", "t") From d9a7c115abb6a01bbcceea894042fd7ea93f2a0f Mon Sep 17 00:00:00 2001 From: Perryvw Date: Thu, 7 Feb 2019 21:56:25 +0100 Subject: [PATCH 10/11] Added test for luaTarget alias --- test/unit/commandLineParser.spec.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/test/unit/commandLineParser.spec.ts b/test/unit/commandLineParser.spec.ts index c8070f98b..feec98ad4 100644 --- a/test/unit/commandLineParser.spec.ts +++ b/test/unit/commandLineParser.spec.ts @@ -42,6 +42,21 @@ export class CommandLineParserTests } } + @TestCase(["-lt", "5.1"], LuaTarget.Lua51) + @TestCase(["-lt", "5.2"], LuaTarget.Lua52) + @TestCase(["-lt", "jit"], LuaTarget.LuaJIT) + @TestCase(["-lt", "JIT"], LuaTarget.LuaJIT) + @TestCase(["-lt", "5.3"], LuaTarget.Lua53) + @Test("CLI parser luaTarget") + public cliParserLuaTargetAlias(args: string[], expected: LuaTarget): void { + const result = parseCommandLine(args); + if (result.isValid === true) { + Expect(result.result.options.luaTarget).toBe(expected); + } else { + Expect(result.isValid).toBeTruthy(); + } + } + @Test("CLI parser invalid luaTarget") public cliParserInvalidLuaTarget(): void { const result = parseCommandLine(["--luatTarget", "invalid"]); From c16831e7669f8750e23253751f6ff2a479ca6e79 Mon Sep 17 00:00:00 2001 From: Perryvw Date: Fri, 8 Feb 2019 20:22:17 +0100 Subject: [PATCH 11/11] Refactored help string a bit, added support for -v --- src/CommandLineParser.ts | 33 ++++++++++++++--------------- src/Compiler.ts | 5 +++++ test/unit/commandLineParser.spec.ts | 13 ++++++++++++ 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/src/CommandLineParser.ts b/src/CommandLineParser.ts index f2bba6f67..4d631a501 100644 --- a/src/CommandLineParser.ts +++ b/src/CommandLineParser.ts @@ -51,19 +51,19 @@ const optionDeclarations: {[key: string]: CLIOption} = { } as CLIOption, }; +export const { version } = require("../package.json"); + const helpString = - "Syntax: tstl [options] [files...]\n\n" + + `Version ${version}\n` + + "Syntax: tstl [options] [files...]\n\n" + + + "Examples: tstl path/to/file.ts [...]\n" + + " tstl -p path/to/tsconfig.json\n\n" + + "In addition to the options listed below you can also pass options\n" + "for the typescript compiler (For a list of options use tsc -h).\n" + "Some tsc options might have no effect."; -const examples = [ - ["Compile files", "tstl path/to/file.ts [...]"], - ["Compile project", "tstl -p path/to/tsconfig.json"], -]; - -class CLIError extends Error {} - /** * Parse the supplied arguments. * The result will include arguments supplied via CLI and arguments from tsconfig. @@ -121,20 +121,19 @@ export function getHelpString(): string { result += "Options:\n"; for (const optionName in optionDeclarations) { const option = optionDeclarations[optionName]; + const aliasStrings = option.aliases + ? option.aliases.map(a => "-" + a) + : []; + + const optionString = aliasStrings.concat(["--" + optionName]).join("|"); + const parameterDescribe = option.choices ? option.choices.join("|") : option.type; - const spacing = " ".repeat(Math.max(1, 45 - optionName.length - parameterDescribe.length)); + const spacing = " ".repeat(Math.max(1, 45 - optionString.length - parameterDescribe.length)); - result += `\n --${optionName} <${parameterDescribe}>${spacing}${option.describe}\n`; - } - - if (examples.length > 0) { - result += "\nExamples:\n"; - for (const [exampleName, example] of examples) { - result += ` ${exampleName}: ${example}\n`; - } + result += `\n ${optionString} <${parameterDescribe}>${spacing}${option.describe}\n`; } return result; diff --git a/src/Compiler.ts b/src/Compiler.ts index 388b92a26..b23b83683 100644 --- a/src/Compiler.ts +++ b/src/Compiler.ts @@ -16,6 +16,11 @@ export function compile(argv: string[]): void { return; } + if (parseResult.result.options.version) { + console.log(CommandLineParser.version); + return; + } + /* istanbul ignore if: tested in test/compiler/watchmode.spec with subproccess */ if (parseResult.result.options.watch) { watchWithOptions(parseResult.result.fileNames, parseResult.result.options); diff --git a/test/unit/commandLineParser.spec.ts b/test/unit/commandLineParser.spec.ts index feec98ad4..3b9bceacd 100644 --- a/test/unit/commandLineParser.spec.ts +++ b/test/unit/commandLineParser.spec.ts @@ -102,6 +102,19 @@ export class CommandLineParserTests } } + @TestCase([""], false) + @TestCase(["--version"], true) + @TestCase(["-v"], true) + @Test("CLI parser project") + public cliParserVersion(args: string[], expected: boolean): void { + const result = parseCommandLine(args); + if (result.isValid === true) { + Expect(result.result.options.version === true).toBe(expected); + } else { + Expect(result.isValid).toBeTruthy(); + } + } + @Test("defaultOption") @TestCase("luaTarget", LuaTarget.LuaJIT) @TestCase("noHeader", false)