From 12b99c39fd478a5e57f5ae34ee0f072615862c0e Mon Sep 17 00:00:00 2001 From: Will Temple Date: Wed, 19 Feb 2020 17:07:13 -0800 Subject: [PATCH 01/24] Azure SDK dev-tool first pass --- common/config/rush/pnpm-lock.yaml | 741 +++++++++--------- common/tools/dev-tool/launch.js | 14 + common/tools/dev-tool/package.json | 43 + .../dev-tool/src/commands/prep-samples.ts | 138 ++++ .../dev-tool/src/commands/run-samples.ts | 69 ++ common/tools/dev-tool/src/index.ts | 36 + .../dev-tool/src/util/findMatchingFiles.ts | 79 ++ common/tools/dev-tool/src/util/printer.ts | 48 ++ common/tools/dev-tool/tsconfig.json | 19 + rush.json | 5 + .../ai-text-analytics/package.json | 7 +- 11 files changed, 815 insertions(+), 384 deletions(-) create mode 100755 common/tools/dev-tool/launch.js create mode 100644 common/tools/dev-tool/package.json create mode 100644 common/tools/dev-tool/src/commands/prep-samples.ts create mode 100644 common/tools/dev-tool/src/commands/run-samples.ts create mode 100644 common/tools/dev-tool/src/index.ts create mode 100644 common/tools/dev-tool/src/util/findMatchingFiles.ts create mode 100644 common/tools/dev-tool/src/util/printer.ts create mode 100644 common/tools/dev-tool/tsconfig.json diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 3ef9835801a3..8bb0f9693e42 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -11,6 +11,7 @@ dependencies: '@rush-temp/core-paging': 'file:projects/core-paging.tgz' '@rush-temp/core-tracing': 'file:projects/core-tracing.tgz' '@rush-temp/cosmos': 'file:projects/cosmos.tgz' + '@rush-temp/dev-tool': 'file:projects/dev-tool.tgz' '@rush-temp/eslint-plugin-azure-sdk': 'file:projects/eslint-plugin-azure-sdk.tgz' '@rush-temp/event-hubs': 'file:projects/event-hubs.tgz' '@rush-temp/event-processor-host': 'file:projects/event-processor-host.tgz' @@ -49,7 +50,7 @@ packages: is-buffer: 2.0.4 jssha: 2.3.1 process: 0.11.10 - rhea: 1.0.19 + rhea: 1.0.20 rhea-promise: 0.1.15 stream-browserify: 2.0.2 tslib: 1.11.1 @@ -103,9 +104,9 @@ packages: dev: false resolution: integrity: sha512-pkFCw6OiJrpR+aH1VQe6DYm3fK2KWCC5Jf3m/Pv1RxF08M1Xm08RCyQ5Qe0YyW5L16yYT2nnV48krVhYZ6SGFA== - /@azure/eslint-plugin-azure-sdk/2.0.1_ee45056a3a53530214d828c032d67185: + /@azure/eslint-plugin-azure-sdk/2.0.1_6eb4f22a85ba1817e9c23ce411f002c2: dependencies: - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 eslint: 6.8.0 fast-levenshtein: 2.0.6 glob: 7.1.6 @@ -190,7 +191,7 @@ packages: dev: false resolution: integrity: sha512-a9gxpmdXtZEInkCSHUJDLHZVBgb1QS0jhss4cPP93EW7s+uC5bikET2twEF3KV+7rDblJcmNvTR7VJejqd2C2g== - /@babel/generator/7.8.7: + /@babel/generator/7.8.8: dependencies: '@babel/types': 7.8.7 jsesc: 2.5.2 @@ -198,7 +199,7 @@ packages: source-map: 0.5.7 dev: false resolution: - integrity: sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew== + integrity: sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg== /@babel/helper-function-name/7.8.3: dependencies: '@babel/helper-get-function-arity': 7.8.3 @@ -227,17 +228,17 @@ packages: dev: false resolution: integrity: sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg== - /@babel/parser/7.8.7: + /@babel/parser/7.8.8: dev: false engines: node: '>=6.0.0' hasBin: true resolution: - integrity: sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A== + integrity: sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA== /@babel/template/7.8.6: dependencies: '@babel/code-frame': 7.8.3 - '@babel/parser': 7.8.7 + '@babel/parser': 7.8.8 '@babel/types': 7.8.7 dev: false resolution: @@ -245,10 +246,10 @@ packages: /@babel/traverse/7.8.6: dependencies: '@babel/code-frame': 7.8.3 - '@babel/generator': 7.8.7 + '@babel/generator': 7.8.8 '@babel/helper-function-name': 7.8.3 '@babel/helper-split-export-declaration': 7.8.3 - '@babel/parser': 7.8.7 + '@babel/parser': 7.8.8 '@babel/types': 7.8.7 debug: 4.1.1 globals: 11.12.0 @@ -273,19 +274,19 @@ packages: node: '>=6' resolution: integrity: sha512-YqW3hPS0RXriqjcCrLOTJj+LWe3c8JpwlL83k1ka1Q8U05ZjAKbGQZYeTzUd0NFEnnfPtsUiKGpFEBJG6kFuvg== - /@microsoft/api-extractor-model/7.7.7: + /@microsoft/api-extractor-model/7.7.9: dependencies: - '@microsoft/node-core-library': 3.19.3 '@microsoft/tsdoc': 0.12.14 + '@rushstack/node-core-library': 3.19.5 dev: false resolution: - integrity: sha512-822kyHMEx2sl+KnBioEiFoTIXuz/4pYBo94nQ4AMqb9BFvY9I1AZUPtC4HFh2zcXQqpFLpKKC55s/o8UOze2wQ== - /@microsoft/api-extractor/7.7.8: + integrity: sha512-WPN99HNlOYz9+pPC+tk7yoa2h1ueEVuYNhcw/5t9rQSHgHbxW6QVM8QPn/caGJ9pHVDzB8+0ySNX+1IZCpidBw== + /@microsoft/api-extractor/7.7.10: dependencies: - '@microsoft/api-extractor-model': 7.7.7 - '@microsoft/node-core-library': 3.19.3 - '@microsoft/ts-command-line': 4.3.10 + '@microsoft/api-extractor-model': 7.7.9 '@microsoft/tsdoc': 0.12.14 + '@rushstack/node-core-library': 3.19.5 + '@rushstack/ts-command-line': 4.3.12 colors: 1.2.5 lodash: 4.17.15 resolve: 1.8.1 @@ -294,27 +295,7 @@ packages: dev: false hasBin: true resolution: - integrity: sha512-XNO6Dk6ByfJq24Cn1/j0B0F16ZtwYnEC/sxgB/M0wTphBdBlHjRXZmxofmjirBBj9f7vG4UJ18IOIZRLbhGFPw== - /@microsoft/node-core-library/3.19.3: - dependencies: - '@types/node': 10.17.13 - colors: 1.2.5 - fs-extra: 7.0.1 - jju: 1.4.0 - semver: 5.3.0 - timsort: 0.3.0 - z-schema: 3.18.4 - dev: false - resolution: - integrity: sha512-rJ+hT6+XK5AESbhn31YBnHKpZSFKCmqHCRZyK9+jyWwav1HXv0qzuXnFvnyrO0MZyJ6rH0seWOZVWbU5KGv1tg== - /@microsoft/ts-command-line/4.3.10: - dependencies: - '@types/argparse': 1.0.33 - argparse: 1.0.10 - colors: 1.2.5 - dev: false - resolution: - integrity: sha512-AgxArGqPt0H5WTo3fxNFP3Blm3obkCCopVG9kwIo+/mMdXaj6qMDn6+8Bv8+5Nke3CvvXpKAZtu3IaGY5cV1Hg== + integrity: sha512-NT31W07KoPwIUrrLnASqHp+/cX6DSUMxYCy/v/qM6GEWRbsyWBUT39kOIM6Fl6DS5VFpOroTgL9oGz8blraYsA== /@microsoft/tsdoc/0.12.14: dev: false resolution: @@ -412,6 +393,26 @@ packages: rollup: ^1.20.0 resolution: integrity: sha512-rYGeAc4sxcZ+kPG/Tw4/fwJODC3IXHYDH4qusdN/b6aLw5LPUbzpecYbEJh4sVQGPFJxd2dBU4kc1H3oy9/bnw== + /@rushstack/node-core-library/3.19.5: + dependencies: + '@types/node': 10.17.13 + colors: 1.2.5 + fs-extra: 7.0.1 + jju: 1.4.0 + semver: 5.3.0 + timsort: 0.3.0 + z-schema: 3.18.4 + dev: false + resolution: + integrity: sha512-dWnA5n7RZNFhW0XeovTSA+K86dy6Nt+kqwxnW1hBZnreIm6vILAI4noOLF1N3bu5De6GC7QIwmBIu2L4MaxZrw== + /@rushstack/ts-command-line/4.3.12: + dependencies: + '@types/argparse': 1.0.33 + argparse: 1.0.10 + colors: 1.2.5 + dev: false + resolution: + integrity: sha512-x+AHiWVy0Wx6OSFlzHmXkm2Z23kn4OVMd8/57hZpzeAn1iAIILGFW1L7uWQaAhnt+mZCFqgwRpggQbPQuZOT7w== /@sinonjs/commons/1.7.1: dependencies: type-detect: 4.0.8 @@ -452,33 +453,40 @@ packages: /@types/body-parser/1.19.0: dependencies: '@types/connect': 3.4.33 - '@types/node': 13.9.0 + '@types/node': 13.9.1 dev: false resolution: integrity: sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ== /@types/chai-as-promised/7.1.2: dependencies: - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 dev: false resolution: integrity: sha512-PO2gcfR3Oxa+u0QvECLe1xKXOqYTzCmWf0FhLhjREoW3fPAVamjihL7v1MOVLJLsnAMdLcjkfrs01yvDMwVK4Q== /@types/chai-string/1.4.2: dependencies: - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 dev: false resolution: integrity: sha512-ld/1hV5qcPRGuwlPdvRfvM3Ka/iofOk2pH4VkasK4b1JJP1LjNmWWn0LsISf6RRzyhVOvs93rb9tM09e+UuF8Q== - /@types/chai/4.2.10: + /@types/chai/4.2.11: dev: false resolution: - integrity: sha512-TlWWgb21+0LdkuFqEqfmy7NEgfB/7Jjux15fWQAh3P93gbmXuwTM/vxEdzW89APIcI2BgKR48yjeAkdeH+4qvQ== + integrity: sha512-t7uW6eFafjO+qJ3BIV2gGUyZs27egcNRkUdalkud+Qa3+kg//f129iuOFivHDXQ+vnU3fDXuwgv0cqMCbcE8sw== + /@types/chalk/2.2.0: + dependencies: + chalk: 3.0.0 + deprecated: 'This is a stub types definition for chalk (https://github.com/chalk/chalk). chalk provides its own type definitions, so you don''t need @types/chalk installed!' + dev: false + resolution: + integrity: sha512-1zzPV9FDe1I/WHhRkf9SNgqtRJWZqrBWgu7JGveuHmmyR9CnAPCie2N/x+iHrgnpYBIcCJWHBoMRv2TRWktsvw== /@types/color-name/1.1.1: dev: false resolution: integrity: sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== /@types/connect/3.4.33: dependencies: - '@types/node': 13.9.0 + '@types/node': 13.9.1 dev: false resolution: integrity: sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A== @@ -511,7 +519,7 @@ packages: integrity: sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g== /@types/express-serve-static-core/4.17.2: dependencies: - '@types/node': 13.9.0 + '@types/node': 13.9.1 '@types/range-parser': 1.2.3 dev: false resolution: @@ -596,6 +604,10 @@ packages: dev: false resolution: integrity: sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== + /@types/minimist/1.2.0: + dev: false + resolution: + integrity: sha1-aaI6OtKcrwCX8G7aWbNh7i8GOfY= /@types/mocha/5.2.7: dev: false resolution: @@ -627,10 +639,10 @@ packages: dev: false resolution: integrity: sha512-pMCcqU2zT4TjqYFrWtYHKal7Sl30Ims6ulZ4UFXxI4xbtQqK/qqKwkDoBFCfooRqqmRu9vY3xaJRwxSh673aYg== - /@types/node/13.9.0: + /@types/node/13.9.1: dev: false resolution: - integrity: sha512-0ARSQootUG1RljH2HncpsY2TJBfGQIKOOi7kxzUY6z54ePu/ZD+wJA8zI2Q6v8rol2qpG/rvqsReco8zNMPvhQ== + integrity: sha512-E6M6N0blf/jiZx8Q3nb0vNaswQeEyn0XlupO+xN6DtJ6r6IT4nXrTry7zhIfYvFCl3/8Cu6WIysmUBKiqV0bqQ== /@types/node/8.10.59: dev: false resolution: @@ -653,7 +665,7 @@ packages: integrity: sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA== /@types/resolve/0.0.8: dependencies: - '@types/node': 13.9.0 + '@types/node': 13.9.1 dev: false resolution: integrity: sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ== @@ -678,7 +690,7 @@ packages: integrity: sha512-wHNBMnkoEBiRAd3s8KTKwIuO9biFtTf0LehITzBhSco+HQI0xkXZbLOD55SW3Aqw3oUkHstkm5SPv58yaAdFPQ== /@types/tunnel/0.0.0: dependencies: - '@types/node': 13.9.0 + '@types/node': 13.9.1 dev: false resolution: integrity: sha512-FGDp0iBRiBdPjOgjJmn1NH0KDLN+Z8fRmo+9J7XGBhubq1DPrGrbmG4UTlGzrpbCpesMqD0sWkzi27EYkOMHyg== @@ -718,9 +730,9 @@ packages: dev: false resolution: integrity: sha512-XAvHLwG7UQ+8M4caKIH0ZozIOYay5fQkAgyIXegXT9jPtdIGdhga+sUEdAr1CiG46aB+c64xQEYyEzlwWVTNzA== - /@typescript-eslint/eslint-plugin-tslint/2.23.0_ccca70319db4d86cd022108bbffd6124: + /@typescript-eslint/eslint-plugin-tslint/2.24.0_ccca70319db4d86cd022108bbffd6124: dependencies: - '@typescript-eslint/experimental-utils': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/experimental-utils': 2.24.0_eslint@6.8.0+typescript@3.7.5 eslint: 6.8.0 lodash: 4.17.15 tslint: 5.20.1_typescript@3.7.5 @@ -733,11 +745,11 @@ packages: tslint: ^5.0.0 typescript: '*' resolution: - integrity: sha512-X6i48FBoIFaSMzgL0YO0G8JsfTuJWXg4m6Kd1jxqTo8SFHpde4sMZWPo8DNY25c5x5GKdP79gAnvHdn4UJADEw== - /@typescript-eslint/eslint-plugin/2.23.0_9f87a6626aabebbe99f88159fc4926b8: + integrity: sha512-bY+lSV4zuh/Z+WtPlv/GXC812Hrwy6FWVptAAXAa22Y5Qr4cTszp9TwsMcTHWaovRWWevnBUpjZIKNBAXuAn4w== + /@typescript-eslint/eslint-plugin/2.24.0_347dbd490cbb0b8bd07c94bdc05f318a: dependencies: - '@typescript-eslint/experimental-utils': 2.23.0_eslint@6.8.0+typescript@3.7.5 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/experimental-utils': 2.24.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 eslint: 6.8.0 eslint-utils: 1.4.3 functional-red-black-tree: 1.0.1 @@ -755,11 +767,11 @@ packages: typescript: optional: true resolution: - integrity: sha512-8iA4FvRsz8qTjR0L/nK9RcRUN3QtIHQiOm69FzV7WS3SE+7P7DyGGwh3k4UNR2JBbk+Ej2Io+jLAaqKibNhmtw== - /@typescript-eslint/experimental-utils/2.23.0_eslint@6.8.0+typescript@3.7.5: + integrity: sha512-wJRBeaMeT7RLQ27UQkDFOu25MqFOBus8PtOa9KaT5ZuxC1kAsd7JEHqWt4YXuY9eancX0GK9C68i5OROnlIzBA== + /@typescript-eslint/experimental-utils/2.24.0_eslint@6.8.0+typescript@3.7.5: dependencies: '@types/json-schema': 7.0.4 - '@typescript-eslint/typescript-estree': 2.23.0_typescript@3.7.5 + '@typescript-eslint/typescript-estree': 2.24.0_typescript@3.7.5 eslint: 6.8.0 eslint-scope: 5.0.0 typescript: 3.7.5 @@ -770,12 +782,12 @@ packages: eslint: '*' typescript: '*' resolution: - integrity: sha512-OswxY59RcXH3NNPmq+4Kis2CYZPurRU6mG5xPcn24CjFyfdVli5mySwZz/g/xDbJXgDsYqNGq7enV0IziWGXVQ== - /@typescript-eslint/parser/2.23.0_eslint@6.8.0+typescript@3.7.5: + integrity: sha512-DXrwuXTdVh3ycNCMYmWhUzn/gfqu9N0VzNnahjiDJvcyhfBy4gb59ncVZVxdp5XzBC77dCncu0daQgOkbvPwBw== + /@typescript-eslint/parser/2.24.0_eslint@6.8.0+typescript@3.7.5: dependencies: '@types/eslint-visitor-keys': 1.0.0 - '@typescript-eslint/experimental-utils': 2.23.0_eslint@6.8.0+typescript@3.7.5 - '@typescript-eslint/typescript-estree': 2.23.0_typescript@3.7.5 + '@typescript-eslint/experimental-utils': 2.24.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/typescript-estree': 2.24.0_typescript@3.7.5 eslint: 6.8.0 eslint-visitor-keys: 1.1.0 typescript: 3.7.5 @@ -789,8 +801,8 @@ packages: typescript: optional: true resolution: - integrity: sha512-k61pn/Nepk43qa1oLMiyqApC6x5eP5ddPz6VUYXCAuXxbmRLqkPYzkFRKl42ltxzB2luvejlVncrEpflgQoSUg== - /@typescript-eslint/typescript-estree/2.23.0_typescript@3.7.5: + integrity: sha512-H2Y7uacwSSg8IbVxdYExSI3T7uM1DzmOn2COGtCahCC3g8YtM1xYAPi2MAHyfPs61VKxP/J/UiSctcRgw4G8aw== + /@typescript-eslint/typescript-estree/2.24.0_typescript@3.7.5: dependencies: debug: 4.1.1 eslint-visitor-keys: 1.1.0 @@ -809,7 +821,7 @@ packages: typescript: optional: true resolution: - integrity: sha512-pmf7IlmvXdlEXvE/JWNNJpEvwBV59wtJqA8MLAxMKLXNKVRC3HZBXR/SlZLPWTCcwOSg9IM7GeRSV3SIerGVqw== + integrity: sha512-RJ0yMe5owMSix55qX7Mi9V6z2FDuuDpN6eR5fzRJrp+8in9UF41IGNQHbg5aMK4/PjVaEQksLvz0IA8n+Mr/FA== /abbrev/1.0.9: dev: false resolution: @@ -1486,7 +1498,7 @@ packages: integrity: sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== /chrome-launcher/0.11.2: dependencies: - '@types/node': 13.9.0 + '@types/node': 13.9.1 is-wsl: 2.1.1 lighthouse-logger: 1.2.0 mkdirp: 0.5.1 @@ -1613,14 +1625,6 @@ packages: dev: false resolution: integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== - /commander/2.9.0: - dependencies: - graceful-readlink: 1.0.1 - dev: false - engines: - node: '>= 0.6.x' - resolution: - integrity: sha1-nJkJQXbhIkDLItbFFGCYQA/g99Q= /common-tags/1.8.0: dev: false engines: @@ -1888,19 +1892,6 @@ packages: node: '>=0.12' resolution: integrity: sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw== - /deep-extend/0.5.1: - dev: false - engines: - iojs: '>=1.0.0' - node: '>=0.10.0' - resolution: - integrity: sha512-N8vBdOa+DF7zkRrDCsaOXoCs/E2fJfx9B9MrKnnSiHNh4ws7eSys6YQE4KvT1cecKmOASYQBhbKjeuDD9lT81w== - /deep-extend/0.6.0: - dev: false - engines: - node: '>=4.0.0' - resolution: - integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== /deep-is/0.1.3: dev: false resolution: @@ -2116,10 +2107,6 @@ packages: dev: false resolution: integrity: sha1-6WQhkyWiHQX0RGai9obtbOX13R0= - /entities/1.1.2: - dev: false - resolution: - integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== /errno/0.1.7: dependencies: prr: 1.0.1 @@ -2295,7 +2282,7 @@ packages: eslint-scope: 5.0.0 eslint-utils: 1.4.3 eslint-visitor-keys: 1.1.0 - espree: 6.2.0 + espree: 6.2.1 esquery: 1.1.0 esutils: 2.0.3 file-entry-cache: 5.0.1 @@ -2305,14 +2292,14 @@ packages: ignore: 4.0.6 import-fresh: 3.2.1 imurmurhash: 0.1.4 - inquirer: 7.0.6 + inquirer: 7.1.0 is-glob: 4.0.1 js-yaml: 3.13.1 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.3.0 lodash: 4.17.15 minimatch: 3.0.4 - mkdirp: 0.5.1 + mkdirp: 0.5.3 natural-compare: 1.4.0 optionator: 0.8.3 progress: 2.0.3 @@ -2335,7 +2322,7 @@ packages: node: '>=6' resolution: integrity: sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA== - /espree/6.2.0: + /espree/6.2.1: dependencies: acorn: 7.1.1 acorn-jsx: 5.2.0_acorn@7.1.1 @@ -2344,7 +2331,7 @@ packages: engines: node: '>=6.0.0' resolution: - integrity: sha512-Xs8airJ7RQolnDIbLtRutmfvSsAe0xqMMAantCN/GMoqf81TFbeI1T7Jpd56qYu1uuh32dOG5W/X9uO+ghPXzA== + integrity: sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw== /esprima/2.7.3: dev: false engines: @@ -2833,12 +2820,6 @@ packages: node: '>=0.10.0' resolution: integrity: sha1-uWjGsKBDhDJJAui/Gl3zJXmkUP4= - /get-stdin/5.0.1: - dev: false - engines: - node: '>=0.12.0' - resolution: - integrity: sha1-Ei4WFZHiH/TFJTAwVpPyDmOTo5g= /get-stdin/6.0.0: dev: false engines: @@ -2977,10 +2958,6 @@ packages: dev: false resolution: integrity: sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ== - /graceful-readlink/1.0.1: - dev: false - resolution: - integrity: sha1-TK+tdrxi8C+gObL5Tpo906ORpyU= /growl/1.10.5: dev: false engines: @@ -3005,7 +2982,7 @@ packages: lodash._reevaluate: 3.0.0 lodash._reinterpolate: 3.0.0 lodash.template: 3.6.2 - minimist: 1.2.0 + minimist: 1.2.5 multipipe: 0.1.2 object-assign: 3.0.0 replace-ext: 0.0.1 @@ -3329,7 +3306,7 @@ packages: dev: false resolution: integrity: sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - /inquirer/7.0.6: + /inquirer/7.0.7: dependencies: ansi-escapes: 4.3.1 chalk: 3.0.0 @@ -3348,7 +3325,27 @@ packages: engines: node: '>=6.0.0' resolution: - integrity: sha512-7SVO4h+QIdMq6XcqIqrNte3gS5MzCCKZdsq9DO4PJziBFNYzP3PGFbDjgadDb//MCahzgjCxvQ/O2wa7kx9o4w== + integrity: sha512-+lV+BE+M3bDVi6Vtejd7sA572D8gJI5qQUciWDqGKvy6Q6GjuEeJAoHRdOSwqtHUvZlNZssnVvTqQyEXKzki/g== + /inquirer/7.1.0: + dependencies: + ansi-escapes: 4.3.1 + chalk: 3.0.0 + cli-cursor: 3.1.0 + cli-width: 2.2.0 + external-editor: 3.1.0 + figures: 3.2.0 + lodash: 4.17.15 + mute-stream: 0.0.8 + run-async: 2.4.0 + rxjs: 6.5.4 + string-width: 4.2.0 + strip-ansi: 6.0.0 + through: 2.3.8 + dev: false + engines: + node: '>=6.0.0' + resolution: + integrity: sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg== /interpret/1.2.0: dev: false engines: @@ -3612,8 +3609,8 @@ packages: integrity: sha512-vrRztU9VRRFDyC+aklfLoeXyNdTfga2EI3udDGn4cZ6fpSXpHLV9X6CHvfoMCPtggg8zvDDmC4b9xfu0z6/llA== /istanbul-lib-instrument/3.3.0: dependencies: - '@babel/generator': 7.8.7 - '@babel/parser': 7.8.7 + '@babel/generator': 7.8.8 + '@babel/parser': 7.8.8 '@babel/template': 7.8.6 '@babel/traverse': 7.8.6 '@babel/types': 7.8.7 @@ -3663,7 +3660,7 @@ packages: glob: 5.0.15 handlebars: 4.7.3 js-yaml: 3.13.1 - mkdirp: 0.5.1 + mkdirp: 0.5.3 nopt: 3.0.6 once: 1.4.0 resolve: 1.1.7 @@ -3752,20 +3749,20 @@ packages: integrity: sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus= /json5/1.0.1: dependencies: - minimist: 1.2.0 + minimist: 1.2.5 dev: false hasBin: true resolution: integrity: sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow== - /json5/2.1.1: + /json5/2.1.2: dependencies: - minimist: 1.2.0 + minimist: 1.2.5 dev: false engines: node: '>=6' hasBin: true resolution: - integrity: sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ== + integrity: sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ== /jsonfile/4.0.0: dev: false optionalDependencies: @@ -3885,7 +3882,7 @@ packages: integrity: sha1-X36ZW+uuS06PCiy1IVBVSq8LHi4= /karma-json-to-file-reporter/1.0.1: dependencies: - json5: 2.1.1 + json5: 2.1.2 dev: false resolution: integrity: sha512-kNCi+0UrXAeTJMpMsHkHNbfmlErsYT+/haNakJIhsE/gtj3Jx7zWRg7BTc1HHSbH5KeVXVRJr3/KLB/NHWY7Hg== @@ -4002,12 +3999,6 @@ packages: dev: false resolution: integrity: sha512-wzUvdIeJZhRsG6gpZfmSCfysaxNEr43i+QT+Hie94wvHDKFLi4n7C2GqZ4sTC+PH5b5iktmXJvU87rWvhP3lHw== - /linkify-it/2.2.0: - dependencies: - uc.micro: 1.0.6 - dev: false - resolution: - integrity: sha512-GnAl/knGn+i1U/wjBz3akz2stz+HrHLsxMwHQGofCDfPvlf+gDKN58UtfmUquTY4/MXeE2x7k19KQmeoZi94Iw== /load-json-file/1.1.0: dependencies: graceful-fs: 4.2.3 @@ -4103,20 +4094,12 @@ packages: dev: false resolution: integrity: sha1-+6HEUkwZ7ppfgTa0YJ8BfPTe1pI= - /lodash.differencewith/4.5.0: - dev: false - resolution: - integrity: sha1-uvr7yRi1UVTheRdqALsK76rIVLc= /lodash.escape/3.2.0: dependencies: lodash._root: 3.0.1 dev: false resolution: integrity: sha1-mV7g3BjBtIzJLv+ucaEKq1tIdpg= - /lodash.flatten/4.4.0: - dev: false - resolution: - integrity: sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8= /lodash.flattendeep/4.4.0: dev: false resolution: @@ -4285,43 +4268,6 @@ packages: node: '>=4' resolution: integrity: sha1-plzSkIepJZi4eRJXpSPgISIqwfk= - /markdown-it/8.4.2: - dependencies: - argparse: 1.0.10 - entities: 1.1.2 - linkify-it: 2.2.0 - mdurl: 1.0.1 - uc.micro: 1.0.6 - dev: false - hasBin: true - resolution: - integrity: sha512-GcRz3AWTqSUphY3vsUqQSFMbgR38a4Lh3GWlHRh/7MRwz8mcu9n2IO7HOh+bXHrR9kOPDl5RNCaEsrneb+xhHQ== - /markdownlint-cli/0.17.0: - dependencies: - commander: 2.9.0 - deep-extend: 0.5.1 - get-stdin: 5.0.1 - glob: 7.1.6 - js-yaml: 3.13.1 - lodash.differencewith: 4.5.0 - lodash.flatten: 4.4.0 - markdownlint: 0.15.0 - minimatch: 3.0.4 - rc: 1.2.8 - dev: false - engines: - node: '>=8' - hasBin: true - resolution: - integrity: sha512-/MInVAuNUDGj5lWeAweL2s4Wk0BY2UqOUcOuOMhY62LXmQZU9qZiFJ5XgHrAZVMggFH/vPupCiUiAMFgGIOqHg== - /markdownlint/0.15.0: - dependencies: - markdown-it: 8.4.2 - dev: false - engines: - node: '>=8' - resolution: - integrity: sha512-sNcrSrUgpNbTQX6rPIMd+RI9rAryGTEbDI9VFpcFyijlC5g8gpkma49k5p98yFLdNbdcB3VW69UJ0smxvTVw6g== /marked/0.8.0: dev: false engines: @@ -4361,10 +4307,6 @@ packages: dev: false resolution: integrity: sha1-U6s41f48iJG6RlMp6iP6wFQBJvk= - /mdurl/1.0.1: - dev: false - resolution: - integrity: sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= /media-typer/0.3.0: dev: false engines: @@ -4392,7 +4334,7 @@ packages: decamelize: 1.2.0 loud-rejection: 1.6.0 map-obj: 1.0.1 - minimist: 1.2.0 + minimist: 1.2.5 normalize-package-data: 2.5.0 object-assign: 4.1.1 read-pkg-up: 1.0.1 @@ -4515,13 +4457,26 @@ packages: dev: false resolution: integrity: sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ= + /minimist/1.2.5: + dev: false + resolution: + integrity: sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw== /mkdirp/0.5.1: dependencies: minimist: 0.0.8 + deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.) dev: false hasBin: true resolution: integrity: sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM= + /mkdirp/0.5.3: + dependencies: + minimist: 1.2.5 + deprecated: Legacy versions of mkdirp are no longer supported. Please update to mkdirp 1.x. (Note that the API surface has changed to use Promises in 1.x.) + dev: false + hasBin: true + resolution: + integrity: sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg== /mocha-chrome/2.2.0: dependencies: chalk: 2.4.2 @@ -4544,7 +4499,7 @@ packages: dependencies: debug: 2.6.9 md5: 2.2.1 - mkdirp: 0.5.1 + mkdirp: 0.5.3 mocha: 6.2.2 strip-ansi: 4.0.0 xml: 1.0.1 @@ -4558,7 +4513,7 @@ packages: debug: 4.1.1 is-string: 1.0.5 lodash.once: 4.1.1 - mkdirp: 0.5.1 + mkdirp: 0.5.3 mocha: 6.2.2 object-assign: 4.1.1 dev: false @@ -4718,7 +4673,7 @@ packages: debug: 4.1.1 json-stringify-safe: 5.0.1 lodash: 4.17.15 - mkdirp: 0.5.1 + mkdirp: 0.5.3 propagate: 2.0.1 dev: false engines: @@ -4830,8 +4785,8 @@ packages: spawn-wrap: 1.4.3 test-exclude: 5.2.3 uuid: 3.4.0 - yargs: 13.3.0 - yargs-parser: 13.1.1 + yargs: 13.3.2 + yargs-parser: 13.1.2 dev: false engines: node: '>=6' @@ -5488,16 +5443,6 @@ packages: node: '>= 0.8' resolution: integrity: sha512-9WmIKF6mkvA0SLmA2Knm9+qj89e+j1zqgyn8aXGd7+nAduPoqgI9lO57SAZNn/Byzo5P7JhXTyg9PzaJbH73bA== - /rc/1.2.8: - dependencies: - deep-extend: 0.6.0 - ini: 1.3.5 - minimist: 1.2.0 - strip-json-comments: 2.0.1 - dev: false - hasBin: true - resolution: - integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw== /read-pkg-up/1.0.1: dependencies: find-up: 1.1.2 @@ -5615,10 +5560,10 @@ packages: dev: false resolution: integrity: sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== - /regenerator-runtime/0.13.3: + /regenerator-runtime/0.13.5: dev: false resolution: - integrity: sha512-naKIZz2GQ8JWh///G7L3X6LaQUAMp2lvb1rvwwsURe/VXwD6VMfr+/1NuNw3ag8v2kY1aQ/go5SNn79O9JU7yw== + integrity: sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA== /regexpp/2.0.1: dev: false engines: @@ -5787,7 +5732,7 @@ packages: /rhea-promise/0.1.15: dependencies: debug: 3.2.6 - rhea: 1.0.19 + rhea: 1.0.20 tslib: 1.11.1 dev: false resolution: @@ -5795,17 +5740,17 @@ packages: /rhea-promise/1.0.0: dependencies: debug: 3.2.6 - rhea: 1.0.19 + rhea: 1.0.20 tslib: 1.11.1 dev: false resolution: integrity: sha512-odAjpbB/IpFFBenPDwPkTWMQldt+DUlMBH9yI48Ct5OgTeDuuQcBnlhB+YCc6g2z8+URiP2ejms88joEanNCaw== - /rhea/1.0.19: + /rhea/1.0.20: dependencies: debug: 3.2.6 dev: false resolution: - integrity: sha512-52fctFB6zZ+n2nMo1HvreC4HzV8FwSH7zZyTdMXxNaDMDWeVbZo42cjGeJh8+BZMC1+r7YUZrd3p4cNVAfJf1Q== + integrity: sha512-qj4LSEykJ0SEYESQLg9Vee6VXH5xHN1pYj7ozPeUk+l+S1OaGKx1FugAu+g+3pPwK46WXV1PJD9XiRx8+tS4cw== /rimraf/2.6.3: dependencies: glob: 7.1.6 @@ -5855,7 +5800,7 @@ packages: rollup: 1.32.1 rollup-pluginutils: 2.8.2 serialize-javascript: 2.1.2 - terser: 4.6.6 + terser: 4.6.7 dev: false peerDependencies: rollup: '>=0.66.0 <3' @@ -5873,15 +5818,15 @@ packages: rollup: '>=0.66.0 <2' resolution: integrity: sha512-ddgqkH02klveu34TF0JqygPwZnsbhHVI6t8+hGTcYHngPkQb5MIHI0XiztXIN/d6V9j+efwHAqEL7LspSxQXGw== - /rollup-plugin-visualizer/3.3.1_rollup@1.32.1: + /rollup-plugin-visualizer/3.3.2_rollup@1.32.1: dependencies: - mkdirp: 0.5.1 + mkdirp: 0.5.3 nanoid: 2.1.11 open: 6.4.0 pupa: 2.0.1 rollup: 1.32.1 source-map: 0.7.3 - yargs: 15.3.0 + yargs: 15.3.1 dev: false engines: node: '>=8.10' @@ -5889,7 +5834,7 @@ packages: peerDependencies: rollup: '>=0.60.0' resolution: - integrity: sha512-ElVm75615Qr7fGLnlyYmdejffK8neMkbLMEUHfTYW04RhbiH8brgO14jBKiZBCykOBSnPQ7EvNHGBGF/CT/QRg== + integrity: sha512-jAJxpC97jHoWU5mQkGw5MroguV8fbZsLPxdV7MdE/fX7lAR+t1UDLpSH41rqdxyJCtqi2/UoDOBuADCyZdHaYA== /rollup-pluginutils/2.8.2: dependencies: estree-walker: 0.6.1 @@ -6051,7 +5996,7 @@ packages: /shx/0.3.2: dependencies: es6-object-assign: 1.1.0 - minimist: 1.2.0 + minimist: 1.2.5 shelljs: 0.8.3 dev: false engines: @@ -6267,7 +6212,7 @@ packages: /spawn-wrap/1.4.3: dependencies: foreground-child: 1.5.6 - mkdirp: 0.5.1 + mkdirp: 0.5.3 os-homedir: 1.0.2 rimraf: 2.7.1 signal-exit: 3.0.2 @@ -6563,7 +6508,7 @@ packages: node: '>=6' resolution: integrity: sha512-4WK/bYZmj8xLr+HUCODHGF1ZFzsYffasLUgEiMBY4fgtltdO6B4WJtlSbPaDTLpYTcGVwM2qLnFTICEcNxs3kA== - /terser/4.6.6: + /terser/4.6.7: dependencies: commander: 2.20.3 source-map: 0.6.1 @@ -6573,7 +6518,7 @@ packages: node: '>=6.0.0' hasBin: true resolution: - integrity: sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g== + integrity: sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g== /test-exclude/5.2.3: dependencies: glob: 7.1.6 @@ -6718,8 +6663,8 @@ packages: buffer-from: 1.1.1 diff: 3.5.0 make-error: 1.3.6 - minimist: 1.2.0 - mkdirp: 0.5.1 + minimist: 1.2.5 + mkdirp: 0.5.3 source-map-support: 0.5.16 yn: 2.0.0 dev: false @@ -6748,7 +6693,7 @@ packages: dependencies: '@types/json5': 0.0.29 json5: 1.0.1 - minimist: 1.2.0 + minimist: 1.2.5 strip-bom: 3.0.0 dev: false optional: true @@ -6775,7 +6720,7 @@ packages: glob: 7.1.6 js-yaml: 3.13.1 minimatch: 3.0.4 - mkdirp: 0.5.1 + mkdirp: 0.5.3 resolve: 1.15.1 semver: 5.7.1 tslib: 1.11.1 @@ -6908,10 +6853,6 @@ packages: hasBin: true resolution: integrity: sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w== - /uc.micro/1.0.6: - dev: false - resolution: - integrity: sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== /uglify-js/3.8.0: dependencies: commander: 2.20.3 @@ -7164,7 +7105,7 @@ packages: integrity: sha512-GaETH5wwsX+GcnzhPgKcKjJ6M2Cq3/iZp1WyY/X1CSqrW+jVNM9Y7D8EC2sM4ZG/V8wZlSniJnCKWPmBYAucRQ== /write/1.0.3: dependencies: - mkdirp: 0.5.1 + mkdirp: 0.5.3 dev: false engines: node: '>=4' @@ -7295,7 +7236,14 @@ packages: dev: false resolution: integrity: sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ== - /yargs-parser/18.1.0: + /yargs-parser/13.1.2: + dependencies: + camelcase: 5.3.1 + decamelize: 1.2.0 + dev: false + resolution: + integrity: sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg== + /yargs-parser/18.1.1: dependencies: camelcase: 5.3.1 decamelize: 1.2.0 @@ -7303,7 +7251,7 @@ packages: engines: node: '>=6' resolution: - integrity: sha512-o/Jr6JBOv6Yx3pL+5naWSoIA2jJ+ZkMYQG/ie9qFbukBe4uzmBatlXFOiu/tNKRWEtyf+n5w7jc/O16ufqOTdQ== + integrity: sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA== /yargs-unparser/1.6.0: dependencies: flat: 4.1.0 @@ -7329,7 +7277,22 @@ packages: dev: false resolution: integrity: sha512-2eehun/8ALW8TLoIl7MVaRUrg+yCnenu8B4kBlRxj3GJGDKU1Og7sMXPNm1BYyM1DOJmTZ4YeN/Nwxv+8XJsUA== - /yargs/15.3.0: + /yargs/13.3.2: + dependencies: + cliui: 5.0.0 + find-up: 3.0.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + require-main-filename: 2.0.0 + set-blocking: 2.0.0 + string-width: 3.1.0 + which-module: 2.0.0 + y18n: 4.0.0 + yargs-parser: 13.1.2 + dev: false + resolution: + integrity: sha512-AX3Zw5iPruN5ie6xGRIDgqkT+ZhnRlZMLMHAs8tg7nRruy2Nb+i5o9bwghAogtM08q1dpr2LVoS8KSTMYpWXUw== + /yargs/15.3.1: dependencies: cliui: 6.0.0 decamelize: 1.2.0 @@ -7341,12 +7304,12 @@ packages: string-width: 4.2.0 which-module: 2.0.0 y18n: 4.0.0 - yargs-parser: 18.1.0 + yargs-parser: 18.1.1 dev: false engines: node: '>=8' resolution: - integrity: sha512-g/QCnmjgOl1YJjGsnUg2SatC7NUYEiLXJqxNOQU9qSpjzGtGXda9b+OKccr1kLTy8BN9yqEyqfq5lxlwdc13TA== + integrity: sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA== /yauzl/2.4.1: dependencies: fd-slicer: 1.0.1 @@ -7382,15 +7345,15 @@ packages: integrity: sha512-DUOKC/IhbkdLKKiV89gw9DUauTV8U/8yJl1sjf6MtDmzevLKOF2duNJ495S3MFVjqZarr+qNGCPbkg4mu4PpLw== 'file:projects/abort-controller.tgz': dependencies: - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 '@types/mocha': 5.2.7 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 cross-env: 6.0.3 delay: 4.3.0 @@ -7425,27 +7388,27 @@ packages: dev: false name: '@rush-temp/abort-controller' resolution: - integrity: sha512-l9Uczvqyxh910HVtQM7aeVzvNHWgSmMT3Td9t7xL6DTOVExjZEgplYt+/EJxsbIw1v7+HMHB+fRnn+HQRT2Jmg== + integrity: sha512-Oo6jSjRdnFoy3NcwxVXIUzGsSZ1gIdvRRikwuqzQy8dIqGJBUas7ymGvAVAqGwnebPL7SIu2ym10OTttx6HrSw== tarball: 'file:projects/abort-controller.tgz' version: 0.0.0 'file:projects/ai-text-analytics.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 '@azure/identity': 1.1.0-preview1 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/chai-as-promised': 7.1.2 '@types/mocha': 5.2.7 '@types/node': 8.10.59 '@types/sinon': 7.5.2 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 chai: 4.2.0 chai-as-promised: 7.1.1_chai@4.2.0 cross-env: 6.0.3 @@ -7478,7 +7441,7 @@ packages: rollup-plugin-shim: 1.0.0 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 sinon: 7.5.0 source-map-support: 0.5.16 tslib: 1.11.1 @@ -7486,20 +7449,20 @@ packages: dev: false name: '@rush-temp/ai-text-analytics' resolution: - integrity: sha512-HXQ7sEeyDnFz7wr8i1YCn/Oq/eUTHNrRxSYBhtCm/4fBw280nhCz4C6hS2x8xp2doCeJAgzouKfQ2tQ6p40S9A== + integrity: sha512-m3UXZjGThRayvIuKYgONPsLHWlwI7KzAm+kxEQFFCm1wpYLEIdziHfYAkX75ZvTYRBua0CF9wPG9tj9+h8KGZA== tarball: 'file:projects/ai-text-analytics.tgz' version: 0.0.0 'file:projects/app-configuration.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 '@azure/identity': 1.1.0-preview1 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/mocha': 5.2.7 '@types/node': 8.10.59 '@types/sinon': 7.5.2 @@ -7529,12 +7492,12 @@ packages: dev: false name: '@rush-temp/app-configuration' resolution: - integrity: sha512-TtffWKbKueVi1E1QiOQaX8DdMjh+Dy1ON0AzlQu2VwqHBq/P6NHLOX4+fzO1P8SQUZ60OSmodQv7Szo7xB4vKQ== + integrity: sha512-n2sA2rs6B+ML0ZnzIFVZ8NRUtZ608QA1AnwejOOfZ36EH+1T0hmeoij4CQ5IFi3u43wW/jQg5B2o2FC/kK+hpQ== tarball: 'file:projects/app-configuration.tgz' version: 0.0.0 'file:projects/core-amqp.tgz': dependencies: - '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/eslint-plugin-azure-sdk': 2.0.1_6eb4f22a85ba1817e9c23ce411f002c2 '@azure/identity': 1.1.0-preview1 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-inject': 4.0.1_rollup@1.32.1 @@ -7543,7 +7506,7 @@ packages: '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 '@types/async-lock': 1.1.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/chai-as-promised': 7.1.2 '@types/debug': 4.1.5 '@types/is-buffer': 2.0.0 @@ -7551,8 +7514,8 @@ packages: '@types/mocha': 5.2.7 '@types/node': 8.10.59 '@types/sinon': 7.5.2 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 async-lock: 1.2.2 buffer: 5.5.0 @@ -7579,7 +7542,7 @@ packages: prettier: 1.19.1 process: 0.11.10 puppeteer: 2.1.1 - rhea: 1.0.19 + rhea: 1.0.20 rhea-promise: 1.0.0 rimraf: 3.0.2 rollup: 1.32.1 @@ -7597,17 +7560,17 @@ packages: dev: false name: '@rush-temp/core-amqp' resolution: - integrity: sha512-+I8NcHJFPvaMTI9zXx5pIrRYdlVUv4gSYrVvwp8RsBd/lt92S6wfpdQH0W3tXqtLLcDRI7GUQdFyVeYbdsHZPQ== + integrity: sha512-i9lVocSI8zfml2em/PykxSx1332a4k+1iotqsA+eSYMWFdSTsq6T98RTkIVBY2kyVE4zUpyW8Xlg2OxcoBkMiw== tarball: 'file:projects/core-amqp.tgz' version: 0.0.0 'file:projects/core-arm.tgz': dependencies: '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/mocha': 5.2.7 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 chai: 4.2.0 eslint: 6.8.0 eslint-config-prettier: 6.10.0_eslint@6.8.0 @@ -7622,7 +7585,7 @@ packages: rimraf: 3.0.2 rollup: 1.32.1 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 shx: 0.3.2 ts-node: 8.6.2_typescript@3.7.5 tslib: 1.11.1 @@ -7631,14 +7594,14 @@ packages: dev: false name: '@rush-temp/core-arm' resolution: - integrity: sha512-0pn2mZyXmj//Y/nBrZacADv0y5cxhrtTmSDh7lJKBkO1mikLkWocQzfkXVQf2zDoZXRSqK5EDICnRx34Iij6og== + integrity: sha512-gMo5M4yZopVded6wxND2JEGB6LyYP6FPbQYlPyhPFy36scETuxnOzg9d965CYEaLS/pa1Y8m73NvYPntfsFrAg== tarball: 'file:projects/core-arm.tgz' version: 0.0.0 'file:projects/core-asynciterator-polyfill.tgz': dependencies: '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 eslint: 6.8.0 eslint-config-prettier: 6.10.0_eslint@6.8.0 eslint-plugin-no-null: 1.0.2_eslint@6.8.0 @@ -7649,14 +7612,14 @@ packages: dev: false name: '@rush-temp/core-asynciterator-polyfill' resolution: - integrity: sha512-jDLggg/4sSiSb9xXQUsivEzh+w65cXOMPe/kPArXFs+LmAHaK1Et7soO3hxgmVW7AZ/5lVp8jcfW3hDXbKIFXQ== + integrity: sha512-wcvU9xzocDcf6JqxBwlUIbDtU8XGBkq8n5TjQ4QYlRSFOUqjEX64a6XdMQr4dcga+OSoZw/QV/12HYPuUypK9w== tarball: 'file:projects/core-asynciterator-polyfill.tgz' version: 0.0.0 'file:projects/core-auth.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 - '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 - '@microsoft/api-extractor': 7.7.8 + '@azure/eslint-plugin-azure-sdk': 2.0.1_6eb4f22a85ba1817e9c23ce411f002c2 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 @@ -7665,8 +7628,8 @@ packages: '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 '@types/mocha': 5.2.7 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 cross-env: 6.0.3 eslint: 6.8.0 @@ -7683,27 +7646,27 @@ packages: rollup: 1.32.1 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 tslib: 1.11.1 typescript: 3.7.5 util: 0.12.2 dev: false name: '@rush-temp/core-auth' resolution: - integrity: sha512-s/939eY3zDHWsLuxyKMR6PfnRaA8fYeoDeAas83xmrRQwU5Nnj2YMfjWl8RwlUrLk4nJ2Hdi2nSZVp2UzCLyuw== + integrity: sha512-SUk7DhjxjZhru6S3LTYJ7ZQjYaTKzrseBnzH632m9cRNiyYKN64ZQy7LlYBuvlFIFUL+6j3unoZlyEThr4SwfA== tarball: 'file:projects/core-auth.tgz' version: 0.0.0 'file:projects/core-http.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 - '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/eslint-plugin-azure-sdk': 2.0.1_6eb4f22a85ba1817e9c23ce411f002c2 '@azure/logger-js': 1.3.2 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/express': 4.17.3 '@types/glob': 7.1.1 '@types/karma': 3.0.8 @@ -7715,8 +7678,8 @@ packages: '@types/tunnel': 0.0.1 '@types/uuid': 3.4.8 '@types/xml2js': 0.4.5 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 babel-runtime: 6.26.0 chai: 4.2.0 cross-env: 6.0.3 @@ -7748,11 +7711,11 @@ packages: prettier: 1.19.1 process: 0.11.10 puppeteer: 2.1.1 - regenerator-runtime: 0.13.3 + regenerator-runtime: 0.13.5 rimraf: 3.0.2 rollup: 1.32.1 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 shx: 0.3.2 sinon: 7.5.0 tough-cookie: 3.0.1 @@ -7768,24 +7731,24 @@ packages: dev: false name: '@rush-temp/core-http' resolution: - integrity: sha512-3iLOCNXEQHsgOsCTDrLmMqSNldS1N8u1mbIovCrlVdkiK3pr+0N8K8Txoj2/PUVR8VMvt31ZeKwvB9txLTE/tw== + integrity: sha512-GyXi+HL7MmVagBw7K+lU3O6JQaBv284xFwL5UsR79rkErw9Xv2nsponHSUb8gYS2YUR8QBfqthOE1IYZ3qkCqA== tarball: 'file:projects/core-http.tgz' version: 0.0.0 'file:projects/core-lro.tgz': dependencies: '@azure/core-arm': 1.0.0-preview.7 '@azure/identity': 1.1.0-preview1 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/mocha': 5.2.7 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 chai: 4.2.0 eslint: 6.8.0 @@ -7816,7 +7779,7 @@ packages: rollup-plugin-shim: 1.0.0 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 shx: 0.3.2 ts-node: 8.6.2_typescript@3.7.5 tslib: 1.11.1 @@ -7825,14 +7788,14 @@ packages: dev: false name: '@rush-temp/core-lro' resolution: - integrity: sha512-Q2ycvTiywEt2t//BciGEsxAoAkoUznSrG/mwOj1ssneeb1Yln9zrgPTBr3OxsZpRRoZuIdqJOmKKXAR2ajVw6w== + integrity: sha512-369Bl3Vj0+V1C/m6abpPWQ1mdJudwYRimtRY9wXLd+0aoLNVq9dm6qcVfn+1ZIRyD7gSZ/YToY0RQ4WBa8q22g== tarball: 'file:projects/core-lro.tgz' version: 0.0.0 'file:projects/core-paging.tgz': dependencies: '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 eslint: 6.8.0 eslint-config-prettier: 6.10.0_eslint@6.8.0 eslint-plugin-no-null: 1.0.2_eslint@6.8.0 @@ -7843,13 +7806,13 @@ packages: dev: false name: '@rush-temp/core-paging' resolution: - integrity: sha512-rRAaeqAsySfmVMMf46j6TljIPw2xVRZBVuyU9wrvQKJb1VzIEBZayG1jm9iTyyqUdFOEMn759//IfYP7KkhJNQ== + integrity: sha512-HtQeUV6/jtHxcEJSGOLfZj58FrapAsPl4Lirod7Yib/yXMrDIBYkkkI3Wwx5mGap+6ChPvmsBeskFIii1ZKcJg== tarball: 'file:projects/core-paging.tgz' version: 0.0.0 'file:projects/core-tracing.tgz': dependencies: - '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 - '@microsoft/api-extractor': 7.7.8 + '@azure/eslint-plugin-azure-sdk': 2.0.1_6eb4f22a85ba1817e9c23ce411f002c2 + '@microsoft/api-extractor': 7.7.10 '@opencensus/web-types': 0.0.7 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 @@ -7859,8 +7822,8 @@ packages: '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 '@types/mocha': 5.2.7 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 cross-env: 6.0.3 eslint: 6.8.0 @@ -7877,20 +7840,20 @@ packages: rollup: 1.32.1 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 tslib: 1.11.1 typescript: 3.7.5 util: 0.12.2 dev: false name: '@rush-temp/core-tracing' resolution: - integrity: sha512-SJ+L9sNBKGjTh1Ye4OWZ1aqXJ534jIKQwi5eDRa6irbh1jSZsNUmSMnBxzkM3+0kfQZ8YP7jm6sjPykaOJ4hUQ== + integrity: sha512-RWETP32JH6m1gxq4dQKAe572XiKX+bLAlU3/fUxPZPbscLNtphMy/E2DRzyvDCBEQfL+C49MxZIzEoy8ECdKgw== tarball: 'file:projects/core-tracing.tgz' version: 0.0.0 'file:projects/cosmos.tgz': dependencies: - '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 - '@microsoft/api-extractor': 7.7.8 + '@azure/eslint-plugin-azure-sdk': 2.0.1_6eb4f22a85ba1817e9c23ce411f002c2 + '@microsoft/api-extractor': 7.7.10 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@types/debug': 4.1.5 @@ -7904,9 +7867,9 @@ packages: '@types/tunnel': 0.0.1 '@types/underscore': 1.9.4 '@types/uuid': 3.4.8 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/eslint-plugin-tslint': 2.23.0_ccca70319db4d86cd022108bbffd6124 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/eslint-plugin-tslint': 2.24.0_ccca70319db4d86cd022108bbffd6124 + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 cross-env: 6.0.3 debug: 4.1.1 dotenv: 8.2.0 @@ -7946,22 +7909,41 @@ packages: dev: false name: '@rush-temp/cosmos' resolution: - integrity: sha512-GRoPrAKiHI521c+rxNT791bVFShNn6YefMT78uX5GqvTkxZa1jY3t5B2IdISXe0w1egUKwqImF2etEza6/pRZw== + integrity: sha512-DKhpoSSi/f/Aq6de+2s/IpX1C5FQbVfBdK063cXTE1SCamCwzYdf6/SLLbWeEVadUgz4wFgBTIy9Ta3Zz79qUQ== tarball: 'file:projects/cosmos.tgz' version: 0.0.0 + 'file:projects/dev-tool.tgz': + dependencies: + '@types/chalk': 2.2.0 + '@types/fs-extra': 8.1.0 + '@types/minimist': 1.2.0 + '@types/node': 8.10.59 + chalk: 3.0.0 + fs-extra: 8.1.0 + inquirer: 7.0.7 + minimist: 1.2.5 + rimraf: 3.0.2 + ts-node: 8.6.2_typescript@3.7.5 + typescript: 3.7.5 + dev: false + name: '@rush-temp/dev-tool' + resolution: + integrity: sha512-TIdr84T7zw94e4DeRD9tfOelunKvA03ho/B0kb1H5oycv0CiaBeOzMYkDCpJzRWCZMOSlAOiNn8EWsWyUjEg+Q== + tarball: 'file:projects/dev-tool.tgz' + version: 0.0.0 'file:projects/eslint-plugin-azure-sdk.tgz': dependencies: '@types/bluebird': 3.5.30 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/eslint': 4.16.8 '@types/estree': 0.0.39 '@types/glob': 7.1.1 '@types/mocha': 5.2.7 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/experimental-utils': 2.23.0_eslint@6.8.0+typescript@3.7.5 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 - '@typescript-eslint/typescript-estree': 2.23.0_typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/experimental-utils': 2.24.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/typescript-estree': 2.24.0_typescript@3.7.5 bluebird: 3.7.2 chai: 4.2.0 eslint: 6.8.0 @@ -7969,7 +7951,6 @@ packages: eslint-plugin-no-only-tests: 2.4.0 eslint-plugin-promise: 4.2.1 glob: 7.1.6 - markdownlint-cli: 0.17.0 mocha: 6.2.2 mocha-junit-reporter: 1.23.3_mocha@6.2.2 mocha-multi: 1.1.3_mocha@6.2.2 @@ -7981,15 +7962,15 @@ packages: dev: false name: '@rush-temp/eslint-plugin-azure-sdk' resolution: - integrity: sha512-aMdxopTddXUy77fzX6jtFk9DIX9Hx8A77E+39V72mYUTU4YCv2Um4vKccChPLOMWbECP/XsEzfe9zT8kaR58/g== + integrity: sha512-gwV4h9V99k0M2vf+8GqC+UjdOuYl0x7R2zD9xNA+6gjUA1ENbsipVhvD4O4uqU36BPXZiEqs4FUoi7z7QJVhbA== tarball: 'file:projects/eslint-plugin-azure-sdk.tgz' version: 0.0.0 'file:projects/event-hubs.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 - '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/eslint-plugin-azure-sdk': 2.0.1_6eb4f22a85ba1817e9c23ce411f002c2 '@azure/identity': 1.1.0-preview1 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-inject': 4.0.1_rollup@1.32.1 @@ -7998,7 +7979,7 @@ packages: '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 '@types/async-lock': 1.1.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/chai-as-promised': 7.1.2 '@types/chai-string': 1.4.2 '@types/debug': 4.1.5 @@ -8008,8 +7989,8 @@ packages: '@types/sinon': 7.5.2 '@types/uuid': 3.4.8 '@types/ws': 6.0.4 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 async-lock: 1.2.2 buffer: 5.5.0 @@ -8062,22 +8043,22 @@ packages: dev: false name: '@rush-temp/event-hubs' resolution: - integrity: sha512-i6rPyYLlb/THBjrZMAcWFweSyM4g3rK7Bqpwzlo4RFGQvNil4ea1Ma0TSDYvUPXIWXybSLPKtelORdMLqCb0Hg== + integrity: sha512-2237N2JS2Y5Lr2+RbbA35+4dE9+M3eAmC4xk1kl1HQXJXCWbZYhatUBbi4+ZuqjBOrbgpcTmgTr8zu7V5H7Jdw== tarball: 'file:projects/event-hubs.tgz' version: 0.0.0 'file:projects/event-processor-host.tgz': dependencies: - '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/eslint-plugin-azure-sdk': 2.0.1_6eb4f22a85ba1817e9c23ce411f002c2 '@azure/event-hubs': 2.1.4 '@azure/ms-rest-nodeauth': 0.9.3 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 '@types/async-lock': 1.1.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/chai-as-promised': 7.1.2 '@types/chai-string': 1.4.2 '@types/debug': 4.1.5 @@ -8085,8 +8066,8 @@ packages: '@types/node': 8.10.59 '@types/uuid': 3.4.8 '@types/ws': 6.0.4 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 async-lock: 1.2.2 azure-storage: 2.10.3 chai: 4.2.0 @@ -8120,26 +8101,26 @@ packages: dev: false name: '@rush-temp/event-processor-host' resolution: - integrity: sha512-UKQZiZyDo3uX1TvimdPeny6Yc0s0t9foQLx1sap3CMzASBIocWrMXwYmxHWn3n8sRlOmMtqXNyJIy/1zru3+9A== + integrity: sha512-SXJ4EJcxMdnl7GQuy7f7YXS4FLEcmojM48uJKlVQpbdFRSPZczCm8NAA8GGXJmJjVdq+nB388bYk2H724/ZTrA== tarball: 'file:projects/event-processor-host.tgz' version: 0.0.0 'file:projects/eventhubs-checkpointstore-blob.tgz': dependencies: - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-inject': 4.0.1_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/chai-as-promised': 7.1.2 '@types/chai-string': 1.4.2 '@types/debug': 4.1.5 '@types/mocha': 5.2.7 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 chai: 4.2.0 chai-as-promised: 7.1.1_chai@4.2.0 @@ -8177,7 +8158,7 @@ packages: rollup-plugin-shim: 1.0.0 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 ts-node: 8.6.2_typescript@3.7.5 tslib: 1.11.1 typescript: 3.7.5 @@ -8185,13 +8166,13 @@ packages: dev: false name: '@rush-temp/eventhubs-checkpointstore-blob' resolution: - integrity: sha512-uhmB8TQ1nS2HzSAVpAuoZx24dn9uyQ8lpwrY65FymlgKH+SaIXNsmS/hQHmtN/bGsV9yqIZa9dVWqggyKivb0A== + integrity: sha512-xPm4nTkmpML4JB5pwW9wNl1n95LZ1tzeXGRxQJkDGPDlVzcMtWNSCd5J2goUy2G5IXw+0rjve2gV2Ork8Ed8hg== tarball: 'file:projects/eventhubs-checkpointstore-blob.tgz' version: 0.0.0 'file:projects/identity.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 @@ -8204,8 +8185,8 @@ packages: '@types/node': 8.10.59 '@types/qs': 6.9.1 '@types/uuid': 3.4.8 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 cross-env: 6.0.3 eslint: 6.8.0 @@ -8233,7 +8214,7 @@ packages: rollup: 1.32.1 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 tslib: 1.11.1 typescript: 3.7.5 util: 0.12.2 @@ -8241,28 +8222,28 @@ packages: dev: false name: '@rush-temp/identity' resolution: - integrity: sha512-RmqqkFY1LlYsh2Vo7LCCoV7FcY/KsNQRqRYkEHt43BP8e77UKHQZkJgRs7GJjd6E0rlIYg+JVf9P3aM9HAThTg== + integrity: sha512-RwVfGqhSsWDLJkaTvHXZkadpjSi14bWO8LbwQx/oy2OmuDGTmabMWoAQlTYtlQ8m6yRFg9QGnro78/Hov7F1uw== tarball: 'file:projects/identity.tgz' version: 0.0.0 'file:projects/keyvault-certificates.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 - '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/eslint-plugin-azure-sdk': 2.0.1_6eb4f22a85ba1817e9c23ce411f002c2 '@azure/identity': 1.1.0-preview1 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/fs-extra': 8.1.0 '@types/mocha': 5.2.7 '@types/node': 8.10.59 '@types/query-string': 6.2.0 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 chai: 4.2.0 cross-env: 6.0.3 @@ -8299,7 +8280,7 @@ packages: rollup-plugin-shim: 1.0.0 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 source-map-support: 0.5.16 tslib: 1.11.1 typescript: 3.7.5 @@ -8308,28 +8289,28 @@ packages: dev: false name: '@rush-temp/keyvault-certificates' resolution: - integrity: sha512-kcF1QeALb78k1ebetS9RMvQ+ERE64bZ+6VbIljX9kFK8eCbWchkiM0VvY3z+4S/L5HAnQ0o7rqdRjgwBqP4w7A== + integrity: sha512-ic/oiqbSJXCePANRztyIwfD+u33114N4N8N1BHnqlhWtKm6e160vEaL/lL9QLGnRqBRFd3xxu1e3YcjS507p5A== tarball: 'file:projects/keyvault-certificates.tgz' version: 0.0.0 'file:projects/keyvault-keys.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 - '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/eslint-plugin-azure-sdk': 2.0.1_6eb4f22a85ba1817e9c23ce411f002c2 '@azure/identity': 1.1.0-preview1 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/fs-extra': 8.1.0 '@types/mocha': 5.2.7 '@types/node': 8.10.59 '@types/query-string': 6.2.0 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 chai: 4.2.0 cross-env: 6.0.3 @@ -8366,7 +8347,7 @@ packages: rollup-plugin-shim: 1.0.0 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 source-map-support: 0.5.16 tslib: 1.11.1 typescript: 3.7.5 @@ -8375,28 +8356,28 @@ packages: dev: false name: '@rush-temp/keyvault-keys' resolution: - integrity: sha512-WRzB1mONnSK1/qZdgaW2rO6QRIZLju/WDPqMn0gj+GOznT01XkpB8VMPQ5B0zRwk/N4g/rxGFCULHZ3YK+2/mQ== + integrity: sha512-QoVCUWU0kYmFCLjhMAzniVmdw3+B/4khLWpZ8zXU2CPvmhw3+BwIwGVhu5QPHyCnqw/FiLoRQPDnRKXALtXfFA== tarball: 'file:projects/keyvault-keys.tgz' version: 0.0.0 'file:projects/keyvault-secrets.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 - '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/eslint-plugin-azure-sdk': 2.0.1_6eb4f22a85ba1817e9c23ce411f002c2 '@azure/identity': 1.1.0-preview1 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/fs-extra': 8.1.0 '@types/mocha': 5.2.7 '@types/node': 8.10.59 '@types/query-string': 6.2.0 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 chai: 4.2.0 cross-env: 6.0.3 @@ -8433,7 +8414,7 @@ packages: rollup-plugin-shim: 1.0.0 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 source-map-support: 0.5.16 tslib: 1.11.1 typescript: 3.7.5 @@ -8442,22 +8423,22 @@ packages: dev: false name: '@rush-temp/keyvault-secrets' resolution: - integrity: sha512-MTwfUVkm4XKNPiROswoPlGtuaY8HX+79Lz3JGhWNCxAnSpVQyMirlis1GwmnTWckVU5xV6bY1vahUaqgw4N0hQ== + integrity: sha512-MUyCM3LE69eXhD4xpMYhYTK/6YkGYECUNMlQ9imvqAjoanaSTZVLyO8ZyQGO2KIgiRQHK46xM+4OehMKu2k/JA== tarball: 'file:projects/keyvault-secrets.tgz' version: 0.0.0 'file:projects/logger.tgz': dependencies: - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/mocha': 5.2.7 '@types/node': 8.10.59 '@types/sinon': 7.5.2 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 chai: 4.2.0 cross-env: 6.0.3 @@ -8496,25 +8477,25 @@ packages: dev: false name: '@rush-temp/logger' resolution: - integrity: sha512-WtwITLuox02tjZotL5EXlCBDioO9z7Zd08RU/xclUCg/3RCpLUfdTC2xdNFiBhr1CsMm9NLYID4VcRDqfw0FQg== + integrity: sha512-60QktvXXzcdfZs3v6MGAI5NPKuJW2kwziFFj4cSlLsnyiwTgNlfRgzFoddxefCYvGfKwobtxm2A8EKcyHL1wFQ== tarball: 'file:projects/logger.tgz' version: 0.0.0 'file:projects/search.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/mocha': 5.2.7 '@types/node': 8.10.59 '@types/sinon': 7.5.2 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 chai: 4.2.0 cross-env: 6.0.3 dotenv: 8.2.0 @@ -8543,7 +8524,7 @@ packages: rollup: 1.32.1 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 sinon: 7.5.0 ts-node: 8.6.2_typescript@3.7.5 tslib: 1.11.1 @@ -8552,14 +8533,14 @@ packages: dev: false name: '@rush-temp/search' resolution: - integrity: sha512-6QA0sKVcNaCv3TDdvbD4tSpaU9f+lUmOQYl3tgEYhgIh+4jBHh9vv9Cpga4/IQFVUwWQlDTbY2hlsfR+vRaZSw== + integrity: sha512-iudvP0IiTV64iQNYW/fi3e/ezwY9lrn8VuFBAYwOuIeVsTNjzYrinx76QCUewY/+KDznLVrgHvHYIOomsPOEVQ== tarball: 'file:projects/search.tgz' version: 0.0.0 'file:projects/service-bus.tgz': dependencies: - '@azure/eslint-plugin-azure-sdk': 2.0.1_ee45056a3a53530214d828c032d67185 + '@azure/eslint-plugin-azure-sdk': 2.0.1_6eb4f22a85ba1817e9c23ce411f002c2 '@azure/identity': 1.1.0-preview1 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-inject': 4.0.1_rollup@1.32.1 @@ -8568,7 +8549,7 @@ packages: '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 '@types/async-lock': 1.1.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/chai-as-promised': 7.1.2 '@types/debug': 4.1.5 '@types/glob': 7.1.1 @@ -8577,8 +8558,8 @@ packages: '@types/mocha': 5.2.7 '@types/node': 8.10.59 '@types/ws': 6.0.4 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 buffer: 5.5.0 chai: 4.2.0 @@ -8631,14 +8612,14 @@ packages: dev: false name: '@rush-temp/service-bus' resolution: - integrity: sha512-o01tMu6B1o7ABcN8vMFmRH8NMBWva5VkUaxvEVItRm65o5FLyhLvmg39KkmEz6JdgD16WulE+uEbIUdl0Yd+iA== + integrity: sha512-YLUzaP1woujpgUu30nEn5TUmYvse7SH1Ycgcq5AhRHRSpuYo//CKc/EVHUmXagoQOpNhq4dCom08nWvgwWvudw== tarball: 'file:projects/service-bus.tgz' version: 0.0.0 'file:projects/storage-blob.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 '@azure/identity': 1.1.0-preview1 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 @@ -8646,8 +8627,8 @@ packages: '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 '@types/mocha': 5.2.7 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 cross-env: 6.0.3 dotenv: 8.2.0 @@ -8685,7 +8666,7 @@ packages: rollup-plugin-shim: 1.0.0 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 source-map-support: 0.5.16 ts-node: 8.6.2_typescript@3.7.5 tslib: 1.11.1 @@ -8694,14 +8675,14 @@ packages: dev: false name: '@rush-temp/storage-blob' resolution: - integrity: sha512-JjNNxByexMxaQqbBHffzgp2DnG6gb2nRs/IILCB1d9r3SZ3/t1axzwzHbxQU3fmXN6hOO0brGqYe45IN9SsBPg== + integrity: sha512-vD9iQN11FhLR+c+0dvYlpsYu6j6x2KOUV2dwsNC2iJHJhba5fv3rhjii9tZYYiK+FQxtwpeMWxAvEEeo7Ilz7A== tarball: 'file:projects/storage-blob.tgz' version: 0.0.0 'file:projects/storage-file-datalake.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 '@azure/identity': 1.1.0-preview1 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 @@ -8712,8 +8693,8 @@ packages: '@types/nise': 1.4.0 '@types/node': 8.10.59 '@types/query-string': 6.2.0 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 cross-env: 6.0.3 dotenv: 8.2.0 @@ -8756,7 +8737,7 @@ packages: rollup-plugin-shim: 1.0.0 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 source-map-support: 0.5.16 ts-node: 8.6.2_typescript@3.7.5 tslib: 1.11.1 @@ -8765,13 +8746,13 @@ packages: dev: false name: '@rush-temp/storage-file-datalake' resolution: - integrity: sha512-YJACP135onaEKbupR2QZYFSf2be3vfYYimWmKDuE4pocBhObK1ylt/AnJA2QrGwItvok2z3xj0jLGE/F6JTCag== + integrity: sha512-13wUhte4I4jM5v1FEPPybCKErfXvff/6xOJg08aESStWYkL2islen6rFaoctjDgSUiZZOWTjzJpV0U86W98vFA== tarball: 'file:projects/storage-file-datalake.tgz' version: 0.0.0 'file:projects/storage-file-share.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 @@ -8779,8 +8760,8 @@ packages: '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 '@types/mocha': 5.2.7 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 cross-env: 6.0.3 dotenv: 8.2.0 @@ -8818,7 +8799,7 @@ packages: rollup-plugin-shim: 1.0.0 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 source-map-support: 0.5.16 ts-node: 8.6.2_typescript@3.7.5 tslib: 1.11.1 @@ -8827,14 +8808,14 @@ packages: dev: false name: '@rush-temp/storage-file-share' resolution: - integrity: sha512-2TEucaS7nUIXXgqLgAWgYhGjLEaTFzIBckjlSNFSqGJxbevcDsWsxBBSvl/M8ld0NTFHqIKnR/ZyJeUEPfcPpA== + integrity: sha512-hPtaJ00XahOTDFvanHCcYPbrL4TAaDT1d+8EjV+1Ijpj5WFjzgzFVmy8G2C7oXu9f4mSt8yjL1mjpQweA80VAA== tarball: 'file:projects/storage-file-share.tgz' version: 0.0.0 'file:projects/storage-queue.tgz': dependencies: '@azure/core-tracing': 1.0.0-preview.7 '@azure/identity': 1.1.0-preview1 - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 @@ -8842,8 +8823,8 @@ packages: '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 '@types/mocha': 5.2.7 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 cross-env: 6.0.3 dotenv: 8.2.0 @@ -8880,7 +8861,7 @@ packages: rollup-plugin-shim: 1.0.0 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 source-map-support: 0.5.16 ts-node: 8.6.2_typescript@3.7.5 tslib: 1.11.1 @@ -8889,12 +8870,12 @@ packages: dev: false name: '@rush-temp/storage-queue' resolution: - integrity: sha512-TYoGzVK8a27ch4yuWj4d/UkDPJNYdQH/d+0ToM7+10G/9384QHPcNRcniMqyVkrDZL8D/4rgfHMqudpxmUA+Lg== + integrity: sha512-UUxi+NcFNRId2Wr9dN0KJl9S+GrZ0WkeZIVk0af9uCNPkmtiRJBW/HmuLe7l8PHwwQyFvz+pHBBwjT+mtJeSvw== tarball: 'file:projects/storage-queue.tgz' version: 0.0.0 'file:projects/template.tgz': dependencies: - '@microsoft/api-extractor': 7.7.8 + '@microsoft/api-extractor': 7.7.10 '@opentelemetry/types': 0.2.0 '@rollup/plugin-commonjs': 11.0.2_rollup@1.32.1 '@rollup/plugin-json': 4.0.2_rollup@1.32.1 @@ -8903,8 +8884,8 @@ packages: '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 '@types/mocha': 5.2.7 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 assert: 1.5.0 cross-env: 6.0.3 eslint: 6.8.0 @@ -8933,14 +8914,14 @@ packages: rollup: 1.32.1 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 tslib: 1.11.1 typescript: 3.7.5 util: 0.12.2 dev: false name: '@rush-temp/template' resolution: - integrity: sha512-3wjLFt9oiVh5758lQ5J4UdDi90JhCk5mU4NdAwod4rTh9lE8NH6AqAbjzKhQ5RyHVEzqpJdVBx+43TgkCnvcsw== + integrity: sha512-6AQ8/uJmr6cE817OM71PjUncS+PIV034TaMB9agHiQmF7LozofZWHYf9WEOylwTvLAY04b5hAmQdZWRUPGgyow== tarball: 'file:projects/template.tgz' version: 0.0.0 'file:projects/test-utils-recorder.tgz': @@ -8950,7 +8931,7 @@ packages: '@rollup/plugin-multi-entry': 3.0.0_rollup@1.32.1 '@rollup/plugin-node-resolve': 7.1.1_rollup@1.32.1 '@rollup/plugin-replace': 2.3.1_rollup@1.32.1 - '@types/chai': 4.2.10 + '@types/chai': 4.2.11 '@types/fs-extra': 8.1.0 '@types/md5': 2.1.33 '@types/mocha': 5.2.7 @@ -8958,8 +8939,8 @@ packages: '@types/mock-require': 2.0.0 '@types/nise': 1.4.0 '@types/node': 8.10.59 - '@typescript-eslint/eslint-plugin': 2.23.0_9f87a6626aabebbe99f88159fc4926b8 - '@typescript-eslint/parser': 2.23.0_eslint@6.8.0+typescript@3.7.5 + '@typescript-eslint/eslint-plugin': 2.24.0_347dbd490cbb0b8bd07c94bdc05f318a + '@typescript-eslint/parser': 2.24.0_eslint@6.8.0+typescript@3.7.5 chai: 4.2.0 eslint: 6.8.0 eslint-plugin-no-only-tests: 2.4.0 @@ -8994,14 +8975,14 @@ packages: rollup-plugin-shim: 1.0.0 rollup-plugin-sourcemaps: 0.4.2_rollup@1.32.1 rollup-plugin-terser: 5.3.0_rollup@1.32.1 - rollup-plugin-visualizer: 3.3.1_rollup@1.32.1 + rollup-plugin-visualizer: 3.3.2_rollup@1.32.1 tslib: 1.11.1 typescript: 3.7.5 xhr-mock: 2.5.1 dev: false name: '@rush-temp/test-utils-recorder' resolution: - integrity: sha512-hWi9AH7zhw/6qiKumaAUr1CcF/i/rct5G2rcq4wwmcr16hMVIM+YXEuuEO42JGwDO34gHFgpzA53ky9iPiPm8w== + integrity: sha512-WOtvN21AzhsWP3Bm1bQs21V11O378t/hDgw8QyDR0/vejKnsbxUBSP8OlbV+vWCH9nANqAVSXZlMmMazErFGfA== tarball: 'file:projects/test-utils-recorder.tgz' version: 0.0.0 'file:projects/testhub.tgz': @@ -9013,19 +8994,18 @@ packages: async-lock: 1.2.2 death: 1.1.0 debug: 4.1.1 - rhea: 1.0.19 + rhea: 1.0.20 rimraf: 3.0.2 tslib: 1.11.1 typescript: 3.7.5 uuid: 3.4.0 - yargs: 15.3.0 + yargs: 15.3.1 dev: false name: '@rush-temp/testhub' resolution: - integrity: sha512-guDU8PdEdKCVnGxNd1JEkmqukDoc1wodkEqQCWpY1+bX4ZT+ZY520gfVcMeMHYCEO8TAAhScGNke/y7p9qBArA== + integrity: sha512-dW7m2LfMTGWZVxeZCvuHSzjBQBmz1868xK4zIx5AQifXDsBDrrQ3wdPHnarQV4yI0wcyzcI0QnVzRJ42++O4Hg== tarball: 'file:projects/testhub.tgz' version: 0.0.0 -registry: '' specifiers: '@rush-temp/abort-controller': 'file:./projects/abort-controller.tgz' '@rush-temp/ai-text-analytics': 'file:./projects/ai-text-analytics.tgz' @@ -9039,6 +9019,7 @@ specifiers: '@rush-temp/core-paging': 'file:./projects/core-paging.tgz' '@rush-temp/core-tracing': 'file:./projects/core-tracing.tgz' '@rush-temp/cosmos': 'file:./projects/cosmos.tgz' + '@rush-temp/dev-tool': 'file:./projects/dev-tool.tgz' '@rush-temp/eslint-plugin-azure-sdk': 'file:./projects/eslint-plugin-azure-sdk.tgz' '@rush-temp/event-hubs': 'file:./projects/event-hubs.tgz' '@rush-temp/event-processor-host': 'file:./projects/event-processor-host.tgz' diff --git a/common/tools/dev-tool/launch.js b/common/tools/dev-tool/launch.js new file mode 100755 index 000000000000..f32bf0beab34 --- /dev/null +++ b/common/tools/dev-tool/launch.js @@ -0,0 +1,14 @@ +#!/usr/bin/env node + +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +const path = require("path"); + +// Shim to invoke true typescript source +require("ts-node").register({ + transpileOnly: true, + project: path.join(__dirname, "tsconfig.json") +}); +require("@azure/dev-tool/src/index.ts"); + diff --git a/common/tools/dev-tool/package.json b/common/tools/dev-tool/package.json new file mode 100644 index 000000000000..563613ef269c --- /dev/null +++ b/common/tools/dev-tool/package.json @@ -0,0 +1,43 @@ +{ + "name": "@azure/dev-tool", + "version": "1.0.0", + "description": "A helpful command for azure-sdk-for-js developers", + "bin": { + "dev-tool": "launch.js" + }, + "scripts": { + "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", + "build": "echo", + "clean": "rimraf dist dist-* *.tgz *.log", + "extract-api": "echo skipped", + "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", + "pack": "npm pack 2>&1", + "prebuild": "npm run clean", + "test": "./launch.js --num 10 --pre pre command --bool --bool2=true --post post -- --after --after-bool=true " + }, + "repository": "github:Azure/azure-sdk-for-js", + "author": "Microsoft Corporation", + "license": "MIT", + "bugs": { + "url": "https://github.com/azure/azure-sdk-for-js/issues" + }, + "homepage": "https://github.com/azure/azure-sdk-for-js/tree/master/common/tools/dev-tool", + "sideEffects": false, + "private": true, + "dependencies": { + "@azure/logger": "^1.0.0", + "fs-extra": "^8.1.0", + "ts-node": "^8.3.0", + "typescript": "~3.7.5", + "inquirer": "~7.0.4", + "minimist": "~1.2.0", + "chalk": "~3.0.0" + }, + "devDependencies": { + "@types/fs-extra": "^8.0.0", + "@types/node": "^8.0.0", + "rimraf": "^3.0.0", + "@types/minimist": "~1.2.0", + "@types/chalk": "~2.2.0" + } +} diff --git a/common/tools/dev-tool/src/commands/prep-samples.ts b/common/tools/dev-tool/src/commands/prep-samples.ts new file mode 100644 index 000000000000..9ced2cab1bae --- /dev/null +++ b/common/tools/dev-tool/src/commands/prep-samples.ts @@ -0,0 +1,138 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * prep-samples.ts + * + * Prepares sample files for execution in CI by replacing abosolute package imports with relative + * imports. This is useful because it allows us to check in "camera-ready" copies of our samples + * so that they can be ingested directly into external documentation pipelines, while still allowing + * us to compile and run our samples in CI. + * + * Usage: node prep-samples.js [PACKAGE PATH] + * - PACKAGE PATH should be set to the directory of a `package.json` for a package that contains + * TypeScript samples. + * - If PACKAGE PATH is not specified, CWD will be used + * + * The command expects to find a directory tree `samples/typescript` under PACKAGE PATH. + * + * WARNING: This script ___WILL NOT___ revert changes it makes to the samples. Make sure any staged + * changes you have made to the samples are committed to git or otherwise preserved, as this script + * will completely overwrite them! + */ + +import fs from "fs-extra"; +import minimist from "minimist"; +import path from "path"; + +import { createPrinter } from "../util/printer"; +import { findMatchingFiles } from "../util/findMatchingFiles"; + +const log = createPrinter("prep-samples"); + +/** + * Replaces package require/import statements with relative paths for CI + * + * @param fileName the name of the file to open and process + * @param baseDir the base directory of the package + * @param pkgName name of the package to use when looking for package-local imports + */ +async function enableLocalRun( + fileName: string, + baseDir: string, + pkgName: string +) { + const fileContents = await fs.readFile(fileName, { encoding: "utf-8" }); + const isTs = fileName.endsWith(".ts"); + const importRegex = isTs + ? new RegExp(`import\\s+(.*)\\s+from\\s+"${pkgName}";?\\s?`, "s") + : new RegExp(`const\\s+(.*)\\s*=\\s*require\\("${pkgName}"\\);?\\s?`, "s"); + + if (!importRegex.exec(fileContents)) { + // With the newer methods of using helper files and batch running, this + // should be a warning + log.warn( + `skipping ${fileName} because it did not contain a matching import/require` + ); + return; + } + + const relativeDir = path.dirname(fileName.replace(baseDir, "")); + + // `string.length - string.split(path.sep).join("").length` is a dirty but well-supported way to + // count the depth of a path and that avoids the difficulty of creating a regexp constructor + // that can escape both linux and windows path separators + const depth = + relativeDir.length - relativeDir.split(path.sep).join("").length; + + let relativePath = new Array(depth).fill("..").join("/"); + + if (isTs) { + // TypeScript imports should use src directly + relativePath += "/src"; + } + + const importRenamedContents = fileContents.replace( + importRegex, + isTs + ? `import $1 from "${relativePath}";` + : `const $1 = require("${relativePath}");` + ); + + // Remove trailing call to main() + const updatedContents = importRenamedContents.replace( + new RegExp("main\\(\\)\\.catch.*", "s"), + isTs ? "" : "module.exports = { main };\n" + ); + + log("Updating imports in", fileName); + return fs.writeFile(fileName, updatedContents, { encoding: "utf-8" }); +} + +async function* cat(...generators: AsyncIterable[]): AsyncIterable { + for (const g of generators) { + yield* g; + } +} + +export default async function main(...args: string[]): Promise { + const parsedArgs = minimist(args); + + let baseDir; + if (parsedArgs._.length) { + baseDir = path.resolve(parsedArgs._[0]); + } else { + baseDir = process.cwd(); + } + + const pkg = require(path.join(baseDir, "package.json")); + log.info("Preparing samples for package:", `${pkg.name}@${pkg.version}`); + + // Create dist-samples and copy to it + const outputDir = path.join(baseDir, "dist-samples"); + if (fs.existsSync(outputDir)) { + log.warn("Cleaning up old dist-samples folder."); + await fs.remove(outputDir); + } + await fs.copy(path.join(baseDir, "samples"), outputDir); + + const tsDir = path.join(outputDir, "typescript", "src"); + const tsFiles = findMatchingFiles( + tsDir, + (name, entry) => + entry.isFile() && name.endsWith(".ts") && !name.endsWith(".d.ts") + ); + + const jsDir = path.join(outputDir, "javascript"); + const jsFiles = findMatchingFiles( + jsDir, + (name, entry) => entry.isFile() && name.endsWith(".js") + ); + + for await (const fileName of cat(tsFiles, jsFiles)) { + await enableLocalRun(fileName, baseDir, pkg.name); + } + + return true; +} + diff --git a/common/tools/dev-tool/src/commands/run-samples.ts b/common/tools/dev-tool/src/commands/run-samples.ts new file mode 100644 index 000000000000..e4b243fe4ad7 --- /dev/null +++ b/common/tools/dev-tool/src/commands/run-samples.ts @@ -0,0 +1,69 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +/** + * run-samples.js + * + * Runs all JavaScript files in a directory, using the calling convention for + * our sample code. + */ + +import chalk from "chalk"; +import path from "path"; + +import { findMatchingFiles } from "../util/findMatchingFiles"; +import { createPrinter } from "../util/printer"; + +const log = createPrinter("run-samples"); + +const IGNORE = ["node_modules"]; + +export default async function(...args: string[]): Promise { + if (args.length === 0) { + throw new Error("At least one argument is required for run-samples"); + } + + const sampleDirs = args.map(dir => path.resolve(dir)); + + // Patch the environment for the sample helper + process.env.BATCH_RUN_SAMPLES = "true"; + + log.info("Running all samples in:", sampleDirs.join(", ")); + + let errors = []; + + for (const sampleDir of sampleDirs) { + for await (const fileName of findMatchingFiles( + sampleDir, + (name, entry) => entry.isFile() && name.endsWith(".js"), + { + ignore: IGNORE + } + )) { + log("Running", fileName); + const { main: sampleMain } = require(fileName); + try { + await sampleMain(); + } catch (err) { + const truncatedError = err + .toString() + .split("\n")[0] + .slice(0, 100); + errors.push([path.basename(fileName), truncatedError]); + log.warn("Error in", fileName, ":", err); + log.warn("Continuing ..."); + } + } + } + + if (errors.length > 0) { + log.error("Errors occurred in the following files:"); + for (const [fileName, error] of errors) { + log.error(" -", fileName, "(", error, ")"); + } + + return false; + } + + return true; +} diff --git a/common/tools/dev-tool/src/index.ts b/common/tools/dev-tool/src/index.ts new file mode 100644 index 000000000000..76dba317a109 --- /dev/null +++ b/common/tools/dev-tool/src/index.ts @@ -0,0 +1,36 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import chalk from "chalk"; +import getArgs from "minimist"; + +import { createPrinter } from "./util/printer"; + +const log = createPrinter("dev-tool"); + +const args = getArgs(process.argv.slice(2), { + stopEarly: true +}); + +async function main() { + const commandName = args._[0]; + const commandArgs = args._.slice(1); + + log.info(`$ ${commandName} ${commandArgs?.join(" ") ?? ""}`); + + const main = require("./commands/" + commandName + ".ts").default; + + const status = await main(...commandArgs); + + if (status) { + log.success("Finished."); + } else { + log.error("Errors occurred. See the output above."); + } +} + +main().catch(err => { + log.error("An exception occurred."); + console.trace(chalk.red(err)); +}); + diff --git a/common/tools/dev-tool/src/util/findMatchingFiles.ts b/common/tools/dev-tool/src/util/findMatchingFiles.ts new file mode 100644 index 000000000000..247bbad2daa1 --- /dev/null +++ b/common/tools/dev-tool/src/util/findMatchingFiles.ts @@ -0,0 +1,79 @@ +import chalk from "chalk"; +import fs from "fs-extra"; +import path from "path"; + +/** + * File information used during breadth-first search + */ +interface FileInfo { + dir: string; + fullPath: string; + name: string; + stat: fs.Stats; +} + +export interface FindOptions { + ignore: string[]; +} + +/** + * Breadth-first search for files matching a given predicate + * + * @param dir The root of the sample tree to search + * @param matches Predicate that decides whether or not a file entry is included + */ +export async function* findMatchingFiles( + dir: string, + matches: (name: string, entry: fs.Stats) => boolean, + options?: Partial +) { + const q: FileInfo[] = []; + + const ignore = options?.ignore ?? []; + + async function enqueueAll(dir: string) { + const files = await fs.readdir(dir); + for (const file of files) { + const fullPath = path.join(dir, file); + q.push({ + dir, + fullPath, + name: file, + stat: await fs.stat(fullPath) + }); + } + } + + await enqueueAll(dir); + + while (q.length) { + const info = q.shift() as FileInfo; + + if (ignore.includes(info.name)) { + console.warn(chalk.yellow("[run-samples] Ignoring", info.fullPath)); + continue; + } + + if (info.stat.isDirectory()) { + await enqueueAll(info.fullPath); + } else if (matches(info.name, info.stat)) { + yield info.fullPath; + } else if ( + info.stat.isBlockDevice() || + info.stat.isCharacterDevice() || + info.stat.isFIFO() || + info.stat.isSocket() || + info.stat.isSymbolicLink() + ) { + console.warn( + "[prep-samples] WARNING: Encountered a special file in the sample tree. Skipping:", + info.fullPath + ); + } + } + + // The full trace of files visited by the iterator is returned and can be accessed using `iter.value` + // once it is `done`, in case it is ever needed for debugging + return q; +} + diff --git a/common/tools/dev-tool/src/util/printer.ts b/common/tools/dev-tool/src/util/printer.ts new file mode 100644 index 000000000000..e921fae891ae --- /dev/null +++ b/common/tools/dev-tool/src/util/printer.ts @@ -0,0 +1,48 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import chalk from "chalk"; + +export type StringConsumer = (...values: string[]) => T; +export type PrintFn = StringConsumer; + +export type PrintMode = ["info", "warn", "error", "success", "debug"]; +export type ModeMap = { [k in PrintMode[number]]: T }; + +export interface Printer extends ModeMap { + (...values: string[]): void; +} + +const printModes: PrintMode = ["info", "warn", "error", "success", "debug"]; + +const colors: ModeMap> = { + info: chalk.blueBright, + warn: chalk.yellow, + error: chalk.red, + debug: chalk.magenta, + success: chalk.green +}; + +const finalLogger: ModeMap = { + info: console.info, + warn: console.warn, + error: console.error, + debug: (...values: string[]) => { + if (process.env.DEBUG) { + console.log(values); + } + }, + success: console.info +}; + +export const createPrinter = (name: string): Printer => { + const prefix = "[" + name + "]"; + const base = (...values: string[]) => + console.log(chalk.reset(prefix, ...values)); + + for (const mode of printModes) { + (base as any)[mode] = (...values: string[]) => + finalLogger[mode](colors[mode](prefix, ...values)); + } + return base as Printer; +}; diff --git a/common/tools/dev-tool/tsconfig.json b/common/tools/dev-tool/tsconfig.json new file mode 100644 index 000000000000..64c720302491 --- /dev/null +++ b/common/tools/dev-tool/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "es6", + "module": "commonjs", + "moduleResolution": "node", + "noEmit": true, + "lib": ["ES6", "ESNext.AsyncIterable"], + "esModuleInterop": true, + + "newLine": "LF", + "strict": true, + "alwaysStrict": true, + "noImplicitAny": true, + "noUnusedLocals": true, + "noUnusedParameters": true + }, + "exclude": ["node_modules"], + "include": ["./src/**/*.ts", "./test/**/*.ts"] +} diff --git a/rush.json b/rush.json index 4aa321db4d6f..0272e3685cf7 100644 --- a/rush.json +++ b/rush.json @@ -387,6 +387,11 @@ "projectFolder": "sdk/cosmosdb/cosmos", "versionPolicyName": "client" }, + { + "packageName": "@azure/dev-tool", + "projectFolder": "common/tools/dev-tool", + "versionPolicyName": "utility" + }, { "packageName": "@azure/event-hubs", "projectFolder": "sdk/eventhub/event-hubs", diff --git a/sdk/textanalytics/ai-text-analytics/package.json b/sdk/textanalytics/ai-text-analytics/package.json index 674ba1bc664b..00a9949e1607 100644 --- a/sdk/textanalytics/ai-text-analytics/package.json +++ b/sdk/textanalytics/ai-text-analytics/package.json @@ -46,14 +46,12 @@ "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c 2>&1", "build:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c 2>&1", - "build:samples": "node ../../../common/scripts/prep-samples.js && cd samples && tsc -p .", + "build:samples": "dev-tool prep-samples && cd dist-samples && tsc -p .", "build:test": "tsc -p . && rollup -c rollup.test.config.js 2>&1", "build": "tsc -p . && rollup -c 2>&1 && api-extractor run --local", "check-format": "prettier --list-different --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist dist-browser dist-esm test-dist temp types *.tgz *.log", - "execute:js-samples": "node ../../../common/scripts/run-samples.js samples/javascript/", - "execute:ts-samples": "node ../../../common/scripts/run-samples.js samples/typescript/dist/samples/typescript/src/", - "execute:samples": "npm run build:samples && npm run execute:js-samples && npm run execute:ts-samples", + "execute:samples": "npm run build:samples && dev-tool run-samples dist-samples/javascript dist-samples/typescript/dist/samples/typescript/src/", "extract-api": "tsc -p . && api-extractor run --local", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", @@ -81,6 +79,7 @@ }, "devDependencies": { "@azure/identity": "1.1.0-preview1", + "@azure/dev-tool": "^1.0.0", "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-commonjs": "^11.0.1", From 8a01be348f80e2db35a8a8cc4710403de6974b4a Mon Sep 17 00:00:00 2001 From: Will Temple Date: Wed, 11 Mar 2020 10:58:04 -0700 Subject: [PATCH 02/24] [dev-tool] dev-tool dev-samples and fixes to all commands --- common/tools/dev-tool/launch.js | 6 +- common/tools/dev-tool/package.json | 5 +- .../dev-tool/src/commands/dev-samples.ts | 45 +++++++++++ common/tools/dev-tool/src/commands/help.ts | 73 +++++++++++++++++ common/tools/dev-tool/src/commands/index.ts | 31 ++++++++ .../dev-tool/src/commands/prep-samples.ts | 21 ++--- .../dev-tool/src/commands/resolve-package.ts | 30 +++++++ .../dev-tool/src/commands/run-samples.ts | 3 +- common/tools/dev-tool/src/index.ts | 45 +++++++---- .../dev-tool/src/util/findMatchingFiles.ts | 4 +- common/tools/dev-tool/src/util/printer.ts | 50 ++++++++---- .../tools/dev-tool/src/util/resolveProject.ts | 79 +++++++++++++++++++ common/tools/dev-tool/tsconfig.json | 2 - .../ai-text-analytics/package.json | 2 +- 14 files changed, 352 insertions(+), 44 deletions(-) create mode 100644 common/tools/dev-tool/src/commands/dev-samples.ts create mode 100644 common/tools/dev-tool/src/commands/help.ts create mode 100644 common/tools/dev-tool/src/commands/index.ts create mode 100644 common/tools/dev-tool/src/commands/resolve-package.ts create mode 100644 common/tools/dev-tool/src/util/resolveProject.ts diff --git a/common/tools/dev-tool/launch.js b/common/tools/dev-tool/launch.js index f32bf0beab34..1678e54c0255 100755 --- a/common/tools/dev-tool/launch.js +++ b/common/tools/dev-tool/launch.js @@ -5,10 +5,14 @@ const path = require("path"); +if (process.env.DEBUG) { + console.info("Azure SDK for JS dev-tool: bootstrapping from", __dirname); +} + // Shim to invoke true typescript source require("ts-node").register({ transpileOnly: true, project: path.join(__dirname, "tsconfig.json") }); -require("@azure/dev-tool/src/index.ts"); +require(path.join(__dirname, "src", "index.ts")); diff --git a/common/tools/dev-tool/package.json b/common/tools/dev-tool/package.json index 563613ef269c..bf75ed4d268a 100644 --- a/common/tools/dev-tool/package.json +++ b/common/tools/dev-tool/package.json @@ -5,9 +5,12 @@ "bin": { "dev-tool": "launch.js" }, + "files": [ + "src" + ], "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", - "build": "echo", + "build": "tsc", "clean": "rimraf dist dist-* *.tgz *.log", "extract-api": "echo skipped", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", diff --git a/common/tools/dev-tool/src/commands/dev-samples.ts b/common/tools/dev-tool/src/commands/dev-samples.ts new file mode 100644 index 000000000000..9dfb2bf662dd --- /dev/null +++ b/common/tools/dev-tool/src/commands/dev-samples.ts @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import fs from "fs-extra"; +import path from "path"; + +import { resolveProject } from "../util/resolveProject"; +import { createPrinter } from "../util/printer"; + +const log = createPrinter("dev-samples"); + +export const helpText = + "link samples to local sources for access to IntelliSense during development"; + +export default async function(..._: string[]): Promise { + const pkg = await resolveProject(process.cwd()); + + const relativeLinkName = path.join( + "samples", + "typescript", + "node_modules", + pkg.name + ); + const linkName = path.join(pkg.path, relativeLinkName); + const relativeLinkTarget = path.relative( + linkName, + path.join(pkg.path, "samples") + ); + + await fs.ensureDir(path.dirname(linkName)); + + if (fs.existsSync(linkName)) { + log.error("Link already exists:", linkName); + log.error( + "Make sure your samples tree is pristine before running dev-samples." + ); + return false; + } + + log.info(`Linking ${relativeLinkName} to ${relativeLinkTarget}`); + + await fs.symlink(relativeLinkTarget, linkName); + + return true; +} diff --git a/common/tools/dev-tool/src/commands/help.ts b/common/tools/dev-tool/src/commands/help.ts new file mode 100644 index 000000000000..9a5e5cbb270a --- /dev/null +++ b/common/tools/dev-tool/src/commands/help.ts @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import chalk from "chalk"; +import { commands } from "."; + +const banner = `\ + _______ __ __ __ + / / ___/ ____/ /__ _ __ / /_____ ____ / / + __ / /\\__ \\ / __ / _ \\ | / /_____/ __/ __ \\/ __ \\/ / +/ /_/ /___/ / / /_/ / __/ |/ /_____/ /_/ /_/ / /_/ / / +\\____//____/ \\__,_/\\___/|___/ \\__/\\____/\\____/_/ + +Developer quality-of-life command for the Azure SDK for JS`; + +const usage = ` +Usage: dev-tool [OPTIONS] COMMAND [COMMAND-OPTIONS] + +COMMAND indicates the subcommand to be run. It can be one of the following: +`; + +export const helpText = "display this help message"; + +/** + * Prints the one-line usage of each command. + * + * @param println + */ +export async function printCommandUsage( + println: (...values: string[]) => void +) { + println(usage); + + // Compute the number of tabs needed to separate commands from + // docstrings assuming a default command-line tabstop of 8 + const tabs = Math.ceil( + (Math.max( + ...Object.keys(commands) + .filter(key => commands.hasOwnProperty(key)) + .map(key => key.length + 2) + ) + + 1) / + 8 + ); + + for (const command in commands) { + if (commands.hasOwnProperty(command)) { + const module = await commands[command](); + const indent = "\t".repeat(tabs - Math.floor((command.length + 2) / 8)); + println(` ${command}${indent}${module.helpText}`); + } + } + + println(); +} + +export default async function(...args: string[]): Promise { + console.info(chalk.blueBright(banner)); + + if (args.length > 0) { + console.warn(chalk.yellow("Warning, unused arguments:", args), "\n"); + } + + await printCommandUsage(console.info); + + console.info( + "For more information about a given command, try `dev-tool COMMAND --help`" + ); + + console.info(); + + return true; +} diff --git a/common/tools/dev-tool/src/commands/index.ts b/common/tools/dev-tool/src/commands/index.ts new file mode 100644 index 000000000000..5b84ecf5ba0f --- /dev/null +++ b/common/tools/dev-tool/src/commands/index.ts @@ -0,0 +1,31 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +/** + * A command for use with the dev-tool + */ +export interface Command { + /** + * The main async function of the command. + * + * @param args the arguments passed to the command + * + * @returns a promise resolving to `true` if the command succeeded, `false` otherwise + */ + default: (...args: string[]) => Promise; + /** + * One line of help text to be printed by the `dev-tool help` command + */ + helpText: string; +} + +/** + * All of dev-tool's commands and the modules that define them + */ +export const commands: { [k: string]: () => Promise } = { + "dev-samples": () => import("./dev-samples"), + help: () => import("./help"), + "prep-samples": () => import("./prep-samples"), + "resolve-package": () => import("./resolve-package"), + "run-samples": () => import("./run-samples") +}; diff --git a/common/tools/dev-tool/src/commands/prep-samples.ts b/common/tools/dev-tool/src/commands/prep-samples.ts index 9ced2cab1bae..68ddcf69692e 100644 --- a/common/tools/dev-tool/src/commands/prep-samples.ts +++ b/common/tools/dev-tool/src/commands/prep-samples.ts @@ -27,9 +27,12 @@ import path from "path"; import { createPrinter } from "../util/printer"; import { findMatchingFiles } from "../util/findMatchingFiles"; +import { resolveProject } from "../util/resolveProject"; const log = createPrinter("prep-samples"); +export const helpText = "prepare samples for local source-linked execution"; + /** * Replaces package require/import statements with relative paths for CI * @@ -95,26 +98,27 @@ async function* cat(...generators: AsyncIterable[]): AsyncIterable { } } -export default async function main(...args: string[]): Promise { +export default async function(...args: string[]): Promise { const parsedArgs = minimist(args); - let baseDir; + let argumentDir; if (parsedArgs._.length) { - baseDir = path.resolve(parsedArgs._[0]); + argumentDir = path.resolve(parsedArgs._[0]); } else { - baseDir = process.cwd(); + argumentDir = process.cwd(); } - const pkg = require(path.join(baseDir, "package.json")); + const pkg = await resolveProject(argumentDir); + log.info("Preparing samples for package:", `${pkg.name}@${pkg.version}`); // Create dist-samples and copy to it - const outputDir = path.join(baseDir, "dist-samples"); + const outputDir = path.join(pkg.path, "dist-samples"); if (fs.existsSync(outputDir)) { log.warn("Cleaning up old dist-samples folder."); await fs.remove(outputDir); } - await fs.copy(path.join(baseDir, "samples"), outputDir); + await fs.copy(path.join(pkg.path, "samples"), outputDir); const tsDir = path.join(outputDir, "typescript", "src"); const tsFiles = findMatchingFiles( @@ -130,9 +134,8 @@ export default async function main(...args: string[]): Promise { ); for await (const fileName of cat(tsFiles, jsFiles)) { - await enableLocalRun(fileName, baseDir, pkg.name); + await enableLocalRun(fileName, pkg.path, pkg.name); } return true; } - diff --git a/common/tools/dev-tool/src/commands/resolve-package.ts b/common/tools/dev-tool/src/commands/resolve-package.ts new file mode 100644 index 000000000000..ec6d6201d761 --- /dev/null +++ b/common/tools/dev-tool/src/commands/resolve-package.ts @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import path from "path"; + +import { resolveProject } from "../util/resolveProject"; +import { createPrinter } from "../util/printer"; + +const log = createPrinter("resolve-package"); + +export const helpText = + "display information about the project that owns the current directory"; + +export default async function(..._: string[]): Promise { + const cwd = process.cwd(); + try { + const currentPackage = await resolveProject(cwd); + log.success("== Detected package:", currentPackage.name); + log.info( + `Version specifier: ${currentPackage.name}@${currentPackage.version}` + ); + log.info(`Location: ${path.resolve(cwd, currentPackage.path)}`); + } catch (error) { + log.error("Could not find package starting from", cwd); + log.error(error); + return false; + } + + return true; +} diff --git a/common/tools/dev-tool/src/commands/run-samples.ts b/common/tools/dev-tool/src/commands/run-samples.ts index e4b243fe4ad7..ad71a50c0389 100644 --- a/common/tools/dev-tool/src/commands/run-samples.ts +++ b/common/tools/dev-tool/src/commands/run-samples.ts @@ -8,7 +8,6 @@ * our sample code. */ -import chalk from "chalk"; import path from "path"; import { findMatchingFiles } from "../util/findMatchingFiles"; @@ -18,6 +17,8 @@ const log = createPrinter("run-samples"); const IGNORE = ["node_modules"]; +export const helpText = "execute a sample or all samples within a directory"; + export default async function(...args: string[]): Promise { if (args.length === 0) { throw new Error("At least one argument is required for run-samples"); diff --git a/common/tools/dev-tool/src/index.ts b/common/tools/dev-tool/src/index.ts index 76dba317a109..02149ccc42d9 100644 --- a/common/tools/dev-tool/src/index.ts +++ b/common/tools/dev-tool/src/index.ts @@ -1,36 +1,53 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import chalk from "chalk"; import getArgs from "minimist"; import { createPrinter } from "./util/printer"; -const log = createPrinter("dev-tool"); +import { commands } from "./commands"; +import { printCommandUsage } from "./commands/help"; -const args = getArgs(process.argv.slice(2), { - stopEarly: true -}); +const log = createPrinter("dev-tool"); +/** + * Entry point for the dev-tool command. + */ async function main() { + const args = getArgs(process.argv.slice(2), { + stopEarly: true + }); + + log.debug("Parsed command line:", JSON.stringify(args)); + const commandName = args._[0]; const commandArgs = args._.slice(1); - log.info(`$ ${commandName} ${commandArgs?.join(" ") ?? ""}`); + if (commandName === undefined) { + log.error("No command provided."); + await printCommandUsage(console.error); + process.exit(1); + } - const main = require("./commands/" + commandName + ".ts").default; + log.info(`$ ${commandName} ${commandArgs?.join(" ") ?? ""}`); - const status = await main(...commandArgs); + if (commands.hasOwnProperty(commandName)) { + const commandModule = await commands[commandName](); + const status = await commandModule.default(...commandArgs); - if (status) { - log.success("Finished."); + if (status) { + log.success("Finished."); + } else { + log.error("Errors occurred. See the output above."); + } } else { - log.error("Errors occurred. See the output above."); + log.error("No such command:", commandName); + await printCommandUsage(console.error); + process.exit(1); } } main().catch(err => { - log.error("An exception occurred."); - console.trace(chalk.red(err)); + log.error("An exception occurred:", err); + process.exit(1); }); - diff --git a/common/tools/dev-tool/src/util/findMatchingFiles.ts b/common/tools/dev-tool/src/util/findMatchingFiles.ts index 247bbad2daa1..5b44e1e6c976 100644 --- a/common/tools/dev-tool/src/util/findMatchingFiles.ts +++ b/common/tools/dev-tool/src/util/findMatchingFiles.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + import chalk from "chalk"; import fs from "fs-extra"; import path from "path"; @@ -76,4 +79,3 @@ export async function* findMatchingFiles( // once it is `done`, in case it is ever needed for debugging return q; } - diff --git a/common/tools/dev-tool/src/util/printer.ts b/common/tools/dev-tool/src/util/printer.ts index e921fae891ae..75c1ce99e531 100644 --- a/common/tools/dev-tool/src/util/printer.ts +++ b/common/tools/dev-tool/src/util/printer.ts @@ -3,19 +3,19 @@ import chalk from "chalk"; -export type StringConsumer = (...values: string[]) => T; -export type PrintFn = StringConsumer; +const printModes = ["info", "warn", "error", "success", "debug"] as const; -export type PrintMode = ["info", "warn", "error", "success", "debug"]; -export type ModeMap = { [k in PrintMode[number]]: T }; +export type Fn = (...values: any[]) => T; +export type ModeMap = { [k in typeof printModes[number]]: T }; -export interface Printer extends ModeMap { - (...values: string[]): void; +/** + * The interface that describes the Printer produced by {@link createPrinter} + */ +export interface Printer extends ModeMap { + (...values: any[]): void; } -const printModes: PrintMode = ["info", "warn", "error", "success", "debug"]; - -const colors: ModeMap> = { +const colors: ModeMap> = { info: chalk.blueBright, warn: chalk.yellow, error: chalk.red, @@ -23,19 +23,41 @@ const colors: ModeMap> = { success: chalk.green }; -const finalLogger: ModeMap = { +const finalLogger: ModeMap = { info: console.info, warn: console.warn, error: console.error, - debug: (...values: string[]) => { + debug(...values: string[]) { if (process.env.DEBUG) { - console.log(values); + console.log(...values); } }, success: console.info }; -export const createPrinter = (name: string): Printer => { +/** + * Create a pre-configured console printer for a given namespace. + * + * ```javascript + * const log = createPrinter("my-command"); + * ``` + * + * The printer can be called directly (`log("A message")`), or a + * log level can be specified (`log.error("An error message")`). + * + * The printer outputs `[]` before each message and colorizes terminal + * output as appropriate using `chalk` according to the log level. The colors are: + * + * - no log level (called directly): white + * - info: bright blue (ANSI #12) + * - warn: yellow (ANSI #3) + * - error: red (ANSI #1) + * - debug: magenta (ANSI #5) + * - success: green (ANSI #2) + * + * @param name the namespace to format log messages with + */ +export function createPrinter(name: string): Printer { const prefix = "[" + name + "]"; const base = (...values: string[]) => console.log(chalk.reset(prefix, ...values)); @@ -45,4 +67,4 @@ export const createPrinter = (name: string): Printer => { finalLogger[mode](colors[mode](prefix, ...values)); } return base as Printer; -}; +} diff --git a/common/tools/dev-tool/src/util/resolveProject.ts b/common/tools/dev-tool/src/util/resolveProject.ts new file mode 100644 index 000000000000..9432c31ba9ef --- /dev/null +++ b/common/tools/dev-tool/src/util/resolveProject.ts @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import fs from "fs-extra"; +import path from "path"; + +import { createPrinter } from "./printer"; + +const { debug } = createPrinter("project resolution"); + +export interface ProjectInfo { + name: string; + path: string; + version: string; + packageJson: any; +} + +async function isAzureSDKPackage(fileName: string): Promise { + const f = await import(fileName); + + if ((f.name as string).startsWith("@azure/")) { + return true; + } else { + return false; + } +} + +async function findAzSDKPackageJson(directory: string): Promise<[string, any]> { + const files = await fs.readdir(directory); + + if (files.includes("rush.json")) { + throw new Error( + "Reached monorepo root, but no matching Azure SDK package was found." + ); + } + + for (const file of files) { + if (file === "package.json") { + const fullPath = path.join(directory, file); + const packageObject = await import(fullPath); + if (await isAzureSDKPackage(fullPath)) { + return [directory, packageObject]; + } + debug( + `found package.json at ${fullPath}, but it is not an Azure SDK package` + ); + } + } + + return findAzSDKPackageJson(path.join(directory, "..")); +} + +/** + * Determine which Azure SDK project a given directory belongs to. + * + * @param workingDirectory + */ +export async function resolveProject( + workingDirectory: string +): Promise { + if (!fs.existsSync(workingDirectory)) { + throw new Error(`No such file or directory: ${workingDirectory}`); + } + + const directory = await fs.stat(workingDirectory); + + if (!directory.isDirectory()) { + throw new Error(`${workingDirectory} is not a directory`); + } + + const [path, packageJson] = await findAzSDKPackageJson(workingDirectory); + + return { + name: packageJson.name, + path, + version: packageJson.version, + packageJson + }; +} diff --git a/common/tools/dev-tool/tsconfig.json b/common/tools/dev-tool/tsconfig.json index 64c720302491..840318d2bd4b 100644 --- a/common/tools/dev-tool/tsconfig.json +++ b/common/tools/dev-tool/tsconfig.json @@ -1,7 +1,5 @@ { "compilerOptions": { - "target": "es6", - "module": "commonjs", "moduleResolution": "node", "noEmit": true, "lib": ["ES6", "ESNext.AsyncIterable"], diff --git a/sdk/textanalytics/ai-text-analytics/package.json b/sdk/textanalytics/ai-text-analytics/package.json index 00a9949e1607..19f1336212e2 100644 --- a/sdk/textanalytics/ai-text-analytics/package.json +++ b/sdk/textanalytics/ai-text-analytics/package.json @@ -51,7 +51,7 @@ "build": "tsc -p . && rollup -c 2>&1 && api-extractor run --local", "check-format": "prettier --list-different --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist dist-browser dist-esm test-dist temp types *.tgz *.log", - "execute:samples": "npm run build:samples && dev-tool run-samples dist-samples/javascript dist-samples/typescript/dist/samples/typescript/src/", + "execute:samples": "npm run build:samples && dev-tool run-samples dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", "extract-api": "tsc -p . && api-extractor run --local", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", From 7970f3e5b84a15e77443ef4336cee3676bbbc707 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Fri, 13 Mar 2020 17:14:48 -0700 Subject: [PATCH 03/24] Nested command structure --- common/tools/dev-tool/src/commands/help.ts | 26 ++++++++-- common/tools/dev-tool/src/commands/index.ts | 13 +++-- common/tools/dev-tool/src/commands/samples.ts | 12 +++++ common/tools/dev-tool/src/index.ts | 51 +++---------------- .../dev-tool/src/util/findMatchingFiles.ts | 28 ++++++---- common/tools/dev-tool/src/util/printer.ts | 37 +++++++++++++- .../tools/dev-tool/src/util/resolveProject.ts | 20 +++++++- common/tools/dev-tool/src/util/subCommand.ts | 50 ++++++++++++++++++ 8 files changed, 168 insertions(+), 69 deletions(-) create mode 100644 common/tools/dev-tool/src/commands/samples.ts create mode 100644 common/tools/dev-tool/src/util/subCommand.ts diff --git a/common/tools/dev-tool/src/commands/help.ts b/common/tools/dev-tool/src/commands/help.ts index 9a5e5cbb270a..e204650240ec 100644 --- a/common/tools/dev-tool/src/commands/help.ts +++ b/common/tools/dev-tool/src/commands/help.ts @@ -2,7 +2,11 @@ // Licensed under the MIT license import chalk from "chalk"; -import { commands } from "."; +import { commands, CommandLoader } from "."; +import { resolveProject } from "../util/resolveProject"; +import { createPrinter } from "../util/printer"; + +const log = createPrinter("help"); const banner = `\ _______ __ __ __ @@ -11,7 +15,8 @@ const banner = `\ / /_/ /___/ / / /_/ / __/ |/ /_____/ /_/ /_/ / /_/ / / \\____//____/ \\__,_/\\___/|___/ \\__/\\____/\\____/_/ -Developer quality-of-life command for the Azure SDK for JS`; +Developer quality-of-life command for the Azure SDK for JS +`; const usage = ` Usage: dev-tool [OPTIONS] COMMAND [COMMAND-OPTIONS] @@ -27,6 +32,7 @@ export const helpText = "display this help message"; * @param println */ export async function printCommandUsage( + commands: CommandLoader, println: (...values: string[]) => void ) { println(usage); @@ -57,11 +63,23 @@ export async function printCommandUsage( export default async function(...args: string[]): Promise { console.info(chalk.blueBright(banner)); + try { + const packageInfo = await resolveProject(__dirname); + console.info( + chalk.blueBright(` Name/Version:\t${packageInfo.name}@${packageInfo.version}`) + ); + console.info(chalk.blueBright(` Location:\t${packageInfo.path}`)); + } catch (error) { + log.error("Could not locate dev-tool package."); + log.error("Unable to display dev-tool version information."); + } + if (args.length > 0) { - console.warn(chalk.yellow("Warning, unused arguments:", args), "\n"); + console.log(); + log.warn("Warning, unused arguments:", args); } - await printCommandUsage(console.info); + await printCommandUsage(commands, console.info); console.info( "For more information about a given command, try `dev-tool COMMAND --help`" diff --git a/common/tools/dev-tool/src/commands/index.ts b/common/tools/dev-tool/src/commands/index.ts index 5b84ecf5ba0f..05a99aae3be8 100644 --- a/common/tools/dev-tool/src/commands/index.ts +++ b/common/tools/dev-tool/src/commands/index.ts @@ -20,12 +20,15 @@ export interface Command { } /** - * All of dev-tool's commands and the modules that define them + * A map from command name to an async function that loads its module */ -export const commands: { [k: string]: () => Promise } = { - "dev-samples": () => import("./dev-samples"), +export type CommandLoader = { [k: string]: () => Promise }; + +/** + * All of dev-tool's base commands and the modules that define them + */ +export const commands: CommandLoader = { help: () => import("./help"), - "prep-samples": () => import("./prep-samples"), "resolve-package": () => import("./resolve-package"), - "run-samples": () => import("./run-samples") + "samples": () => import("./samples") }; diff --git a/common/tools/dev-tool/src/commands/samples.ts b/common/tools/dev-tool/src/commands/samples.ts new file mode 100644 index 000000000000..7057827f8255 --- /dev/null +++ b/common/tools/dev-tool/src/commands/samples.ts @@ -0,0 +1,12 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { subCommand } from "../util/subCommand"; + +export const helpText = "manage samples in an SDK package"; + +export default subCommand("samples", { + "dev": () => import("./dev-samples"), + "prep": () => import("./prep-samples"), + "run": () => import("./run-samples") +}); \ No newline at end of file diff --git a/common/tools/dev-tool/src/index.ts b/common/tools/dev-tool/src/index.ts index 02149ccc42d9..b2f7e1deec25 100644 --- a/common/tools/dev-tool/src/index.ts +++ b/common/tools/dev-tool/src/index.ts @@ -1,53 +1,14 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -import getArgs from "minimist"; - -import { createPrinter } from "./util/printer"; +import chalk from "chalk"; import { commands } from "./commands"; -import { printCommandUsage } from "./commands/help"; - -const log = createPrinter("dev-tool"); - -/** - * Entry point for the dev-tool command. - */ -async function main() { - const args = getArgs(process.argv.slice(2), { - stopEarly: true - }); - - log.debug("Parsed command line:", JSON.stringify(args)); - - const commandName = args._[0]; - const commandArgs = args._.slice(1); - - if (commandName === undefined) { - log.error("No command provided."); - await printCommandUsage(console.error); - process.exit(1); - } - - log.info(`$ ${commandName} ${commandArgs?.join(" ") ?? ""}`); - - if (commands.hasOwnProperty(commandName)) { - const commandModule = await commands[commandName](); - const status = await commandModule.default(...commandArgs); - - if (status) { - log.success("Finished."); - } else { - log.error("Errors occurred. See the output above."); - } - } else { - log.error("No such command:", commandName); - await printCommandUsage(console.error); - process.exit(1); - } -} +import { subCommand } from "./util/subCommand"; -main().catch(err => { - log.error("An exception occurred:", err); +// The main command is implemented using the same `subCommand` method, +// making the command semantics truly recursive. +subCommand("dev-tool", commands)(...process.argv.slice(2)).catch(err => { + console.trace(chalk.red("[Internal Error] An unhandled exception occurred:", err)); process.exit(1); }); diff --git a/common/tools/dev-tool/src/util/findMatchingFiles.ts b/common/tools/dev-tool/src/util/findMatchingFiles.ts index 5b44e1e6c976..1c4da081240f 100644 --- a/common/tools/dev-tool/src/util/findMatchingFiles.ts +++ b/common/tools/dev-tool/src/util/findMatchingFiles.ts @@ -1,9 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -import chalk from "chalk"; import fs from "fs-extra"; import path from "path"; +import { createPrinter } from "./printer"; + +const { debug } = createPrinter(__filename); /** * File information used during breadth-first search @@ -15,24 +17,32 @@ interface FileInfo { stat: fs.Stats; } +/** + * Options for {@link findMatchingFiles} + */ export interface FindOptions { ignore: string[]; } +const defaultFindOptions: FindOptions = { + ignore: [] +}; + /** * Breadth-first search for files matching a given predicate * * @param dir The root of the sample tree to search * @param matches Predicate that decides whether or not a file entry is included + * @param options options bag for */ export async function* findMatchingFiles( dir: string, matches: (name: string, entry: fs.Stats) => boolean, - options?: Partial + findOptions?: Partial ) { const q: FileInfo[] = []; - const ignore = options?.ignore ?? []; + const options: FindOptions = {...defaultFindOptions, ...findOptions}; async function enqueueAll(dir: string) { const files = await fs.readdir(dir); @@ -52,8 +62,8 @@ export async function* findMatchingFiles( while (q.length) { const info = q.shift() as FileInfo; - if (ignore.includes(info.name)) { - console.warn(chalk.yellow("[run-samples] Ignoring", info.fullPath)); + if (options.ignore.includes(info.name)) { + debug("Ignoring", info.fullPath); continue; } @@ -68,14 +78,10 @@ export async function* findMatchingFiles( info.stat.isSocket() || info.stat.isSymbolicLink() ) { - console.warn( - "[prep-samples] WARNING: Encountered a special file in the sample tree. Skipping:", + debug( + "Encountered a special file in the sample tree. Skipping:", info.fullPath ); } } - - // The full trace of files visited by the iterator is returned and can be accessed using `iter.value` - // once it is `done`, in case it is ever needed for debugging - return q; } diff --git a/common/tools/dev-tool/src/util/printer.ts b/common/tools/dev-tool/src/util/printer.ts index 75c1ce99e531..c4884132082c 100644 --- a/common/tools/dev-tool/src/util/printer.ts +++ b/common/tools/dev-tool/src/util/printer.ts @@ -2,6 +2,7 @@ // Licensed under the MIT license. import chalk from "chalk"; +import path from "path"; const printModes = ["info", "warn", "error", "success", "debug"] as const; @@ -29,12 +30,44 @@ const finalLogger: ModeMap = { error: console.error, debug(...values: string[]) { if (process.env.DEBUG) { - console.log(...values); + const caller = getCaller(); + const fileName = caller?.getFileName(); + const callerInfo = `(@ ${ + fileName ? fileName : "" + }#${caller?.getFunctionName() ?? + ""}:${caller?.getLineNumber()}:${caller?.getColumnNumber()})`; + console.log(values[0], colors.debug(callerInfo), ...values.slice(1)); } }, success: console.info }; +/** + * Gets the filename of the calling function + */ +function getCaller(): NodeJS.CallSite | undefined { + const savedPrepareStackTrace = Error.prepareStackTrace; + + let caller: NodeJS.CallSite | undefined = undefined; + try { + const error = new Error() as any; + + Error.prepareStackTrace = (_, stack) => stack; + + const next = () => error.stack.shift(); + + const current = next(); + + while (error.stack.length > 0 && current !== caller) { + caller = next(); + } + } catch (_) {} + + Error.prepareStackTrace = savedPrepareStackTrace; + + return caller; +} + /** * Create a pre-configured console printer for a given namespace. * @@ -64,7 +97,7 @@ export function createPrinter(name: string): Printer { for (const mode of printModes) { (base as any)[mode] = (...values: string[]) => - finalLogger[mode](colors[mode](prefix, ...values)); + finalLogger[mode](...[prefix, ...values].map((value: string) => colors[mode](value))); } return base as Printer; } diff --git a/common/tools/dev-tool/src/util/resolveProject.ts b/common/tools/dev-tool/src/util/resolveProject.ts index 9432c31ba9ef..d17ed433aad6 100644 --- a/common/tools/dev-tool/src/util/resolveProject.ts +++ b/common/tools/dev-tool/src/util/resolveProject.ts @@ -6,12 +6,27 @@ import path from "path"; import { createPrinter } from "./printer"; -const { debug } = createPrinter("project resolution"); +const { debug } = createPrinter(__filename); +/** + * Information about an Azure SDK for JS package + */ export interface ProjectInfo { + /** + * The name of the package + */ name: string; + /** + * An absolute path to the package directory + */ path: string; + /** + * The package SemVer string, e.g. 1.0.0-preview.3 or 4.0.0 + */ version: string; + /** + * The package info object (result of reading/parsing package.json) + */ packageJson: any; } @@ -53,7 +68,8 @@ async function findAzSDKPackageJson(directory: string): Promise<[string, any]> { /** * Determine which Azure SDK project a given directory belongs to. * - * @param workingDirectory + * @param workingDirectory the directory to resolve the package from + * @returns the package info for the SDK project that owns the given directory */ export async function resolveProject( workingDirectory: string diff --git a/common/tools/dev-tool/src/util/subCommand.ts b/common/tools/dev-tool/src/util/subCommand.ts new file mode 100644 index 000000000000..85175e521e56 --- /dev/null +++ b/common/tools/dev-tool/src/util/subCommand.ts @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import getArgs from "minimist"; + +import { Command } from "../commands"; +import { createPrinter } from "./printer"; +import { printCommandUsage } from "../commands/help"; + +export function subCommand(name: string, commands: { + [k: string]: () => Promise; +}): (...args: string[]) => Promise { + const log = createPrinter(name); + return async (...rawArgs: string[]) => { + const args = getArgs(rawArgs, { + stopEarly: true, + boolean: ["help"] + }); + + log.debug("Parsed command line:", JSON.stringify(args)); + + const commandName = args._[0]; + const commandArgs = args._.slice(1); + + if (commandName === undefined) { + log.error("No command provided."); + await printCommandUsage(commands, console.error); + process.exit(1); + } + + log.info(`$ ${commandName} ${commandArgs?.join(" ") ?? ""}`); + + if (commands.hasOwnProperty(commandName)) { + const commandModule = await commands[commandName](); + const status = await commandModule.default(...commandArgs); + + if (status) { + log.success("Finished."); + return true; + } else { + log.error("Errors occurred. See the output above."); + return false; + } + } else { + log.error("No such command:", commandName); + await printCommandUsage(commands, console.error); + process.exit(1); + } + }; +} From ae97341fa8671292e4ebacac85a71cb0c3869aec Mon Sep 17 00:00:00 2001 From: Will Temple Date: Wed, 18 Mar 2020 10:44:14 -0700 Subject: [PATCH 04/24] Better argument parsing, type-checking, and recursive command structure. Added support for running a single sample. --- common/config/rush/pnpm-lock.yaml | 23 +- common/tools/dev-tool/package.json | 4 +- common/tools/dev-tool/src/commands/about.ts | 60 +++++ .../dev-tool/src/commands/hello/index.ts | 13 ++ .../dev-tool/src/commands/hello/world.ts | 32 +++ common/tools/dev-tool/src/commands/help.ts | 91 -------- common/tools/dev-tool/src/commands/index.ts | 39 ++-- .../dev-tool/src/commands/package/index.ts | 13 ++ .../dev-tool/src/commands/package/resolve.ts | 52 +++++ .../dev-tool/src/commands/resolve-package.ts | 30 --- .../dev-tool/src/commands/run-samples.ts | 70 ------ common/tools/dev-tool/src/commands/samples.ts | 12 - .../{dev-samples.ts => samples/dev.ts} | 16 +- .../dev-tool/src/commands/samples/index.ts | 15 ++ .../{prep-samples.ts => samples/prep.ts} | 42 +--- .../dev-tool/src/commands/samples/run.ts | 96 ++++++++ common/tools/dev-tool/src/index.ts | 10 +- .../tools/dev-tool/src/util/commandBuilder.ts | 221 ++++++++++++++++++ .../tools/dev-tool/src/util/commandModule.ts | 101 ++++++++ .../dev-tool/src/util/findMatchingFiles.ts | 2 +- .../dev-tool/src/util/printCommandUsage.ts | 82 +++++++ common/tools/dev-tool/src/util/printer.ts | 1 - .../tools/dev-tool/src/util/resolveProject.ts | 2 +- common/tools/dev-tool/src/util/subCommand.ts | 50 ---- 24 files changed, 731 insertions(+), 346 deletions(-) create mode 100644 common/tools/dev-tool/src/commands/about.ts create mode 100644 common/tools/dev-tool/src/commands/hello/index.ts create mode 100644 common/tools/dev-tool/src/commands/hello/world.ts delete mode 100644 common/tools/dev-tool/src/commands/help.ts create mode 100644 common/tools/dev-tool/src/commands/package/index.ts create mode 100644 common/tools/dev-tool/src/commands/package/resolve.ts delete mode 100644 common/tools/dev-tool/src/commands/resolve-package.ts delete mode 100644 common/tools/dev-tool/src/commands/run-samples.ts delete mode 100644 common/tools/dev-tool/src/commands/samples.ts rename common/tools/dev-tool/src/commands/{dev-samples.ts => samples/dev.ts} (69%) create mode 100644 common/tools/dev-tool/src/commands/samples/index.ts rename common/tools/dev-tool/src/commands/{prep-samples.ts => samples/prep.ts} (70%) create mode 100644 common/tools/dev-tool/src/commands/samples/run.ts create mode 100644 common/tools/dev-tool/src/util/commandBuilder.ts create mode 100644 common/tools/dev-tool/src/util/commandModule.ts create mode 100644 common/tools/dev-tool/src/util/printCommandUsage.ts delete mode 100644 common/tools/dev-tool/src/util/subCommand.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 8bb0f9693e42..6711cda1711f 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -3306,26 +3306,6 @@ packages: dev: false resolution: integrity: sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw== - /inquirer/7.0.7: - dependencies: - ansi-escapes: 4.3.1 - chalk: 3.0.0 - cli-cursor: 3.1.0 - cli-width: 2.2.0 - external-editor: 3.1.0 - figures: 3.2.0 - lodash: 4.17.15 - mute-stream: 0.0.8 - run-async: 2.4.0 - rxjs: 6.5.4 - string-width: 4.2.0 - strip-ansi: 6.0.0 - through: 2.3.8 - dev: false - engines: - node: '>=6.0.0' - resolution: - integrity: sha512-+lV+BE+M3bDVi6Vtejd7sA572D8gJI5qQUciWDqGKvy6Q6GjuEeJAoHRdOSwqtHUvZlNZssnVvTqQyEXKzki/g== /inquirer/7.1.0: dependencies: ansi-escapes: 4.3.1 @@ -7920,7 +7900,6 @@ packages: '@types/node': 8.10.59 chalk: 3.0.0 fs-extra: 8.1.0 - inquirer: 7.0.7 minimist: 1.2.5 rimraf: 3.0.2 ts-node: 8.6.2_typescript@3.7.5 @@ -7928,7 +7907,7 @@ packages: dev: false name: '@rush-temp/dev-tool' resolution: - integrity: sha512-TIdr84T7zw94e4DeRD9tfOelunKvA03ho/B0kb1H5oycv0CiaBeOzMYkDCpJzRWCZMOSlAOiNn8EWsWyUjEg+Q== + integrity: sha512-WRwDdA26SFMnb8jDvr/DfEP/usN7B8j2PFW3F59tFJld+EYZLm0De2D/vVmHrHWmA4D24RdZgFDhevM8PhsXPg== tarball: 'file:projects/dev-tool.tgz' version: 0.0.0 'file:projects/eslint-plugin-azure-sdk.tgz': diff --git a/common/tools/dev-tool/package.json b/common/tools/dev-tool/package.json index bf75ed4d268a..5c0545d92c2d 100644 --- a/common/tools/dev-tool/package.json +++ b/common/tools/dev-tool/package.json @@ -28,12 +28,10 @@ "sideEffects": false, "private": true, "dependencies": { - "@azure/logger": "^1.0.0", "fs-extra": "^8.1.0", "ts-node": "^8.3.0", "typescript": "~3.7.5", - "inquirer": "~7.0.4", - "minimist": "~1.2.0", + "minimist": "~1.2.5", "chalk": "~3.0.0" }, "devDependencies": { diff --git a/common/tools/dev-tool/src/commands/about.ts b/common/tools/dev-tool/src/commands/about.ts new file mode 100644 index 000000000000..6e8b084b64a9 --- /dev/null +++ b/common/tools/dev-tool/src/commands/about.ts @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import chalk from "chalk"; + +import { baseCommands, baseCommandInfo } from "."; +import { resolveProject } from "../util/resolveProject"; +import { createPrinter } from "../util/printer"; +import { leafCommand } from "../util/commandBuilder"; +import { printCommandUsage } from "../util/printCommandUsage"; + +const log = createPrinter("help"); + +const banner = `\ + _______ __ __ __ + / / ___/ ____/ /__ _ __ / /_____ ____ / / + __ / /\\__ \\ / __ / _ \\ | / /_____/ __/ __ \\/ __ \\/ / +/ /_/ /___/ / / /_/ / __/ |/ /_____/ /_/ /_/ / /_/ / / +\\____//____/ \\__,_/\\___/|___/ \\__/\\____/\\____/_/ + +Developer quality-of-life command for the Azure SDK for JS +`; + +export const commandInfo = { + name: "about", + description: "display command help and information" +} as const; + +export default leafCommand(commandInfo, async (options) => { + console.log(chalk.blueBright(banner)); + + try { + const packageInfo = await resolveProject(__dirname); + console.log( + chalk.blueBright( + ` Name/Version:\t${packageInfo.name}@${packageInfo.version}` + ) + ); + console.log(chalk.blueBright(` Location:\t${packageInfo.path}`)); + console.log(); + } catch (error) { + log.error("Could not locate dev-tool package."); + log.error("Unable to display dev-tool version information."); + } + + if (options.args.length || options["--"]?.length) { + console.log(); + log.warn("Warning, unused options:", JSON.stringify(options)); + } + + await printCommandUsage(baseCommandInfo, baseCommands); + + console.log( + "For more information about a given command, try `dev-tool COMMAND --help`" + ); + + console.log(); + + return true; +}); diff --git a/common/tools/dev-tool/src/commands/hello/index.ts b/common/tools/dev-tool/src/commands/hello/index.ts new file mode 100644 index 000000000000..aea94ae7873d --- /dev/null +++ b/common/tools/dev-tool/src/commands/hello/index.ts @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { subCommand } from "../../util/commandBuilder"; + +export const commandInfo = { + name: "hello", + description: "commands for printing some lovely messages" +}; + +export default subCommand(commandInfo, { + world: () => import("./world") +}); diff --git a/common/tools/dev-tool/src/commands/hello/world.ts b/common/tools/dev-tool/src/commands/hello/world.ts new file mode 100644 index 000000000000..85fccc5bb3d1 --- /dev/null +++ b/common/tools/dev-tool/src/commands/hello/world.ts @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { createPrinter } from "../../util/printer"; +import { leafCommand } from "../../util/commandBuilder"; + +const log = createPrinter("world"); + +export const commandInfo = { + name: "world", + description: + "print a lovely message", + options: { + echo: { + kind: "string", + description: "override the message to be printed", + default: "Hello world!" + } + } +} as const; + +export default leafCommand(commandInfo, async (options) => { + // Demonstrate the colorized command output. + log("Normal:", options.echo); + log.success("Success:", options.echo); + log.info("Info:", options.echo); + log.warn("Warn:", options.echo); + log.error("Error:", options.echo); + log.debug("Debug:", options.echo); + + return true; +}); \ No newline at end of file diff --git a/common/tools/dev-tool/src/commands/help.ts b/common/tools/dev-tool/src/commands/help.ts deleted file mode 100644 index e204650240ec..000000000000 --- a/common/tools/dev-tool/src/commands/help.ts +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license - -import chalk from "chalk"; -import { commands, CommandLoader } from "."; -import { resolveProject } from "../util/resolveProject"; -import { createPrinter } from "../util/printer"; - -const log = createPrinter("help"); - -const banner = `\ - _______ __ __ __ - / / ___/ ____/ /__ _ __ / /_____ ____ / / - __ / /\\__ \\ / __ / _ \\ | / /_____/ __/ __ \\/ __ \\/ / -/ /_/ /___/ / / /_/ / __/ |/ /_____/ /_/ /_/ / /_/ / / -\\____//____/ \\__,_/\\___/|___/ \\__/\\____/\\____/_/ - -Developer quality-of-life command for the Azure SDK for JS -`; - -const usage = ` -Usage: dev-tool [OPTIONS] COMMAND [COMMAND-OPTIONS] - -COMMAND indicates the subcommand to be run. It can be one of the following: -`; - -export const helpText = "display this help message"; - -/** - * Prints the one-line usage of each command. - * - * @param println - */ -export async function printCommandUsage( - commands: CommandLoader, - println: (...values: string[]) => void -) { - println(usage); - - // Compute the number of tabs needed to separate commands from - // docstrings assuming a default command-line tabstop of 8 - const tabs = Math.ceil( - (Math.max( - ...Object.keys(commands) - .filter(key => commands.hasOwnProperty(key)) - .map(key => key.length + 2) - ) + - 1) / - 8 - ); - - for (const command in commands) { - if (commands.hasOwnProperty(command)) { - const module = await commands[command](); - const indent = "\t".repeat(tabs - Math.floor((command.length + 2) / 8)); - println(` ${command}${indent}${module.helpText}`); - } - } - - println(); -} - -export default async function(...args: string[]): Promise { - console.info(chalk.blueBright(banner)); - - try { - const packageInfo = await resolveProject(__dirname); - console.info( - chalk.blueBright(` Name/Version:\t${packageInfo.name}@${packageInfo.version}`) - ); - console.info(chalk.blueBright(` Location:\t${packageInfo.path}`)); - } catch (error) { - log.error("Could not locate dev-tool package."); - log.error("Unable to display dev-tool version information."); - } - - if (args.length > 0) { - console.log(); - log.warn("Warning, unused arguments:", args); - } - - await printCommandUsage(commands, console.info); - - console.info( - "For more information about a given command, try `dev-tool COMMAND --help`" - ); - - console.info(); - - return true; -} diff --git a/common/tools/dev-tool/src/commands/index.ts b/common/tools/dev-tool/src/commands/index.ts index 05a99aae3be8..e141e8e48a6c 100644 --- a/common/tools/dev-tool/src/commands/index.ts +++ b/common/tools/dev-tool/src/commands/index.ts @@ -1,34 +1,27 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license +import { subCommand } from "../util/commandBuilder"; + /** - * A command for use with the dev-tool + * All of dev-tool's base commands and the modules that define them */ -export interface Command { - /** - * The main async function of the command. - * - * @param args the arguments passed to the command - * - * @returns a promise resolving to `true` if the command succeeded, `false` otherwise - */ - default: (...args: string[]) => Promise; - /** - * One line of help text to be printed by the `dev-tool help` command - */ - helpText: string; -} +export const baseCommands = { + about: () => import("./about"), + "package": () => import("./package"), + "samples": () => import("./samples"), + "hello": () => import("./hello"), +} as const; /** - * A map from command name to an async function that loads its module + * Metadata about the base command, only used in `dev-tool help` */ -export type CommandLoader = { [k: string]: () => Promise }; +export const baseCommandInfo = { + name: "dev-tool", + description: "Azure SDK for JS dev-tool" +} as const; /** - * All of dev-tool's base commands and the modules that define them + * Default dev-tool subcommand */ -export const commands: CommandLoader = { - help: () => import("./help"), - "resolve-package": () => import("./resolve-package"), - "samples": () => import("./samples") -}; +export const baseCommand = subCommand(baseCommandInfo, baseCommands); diff --git a/common/tools/dev-tool/src/commands/package/index.ts b/common/tools/dev-tool/src/commands/package/index.ts new file mode 100644 index 000000000000..e620eafbda9a --- /dev/null +++ b/common/tools/dev-tool/src/commands/package/index.ts @@ -0,0 +1,13 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { subCommand } from "../../util/commandBuilder"; + +export const commandInfo = { + name: "package", + description: "manage SDK packages in the monorepo" +}; + +export default subCommand(commandInfo, { + resolve: () => import("./resolve") +}); diff --git a/common/tools/dev-tool/src/commands/package/resolve.ts b/common/tools/dev-tool/src/commands/package/resolve.ts new file mode 100644 index 000000000000..ff567c266b9a --- /dev/null +++ b/common/tools/dev-tool/src/commands/package/resolve.ts @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import path from "path"; + +import { resolveProject } from "../../util/resolveProject"; +import { createPrinter } from "../../util/printer"; +import { leafCommand } from "../../util/commandBuilder"; + +const log = createPrinter("resolve-package"); + +export const commandInfo = { + name: "resolve", + description: + "display information about the project that owns a directory", + options: { + directory: { + shortName: "d", + kind: "multistring", + description: "base directory for resolution (uses CWD if unset)" + }, + quiet: { + shortName: "q", + kind: "boolean", + default: false, + description: "output only the directory name with no extra formatting" + } + } +} as const; + +export default leafCommand(commandInfo, async options => { + const dirs = (options.directory || [process.cwd()]).map(p => path.resolve(p)); + for (const dir of dirs) { + try { + const currentPackage = await resolveProject(dir); + if (options.quiet) { + console.log(currentPackage.path); + } else { + log.success("== Detected package:", currentPackage.name); + log.info( + `Version specifier: ${currentPackage.name}@${currentPackage.version}` + ); + log.info(`Location: ${path.resolve(dir, currentPackage.path)}`); + } + } catch (error) { + log.error("Could not find package starting from", dir); + log.error(error); + } + } + + return true; +}); diff --git a/common/tools/dev-tool/src/commands/resolve-package.ts b/common/tools/dev-tool/src/commands/resolve-package.ts deleted file mode 100644 index ec6d6201d761..000000000000 --- a/common/tools/dev-tool/src/commands/resolve-package.ts +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license - -import path from "path"; - -import { resolveProject } from "../util/resolveProject"; -import { createPrinter } from "../util/printer"; - -const log = createPrinter("resolve-package"); - -export const helpText = - "display information about the project that owns the current directory"; - -export default async function(..._: string[]): Promise { - const cwd = process.cwd(); - try { - const currentPackage = await resolveProject(cwd); - log.success("== Detected package:", currentPackage.name); - log.info( - `Version specifier: ${currentPackage.name}@${currentPackage.version}` - ); - log.info(`Location: ${path.resolve(cwd, currentPackage.path)}`); - } catch (error) { - log.error("Could not find package starting from", cwd); - log.error(error); - return false; - } - - return true; -} diff --git a/common/tools/dev-tool/src/commands/run-samples.ts b/common/tools/dev-tool/src/commands/run-samples.ts deleted file mode 100644 index ad71a50c0389..000000000000 --- a/common/tools/dev-tool/src/commands/run-samples.ts +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -/** - * run-samples.js - * - * Runs all JavaScript files in a directory, using the calling convention for - * our sample code. - */ - -import path from "path"; - -import { findMatchingFiles } from "../util/findMatchingFiles"; -import { createPrinter } from "../util/printer"; - -const log = createPrinter("run-samples"); - -const IGNORE = ["node_modules"]; - -export const helpText = "execute a sample or all samples within a directory"; - -export default async function(...args: string[]): Promise { - if (args.length === 0) { - throw new Error("At least one argument is required for run-samples"); - } - - const sampleDirs = args.map(dir => path.resolve(dir)); - - // Patch the environment for the sample helper - process.env.BATCH_RUN_SAMPLES = "true"; - - log.info("Running all samples in:", sampleDirs.join(", ")); - - let errors = []; - - for (const sampleDir of sampleDirs) { - for await (const fileName of findMatchingFiles( - sampleDir, - (name, entry) => entry.isFile() && name.endsWith(".js"), - { - ignore: IGNORE - } - )) { - log("Running", fileName); - const { main: sampleMain } = require(fileName); - try { - await sampleMain(); - } catch (err) { - const truncatedError = err - .toString() - .split("\n")[0] - .slice(0, 100); - errors.push([path.basename(fileName), truncatedError]); - log.warn("Error in", fileName, ":", err); - log.warn("Continuing ..."); - } - } - } - - if (errors.length > 0) { - log.error("Errors occurred in the following files:"); - for (const [fileName, error] of errors) { - log.error(" -", fileName, "(", error, ")"); - } - - return false; - } - - return true; -} diff --git a/common/tools/dev-tool/src/commands/samples.ts b/common/tools/dev-tool/src/commands/samples.ts deleted file mode 100644 index 7057827f8255..000000000000 --- a/common/tools/dev-tool/src/commands/samples.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license - -import { subCommand } from "../util/subCommand"; - -export const helpText = "manage samples in an SDK package"; - -export default subCommand("samples", { - "dev": () => import("./dev-samples"), - "prep": () => import("./prep-samples"), - "run": () => import("./run-samples") -}); \ No newline at end of file diff --git a/common/tools/dev-tool/src/commands/dev-samples.ts b/common/tools/dev-tool/src/commands/samples/dev.ts similarity index 69% rename from common/tools/dev-tool/src/commands/dev-samples.ts rename to common/tools/dev-tool/src/commands/samples/dev.ts index 9dfb2bf662dd..034ebbe6e12f 100644 --- a/common/tools/dev-tool/src/commands/dev-samples.ts +++ b/common/tools/dev-tool/src/commands/samples/dev.ts @@ -4,15 +4,19 @@ import fs from "fs-extra"; import path from "path"; -import { resolveProject } from "../util/resolveProject"; -import { createPrinter } from "../util/printer"; +import { resolveProject } from "../../util/resolveProject"; +import { createPrinter } from "../../util/printer"; +import { leafCommand } from "../../util/commandBuilder"; const log = createPrinter("dev-samples"); -export const helpText = - "link samples to local sources for access to IntelliSense during development"; +export const commandInfo = { + name: "dev", + description: + "link samples to local sources for access to IntelliSense during development" +} as const; -export default async function(..._: string[]): Promise { +export default leafCommand(commandInfo, async (_) => { const pkg = await resolveProject(process.cwd()); const relativeLinkName = path.join( @@ -42,4 +46,4 @@ export default async function(..._: string[]): Promise { await fs.symlink(relativeLinkTarget, linkName); return true; -} +}); diff --git a/common/tools/dev-tool/src/commands/samples/index.ts b/common/tools/dev-tool/src/commands/samples/index.ts new file mode 100644 index 000000000000..8bb6daf74960 --- /dev/null +++ b/common/tools/dev-tool/src/commands/samples/index.ts @@ -0,0 +1,15 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { subCommand } from "../../util/commandBuilder"; + +export const commandInfo = { + name: "samples", + description: "manage samples in an SDK package" +}; + +export default subCommand(commandInfo, { + "dev": () => import("./dev"), + "prep": () => import("./prep"), + "run": () => import("./run") +}); \ No newline at end of file diff --git a/common/tools/dev-tool/src/commands/prep-samples.ts b/common/tools/dev-tool/src/commands/samples/prep.ts similarity index 70% rename from common/tools/dev-tool/src/commands/prep-samples.ts rename to common/tools/dev-tool/src/commands/samples/prep.ts index 68ddcf69692e..dd3062433ec0 100644 --- a/common/tools/dev-tool/src/commands/prep-samples.ts +++ b/common/tools/dev-tool/src/commands/samples/prep.ts @@ -1,37 +1,20 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. -/** - * prep-samples.ts - * - * Prepares sample files for execution in CI by replacing abosolute package imports with relative - * imports. This is useful because it allows us to check in "camera-ready" copies of our samples - * so that they can be ingested directly into external documentation pipelines, while still allowing - * us to compile and run our samples in CI. - * - * Usage: node prep-samples.js [PACKAGE PATH] - * - PACKAGE PATH should be set to the directory of a `package.json` for a package that contains - * TypeScript samples. - * - If PACKAGE PATH is not specified, CWD will be used - * - * The command expects to find a directory tree `samples/typescript` under PACKAGE PATH. - * - * WARNING: This script ___WILL NOT___ revert changes it makes to the samples. Make sure any staged - * changes you have made to the samples are committed to git or otherwise preserved, as this script - * will completely overwrite them! - */ - import fs from "fs-extra"; -import minimist from "minimist"; import path from "path"; -import { createPrinter } from "../util/printer"; -import { findMatchingFiles } from "../util/findMatchingFiles"; -import { resolveProject } from "../util/resolveProject"; +import { createPrinter } from "../../util/printer"; +import { findMatchingFiles } from "../../util/findMatchingFiles"; +import { resolveProject } from "../../util/resolveProject"; +import { leafCommand } from "../../util/commandBuilder"; const log = createPrinter("prep-samples"); -export const helpText = "prepare samples for local source-linked execution"; +export const commandInfo = { + name: "prep", + description: "prepare samples for local source-linked execution" +} as const; /** * Replaces package require/import statements with relative paths for CI @@ -98,12 +81,11 @@ async function* cat(...generators: AsyncIterable[]): AsyncIterable { } } -export default async function(...args: string[]): Promise { - const parsedArgs = minimist(args); +export default leafCommand(commandInfo, async (options) => { let argumentDir; - if (parsedArgs._.length) { - argumentDir = path.resolve(parsedArgs._[0]); + if (options.args.length) { + argumentDir = path.resolve(options.args[0]); } else { argumentDir = process.cwd(); } @@ -138,4 +120,4 @@ export default async function(...args: string[]): Promise { } return true; -} +}); diff --git a/common/tools/dev-tool/src/commands/samples/run.ts b/common/tools/dev-tool/src/commands/samples/run.ts new file mode 100644 index 000000000000..ecc9d52f6227 --- /dev/null +++ b/common/tools/dev-tool/src/commands/samples/run.ts @@ -0,0 +1,96 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import fs from "fs-extra"; +import path from "path"; + +import { findMatchingFiles } from "../../util/findMatchingFiles"; +import { createPrinter } from "../../util/printer"; +import { leafCommand } from "../../util/commandBuilder"; + +const log = createPrinter("run-samples"); + +const IGNORE = ["node_modules"]; + +export const commandInfo = { + name: "run", + description: "execute a sample or all samples within a directory" +}; + +/** + * Run a single sample file, accumulating any thrown errors into `accumulatedErrors` + * + * @param name the file to run + * @param accumulatedErrors an array to push truncated errors onto as tuples of [fileName, error] + */ +async function runSingle( + name: string, + accumulatedErrors: Array<[string, string]> +) { + log("Running", name); + try { + if (/.*\/samples\/.*/.exec(name)) { + // This is an un-prepared sample, so just require it and it will run. + await import(name); + } else if (!(/.*\/dist-samples\/.*/.exec(name))) { + // This is not an unprepared or a prepared sample + log.warn("Executing a file that is neither in samples nor dist-samples."); + } else { + const { main: sampleMain } = await import(name); + await sampleMain(); + } + } catch (err) { + const truncatedError: string = err + .toString() + .split("\n")[0] + .slice(0, 100); + accumulatedErrors.push([path.basename(name), truncatedError]); + log.warn("Error in", name, ":", err); + log.warn("Continuing ..."); + } +} + +export default leafCommand(commandInfo, async options => { + if (options.args.length === 0) { + throw new Error("At least one argument is required for run-samples"); + } + + const samples = options.args.map(dir => path.resolve(dir)); + + // Patch the environment for the sample helper + process.env.BATCH_RUN_SAMPLES = "true"; + + let errors: Array<[string, string]> = []; + + for (const sample of samples) { + const stats = await fs.stat(sample); + if (stats.isFile()) { + runSingle(sample, errors); + } else if (stats.isDirectory()) { + for await (const fileName of findMatchingFiles( + sample, + (name, entry) => entry.isFile() && name.endsWith(".js"), + { + ignore: IGNORE + } + )) { + runSingle(fileName, errors); + } + } else { + log.warn(`Sample ${sample} is neither a file nor a directory.`); + log.warn("Continuing ..."); + errors.push([path.basename(sample), "Neither a file nor a directory"]); + } + } + + if (errors.length > 0) { + log.error("Errors occurred in the following files:"); + for (const [fileName, error] of errors) { + log.error(" -", fileName, "(", error, ")"); + } + + return false; + } + + return true; +}); diff --git a/common/tools/dev-tool/src/index.ts b/common/tools/dev-tool/src/index.ts index b2f7e1deec25..7e17e57bd712 100644 --- a/common/tools/dev-tool/src/index.ts +++ b/common/tools/dev-tool/src/index.ts @@ -3,12 +3,10 @@ import chalk from "chalk"; -import { commands } from "./commands"; -import { subCommand } from "./util/subCommand"; +import { baseCommand } from "./commands"; -// The main command is implemented using the same `subCommand` method, -// making the command semantics truly recursive. -subCommand("dev-tool", commands)(...process.argv.slice(2)).catch(err => { - console.trace(chalk.red("[Internal Error] An unhandled exception occurred:", err)); +// The main command is implemented using the same `subCommand` method. +baseCommand(...process.argv.slice(2)).catch(err => { + console.trace(chalk.red("[Internal Error]", err.stack)); process.exit(1); }); diff --git a/common/tools/dev-tool/src/util/commandBuilder.ts b/common/tools/dev-tool/src/util/commandBuilder.ts new file mode 100644 index 000000000000..4467a7038ad4 --- /dev/null +++ b/common/tools/dev-tool/src/util/commandBuilder.ts @@ -0,0 +1,221 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import getArgs from "minimist"; + +import { CommandLoader, CommandOptions, CommandInfo } from "./commandModule"; +import { createPrinter } from "./printer"; +import { printCommandUsage, commandStack } from "./printCommandUsage"; + +const { debug: parseDebug, error: parseError } = createPrinter("parseOptions"); + +/** + * The type of the Options map produced by {@link parseOptions} + * given a const input type Opts that extends CommandOptions. + * + * You should probably not use this type directly. It is designed + * to be inferred when using {@link leafCommand}. + * + * Make sure the input type is declared const, otherwise a + * fully dynamic map will be produced. + */ +export type ParsedOptions = { + /** + * If the argument "--" was encountered when parsing, this property + * holds all arguments that occurred after the "--" + */ + "--": string[] | undefined; + /** + * Array of arguments to the command + */ + args: string[]; +} & (Opts extends CommandOptions + ? { + // This heinous type signature here just takes the options structure + // and folds it into a map from long-key to resulting type. + // If no default is specified, then the type will be implicitly or'd + // with undefined + [K in Exclude< + keyof Opts, + "--" | "args" | "help" + >]: undefined extends Opts[K]["default"] + ? + | (Opts[K]["kind"] extends "string" + ? string + : Opts[K]["kind"] extends "boolean" + ? boolean + : Opts[K]["kind"] extends "multistring" + ? string[] + : string[] | string | boolean) + | undefined + : Opts[K]["kind"] extends "string" + ? string + : Opts[K]["kind"] extends "boolean" + ? boolean + : Opts[K]["kind"] extends "multistring" + ? string[] + : string[] | string | boolean; + } + : {}); + +/** + * Helper function to convert an args array to a parsed argument map. + * + * This is a strongly-typed wrapper around `minimist`. + * + * You should probably not use this function directly. The + * options should be parsed and their types should be inferred + * from {@link leafCommand}. + * + * @param args input array of arg strings from argv + * @param options option map of a command + */ +export function parseOptions( + args: string[], + opts?: Opts +): ParsedOptions { + // If options are not provided, use an empty set + const options: CommandOptions = opts ?? {}; + + const keys = Object.keys(options).filter(k => options.hasOwnProperty(k)); + const argMap = getArgs(args, { + // Once an unidentified argument is encountered, stop parsing + stopEarly: true, + // Use type information for hinting to minimist about how arguments should + // be handled + boolean: ["help", ...keys.filter(k => options[k].kind === "boolean")], + string: keys.filter(k => options[k].kind === "string"), + // Roll up the optional short-names into aliases + alias: keys.reduce( + (o, key) => + options[key].shortName !== undefined + ? { ...o, [options[key].shortName!]: key } + : o, + {} + ), + // Roll up the default values into the arg parser + default: { + ...keys.reduce( + (o, key) => + options[key].default !== undefined + ? { ...o, [key]: options[key].default } + : o, + {} + ), + help: false + } + }); + + parseDebug(JSON.stringify(argMap)); + + const result: any = { ...argMap, help: argMap.help, args: argMap._ }; + + delete result._; + + function expectType(key: string, value : any, expected: string) : void { + if (Array.isArray(value)) { + parseError(`Too many arguments for "${key}"`); + throw new Error(`More than one value for "${key}" was given, but only one was expected`); + } else if (typeof value !== expected && typeof value !== "undefined") { + parseError(`Bad argument: "${key}" = ${value}`); + throw new Error(`Value of argument "${key}" was a ${typeof value} but a ${expected} was expected.`) + } + } + + // Validate that multi-strings were passed correctly + // no boolean values, and single-strings get wrapped into arrays + for (const multiKey of keys.filter(k => options[k].kind === "multistring" && result[k] !== undefined)) { + if (!Array.isArray(result[multiKey])) { + expectType(multiKey, result[multiKey], "string"); + // Wrap in an array to preserve types + result[multiKey] = [result[multiKey]]; + } else { + for (const val of result[multiKey]) { + expectType(multiKey, val, "string"); + } + } + } + + // Check that single-strings were not passed more than once + for (const stringKey of keys.filter(k => options[k].kind === "string")) { + expectType(stringKey, result[stringKey], "string"); + } + + // Check that booleans were passed correctly + for (const boolKey of keys.filter(k => options[k].kind === "boolean")) { + expectType(boolKey, result[boolKey], "boolean"); + } + + return result as any; +} + +/** + * Create a subcommand handler that will delegate handling + * to a set of lower-order subcommands. + * + * @param name name of this command (used in printed output) + * @param commands map from subcommand name to module implementing that command + */ +export function subCommand( + info: CommandInfo, + commands: CommandLoader +): (...args: string[]) => Promise { + const log = createPrinter(info.name); + return async (...rawArgs: string[]) => { + commandStack.push(info.name); + + const options = parseOptions(rawArgs, info.options); + + log.debug("Parsed command line:", JSON.stringify(options)); + + if (options.help) { + await printCommandUsage(info, commands); + return true; + } + + const commandName = options.args[0]; + const commandArgs = options.args.slice(1); + + if (commandName === undefined) { + log.error("No sub-command provided."); + await printCommandUsage(info, commands, console.error); + process.exit(1); + } + + log.debug(`$ ${commandName} ${commandArgs?.join(" ") ?? ""}`); + + if (commands.hasOwnProperty(commandName)) { + const commandModule = await commands[commandName](); + + const status = await commandModule.default(...commandArgs); + + if (!status) { + log.error("Errors occurred. See the output above."); + } + return status; + } else { + log.error("No such sub-command:", commandName); + await printCommandUsage(info, commands, console.error); + process.exit(1); + } + }; +} + +export function leafCommand( + info: Info, + handler: (options: ParsedOptions) => Promise +): (...args: string[]) => Promise { + return async (...args: string[]) => { + const options = parseOptions(args, info.options); + + commandStack.push(info.name); + + // --help is treated specially + if (options.help) { + await printCommandUsage(info); + return true; + } + + return handler(options as any); + }; +} diff --git a/common/tools/dev-tool/src/util/commandModule.ts b/common/tools/dev-tool/src/util/commandModule.ts new file mode 100644 index 000000000000..41086d535797 --- /dev/null +++ b/common/tools/dev-tool/src/util/commandModule.ts @@ -0,0 +1,101 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +/** + * A command for use with the dev-tool + */ +export interface CommandModule { + /** + * The main async function of the command. + * + * @param args the arguments passed to the command + * + * @returns a promise resolving to `true` if the command succeeded, `false` otherwise + */ + default: (...args: string[]) => Promise; + + /** + * Metadata information about this subcommand + */ + commandInfo: CommandInfo; +} + +/** + * Information about this command + */ +export interface CommandInfo { + /** + * Name of this command + */ + name: string; + + /** + * One line of help text to be printed by the `dev-tool help` command + */ + description: string; + + /** + * Argument configuration for this command + * + * Used for type-checked argument parsing and help text. + * + * The options keys "help", "args", and "--" will be discarded, as they are handled internally. + */ + options?: CommandOptions; +} + +/** + * Discriminated union representing the description of a command-line options + */ +export type OptionDescription = + | StringOptionDescription + | MultiStringOptionDescription + | BooleanOptionDescription; + +/** + * Option description for a string argument + */ +export interface StringOptionDescription { + kind: "string"; + default?: string; +} + +/** + * Option description for a string argument that + * may be specified multiple times + */ +export interface MultiStringOptionDescription { + kind: "multistring", + default?: string[] +} + +/** + * Option description for a boolean argument + */ +export interface BooleanOptionDescription { + kind: "boolean"; + default?: boolean; +} + +/** + * Structure for definining options of a command, represented as + * a map from option name to a description, argument kind (string or boolean), + * optional default value, and optional short name (one-letter alias) + */ +export interface CommandOptions { + [k: string] : { + /** + * Optional one-letter alias + */ + shortName?: string; + /** + * Help text for this option + */ + description: string; + } & OptionDescription +} + +/** + * A map from command name to an async function that loads its module + */ +export type CommandLoader = { [k: string]: () => Promise }; \ No newline at end of file diff --git a/common/tools/dev-tool/src/util/findMatchingFiles.ts b/common/tools/dev-tool/src/util/findMatchingFiles.ts index 1c4da081240f..b6fa199eb191 100644 --- a/common/tools/dev-tool/src/util/findMatchingFiles.ts +++ b/common/tools/dev-tool/src/util/findMatchingFiles.ts @@ -5,7 +5,7 @@ import fs from "fs-extra"; import path from "path"; import { createPrinter } from "./printer"; -const { debug } = createPrinter(__filename); +const { debug } = createPrinter("find-matching-files"); /** * File information used during breadth-first search diff --git a/common/tools/dev-tool/src/util/printCommandUsage.ts b/common/tools/dev-tool/src/util/printCommandUsage.ts new file mode 100644 index 000000000000..ff14fa270b32 --- /dev/null +++ b/common/tools/dev-tool/src/util/printCommandUsage.ts @@ -0,0 +1,82 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { CommandInfo, CommandLoader } from "./commandModule"; + +/** + * The stack of subcommands executed so far + * + * Used in help output. + */ +export const commandStack: string[] = []; + +/** + * Prints the help information for a given command + * + * @param info CommandInfo structure to use in printing command information + * @param subCommands optional CommandLoader structure that defines subcommand usage + * @param println optionally override the default printer + */ +export async function printCommandUsage( + info: CommandInfo, + subCommands?: CommandLoader, + println: (...values: string[]) => void = console.log +) { + println(`${info.name} - ${info.description}\n`); + println( + `Usage: ${commandStack.join(" ")} [OPTIONS] ${ + subCommands !== undefined ? "COMMAND [... COMMAND-OPTIONS]" : "" + }\n` + ); + + // OPTIONS info + println("Options:"); + println(" --help\t display this help message"); + if (info.options) { + for (const k in info.options) { + if (info.options.hasOwnProperty(k)) { + const shortName = + info.options[k].shortName !== undefined + ? `-${info.options[k].shortName},` + : ""; + const valueType = info.options[k].kind !== "boolean" + ? "" + : ""; + const acceptsMulti = info.options[k].kind === "multistring" + ? " (can be set multiple times)" + : ""; + println(` ${shortName}--${k}\t${valueType} ${info.options[k].description}${acceptsMulti}`); + } + } + } + + // COMMAND info + if (subCommands) { + println(); + println( + "COMMAND indicates the subcommand to be run. It can be one of the following:\n" + ); + + // Compute the number of tabs needed to separate commands from + // docstrings assuming a default command-line tabstop of 8 + const tabs = Math.ceil( + (Math.max( + ...Object.keys(subCommands) + .filter(key => subCommands.hasOwnProperty(key)) + .map(key => key.length + 2) + ) + + 1) / + 8 + ); + + for (const command in subCommands) { + if (subCommands.hasOwnProperty(command)) { + const module = await subCommands[command](); + const indent = "\t".repeat(tabs - Math.floor((command.length + 2) / 8)); + println(` ${command}${indent}${module.commandInfo.description}`); + } + } + } + + println(); +} diff --git a/common/tools/dev-tool/src/util/printer.ts b/common/tools/dev-tool/src/util/printer.ts index c4884132082c..021aad92920e 100644 --- a/common/tools/dev-tool/src/util/printer.ts +++ b/common/tools/dev-tool/src/util/printer.ts @@ -2,7 +2,6 @@ // Licensed under the MIT license. import chalk from "chalk"; -import path from "path"; const printModes = ["info", "warn", "error", "success", "debug"] as const; diff --git a/common/tools/dev-tool/src/util/resolveProject.ts b/common/tools/dev-tool/src/util/resolveProject.ts index d17ed433aad6..56be71303ea1 100644 --- a/common/tools/dev-tool/src/util/resolveProject.ts +++ b/common/tools/dev-tool/src/util/resolveProject.ts @@ -6,7 +6,7 @@ import path from "path"; import { createPrinter } from "./printer"; -const { debug } = createPrinter(__filename); +const { debug } = createPrinter("resolve-project"); /** * Information about an Azure SDK for JS package diff --git a/common/tools/dev-tool/src/util/subCommand.ts b/common/tools/dev-tool/src/util/subCommand.ts deleted file mode 100644 index 85175e521e56..000000000000 --- a/common/tools/dev-tool/src/util/subCommand.ts +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license - -import getArgs from "minimist"; - -import { Command } from "../commands"; -import { createPrinter } from "./printer"; -import { printCommandUsage } from "../commands/help"; - -export function subCommand(name: string, commands: { - [k: string]: () => Promise; -}): (...args: string[]) => Promise { - const log = createPrinter(name); - return async (...rawArgs: string[]) => { - const args = getArgs(rawArgs, { - stopEarly: true, - boolean: ["help"] - }); - - log.debug("Parsed command line:", JSON.stringify(args)); - - const commandName = args._[0]; - const commandArgs = args._.slice(1); - - if (commandName === undefined) { - log.error("No command provided."); - await printCommandUsage(commands, console.error); - process.exit(1); - } - - log.info(`$ ${commandName} ${commandArgs?.join(" ") ?? ""}`); - - if (commands.hasOwnProperty(commandName)) { - const commandModule = await commands[commandName](); - const status = await commandModule.default(...commandArgs); - - if (status) { - log.success("Finished."); - return true; - } else { - log.error("Errors occurred. See the output above."); - return false; - } - } else { - log.error("No such command:", commandName); - await printCommandUsage(commands, console.error); - process.exit(1); - } - }; -} From 4379675fbeaa872a11de6dbb85e400aae74000ed Mon Sep 17 00:00:00 2001 From: Will Temple Date: Wed, 18 Mar 2020 10:44:39 -0700 Subject: [PATCH 05/24] Added dev-tool README --- common/tools/dev-tool/README.md | 223 ++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 common/tools/dev-tool/README.md diff --git a/common/tools/dev-tool/README.md b/common/tools/dev-tool/README.md new file mode 100644 index 000000000000..98b9c9e38fef --- /dev/null +++ b/common/tools/dev-tool/README.md @@ -0,0 +1,223 @@ +# @azure/dev-tool + +`dev-tool` is an extensible command-line utility for Azure SDK for JS contributors. + +It provides a place to centralize scripts, resources, and processes for development of the Azure SDK for JavaScript. It is its own unpublished package and has the ability to use dependencies that are managed with Rush in the development process, and it is written in TypeScript. + +## Installation + +`dev-tool` runs using ts-node, so it does not need to be built. It is ready-to-go after a `rush update`. It additionally does not need to be installed to a user's machine in order to be used in `package.json` scripts, since it provdes the `dev-tool` binary to any dependent packages through the `bin` entry in its `package.json`. Simply add `@azure/dev-tool` to the `devDependencies` of a package, and the `dev-tool` binary will become available. If you wish to use `dev-tool` from the CLI manually, you can install it globally on your system by running `npm install -g` from this directory. + +## Usage + +`dev-tool` uses a tree-shaped command hierarchy. For example, at the time of writing, the command tree looks like this: + +`dev-tool` + - `about` (display command help and information) + - `package` + - `resolve` (display information about the project that owns a directory) + - `samples` + - `dev` (link samples to local sources for access to IntelliSense during development) + - `prep` (prepare samples for local source-linked execution) + - `run` (execute a sample or all samples within a directory) + +The `dev-tool about` command will print some information about how to use the command. All commands additionally accept the `--help` argument, which will print information about the usage of that specific command. For example, to show help information for the `resolve` command above, issue the command `dev-tool package resolve --help`. + +## Extending the Tool + +The source hierarchy matches the command hierarchy. Every sub-command has its own folder and `index.ts` file in `src/commands`, where `src/commands/index.ts` defines the behavior of the root `dev-tool` command, and each subfolder's `index.ts` file describes a nested sub-command. Every leaf node in the command tree ("leaf command") has its own TypeScript file. For example, `src/commands/about.ts` defines the behavior of the `dev-tool about` command, and `src/commands/package/resolve.ts` defines the behavior of the `dev-tool package resolve` command. + +### Command Interface + +Every command file's exports must implement the `CommandModule` interface defined in `src/util/commandModule.ts`. The interface requires that every command export a constant `commandInfo` that implements the `CommandInfo` interface defined in the same file. The `CommandInfo` interface specifies the name, description, and options (command-line arguments) of the command. The command module must also export an async handler function as its default export. Two helper functions, `leafCommand` and `subCommand` are provided to assist with development and to provide strong type-checking when +extending dev-tool. + +### Creating a new leaf command + +To create a new leaf command in one of the existing sub-command, create a new TypeScript file for that command. Make sure that your module exports the required `commandInfo` and default handler function. When creating a command, use the `leafCommand` helper to get a strongly-typed `options` parameter for your handler. + +As an example, we can create a new `hello-world` command under the `dev-tool package` sub-command. The command will print out a string using the many different logging functions. It will accept an argument `--echo ` that specifies the string to be printed. + +`src/commands/package/hello-world.ts` +```typescript +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { createPrinter } from "../../util/printer"; +import { leafCommand } from "../../util/commandBuilder"; + +const log = createPrinter("hello-world"); + +export const commandInfo = { + name: "hello-world", + description: + "print a lovely message", + options: { + echo: { + kind: "string", + description: "override the message to be printed", + default: "Hello world!" + } + } +} as const; + +export default leafCommand(commandInfo, async (options) => { + // Demonstrate the colorized command output. + log("Normal:", options.echo); + log.success("Success:", options.echo); + log.info("Info:", options.echo); + log.warn("Warn:", options.echo); + log.error("Error:", options.echo); + log.debug("Debug:", options.echo); + + return true; +}); +``` + +(__Note__: the `as const` after the definition of `commandInfo` is important for the type of `options` in the handler to be inferred as tightly as possible.) + +As a last step, add a mapping for the `"hello-world"` command to the sub-command map in `src/commands/package/index.ts`. This will allow the command to resolve: + +`src/commands/package/index.ts` +```typescript +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { subCommand } from "../../util/commandBuilder"; + +export const commandInfo = { + name: "package", + description: "manage SDK packages in the monorepo" +}; + +export default subCommand(commandInfo, { + "hello-world": () => import("./hello-world"), + // ... rest of the sub-commands still here +}); +``` + +At this point, the command is ready. When using `leafCommand` or `subCommand`, parsing and handling of arguments, including the `--help` output will be handled automatically by the command infrastructure. Debug output will only be shown if the `DEBUG` environment variable is set. Try it out: + +- Use `dev-tool package hello-world` to see the default output of the command +- Use `DEBUG=true dev-tool package hello-world` to see the full debugging output +- Use `dev-tool package hello-world --help` to view the generated help pages and make sure they are correct +- Use `dev-tool package hello-world --echo ` to change the default `"Hello world!"` text to something else. +- Use `dev-tool package --help` to see the `hello-world` command in the help message of its parent command + +### Creating a new command with sub-commands + +To create a new branching sub-command, create a new folder in the source tree and add an `index.ts` file. The folder should be named the same as the new command. The `subCommand` helper function can assist with creating a branching command. + +As an example, we can convert the `hello-world` example above into a branching command `hello` with a single sub-command `world`. Instead of adding it to the `package` sub-command, we will add it to the root `dev-tool` command. + +Instead of creating a single file `hello-world.ts`, we will instead create a folder `src/commands/hello` and two ts files: `src/commands/hello/index.ts` and `src/commands/hello/world.ts`. In `src/commands/hello/index.ts`, we can define our new sub-command: + +`src/commands/hello/index.ts` +```typescript +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { subCommand } from "../../util/commandBuilder"; + +export const commandInfo = { + name: "hello", + description: "commands for printing some lovely messages" +}; + +export default subCommand(commandInfo, { + world: () => import("./world") +}); +``` + +(__Note__: Since we don't have any arguments or options to add to the sub-command, the `options` field of `commandInfo` is omitted (since the sub-command just delegates to its child commands, we wouldn't be able to use any options in this parent command anyway).) + +This simple file establishes the mapping from the command name `"world"` to our new command module `src/commands/hello/world.ts`. The contents of `world.ts` are very similar to the previous `hello-world.ts` module, but we will change the `name` field of `commandInfo` and the argument to `createPrinter`: + +`src/commands/hello/world.ts` +```typescript +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { createPrinter } from "../../util/printer"; +import { leafCommand } from "../../util/commandBuilder"; + +const log = createPrinter("world"); + +export const commandInfo = { + name: "world", + description: + "print a lovely message", + options: { + echo: { + kind: "string", + description: "override the message to be printed", + default: "Hello world!" + } + } +} as const; + +export default leafCommand(commandInfo, async (options) => { + // Demonstrate the colorized command output. + log("Normal:", options.echo); + log.success("Success:", options.echo); + log.info("Info:", options.echo); + log.warn("Warn:", options.echo); + log.error("Error:", options.echo); + log.debug("Debug:", options.echo); + + return true; +}); +``` + +The final step is to add a mapping to our new subcommand to the`baseCommands` map root `src/commands/index.ts` file: + +`src/commands/index.ts` +```typescript +// ... + +/** + * All of dev-tool's base commands and the modules that define them + */ +export const baseCommands = { + "hello": () => import("./hello") + // ... all other sub-commands still here +} as const; + +// ... +``` + +(__Note__: If we were adding our `hello` command to another sub-command rather than the root, we would just add it to that sub-command's `index.ts` instead of the root `src/commands/index.ts`, similar to how we added `hello-world` to `src/commands/package/index.ts` in the previous example.) + +### Understanding the Options Type + +When using `leafCommand`, the handler function takes a value `options` with a type that is generated from the `options` property of the `CommandInfo` object given as the first argument to `leafCommand`. The underlying parsing behavior is implemented by `minimist` and is validated in the `parseOptions` function in `src/util/commandBuilder.ts`. + +The structure of the `CommandInfo.options` field is a map from option names to a tagged union that supports three variants (using the "kind" property as the disciminant): + +- `"string"` for command-line flags that have a string value (for example, `--directory path/to/directory`) +- `"boolean"` for command-line flags that have a boolean value (for example, `--quiet` with no argument) +- `"multistring"` for command-line flags that have string values and may be specified more than once (for example, `--add-dir path/to/dir1 --add-dir path/to/dir2`) + +Each variant supports an optional `shortName` field that specifies a one-letter command alias (e.g. a value of `shortName: "d"` would make `-d` an alias of the `--directory` option above). Each also has an optional `default` parameter to specify the default value should the argument not be specified on the command-line. If no default value is provided, the type of the `options` value passed to the handler will be expanded to include `undefined` as a possible value. Finally, each option has a `description` field that includes the help text shown in the messages produced by `--help`. + +### Final Developer Notes + +- Using the `subCommand` and `leafCommand` helpers is not required. If a command module exports any function with the signature `(...args: string[]) => Promise` as its default export, it will run when the command is invoked and will be given the arguments passed in the parameters. __However__, only `subCommand` and `leafCommand` provide automatic argument parsing and handling of `--help`. The functions used to provide this behavior are located in the `src/util/commandBuilder.ts` module. +- Some additional helper modules can be found in `src/util` such as `resolveProject.ts` which walks up the directory hierarchy and finds the absolute path of the nearest SDK package directory (useful for commands like `samples` which always operate relative to the package directory) +- The tool runs using the `transpileOnly` option in the `ts-node` configuration, meaning it does not perform run-time type-checking. The build step of the package will run type-checking using `tsc`, so to check the tool's code for type errors, simply use `rushx build`. + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit https://cla.microsoft.com. + +When you submit a pull request, a CLA-bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. + +If you'd like to contribute to this library, please read the [contributing guide](https://github.com/Azure/azure-sdk-for-js/blob/master/CONTRIBUTING.md) to learn more about how to build and test the code. From bbd0d477cf4b1dd076c9d6c008970ea2bf5a85d8 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Wed, 18 Mar 2020 10:45:05 -0700 Subject: [PATCH 06/24] [ai-text-analytics] Update package.json to use new script. --- sdk/textanalytics/ai-text-analytics/package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sdk/textanalytics/ai-text-analytics/package.json b/sdk/textanalytics/ai-text-analytics/package.json index 19f1336212e2..6a83a5b07c81 100644 --- a/sdk/textanalytics/ai-text-analytics/package.json +++ b/sdk/textanalytics/ai-text-analytics/package.json @@ -46,12 +46,12 @@ "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c 2>&1", "build:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c 2>&1", - "build:samples": "dev-tool prep-samples && cd dist-samples && tsc -p .", + "build:samples": "dev-tool samples prep && cd dist-samples && tsc -p .", "build:test": "tsc -p . && rollup -c rollup.test.config.js 2>&1", "build": "tsc -p . && rollup -c 2>&1 && api-extractor run --local", "check-format": "prettier --list-different --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist dist-browser dist-esm test-dist temp types *.tgz *.log", - "execute:samples": "npm run build:samples && dev-tool run-samples dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", "extract-api": "tsc -p . && api-extractor run --local", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", From 8c790ce274996035c0e677d44f9313a20369b097 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Wed, 18 Mar 2020 10:53:30 -0700 Subject: [PATCH 07/24] Removed some development cruft --- common/tools/dev-tool/package.json | 2 +- .../dev-tool/src/commands/hello/index.ts | 13 -------- .../dev-tool/src/commands/hello/world.ts | 32 ------------------- common/tools/dev-tool/src/commands/index.ts | 5 ++- 4 files changed, 3 insertions(+), 49 deletions(-) delete mode 100644 common/tools/dev-tool/src/commands/hello/index.ts delete mode 100644 common/tools/dev-tool/src/commands/hello/world.ts diff --git a/common/tools/dev-tool/package.json b/common/tools/dev-tool/package.json index 5c0545d92c2d..49075f16e232 100644 --- a/common/tools/dev-tool/package.json +++ b/common/tools/dev-tool/package.json @@ -16,7 +16,7 @@ "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "pack": "npm pack 2>&1", "prebuild": "npm run clean", - "test": "./launch.js --num 10 --pre pre command --bool --bool2=true --post post -- --after --after-bool=true " + "unit-test": "echo skipped" }, "repository": "github:Azure/azure-sdk-for-js", "author": "Microsoft Corporation", diff --git a/common/tools/dev-tool/src/commands/hello/index.ts b/common/tools/dev-tool/src/commands/hello/index.ts deleted file mode 100644 index aea94ae7873d..000000000000 --- a/common/tools/dev-tool/src/commands/hello/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license - -import { subCommand } from "../../util/commandBuilder"; - -export const commandInfo = { - name: "hello", - description: "commands for printing some lovely messages" -}; - -export default subCommand(commandInfo, { - world: () => import("./world") -}); diff --git a/common/tools/dev-tool/src/commands/hello/world.ts b/common/tools/dev-tool/src/commands/hello/world.ts deleted file mode 100644 index 85fccc5bb3d1..000000000000 --- a/common/tools/dev-tool/src/commands/hello/world.ts +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license - -import { createPrinter } from "../../util/printer"; -import { leafCommand } from "../../util/commandBuilder"; - -const log = createPrinter("world"); - -export const commandInfo = { - name: "world", - description: - "print a lovely message", - options: { - echo: { - kind: "string", - description: "override the message to be printed", - default: "Hello world!" - } - } -} as const; - -export default leafCommand(commandInfo, async (options) => { - // Demonstrate the colorized command output. - log("Normal:", options.echo); - log.success("Success:", options.echo); - log.info("Info:", options.echo); - log.warn("Warn:", options.echo); - log.error("Error:", options.echo); - log.debug("Debug:", options.echo); - - return true; -}); \ No newline at end of file diff --git a/common/tools/dev-tool/src/commands/index.ts b/common/tools/dev-tool/src/commands/index.ts index e141e8e48a6c..53915cb927ed 100644 --- a/common/tools/dev-tool/src/commands/index.ts +++ b/common/tools/dev-tool/src/commands/index.ts @@ -8,9 +8,8 @@ import { subCommand } from "../util/commandBuilder"; */ export const baseCommands = { about: () => import("./about"), - "package": () => import("./package"), - "samples": () => import("./samples"), - "hello": () => import("./hello"), + package: () => import("./package"), + samples: () => import("./samples") } as const; /** From d746960652c1490282a49605b380f05bad59be94 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Wed, 18 Mar 2020 10:58:30 -0700 Subject: [PATCH 08/24] prettier + eslint --- common/tools/dev-tool/.eslintrc.json | 14 +++++ common/tools/dev-tool/launch.js | 1 - common/tools/dev-tool/package.json | 19 ++++--- common/tools/dev-tool/src/commands/about.ts | 10 +--- .../dev-tool/src/commands/package/resolve.ts | 11 ++-- .../dev-tool/src/commands/samples/dev.ts | 21 ++------ .../dev-tool/src/commands/samples/index.ts | 8 +-- .../dev-tool/src/commands/samples/prep.ts | 26 +++------ .../dev-tool/src/commands/samples/run.ts | 15 +++--- common/tools/dev-tool/src/index.ts | 2 +- .../tools/dev-tool/src/util/commandBuilder.ts | 39 +++++++------- .../tools/dev-tool/src/util/commandModule.ts | 42 +++++++-------- .../dev-tool/src/util/findMatchingFiles.ts | 9 ++-- .../dev-tool/src/util/printCommandUsage.ts | 27 ++++------ common/tools/dev-tool/src/util/printer.ts | 54 +++++++++---------- .../tools/dev-tool/src/util/resolveProject.ts | 12 ++--- 16 files changed, 138 insertions(+), 172 deletions(-) create mode 100644 common/tools/dev-tool/.eslintrc.json diff --git a/common/tools/dev-tool/.eslintrc.json b/common/tools/dev-tool/.eslintrc.json new file mode 100644 index 000000000000..4c169f1e2e67 --- /dev/null +++ b/common/tools/dev-tool/.eslintrc.json @@ -0,0 +1,14 @@ +{ + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:@typescript-eslint/eslint-recommended", + "plugin:@typescript-eslint/recommended" + ], + "rules": { + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-explicit-any": "off", + "@typescript-eslint/no-non-null-assertion": "off" + } +} diff --git a/common/tools/dev-tool/launch.js b/common/tools/dev-tool/launch.js index 1678e54c0255..908d0971110a 100755 --- a/common/tools/dev-tool/launch.js +++ b/common/tools/dev-tool/launch.js @@ -15,4 +15,3 @@ require("ts-node").register({ project: path.join(__dirname, "tsconfig.json") }); require(path.join(__dirname, "src", "index.ts")); - diff --git a/common/tools/dev-tool/package.json b/common/tools/dev-tool/package.json index 49075f16e232..539705696eeb 100644 --- a/common/tools/dev-tool/package.json +++ b/common/tools/dev-tool/package.json @@ -13,7 +13,8 @@ "build": "tsc", "clean": "rimraf dist dist-* *.tgz *.log", "extract-api": "echo skipped", - "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", + "format": "prettier --write \"src/**/*.ts\" \"*.{js,json}\"", + "lint": "eslint src --ext .ts -f html -o template-lintReport.html || exit 0", "pack": "npm pack 2>&1", "prebuild": "npm run clean", "unit-test": "echo skipped" @@ -27,18 +28,24 @@ "homepage": "https://github.com/azure/azure-sdk-for-js/tree/master/common/tools/dev-tool", "sideEffects": false, "private": true, + "prettier": "@azure/eslint-plugin-azure-sdk/prettier.json", "dependencies": { + "chalk": "~3.0.0", "fs-extra": "^8.1.0", - "ts-node": "^8.3.0", - "typescript": "~3.7.5", "minimist": "~1.2.5", - "chalk": "~3.0.0" + "ts-node": "^8.3.0", + "typescript": "~3.7.5" }, "devDependencies": { + "@azure/eslint-plugin-azure-sdk": "^3.0.0", + "@types/chalk": "~2.2.0", "@types/fs-extra": "^8.0.0", + "@types/minimist": "~1.2.0", "@types/node": "^8.0.0", + "@typescript-eslint/eslint-plugin": "^2.0.0", + "@typescript-eslint/parser": "^2.0.0", + "eslint": "^6.1.0", "rimraf": "^3.0.0", - "@types/minimist": "~1.2.0", - "@types/chalk": "~2.2.0" + "prettier": "^1.16.4" } } diff --git a/common/tools/dev-tool/src/commands/about.ts b/common/tools/dev-tool/src/commands/about.ts index 6e8b084b64a9..d8f89e673973 100644 --- a/common/tools/dev-tool/src/commands/about.ts +++ b/common/tools/dev-tool/src/commands/about.ts @@ -31,11 +31,7 @@ export default leafCommand(commandInfo, async (options) => { try { const packageInfo = await resolveProject(__dirname); - console.log( - chalk.blueBright( - ` Name/Version:\t${packageInfo.name}@${packageInfo.version}` - ) - ); + console.log(chalk.blueBright(` Name/Version:\t${packageInfo.name}@${packageInfo.version}`)); console.log(chalk.blueBright(` Location:\t${packageInfo.path}`)); console.log(); } catch (error) { @@ -50,9 +46,7 @@ export default leafCommand(commandInfo, async (options) => { await printCommandUsage(baseCommandInfo, baseCommands); - console.log( - "For more information about a given command, try `dev-tool COMMAND --help`" - ); + console.log("For more information about a given command, try `dev-tool COMMAND --help`"); console.log(); diff --git a/common/tools/dev-tool/src/commands/package/resolve.ts b/common/tools/dev-tool/src/commands/package/resolve.ts index ff567c266b9a..d35b2c9eac77 100644 --- a/common/tools/dev-tool/src/commands/package/resolve.ts +++ b/common/tools/dev-tool/src/commands/package/resolve.ts @@ -11,8 +11,7 @@ const log = createPrinter("resolve-package"); export const commandInfo = { name: "resolve", - description: - "display information about the project that owns a directory", + description: "display information about the project that owns a directory", options: { directory: { shortName: "d", @@ -28,8 +27,8 @@ export const commandInfo = { } } as const; -export default leafCommand(commandInfo, async options => { - const dirs = (options.directory || [process.cwd()]).map(p => path.resolve(p)); +export default leafCommand(commandInfo, async (options) => { + const dirs = (options.directory || [process.cwd()]).map((p) => path.resolve(p)); for (const dir of dirs) { try { const currentPackage = await resolveProject(dir); @@ -37,9 +36,7 @@ export default leafCommand(commandInfo, async options => { console.log(currentPackage.path); } else { log.success("== Detected package:", currentPackage.name); - log.info( - `Version specifier: ${currentPackage.name}@${currentPackage.version}` - ); + log.info(`Version specifier: ${currentPackage.name}@${currentPackage.version}`); log.info(`Location: ${path.resolve(dir, currentPackage.path)}`); } } catch (error) { diff --git a/common/tools/dev-tool/src/commands/samples/dev.ts b/common/tools/dev-tool/src/commands/samples/dev.ts index 034ebbe6e12f..160d4019d5a1 100644 --- a/common/tools/dev-tool/src/commands/samples/dev.ts +++ b/common/tools/dev-tool/src/commands/samples/dev.ts @@ -12,32 +12,21 @@ const log = createPrinter("dev-samples"); export const commandInfo = { name: "dev", - description: - "link samples to local sources for access to IntelliSense during development" + description: "link samples to local sources for access to IntelliSense during development" } as const; -export default leafCommand(commandInfo, async (_) => { +export default leafCommand(commandInfo, async () => { const pkg = await resolveProject(process.cwd()); - const relativeLinkName = path.join( - "samples", - "typescript", - "node_modules", - pkg.name - ); + const relativeLinkName = path.join("samples", "typescript", "node_modules", pkg.name); const linkName = path.join(pkg.path, relativeLinkName); - const relativeLinkTarget = path.relative( - linkName, - path.join(pkg.path, "samples") - ); + const relativeLinkTarget = path.relative(linkName, path.join(pkg.path, "samples")); await fs.ensureDir(path.dirname(linkName)); if (fs.existsSync(linkName)) { log.error("Link already exists:", linkName); - log.error( - "Make sure your samples tree is pristine before running dev-samples." - ); + log.error("Make sure your samples tree is pristine before running dev-samples."); return false; } diff --git a/common/tools/dev-tool/src/commands/samples/index.ts b/common/tools/dev-tool/src/commands/samples/index.ts index 8bb6daf74960..135cf6e739d8 100644 --- a/common/tools/dev-tool/src/commands/samples/index.ts +++ b/common/tools/dev-tool/src/commands/samples/index.ts @@ -9,7 +9,7 @@ export const commandInfo = { }; export default subCommand(commandInfo, { - "dev": () => import("./dev"), - "prep": () => import("./prep"), - "run": () => import("./run") -}); \ No newline at end of file + dev: () => import("./dev"), + prep: () => import("./prep"), + run: () => import("./run") +}); diff --git a/common/tools/dev-tool/src/commands/samples/prep.ts b/common/tools/dev-tool/src/commands/samples/prep.ts index dd3062433ec0..b3fbabe1fcb4 100644 --- a/common/tools/dev-tool/src/commands/samples/prep.ts +++ b/common/tools/dev-tool/src/commands/samples/prep.ts @@ -23,11 +23,7 @@ export const commandInfo = { * @param baseDir the base directory of the package * @param pkgName name of the package to use when looking for package-local imports */ -async function enableLocalRun( - fileName: string, - baseDir: string, - pkgName: string -) { +async function enableLocalRun(fileName: string, baseDir: string, pkgName: string) { const fileContents = await fs.readFile(fileName, { encoding: "utf-8" }); const isTs = fileName.endsWith(".ts"); const importRegex = isTs @@ -37,9 +33,7 @@ async function enableLocalRun( if (!importRegex.exec(fileContents)) { // With the newer methods of using helper files and batch running, this // should be a warning - log.warn( - `skipping ${fileName} because it did not contain a matching import/require` - ); + log.warn(`skipping ${fileName} because it did not contain a matching import/require`); return; } @@ -48,8 +42,7 @@ async function enableLocalRun( // `string.length - string.split(path.sep).join("").length` is a dirty but well-supported way to // count the depth of a path and that avoids the difficulty of creating a regexp constructor // that can escape both linux and windows path separators - const depth = - relativeDir.length - relativeDir.split(path.sep).join("").length; + const depth = relativeDir.length - relativeDir.split(path.sep).join("").length; let relativePath = new Array(depth).fill("..").join("/"); @@ -60,9 +53,7 @@ async function enableLocalRun( const importRenamedContents = fileContents.replace( importRegex, - isTs - ? `import $1 from "${relativePath}";` - : `const $1 = require("${relativePath}");` + isTs ? `import $1 from "${relativePath}";` : `const $1 = require("${relativePath}");` ); // Remove trailing call to main() @@ -82,7 +73,6 @@ async function* cat(...generators: AsyncIterable[]): AsyncIterable { } export default leafCommand(commandInfo, async (options) => { - let argumentDir; if (options.args.length) { argumentDir = path.resolve(options.args[0]); @@ -105,15 +95,11 @@ export default leafCommand(commandInfo, async (options) => { const tsDir = path.join(outputDir, "typescript", "src"); const tsFiles = findMatchingFiles( tsDir, - (name, entry) => - entry.isFile() && name.endsWith(".ts") && !name.endsWith(".d.ts") + (name, entry) => entry.isFile() && name.endsWith(".ts") && !name.endsWith(".d.ts") ); const jsDir = path.join(outputDir, "javascript"); - const jsFiles = findMatchingFiles( - jsDir, - (name, entry) => entry.isFile() && name.endsWith(".js") - ); + const jsFiles = findMatchingFiles(jsDir, (name, entry) => entry.isFile() && name.endsWith(".js")); for await (const fileName of cat(tsFiles, jsFiles)) { await enableLocalRun(fileName, pkg.path, pkg.name); diff --git a/common/tools/dev-tool/src/commands/samples/run.ts b/common/tools/dev-tool/src/commands/samples/run.ts index ecc9d52f6227..8db9a6c35cb8 100644 --- a/common/tools/dev-tool/src/commands/samples/run.ts +++ b/common/tools/dev-tool/src/commands/samples/run.ts @@ -19,20 +19,17 @@ export const commandInfo = { /** * Run a single sample file, accumulating any thrown errors into `accumulatedErrors` - * + * * @param name the file to run * @param accumulatedErrors an array to push truncated errors onto as tuples of [fileName, error] */ -async function runSingle( - name: string, - accumulatedErrors: Array<[string, string]> -) { +async function runSingle(name: string, accumulatedErrors: Array<[string, string]>) { log("Running", name); try { if (/.*\/samples\/.*/.exec(name)) { // This is an un-prepared sample, so just require it and it will run. await import(name); - } else if (!(/.*\/dist-samples\/.*/.exec(name))) { + } else if (!/.*\/dist-samples\/.*/.exec(name)) { // This is not an unprepared or a prepared sample log.warn("Executing a file that is neither in samples nor dist-samples."); } else { @@ -50,17 +47,17 @@ async function runSingle( } } -export default leafCommand(commandInfo, async options => { +export default leafCommand(commandInfo, async (options) => { if (options.args.length === 0) { throw new Error("At least one argument is required for run-samples"); } - const samples = options.args.map(dir => path.resolve(dir)); + const samples = options.args.map((dir) => path.resolve(dir)); // Patch the environment for the sample helper process.env.BATCH_RUN_SAMPLES = "true"; - let errors: Array<[string, string]> = []; + const errors: Array<[string, string]> = []; for (const sample of samples) { const stats = await fs.stat(sample); diff --git a/common/tools/dev-tool/src/index.ts b/common/tools/dev-tool/src/index.ts index 7e17e57bd712..0b33ab9c9a3a 100644 --- a/common/tools/dev-tool/src/index.ts +++ b/common/tools/dev-tool/src/index.ts @@ -6,7 +6,7 @@ import chalk from "chalk"; import { baseCommand } from "./commands"; // The main command is implemented using the same `subCommand` method. -baseCommand(...process.argv.slice(2)).catch(err => { +baseCommand(...process.argv.slice(2)).catch((err) => { console.trace(chalk.red("[Internal Error]", err.stack)); process.exit(1); }); diff --git a/common/tools/dev-tool/src/util/commandBuilder.ts b/common/tools/dev-tool/src/util/commandBuilder.ts index 4467a7038ad4..c1fc43095c9d 100644 --- a/common/tools/dev-tool/src/util/commandBuilder.ts +++ b/common/tools/dev-tool/src/util/commandBuilder.ts @@ -9,6 +9,8 @@ import { printCommandUsage, commandStack } from "./printCommandUsage"; const { debug: parseDebug, error: parseError } = createPrinter("parseOptions"); +const hasKey = (o: any, k: string) => Object.prototype.hasOwnProperty.call(o, k); + /** * The type of the Options map produced by {@link parseOptions} * given a const input type Opts that extends CommandOptions. @@ -35,10 +37,7 @@ export type ParsedOptions = { // and folds it into a map from long-key to resulting type. // If no default is specified, then the type will be implicitly or'd // with undefined - [K in Exclude< - keyof Opts, - "--" | "args" | "help" - >]: undefined extends Opts[K]["default"] + [K in Exclude]: undefined extends Opts[K]["default"] ? | (Opts[K]["kind"] extends "string" ? string @@ -77,29 +76,25 @@ export function parseOptions( // If options are not provided, use an empty set const options: CommandOptions = opts ?? {}; - const keys = Object.keys(options).filter(k => options.hasOwnProperty(k)); + const keys = Object.keys(options).filter((k) => hasKey(options, k)); const argMap = getArgs(args, { // Once an unidentified argument is encountered, stop parsing stopEarly: true, // Use type information for hinting to minimist about how arguments should // be handled - boolean: ["help", ...keys.filter(k => options[k].kind === "boolean")], - string: keys.filter(k => options[k].kind === "string"), + boolean: ["help", ...keys.filter((k) => options[k].kind === "boolean")], + string: keys.filter((k) => options[k].kind === "string"), // Roll up the optional short-names into aliases alias: keys.reduce( (o, key) => - options[key].shortName !== undefined - ? { ...o, [options[key].shortName!]: key } - : o, + options[key].shortName !== undefined ? { ...o, [options[key].shortName!]: key } : o, {} ), // Roll up the default values into the arg parser default: { ...keys.reduce( (o, key) => - options[key].default !== undefined - ? { ...o, [key]: options[key].default } - : o, + options[key].default !== undefined ? { ...o, [key]: options[key].default } : o, {} ), help: false @@ -112,19 +107,23 @@ export function parseOptions( delete result._; - function expectType(key: string, value : any, expected: string) : void { + function expectType(key: string, value: any, expected: string): void { if (Array.isArray(value)) { parseError(`Too many arguments for "${key}"`); throw new Error(`More than one value for "${key}" was given, but only one was expected`); } else if (typeof value !== expected && typeof value !== "undefined") { parseError(`Bad argument: "${key}" = ${value}`); - throw new Error(`Value of argument "${key}" was a ${typeof value} but a ${expected} was expected.`) + throw new Error( + `Value of argument "${key}" was a ${typeof value} but a ${expected} was expected.` + ); } } // Validate that multi-strings were passed correctly // no boolean values, and single-strings get wrapped into arrays - for (const multiKey of keys.filter(k => options[k].kind === "multistring" && result[k] !== undefined)) { + for (const multiKey of keys.filter( + (k) => options[k].kind === "multistring" && result[k] !== undefined + )) { if (!Array.isArray(result[multiKey])) { expectType(multiKey, result[multiKey], "string"); // Wrap in an array to preserve types @@ -137,12 +136,12 @@ export function parseOptions( } // Check that single-strings were not passed more than once - for (const stringKey of keys.filter(k => options[k].kind === "string")) { + for (const stringKey of keys.filter((k) => options[k].kind === "string")) { expectType(stringKey, result[stringKey], "string"); } // Check that booleans were passed correctly - for (const boolKey of keys.filter(k => options[k].kind === "boolean")) { + for (const boolKey of keys.filter((k) => options[k].kind === "boolean")) { expectType(boolKey, result[boolKey], "boolean"); } @@ -184,7 +183,7 @@ export function subCommand( log.debug(`$ ${commandName} ${commandArgs?.join(" ") ?? ""}`); - if (commands.hasOwnProperty(commandName)) { + if (hasKey(commands, commandName)) { const commandModule = await commands[commandName](); const status = await commandModule.default(...commandArgs); @@ -205,7 +204,7 @@ export function leafCommand( info: Info, handler: (options: ParsedOptions) => Promise ): (...args: string[]) => Promise { - return async (...args: string[]) => { + return async (...args: string[]): Promise => { const options = parseOptions(args, info.options); commandStack.push(info.name); diff --git a/common/tools/dev-tool/src/util/commandModule.ts b/common/tools/dev-tool/src/util/commandModule.ts index 41086d535797..8fcf68ef305d 100644 --- a/common/tools/dev-tool/src/util/commandModule.ts +++ b/common/tools/dev-tool/src/util/commandModule.ts @@ -24,24 +24,24 @@ export interface CommandModule { * Information about this command */ export interface CommandInfo { - /** - * Name of this command - */ - name: string; + /** + * Name of this command + */ + name: string; - /** - * One line of help text to be printed by the `dev-tool help` command - */ - description: string; + /** + * One line of help text to be printed by the `dev-tool help` command + */ + description: string; - /** - * Argument configuration for this command - * - * Used for type-checked argument parsing and help text. - * - * The options keys "help", "args", and "--" will be discarded, as they are handled internally. - */ - options?: CommandOptions; + /** + * Argument configuration for this command + * + * Used for type-checked argument parsing and help text. + * + * The options keys "help", "args", and "--" will be discarded, as they are handled internally. + */ + options?: CommandOptions; } /** @@ -65,8 +65,8 @@ export interface StringOptionDescription { * may be specified multiple times */ export interface MultiStringOptionDescription { - kind: "multistring", - default?: string[] + kind: "multistring"; + default?: string[]; } /** @@ -83,7 +83,7 @@ export interface BooleanOptionDescription { * optional default value, and optional short name (one-letter alias) */ export interface CommandOptions { - [k: string] : { + [k: string]: { /** * Optional one-letter alias */ @@ -92,10 +92,10 @@ export interface CommandOptions { * Help text for this option */ description: string; - } & OptionDescription + } & OptionDescription; } /** * A map from command name to an async function that loads its module */ -export type CommandLoader = { [k: string]: () => Promise }; \ No newline at end of file +export type CommandLoader = { [k: string]: () => Promise }; diff --git a/common/tools/dev-tool/src/util/findMatchingFiles.ts b/common/tools/dev-tool/src/util/findMatchingFiles.ts index b6fa199eb191..4e1249652864 100644 --- a/common/tools/dev-tool/src/util/findMatchingFiles.ts +++ b/common/tools/dev-tool/src/util/findMatchingFiles.ts @@ -33,7 +33,7 @@ const defaultFindOptions: FindOptions = { * * @param dir The root of the sample tree to search * @param matches Predicate that decides whether or not a file entry is included - * @param options options bag for + * @param options options bag for */ export async function* findMatchingFiles( dir: string, @@ -42,7 +42,7 @@ export async function* findMatchingFiles( ) { const q: FileInfo[] = []; - const options: FindOptions = {...defaultFindOptions, ...findOptions}; + const options: FindOptions = { ...defaultFindOptions, ...findOptions }; async function enqueueAll(dir: string) { const files = await fs.readdir(dir); @@ -78,10 +78,7 @@ export async function* findMatchingFiles( info.stat.isSocket() || info.stat.isSymbolicLink() ) { - debug( - "Encountered a special file in the sample tree. Skipping:", - info.fullPath - ); + debug("Encountered a special file in the sample tree. Skipping:", info.fullPath); } } } diff --git a/common/tools/dev-tool/src/util/printCommandUsage.ts b/common/tools/dev-tool/src/util/printCommandUsage.ts index ff14fa270b32..143d044ece5c 100644 --- a/common/tools/dev-tool/src/util/printCommandUsage.ts +++ b/common/tools/dev-tool/src/util/printCommandUsage.ts @@ -3,6 +3,8 @@ import { CommandInfo, CommandLoader } from "./commandModule"; +const hasKey = (o: any, k: string) => Object.prototype.hasOwnProperty.call(o, k); + /** * The stack of subcommands executed so far * @@ -34,17 +36,12 @@ export async function printCommandUsage( println(" --help\t display this help message"); if (info.options) { for (const k in info.options) { - if (info.options.hasOwnProperty(k)) { + if (hasKey(info.options, k)) { const shortName = - info.options[k].shortName !== undefined - ? `-${info.options[k].shortName},` - : ""; - const valueType = info.options[k].kind !== "boolean" - ? "" - : ""; - const acceptsMulti = info.options[k].kind === "multistring" - ? " (can be set multiple times)" - : ""; + info.options[k].shortName !== undefined ? `-${info.options[k].shortName},` : ""; + const valueType = info.options[k].kind !== "boolean" ? "" : ""; + const acceptsMulti = + info.options[k].kind === "multistring" ? " (can be set multiple times)" : ""; println(` ${shortName}--${k}\t${valueType} ${info.options[k].description}${acceptsMulti}`); } } @@ -53,24 +50,22 @@ export async function printCommandUsage( // COMMAND info if (subCommands) { println(); - println( - "COMMAND indicates the subcommand to be run. It can be one of the following:\n" - ); + println("COMMAND indicates the subcommand to be run. It can be one of the following:\n"); // Compute the number of tabs needed to separate commands from // docstrings assuming a default command-line tabstop of 8 const tabs = Math.ceil( (Math.max( ...Object.keys(subCommands) - .filter(key => subCommands.hasOwnProperty(key)) - .map(key => key.length + 2) + .filter((key) => hasKey(subCommands, key)) + .map((key) => key.length + 2) ) + 1) / 8 ); for (const command in subCommands) { - if (subCommands.hasOwnProperty(command)) { + if (hasKey(subCommands, command)) { const module = await subCommands[command](); const indent = "\t".repeat(tabs - Math.floor((command.length + 2) / 8)); println(` ${command}${indent}${module.commandInfo.description}`); diff --git a/common/tools/dev-tool/src/util/printer.ts b/common/tools/dev-tool/src/util/printer.ts index 021aad92920e..e309811bb092 100644 --- a/common/tools/dev-tool/src/util/printer.ts +++ b/common/tools/dev-tool/src/util/printer.ts @@ -15,32 +15,6 @@ export interface Printer extends ModeMap { (...values: any[]): void; } -const colors: ModeMap> = { - info: chalk.blueBright, - warn: chalk.yellow, - error: chalk.red, - debug: chalk.magenta, - success: chalk.green -}; - -const finalLogger: ModeMap = { - info: console.info, - warn: console.warn, - error: console.error, - debug(...values: string[]) { - if (process.env.DEBUG) { - const caller = getCaller(); - const fileName = caller?.getFileName(); - const callerInfo = `(@ ${ - fileName ? fileName : "" - }#${caller?.getFunctionName() ?? - ""}:${caller?.getLineNumber()}:${caller?.getColumnNumber()})`; - console.log(values[0], colors.debug(callerInfo), ...values.slice(1)); - } - }, - success: console.info -}; - /** * Gets the filename of the calling function */ @@ -60,6 +34,7 @@ function getCaller(): NodeJS.CallSite | undefined { while (error.stack.length > 0 && current !== caller) { caller = next(); } + // eslint-disable-next-line no-empty } catch (_) {} Error.prepareStackTrace = savedPrepareStackTrace; @@ -67,6 +42,30 @@ function getCaller(): NodeJS.CallSite | undefined { return caller; } +const colors: ModeMap> = { + info: chalk.blueBright, + warn: chalk.yellow, + error: chalk.red, + debug: chalk.magenta, + success: chalk.green +}; + +const finalLogger: ModeMap = { + info: console.info, + warn: console.warn, + error: console.error, + debug(...values: string[]) { + if (process.env.DEBUG) { + const caller = getCaller(); + const fileName = caller?.getFileName(); + const callerInfo = `(@ ${fileName ? fileName : ""}#${caller?.getFunctionName() ?? + ""}:${caller?.getLineNumber()}:${caller?.getColumnNumber()})`; + console.log(values[0], colors.debug(callerInfo), ...values.slice(1)); + } + }, + success: console.info +}; + /** * Create a pre-configured console printer for a given namespace. * @@ -91,8 +90,7 @@ function getCaller(): NodeJS.CallSite | undefined { */ export function createPrinter(name: string): Printer { const prefix = "[" + name + "]"; - const base = (...values: string[]) => - console.log(chalk.reset(prefix, ...values)); + const base = (...values: string[]) => console.log(chalk.reset(prefix, ...values)); for (const mode of printModes) { (base as any)[mode] = (...values: string[]) => diff --git a/common/tools/dev-tool/src/util/resolveProject.ts b/common/tools/dev-tool/src/util/resolveProject.ts index 56be71303ea1..dd496831c6f7 100644 --- a/common/tools/dev-tool/src/util/resolveProject.ts +++ b/common/tools/dev-tool/src/util/resolveProject.ts @@ -44,9 +44,7 @@ async function findAzSDKPackageJson(directory: string): Promise<[string, any]> { const files = await fs.readdir(directory); if (files.includes("rush.json")) { - throw new Error( - "Reached monorepo root, but no matching Azure SDK package was found." - ); + throw new Error("Reached monorepo root, but no matching Azure SDK package was found."); } for (const file of files) { @@ -56,9 +54,7 @@ async function findAzSDKPackageJson(directory: string): Promise<[string, any]> { if (await isAzureSDKPackage(fullPath)) { return [directory, packageObject]; } - debug( - `found package.json at ${fullPath}, but it is not an Azure SDK package` - ); + debug(`found package.json at ${fullPath}, but it is not an Azure SDK package`); } } @@ -71,9 +67,7 @@ async function findAzSDKPackageJson(directory: string): Promise<[string, any]> { * @param workingDirectory the directory to resolve the package from * @returns the package info for the SDK project that owns the given directory */ -export async function resolveProject( - workingDirectory: string -): Promise { +export async function resolveProject(workingDirectory: string): Promise { if (!fs.existsSync(workingDirectory)) { throw new Error(`No such file or directory: ${workingDirectory}`); } From 2e70796afc4d9b0206197b767c405ac9d79f4de1 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Wed, 18 Mar 2020 11:45:17 -0700 Subject: [PATCH 09/24] Quick fix to ParsedOptions type --- common/tools/dev-tool/src/util/commandBuilder.ts | 2 +- common/tools/dev-tool/src/util/printer.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/common/tools/dev-tool/src/util/commandBuilder.ts b/common/tools/dev-tool/src/util/commandBuilder.ts index c1fc43095c9d..e4d669c51e5c 100644 --- a/common/tools/dev-tool/src/util/commandBuilder.ts +++ b/common/tools/dev-tool/src/util/commandBuilder.ts @@ -55,7 +55,7 @@ export type ParsedOptions = { ? string[] : string[] | string | boolean; } - : {}); + : { [k: string]: any }); /** * Helper function to convert an args array to a parsed argument map. diff --git a/common/tools/dev-tool/src/util/printer.ts b/common/tools/dev-tool/src/util/printer.ts index e309811bb092..cd5d25a5e6d1 100644 --- a/common/tools/dev-tool/src/util/printer.ts +++ b/common/tools/dev-tool/src/util/printer.ts @@ -34,7 +34,7 @@ function getCaller(): NodeJS.CallSite | undefined { while (error.stack.length > 0 && current !== caller) { caller = next(); } - // eslint-disable-next-line no-empty + // eslint-disable-next-line no-empty } catch (_) {} Error.prepareStackTrace = savedPrepareStackTrace; From b024362b0a7f078e78d66b1280670d2c7e1ca45c Mon Sep 17 00:00:00 2001 From: Will Temple Date: Fri, 20 Mar 2020 10:40:53 -0700 Subject: [PATCH 10/24] WIP --- common/tools/dev-tool/.eslintrc.json | 4 +- common/tools/dev-tool/src/commands/about.ts | 4 +- common/tools/dev-tool/src/commands/index.ts | 2 +- .../dev-tool/src/commands/package/index.ts | 2 +- .../dev-tool/src/commands/package/resolve.ts | 20 +- .../dev-tool/src/commands/samples/dev.ts | 2 +- .../dev-tool/src/commands/samples/index.ts | 7 +- .../dev-tool/src/commands/samples/prep.ts | 2 +- .../dev-tool/src/commands/samples/run.ts | 2 +- .../CommandInfo.ts} | 85 +++---- .../dev-tool/src/framework/CommandModule.ts | 28 +++ .../tools/dev-tool/src/framework/command.ts | 135 +++++++++++ .../dev-tool/src/framework/parseOptions.ts | 140 +++++++++++ .../{util => framework}/printCommandUsage.ts | 42 ++-- .../tools/dev-tool/src/util/commandBuilder.ts | 220 ------------------ common/tools/dev-tool/src/util/printer.ts | 2 + .../tools/dev-tool/src/util/resolveProject.ts | 15 +- common/tools/dev-tool/tsconfig.json | 3 +- 18 files changed, 385 insertions(+), 330 deletions(-) rename common/tools/dev-tool/src/{util/commandModule.ts => framework/CommandInfo.ts} (54%) create mode 100644 common/tools/dev-tool/src/framework/CommandModule.ts create mode 100644 common/tools/dev-tool/src/framework/command.ts create mode 100644 common/tools/dev-tool/src/framework/parseOptions.ts rename common/tools/dev-tool/src/{util => framework}/printCommandUsage.ts (52%) delete mode 100644 common/tools/dev-tool/src/util/commandBuilder.ts diff --git a/common/tools/dev-tool/.eslintrc.json b/common/tools/dev-tool/.eslintrc.json index 4c169f1e2e67..5b9e539283c0 100644 --- a/common/tools/dev-tool/.eslintrc.json +++ b/common/tools/dev-tool/.eslintrc.json @@ -7,8 +7,6 @@ "plugin:@typescript-eslint/recommended" ], "rules": { - "@typescript-eslint/explicit-function-return-type": "off", - "@typescript-eslint/no-explicit-any": "off", - "@typescript-eslint/no-non-null-assertion": "off" + "@typescript-eslint/explicit-function-return-type": "off" } } diff --git a/common/tools/dev-tool/src/commands/about.ts b/common/tools/dev-tool/src/commands/about.ts index d8f89e673973..6b1730602362 100644 --- a/common/tools/dev-tool/src/commands/about.ts +++ b/common/tools/dev-tool/src/commands/about.ts @@ -6,8 +6,8 @@ import chalk from "chalk"; import { baseCommands, baseCommandInfo } from "."; import { resolveProject } from "../util/resolveProject"; import { createPrinter } from "../util/printer"; -import { leafCommand } from "../util/commandBuilder"; -import { printCommandUsage } from "../util/printCommandUsage"; +import { leafCommand } from "../framework/command"; +import { printCommandUsage } from "../framework/printCommandUsage"; const log = createPrinter("help"); diff --git a/common/tools/dev-tool/src/commands/index.ts b/common/tools/dev-tool/src/commands/index.ts index 53915cb927ed..232a1566c911 100644 --- a/common/tools/dev-tool/src/commands/index.ts +++ b/common/tools/dev-tool/src/commands/index.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -import { subCommand } from "../util/commandBuilder"; +import { subCommand } from "../framework/command"; /** * All of dev-tool's base commands and the modules that define them diff --git a/common/tools/dev-tool/src/commands/package/index.ts b/common/tools/dev-tool/src/commands/package/index.ts index e620eafbda9a..71f99f680033 100644 --- a/common/tools/dev-tool/src/commands/package/index.ts +++ b/common/tools/dev-tool/src/commands/package/index.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -import { subCommand } from "../../util/commandBuilder"; +import { subCommand } from "../../framework/command"; export const commandInfo = { name: "package", diff --git a/common/tools/dev-tool/src/commands/package/resolve.ts b/common/tools/dev-tool/src/commands/package/resolve.ts index d35b2c9eac77..879712a209aa 100644 --- a/common/tools/dev-tool/src/commands/package/resolve.ts +++ b/common/tools/dev-tool/src/commands/package/resolve.ts @@ -5,18 +5,20 @@ import path from "path"; import { resolveProject } from "../../util/resolveProject"; import { createPrinter } from "../../util/printer"; -import { leafCommand } from "../../util/commandBuilder"; +import { leafCommand } from "../../framework/command"; +import { describe } from "../../framework/command"; const log = createPrinter("resolve-package"); -export const commandInfo = { - name: "resolve", - description: "display information about the project that owns a directory", - options: { +export const commandInfo = describe( + "resolve", + "display information about the project that owns a directory", + { directory: { shortName: "d", - kind: "multistring", - description: "base directory for resolution (uses CWD if unset)" + kind: "string", + description: "base directory for resolution (uses CWD if unset)", + allowMultiple: true }, quiet: { shortName: "q", @@ -25,10 +27,10 @@ export const commandInfo = { description: "output only the directory name with no extra formatting" } } -} as const; +); export default leafCommand(commandInfo, async (options) => { - const dirs = (options.directory || [process.cwd()]).map((p) => path.resolve(p)); + const dirs = (options.directory ?? [process.cwd()]).map((p) => path.resolve(p)); for (const dir of dirs) { try { const currentPackage = await resolveProject(dir); diff --git a/common/tools/dev-tool/src/commands/samples/dev.ts b/common/tools/dev-tool/src/commands/samples/dev.ts index 160d4019d5a1..79ddf3773718 100644 --- a/common/tools/dev-tool/src/commands/samples/dev.ts +++ b/common/tools/dev-tool/src/commands/samples/dev.ts @@ -6,7 +6,7 @@ import path from "path"; import { resolveProject } from "../../util/resolveProject"; import { createPrinter } from "../../util/printer"; -import { leafCommand } from "../../util/commandBuilder"; +import { leafCommand } from "../../framework/command"; const log = createPrinter("dev-samples"); diff --git a/common/tools/dev-tool/src/commands/samples/index.ts b/common/tools/dev-tool/src/commands/samples/index.ts index 135cf6e739d8..b3b90a10562a 100644 --- a/common/tools/dev-tool/src/commands/samples/index.ts +++ b/common/tools/dev-tool/src/commands/samples/index.ts @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -import { subCommand } from "../../util/commandBuilder"; +import { subCommand, describe } from "../../framework/command"; -export const commandInfo = { - name: "samples", - description: "manage samples in an SDK package" -}; +export const commandInfo = describe("samples", "manage samples in an SDK package"); export default subCommand(commandInfo, { dev: () => import("./dev"), diff --git a/common/tools/dev-tool/src/commands/samples/prep.ts b/common/tools/dev-tool/src/commands/samples/prep.ts index b3fbabe1fcb4..f4681bc85dfd 100644 --- a/common/tools/dev-tool/src/commands/samples/prep.ts +++ b/common/tools/dev-tool/src/commands/samples/prep.ts @@ -7,7 +7,7 @@ import path from "path"; import { createPrinter } from "../../util/printer"; import { findMatchingFiles } from "../../util/findMatchingFiles"; import { resolveProject } from "../../util/resolveProject"; -import { leafCommand } from "../../util/commandBuilder"; +import { leafCommand } from "../../framework/command"; const log = createPrinter("prep-samples"); diff --git a/common/tools/dev-tool/src/commands/samples/run.ts b/common/tools/dev-tool/src/commands/samples/run.ts index 8db9a6c35cb8..30f850c316e5 100644 --- a/common/tools/dev-tool/src/commands/samples/run.ts +++ b/common/tools/dev-tool/src/commands/samples/run.ts @@ -6,7 +6,7 @@ import path from "path"; import { findMatchingFiles } from "../../util/findMatchingFiles"; import { createPrinter } from "../../util/printer"; -import { leafCommand } from "../../util/commandBuilder"; +import { leafCommand } from "../../framework/command"; const log = createPrinter("run-samples"); diff --git a/common/tools/dev-tool/src/util/commandModule.ts b/common/tools/dev-tool/src/framework/CommandInfo.ts similarity index 54% rename from common/tools/dev-tool/src/util/commandModule.ts rename to common/tools/dev-tool/src/framework/CommandInfo.ts index 8fcf68ef305d..ad572d0406a2 100644 --- a/common/tools/dev-tool/src/util/commandModule.ts +++ b/common/tools/dev-tool/src/framework/CommandInfo.ts @@ -1,29 +1,16 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -/** - * A command for use with the dev-tool - */ -export interface CommandModule { - /** - * The main async function of the command. - * - * @param args the arguments passed to the command - * - * @returns a promise resolving to `true` if the command succeeded, `false` otherwise - */ - default: (...args: string[]) => Promise; - - /** - * Metadata information about this subcommand - */ - commandInfo: CommandInfo; -} +export type StrictAllowMultiple = { + [K in keyof Opts]: boolean extends Opts[K]["allowMultiple"] + ? Opts[K] & { allowMultiple?: undefined } + : Opts[K]; +}; /** * Information about this command */ -export interface CommandInfo { +export interface CommandInfo { /** * Name of this command */ @@ -41,40 +28,7 @@ export interface CommandInfo { * * The options keys "help", "args", and "--" will be discarded, as they are handled internally. */ - options?: CommandOptions; -} - -/** - * Discriminated union representing the description of a command-line options - */ -export type OptionDescription = - | StringOptionDescription - | MultiStringOptionDescription - | BooleanOptionDescription; - -/** - * Option description for a string argument - */ -export interface StringOptionDescription { - kind: "string"; - default?: string; -} - -/** - * Option description for a string argument that - * may be specified multiple times - */ -export interface MultiStringOptionDescription { - kind: "multistring"; - default?: string[]; -} - -/** - * Option description for a boolean argument - */ -export interface BooleanOptionDescription { - kind: "boolean"; - default?: boolean; + options?: Options; } /** @@ -92,10 +46,31 @@ export interface CommandOptions { * Help text for this option */ description: string; + /** + * Whether or not the option may be specified multiple times + * + * Default: false + */ + allowMultiple?: boolean; } & OptionDescription; } +export type OptionDescription = StringOptionDescription | BooleanOptionDescription; + +/** + * Option description for a string argument + */ +export interface StringOptionDescription { + kind: "string"; + default?: string; +} + /** - * A map from command name to an async function that loads its module + * Option description for a boolean argument */ -export type CommandLoader = { [k: string]: () => Promise }; +export interface BooleanOptionDescription { + kind: "boolean"; + default?: boolean; + // minimist seemingly cannot handle booleans specified multiple times + allowMultiple?: false; +} diff --git a/common/tools/dev-tool/src/framework/CommandModule.ts b/common/tools/dev-tool/src/framework/CommandModule.ts new file mode 100644 index 000000000000..ad1d2f6608f4 --- /dev/null +++ b/common/tools/dev-tool/src/framework/CommandModule.ts @@ -0,0 +1,28 @@ +import { CommandInfo, CommandOptions } from "./commandInfo"; + +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +/** + * A command for use with the dev-tool + */ +export interface CommandModule { + /** + * The main async function of the command. + * + * @param args the arguments passed to the command + * + * @returns a promise resolving to `true` if the command succeeded, `false` otherwise + */ + default: (...args: string[]) => Promise; + + /** + * Metadata information about this subcommand + */ + commandInfo: CommandInfo; +} + +/** + * A map from command name to an async function that loads its module + */ +export type CommandLoader = { [k: string]: () => Promise> }; diff --git a/common/tools/dev-tool/src/framework/command.ts b/common/tools/dev-tool/src/framework/command.ts new file mode 100644 index 000000000000..c8362e53483e --- /dev/null +++ b/common/tools/dev-tool/src/framework/command.ts @@ -0,0 +1,135 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { CommandLoader } from "./commandModule"; +import { createPrinter } from "../util/printer"; +import { printCommandUsage, commandStack } from "./printCommandUsage"; +import { ParsedOptions, parseOptions } from "./parseOptions"; +import { CommandInfo, CommandOptions } from "./commandInfo"; + +/** + * Utility type that makes the type of the "allowMultiple" key in + * any options descriptions strictly "undefined" when it is not + * specified in the context of a refined extending type + */ +export type StrictAllowMultiple = { + [K in keyof Opts]: boolean extends Opts[K]["allowMultiple"] + ? Opts[K] & { allowMultiple?: undefined } + : Opts[K]; +}; + +/** + * Create a CommandInfo object that describes a command and its functionality. + * + * For the best type-checking support, please ensure that the `options` parameter + * is given to this function as a literal. + * + * Example: + * + * ```typescript + * export const commandInfo = describe( + * "resolve", + * "display information about the project that owns a directory", + * { + * directory: { + * shortName: "d", + * kind: "string", + * description: "base directory for resolution (uses CWD if unset)", + * allowMultiple: true + * }, + * quiet: { + * shortName: "q", + * kind: "boolean", + * default: false, + * description: "output only the directory name with no extra formatting" + * } + * } + * ); + * ``` + * + * @param name the name of this command + * @param description a one-line description of this command's functionality + * @param options the command's command-line options descriptions (see: {@link CommandOptions}) + */ +export function describe( + name: string, + description: string, + options?: Opts +): CommandInfo> { + return { + name, + description, + options: options as StrictAllowMultiple + }; +} + +/** + * Create a subcommand handler that will delegate handling + * to a set of lower-order subcommands. + * + * @param name name of this command (used in printed output) + * @param commands map from subcommand name to module implementing that command + */ +export function subCommand>( + info: Info, + commands: CommandLoader +): (...args: string[]) => Promise { + const log = createPrinter(info.name); + return async (...rawArgs: string[]) => { + commandStack.push(info.name); + + const options = parseOptions(rawArgs, info.options); + + log.debug("Parsed command line:", JSON.stringify(options)); + + if (options.help) { + await printCommandUsage(info, commands); + return true; + } + + const commandName = options.args[0]; + const commandArgs = options.args.slice(1); + + if (commandName === undefined) { + log.error("No sub-command provided."); + await printCommandUsage(info, commands, console.error); + process.exit(1); + } + + log.debug(`$ ${commandName} ${commandArgs?.join(" ") ?? ""}`); + + if (Object.prototype.hasOwnProperty.call(commands, commandName)) { + const commandModule = await commands[commandName](); + + const status = await commandModule.default(...commandArgs); + + if (!status) { + log.error("Errors occurred. See the output above."); + } + return status; + } else { + log.error("No such sub-command:", commandName); + await printCommandUsage(info, commands, console.error); + process.exit(1); + } + }; +} + +export function leafCommand>( + info: Info, + handler: (options: ParsedOptions>) => Promise +): (...args: string[]) => Promise { + return async (...args: string[]): Promise => { + const options = parseOptions(args, info.options); + + commandStack.push(info.name); + + // --help is treated specially + if (options.help) { + await printCommandUsage(info); + return true; + } + + return handler(options as ParsedOptions>); + }; +} diff --git a/common/tools/dev-tool/src/framework/parseOptions.ts b/common/tools/dev-tool/src/framework/parseOptions.ts new file mode 100644 index 000000000000..e16b4d875750 --- /dev/null +++ b/common/tools/dev-tool/src/framework/parseOptions.ts @@ -0,0 +1,140 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import getArgs from "minimist"; + +import { createPrinter } from "../util/printer"; +import { CommandOptions, StringOptionDescription, BooleanOptionDescription } from "./commandInfo"; + +const { debug: parseDebug, error: parseError } = createPrinter("parseOptions"); + +/** + * This helper type implements the logic for determining the + * type of an option according to its possible multiplicity + */ +export type MaybeMultiple = boolean extends P ? T | T[] : true extends P ? T[] : T; + +/** + * The real type of an option parsed from an OptionDescription + */ +export type OptionFor = MaybeMultiple< + Opt["allowMultiple"], + Opt extends StringOptionDescription + ? string + : Opt extends BooleanOptionDescription + ? boolean + : string | boolean +>; + +/** + * The type of the Options map produced by {@link parseOptions} + * given a const input type Opts that extends CommandOptions. + * + * You should probably not use this type directly. It is designed + * to be inferred when using {@link leafCommand}. + * + * Make sure the input type is declared const, otherwise a + * fully dynamic map will be produced. + */ +export type ParsedOptions = { + /** + * If the argument "--" was encountered when parsing, this property + * holds all arguments that occurred after the "--" + */ + "--"?: string[] | undefined; + /** + * Array of arguments to the command + */ + args: string[]; +} & { + // If no default is specified, then the type will be implicitly or'd + // with undefined + [K in Exclude]: undefined extends Opts[K]["default"] + ? OptionFor | undefined + : OptionFor; +}; + +/** + * Helper function to convert an args array to a parsed argument map. + * + * This is a strongly-typed wrapper around `minimist`. + * + * You should probably not use this function directly. The + * options should be parsed and their types should be inferred + * from {@link leafCommand}. + * + * @param args input array of arg strings from argv + * @param options option map of a command + */ +export function parseOptions( + args: string[], + opts?: Opts +): ParsedOptions> { + // If options are not provided, use an empty set + const options: CommandOptions = opts ?? {}; + + const keys = Object.keys(options); + const argMap = getArgs(args, { + // Once an unidentified argument is encountered, stop parsing + stopEarly: true, + // Use type information for hinting to minimist about how arguments should + // be handled + boolean: ["help", ...keys.filter((k) => options[k].kind === "boolean")], + string: keys.filter((k) => options[k].kind === "string"), + // Roll up the optional short-names into aliases + alias: keys.reduce( + (o, key) => + options[key].shortName !== undefined + ? { ...o, [options[key].shortName as string]: key } + : o, + {} + ), + // Roll up the default values into the arg parser + default: { + ...keys.reduce( + (o, key) => + options[key].default !== undefined ? { ...o, [key]: options[key].default } : o, + {} + ), + help: false + } + }); + + parseDebug("Parsed args:", JSON.stringify(argMap)); + + const result: ParsedOptions = { help: argMap.help, args: argMap._ }; + + function expectType(key: string, value: T, expected: string): void { + if (Array.isArray(value)) { + parseError(`Too many arguments for "${key}"`); + throw new Error(`More than one value for "${key}" was given, but only one was expected`); + } else if (typeof value !== expected && typeof value !== "undefined") { + parseError(`Bad argument: "${key}" = ${value}`); + throw new Error( + `Value of argument "${key}" was a ${typeof value} but a ${expected} was expected.` + ); + } + } + + // Type validation + for (const k of keys) { + result[k] = argMap[k]; + const opt = options[k]; + if (!opt.allowMultiple) { + expectType(k, result[k], opt.kind); + } else if (Array.isArray(result[k])) { + // allowMultiple === true, check all + for (const val of result[k] as (string | boolean)[]) { + expectType(k, val, opt.kind); + } + } else if (result[k] !== undefined) { + // allowMultiple === true, wrap + expectType(k, result[k], opt.kind); + result[k] = [result[k] as string | boolean]; + } + } + + parseDebug("Final arguments:", JSON.stringify(result)); + + return result as ParsedOptions>; +} diff --git a/common/tools/dev-tool/src/util/printCommandUsage.ts b/common/tools/dev-tool/src/framework/printCommandUsage.ts similarity index 52% rename from common/tools/dev-tool/src/util/printCommandUsage.ts rename to common/tools/dev-tool/src/framework/printCommandUsage.ts index 143d044ece5c..abb06cf68c49 100644 --- a/common/tools/dev-tool/src/util/printCommandUsage.ts +++ b/common/tools/dev-tool/src/framework/printCommandUsage.ts @@ -1,9 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -import { CommandInfo, CommandLoader } from "./commandModule"; - -const hasKey = (o: any, k: string) => Object.prototype.hasOwnProperty.call(o, k); +import { CommandLoader } from "./commandModule"; +import { CommandInfo, CommandOptions } from "./commandInfo"; /** * The stack of subcommands executed so far @@ -20,10 +19,10 @@ export const commandStack: string[] = []; * @param println optionally override the default printer */ export async function printCommandUsage( - info: CommandInfo, + info: CommandInfo, subCommands?: CommandLoader, println: (...values: string[]) => void = console.log -) { +): Promise { println(`${info.name} - ${info.description}\n`); println( `Usage: ${commandStack.join(" ")} [OPTIONS] ${ @@ -35,15 +34,12 @@ export async function printCommandUsage( println("Options:"); println(" --help\t display this help message"); if (info.options) { - for (const k in info.options) { - if (hasKey(info.options, k)) { - const shortName = - info.options[k].shortName !== undefined ? `-${info.options[k].shortName},` : ""; - const valueType = info.options[k].kind !== "boolean" ? "" : ""; - const acceptsMulti = - info.options[k].kind === "multistring" ? " (can be set multiple times)" : ""; - println(` ${shortName}--${k}\t${valueType} ${info.options[k].description}${acceptsMulti}`); - } + for (const [k, option] of Object.entries(info.options)) { + const shortName = option.shortName !== undefined ? `-${option.shortName},` : ""; + const valueType = option.kind !== "boolean" ? "" : ""; + const acceptsMulti = + option.kind === "string" && option.allowMultiple ? " (can be set multiple times)" : ""; + println(` ${shortName}--${k}\t${valueType} ${option.description}${acceptsMulti}`); } } @@ -55,21 +51,13 @@ export async function printCommandUsage( // Compute the number of tabs needed to separate commands from // docstrings assuming a default command-line tabstop of 8 const tabs = Math.ceil( - (Math.max( - ...Object.keys(subCommands) - .filter((key) => hasKey(subCommands, key)) - .map((key) => key.length + 2) - ) + - 1) / - 8 + (Math.max(...Object.keys(subCommands).map((key) => key.length + 2)) + 1) / 8 ); - for (const command in subCommands) { - if (hasKey(subCommands, command)) { - const module = await subCommands[command](); - const indent = "\t".repeat(tabs - Math.floor((command.length + 2) / 8)); - println(` ${command}${indent}${module.commandInfo.description}`); - } + for (const [command, load] of Object.entries(subCommands)) { + const module = await load(); + const indent = "\t".repeat(tabs - Math.floor((command.length + 2) / 8)); + println(` ${command}${indent}${module.commandInfo.description}`); } } diff --git a/common/tools/dev-tool/src/util/commandBuilder.ts b/common/tools/dev-tool/src/util/commandBuilder.ts deleted file mode 100644 index e4d669c51e5c..000000000000 --- a/common/tools/dev-tool/src/util/commandBuilder.ts +++ /dev/null @@ -1,220 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license - -import getArgs from "minimist"; - -import { CommandLoader, CommandOptions, CommandInfo } from "./commandModule"; -import { createPrinter } from "./printer"; -import { printCommandUsage, commandStack } from "./printCommandUsage"; - -const { debug: parseDebug, error: parseError } = createPrinter("parseOptions"); - -const hasKey = (o: any, k: string) => Object.prototype.hasOwnProperty.call(o, k); - -/** - * The type of the Options map produced by {@link parseOptions} - * given a const input type Opts that extends CommandOptions. - * - * You should probably not use this type directly. It is designed - * to be inferred when using {@link leafCommand}. - * - * Make sure the input type is declared const, otherwise a - * fully dynamic map will be produced. - */ -export type ParsedOptions = { - /** - * If the argument "--" was encountered when parsing, this property - * holds all arguments that occurred after the "--" - */ - "--": string[] | undefined; - /** - * Array of arguments to the command - */ - args: string[]; -} & (Opts extends CommandOptions - ? { - // This heinous type signature here just takes the options structure - // and folds it into a map from long-key to resulting type. - // If no default is specified, then the type will be implicitly or'd - // with undefined - [K in Exclude]: undefined extends Opts[K]["default"] - ? - | (Opts[K]["kind"] extends "string" - ? string - : Opts[K]["kind"] extends "boolean" - ? boolean - : Opts[K]["kind"] extends "multistring" - ? string[] - : string[] | string | boolean) - | undefined - : Opts[K]["kind"] extends "string" - ? string - : Opts[K]["kind"] extends "boolean" - ? boolean - : Opts[K]["kind"] extends "multistring" - ? string[] - : string[] | string | boolean; - } - : { [k: string]: any }); - -/** - * Helper function to convert an args array to a parsed argument map. - * - * This is a strongly-typed wrapper around `minimist`. - * - * You should probably not use this function directly. The - * options should be parsed and their types should be inferred - * from {@link leafCommand}. - * - * @param args input array of arg strings from argv - * @param options option map of a command - */ -export function parseOptions( - args: string[], - opts?: Opts -): ParsedOptions { - // If options are not provided, use an empty set - const options: CommandOptions = opts ?? {}; - - const keys = Object.keys(options).filter((k) => hasKey(options, k)); - const argMap = getArgs(args, { - // Once an unidentified argument is encountered, stop parsing - stopEarly: true, - // Use type information for hinting to minimist about how arguments should - // be handled - boolean: ["help", ...keys.filter((k) => options[k].kind === "boolean")], - string: keys.filter((k) => options[k].kind === "string"), - // Roll up the optional short-names into aliases - alias: keys.reduce( - (o, key) => - options[key].shortName !== undefined ? { ...o, [options[key].shortName!]: key } : o, - {} - ), - // Roll up the default values into the arg parser - default: { - ...keys.reduce( - (o, key) => - options[key].default !== undefined ? { ...o, [key]: options[key].default } : o, - {} - ), - help: false - } - }); - - parseDebug(JSON.stringify(argMap)); - - const result: any = { ...argMap, help: argMap.help, args: argMap._ }; - - delete result._; - - function expectType(key: string, value: any, expected: string): void { - if (Array.isArray(value)) { - parseError(`Too many arguments for "${key}"`); - throw new Error(`More than one value for "${key}" was given, but only one was expected`); - } else if (typeof value !== expected && typeof value !== "undefined") { - parseError(`Bad argument: "${key}" = ${value}`); - throw new Error( - `Value of argument "${key}" was a ${typeof value} but a ${expected} was expected.` - ); - } - } - - // Validate that multi-strings were passed correctly - // no boolean values, and single-strings get wrapped into arrays - for (const multiKey of keys.filter( - (k) => options[k].kind === "multistring" && result[k] !== undefined - )) { - if (!Array.isArray(result[multiKey])) { - expectType(multiKey, result[multiKey], "string"); - // Wrap in an array to preserve types - result[multiKey] = [result[multiKey]]; - } else { - for (const val of result[multiKey]) { - expectType(multiKey, val, "string"); - } - } - } - - // Check that single-strings were not passed more than once - for (const stringKey of keys.filter((k) => options[k].kind === "string")) { - expectType(stringKey, result[stringKey], "string"); - } - - // Check that booleans were passed correctly - for (const boolKey of keys.filter((k) => options[k].kind === "boolean")) { - expectType(boolKey, result[boolKey], "boolean"); - } - - return result as any; -} - -/** - * Create a subcommand handler that will delegate handling - * to a set of lower-order subcommands. - * - * @param name name of this command (used in printed output) - * @param commands map from subcommand name to module implementing that command - */ -export function subCommand( - info: CommandInfo, - commands: CommandLoader -): (...args: string[]) => Promise { - const log = createPrinter(info.name); - return async (...rawArgs: string[]) => { - commandStack.push(info.name); - - const options = parseOptions(rawArgs, info.options); - - log.debug("Parsed command line:", JSON.stringify(options)); - - if (options.help) { - await printCommandUsage(info, commands); - return true; - } - - const commandName = options.args[0]; - const commandArgs = options.args.slice(1); - - if (commandName === undefined) { - log.error("No sub-command provided."); - await printCommandUsage(info, commands, console.error); - process.exit(1); - } - - log.debug(`$ ${commandName} ${commandArgs?.join(" ") ?? ""}`); - - if (hasKey(commands, commandName)) { - const commandModule = await commands[commandName](); - - const status = await commandModule.default(...commandArgs); - - if (!status) { - log.error("Errors occurred. See the output above."); - } - return status; - } else { - log.error("No such sub-command:", commandName); - await printCommandUsage(info, commands, console.error); - process.exit(1); - } - }; -} - -export function leafCommand( - info: Info, - handler: (options: ParsedOptions) => Promise -): (...args: string[]) => Promise { - return async (...args: string[]): Promise => { - const options = parseOptions(args, info.options); - - commandStack.push(info.name); - - // --help is treated specially - if (options.help) { - await printCommandUsage(info); - return true; - } - - return handler(options as any); - }; -} diff --git a/common/tools/dev-tool/src/util/printer.ts b/common/tools/dev-tool/src/util/printer.ts index cd5d25a5e6d1..5f2b99d319c2 100644 --- a/common/tools/dev-tool/src/util/printer.ts +++ b/common/tools/dev-tool/src/util/printer.ts @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license. +/* eslint-disable @typescript-eslint/no-explicit-any */ + import chalk from "chalk"; const printModes = ["info", "warn", "error", "success", "debug"] as const; diff --git a/common/tools/dev-tool/src/util/resolveProject.ts b/common/tools/dev-tool/src/util/resolveProject.ts index dd496831c6f7..dd875ee3065a 100644 --- a/common/tools/dev-tool/src/util/resolveProject.ts +++ b/common/tools/dev-tool/src/util/resolveProject.ts @@ -8,6 +8,9 @@ import { createPrinter } from "./printer"; const { debug } = createPrinter("resolve-project"); +// eslint-disable-next-line @typescript-eslint/no-explicit-any +type PackageJson = any; + /** * Information about an Azure SDK for JS package */ @@ -27,7 +30,7 @@ export interface ProjectInfo { /** * The package info object (result of reading/parsing package.json) */ - packageJson: any; + packageJson: PackageJson; } async function isAzureSDKPackage(fileName: string): Promise { @@ -40,7 +43,7 @@ async function isAzureSDKPackage(fileName: string): Promise { } } -async function findAzSDKPackageJson(directory: string): Promise<[string, any]> { +async function findAzSDKPackageJson(directory: string): Promise<[string, PackageJson]> { const files = await fs.readdir(directory); if (files.includes("rush.json")) { @@ -58,7 +61,13 @@ async function findAzSDKPackageJson(directory: string): Promise<[string, any]> { } } - return findAzSDKPackageJson(path.join(directory, "..")); + const nextPath = path.resolve(path.join(directory, "..")); + + if (nextPath === directory) { + throw new Error("Reached filesystem root, but no matching Azure SDK package was found."); + } + + return findAzSDKPackageJson(nextPath); } /** diff --git a/common/tools/dev-tool/tsconfig.json b/common/tools/dev-tool/tsconfig.json index 840318d2bd4b..e85a3757a2d8 100644 --- a/common/tools/dev-tool/tsconfig.json +++ b/common/tools/dev-tool/tsconfig.json @@ -10,7 +10,8 @@ "alwaysStrict": true, "noImplicitAny": true, "noUnusedLocals": true, - "noUnusedParameters": true + "noUnusedParameters": true, + "downlevelIteration": true }, "exclude": ["node_modules"], "include": ["./src/**/*.ts", "./test/**/*.ts"] From 47ef1cbe29e70cd7883fcbce155297e805f94fe7 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Tue, 24 Mar 2020 11:47:16 -0700 Subject: [PATCH 11/24] Command framework improvements --- common/tools/dev-tool/README.md | 4 ++-- common/tools/dev-tool/src/commands/about.ts | 7 ++----- common/tools/dev-tool/src/commands/index.ts | 7 ++----- .../tools/dev-tool/src/commands/package/index.ts | 7 ++----- .../tools/dev-tool/src/commands/package/resolve.ts | 4 ++-- common/tools/dev-tool/src/commands/samples/dev.ts | 10 +++++----- .../tools/dev-tool/src/commands/samples/index.ts | 4 ++-- common/tools/dev-tool/src/commands/samples/prep.ts | 10 +++++----- common/tools/dev-tool/src/commands/samples/run.ts | 10 +++++----- common/tools/dev-tool/src/framework/CommandInfo.ts | 6 ------ .../tools/dev-tool/src/framework/CommandModule.ts | 2 +- common/tools/dev-tool/src/framework/command.ts | 14 ++++++++++---- .../tools/dev-tool/src/framework/parseOptions.ts | 2 +- .../dev-tool/src/framework/printCommandUsage.ts | 4 ++-- common/tools/dev-tool/src/util/printer.ts | 6 +++--- common/tools/dev-tool/tsconfig.json | 3 ++- 16 files changed, 46 insertions(+), 54 deletions(-) diff --git a/common/tools/dev-tool/README.md b/common/tools/dev-tool/README.md index 98b9c9e38fef..39394e041849 100644 --- a/common/tools/dev-tool/README.md +++ b/common/tools/dev-tool/README.md @@ -6,11 +6,11 @@ It provides a place to centralize scripts, resources, and processes for developm ## Installation -`dev-tool` runs using ts-node, so it does not need to be built. It is ready-to-go after a `rush update`. It additionally does not need to be installed to a user's machine in order to be used in `package.json` scripts, since it provdes the `dev-tool` binary to any dependent packages through the `bin` entry in its `package.json`. Simply add `@azure/dev-tool` to the `devDependencies` of a package, and the `dev-tool` binary will become available. If you wish to use `dev-tool` from the CLI manually, you can install it globally on your system by running `npm install -g` from this directory. +`dev-tool` runs using ts-node, so it does not need to be built. It is ready-to-go after a `rush update`. It additionally does not need to be installed to a user's machine in order to be used in `package.json` scripts, since it provides the `dev-tool` binary to any dependent packages through the `bin` entry in its `package.json`. Simply add `@azure/dev-tool` to the `devDependencies` of a package, and the `dev-tool` binary will become available. If you wish to use `dev-tool` from the CLI manually, you can install it globally on your system by running `npm install -g` from this directory. ## Usage -`dev-tool` uses a tree-shaped command hierarchy. For example, at the time of writing, the command tree looks like this: +`dev-tool` uses a command hierarchy. For example, at the time of writing, the command tree looks like this: `dev-tool` - `about` (display command help and information) diff --git a/common/tools/dev-tool/src/commands/about.ts b/common/tools/dev-tool/src/commands/about.ts index 6b1730602362..9a6187f1c713 100644 --- a/common/tools/dev-tool/src/commands/about.ts +++ b/common/tools/dev-tool/src/commands/about.ts @@ -6,7 +6,7 @@ import chalk from "chalk"; import { baseCommands, baseCommandInfo } from "."; import { resolveProject } from "../util/resolveProject"; import { createPrinter } from "../util/printer"; -import { leafCommand } from "../framework/command"; +import { leafCommand, makeCommandInfo } from "../framework/command"; import { printCommandUsage } from "../framework/printCommandUsage"; const log = createPrinter("help"); @@ -21,10 +21,7 @@ const banner = `\ Developer quality-of-life command for the Azure SDK for JS `; -export const commandInfo = { - name: "about", - description: "display command help and information" -} as const; +export const commandInfo = makeCommandInfo("about", "display command help and information"); export default leafCommand(commandInfo, async (options) => { console.log(chalk.blueBright(banner)); diff --git a/common/tools/dev-tool/src/commands/index.ts b/common/tools/dev-tool/src/commands/index.ts index 232a1566c911..6d157d8fe941 100644 --- a/common/tools/dev-tool/src/commands/index.ts +++ b/common/tools/dev-tool/src/commands/index.ts @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -import { subCommand } from "../framework/command"; +import { subCommand, makeCommandInfo } from "../framework/command"; /** * All of dev-tool's base commands and the modules that define them @@ -15,10 +15,7 @@ export const baseCommands = { /** * Metadata about the base command, only used in `dev-tool help` */ -export const baseCommandInfo = { - name: "dev-tool", - description: "Azure SDK for JS dev-tool" -} as const; +export const baseCommandInfo = makeCommandInfo("dev-tool", "Azure SDK for JS dev-tool"); /** * Default dev-tool subcommand diff --git a/common/tools/dev-tool/src/commands/package/index.ts b/common/tools/dev-tool/src/commands/package/index.ts index 71f99f680033..4badd034bf05 100644 --- a/common/tools/dev-tool/src/commands/package/index.ts +++ b/common/tools/dev-tool/src/commands/package/index.ts @@ -1,12 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -import { subCommand } from "../../framework/command"; +import { subCommand, makeCommandInfo } from "../../framework/command"; -export const commandInfo = { - name: "package", - description: "manage SDK packages in the monorepo" -}; +export const commandInfo = makeCommandInfo("package", "manage SDK packages in the monorepo"); export default subCommand(commandInfo, { resolve: () => import("./resolve") diff --git a/common/tools/dev-tool/src/commands/package/resolve.ts b/common/tools/dev-tool/src/commands/package/resolve.ts index 879712a209aa..e5e2d33116c4 100644 --- a/common/tools/dev-tool/src/commands/package/resolve.ts +++ b/common/tools/dev-tool/src/commands/package/resolve.ts @@ -6,11 +6,11 @@ import path from "path"; import { resolveProject } from "../../util/resolveProject"; import { createPrinter } from "../../util/printer"; import { leafCommand } from "../../framework/command"; -import { describe } from "../../framework/command"; +import { makeCommandInfo } from "../../framework/command"; const log = createPrinter("resolve-package"); -export const commandInfo = describe( +export const commandInfo = makeCommandInfo( "resolve", "display information about the project that owns a directory", { diff --git a/common/tools/dev-tool/src/commands/samples/dev.ts b/common/tools/dev-tool/src/commands/samples/dev.ts index 79ddf3773718..574ef5ab044b 100644 --- a/common/tools/dev-tool/src/commands/samples/dev.ts +++ b/common/tools/dev-tool/src/commands/samples/dev.ts @@ -6,14 +6,14 @@ import path from "path"; import { resolveProject } from "../../util/resolveProject"; import { createPrinter } from "../../util/printer"; -import { leafCommand } from "../../framework/command"; +import { leafCommand, makeCommandInfo } from "../../framework/command"; const log = createPrinter("dev-samples"); -export const commandInfo = { - name: "dev", - description: "link samples to local sources for access to IntelliSense during development" -} as const; +export const commandInfo = makeCommandInfo( + "dev", + "link samples to local sources for access to IntelliSense during development" +); export default leafCommand(commandInfo, async () => { const pkg = await resolveProject(process.cwd()); diff --git a/common/tools/dev-tool/src/commands/samples/index.ts b/common/tools/dev-tool/src/commands/samples/index.ts index b3b90a10562a..5639d589528c 100644 --- a/common/tools/dev-tool/src/commands/samples/index.ts +++ b/common/tools/dev-tool/src/commands/samples/index.ts @@ -1,9 +1,9 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -import { subCommand, describe } from "../../framework/command"; +import { subCommand, makeCommandInfo } from "../../framework/command"; -export const commandInfo = describe("samples", "manage samples in an SDK package"); +export const commandInfo = makeCommandInfo("samples", "manage samples in an SDK package"); export default subCommand(commandInfo, { dev: () => import("./dev"), diff --git a/common/tools/dev-tool/src/commands/samples/prep.ts b/common/tools/dev-tool/src/commands/samples/prep.ts index f4681bc85dfd..47990faffc71 100644 --- a/common/tools/dev-tool/src/commands/samples/prep.ts +++ b/common/tools/dev-tool/src/commands/samples/prep.ts @@ -7,14 +7,14 @@ import path from "path"; import { createPrinter } from "../../util/printer"; import { findMatchingFiles } from "../../util/findMatchingFiles"; import { resolveProject } from "../../util/resolveProject"; -import { leafCommand } from "../../framework/command"; +import { leafCommand, makeCommandInfo } from "../../framework/command"; const log = createPrinter("prep-samples"); -export const commandInfo = { - name: "prep", - description: "prepare samples for local source-linked execution" -} as const; +export const commandInfo = makeCommandInfo( + "prep", + "prepare samples for local source-linked execution" +); /** * Replaces package require/import statements with relative paths for CI diff --git a/common/tools/dev-tool/src/commands/samples/run.ts b/common/tools/dev-tool/src/commands/samples/run.ts index 30f850c316e5..72f86e2da085 100644 --- a/common/tools/dev-tool/src/commands/samples/run.ts +++ b/common/tools/dev-tool/src/commands/samples/run.ts @@ -6,16 +6,16 @@ import path from "path"; import { findMatchingFiles } from "../../util/findMatchingFiles"; import { createPrinter } from "../../util/printer"; -import { leafCommand } from "../../framework/command"; +import { leafCommand, makeCommandInfo } from "../../framework/command"; const log = createPrinter("run-samples"); const IGNORE = ["node_modules"]; -export const commandInfo = { - name: "run", - description: "execute a sample or all samples within a directory" -}; +export const commandInfo = makeCommandInfo( + "run", + "execute a sample or all samples within a directory" +); /** * Run a single sample file, accumulating any thrown errors into `accumulatedErrors` diff --git a/common/tools/dev-tool/src/framework/CommandInfo.ts b/common/tools/dev-tool/src/framework/CommandInfo.ts index ad572d0406a2..8e8e197479b7 100644 --- a/common/tools/dev-tool/src/framework/CommandInfo.ts +++ b/common/tools/dev-tool/src/framework/CommandInfo.ts @@ -1,12 +1,6 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -export type StrictAllowMultiple = { - [K in keyof Opts]: boolean extends Opts[K]["allowMultiple"] - ? Opts[K] & { allowMultiple?: undefined } - : Opts[K]; -}; - /** * Information about this command */ diff --git a/common/tools/dev-tool/src/framework/CommandModule.ts b/common/tools/dev-tool/src/framework/CommandModule.ts index ad1d2f6608f4..a5345a5d28b6 100644 --- a/common/tools/dev-tool/src/framework/CommandModule.ts +++ b/common/tools/dev-tool/src/framework/CommandModule.ts @@ -1,4 +1,4 @@ -import { CommandInfo, CommandOptions } from "./commandInfo"; +import { CommandInfo, CommandOptions } from "./CommandInfo"; // Copyright (c) Microsoft Corporation. // Licensed under the MIT license diff --git a/common/tools/dev-tool/src/framework/command.ts b/common/tools/dev-tool/src/framework/command.ts index c8362e53483e..0996eef7e8b9 100644 --- a/common/tools/dev-tool/src/framework/command.ts +++ b/common/tools/dev-tool/src/framework/command.ts @@ -1,11 +1,11 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -import { CommandLoader } from "./commandModule"; +import { CommandLoader } from "./CommandModule"; import { createPrinter } from "../util/printer"; import { printCommandUsage, commandStack } from "./printCommandUsage"; import { ParsedOptions, parseOptions } from "./parseOptions"; -import { CommandInfo, CommandOptions } from "./commandInfo"; +import { CommandInfo, CommandOptions } from "./CommandInfo"; /** * Utility type that makes the type of the "allowMultiple" key in @@ -51,7 +51,7 @@ export type StrictAllowMultiple = { * @param description a one-line description of this command's functionality * @param options the command's command-line options descriptions (see: {@link CommandOptions}) */ -export function describe( +export function makeCommandInfo( name: string, description: string, options?: Opts @@ -67,7 +67,7 @@ export function describe( * Create a subcommand handler that will delegate handling * to a set of lower-order subcommands. * - * @param name name of this command (used in printed output) + * @param info a the CommandInfo object that describes this command * @param commands map from subcommand name to module implementing that command */ export function subCommand>( @@ -115,6 +115,12 @@ export function subCommand>( }; } +/** + * Construct a command that runs a handler when invoked. + * + * @param info the CommandInfo object that describes this command + * @param handler a function to handle the execution of the command + */ export function leafCommand>( info: Info, handler: (options: ParsedOptions>) => Promise diff --git a/common/tools/dev-tool/src/framework/parseOptions.ts b/common/tools/dev-tool/src/framework/parseOptions.ts index e16b4d875750..7ea15944c6b0 100644 --- a/common/tools/dev-tool/src/framework/parseOptions.ts +++ b/common/tools/dev-tool/src/framework/parseOptions.ts @@ -4,7 +4,7 @@ import getArgs from "minimist"; import { createPrinter } from "../util/printer"; -import { CommandOptions, StringOptionDescription, BooleanOptionDescription } from "./commandInfo"; +import { CommandOptions, StringOptionDescription, BooleanOptionDescription } from "./CommandInfo"; const { debug: parseDebug, error: parseError } = createPrinter("parseOptions"); diff --git a/common/tools/dev-tool/src/framework/printCommandUsage.ts b/common/tools/dev-tool/src/framework/printCommandUsage.ts index abb06cf68c49..bf27316addd2 100644 --- a/common/tools/dev-tool/src/framework/printCommandUsage.ts +++ b/common/tools/dev-tool/src/framework/printCommandUsage.ts @@ -1,8 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -import { CommandLoader } from "./commandModule"; -import { CommandInfo, CommandOptions } from "./commandInfo"; +import { CommandLoader } from "./CommandModule"; +import { CommandInfo, CommandOptions } from "./CommandInfo"; /** * The stack of subcommands executed so far diff --git a/common/tools/dev-tool/src/util/printer.ts b/common/tools/dev-tool/src/util/printer.ts index 5f2b99d319c2..61f738402464 100644 --- a/common/tools/dev-tool/src/util/printer.ts +++ b/common/tools/dev-tool/src/util/printer.ts @@ -92,11 +92,11 @@ const finalLogger: ModeMap = { */ export function createPrinter(name: string): Printer { const prefix = "[" + name + "]"; - const base = (...values: string[]) => console.log(chalk.reset(prefix, ...values)); + const base = ((...values: string[]) => console.log(chalk.reset(prefix, ...values))) as Printer; for (const mode of printModes) { - (base as any)[mode] = (...values: string[]) => + base[mode] = (...values: string[]) => finalLogger[mode](...[prefix, ...values].map((value: string) => colors[mode](value))); } - return base as Printer; + return base; } diff --git a/common/tools/dev-tool/tsconfig.json b/common/tools/dev-tool/tsconfig.json index e85a3757a2d8..3174b214dcdd 100644 --- a/common/tools/dev-tool/tsconfig.json +++ b/common/tools/dev-tool/tsconfig.json @@ -1,8 +1,9 @@ { "compilerOptions": { + "target": "ESNext", + "module": "ESNext", "moduleResolution": "node", "noEmit": true, - "lib": ["ES6", "ESNext.AsyncIterable"], "esModuleInterop": true, "newLine": "LF", From 2a6a1292cb477b125be42439e0668888d7fb3be3 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Tue, 24 Mar 2020 12:33:52 -0700 Subject: [PATCH 12/24] Basic unit-tests for package resolution. --- common/tools/dev-tool/package.json | 17 +++++++++++----- .../dev-tool/test/resolveProject.spec.ts | 20 +++++++++++++++++++ common/tools/dev-tool/tsconfig.json | 6 ++++-- 3 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 common/tools/dev-tool/test/resolveProject.spec.ts diff --git a/common/tools/dev-tool/package.json b/common/tools/dev-tool/package.json index 539705696eeb..319fa7652276 100644 --- a/common/tools/dev-tool/package.json +++ b/common/tools/dev-tool/package.json @@ -11,13 +11,14 @@ "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build": "tsc", + "build:test": "echo skipped", "clean": "rimraf dist dist-* *.tgz *.log", "extract-api": "echo skipped", - "format": "prettier --write \"src/**/*.ts\" \"*.{js,json}\"", - "lint": "eslint src --ext .ts -f html -o template-lintReport.html || exit 0", + "format": "prettier --write src/**/*.ts test/**/*.ts *.{js,json}", + "lint": "eslint src test --ext .ts -f html -o template-lintReport.html || exit 0", "pack": "npm pack 2>&1", "prebuild": "npm run clean", - "unit-test": "echo skipped" + "unit-test": "mocha --require ts-node/register test/**/*.spec.ts" }, "repository": "github:Azure/azure-sdk-for-js", "author": "Microsoft Corporation", @@ -38,14 +39,20 @@ }, "devDependencies": { "@azure/eslint-plugin-azure-sdk": "^3.0.0", + "@types/chai": "^4.1.6", + "@types/chai-as-promised": "^7.1.0", "@types/chalk": "~2.2.0", "@types/fs-extra": "^8.0.0", "@types/minimist": "~1.2.0", + "@types/mocha": "^5.2.5", "@types/node": "^8.0.0", "@typescript-eslint/eslint-plugin": "^2.0.0", "@typescript-eslint/parser": "^2.0.0", + "chai": "^4.2.0", + "chai-as-promised": "^7.1.1", "eslint": "^6.1.0", - "rimraf": "^3.0.0", - "prettier": "^1.16.4" + "mocha": "^6.2.2", + "prettier": "^1.16.4", + "rimraf": "^3.0.0" } } diff --git a/common/tools/dev-tool/test/resolveProject.spec.ts b/common/tools/dev-tool/test/resolveProject.spec.ts new file mode 100644 index 000000000000..f440c7e503f3 --- /dev/null +++ b/common/tools/dev-tool/test/resolveProject.spec.ts @@ -0,0 +1,20 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license + +import { assert, use as chaiUse } from "chai"; +import chaiPromises from "chai-as-promised"; +chaiUse(chaiPromises); + +import path from "path"; + +import { resolveProject } from "../src/util/resolveProject"; + +describe("Project Resolution", async () => { + it("resolution halts at monorepo root", async () => { + await assert.isRejected(resolveProject(path.join(__dirname, "..", "..")), /monorepo root/); + }); + it("resolution halts at filesystem root", async () => { + const p = path.join(__dirname, "..", "..", "..", "..", ".."); + await assert.isRejected(resolveProject(p), /filesystem root/); + }); +}); diff --git a/common/tools/dev-tool/tsconfig.json b/common/tools/dev-tool/tsconfig.json index 3174b214dcdd..b5ecd73523d6 100644 --- a/common/tools/dev-tool/tsconfig.json +++ b/common/tools/dev-tool/tsconfig.json @@ -1,11 +1,13 @@ { "compilerOptions": { - "target": "ESNext", - "module": "ESNext", + "target": "ES2015", + "module": "commonjs", "moduleResolution": "node", "noEmit": true, "esModuleInterop": true, + "lib": ["ES6", "ESNext.AsyncIterable"], + "newLine": "LF", "strict": true, "alwaysStrict": true, From a4a40512749d055986e619bbfc00e5c77e7af280 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Tue, 24 Mar 2020 16:38:18 -0700 Subject: [PATCH 13/24] One more test, assorted changes --- common/tools/dev-tool/src/util/printer.ts | 5 +++-- common/tools/dev-tool/src/util/resolveProject.ts | 2 +- common/tools/dev-tool/test/resolveProject.spec.ts | 10 ++++++++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/common/tools/dev-tool/src/util/printer.ts b/common/tools/dev-tool/src/util/printer.ts index 61f738402464..418d7c379b98 100644 --- a/common/tools/dev-tool/src/util/printer.ts +++ b/common/tools/dev-tool/src/util/printer.ts @@ -4,6 +4,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import chalk from "chalk"; +import path from "path"; const printModes = ["info", "warn", "error", "success", "debug"] as const; @@ -25,7 +26,7 @@ function getCaller(): NodeJS.CallSite | undefined { let caller: NodeJS.CallSite | undefined = undefined; try { - const error = new Error() as any; + const error = new Error() as any as { stack: NodeJS.CallSite[] }; Error.prepareStackTrace = (_, stack) => stack; @@ -59,7 +60,7 @@ const finalLogger: ModeMap = { debug(...values: string[]) { if (process.env.DEBUG) { const caller = getCaller(); - const fileName = caller?.getFileName(); + const fileName = caller?.getFileName()?.split(path.join("azure-sdk-for-js", "common", "tools", "dev-tool")); const callerInfo = `(@ ${fileName ? fileName : ""}#${caller?.getFunctionName() ?? ""}:${caller?.getLineNumber()}:${caller?.getColumnNumber()})`; console.log(values[0], colors.debug(callerInfo), ...values.slice(1)); diff --git a/common/tools/dev-tool/src/util/resolveProject.ts b/common/tools/dev-tool/src/util/resolveProject.ts index dd875ee3065a..71be20ebeb64 100644 --- a/common/tools/dev-tool/src/util/resolveProject.ts +++ b/common/tools/dev-tool/src/util/resolveProject.ts @@ -53,7 +53,7 @@ async function findAzSDKPackageJson(directory: string): Promise<[string, Package for (const file of files) { if (file === "package.json") { const fullPath = path.join(directory, file); - const packageObject = await import(fullPath); + const packageObject = (await import(fullPath)).default; if (await isAzureSDKPackage(fullPath)) { return [directory, packageObject]; } diff --git a/common/tools/dev-tool/test/resolveProject.spec.ts b/common/tools/dev-tool/test/resolveProject.spec.ts index f440c7e503f3..6300962ed0cc 100644 --- a/common/tools/dev-tool/test/resolveProject.spec.ts +++ b/common/tools/dev-tool/test/resolveProject.spec.ts @@ -13,8 +13,18 @@ describe("Project Resolution", async () => { it("resolution halts at monorepo root", async () => { await assert.isRejected(resolveProject(path.join(__dirname, "..", "..")), /monorepo root/); }); + it("resolution halts at filesystem root", async () => { const p = path.join(__dirname, "..", "..", "..", "..", ".."); await assert.isRejected(resolveProject(p), /filesystem root/); }); + + it("resolution finds dev-tool package", async () => { + const packageInfo = await resolveProject(__dirname); + assert.equal(packageInfo.name, "@azure/dev-tool"); + assert.match( + packageInfo.path, + new RegExp(`.*${path.sep}${path.join("azure-sdk-for-js", "common", "tools", "dev-tool")}`) + ); + }); }); From 5e95de2ef557d8accd583d04451139abed18e544 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Tue, 24 Mar 2020 17:02:39 -0700 Subject: [PATCH 14/24] README update --- common/tools/dev-tool/README.md | 82 ++++++++++++--------------------- 1 file changed, 30 insertions(+), 52 deletions(-) diff --git a/common/tools/dev-tool/README.md b/common/tools/dev-tool/README.md index 39394e041849..a64d9d8516c7 100644 --- a/common/tools/dev-tool/README.md +++ b/common/tools/dev-tool/README.md @@ -29,12 +29,12 @@ The source hierarchy matches the command hierarchy. Every sub-command has its ow ### Command Interface -Every command file's exports must implement the `CommandModule` interface defined in `src/util/commandModule.ts`. The interface requires that every command export a constant `commandInfo` that implements the `CommandInfo` interface defined in the same file. The `CommandInfo` interface specifies the name, description, and options (command-line arguments) of the command. The command module must also export an async handler function as its default export. Two helper functions, `leafCommand` and `subCommand` are provided to assist with development and to provide strong type-checking when +Every command file's exports must implement the `CommandModule` interface defined in `src/util/commandModule.ts`. The interface requires that every command export a constant `commandInfo` that implements the `CommandInfo` interface defined in the same file. A helper command `makeCommandInfo` is provided to assist with the creation of this interface while providing strong type-checking of command-line options. The `CommandInfo` interface specifies the name, description, and options (command-line arguments) of the command. The command module must also export an async handler function as its default export. Two helper functions, `leafCommand` and `subCommand` are provided to assist with development and to provide strong type-checking when extending dev-tool. ### Creating a new leaf command -To create a new leaf command in one of the existing sub-command, create a new TypeScript file for that command. Make sure that your module exports the required `commandInfo` and default handler function. When creating a command, use the `leafCommand` helper to get a strongly-typed `options` parameter for your handler. +To create a new leaf command in one of the existing sub-command, create a new TypeScript file for that command. Make sure that your module exports the required `commandInfo` and default handler function. When creating the `commandInfo` object, use the `makeCommandInfo` helper function. When creating a command, use the `leafCommand` helper to get a strongly-typed `options` parameter for your handler. As an example, we can create a new `hello-world` command under the `dev-tool package` sub-command. The command will print out a string using the many different logging functions. It will accept an argument `--echo ` that specifies the string to be printed. @@ -44,51 +44,38 @@ As an example, we can create a new `hello-world` command under the `dev-tool pac // Licensed under the MIT license import { createPrinter } from "../../util/printer"; -import { leafCommand } from "../../util/commandBuilder"; +import { leafCommand, makeCommandInfo } from "../../framework/command"; const log = createPrinter("hello-world"); -export const commandInfo = { - name: "hello-world", - description: - "print a lovely message", - options: { - echo: { - kind: "string", - description: "override the message to be printed", - default: "Hello world!" - } +export const commandInfo = makeCommandInfo("hello-world", "print a lovely message", { + echo: { + kind: "string", + description: "override the message to be printed", + default: "Hello world!" } -} as const; +}); export default leafCommand(commandInfo, async (options) => { - // Demonstrate the colorized command output. - log("Normal:", options.echo); - log.success("Success:", options.echo); - log.info("Info:", options.echo); - log.warn("Warn:", options.echo); - log.error("Error:", options.echo); - log.debug("Debug:", options.echo); - - return true; + // Demonstrate the colorized command output. + log("Normal:", options.echo); + log.success("Success:", options.echo); + log.info("Info:", options.echo); + log.warn("Warn:", options.echo); + log.error("Error:", options.echo); + log.debug("Debug:", options.echo); + + return true; }); ``` -(__Note__: the `as const` after the definition of `commandInfo` is important for the type of `options` in the handler to be inferred as tightly as possible.) +(__Note__: using the `makeCommandInfo` function is required to have strong type-checking on the `options` parameter of the handler. The `options` field of `commandInfo` must have a very strong type, and `makeCommandInfo` takes care of ensuring that the type is as strongly specified as possible.) As a last step, add a mapping for the `"hello-world"` command to the sub-command map in `src/commands/package/index.ts`. This will allow the command to resolve: `src/commands/package/index.ts` ```typescript -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license - -import { subCommand } from "../../util/commandBuilder"; - -export const commandInfo = { - name: "package", - description: "manage SDK packages in the monorepo" -}; +// ... export default subCommand(commandInfo, { "hello-world": () => import("./hello-world"), @@ -117,19 +104,16 @@ Instead of creating a single file `hello-world.ts`, we will instead create a fol // Copyright (c) Microsoft Corporation. // Licensed under the MIT license -import { subCommand } from "../../util/commandBuilder"; +import { subCommand, makeCommandInfo } from "../../framework/command"; -export const commandInfo = { - name: "hello", - description: "commands for printing some lovely messages" -}; +export const commandInfo = makeCommandInfo("hello", "commands for printing some lovely messages"); export default subCommand(commandInfo, { world: () => import("./world") }); ``` -(__Note__: Since we don't have any arguments or options to add to the sub-command, the `options` field of `commandInfo` is omitted (since the sub-command just delegates to its child commands, we wouldn't be able to use any options in this parent command anyway).) +(__Note__: Since we don't have any arguments or options to add to the sub-command, the `options` argument to `makeCommandInfo` is omitted (since the sub-command just delegates to its child commands, we wouldn't be able to use any options in this parent command anyway).) This simple file establishes the mapping from the command name `"world"` to our new command module `src/commands/hello/world.ts`. The contents of `world.ts` are very similar to the previous `hello-world.ts` module, but we will change the `name` field of `commandInfo` and the argument to `createPrinter`: @@ -139,22 +123,16 @@ This simple file establishes the mapping from the command name `"world"` to our // Licensed under the MIT license import { createPrinter } from "../../util/printer"; -import { leafCommand } from "../../util/commandBuilder"; +import { leafCommand, makeCommandInfo } from "../../framework/command"; const log = createPrinter("world"); -export const commandInfo = { - name: "world", - description: - "print a lovely message", - options: { - echo: { - kind: "string", - description: "override the message to be printed", - default: "Hello world!" - } - } -} as const; +export const commandInfo = makeCommandInfo("world", "print a lovely message", { + echo: { + kind: "string", + description: "override the message to be printed", + default: "Hello world!" + }}); export default leafCommand(commandInfo, async (options) => { // Demonstrate the colorized command output. From 96238abc3c4cf7d80d70fc5e4a354b4636487434 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Fri, 27 Mar 2020 12:07:17 -0700 Subject: [PATCH 15/24] Migrated all packages with sample code to use dev-tool --- common/scripts/prep-samples.js | 209 ------------------ common/scripts/run-samples.js | 115 ---------- .../app-configuration/package.json | 2 +- sdk/eventhub/event-hubs/package.json | 2 +- .../keyvault-certificates/package.json | 6 +- sdk/keyvault/keyvault-keys/package.json | 6 +- sdk/keyvault/keyvault-secrets/package.json | 6 +- sdk/search/search/package.json | 6 +- sdk/servicebus/service-bus/package.json | 4 +- sdk/storage/storage-blob/package.json | 6 +- sdk/storage/storage-file-share/package.json | 6 +- sdk/storage/storage-queue/package.json | 6 +- 12 files changed, 17 insertions(+), 357 deletions(-) delete mode 100644 common/scripts/prep-samples.js delete mode 100644 common/scripts/run-samples.js diff --git a/common/scripts/prep-samples.js b/common/scripts/prep-samples.js deleted file mode 100644 index 6c521c039c29..000000000000 --- a/common/scripts/prep-samples.js +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -/** - * prep-samples.js - * - * Prepares sample files for execution in CI by replacing abosolute package imports with relative - * imports. This is useful because it allows us to check in "camera-ready" copies of our samples - * so that they can be ingested directly into external documentation pipelines, while still allowing - * us to compile and run our samples in CI. - * - * Usage: node prep-samples.js [PACKAGE PATH] - * - PACKAGE PATH should be set to the directory of a `package.json` for a package that contains - * TypeScript samples. - * - If PACKAGE PATH is not specified, CWD will be used - * - * The command expects to find a directory tree `samples/typescript` under PACKAGE PATH. - * - * WARNING: This script ___WILL NOT___ revert changes it makes to the samples. Make sure any staged - * changes you have made to the samples are committed to git or otherwise preserved, as this script - * will completely overwrite them! - */ - -const baseFS = require("fs"); -const path = require("path"); -const promisify = require("util").promisify; - -const exec = promisify(require("child_process").execFile); - -// Node >= 10 provide fs.promises, but since we're still building Node 8 for now -// we need to use util.promisify if fs.promises doesn't exist -const fs = - baseFS.promises || - (() => { - return { - readdir: promisify(baseFS.readdir), - readFile: promisify(baseFS.readFile), - writeFile: promisify(baseFS.writeFile) - }; - })(); - -/** - * Breadth-first search for files matching a given predicate - * - * @param {string} dir The root of the sample tree to search - * @param {(fs.Entry) => boolean} matches Predicate that decides whether or not a file entry is included - * @returns - */ -async function* findMatchingFiles(dir, matches) { - const initialFiles = await fs.readdir(dir, { withFileTypes: true }); - - // BFS Queue and queue index - const q = initialFiles.map(f => [f, dir]); - - while (q.length) { - // [fs.Dirent, string] (file and dirName part of the full path) - const [entry, dirName] = q.shift(); - const fullPath = path.join(dirName, entry.name); - - if (entry.isDirectory()) { - // Enqueue children of this directory to the bfs - const children = await fs.readdir(fullPath, { withFileTypes: true }); - for (const child of children) { - q.push([child, fullPath]); - } - } else if (matches(entry)) { - yield fullPath; - } else if ( - entry.isBlockDevice() || - entry.isCharacterDevice() || - entry.isFIFO() || - entry.isSocket() || - entry.isSymbolicLink() - ) { - console.warn( - "[prep-samples] WARNING: Encountered a special file in the sample tree. Skipping:", - fullPath - ); - } - } - - // The full trace of files visited by the iterator is returned and can be accessed using `iter.value` - // once it is `done`, in case it is ever needed for debugging - return q; -} - -/** - * Replaces package require/import statements with relative paths for CI - * - * @param {string} fileName the name of the file to open and process - * @param {string} baseDir the base directory of the package - * @param {string} pkgName name of the package to use when looking for package-local imports - */ -async function enableLocalRun(fileName, baseDir, pkgName) { - const fileContents = await fs.readFile(fileName, { encoding: "utf-8" }); - const isTs = fileName.endsWith(".ts"); - const importRegex = isTs - ? new RegExp(`import\\s+(.*)\\s+from\\s+"${pkgName}";?\\s?`, "s") - : new RegExp(`const\\s+(.*)\\s*=\\s*require\\("${pkgName}"\\);?\\s?`, "s"); - - if (!importRegex.exec(fileContents)) { - // With the newer methods of using helper files and batch running, this - // should be a warning - console.warn( - `[prep-samples] skipping ${fileName} because it did not contain a matching import/require` - ); - return; - } - - const relativeDir = path.dirname(fileName.replace(baseDir, "")); - - // `string.length - string.split(path.sep).join("").length` is a dirty but well-supported way to - // count the depth of a path and that avoids the difficulty of creating a regexp constructor - // that can escape both linux and windows path separators - const depth = - relativeDir.length - relativeDir.split(path.sep).join("").length; - - let relativePath = new Array(depth).fill("..").join("/"); - - if (isTs) { - // TypeScript imports should use src directly - relativePath += "/src"; - } - - const importRenamedContents = fileContents.replace( - importRegex, - isTs - ? `import $1 from "${relativePath}";` - : `const $1 = require("${relativePath}");` - ); - - // Remove trailing call to main() - const updatedContents = importRenamedContents.replace( - new RegExp("main\\(\\)\\.catch.*", "s"), - isTs ? "" : "module.exports = { main };\n" - ); - - console.log("[prep-samples] Updating imports in", fileName); - return fs.writeFile(fileName, updatedContents, { encoding: "utf-8" }); -} - -async function main() { - // Accept a base directory (package directory) as an argument or use CWD - const args = process.argv.slice(2); - - let baseDir; - if (args.length) { - baseDir = path.resolve(args[0]); - } else { - baseDir = process.cwd(); - } - - const package = require(path.join(baseDir, "package.json")); - console.log( - "[prep-samples] Preparing samples for package:", - `${package.name}@${package.version}` - ); - - // Check if the package samples directory is dirty using git - // Refuse to proceed if this script may overwrite changes to samples. - try { - const gitDiff = await exec("git", [ - "status", - "-s", - path.join(baseDir, "samples") - ]); - if (gitDiff.stdout !== "") { - console.error( - "[prep-samples] Error: The samples tree is dirty. Refusing to continue." - ); - console.error( - "[prep-samples] Stash or commit your changes to the following files:" - ); - for (const line of gitDiff.stdout.trim().split("\n")) { - console.error(" -", line); - } - process.exit(1); - } - } catch (err) { - console.error( - "[prep-samples] Error: Failed to check the git status. Refusing to continue." - ); - process.exit(1); - } - - const tsDir = path.join(baseDir, "samples", "typescript", "src"); - for await (const fileName of findMatchingFiles( - tsDir, - entry => - entry.isFile() && - entry.name.endsWith(".ts") && - !entry.name.endsWith(".d.ts") - )) { - await enableLocalRun(fileName, baseDir, package.name); - } - - const jsDir = path.join(baseDir, "samples", "javascript"); - for await (const fileName of findMatchingFiles( - jsDir, - entry => entry.isFile() && entry.name.endsWith(".js") - )) { - await enableLocalRun(fileName, baseDir, package.name); - } -} - -main().catch(err => { - console.error("[prep-samples]", err); - process.exit(1); -}); diff --git a/common/scripts/run-samples.js b/common/scripts/run-samples.js deleted file mode 100644 index aeabbbc7b552..000000000000 --- a/common/scripts/run-samples.js +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT license. - -/** - * run-samples.js - * - * Runs all JavaScript files in a directory, using the calling convention for - * our sample code. - */ - -const baseFS = require("fs"); -const path = require("path"); - -const IGNORE = ["node_modules"]; - -// Node >= 10 provide fs.promises, but since we're still building Node 8 for now -// we need to use util.promisify if fs.promises doesn't exist -const fs = - baseFS.promises || - (() => { - const promisify = require("util").promisify; - return { - readdir: promisify(baseFS.readdir) - }; - })(); - -/** - * Breadth-first search for files matching a given predicate - * - * @param {string} tsDir The root of the sample tree to search - * @param {(fs.Entry) => boolean} matches Predicate that decides whether or not a file entry is included - * @returns - */ -async function* findMatchingFiles(tsDir, matches) { - const initialFiles = await fs.readdir(tsDir, { withFileTypes: true }); - - // BFS Queue and queue index - const q = initialFiles.map(f => [f, tsDir]); - - while (q.length) { - // [fs.Dirent, string] (file and dirName part of the full path) - const [entry, dirName] = q.shift(); - const fullPath = path.join(dirName, entry.name); - - if (IGNORE.includes(entry.name)) { - console.log("[run-samples] Ignoring", fullPath); - continue; - } - - if (entry.isDirectory()) { - // Enqueue children of this directory to the bfs - const children = await fs.readdir(fullPath, { withFileTypes: true }); - for (const child of children) { - q.push([child, fullPath]); - } - } else if (matches(entry)) { - yield fullPath; - } - } - - // The full trace of files visited by the iterator is returned and can be accessed using `iter.value` - // once it is `done`, in case it is ever needed for debugging - return q; -} - -async function main() { - // Accept a base directory - const args = process.argv.slice(2); - - let sampleDir; - if (args.length) { - sampleDir = path.resolve(args[0]); - } else { - sampleDir = process.cwd(); - } - - // Patch the environment for the sample helper - process.env.BATCH_RUN_SAMPLES = "true"; - - console.log("[run-samples] Running all samples in:", sampleDir); - - let errors = []; - - for await (const fileName of findMatchingFiles( - sampleDir, - entry => entry.isFile() && entry.name.endsWith(".js") - )) { - console.log("[run-samples] Running", fileName); - const { main: sampleMain } = require(fileName); - try { - await sampleMain(); - } catch (err) { - const truncatedError = err - .toString() - .split("\n")[0] - .slice(0, 100); - errors.push([path.basename(fileName), truncatedError]); - console.warn("[run-samples] Error in", fileName, ":", err); - console.warn("[run-samples] Continuing ..."); - } - } - - if (errors.length > 0) { - console.error("[run-samples] Errors occurred in the following files:"); - for (const [fileName, error] of errors) { - console.error(" -", fileName, "(", error, ")"); - } - process.exit(1); - } -} - -main().catch(err => { - console.error("[run-samples] Error:", err); - process.exit(1); -}); diff --git a/sdk/appconfiguration/app-configuration/package.json b/sdk/appconfiguration/app-configuration/package.json index 6cadbd71caa0..9f2233090f71 100644 --- a/sdk/appconfiguration/app-configuration/package.json +++ b/sdk/appconfiguration/app-configuration/package.json @@ -40,7 +40,7 @@ "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build": "tsc -p . && rollup -c 2>&1 && npm run extract-api", "build:test": "tsc -p . && rollup -c rollup.test.config.js 2>&1", - "build:samples": "node ../../../common/scripts/prep-samples.js && cd samples && tsc", + "build:samples": "dev-tool samples prep && cd samples && tsc", "check-format": "prettier --list-different --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist dist-esm test-dist types *.tgz *.log", "coverage": "nyc --reporter=lcov --exclude-after-remap=false mocha -t 120000 test-dist/index.js --reporter mocha-multi --reporter-options spec=-,mocha-junit-reporter=-", diff --git a/sdk/eventhub/event-hubs/package.json b/sdk/eventhub/event-hubs/package.json index ae117731a1ff..e0ffe8a76bd3 100644 --- a/sdk/eventhub/event-hubs/package.json +++ b/sdk/eventhub/event-hubs/package.json @@ -37,7 +37,7 @@ "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c 2>&1", "build:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c 2>&1", - "build:samples": "node ../../../common/scripts/prep-samples.js && cd samples && tsc", + "build:samples": "dev-tool samples prep && cd samples && tsc", "build:test:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c rollup.test.config.js 2>&1", "build:test:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c rollup.test.config.js 2>&1", "build:test": "tsc -p . && rollup -c rollup.test.config.js 2>&1", diff --git a/sdk/keyvault/keyvault-certificates/package.json b/sdk/keyvault/keyvault-certificates/package.json index 81e966ea8755..778fb38380f5 100644 --- a/sdk/keyvault/keyvault-certificates/package.json +++ b/sdk/keyvault/keyvault-certificates/package.json @@ -39,16 +39,14 @@ "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:minify": "uglifyjs -c -m --comments --source-map \"content='./dist/index.js.map'\" -o ./dist/index.min.js ./dist/index.js 2>&1", - "build:samples": "node ../../../common/scripts/prep-samples.js && cd samples && tsc", + "build:samples": "dev-tool samples prep && cd samples && tsc", "build:es6": "tsc -p tsconfig.json", "build:nodebrowser": "rollup -c 2>&1", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", "build": "npm run extract-api && npm run build:es6 && npm run build:nodebrowser", "check-format": "prettier --list-different --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist-esm dist-test typings *.tgz *.log samples/typescript/dist", - "execute:js-samples": "node ../../../common/scripts/run-samples.js samples/javascript/", - "execute:ts-samples": "node ../../../common/scripts/run-samples.js samples/typescript/dist/samples/typescript/src/", - "execute:samples": "npm run build:samples && npm run execute:js-samples && npm run execute:ts-samples", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", "extract-api": "tsc -p . && api-extractor run --local", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"samples/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", diff --git a/sdk/keyvault/keyvault-keys/package.json b/sdk/keyvault/keyvault-keys/package.json index 53aee4276b00..37ffe3835f41 100644 --- a/sdk/keyvault/keyvault-keys/package.json +++ b/sdk/keyvault/keyvault-keys/package.json @@ -38,16 +38,14 @@ "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:minify": "uglifyjs -c -m --comments --source-map \"content='./dist/index.js.map'\" -o ./dist/index.min.js ./dist/index.js 2>&1", - "build:samples": "node ../../../common/scripts/prep-samples.js && cd samples && tsc", + "build:samples": "dev-tool samples prep && cd samples && tsc", "build:es6": "tsc -p tsconfig.json", "build:nodebrowser": "rollup -c 2>&1", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", "build": "npm run extract-api && npm run build:es6 && npm run build:nodebrowser", "check-format": "prettier --list-different --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist dist-esm dist-test types *.tgz *.log dist-browser statistics.html coverage && rimraf src/**/*.js && rimraf test/**/*.js", - "execute:js-samples": "node ../../../common/scripts/run-samples.js samples/javascript/", - "execute:ts-samples": "node ../../../common/scripts/run-samples.js samples/typescript/dist/samples/typescript/src/", - "execute:samples": "npm run build:samples && npm run execute:js-samples && npm run execute:ts-samples", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", "extract-api": "tsc -p . && api-extractor run --local", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", diff --git a/sdk/keyvault/keyvault-secrets/package.json b/sdk/keyvault/keyvault-secrets/package.json index a596f5688606..6207a48bc73a 100644 --- a/sdk/keyvault/keyvault-secrets/package.json +++ b/sdk/keyvault/keyvault-secrets/package.json @@ -38,16 +38,14 @@ "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:minify": "uglifyjs -c -m --comments --source-map \"content='./dist/index.js.map'\" -o ./dist/index.min.js ./dist/index.js 2>&1", - "build:samples": "node ../../../common/scripts/prep-samples.js && cd samples && tsc", + "build:samples": "dev-tool samples prep && cd samples && tsc", "build:es6": "tsc -p tsconfig.json", "build:nodebrowser": "rollup -c 2>&1", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", "build": "npm run extract-api && npm run build:es6 && npm run build:nodebrowser", "check-format": "prettier --list-different --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist dist-esm dist-test types *.tgz *.log dist-browser statistics.html coverage && rimraf src/**/*.js && rimraf test/**/*.js", - "execute:js-samples": "node ../../../common/scripts/run-samples.js samples/javascript/", - "execute:ts-samples": "node ../../../common/scripts/run-samples.js samples/typescript/dist/samples/typescript/src/", - "execute:samples": "npm run build:samples && npm run execute:js-samples && npm run execute:ts-samples", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", "extract-api": "tsc -p . && api-extractor run --local", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", diff --git a/sdk/search/search/package.json b/sdk/search/search/package.json index 0128c3d60808..d41ae9e6acb4 100644 --- a/sdk/search/search/package.json +++ b/sdk/search/search/package.json @@ -10,14 +10,12 @@ "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c 2>&1", "build:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c 2>&1", - "build:samples": "node ../../../common/scripts/prep-samples.js && cd samples && tsc -p .", + "build:samples": "dev-tool samples prep && cd samples && tsc -p .", "build:test": "tsc -p . && rollup -c rollup.test.config.js 2>&1", "build": "tsc -p . && rollup -c 2>&1 && api-extractor run --local", "check-format": "prettier --list-different --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist dist-esm test-dist temp types *.tgz *.log", - "execute:js-samples": "node ../../../common/scripts/run-samples.js samples/javascript/", - "execute:ts-samples": "node ../../../common/scripts/run-samples.js samples/typescript/dist/samples/typescript/src/", - "execute:samples": "npm run build:samples && npm run execute:js-samples && npm run execute:ts-samples", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", "extract-api": "tsc -p . && api-extractor run --local", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "echo skipped", diff --git a/sdk/servicebus/service-bus/package.json b/sdk/servicebus/service-bus/package.json index 3443e02c01d5..24f5a2d3d449 100644 --- a/sdk/servicebus/service-bus/package.json +++ b/sdk/servicebus/service-bus/package.json @@ -39,15 +39,13 @@ "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c 2>&1", "build:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c 2>&1", - "build:samples": "node ../../../common/scripts/prep-samples.js && cd samples && tsc -p .", + "build:samples": "dev-tool samples prep && cd samples && tsc -p .", "build:test:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c rollup.test.config.js 2>&1", "build:test:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c rollup.test.config.js 2>&1", "build:test": "npm run build:test:node && npm run build:test:browser", "build": "tsc -p . && rollup -c 2>&1 && npm run extract-api", "check-format": "prettier --list-different --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist dist-esm test-dist typings *.tgz *.log coverage coverage-browser .nyc_output", - "execute:js-samples": "node ../../../common/scripts/run-samples.js samples/javascript/", - "execute:ts-samples": "node ../../../common/scripts/run-samples.js samples/typescript/dist/samples/typescript/src/", "execute:samples": "echo skipped", "extract-api": "tsc -p . && api-extractor run --local", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", diff --git a/sdk/storage/storage-blob/package.json b/sdk/storage/storage-blob/package.json index ff5691a0ea35..0949ce2727b0 100644 --- a/sdk/storage/storage-blob/package.json +++ b/sdk/storage/storage-blob/package.json @@ -33,7 +33,7 @@ "build:es6": "tsc -p tsconfig.json && npm run build:types", "build:nodebrowser": "rollup -c 2>&1", "build:samples": "npm run clean && npm run build:es6 && cross-env ONLY_NODE=true rollup -c 2>&1 && npm run build:prep-samples", - "build:prep-samples": "node ../../../common/scripts/prep-samples.js && cd samples && tsc", + "build:prep-samples": "dev-tool samples prep && cd samples && tsc", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", "build:types": "downlevel-dts typings/latest typings/3.1", "build": "npm run build:es6 && npm run build:nodebrowser && api-extractor run --local", @@ -41,9 +41,7 @@ "clean": "rimraf dist dist-esm dist-test typings temp dist-browser/*.js* dist-browser/*.zip statistics.html coverage coverage-browser .nyc_output *.tgz *.log test*.xml TEST*.xml", "clean:samples": "rimraf samples/javascript/node_modules samples/typescript/node_modules samples/typescript/dist samples/typescript/package-lock.json samples/javascript/package-lock.json", "extract-api": "tsc -p . && api-extractor run --local", - "execute:js-samples": "node ../../../common/scripts/run-samples.js samples/javascript/", - "execute:ts-samples": "node ../../../common/scripts/run-samples.js samples/typescript/dist/samples/typescript/src/", - "execute:samples": "npm run build:samples && npm run execute:js-samples && npm run execute:ts-samples", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", "integration-test:node": "nyc mocha -r esm --require source-map-support/register --reporter mocha-multi --reporter-options spec=-,mocha-junit-reporter=- --full-trace -t 300000 dist-esm/test/*.spec.js dist-esm/test/node/*.spec.js", diff --git a/sdk/storage/storage-file-share/package.json b/sdk/storage/storage-file-share/package.json index 19ff9bd4635e..ee527416ef7f 100644 --- a/sdk/storage/storage-file-share/package.json +++ b/sdk/storage/storage-file-share/package.json @@ -32,7 +32,7 @@ "build:es6": "tsc -p tsconfig.json && npm run build:types", "build:nodebrowser": "rollup -c 2>&1", "build:samples": "npm run clean && npm run build:es6 && cross-env ONLY_NODE=true rollup -c 2>&1 && npm run build:prep-samples", - "build:prep-samples": "node ../../../common/scripts/prep-samples.js && cd samples && tsc", + "build:prep-samples": "dev-tool samples prep && cd samples && tsc", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", "build:types": "downlevel-dts typings/latest typings/3.1", "build": "npm run build:es6 && npm run build:nodebrowser && api-extractor run --local", @@ -40,9 +40,7 @@ "clean": "rimraf dist dist-esm dist-test typings temp dist-browser/*.js* dist-browser/*.zip statistics.html coverage coverage-browser .nyc_output *.tgz *.log test*.xml TEST*.xml", "clean:samples": "rimraf samples/javascript/node_modules samples/typescript/node_modules samples/typescript/dist samples/typescript/package-lock.json samples/javascript/package-lock.json", "extract-api": "tsc -p . && api-extractor run --local", - "execute:js-samples": "node ../../../common/scripts/run-samples.js samples/javascript/", - "execute:ts-samples": "node ../../../common/scripts/run-samples.js samples/typescript/dist/samples/typescript/src/", - "execute:samples": "npm run build:samples && npm run execute:js-samples && npm run execute:ts-samples", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", "integration-test:node": "nyc mocha -r esm --require source-map-support/register --reporter mocha-multi --reporter-options spec=-,mocha-junit-reporter=- --full-trace -t 300000 dist-esm/test/*.spec.js dist-esm/test/node/*.spec.js", diff --git a/sdk/storage/storage-queue/package.json b/sdk/storage/storage-queue/package.json index 2a273ba87154..0fa58d9e415a 100644 --- a/sdk/storage/storage-queue/package.json +++ b/sdk/storage/storage-queue/package.json @@ -29,7 +29,7 @@ "build:es6": "tsc -p tsconfig.json && npm run build:types", "build:nodebrowser": "rollup -c 2>&1", "build:samples": "npm run clean && npm run build:es6 && cross-env ONLY_NODE=true rollup -c 2>&1 && npm run build:prep-samples", - "build:prep-samples": "node ../../../common/scripts/prep-samples.js && cd samples && tsc", + "build:prep-samples": "dev-tool samples prep && cd samples && tsc", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", "build:types": "downlevel-dts typings/latest typings/3.1", "build": "npm run build:es6 && npm run build:nodebrowser && api-extractor run --local", @@ -37,9 +37,7 @@ "clean": "rimraf dist dist-esm dist-test typings temp dist-browser/*.js* dist-browser/*.zip statistics.html coverage coverage-browser .nyc_output *.tgz *.log test*.xml TEST*.xml", "clean:samples": "rimraf samples/javascript/node_modules samples/typescript/node_modules samples/typescript/dist samples/typescript/package-lock.json samples/javascript/package-lock.json", "extract-api": "tsc -p . && api-extractor run --local", - "execute:js-samples": "node ../../../common/scripts/run-samples.js samples/javascript/", - "execute:ts-samples": "node ../../../common/scripts/run-samples.js samples/typescript/dist/samples/typescript/src/", - "execute:samples": "npm run build:samples && npm run execute:js-samples && npm run execute:ts-samples", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", "format": "prettier --write --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", "integration-test:node": "nyc mocha -r esm --require source-map-support/register --reporter mocha-multi --reporter-options spec=-,mocha-junit-reporter=- --full-trace -t 120000 dist-esm/test/*.spec.js dist-esm/test/node/*.spec.js", From 2401ec8092e049f9afd62586b1550a438de121b7 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Mon, 30 Mar 2020 10:18:18 -0700 Subject: [PATCH 16/24] Added dummy integration-test stub --- common/tools/dev-tool/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/common/tools/dev-tool/package.json b/common/tools/dev-tool/package.json index 319fa7652276..072df3e7a054 100644 --- a/common/tools/dev-tool/package.json +++ b/common/tools/dev-tool/package.json @@ -15,6 +15,7 @@ "clean": "rimraf dist dist-* *.tgz *.log", "extract-api": "echo skipped", "format": "prettier --write src/**/*.ts test/**/*.ts *.{js,json}", + "integration-test:node": "echo skipped", "lint": "eslint src test --ext .ts -f html -o template-lintReport.html || exit 0", "pack": "npm pack 2>&1", "prebuild": "npm run clean", From 127a7d9727b470aee188708c25f7de0f80a0c9e8 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Mon, 30 Mar 2020 10:50:28 -0700 Subject: [PATCH 17/24] Added dummy integration-test stub for eslint plugin --- common/tools/eslint-plugin-azure-sdk/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/common/tools/eslint-plugin-azure-sdk/package.json b/common/tools/eslint-plugin-azure-sdk/package.json index 66ed012aab01..9c7b0f2583e2 100644 --- a/common/tools/eslint-plugin-azure-sdk/package.json +++ b/common/tools/eslint-plugin-azure-sdk/package.json @@ -41,6 +41,7 @@ "clean": "rimraf dist/", "format": "prettier --write \"./**/*.{ts,json,md}\"", "format:check": "prettier --check \"./**/*.{ts,json,md}\"", + "integration-test:node": "echo skipped", "lint": "eslint src tests --ext .ts", "pack": "npm pack 2>&1", "prebuild": "npm run clean", From b7752c8230cbf80534d0e3cd1344cd7a10da96cb Mon Sep 17 00:00:00 2001 From: Will Temple Date: Mon, 30 Mar 2020 11:30:49 -0700 Subject: [PATCH 18/24] Added dummy integration-test:browser stubs --- common/tools/dev-tool/package.json | 1 + common/tools/eslint-plugin-azure-sdk/package.json | 1 + 2 files changed, 2 insertions(+) diff --git a/common/tools/dev-tool/package.json b/common/tools/dev-tool/package.json index 072df3e7a054..ef6ec4fb91c8 100644 --- a/common/tools/dev-tool/package.json +++ b/common/tools/dev-tool/package.json @@ -15,6 +15,7 @@ "clean": "rimraf dist dist-* *.tgz *.log", "extract-api": "echo skipped", "format": "prettier --write src/**/*.ts test/**/*.ts *.{js,json}", + "integration-test:browser": "echo skipped", "integration-test:node": "echo skipped", "lint": "eslint src test --ext .ts -f html -o template-lintReport.html || exit 0", "pack": "npm pack 2>&1", diff --git a/common/tools/eslint-plugin-azure-sdk/package.json b/common/tools/eslint-plugin-azure-sdk/package.json index 9c7b0f2583e2..0ab441102302 100644 --- a/common/tools/eslint-plugin-azure-sdk/package.json +++ b/common/tools/eslint-plugin-azure-sdk/package.json @@ -41,6 +41,7 @@ "clean": "rimraf dist/", "format": "prettier --write \"./**/*.{ts,json,md}\"", "format:check": "prettier --check \"./**/*.{ts,json,md}\"", + "integration-test:browser": "echo skipped", "integration-test:node": "echo skipped", "lint": "eslint src tests --ext .ts", "pack": "npm pack 2>&1", From 3b44d8fd83cf655dd6fba230e83b60aff0c94b8e Mon Sep 17 00:00:00 2001 From: Will Temple Date: Fri, 3 Apr 2020 11:52:42 -0700 Subject: [PATCH 19/24] Added dev-tool dependency to packages using it --- sdk/appconfiguration/app-configuration/package.json | 1 + sdk/eventhub/event-hubs/package.json | 1 + sdk/keyvault/keyvault-certificates/package.json | 1 + sdk/keyvault/keyvault-keys/package.json | 1 + sdk/keyvault/keyvault-secrets/package.json | 1 + sdk/search/search/package.json | 1 + sdk/servicebus/service-bus/package.json | 1 + sdk/storage/storage-blob/package.json | 1 + sdk/storage/storage-file-share/package.json | 1 + sdk/storage/storage-queue/package.json | 1 + 10 files changed, 10 insertions(+) diff --git a/sdk/appconfiguration/app-configuration/package.json b/sdk/appconfiguration/app-configuration/package.json index ba7174eaffb9..faad4606c3f6 100644 --- a/sdk/appconfiguration/app-configuration/package.json +++ b/sdk/appconfiguration/app-configuration/package.json @@ -79,6 +79,7 @@ "tslib": "^1.10.0" }, "devDependencies": { + "@azure/dev-tool": "^1.0.0", "@azure/identity": "1.1.0-preview.2", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-commonjs": "^11.0.1", diff --git a/sdk/eventhub/event-hubs/package.json b/sdk/eventhub/event-hubs/package.json index 0dcd1a7aa265..31a72074e191 100644 --- a/sdk/eventhub/event-hubs/package.json +++ b/sdk/eventhub/event-hubs/package.json @@ -87,6 +87,7 @@ "uuid": "^3.3.2" }, "devDependencies": { + "@azure/dev-tool": "^1.0.0", "@azure/eslint-plugin-azure-sdk": "^2.0.1", "@azure/identity": "1.1.0-preview.2", "@microsoft/api-extractor": "^7.5.4", diff --git a/sdk/keyvault/keyvault-certificates/package.json b/sdk/keyvault/keyvault-certificates/package.json index a396b52ffe7c..f597d2dc722a 100644 --- a/sdk/keyvault/keyvault-certificates/package.json +++ b/sdk/keyvault/keyvault-certificates/package.json @@ -91,6 +91,7 @@ "tslib": "^1.10.0" }, "devDependencies": { + "@azure/dev-tool": "^1.0.0", "@azure/eslint-plugin-azure-sdk": "^2.0.1", "@azure/identity": "1.1.0-preview.2", "@azure/keyvault-keys": "^4.1.0-preview.1", diff --git a/sdk/keyvault/keyvault-keys/package.json b/sdk/keyvault/keyvault-keys/package.json index c532104d5e31..97fb00320fa7 100644 --- a/sdk/keyvault/keyvault-keys/package.json +++ b/sdk/keyvault/keyvault-keys/package.json @@ -90,6 +90,7 @@ }, "devDependencies": { "@azure/abort-controller": "^1.0.0", + "@azure/dev-tool": "^1.0.0", "@azure/eslint-plugin-azure-sdk": "^2.0.1", "@azure/identity": "1.1.0-preview.2", "@azure/test-utils-recorder": "^1.0.0", diff --git a/sdk/keyvault/keyvault-secrets/package.json b/sdk/keyvault/keyvault-secrets/package.json index d41acf086ea5..f346619e780b 100644 --- a/sdk/keyvault/keyvault-secrets/package.json +++ b/sdk/keyvault/keyvault-secrets/package.json @@ -90,6 +90,7 @@ "tslib": "^1.10.0" }, "devDependencies": { + "@azure/dev-tool": "^1.0.0", "@azure/eslint-plugin-azure-sdk": "^2.0.1", "@azure/identity": "1.1.0-preview.2", "@azure/test-utils-recorder": "^1.0.0", diff --git a/sdk/search/search/package.json b/sdk/search/search/package.json index 2a3bab1294e7..358c4a2e3366 100644 --- a/sdk/search/search/package.json +++ b/sdk/search/search/package.json @@ -79,6 +79,7 @@ "tslib": "^1.10.0" }, "devDependencies": { + "@azure/dev-tool": "^1.0.0", "@azure/eslint-plugin-azure-sdk": "^3.0.0", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-json": "^4.0.0", diff --git a/sdk/servicebus/service-bus/package.json b/sdk/servicebus/service-bus/package.json index 0732ad8ac6f2..657397a97a45 100644 --- a/sdk/servicebus/service-bus/package.json +++ b/sdk/servicebus/service-bus/package.json @@ -89,6 +89,7 @@ "rhea-promise": "^1.0.0" }, "devDependencies": { + "@azure/dev-tool": "^1.0.0", "@azure/eslint-plugin-azure-sdk": "^2.0.1", "@azure/identity": "1.1.0-preview.2", "@microsoft/api-extractor": "^7.5.4", diff --git a/sdk/storage/storage-blob/package.json b/sdk/storage/storage-blob/package.json index 288a9c7de02e..ff6c602fe90f 100644 --- a/sdk/storage/storage-blob/package.json +++ b/sdk/storage/storage-blob/package.json @@ -107,6 +107,7 @@ "tslib": "^1.10.0" }, "devDependencies": { + "@azure/dev-tool": "^1.0.0", "@azure/identity": "1.1.0-preview.2", "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "^7.5.4", diff --git a/sdk/storage/storage-file-share/package.json b/sdk/storage/storage-file-share/package.json index 6ecad27346e0..c691e6df8aca 100644 --- a/sdk/storage/storage-file-share/package.json +++ b/sdk/storage/storage-file-share/package.json @@ -109,6 +109,7 @@ "tslib": "^1.10.0" }, "devDependencies": { + "@azure/dev-tool": "^1.0.0", "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "^7.5.4", "@rollup/plugin-commonjs": "^11.0.1", diff --git a/sdk/storage/storage-queue/package.json b/sdk/storage/storage-queue/package.json index 20b5ce4eb5ed..e14ad8f5095e 100644 --- a/sdk/storage/storage-queue/package.json +++ b/sdk/storage/storage-queue/package.json @@ -105,6 +105,7 @@ "tslib": "^1.10.0" }, "devDependencies": { + "@azure/dev-tool": "^1.0.0", "@azure/identity": "1.1.0-preview.2", "@azure/test-utils-recorder": "^1.0.0", "@microsoft/api-extractor": "^7.5.4", From 1cbef165780ed4ede8eae70496c387693eee058c Mon Sep 17 00:00:00 2001 From: Will Temple Date: Fri, 3 Apr 2020 12:02:18 -0700 Subject: [PATCH 20/24] Corrected build:samples step in package.json --- sdk/appconfiguration/app-configuration/package.json | 2 +- sdk/eventhub/event-hubs/package.json | 2 +- sdk/keyvault/keyvault-certificates/package.json | 2 +- sdk/keyvault/keyvault-keys/package.json | 2 +- sdk/keyvault/keyvault-secrets/package.json | 2 +- sdk/search/search/package.json | 2 +- sdk/servicebus/service-bus/package.json | 2 +- sdk/storage/storage-blob/package.json | 2 +- sdk/storage/storage-file-share/package.json | 2 +- sdk/storage/storage-queue/package.json | 2 +- 10 files changed, 10 insertions(+), 10 deletions(-) diff --git a/sdk/appconfiguration/app-configuration/package.json b/sdk/appconfiguration/app-configuration/package.json index faad4606c3f6..445620dfafb7 100644 --- a/sdk/appconfiguration/app-configuration/package.json +++ b/sdk/appconfiguration/app-configuration/package.json @@ -40,7 +40,7 @@ "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build": "tsc -p . && rollup -c 2>&1 && npm run extract-api", "build:test": "tsc -p . && rollup -c rollup.test.config.js 2>&1", - "build:samples": "dev-tool samples prep && cd samples && tsc", + "build:samples": "dev-tool samples prep && cd dist-samples && tsc", "check-format": "prettier --list-different --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist dist-esm test-dist types *.tgz *.log", "coverage": "nyc --reporter=lcov --exclude-after-remap=false mocha -t 120000 test-dist/index.js --reporter ../../../common/tools/mocha-multi-reporter.js", diff --git a/sdk/eventhub/event-hubs/package.json b/sdk/eventhub/event-hubs/package.json index 31a72074e191..8c1580f5d81b 100644 --- a/sdk/eventhub/event-hubs/package.json +++ b/sdk/eventhub/event-hubs/package.json @@ -37,7 +37,7 @@ "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c 2>&1", "build:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c 2>&1", - "build:samples": "dev-tool samples prep && cd samples && tsc", + "build:samples": "dev-tool samples prep && cd dist-samples && tsc", "build:test:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c rollup.test.config.js 2>&1", "build:test:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c rollup.test.config.js 2>&1", "build:test": "tsc -p . && rollup -c rollup.test.config.js 2>&1", diff --git a/sdk/keyvault/keyvault-certificates/package.json b/sdk/keyvault/keyvault-certificates/package.json index f597d2dc722a..724a0e06b9f7 100644 --- a/sdk/keyvault/keyvault-certificates/package.json +++ b/sdk/keyvault/keyvault-certificates/package.json @@ -39,7 +39,7 @@ "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:minify": "uglifyjs -c -m --comments --source-map \"content='./dist/index.js.map'\" -o ./dist/index.min.js ./dist/index.js 2>&1", - "build:samples": "dev-tool samples prep && cd samples && tsc", + "build:samples": "dev-tool samples prep && cd dist-samples && tsc", "build:es6": "tsc -p tsconfig.json", "build:nodebrowser": "rollup -c 2>&1", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", diff --git a/sdk/keyvault/keyvault-keys/package.json b/sdk/keyvault/keyvault-keys/package.json index 97fb00320fa7..a5be7f5c083d 100644 --- a/sdk/keyvault/keyvault-keys/package.json +++ b/sdk/keyvault/keyvault-keys/package.json @@ -38,7 +38,7 @@ "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:minify": "uglifyjs -c -m --comments --source-map \"content='./dist/index.js.map'\" -o ./dist/index.min.js ./dist/index.js 2>&1", - "build:samples": "dev-tool samples prep && cd samples && tsc", + "build:samples": "dev-tool samples prep && cd dist-samples && tsc", "build:es6": "tsc -p tsconfig.json", "build:nodebrowser": "rollup -c 2>&1", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", diff --git a/sdk/keyvault/keyvault-secrets/package.json b/sdk/keyvault/keyvault-secrets/package.json index f346619e780b..6d5cfde6c560 100644 --- a/sdk/keyvault/keyvault-secrets/package.json +++ b/sdk/keyvault/keyvault-secrets/package.json @@ -38,7 +38,7 @@ "scripts": { "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:minify": "uglifyjs -c -m --comments --source-map \"content='./dist/index.js.map'\" -o ./dist/index.min.js ./dist/index.js 2>&1", - "build:samples": "dev-tool samples prep && cd samples && tsc", + "build:samples": "dev-tool samples prep && cd dist-samples && tsc", "build:es6": "tsc -p tsconfig.json", "build:nodebrowser": "rollup -c 2>&1", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", diff --git a/sdk/search/search/package.json b/sdk/search/search/package.json index 358c4a2e3366..a855fd0465ad 100644 --- a/sdk/search/search/package.json +++ b/sdk/search/search/package.json @@ -10,7 +10,7 @@ "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c 2>&1", "build:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c 2>&1", - "build:samples": "dev-tool samples prep && cd samples && tsc -p .", + "build:samples": "dev-tool samples prep && cd dist-samples && tsc -p .", "build:test": "tsc -p . && rollup -c rollup.test.config.js 2>&1", "build": "tsc -p . && rollup -c 2>&1 && api-extractor run --local", "check-format": "prettier --list-different --config ../../.prettierrc.json \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", diff --git a/sdk/servicebus/service-bus/package.json b/sdk/servicebus/service-bus/package.json index 657397a97a45..47f503dc4469 100644 --- a/sdk/servicebus/service-bus/package.json +++ b/sdk/servicebus/service-bus/package.json @@ -39,7 +39,7 @@ "audit": "node ../../../common/scripts/rush-audit.js && rimraf node_modules package-lock.json && npm i --package-lock-only 2>&1 && npm audit", "build:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c 2>&1", "build:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c 2>&1", - "build:samples": "dev-tool samples prep && cd samples && tsc -p .", + "build:samples": "dev-tool samples prep && cd dist-samples && tsc -p .", "build:test:browser": "tsc -p . && cross-env ONLY_BROWSER=true rollup -c rollup.test.config.js 2>&1", "build:test:node": "tsc -p . && cross-env ONLY_NODE=true rollup -c rollup.test.config.js 2>&1", "build:test": "npm run build:test:node && npm run build:test:browser", diff --git a/sdk/storage/storage-blob/package.json b/sdk/storage/storage-blob/package.json index ff6c602fe90f..09256e49fb6b 100644 --- a/sdk/storage/storage-blob/package.json +++ b/sdk/storage/storage-blob/package.json @@ -33,7 +33,7 @@ "build:es6": "tsc -p tsconfig.json", "build:nodebrowser": "rollup -c 2>&1", "build:samples": "npm run clean && npm run build:es6 && cross-env ONLY_NODE=true rollup -c 2>&1 && npm run build:prep-samples", - "build:prep-samples": "dev-tool samples prep && cd samples && tsc", + "build:prep-samples": "dev-tool samples prep && cd dist-samples && tsc", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", "build:types": "downlevel-dts typings/latest typings/3.1", "build": "npm run build:es6 && npm run build:nodebrowser && api-extractor run --local && npm run build:types", diff --git a/sdk/storage/storage-file-share/package.json b/sdk/storage/storage-file-share/package.json index c691e6df8aca..2f35fceef1c7 100644 --- a/sdk/storage/storage-file-share/package.json +++ b/sdk/storage/storage-file-share/package.json @@ -32,7 +32,7 @@ "build:es6": "tsc -p tsconfig.json", "build:nodebrowser": "rollup -c 2>&1", "build:samples": "npm run clean && npm run build:es6 && cross-env ONLY_NODE=true rollup -c 2>&1 && npm run build:prep-samples", - "build:prep-samples": "dev-tool samples prep && cd samples && tsc", + "build:prep-samples": "dev-tool samples prep && cd dist-samples && tsc", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", "build:types": "downlevel-dts typings/latest typings/3.1", "build": "npm run build:es6 && npm run build:nodebrowser && api-extractor run --local && npm run build:types", diff --git a/sdk/storage/storage-queue/package.json b/sdk/storage/storage-queue/package.json index e14ad8f5095e..ff9089c6d612 100644 --- a/sdk/storage/storage-queue/package.json +++ b/sdk/storage/storage-queue/package.json @@ -29,7 +29,7 @@ "build:es6": "tsc -p tsconfig.json", "build:nodebrowser": "rollup -c 2>&1", "build:samples": "npm run clean && npm run build:es6 && cross-env ONLY_NODE=true rollup -c 2>&1 && npm run build:prep-samples", - "build:prep-samples": "dev-tool samples prep && cd samples && tsc", + "build:prep-samples": "dev-tool samples prep && cd dist-samples && tsc", "build:test": "npm run build:es6 && rollup -c rollup.test.config.js 2>&1", "build:types": "downlevel-dts typings/latest typings/3.1", "build": "npm run build:es6 && npm run build:nodebrowser && api-extractor run --local && npm run build:types", From 2832fd234176816b91699d043b28bf9a0f9887d8 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Thu, 18 Jun 2020 10:34:34 -0700 Subject: [PATCH 21/24] WIP --- common/tools/dev-tool/test/framework.spec.ts | 60 ++++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 common/tools/dev-tool/test/framework.spec.ts diff --git a/common/tools/dev-tool/test/framework.spec.ts b/common/tools/dev-tool/test/framework.spec.ts new file mode 100644 index 000000000000..cd8fa7bad625 --- /dev/null +++ b/common/tools/dev-tool/test/framework.spec.ts @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import { assert } from "chai"; + +import { parseOptions } from "../src/framework/parseOptions"; +import { makeCommandInfo, subCommand, leafCommand } from "../src/framework/command"; + +const simpleCommandInfo = makeCommandInfo("simple", "a simple command", { + simpleArg: { + kind: "string", + description: "a simple argument", + allowMultiple: false, + default: "foo" + } +}); + +interface SimpleExpectedOptionsType { + simpleArg: string; + help?: boolean; + args?: string[]; +} + +describe("Command Framework", async () => { + describe("subCommand", async () => { + it("simple dispatcher", async () => { + const dispatcher = subCommand( + { name: "test", description: "a sub-command dispatcher" }, + { + sub: async () => ({ + commandInfo: { name: "sub", description: "a leaf command" }, + default: leafCommand({ name: "sub", description: "a leaf command" }, async () => true) + }), + fail: async () => ({ + commandInfo: { name: "fail", description: "a command that fails" }, + default: leafCommand( + { name: "fail", description: "a command that fails" }, + async () => false + ) + }) + } + ); + + assert.isTrue(await dispatcher("sub")); + assert.isFalse(await dispatcher("fail")); + }); + }); + + describe("leafCommand", async () => {}); + + describe("parseOptions", async () => { + it("simple", async () => { + const opts: SimpleExpectedOptionsType = parseOptions( + ["--simpleArg", "test"], + simpleCommandInfo.options + ); + assert.equal(opts.simpleArg, "test"); + }); + }); +}); From 09fd24969e42dcb83f5ebec4a41980afd7dd3389 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Thu, 18 Jun 2020 20:08:18 -0700 Subject: [PATCH 22/24] [dev-tool] ts-to-js command --- common/config/rush/pnpm-lock.yaml | 7 +- common/tools/dev-tool/package.json | 3 +- .../dev-tool/src/commands/samples/index.ts | 3 +- .../dev-tool/src/commands/samples/tsToJs.ts | 90 +++++++++++++++++++ common/tools/dev-tool/tsconfig.json | 4 +- .../javascript/alternativeDocumentInput.js | 23 +++-- .../samples/javascript/analyzeSentiment.js | 2 +- .../samples/javascript/detectLanguage.js | 2 +- .../samples/javascript/extractKeyPhrases.js | 2 +- .../samples/javascript/recognizeEntities.js | 2 +- 10 files changed, 121 insertions(+), 17 deletions(-) create mode 100644 common/tools/dev-tool/src/commands/samples/tsToJs.ts diff --git a/common/config/rush/pnpm-lock.yaml b/common/config/rush/pnpm-lock.yaml index 81f0152bc412..242192b186b4 100644 --- a/common/config/rush/pnpm-lock.yaml +++ b/common/config/rush/pnpm-lock.yaml @@ -672,6 +672,10 @@ packages: dev: false resolution: integrity: sha512-l+zSbvT8TPRaCxL1l9cwHCb0tSqGAGcjPJFItGGYat5oCTiq1uQQKYg5m7AF1mgnEBzFXGLJ2LRmNjtreRX76Q== + /@types/prettier/2.0.1: + dev: false + resolution: + integrity: sha512-boy4xPNEtiw6N3abRhBi/e7hNvy3Tt8E9ZRAQrwAGzoCGZS/1wjo9KY7JHhnfnEsG5wSjDbymCozUM9a3ea7OQ== /@types/priorityqueuejs/1.0.1: dev: false resolution: @@ -8030,6 +8034,7 @@ packages: '@types/minimist': 1.2.0 '@types/mocha': 7.0.2 '@types/node': 8.10.61 + '@types/prettier': 2.0.1 '@typescript-eslint/eslint-plugin': 2.34.0_2ce5ff4d4c428c35a55f8b98c167bebb '@typescript-eslint/parser': 2.34.0_eslint@6.8.0+typescript@3.9.5 chai: 4.2.0 @@ -8046,7 +8051,7 @@ packages: dev: false name: '@rush-temp/dev-tool' resolution: - integrity: sha512-ZVHMFOYyqmqMwDlbVE98ovEwtq2jnNTo6ny8xL5ol5qwunVMERwOoMAo23grVB72NG2jorFiACDlvcP639LlWg== + integrity: sha512-5TXnGsXxkseKtD1Uu/f8Ua++Np5VFavRs7Hl4IfMbFGBrbWPxXGJJLs/CJLljI71Q9Dqc5YmxoFbtY3agUTrNQ== tarball: 'file:projects/dev-tool.tgz' version: 0.0.0 'file:projects/eslint-plugin-azure-sdk.tgz': diff --git a/common/tools/dev-tool/package.json b/common/tools/dev-tool/package.json index 9fc0562e580b..a019a27f1272 100644 --- a/common/tools/dev-tool/package.json +++ b/common/tools/dev-tool/package.json @@ -36,6 +36,7 @@ "chalk": "~3.0.0", "fs-extra": "^8.1.0", "minimist": "~1.2.5", + "prettier": "^1.16.4", "ts-node": "^8.3.0", "typescript": "~3.9.3" }, @@ -48,13 +49,13 @@ "@types/minimist": "~1.2.0", "@types/mocha": "^7.0.2", "@types/node": "^8.0.0", + "@types/prettier": "~2.0.1", "@typescript-eslint/eslint-plugin": "^2.0.0", "@typescript-eslint/parser": "^2.0.0", "chai": "^4.2.0", "chai-as-promised": "^7.1.1", "eslint": "^6.1.0", "mocha": "^7.1.1", - "prettier": "^1.16.4", "rimraf": "^3.0.0" } } diff --git a/common/tools/dev-tool/src/commands/samples/index.ts b/common/tools/dev-tool/src/commands/samples/index.ts index 5639d589528c..f93af14611cf 100644 --- a/common/tools/dev-tool/src/commands/samples/index.ts +++ b/common/tools/dev-tool/src/commands/samples/index.ts @@ -8,5 +8,6 @@ export const commandInfo = makeCommandInfo("samples", "manage samples in an SDK export default subCommand(commandInfo, { dev: () => import("./dev"), prep: () => import("./prep"), - run: () => import("./run") + run: () => import("./run"), + "ts-to-js": () => import("./tsToJs") }); diff --git a/common/tools/dev-tool/src/commands/samples/tsToJs.ts b/common/tools/dev-tool/src/commands/samples/tsToJs.ts new file mode 100644 index 000000000000..c9f0c9311ea3 --- /dev/null +++ b/common/tools/dev-tool/src/commands/samples/tsToJs.ts @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +import fs from "fs-extra"; +import path from "path"; + +import * as prettier from "prettier"; +import ts from "typescript"; + +import { leafCommand, makeCommandInfo } from "../../framework/command"; + +export const commandInfo = makeCommandInfo( + "ts-to-js", + "convert a TypeScript sample to a JavaScript equivalent using our conventions for samples" +); + +import untypedPrettierOptions from "@azure/eslint-plugin-azure-sdk/prettier.json"; +const prettierOptions = untypedPrettierOptions as prettier.Options; + +const compilerOptions: ts.CompilerOptions = { + target: ts.ScriptTarget.ESNext, + module: ts.ModuleKind.ES2015 +}; + +const NEWLINE_SIGIL = "\n//@@TS-MAGIC-NEWLINE@@\n"; +const NEWLINE_SIGIL_SEARCH = /\n\s*\/\/@@TS-MAGIC-NEWLINE@@\n/; + +/** + * A set of replacements to perform. Structured as an array of doubles: + * + * [, ] + * + * Given as arguments to string.replace. Called in this order. + */ +const REGEX_STACK: Array<[RegExp, string]> = [ + // import * as dotenv ... -> require("dotenv").config() + [ + /import\s+\*\s+as\s+dotenv\s+from\s*"dotenv"\s*;\s*\n\s*dotenv.config\({[^{]*}\)\s*;\s*/, + 'require("dotenv").config();\n\n' + ], // Needs some special handling + // import { ... } from -> const { ... } = require + [/import\s+({[^}]+})\s+from\s*("[^"]+");/gs, "const $1 = require($2);"], + [/import\s+([^\s]+)\s+from\s*("[^"]+");/g, "const $1 = require($2);"], + [/import\s+\*\s+as\s+([^\s]+)\s+from\s*("[^"]+");/g, "const $1 = require($2);"], + [/export async function main/, "async function main"] +]; + +/** + * Handles the formatting of the resulting JS text. + */ +function postTransform(outText: string, _inText: string): string { + // Replace the sigils that we inserted + let text = outText.split(NEWLINE_SIGIL_SEARCH).join("\n\n"); + + // Format first so that we can write matching regexps + // that are humanly comprehensible + text = prettier.format(text, prettierOptions); + + for (const [match, replacement] of REGEX_STACK) { + text = text.replace(match, replacement); + } + + // Format once more for the final output. + return prettier.format(text, prettierOptions); +} + +export default leafCommand(commandInfo, async (options) => { + if (options.args.length !== 2) { + throw new Error("Wrong number of arguments. Got " + options.args.length + " but expected 2."); + } + + const [src, dest] = options.args.map(path.normalize); + + const srcText = (await fs.readFile(src)).toString("utf-8"); + + // TypeScript doesn't preserve newlines in compiled JS output, + // but we can pre-process each blank line by replacing it with a special + // sigil (in a comment, since TS preserves comments + const processedSrcText = srcText.split(/\n\s*\n/).join(NEWLINE_SIGIL); + + const output = ts.transpileModule(processedSrcText, { + compilerOptions, + fileName: src + }); + + await fs.ensureDir(path.dirname(dest)); + await fs.writeFile(dest, postTransform(output.outputText, srcText)); + + return true; +}); diff --git a/common/tools/dev-tool/tsconfig.json b/common/tools/dev-tool/tsconfig.json index b5ecd73523d6..af4008fc8754 100644 --- a/common/tools/dev-tool/tsconfig.json +++ b/common/tools/dev-tool/tsconfig.json @@ -14,7 +14,9 @@ "noImplicitAny": true, "noUnusedLocals": true, "noUnusedParameters": true, - "downlevelIteration": true + "downlevelIteration": true, + + "resolveJsonModule": true }, "exclude": ["node_modules"], "include": ["./src/**/*.ts", "./test/**/*.ts"] diff --git a/sdk/textanalytics/ai-text-analytics/samples/javascript/alternativeDocumentInput.js b/sdk/textanalytics/ai-text-analytics/samples/javascript/alternativeDocumentInput.js index faaede25a520..394dd7694d61 100644 --- a/sdk/textanalytics/ai-text-analytics/samples/javascript/alternativeDocumentInput.js +++ b/sdk/textanalytics/ai-text-analytics/samples/javascript/alternativeDocumentInput.js @@ -17,9 +17,11 @@ const endpoint = process.env["ENDPOINT"] || ""; const apiKey = process.env["TEXT_ANALYTICS_API_KEY"] || ""; /** - * Inputs for the `detectLanguage` method have an `id`, the document `text, and an optional - * `countryHint` (an ISO 3166 two-letter country code). The `id` field is required and must - * be unique for each document in a given request. + * DetectLanguageInput objects allow for specification of country hints on a + * document-by-document basis. + * + * When using DetectLanguageInput, the `id` field is required and must be unique + * for each document in a given request. */ const detectLanguageInputs = [ { id: "0", countryHint: "us", text: "I had the best day of my life." }, @@ -28,13 +30,16 @@ const detectLanguageInputs = [ { id: "3", countryHint: "fr", - text: "L'hôtel n'était pas très confortable. L'éclairage était trop sombre.", - }, + text: "L'hôtel n'était pas très confortable. L'éclairage était trop sombre." + } ]; /** - * Inputs for all other methods are similar to the input for `detectLanguage`, but have an - * optional `language` field (a ISO 639-1 two-letter language code) and no `countryHint` field. + * TextDocumentInput objects are used by all methods except for `detectLanguage`. + * They allow for specification of input language on a document-by-document basis. + * + * Like `DetectLanguageInput`, the `id` field is required and must be unique for + * each document in the request. */ const textDocumentInputs = [ { id: "0", language: "en", text: "I had the best day of my life." }, @@ -43,8 +48,8 @@ const textDocumentInputs = [ { id: "3", language: "fr", - text: "L'hôtel n'était pas très confortable. L'éclairage était trop sombre.", - }, + text: "L'hôtel n'était pas très confortable. L'éclairage était trop sombre." + } ]; async function main() { diff --git a/sdk/textanalytics/ai-text-analytics/samples/javascript/analyzeSentiment.js b/sdk/textanalytics/ai-text-analytics/samples/javascript/analyzeSentiment.js index f2ff3169d317..aca300ecbbe8 100644 --- a/sdk/textanalytics/ai-text-analytics/samples/javascript/analyzeSentiment.js +++ b/sdk/textanalytics/ai-text-analytics/samples/javascript/analyzeSentiment.js @@ -18,7 +18,7 @@ const apiKey = process.env["TEXT_ANALYTICS_API_KEY"] || ""; const documents = [ "I had the best day of my life.", - "This was a waste of my time. The speaker put me to sleep.", + "This was a waste of my time. The speaker put me to sleep." ]; async function main() { diff --git a/sdk/textanalytics/ai-text-analytics/samples/javascript/detectLanguage.js b/sdk/textanalytics/ai-text-analytics/samples/javascript/detectLanguage.js index 3aaa10383c8e..248058b1dd02 100644 --- a/sdk/textanalytics/ai-text-analytics/samples/javascript/detectLanguage.js +++ b/sdk/textanalytics/ai-text-analytics/samples/javascript/detectLanguage.js @@ -20,7 +20,7 @@ const documents = [ "Este es un document escrito en Español.", "这是一个用中文写的文件", "Dies ist ein Dokument in deutsche Sprache.", - "Detta är ett dokument skrivet på engelska.", + "Detta är ett dokument skrivet på engelska." ]; async function main() { diff --git a/sdk/textanalytics/ai-text-analytics/samples/javascript/extractKeyPhrases.js b/sdk/textanalytics/ai-text-analytics/samples/javascript/extractKeyPhrases.js index 8f6ea63fe6dc..af4349271527 100644 --- a/sdk/textanalytics/ai-text-analytics/samples/javascript/extractKeyPhrases.js +++ b/sdk/textanalytics/ai-text-analytics/samples/javascript/extractKeyPhrases.js @@ -18,7 +18,7 @@ const apiKey = process.env["TEXT_ANALYTICS_API_KEY"] || ""; const documents = [ "Redmond is a city in King County, Washington, United States, located 15 miles east of Seattle.", "I need to take my cat to the veterinarian.", - "I will travel to South America in the summer.", + "I will travel to South America in the summer." ]; async function main() { diff --git a/sdk/textanalytics/ai-text-analytics/samples/javascript/recognizeEntities.js b/sdk/textanalytics/ai-text-analytics/samples/javascript/recognizeEntities.js index 432a9c53b3b3..dde4a0dd059b 100644 --- a/sdk/textanalytics/ai-text-analytics/samples/javascript/recognizeEntities.js +++ b/sdk/textanalytics/ai-text-analytics/samples/javascript/recognizeEntities.js @@ -18,7 +18,7 @@ const apiKey = process.env["TEXT_ANALYTICS_API_KEY"] || ""; const documents = [ "Microsoft was founded by Bill Gates and Paul Allen.", "I had a wonderful trip to Seattle last week.", - "I visited the Space Needle 2 times.", + "I visited the Space Needle 2 times." ]; async function main() { From 609b38b3877e5249e5273c85c9e95029a88a96aa Mon Sep 17 00:00:00 2001 From: Will Temple Date: Fri, 10 Jul 2020 12:05:21 -0700 Subject: [PATCH 23/24] [dev-tool] leaf command test --- common/tools/dev-tool/src/util/printer.ts | 53 +++++++++++++++++--- common/tools/dev-tool/test/framework.spec.ts | 35 +++++++++++-- 2 files changed, 77 insertions(+), 11 deletions(-) diff --git a/common/tools/dev-tool/src/util/printer.ts b/common/tools/dev-tool/src/util/printer.ts index 418d7c379b98..a5934c8da567 100644 --- a/common/tools/dev-tool/src/util/printer.ts +++ b/common/tools/dev-tool/src/util/printer.ts @@ -18,6 +18,39 @@ export interface Printer extends ModeMap { (...values: any[]): void; } +export interface PrinterBackend { + error: Fn; + log: Fn; + info: Fn; + warn: Fn; + trace: Fn; +} + +/** + * The object that is used to write output. + */ +let backend: PrinterBackend = { + error: console.error, + log: console.log, + info: console.info, + warn: console.warn, + trace: console.trace +}; + +/** + * Change the backend used to print output. This function will + * replace the methods of the existing backend with those specified + * in `update`, but will leave unspecified methods intact. + * + * @param backend partial specification of a PrinterBackend + */ +export function updateBackend(update: Partial): void { + backend = { + ...backend, + ...update + }; +} + /** * Gets the filename of the calling function */ @@ -26,7 +59,7 @@ function getCaller(): NodeJS.CallSite | undefined { let caller: NodeJS.CallSite | undefined = undefined; try { - const error = new Error() as any as { stack: NodeJS.CallSite[] }; + const error = (new Error() as any) as { stack: NodeJS.CallSite[] }; Error.prepareStackTrace = (_, stack) => stack; @@ -54,16 +87,24 @@ const colors: ModeMap> = { }; const finalLogger: ModeMap = { - info: console.info, - warn: console.warn, - error: console.error, + info(...values) { + backend.info(...values); + }, + warn(...values) { + backend.warn(...values); + }, + error(...values) { + backend.error(...values); + }, debug(...values: string[]) { if (process.env.DEBUG) { const caller = getCaller(); - const fileName = caller?.getFileName()?.split(path.join("azure-sdk-for-js", "common", "tools", "dev-tool")); + const fileName = caller + ?.getFileName() + ?.split(path.join("azure-sdk-for-js", "common", "tools", "dev-tool")); const callerInfo = `(@ ${fileName ? fileName : ""}#${caller?.getFunctionName() ?? ""}:${caller?.getLineNumber()}:${caller?.getColumnNumber()})`; - console.log(values[0], colors.debug(callerInfo), ...values.slice(1)); + backend.error(values[0], colors.debug(callerInfo), ...values.slice(1)); } }, success: console.info diff --git a/common/tools/dev-tool/test/framework.spec.ts b/common/tools/dev-tool/test/framework.spec.ts index cd8fa7bad625..07e5530942c2 100644 --- a/common/tools/dev-tool/test/framework.spec.ts +++ b/common/tools/dev-tool/test/framework.spec.ts @@ -6,6 +6,8 @@ import { assert } from "chai"; import { parseOptions } from "../src/framework/parseOptions"; import { makeCommandInfo, subCommand, leafCommand } from "../src/framework/command"; +import { updateBackend } from "../src/util/printer"; + const simpleCommandInfo = makeCommandInfo("simple", "a simple command", { simpleArg: { kind: "string", @@ -21,8 +23,18 @@ interface SimpleExpectedOptionsType { args?: string[]; } -describe("Command Framework", async () => { - describe("subCommand", async () => { +describe("Command Framework", () => { + before(() => { + // Silence the logger + updateBackend({ + error: () => {}, + warn: () => {}, + info: () => {}, + log: () => {} + }); + }); + + describe("subCommand", () => { it("simple dispatcher", async () => { const dispatcher = subCommand( { name: "test", description: "a sub-command dispatcher" }, @@ -46,10 +58,23 @@ describe("Command Framework", async () => { }); }); - describe("leafCommand", async () => {}); + describe("leafCommand", () => { + it("simple leaf command with argument", async () => { + const command = leafCommand(simpleCommandInfo, (opts: SimpleExpectedOptionsType) => { + if (opts.simpleArg === "yes") { + return Promise.resolve(true); + } else { + return Promise.resolve(false); + } + }); + + assert.isTrue(await command("--simpleArg", "yes")); + assert.isFalse(await command("--simpleArg", "no")); + }); + }); - describe("parseOptions", async () => { - it("simple", async () => { + describe("parseOptions", () => { + it("simple", () => { const opts: SimpleExpectedOptionsType = parseOptions( ["--simpleArg", "test"], simpleCommandInfo.options From f3cd0ff065c9f88a2f4fa5c6dc0b773feb4a6a83 Mon Sep 17 00:00:00 2001 From: Will Temple Date: Fri, 10 Jul 2020 15:38:58 -0700 Subject: [PATCH 24/24] Fixed more deeply nested samples due to shared code --- sdk/keyvault/keyvault-certificates/package.json | 2 +- sdk/keyvault/keyvault-keys/package.json | 2 +- sdk/keyvault/keyvault-secrets/package.json | 2 +- sdk/storage/storage-blob/package.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/sdk/keyvault/keyvault-certificates/package.json b/sdk/keyvault/keyvault-certificates/package.json index 4cd6a269fffc..f9e5aa7ec0d1 100644 --- a/sdk/keyvault/keyvault-certificates/package.json +++ b/sdk/keyvault/keyvault-certificates/package.json @@ -48,7 +48,7 @@ "build": "npm run extract-api && npm run build:es6 && npm run build:nodebrowser", "check-format": "prettier --list-different --config ../../.prettierrc.json --ignore-path ../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist-esm dist-test typings *.tgz *.log samples/typescript/dist", - "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/keyvault-certificates/dist-samples/typescript/src/", "extract-api": "tsc -p . && api-extractor run --local", "format": "prettier --write --config ../../.prettierrc.json --ignore-path ../../.prettierignore \"src/**/*.ts\" \"samples/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", diff --git a/sdk/keyvault/keyvault-keys/package.json b/sdk/keyvault/keyvault-keys/package.json index 21a2d0005fb5..2a823dfdec8e 100644 --- a/sdk/keyvault/keyvault-keys/package.json +++ b/sdk/keyvault/keyvault-keys/package.json @@ -48,7 +48,7 @@ "build": "npm run extract-api && npm run build:es6 && npm run build:nodebrowser", "check-format": "prettier --list-different --config ../../.prettierrc.json --ignore-path ../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist dist-esm dist-test types *.tgz *.log dist-browser statistics.html coverage && rimraf src/**/*.js && rimraf test/**/*.js", - "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/keyvault-keys/dist-samples/typescript/src/", "extract-api": "tsc -p . && api-extractor run --local", "format": "prettier --write --config ../../.prettierrc.json --ignore-path ../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", diff --git a/sdk/keyvault/keyvault-secrets/package.json b/sdk/keyvault/keyvault-secrets/package.json index 752969e74421..7c7edf278dcf 100644 --- a/sdk/keyvault/keyvault-secrets/package.json +++ b/sdk/keyvault/keyvault-secrets/package.json @@ -51,7 +51,7 @@ "build": "npm run extract-api && npm run build:es6 && npm run build:nodebrowser", "check-format": "prettier --list-different --config ../../.prettierrc.json --ignore-path ../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "clean": "rimraf dist dist-esm dist-test types *.tgz *.log dist-browser statistics.html coverage && rimraf src/**/*.js && rimraf test/**/*.js", - "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/keyvault-secrets/dist-samples/typescript/src/", "extract-api": "tsc -p . && api-extractor run --local", "format": "prettier --write --config ../../.prettierrc.json --ignore-path ../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", diff --git a/sdk/storage/storage-blob/package.json b/sdk/storage/storage-blob/package.json index 29227426ba32..e45b2078ac18 100644 --- a/sdk/storage/storage-blob/package.json +++ b/sdk/storage/storage-blob/package.json @@ -42,7 +42,7 @@ "clean": "rimraf dist dist-esm dist-test typings temp dist-browser/*.js* dist-browser/*.zip statistics.html coverage coverage-browser .nyc_output *.tgz *.log test*.xml TEST*.xml", "clean:samples": "rimraf samples/javascript/node_modules samples/typescript/node_modules samples/typescript/dist samples/typescript/package-lock.json samples/javascript/package-lock.json", "extract-api": "tsc -p . && api-extractor run --local", - "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/dist-samples/typescript/src/", + "execute:samples": "npm run build:samples && dev-tool samples run dist-samples/javascript dist-samples/typescript/dist/storage-blob/dist-samples/typescript/src/", "format": "prettier --write --config ../../.prettierrc.json --ignore-path ../../.prettierignore \"src/**/*.ts\" \"test/**/*.ts\" \"*.{js,json}\"", "integration-test:browser": "karma start --single-run", "integration-test:node": "nyc mocha -r esm --require source-map-support/register --reporter ../../../common/tools/mocha-multi-reporter.js --full-trace -t 300000 dist-esm/storage-blob/test/*.spec.js dist-esm/storage-blob/test/node/*.spec.js",