diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 311827173..45207b077 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,14 +1,12 @@ { "name": "TypeScriptToLua", - "image": "mcr.microsoft.com/vscode/devcontainers/javascript-node:14", - "settings": { - "terminal.integrated.shell.linux": "/bin/bash" - }, + "image": "mcr.microsoft.com/vscode/devcontainers/javascript-node:18", "extensions": [ - "ark120202.vscode-typescript-to-lua", + "typescript-to-lua.vscode-typescript-to-lua", "dbaeumer.vscode-eslint", "editorconfig.editorconfig", "esbenp.prettier-vscode" ], - "postCreateCommand": "npm ci" + "postCreateCommand": "npm ci", + "remoteUser": "node" } diff --git a/.eslintignore b/.eslintignore deleted file mode 100644 index 492195f96..000000000 --- a/.eslintignore +++ /dev/null @@ -1,7 +0,0 @@ -/dist -/test/translation/transformation -/test/cli/errors -/test/cli/watch -/test/transpile/directories -/test/transpile/module-resolution/*/node_modules -/test/transpile/outFile diff --git a/.eslintrc.js b/.eslintrc.js deleted file mode 100644 index 52df969ff..000000000 --- a/.eslintrc.js +++ /dev/null @@ -1,227 +0,0 @@ -module.exports = { - extends: ["plugin:jest/recommended", "plugin:jest/style"], - parserOptions: { - sourceType: "module", - project: ["test/tsconfig.json", "src/lualib/tsconfig.json", "benchmark/tsconfig.json"], - }, - env: { es6: true, node: true }, - plugins: ["import"], - rules: { - "arrow-body-style": "error", - curly: ["error", "multi-line"], - eqeqeq: ["error", "always", { null: "ignore" }], - "no-caller": "error", - "no-cond-assign": "error", - "no-debugger": "error", - "no-duplicate-case": "error", - "no-new-wrappers": "error", - "no-restricted-globals": ["error", "parseInt", "parseFloat"], - "no-unused-labels": "error", - "no-var": "error", - "object-shorthand": "error", - "prefer-const": ["error", { destructuring: "all" }], - radix: "error", - "use-isnan": "error", - "object-shorthand": [ - "error", - "always", - { avoidQuotes: true, ignoreConstructors: false, avoidExplicitReturnArrows: true }, - ], - "no-restricted-syntax": ["error", "ForInStatement", "LabeledStatement", "SequenceExpression"], - "spaced-comment": [ - "error", - "always", - { - line: { exceptions: ["-", "+"], markers: ["=", "!", "/"] }, - block: { exceptions: ["-", "+"], markers: ["=", "!", ":", "::"], balanced: true }, - }, - ], - "no-delete-var": ["error"], - "no-label-var": ["error"], - yoda: ["error"], - "prefer-numeric-literals": ["error"], - "prefer-rest-params": ["error"], - "prefer-spread": ["error"], - "no-useless-computed-key": ["error"], - "for-direction": ["error"], - "no-compare-neg-zero": ["error"], - "no-dupe-else-if": ["error"], - "no-empty": ["error", { allowEmptyCatch: true }], - "no-implicit-coercion": ["error", { boolean: true, number: true, string: true }], - "operator-assignment": ["error"], - "no-path-concat": ["error"], - "no-compare-neg-zero": ["error"], - "no-control-regex": ["error"], - "no-unneeded-ternary": ["error", { defaultAssignment: false }], - "one-var": ["error", "never"], - "prefer-exponentiation-operator": ["error"], - "prefer-object-spread": ["error"], - "no-useless-call": ["off"], - "no-useless-catch": ["error"], - "no-useless-concat": ["error"], - "no-useless-escape": ["error"], - "no-useless-return": ["error"], - - "import/no-default-export": "error", - // TODO currently only works for direct imports (useless for now) https://github.com/benmosher/eslint-plugin-import/issues/1729 - // "import/no-deprecated": "error", - - "jest/expect-expect": "off", - "jest/consistent-test-it": ["error", { fn: "test", withinDescribe: "test" }], - "jest/no-expect-resolves": "error", - "jest/no-test-return-statement": "error", - "jest/no-truthy-falsy": "error", - "jest/prefer-spy-on": "error", - "jest/prefer-todo": "error", - "jest/valid-title": "error", - // TODO: - // "jest/lowercase-name": "error", - }, - overrides: [ - { - files: "**/*.ts", - extends: ["plugin:@typescript-eslint/base"], - rules: { - // https://github.com/ark120202/eslint-config/blob/2c24f13fd99af7ccf29e56d5d936b3ab0f237db6/bases/typescript.js - "@typescript-eslint/adjacent-overload-signatures": "error", - "@typescript-eslint/array-type": "error", - "@typescript-eslint/await-thenable": "error", - "@typescript-eslint/ban-types": [ - "error", - { - types: { - Function: null, - CallableFunction: { fixWith: "(...args: any[]) => any" }, - NewableFunction: { fixWith: "new (...args: any[]) => any" }, - }, - }, - ], - camelcase: "off", - "@typescript-eslint/camelcase": ["error", { properties: "never", ignoreDestructuring: true }], - "@typescript-eslint/consistent-type-assertions": [ - "error", - { assertionStyle: "as", objectLiteralTypeAssertions: "never" }, - ], - "@typescript-eslint/consistent-type-definitions": "error", - "@typescript-eslint/explicit-member-accessibility": [ - "error", - { overrides: { constructors: "no-public" } }, - ], - "no-array-constructor": "off", - "@typescript-eslint/no-array-constructor": "error", - "@typescript-eslint/no-empty-interface": "error", - "@typescript-eslint/no-extra-non-null-assertion": "error", - "@typescript-eslint/no-extraneous-class": "error", - "@typescript-eslint/no-floating-promises": "error", - "@typescript-eslint/no-for-in-array": "error", - "@typescript-eslint/no-inferrable-types": "error", - "@typescript-eslint/no-misused-new": "error", - "@typescript-eslint/no-misused-promises": "error", - "@typescript-eslint/no-namespace": "error", - "@typescript-eslint/no-require-imports": "error", - "@typescript-eslint/no-this-alias": "error", - "no-throw-literal": "off", - "@typescript-eslint/no-throw-literal": "error", - "no-constant-condition": "off", - "@typescript-eslint/no-unnecessary-condition": [ - "error", - { ignoreRhs: true, allowConstantLoopConditions: true }, - ], - "@typescript-eslint/no-unnecessary-qualifier": "error", - "@typescript-eslint/no-unnecessary-type-arguments": "error", - "@typescript-eslint/no-unnecessary-type-assertion": "error", - "no-unused-expressions": "off", - "@typescript-eslint/no-unused-expressions": "error", - "no-useless-constructor": "off", - "@typescript-eslint/no-useless-constructor": "error", - "@typescript-eslint/prefer-function-type": "error", - "@typescript-eslint/prefer-includes": "error", - "@typescript-eslint/prefer-optional-chain": "error", - "@typescript-eslint/prefer-namespace-keyword": "error", - "@typescript-eslint/prefer-readonly": "error", - "@typescript-eslint/prefer-string-starts-ends-with": "error", - "@typescript-eslint/promise-function-async": ["error", { checkArrowFunctions: false }], - quotes: "off", - "@typescript-eslint/quotes": ["error", "single", { avoidEscape: true, allowTemplateLiterals: false }], - "@typescript-eslint/require-array-sort-compare": "error", - "@typescript-eslint/require-await": "error", - "@typescript-eslint/restrict-plus-operands": ["error", { checkCompoundAssignments: true }], - "@typescript-eslint/return-await": "error", - "@typescript-eslint/triple-slash-reference": "error", - "@typescript-eslint/unified-signatures": "error", - // end of https://github.com/ark120202/eslint-config/blob/2c24f13fd99af7ccf29e56d5d936b3ab0f237db6/bases/typescript.js - "@typescript-eslint/array-type": ["error", { default: "array-simple" }], - "@typescript-eslint/ban-types": ["error", { types: { null: null } }], - "@typescript-eslint/no-namespace": ["error", { allowDeclarations: true }], - "@typescript-eslint/no-require-imports": "off", - "@typescript-eslint/no-unnecessary-condition": "off", - "@typescript-eslint/prefer-for-of": "error", - "@typescript-eslint/prefer-nullish-coalescing": "error", - "@typescript-eslint/prefer-readonly": "off", - "@typescript-eslint/quotes": ["error", "double", { avoidEscape: true, allowTemplateLiterals: false }], - "@typescript-eslint/require-array-sort-compare": "off", - "@typescript-eslint/camelcase": "off", - - "@typescript-eslint/naming-convention": [ - "error", - { - selector: "default", - format: ["camelCase"], - leadingUnderscore: "allow", - }, - { - selector: "variable", - format: ["camelCase", "UPPER_CASE"], - leadingUnderscore: "allow", - }, - { - selector: "typeLike", - format: ["PascalCase"], - }, - { - selector: "enumMember", - format: ["PascalCase"], - }, - { - selector: "typeParameter", - format: ["PascalCase"], - prefix: ["T"], - filter: { - regex: "K|V", - match: false, - }, - }, - { - selector: "interface", - format: ["PascalCase"], - custom: { - regex: "^I[A-Z]", - match: false, - }, - }, - ], - }, - }, - { - files: "src/lualib/**/*.ts", - rules: { - "no-restricted-syntax": ["error", "LabeledStatement", "SequenceExpression"], - "@typescript-eslint/no-throw-literal": "off", - "@typescript-eslint/prefer-optional-chain": "off", - "@typescript-eslint/naming-convention": "off", - }, - }, - { - files: "language-extensions/index.d.ts", - rules: { - "@typescript-eslint/naming-convention": "off", - }, - }, - { - files: "benchmark/src/memory_benchmarks/**/*.ts", - rules: { - "import/no-default-export": "off", - }, - }, - ], -}; diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..cd3dd9f16 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,2 @@ +# Sponsoring TypeScriptToLua helps us stay motivated and shows your appreciation for our work! +github: [Perryvw] diff --git a/.github/scripts/create_benchmark_check.js b/.github/scripts/create_benchmark_check.js index e1d303e06..4d9ac86cc 100644 --- a/.github/scripts/create_benchmark_check.js +++ b/.github/scripts/create_benchmark_check.js @@ -16,7 +16,13 @@ module.exports = ({ github, context, core }) => { const summary = `[Open visualizer](https://typescripttolua.github.io/benchviz?d=${compressed.toString("base64")})\n` + - `### Lua5.3\n${benchmarkInfoLua.comparison.summary}\n### LuaJIT\n${benchmarkInfoJIT.comparison.summary}`; + `### Lua5.3 +${benchmarkInfoLua.comparison.memory.summary} +${benchmarkInfoLua.comparison.runtime.summary} +--- +### LuaJIT +${benchmarkInfoJIT.comparison.memory.summary} +${benchmarkInfoJIT.comparison.runtime.summary}`; return summary; }; diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 968cd9b87..8353ca6d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,14 +5,19 @@ on: branches: master pull_request: +env: + NODE_VERSION: 20.17.0 + jobs: lint: name: Lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: ${{ env.NODE_VERSION }} - run: npm ci - run: npm run lint env: @@ -26,18 +31,18 @@ jobs: os: [ubuntu-latest, windows-latest] steps: - - uses: actions/checkout@v2 - - name: Use Node.js 12.13.1 - uses: actions/setup-node@v1 + - uses: actions/checkout@v4 + - name: Use Node.js 16.14.0 + uses: actions/setup-node@v4 with: - node-version: 12.13.1 + node-version: ${{ env.NODE_VERSION }} - run: npm ci - run: npm run build - run: npx jest --maxWorkers 2 --coverage env: CI: true - if: matrix.os == 'ubuntu-latest' - uses: codecov/codecov-action@v1 + uses: codecov/codecov-action@v4 benchmark: name: Benchmark @@ -45,28 +50,44 @@ jobs: steps: - name: Lua Install run: sudo apt-get install lua5.3 luajit + - name: Add Brew to Path (Required since Nov 2022) + run: echo "/home/linuxbrew/.linuxbrew/bin:/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH - name: Glow Install run: brew install glow # Checkout master & commit - name: Checkout master - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: ref: master path: master - name: Checkout commit - uses: actions/checkout@v2 + uses: actions/checkout@v4 with: path: commit - - name: Use Node.js 12.13.1 - uses: actions/setup-node@v1 + - name: Use Node.js 16.14.0 + uses: actions/setup-node@v4 with: - node-version: 12.13.1 + node-version: ${{ env.NODE_VERSION }} # NPM - - name: NPM master - run: npm ci && npm run build + # install and build master + - name: npm ci master + run: npm ci + working-directory: master + - name: Use local tstl language extensions + run: npm i lua-types@latest && npm i -D file:. working-directory: master - - name: NPM commit - run: npm ci && npm run build + - name: Build master + run: npm run build + working-directory: master + # install and build commit + - name: npm ci commit + run: npm ci + working-directory: commit + - name: Use local tstl language extensions + run: npm i -D file:. + working-directory: commit + - name: Build commit + run: npm run build working-directory: commit # Benchmark directory setup - name: Ensure benchmark data dir exists @@ -76,14 +97,14 @@ jobs: run: rm -rf ./master/benchmark && cp -rf ./commit/benchmark ./master/benchmark # Run master benchmark first and output to commit benchmark data - name: Build benchmark Lua 5.3 master - run: node ../../commit/dist/tstl.js -p tsconfig.53.json + run: node ../dist/tstl.js -p tsconfig.53.json working-directory: master/benchmark - name: Run benchmark Lua 5.3 master id: benchmark-lua-master run: lua5.3 -- run.lua ../../../commit/benchmark/data/benchmark_master_53.json working-directory: master/benchmark/dist - name: Build benchmark LuaJIT master - run: node ../../commit/dist/tstl.js -p tsconfig.jit.json + run: node ../dist/tstl.js -p tsconfig.jit.json working-directory: master/benchmark - name: Run benchmark LuaJIT master id: benchmark-jit-master @@ -91,14 +112,14 @@ jobs: working-directory: master/benchmark/dist # Run commit benchmark and compare with master - name: Build benchmark Lua 5.3 commit - run: node ../../commit/dist/tstl.js -p tsconfig.53.json + run: node ../dist/tstl.js -p tsconfig.53.json working-directory: commit/benchmark - name: Run benchmark Lua 5.3 commit id: benchmark-lua-commit run: lua5.3 -- run.lua ../data/benchmark_master_vs_commit_53.json ../data/benchmark_master_53.json working-directory: commit/benchmark/dist - name: Build benchmark LuaJIT commit - run: node ../../commit/dist/tstl.js -p tsconfig.jit.json + run: node ../dist/tstl.js -p tsconfig.jit.json working-directory: commit/benchmark - name: Run benchmark LuaJIT commit id: benchmark-jit-commit @@ -106,7 +127,7 @@ jobs: working-directory: commit/benchmark/dist - name: Combine benchmark results id: script-combine-results - uses: actions/github-script@v3 + uses: actions/github-script@v7 with: benchmark-result-path-lua: commit/benchmark/data/benchmark_master_vs_commit_53.json benchmark-result-path-jit: commit/benchmark/data/benchmark_master_vs_commit_jit.json diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e6e9f26fd..f8146b8cb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,10 @@ on: push: tags: "*" +permissions: + id-token: write + contents: read + jobs: release: name: Release @@ -11,13 +15,11 @@ jobs: steps: - uses: actions/checkout@v2 - - name: Use Node.js 12.13.1 + - name: Use Node.js 24.12.0 uses: actions/setup-node@v1 with: - node-version: 12.13.1 + node-version: 24.12.0 registry-url: "https://registry.npmjs.org" - run: npm ci - run: npm run build - run: npm publish - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }} diff --git a/.gitignore b/.gitignore index 5546914da..fc3920330 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ node_modules /dist /coverage +/test/transpile/module-resolution/**/dist +/test/transpile/module-resolution/**/tsconfig.tsbuildinfo yarn.lock .vscode @@ -9,4 +11,7 @@ yarn.lock benchmark/data/* benchmark/dist/* -!benchmark/dist/json.lua + +# v8 cpu profiles +*-.log +*.cpuprofile diff --git a/.prettierignore b/.prettierignore index cbfe6aa04..fb6d91319 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,3 +1,7 @@ /dist /coverage /test/translation/transformation/characterEscapeSequence.ts +/test/translation/transformation/exportStatement.ts +/benchmark/dist +/test/transpile/module-resolution/**/node_modules +/test/transpile/module-resolution/**/dist diff --git a/CHANGELOG.md b/CHANGELOG.md index 12a8fda3b..46b2daaed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,9 +1,366 @@ # Changelog +## 1.37.0 + +- Fixed a bug with where tsconfig paths were not correctly handled +- Improved error message for why >> is not allowed on Lua 5.3 and higher +- Added clearer diganostic for async generators and for-await-of not being supported +- Made @noSelf be respected on interfaces with call signatures +- Fixed promise.finally not being handled according to the javascript spec + +## 1.36.0 + +- **[Breaking]** Upgraded to TypeScript 6.0 + Thanks @RealColdFry for the following fixes: +- Fixed many bugs with try/catch/finally + - Also fixed some bugs with try/catch/finally in promises +- Fixed a bug where collection iterators would not correctly update when the collection updated +- No longer generate dead code after `break` statements +- Fixed a bug where math.atan2 was incorrectly used instead of math.atan for Lua 5.4 +- Fixed some inconsistencies with number constants (Number.MAX_SAFE_INTEGER, Number.MIN_VALUE, etc) +- Fixed a bug with the `>>>` operator for Lua 5.3 +- Fixed incorrect side effects for array destructors +- Fixed broken code when generating requires for files with `.` in the file name (e.g. `foo.tests.ts`), now the periods will be translated to `_` +- Fixed some incorrect handling of synthetic nodes +- Fixed a bug where object spread could lead to incorrect code being generated +- Fixed a bug with Object.defineProperty sharing values accross object instances + +## 1.34.0 + +- Added support for the Lua 5.5 target (it mostly does the same as the 5.4 target for now) +- Fixed a bug where enums in namespaces were not merged correctly +- Fixed an issue with sourcemap traceback that caused lines from anonymous functions not to be translated correctly. +- Fixed a small bug relating to the stacktraces produced by bundled lua + +## 1.33.0 + +- Upgraded TypeScript to 5.9.3 + +## 1.32.0 + +- Fixed a broken `@customName` interation with import statements +- Use `(table.)unpack(expression, from, to)` when using array destructing syntax `const [a,b] = array;` to avoid having to unpack the entire array +- Fixed compiler annotations also considering the next line as part of any possible arguments +- Fixed a bug with unicode classnames not being properly escaped in static initializer blocks +- Fixed a bug where `@noSelf` still was not respected for index signature methods +- Fixed a case where loop variables were incorrectly missing `local` +- Removed dead code that was sometimes generated using `continue` in a loop +- Fixed a bug with tagged template literals when the tag is a function call +- Fixed a bug with class decorators leading to invalid Lua code being generated +- A `-` or `+` prefix now converts expressions to numbers with `Number()` +- Fixed a bug with root level `using` statements not properly disposing objects + +## 1.31.0 + +- Upgraded TypeScript to 5.8.2 +- Changed `currentIndent` from private to protected in the `LuaPrinter` to allow custom printers with alternate indentation +- Added `bit` and `bit32` as reserved Lua keywords to avoid accidental naming clashes. + +## 1.30.0 + +- Allow passing in-memory plugins when using the tstl API, for more flexible integration into scripts +- Changed how stacktraces are handled for `Error` in Lua 5.1 and LuaJIT + +## 1.29.0 + +- Added support for the `Luau` luaTarget. This will use Luau's `continue` statement and ternary conditional expression `if ... then ... else ...` where appropriate. +- Added support for `new Array()` syntax to construct arrays (constructing with a length argument is not allowed). +- Fixed a bug causing arrays to sometimes be indexed with a wrong index. + +## 1.28.0 + +- Upgraded TypeScript to 5.7.2 +- Support `String(x)` transforming it to `tostring(x)`. +- Fixed statements before class super call +- Fixed some bugs with `LuaMultiReturn` used in iterables + +## 1.27.0 + +- Upgraded TypeScript to 5.6.2 +- Added support for `Math.trunc` (see [Math.trunc()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc)) +- Fixed a runtime error when disposing a disposable class +- Fixed the wrong `this` value being passed when using `?.` to call a `super` method +- Fixed a bug causing exported `/** @compileMembersOnly */` enums to break +- Fixed a bug in `Array.from` when calling it with a non-array iterable +- Fixed an incorrect diagnostic being shown for `await using` code +- Fixed a bug causing not all getters/setters to be transpiled + +## 1.26.0 + +- Upgraded TypeScript to 5.5.2 +- Added support for the new proposed ECMAScript Set methods in ESNext: `intersection`, `union`, `difference`, `symmetricDifference`, `isSubsetOf`, `isSupersetOf`, `isDisjointFrom`. For more info see [the TypeScript release notes](https://devblogs.microsoft.com/typescript/announcing-typescript-5-5/#support-for-new-ecmascript-set-methods). +- Fixed a bug causing bundled code to be executed twice in some edge cases. +- Fixed a bug causing errors when using lualib in an environment without coroutines. + +## 1.25.0 + +- Upgraded TypeScript to 5.4.2 +- Added support for new TypeScript 5.4 features `Map.groupBy` and `Object.groupBy` +- Fixed a bug causing files to not be emitted at the correct output path +- Fixed a bug causing `@customname` to not work together with `@noSelf` +- Fixed a bug causing extended tsconfigs to not be correctly read when using watch mode + +## 1.24.0 + +- Optimized promises and async/await to better handle long chains of promises, like for example using await in a loop +- Fixed a bug causing errors when accessing `super` properties + +## 1.23.0 + +- Upgraded TypeScript to 5.3.3 + +## 1.22.0 + +- Added support for `Number.isInteger(n)` +- Added support for `afterEmit` plugin hook that can be used to post-process lua files after (possibly incremental) builds +- Fixed a bug causing `@noSelfInFile` sometimes to be ignored + +## 1.21.0 + +- Added support for `continue` for Lua 5.0, 5.1 and universal targets. +- Added support for the new `/** @customName myCustomName **/` decorator, which allows renaming of variables and identifiers. + - This is useful to get around names that are reserved keywords in TypeScript, but are used in Lua API +- Fixed a bug that caused super calls in static methods to throw an error + +## 1.20.0 + +- Added support for `Number.parseInt` and `Number.parseFloat` (mapped to same implementation as global `parseInt` and `parseFloat`) +- Added implementation for multiple `Number` constants like `Number.EPSILON` +- Added support for `Array.at` +- Fixed a bug when throwing an error object in a Lua environment without `debug` module +- Fixed a bug causing files not to be found when returning an absolute path from a `moduleResolution` plugin + +## 1.19.0 + +- Added support for the new TypeScript 5.2 `using` keyword for explicit resource management. See the [TypeScript release notes](https://devblogs.microsoft.com/typescript/announcing-typescript-5-2/#using-declarations-and-explicit-resource-management) for more information. +- Added support for the newly introduced 'copying array methods' `toReversed`, `toSorted`, `toSpliced` and `with`. These were also introduced in TypeScript 5.2, see [their release notes](https://devblogs.microsoft.com/typescript/announcing-typescript-5-2/#copying-array-methods) for more information. + +## 1.18.0 + +- Upgraded TypeScript to 5.2.2 +- The `noResolvePaths` option now accepts glob paths (for example, 'mydir/hello\*' to not resolve any files in mydir starting with hello). + - This also allows disabling module resolution completely by providing a '\*\*' pattern in your tsconfig.json `noResolvePaths`. + +## 1.17.0 + +- Added the `moduleResolution` plugin, allowing you to provide custom module resolution logic. See [the docs](https://typescripttolua.github.io/docs/api/plugins#moduleresolution) for more info. +- Added `isEmpty` to `LuaTable`, `LuaMap` and `LuaSet` (and their read-only counterparts). This simply to `next(tbl) == nil`, allowing for a simple check to see if a table is empty or not. +- Fixed a bug with synthetic nodes (e.g. created by custom TypeScript transformers) throwing an exception. +- Fixed unnecessary extra unpacking of tables +- Fixed some bugs with new decorators + +## 1.16.0 + +- Upgraded TypeScript to 5.1.3. +- Added support for [TypeScript 5.0 decorators](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-0.html#decorators). + - Old-style decorators will still work as long as you have `experimentalDecorators` configured, otherwise the new standard is used. +- Added support for [class static initialization blocks](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/Static_initialization_blocks). +- Fixed a bug causing the `tstl` object in tsconfig.json not to be properly extended when extending a tsconfig from node_modules. + +## 1.15.0 + +- Using `extends` in tsconfig.json now also correctly merges settings in the `tstl` block (shallow merge). +- Now avoiding assigning default parameter values if the default value is `nil` (`null` or `undefined`). +- Fixed a bug where indexing a `LuaMultiReturn` value with [0] would still return everything. +- Fixed a bug with nested namespaces causing unexpected nil indexing errors. + +## 1.14.0 + +- **[Breaking]** Upgraded TypeScript to 5.0. +- Added support for `Number.toFixed`. +- Added support for spread expressions with `LuaPairsIterable` and `LuaPairsKeysIterable`. +- Fixed a bug breaking module resolution when using a custom file extension. +- Fixed various exceptions that could happen when trying to translate invalid TS. + +## 1.13.0 + +- Fixed alternate file extensions (other than .lua, if configured) breaking module resolution and emitted require statements. +- Added experimental support for `"luaLibImport": "require-minimal"` configuration option. This will output a lualib bundle containing only the lualib functions used by your code. This might not work if you are including external tstl-generated Lua, for example from a npm package. +- Added support for the "exports" field in package.json. +- Fixed some exceptions resulting from invalid language-extensions use. +- Fixed an exception when using compound assignment (like `+=`) with array length. + +## 1.12.0 + +- Reworked how tstl detects and rewrites `require` statements during dependency resolution. This should reduce the amount of false-positive matches of require statements: require statements in string literals or comments should no longer be detected by tstl. This means require statements in string literals or comments can survive the transpiler without causing a 'could not resolve lua sources' error or getting rewritten into nonsense. +- Now using `math.mod` for Lua 5.0 modulo operations. + +## 1.11.0 + +- **[Breaking]** Upgraded TypeScript to 4.9. +- `--tstlVerbose` now prints more resolver output when failing to resolve Lua sources. +- Fixed a bug breaking default exported classes with unicode names +- Relaxed conditions for the always-true warning to false positives. + +## 1.10.0 + +- **[Breaking]** Upgraded TypeScript to 4.8. +- **[Breaking]** Changed how language-extensions are distributed, you should now put `"types": ["@typescript-to-lua/language-extensions"]` in your tsconfig.json (instead of "typescript-to-lua/..."). +- Added support for **Lua 5.0**, thanks for the effort @YoRyan! +- Added support for TypeScript 4.7 [instantiation expressions](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-4-7.html#instantiation-expressions). +- Fixed a bug causing some `require` uses not being recognized my module resolution, leading to missing files in the output. + +## 1.9.0 + +- Added a warning when trying to use a type in a condition that can never be false in Lua, such as numbers or strings. (Only when `strictNullChecks` is enabled.) +- Fixed some missing and misplaced errors when trying to reference LuaTable/LuaMap/LuaSet functions without calling them. +- Fixed a bug in the `get()` type of `ReadOnlyLuaMap`. It is now typed the same as `LuaMap`, i.e. it can return `undefined`. +- Fixed an issue in bundling that could sometimes lead to invalid bundle entry requires. +- Added a warning when using `paths` without specifying `baseUrl`. +- Fixed exception while checking for standard library types. + +## 1.8.0 + +- Added support for the [tsconfig.json paths](https://www.typescriptlang.org/tsconfig#paths) configuration option. +- Fixed spreading lua iterables & iterators translating to incorrect lua. + - You can now write things like `[...pairs(obj)]`. +- Fixed a bug in module resolution resolving the wrong lua files when having the same file names in nested directories. +- Fixed a bug causing temporary variables for nested destructuring in loop variables to be outside the loop, instead of inside. +- Fixed import expressions not actually requiring their import. +- Fixed `super` calls not being source-mapped correctly. + +## 1.7.0 + +- Added support for `LuaMap` and `LuaSet` language extensions that translate to very low level Lua table operations. See [our docs](https://typescripttolua.github.io/docs/advanced/language-extensions/#luamap-and-luaset) for more information. +- Big performance improvements, speeding up TSTL translation by 2-3x. Thanks @GlassBricks! +- Reduced the use of temorary variables. +- Moved tsconfig-schema into main TypeScriptToLua repository. +- Added support for array options in tstl CLI. +- Fixed bug where promise `then` was not correctly forwarding the result value to chained promises. +- Fixed a bug causing false positive errors from jsdoc documentation comments. +- Fixed various calling context bugs. + +## 1.6.0 + +- **[Breaking]** Upgraded TypeScript to 4.7 +- Fixed a bug where EmitOptions plugins were ignored +- Fixed a bug where sometimes function calls (like those to a custom jsx factory) would have a context argument even though `noImplicitSelf` was specified. +- Fixed a bug where sometimes `noImplicitSelf` was ignored because of incorrect file path separators. +- Fixed lualib_bundle files not correctly being included from node_module packages. +- Fixed compound assignment operators (e.g. ??= or ||=) not correctly updating the lhs if used as expression instead of statement. + +## 1.5.0 + +- Added support for `Array.from` and `Array.of` +- Added support for `beforeEmit` hook to plugins that runs after tstl is totally done, but before emitting the result. + - For more info about plugin hooks, see: https://typescripttolua.github.io/docs/api/plugins +- Added support for import expressions (`import("./module").then(m => m.foo());`) +- Added tsconfig setting `lua51AllowTryCatchInAsyncAwait` to disable restrictions on try/catch in combination with async/await in 5.1 (default: false) +- Added tsconfig setting `noImplicitGlobalVariables` to disable tstl making variables global in non-module files. +- Various lualib optimizations +- JSDoc comments from input TS are now also part of output Lua as LDoc comments. + - Can be disabled with `removeComments` tsconfig setting. +- Rewrote how try/catch works in async functions, fixing many bugs. +- Fixed a bug where methods with non-null expressions (i.e. `obj.method!()`) would not pass the correct self parameter, causing runtime errors. +- Fixed a bug where symlinked node_modules (for example when using `npm link`) were not recognized as external dependencies by module resolution. +- Fixed a bug with sourcemap traceback leading to invalid lua +- Improved sourcemap traceback interaction with `loadstring` + +## 1.4.0 + +- Upgraded to TypeScript 4.6 +- Added two event hooks to TSTL plugins: `beforeTransform` and `afterPrint` + - These allow you to run plugin code at specific points in the transpilation process. +- Lualib polyfills are now modules required into locals, instead of global functions + - This change also removes the `"always"` option for the `"lualibImport"` tsconfig key. +- Added support for `Math.sign` +- Switched to `^` instead of `math.pow`, the latter was deprecated in 5.3 +- Added an error when using `null` or `undefined` in tuples, as that is undefined behavior in the Lua spec and causes unexpected behavior +- Added tsconfig setting `extension`, allowing to specify a different output file extension +- Fixed multiple issues with optional chaining and lualib/language extensions +- Fixed issue assigning function with properties to variable declarations +- Fixed multiple issues with preceding statements in class constructors +- Fixed external code module resolution exploding into a stack overflow in complicated module hierarchies +- Fixed a `function.apply(context)` breaking the transpiler if called with only one parameter +- Fixed preceding statements in ternary conditionals (`x ? y : z`) leading to incorrect code + +## 1.3.0 + +- Added `LuaPairsIterable` language extension to mark objects as iterable with Lua's `pairs`. +- Added support for properties on functions. +- Unicode is no longer escaped and used as-is for `"luaTarget": "JIT"`. +- Fixed some bugs related to destructuring in loops and function parameters. +- Fixed incorrect global being generated for some `try` statements. +- Fixed some incorrect behavior for `??` and `? :` when generic types were involved. +- Added missing `...` optimization in cases where casts or parentheses are used. +- Fixed a bug for `Promise` when resolving with another Promise. This also fixes some unexpected behavior with `async` which is built with Promises. +- Fixed `async` functions not aborting after returning from a `catch` block. + +## 1.2.0 + +- Upgraded to TypeScript 4.5.x. +- Improved general output formatting. +- Added support for more complicated (nested) destructuring patterns, also fixing an exception they caused before. +- Fixed incorrect interactions between standard library functionality and optional chaining, e.g. `myArray?.forEach()`. +- Fixed rejected promises sometimes not getting the correct rejection reason. +- Fixed some `delete` behavior that was different in Lua compared to JS. +- Fixed a bug causing exported classes to lose their decorators. +- Fixed plugins checking for ts-node from the wrong location (tsconfig directory), plugins will now check for ts-node relative to the tstl directory. + +Under the hood: + +- We can now transform using preceding statements, allowing all kinds of optimizations and improvements to output Lua. +- Updated various language constructs to use preceding statements instead of inline immediately-invoked functions. + +## 1.1.0 + +- **[Breaking]** We now use TypeScript's JSX transformer instead of maintaining our own. As a result, `React.createElement` now requires a self parameter, so remove `@noSelf`, `this: void` if necessary. +- **[Breaking(-ish)]** Due to limitations in 5.1, try/catch can no longer be used in async or generator functions when targetting Lua 5.1. This was already broken but now tstl will explicitly give an error if you try. +- Added support for the `switch` statement in all versions! (Before they were not supported in 5.1 and universal). +- Added support for `string.prototype.replaceAll` and improved `string.prototype.replace` implementation. +- Added `noResolvePaths` tsconfig option to disable module resolution for environment-provided modules. +- Implemented support for void expressions, i.e `void(0)` or `void(ignoreThisReturnValue())`. +- Upgraded TypeScript to 4.4.4 and made it a peer dependency to hopefully avoid plugin issues due to mismatching TypeScript versions. +- The `$vararg` language extension can be used to access CLI arguments, now also in bundles. +- Fixed a bug regarding `baseUrl` and relative imports. +- Fixed `sourceMapTraceback: true` not working correctly for bundles. +- Fixed an issue regarding hoisting in switch case clauses. +- Added missing function context validation cases for object literals. +- Fixed a problem where awaiting rejected promises in try/catch would give the wrong result. +- Fixed an issue where chained `.then` calls on already-resolved or already-rejected promises would cause some callbacks to not fire while they should. +- Fixed source file paths in source maps being absolute, they are now relative again. + +## 1.0.0 + +- **[Breaking]** `/* @tupleReturn */` has been removed and will no longer have any effect. You will get an error if you try ot use it or if you use declarations that use it. +- Added support for the `Promise` class. +- Added support for `async` and `await` using coroutines. +- Module resolution now also correctly resolves `/init.lua` files for `require("")`. +- Fixed an error not being thrown when trying to call a method in an optional chain that does not exist. (If the method itself is not optional) +- Fixed a bug where parentheses could break the context parameter being resolved for a method. +- Fixed a bug where context parameters in object literal methods were not inferred correctly. +- Fixed a bug with sourceMapTraceback. +- Fixed TS emitting empty JSON files if you use JSON source files. + +## 0.42.0 + +- **[Breaking]** The `/** @tupleReturn */` is now deprecated, and will be removed next release. If you are still using it, please upgrade to the [LuaMultiReturn language extension](https://typescripttolua.github.io/docs/advanced/language-extensions#luamultireturn-type). +- Added support for JSX, see [documentation](https://typescripttolua.github.io/docs/jsx) for more information. +- Added support for the `baseUrl` configuration key for module resolution. + +A large list of bugfixes: + +- Fixed an exception causing tstl to exit when trying to assign to an optional chain. +- Fixed resolved files appearing duplicated in lua bundles. +- Fixed a problem resolving external Lua files in nested directories. +- Fixed `@noResolution` in library packages losing their NoResolution tag, causing attempts to resolve them for package users. +- Fixed a bug in the bundling code causing modules not to be cached if they return nil (which happens if they are not a module) +- Fixed module resolution trying to incorrectly resolve and rewrite things like `myObject.require()` or `my_custom_require()`. +- Fixed lualib bundle not being included in the output if external packages use it, but the client code does not. + +## 0.41.0 + +- Added support for [optional chaining](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html) `a?.b`, `a?.[b]` and `a?.()`. +- Added an error when trying to bundle a library (`"buildmode": "library"`) project. +- Added `--tstlVerbose` CLI flag to help with diagnosing problems. +- Fixed a bug where vararg (`...`) was not correctly optimized. +- Fixed .tsx files not correctly being resolved. +- Fixed a bug where files were emitted to the wrong location if no `outDir` was specified. + ## 0.40.0 - Added support for using external Lua code in your project. This means you can create and install node_modules packages containing Lua code. It also lets you include Lua source files as part of your source files. Used Lua will automatically be added to your output. For more information, see the [External Lua Code](https://typescripttolua.github.io/docs/external-lua-code) page in the docs. -- **[Breaking]** Removed support for deprecated annotations that have been replaced with language extensions: `/** @luaIterator */`, `/** @vararg */`, `/** @luatable */` and `/** forRange */`. If you were still using these, see [the docs](https://typescripttolua.github.io/docs/advanced/compiler-annotations#vararg) for instructions how to upgrade. +- **[Breaking]** Removed support for deprecated annotations that have been replaced with language extensions: `/** @luaIterator */`, `/** @vararg */`, `/** @luatable */` and `/** forRange */`. If you were still using these, see [the docs](https://typescripttolua.github.io/docs/advanced/compiler-annotations) for instructions how to upgrade. - Added support for `array.entries()`. - Added support for `LuaTable.has(key)` and `LuaTable.delete(key)` to the language extensions. See [docs](https://typescripttolua.github.io/docs/advanced/language-extensions#lua-table-types) for more info. - Made language extension types more strict, disallowing `null` and `undefined` in some places where they would cause problems in Lua. diff --git a/benchmark/README.md b/benchmark/README.md index c7b7d39c2..321218c9c 100644 --- a/benchmark/README.md +++ b/benchmark/README.md @@ -2,12 +2,12 @@ These benchmarks are written in typescript and transpiled to lua by using tstl. -### Currently only memory benchmarks are supported +### Memory benchmarks To add a new benchmark add a new file to `memory_benchmarks` and **default** export a function with the following type: `() => void`. To prevent the benchmark from reporting "useful" results of your benchmark function as garbage, simply return the result. -The memory used by the returned result wont count towards the total garbage amount. +The memory used by the returned result won't count towards the total garbage amount. For example (memory_benchmarks/my_benchmark.ts): @@ -25,19 +25,23 @@ export default function myBenchmark() { **Goal** The goal of memory benchmarks is to track how much (memory) `"garbage"` is created by tstl. -For that reason garabage collection is disabled in the benchmarks. +For that reason garbage collection is disabled in the benchmarks. You can force the creation of `"garbage"` by creating a lot of anonymous functions or temporary tables (see [lua-users.org](http://lua-users.org/wiki/OptimisingGarbageCollection) for more information). To avoid crashes in the CI your benchmark should not use more than 500MB of memory. -**Running locally** +### Runtime benchmarks -1. Create a benchmark baseline called "benchmark_baseline.json": +To add a new runtime benchmark (execution time), add a new file to `runtime_benchmarks` and **default** export a function with the following type: `() => void`. + +### Running locally + +1. Create a benchmark baseline called "benchmark_baseline.json": `tstl -p tsconfig.53.json && cd dist && lua -- run.lua benchmark_baseline.json` 2. Make some changes to tstl. -3. Create an updated benchmark and compare with the baseline: +3. Create an updated benchmark and compare with the baseline: `tstl -p tsconfig.53.json && cd dist && lua -- run.lua benchmark_updated.json benchmark_baseline.json` 4. The above command will output comparison data as json to stdout. - If you provide a path as third argument the comparison data will be written to that path instead. + If you provide a path as third argument the comparison data will be written to that path instead. `tstl -p tsconfig.53.json && cd dist && lua -- run.lua benchmark_updated.json benchmark_baseline.json result.md` diff --git a/benchmark/src/benchmark_types.ts b/benchmark/src/benchmark_types.ts index 6ec80efe6..38c2c1fd6 100644 --- a/benchmark/src/benchmark_types.ts +++ b/benchmark/src/benchmark_types.ts @@ -1,10 +1,11 @@ export enum BenchmarkKind { Memory = "memory", + Runtime = "runtime", } export type BenchmarkFunction = () => void; -export type BenchmarkResult = MemoryBenchmarkResult; +export type BenchmarkResult = MemoryBenchmarkResult | RuntimeBenchmarkResult; export enum MemoryBenchmarkCategory { TotalMemory = "totalMemory", @@ -21,6 +22,16 @@ export function isMemoryBenchmarkResult(result: BenchmarkResult): result is Memo return result.kind === BenchmarkKind.Memory; } +export interface RuntimeBenchmarkResult { + kind: BenchmarkKind.Runtime; + time: number; + benchmarkName: string; +} + +export function isRuntimeBenchmarkResult(result: BenchmarkResult): result is RuntimeBenchmarkResult { + return result.kind === BenchmarkKind.Runtime; +} + export interface ComparisonInfo { summary: string; text: string; diff --git a/benchmark/src/benchmark_util.ts b/benchmark/src/benchmark_util.ts new file mode 100644 index 000000000..fedbfdb93 --- /dev/null +++ b/benchmark/src/benchmark_util.ts @@ -0,0 +1,60 @@ +import { BenchmarkResult } from "./benchmark_types"; +import { calculatePercentageChange, toFixed } from "./util"; + +export const makeMarkdownTableRow = (cells: string[]) => `| ${cells.join(" | ")} |\n`; +export const makeBold = (input: string) => `**${input}**`; + +export function compareNumericBenchmarks( + newResults: T[], + oldResults: T[], + unit: string, + extractValue: (result: T) => number, + formatValue: (value: number) => string +): string { + let comparisonTable = makeMarkdownTableRow([ + "name", + `master (${unit})`, + `commit (${unit})`, + `change (${unit})`, + "change (%)", + ]); + comparisonTable += makeMarkdownTableRow(["---", "---", "---", "---", "---"]); + + let oldValueSum = 0; + let newValueSum = 0; + + newResults.forEach(newResult => { + const oldResult = oldResults.find(r => r.benchmarkName === newResult.benchmarkName); + const newValue = extractValue(newResult); + if (oldResult) { + const oldValue = extractValue(oldResult); + const percentageChange = calculatePercentageChange(oldValue, newValue); + const change = newValue - oldValue; + const row = [ + newResult.benchmarkName, + formatValue(oldValue), + formatValue(newValue), + formatValue(change), + toFixed(percentageChange, 2), + ]; + comparisonTable += makeMarkdownTableRow(row); + oldValueSum += oldValue; + newValueSum += newValue; + } else { + // No master found => new benchmark + const row = [newResult.benchmarkName, formatValue(newValue), "/", "/", "/"]; + comparisonTable += makeMarkdownTableRow(row); + } + }); + + const sumPercentageChange = calculatePercentageChange(oldValueSum, newValueSum); + comparisonTable += makeMarkdownTableRow([ + makeBold("sum"), + makeBold(formatValue(oldValueSum)), + makeBold(formatValue(newValueSum)), + makeBold(formatValue(newValueSum - oldValueSum)), + makeBold(toFixed(sumPercentageChange, 2)), + ]); + + return comparisonTable; +} diff --git a/benchmark/src/memory_benchmark.ts b/benchmark/src/memory_benchmark.ts index 51f8031be..8434f3881 100644 --- a/benchmark/src/memory_benchmark.ts +++ b/benchmark/src/memory_benchmark.ts @@ -1,5 +1,6 @@ import { BenchmarkKind, MemoryBenchmarkResult, ComparisonInfo, MemoryBenchmarkCategory } from "./benchmark_types"; -import { toFixed, json, calculatePercentageChange } from "./util"; +import { compareNumericBenchmarks } from "./benchmark_util"; +import { toFixed, json } from "./util"; export function runMemoryBenchmark(benchmarkFunction: () => void): MemoryBenchmarkResult { const result: MemoryBenchmarkResult = { @@ -42,8 +43,6 @@ export function runMemoryBenchmark(benchmarkFunction: () => void): MemoryBenchma } const formatMemory = (memInKB: number) => toFixed(memInKB / 1024, 3); -const makeMarkdownTableRow = (cells: string[]) => `| ${cells.join(" | ")} |\n`; -const makeBold = (input: string) => `**${input}**`; export function compareMemoryBenchmarks( oldResults: MemoryBenchmarkResult[], @@ -53,10 +52,10 @@ export function compareMemoryBenchmarks( const categories = [MemoryBenchmarkCategory.TotalMemory, MemoryBenchmarkCategory.Garbage]; const summary = categories - .map(category => `${makeBold(category)}\n${compareCategory(newResults, oldResults, category)}`) + .map(category => `### ${category}\n\n${compareCategory(newResults, oldResults, category)}`) .join("\n"); - const text = `**master:**\n\`\`\`json\n${json.encode(oldResults)}\n\`\`\`\n**commit:**\n\`\`\`json\n${json.encode( + const text = `### master:\n\`\`\`json\n${json.encode(oldResults)}\n\`\`\`\n### commit:\n\`\`\`json\n${json.encode( newResults )}\n\`\`\``; @@ -68,47 +67,5 @@ function compareCategory( oldResults: MemoryBenchmarkResult[], category: MemoryBenchmarkCategory ): string { - let comparisonTable = makeMarkdownTableRow(["name", "master (mb)", "commit (mb)", "change (mb)", "change (%)"]); - comparisonTable += makeMarkdownTableRow(["-", "-", "-", "-", "-"]); - - let oldValueSum = 0; - let newValueSum = 0; - - newResults.forEach(newResult => { - const oldResult = oldResults.find(r => r.benchmarkName === newResult.benchmarkName); - if (oldResult) { - const oldValue = oldResult.categories[category]; - const newValue = newResult.categories[category]; - const percentageChange = calculatePercentageChange( - newResult.categories[category], - oldResult.categories[category] - ); - const change = newResult.categories[category] - oldResult.categories[category]; - const row = [ - newResult.benchmarkName, - formatMemory(oldValue), - formatMemory(newValue), - formatMemory(change), - toFixed(percentageChange, 2), - ]; - comparisonTable += makeMarkdownTableRow(row); - oldValueSum += oldValue; - newValueSum += newValue; - } else { - // No master found => new benchmark - const row = [newResult.benchmarkName, formatMemory(newResult.categories[category]), "/", "/", "/"]; - comparisonTable += makeMarkdownTableRow(row); - } - }); - - const sumPercentageChange = calculatePercentageChange(oldValueSum, newValueSum); - comparisonTable += makeMarkdownTableRow([ - makeBold("sum"), - makeBold(formatMemory(oldValueSum)), - makeBold(formatMemory(newValueSum)), - makeBold(formatMemory(newValueSum - oldValueSum)), - makeBold(toFixed(sumPercentageChange, 2)), - ]); - - return comparisonTable; + return compareNumericBenchmarks(newResults, oldResults, "mb", result => result.categories[category], formatMemory); } diff --git a/benchmark/src/run.ts b/benchmark/src/run.ts index b880537e5..8b02cc7da 100644 --- a/benchmark/src/run.ts +++ b/benchmark/src/run.ts @@ -1,5 +1,14 @@ import { runMemoryBenchmark, compareMemoryBenchmarks } from "./memory_benchmark"; -import { isMemoryBenchmarkResult, BenchmarkResult, MemoryBenchmarkResult, ComparisonInfo } from "./benchmark_types"; +import { + isMemoryBenchmarkResult, + BenchmarkResult, + MemoryBenchmarkResult, + ComparisonInfo, + RuntimeBenchmarkResult, + isRuntimeBenchmarkResult, + BenchmarkKind, +} from "./benchmark_types"; +import { runRuntimeBenchmark, compareRuntimeBenchmarks } from "./runtime_benchmark"; import { json, loadBenchmarksFromDirectory, readFile } from "./util"; // CLI arguments @@ -10,15 +19,16 @@ declare const arg: [string | undefined, string | undefined, string | undefined]; function benchmark(): void { // Memory tests - let memoryBenchmarkNewResults: MemoryBenchmarkResult[] = []; const memoryBenchmarks = loadBenchmarksFromDirectory("memory_benchmarks"); + const memoryBenchmarkNewResults: MemoryBenchmarkResult[] = memoryBenchmarks.map(runMemoryBenchmark); - memoryBenchmarkNewResults = memoryBenchmarks.map(runMemoryBenchmark); + // Run time tests - // run future benchmarks types here + const runtimeBenchmarks = loadBenchmarksFromDirectory("runtime_benchmarks"); + const runtimeBenchmarkNewResults: RuntimeBenchmarkResult[] = runtimeBenchmarks.map(runRuntimeBenchmark); - const newBenchmarkResults = [...memoryBenchmarkNewResults]; + const newBenchmarkResults = [...memoryBenchmarkNewResults, ...runtimeBenchmarkNewResults]; // Try to read the baseline benchmark result let oldBenchmarkResults: BenchmarkResult[] = []; @@ -28,17 +38,48 @@ function benchmark(): void { } // Output comparison info + oldBenchmarkResults.sort(sortByName); + newBenchmarkResults.sort(sortByName); outputBenchmarkData(oldBenchmarkResults, newBenchmarkResults); } benchmark(); -function compareBenchmarks(oldResults: BenchmarkResult[], newResults: BenchmarkResult[]): ComparisonInfo { +function sortByName({ benchmarkName: a }: BenchmarkResult, { benchmarkName: b }: BenchmarkResult): number { + if (a < b) return -1; + if (a > b) return 1; + return 0; +} + +function compareBenchmarks( + oldResults: BenchmarkResult[], + newResults: BenchmarkResult[] +): Record { const oldResultsMemory = oldResults.filter(isMemoryBenchmarkResult); const newResultsMemory = newResults.filter(isMemoryBenchmarkResult); const memoryComparisonInfo = compareMemoryBenchmarks(oldResultsMemory, newResultsMemory); - return { summary: memoryComparisonInfo.summary, text: memoryComparisonInfo.text }; + const oldResultsRuntime = oldResults.filter(isRuntimeBenchmarkResult); + const newResultsRuntime = newResults.filter(isRuntimeBenchmarkResult); + + const runtimeComparisonInfo = compareRuntimeBenchmarks(oldResultsRuntime, newResultsRuntime); + + return { + [BenchmarkKind.Memory]: memoryComparisonInfo, + [BenchmarkKind.Runtime]: runtimeComparisonInfo, + }; +} + +function formatComparisonMarkdownFile(comparisonInfo: Record): string { + let result = ""; + const benchmarkKinds = [BenchmarkKind.Memory, BenchmarkKind.Runtime]; + for (const kind of benchmarkKinds) { + result += comparisonInfo[kind].summary + "\n"; + } + for (const kind of benchmarkKinds) { + result += comparisonInfo[kind].text + "\n"; + } + return result; } function outputBenchmarkData(oldResults: BenchmarkResult[], newResults: BenchmarkResult[]): void { @@ -59,6 +100,6 @@ function outputBenchmarkData(oldResults: BenchmarkResult[], newResults: Benchmar // Compare results const comparisonInfo = compareBenchmarks(oldResults, newResults); const markdownDataFile = io.open(arg[2], "w+")[0]!; - markdownDataFile.write(comparisonInfo.summary + comparisonInfo.text); + markdownDataFile.write(formatComparisonMarkdownFile(comparisonInfo)); } } diff --git a/benchmark/src/runtime_benchmark.ts b/benchmark/src/runtime_benchmark.ts new file mode 100644 index 000000000..c56cd1b0d --- /dev/null +++ b/benchmark/src/runtime_benchmark.ts @@ -0,0 +1,43 @@ +import { BenchmarkKind, ComparisonInfo, RuntimeBenchmarkResult } from "./benchmark_types"; +import { compareNumericBenchmarks } from "./benchmark_util"; +import { json, toFixed } from "./util"; + +export function runRuntimeBenchmark(benchmarkFunction: () => void): RuntimeBenchmarkResult { + const result: RuntimeBenchmarkResult = { + kind: BenchmarkKind.Runtime, + benchmarkName: "NO_NAME", + time: 0, + }; + + // normalize times a bit + collectgarbage("collect"); + + const startTime = os.clock(); + benchmarkFunction(); + const time = os.clock() - startTime; + + result.benchmarkName = debug.getinfo(benchmarkFunction).short_src; + result.time = time; + return result; +} + +export function compareRuntimeBenchmarks( + oldResults: RuntimeBenchmarkResult[], + newResults: RuntimeBenchmarkResult[] +): ComparisonInfo { + const summary = + "### runtime\n\n" + + compareNumericBenchmarks( + newResults, + oldResults, + "s", + result => result.time, + time => toFixed(time, 4) + ); + + const text = `### master\n\`\`\`json\n${json.encode(oldResults)}\n\`\`\`\n### commit\n\`\`\`json\n${json.encode( + newResults + )}\n\`\`\``; + + return { summary, text }; +} diff --git a/benchmark/src/runtime_benchmarks/array_concat_array.ts b/benchmark/src/runtime_benchmarks/array_concat_array.ts new file mode 100644 index 000000000..9c9a7e83d --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_concat_array.ts @@ -0,0 +1,9 @@ +export default function arrayConcat(): number[] { + const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + const arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + const n = 50000; + for (let i = 0; i < n; i++) { + arr1.concat(arr2); + } + return arr1; +} diff --git a/benchmark/src/runtime_benchmarks/array_concat_spread.ts b/benchmark/src/runtime_benchmarks/array_concat_spread.ts new file mode 100644 index 000000000..a04eff958 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_concat_spread.ts @@ -0,0 +1,9 @@ +export default function arrayConcat(): number[] { + const arr1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + const arr2 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 0]; + const n = 50000; + for (let i = 0; i < n; i++) { + arr1.concat(...arr2); + } + return arr1; +} diff --git a/benchmark/src/runtime_benchmarks/array_every.ts b/benchmark/src/runtime_benchmarks/array_every.ts new file mode 100644 index 000000000..f5dbfc4e4 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_every.ts @@ -0,0 +1,7 @@ +export default function arrayEvery() { + const n = 200000; + const array = [1, 2, 3, 4, 5, 6, 7, 8, 7, 10]; + for (let i = 0; i < n; i++) { + array.every((item, index) => item > index); + } +} diff --git a/benchmark/src/runtime_benchmarks/array_filter.ts b/benchmark/src/runtime_benchmarks/array_filter.ts new file mode 100644 index 000000000..127d2476d --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_filter.ts @@ -0,0 +1,7 @@ +export default function arrayFilter() { + const n = 100000; + const array = [1, 2, 3, 4, 3, 6, 7, 8, 7, 10]; + for (let i = 0; i < n; i++) { + array.filter((item, index) => item > index); + } +} diff --git a/benchmark/src/runtime_benchmarks/array_find.ts b/benchmark/src/runtime_benchmarks/array_find.ts new file mode 100644 index 000000000..8a84ff4e5 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_find.ts @@ -0,0 +1,9 @@ +export default function arrayFind() { + const n = 50000; + const array = [1, 2, 3, 4, 3, 6, 7, 8, 9, 10]; + for (let i = 0; i < n; i++) { + for (let j = 0; j < 10; j++) { + array.find(value => value === j); + } + } +} diff --git a/benchmark/src/runtime_benchmarks/array_findIndex.ts b/benchmark/src/runtime_benchmarks/array_findIndex.ts new file mode 100644 index 000000000..c058a99df --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_findIndex.ts @@ -0,0 +1,9 @@ +export default function arrayFindIndex() { + const n = 50000; + const array = [1, 2, 3, 4, 3, 6, 7, 8, 9, 10]; + for (let i = 0; i < n; i++) { + for (let j = 0; j < 10; j++) { + array.findIndex(value => value === j); + } + } +} diff --git a/benchmark/src/runtime_benchmarks/array_flat.ts b/benchmark/src/runtime_benchmarks/array_flat.ts new file mode 100644 index 000000000..36ace2d95 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_flat.ts @@ -0,0 +1,7 @@ +export default function arrayFlat() { + const n = 50000; + const array = [1, 2, [3, [4, 5], 6], 7, [8, 9], 10]; + for (let i = 0; i < n; i++) { + array.flat(2); + } +} diff --git a/benchmark/src/runtime_benchmarks/array_flatMap.ts b/benchmark/src/runtime_benchmarks/array_flatMap.ts new file mode 100644 index 000000000..26c6490e5 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_flatMap.ts @@ -0,0 +1,13 @@ +export default function arrayFlatMap() { + const n = 50000; + const array = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + for (let i = 0; i < n; i++) { + array.flatMap((el, index) => { + if (index < 5) { + return [el, el + 1]; + } else { + return el + 2; + } + }); + } +} diff --git a/benchmark/src/runtime_benchmarks/array_foreach.ts b/benchmark/src/runtime_benchmarks/array_foreach.ts new file mode 100644 index 000000000..8e14c8119 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_foreach.ts @@ -0,0 +1,10 @@ +export default function arrayForeach() { + const n = 200000; + const array = [1, 2, 3, 4, 3, 6, 7, 8, 9, 10]; + for (let i = 0; i < n; i++) { + array.forEach(value => { + let foo = value * 2; + foo = foo; + }); + } +} diff --git a/benchmark/src/runtime_benchmarks/array_includes.ts b/benchmark/src/runtime_benchmarks/array_includes.ts new file mode 100644 index 000000000..828c167a3 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_includes.ts @@ -0,0 +1,9 @@ +export default function arrayIncludes() { + const n = 50000; + const array = [1, 2, 3, 4, 3, 6, 7, 8, 9, 10]; + for (let i = 0; i < n; i++) { + for (let j = 0; j < 10; j++) { + array.includes(j); + } + } +} diff --git a/benchmark/src/runtime_benchmarks/array_indexOf.ts b/benchmark/src/runtime_benchmarks/array_indexOf.ts new file mode 100644 index 000000000..333c67988 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_indexOf.ts @@ -0,0 +1,9 @@ +export default function arrayIndexOf() { + const n = 50000; + const array = [1, 2, 3, 4, 3, 6, 7, 8, 9, 10]; + for (let i = 0; i < n; i++) { + for (let j = 0; j < 10; j++) { + array.indexOf(j); + } + } +} diff --git a/benchmark/src/runtime_benchmarks/array_join.ts b/benchmark/src/runtime_benchmarks/array_join.ts new file mode 100644 index 000000000..5a9f5bae4 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_join.ts @@ -0,0 +1,8 @@ +const array = [1, 2, "3", 4, 3, "6", { foo: 3 }, 8, 9, 10]; + +export default function arrayJoin() { + const n = 3000; + for (let i = 0; i < n; i++) { + array.join("|"); + } +} diff --git a/benchmark/src/runtime_benchmarks/array_map.ts b/benchmark/src/runtime_benchmarks/array_map.ts new file mode 100644 index 000000000..79d00d66a --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_map.ts @@ -0,0 +1,7 @@ +export default function arrayMap() { + const n = 100000; + const array = [1, 2, 3, 4, 3, 6, 7, 8, 9, 10]; + for (let i = 0; i < n; i++) { + array.map(value => value * 2); + } +} diff --git a/benchmark/src/runtime_benchmarks/array_push_array.ts b/benchmark/src/runtime_benchmarks/array_push_array.ts new file mode 100644 index 000000000..fcc239a31 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_push_array.ts @@ -0,0 +1,9 @@ +export default function arrayPush(): number[] { + const n = 200000; + const numberList: number[] = []; + const numbers = [1, 2, 3]; + for (let i = 0; i < n; i++) { + numberList.push(...numbers); + } + return numberList; +} diff --git a/benchmark/src/runtime_benchmarks/array_push_multiple.ts b/benchmark/src/runtime_benchmarks/array_push_multiple.ts new file mode 100644 index 000000000..dbef5460c --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_push_multiple.ts @@ -0,0 +1,8 @@ +export default function arrayPush(): number[] { + const n = 200000; + const numberList: number[] = []; + for (let i = 0; i < n; i++) { + numberList.push(i * i, i + 1); + } + return numberList; +} diff --git a/benchmark/src/runtime_benchmarks/array_push_single.ts b/benchmark/src/runtime_benchmarks/array_push_single.ts new file mode 100644 index 000000000..f09bc21ec --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_push_single.ts @@ -0,0 +1,8 @@ +export default function arrayPush(): number[] { + const n = 500000; + const numberList: number[] = []; + for (let i = 0; i < n; i++) { + numberList.push(i * i); + } + return numberList; +} diff --git a/benchmark/src/runtime_benchmarks/array_push_vararg.ts b/benchmark/src/runtime_benchmarks/array_push_vararg.ts new file mode 100644 index 000000000..e780461ee --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_push_vararg.ts @@ -0,0 +1,12 @@ +export default function arrayPush(): number[] { + const n = 200000; + const numberList: number[] = []; + function pushArgs(...args: number[]): void { + numberList.push(...args); + } + + for (let i = 0; i < n; i++) { + pushArgs(3, 4); + } + return numberList; +} diff --git a/benchmark/src/runtime_benchmarks/array_reduce.ts b/benchmark/src/runtime_benchmarks/array_reduce.ts new file mode 100644 index 000000000..2d615e80f --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_reduce.ts @@ -0,0 +1,7 @@ +export default function arrayReduce() { + const n = 200000; + const array = [1, 2, 3, 4, 5, 6, 7, 8, 7, 10]; + for (let i = 0; i < n; i++) { + array.reduce((prev, cur, i) => prev + cur + i, 1); + } +} diff --git a/benchmark/src/runtime_benchmarks/array_reduceRight.ts b/benchmark/src/runtime_benchmarks/array_reduceRight.ts new file mode 100644 index 000000000..9dd97d3d2 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_reduceRight.ts @@ -0,0 +1,7 @@ +export default function arrayReduce() { + const n = 200000; + const array = [1, 2, 3, 4, 5, 6, 7, 8, 7, 10]; + for (let i = 0; i < n; i++) { + array.reduceRight((prev, cur, i) => prev + cur + i, 1); + } +} diff --git a/benchmark/src/runtime_benchmarks/array_reverse.ts b/benchmark/src/runtime_benchmarks/array_reverse.ts new file mode 100644 index 000000000..539d26138 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_reverse.ts @@ -0,0 +1,7 @@ +export default function arrayReverse(): void { + const n = 500000; + const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + for (let i = 0; i < n; i++) { + numbers.reverse(); + } +} diff --git a/benchmark/src/runtime_benchmarks/array_slice.ts b/benchmark/src/runtime_benchmarks/array_slice.ts new file mode 100644 index 000000000..5419cb0d9 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_slice.ts @@ -0,0 +1,9 @@ +export default function arraySlice() { + const n = 50000; + const array = [1, 2, 3, 4, 3, 6, 7, 8, 9, 10]; + for (let i = 0; i < n; i++) { + for (let j = 0; j < 6; j++) { + array.slice(j, -j); + } + } +} diff --git a/benchmark/src/runtime_benchmarks/array_some.ts b/benchmark/src/runtime_benchmarks/array_some.ts new file mode 100644 index 000000000..8add42a56 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_some.ts @@ -0,0 +1,7 @@ +export default function arraySome() { + const n = 200000; + const array = [1, 2, 3, 4, 5, 6, 7, 8, 7, 10]; + for (let i = 0; i < n; i++) { + array.some((item, index) => item < index); + } +} diff --git a/benchmark/src/runtime_benchmarks/array_splice.ts b/benchmark/src/runtime_benchmarks/array_splice.ts new file mode 100644 index 000000000..4620bad86 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_splice.ts @@ -0,0 +1,9 @@ +export default function arraySplice() { + const n = 20000; + const array = [1, 2, 3, 4, 3, 6, 7, 8, 9, 10]; + for (let i = 0; i < n; i++) { + for (let j = 0; j < 10; j++) { + array.splice(j, 2, 1, 2); + } + } +} diff --git a/benchmark/src/runtime_benchmarks/array_unshift.ts b/benchmark/src/runtime_benchmarks/array_unshift.ts new file mode 100644 index 000000000..b0f59a0b2 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/array_unshift.ts @@ -0,0 +1,9 @@ +export default function arrayUnshift(): number[] { + const n = 2000; + const numberList: number[] = []; + const numbers = [1, 2, 3]; + for (let i = 0; i < n; i++) { + numberList.unshift(...numbers); + } + return numberList; +} diff --git a/benchmark/src/runtime_benchmarks/string_concat.ts b/benchmark/src/runtime_benchmarks/string_concat.ts new file mode 100644 index 000000000..47d520e9a --- /dev/null +++ b/benchmark/src/runtime_benchmarks/string_concat.ts @@ -0,0 +1,8 @@ +export default function stringReplace() { + const str = "one"; + const n = 50000; + for (let i = 0; i < n; i++) { + str.concat("two", "three", "four", "five"); + } + return str; +} diff --git a/benchmark/src/runtime_benchmarks/string_replace.ts b/benchmark/src/runtime_benchmarks/string_replace.ts new file mode 100644 index 000000000..e7a00fc35 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/string_replace.ts @@ -0,0 +1,8 @@ +export default function stringReplace() { + const str = "Hello, World!"; + const n = 50000; + for (let i = 0; i < n; i++) { + str.replace("World", "Universe"); + } + return str; +} diff --git a/benchmark/src/runtime_benchmarks/string_replaceAll.ts b/benchmark/src/runtime_benchmarks/string_replaceAll.ts new file mode 100644 index 000000000..268bb3642 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/string_replaceAll.ts @@ -0,0 +1,8 @@ +export default function stringReplaceAll() { + const str = "hello, hello world!"; + const n = 50000; + for (let i = 0; i < n; i++) { + str.replaceAll("Hello", "Goodbye"); + } + return str; +} diff --git a/benchmark/src/runtime_benchmarks/string_split.ts b/benchmark/src/runtime_benchmarks/string_split.ts new file mode 100644 index 000000000..22f6c9bb1 --- /dev/null +++ b/benchmark/src/runtime_benchmarks/string_split.ts @@ -0,0 +1,8 @@ +export default function stringReplaceAll() { + const str = "This is a string"; + const n = 50000; + for (let i = 0; i < n; i++) { + str.split(" "); + } + return str; +} diff --git a/benchmark/src/util.ts b/benchmark/src/util.ts index bd1c0a10f..41f2159f5 100644 --- a/benchmark/src/util.ts +++ b/benchmark/src/util.ts @@ -30,16 +30,21 @@ export function readFile(path: string): string { } export function readAll(file: LuaFile): string { - const content = file.read(_VERSION === "Lua 5.3" ? "a" : ("*a" as any)) as [string | undefined]; + const content = file.read(_VERSION === "Lua 5.3" ? "a" : ("*a" as any)); - if (content[0]) { - return content[0]; + if (content) { + return content as string; } throw Error(`Can't readAll for file ${file}`); } export function readDir(dir: string): string[] { - const findHandle = io.popen(isWindows ? `dir /A-D /B ${dir}` : `find '${dir}' -maxdepth 1 -type f`); + const [findHandle] = io.popen(isWindows ? `dir /A-D /B ${dir}` : `find '${dir}' -maxdepth 1 -type f`); + + if (!findHandle) { + throw new Error(`Failed to read dir ${dir}`); + } + const findResult = readAll(findHandle); if (!findHandle.close()) { diff --git a/benchmark/tsconfig.53.json b/benchmark/tsconfig.53.json index 0a7096234..6dbbe3bca 100644 --- a/benchmark/tsconfig.53.json +++ b/benchmark/tsconfig.53.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "types": ["lua-types/5.3"] + "types": ["lua-types/5.3", "@typescript-to-lua/language-extensions"] }, "tstl": { "luaTarget": "5.3" diff --git a/benchmark/tsconfig.jit.json b/benchmark/tsconfig.jit.json index 1fe18fa2c..0c6912d31 100644 --- a/benchmark/tsconfig.jit.json +++ b/benchmark/tsconfig.jit.json @@ -1,7 +1,7 @@ { "extends": "./tsconfig.json", "compilerOptions": { - "types": ["lua-types/jit"] + "types": ["lua-types/jit", "@typescript-to-lua/language-extensions"] }, "tstl": { "luaTarget": "JIT" diff --git a/benchmark/tsconfig.json b/benchmark/tsconfig.json index aefc13ae9..5422f1770 100644 --- a/benchmark/tsconfig.json +++ b/benchmark/tsconfig.json @@ -3,8 +3,9 @@ "target": "esnext", "lib": ["esnext"], // Dev types are JIT - "types": ["lua-types/jit"], - "moduleResolution": "node", + "types": ["lua-types/jit", "@typescript-to-lua/language-extensions"], + "module": "nodenext", + "moduleResolution": "nodenext", "outDir": "dist", "rootDir": "src", "strict": true, diff --git a/build-lualib.js b/build-lualib.js deleted file mode 100644 index 41ece7e32..000000000 --- a/build-lualib.js +++ /dev/null @@ -1,17 +0,0 @@ -require("ts-node/register/transpile-only"); -const fs = require("fs"); -const path = require("path"); -const ts = require("typescript"); -const tstl = require("./src"); -const { loadLuaLibFeatures } = require("./src/LuaLib"); - -const configFileName = path.resolve(__dirname, "src/lualib/tsconfig.json"); -const { diagnostics } = tstl.transpileProject(configFileName); -diagnostics.forEach(tstl.createDiagnosticReporter(true)); - -const bundlePath = path.join(__dirname, "dist/lualib/lualib_bundle.lua"); -if (fs.existsSync(bundlePath)) { - fs.unlinkSync(bundlePath); -} - -fs.writeFileSync(bundlePath, loadLuaLibFeatures(Object.values(tstl.LuaLibFeature), ts.sys)); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 000000000..0ea4d78ca --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,230 @@ +// @ts-check + +import tseslint from "typescript-eslint"; +import eslintPluginJest from "eslint-plugin-jest"; + +export default tseslint.config( + // Enable linting on TypeScript file extensions. + { + files: ["**/*.ts", "**/*.tsx", "**/*.mts", "**/*.cts"], + }, + + // Base ESLint rules + { + rules: { + "arrow-body-style": "error", + curly: ["error", "multi-line"], + eqeqeq: ["error", "always", { null: "ignore" }], + "no-caller": "error", + "no-cond-assign": "error", + "no-debugger": "error", + "no-duplicate-case": "error", + "no-new-wrappers": "error", + "no-restricted-globals": ["error", "parseInt", "parseFloat"], + "no-unused-labels": "error", + "no-var": "error", + "prefer-const": ["error", { destructuring: "all" }], + radix: "error", + "use-isnan": "error", + "object-shorthand": [ + "error", + "always", + { avoidQuotes: true, ignoreConstructors: false, avoidExplicitReturnArrows: true }, + ], + "no-restricted-syntax": ["error", "ForInStatement", "LabeledStatement", "SequenceExpression"], + "spaced-comment": [ + "error", + "always", + { + line: { exceptions: ["-", "+"], markers: ["=", "!", "/"] }, + block: { exceptions: ["-", "+"], markers: ["=", "!", ":", "::"], balanced: true }, + }, + ], + "no-delete-var": ["error"], + "no-label-var": ["error"], + yoda: ["error"], + "prefer-numeric-literals": ["error"], + "prefer-rest-params": ["error"], + "prefer-spread": ["error"], + "no-useless-computed-key": ["error"], + "for-direction": ["error"], + "no-compare-neg-zero": ["error"], + "no-dupe-else-if": ["error"], + "no-empty": ["error", { allowEmptyCatch: true }], + "no-implicit-coercion": ["error", { boolean: true, number: true, string: true }], + "operator-assignment": ["error"], + "no-path-concat": ["error"], + "no-control-regex": ["error"], + "no-unneeded-ternary": ["error", { defaultAssignment: false }], + "one-var": ["error", "never"], + "prefer-exponentiation-operator": ["error"], + "prefer-object-spread": ["error"], + "no-useless-catch": ["error"], + "no-useless-concat": ["error"], + "no-useless-escape": ["error"], + "no-useless-return": ["error"], + }, + }, + + // typescript-eslint + { + plugins: { + "@typescript-eslint": tseslint.plugin, + }, + languageOptions: { + parser: tseslint.parser, + parserOptions: { + project: [ + "test/tsconfig.json", + "src/lualib/tsconfig.json", + "src/lualib/tsconfig.lua50.json", + "benchmark/tsconfig.json", + "language-extensions/tsconfig.json", + "tsconfig.eslint.json", + ], + tsconfigRootDir: import.meta.dirname, + }, + }, + }, + { + rules: { + "@typescript-eslint/adjacent-overload-signatures": "error", + "@typescript-eslint/array-type": ["error", { default: "array-simple" }], + "@typescript-eslint/await-thenable": "error", + "@typescript-eslint/consistent-type-assertions": [ + "error", + { assertionStyle: "as", objectLiteralTypeAssertions: "never" }, + ], + "@typescript-eslint/consistent-type-definitions": "error", + "@typescript-eslint/explicit-member-accessibility": ["error", { overrides: { constructors: "no-public" } }], + "@typescript-eslint/naming-convention": [ + "error", + { + selector: "default", + format: ["camelCase"], + leadingUnderscore: "allow", + }, + { + selector: "variable", + format: ["camelCase", "UPPER_CASE"], + leadingUnderscore: "allow", + }, + { + selector: "typeLike", + format: ["PascalCase"], + }, + { + selector: "enumMember", + format: ["PascalCase"], + }, + { + selector: "typeParameter", + format: ["PascalCase"], + prefix: ["T"], + filter: { + regex: "K|V", + match: false, + }, + }, + { + selector: "interface", + format: ["PascalCase"], + custom: { + regex: "^I[A-Z]", + match: false, + }, + }, + { + // Ignore properties that require quotes + selector: "objectLiteralProperty", + modifiers: ["requiresQuotes"], + format: null, + }, + ], + "no-array-constructor": "off", + "@typescript-eslint/no-array-constructor": "error", + "@typescript-eslint/no-empty-interface": "error", + "@typescript-eslint/no-empty-object-type": "error", + "@typescript-eslint/no-extra-non-null-assertion": "error", + "@typescript-eslint/no-extraneous-class": "error", + "@typescript-eslint/no-floating-promises": "error", + "@typescript-eslint/no-for-in-array": "error", + "@typescript-eslint/no-inferrable-types": "error", + "@typescript-eslint/no-misused-new": "error", + "@typescript-eslint/no-misused-promises": "error", + "@typescript-eslint/no-namespace": ["error", { allowDeclarations: true }], + "@typescript-eslint/no-this-alias": "error", + "@typescript-eslint/no-unnecessary-qualifier": "error", + "@typescript-eslint/no-unnecessary-type-arguments": "error", + "@typescript-eslint/no-unnecessary-type-assertion": "error", + "@typescript-eslint/no-unsafe-function-type": "error", + "no-unused-expressions": "off", + "@typescript-eslint/no-unused-expressions": "error", + "no-useless-constructor": "off", + "@typescript-eslint/no-useless-constructor": "error", + "@typescript-eslint/no-wrapper-object-types": "error", + "no-throw-literal": "off", + "@typescript-eslint/only-throw-error": "error", // "no-throw-literal" was renamed to "only-throw-error". + "@typescript-eslint/prefer-for-of": "error", + "@typescript-eslint/prefer-function-type": "error", + "@typescript-eslint/prefer-includes": "error", + "@typescript-eslint/prefer-optional-chain": "error", + "@typescript-eslint/prefer-namespace-keyword": "error", + "@typescript-eslint/prefer-nullish-coalescing": "error", + "@typescript-eslint/prefer-string-starts-ends-with": "error", + "@typescript-eslint/promise-function-async": ["error", { checkArrowFunctions: false }], + "@typescript-eslint/require-await": "error", + "@typescript-eslint/restrict-plus-operands": ["error", { skipCompoundAssignments: false }], + "@typescript-eslint/return-await": "error", + "@typescript-eslint/triple-slash-reference": "error", + "@typescript-eslint/unified-signatures": "error", + }, + }, + + // eslint-plugin-jest + eslintPluginJest.configs["flat/recommended"], + eslintPluginJest.configs["flat/style"], + { + rules: { + "jest/expect-expect": "off", + "jest/consistent-test-it": ["error", { fn: "test", withinDescribe: "test" }], + "jest/no-disabled-tests": "error", + "jest/no-identical-title": "off", + "jest/no-test-return-statement": "error", + "jest/prefer-spy-on": "error", + "jest/prefer-todo": "error", + "jest/valid-title": "error", + // TODO: + // "jest/lowercase-name": "error", + }, + }, + + // Exceptions for specific files/directories + { + files: ["src/lualib/**/*.ts"], + rules: { + "no-restricted-syntax": ["error", "LabeledStatement", "SequenceExpression"], + "@typescript-eslint/only-throw-error": "off", + "@typescript-eslint/prefer-optional-chain": "off", + "@typescript-eslint/naming-convention": "off", + }, + }, + + // Ignore some specific files and directories + { + ignores: [ + ".github/scripts/create_benchmark_check.js", + "dist/", + "eslint.config.mjs", + "jest.config.js", + "test/cli/errors/", + "test/cli/watch/", + "test/translation/transformation/", + "test/transpile/directories/", + "test/transpile/module-resolution/**/node_modules/", + "test/transpile/module-resolution/**/dist/", + "test/transpile/outFile/", + "test/transpile/resolve-plugin/", + ], + } +); diff --git a/jest.config.js b/jest.config.js index 2d390c93d..26b60c1cf 100644 --- a/jest.config.js +++ b/jest.config.js @@ -1,4 +1,4 @@ -const isCI = require("is-ci"); +const isCI = process.env.GITHUB_ACTIONS !== undefined; /** @type {Partial} */ module.exports = { @@ -15,10 +15,13 @@ module.exports = { testEnvironment: "node", testRunner: "jest-circus/runner", preset: "ts-jest", - globals: { - "ts-jest": { - tsconfig: "/test/tsconfig.json", - diagnostics: { warnOnly: !isCI }, - }, + transform: { + "^.+\\.ts?$": [ + "ts-jest", + { + tsconfig: "/test/tsconfig.json", + diagnostics: { warnOnly: !isCI }, + }, + ], }, }; diff --git a/language-extensions/index.d.ts b/language-extensions/index.d.ts index 30eb70009..6d805f189 100644 --- a/language-extensions/index.d.ts +++ b/language-extensions/index.d.ts @@ -1,14 +1,28 @@ +/* eslint-disable @typescript-eslint/naming-convention */ + type AnyTable = Record; -// eslint-disable-next-line @typescript-eslint/ban-types, @typescript-eslint/consistent-type-definitions +// eslint-disable-next-line @typescript-eslint/consistent-type-definitions,@typescript-eslint/no-empty-object-type type AnyNotNil = {}; /** - * Indicates a type is a language extension provided by TypescriptToLua. + * Indicates a type is a language extension provided by TypescriptToLua when used as a value or function call. + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * + * @param TBrand A string used to uniquely identify the language extension type + */ +declare interface LuaExtension { + readonly __tstlExtension: TBrand; +} + +/** + * Indicates a type is a language extension provided by TypescriptToLua when used in a for-of loop. * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions * * @param TBrand A string used to uniquely identify the language extension type */ -declare type LuaExtension = { [T in TBrand]: { readonly __luaExtensionSymbol: unique symbol } }; +declare interface LuaIterationExtension { + readonly __tstlIterable: TBrand; +} /** * Returns multiple values from a function, by wrapping them in a LuaMultiReturn tuple. @@ -17,7 +31,7 @@ declare type LuaExtension = { [T in TBrand]: { readonly _ * @param T A tuple type with each element type representing a return value's type. * @param values Return values. */ -declare const $multi: ((...values: T) => LuaMultiReturn) & LuaExtension<"__luaMultiFunctionBrand">; +declare const $multi: ((...values: T) => LuaMultiReturn) & LuaExtension<"MultiFunction">; /** * Represents multiple return values as a tuple. @@ -25,7 +39,9 @@ declare const $multi: ((...values: T) => LuaMultiReturn) & L * * @param T A tuple type with each element type representing a return value's type. */ -declare type LuaMultiReturn = T & LuaExtension<"__luaMultiReturnBrand">; +declare type LuaMultiReturn = T & { + readonly __tstlMultiReturn: any; +}; /** * Creates a Lua-style numeric for loop (for i=start,limit,step) when used in for...of. Not valid in any other context. @@ -36,13 +52,13 @@ declare type LuaMultiReturn = T & LuaExtension<"__luaMultiRetur * @param step The amount to increment each iteration. */ declare const $range: ((start: number, limit: number, step?: number) => Iterable) & - LuaExtension<"__luaRangeFunctionBrand">; + LuaExtension<"RangeFunction">; /** * Transpiles to the global vararg (`...`) * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions */ -declare const $vararg: string[] & LuaExtension<"__luaVarargConstantBrand">; +declare const $vararg: string[] & LuaExtension<"VarargConstant">; /** * Represents a Lua-style iterator which is returned from a LuaIterable. @@ -76,7 +92,24 @@ declare type LuaIterator = TState extends undefined */ declare type LuaIterable = Iterable & LuaIterator & - LuaExtension<"__luaIterableBrand">; + LuaIterationExtension<"Iterable">; + +/** + * Represents an object that can be iterated with pairs() + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * + * @param TKey The type of the key returned each iteration. + * @param TValue The type of the value returned each iteration. + */ +declare type LuaPairsIterable = Iterable<[TKey, TValue]> & + LuaIterationExtension<"Pairs">; + +/** + * Represents an object that can be iterated with pairs(), where only the key value is used. + * + * @param TKey The type of the key returned each iteration. + */ +declare type LuaPairsKeyIterable = Iterable & LuaIterationExtension<"PairsKey">; /** * Calls to functions with this type are translated to `left + right`. @@ -86,8 +119,7 @@ declare type LuaIterable = Iterable & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaAddition = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaAdditionBrand">; +declare type LuaAddition = ((left: TLeft, right: TRight) => TReturn) & LuaExtension<"Addition">; /** * Calls to methods with this type are translated to `left + right`, where `left` is the object with the method. @@ -96,8 +128,7 @@ declare type LuaAddition = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaAdditionMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaAdditionMethodBrand">; +declare type LuaAdditionMethod = ((right: TRight) => TReturn) & LuaExtension<"AdditionMethod">; /** * Calls to functions with this type are translated to `left - right`. @@ -108,7 +139,7 @@ declare type LuaAdditionMethod = ((right: TRight) => TReturn) & * @param TReturn The resulting (return) type of the operation. */ declare type LuaSubtraction = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaSubtractionBrand">; + LuaExtension<"Subtraction">; /** * Calls to methods with this type are translated to `left - right`, where `left` is the object with the method. @@ -117,8 +148,7 @@ declare type LuaSubtraction = ((left: TLeft, right: TRig * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaSubtractionMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaSubtractionMethodBrand">; +declare type LuaSubtractionMethod = ((right: TRight) => TReturn) & LuaExtension<"SubtractionMethod">; /** * Calls to functions with this type are translated to `left * right`. @@ -129,7 +159,7 @@ declare type LuaSubtractionMethod = ((right: TRight) => TReturn * @param TReturn The resulting (return) type of the operation. */ declare type LuaMultiplication = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaMultiplicationBrand">; + LuaExtension<"Multiplication">; /** * Calls to methods with this type are translated to `left * right`, where `left` is the object with the method. @@ -139,7 +169,7 @@ declare type LuaMultiplication = ((left: TLeft, right: T * @param TReturn The resulting (return) type of the operation. */ declare type LuaMultiplicationMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaMultiplicationMethodBrand">; + LuaExtension<"MultiplicationMethod">; /** * Calls to functions with this type are translated to `left / right`. @@ -149,8 +179,7 @@ declare type LuaMultiplicationMethod = ((right: TRight) => TRet * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaDivision = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaDivisionBrand">; +declare type LuaDivision = ((left: TLeft, right: TRight) => TReturn) & LuaExtension<"Division">; /** * Calls to methods with this type are translated to `left / right`, where `left` is the object with the method. @@ -159,8 +188,7 @@ declare type LuaDivision = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaDivisionMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaDivisionMethodBrand">; +declare type LuaDivisionMethod = ((right: TRight) => TReturn) & LuaExtension<"DivisionMethod">; /** * Calls to functions with this type are translated to `left % right`. @@ -170,8 +198,7 @@ declare type LuaDivisionMethod = ((right: TRight) => TReturn) & * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaModulo = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaModuloBrand">; +declare type LuaModulo = ((left: TLeft, right: TRight) => TReturn) & LuaExtension<"Modulo">; /** * Calls to methods with this type are translated to `left % right`, where `left` is the object with the method. @@ -180,7 +207,7 @@ declare type LuaModulo = ((left: TLeft, right: TRight) = * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaModuloMethod = ((right: TRight) => TReturn) & LuaExtension<"__luaModuloMethodBrand">; +declare type LuaModuloMethod = ((right: TRight) => TReturn) & LuaExtension<"ModuloMethod">; /** * Calls to functions with this type are translated to `left ^ right`. @@ -190,8 +217,7 @@ declare type LuaModuloMethod = ((right: TRight) => TReturn) & L * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaPower = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaPowerBrand">; +declare type LuaPower = ((left: TLeft, right: TRight) => TReturn) & LuaExtension<"Power">; /** * Calls to methods with this type are translated to `left ^ right`, where `left` is the object with the method. @@ -200,7 +226,7 @@ declare type LuaPower = ((left: TLeft, right: TRight) => * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaPowerMethod = ((right: TRight) => TReturn) & LuaExtension<"__luaPowerMethodBrand">; +declare type LuaPowerMethod = ((right: TRight) => TReturn) & LuaExtension<"PowerMethod">; /** * Calls to functions with this type are translated to `left // right`. @@ -211,7 +237,7 @@ declare type LuaPowerMethod = ((right: TRight) => TReturn) & Lu * @param TReturn The resulting (return) type of the operation. */ declare type LuaFloorDivision = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaFloorDivisionBrand">; + LuaExtension<"FloorDivision">; /** * Calls to methods with this type are translated to `left // right`, where `left` is the object with the method. @@ -221,7 +247,7 @@ declare type LuaFloorDivision = ((left: TLeft, right: TR * @param TReturn The resulting (return) type of the operation. */ declare type LuaFloorDivisionMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaFloorDivisionMethodBrand">; + LuaExtension<"FloorDivisionMethod">; /** * Calls to functions with this type are translated to `left & right`. @@ -232,7 +258,7 @@ declare type LuaFloorDivisionMethod = ((right: TRight) => TRetu * @param TReturn The resulting (return) type of the operation. */ declare type LuaBitwiseAnd = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaBitwiseAndBrand">; + LuaExtension<"BitwiseAnd">; /** * Calls to methods with this type are translated to `left & right`, where `left` is the object with the method. @@ -241,8 +267,7 @@ declare type LuaBitwiseAnd = ((left: TLeft, right: TRigh * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaBitwiseAndMethodBrand">; +declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) & LuaExtension<"BitwiseAndMethod">; /** * Calls to functions with this type are translated to `left | right`. @@ -253,7 +278,7 @@ declare type LuaBitwiseAndMethod = ((right: TRight) => TReturn) * @param TReturn The resulting (return) type of the operation. */ declare type LuaBitwiseOr = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaBitwiseOrBrand">; + LuaExtension<"BitwiseOr">; /** * Calls to methods with this type are translated to `left | right`, where `left` is the object with the method. @@ -262,8 +287,7 @@ declare type LuaBitwiseOr = ((left: TLeft, right: TRight * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaBitwiseOrMethodBrand">; +declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) & LuaExtension<"BitwiseOrMethod">; /** * Calls to functions with this type are translated to `left ~ right`. @@ -274,7 +298,7 @@ declare type LuaBitwiseOrMethod = ((right: TRight) => TReturn) * @param TReturn The resulting (return) type of the operation. */ declare type LuaBitwiseExclusiveOr = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaBitwiseExclusiveOrBrand">; + LuaExtension<"BitwiseExclusiveOr">; /** * Calls to methods with this type are translated to `left ~ right`, where `left` is the object with the method. @@ -284,7 +308,7 @@ declare type LuaBitwiseExclusiveOr = ((left: TLeft, righ * @param TReturn The resulting (return) type of the operation. */ declare type LuaBitwiseExclusiveOrMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaBitwiseExclusiveOrMethodBrand">; + LuaExtension<"BitwiseExclusiveOrMethod">; /** * Calls to functions with this type are translated to `left << right`. @@ -295,7 +319,7 @@ declare type LuaBitwiseExclusiveOrMethod = ((right: TRight) => * @param TReturn The resulting (return) type of the operation. */ declare type LuaBitwiseLeftShift = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaBitwiseLeftShiftBrand">; + LuaExtension<"BitwiseLeftShift">; /** * Calls to methods with this type are translated to `left << right`, where `left` is the object with the method. @@ -305,7 +329,7 @@ declare type LuaBitwiseLeftShift = ((left: TLeft, right: * @param TReturn The resulting (return) type of the operation. */ declare type LuaBitwiseLeftShiftMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaBitwiseLeftShiftMethodBrand">; + LuaExtension<"BitwiseLeftShiftMethod">; /** * Calls to functions with this type are translated to `left >> right`. @@ -316,7 +340,7 @@ declare type LuaBitwiseLeftShiftMethod = ((right: TRight) => TR * @param TReturn The resulting (return) type of the operation. */ declare type LuaBitwiseRightShift = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaBitwiseRightShiftBrand">; + LuaExtension<"BitwiseRightShift">; /** * Calls to methods with this type are translated to `left >> right`, where `left` is the object with the method. @@ -326,7 +350,7 @@ declare type LuaBitwiseRightShift = ((left: TLeft, right * @param TReturn The resulting (return) type of the operation. */ declare type LuaBitwiseRightShiftMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaBitwiseRightShiftMethodBrand">; + LuaExtension<"BitwiseRightShiftMethod">; /** * Calls to functions with this type are translated to `left .. right`. @@ -336,8 +360,7 @@ declare type LuaBitwiseRightShiftMethod = ((right: TRight) => T * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaConcat = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaConcatBrand">; +declare type LuaConcat = ((left: TLeft, right: TRight) => TReturn) & LuaExtension<"Concat">; /** * Calls to methods with this type are translated to `left .. right`, where `left` is the object with the method. @@ -346,7 +369,7 @@ declare type LuaConcat = ((left: TLeft, right: TRight) = * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaConcatMethod = ((right: TRight) => TReturn) & LuaExtension<"__luaConcatMethodBrand">; +declare type LuaConcatMethod = ((right: TRight) => TReturn) & LuaExtension<"ConcatMethod">; /** * Calls to functions with this type are translated to `left < right`. @@ -356,8 +379,7 @@ declare type LuaConcatMethod = ((right: TRight) => TReturn) & L * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLessThan = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaLessThanBrand">; +declare type LuaLessThan = ((left: TLeft, right: TRight) => TReturn) & LuaExtension<"LessThan">; /** * Calls to methods with this type are translated to `left < right`, where `left` is the object with the method. @@ -366,8 +388,7 @@ declare type LuaLessThan = ((left: TLeft, right: TRight) * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLessThanMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaLessThanMethodBrand">; +declare type LuaLessThanMethod = ((right: TRight) => TReturn) & LuaExtension<"LessThanMethod">; /** * Calls to functions with this type are translated to `left > right`. @@ -378,7 +399,7 @@ declare type LuaLessThanMethod = ((right: TRight) => TReturn) & * @param TReturn The resulting (return) type of the operation. */ declare type LuaGreaterThan = ((left: TLeft, right: TRight) => TReturn) & - LuaExtension<"__luaGreaterThanBrand">; + LuaExtension<"GreaterThan">; /** * Calls to methods with this type are translated to `left > right`, where `left` is the object with the method. @@ -387,8 +408,7 @@ declare type LuaGreaterThan = ((left: TLeft, right: TRig * @param TRight The type of the right-hand-side of the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaGreaterThanMethod = ((right: TRight) => TReturn) & - LuaExtension<"__luaGreaterThanMethodBrand">; +declare type LuaGreaterThanMethod = ((right: TRight) => TReturn) & LuaExtension<"GreaterThanMethod">; /** * Calls to functions with this type are translated to `-operand`. @@ -397,7 +417,7 @@ declare type LuaGreaterThanMethod = ((right: TRight) => TReturn * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaNegation = ((operand: TOperand) => TReturn) & LuaExtension<"__luaNegationBrand">; +declare type LuaNegation = ((operand: TOperand) => TReturn) & LuaExtension<"Negation">; /** * Calls to method with this type are translated to `-operand`, where `operand` is the object with the method. @@ -405,7 +425,7 @@ declare type LuaNegation = ((operand: TOperand) => TReturn) & * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaNegationMethod = (() => TReturn) & LuaExtension<"__luaNegationMethodBrand">; +declare type LuaNegationMethod = (() => TReturn) & LuaExtension<"NegationMethod">; /** * Calls to functions with this type are translated to `~operand`. @@ -414,7 +434,7 @@ declare type LuaNegationMethod = (() => TReturn) & LuaExtension<"__luaN * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) & LuaExtension<"__luaBitwiseNotBrand">; +declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) & LuaExtension<"BitwiseNot">; /** * Calls to method with this type are translated to `~operand`, where `operand` is the object with the method. @@ -422,7 +442,7 @@ declare type LuaBitwiseNot = ((operand: TOperand) => TReturn) * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaBitwiseNotMethod = (() => TReturn) & LuaExtension<"__luaBitwiseNotMethodBrand">; +declare type LuaBitwiseNotMethod = (() => TReturn) & LuaExtension<"BitwiseNotMethod">; /** * Calls to functions with this type are translated to `#operand`. @@ -431,7 +451,7 @@ declare type LuaBitwiseNotMethod = (() => TReturn) & LuaExtension<"__lu * @param TOperand The type of the value in the operation. * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLength = ((operand: TOperand) => TReturn) & LuaExtension<"__luaLengthBrand">; +declare type LuaLength = ((operand: TOperand) => TReturn) & LuaExtension<"Length">; /** * Calls to method with this type are translated to `#operand`, where `operand` is the object with the method. @@ -439,7 +459,7 @@ declare type LuaLength = ((operand: TOperand) => TReturn) & L * * @param TReturn The resulting (return) type of the operation. */ -declare type LuaLengthMethod = (() => TReturn) & LuaExtension<"__luaLengthMethodBrand">; +declare type LuaLengthMethod = (() => TReturn) & LuaExtension<"LengthMethod">; /** * Calls to functions with this type are translated to `table[key]`. @@ -453,7 +473,7 @@ declare type LuaTableGet TValue) & - LuaExtension<"__luaTableGetBrand">; + LuaExtension<"TableGet">; /** * Calls to methods with this type are translated to `table[key]`, where `table` is the object with the method. @@ -463,7 +483,7 @@ declare type LuaTableGet = ((key: TKey) => TValue) & - LuaExtension<"__luaTableGetMethodBrand">; + LuaExtension<"TableGetMethod">; /** * Calls to functions with this type are translated to `table[key] = value`. @@ -478,7 +498,7 @@ declare type LuaTableSet void) & - LuaExtension<"__luaTableSetBrand">; + LuaExtension<"TableSet">; /** * Calls to methods with this type are translated to `table[key] = value`, where `table` is the object with the method. @@ -488,7 +508,24 @@ declare type LuaTableSet = ((key: TKey, value: TValue) => void) & - LuaExtension<"__luaTableSetMethodBrand">; + LuaExtension<"TableSetMethod">; + +/** + * Calls to functions with this type are translated to `table[key] = true`. + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * + * @param TTable The type to access as a Lua table. + * @param TKey The type of the key to use to access the table. + */ +declare type LuaTableAddKey = ((table: TTable, key: TKey) => void) & + LuaExtension<"TableAddKey">; + +/** + * Calls to methods with this type are translated to `table[key] = true`, where `table` is the object with the method. + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * @param TKey The type of the key to use to access the table. + */ +declare type LuaTableAddKeyMethod = ((key: TKey) => void) & LuaExtension<"TableAddKeyMethod">; /** * Calls to functions with this type are translated to `table[key] ~= nil`. @@ -498,7 +535,7 @@ declare type LuaTableSetMethod = ((key: TKey, va * @param TKey The type of the key to use to access the table. */ declare type LuaTableHas = ((table: TTable, key: TKey) => boolean) & - LuaExtension<"__luaTableHasBrand">; + LuaExtension<"TableHas">; /** * Calls to methods with this type are translated to `table[key] ~= nil`, where `table` is the object with the method. @@ -506,8 +543,7 @@ declare type LuaTableHas = ((ta * * @param TKey The type of the key to use to access the table. */ -declare type LuaTableHasMethod = ((key: TKey) => boolean) & - LuaExtension<"__luaTableHasMethodBrand">; +declare type LuaTableHasMethod = ((key: TKey) => boolean) & LuaExtension<"TableHasMethod">; /** * Calls to functions with this type are translated to `table[key] = nil`. @@ -517,7 +553,7 @@ declare type LuaTableHasMethod = ((key: TKey) => boolean * @param TKey The type of the key to use to access the table. */ declare type LuaTableDelete = ((table: TTable, key: TKey) => boolean) & - LuaExtension<"__luaTableDeleteBrand">; + LuaExtension<"TableDelete">; /** * Calls to methods with this type are translated to `table[key] = nil`, where `table` is the object with the method. @@ -526,7 +562,21 @@ declare type LuaTableDelete = ( * @param TKey The type of the key to use to access the table. */ declare type LuaTableDeleteMethod = ((key: TKey) => boolean) & - LuaExtension<"__luaTableDeleteMethodBrand">; + LuaExtension<"TableDeleteMethod">; + +/** + * Calls to functions with this type are translated to `next(myTable) == nil`. + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * + * @param TTable The type to access as a Lua table. + */ +declare type LuaTableIsEmpty = ((table: TTable) => boolean) & LuaExtension<"TableIsEmpty">; + +/** + * Calls to methods with this type are translated to `next(myTable) == nil`, where `table` is the object with the method. + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + */ +declare type LuaTableIsEmptyMethod = (() => boolean) & LuaExtension<"TableIsEmptyMethod">; /** * A convenience type for working directly with a Lua table. @@ -535,12 +585,13 @@ declare type LuaTableDeleteMethod = ((key: TKey) => bool * @param TKey The type of the keys used to access the table. * @param TValue The type of the values stored in the table. */ -declare interface LuaTable { +declare interface LuaTable extends LuaPairsIterable { length: LuaLengthMethod; get: LuaTableGetMethod; set: LuaTableSetMethod; has: LuaTableHasMethod; delete: LuaTableDeleteMethod; + isEmpty: LuaTableIsEmptyMethod; } /** @@ -554,7 +605,7 @@ declare type LuaTableConstructor = (new ) & - LuaExtension<"__luaTableNewBrand">; + LuaExtension<"TableNew">; /** * A convenience type for working directly with a Lua table. @@ -564,3 +615,83 @@ declare type LuaTableConstructor = (new extends LuaPairsIterable { + get: LuaTableGetMethod; + set: LuaTableSetMethod; + has: LuaTableHasMethod; + delete: LuaTableDeleteMethod; + isEmpty: LuaTableIsEmptyMethod; +} + +/** + * A convenience type for working directly with a Lua table, used as a map. + * + * This differs from LuaTable in that the `get` method may return `nil`. + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * @param K The type of the keys used to access the table. + * @param V The type of the values stored in the table. + */ +declare const LuaMap: (new () => LuaMap) & LuaExtension<"TableNew">; + +/** + * Readonly version of {@link LuaMap}. + * + * @param K The type of the keys used to access the table. + * @param V The type of the values stored in the table. + */ +declare interface ReadonlyLuaMap extends LuaPairsIterable { + get: LuaTableGetMethod; + has: LuaTableHasMethod; + isEmpty: LuaTableIsEmptyMethod; +} + +/** + * A convenience type for working directly with a Lua table, used as a set. + * + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * @param T The type of the keys used to access the table. + */ +declare interface LuaSet extends LuaPairsKeyIterable { + add: LuaTableAddKeyMethod; + has: LuaTableHasMethod; + delete: LuaTableDeleteMethod; + isEmpty: LuaTableIsEmptyMethod; +} + +/** + * A convenience type for working directly with a Lua table, used as a set. + * + * For more information see: https://typescripttolua.github.io/docs/advanced/language-extensions + * @param T The type of the keys used to access the table. + */ +declare const LuaSet: (new () => LuaSet) & LuaExtension<"TableNew">; + +/** + * Readonly version of {@link LuaSet}. + * + * @param T The type of the keys used to access the table. + */ +declare interface ReadonlyLuaSet extends LuaPairsKeyIterable { + has: LuaTableHasMethod; + isEmpty: LuaTableIsEmptyMethod; +} + +interface ObjectConstructor { + /** Returns an array of keys of an object, when iterated with `pairs`. */ + keys(o: LuaPairsIterable | LuaPairsKeyIterable): K[]; + + /** Returns an array of values of an object, when iterated with `pairs`. */ + values(o: LuaPairsIterable): V[]; + + /** Returns an array of key/values of an object, when iterated with `pairs`. */ + entries(o: LuaPairsIterable): Array<[K, V]>; +} diff --git a/language-extensions/package.json b/language-extensions/package.json new file mode 100644 index 000000000..194ab1212 --- /dev/null +++ b/language-extensions/package.json @@ -0,0 +1,23 @@ +{ + "name": "@typescript-to-lua/language-extensions", + "version": "1.19.0", + "description": "Language extensions used by typescript-to-lua", + "repository": "https://github.com/TypeScriptToLua/TypeScriptToLua", + "homepage": "https://typescripttolua.github.io/", + "bugs": { + "url": "https://github.com/TypeScriptToLua/TypeScriptToLua/issues" + }, + "license": "MIT", + "keywords": [ + "typescript", + "lua", + "tstl", + "transpiler", + "language extensions" + ], + "files": [ + "**.d.ts" + ], + "main": "", + "types": "index.d.ts" +} diff --git a/language-extensions/tsconfig.json b/language-extensions/tsconfig.json new file mode 100644 index 000000000..92894f9c4 --- /dev/null +++ b/language-extensions/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "target": "esnext", + "lib": ["esnext"], + "strict": true + } +} diff --git a/package-lock.json b/package-lock.json index 002afb94a..a1c9b5a76 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,306 +1,285 @@ { "name": "typescript-to-lua", - "version": "0.40.0", - "lockfileVersion": 2, + "version": "1.37.0", + "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "typescript-to-lua", - "version": "0.40.0", + "version": "1.37.0", "license": "MIT", "dependencies": { - "enhanced-resolve": "^5.8.2", + "@typescript-to-lua/language-extensions": "1.19.0", + "enhanced-resolve": "5.8.2", + "picomatch": "^2.3.1", "resolve": "^1.15.1", - "source-map": "^0.7.3", - "typescript": "~4.3.2" + "source-map": "^0.7.3" }, "bin": { "tstl": "dist/tstl.js" }, "devDependencies": { "@types/fs-extra": "^8.1.0", - "@types/glob": "^7.1.1", - "@types/jest": "^25.1.3", - "@types/node": "^13.7.7", + "@types/glob": "^7.2.0", + "@types/jest": "^27.5.2", + "@types/node": "^22.10.0", + "@types/picomatch": "^2.3.0", "@types/resolve": "1.14.0", - "@typescript-eslint/eslint-plugin": "^4.26.1", - "@typescript-eslint/parser": "^4.26.1", - "eslint": "^7.28.0", - "eslint-plugin-import": "^2.20.1", - "eslint-plugin-jest": "^23.8.2", + "eslint": "^9.39.4", + "eslint-plugin-jest": "^28.14.0", "fs-extra": "^8.1.0", "javascript-stringify": "^2.0.1", - "jest": "^26.0.1", - "jest-circus": "^25.1.0", - "lua-types": "^2.8.0", - "lua-wasm-bindings": "^0.2.2", - "prettier": "^2.0.5", - "ts-jest": "^26.3.0", - "ts-node": "^8.6.2" + "jest": "^29.7.0", + "jest-circus": "^29.7.0", + "lua-types": "^2.14.1", + "lua-wasm-bindings": "^0.5.3", + "prettier": "^2.8.8", + "ts-jest": "^29.4.9", + "ts-node": "^10.9.2", + "typescript": "6.0.2", + "typescript-eslint": "^8.58.0" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.10.0" + }, + "peerDependencies": { + "typescript": "6.0.2" } }, - "node_modules/@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@babel/highlight": "^7.12.13" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" } }, - "node_modules/@babel/core": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.6.tgz", - "integrity": "sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==", + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.6", - "@babel/parser": "^7.9.6", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/core/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "node_modules/@babel/compat-data": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.25.4.tgz", + "integrity": "sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/generator": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", - "integrity": "sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==", + "node_modules/@babel/core": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.25.2.tgz", + "integrity": "sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==", "dev": true, - "dependencies": { - "@babel/types": "^7.14.2", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" + "license": "MIT", + "peer": true, + "dependencies": { + "@ampproject/remapping": "^2.2.0", + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.0", + "@babel/helper-compilation-targets": "^7.25.2", + "@babel/helper-module-transforms": "^7.25.2", + "@babel/helpers": "^7.25.0", + "@babel/parser": "^7.25.0", + "@babel/template": "^7.25.0", + "@babel/traverse": "^7.25.2", + "@babel/types": "^7.25.2", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" } }, - "node_modules/@babel/generator/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-function-name": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", - "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", + "node_modules/@babel/generator": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.25.6.tgz", + "integrity": "sha512-VPC82gr1seXOpkjAAKoLhP50vx4vGNlF4msF64dSFq1P8RfB+QAuJWGHPXXPc8QyfVWwwB/TNNU4+ayZmHNbZw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.14.2" + "@babel/types": "^7.25.6", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^2.5.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.25.2.tgz", + "integrity": "sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.12.13" + "@babel/compat-data": "^7.25.2", + "@babel/helper-validator-option": "^7.24.8", + "browserslist": "^4.23.1", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "dependencies": { - "@babel/types": "^7.8.3" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.24.7.tgz", + "integrity": "sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.8.3" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", - "dev": true, - "dependencies": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", - "lodash": "^4.17.13" - } - }, - "node_modules/@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", + "version": "7.25.2", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.25.2.tgz", + "integrity": "sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.8.3" + "@babel/helper-module-imports": "^7.24.7", + "@babel/helper-simple-access": "^7.24.7", + "@babel/helper-validator-identifier": "^7.24.7", + "@babel/traverse": "^7.25.2" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - }, - "node_modules/@babel/helper-replace-supers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz", - "integrity": "sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==", + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.24.8.tgz", + "integrity": "sha512-FFWx5142D8h2Mgr/iPVGH5G7w6jDn4jUSpZTyDnQO0Yn7Ks2Kuz6Pci8H6MPCoUJegd/UZQ3tAvfLCxQSnWWwg==", "dev": true, - "dependencies": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, "node_modules/@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", - "dev": true, - "dependencies": { - "@babel/types": "^7.12.13" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", - "dev": true - }, - "node_modules/@babel/helpers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.6.tgz", - "integrity": "sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==", - "dev": true, - "dependencies": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" - } - }, - "node_modules/@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "dev": true, - "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.24.7.tgz", + "integrity": "sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^1.9.0" + "@babel/traverse": "^7.24.7", + "@babel/types": "^7.24.7" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, - "dependencies": { - "color-name": "1.1.3" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "node_modules/@babel/helper-validator-option": { + "version": "7.24.8", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.24.8.tgz", + "integrity": "sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/@babel/helpers": { + "version": "7.28.4", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.4.tgz", + "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.4" }, "engines": { - "node": ">=4" + "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.3.tgz", - "integrity": "sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.5.tgz", + "integrity": "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.5" + }, "bin": { "parser": "bin/babel-parser.js" }, @@ -313,8 +292,12 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-bigint": { @@ -322,17 +305,70 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-class-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.3.tgz", - "integrity": "sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg==", + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.25.6.tgz", + "integrity": "sha512-sXaDXaJN9SNLymBdlWFA+bjzBhFD617ZaFiY13dGt7TVslVvVgA6fkZOP7Ki3IGElC45lwHdOTrCtKZGVAWeLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-json-strings": { @@ -340,17 +376,41 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.24.7.tgz", + "integrity": "sha512-6ddciUPe/mpMnOKv/U+RSd2vvVy+Yw/JfBB0ZHYjEZt9NLHmCUylNYlsbqCCS1Bffjlb0fCwC9Vqz+sBz6PsiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.24.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz", - "integrity": "sha512-Zpg2Sgc++37kuFl6ppq2Q7Awc6E6AIW671x5PY8E/f7MCIyPPGK/EoeZXvvY3P42exZ3Q4/t3YOzP/HiN79jDg==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { @@ -358,17 +418,25 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-numeric-separator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", - "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.8.3" + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-object-rest-spread": { @@ -376,8 +444,12 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-optional-catch-binding": { @@ -385,8 +457,12 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-optional-chaining": { @@ -394,3648 +470,3831 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", - "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.2", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.2", - "@babel/types": "^7.14.2", - "debug": "^4.1.0", - "globals": "^11.1.0" + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@babel/types": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", - "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", + "node_modules/@babel/plugin-syntax-typescript": { + "version": "7.25.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.25.4.tgz", + "integrity": "sha512-uMOCoHVU52BsSWxPOMVv5qKRdeSlPuImUCB2dlPuBSU+W2/ROE7/Zg8F2Kepbk+8yBa68LlRKxO+xgEVWorsDg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.14.0", - "to-fast-properties": "^2.0.0" + "@babel/helper-plugin-utils": "^7.24.8" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "node_modules/@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, + "license": "MIT", "dependencies": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - }, - "bin": { - "watch": "cli.js" + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" }, "engines": { - "node": ">=0.1.95" + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", - "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", + "node_modules/@babel/traverse": { + "version": "7.25.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.25.6.tgz", + "integrity": "sha512-9Vrcx5ZW6UwK5tvqsj0nGpp/XzqthkT0dqIc9g1AdtygFToNtTF67XzYS//dm+SAK9cp3B9R4ZO/46p63SCjlQ==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" + "@babel/code-frame": "^7.24.7", + "@babel/generator": "^7.25.6", + "@babel/parser": "^7.25.6", + "@babel/template": "^7.25.0", + "@babel/types": "^7.25.6", + "debug": "^4.3.1", + "globals": "^11.1.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "node_modules/@babel/types": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.5.tgz", + "integrity": "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==", "dev": true, + "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": ">=6.9.0" } }, - "node_modules/@eslint/eslintrc/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } + "license": "MIT" }, - "node_modules/@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", "dev": true, + "license": "MIT", "dependencies": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" + "@jridgewell/trace-mapping": "0.3.9" }, "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", "dev": true, - "engines": { - "node": ">=8" + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" } }, - "node_modules/@jest/console": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.5.0.tgz", - "integrity": "sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==", + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "jest-message-util": "^25.5.0", - "jest-util": "^25.5.0", - "slash": "^3.0.0" + "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": ">= 8.3" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "node_modules/@jest/core": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.0.1.tgz", - "integrity": "sha512-Xq3eqYnxsG9SjDC+WLeIgf7/8KU6rddBxH+SCt18gEpOhAGYC/Mq+YbtlNcIdwjnnT+wDseXSbU0e5X84Y4jTQ==", + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "@jest/console": "^26.0.1", - "@jest/reporters": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.0.1", - "jest-config": "^26.0.1", - "jest-haste-map": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-resolve-dependencies": "^26.0.1", - "jest-runner": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "jest-watcher": "^26.0.1", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, + "license": "Apache-2.0", "engines": { - "node": ">= 10.14.2" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@jest/core/node_modules/@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">= 10.14.2" + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "node_modules/@jest/core/node_modules/@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" }, "engines": { - "node": ">= 10.14.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@jest/core/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" + "@eslint/core": "^0.17.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@jest/core/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@types/istanbul-lib-report": "*" + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@jest/core/node_modules/@types/prettier": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", - "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", - "dev": true - }, - "node_modules/@jest/core/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "node_modules/@eslint/eslintrc": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.1", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://opencollective.com/eslint" } }, - "node_modules/@jest/core/node_modules/diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", + "node_modules/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 10.14.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, - "node_modules/@jest/core/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@jest/core/node_modules/expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" }, "engines": { - "node": ">= 10.14.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "engines": { + "node": ">=18.18.0" } }, - "node_modules/@jest/core/node_modules/jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", "dev": true, "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">=18.18.0" } }, - "node_modules/@jest/core/node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", "dev": true, "engines": { - "node": ">= 10.14.2" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jest/core/node_modules/jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, "engines": { - "node": ">= 10.14.2" + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jest/core/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, + "license": "ISC", "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">=8" } }, - "node_modules/@jest/core/node_modules/jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, - "engines": { - "node": ">= 10.14.2" + "license": "MIT", + "dependencies": { + "sprintf-js": "~1.0.2" } }, - "node_modules/@jest/core/node_modules/jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">=8" } }, - "node_modules/@jest/core/node_modules/jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" + "argparse": "^1.0.7", + "esprima": "^4.0.0" }, - "engines": { - "node": ">= 10.14.2" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/@jest/core/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" + "p-locate": "^4.1.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">=8" } }, - "node_modules/@jest/core/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" + "p-try": "^2.0.0" }, "engines": { - "node": ">=8" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@jest/core/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" + "p-limit": "^2.2.0" }, "engines": { - "node": ">= 10" + "node": ">=8" } }, - "node_modules/@jest/core/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/@jest/core/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/@jest/core/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, + "license": "MIT", "engines": { "node": ">=8" } }, - "node_modules/@jest/core/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "node_modules/@jest/console": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", + "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/core/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/@jest/core": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", + "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" + "@jest/console": "^29.7.0", + "@jest/reporters": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-changed-files": "^29.7.0", + "jest-config": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-resolve-dependencies": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "jest-watcher": "^29.7.0", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-ansi": "^6.0.0" }, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } + } + }, + "node_modules/@jest/core/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@jest/core/node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "node_modules/@jest/core/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { - "escape-string-regexp": "^2.0.0" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/@jest/core/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/@jest/environment": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.5.0.tgz", - "integrity": "sha512-U2VXPEqL07E/V7pSZMSQCvV5Ea4lqOlT+0ZFijl/i316cRMHvZ4qC+jBdryd+lmRetjQo0YIQr6cVPNxxK87mA==", + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/fake-timers": "^25.5.0", - "@jest/types": "^25.5.0", - "jest-mock": "^25.5.0" + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" }, "engines": { - "node": ">= 8.3" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/fake-timers": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.5.0.tgz", - "integrity": "sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ==", + "node_modules/@jest/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-mock": "^25.5.0", - "jest-util": "^25.5.0", - "lolex": "^5.0.0" + "expect": "^29.7.0", + "jest-snapshot": "^29.7.0" }, "engines": { - "node": ">= 8.3" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.0.1.tgz", - "integrity": "sha512-iuucxOYB7BRCvT+TYBzUqUNuxFX1hqaR6G6IcGgEqkJ5x4htNKo1r7jk1ji9Zj8ZMiMw0oB5NaA7k5Tx6MVssA==", + "node_modules/@jest/expect-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", + "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/environment": "^26.0.1", - "@jest/types": "^26.0.1", - "expect": "^26.0.1" + "jest-get-type": "^29.6.3" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals/node_modules/@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", - "jest-mock": "^26.6.2" + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals/node_modules/@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", + "node_modules/@jest/globals": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", + "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/types": "^29.6.3", + "jest-mock": "^29.7.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "node_modules/@jest/reporters": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", + "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", + "@bcoe/v8-coverage": "^0.2.3", + "@jest/console": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" + "chalk": "^4.0.0", + "collect-v8-coverage": "^1.0.0", + "exit": "^0.1.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "istanbul-lib-coverage": "^3.0.0", + "istanbul-lib-instrument": "^6.0.0", + "istanbul-lib-report": "^3.0.0", + "istanbul-lib-source-maps": "^4.0.0", + "istanbul-reports": "^3.1.3", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "slash": "^3.0.0", + "string-length": "^4.0.1", + "strip-ansi": "^6.0.0", + "v8-to-istanbul": "^9.0.1" }, "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/globals/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/@jest/globals/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "node_modules/@jest/globals/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, - "engines": { - "node": ">=10" + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/globals/node_modules/diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true, - "engines": { - "node": ">= 10.14.2" + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/@jest/globals/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, + "license": "MIT", + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals/node_modules/expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", + "node_modules/@jest/source-map": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", + "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" + "@jridgewell/trace-mapping": "^0.3.18", + "callsites": "^3.0.0", + "graceful-fs": "^4.2.9" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals/node_modules/jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", + "node_modules/@jest/test-result": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", + "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, + "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" + "@jest/console": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "collect-v8-coverage": "^1.0.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals/node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", + "node_modules/@jest/test-sequencer": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", + "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/test-result": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "slash": "^3.0.0" + }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals/node_modules/jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, + "license": "MIT", "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/globals/node_modules/jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", + "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*" + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { - "node": ">= 10.14.2" + "node": ">=6.0.0" } }, - "node_modules/@jest/globals/node_modules/jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 10.14.2" + "node": ">=6.0.0" } }, - "node_modules/@jest/globals/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, + "license": "MIT", "engines": { - "node": ">= 10.14.2" + "node": ">=6.0.0" } }, - "node_modules/@jest/globals/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@jest/globals/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true + "node_modules/@sinclair/typebox": { + "version": "0.27.8", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", + "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", + "dev": true, + "license": "MIT" }, - "node_modules/@jest/globals/node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" + "type-detect": "4.0.8" } }, - "node_modules/@jest/reporters": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.0.1.tgz", - "integrity": "sha512-NWWy9KwRtE1iyG/m7huiFVF9YsYv/e+mbflKRV84WDoJfBqUrNRyDbL/vFxQcYLl8IRqI4P3MgPn386x76Gf2g==", + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.0.1", - "jest-resolve": "^26.0.1", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^4.1.3" - }, - "engines": { - "node": ">= 10.14.2" - }, - "optionalDependencies": { - "node-notifier": "^7.0.0" + "@sinonjs/commons": "^3.0.0" } }, - "node_modules/@jest/reporters/node_modules/@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" } }, - "node_modules/@jest/reporters/node_modules/@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "node_modules/@types/babel__generator": { + "version": "7.6.8", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", + "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": ">= 10.14.2" + "@babel/types": "^7.0.0" } }, - "node_modules/@jest/reporters/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" } }, - "node_modules/@jest/reporters/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "node_modules/@types/babel__traverse": { + "version": "7.20.6", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.6.tgz", + "integrity": "sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/istanbul-lib-report": "*" + "@babel/types": "^7.20.7" } }, - "node_modules/@jest/reporters/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true + "node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true, + "license": "MIT" }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "node_modules/@types/fs-extra": { + "version": "8.1.5", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.5.tgz", + "integrity": "sha512-0dzKcwO+S8s2kuF5Z9oUWatQJj5Uq/iqphEtE3GQJVRRYm/tD1LglU2UnXi2A8jLq5umkGouOXOR9y0n613ZwQ==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "@types/node": "*" } }, - "node_modules/@jest/reporters/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", "dev": true, - "engines": { - "node": ">=8" + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" } }, - "node_modules/@jest/reporters/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "engines": { - "node": ">= 10.14.2" + "@types/node": "*" } }, - "node_modules/@jest/reporters/node_modules/jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } + "license": "MIT" }, - "node_modules/@jest/reporters/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" + "@types/istanbul-lib-coverage": "*" } }, - "node_modules/@jest/reporters/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "@types/istanbul-lib-report": "*" } }, - "node_modules/@jest/reporters/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "node_modules/@types/jest": { + "version": "27.5.2", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-27.5.2.tgz", + "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", "dev": true, "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" } }, - "node_modules/@jest/reporters/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" }, - "node_modules/@jest/reporters/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/@jest/reporters/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "node_modules/@types/node": { + "version": "22.10.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.0.tgz", + "integrity": "sha512-XC70cRZVElFHfIUB40FgZOBbgJYFKKMa5nb9lxcwYstFG/Mi+/Y0bGS+rs6Dmhmkpq4pnNiLiuZAbc02YCOnmA==", "dev": true, + "peer": true, "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" + "undici-types": "~6.20.0" } }, - "node_modules/@jest/reporters/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "node_modules/@types/picomatch": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.4.tgz", + "integrity": "sha512-0so8lU8O5zatZS/2Fi4zrwks+vZv7e0dygrgEZXljODXBig97l4cPQD+9LabXfGJOWwoRkTVz6Q4edZvD12UOA==", "dev": true, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/@jest/reporters/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/@types/resolve": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.14.0.tgz", + "integrity": "sha512-bmjNBW6tok+67iOsASeYSJxSgY++BIR35nGyGLORTDirhra9reJ0shgGL3U7KPDUbOBCx8JrlCjd4d/y5uiMRQ==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, - "node_modules/@jest/reporters/node_modules/stack-utils": { + "node_modules/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stack-utils": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } + "license": "MIT" }, - "node_modules/@jest/source-map": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.0.0.tgz", - "integrity": "sha512-S2Z+Aj/7KOSU2TfW0dyzBze7xr95bkm5YXNUqqCek+HE0VbNNSNzrRwfIi5lf7wvzDTSS0/ib8XQ1krFNyYgbQ==", + "node_modules/@types/yargs": { + "version": "17.0.33", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", + "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, + "license": "MIT", "dependencies": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - }, - "engines": { - "node": ">= 10.14.2" + "@types/yargs-parser": "*" } }, - "node_modules/@jest/source-map/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/@jest/test-result": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.5.0.tgz", - "integrity": "sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==", + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", "dev": true, - "dependencies": { - "@jest/console": "^25.5.0", - "@jest/types": "^25.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">= 8.3" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.58.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@jest/test-sequencer": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.0.1.tgz", - "integrity": "sha512-ssga8XlwfP8YjbDcmVhwNlrmblddMfgUeAkWIXts1V22equp2GMIHxm7cyeD5Q/B0ZgKPK/tngt45sH99yLLGg==", + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, - "dependencies": { - "@jest/test-result": "^26.0.1", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.0.1", - "jest-runner": "^26.0.1", - "jest-runtime": "^26.0.1" - }, + "license": "MIT", "engines": { - "node": ">= 10.14.2" + "node": ">= 4" } }, - "node_modules/@jest/test-sequencer/node_modules/@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", + "node_modules/@typescript-eslint/parser": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", "dev": true, + "license": "MIT", + "peer": true, "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3" }, "engines": { - "node": ">= 10.14.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@jest/test-sequencer/node_modules/@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "debug": "^4.4.3" }, "engines": { - "node": ">= 10.14.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@jest/test-sequencer/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", "dev": true, + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@jest/test-sequencer/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@jest/test-sequencer/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "node_modules/@jest/test-sequencer/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@jest/test-sequencer/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/test-sequencer/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "engines": { - "node": ">= 10.14.2" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@jest/test-sequencer/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@jest/test-sequencer/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, + "license": "MIT", "engines": { - "node": ">= 10" + "node": "18 || 20 || >=22" } }, - "node_modules/@jest/test-sequencer/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/@jest/test-sequencer/node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, + "license": "MIT", "dependencies": { - "escape-string-regexp": "^2.0.0" + "balanced-match": "^4.0.2" }, "engines": { - "node": ">=10" + "node": "18 || 20 || >=22" } }, - "node_modules/@jest/transform": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.0.1.tgz", - "integrity": "sha512-pPRkVkAQ91drKGbzCfDOoHN838+FSbYaEAvBXvKuWeeRRUD8FjwXkqfUNUZL6Ke48aA/1cqq/Ni7kVMCoqagWA==", + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "@babel/core": "^7.1.0", - "@jest/types": "^26.0.1", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-util": "^26.0.1", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">= 10.14.2" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@jest/transform/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "node_modules/@typescript-eslint/utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" }, "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/@jest/transform/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@typescript-eslint/types": "8.58.0", + "eslint-visitor-keys": "^5.0.0" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@jest/transform/node_modules/jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">= 10.14.2" + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/@jest/transform/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", + "node_modules/@typescript-to-lua/language-extensions": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/@typescript-to-lua/language-extensions/-/language-extensions-1.19.0.tgz", + "integrity": "sha512-Os5wOKwviTD4LeqI29N0btYOjokSJ97iCf45EOjIABlb5IwNQy7AE/AqZJobRw3ywHH8+KzJUMkEirWPzh2tUA==", + "license": "MIT" + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" }, "engines": { - "node": ">= 10.14.2" + "node": ">=0.4.0" } }, - "node_modules/@jest/transform/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "node_modules/@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", "dev": true, + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" + "acorn": "^8.11.0" }, "engines": { - "node": ">= 8.3" + "node": ">=0.4.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, + "license": "MIT", "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" }, - "engines": { - "node": ">= 8" + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, + "license": "MIT", + "dependencies": { + "type-fest": "^0.21.3" + }, "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", - "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", - "dev": true, - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" + "node": ">=8" }, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@sinonjs/commons": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz", - "integrity": "sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q==", + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, - "dependencies": { - "type-detect": "4.0.8" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "node_modules/@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { - "@sinonjs/commons": "^1.7.0" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@types/babel__core": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz", - "integrity": "sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw==", + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "license": "ISC", "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@types/babel__generator": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", - "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", "dev": true, - "dependencies": { - "@babel/types": "^7.0.0" - } + "license": "MIT" }, - "node_modules/@types/babel__template": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", - "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true, - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } + "license": "Python-2.0" }, - "node_modules/@types/babel__traverse": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.11.tgz", - "integrity": "sha512-ddHK5icION5U6q11+tV2f9Mo6CZVuT8GJKld2q9LqHSZbvLbH34Kcu2yFGckZut453+eQU6btIA3RihmnRgI+Q==", + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.3.0" + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" } }, - "node_modules/@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true - }, - "node_modules/@types/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==", + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@types/node": "*" + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/@types/graceful-fs": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.3.tgz", - "integrity": "sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==", + "node_modules/babel-plugin-istanbul/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, - "dependencies": { - "@types/node": "*" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" } }, - "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", - "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", - "dev": true - }, - "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "*" + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", + "node_modules/babel-preset-current-node-syntax": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", + "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dev": true, + "license": "MIT", "dependencies": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@types/jest": { - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.2.3.tgz", - "integrity": "sha512-JXc1nK/tXHiDhV55dvfzqtmP4S3sy3T3ouV2tkViZgxY/zeUkcpQcQPGRlgF4KmWzWW5oiWYSZwtCB+2RsE4Fw==", + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, + "license": "MIT", "dependencies": { - "jest-diff": "^25.2.1", - "pretty-format": "^25.2.1" + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "node_modules/@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", - "dev": true - }, - "node_modules/@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "node_modules/@types/node": { - "version": "13.7.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.7.tgz", - "integrity": "sha512-Uo4chgKbnPNlxQwoFmYIwctkQVkMMmsAoGGU4JKwLuvBefF0pCq4FybNSnfkfRCpC7ZW7kttcC/TrRtAJsvGtg==", - "dev": true - }, - "node_modules/@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true - }, - "node_modules/@types/prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==", - "dev": true - }, - "node_modules/@types/resolve": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.14.0.tgz", - "integrity": "sha512-bmjNBW6tok+67iOsASeYSJxSgY++BIR35nGyGLORTDirhra9reJ0shgGL3U7KPDUbOBCx8JrlCjd4d/y5uiMRQ==", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true, - "dependencies": { - "@types/node": "*" - } - }, - "node_modules/@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", - "dev": true + "license": "MIT" }, - "node_modules/@types/yargs": { - "version": "15.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", - "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, + "license": "MIT", "dependencies": { - "@types/yargs-parser": "*" + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" } }, - "node_modules/@types/yargs-parser": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", - "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", - "dev": true - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.1.tgz", - "integrity": "sha512-aoIusj/8CR+xDWmZxARivZjbMBQTT9dImUtdZ8tVCVRXgBUuuZyM5Of5A9D9arQPxbi/0rlJLcuArclz/rCMJw==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/experimental-utils": "4.26.1", - "@typescript-eslint/scope-manager": "4.26.1", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.21", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "fill-range": "^7.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^4.0.0", - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=8" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/experimental-utils": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.1.tgz", - "integrity": "sha512-sQHBugRhrXzRCs9PaGg6rowie4i8s/iD/DpTB+EXte8OMDfdCG5TvO73XlO9Wc/zi0uyN4qOmX9hIjQEyhnbmQ==", + "node_modules/browserslist": { + "version": "4.23.3", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.23.3.tgz", + "integrity": "sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==", "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, "dependencies": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.26.1", - "@typescript-eslint/types": "4.26.1", - "@typescript-eslint/typescript-estree": "4.26.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "caniuse-lite": "^1.0.30001646", + "electron-to-chromium": "^1.5.4", + "node-releases": "^2.0.18", + "update-browserslist-db": "^1.1.0" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "bin": { + "browserslist": "cli.js" }, - "peerDependencies": { - "eslint": "*" + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz", - "integrity": "sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg==", + "node_modules/bs-logger": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", + "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "4.26.1", - "@typescript-eslint/visitor-keys": "4.26.1", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "fast-json-stable-stringify": "2.x" }, "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">= 6" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, - "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" + "node-int64": "^0.4.0" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true, + "license": "MIT" + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.31.0.tgz", - "integrity": "sha512-MI6IWkutLYQYTQgZ48IVnRXmLR/0Q6oAyJgiOror74arUMh7EWjJkADfirZhRsUMHeLJ85U2iySDwHTSnNi9vA==", + "node_modules/caniuse-lite": { + "version": "1.0.30001663", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001663.tgz", + "integrity": "sha512-o9C3X27GLKbLeTYZ6HBOLU1tsAcBZsLis28wrVzddShCS16RujjHp9GDHKZqrB3meE0YjhawvMFsGb/igqiPzA==", "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.31.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - } + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" }, - "node_modules/@typescript-eslint/parser": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.26.1.tgz", - "integrity": "sha512-q7F3zSo/nU6YJpPJvQveVlIIzx9/wu75lr6oDbDzoeIRWxpoc/HQ43G4rmMoCc5my/3uSj2VEpg/D83LYZF5HQ==", + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "4.26.1", - "@typescript-eslint/types": "4.26.1", - "@typescript-eslint/typescript-estree": "4.26.1", - "debug": "^4.3.1" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=10" }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz", - "integrity": "sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg==", + "node_modules/char-regex": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", + "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, - "dependencies": { - "@typescript-eslint/types": "4.26.1", - "@typescript-eslint/visitor-keys": "4.26.1", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, + "license": "MIT", "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=10" } }, - "node_modules/@typescript-eslint/parser/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=8" } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.26.1.tgz", - "integrity": "sha512-TW1X2p62FQ8Rlne+WEShyd7ac2LA6o27S9i131W4NwDSfyeVlQWhw8ylldNNS8JG6oJB9Ha9Xyc+IUcqipvheQ==", + "node_modules/cjs-module-lexer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.1.tgz", + "integrity": "sha512-cuSVIHi9/9E/+821Qjdvngor+xpnlwnuwIyZOaLmHBVdXL+gP+I6QQB9VkO7RI77YIcTV+S1W9AreJ5eN63JBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { - "@typescript-eslint/types": "4.26.1", - "@typescript-eslint/visitor-keys": "4.26.1" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "node": ">=12" } }, - "node_modules/@typescript-eslint/types": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.26.1.tgz", - "integrity": "sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg==", + "node_modules/co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, + "license": "MIT", "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" + "iojs": ">= 1.0.0", + "node": ">= 0.12.0" } }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.31.0.tgz", - "integrity": "sha512-vxW149bXFXXuBrAak0eKHOzbcu9cvi6iNcJDzEtOkRwGHxJG15chiAQAwhLOsk+86p9GTr/TziYvw+H9kMaIgA==", + "node_modules/collect-v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", + "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^4.1.1", - "eslint-visitor-keys": "^1.1.0", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^6.3.0", - "tsutils": "^3.17.1" + "color-name": "~1.1.4" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "node": ">=7.0.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "bin": { - "semver": "bin/semver.js" - } + "license": "MIT" }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.1.tgz", - "integrity": "sha512-IGouNSSd+6x/fHtYRyLOM6/C+QxMDzWlDtN41ea+flWuSF9g02iqcIlX8wM53JkfljoIjP0U+yp7SiTS1onEkw==", + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", + "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "4.26.1", - "eslint-visitor-keys": "^2.0.0" + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "exit": "^0.1.2", + "graceful-fs": "^4.2.9", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "prompts": "^2.0.1" }, - "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "bin": { + "create-jest": "bin/create-jest.js" }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/abab": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", - "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", - "dev": true + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" }, - "node_modules/acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, - "bin": { - "acorn": "bin/acorn" + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" }, "engines": { - "node": ">=0.4.0" + "node": ">= 8" } }, - "node_modules/acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "node_modules/dedent": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.3.tgz", + "integrity": "sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==", "dev": true, + "license": "MIT", "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + "babel-plugin-macros": "^3.1.0" + }, + "peerDependenciesMeta": { + "babel-plugin-macros": { + "optional": true + } } }, - "node_modules/acorn-walk": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", - "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, - "engines": { - "node": ">=0.4.0" - } + "license": "MIT" }, - "node_modules/ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "node_modules/detect-newline": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", + "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/ansi-escapes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", "dev": true, - "dependencies": { - "type-fest": "^0.8.1" - }, + "license": "BSD-3-Clause", "engines": { - "node": ">=8" + "node": ">=0.3.1" } }, - "node_modules/ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", + "node_modules/diff-sequences": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-27.5.1.tgz", + "integrity": "sha512-k1gCAXAsNgLwEL+Y8Wvl+M6oEFj5bgazfZULpS5CneoPPXRaCCW7dm+q21Ky2VEE5X+VeRDBVg1Pcvvsr4TtNQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/electron-to-chromium": { + "version": "1.5.27", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.27.tgz", + "integrity": "sha512-o37j1vZqCoEgBuWWXLHQgTN/KDKe7zwpiY5CPeq2RvUqOyJw9xnrULzZAEVQ5p4h+zjMk7hgtOoPdnLxr7m/jw==", "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, + "license": "ISC" + }, + "node_modules/emittery": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", + "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, - "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, + "license": "MIT" + }, + "node_modules/enhanced-resolve": { + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz", + "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==", + "license": "MIT", "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" }, "engines": { - "node": ">= 8" + "node": ">=10.13.0" } }, - "node_modules/arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "node_modules/argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, + "license": "MIT", "dependencies": { - "sprintf-js": "~1.0.2" + "is-arrayish": "^0.2.1" } }, - "node_modules/argparse/node_modules/sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - }, - "node_modules/arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "node_modules/eslint": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, "engines": { - "node": ">=0.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, - "node_modules/array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", + "node_modules/eslint-plugin-jest": { + "version": "28.14.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-28.14.0.tgz", + "integrity": "sha512-P9s/qXSMTpRTerE2FQ0qJet2gKbcGyFTPAJipoKxmWqR6uuFqIqk8FuEfg5yBieOezVrEfAMZrEwJ6yEp+1MFQ==", "dev": true, + "license": "MIT", "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", - "is-string": "^1.0.5" + "@typescript-eslint/utils": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^16.10.0 || ^18.12.0 || >=20.0.0" + }, + "peerDependencies": { + "@typescript-eslint/eslint-plugin": "^6.0.0 || ^7.0.0 || ^8.0.0", + "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0", + "jest": "*" + }, + "peerDependenciesMeta": { + "@typescript-eslint/eslint-plugin": { + "optional": true + }, + "jest": { + "optional": true + } } }, - "node_modules/array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=8" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=0.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/array.prototype.flat": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", - "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" }, "engines": { - "node": ">= 0.4" - } - }, - "node_modules/asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "dependencies": { - "safer-buffer": "~2.1.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, "engines": { - "node": ">=0.8" + "node": ">=4" } }, - "node_modules/assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=0.10" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, "engines": { - "node": ">=8" + "node": ">=4.0" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "node_modules/atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "bin": { - "atob": "bin/atob.js" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">= 4.5.0" + "node": ">=4.0" } }, - "node_modules/aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { - "node": "*" + "node": ">=0.10.0" } }, - "node_modules/aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", - "dev": true - }, - "node_modules/babel-jest": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.0.1.tgz", - "integrity": "sha512-Z4GGmSNQ8pX3WS1O+6v3fo41YItJJZsVxG5gIQ+HuB/iuAQBJxMTHTwz292vuYws1LnHfwSRgoqI+nxdy/pcvw==", + "node_modules/execa": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", + "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" + "cross-spawn": "^7.0.3", + "get-stream": "^6.0.0", + "human-signals": "^2.1.0", + "is-stream": "^2.0.0", + "merge-stream": "^2.0.0", + "npm-run-path": "^4.0.1", + "onetime": "^5.1.2", + "signal-exit": "^3.0.3", + "strip-final-newline": "^2.0.0" }, "engines": { - "node": ">= 10.14.2" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, - "node_modules/babel-jest/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", + "node_modules/exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, "engines": { - "node": ">= 10.14.2" + "node": ">= 0.8.0" } }, - "node_modules/babel-jest/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "node_modules/expect": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", + "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, + "license": "MIT", "dependencies": { - "@types/istanbul-lib-report": "*" + "@jest/expect-utils": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "node_modules/expect/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "MIT", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/babel-plugin-istanbul": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", - "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", + "node_modules/expect/node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, - "dependencies": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^4.0.0", - "test-exclude": "^6.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/babel-plugin-jest-hoist": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.0.0.tgz", - "integrity": "sha512-+AuoehOrjt9irZL7DOt2+4ZaTM6dlu1s5TTS46JBa0/qem4dy7VNW3tMb96qeEqcIh20LD73TVNtmVEeymTG7w==", + "node_modules/expect/node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__traverse": "^7.0.6" + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/babel-preset-current-node-syntax": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz", - "integrity": "sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw==", + "node_modules/expect/node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/babel-preset-jest": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.0.0.tgz", - "integrity": "sha512-9ce+DatAa31DpR4Uir8g4Ahxs5K4W4L8refzt+qHWQANb6LhGcAEfIFgLUwk67oya2cCUd6t4eUMtO/z64ocNw==", + "node_modules/expect/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { - "babel-plugin-jest-hoist": "^26.0.0", - "babel-preset-current-node-syntax": "^0.1.2" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">= 10.14.2" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true + "node_modules/expect/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" + "bser": "2.1.1" } }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-descriptor": "^1.0.0" + "flat-cache": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=16.0.0" } }, - "node_modules/base/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { - "kind-of": "^6.0.0" + "to-regex-range": "^5.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/base/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { - "kind-of": "^6.0.0" + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/base/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "flatted": "^3.2.9", + "keyv": "^4.5.4" }, "engines": { - "node": ">=0.10.0" + "node": ">=16" } }, - "node_modules/bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, - "dependencies": { - "tweetnacl": "^0.14.3" - } + "license": "ISC" }, - "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", "dev": true, + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "dependencies": { - "fill-range": "^7.0.1" + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=6 <7 || >=8" } }, - "node_modules/browser-process-hrtime": { + "node_modules/fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "node_modules/browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true, - "dependencies": { - "resolve": "1.1.7" - } - }, - "node_modules/browser-resolve/node_modules/resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true + "license": "ISC" }, - "node_modules/bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, - "dependencies": { - "fast-json-stable-stringify": "2.x" - }, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">= 6" + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "dependencies": { - "node-int64": "^0.4.0" + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==", - "dev": true + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - }, + "license": "ISC", "engines": { - "node": ">=0.10.0" + "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=8.0.0" } }, - "node_modules/camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "node_modules/get-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", + "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { - "rsvp": "^4.8.4" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "node_modules/chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, + "license": "ISC", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "is-glob": "^4.0.3" }, "engines": { - "node": ">=8" + "node": ">=10.13.0" } }, - "node_modules/char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", + "node_modules/handlebars": { + "version": "4.7.9", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.9.tgz", + "integrity": "sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==", "dev": true, + "license": "MIT", "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" }, "engines": { - "node": ">=0.10.0" + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" } }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" } }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, + "license": "MIT" + }, + "node_modules/human-signals": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", + "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", + "dev": true, + "license": "Apache-2.0", "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" + "node": ">=10.17.0" } }, - "node_modules/collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, - "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 4" } }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" }, "engines": { - "node": ">=7.0.0" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/import-local": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, + "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "pkg-dir": "^4.2.0", + "resolve-cwd": "^3.0.0" + }, + "bin": { + "import-local-fixture": "fixtures/cli.js" }, "engines": { - "node": ">= 0.8" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "node_modules/contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=0.8.19" } }, - "node_modules/convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "license": "ISC", "dependencies": { - "safe-buffer": "~5.1.1" + "once": "^1.3.0", + "wrappy": "1" } }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "ISC" }, - "node_modules/core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "node_modules/cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true, + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.15.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz", + "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==", + "license": "MIT", "dependencies": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" + "hasown": "^2.0.2" }, "engines": { - "node": ">=4.8" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "node_modules/cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, - "dependencies": { - "cssom": "~0.3.6" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=0.10.0" } }, - "node_modules/cssstyle/node_modules/cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - }, - "node_modules/dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, - "dependencies": { - "assert-plus": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">=8" } }, - "node_modules/data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", + "node_modules/is-generator-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", + "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, - "dependencies": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=6" } }, - "node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "is-extglob": "^2.1.1" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">=0.10.0" } }, - "node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=0.12.0" } }, - "node_modules/decimal.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", - "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==", - "dev": true - }, - "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" }, - "node_modules/deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "node_modules/istanbul-lib-instrument": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "object-keys": "^1.0.12" + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^7.5.4" }, "engines": { - "node": ">= 0.4" + "node": ">=10" } }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/define-property/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "node_modules/istanbul-lib-source-maps": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", + "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "kind-of": "^6.0.0" + "debug": "^4.1.1", + "istanbul-lib-coverage": "^3.0.0", + "source-map": "^0.6.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/define-property/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "node_modules/istanbul-lib-source-maps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, - "node_modules/define-property/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "node_modules/javascript-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.1.0.tgz", + "integrity": "sha512-JVAfqNPTvNq3sB/VHQJAFxN/sPgKnsKrCwyRt15zwNCdrMMJDdcEOdubuy+DuJYYdm0ox1J4uzEuYKkN+9yhVg==", "dev": true, - "engines": { - "node": ">=0.4.0" - } + "license": "MIT" }, - "node_modules/detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", + "node_modules/jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", + "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/core": "^29.7.0", + "@jest/types": "^29.6.3", + "import-local": "^3.0.2", + "jest-cli": "^29.7.0" + }, + "bin": { + "jest": "bin/jest.js" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "node_modules/jest-changed-files": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", + "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, + "license": "MIT", + "dependencies": { + "execa": "^5.0.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0" + }, "engines": { - "node": ">=0.3.1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/diff-sequences": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", - "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", + "node_modules/jest-circus": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", + "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/expect": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "co": "^4.6.0", + "dedent": "^1.0.0", + "is-generator-fn": "^2.0.0", + "jest-each": "^29.7.0", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "p-limit": "^3.1.0", + "pretty-format": "^29.7.0", + "pure-rand": "^6.0.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, "engines": { - "node": ">= 8.3" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "node_modules/jest-circus/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/dir-glob/node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "node_modules/jest-circus/node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "node_modules/jest-circus/node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, + "license": "MIT", "dependencies": { - "esutils": "^2.0.2" + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=6.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", + "node_modules/jest-circus/node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, + "license": "MIT", "dependencies": { - "webidl-conversions": "^5.0.0" + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/domexception/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", + "node_modules/jest-circus/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "node_modules/jest-circus/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "dependencies": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "license": "MIT" }, - "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "node_modules/jest-cli": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", + "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, + "license": "MIT", "dependencies": { - "once": "^1.4.0" - } - }, - "node_modules/enhanced-resolve": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz", - "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==", - "dependencies": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" + "@jest/core": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "create-jest": "^29.7.0", + "exit": "^0.1.2", + "import-local": "^3.0.2", + "jest-config": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "yargs": "^17.3.1" + }, + "bin": { + "jest": "bin/jest.js" }, "engines": { - "node": ">=10.13.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" + }, + "peerDependenciesMeta": { + "node-notifier": { + "optional": true + } } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "node_modules/jest-config": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", + "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.1" + "@babel/core": "^7.11.6", + "@jest/test-sequencer": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-jest": "^29.7.0", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "deepmerge": "^4.2.2", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-circus": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-runner": "^29.7.0", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "micromatch": "^4.0.4", + "parse-json": "^5.2.0", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "strip-json-comments": "^3.1.1" }, "engines": { - "node": ">=8.6" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@types/node": "*", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "ts-node": { + "optional": true + } } }, - "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "node_modules/jest-config/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "is-arrayish": "^0.2.1" + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/es-abstract": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", - "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", + "node_modules/jest-config/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">= 0.4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "node_modules/jest-config/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, + "node_modules/jest-diff": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-27.5.1.tgz", + "integrity": "sha512-m0NvkX55LDt9T4mctTEgnZk3fmEg3NRYutvMPWM/0iPnkFj2wIeF45O1718cMSOFO1vINkqmxqD8vE37uTEbqw==", "dev": true, + "license": "MIT", "dependencies": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" + "chalk": "^4.0.0", + "diff-sequences": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" }, "engines": { - "node": ">= 0.4" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "node_modules/jest-diff/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.8.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, + "license": "MIT", "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" + "detect-newline": "^3.0.0" }, "engines": { - "node": ">=4.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/escodegen/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, - "optional": true, + "license": "MIT", + "dependencies": { + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" + }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", + "node_modules/jest-each/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, + "license": "MIT", "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=10" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint-import-resolver-node": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", - "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", + "node_modules/jest-each/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^2.6.9", - "resolve": "^1.13.1" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-import-resolver-node/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/jest-each/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "dependencies": { - "ms": "2.0.0" - } + "license": "MIT" }, - "node_modules/eslint-import-resolver-node/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/eslint-module-utils": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz", - "integrity": "sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q==", + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-module-utils/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, - "dependencies": { - "ms": "2.0.0" + "license": "MIT", + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-module-utils/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^2.0.0" + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "node_modules/eslint-module-utils/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", + "node_modules/jest-leak-detector": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, + "license": "MIT", "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-module-utils/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/eslint-module-utils/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "node_modules/jest-leak-detector/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint-module-utils/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", + "node_modules/jest-leak-detector/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { - "p-limit": "^1.1.0" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-module-utils/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", + "node_modules/jest-leak-detector/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "engines": { - "node": ">=4" - } + "license": "MIT" }, - "node_modules/eslint-module-utils/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "node_modules/jest-matcher-utils": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-27.5.1.tgz", + "integrity": "sha512-z2uTx/T6LBaCoNWNFWwChLBKYxTMcGBRjAt+2SbP929/Fflb9aa5LGma654Rz8z9HLxsrUaYzxE9T/EFIL/PAw==", "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.0.0", + "jest-diff": "^27.5.1", + "jest-get-type": "^27.5.1", + "pretty-format": "^27.5.1" + }, "engines": { - "node": ">=4" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/eslint-module-utils/node_modules/pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", + "node_modules/jest-matcher-utils/node_modules/jest-get-type": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-27.5.1.tgz", + "integrity": "sha512-2KY95ksYSaK7DMBWQn6dQz3kqAf3BB64y2udeG+hv4KfSOb9qwcYQstTJc1KCbsix+wLZWZYN8t7nwX3GOBLRw==", "dev": true, - "dependencies": { - "find-up": "^2.1.0" - }, + "license": "MIT", "engines": { - "node": ">=4" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/eslint-plugin-import": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz", - "integrity": "sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw==", + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, + "license": "MIT", "dependencies": { - "array-includes": "^3.0.3", - "array.prototype.flat": "^1.2.1", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.1", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.0", - "read-pkg-up": "^2.0.0", - "resolve": "^1.12.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-import/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/jest-message-util/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "ms": "2.0.0" + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint-plugin-import/node_modules/doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", + "node_modules/jest-message-util/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-plugin-import/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "node_modules/jest-message-util/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" }, - "node_modules/eslint-plugin-jest": { - "version": "23.8.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-23.8.2.tgz", - "integrity": "sha512-xwbnvOsotSV27MtAe7s8uGWOori0nUsrXh2f1EnpmXua8sDfY6VZhHAhHg2sqK7HBNycRQExF074XSZ7DvfoFg==", + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/experimental-utils": "^2.5.0" + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" + "license": "MIT", + "engines": { + "node": ">=6" + }, + "peerDependencies": { + "jest-resolve": "*" }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "node_modules/jest-resolve": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, + "license": "MIT", "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", + "jest-util": "^29.7.0", + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" }, "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, + "license": "MIT", + "dependencies": { + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" + }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.10.4" + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, + "license": "MIT", "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" }, "engines": { - "node": ">= 8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "node_modules/jest-snapshot/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "node_modules/jest-snapshot/node_modules/diff-sequences": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", + "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint/node_modules/globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", + "node_modules/jest-snapshot/node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, + "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint/node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "node_modules/jest-snapshot/node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, + "license": "MIT", "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">= 0.8.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint/node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "node_modules/jest-snapshot/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/eslint/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint/node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "node_modules/jest-snapshot/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, - "engines": { - "node": ">= 0.8.0" - } + "license": "MIT" }, - "node_modules/eslint/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { - "node": ">=10" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, + "license": "MIT", "dependencies": { - "shebang-regex": "^3.0.0" + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/eslint/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "node_modules/jest-validate/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" - } - }, - "node_modules/eslint/node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "dependencies": { - "prelude-ls": "^1.2.1" + "node": ">=10" }, - "engines": { - "node": ">= 0.8.0" + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/eslint/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -4043,16860 +4302,1596 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "node_modules/jest-validate/node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" }, "engines": { - "node": ">= 8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "node_modules/jest-validate/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true, + "license": "MIT" + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", + "dev": true, + "license": "MIT", "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" + "license": "MIT", + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" }, "engines": { - "node": ">=4" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { - "estraverse": "^5.1.0" + "has-flag": "^4.0.0" }, "engines": { - "node": ">=0.10" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "dev": true, - "engines": { - "node": ">=4.0" - } + "license": "MIT" }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { - "estraverse": "^5.2.0" + "argparse": "^2.0.1" }, - "engines": { - "node": ">=4.0" + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/esrecurse/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "node_modules/jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, "engines": { - "node": ">=4.0" + "node": ">=4" } }, - "node_modules/estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, - "engines": { - "node": ">=4.0" - } + "license": "MIT" }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/exec-sh": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", - "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", - "dev": true + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" }, - "node_modules/execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "dependencies": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" }, "engines": { "node": ">=6" } }, - "node_modules/exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, - "engines": { - "node": ">= 0.8.0" + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" } }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" + "json-buffer": "3.0.1" } }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "node_modules/kleur": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", + "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, - "dependencies": { - "ms": "2.0.0" + "license": "MIT", + "engines": { + "node": ">=6" } }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" }, - "node_modules/expect": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-25.5.0.tgz", - "integrity": "sha512-w7KAXo0+6qqZZhovCaBVPSIqQp7/UTcx4M9uKt2m6pd2VB1voyC8JizLRqeEqud3AAVP02g+hbErDu5gu64tlA==", + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^25.5.0", - "ansi-styles": "^4.0.0", - "jest-get-type": "^25.2.6", - "jest-matcher-utils": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-regex-util": "^25.2.6" + "p-locate": "^5.0.0" }, "engines": { - "node": ">= 8.3" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true + "node_modules/lodash.memoize": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", + "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" }, - "node_modules/extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" + "yallist": "^3.0.2" } }, - "node_modules/extend-shallow/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "node_modules/lua-types": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/lua-types/-/lua-types-2.14.1.tgz", + "integrity": "sha512-+EbnTjF+Bbfp5xmpfT0O9AZgC5J6Oq6jRnZei3HLb0pbWuCRHmdnIVRPIgEpUncWvHq21DHB2qTHhA4xGceHHg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lua-wasm-bindings": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/lua-wasm-bindings/-/lua-wasm-bindings-0.5.3.tgz", + "integrity": "sha512-GnGSkZmYA2VzkNV/xkwOSoxwNsqEk/EcX6uvRLKQw0pCAxCauTymRO2KmJUIrG/psV3cloaS4Cjsr39iCRkrpQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" + "@types/semver": "^7.3.9", + "semver": "^7.3.7" } }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, + "license": "MIT", "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" + "semver": "^7.5.3" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true, + "license": "ISC" + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "tmpl": "1.0.5" } }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "is-extendable": "^0.1.0" + "braces": "^3.0.3", + "picomatch": "^2.3.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8.6" } }, - "node_modules/extglob/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/extglob/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, + "license": "ISC", "dependencies": { - "kind-of": "^6.0.0" + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/extglob/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "engines": [ - "node >=0.6.0" - ] + "license": "MIT" }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" }, - "node_modules/fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", "dev": true, - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "dev": true, + "license": "MIT" }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "node_modules/node-releases": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", + "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==", + "dev": true, + "license": "MIT" }, - "node_modules/fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, - "dependencies": { - "reusify": "^1.0.4" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "node_modules/fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", + "node_modules/npm-run-path": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", + "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, + "license": "MIT", "dependencies": { - "bser": "2.1.1" + "path-key": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { - "flat-cache": "^3.0.4" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" + "wrappy": "1" } }, - "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { - "to-regex-range": "^5.0.1" + "mimic-fn": "^2.1.0" }, "engines": { - "node": ">=8" + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, + "license": "MIT", "dependencies": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" }, "engines": { - "node": ">=8" + "node": ">= 0.8.0" } }, - "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" + "yocto-queue": "^0.1.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "node_modules/for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, + "license": "MIT", "engines": { - "node": "*" + "node": ">=6" } }, - "node_modules/form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" + "callsites": "^3.0.0" }, "engines": { - "node": ">= 0.12" + "node": ">=6" } }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, + "license": "MIT", "dependencies": { - "map-cache": "^0.2.2" + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, + "license": "MIT", "engines": { - "node": ">=6 <7 || >=8" + "node": ">=8" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "node_modules/fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, - "optional": true, - "os": [ - "darwin" - ], + "license": "MIT", "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + "node": ">=0.10.0" } }, - "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "node_modules/gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6.9.0" + "node": ">=8" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "license": "MIT", "engines": { - "node": "6.* || 8.* || >= 10.*" + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8.0.0" + "node": ">= 6" } }, - "node_modules/get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "node_modules/pkg-dir": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", + "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, + "license": "MIT", "dependencies": { - "pump": "^3.0.0" + "find-up": "^4.0.0" }, "engines": { - "node": ">=6" + "node": ">=8" } }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "node_modules/pkg-dir/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { - "assert-plus": "^1.0.0" + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" } }, - "node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - } - }, - "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", - "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" + "p-try": "^2.0.0" }, "engines": { - "node": ">=10" + "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/globby/node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" - }, - "node_modules/growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true, - "optional": true - }, - "node_modules/har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "dependencies": { - "function-bind": "^1.1.1" + "p-limit": "^2.2.0" }, - "engines": { - "node": ">= 0.4.0" - } - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, "engines": { "node": ">=8" } }, - "node_modules/has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 0.8.0" } }, - "node_modules/has-values/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", "dev": true, - "dependencies": { - "kind-of": "^3.0.2" + "license": "MIT", + "bin": { + "prettier": "bin-prettier.js" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" + "node": ">=10.13.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" } }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-buffer": "^1.1.5" + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" }, "engines": { - "node": ">=0.10.0" + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" } }, - "node_modules/hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "node_modules/html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, - "dependencies": { - "whatwg-encoding": "^1.0.5" - }, + "license": "MIT", "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "node_modules/http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "node_modules/prompts": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", + "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, + "license": "MIT", "dependencies": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" + "kleur": "^3.0.3", + "sisteransi": "^1.0.5" }, "engines": { - "node": ">=0.8", - "npm": ">=1.3.7" - } - }, - "node_modules/human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true, - "engines": { - "node": ">=8.12.0" + "node": ">= 6" } }, - "node_modules/iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=6" } }, - "node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/pure-rand": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, - "engines": { - "node": ">= 4" - } + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/dubzzz" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fast-check" + } + ], + "license": "MIT" }, - "node_modules/import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - } + "license": "MIT" }, - "node_modules/import-fresh/node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=0.10.0" } }, - "node_modules/import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", - "dev": true, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "license": "MIT", "dependencies": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { - "import-local-fixture": "fixtures/cli.js" + "resolve": "bin/resolve" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true, - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "node_modules/ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true, - "engines": { - "node": ">=4" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "node_modules/resolve-cwd": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", + "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, + "license": "MIT", "dependencies": { - "kind-of": "^3.0.2" + "resolve-from": "^5.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "node_modules/resolve-cwd/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "node_modules/is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "node_modules/is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=4" } }, - "node_modules/is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", + "node_modules/resolve.exports": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", + "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", "dev": true, - "dependencies": { - "ci-info": "^2.0.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, - "node_modules/is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", - "dependencies": { - "has": "^1.0.3" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-docker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", - "dev": true, - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-potential-custom-element-name": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", - "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", - "dev": true - }, - "node_modules/is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, - "dependencies": { - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "dependencies": { - "has-symbols": "^1.0.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "node_modules/is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "optional": true, - "dependencies": { - "is-docker": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "node_modules/istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "dependencies": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "dependencies": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "dependencies": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-source-maps/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "dependencies": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/javascript-stringify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.0.1.tgz", - "integrity": "sha512-yV+gqbd5vaOYjqlbk16EG89xB5udgjqQF3C5FAORDg4f/IS1Yc5ERCv5e/57yBcfJYw05V5JyIXabhwb75Xxow==", - "dev": true - }, - "node_modules/jest": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.0.1.tgz", - "integrity": "sha512-29Q54kn5Bm7ZGKIuH2JRmnKl85YRigp0o0asTc6Sb6l2ch1DCXIeZTLLFy9ultJvhkTqbswF5DEx4+RlkmCxWg==", - "dev": true, - "dependencies": { - "@jest/core": "^26.0.1", - "import-local": "^3.0.2", - "jest-cli": "^26.0.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-changed-files": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.0.1.tgz", - "integrity": "sha512-q8LP9Sint17HaE2LjxQXL+oYWW/WeeXMPE2+Op9X3mY8IEGFVc14xRxFjUuXUbcPAlDLhtWdIEt59GdQbn76Hw==", - "dev": true, - "dependencies": { - "@jest/types": "^26.0.1", - "execa": "^4.0.0", - "throat": "^5.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-changed-files/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-changed-files/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-changed-files/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-changed-files/node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/jest-changed-files/node_modules/execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "dependencies": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/execa?sponsor=1" - } - }, - "node_modules/jest-changed-files/node_modules/get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "dependencies": { - "pump": "^3.0.0" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-changed-files/node_modules/is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/node_modules/npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "dependencies": { - "path-key": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-changed-files/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/jest-circus": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-25.1.0.tgz", - "integrity": "sha512-Axlcr2YMxVarMW4SiZhCFCjNKhdF4xF9AIdltyutQOKyyDT795Kl/fzI95O0l8idE51Npj2wDj5GhrV7uEoEJA==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", - "chalk": "^3.0.0", - "co": "^4.6.0", - "expect": "^25.1.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^25.1.0", - "jest-matcher-utils": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-snapshot": "^25.1.0", - "jest-util": "^25.1.0", - "pretty-format": "^25.1.0", - "stack-utils": "^1.0.1", - "throat": "^5.0.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-config": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.0.1.tgz", - "integrity": "sha512-9mWKx2L1LFgOXlDsC4YSeavnblN6A4CPfXFiobq+YYLaBMymA/SczN7xYTSmLaEYHZOcB98UdoN4m5uNt6tztg==", - "dev": true, - "dependencies": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.0.1", - "@jest/types": "^26.0.1", - "babel-jest": "^26.0.1", - "chalk": "^4.0.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.0.1", - "jest-environment-node": "^26.0.1", - "jest-get-type": "^26.0.0", - "jest-jasmine2": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "micromatch": "^4.0.2", - "pretty-format": "^26.0.1" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-config/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-config/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-config/node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-config/node_modules/jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-config/node_modules/jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-config/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-config/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-config/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/jest-config/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/jest-config/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-config/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", - "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==", - "dev": true, - "dependencies": { - "chalk": "^3.0.0", - "diff-sequences": "^25.2.6", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-docblock": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", - "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", - "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-each": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.5.0.tgz", - "integrity": "sha512-QBogUxna3D8vtiItvn54xXde7+vuzqRrEeaw8r1s+1TG9eZLVJE5ZkKoSUlqFwRjnlaA4hyKGiu9OlkFIuKnjA==", - "dev": true, - "dependencies": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "jest-get-type": "^25.2.6", - "jest-util": "^25.5.0", - "pretty-format": "^25.5.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-environment-jsdom": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.0.1.tgz", - "integrity": "sha512-u88NJa3aptz2Xix2pFhihRBAatwZHWwSiRLBDBQE1cdJvDjPvv7ZGA0NQBxWwDDn7D0g1uHqxM8aGgfA9Bx49g==", - "dev": true, - "dependencies": { - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1", - "jsdom": "^16.2.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-environment-jsdom/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "node_modules/jest-environment-jsdom/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-environment-jsdom/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-jsdom/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-jsdom/node_modules/jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-jsdom/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-jsdom/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/jest-environment-jsdom/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/jest-environment-jsdom/node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-environment-node": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.0.1.tgz", - "integrity": "sha512-4FRBWcSn5yVo0KtNav7+5NH5Z/tEgDLp7VRQVS5tCouWORxj+nI+1tOLutM07Zb2Qi7ja+HEDoOUkjBSWZg/IQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-node/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-node/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-environment-node/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "node_modules/jest-environment-node/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-environment-node/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-environment-node/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-node/node_modules/jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-node/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-environment-node/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/jest-environment-node/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/jest-environment-node/node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-get-type": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", - "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", - "dev": true, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-haste-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", - "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7" - }, - "engines": { - "node": ">= 10.14.2" - }, - "optionalDependencies": { - "fsevents": "^2.1.2" - } - }, - "node_modules/jest-haste-map/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-haste-map/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-haste-map/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-haste-map/node_modules/jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-haste-map/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.0.1.tgz", - "integrity": "sha512-ILaRyiWxiXOJ+RWTKupzQWwnPaeXPIoLS5uW41h18varJzd9/7I0QJGqg69fhTT1ev9JpSSo9QtalriUN0oqOg==", - "dev": true, - "dependencies": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.0.1", - "@jest/source-map": "^26.0.0", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^26.0.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^26.0.1", - "jest-matcher-utils": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "pretty-format": "^26.0.1", - "throat": "^5.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "dependencies": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-jasmine2/node_modules/@types/prettier": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", - "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", - "dev": true - }, - "node_modules/jest-jasmine2/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "node_modules/jest-jasmine2/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-jasmine2/node_modules/diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/node_modules/expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-each": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", - "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-jasmine2/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-jasmine2/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/jest-jasmine2/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/jest-jasmine2/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-jasmine2/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-jasmine2/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-jasmine2/node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-leak-detector": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.0.1.tgz", - "integrity": "sha512-93FR8tJhaYIWrWsbmVN1pQ9ZNlbgRpfvrnw5LmgLRX0ckOJ8ut/I35CL7awi2ecq6Ca4lL59bEK9hr7nqoHWPA==", - "dev": true, - "dependencies": { - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-leak-detector/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-leak-detector/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-leak-detector/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-leak-detector/node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-leak-detector/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/jest-leak-detector/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/jest-matcher-utils": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz", - "integrity": "sha512-VWI269+9JS5cpndnpCwm7dy7JtGQT30UHfrnM3mXl22gHGt/b7NkjBqXfbhZ8V4B7ANUsjK18PlSBmG0YH7gjw==", - "dev": true, - "dependencies": { - "chalk": "^3.0.0", - "jest-diff": "^25.5.0", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-mock": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.5.0.tgz", - "integrity": "sha512-eXWuTV8mKzp/ovHc5+3USJMYsTBhyQ+5A1Mak35dey/RG8GlM4YWVylZuGgVXinaW6tpvk/RSecmF37FKUlpXA==", - "dev": true, - "dependencies": { - "@jest/types": "^25.5.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", - "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", - "dev": true, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-resolve": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.5.1.tgz", - "integrity": "sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ==", - "dev": true, - "dependencies": { - "@jest/types": "^25.5.0", - "browser-resolve": "^1.11.3", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.1", - "read-pkg-up": "^7.0.1", - "realpath-native": "^2.0.0", - "resolve": "^1.17.0", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.0.1.tgz", - "integrity": "sha512-9d5/RS/ft0vB/qy7jct/qAhzJsr6fRQJyGAFigK3XD4hf9kIbEH5gks4t4Z7kyMRhowU6HWm/o8ILqhaHdSqLw==", - "dev": true, - "dependencies": { - "@jest/types": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.0.1" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/@types/prettier": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", - "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", - "dev": true - }, - "node_modules/jest-resolve-dependencies/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "node_modules/jest-resolve-dependencies/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/jest-resolve-dependencies/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-resolve-dependencies/node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-resolve/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-resolve/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.0.1.tgz", - "integrity": "sha512-CApm0g81b49Znm4cZekYQK67zY7kkB4umOlI2Dx5CwKAzdgw75EN+ozBHRvxBzwo1ZLYZ07TFxkaPm+1t4d8jA==", - "dev": true, - "dependencies": { - "@jest/console": "^26.0.1", - "@jest/environment": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-config": "^26.0.1", - "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.0.1", - "jest-jasmine2": "^26.0.1", - "jest-leak-detector": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-resolve": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", - "source-map-support": "^0.5.6", - "throat": "^5.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runner/node_modules/@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runner/node_modules/@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runner/node_modules/@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runner/node_modules/@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "dependencies": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runner/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runner/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-runner/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runner/node_modules/jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runner/node_modules/jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runner/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runner/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-runner/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/jest-runner/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/jest-runner/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-runtime": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.0.1.tgz", - "integrity": "sha512-Ci2QhYFmANg5qaXWf78T2Pfo6GtmIBn2rRaLnklRyEucmPccmCKvS9JPljcmtVamsdMmkyNkVFb9pBTD6si9Lw==", - "dev": true, - "dependencies": { - "@jest/console": "^26.0.1", - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/globals": "^26.0.1", - "@jest/source-map": "^26.0.0", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^26.0.1", - "jest-haste-map": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.3.1" - }, - "bin": { - "jest-runtime": "bin/jest-runtime.js" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "dependencies": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "dependencies": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-runtime/node_modules/@types/prettier": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", - "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", - "dev": true - }, - "node_modules/jest-runtime/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-runtime/node_modules/parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-runtime/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/jest-runtime/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/jest-runtime/node_modules/read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "dependencies": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "dependencies": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/read-pkg/node_modules/type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-runtime/node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-serializer": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", - "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", - "dev": true, - "dependencies": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-snapshot": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.5.1.tgz", - "integrity": "sha512-C02JE1TUe64p2v1auUJ2ze5vcuv32tkv9PyhEb318e8XOKF7MOyXdJ7kdjbvrp3ChPLU2usI7Rjxs97Dj5P0uQ==", - "dev": true, - "dependencies": { - "@babel/types": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/prettier": "^1.19.0", - "chalk": "^3.0.0", - "expect": "^25.5.0", - "graceful-fs": "^4.2.4", - "jest-diff": "^25.5.0", - "jest-get-type": "^25.2.6", - "jest-matcher-utils": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-resolve": "^25.5.1", - "make-dir": "^3.0.0", - "natural-compare": "^1.4.0", - "pretty-format": "^25.5.0", - "semver": "^6.3.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "dependencies": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/jest-validate": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.0.1.tgz", - "integrity": "sha512-u0xRc+rbmov/VqXnX3DlkxD74rHI/CfS5xaV2VpeaVySjbb1JioNVOyly5b56q2l9ZKe7bVG5qWmjfctkQb0bA==", - "dev": true, - "dependencies": { - "@jest/types": "^26.0.1", - "camelcase": "^6.0.0", - "chalk": "^4.0.0", - "jest-get-type": "^26.0.0", - "leven": "^3.1.0", - "pretty-format": "^26.0.1" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-validate/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-validate/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-validate/node_modules/camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-validate/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/jest-validate/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/jest-watcher": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.0.1.tgz", - "integrity": "sha512-pdZPydsS8475f89kGswaNsN3rhP6lnC3/QDCppP7bg1L9JQz7oU9Mb/5xPETk1RHDCWeqmVC47M4K5RR7ejxFw==", - "dev": true, - "dependencies": { - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^26.0.1", - "string-length": "^4.0.1" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-watcher/node_modules/@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-watcher/node_modules/@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "dependencies": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-watcher/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-watcher/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest-watcher/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-watcher/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest-watcher/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/jest-watcher/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/jest-watcher/node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "dependencies": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">= 10.13.0" - } - }, - "node_modules/jest/node_modules/@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest/node_modules/@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "dependencies": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/jest/node_modules/@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "node_modules/jest/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest/node_modules/jest-cli": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.0.1.tgz", - "integrity": "sha512-pFLfSOBcbG9iOZWaMK4Een+tTxi/Wcm34geqZEqrst9cZDkTQ1LZ2CnBrTlHWuYAiTMFr0EQeK52ScyFU8wK+w==", - "dev": true, - "dependencies": { - "@jest/core": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "prompts": "^2.0.1", - "yargs": "^15.3.1" - }, - "bin": { - "jest": "bin/jest.js" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest/node_modules/jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/jest/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/jest/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/jest/node_modules/stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "node_modules/js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "dependencies": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "node_modules/jsdom": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.2.2.tgz", - "integrity": "sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg==", - "dev": true, - "dependencies": { - "abab": "^2.0.3", - "acorn": "^7.1.1", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.2.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", - "domexception": "^2.0.1", - "escodegen": "^1.14.1", - "html-encoding-sniffer": "^2.0.1", - "is-potential-custom-element-name": "^1.0.0", - "nwsapi": "^2.2.0", - "parse5": "5.1.1", - "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.0.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0", - "ws": "^7.2.3", - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true, - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "node_modules/json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "node_modules/json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "node_modules/json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "dependencies": { - "minimist": "^1.2.5" - }, - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "node_modules/load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/load-json-file/node_modules/strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "dependencies": { - "p-locate": "^4.1.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "node_modules/lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "node_modules/lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "node_modules/lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/lua-types": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/lua-types/-/lua-types-2.8.0.tgz", - "integrity": "sha512-FJY32giHIqD/XW1XGkJnl8XotXIJsJ2M42fj9A2UudttWA6orJioToW1OpgPdayTr+S1/oTO7i+hfBY3UVG8Fg==", - "dev": true - }, - "node_modules/lua-wasm-bindings": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/lua-wasm-bindings/-/lua-wasm-bindings-0.2.2.tgz", - "integrity": "sha512-Z2v3An8jEvYbhUJ/gYxNd65ZBbaRyPMmUaleDoOhzJSIGrMgjURvy1EtHtgRGTzdtpmKtHA+YYKDrSZBE9g/ig==", - "dev": true - }, - "node_modules/make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "dependencies": { - "semver": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/make-dir/node_modules/semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true, - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "dependencies": { - "tmpl": "1.0.x" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "dependencies": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", - "dev": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "dev": true, - "dependencies": { - "mime-db": "1.44.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/mixin-deep/node_modules/is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.4" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "node_modules/nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node_modules/node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node_modules/node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/node-notifier": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.1.tgz", - "integrity": "sha512-VkzhierE7DBmQEElhTGJIoiZa1oqRijOtgOlsXg32KrJRXsPy0NXFBqWGW/wTswnJlDCs5viRYaqWguqzsKcmg==", - "dev": true, - "optional": true, - "dependencies": { - "growly": "^1.3.0", - "is-wsl": "^2.1.1", - "semver": "^7.2.1", - "shellwords": "^0.1.1", - "uuid": "^7.0.3", - "which": "^2.0.2" - } - }, - "node_modules/node-notifier/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "optional": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-notifier/node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "optional": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "dependencies": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "dependencies": { - "path-key": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, - "node_modules/oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, - "node_modules/object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, - "node_modules/onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "dependencies": { - "mimic-fn": "^2.1.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-each-series": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", - "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "dependencies": { - "p-try": "^2.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "dependencies": { - "p-limit": "^2.2.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true - }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "node_modules/path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "dependencies": { - "pify": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "node_modules/picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", - "dev": true, - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "dependencies": { - "node-modules-regexp": "^1.0.0" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "dependencies": { - "find-up": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "dependencies": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - }, - "engines": { - "node": ">= 8.3" - } - }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/prompts": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", - "integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==", - "dev": true, - "dependencies": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true, - "engines": { - "node": ">=0.6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "node_modules/read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "dependencies": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "dependencies": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "dependencies": { - "locate-path": "^2.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "dependencies": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "dependencies": { - "p-try": "^1.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "dependencies": { - "p-limit": "^1.1.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/realpath-native": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", - "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "node_modules/repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "dependencies": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/request-promise-core": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", - "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", - "dev": true, - "dependencies": { - "lodash": "^4.17.15" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/request-promise-native": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", - "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", - "dev": true, - "dependencies": { - "request-promise-core": "1.1.3", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/request-promise-native/node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/request/node_modules/tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "dependencies": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/request/node_modules/uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true, - "bin": { - "uuid": "bin/uuid" - } - }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "node_modules/resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "dependencies": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "dependencies": { - "resolve-from": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true, - "engines": { - "node": ">=0.12" - } - }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - } - }, - "node_modules/rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "dev": true, - "engines": { - "node": "6.* || >= 7.*" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "dependencies": { - "ret": "~0.1.10" - } - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "node_modules/sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "dependencies": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "bin": { - "sane": "src/cli.js" - }, - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, - "node_modules/sane/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/sane/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sane/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "dependencies": { - "xmlchars": "^2.2.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true, - "bin": { - "semver": "bin/semver" - } - }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "dependencies": { - "shebang-regex": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true, - "optional": true - }, - "node_modules/signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "node_modules/sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "dependencies": { - "kind-of": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==", - "engines": { - "node": ">= 8" - } - }, - "node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "dev": true, - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" - } - }, - "node_modules/source-map-support": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz", - "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", - "dev": true, - "dependencies": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - } - }, - "node_modules/source-map-support/node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true - }, - "node_modules/spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", - "dev": true, - "dependencies": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true - }, - "node_modules/spdx-expression-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", - "dev": true, - "dependencies": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" - } - }, - "node_modules/spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true - }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", - "dev": true, - "dependencies": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stack-utils": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", - "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", - "dev": true, - "dependencies": { - "escape-string-regexp": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/stack-utils/node_modules/escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", - "dev": true, - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/string-length": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", - "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", - "dev": true, - "dependencies": { - "char-regex": "^1.0.2", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", - "dev": true, - "dependencies": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-bom": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", - "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/strip-final-newline": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", - "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-hyperlinks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", - "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "node_modules/table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", - "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "node_modules/tapable": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", - "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==", - "engines": { - "node": ">=6" - } - }, - "node_modules/terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "dependencies": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", - "dev": true, - "dependencies": { - "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "node_modules/throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "dev": true - }, - "node_modules/tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", - "dev": true, - "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", - "dev": true, - "dependencies": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/tr46": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", - "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", - "dev": true, - "dependencies": { - "punycode": "^2.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-jest": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.3.0.tgz", - "integrity": "sha512-Jq2uKfx6bPd9+JDpZNMBJMdMQUC3sJ08acISj8NXlVgR2d5OqslEHOR2KHMgwymu8h50+lKIm0m0xj/ioYdW2Q==", - "dev": true, - "dependencies": { - "@types/jest": "26.x", - "bs-logger": "0.x", - "buffer-from": "1.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "26.x", - "json5": "2.x", - "lodash.memoize": "4.x", - "make-error": "1.x", - "mkdirp": "1.x", - "semver": "7.x", - "yargs-parser": "18.x" - }, - "bin": { - "ts-jest": "cli.js" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/ts-jest/node_modules/@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/ts-jest/node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-report": "*" - } - }, - "node_modules/ts-jest/node_modules/@types/jest": { - "version": "26.0.23", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz", - "integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==", - "dev": true, - "dependencies": { - "jest-diff": "^26.0.0", - "pretty-format": "^26.0.0" - } - }, - "node_modules/ts-jest/node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ts-jest/node_modules/diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/ts-jest/node_modules/jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/ts-jest/node_modules/jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/ts-jest/node_modules/jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - }, - "engines": { - "node": ">= 10.14.2" - } - }, - "node_modules/ts-jest/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-jest/node_modules/pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "dependencies": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/ts-jest/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "node_modules/ts-jest/node_modules/semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/ts-node": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.6.2.tgz", - "integrity": "sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg==", - "dev": true, - "dependencies": { - "arg": "^4.1.0", - "diff": "^4.0.1", - "make-error": "^1.1.1", - "source-map-support": "^0.5.6", - "yn": "3.1.1" - }, - "bin": { - "ts-node": "dist/bin.js", - "ts-script": "dist/script.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/tslib": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.0.tgz", - "integrity": "sha512-BmndXUtiTn/VDDrJzQE7Mm22Ix3PxgLltW9bSNLoeCY31gnG2OPx0QqJnuc9oMIKioYrz487i6K9o4Pdn0j+Kg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, - "node_modules/tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "dependencies": { - "safe-buffer": "^5.0.1" - }, - "engines": { - "node": "*" - } - }, - "node_modules/tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/type-detect": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", - "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", - "dev": true, - "dependencies": { - "is-typedarray": "^1.0.0" - } - }, - "node_modules/typescript": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", - "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=4.2.0" - } - }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", - "dev": true, - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", - "dev": true, - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", - "dev": true, - "optional": true, - "bin": { - "uuid": "dist/bin/uuid" - } - }, - "node_modules/v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", - "dev": true - }, - "node_modules/v8-to-istanbul": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz", - "integrity": "sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==", - "dev": true, - "dependencies": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - }, - "engines": { - "node": "8.x.x || >=10.10.0" - } - }, - "node_modules/validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", - "dev": true, - "dependencies": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" - } - }, - "node_modules/verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "dev": true, - "engines": [ - "node >=0.6.0" - ], - "dependencies": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" - } - }, - "node_modules/w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", - "dev": true, - "dependencies": { - "browser-process-hrtime": "^1.0.0" - } - }, - "node_modules/w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", - "dev": true, - "dependencies": { - "xml-name-validator": "^3.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", - "dev": true, - "dependencies": { - "makeerror": "1.0.x" - } - }, - "node_modules/webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true, - "engines": { - "node": ">=10.4" - } - }, - "node_modules/whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, - "dependencies": { - "iconv-lite": "0.4.24" - } - }, - "node_modules/whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "node_modules/whatwg-url": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz", - "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==", - "dev": true, - "dependencies": { - "lodash.sortby": "^4.7.0", - "tr46": "^2.0.2", - "webidl-conversions": "^5.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/whatwg-url/node_modules/webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", - "dev": true, - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "which": "bin/which" - } - }, - "node_modules/which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "node_modules/word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true - }, - "node_modules/write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", - "dev": true, - "dependencies": { - "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" - } - }, - "node_modules/ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", - "dev": true, - "engines": { - "node": ">=8.3.0" - }, - "peerDependencies": { - "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" - }, - "peerDependenciesMeta": { - "bufferutil": { - "optional": true - }, - "utf-8-validate": { - "optional": true - } - } - }, - "node_modules/xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "node_modules/xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "node_modules/y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", - "dev": true, - "dependencies": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", - "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", - "dev": true, - "dependencies": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/yn": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", - "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true, - "engines": { - "node": ">=6" - } - } - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.13.tgz", - "integrity": "sha512-HV1Cm0Q3ZrpCR93tkWOYiuYIgLxZXZFVG2VgK+MBWjUqZTundupbfx2aXarXuw5Ko5aMcjtJgbSs4vUGBS5v6g==", - "dev": true, - "requires": { - "@babel/highlight": "^7.12.13" - } - }, - "@babel/core": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.9.6.tgz", - "integrity": "sha512-nD3deLvbsApbHAHttzIssYqgb883yU/d9roe4RZymBCDaZryMJDbptVpEpeQuRh4BJ+SYI8le9YGxKvFEvl1Wg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.8.3", - "@babel/generator": "^7.9.6", - "@babel/helper-module-transforms": "^7.9.0", - "@babel/helpers": "^7.9.6", - "@babel/parser": "^7.9.6", - "@babel/template": "^7.8.6", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6", - "convert-source-map": "^1.7.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.1", - "json5": "^2.1.2", - "lodash": "^4.17.13", - "resolve": "^1.3.2", - "semver": "^5.4.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/generator": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.14.3.tgz", - "integrity": "sha512-bn0S6flG/j0xtQdz3hsjJ624h3W0r3llttBMfyHX3YrZ/KtLYr15bjA0FXkgW7FpvrDuTuElXeVjiKlYRpnOFA==", - "dev": true, - "requires": { - "@babel/types": "^7.14.2", - "jsesc": "^2.5.1", - "source-map": "^0.5.0" - }, - "dependencies": { - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } - } - }, - "@babel/helper-function-name": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.14.2.tgz", - "integrity": "sha512-NYZlkZRydxw+YT56IlhIcS8PAhb+FEUiOzuhFTfqDyPmzAhRge6ua0dQYT/Uh0t/EDHq05/i+e5M2d4XvjgarQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.12.13", - "@babel/template": "^7.12.13", - "@babel/types": "^7.14.2" - } - }, - "@babel/helper-get-function-arity": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.13.tgz", - "integrity": "sha512-DjEVzQNz5LICkzN0REdpD5prGoidvbdYk1BVgRUOINaWJP2t6avB27X1guXK1kXNrX0WMfsrm1A/ZBthYuIMQg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-member-expression-to-functions": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz", - "integrity": "sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-module-imports": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz", - "integrity": "sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-module-transforms": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz", - "integrity": "sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==", - "dev": true, - "requires": { - "@babel/helper-module-imports": "^7.8.3", - "@babel/helper-replace-supers": "^7.8.6", - "@babel/helper-simple-access": "^7.8.3", - "@babel/helper-split-export-declaration": "^7.8.3", - "@babel/template": "^7.8.6", - "@babel/types": "^7.9.0", - "lodash": "^4.17.13" - } - }, - "@babel/helper-optimise-call-expression": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz", - "integrity": "sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==", - "dev": true, - "requires": { - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-plugin-utils": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz", - "integrity": "sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.9.6.tgz", - "integrity": "sha512-qX+chbxkbArLyCImk3bWV+jB5gTNU/rsze+JlcF6Nf8tVTigPJSI1o1oBow/9Resa1yehUO9lIipsmu9oG4RzA==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.8.3", - "@babel/helper-optimise-call-expression": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" - } - }, - "@babel/helper-simple-access": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz", - "integrity": "sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==", - "dev": true, - "requires": { - "@babel/template": "^7.8.3", - "@babel/types": "^7.8.3" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.13.tgz", - "integrity": "sha512-tCJDltF83htUtXx5NLcaDqRmknv652ZWCHyoTETf1CXYJdPC7nohZohjUgieXhv0hTJdRf2FjDueFehdNucpzg==", - "dev": true, - "requires": { - "@babel/types": "^7.12.13" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.0.tgz", - "integrity": "sha512-V3ts7zMSu5lfiwWDVWzRDGIN+lnCEUdaXgtVHJgLb1rGaA6jMrtB9EmE7L18foXJIE8Un/A/h6NJfGQp/e1J4A==", - "dev": true - }, - "@babel/helpers": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.9.6.tgz", - "integrity": "sha512-tI4bUbldloLcHWoRUMAj4g1bF313M/o6fBKhIsb3QnGVPwRm9JsNf/gqMkQ7zjqReABiffPV6RWj7hEglID5Iw==", - "dev": true, - "requires": { - "@babel/template": "^7.8.3", - "@babel/traverse": "^7.9.6", - "@babel/types": "^7.9.6" - } - }, - "@babel/highlight": { - "version": "7.14.0", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.0.tgz", - "integrity": "sha512-YSCOwxvTYEIMSGaBQb5kDDsCopDdiUGsqpatp3fOlI4+2HQSkTmEVWnVuySdAC5EWCqSWWTv0ib63RjR7dTBdg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" - } - }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - } - } - }, - "@babel/parser": { - "version": "7.14.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.14.3.tgz", - "integrity": "sha512-7MpZDIfI7sUC5zWo2+foJ50CSI5lcqDehZ0lVgIhSi4bFEk94fLAKlF3Q0nzSQQ+ca0lm+O6G9ztKVBeu8PMRQ==", - "dev": true - }, - "@babel/plugin-syntax-async-generators": { - "version": "7.8.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", - "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-bigint": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", - "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-class-properties": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.8.3.tgz", - "integrity": "sha512-UcAyQWg2bAN647Q+O811tG9MrJ38Z10jjhQdKNAL8fsyPzE3cCN/uT+f55cFVY4aGO4jqJAvmqsuY3GQDwAoXg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-json-strings": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", - "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-logical-assignment-operators": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.8.3.tgz", - "integrity": "sha512-Zpg2Sgc++37kuFl6ppq2Q7Awc6E6AIW671x5PY8E/f7MCIyPPGK/EoeZXvvY3P42exZ3Q4/t3YOzP/HiN79jDg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-nullish-coalescing-operator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", - "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-numeric-separator": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.8.3.tgz", - "integrity": "sha512-H7dCMAdN83PcCmqmkHB5dtp+Xa9a6LKSvA2hiFBC/5alSHxM5VgWZXFqDi0YFe8XNGT6iCa+z4V4zSt/PdZ7Dw==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.3" - } - }, - "@babel/plugin-syntax-object-rest-spread": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", - "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-catch-binding": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", - "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/plugin-syntax-optional-chaining": { - "version": "7.8.3", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", - "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.8.0" - } - }, - "@babel/template": { - "version": "7.12.13", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.13.tgz", - "integrity": "sha512-/7xxiGA57xMo/P2GVvdEumr8ONhFOhfgq2ihK3h1e6THqzTAkHbkXgB0xI9yeTfIUoH3+oAeHhqm/I43OTbbjA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/parser": "^7.12.13", - "@babel/types": "^7.12.13" - } - }, - "@babel/traverse": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.14.2.tgz", - "integrity": "sha512-TsdRgvBFHMyHOOzcP9S6QU0QQtjxlRpEYOy3mcCO5RgmC305ki42aSAmfZEMSSYBla2oZ9BMqYlncBaKmD/7iA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.12.13", - "@babel/generator": "^7.14.2", - "@babel/helper-function-name": "^7.14.2", - "@babel/helper-split-export-declaration": "^7.12.13", - "@babel/parser": "^7.14.2", - "@babel/types": "^7.14.2", - "debug": "^4.1.0", - "globals": "^11.1.0" - } - }, - "@babel/types": { - "version": "7.14.2", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.14.2.tgz", - "integrity": "sha512-SdjAG/3DikRHpUOjxZgnkbR11xUlyDMUFJdvnIgZEE16mqmY0BINMmc4//JMJglEmn6i7sq6p+mGrFWyZ98EEw==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.0", - "to-fast-properties": "^2.0.0" - } - }, - "@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, - "@cnakazawa/watch": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@cnakazawa/watch/-/watch-1.0.4.tgz", - "integrity": "sha512-v9kIhKwjeZThiWrLmj0y17CWoyddASLj9O2yvbZkbvw/N3rWOYy9zkV66ursAoVr0mV15bL8g0c4QZUE6cdDoQ==", - "dev": true, - "requires": { - "exec-sh": "^0.3.2", - "minimist": "^1.2.0" - } - }, - "@eslint/eslintrc": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.2.tgz", - "integrity": "sha512-8nmGq/4ycLpIwzvhI4tNDmQztZ8sp+hI7cyG8i1nQDhkAbRzHpXPidRAHlNvCZQpJTKw5ItIpMw9RSToGF00mg==", - "dev": true, - "requires": { - "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", - "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", - "strip-json-comments": "^3.1.1" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - } - } - }, - "@istanbuljs/load-nyc-config": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", - "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", - "dev": true, - "requires": { - "camelcase": "^5.3.1", - "find-up": "^4.1.0", - "get-package-type": "^0.1.0", - "js-yaml": "^3.13.1", - "resolve-from": "^5.0.0" - } - }, - "@istanbuljs/schema": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.2.tgz", - "integrity": "sha512-tsAQNx32a8CoFhjhijUIhI4kccIAgmGhy8LZMZgGfmXcpMbPRUqn5LWmgRttILi6yeGmBJd2xsPkFMs0PzgPCw==", - "dev": true - }, - "@jest/console": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-25.5.0.tgz", - "integrity": "sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "jest-message-util": "^25.5.0", - "jest-util": "^25.5.0", - "slash": "^3.0.0" - } - }, - "@jest/core": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.0.1.tgz", - "integrity": "sha512-Xq3eqYnxsG9SjDC+WLeIgf7/8KU6rddBxH+SCt18gEpOhAGYC/Mq+YbtlNcIdwjnnT+wDseXSbU0e5X84Y4jTQ==", - "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/reporters": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-changed-files": "^26.0.1", - "jest-config": "^26.0.1", - "jest-haste-map": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-resolve-dependencies": "^26.0.1", - "jest-runner": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "jest-watcher": "^26.0.1", - "micromatch": "^4.0.2", - "p-each-series": "^2.1.0", - "rimraf": "^3.0.0", - "slash": "^3.0.0", - "strip-ansi": "^6.0.0" - }, - "dependencies": { - "@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - } - }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/prettier": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", - "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - } - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true - }, - "jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - } - }, - "jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "@jest/environment": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-25.5.0.tgz", - "integrity": "sha512-U2VXPEqL07E/V7pSZMSQCvV5Ea4lqOlT+0ZFijl/i316cRMHvZ4qC+jBdryd+lmRetjQo0YIQr6cVPNxxK87mA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^25.5.0", - "@jest/types": "^25.5.0", - "jest-mock": "^25.5.0" - } - }, - "@jest/fake-timers": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-25.5.0.tgz", - "integrity": "sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-mock": "^25.5.0", - "jest-util": "^25.5.0", - "lolex": "^5.0.0" - } - }, - "@jest/globals": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.0.1.tgz", - "integrity": "sha512-iuucxOYB7BRCvT+TYBzUqUNuxFX1hqaR6G6IcGgEqkJ5x4htNKo1r7jk1ji9Zj8ZMiMw0oB5NaA7k5Tx6MVssA==", - "dev": true, - "requires": { - "@jest/environment": "^26.0.1", - "@jest/types": "^26.0.1", - "expect": "^26.0.1" - }, - "dependencies": { - "@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - } - }, - "@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - } - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" - } - }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "@jest/reporters": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.0.1.tgz", - "integrity": "sha512-NWWy9KwRtE1iyG/m7huiFVF9YsYv/e+mbflKRV84WDoJfBqUrNRyDbL/vFxQcYLl8IRqI4P3MgPn386x76Gf2g==", - "dev": true, - "requires": { - "@bcoe/v8-coverage": "^0.2.3", - "@jest/console": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.2", - "graceful-fs": "^4.2.4", - "istanbul-lib-coverage": "^3.0.0", - "istanbul-lib-instrument": "^4.0.0", - "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.0", - "istanbul-reports": "^3.0.2", - "jest-haste-map": "^26.0.1", - "jest-resolve": "^26.0.1", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", - "node-notifier": "^7.0.0", - "slash": "^3.0.0", - "source-map": "^0.6.0", - "string-length": "^4.0.1", - "terminal-link": "^2.0.0", - "v8-to-istanbul": "^4.1.3" - }, - "dependencies": { - "@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - } - }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "@jest/source-map": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.0.0.tgz", - "integrity": "sha512-S2Z+Aj/7KOSU2TfW0dyzBze7xr95bkm5YXNUqqCek+HE0VbNNSNzrRwfIi5lf7wvzDTSS0/ib8XQ1krFNyYgbQ==", - "dev": true, - "requires": { - "callsites": "^3.0.0", - "graceful-fs": "^4.2.4", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@jest/test-result": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-25.5.0.tgz", - "integrity": "sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==", - "dev": true, - "requires": { - "@jest/console": "^25.5.0", - "@jest/types": "^25.5.0", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/test-sequencer": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.0.1.tgz", - "integrity": "sha512-ssga8XlwfP8YjbDcmVhwNlrmblddMfgUeAkWIXts1V22equp2GMIHxm7cyeD5Q/B0ZgKPK/tngt45sH99yLLGg==", - "dev": true, - "requires": { - "@jest/test-result": "^26.0.1", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.0.1", - "jest-runner": "^26.0.1", - "jest-runtime": "^26.0.1" - }, - "dependencies": { - "@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - } - }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "@jest/transform": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.0.1.tgz", - "integrity": "sha512-pPRkVkAQ91drKGbzCfDOoHN838+FSbYaEAvBXvKuWeeRRUD8FjwXkqfUNUZL6Ke48aA/1cqq/Ni7kVMCoqagWA==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/types": "^26.0.1", - "babel-plugin-istanbul": "^6.0.0", - "chalk": "^4.0.0", - "convert-source-map": "^1.4.0", - "fast-json-stable-stringify": "^2.0.0", - "graceful-fs": "^4.2.4", - "jest-haste-map": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-util": "^26.0.1", - "micromatch": "^4.0.2", - "pirates": "^4.0.1", - "slash": "^3.0.0", - "source-map": "^0.6.1", - "write-file-atomic": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "@jest/types": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-25.5.0.tgz", - "integrity": "sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^1.1.1", - "@types/yargs": "^15.0.0", - "chalk": "^3.0.0" - } - }, - "@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - } - }, - "@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true - }, - "@nodelib/fs.walk": { - "version": "1.2.7", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.7.tgz", - "integrity": "sha512-BTIhocbPBSrRmHxOAJFtR18oLhxTtAFDAvL8hY1S3iU8k+E60W/YFs4jrixGzQjMpF4qPXxIQHcjVD9dz1C2QA==", - "dev": true, - "requires": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - } - }, - "@sinonjs/commons": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.0.tgz", - "integrity": "sha512-wEj54PfsZ5jGSwMX68G8ZXFawcSglQSXqCftWX3ec8MDUzQdHgcKvw97awHbY0efQEL5iKUOAmmVtoYgmrSG4Q==", - "dev": true, - "requires": { - "type-detect": "4.0.8" - } - }, - "@sinonjs/fake-timers": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz", - "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "@types/babel__core": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.7.tgz", - "integrity": "sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "@types/babel__generator": { - "version": "7.6.1", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.1.tgz", - "integrity": "sha512-bBKm+2VPJcMRVwNhxKu8W+5/zT7pwNEqeokFOmbvVSqGzFneNxYcEBro9Ac7/N9tlsaPYnZLK8J1LWKkMsLAew==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0" - } - }, - "@types/babel__template": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz", - "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==", - "dev": true, - "requires": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "@types/babel__traverse": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.11.tgz", - "integrity": "sha512-ddHK5icION5U6q11+tV2f9Mo6CZVuT8GJKld2q9LqHSZbvLbH34Kcu2yFGckZut453+eQU6btIA3RihmnRgI+Q==", - "dev": true, - "requires": { - "@babel/types": "^7.3.0" - } - }, - "@types/events": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", - "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", - "dev": true - }, - "@types/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-UoOfVEzAUpeSPmjm7h1uk5MH6KZma2z2O7a75onTGjnNvAvMVrPzPL/vBbT65iIGHWj6rokwfmYcmxmlSf2uwg==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", - "dev": true, - "requires": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" - } - }, - "@types/graceful-fs": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.3.tgz", - "integrity": "sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/istanbul-lib-coverage": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz", - "integrity": "sha512-hRJD2ahnnpLgsj6KWMYSrmXkM3rm2Dl1qkx6IOFD5FnuNPXJIG5L0dhgKXCYTRMGzU4n0wImQ/xfmRc4POUFlg==", - "dev": true - }, - "@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*" - } - }, - "@types/istanbul-reports": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-1.1.2.tgz", - "integrity": "sha512-P/W9yOX/3oPZSpaYOCQzGqgCQRXn0FFO/V8bWrCQs+wLmvVVxk6CRBXALEvNs9OHIatlnlFokfhuDo2ug01ciw==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "*", - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "25.2.3", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-25.2.3.tgz", - "integrity": "sha512-JXc1nK/tXHiDhV55dvfzqtmP4S3sy3T3ouV2tkViZgxY/zeUkcpQcQPGRlgF4KmWzWW5oiWYSZwtCB+2RsE4Fw==", - "dev": true, - "requires": { - "jest-diff": "^25.2.1", - "pretty-format": "^25.2.1" - } - }, - "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", - "dev": true - }, - "@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true - }, - "@types/node": { - "version": "13.7.7", - "resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.7.tgz", - "integrity": "sha512-Uo4chgKbnPNlxQwoFmYIwctkQVkMMmsAoGGU4JKwLuvBefF0pCq4FybNSnfkfRCpC7ZW7kttcC/TrRtAJsvGtg==", - "dev": true - }, - "@types/normalize-package-data": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz", - "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==", - "dev": true - }, - "@types/prettier": { - "version": "1.19.1", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-1.19.1.tgz", - "integrity": "sha512-5qOlnZscTn4xxM5MeGXAMOsIOIKIbh9e85zJWfBRVPlRMEVawzoPhINYbRGkBZCI8LxvBe7tJCdWiarA99OZfQ==", - "dev": true - }, - "@types/resolve": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.14.0.tgz", - "integrity": "sha512-bmjNBW6tok+67iOsASeYSJxSgY++BIR35nGyGLORTDirhra9reJ0shgGL3U7KPDUbOBCx8JrlCjd4d/y5uiMRQ==", - "dev": true, - "requires": { - "@types/node": "*" - } - }, - "@types/stack-utils": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", - "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==", - "dev": true - }, - "@types/yargs": { - "version": "15.0.4", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.4.tgz", - "integrity": "sha512-9T1auFmbPZoxHz0enUFlUuKRy3it01R+hlggyVUMtnCTQRunsQYifnSGb8hET4Xo8yiC0o0r1paW3ud5+rbURg==", - "dev": true, - "requires": { - "@types/yargs-parser": "*" - } - }, - "@types/yargs-parser": { - "version": "15.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-15.0.0.tgz", - "integrity": "sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==", - "dev": true - }, - "@typescript-eslint/eslint-plugin": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.26.1.tgz", - "integrity": "sha512-aoIusj/8CR+xDWmZxARivZjbMBQTT9dImUtdZ8tVCVRXgBUuuZyM5Of5A9D9arQPxbi/0rlJLcuArclz/rCMJw==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "4.26.1", - "@typescript-eslint/scope-manager": "4.26.1", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "lodash": "^4.17.21", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - }, - "dependencies": { - "@typescript-eslint/experimental-utils": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.26.1.tgz", - "integrity": "sha512-sQHBugRhrXzRCs9PaGg6rowie4i8s/iD/DpTB+EXte8OMDfdCG5TvO73XlO9Wc/zi0uyN4qOmX9hIjQEyhnbmQ==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.26.1", - "@typescript-eslint/types": "4.26.1", - "@typescript-eslint/typescript-estree": "4.26.1", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, - "@typescript-eslint/typescript-estree": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz", - "integrity": "sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.26.1", - "@typescript-eslint/visitor-keys": "4.26.1", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - } - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/experimental-utils": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.31.0.tgz", - "integrity": "sha512-MI6IWkutLYQYTQgZ48IVnRXmLR/0Q6oAyJgiOror74arUMh7EWjJkADfirZhRsUMHeLJ85U2iySDwHTSnNi9vA==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.3", - "@typescript-eslint/typescript-estree": "2.31.0", - "eslint-scope": "^5.0.0", - "eslint-utils": "^2.0.0" - } - }, - "@typescript-eslint/parser": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.26.1.tgz", - "integrity": "sha512-q7F3zSo/nU6YJpPJvQveVlIIzx9/wu75lr6oDbDzoeIRWxpoc/HQ43G4rmMoCc5my/3uSj2VEpg/D83LYZF5HQ==", - "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "4.26.1", - "@typescript-eslint/types": "4.26.1", - "@typescript-eslint/typescript-estree": "4.26.1", - "debug": "^4.3.1" - }, - "dependencies": { - "@typescript-eslint/typescript-estree": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.26.1.tgz", - "integrity": "sha512-l3ZXob+h0NQzz80lBGaykdScYaiEbFqznEs99uwzm8fPHhDjwaBFfQkjUC/slw6Sm7npFL8qrGEAMxcfBsBJUg==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.26.1", - "@typescript-eslint/visitor-keys": "4.26.1", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - } - } - }, - "@typescript-eslint/scope-manager": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.26.1.tgz", - "integrity": "sha512-TW1X2p62FQ8Rlne+WEShyd7ac2LA6o27S9i131W4NwDSfyeVlQWhw8ylldNNS8JG6oJB9Ha9Xyc+IUcqipvheQ==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.26.1", - "@typescript-eslint/visitor-keys": "4.26.1" - } - }, - "@typescript-eslint/types": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.26.1.tgz", - "integrity": "sha512-STyMPxR3cS+LaNvS8yK15rb8Y0iL0tFXq0uyl6gY45glyI7w0CsyqyEXl/Fa0JlQy+pVANeK3sbwPneCbWE7yg==", - "dev": true - }, - "@typescript-eslint/typescript-estree": { - "version": "2.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.31.0.tgz", - "integrity": "sha512-vxW149bXFXXuBrAak0eKHOzbcu9cvi6iNcJDzEtOkRwGHxJG15chiAQAwhLOsk+86p9GTr/TziYvw+H9kMaIgA==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "eslint-visitor-keys": "^1.1.0", - "glob": "^7.1.6", - "is-glob": "^4.0.1", - "lodash": "^4.17.15", - "semver": "^6.3.0", - "tsutils": "^3.17.1" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "@typescript-eslint/visitor-keys": { - "version": "4.26.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.26.1.tgz", - "integrity": "sha512-IGouNSSd+6x/fHtYRyLOM6/C+QxMDzWlDtN41ea+flWuSF9g02iqcIlX8wM53JkfljoIjP0U+yp7SiTS1onEkw==", - "dev": true, - "requires": { - "@typescript-eslint/types": "4.26.1", - "eslint-visitor-keys": "^2.0.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - } - } - }, - "abab": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz", - "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==", - "dev": true - }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true - }, - "acorn-globals": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz", - "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==", - "dev": true, - "requires": { - "acorn": "^7.1.1", - "acorn-walk": "^7.1.1" - } - }, - "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", - "dev": true, - "requires": {} - }, - "acorn-walk": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.1.1.tgz", - "integrity": "sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==", - "dev": true - }, - "ajv": { - "version": "6.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.0.tgz", - "integrity": "sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true - }, - "ansi-escapes": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.0.tgz", - "integrity": "sha512-EiYhwo0v255HUL6eDyuLrXEkTi7WwVCLAw+SeOQ7M7qdun1z1pum4DEm/nuqIVbPvi9RPPc9k9LbyBv6H0DwVg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, - "ansi-regex": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.0.tgz", - "integrity": "sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==", - "dev": true - }, - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", - "dev": true, - "requires": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - } - }, - "arg": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", - "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", - "dev": true - }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", - "dev": true, - "requires": { - "sprintf-js": "~1.0.2" - }, - "dependencies": { - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", - "dev": true - } - } - }, - "arr-diff": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", - "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", - "dev": true - }, - "arr-flatten": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", - "integrity": "sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg==", - "dev": true - }, - "arr-union": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", - "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", - "dev": true - }, - "array-includes": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz", - "integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0", - "is-string": "^1.0.5" - } - }, - "array-union": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", - "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", - "dev": true - }, - "array.prototype.flat": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz", - "integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1" - } - }, - "asn1": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", - "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", - "dev": true, - "requires": { - "safer-buffer": "~2.1.0" - } - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", - "dev": true - }, - "assign-symbols": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", - "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", - "dev": true - }, - "atob": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", - "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", - "dev": true - }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", - "dev": true - }, - "aws4": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz", - "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==", - "dev": true - }, - "babel-jest": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.0.1.tgz", - "integrity": "sha512-Z4GGmSNQ8pX3WS1O+6v3fo41YItJJZsVxG5gIQ+HuB/iuAQBJxMTHTwz292vuYws1LnHfwSRgoqI+nxdy/pcvw==", - "dev": true, - "requires": { - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "@types/babel__core": "^7.1.7", - "babel-plugin-istanbul": "^6.0.0", - "babel-preset-jest": "^26.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "slash": "^3.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - } - } - }, - "babel-plugin-istanbul": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.0.0.tgz", - "integrity": "sha512-AF55rZXpe7trmEylbaE1Gv54wn6rwU03aptvRoVIGP8YykoSxqdVLV1TfwflBCE/QtHmqtP8SWlTENqbK8GCSQ==", - "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.0.0", - "@istanbuljs/load-nyc-config": "^1.0.0", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-instrument": "^4.0.0", - "test-exclude": "^6.0.0" - } - }, - "babel-plugin-jest-hoist": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.0.0.tgz", - "integrity": "sha512-+AuoehOrjt9irZL7DOt2+4ZaTM6dlu1s5TTS46JBa0/qem4dy7VNW3tMb96qeEqcIh20LD73TVNtmVEeymTG7w==", - "dev": true, - "requires": { - "@babel/template": "^7.3.3", - "@babel/types": "^7.3.3", - "@types/babel__traverse": "^7.0.6" - } - }, - "babel-preset-current-node-syntax": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.2.tgz", - "integrity": "sha512-u/8cS+dEiK1SFILbOC8/rUI3ml9lboKuuMvZ/4aQnQmhecQAgPw5ew066C1ObnEAUmlx7dv/s2z52psWEtLNiw==", - "dev": true, - "requires": { - "@babel/plugin-syntax-async-generators": "^7.8.4", - "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", - "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", - "@babel/plugin-syntax-object-rest-spread": "^7.8.3", - "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", - "@babel/plugin-syntax-optional-chaining": "^7.8.3" - } - }, - "babel-preset-jest": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.0.0.tgz", - "integrity": "sha512-9ce+DatAa31DpR4Uir8g4Ahxs5K4W4L8refzt+qHWQANb6LhGcAEfIFgLUwk67oya2cCUd6t4eUMtO/z64ocNw==", - "dev": true, - "requires": { - "babel-plugin-jest-hoist": "^26.0.0", - "babel-preset-current-node-syntax": "^0.1.2" - } - }, - "balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", - "dev": true - }, - "base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "requires": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "bcrypt-pbkdf": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", - "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", - "dev": true, - "requires": { - "tweetnacl": "^0.14.3" - } - }, - "brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "requires": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", - "dev": true, - "requires": { - "fill-range": "^7.0.1" - } - }, - "browser-process-hrtime": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", - "integrity": "sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow==", - "dev": true - }, - "browser-resolve": { - "version": "1.11.3", - "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz", - "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==", - "dev": true, - "requires": { - "resolve": "1.1.7" - }, - "dependencies": { - "resolve": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", - "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=", - "dev": true - } - } - }, - "bs-logger": { - "version": "0.2.6", - "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", - "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", - "dev": true, - "requires": { - "fast-json-stable-stringify": "2.x" - } - }, - "bser": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", - "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", - "dev": true, - "requires": { - "node-int64": "^0.4.0" - } - }, - "buffer-from": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.0.tgz", - "integrity": "sha512-c5mRlguI/Pe2dSZmpER62rSCu0ryKmWddzRYsuXc50U2/g8jMOulc31VZMa4mYx31U5xsmSOpDCgH88Vl9cDGQ==", - "dev": true - }, - "cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "requires": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" - } - }, - "callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true - }, - "camelcase": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", - "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true - }, - "capture-exit": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/capture-exit/-/capture-exit-2.0.0.tgz", - "integrity": "sha512-PiT/hQmTonHhl/HFGN+Lx3JJUznrVYJ3+AQsnthneZbvW7x+f08Tk7yLJTLEOUvBTbduLeeBkxEaYXUOUrRq6g==", - "dev": true, - "requires": { - "rsvp": "^4.8.4" - } - }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", - "dev": true - }, - "chalk": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", - "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "char-regex": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", - "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", - "dev": true - }, - "ci-info": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", - "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", - "dev": true - }, - "class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "requires": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } - } - }, - "cliui": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", - "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==", - "dev": true, - "requires": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^6.2.0" - } - }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=", - "dev": true - }, - "collect-v8-coverage": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz", - "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==", - "dev": true - }, - "collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", - "dev": true, - "requires": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "dev": true, - "requires": { - "delayed-stream": "~1.0.0" - } - }, - "component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, - "concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", - "dev": true - }, - "contains-path": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", - "integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", - "dev": true - }, - "convert-source-map": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", - "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.1" - } - }, - "copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", - "dev": true - }, - "core-util-is": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", - "dev": true - }, - "cross-spawn": { - "version": "6.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", - "integrity": "sha512-eTVLrBSt7fjbDygz805pMnstIs2VTBNkRm0qxZd+M7A5XDdxVRWO5MxGBXZhjY4cqLYLdtrGqRf8mBPmzwSpWQ==", - "dev": true, - "requires": { - "nice-try": "^1.0.4", - "path-key": "^2.0.1", - "semver": "^5.5.0", - "shebang-command": "^1.2.0", - "which": "^1.2.9" - } - }, - "cssom": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.4.4.tgz", - "integrity": "sha512-p3pvU7r1MyyqbTk+WbNJIgJjG2VmTIaB10rI93LzVPrmDJKkzKYMtxxyAvQXR/NS6otuzveI7+7BBq3SjBS2mw==", - "dev": true - }, - "cssstyle": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", - "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", - "dev": true, - "requires": { - "cssom": "~0.3.6" - }, - "dependencies": { - "cssom": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", - "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", - "dev": true - } - } - }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "data-urls": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz", - "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0" - } - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - } - }, - "decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", - "dev": true - }, - "decimal.js": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz", - "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==", - "dev": true - }, - "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", - "dev": true - }, - "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true - }, - "deepmerge": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", - "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", - "dev": true - }, - "define-properties": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", - "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", - "dev": true, - "requires": { - "object-keys": "^1.0.12" - } - }, - "define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "requires": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "dependencies": { - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", - "dev": true - }, - "detect-newline": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", - "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", - "dev": true - }, - "diff": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", - "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", - "dev": true - }, - "diff-sequences": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-25.2.6.tgz", - "integrity": "sha512-Hq8o7+6GaZeoFjtpgvRBUknSXNeJiCx7V9Fr94ZMljNiCr9n9L8H8aJqgWOQiDDGdyn29fRNcDdRVJ5fdyihfg==", - "dev": true - }, - "dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "requires": { - "path-type": "^4.0.0" - }, - "dependencies": { - "path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true - } - } - }, - "doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "requires": { - "esutils": "^2.0.2" - } - }, - "domexception": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz", - "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==", - "dev": true, - "requires": { - "webidl-conversions": "^5.0.0" - }, - "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } - } - }, - "ecc-jsbn": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", - "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", - "dev": true, - "requires": { - "jsbn": "~0.1.0", - "safer-buffer": "^2.1.0" - } - }, - "emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, - "end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "dev": true, - "requires": { - "once": "^1.4.0" - } - }, - "enhanced-resolve": { - "version": "5.8.2", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.8.2.tgz", - "integrity": "sha512-F27oB3WuHDzvR2DOGNTaYy0D5o0cnrv8TeI482VM4kYgQd/FT9lUQwuNsJ0oOHtBUq7eiW5ytqzp7nBFknL+GA==", - "requires": { - "graceful-fs": "^4.2.4", - "tapable": "^2.2.0" - } - }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", - "dev": true, - "requires": { - "is-arrayish": "^0.2.1" - } - }, - "es-abstract": { - "version": "1.17.4", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.4.tgz", - "integrity": "sha512-Ae3um/gb8F0mui/jPL+QiqmglkUsaQf7FwBEHYIFkztkneosu9imhqHpBzQ3h1vit8t5iQ74t6PEVvphBZiuiQ==", - "dev": true, - "requires": { - "es-to-primitive": "^1.2.1", - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1", - "is-callable": "^1.1.5", - "is-regex": "^1.0.5", - "object-inspect": "^1.7.0", - "object-keys": "^1.1.1", - "object.assign": "^4.1.0", - "string.prototype.trimleft": "^2.1.1", - "string.prototype.trimright": "^2.1.1" - } - }, - "es-to-primitive": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", - "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", - "dev": true, - "requires": { - "is-callable": "^1.1.4", - "is-date-object": "^1.0.1", - "is-symbol": "^1.0.2" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - }, - "escodegen": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.1.tgz", - "integrity": "sha512-Bmt7NcRySdIfNPfU2ZoXDrrXsG9ZjvDxcAlMfDUgRBjLOWTuIACXPBFJH7Z+cLb40JeQco5toikyc9t9P8E9SQ==", - "dev": true, - "requires": { - "esprima": "^4.0.1", - "estraverse": "^4.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1", - "source-map": "~0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, - "optional": true - } - } - }, - "eslint": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.28.0.tgz", - "integrity": "sha512-UMfH0VSjP0G4p3EWirscJEQ/cHqnT/iuH6oNZOB94nBjWbMnhGEPxsZm1eyIW0C/9jLI0Fow4W5DXLjEI7mn1g==", - "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.2", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.0.1", - "doctrine": "^3.0.0", - "enquirer": "^2.3.5", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true - }, - "globals": { - "version": "13.9.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.9.0.tgz", - "integrity": "sha512-74/FduwI/JaIrr1H8e71UbDE+5x7pIPs1C2rrwC52SszOo043CsWOZEMW7o2Y58xwm9b+0RBKDxY5n2sUpEFxA==", - "dev": true, - "requires": { - "type-fest": "^0.20.2" - } - }, - "levn": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - } - }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", - "dev": true, - "requires": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "requires": { - "prelude-ls": "^1.2.1" - } - }, - "type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "eslint-import-resolver-node": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.3.tgz", - "integrity": "sha512-b8crLDo0M5RSe5YG8Pu2DYBj71tSB6OvXkfzwbJU2w7y8P4/yo0MyF8jU26IEuEuHF2K5/gcAJE3LhQGqBBbVg==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "resolve": "^1.13.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-module-utils": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.5.2.tgz", - "integrity": "sha512-LGScZ/JSlqGKiT8OC+cYRxseMjyqt6QO54nl281CK93unD89ijSeRV6An8Ci/2nvWVKe8K/Tqdm75RQoIOCr+Q==", - "dev": true, - "requires": { - "debug": "^2.6.9", - "pkg-dir": "^2.0.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - }, - "pkg-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz", - "integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=", - "dev": true, - "requires": { - "find-up": "^2.1.0" - } - } - } - }, - "eslint-plugin-import": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.20.1.tgz", - "integrity": "sha512-qQHgFOTjguR+LnYRoToeZWT62XM55MBVXObHM6SKFd1VzDcX/vqT1kAz8ssqigh5eMj8qXcRoXXGZpPP6RfdCw==", - "dev": true, - "requires": { - "array-includes": "^3.0.3", - "array.prototype.flat": "^1.2.1", - "contains-path": "^0.1.0", - "debug": "^2.6.9", - "doctrine": "1.5.0", - "eslint-import-resolver-node": "^0.3.2", - "eslint-module-utils": "^2.4.1", - "has": "^1.0.3", - "minimatch": "^3.0.4", - "object.values": "^1.1.0", - "read-pkg-up": "^2.0.0", - "resolve": "^1.12.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "doctrine": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz", - "integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=", - "dev": true, - "requires": { - "esutils": "^2.0.2", - "isarray": "^1.0.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "eslint-plugin-jest": { - "version": "23.8.2", - "resolved": "https://registry.npmjs.org/eslint-plugin-jest/-/eslint-plugin-jest-23.8.2.tgz", - "integrity": "sha512-xwbnvOsotSV27MtAe7s8uGWOori0nUsrXh2f1EnpmXua8sDfY6VZhHAhHg2sqK7HBNycRQExF074XSZ7DvfoFg==", - "dev": true, - "requires": { - "@typescript-eslint/experimental-utils": "^2.5.0" - } - }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^4.1.1" - } - }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - } - }, - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", - "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" - } - }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true - }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", - "dev": true, - "requires": { - "estraverse": "^5.1.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "requires": { - "estraverse": "^5.2.0" - }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } - } - }, - "estraverse": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", - "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true - }, - "esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true - }, - "exec-sh": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/exec-sh/-/exec-sh-0.3.4.tgz", - "integrity": "sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A==", - "dev": true - }, - "execa": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", - "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==", - "dev": true, - "requires": { - "cross-spawn": "^6.0.0", - "get-stream": "^4.0.0", - "is-stream": "^1.1.0", - "npm-run-path": "^2.0.0", - "p-finally": "^1.0.0", - "signal-exit": "^3.0.0", - "strip-eof": "^1.0.0" - } - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", - "dev": true - }, - "expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", - "dev": true, - "requires": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - } - } - }, - "expect": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/expect/-/expect-25.5.0.tgz", - "integrity": "sha512-w7KAXo0+6qqZZhovCaBVPSIqQp7/UTcx4M9uKt2m6pd2VB1voyC8JizLRqeEqud3AAVP02g+hbErDu5gu64tlA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-styles": "^4.0.0", - "jest-get-type": "^25.2.6", - "jest-matcher-utils": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-regex-util": "^25.2.6" - } - }, - "extend": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "dev": true - }, - "extend-shallow": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", - "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", - "dev": true, - "requires": { - "assign-symbols": "^1.0.0", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "requires": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } - } - }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", - "dev": true - }, - "fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true - }, - "fast-glob": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.5.tgz", - "integrity": "sha512-2DtFcgT68wiTTiwZ2hNdJfcHNke9XOfnwmBRWXhmeKM8rF0TGwmC/Qto3S7RoZKp5cilZbxzO5iTNTQsJ+EeDg==", - "dev": true, - "requires": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.0", - "merge2": "^1.3.0", - "micromatch": "^4.0.2", - "picomatch": "^2.2.1" - } - }, - "fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true - }, - "fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true - }, - "fastq": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", - "integrity": "sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g==", - "dev": true, - "requires": { - "reusify": "^1.0.4" - } - }, - "fb-watchman": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.1.tgz", - "integrity": "sha512-DkPJKQeY6kKwmuMretBhr7G6Vodr7bFwDYTXIkfG1gjvNpaxBTQV3PbXg6bR1c1UP4jPOX0jHUbbHANL9vRjVg==", - "dev": true, - "requires": { - "bser": "2.1.1" - } - }, - "file-entry-cache": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", - "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", - "dev": true, - "requires": { - "flat-cache": "^3.0.4" - } - }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", - "dev": true, - "requires": { - "to-regex-range": "^5.0.1" - } - }, - "find-up": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", - "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", - "dev": true, - "requires": { - "locate-path": "^5.0.0", - "path-exists": "^4.0.0" - } - }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", - "dev": true, - "requires": { - "flatted": "^3.1.0", - "rimraf": "^3.0.2" - } - }, - "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true - }, - "for-in": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", - "dev": true - }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", - "dev": true - }, - "form-data": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", - "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", - "dev": true, - "requires": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.6", - "mime-types": "^2.1.12" - } - }, - "fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", - "dev": true, - "requires": { - "map-cache": "^0.2.2" - } - }, - "fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", - "dev": true, - "requires": { - "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - } - }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", - "dev": true - }, - "fsevents": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", - "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", - "dev": true, - "optional": true - }, - "function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" - }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true - }, - "gensync": { - "version": "1.0.0-beta.1", - "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.1.tgz", - "integrity": "sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg==", - "dev": true - }, - "get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true - }, - "get-package-type": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", - "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", - "dev": true - }, - "get-stream": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", - "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", - "dev": true - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0" - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true - }, - "globby": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.3.tgz", - "integrity": "sha512-ffdmosjA807y7+lA1NM0jELARVmYul/715xiILEjo3hBLPTcirgQNnXECn5g3mtR8TOLCVbkfua1Hpen25/Xcg==", - "dev": true, - "requires": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", - "slash": "^3.0.0" - }, - "dependencies": { - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true - } - } - }, - "graceful-fs": { - "version": "4.2.6", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.6.tgz", - "integrity": "sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==" - }, - "growly": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/growly/-/growly-1.3.0.tgz", - "integrity": "sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE=", - "dev": true, - "optional": true - }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", - "dev": true - }, - "har-validator": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", - "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", - "dev": true, - "requires": { - "ajv": "^6.5.5", - "har-schema": "^2.0.0" - } - }, - "has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", - "requires": { - "function-bind": "^1.1.1" - } - }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true - }, - "has-symbols": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", - "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==", - "dev": true - }, - "has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", - "dev": true, - "requires": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - } - }, - "has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "dependencies": { - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "hosted-git-info": { - "version": "2.8.9", - "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", - "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "dev": true - }, - "html-encoding-sniffer": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz", - "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==", - "dev": true, - "requires": { - "whatwg-encoding": "^1.0.5" - } - }, - "html-escaper": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", - "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true - }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "jsprim": "^1.2.2", - "sshpk": "^1.7.0" - } - }, - "human-signals": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz", - "integrity": "sha512-SEQu7vl8KjNL2eoGBLF3+wAjpsNfA9XMlXAYj/3EdaNfAlxKthD1xjEQfGOUhllCGGJVNY34bRr6lPINhNjyZw==", - "dev": true - }, - "iconv-lite": { - "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", - "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, - "requires": { - "safer-buffer": ">= 2.1.2 < 3" - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "import-fresh": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz", - "integrity": "sha512-6e1q1cnWP2RXD9/keSkxHScg508CdXqXWgWBaETNhyuBFz+kUZlKboh+ISK+bU++DmbHimVBrOz/zzPe0sZ3sQ==", - "dev": true, - "requires": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "dependencies": { - "resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true - } - } - }, - "import-local": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.0.2.tgz", - "integrity": "sha512-vjL3+w0oulAVZ0hBHnxa/Nm5TAurf9YLQJDhqRZyqb+VKGOB6LU8t9H1Nr5CIo16vh9XfJTOoHwU0B71S557gA==", - "dev": true, - "requires": { - "pkg-dir": "^4.2.0", - "resolve-cwd": "^3.0.0" - } - }, - "imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true - }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, - "inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true - }, - "ip-regex": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", - "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", - "dev": true - }, - "is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-arrayish": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", - "dev": true - }, - "is-buffer": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", - "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==", - "dev": true - }, - "is-callable": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.5.tgz", - "integrity": "sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==", - "dev": true - }, - "is-ci": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz", - "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==", - "dev": true, - "requires": { - "ci-info": "^2.0.0" - } - }, - "is-core-module": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.4.0.tgz", - "integrity": "sha512-6A2fkfq1rfeQZjxrZJGerpLCTHRNEBiSgnu0+obeJpEPZRUooHgsizvzv0ZjJwOz3iWIHdJtVWJ/tmPr3D21/A==", - "requires": { - "has": "^1.0.3" - } - }, - "is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "is-date-object": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", - "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==", - "dev": true - }, - "is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "dependencies": { - "kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true - } - } - }, - "is-docker": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.0.0.tgz", - "integrity": "sha512-pJEdRugimx4fBMra5z2/5iRdZ63OhYV0vr0Dwm5+xtW4D1FvRkB8hamMIhnWfyJeDdyr/aa7BDyNbtG38VxgoQ==", - "dev": true, - "optional": true - }, - "is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", - "dev": true - }, - "is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true - }, - "is-generator-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", - "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", - "dev": true - }, - "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", - "dev": true, - "requires": { - "is-extglob": "^2.1.1" - } - }, - "is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true - }, - "is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "is-potential-custom-element-name": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz", - "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=", - "dev": true - }, - "is-regex": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", - "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", - "dev": true, - "requires": { - "has": "^1.0.3" - } - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, - "is-symbol": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", - "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", - "dev": true, - "requires": { - "has-symbols": "^1.0.1" - } - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", - "dev": true - }, - "is-windows": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", - "integrity": "sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==", - "dev": true - }, - "is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", - "dev": true, - "optional": true, - "requires": { - "is-docker": "^2.0.0" - } - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", - "dev": true - }, - "istanbul-lib-coverage": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.0.0.tgz", - "integrity": "sha512-UiUIqxMgRDET6eR+o5HbfRYP1l0hqkWOs7vNxC/mggutCMUIhWMm8gAHb8tHlyfD3/l6rlgNA5cKdDzEAf6hEg==", - "dev": true - }, - "istanbul-lib-instrument": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz", - "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==", - "dev": true, - "requires": { - "@babel/core": "^7.7.5", - "@istanbuljs/schema": "^0.1.2", - "istanbul-lib-coverage": "^3.0.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==", - "dev": true, - "requires": { - "istanbul-lib-coverage": "^3.0.0", - "make-dir": "^3.0.0", - "supports-color": "^7.1.0" - } - }, - "istanbul-lib-source-maps": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.0.tgz", - "integrity": "sha512-c16LpFRkR8vQXyHZ5nLpY35JZtzj1PQY1iZmesUbf1FZHbIupcWfjgOXBY9YHkLEQ6puz1u4Dgj6qmU/DisrZg==", - "dev": true, - "requires": { - "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "istanbul-reports": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz", - "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==", - "dev": true, - "requires": { - "html-escaper": "^2.0.0", - "istanbul-lib-report": "^3.0.0" - } - }, - "javascript-stringify": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/javascript-stringify/-/javascript-stringify-2.0.1.tgz", - "integrity": "sha512-yV+gqbd5vaOYjqlbk16EG89xB5udgjqQF3C5FAORDg4f/IS1Yc5ERCv5e/57yBcfJYw05V5JyIXabhwb75Xxow==", - "dev": true - }, - "jest": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest/-/jest-26.0.1.tgz", - "integrity": "sha512-29Q54kn5Bm7ZGKIuH2JRmnKl85YRigp0o0asTc6Sb6l2ch1DCXIeZTLLFy9ultJvhkTqbswF5DEx4+RlkmCxWg==", - "dev": true, - "requires": { - "@jest/core": "^26.0.1", - "import-local": "^3.0.2", - "jest-cli": "^26.0.1" - }, - "dependencies": { - "@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - } - }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "jest-cli": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.0.1.tgz", - "integrity": "sha512-pFLfSOBcbG9iOZWaMK4Een+tTxi/Wcm34geqZEqrst9cZDkTQ1LZ2CnBrTlHWuYAiTMFr0EQeK52ScyFU8wK+w==", - "dev": true, - "requires": { - "@jest/core": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "import-local": "^3.0.2", - "is-ci": "^2.0.0", - "jest-config": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "prompts": "^2.0.1", - "yargs": "^15.3.1" - } - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "jest-changed-files": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.0.1.tgz", - "integrity": "sha512-q8LP9Sint17HaE2LjxQXL+oYWW/WeeXMPE2+Op9X3mY8IEGFVc14xRxFjUuXUbcPAlDLhtWdIEt59GdQbn76Hw==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "execa": "^4.0.0", - "throat": "^5.0.0" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", - "dev": true, - "requires": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - } - }, - "execa": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/execa/-/execa-4.1.0.tgz", - "integrity": "sha512-j5W0//W7f8UxAn8hXVnwG8tLwdiUy4FJLcSupCg6maBYZDpyBvTApK7KyuI4bKj8KOh1r2YH+6ucuYtJv1bTZA==", - "dev": true, - "requires": { - "cross-spawn": "^7.0.0", - "get-stream": "^5.0.0", - "human-signals": "^1.1.1", - "is-stream": "^2.0.0", - "merge-stream": "^2.0.0", - "npm-run-path": "^4.0.0", - "onetime": "^5.1.0", - "signal-exit": "^3.0.2", - "strip-final-newline": "^2.0.0" - } - }, - "get-stream": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", - "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", - "dev": true, - "requires": { - "pump": "^3.0.0" - } - }, - "is-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.0.tgz", - "integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==", - "dev": true - }, - "npm-run-path": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", - "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", - "dev": true, - "requires": { - "path-key": "^3.0.0" - } - }, - "path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true - }, - "shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "requires": { - "shebang-regex": "^3.0.0" - } - }, - "shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "jest-circus": { - "version": "25.1.0", - "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-25.1.0.tgz", - "integrity": "sha512-Axlcr2YMxVarMW4SiZhCFCjNKhdF4xF9AIdltyutQOKyyDT795Kl/fzI95O0l8idE51Npj2wDj5GhrV7uEoEJA==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^25.1.0", - "@jest/test-result": "^25.1.0", - "@jest/types": "^25.1.0", - "chalk": "^3.0.0", - "co": "^4.6.0", - "expect": "^25.1.0", - "is-generator-fn": "^2.0.0", - "jest-each": "^25.1.0", - "jest-matcher-utils": "^25.1.0", - "jest-message-util": "^25.1.0", - "jest-snapshot": "^25.1.0", - "jest-util": "^25.1.0", - "pretty-format": "^25.1.0", - "stack-utils": "^1.0.1", - "throat": "^5.0.0" - } - }, - "jest-config": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.0.1.tgz", - "integrity": "sha512-9mWKx2L1LFgOXlDsC4YSeavnblN6A4CPfXFiobq+YYLaBMymA/SczN7xYTSmLaEYHZOcB98UdoN4m5uNt6tztg==", - "dev": true, - "requires": { - "@babel/core": "^7.1.0", - "@jest/test-sequencer": "^26.0.1", - "@jest/types": "^26.0.1", - "babel-jest": "^26.0.1", - "chalk": "^4.0.0", - "deepmerge": "^4.2.2", - "glob": "^7.1.1", - "graceful-fs": "^4.2.4", - "jest-environment-jsdom": "^26.0.1", - "jest-environment-node": "^26.0.1", - "jest-get-type": "^26.0.0", - "jest-jasmine2": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "micromatch": "^4.0.2", - "pretty-format": "^26.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true - }, - "jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } - } - } - }, - "jest-diff": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-25.5.0.tgz", - "integrity": "sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==", - "dev": true, - "requires": { - "chalk": "^3.0.0", - "diff-sequences": "^25.2.6", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" - } - }, - "jest-docblock": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz", - "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==", - "dev": true, - "requires": { - "detect-newline": "^3.0.0" - } - }, - "jest-each": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-25.5.0.tgz", - "integrity": "sha512-QBogUxna3D8vtiItvn54xXde7+vuzqRrEeaw8r1s+1TG9eZLVJE5ZkKoSUlqFwRjnlaA4hyKGiu9OlkFIuKnjA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "jest-get-type": "^25.2.6", - "jest-util": "^25.5.0", - "pretty-format": "^25.5.0" - } - }, - "jest-environment-jsdom": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.0.1.tgz", - "integrity": "sha512-u88NJa3aptz2Xix2pFhihRBAatwZHWwSiRLBDBQE1cdJvDjPvv7ZGA0NQBxWwDDn7D0g1uHqxM8aGgfA9Bx49g==", - "dev": true, - "requires": { - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1", - "jsdom": "^16.2.2" - }, - "dependencies": { - "@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - } - }, - "@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "jest-environment-node": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.0.1.tgz", - "integrity": "sha512-4FRBWcSn5yVo0KtNav7+5NH5Z/tEgDLp7VRQVS5tCouWORxj+nI+1tOLutM07Zb2Qi7ja+HEDoOUkjBSWZg/IQ==", - "dev": true, - "requires": { - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/types": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-util": "^26.0.1" - }, - "dependencies": { - "@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - } - }, - "@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "jest-get-type": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-25.2.6.tgz", - "integrity": "sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==", - "dev": true - }, - "jest-haste-map": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.6.2.tgz", - "integrity": "sha512-easWIJXIw71B2RdR8kgqpjQrbMRWQBgiBwXYEhtGUTaX+doCjBheluShdDMeR8IMfJiTqH4+zfhtg29apJf/8w==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/graceful-fs": "^4.1.2", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "fsevents": "^2.1.2", - "graceful-fs": "^4.2.4", - "jest-regex-util": "^26.0.0", - "jest-serializer": "^26.6.2", - "jest-util": "^26.6.2", - "jest-worker": "^26.6.2", - "micromatch": "^4.0.2", - "sane": "^4.0.3", - "walker": "^1.0.7" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - } - } - }, - "jest-jasmine2": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.0.1.tgz", - "integrity": "sha512-ILaRyiWxiXOJ+RWTKupzQWwnPaeXPIoLS5uW41h18varJzd9/7I0QJGqg69fhTT1ev9JpSSo9QtalriUN0oqOg==", - "dev": true, - "requires": { - "@babel/traverse": "^7.1.0", - "@jest/environment": "^26.0.1", - "@jest/source-map": "^26.0.0", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "co": "^4.6.0", - "expect": "^26.0.1", - "is-generator-fn": "^2.0.0", - "jest-each": "^26.0.1", - "jest-matcher-utils": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "pretty-format": "^26.0.1", - "throat": "^5.0.0" - }, - "dependencies": { - "@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - } - }, - "@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - } - }, - "@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } - }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/prettier": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", - "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - } - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-each": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.6.2.tgz", - "integrity": "sha512-Mer/f0KaATbjl8MCJ+0GEpNdqmnVmDYqCTJYTvoo7rqmRiDllmp2AYN+06F93nXcY3ur9ShIjS+CO/uD+BbH4A==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-util": "^26.6.2", - "pretty-format": "^26.6.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" - } - }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true - }, - "jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - } - }, - "jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "jest-leak-detector": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.0.1.tgz", - "integrity": "sha512-93FR8tJhaYIWrWsbmVN1pQ9ZNlbgRpfvrnw5LmgLRX0ckOJ8ut/I35CL7awi2ecq6Ca4lL59bEK9hr7nqoHWPA==", - "dev": true, - "requires": { - "jest-get-type": "^26.0.0", - "pretty-format": "^26.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - } - } - }, - "jest-matcher-utils": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz", - "integrity": "sha512-VWI269+9JS5cpndnpCwm7dy7JtGQT30UHfrnM3mXl22gHGt/b7NkjBqXfbhZ8V4B7ANUsjK18PlSBmG0YH7gjw==", - "dev": true, - "requires": { - "chalk": "^3.0.0", - "jest-diff": "^25.5.0", - "jest-get-type": "^25.2.6", - "pretty-format": "^25.5.0" - } - }, - "jest-message-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-25.5.0.tgz", - "integrity": "sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/stack-utils": "^1.0.1", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "slash": "^3.0.0", - "stack-utils": "^1.0.1" - } - }, - "jest-mock": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-25.5.0.tgz", - "integrity": "sha512-eXWuTV8mKzp/ovHc5+3USJMYsTBhyQ+5A1Mak35dey/RG8GlM4YWVylZuGgVXinaW6tpvk/RSecmF37FKUlpXA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0" - } - }, - "jest-pnp-resolver": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz", - "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==", - "dev": true, - "requires": {} - }, - "jest-regex-util": { - "version": "25.2.6", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-25.2.6.tgz", - "integrity": "sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==", - "dev": true - }, - "jest-resolve": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-25.5.1.tgz", - "integrity": "sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "browser-resolve": "^1.11.3", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.1", - "read-pkg-up": "^7.0.1", - "realpath-native": "^2.0.0", - "resolve": "^1.17.0", - "slash": "^3.0.0" - }, - "dependencies": { - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } - } - } - }, - "jest-resolve-dependencies": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.0.1.tgz", - "integrity": "sha512-9d5/RS/ft0vB/qy7jct/qAhzJsr6fRQJyGAFigK3XD4hf9kIbEH5gks4t4Z7kyMRhowU6HWm/o8ILqhaHdSqLw==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-snapshot": "^26.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/prettier": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", - "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - } - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true - }, - "jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - } - }, - "jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "jest-runner": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.0.1.tgz", - "integrity": "sha512-CApm0g81b49Znm4cZekYQK67zY7kkB4umOlI2Dx5CwKAzdgw75EN+ozBHRvxBzwo1ZLYZ07TFxkaPm+1t4d8jA==", - "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/environment": "^26.0.1", - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "chalk": "^4.0.0", - "exit": "^0.1.2", - "graceful-fs": "^4.2.4", - "jest-config": "^26.0.1", - "jest-docblock": "^26.0.0", - "jest-haste-map": "^26.0.1", - "jest-jasmine2": "^26.0.1", - "jest-leak-detector": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-resolve": "^26.0.1", - "jest-runtime": "^26.0.1", - "jest-util": "^26.0.1", - "jest-worker": "^26.0.0", - "source-map-support": "^0.5.6", - "throat": "^5.0.0" - }, - "dependencies": { - "@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - } - }, - "@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - } - }, - "@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } - }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" - } - }, - "jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "jest-runtime": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.0.1.tgz", - "integrity": "sha512-Ci2QhYFmANg5qaXWf78T2Pfo6GtmIBn2rRaLnklRyEucmPccmCKvS9JPljcmtVamsdMmkyNkVFb9pBTD6si9Lw==", - "dev": true, - "requires": { - "@jest/console": "^26.0.1", - "@jest/environment": "^26.0.1", - "@jest/fake-timers": "^26.0.1", - "@jest/globals": "^26.0.1", - "@jest/source-map": "^26.0.0", - "@jest/test-result": "^26.0.1", - "@jest/transform": "^26.0.1", - "@jest/types": "^26.0.1", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0", - "collect-v8-coverage": "^1.0.0", - "exit": "^0.1.2", - "glob": "^7.1.3", - "graceful-fs": "^4.2.4", - "jest-config": "^26.0.1", - "jest-haste-map": "^26.0.1", - "jest-message-util": "^26.0.1", - "jest-mock": "^26.0.1", - "jest-regex-util": "^26.0.0", - "jest-resolve": "^26.0.1", - "jest-snapshot": "^26.0.1", - "jest-util": "^26.0.1", - "jest-validate": "^26.0.1", - "slash": "^3.0.0", - "strip-bom": "^4.0.0", - "yargs": "^15.3.1" - }, - "dependencies": { - "@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - } - }, - "@jest/environment": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.6.2.tgz", - "integrity": "sha512-nFy+fHl28zUrRsCeMB61VDThV1pVTtlEokBRgqPrcT1JNq4yRNIyTHfyht6PqtUvY9IsuLGTrbG8kPXjSZIZwA==", - "dev": true, - "requires": { - "@jest/fake-timers": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/node": "*", - "jest-mock": "^26.6.2" - } - }, - "@jest/fake-timers": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.6.2.tgz", - "integrity": "sha512-14Uleatt7jdzefLPYM3KLcnUl1ZNikaKq34enpb5XG9i81JpppDb5muZvonvKyrl7ftEHkKS5L5/eB/kxJ+bvA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@sinonjs/fake-timers": "^6.0.1", - "@types/node": "*", - "jest-message-util": "^26.6.2", - "jest-mock": "^26.6.2", - "jest-util": "^26.6.2" - } - }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/prettier": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.2.3.tgz", - "integrity": "sha512-PijRCG/K3s3w1We6ynUKdxEc5AcuuH3NBmMDP8uvKVp6X43UY7NQlTzczakXP3DJR0F4dfNQIGjU2cUeRYs2AA==", - "dev": true - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "expect": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/expect/-/expect-26.6.2.tgz", - "integrity": "sha512-9/hlOBkQl2l/PLHJx6JjoDF6xPKcJEsUlWKb23rKE7KzeDqUZKXKNMW27KIue5JMdBV9HgmoJPcc8HtO85t9IA==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-styles": "^4.0.0", - "jest-get-type": "^26.3.0", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-regex-util": "^26.0.0" - } - }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "jest-matcher-utils": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.6.2.tgz", - "integrity": "sha512-llnc8vQgYcNqDrqRDXWwMr9i7rS5XFiCwvh6DTP7Jqa2mqpcCBBlpCbn+trkG0KNhPu/h8rzyBkriOtBstvWhw==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-mock": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.6.2.tgz", - "integrity": "sha512-YyFjePHHp1LzpzYcmgqkJ0nm0gg/lJx2aZFzFy1S6eUqNjXsOqTK10zNRff2dNfssgokjkG65OlWNcIlgd3zew==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*" - } - }, - "jest-regex-util": { - "version": "26.0.0", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz", - "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==", - "dev": true - }, - "jest-resolve": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.6.2.tgz", - "integrity": "sha512-sOxsZOq25mT1wRsfHcbtkInS+Ek7Q8jCHUB0ZUTP0tc/c41QHriU/NunqMfCUWsL4H3MHpvQD4QR9kSYhS7UvQ==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^26.6.2", - "read-pkg-up": "^7.0.1", - "resolve": "^1.18.1", - "slash": "^3.0.0" - } - }, - "jest-snapshot": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.6.2.tgz", - "integrity": "sha512-OLhxz05EzUtsAmOMzuupt1lHYXCNib0ECyuZ/PZOx9TrZcC8vL0x+DUG3TL+GLX3yHG45e6YGjIm0XwDc3q3og==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/babel__traverse": "^7.0.4", - "@types/prettier": "^2.0.0", - "chalk": "^4.0.0", - "expect": "^26.6.2", - "graceful-fs": "^4.2.4", - "jest-diff": "^26.6.2", - "jest-get-type": "^26.3.0", - "jest-haste-map": "^26.6.2", - "jest-matcher-utils": "^26.6.2", - "jest-message-util": "^26.6.2", - "jest-resolve": "^26.6.2", - "natural-compare": "^1.4.0", - "pretty-format": "^26.6.2", - "semver": "^7.3.2" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "parse-json": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", - "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "error-ex": "^1.3.1", - "json-parse-even-better-errors": "^2.3.0", - "lines-and-columns": "^1.1.6" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "read-pkg": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", - "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", - "dev": true, - "requires": { - "@types/normalize-package-data": "^2.4.0", - "normalize-package-data": "^2.5.0", - "parse-json": "^5.0.0", - "type-fest": "^0.6.0" - }, - "dependencies": { - "type-fest": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", - "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", - "dev": true - } - } - }, - "read-pkg-up": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", - "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", - "dev": true, - "requires": { - "find-up": "^4.1.0", - "read-pkg": "^5.2.0", - "type-fest": "^0.8.1" - } - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "jest-serializer": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.6.2.tgz", - "integrity": "sha512-S5wqyz0DXnNJPd/xfIzZ5Xnp1HrJWBczg8mMfMpN78OJ5eDxXyf+Ygld9wX1DnUWbIbhM1YDY95NjR4CBXkb2g==", - "dev": true, - "requires": { - "@types/node": "*", - "graceful-fs": "^4.2.4" - } - }, - "jest-snapshot": { - "version": "25.5.1", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-25.5.1.tgz", - "integrity": "sha512-C02JE1TUe64p2v1auUJ2ze5vcuv32tkv9PyhEb318e8XOKF7MOyXdJ7kdjbvrp3ChPLU2usI7Rjxs97Dj5P0uQ==", - "dev": true, - "requires": { - "@babel/types": "^7.0.0", - "@jest/types": "^25.5.0", - "@types/prettier": "^1.19.0", - "chalk": "^3.0.0", - "expect": "^25.5.0", - "graceful-fs": "^4.2.4", - "jest-diff": "^25.5.0", - "jest-get-type": "^25.2.6", - "jest-matcher-utils": "^25.5.0", - "jest-message-util": "^25.5.0", - "jest-resolve": "^25.5.1", - "make-dir": "^3.0.0", - "natural-compare": "^1.4.0", - "pretty-format": "^25.5.0", - "semver": "^6.3.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "jest-util": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-25.5.0.tgz", - "integrity": "sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "chalk": "^3.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "make-dir": "^3.0.0" - } - }, - "jest-validate": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-26.0.1.tgz", - "integrity": "sha512-u0xRc+rbmov/VqXnX3DlkxD74rHI/CfS5xaV2VpeaVySjbb1JioNVOyly5b56q2l9ZKe7bVG5qWmjfctkQb0bA==", - "dev": true, - "requires": { - "@jest/types": "^26.0.1", - "camelcase": "^6.0.0", - "chalk": "^4.0.0", - "jest-get-type": "^26.0.0", - "leven": "^3.1.0", - "pretty-format": "^26.0.1" - }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "camelcase": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", - "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - } - } - }, - "jest-watcher": { - "version": "26.0.1", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-26.0.1.tgz", - "integrity": "sha512-pdZPydsS8475f89kGswaNsN3rhP6lnC3/QDCppP7bg1L9JQz7oU9Mb/5xPETk1RHDCWeqmVC47M4K5RR7ejxFw==", - "dev": true, - "requires": { - "@jest/test-result": "^26.0.1", - "@jest/types": "^26.0.1", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "jest-util": "^26.0.1", - "string-length": "^4.0.1" - }, - "dependencies": { - "@jest/console": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.6.2.tgz", - "integrity": "sha512-IY1R2i2aLsLr7Id3S6p2BA82GNWryt4oSvEXLAKc+L2zdi89dSkE8xC1C+0kpATG4JhBJREnQOH7/zmccM2B0g==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "jest-message-util": "^26.6.2", - "jest-util": "^26.6.2", - "slash": "^3.0.0" - } - }, - "@jest/test-result": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.6.2.tgz", - "integrity": "sha512-5O7H5c/7YlojphYNrK02LlDIV2GNPYisKwHm2QTKjNZeEzezCbwYs9swJySv2UfPMyZ0VdsmMv7jIlD/IKYQpQ==", - "dev": true, - "requires": { - "@jest/console": "^26.6.2", - "@jest/types": "^26.6.2", - "@types/istanbul-lib-coverage": "^2.0.0", - "collect-v8-coverage": "^1.0.0" - } - }, - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/stack-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.0.tgz", - "integrity": "sha512-RJJrrySY7A8havqpGObOB4W92QXKJo63/jFLLgpvOtsGUqbQZ9Sbgl35KMm1DjC6j7AvmmU2bIno+3IyEaemaw==", - "dev": true - }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - }, - "jest-message-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.6.2.tgz", - "integrity": "sha512-rGiLePzQ3AzwUshu2+Rn+UMFk0pHN58sOG+IaJbk5Jxuqo3NYO1U2/MIR4S1sKgsoYSXSzdtSa0TgrmtUwEbmA==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.0.0", - "@jest/types": "^26.6.2", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "micromatch": "^4.0.2", - "pretty-format": "^26.6.2", - "slash": "^3.0.0", - "stack-utils": "^2.0.2" - } - }, - "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "stack-utils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.3.tgz", - "integrity": "sha512-gL//fkxfWUsIlFL2Tl42Cl6+HFALEaB1FU76I/Fy+oZjRreP7OPMXFlGbxM7NQsI0ZpUfw76sHnv0WNYuTb7Iw==", - "dev": true, - "requires": { - "escape-string-regexp": "^2.0.0" - } - } - } - }, - "jest-worker": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.6.2.tgz", - "integrity": "sha512-KWYVV1c4i+jbMpaBC+U++4Va0cp8OisU185o73T1vo99hqi7w8tSJfUXYswwqqrjzwxa6KpRK54WhPvwf5w6PQ==", - "dev": true, - "requires": { - "@types/node": "*", - "merge-stream": "^2.0.0", - "supports-color": "^7.0.0" - } - }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.13.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz", - "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==", - "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" - } - }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "dev": true - }, - "jsdom": { - "version": "16.2.2", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-16.2.2.tgz", - "integrity": "sha512-pDFQbcYtKBHxRaP55zGXCJWgFHkDAYbKcsXEK/3Icu9nKYZkutUXfLBwbD+09XDutkYSHcgfQLZ0qvpAAm9mvg==", - "dev": true, - "requires": { - "abab": "^2.0.3", - "acorn": "^7.1.1", - "acorn-globals": "^6.0.0", - "cssom": "^0.4.4", - "cssstyle": "^2.2.0", - "data-urls": "^2.0.0", - "decimal.js": "^10.2.0", - "domexception": "^2.0.1", - "escodegen": "^1.14.1", - "html-encoding-sniffer": "^2.0.1", - "is-potential-custom-element-name": "^1.0.0", - "nwsapi": "^2.2.0", - "parse5": "5.1.1", - "request": "^2.88.2", - "request-promise-native": "^1.0.8", - "saxes": "^5.0.0", - "symbol-tree": "^3.2.4", - "tough-cookie": "^3.0.1", - "w3c-hr-time": "^1.0.2", - "w3c-xmlserializer": "^2.0.0", - "webidl-conversions": "^6.0.0", - "whatwg-encoding": "^1.0.5", - "whatwg-mimetype": "^2.3.0", - "whatwg-url": "^8.0.0", - "ws": "^7.2.3", - "xml-name-validator": "^3.0.0" - } - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "json-parse-even-better-errors": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "dev": true - }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", - "dev": true - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true - }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", - "dev": true - }, - "json5": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", - "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", - "dev": true, - "requires": { - "minimist": "^1.2.5" - } - }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.6" - } - }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "dev": true, - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true - }, - "kleur": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", - "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", - "dev": true - }, - "leven": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", - "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", - "dev": true - }, - "levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, - "requires": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - } - }, - "lines-and-columns": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz", - "integrity": "sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=", - "dev": true - }, - "load-json-file": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", - "integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", - "dev": true, - "requires": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "strip-bom": "^3.0.0" - }, - "dependencies": { - "strip-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", - "integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", - "dev": true - } - } - }, - "locate-path": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", - "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", - "dev": true, - "requires": { - "p-locate": "^4.1.0" - } - }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true - }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.memoize": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", - "integrity": "sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=", - "dev": true - }, - "lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.sortby": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true - }, - "lolex": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", - "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==", - "dev": true, - "requires": { - "@sinonjs/commons": "^1.7.0" - } - }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } - }, - "lua-types": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/lua-types/-/lua-types-2.8.0.tgz", - "integrity": "sha512-FJY32giHIqD/XW1XGkJnl8XotXIJsJ2M42fj9A2UudttWA6orJioToW1OpgPdayTr+S1/oTO7i+hfBY3UVG8Fg==", - "dev": true - }, - "lua-wasm-bindings": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/lua-wasm-bindings/-/lua-wasm-bindings-0.2.2.tgz", - "integrity": "sha512-Z2v3An8jEvYbhUJ/gYxNd65ZBbaRyPMmUaleDoOhzJSIGrMgjURvy1EtHtgRGTzdtpmKtHA+YYKDrSZBE9g/ig==", - "dev": true - }, - "make-dir": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", - "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", - "dev": true, - "requires": { - "semver": "^6.0.0" - }, - "dependencies": { - "semver": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", - "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", - "dev": true - } - } - }, - "make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "makeerror": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.11.tgz", - "integrity": "sha1-4BpckQnyr3lmDk6LlYd5AYT1qWw=", - "dev": true, - "requires": { - "tmpl": "1.0.x" - } - }, - "map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", - "dev": true - }, - "map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", - "dev": true, - "requires": { - "object-visit": "^1.0.0" - } - }, - "merge-stream": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", - "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true - }, - "merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true - }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", - "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" - } - }, - "mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", - "dev": true - }, - "mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", - "dev": true, - "requires": { - "mime-db": "1.44.0" - } - }, - "mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true - }, - "minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", - "dev": true, - "requires": { - "brace-expansion": "^1.1.7" - } - }, - "minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true - }, - "mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "requires": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "dependencies": { - "is-extendable": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", - "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "dev": true, - "requires": { - "is-plain-object": "^2.0.4" - } - } - } - }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - } - }, - "natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true - }, - "nice-try": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", - "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", - "dev": true - }, - "node-int64": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", - "integrity": "sha1-h6kGXNs1XTGC2PlM4RGIuCXGijs=", - "dev": true - }, - "node-modules-regexp": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", - "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", - "dev": true - }, - "node-notifier": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/node-notifier/-/node-notifier-7.0.1.tgz", - "integrity": "sha512-VkzhierE7DBmQEElhTGJIoiZa1oqRijOtgOlsXg32KrJRXsPy0NXFBqWGW/wTswnJlDCs5viRYaqWguqzsKcmg==", - "dev": true, - "optional": true, - "requires": { - "growly": "^1.3.0", - "is-wsl": "^2.1.1", - "semver": "^7.2.1", - "shellwords": "^0.1.1", - "uuid": "^7.0.3", - "which": "^2.0.2" - }, - "dependencies": { - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "optional": true, - "requires": { - "lru-cache": "^6.0.0" - } - }, - "which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "optional": true, - "requires": { - "isexe": "^2.0.0" - } - } - } - }, - "normalize-package-data": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", - "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", - "dev": true, - "requires": { - "hosted-git-info": "^2.1.4", - "resolve": "^1.10.0", - "semver": "2 || 3 || 4 || 5", - "validate-npm-package-license": "^3.0.1" - } - }, - "normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true - }, - "npm-run-path": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", - "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", - "dev": true, - "requires": { - "path-key": "^2.0.0" - } - }, - "nwsapi": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.0.tgz", - "integrity": "sha512-h2AatdwYH+JHiZpv7pt/gSX1XoRGb7L/qSIeuqA6GwYoF9w1vP1cw42TO0aI2pNyshRK5893hNSl+1//vHK7hQ==", - "dev": true - }, - "oauth-sign": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", - "dev": true - }, - "object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", - "dev": true, - "requires": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "object-inspect": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", - "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==", - "dev": true - }, - "object-keys": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", - "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true - }, - "object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", - "dev": true, - "requires": { - "isobject": "^3.0.0" - } - }, - "object.assign": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", - "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", - "dev": true, - "requires": { - "define-properties": "^1.1.2", - "function-bind": "^1.1.1", - "has-symbols": "^1.0.0", - "object-keys": "^1.0.11" - } - }, - "object.pick": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", - "dev": true, - "requires": { - "isobject": "^3.0.1" - } - }, - "object.values": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz", - "integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "es-abstract": "^1.17.0-next.1", - "function-bind": "^1.1.1", - "has": "^1.0.3" - } - }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", - "dev": true, - "requires": { - "wrappy": "1" - } - }, - "onetime": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.0.tgz", - "integrity": "sha512-5NcSkPHhwTVFIQN+TUqXoS5+dlElHXdpAWu9I0HP20YOtIi+aZ0Ct82jdlILDxjLEAWwvm+qj1m6aEtsDVmm6Q==", - "dev": true, - "requires": { - "mimic-fn": "^2.1.0" - } - }, - "optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", - "dev": true, - "requires": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - } - }, - "p-each-series": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/p-each-series/-/p-each-series-2.1.0.tgz", - "integrity": "sha512-ZuRs1miPT4HrjFa+9fRfOFXxGJfORgelKV9f9nNOWw2gl6gVsRaVDOQP0+MI0G0wGKns1Yacsu0GjOFbTK0JFQ==", - "dev": true - }, - "p-finally": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", - "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", - "dev": true - }, - "p-limit": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", - "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", - "dev": true, - "requires": { - "p-try": "^2.0.0" - } - }, - "p-locate": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", - "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", - "dev": true, - "requires": { - "p-limit": "^2.2.0" - } - }, - "p-try": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", - "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", - "dev": true - }, - "parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "requires": { - "callsites": "^3.0.0" - } - }, - "parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", - "dev": true, - "requires": { - "error-ex": "^1.2.0" - } - }, - "parse5": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", - "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", - "dev": true - }, - "pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", - "dev": true - }, - "path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "path-key": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", - "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", - "dev": true - }, - "path-parse": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", - "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" - }, - "path-type": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", - "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", - "dev": true, - "requires": { - "pify": "^2.0.0" - } - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=", - "dev": true - }, - "picomatch": { - "version": "2.2.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.3.tgz", - "integrity": "sha512-KpELjfwcCDUb9PeigTs2mBJzXUPzAuP2oPcA989He8Rte0+YUAjw1JVedDhuTKPkHjSYzMN3npC9luThGYEKdg==", - "dev": true - }, - "pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true - }, - "pirates": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", - "integrity": "sha512-WuNqLTbMI3tmfef2TKxlQmAiLHKtFhlsCZnPIpuv2Ow0RDVO8lfy1Opf4NUzlMXLjPl+Men7AuVdX6TA+s+uGA==", - "dev": true, - "requires": { - "node-modules-regexp": "^1.0.0" - } - }, - "pkg-dir": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", - "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", - "dev": true, - "requires": { - "find-up": "^4.0.0" - } - }, - "posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", - "dev": true - }, - "prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true - }, - "prettier": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.0.5.tgz", - "integrity": "sha512-7PtVymN48hGcO4fGjybyBSIWDsLU4H4XlvOHfq91pz9kkGlonzwTfYkaIEwiRg/dAJF9YlbsduBAgtYLi+8cFg==", - "dev": true - }, - "pretty-format": { - "version": "25.5.0", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-25.5.0.tgz", - "integrity": "sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==", - "dev": true, - "requires": { - "@jest/types": "^25.5.0", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^16.12.0" - } - }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "prompts": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.3.2.tgz", - "integrity": "sha512-Q06uKs2CkNYVID0VqwfAl9mipo99zkBv/n2JtWY89Yxa3ZabWSrs0e2KTudKVa3peLUvYXMefDqIleLPVUBZMA==", - "dev": true, - "requires": { - "kleur": "^3.0.3", - "sisteransi": "^1.0.4" - } - }, - "psl": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", - "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==", - "dev": true - }, - "pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "requires": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true - }, - "qs": { - "version": "6.5.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", - "dev": true - }, - "queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true - }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true - }, - "read-pkg": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", - "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", - "dev": true, - "requires": { - "load-json-file": "^2.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^2.0.0" - } - }, - "read-pkg-up": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", - "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", - "dev": true, - "requires": { - "find-up": "^2.0.0", - "read-pkg": "^2.0.0" - }, - "dependencies": { - "find-up": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", - "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", - "dev": true, - "requires": { - "locate-path": "^2.0.0" - } - }, - "locate-path": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", - "integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=", - "dev": true, - "requires": { - "p-locate": "^2.0.0", - "path-exists": "^3.0.0" - } - }, - "p-limit": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", - "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", - "dev": true, - "requires": { - "p-try": "^1.0.0" - } - }, - "p-locate": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", - "integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=", - "dev": true, - "requires": { - "p-limit": "^1.1.0" - } - }, - "p-try": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", - "integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=", - "dev": true - }, - "path-exists": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", - "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", - "dev": true - } - } - }, - "realpath-native": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/realpath-native/-/realpath-native-2.0.0.tgz", - "integrity": "sha512-v1SEYUOXXdbBZK8ZuNgO4TBjamPsiSgcFr0aP+tEKpQZK8vooEUqV6nm6Cv502mX4NF2EfsnVqtNAHG+/6Ur1Q==", - "dev": true - }, - "regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", - "dev": true, - "requires": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" - } - }, - "regexpp": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz", - "integrity": "sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==", - "dev": true - }, - "remove-trailing-separator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", - "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", - "dev": true - }, - "repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true - }, - "repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", - "dev": true - }, - "request": { - "version": "2.88.2", - "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", - "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", - "dev": true, - "requires": { - "aws-sign2": "~0.7.0", - "aws4": "^1.8.0", - "caseless": "~0.12.0", - "combined-stream": "~1.0.6", - "extend": "~3.0.2", - "forever-agent": "~0.6.1", - "form-data": "~2.3.2", - "har-validator": "~5.1.3", - "http-signature": "~1.2.0", - "is-typedarray": "~1.0.0", - "isstream": "~0.1.2", - "json-stringify-safe": "~5.0.1", - "mime-types": "~2.1.19", - "oauth-sign": "~0.9.0", - "performance-now": "^2.1.0", - "qs": "~6.5.2", - "safe-buffer": "^5.1.2", - "tough-cookie": "~2.5.0", - "tunnel-agent": "^0.6.0", - "uuid": "^3.3.2" - }, - "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - }, - "uuid": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", - "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==", - "dev": true - } - } - }, - "request-promise-core": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.3.tgz", - "integrity": "sha512-QIs2+ArIGQVp5ZYbWD5ZLCY29D5CfWizP8eWnm8FoGD1TX61veauETVQbrV60662V0oFBkrDOuaBI8XgtuyYAQ==", - "dev": true, - "requires": { - "lodash": "^4.17.15" - } - }, - "request-promise-native": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.8.tgz", - "integrity": "sha512-dapwLGqkHtwL5AEbfenuzjTYg35Jd6KPytsC2/TLkVMz8rm+tNt72MGUWT1RP/aYawMpN6HqbNGBQaRcBtjQMQ==", - "dev": true, - "requires": { - "request-promise-core": "1.1.3", - "stealthy-require": "^1.1.1", - "tough-cookie": "^2.3.3" - }, - "dependencies": { - "tough-cookie": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", - "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", - "dev": true, - "requires": { - "psl": "^1.1.28", - "punycode": "^2.1.1" - } - } - } - }, - "require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true - }, - "require-main-filename": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", - "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", - "dev": true - }, - "resolve": { - "version": "1.20.0", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", - "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", - "requires": { - "is-core-module": "^2.2.0", - "path-parse": "^1.0.6" - } - }, - "resolve-cwd": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", - "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", - "dev": true, - "requires": { - "resolve-from": "^5.0.0" - } - }, - "resolve-from": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", - "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", - "dev": true - }, - "resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", - "dev": true - }, - "ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", - "dev": true - }, - "reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true - }, - "rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - }, - "rsvp": { - "version": "4.8.5", - "resolved": "https://registry.npmjs.org/rsvp/-/rsvp-4.8.5.tgz", - "integrity": "sha512-nfMOlASu9OnRJo1mbEk2cz0D56a1MBNrJ7orjRZQG10XDyuvwksKbuXNp6qa+kbn839HwjwhBzhFmdsaEAfauA==", - "dev": true - }, - "run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "requires": { - "queue-microtask": "^1.2.2" - } - }, - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - }, - "safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", - "dev": true, - "requires": { - "ret": "~0.1.10" - } - }, - "safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, - "sane": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/sane/-/sane-4.1.0.tgz", - "integrity": "sha512-hhbzAgTIX8O7SHfp2c8/kREfEn4qO/9q8C9beyY6+tvZ87EpoZ3i1RIEvp27YBswnNbY9mWd6paKVmKbAgLfZA==", - "dev": true, - "requires": { - "@cnakazawa/watch": "^1.0.3", - "anymatch": "^2.0.0", - "capture-exit": "^2.0.0", - "exec-sh": "^0.3.2", - "execa": "^1.0.0", - "fb-watchman": "^2.0.0", - "micromatch": "^3.1.4", - "minimist": "^1.1.1", - "walker": "~1.0.5" - }, - "dependencies": { - "anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "requires": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "requires": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", - "dev": true, - "requires": { - "kind-of": "^3.0.2" - }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } - } - }, - "micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "requires": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - } - }, - "normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", - "dev": true, - "requires": { - "remove-trailing-separator": "^1.0.1" - } - }, - "to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", - "dev": true, - "requires": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - } - } - } - }, - "saxes": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/saxes/-/saxes-5.0.1.tgz", - "integrity": "sha512-5LBh1Tls8c9xgGjw3QrMwETmTMVk0oFgvrFSvWx62llR2hcEInrKNZ2GZCCuuy2lvWrdl5jhbpeqc5hRYKFOcw==", - "dev": true, - "requires": { - "xmlchars": "^2.2.0" - } - }, - "semver": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", - "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", - "dev": true - }, - "set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", - "dev": true - }, - "set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "requires": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "dependencies": { - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - } - } - }, - "shebang-command": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", - "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", - "dev": true, - "requires": { - "shebang-regex": "^1.0.0" - } - }, - "shebang-regex": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", - "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", - "dev": true - }, - "shellwords": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/shellwords/-/shellwords-0.1.1.tgz", - "integrity": "sha512-vFwSUfQvqybiICwZY5+DAWIPLKsWO31Q91JSKl3UYv+K5c2QRPzn0qzec6QPu1Qc9eHYItiP3NdJqNVqetYAww==", - "dev": true, - "optional": true - }, - "signal-exit": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", - "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", - "dev": true - }, - "sisteransi": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", - "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true - }, - "slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true - }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - } - }, - "snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "requires": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "requires": { - "ms": "2.0.0" - } - }, - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - }, - "extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", - "dev": true, - "requires": { - "is-extendable": "^0.1.0" - } - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", - "dev": true - }, - "source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", - "dev": true - } + "license": "MIT", + "engines": { + "node": ">=10" } }, - "snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", + "node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "dev": true, - "requires": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, - "dependencies": { - "define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", - "dev": true, - "requires": { - "is-descriptor": "^1.0.0" - } - }, - "is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "dev": true, - "requires": { - "kind-of": "^6.0.0" - } - }, - "is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "requires": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - } - } + "engines": { + "node": ">=10" } }, - "snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { - "kind-of": "^3.2.0" - }, + "license": "MIT", "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } - } + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "source-map": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz", - "integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==" - }, - "source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, - "requires": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "source-map-support": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.6.tgz", - "integrity": "sha512-N4KXEz7jcKqPf2b2vZF11lQIz9W5ZMuUcIOGj243lduidkf2fjkVKJS9vNxVWn3u/uxX38AcE8U9nnH9FPcq+g==", + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true, - "requires": { - "buffer-from": "^1.0.0", - "source-map": "^0.6.0" - }, - "dependencies": { - "source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true - } - } - }, - "source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", - "dev": true + "license": "ISC" }, - "spdx-correct": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.0.tgz", - "integrity": "sha512-lr2EZCctC2BNR7j7WzJ2FpDznxky1sjfxvvYEyzxNyb6lZXHODmEoJeFu4JupYlkfha1KZpJyoqiJ7pgA1qq8Q==", + "node_modules/sisteransi": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", + "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true, - "requires": { - "spdx-expression-parse": "^3.0.0", - "spdx-license-ids": "^3.0.0" - } - }, - "spdx-exceptions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.2.0.tgz", - "integrity": "sha512-2XQACfElKi9SlVb1CYadKDXvoajPgBVPn/gOQLrTvHdElaVhr7ZEbqJaRnJLVNeaI4cMEAgVCeBMKF6MWRDCRA==", - "dev": true + "license": "MIT" }, - "spdx-expression-parse": { + "node_modules/slash": { "version": "3.0.0", - "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.0.tgz", - "integrity": "sha512-Yg6D3XpRD4kkOmTpdgbUiEJFKghJH03fiC1OPll5h/0sO6neh2jqRDVHOQ4o/LMea0tgCkbMgea5ip/e+MkWyg==", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, - "requires": { - "spdx-exceptions": "^2.1.0", - "spdx-license-ids": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "spdx-license-ids": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz", - "integrity": "sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==", - "dev": true + "node_modules/source-map": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", + "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 8" + } }, - "split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", + "node_modules/source-map-support": { + "version": "0.5.13", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", + "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, - "requires": { - "extend-shallow": "^3.0.0" + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" } }, - "sshpk": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", - "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, - "requires": { - "asn1": "~0.2.3", - "assert-plus": "^1.0.0", - "bcrypt-pbkdf": "^1.0.0", - "dashdash": "^1.12.0", - "ecc-jsbn": "~0.1.1", - "getpass": "^0.1.1", - "jsbn": "~0.1.0", - "safer-buffer": "^2.0.2", - "tweetnacl": "~0.14.0" + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" } }, - "stack-utils": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-1.0.5.tgz", - "integrity": "sha512-KZiTzuV3CnSnSvgMRrARVCj+Ht7rMbauGDK0LdVFRGyenwdylpajAp4Q0i6SX8rEmbTpMMf6ryq2gb8pPq2WgQ==", + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true, - "requires": { + "license": "BSD-3-Clause" + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "dev": true, + "license": "MIT", + "dependencies": { "escape-string-regexp": "^2.0.0" }, - "dependencies": { - "escape-string-regexp": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", - "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", - "dev": true - } + "engines": { + "node": ">=10" } }, - "static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, - "requires": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "dependencies": { - "define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", - "dev": true, - "requires": { - "is-descriptor": "^0.1.0" - } - } + "license": "MIT", + "engines": { + "node": ">=8" } }, - "stealthy-require": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true - }, - "string-length": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.1.tgz", - "integrity": "sha512-PKyXUd0LK0ePjSOnWn34V2uD6acUWev9uy0Ft05k0E8xRW+SKcA0F7eMr7h5xlzfn+4O3N+55rduYyet3Jk+jw==", + "node_modules/string-length": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", + "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" } }, - "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" - } - }, - "string.prototype.trimleft": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimleft/-/string.prototype.trimleft-2.1.1.tgz", - "integrity": "sha512-iu2AGd3PuP5Rp7x2kEZCrB2Nf41ehzh+goo8TV7z8/XDBbsvc6HQIlUl9RjkZ4oyrW1XM5UwlGl1oVEaDjg6Ag==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" - } - }, - "string.prototype.trimright": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string.prototype.trimright/-/string.prototype.trimright-2.1.1.tgz", - "integrity": "sha512-qFvWL3/+QIgZXVmJBfpHmxLB7xsUXz6HsUmP8+5dRaC3Q7oKUv9Vo6aMCRZC1smrtyECFsIT30PqBJ1gTjAs+g==", - "dev": true, - "requires": { - "define-properties": "^1.1.3", - "function-bind": "^1.1.1" + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "requires": { - "ansi-regex": "^5.0.0" + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-bom": { + "node_modules/strip-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", - "dev": true - }, - "strip-eof": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", - "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "strip-final-newline": { + "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "strip-json-comments": { + "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "supports-color": { + "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "supports-hyperlinks": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-2.1.0.tgz", - "integrity": "sha512-zoE5/e+dnEijk6ASB6/qrK+oYdm2do1hjoLWrqUC/8WEIW1gbxFcKuBof7sW8ArN6e+AYvsE8HBGiVRWL/F5CA==", - "dev": true, - "requires": { - "has-flag": "^4.0.0", - "supports-color": "^7.0.0" - } - }, - "symbol-tree": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", - "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", - "dev": true - }, - "table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" }, - "dependencies": { - "ajv": { - "version": "8.6.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.0.tgz", - "integrity": "sha512-cnUG4NSBiM4YFBxgZIj/In3/6KX+rQ2l2YPRVcvAMQGWEPKuXoPIhxzwqh31jA3IPbI4qEOp/5ILI4ynioXsGQ==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "tapable": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", - "integrity": "sha512-FBk4IesMV1rBxX2tfiK8RAmogtWn53puLOQlvO8XuwlgxcYbP4mVPS9Ph4aeamSyyVjOl24aYWAuc8U5kCVwMw==" - }, - "terminal-link": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-2.1.1.tgz", - "integrity": "sha512-un0FmiRUQNr5PJqy9kP7c40F5BOfpGlYTrxonDChEZB7pzZxRNp/bt+ymiy9/npwXya9KH99nJ/GXFIiUkYGFQ==", - "dev": true, - "requires": { - "ansi-escapes": "^4.2.1", - "supports-hyperlinks": "^2.0.0" + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "license": "MIT", + "engines": { + "node": ">=6" } }, - "test-exclude": { + "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, - "requires": { + "license": "ISC", + "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" } }, - "text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true - }, - "throat": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", - "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", - "dev": true - }, - "tmpl": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.4.tgz", - "integrity": "sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE=", - "dev": true - }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } }, - "to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, - "requires": { - "kind-of": "^3.0.2" + "license": "MIT", + "engines": { + "node": ">=12.0.0" }, - "dependencies": { - "kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", - "dev": true, - "requires": { - "is-buffer": "^1.1.5" - } + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true } } }, - "to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, - "requires": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "to-regex-range": { + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "tough-cookie": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", - "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, - "requires": { - "ip-regex": "^2.1.0", - "psl": "^1.1.28", - "punycode": "^2.1.1" + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" } }, - "tr46": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-2.0.2.tgz", - "integrity": "sha512-3n1qG+/5kg+jrbTzwAykB5yRYtQCTqOGKq5U5PE3b0a1/mzo6snDhjGS0zJVJunO0NrT3Dg1MLy5TjWP/UJppg==", - "dev": true, - "requires": { - "punycode": "^2.1.1" - } - }, - "ts-jest": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-26.3.0.tgz", - "integrity": "sha512-Jq2uKfx6bPd9+JDpZNMBJMdMQUC3sJ08acISj8NXlVgR2d5OqslEHOR2KHMgwymu8h50+lKIm0m0xj/ioYdW2Q==", - "dev": true, - "requires": { - "@types/jest": "26.x", - "bs-logger": "0.x", - "buffer-from": "1.x", - "fast-json-stable-stringify": "2.x", - "jest-util": "26.x", - "json5": "2.x", - "lodash.memoize": "4.x", - "make-error": "1.x", - "mkdirp": "1.x", - "semver": "7.x", - "yargs-parser": "18.x" + "node_modules/ts-jest": { + "version": "29.4.9", + "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.9.tgz", + "integrity": "sha512-LTb9496gYPMCqjeDLdPrKuXtncudeV1yRZnF4Wo5l3SFi0RYEnYRNgMrFIdg+FHvfzjCyQk1cLncWVqiSX+EvQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bs-logger": "^0.2.6", + "fast-json-stable-stringify": "^2.1.0", + "handlebars": "^4.7.9", + "json5": "^2.2.3", + "lodash.memoize": "^4.1.2", + "make-error": "^1.3.6", + "semver": "^7.7.4", + "type-fest": "^4.41.0", + "yargs-parser": "^21.1.1" + }, + "bin": { + "ts-jest": "cli.js" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" }, - "dependencies": { - "@jest/types": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.6.2.tgz", - "integrity": "sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.0", - "@types/istanbul-reports": "^3.0.0", - "@types/node": "*", - "@types/yargs": "^15.0.0", - "chalk": "^4.0.0" - } - }, - "@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", - "dev": true, - "requires": { - "@types/istanbul-lib-report": "*" - } - }, - "@types/jest": { - "version": "26.0.23", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.23.tgz", - "integrity": "sha512-ZHLmWMJ9jJ9PTiT58juykZpL7KjwJywFN3Rr2pTSkyQfydf/rk22yS7W8p5DaVUMQ2BQC7oYiU3FjbTM/mYrOA==", - "dev": true, - "requires": { - "jest-diff": "^26.0.0", - "pretty-format": "^26.0.0" - } + "peerDependencies": { + "@babel/core": ">=7.0.0-beta.0 <8", + "@jest/transform": "^29.0.0 || ^30.0.0", + "@jest/types": "^29.0.0 || ^30.0.0", + "babel-jest": "^29.0.0 || ^30.0.0", + "jest": "^29.0.0 || ^30.0.0", + "jest-util": "^29.0.0 || ^30.0.0", + "typescript": ">=4.3 <7" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true }, - "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", - "dev": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } + "@jest/transform": { + "optional": true }, - "diff-sequences": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.6.2.tgz", - "integrity": "sha512-Mv/TDa3nZ9sbc5soK+OoA74BsS3mL37yixCvUAQkiuA4Wz6YtwP/K47n2rv2ovzHZvoiQeA5FTQOschKkEwB0Q==", - "dev": true + "@jest/types": { + "optional": true }, - "jest-diff": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.6.2.tgz", - "integrity": "sha512-6m+9Z3Gv9wN0WFVasqjCL/06+EFCMTqDEUl/b87HYK2rAPTyfz4ZIuSlPhY51PIQRWx5TaxeF1qmXKe9gfN3sA==", - "dev": true, - "requires": { - "chalk": "^4.0.0", - "diff-sequences": "^26.6.2", - "jest-get-type": "^26.3.0", - "pretty-format": "^26.6.2" - } + "babel-jest": { + "optional": true }, - "jest-get-type": { - "version": "26.3.0", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.3.0.tgz", - "integrity": "sha512-TpfaviN1R2pQWkIihlfEanwOXK0zcxrKEE4MlU6Tn7keoXdN6/3gK/xl0yEh8DOunn5pOVGKf8hB4R9gVh04ig==", - "dev": true + "esbuild": { + "optional": true }, "jest-util": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-26.6.2.tgz", - "integrity": "sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "@types/node": "*", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.4", - "is-ci": "^2.0.0", - "micromatch": "^4.0.2" - } - }, - "mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true - }, - "pretty-format": { - "version": "26.6.2", - "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", - "integrity": "sha512-7AeGuCYNGmycyQbCqd/3PWH4eOoX/OiCa0uphp57NVTeAGdJGaAliecxwBDHYQCIvrW7aDBZCYeNTP/WX69mkg==", - "dev": true, - "requires": { - "@jest/types": "^26.6.2", - "ansi-regex": "^5.0.0", - "ansi-styles": "^4.0.0", - "react-is": "^17.0.1" - } - }, - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", - "dev": true - }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", - "dev": true, - "requires": { - "lru-cache": "^6.0.0" - } + "optional": true } } }, - "ts-node": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.6.2.tgz", - "integrity": "sha512-4mZEbofxGqLL2RImpe3zMJukvEvcO1XP8bj8ozBPySdCUXEcU5cIRwR0aM3R+VoZq7iXc8N86NC0FspGRqP4gg==", + "node_modules/ts-jest/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, - "requires": { + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "peer": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", "arg": "^4.1.0", + "create-require": "^1.1.0", "diff": "^4.0.1", "make-error": "^1.1.1", - "source-map-support": "^0.5.6", + "v8-compile-cache-lib": "^3.0.1", "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } } }, - "tslib": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.0.tgz", - "integrity": "sha512-BmndXUtiTn/VDDrJzQE7Mm22Ix3PxgLltW9bSNLoeCY31gnG2OPx0QqJnuc9oMIKioYrz487i6K9o4Pdn0j+Kg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "dev": true, - "requires": { - "safe-buffer": "^5.0.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "dev": true - }, - "type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { - "prelude-ls": "~1.1.2" + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "type-detect": { + "node_modules/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", - "dev": true - }, - "type-fest": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", - "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", - "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", "dev": true, - "requires": { - "is-typedarray": "^1.0.0" + "license": "MIT", + "engines": { + "node": ">=4" } }, - "typescript": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.2.tgz", - "integrity": "sha512-zZ4hShnmnoVnAHpVHWpTcxdv7dWP60S2FsydQLV8V5PbS3FifjWFFRiHSWpDJahly88PRyV5teTSLoq4eG7mKw==" - }, - "union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, - "requires": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true - }, - "unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "node_modules/typescript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.2.tgz", + "integrity": "sha512-bGdAIrZ0wiGDo5l8c++HWtbaNCWTS4UTv7RaTH/ThVIgjkveJt83m74bBHMJkuCbslY8ixgLBVZJIOiQlQTjfQ==", "dev": true, - "requires": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" }, - "dependencies": { - "has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", - "dev": true, - "requires": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "dependencies": { - "isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", - "dev": true, - "requires": { - "isarray": "1.0.0" - } - } - } - }, - "has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", - "dev": true - } + "engines": { + "node": ">=14.17" } }, - "uri-js": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", - "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "node_modules/typescript-eslint": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", "dev": true, - "requires": { - "punycode": "^2.1.0" + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" } }, - "urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", - "dev": true - }, - "use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true - }, - "uuid": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz", - "integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==", + "node_modules/uglify-js": { + "version": "3.19.3", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.19.3.tgz", + "integrity": "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==", "dev": true, - "optional": true + "license": "BSD-2-Clause", + "optional": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } }, - "v8-compile-cache": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz", - "integrity": "sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g==", + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true }, - "v8-to-istanbul": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-4.1.4.tgz", - "integrity": "sha512-Rw6vJHj1mbdK8edjR7+zuJrpDtKIgNdAvTSAcpYfgMIw+u2dPDntD3dgN4XQFLU2/fvFQdzj+EeSGfd/jnY5fQ==", - "dev": true, - "requires": { - "@types/istanbul-lib-coverage": "^2.0.1", - "convert-source-map": "^1.6.0", - "source-map": "^0.7.3" - } - }, - "validate-npm-package-license": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", - "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, - "requires": { - "spdx-correct": "^3.0.0", - "spdx-expression-parse": "^3.0.0" + "license": "MIT", + "engines": { + "node": ">= 4.0.0" } }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "node_modules/update-browserslist-db": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", + "integrity": "sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==", "dev": true, - "requires": { - "assert-plus": "^1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "^1.2.0" + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.1.2", + "picocolors": "^1.0.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" } }, - "w3c-hr-time": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", - "integrity": "sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { - "browser-process-hrtime": "^1.0.0" + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" } }, - "w3c-xmlserializer": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-2.0.0.tgz", - "integrity": "sha512-4tzD0mF8iSiMiNs30BiLO3EpfGLZUT2MSX/G+o7ZywDzliWQ3OPtTZ0PTC3B3ca1UAf4cJMHB+2Bf56EriJuRA==", + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", "dev": true, - "requires": { - "xml-name-validator": "^3.0.0" - } + "license": "MIT" }, - "walker": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", - "integrity": "sha1-L3+bj9ENZ3JisYqITijRlhjgKPs=", + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, - "requires": { - "makeerror": "1.0.x" + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" } }, - "webidl-conversions": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-6.1.0.tgz", - "integrity": "sha512-qBIvFLGiBpLjfwmYAaHPXsn+ho5xZnGvyGvsarywGNc8VyQJUMHJ8OBKGGrPER0okBeMDaan4mNBlgBROxuI8w==", - "dev": true - }, - "whatwg-encoding": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", - "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, - "requires": { - "iconv-lite": "0.4.24" + "license": "Apache-2.0", + "dependencies": { + "makeerror": "1.0.12" } }, - "whatwg-mimetype": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", - "dev": true - }, - "whatwg-url": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-8.1.0.tgz", - "integrity": "sha512-vEIkwNi9Hqt4TV9RdnaBPNt+E2Sgmo3gePebCRgZ1R7g6d23+53zCTnuB0amKI4AXq6VM8jj2DUAa0S1vjJxkw==", + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^2.0.2", - "webidl-conversions": "^5.0.0" - }, + "license": "ISC", "dependencies": { - "webidl-conversions": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz", - "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==", - "dev": true - } + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, - "requires": { - "isexe": "^2.0.0" + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "which-module": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", - "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", - "dev": true - }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "license": "MIT" }, - "wrap-ansi": { - "version": "6.2.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", - "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==", + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "wrappy": { + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", - "dev": true + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" }, - "write-file-atomic": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-3.0.3.tgz", - "integrity": "sha512-AvHcyZ5JnSfq3ioSyjrBkH9yW4m7Ayk8/9My/DD9onKeu/94fwrMocemO2QAJFAlnnDN+ZDS+ZjAR5ua1/PV/Q==", + "node_modules/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, - "requires": { + "license": "ISC", + "dependencies": { "imurmurhash": "^0.1.4", - "is-typedarray": "^1.0.0", - "signal-exit": "^3.0.2", - "typedarray-to-buffer": "^3.1.5" + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, - "ws": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", - "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, - "requires": {} - }, - "xml-name-validator": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true - }, - "xmlchars": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", - "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", - "dev": true - }, - "y18n": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.1.tgz", - "integrity": "sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ==", - "dev": true + "license": "ISC", + "engines": { + "node": ">=10" + } }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" }, - "yargs": { - "version": "15.3.1", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz", - "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==", + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, - "requires": { - "cliui": "^6.0.0", - "decamelize": "^1.2.0", - "find-up": "^4.1.0", - "get-caller-file": "^2.0.1", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^2.0.0", - "set-blocking": "^2.0.0", - "string-width": "^4.2.0", - "which-module": "^2.0.0", - "y18n": "^4.0.0", - "yargs-parser": "^18.1.1" + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" } }, - "yargs-parser": { - "version": "18.1.3", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz", - "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==", + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, - "requires": { - "camelcase": "^5.0.0", - "decamelize": "^1.2.0" + "license": "ISC", + "engines": { + "node": ">=12" } }, - "yn": { + "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 5cba2dd38..504dad78f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "typescript-to-lua", - "version": "0.40.0", + "version": "1.37.0", "description": "A generic TypeScript to Lua transpiler. Write your code in TypeScript and publish Lua!", "repository": "https://github.com/TypeScriptToLua/TypeScriptToLua", "homepage": "https://typescripttolua.github.io/", @@ -18,20 +18,20 @@ "dist/**/*.js", "dist/**/*.lua", "dist/**/*.ts", - "language-extensions/**/*.ts" + "dist/lualib/**/*.json" ], "main": "dist/index.js", "types": "dist/index.d.ts", "scripts": { "build": "tsc && npm run build-lualib", - "build-lualib": "node build-lualib.js", + "build-lualib": "node dist/tstl.js -p src/lualib/tsconfig.json && node dist/tstl.js -p src/lualib/tsconfig.lua50.json", "pretest": "npm run lint && npm run check:language-extensions && npm run build-lualib", "test": "jest", "lint": "npm run lint:eslint && npm run lint:prettier", "lint:prettier": "prettier --check . || (echo 'Run `npm run fix:prettier` to fix it.' && exit 1)", - "lint:eslint": "eslint . --ext .js,.ts", + "lint:eslint": "eslint .", "fix:prettier": "prettier --write .", - "check:language-extensions": "tsc language-extensions/index.d.ts", + "check:language-extensions": "tsc --strict --ignoreConfig language-extensions/index.d.ts", "preversion": "npm run build && npm test", "postversion": "git push && git push --tags" }, @@ -39,33 +39,37 @@ "tstl": "dist/tstl.js" }, "engines": { - "node": ">=12.13.0" + "node": ">=16.10.0" + }, + "peerDependencies": { + "typescript": "6.0.2" }, "dependencies": { - "enhanced-resolve": "^5.8.2", + "@typescript-to-lua/language-extensions": "1.19.0", + "enhanced-resolve": "5.8.2", + "picomatch": "^2.3.1", "resolve": "^1.15.1", - "source-map": "^0.7.3", - "typescript": "~4.3.2" + "source-map": "^0.7.3" }, "devDependencies": { "@types/fs-extra": "^8.1.0", - "@types/glob": "^7.1.1", - "@types/jest": "^25.1.3", - "@types/node": "^13.7.7", + "@types/glob": "^7.2.0", + "@types/jest": "^27.5.2", + "@types/node": "^22.10.0", + "@types/picomatch": "^2.3.0", "@types/resolve": "1.14.0", - "@typescript-eslint/eslint-plugin": "^4.26.1", - "@typescript-eslint/parser": "^4.26.1", - "eslint": "^7.28.0", - "eslint-plugin-import": "^2.20.1", - "eslint-plugin-jest": "^23.8.2", + "eslint": "^9.39.4", + "eslint-plugin-jest": "^28.14.0", "fs-extra": "^8.1.0", "javascript-stringify": "^2.0.1", - "jest": "^26.0.1", - "jest-circus": "^25.1.0", - "lua-types": "^2.8.0", - "lua-wasm-bindings": "^0.2.2", - "prettier": "^2.0.5", - "ts-jest": "^26.3.0", - "ts-node": "^8.6.2" + "jest": "^29.7.0", + "jest-circus": "^29.7.0", + "lua-types": "^2.14.1", + "lua-wasm-bindings": "^0.5.3", + "prettier": "^2.8.8", + "ts-jest": "^29.4.9", + "ts-node": "^10.9.2", + "typescript": "6.0.2", + "typescript-eslint": "^8.58.0" } } diff --git a/src/CompilerOptions.ts b/src/CompilerOptions.ts index e06adc1f3..1a167e7c3 100644 --- a/src/CompilerOptions.ts +++ b/src/CompilerOptions.ts @@ -1,13 +1,11 @@ import * as ts from "typescript"; +import { JsxEmit } from "typescript"; import * as diagnosticFactories from "./transpilation/diagnostics"; +import { Plugin } from "./transpilation/plugins"; -type KnownKeys = { [K in keyof T]: string extends K ? never : number extends K ? never : K } extends { - [K in keyof T]: infer U; -} - ? U - : never; - -type OmitIndexSignature> = Pick>; +type OmitIndexSignature = { + [K in keyof T as string extends K ? never : number extends K ? never : K]: T[K]; +}; export interface TransformerImport { transform: string; @@ -15,43 +13,63 @@ export interface TransformerImport { after?: boolean; afterDeclarations?: boolean; type?: "program" | "config" | "checker" | "raw" | "compilerOptions"; + [option: string]: any; } export interface LuaPluginImport { name: string; import?: string; + [option: string]: any; } -export type CompilerOptions = OmitIndexSignature & { +export interface InMemoryLuaPlugin { + plugin: Plugin | ((options: Record) => Plugin); + [option: string]: any; +} + +export interface TypeScriptToLuaOptions { buildMode?: BuildMode; - noImplicitSelf?: boolean; - noHeader?: boolean; + extension?: string; luaBundle?: string; luaBundleEntry?: string; luaTarget?: LuaTarget; luaLibImport?: LuaLibImportKind; - sourceMapTraceback?: boolean; - luaPlugins?: LuaPluginImport[]; + luaPlugins?: Array; + noImplicitGlobalVariables?: boolean; + noImplicitSelf?: boolean; + noHeader?: boolean; + noResolvePaths?: string[]; plugins?: Array; - [option: string]: any; -}; + sourceMapTraceback?: boolean; + tstlVerbose?: boolean; + lua51AllowTryCatchInAsyncAwait?: boolean; + measurePerformance?: boolean; +} + +export type CompilerOptions = OmitIndexSignature & + TypeScriptToLuaOptions & { + [option: string]: any; + }; export enum LuaLibImportKind { None = "none", - Always = "always", Inline = "inline", Require = "require", + RequireMinimal = "require-minimal", } export enum LuaTarget { Universal = "universal", + Lua50 = "5.0", Lua51 = "5.1", Lua52 = "5.2", Lua53 = "5.3", Lua54 = "5.4", + Lua55 = "5.5", LuaJIT = "JIT", + Luau = "Luau", } export enum BuildMode { @@ -73,5 +91,13 @@ export function validateOptions(options: CompilerOptions): ts.Diagnostic[] { diagnostics.push(diagnosticFactories.usingLuaBundleWithInlineMightGenerateDuplicateCode()); } + if (options.luaBundle && options.buildMode === BuildMode.Library) { + diagnostics.push(diagnosticFactories.cannotBundleLibrary()); + } + + if (options.jsx && options.jsx !== JsxEmit.React) { + diagnostics.push(diagnosticFactories.unsupportedJsxEmit()); + } + return diagnostics; } diff --git a/src/LuaAST.ts b/src/LuaAST.ts index 1ac86d3f5..ad4a7e06b 100644 --- a/src/LuaAST.ts +++ b/src/LuaAST.ts @@ -5,7 +5,7 @@ // because we don't create the AST from text import * as ts from "typescript"; -import { LuaLibFeature } from "./transformation/utils/lualib"; +import { LuaLibFeature } from "./LuaLib"; import { castArray } from "./utils"; export enum SyntaxKind { @@ -25,6 +25,7 @@ export enum SyntaxKind { LabelStatement, ReturnStatement, BreakStatement, + ContinueStatement, // Luau only. ExpressionStatement, // Expression @@ -32,6 +33,7 @@ export enum SyntaxKind { NumericLiteral, NilKeyword, DotsKeyword, + ArgKeyword, TrueKeyword, FalseKeyword, FunctionExpression, @@ -43,6 +45,8 @@ export enum SyntaxKind { MethodCallExpression, Identifier, TableIndexExpression, + ParenthesizedExpression, + ConditionalExpression, // Luau only // Operators @@ -125,6 +129,13 @@ export type Operator = UnaryOperator | BinaryOperator; export type SymbolId = number & { _symbolIdBrand: any }; +export enum NodeFlags { + None = 0, + Inline = 1 << 0, // Keep function body on same line + Declaration = 1 << 1, // Prefer declaration syntax `function foo()` over assignment syntax `foo = function()` + TableUnpackCall = 1 << 2, // This is a table.unpack call +} + export interface TextRange { line?: number; column?: number; @@ -132,18 +143,19 @@ export interface TextRange { export interface Node extends TextRange { kind: SyntaxKind; + flags: NodeFlags; } export function createNode(kind: SyntaxKind, tsOriginal?: ts.Node): Node { if (tsOriginal === undefined) { - return { kind }; + return { kind, flags: NodeFlags.None }; } const sourcePosition = getSourcePosition(tsOriginal); if (sourcePosition) { - return { kind, line: sourcePosition.line, column: sourcePosition.column }; + return { kind, line: sourcePosition.line, column: sourcePosition.column, flags: NodeFlags.None }; } else { - return { kind }; + return { kind, flags: NodeFlags.None }; } } @@ -190,6 +202,11 @@ export function getOriginalPos(node: Node): TextRange { return { line: node.line, column: node.column }; } +export function setNodeFlags(node: T, flags: NodeFlags): T { + node.flags = flags; + return node; +} + export interface File extends Node { kind: SyntaxKind.File; statements: Statement[]; @@ -473,6 +490,18 @@ export function createBreakStatement(tsOriginal?: ts.Node): BreakStatement { return createNode(SyntaxKind.BreakStatement, tsOriginal) as BreakStatement; } +export interface ContinueStatement extends Statement { + kind: SyntaxKind.ContinueStatement; +} + +export function isContinueStatement(node: Node): node is ContinueStatement { + return node.kind === SyntaxKind.ContinueStatement; +} + +export function createContinueStatement(tsOriginal?: ts.Node): ContinueStatement { + return createNode(SyntaxKind.ContinueStatement, tsOriginal) as ContinueStatement; +} + export interface ExpressionStatement extends Statement { kind: SyntaxKind.ExpressionStatement; expression: Expression; @@ -531,6 +560,18 @@ export function createDotsLiteral(tsOriginal?: ts.Node): DotsLiteral { return createNode(SyntaxKind.DotsKeyword, tsOriginal) as DotsLiteral; } +export interface ArgLiteral extends Expression { + kind: SyntaxKind.ArgKeyword; +} + +export function isArgLiteral(node: Node): node is ArgLiteral { + return node.kind === SyntaxKind.ArgKeyword; +} + +export function createArgLiteral(tsOriginal?: ts.Node): ArgLiteral { + return createNode(SyntaxKind.ArgKeyword, tsOriginal) as ArgLiteral; +} + // StringLiteral / NumberLiteral // TODO TS uses the export interface "LiteralLikeNode" with a "text: string" member // but since we don't parse from text I think we can simplify by just having a value member @@ -566,10 +607,17 @@ export function createStringLiteral(value: string, tsOriginal?: ts.Node): String return expression; } -export enum FunctionExpressionFlags { - None = 1 << 0, - Inline = 1 << 1, // Keep function body on same line - Declaration = 1 << 2, // Prefer declaration syntax `function foo()` over assignment syntax `foo = function()` +export function isLiteral( + node: Node +): node is NilLiteral | DotsLiteral | ArgLiteral | BooleanLiteral | NumericLiteral | StringLiteral { + return ( + isNilLiteral(node) || + isDotsLiteral(node) || + isArgLiteral(node) || + isBooleanLiteral(node) || + isNumericLiteral(node) || + isStringLiteral(node) + ); } export interface FunctionExpression extends Expression { @@ -577,7 +625,6 @@ export interface FunctionExpression extends Expression { params?: Identifier[]; dots?: DotsLiteral; body: Block; - flags: FunctionExpressionFlags; } export function isFunctionExpression(node: Node): node is FunctionExpression { @@ -588,7 +635,7 @@ export function createFunctionExpression( body: Block, params?: Identifier[], dots?: DotsLiteral, - flags = FunctionExpressionFlags.None, + flags = NodeFlags.None, tsOriginal?: ts.Node ): FunctionExpression { const expression = createNode(SyntaxKind.FunctionExpression, tsOriginal) as FunctionExpression; @@ -784,6 +831,7 @@ export function createTableIndexExpression( } export type AssignmentLeftHandSideExpression = Identifier | TableIndexExpression; + export function isAssignmentLeftHandSideExpression(node: Node): node is AssignmentLeftHandSideExpression { return isIdentifier(node) || isTableIndexExpression(node); } @@ -807,6 +855,46 @@ export function isInlineFunctionExpression(expression: FunctionExpression): expr expression.body.statements?.length === 1 && isReturnStatement(expression.body.statements[0]) && expression.body.statements[0].expressions !== undefined && - (expression.flags & FunctionExpressionFlags.Inline) !== 0 + (expression.flags & NodeFlags.Inline) !== 0 ); } + +export type ParenthesizedExpression = Expression & { + expression: Expression; +}; + +export function isParenthesizedExpression(node: Node): node is ParenthesizedExpression { + return node.kind === SyntaxKind.ParenthesizedExpression; +} + +export function createParenthesizedExpression(expression: Expression, tsOriginal?: ts.Node): ParenthesizedExpression { + const parenthesizedExpression = createNode( + SyntaxKind.ParenthesizedExpression, + tsOriginal + ) as ParenthesizedExpression; + parenthesizedExpression.expression = expression; + return parenthesizedExpression; +} + +export type ConditionalExpression = Expression & { + condition: Expression; + whenTrue: Expression; + whenFalse: Expression; +}; + +export function isConditionalExpression(node: Node): node is ConditionalExpression { + return node.kind === SyntaxKind.ConditionalExpression; +} + +export function createConditionalExpression( + condition: Expression, + whenTrue: Expression, + whenFalse: Expression, + tsOriginal?: ts.Node +): ConditionalExpression { + const conditionalExpression = createNode(SyntaxKind.ConditionalExpression, tsOriginal) as ConditionalExpression; + conditionalExpression.condition = condition; + conditionalExpression.whenTrue = whenTrue; + conditionalExpression.whenFalse = whenFalse; + return conditionalExpression; +} diff --git a/src/LuaLib.ts b/src/LuaLib.ts index d98b78044..6320bc99c 100644 --- a/src/LuaLib.ts +++ b/src/LuaLib.ts @@ -1,24 +1,30 @@ import * as path from "path"; import { EmitHost } from "./transpilation"; +import * as lua from "./LuaAST"; +import { LuaTarget } from "./CompilerOptions"; +import { getOrUpdate } from "./utils"; export enum LuaLibFeature { + ArrayAt = "ArrayAt", ArrayConcat = "ArrayConcat", ArrayEntries = "ArrayEntries", ArrayEvery = "ArrayEvery", + ArrayFill = "ArrayFill", ArrayFilter = "ArrayFilter", ArrayForEach = "ArrayForEach", ArrayFind = "ArrayFind", ArrayFindIndex = "ArrayFindIndex", + ArrayFrom = "ArrayFrom", ArrayIncludes = "ArrayIncludes", ArrayIndexOf = "ArrayIndexOf", ArrayIsArray = "ArrayIsArray", ArrayJoin = "ArrayJoin", ArrayMap = "ArrayMap", ArrayPush = "ArrayPush", + ArrayPushArray = "ArrayPushArray", ArrayReduce = "ArrayReduce", ArrayReduceRight = "ArrayReduceRight", ArrayReverse = "ArrayReverse", - ArrayShift = "ArrayShift", ArrayUnshift = "ArrayUnshift", ArraySort = "ArraySort", ArraySlice = "ArraySlice", @@ -28,39 +34,67 @@ export enum LuaLibFeature { ArrayFlat = "ArrayFlat", ArrayFlatMap = "ArrayFlatMap", ArraySetLength = "ArraySetLength", + ArrayToReversed = "ArrayToReversed", + ArrayToSorted = "ArrayToSorted", + ArrayToSpliced = "ArrayToSpliced", + ArrayWith = "ArrayWith", + Await = "Await", Class = "Class", ClassExtends = "ClassExtends", CloneDescriptor = "CloneDescriptor", + CountVarargs = "CountVarargs", Decorate = "Decorate", + DecorateLegacy = "DecorateLegacy", DecorateParam = "DecorateParam", Delete = "Delete", DelegatedYield = "DelegatedYield", + DescriptorGet = "DescriptorGet", + DescriptorSet = "DescriptorSet", Error = "Error", FunctionBind = "FunctionBind", Generator = "Generator", InstanceOf = "InstanceOf", InstanceOfObject = "InstanceOfObject", Iterator = "Iterator", + LuaIteratorSpread = "LuaIteratorSpread", Map = "Map", + MapGroupBy = "MapGroupBy", + Match = "Match", MathAtan2 = "MathAtan2", + MathModf = "MathModf", + MathSign = "MathSign", + MathTrunc = "MathTrunc", New = "New", Number = "Number", NumberIsFinite = "NumberIsFinite", + NumberIsInteger = "NumberIsInteger", NumberIsNaN = "NumberIsNaN", + NumberParseInt = "ParseInt", + NumberParseFloat = "ParseFloat", NumberToString = "NumberToString", + NumberToFixed = "NumberToFixed", ObjectAssign = "ObjectAssign", ObjectDefineProperty = "ObjectDefineProperty", ObjectEntries = "ObjectEntries", ObjectFromEntries = "ObjectFromEntries", ObjectGetOwnPropertyDescriptor = "ObjectGetOwnPropertyDescriptor", ObjectGetOwnPropertyDescriptors = "ObjectGetOwnPropertyDescriptors", + ObjectGroupBy = "ObjectGroupBy", ObjectKeys = "ObjectKeys", ObjectRest = "ObjectRest", ObjectValues = "ObjectValues", ParseFloat = "ParseFloat", ParseInt = "ParseInt", + Promise = "Promise", + PromiseAll = "PromiseAll", + PromiseAllSettled = "PromiseAllSettled", + PromiseAny = "PromiseAny", + PromiseRace = "PromiseRace", Set = "Set", SetDescriptor = "SetDescriptor", + SparseArrayNew = "SparseArrayNew", + SparseArrayPush = "SparseArrayPush", + SparseArraySpread = "SparseArraySpread", WeakMap = "WeakMap", WeakSet = "WeakSet", SourceMapTraceBack = "SourceMapTraceBack", @@ -68,12 +102,12 @@ export enum LuaLibFeature { StringAccess = "StringAccess", StringCharAt = "StringCharAt", StringCharCodeAt = "StringCharCodeAt", - StringConcat = "StringConcat", StringEndsWith = "StringEndsWith", StringIncludes = "StringIncludes", StringPadEnd = "StringPadEnd", StringPadStart = "StringPadStart", StringReplace = "StringReplace", + StringReplaceAll = "StringReplaceAll", StringSlice = "StringSlice", StringSplit = "StringSplit", StringStartsWith = "StringStartsWith", @@ -86,53 +120,93 @@ export enum LuaLibFeature { SymbolRegistry = "SymbolRegistry", TypeOf = "TypeOf", Unpack = "Unpack", + Using = "Using", + UsingAsync = "UsingAsync", } -/* eslint-disable @typescript-eslint/naming-convention */ -const luaLibDependencies: Partial> = { - ArrayConcat: [LuaLibFeature.ArrayIsArray], - ArrayFlat: [LuaLibFeature.ArrayConcat, LuaLibFeature.ArrayIsArray], - ArrayFlatMap: [LuaLibFeature.ArrayConcat, LuaLibFeature.ArrayIsArray], - Decorate: [LuaLibFeature.CloneDescriptor], - Delete: [LuaLibFeature.ObjectGetOwnPropertyDescriptors], - Error: [LuaLibFeature.New, LuaLibFeature.Class], - FunctionBind: [LuaLibFeature.Unpack], - Generator: [LuaLibFeature.Symbol], - InstanceOf: [LuaLibFeature.Symbol], - Iterator: [LuaLibFeature.Symbol], - ObjectDefineProperty: [LuaLibFeature.CloneDescriptor, LuaLibFeature.SetDescriptor], - ObjectFromEntries: [LuaLibFeature.Iterator, LuaLibFeature.Symbol], - Map: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class], - Set: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class], - WeakMap: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class], - WeakSet: [LuaLibFeature.InstanceOf, LuaLibFeature.Iterator, LuaLibFeature.Symbol, LuaLibFeature.Class], - Spread: [LuaLibFeature.Iterator, LuaLibFeature.Unpack], - StringSplit: [LuaLibFeature.StringSubstring, LuaLibFeature.StringAccess], - SymbolRegistry: [LuaLibFeature.Symbol], -}; -/* eslint-enable @typescript-eslint/naming-convention */ - -export function loadLuaLibFeatures(features: Iterable, emitHost: EmitHost): string { - let result = ""; +export interface LuaLibFeatureInfo { + dependencies?: LuaLibFeature[]; + exports: string[]; +} + +export type LuaLibModulesInfo = Record; + +export function resolveLuaLibDir(luaTarget: LuaTarget) { + const luaLibDir = luaTarget === LuaTarget.Lua50 ? "5.0" : "universal"; + return path.resolve(__dirname, path.join("..", "dist", "lualib", luaLibDir)); +} + +export const luaLibModulesInfoFileName = "lualib_module_info.json"; +const luaLibModulesInfo = new Map(); + +export function getLuaLibModulesInfo(luaTarget: LuaTarget, emitHost: EmitHost): LuaLibModulesInfo { + if (!luaLibModulesInfo.has(luaTarget)) { + const lualibPath = path.join(resolveLuaLibDir(luaTarget), luaLibModulesInfoFileName); + const result = emitHost.readFile(lualibPath); + if (result !== undefined) { + luaLibModulesInfo.set(luaTarget, JSON.parse(result) as LuaLibModulesInfo); + } else { + throw new Error(`Could not load lualib dependencies from '${lualibPath}'`); + } + } + return luaLibModulesInfo.get(luaTarget)!; +} + +// This caches the names of lualib exports to their LuaLibFeature, avoiding a linear search for every lookup +const lualibExportToFeature = new Map>(); + +export function getLuaLibExportToFeatureMap( + luaTarget: LuaTarget, + emitHost: EmitHost +): ReadonlyMap { + if (!lualibExportToFeature.has(luaTarget)) { + const luaLibModulesInfo = getLuaLibModulesInfo(luaTarget, emitHost); + const map = new Map(); + for (const [feature, info] of Object.entries(luaLibModulesInfo)) { + for (const exportName of info.exports) { + map.set(exportName, feature as LuaLibFeature); + } + } + lualibExportToFeature.set(luaTarget, map); + } + + return lualibExportToFeature.get(luaTarget)!; +} + +const lualibFeatureCache = new Map>(); + +export function readLuaLibFeature(feature: LuaLibFeature, luaTarget: LuaTarget, emitHost: EmitHost): string { + const featureMap = getOrUpdate(lualibFeatureCache, luaTarget, () => new Map()); + if (!featureMap.has(feature)) { + const featurePath = path.join(resolveLuaLibDir(luaTarget), `${feature}.lua`); + const luaLibFeature = emitHost.readFile(featurePath); + if (luaLibFeature === undefined) { + throw new Error(`Could not load lualib feature from '${featurePath}'`); + } + featureMap.set(feature, luaLibFeature); + } + return featureMap.get(feature)!; +} +export function resolveRecursiveLualibFeatures( + features: Iterable, + luaTarget: LuaTarget, + emitHost: EmitHost, + luaLibModulesInfo: LuaLibModulesInfo = getLuaLibModulesInfo(luaTarget, emitHost) +): LuaLibFeature[] { const loadedFeatures = new Set(); + const result: LuaLibFeature[] = []; function load(feature: LuaLibFeature): void { if (loadedFeatures.has(feature)) return; loadedFeatures.add(feature); - const dependencies = luaLibDependencies[feature]; + const dependencies = luaLibModulesInfo[feature]?.dependencies; if (dependencies) { dependencies.forEach(load); } - const featurePath = path.resolve(__dirname, `../dist/lualib/${feature}.lua`); - const luaLibFeature = emitHost.readFile(featurePath); - if (luaLibFeature !== undefined) { - result += luaLibFeature + "\n"; - } else { - throw new Error(`Could not load lualib feature from '${featurePath}'`); - } + result.push(feature); } for (const feature of features) { @@ -142,17 +216,98 @@ export function loadLuaLibFeatures(features: Iterable, emitHost: return result; } -let luaLibBundleContent: string; -export function getLuaLibBundle(emitHost: EmitHost): string { - if (luaLibBundleContent === undefined) { - const lualibPath = path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"); +export function loadInlineLualibFeatures( + features: Iterable, + luaTarget: LuaTarget, + emitHost: EmitHost +): string { + return resolveRecursiveLualibFeatures(features, luaTarget, emitHost) + .map(feature => readLuaLibFeature(feature, luaTarget, emitHost)) + .join("\n"); +} + +export function loadImportedLualibFeatures( + features: Iterable, + luaTarget: LuaTarget, + emitHost: EmitHost +): lua.Statement[] { + const luaLibModuleInfo = getLuaLibModulesInfo(luaTarget, emitHost); + + const imports = Array.from(features).flatMap(feature => luaLibModuleInfo[feature].exports); + if (imports.length === 0) { + return []; + } + + const requireCall = lua.createCallExpression(lua.createIdentifier("require"), [ + lua.createStringLiteral("lualib_bundle"), + ]); + + const luaLibId = lua.createIdentifier("____lualib"); + const importStatement = lua.createVariableDeclarationStatement(luaLibId, requireCall); + const statements: lua.Statement[] = [importStatement]; + // local = ____luaLib. + for (const item of imports) { + statements.push( + lua.createVariableDeclarationStatement( + lua.createIdentifier(item), + lua.createTableIndexExpression(luaLibId, lua.createStringLiteral(item)) + ) + ); + } + return statements; +} + +const luaLibBundleContent = new Map(); + +export function getLuaLibBundle(luaTarget: LuaTarget, emitHost: EmitHost): string { + const lualibPath = path.join(resolveLuaLibDir(luaTarget), "lualib_bundle.lua"); + if (!luaLibBundleContent.has(lualibPath)) { const result = emitHost.readFile(lualibPath); if (result !== undefined) { - luaLibBundleContent = result; + luaLibBundleContent.set(lualibPath, result); } else { throw new Error(`Could not load lualib bundle from '${lualibPath}'`); } } - return luaLibBundleContent; + return luaLibBundleContent.get(lualibPath) as string; +} + +export function getLualibBundleReturn(exportedValues: string[]): string { + return `\nreturn {\n${exportedValues.map(exportName => ` ${exportName} = ${exportName}`).join(",\n")}\n}\n`; +} + +export function buildMinimalLualibBundle( + features: Iterable, + luaTarget: LuaTarget, + emitHost: EmitHost +): string { + const code = loadInlineLualibFeatures(features, luaTarget, emitHost); + const moduleInfo = getLuaLibModulesInfo(luaTarget, emitHost); + const exports = Array.from(features).flatMap(feature => moduleInfo[feature].exports); + + return code + getLualibBundleReturn(exports); +} + +export function findUsedLualibFeatures( + luaTarget: LuaTarget, + emitHost: EmitHost, + luaContents: string[] +): Set { + const features = new Set(); + const exportToFeatureMap = getLuaLibExportToFeatureMap(luaTarget, emitHost); + + for (const lua of luaContents) { + const regex = /^local (\w+) = ____lualib\.(\w+)$/gm; + while (true) { + const match = regex.exec(lua); + if (!match) break; + const [, localName, exportName] = match; + if (localName !== exportName) continue; + const feature = exportToFeatureMap.get(exportName); + if (feature) features.add(feature); + } + } + + return features; } diff --git a/src/LuaPrinter.ts b/src/LuaPrinter.ts index 390b62977..26b55421b 100644 --- a/src/LuaPrinter.ts +++ b/src/LuaPrinter.ts @@ -1,11 +1,12 @@ +import * as path from "path"; import { Mapping, SourceMapGenerator, SourceNode } from "source-map"; import * as ts from "typescript"; -import { CompilerOptions, LuaLibImportKind } from "./CompilerOptions"; +import { CompilerOptions, isBundleEnabled, LuaLibImportKind, LuaTarget } from "./CompilerOptions"; import * as lua from "./LuaAST"; -import { loadLuaLibFeatures, LuaLibFeature } from "./LuaLib"; -import { isValidLuaIdentifier } from "./transformation/utils/safe-names"; -import { EmitHost } from "./transpilation"; -import { intersperse, trimExtension } from "./utils"; +import { loadImportedLualibFeatures, loadInlineLualibFeatures, LuaLibFeature } from "./LuaLib"; +import { isValidLuaIdentifier, shouldAllowUnicode } from "./transformation/utils/safe-names"; +import { EmitHost, getEmitPath } from "./transpilation"; +import { intersperse, normalizeSlashes } from "./utils"; // https://www.lua.org/pil/2.4.html // https://www.ecma-international.org/ecma-262/10.0/index.html#table-34 @@ -32,7 +33,8 @@ export const tstlHeader = "--[[ Generated with https://github.com/TypeScriptToLu * `foo.bar` => passes (`function foo.bar()` is valid) * `getFoo().bar` => fails (`function getFoo().bar()` would be illegal) */ -const isValidLuaFunctionDeclarationName = (str: string) => /^[a-zA-Z0-9_.]+$/.test(str); +const isValidLuaFunctionDeclarationName = (str: string, options: CompilerOptions) => + (shouldAllowUnicode(options) ? /^[a-zA-Z0-9_\u00FF-\uFFFD.]+$/ : /^[a-zA-Z0-9_.]+$/).test(str); /** * Returns true if expression contains no function calls. @@ -118,14 +120,55 @@ export class LuaPrinter { [lua.SyntaxKind.BitwiseLeftShiftOperator]: "<<", [lua.SyntaxKind.BitwiseNotOperator]: "~", }; + private static operatorPrecedence: Record = { + [lua.SyntaxKind.OrOperator]: 1, + [lua.SyntaxKind.AndOperator]: 2, - private currentIndent = ""; - private sourceFile: string; - private options: CompilerOptions; + [lua.SyntaxKind.EqualityOperator]: 3, + [lua.SyntaxKind.InequalityOperator]: 3, + [lua.SyntaxKind.LessThanOperator]: 3, + [lua.SyntaxKind.LessEqualOperator]: 3, + [lua.SyntaxKind.GreaterThanOperator]: 3, + [lua.SyntaxKind.GreaterEqualOperator]: 3, - constructor(private emitHost: EmitHost, program: ts.Program, fileName: string) { + [lua.SyntaxKind.BitwiseOrOperator]: 4, + [lua.SyntaxKind.BitwiseExclusiveOrOperator]: 5, + [lua.SyntaxKind.BitwiseAndOperator]: 6, + + [lua.SyntaxKind.BitwiseLeftShiftOperator]: 7, + [lua.SyntaxKind.BitwiseRightShiftOperator]: 7, + + [lua.SyntaxKind.ConcatOperator]: 8, + + [lua.SyntaxKind.AdditionOperator]: 9, + [lua.SyntaxKind.SubtractionOperator]: 9, + + [lua.SyntaxKind.MultiplicationOperator]: 10, + [lua.SyntaxKind.DivisionOperator]: 10, + [lua.SyntaxKind.FloorDivisionOperator]: 10, + [lua.SyntaxKind.ModuloOperator]: 10, + + [lua.SyntaxKind.NotOperator]: 11, + [lua.SyntaxKind.LengthOperator]: 11, + [lua.SyntaxKind.NegationOperator]: 11, + [lua.SyntaxKind.BitwiseNotOperator]: 11, + + [lua.SyntaxKind.PowerOperator]: 12, + }; + private static rightAssociativeOperators = new Set([lua.SyntaxKind.ConcatOperator, lua.SyntaxKind.PowerOperator]); + + protected currentIndent = ""; + protected luaFile: string; + protected relativeSourcePath: string; + protected options: CompilerOptions; + + public static readonly sourceMapTracebackPlaceholder = "{#SourceMapTraceback}"; + + constructor(private emitHost: EmitHost, private program: ts.Program, private sourceFile: string) { this.options = program.getCompilerOptions(); - this.sourceFile = fileName; + this.luaFile = normalizeSlashes(getEmitPath(this.sourceFile, this.program)); + // Source nodes contain relative path from mapped lua file to original TS source file + this.relativeSourcePath = normalizeSlashes(path.relative(path.dirname(this.luaFile), this.sourceFile)); } public print(file: lua.File): PrintResult { @@ -149,7 +192,7 @@ export class LuaPrinter { if (this.options.sourceMapTraceback) { const stackTraceOverride = this.printStackTraceOverride(rootSourceNode); - code = code.replace("{#SourceMapTraceback}", stackTraceOverride); + code = code.replace(LuaPrinter.sourceMapTracebackPlaceholder, stackTraceOverride); } return { code, sourceMap: sourceMap.toString(), sourceMapNode: rootSourceNode }; @@ -183,31 +226,40 @@ export class LuaPrinter { return `__TS__SourceMapTraceBack(debug.getinfo(1).short_src, ${mapString});`; } - private printFile(file: lua.File): SourceNode { - let header = file.trivia; + protected printFile(file: lua.File): SourceNode { + let sourceChunks: SourceChunk[] = [file.trivia]; if (!this.options.noHeader) { - header += tstlHeader; + sourceChunks.push(tstlHeader); } + const luaTarget = this.options.luaTarget ?? LuaTarget.Universal; const luaLibImport = this.options.luaLibImport ?? LuaLibImportKind.Require; if ( - luaLibImport === LuaLibImportKind.Always || - (luaLibImport === LuaLibImportKind.Require && file.luaLibFeatures.size > 0) + (luaLibImport === LuaLibImportKind.Require || luaLibImport === LuaLibImportKind.RequireMinimal) && + file.luaLibFeatures.size > 0 ) { - // Require lualib bundle - header += 'require("lualib_bundle");\n'; + // Import lualib features + sourceChunks = this.printStatementArray( + loadImportedLualibFeatures(file.luaLibFeatures, luaTarget, this.emitHost) + ); } else if (luaLibImport === LuaLibImportKind.Inline && file.luaLibFeatures.size > 0) { // Inline lualib features - header += "-- Lua Library inline imports\n"; - header += loadLuaLibFeatures(file.luaLibFeatures, this.emitHost); + sourceChunks.push("-- Lua Library inline imports\n"); + sourceChunks.push(loadInlineLualibFeatures(file.luaLibFeatures, luaTarget, this.emitHost)); + sourceChunks.push("-- End of Lua Library inline imports\n"); } - if (this.options.sourceMapTraceback) { - header += "{#SourceMapTraceback}\n"; + if (this.options.sourceMapTraceback && !isBundleEnabled(this.options)) { + // In bundle mode the traceback is being generated for the entire file in getBundleResult + // Otherwise, traceback is being generated locally + sourceChunks.push(`${LuaPrinter.sourceMapTracebackPlaceholder}\n`); } - return this.concatNodes(header, ...this.printStatementArray(file.statements)); + // Print reest of the statements in file + sourceChunks.push(...this.printStatementArray(file.statements)); + + return this.concatNodes(...sourceChunks); } protected pushIndent(): void { @@ -226,12 +278,12 @@ export class LuaPrinter { const { line, column } = lua.getOriginalPos(node); return line !== undefined && column !== undefined - ? new SourceNode(line + 1, column, this.sourceFile, chunks, name) - : new SourceNode(null, null, this.sourceFile, chunks, name); + ? new SourceNode(line + 1, column, this.relativeSourcePath, chunks, name) + : new SourceNode(null, null, this.relativeSourcePath, chunks, name); } protected concatNodes(...chunks: SourceChunk[]): SourceNode { - return new SourceNode(null, null, this.sourceFile, chunks); + return new SourceNode(null, null, this.relativeSourcePath, chunks); } protected printBlock(block: lua.Block): SourceNode { @@ -276,7 +328,7 @@ export class LuaPrinter { statementNodes.push(node); - if (lua.isReturnStatement(statement)) break; + if (lua.isReturnStatement(statement) || lua.isBreakStatement(statement)) break; } return statementNodes.length > 0 ? [...intersperse(statementNodes, "\n"), "\n"] : []; @@ -344,6 +396,8 @@ export class LuaPrinter { return this.printReturnStatement(statement as lua.ReturnStatement); case lua.SyntaxKind.BreakStatement: return this.printBreakStatement(statement as lua.BreakStatement); + case lua.SyntaxKind.ContinueStatement: + return this.printContinueStatement(statement as lua.ContinueStatement); case lua.SyntaxKind.ExpressionStatement: return this.printExpressionStatement(statement as lua.ExpressionStatement); default: @@ -388,13 +442,10 @@ export class LuaPrinter { chunks.push(this.indent()); - if ( - lua.isFunctionDefinition(statement) && - (statement.right[0].flags & lua.FunctionExpressionFlags.Declaration) !== 0 - ) { + if (lua.isFunctionDefinition(statement) && (statement.right[0].flags & lua.NodeFlags.Declaration) !== 0) { // Use `function foo()` instead of `foo = function()` const name = this.printExpression(statement.left[0]); - if (isValidLuaFunctionDeclarationName(name.toString())) { + if (isValidLuaFunctionDeclarationName(name.toString(), this.options)) { chunks.push(this.printFunctionDefinition(statement)); return this.createSourceNode(statement, chunks); } @@ -525,6 +576,10 @@ export class LuaPrinter { return this.createSourceNode(statement, this.indent("break")); } + public printContinueStatement(statement: lua.ContinueStatement): SourceNode { + return this.createSourceNode(statement, this.indent("continue")); + } + public printExpressionStatement(statement: lua.ExpressionStatement): SourceNode { return this.createSourceNode(statement, [this.indent(), this.printExpression(statement.expression)]); } @@ -540,6 +595,8 @@ export class LuaPrinter { return this.printNilLiteral(expression as lua.NilLiteral); case lua.SyntaxKind.DotsKeyword: return this.printDotsLiteral(expression as lua.DotsLiteral); + case lua.SyntaxKind.ArgKeyword: + return this.printArgLiteral(expression as lua.ArgLiteral); case lua.SyntaxKind.TrueKeyword: case lua.SyntaxKind.FalseKeyword: return this.printBooleanLiteral(expression as lua.BooleanLiteral); @@ -561,6 +618,10 @@ export class LuaPrinter { return this.printIdentifier(expression as lua.Identifier); case lua.SyntaxKind.TableIndexExpression: return this.printTableIndexExpression(expression as lua.TableIndexExpression); + case lua.SyntaxKind.ParenthesizedExpression: + return this.printParenthesizedExpression(expression as lua.ParenthesizedExpression); + case lua.SyntaxKind.ConditionalExpression: + return this.printConditionalExpression(expression as lua.ConditionalExpression); default: throw new Error(`Tried to print unknown statement kind: ${lua.SyntaxKind[expression.kind]}`); } @@ -582,6 +643,10 @@ export class LuaPrinter { return this.createSourceNode(expression, "..."); } + public printArgLiteral(expression: lua.ArgLiteral): SourceNode { + return this.createSourceNode(expression, "arg"); + } + public printBooleanLiteral(expression: lua.BooleanLiteral): SourceNode { return this.createSourceNode(expression, expression.kind === lua.SyntaxKind.TrueKeyword ? "true" : "false"); } @@ -647,7 +712,7 @@ export class LuaPrinter { const value = this.printExpression(expression.value); if (expression.key) { - if (lua.isStringLiteral(expression.key) && isValidLuaIdentifier(expression.key.value)) { + if (lua.isStringLiteral(expression.key) && isValidLuaIdentifier(expression.key.value, this.options)) { chunks.push(expression.key.value, " = ", value); } else { chunks.push("[", this.printExpression(expression.key), "] = ", value); @@ -667,34 +732,49 @@ export class LuaPrinter { const chunks: SourceChunk[] = []; chunks.push(this.printOperator(expression.operator)); - chunks.push(this.printExpressionInParenthesesIfNeeded(expression.operand)); + chunks.push( + this.printExpressionInParenthesesIfNeeded( + expression.operand, + LuaPrinter.operatorPrecedence[expression.operator] + ) + ); return this.createSourceNode(expression, chunks); } public printBinaryExpression(expression: lua.BinaryExpression): SourceNode { const chunks: SourceChunk[] = []; - - chunks.push(this.printExpressionInParenthesesIfNeeded(expression.left)); + const isRightAssociative = LuaPrinter.rightAssociativeOperators.has(expression.operator); + const precedence = LuaPrinter.operatorPrecedence[expression.operator]; + chunks.push( + this.printExpressionInParenthesesIfNeeded(expression.left, isRightAssociative ? precedence + 1 : precedence) + ); chunks.push(" ", this.printOperator(expression.operator), " "); - chunks.push(this.printExpressionInParenthesesIfNeeded(expression.right)); + chunks.push( + this.printExpressionInParenthesesIfNeeded( + expression.right, + isRightAssociative ? precedence : precedence + 1 + ) + ); return this.createSourceNode(expression, chunks); } - private printExpressionInParenthesesIfNeeded(expression: lua.Expression): SourceNode { - return this.needsParenthesis(expression) + private printExpressionInParenthesesIfNeeded(expression: lua.Expression, minPrecedenceToOmit?: number): SourceNode { + return this.needsParenthesis(expression, minPrecedenceToOmit) ? this.createSourceNode(expression, ["(", this.printExpression(expression), ")"]) : this.printExpression(expression); } - private needsParenthesis(expression: lua.Expression): boolean { - return ( - lua.isBinaryExpression(expression) || - lua.isFunctionExpression(expression) || - lua.isTableExpression(expression) || - (lua.isUnaryExpression(expression) && expression.operator === lua.SyntaxKind.NotOperator) - ); + private needsParenthesis(expression: lua.Expression, minPrecedenceToOmit?: number): boolean { + if (lua.isBinaryExpression(expression) || lua.isUnaryExpression(expression)) { + return ( + minPrecedenceToOmit === undefined || + LuaPrinter.operatorPrecedence[expression.operator] < minPrecedenceToOmit + ); + } else { + return lua.isFunctionExpression(expression) || lua.isTableExpression(expression); + } } public printCallExpression(expression: lua.CallExpression): SourceNode { @@ -744,7 +824,7 @@ export class LuaPrinter { const chunks: SourceChunk[] = []; chunks.push(this.printExpressionInParenthesesIfNeeded(expression.table)); - if (lua.isStringLiteral(expression.index) && isValidLuaIdentifier(expression.index.value)) { + if (lua.isStringLiteral(expression.index) && isValidLuaIdentifier(expression.index.value, this.options)) { chunks.push(".", this.createSourceNode(expression.index, expression.index.value)); } else { chunks.push("[", this.printExpression(expression.index), "]"); @@ -752,18 +832,42 @@ export class LuaPrinter { return this.createSourceNode(expression, chunks); } + public printParenthesizedExpression(expression: lua.ParenthesizedExpression) { + return this.createSourceNode(expression, ["(", this.printExpression(expression.expression), ")"]); + } + + public printConditionalExpression(expression: lua.ConditionalExpression): SourceNode { + return this.createSourceNode(expression, [ + "if ", + this.printExpression(expression.condition), + " then ", + this.printExpression(expression.whenTrue), + " else ", + this.printExpression(expression.whenFalse), + ]); + } + public printOperator(kind: lua.Operator): SourceNode { - return new SourceNode(null, null, this.sourceFile, LuaPrinter.operatorMap[kind]); + return new SourceNode(null, null, this.relativeSourcePath, LuaPrinter.operatorMap[kind]); } protected joinChunksWithComma(chunks: SourceChunk[]): SourceChunk[] { return intersperse(chunks, ", "); } + /** + * Returns true if the expression list (table field or parameters) should be printed on one line. + */ + protected isSimpleExpressionList(expressions: lua.Expression[]): boolean { + if (expressions.length <= 1) return true; + if (expressions.length > 4) return false; + return expressions.every(isSimpleExpression); + } + protected printExpressionList(expressions: lua.Expression[]): SourceChunk[] { const chunks: SourceChunk[] = []; - if (expressions.every(isSimpleExpression)) { + if (this.isSimpleExpressionList(expressions)) { chunks.push(...this.joinChunksWithComma(expressions.map(e => this.printExpression(e)))); } else { chunks.push("\n"); @@ -783,7 +887,7 @@ export class LuaPrinter { // will not generate 'empty' mappings in the source map that point to nothing in the original TS. private buildSourceMap(sourceRoot: string, rootSourceNode: SourceNode): SourceMapGenerator { const map = new SourceMapGenerator({ - file: trimExtension(this.sourceFile) + ".lua", + file: path.basename(this.luaFile), sourceRoot, }); @@ -823,9 +927,9 @@ export class LuaPrinter { map.addMapping(currentMapping); } - for (const chunk of sourceNode.children) { + for (const chunk of sourceNode.children as SourceChunk[]) { if (typeof chunk === "string") { - const lines = (chunk as string).split("\n"); + const lines = chunk.split("\n"); if (lines.length > 1) { generatedLine += lines.length - 1; generatedColumn = 0; diff --git a/src/cli/diagnostics.ts b/src/cli/diagnostics.ts index e386fa391..963713d2b 100644 --- a/src/cli/diagnostics.ts +++ b/src/cli/diagnostics.ts @@ -33,6 +33,11 @@ export const compilerOptionRequiresAValueOfType = createCommandLineError( (name: string, type: string) => `Compiler option '${name}' requires a value of type ${type}.` ); +export const compilerOptionCouldNotParseJson = createCommandLineError( + 5025, + (name: string, error: string) => `Compiler option '${name}' failed to parse the given JSON value: '${error}'.` +); + export const optionProjectCannotBeMixedWithSourceFilesOnACommandLine = createCommandLineError( 5042, () => "Option 'project' cannot be mixed with source files on a command line." diff --git a/src/cli/parse.ts b/src/cli/parse.ts index d29bf4e83..ca4f039ad 100644 --- a/src/cli/parse.ts +++ b/src/cli/parse.ts @@ -18,7 +18,7 @@ interface CommandLineOptionOfEnum extends CommandLineOptionBase { } interface CommandLineOptionOfPrimitive extends CommandLineOptionBase { - type: "boolean" | "string" | "object"; + type: "boolean" | "string" | "json-array-of-objects" | "array"; } type CommandLineOption = CommandLineOptionOfEnum | CommandLineOptionOfPrimitive; @@ -30,6 +30,11 @@ export const optionDeclarations: CommandLineOption[] = [ type: "enum", choices: Object.values(BuildMode), }, + { + name: "extension", + description: 'File extension for the resulting Lua files. Defaults to ".lua"', + type: "string", + }, { name: "luaBundle", description: "The name of the lua file to bundle output lua to. Requires luaBundleEntry.", @@ -53,6 +58,12 @@ export const optionDeclarations: CommandLineOption[] = [ type: "enum", choices: Object.values(LuaTarget), }, + { + name: "noImplicitGlobalVariables", + description: + 'Specify to prevent implicitly turning "normal" variants into global variables in the transpiled output.', + type: "boolean", + }, { name: "noImplicitSelf", description: 'If "this" is implicitly considered an any type, do not generate a self parameter.', @@ -71,7 +82,27 @@ export const optionDeclarations: CommandLineOption[] = [ { name: "luaPlugins", description: "List of TypeScriptToLua plugins.", - type: "object", + type: "json-array-of-objects", + }, + { + name: "tstlVerbose", + description: "Provide verbose output useful for diagnosing problems.", + type: "boolean", + }, + { + name: "noResolvePaths", + description: "An array of paths that tstl should not resolve and keep as-is.", + type: "array", + }, + { + name: "lua51AllowTryCatchInAsyncAwait", + description: "Always allow try/catch in async/await functions for Lua 5.1.", + type: "boolean", + }, + { + name: "measurePerformance", + description: "Measure performance of the tstl compiler.", + type: "boolean", }, ]; @@ -98,7 +129,7 @@ export function updateParsedConfigFile(parsedConfigFile: ts.ParsedCommandLine): continue; } - const { error, value } = readValue(option, rawValue); + const { error, value } = readValue(option, rawValue, OptionSource.TsConfig); if (error) parsedConfigFile.errors.push(error); if (parsedConfigFile.options[name] === undefined) parsedConfigFile.options[name] = value; } @@ -116,7 +147,7 @@ function updateParsedCommandLine(parsedCommandLine: ts.ParsedCommandLine, args: if (!args[i].startsWith("-")) continue; const isShorthand = !args[i].startsWith("--"); - const argumentName = args[i].substr(isShorthand ? 1 : 2); + const argumentName = args[i].substring(isShorthand ? 1 : 2); const option = optionDeclarations.find(option => { if (option.name.toLowerCase() === argumentName.toLowerCase()) return true; if (isShorthand && option.aliases) { @@ -138,9 +169,9 @@ function updateParsedCommandLine(parsedCommandLine: ts.ParsedCommandLine, args: if (error) parsedCommandLine.errors.push(error); parsedCommandLine.options[option.name] = value; if (consumed) { - i += 1; // Values of custom options are parsed as a file name, exclude them - parsedCommandLine.fileNames = parsedCommandLine.fileNames.filter(f => f !== value); + parsedCommandLine.fileNames = parsedCommandLine.fileNames.filter(f => f !== args[i + 1]); + i += 1; } } } @@ -170,7 +201,12 @@ function readCommandLineArgument(option: CommandLineOption, value: any): Command }; } - return { ...readValue(option, value), consumed: true }; + return { ...readValue(option, value, OptionSource.CommandLine), consumed: true }; +} + +enum OptionSource { + CommandLine, + TsConfig, } interface ReadValueResult { @@ -178,13 +214,12 @@ interface ReadValueResult { value: any; } -function readValue(option: CommandLineOption, value: unknown): ReadValueResult { +function readValue(option: CommandLineOption, value: unknown, source: OptionSource): ReadValueResult { if (value === null) return { value }; switch (option.type) { case "boolean": - case "string": - case "object": { + case "string": { if (typeof value !== option.type) { return { value: undefined, @@ -194,7 +229,45 @@ function readValue(option: CommandLineOption, value: unknown): ReadValueResult { return { value }; } + case "array": + case "json-array-of-objects": { + const isInvalidNonCliValue = source === OptionSource.TsConfig && !Array.isArray(value); + const isInvalidCliValue = source === OptionSource.CommandLine && typeof value !== "string"; + + if (isInvalidNonCliValue || isInvalidCliValue) { + return { + value: undefined, + error: cliDiagnostics.compilerOptionRequiresAValueOfType(option.name, option.type), + }; + } + + const shouldParseValue = source === OptionSource.CommandLine && typeof value === "string"; + if (!shouldParseValue) return { value }; + if (option.type === "array") { + const array = value.split(","); + return { value: array }; + } + + try { + const objects = JSON.parse(value); + if (!Array.isArray(objects)) { + return { + value: undefined, + error: cliDiagnostics.compilerOptionRequiresAValueOfType(option.name, option.type), + }; + } + + return { value: objects }; + } catch (e) { + if (!(e instanceof SyntaxError)) throw e; + + return { + value: undefined, + error: cliDiagnostics.compilerOptionCouldNotParseJson(option.name, e.message), + }; + } + } case "enum": { if (typeof value !== "string") { return { diff --git a/src/cli/tsconfig.ts b/src/cli/tsconfig.ts index bc6fb5ae9..33e469368 100644 --- a/src/cli/tsconfig.ts +++ b/src/cli/tsconfig.ts @@ -1,6 +1,6 @@ import * as path from "path"; import * as ts from "typescript"; -import { CompilerOptions } from "../CompilerOptions"; +import { CompilerOptions, TypeScriptToLuaOptions } from "../CompilerOptions"; import { normalizeSlashes } from "../utils"; import * as cliDiagnostics from "./diagnostics"; import { ParsedCommandLine, updateParsedConfigFile } from "./parse"; @@ -41,17 +41,93 @@ export function parseConfigFileWithSystem( commandLineOptions?: CompilerOptions, system = ts.sys ): ParsedCommandLine { + const configRootDir = path.dirname(configFileName); const parsedConfigFile = ts.parseJsonSourceFileConfigFileContent( ts.readJsonConfigFile(configFileName, system.readFile), system, - path.dirname(configFileName), + configRootDir, commandLineOptions, configFileName ); + const cycleCache = new Set(); + const extendedTstlOptions = getExtendedTstlOptions(configFileName, configRootDir, cycleCache, system); + + parsedConfigFile.raw.tstl = Object.assign(extendedTstlOptions, parsedConfigFile.raw.tstl ?? {}); + return updateParsedConfigFile(parsedConfigFile); } +function resolveNpmModuleConfig( + moduleName: string, + configRootDir: string, + host: ts.ModuleResolutionHost +): string | undefined { + const resolved = ts.nodeNextJsonConfigResolver(moduleName, path.join(configRootDir, "tsconfig.json"), host); + if (resolved.resolvedModule) { + return resolved.resolvedModule.resolvedFileName; + } +} + +function getExtendedTstlOptions( + configFilePath: string, + configRootDir: string, + cycleCache: Set, + system: ts.System +): TypeScriptToLuaOptions { + const absolutePath = ts.pathIsAbsolute(configFilePath) + ? configFilePath + : ts.pathIsRelative(configFilePath) + ? path.resolve(configRootDir, configFilePath) + : resolveNpmModuleConfig(configFilePath, configRootDir, system); // if a path is neither relative nor absolute, it is probably a npm module + + if (!absolutePath) { + return {}; + } + + const newConfigRoot = path.dirname(absolutePath); + + if (cycleCache.has(absolutePath)) { + return {}; + } + + cycleCache.add(absolutePath); + const fileContent = system.readFile(absolutePath); + const options = {}; + + if (fileContent) { + const { config: parsedConfig } = ts.parseConfigFileTextToJson(configFilePath, fileContent) as { + config?: { + extends?: string | string[]; + tstl?: TypeScriptToLuaOptions; + }; + }; + + if (!parsedConfig) { + return {}; + } + + if (parsedConfig.extends) { + if (Array.isArray(parsedConfig.extends)) { + for (const extendedConfigFile of parsedConfig.extends) { + Object.assign( + options, + getExtendedTstlOptions(extendedConfigFile, newConfigRoot, cycleCache, system) + ); + } + } else { + Object.assign(options, getExtendedTstlOptions(parsedConfig.extends, newConfigRoot, cycleCache, system)); + } + } + + if (parsedConfig.tstl) { + Object.assign(options, parsedConfig.tstl); + } + } + + return options; +} + export function createConfigFileUpdater( optionsToExtend: CompilerOptions ): (options: ts.CompilerOptions) => ts.Diagnostic[] { @@ -61,16 +137,7 @@ export function createConfigFileUpdater( if (!configFile || !configFilePath) return []; if (!configFileMap.has(configFile)) { - const parsedConfigFile = updateParsedConfigFile( - ts.parseJsonSourceFileConfigFileContent( - configFile, - ts.sys, - path.dirname(configFilePath), - optionsToExtend, - configFilePath - ) - ); - + const parsedConfigFile = parseConfigFileWithSystem(configFilePath, optionsToExtend, ts.sys); configFileMap.set(configFile, parsedConfigFile); } diff --git a/src/index.ts b/src/index.ts index 3d060a994..2d2e3de54 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,3 +8,4 @@ export { LuaLibFeature } from "./LuaLib"; export * from "./LuaPrinter"; export * from "./transformation/context"; export * from "./transpilation"; +export { EmitHost, EmitFile, ProcessedFile } from "./transpilation/utils"; diff --git a/src/lualib-build/plugin.ts b/src/lualib-build/plugin.ts new file mode 100644 index 000000000..79233f3df --- /dev/null +++ b/src/lualib-build/plugin.ts @@ -0,0 +1,188 @@ +import { SourceNode } from "source-map"; +import * as ts from "typescript"; +import * as tstl from ".."; +import * as path from "path"; +import { + getLualibBundleReturn, + LuaLibFeature, + LuaLibModulesInfo, + luaLibModulesInfoFileName, + resolveRecursiveLualibFeatures, +} from "../LuaLib"; +import { EmitHost, ProcessedFile } from "../transpilation/utils"; +import { + isExportAlias, + isExportAssignment, + isExportsReturn, + isExportTableDeclaration, + isImport, + isRequire, +} from "./util"; +import { createDiagnosticFactoryWithCode } from "../utils"; + +export const lualibDiagnostic = createDiagnosticFactoryWithCode(200000, (message: string, file?: ts.SourceFile) => ({ + messageText: message, + file, + start: file && 0, + length: file && 0, +})); + +class LuaLibPlugin implements tstl.Plugin { + // Plugin members + public visitors = { + [ts.SyntaxKind.SourceFile]: this.lualibFileVisitor.bind(this), + }; + + public printer: tstl.Printer = (program, emitHost, fileName, file) => + new LuaLibPrinter(emitHost, program, fileName).print(file); + + public afterPrint(program: ts.Program, options: tstl.CompilerOptions, emitHost: EmitHost, result: ProcessedFile[]) { + void options; + + // Write lualib dependency json + const { result: luaLibModuleInfo, diagnostics } = this.createLuaLibModulesInfo(); + const emitBOM = options.emitBOM ?? false; + emitHost.writeFile( + path.join(tstl.getEmitOutDir(program), luaLibModulesInfoFileName), + JSON.stringify(luaLibModuleInfo, null, 2), + emitBOM + ); + + // Flatten the output folder structure; we do not want to keep the target-specific directories + for (const file of result) { + let outPath = file.fileName; + while (outPath.includes("lualib") && path.basename(path.dirname(outPath)) !== "lualib") { + const upOne = path.join(path.dirname(outPath), "..", path.basename(outPath)); + outPath = path.normalize(upOne); + } + file.fileName = outPath; + } + + // Create map of result files keyed by their 'lualib name' + const exportedLualibFeatures = new Map(result.map(f => [path.basename(f.fileName).split(".")[0], f.code])); + + // Figure out the order required in the bundle by recursively resolving all dependency features + const allFeatures = Object.values(LuaLibFeature) as LuaLibFeature[]; + const luaTarget = options.luaTarget ?? tstl.LuaTarget.Universal; + const orderedFeatures = resolveRecursiveLualibFeatures(allFeatures, luaTarget, emitHost, luaLibModuleInfo); + + // Concatenate lualib files into bundle with exports table and add lualib_bundle.lua to results + let lualibBundle = orderedFeatures.map(f => exportedLualibFeatures.get(LuaLibFeature[f])).join("\n"); + const exports = allFeatures.flatMap(feature => luaLibModuleInfo[feature].exports); + lualibBundle += getLualibBundleReturn(exports); + result.push({ fileName: "lualib_bundle.lua", code: lualibBundle }); + + return diagnostics; + } + + // Internals + protected featureExports: Map> = new Map(); + protected featureDependencies: Map> = new Map(); + + protected lualibFileVisitor(file: ts.SourceFile, context: tstl.TransformationContext): tstl.File { + const featureName = path.basename(file.fileName, ".ts") as tstl.LuaLibFeature; + if (!(featureName in tstl.LuaLibFeature)) { + context.diagnostics.push(lualibDiagnostic(`File is not a lualib feature: ${featureName}`, file)); + } + + // Transpile file as normal with tstl + const fileResult = context.superTransformNode(file)[0] as tstl.File; + + const usedFeatures = new Set(context.usedLuaLibFeatures); + + // Get all imports in file + const importNames = new Set(); + const imports = file.statements.filter(ts.isImportDeclaration); + for (const { importClause, moduleSpecifier } of imports) { + if (importClause?.namedBindings && ts.isNamedImports(importClause.namedBindings)) { + for (const { name } of importClause.namedBindings.elements) { + importNames.add(name.text); + } + } + // track lualib imports + if (ts.isStringLiteral(moduleSpecifier)) { + const featureName = path.basename(moduleSpecifier.text, ".ts") as tstl.LuaLibFeature; + if (featureName in tstl.LuaLibFeature) { + usedFeatures.add(featureName); + } + } + } + + const filteredStatements = fileResult.statements + .filter( + s => !isExportTableDeclaration(s) && !isRequire(s) && !isImport(s, importNames) && !isExportsReturn(s) + ) + .map(statement => { + if (isExportAlias(statement)) { + const name = statement.left[0]; + const exportName = statement.right[0].index.value; + if (name.text === exportName) return undefined; // Remove "x = x" statements + return tstl.createAssignmentStatement(name, tstl.createIdentifier(exportName)); + } + return statement; + }) + .filter(statement => statement !== undefined); + + const exportNames = filteredStatements.filter(isExportAssignment).map(s => s.left[0].index.value); + if (!filteredStatements.every(isExportAssignment)) { + // If there are local statements, wrap them in a do ... end with exports outside + const exports = tstl.createVariableDeclarationStatement(exportNames.map(k => tstl.createIdentifier(k))); + // transform export assignments to local assignments + const bodyStatements = filteredStatements.map(s => + isExportAssignment(s) + ? tstl.createAssignmentStatement(tstl.createIdentifier(s.left[0].index.value), s.right[0]) + : s + ); + + fileResult.statements = [exports, tstl.createDoStatement(bodyStatements)]; + } else { + // transform export assignments to local variable declarations + fileResult.statements = filteredStatements.map(s => + tstl.createVariableDeclarationStatement(tstl.createIdentifier(s.left[0].index.value), s.right[0]) + ); + } + + // Save dependency information + this.featureExports.set(featureName, new Set(exportNames)); + if (usedFeatures.size > 0) { + this.featureDependencies.set(featureName, usedFeatures); + } + + return fileResult; + } + + protected createLuaLibModulesInfo(): { result: LuaLibModulesInfo; diagnostics: ts.Diagnostic[] } { + const result: Partial = {}; + const diagnostics: ts.Diagnostic[] = []; + for (const feature of Object.values(tstl.LuaLibFeature)) { + const exports = this.featureExports.get(feature); + if (!exports) { + diagnostics.push(lualibDiagnostic(`Missing file for lualib feature: ${feature}`)); + continue; + } + const dependencies = this.featureDependencies.get(feature); + result[feature] = { + exports: Array.from(exports), + dependencies: dependencies ? Array.from(dependencies) : undefined, + }; + } + return { result: result as LuaLibModulesInfo, diagnostics }; + } +} + +class LuaLibPrinter extends tstl.LuaPrinter { + // Strip all exports during print + public printTableIndexExpression(expression: tstl.TableIndexExpression): SourceNode { + if ( + tstl.isIdentifier(expression.table) && + expression.table.text === "____exports" && + tstl.isStringLiteral(expression.index) + ) { + return super.printExpression(tstl.createIdentifier(expression.index.value)); + } + return super.printTableIndexExpression(expression); + } +} + +const pluginInstance = new LuaLibPlugin(); +export default pluginInstance; diff --git a/src/lualib-build/util.ts b/src/lualib-build/util.ts new file mode 100644 index 000000000..c8ea1e3a8 --- /dev/null +++ b/src/lualib-build/util.ts @@ -0,0 +1,47 @@ +import * as tstl from ".."; + +export function isExportTableDeclaration(node: tstl.Node): node is tstl.VariableDeclarationStatement & { left: [] } { + return tstl.isVariableDeclarationStatement(node) && isExportTable(node.left[0]); +} + +export function isExportTable(node: tstl.Node): node is tstl.Identifier { + return tstl.isIdentifier(node) && node.text === "____exports"; +} + +export type ExportTableIndex = tstl.TableIndexExpression & { index: tstl.StringLiteral }; +export function isExportTableIndex(node: tstl.Node): node is ExportTableIndex { + return tstl.isTableIndexExpression(node) && isExportTable(node.table) && tstl.isStringLiteral(node.index); +} + +export function isExportAlias( + node: tstl.Node +): node is tstl.VariableDeclarationStatement & { right: [ExportTableIndex] } { + return tstl.isVariableDeclarationStatement(node) && node.right !== undefined && isExportTableIndex(node.right[0]); +} + +export type ExportAssignment = tstl.AssignmentStatement & { left: [ExportTableIndex] }; +export function isExportAssignment(node: tstl.Node): node is ExportAssignment { + return tstl.isAssignmentStatement(node) && isExportTableIndex(node.left[0]); +} + +export function isRequire(node: tstl.Node) { + return ( + tstl.isVariableDeclarationStatement(node) && + node.right && + tstl.isCallExpression(node.right[0]) && + tstl.isIdentifier(node.right[0].expression) && + node.right[0].expression.text === "require" + ); +} + +export function isImport(node: tstl.Node, importNames: Set) { + return tstl.isVariableDeclarationStatement(node) && importNames.has(node.left[0].text); +} + +export function isExportsReturn(node: tstl.Node) { + return ( + tstl.isReturnStatement(node) && + tstl.isIdentifier(node.expressions[0]) && + node.expressions[0].text === "____exports" + ); +} diff --git a/src/lualib/5.0/CountVarargs.ts b/src/lualib/5.0/CountVarargs.ts new file mode 100644 index 000000000..34467ebd4 --- /dev/null +++ b/src/lualib/5.0/CountVarargs.ts @@ -0,0 +1,7 @@ +/** @noSelfInFile */ + +export function __TS__CountVarargs(...args: T[]): number { + // select() is not available in Lua 5.0. In this version, the arg table + // includes trailing nils. + return args.length; +} diff --git a/src/lualib/5.0/Match.ts b/src/lualib/5.0/Match.ts new file mode 100644 index 000000000..3b1705939 --- /dev/null +++ b/src/lualib/5.0/Match.ts @@ -0,0 +1,12 @@ +/** @noSelfInFile */ + +export function __TS__Match(s: string, pattern: string, init?: number): LuaMultiReturn { + const [start, end, ...captures] = string.find(s, pattern, init); + if (start === undefined || end === undefined) { + return $multi(); + } else if (captures.length <= 0) { + return $multi(s.slice(start - 1, end)); + } else { + return $multi(...(captures as string[])); + } +} diff --git a/src/lualib/5.0/MathModf.ts b/src/lualib/5.0/MathModf.ts new file mode 100644 index 000000000..ec816ebe0 --- /dev/null +++ b/src/lualib/5.0/MathModf.ts @@ -0,0 +1,6 @@ +/** @noSelfInFile */ + +export function __TS__MathModf(x: number): LuaMultiReturn<[number, number]> { + const integral = x > 0 ? Math.floor(x) : Math.ceil(x); + return $multi(integral, x - integral); +} diff --git a/src/lualib/5.0/SparseArraySpread.ts b/src/lualib/5.0/SparseArraySpread.ts new file mode 100644 index 000000000..9a11f3cc9 --- /dev/null +++ b/src/lualib/5.0/SparseArraySpread.ts @@ -0,0 +1,6 @@ +import { __TS__SparseArray } from "./SparseArray"; +import { __TS__Unpack } from "./Unpack"; + +export function __TS__SparseArraySpread(this: void, sparseArray: __TS__SparseArray): LuaMultiReturn { + return __TS__Unpack(sparseArray, 1, sparseArray.sparseLength); +} diff --git a/src/lualib/5.0/Unpack.ts b/src/lualib/5.0/Unpack.ts new file mode 100644 index 000000000..a84416233 --- /dev/null +++ b/src/lualib/5.0/Unpack.ts @@ -0,0 +1,16 @@ +/** @noSelfInFile */ + +// We're not interested in emulating all of the behaviors of unpack() from Lua +// 5.1, just the ones needed by other parts of lualib. +export function __TS__Unpack(list: T[], i: number, j?: number): LuaMultiReturn { + if (i === 1 && j === undefined) { + return unpack(list); + } else { + j ??= list.length; + const slice: T[] = []; + for (let n = i; n <= j; n++) { + slice[n - i] = list[n - 1]; // We don't want to add 1 to the index into list. + } + return $multi(...slice); + } +} diff --git a/src/lualib/ArrayAt.ts b/src/lualib/ArrayAt.ts new file mode 100644 index 000000000..32e4ea2b4 --- /dev/null +++ b/src/lualib/ArrayAt.ts @@ -0,0 +1,13 @@ +// https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.at +// Technically the specs also allow non-numeric types to be passed as index. +// However, TypeScript types the `Array.at` index param as number so we also expect only numbers +// This behaviour also matches the implementation of other Array functions in lualib. +export function __TS__ArrayAt(this: T[], relativeIndex: number): T | undefined { + const absoluteIndex = relativeIndex < 0 ? this.length + relativeIndex : relativeIndex; + + if (absoluteIndex >= 0 && absoluteIndex < this.length) { + return this[absoluteIndex]; + } + + return undefined; +} diff --git a/src/lualib/ArrayConcat.ts b/src/lualib/ArrayConcat.ts index 30da83514..caaafa369 100644 --- a/src/lualib/ArrayConcat.ts +++ b/src/lualib/ArrayConcat.ts @@ -1,18 +1,22 @@ -function __TS__ArrayConcat(this: void, arr1: any[], ...args: any[]): any[] { - const out: any[] = []; - for (const val of arr1) { - out[out.length] = val; +export function __TS__ArrayConcat(this: T[], ...items: Array): T[] { + const result: T[] = []; + let len = 0; + for (const i of $range(1, this.length)) { + len++; + result[len - 1] = this[i - 1]; } - for (const arg of args) { - if (Array.isArray(arg)) { - const argAsArray = arg; - for (const val of argAsArray) { - out[out.length] = val; + for (const i of $range(1, items.length)) { + const item = items[i - 1]; + if (Array.isArray(item)) { + for (const j of $range(1, item.length)) { + len++; + result[len - 1] = item[j - 1]; } } else { - out[out.length] = arg; + len++; + result[len - 1] = item; } } - return out; + return result; } diff --git a/src/lualib/ArrayEntries.ts b/src/lualib/ArrayEntries.ts index 7d12f1347..6cdef13a3 100644 --- a/src/lualib/ArrayEntries.ts +++ b/src/lualib/ArrayEntries.ts @@ -1,5 +1,5 @@ // https://262.ecma-international.org/10.0/#sec-array.prototype.entries -function __TS__ArrayEntries(this: void, array: T[]): IterableIterator<[number, T]> { +export function __TS__ArrayEntries(this: void, array: T[]): IterableIterator<[number, T]> { let key = 0; return { [Symbol.iterator](): IterableIterator<[number, T]> { diff --git a/src/lualib/ArrayEvery.ts b/src/lualib/ArrayEvery.ts index 011dd9abe..d94efec54 100644 --- a/src/lualib/ArrayEvery.ts +++ b/src/lualib/ArrayEvery.ts @@ -1,10 +1,10 @@ -function __TS__ArrayEvery( - this: void, - arr: T[], - callbackfn: (value: T, index?: number, array?: any[]) => boolean +export function __TS__ArrayEvery( + this: T[], + callbackfn: (value: T, index?: number, array?: any[]) => boolean, + thisArg?: any ): boolean { - for (let i = 0; i < arr.length; i++) { - if (!callbackfn(arr[i], i, arr)) { + for (const i of $range(1, this.length)) { + if (!callbackfn.call(thisArg, this[i - 1], i - 1, this)) { return false; } } diff --git a/src/lualib/ArrayFill.ts b/src/lualib/ArrayFill.ts new file mode 100644 index 000000000..a1654114a --- /dev/null +++ b/src/lualib/ArrayFill.ts @@ -0,0 +1,19 @@ +// https://tc39.es/ecma262/multipage/indexed-collections.html#sec-array.prototype.fill +export function __TS__ArrayFill(this: T[], value: T, start?: number, end?: number): T[] { + let relativeStart = start ?? 0; + let relativeEnd = end ?? this.length; + + if (relativeStart < 0) { + relativeStart += this.length; + } + + if (relativeEnd < 0) { + relativeEnd += this.length; + } + + for (let i = relativeStart; i < relativeEnd; i++) { + this[i] = value; + } + + return this; +} diff --git a/src/lualib/ArrayFilter.ts b/src/lualib/ArrayFilter.ts index 3d88ddffa..5d63a0494 100644 --- a/src/lualib/ArrayFilter.ts +++ b/src/lualib/ArrayFilter.ts @@ -1,12 +1,14 @@ -function __TS__ArrayFilter( - this: void, - arr: T[], - callbackfn: (value: T, index?: number, array?: any[]) => boolean +export function __TS__ArrayFilter( + this: T[], + callbackfn: (value: T, index?: number, array?: any[]) => boolean, + thisArg?: any ): T[] { const result: T[] = []; - for (let i = 0; i < arr.length; i++) { - if (callbackfn(arr[i], i, arr)) { - result[result.length] = arr[i]; + let len = 0; + for (const i of $range(1, this.length)) { + if (callbackfn.call(thisArg, this[i - 1], i - 1, this)) { + len++; + result[len - 1] = this[i - 1]; } } return result; diff --git a/src/lualib/ArrayFind.ts b/src/lualib/ArrayFind.ts index 608bd3ef6..3beb9c83b 100644 --- a/src/lualib/ArrayFind.ts +++ b/src/lualib/ArrayFind.ts @@ -1,17 +1,14 @@ // https://www.ecma-international.org/ecma-262/10.0/index.html#sec-array.prototype.find -function __TS__ArrayFind( - this: void, - arr: T[], - predicate: (value: T, index: number, obj: T[]) => unknown +export function __TS__ArrayFind( + this: T[], + predicate: (value: T, index: number, obj: T[]) => unknown, + thisArg?: any ): T | undefined { - const len = arr.length; - let k = 0; - while (k < len) { - const elem = arr[k]; - if (predicate(elem, k, arr)) { + for (const i of $range(1, this.length)) { + const elem = this[i - 1]; + if (predicate.call(thisArg, elem, i - 1, this)) { return elem; } - k += 1; } return undefined; diff --git a/src/lualib/ArrayFindIndex.ts b/src/lualib/ArrayFindIndex.ts index 53d83cb39..3741f3a0f 100644 --- a/src/lualib/ArrayFindIndex.ts +++ b/src/lualib/ArrayFindIndex.ts @@ -1,11 +1,11 @@ -function __TS__ArrayFindIndex( - this: void, - arr: T[], - callbackFn: (element: T, index?: number, array?: T[]) => boolean +export function __TS__ArrayFindIndex( + this: T[], + callbackFn: (element: T, index?: number, array?: T[]) => boolean, + thisArg?: any ): number { - for (let i = 0, len = arr.length; i < len; i++) { - if (callbackFn(arr[i], i, arr)) { - return i; + for (const i of $range(1, this.length)) { + if (callbackFn.call(thisArg, this[i - 1], i - 1, this)) { + return i - 1; } } return -1; diff --git a/src/lualib/ArrayFlat.ts b/src/lualib/ArrayFlat.ts index 2bed314cb..3e15b455b 100644 --- a/src/lualib/ArrayFlat.ts +++ b/src/lualib/ArrayFlat.ts @@ -1,10 +1,23 @@ -function __TS__ArrayFlat(this: void, array: any[], depth = 1): any[] { - let result: any[] = []; - for (const value of array) { +export function __TS__ArrayFlat(this: any[], depth = 1): any[] { + const result: any[] = []; + let len = 0; + for (const i of $range(1, this.length)) { + const value = this[i - 1]; if (depth > 0 && Array.isArray(value)) { - result = result.concat(__TS__ArrayFlat(value, depth - 1)); + let toAdd: any[]; + if (depth === 1) { + toAdd = value; + } else { + toAdd = value.flat(depth - 1); + } + for (const j of $range(1, toAdd.length)) { + const val = toAdd[j - 1]; + len++; + result[len - 1] = val; + } } else { - result[result.length] = value; + len++; + result[len - 1] = value; } } diff --git a/src/lualib/ArrayFlatMap.ts b/src/lualib/ArrayFlatMap.ts index c31af9046..fa68fce8f 100644 --- a/src/lualib/ArrayFlatMap.ts +++ b/src/lualib/ArrayFlatMap.ts @@ -1,15 +1,20 @@ -function __TS__ArrayFlatMap( - this: void, - array: T[], - callback: (value: T, index: number, array: T[]) => U | readonly U[] +export function __TS__ArrayFlatMap( + this: T[], + callback: (value: T, index: number, array: T[]) => U | readonly U[], + thisArg?: any ): U[] { - let result: U[] = []; - for (let i = 0; i < array.length; i++) { - const value = callback(array[i], i, array); - if (type(value) === "table" && Array.isArray(value)) { - result = result.concat(value); + const result: U[] = []; + let len = 0; + for (const i of $range(1, this.length)) { + const value = callback.call(thisArg, this[i - 1], i - 1, this); + if (Array.isArray(value)) { + for (const j of $range(1, value.length)) { + len++; + result[len - 1] = value[j - 1]; + } } else { - result[result.length] = value as U; + len++; + result[len - 1] = value as U; } } diff --git a/src/lualib/ArrayForEach.ts b/src/lualib/ArrayForEach.ts index 96ac4a806..caf4ec53e 100644 --- a/src/lualib/ArrayForEach.ts +++ b/src/lualib/ArrayForEach.ts @@ -1,9 +1,9 @@ -function __TS__ArrayForEach( - this: void, - arr: T[], - callbackFn: (value: T, index?: number, array?: any[]) => any +export function __TS__ArrayForEach( + this: T[], + callbackFn: (value: T, index?: number, array?: any[]) => any, + thisArg?: any ): void { - for (let i = 0; i < arr.length; i++) { - callbackFn(arr[i], i, arr); + for (const i of $range(1, this.length)) { + callbackFn.call(thisArg, this[i - 1], i - 1, this); } } diff --git a/src/lualib/ArrayFrom.ts b/src/lualib/ArrayFrom.ts new file mode 100644 index 000000000..3742d7949 --- /dev/null +++ b/src/lualib/ArrayFrom.ts @@ -0,0 +1,37 @@ +/** @noSelfInFile */ + +import { __TS__Iterator } from "./Iterator"; + +function arrayLikeStep(this: ArrayLike, index: number): LuaMultiReturn<[number, unknown] | []> { + index += 1; + if (index > this.length) return $multi(); + return $multi(index, this[index]); +} + +const arrayLikeIterator: ( + this: void, + arr: ArrayLike | Iterable +) => LuaIterable> = ((arr: any) => { + if (typeof arr.length === "number") return $multi(arrayLikeStep, arr, 0); + return __TS__Iterator(arr); +}) as any; + +export function __TS__ArrayFrom( + this: void, + arrayLike: ArrayLike | Iterable, + mapFn?: (this: unknown, element: unknown, index: number) => unknown, + thisArg?: unknown +): unknown[] { + const result = []; + if (mapFn === undefined) { + for (const [, v] of arrayLikeIterator(arrayLike)) { + result.push(v); + } + } else { + let i = 0; + for (const [, v] of arrayLikeIterator(arrayLike)) { + result.push(mapFn.call(thisArg, v, i++)); + } + } + return result; +} diff --git a/src/lualib/ArrayIncludes.ts b/src/lualib/ArrayIncludes.ts index b012f87bf..b3fd871f6 100644 --- a/src/lualib/ArrayIncludes.ts +++ b/src/lualib/ArrayIncludes.ts @@ -1,5 +1,5 @@ // https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.includes -function __TS__ArrayIncludes(this: T[], searchElement: T, fromIndex = 0): boolean { +export function __TS__ArrayIncludes(this: T[], searchElement: T, fromIndex = 0): boolean { const len = this.length; let k = fromIndex; @@ -11,8 +11,8 @@ function __TS__ArrayIncludes(this: T[], searchElement: T, fromIndex = 0): boo k = 0; } - for (const i of $range(k, len)) { - if (this[i] === searchElement) { + for (const i of $range(k + 1, len)) { + if (this[i - 1] === searchElement) { return true; } } diff --git a/src/lualib/ArrayIndexOf.ts b/src/lualib/ArrayIndexOf.ts index e91a1a5dc..0a3ebc1f9 100644 --- a/src/lualib/ArrayIndexOf.ts +++ b/src/lualib/ArrayIndexOf.ts @@ -1,31 +1,23 @@ -function __TS__ArrayIndexOf(this: void, arr: T[], searchElement: T, fromIndex?: number): number { - const len = arr.length; +export function __TS__ArrayIndexOf(this: T[], searchElement: T, fromIndex = 0): number { + const len = this.length; if (len === 0) { return -1; } - let n = 0; - if (fromIndex) { - n = fromIndex; - } - - if (n >= len) { + if (fromIndex >= len) { return -1; } - let k: number; - if (n >= 0) { - k = n; - } else { - k = len + n; - if (k < 0) { - k = 0; + if (fromIndex < 0) { + fromIndex = len + fromIndex; + if (fromIndex < 0) { + fromIndex = 0; } } - for (let i = k; i < len; i++) { - if (arr[i] === searchElement) { - return i; + for (const i of $range(fromIndex + 1, len)) { + if (this[i - 1] === searchElement) { + return i - 1; } } diff --git a/src/lualib/ArrayIsArray.ts b/src/lualib/ArrayIsArray.ts index 38a4525ef..61fca760b 100644 --- a/src/lualib/ArrayIsArray.ts +++ b/src/lualib/ArrayIsArray.ts @@ -1,5 +1,7 @@ -function __TS__ArrayIsArray(this: void, value: any): value is any[] { +declare type NextEmptyCheck = (this: void, table: any, index?: undefined) => unknown | undefined; + +export function __TS__ArrayIsArray(this: void, value: any): value is any[] { // Workaround to determine if value is an array or not (fails in case of objects without keys) - // See discussion in: https://github.com/TypeScriptToLua/TypeScriptToLua/pull/7 - return type(value) === "table" && (1 in value || (next as NextEmptyCheck)(value, undefined) === undefined); + // See discussion in: https://github.com/TypeScriptToLua/TypeScriptToLua/pull/737 + return type(value) === "table" && (1 in value || (next as NextEmptyCheck)(value) === undefined); } diff --git a/src/lualib/ArrayJoin.ts b/src/lualib/ArrayJoin.ts index 33e220181..0f0b11c9f 100644 --- a/src/lualib/ArrayJoin.ts +++ b/src/lualib/ArrayJoin.ts @@ -1,8 +1,7 @@ -function __TS__ArrayJoin(this: unknown[], separator = ",") { - let result = ""; - for (const [index, value] of ipairs(this)) { - if (index > 1) result += separator; - result += value.toString(); +export function __TS__ArrayJoin(this: any[], separator = ",") { + const parts: string[] = []; + for (const i of $range(1, this.length)) { + parts[i - 1] = this[i - 1].toString(); } - return result; + return table.concat(parts, separator); } diff --git a/src/lualib/ArrayMap.ts b/src/lualib/ArrayMap.ts index 04ee22a48..2caf84028 100644 --- a/src/lualib/ArrayMap.ts +++ b/src/lualib/ArrayMap.ts @@ -1,7 +1,11 @@ -function __TS__ArrayMap(this: void, arr: T[], callbackfn: (value: T, index?: number, array?: T[]) => U): U[] { - const newArray: U[] = []; - for (let i = 0; i < arr.length; i++) { - newArray[i] = callbackfn(arr[i], i, arr); +export function __TS__ArrayMap( + this: T[], + callbackfn: (value: T, index?: number, array?: T[]) => U, + thisArg?: any +): U[] { + const result: U[] = []; + for (const i of $range(1, this.length)) { + result[i - 1] = callbackfn.call(thisArg, this[i - 1], i - 1, this); } - return newArray; + return result; } diff --git a/src/lualib/ArrayPush.ts b/src/lualib/ArrayPush.ts index f09005e44..9f181d1f2 100644 --- a/src/lualib/ArrayPush.ts +++ b/src/lualib/ArrayPush.ts @@ -1,6 +1,8 @@ -function __TS__ArrayPush(this: void, arr: T[], ...items: T[]): number { - for (const item of items) { - arr[arr.length] = item; +export function __TS__ArrayPush(this: T[], ...items: T[]): number { + let len = this.length; + for (const i of $range(1, items.length)) { + len++; + this[len - 1] = items[i - 1]; } - return arr.length; + return len; } diff --git a/src/lualib/ArrayPushArray.ts b/src/lualib/ArrayPushArray.ts new file mode 100644 index 000000000..8ea9b6916 --- /dev/null +++ b/src/lualib/ArrayPushArray.ts @@ -0,0 +1,8 @@ +export function __TS__ArrayPushArray(this: T[], items: T[]): number { + let len = this.length; + for (const i of $range(1, items.length)) { + len++; + this[len - 1] = items[i - 1]; + } + return len; +} diff --git a/src/lualib/ArrayReduce.ts b/src/lualib/ArrayReduce.ts index c8ea2de50..cc569409a 100644 --- a/src/lualib/ArrayReduce.ts +++ b/src/lualib/ArrayReduce.ts @@ -1,27 +1,28 @@ +import { __TS__CountVarargs } from "./CountVarargs"; + // https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.reduce -function __TS__ArrayReduce( - this: void, - arr: T[], - callbackFn: (accumulator: T, currentValue: T, index: number, array: T[]) => T, - ...initial: T[] -): T { - const len = arr.length; +export function __TS__ArrayReduce( + this: TElement[], + callbackFn: (accumulator: TAccumulator, currentValue: TElement, index: number, array: TElement[]) => TAccumulator, + ...initial: TAccumulator[] +): TAccumulator { + const len = this.length; let k = 0; - let accumulator = undefined; + let accumulator: TAccumulator = undefined!; // Check if initial value is present in function call - if (select("#", ...initial) !== 0) { - accumulator = select(1, ...initial); + if (__TS__CountVarargs(...initial) !== 0) { + [accumulator] = [...initial]; } else if (len > 0) { - accumulator = arr[0]; + accumulator = this[0] as unknown as TAccumulator; k = 1; } else { throw "Reduce of empty array with no initial value"; } - for (const i of $range(k, len - 1)) { - accumulator = callbackFn(accumulator, arr[i], i, arr); + for (const i of $range(k + 1, len)) { + accumulator = callbackFn(accumulator, this[i - 1], i - 1, this); } return accumulator; diff --git a/src/lualib/ArrayReduceRight.ts b/src/lualib/ArrayReduceRight.ts index a0d5933d2..2316fa048 100644 --- a/src/lualib/ArrayReduceRight.ts +++ b/src/lualib/ArrayReduceRight.ts @@ -1,27 +1,28 @@ +import { __TS__CountVarargs } from "./CountVarargs"; + // https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.reduce -function __TS__ArrayReduceRight( - this: void, - arr: T[], - callbackFn: (accumulator: T, currentValue: T, index: number, array: T[]) => T, - ...initial: T[] -): T { - const len = arr.length; +export function __TS__ArrayReduceRight( + this: TElement[], + callbackFn: (accumulator: TAccumulator, currentValue: TElement, index: number, array: TElement[]) => TAccumulator, + ...initial: TAccumulator[] +): TAccumulator { + const len = this.length; let k = len - 1; - let accumulator = undefined; + let accumulator: TAccumulator = undefined!; // Check if initial value is present in function call - if (select("#", ...initial) !== 0) { - accumulator = select(1, ...initial); + if (__TS__CountVarargs(...initial) !== 0) { + [accumulator] = [...initial]; } else if (len > 0) { - accumulator = arr[k]; + accumulator = this[k] as unknown as TAccumulator; k -= 1; } else { throw "Reduce of empty array with no initial value"; } - for (const i of $range(k, 0, -1)) { - accumulator = callbackFn(accumulator, arr[i], i, arr); + for (const i of $range(k + 1, 1, -1)) { + accumulator = callbackFn(accumulator, this[i - 1], i - 1, this); } return accumulator; diff --git a/src/lualib/ArrayReverse.ts b/src/lualib/ArrayReverse.ts index b4a97a2c1..96403753b 100644 --- a/src/lualib/ArrayReverse.ts +++ b/src/lualib/ArrayReverse.ts @@ -1,12 +1,12 @@ -function __TS__ArrayReverse(this: void, arr: any[]): any[] { - let i = 0; - let j = arr.length - 1; +export function __TS__ArrayReverse(this: any[]): any[] { + let i = 1; + let j = this.length; while (i < j) { - const temp = arr[j]; - arr[j] = arr[i]; - arr[i] = temp; - i += 1; - j -= 1; + const temp = this[j - 1]; + this[j - 1] = this[i - 1]; + this[i - 1] = temp; + i++; + j--; } - return arr; + return this; } diff --git a/src/lualib/ArraySetLength.ts b/src/lualib/ArraySetLength.ts index e1bd8ea03..6f4de4b18 100644 --- a/src/lualib/ArraySetLength.ts +++ b/src/lualib/ArraySetLength.ts @@ -1,4 +1,4 @@ -function __TS__ArraySetLength(this: void, arr: T[], length: number): number { +export function __TS__ArraySetLength(this: T[], length: number): number { if ( length < 0 || length !== length || // NaN @@ -8,8 +8,8 @@ function __TS__ArraySetLength(this: void, arr: T[], length: number): number { // non-integer throw `invalid array length: ${length}`; } - for (let i = arr.length - 1; i >= length; --i) { - arr[i] = undefined; + for (const i of $range(length + 1, this.length)) { + this[i - 1] = undefined!; } return length; } diff --git a/src/lualib/ArrayShift.ts b/src/lualib/ArrayShift.ts deleted file mode 100644 index e39d9616b..000000000 --- a/src/lualib/ArrayShift.ts +++ /dev/null @@ -1,3 +0,0 @@ -function __TS__ArrayShift(this: void, arr: T[]): T { - return table.remove(arr, 1); -} diff --git a/src/lualib/ArraySlice.ts b/src/lualib/ArraySlice.ts index d38d96006..e8c357e7f 100644 --- a/src/lualib/ArraySlice.ts +++ b/src/lualib/ArraySlice.ts @@ -1,34 +1,39 @@ // https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.slice -function __TS__ArraySlice(this: void, list: T[], first: number, last: number): T[] { - const len = list.length; +export function __TS__ArraySlice(this: T[], first?: number, last?: number): T[] { + const len = this.length; - const relativeStart = first || 0; - - let k: number; - if (relativeStart < 0) { - k = Math.max(len + relativeStart, 0); + first = first ?? 0; + if (first < 0) { + first = len + first; + if (first < 0) { + first = 0; + } } else { - k = Math.min(relativeStart, len); - } - - let relativeEnd = last; - if (last === undefined) { - relativeEnd = len; + if (first > len) { + first = len; + } } - let final: number; - if (relativeEnd < 0) { - final = Math.max(len + relativeEnd, 0); + last = last ?? len; + if (last < 0) { + last = len + last; + if (last < 0) { + last = 0; + } } else { - final = Math.min(relativeEnd, len); + if (last > len) { + last = len; + } } const out = []; - let n = 0; - while (k < final) { - out[n] = list[k]; - k++; + first++; + last++; + let n = 1; + while (first < last) { + out[n - 1] = this[first - 1]; + first++; n++; } return out; diff --git a/src/lualib/ArraySome.ts b/src/lualib/ArraySome.ts index 9122b1bbc..33ffe69ab 100644 --- a/src/lualib/ArraySome.ts +++ b/src/lualib/ArraySome.ts @@ -1,10 +1,10 @@ -function __TS__ArraySome( - this: void, - arr: T[], - callbackfn: (value: T, index?: number, array?: any[]) => boolean +export function __TS__ArraySome( + this: T[], + callbackfn: (value: T, index?: number, array?: any[]) => boolean, + thisArg?: any ): boolean { - for (let i = 0; i < arr.length; i++) { - if (callbackfn(arr[i], i, arr)) { + for (const i of $range(1, this.length)) { + if (callbackfn.call(thisArg, this[i - 1], i - 1, this)) { return true; } } diff --git a/src/lualib/ArraySort.ts b/src/lualib/ArraySort.ts index 1855c3bb5..2a6495511 100644 --- a/src/lualib/ArraySort.ts +++ b/src/lualib/ArraySort.ts @@ -1,8 +1,8 @@ -function __TS__ArraySort(this: void, arr: T[], compareFn?: (a: T, b: T) => number): T[] { +export function __TS__ArraySort(this: T[], compareFn?: (a: T, b: T) => number): T[] { if (compareFn !== undefined) { - table.sort(arr, (a, b) => compareFn(a, b) < 0); + table.sort(this, (a, b) => compareFn(a, b) < 0); } else { - table.sort(arr); + table.sort(this); } - return arr; + return this; } diff --git a/src/lualib/ArraySplice.ts b/src/lualib/ArraySplice.ts index 0fae685e8..24b5264b2 100644 --- a/src/lualib/ArraySplice.ts +++ b/src/lualib/ArraySplice.ts @@ -1,20 +1,26 @@ -// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.splice -function __TS__ArraySplice(this: void, list: T[], ...args: unknown[]): T[] { - const len = list.length; +import { __TS__CountVarargs } from "./CountVarargs"; - const actualArgumentCount = select("#", ...args); - const start = select(1, ...args) as number; - const deleteCount = select(2, ...args) as number; +// https://www.ecma-international.org/ecma-262/9.0/index.html#sec-array.prototype.splice +export function __TS__ArraySplice(this: T[], ...args: any[]): T[] { + const len = this.length; - let actualStart: number; + const actualArgumentCount = __TS__CountVarargs(...args); + let start = args[0] as number; + const deleteCount = args[1] as number; if (start < 0) { - actualStart = Math.max(len + start, 0); - } else { - actualStart = Math.min(start, len); + start = len + start; + if (start < 0) { + start = 0; + } + } else if (start > len) { + start = len; } - const itemCount = Math.max(actualArgumentCount - 2, 0); + let itemCount = actualArgumentCount - 2; + if (itemCount < 0) { + itemCount = 0; + } let actualDeleteCount: number; @@ -23,56 +29,62 @@ function __TS__ArraySplice(this: void, list: T[], ...args: unknown[]): T[] { actualDeleteCount = 0; } else if (actualArgumentCount === 1) { // ECMA-spec line 6: if number of actual arguments is 1 - actualDeleteCount = len - actualStart; + actualDeleteCount = len - start; } else { - actualDeleteCount = Math.min(Math.max(deleteCount || 0, 0), len - actualStart); + actualDeleteCount = deleteCount ?? 0; + if (actualDeleteCount < 0) { + actualDeleteCount = 0; + } + if (actualDeleteCount > len - start) { + actualDeleteCount = len - start; + } } const out: T[] = []; - for (let k = 0; k < actualDeleteCount; k++) { - const from = actualStart + k; + for (const k of $range(1, actualDeleteCount)) { + const from = start + k; - if (list[from]) { - out[k] = list[from]; + if (this[from - 1] !== undefined) { + out[k - 1] = this[from - 1]; } } if (itemCount < actualDeleteCount) { - for (let k = actualStart; k < len - actualDeleteCount; k++) { + for (const k of $range(start + 1, len - actualDeleteCount)) { const from = k + actualDeleteCount; const to = k + itemCount; - if (list[from]) { - list[to] = list[from]; + if (this[from - 1]) { + this[to - 1] = this[from - 1]; } else { - list[to] = undefined; + this[to - 1] = undefined!; } } - for (let k = len; k > len - actualDeleteCount + itemCount; k--) { - list[k - 1] = undefined; + for (const k of $range(len - actualDeleteCount + itemCount + 1, len)) { + this[k - 1] = undefined!; } } else if (itemCount > actualDeleteCount) { - for (let k = len - actualDeleteCount; k > actualStart; k--) { - const from = k + actualDeleteCount - 1; - const to = k + itemCount - 1; + for (const k of $range(len - actualDeleteCount, start + 1, -1)) { + const from = k + actualDeleteCount; + const to = k + itemCount; - if (list[from]) { - list[to] = list[from]; + if (this[from - 1]) { + this[to - 1] = this[from - 1]; } else { - list[to] = undefined; + this[to - 1] = undefined!; } } } - let j = actualStart; + let j = start + 1; for (const i of $range(3, actualArgumentCount)) { - list[j] = select(i, ...args) as T; + this[j - 1] = args[i - 1]; j++; } - for (let k = list.length - 1; k >= len - actualDeleteCount + itemCount; k--) { - list[k] = undefined; + for (const k of $range(this.length, len - actualDeleteCount + itemCount + 1, -1)) { + this[k - 1] = undefined!; } return out; diff --git a/src/lualib/ArrayToObject.ts b/src/lualib/ArrayToObject.ts index 460a77250..1f9e9778a 100644 --- a/src/lualib/ArrayToObject.ts +++ b/src/lualib/ArrayToObject.ts @@ -1,7 +1,7 @@ -function __TS__ArrayToObject(this: void, array: T[]): Record { +export function __TS__ArrayToObject(this: T[]): Record { const object: Record = {}; - for (let i = 0; i < array.length; i += 1) { - object[i] = array[i]; + for (const i of $range(1, this.length)) { + object[i - 1] = this[i - 1]; } return object; } diff --git a/src/lualib/ArrayToReversed.ts b/src/lualib/ArrayToReversed.ts new file mode 100644 index 000000000..50b58855f --- /dev/null +++ b/src/lualib/ArrayToReversed.ts @@ -0,0 +1,5 @@ +export function __TS__ArrayToReversed(this: T[]): T[] { + const copy = [...this]; + copy.reverse(); + return copy; +} diff --git a/src/lualib/ArrayToSorted.ts b/src/lualib/ArrayToSorted.ts new file mode 100644 index 000000000..47c033d2d --- /dev/null +++ b/src/lualib/ArrayToSorted.ts @@ -0,0 +1,5 @@ +export function __TS__ArrayToSorted(this: T[], compareFn?: (a: T, b: T) => number): T[] { + const copy = [...this]; + copy.sort(compareFn); + return copy; +} diff --git a/src/lualib/ArrayToSpliced.ts b/src/lualib/ArrayToSpliced.ts new file mode 100644 index 000000000..298ed8035 --- /dev/null +++ b/src/lualib/ArrayToSpliced.ts @@ -0,0 +1,5 @@ +export function __TS__ArrayToSpliced(this: T[], start: number, deleteCount: number, ...items: T[]): T[] { + const copy = [...this]; + copy.splice(start, deleteCount, ...items); + return copy; +} diff --git a/src/lualib/ArrayUnshift.ts b/src/lualib/ArrayUnshift.ts index 95dc15b8f..6c628c625 100644 --- a/src/lualib/ArrayUnshift.ts +++ b/src/lualib/ArrayUnshift.ts @@ -1,6 +1,12 @@ -function __TS__ArrayUnshift(this: void, arr: T[], ...items: T[]): number { - for (let i = items.length - 1; i >= 0; --i) { - table.insert(arr, 1, items[i]); +export function __TS__ArrayUnshift(this: T[], ...items: T[]): number { + const numItemsToInsert = items.length; + if (numItemsToInsert === 0) return this.length; + + for (const i of $range(this.length, 1, -1)) { + this[i + numItemsToInsert - 1] = this[i - 1]; } - return arr.length; + for (const i of $range(1, numItemsToInsert)) { + this[i - 1] = items[i - 1]; + } + return this.length; } diff --git a/src/lualib/ArrayWith.ts b/src/lualib/ArrayWith.ts new file mode 100644 index 000000000..90d818a5a --- /dev/null +++ b/src/lualib/ArrayWith.ts @@ -0,0 +1,5 @@ +export function __TS__ArrayWith(this: T[], index: number, value: T): T[] { + const copy = [...this]; + copy[index] = value; + return copy; +} diff --git a/src/lualib/Await.ts b/src/lualib/Await.ts new file mode 100644 index 000000000..7f6a345ee --- /dev/null +++ b/src/lualib/Await.ts @@ -0,0 +1,69 @@ +// The following is a translation of the TypeScript async awaiter which uses generators and yields. +// For Lua we use coroutines instead. +// +// Source: +// +// var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { +// function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } +// return new (P || (P = Promise))(function (resolve, reject) { +// function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } +// function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } +// function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } +// step((generator = generator.apply(thisArg, _arguments || [])).next()); +// }); +// }; +// + +import { __TS__Promise } from "./Promise"; + +const coroutine = _G.coroutine ?? {}; +const cocreate = coroutine.create; +const coresume = coroutine.resume; +const costatus = coroutine.status; +const coyield = coroutine.yield; + +// Be extremely careful editing this function. A single non-tail function call may ruin chained awaits performance +// eslint-disable-next-line @typescript-eslint/promise-function-async +export function __TS__AsyncAwaiter(this: void, generator: (this: void) => void) { + return new Promise((resolve, reject) => { + let resolved = false; + const asyncCoroutine = cocreate(generator); + + function fulfilled(value: unknown): void { + const [success, resultOrError] = coresume(asyncCoroutine, value); + if (success) { + // `step` never throws. Tail call return is important! + return step(resultOrError); + } + // `reject` should never throw. Tail call return is important! + return reject(resultOrError); + } + + function step(this: void, result: unknown): void { + if (resolved) { + return; + } + if (costatus(asyncCoroutine) === "dead") { + // `resolve` never throws. Tail call return is important! + return resolve(result); + } + // We cannot use `then` because we need to avoid calling `coroutine.resume` from inside `pcall` + // `fulfilled` and `reject` should never throw. Tail call return is important! + return __TS__Promise.resolve(result).addCallbacks(fulfilled, reject); + } + + const [success, resultOrError] = coresume(asyncCoroutine, (v: unknown) => { + resolved = true; + return __TS__Promise.resolve(v).addCallbacks(resolve, reject); + }); + if (success) { + return step(resultOrError); + } else { + return reject(resultOrError); + } + }); +} + +export function __TS__Await(this: void, thing: unknown) { + return coyield(thing); +} diff --git a/src/lualib/Class.ts b/src/lualib/Class.ts index d5d7af93d..bdcd7ce4f 100644 --- a/src/lualib/Class.ts +++ b/src/lualib/Class.ts @@ -1,4 +1,4 @@ -function __TS__Class(): LuaClass { +export function __TS__Class(): LuaClass { const c: LuaClass = { prototype: {} }; c.prototype.__index = c.prototype; c.prototype.constructor = c; diff --git a/src/lualib/ClassExtends.ts b/src/lualib/ClassExtends.ts index 0df851629..6075868d7 100644 --- a/src/lualib/ClassExtends.ts +++ b/src/lualib/ClassExtends.ts @@ -1,4 +1,4 @@ -function __TS__ClassExtends(this: void, target: LuaClass, base: LuaClass): void { +export function __TS__ClassExtends(this: void, target: LuaClass, base: LuaClass): void { target.____super = base; // Set base class as a metatable, because descriptors use `getmetatable` to get extended prototype diff --git a/src/lualib/CloneDescriptor.ts b/src/lualib/CloneDescriptor.ts index d74292bab..2afe76914 100644 --- a/src/lualib/CloneDescriptor.ts +++ b/src/lualib/CloneDescriptor.ts @@ -1,4 +1,4 @@ -function __TS__CloneDescriptor( +export function __TS__CloneDescriptor( this: void, { enumerable, configurable, get, set, writable, value }: PropertyDescriptor ): PropertyDescriptor { diff --git a/src/lualib/Decorate.ts b/src/lualib/Decorate.ts index 816721da7..fd65f44e8 100644 --- a/src/lualib/Decorate.ts +++ b/src/lualib/Decorate.ts @@ -1,48 +1,20 @@ /** - * SEE: https://github.com/Microsoft/TypeScript/blob/master/src/compiler/transformers/ts.ts#L3598 + * TypeScript 5.0 decorators */ -type Decorator = ( - target: TTarget, - key?: TKey, - descriptor?: PropertyDescriptor -) => TTarget; -function __TS__Decorate( - this: void, - decorators: Array>, - target: TTarget, - key?: TKey, - desc?: any +import { Decorator } from "./Decorator"; + +export function __TS__Decorate( + this: TClass, + originalValue: TTarget, + decorators: Array>, + context: DecoratorContext ): TTarget { - let result = target; + let result = originalValue; for (let i = decorators.length; i >= 0; i--) { const decorator = decorators[i]; - if (decorator) { - const oldResult = result; - - if (key === undefined) { - result = decorator(result); - } else if (desc === true) { - const value = rawget(target, key); - const descriptor = __TS__ObjectGetOwnPropertyDescriptor(target, key) || { - configurable: true, - writable: true, - value, - }; - const desc = decorator(target, key, descriptor) || descriptor; - const isSimpleValue = desc.configurable === true && desc.writable === true && !desc.get && !desc.set; - if (isSimpleValue) { - rawset(target, key, desc.value); - } else { - __TS__SetDescriptor(target, key, { ...descriptor, ...desc }); - } - } else if (desc === false) { - result = decorator(target, key, desc); - } else { - result = decorator(target, key); - } - - result = result || oldResult; + if (decorator !== undefined) { + result = decorator.call(this, result, context) ?? result; } } diff --git a/src/lualib/DecorateLegacy.ts b/src/lualib/DecorateLegacy.ts new file mode 100644 index 000000000..0369623d3 --- /dev/null +++ b/src/lualib/DecorateLegacy.ts @@ -0,0 +1,54 @@ +/** + * Old-style decorators, activated by enabling the experimentalDecorators flag + */ +import { __TS__ObjectGetOwnPropertyDescriptor } from "./ObjectGetOwnPropertyDescriptor"; +import { __TS__SetDescriptor } from "./SetDescriptor"; + +export type LegacyDecorator = ( + target: TTarget, + key?: TKey, + descriptor?: PropertyDescriptor +) => TTarget; + +export function __TS__DecorateLegacy( + this: void, + decorators: Array>, + target: TTarget, + key?: TKey, + desc?: any +): TTarget { + let result = target; + + for (let i = decorators.length; i >= 0; i--) { + const decorator = decorators[i]; + if (decorator !== undefined) { + const oldResult = result; + + if (key === undefined) { + result = decorator(result); + } else if (desc === true) { + const value = rawget(target, key); + const descriptor = __TS__ObjectGetOwnPropertyDescriptor(target, key) ?? { + configurable: true, + writable: true, + value, + }; + const desc = decorator(target, key, descriptor) || descriptor; + const isSimpleValue = desc.configurable === true && desc.writable === true && !desc.get && !desc.set; + if (isSimpleValue) { + rawset(target, key, desc.value); + } else { + __TS__SetDescriptor(target, key, { ...descriptor, ...desc }); + } + } else if (desc === false) { + result = decorator(target, key, desc); + } else { + result = decorator(target, key); + } + + result = result || oldResult; + } + } + + return result; +} diff --git a/src/lualib/DecorateParam.ts b/src/lualib/DecorateParam.ts index 4e11f3e54..0165294b2 100644 --- a/src/lualib/DecorateParam.ts +++ b/src/lualib/DecorateParam.ts @@ -1,12 +1,14 @@ +import type { LegacyDecorator } from "./DecorateLegacy"; + type ParamDecorator = ( target: TTarget, - key: TKey, + key: TKey | undefined, index: number ) => TTarget; -function __TS__DecorateParam( +export function __TS__DecorateParam( this: void, paramIndex: number, decorator: ParamDecorator -): Decorator { +): LegacyDecorator { return (target: TTarget, key?: TKey) => decorator(target, key, paramIndex); } diff --git a/src/lualib/Decorator.d.ts b/src/lualib/Decorator.d.ts new file mode 100644 index 000000000..d8f84febf --- /dev/null +++ b/src/lualib/Decorator.d.ts @@ -0,0 +1 @@ +export type Decorator = (target: TTarget, context: DecoratorContext) => TTarget; diff --git a/src/lualib/DelegatedYield.ts b/src/lualib/DelegatedYield.ts index ab47d02d8..827dd6326 100644 --- a/src/lualib/DelegatedYield.ts +++ b/src/lualib/DelegatedYield.ts @@ -1,4 +1,6 @@ -function __TS__DelegatedYield(this: void, iterable: string | GeneratorIterator | Iterable | readonly T[]) { +import { GeneratorIterator } from "./GeneratorIterator"; + +export function __TS__DelegatedYield(this: void, iterable: string | GeneratorIterator | Iterable | readonly T[]) { if (typeof iterable === "string") { for (const index of $range(0, iterable.length - 1)) { coroutine.yield(iterable[index]); diff --git a/src/lualib/Delete.ts b/src/lualib/Delete.ts index 079dcb93d..07499f44f 100644 --- a/src/lualib/Delete.ts +++ b/src/lualib/Delete.ts @@ -1,19 +1,17 @@ -function __TS__Delete(this: void, target: any, key: any): boolean { +import { __TS__ObjectGetOwnPropertyDescriptors } from "./ObjectGetOwnPropertyDescriptors"; + +export function __TS__Delete(this: void, target: any, key: any): boolean { const descriptors = __TS__ObjectGetOwnPropertyDescriptors(target); const descriptor = descriptors[key]; if (descriptor) { if (!descriptor.configurable) { - throw `Cannot delete property ${key} of ${target}.`; + throw new TypeError(`Cannot delete property ${key} of ${target}.`); } descriptors[key] = undefined; return true; } - if (target[key] !== undefined) { - target[key] = undefined; - return true; - } - - return false; + target[key] = undefined; + return true; } diff --git a/src/lualib/DescriptorGet.ts b/src/lualib/DescriptorGet.ts new file mode 100644 index 000000000..034a5825a --- /dev/null +++ b/src/lualib/DescriptorGet.ts @@ -0,0 +1,25 @@ +const getmetatable = _G.getmetatable; +const rawget = _G.rawget; + +export function __TS__DescriptorGet(this: any, metatable: any, key: string): void { + while (metatable) { + const rawResult = rawget(metatable, key as any); + if (rawResult !== undefined) { + return rawResult; + } + + const descriptors = rawget(metatable, "_descriptors"); + if (descriptors) { + const descriptor: PropertyDescriptor = descriptors[key]; + if (descriptor !== undefined) { + if (descriptor.get) { + return descriptor.get.call(this); + } + + return descriptor.value; + } + } + + metatable = getmetatable(metatable); + } +} diff --git a/src/lualib/DescriptorSet.ts b/src/lualib/DescriptorSet.ts new file mode 100644 index 000000000..bb50ca4d3 --- /dev/null +++ b/src/lualib/DescriptorSet.ts @@ -0,0 +1,28 @@ +const getmetatable = _G.getmetatable; +const rawget = _G.rawget; +const rawset = _G.rawset; + +export function __TS__DescriptorSet(this: any, metatable: any, key: string, value: any): void { + while (metatable) { + const descriptors = rawget(metatable, "_descriptors"); + if (descriptors) { + const descriptor: PropertyDescriptor = descriptors[key]; + if (descriptor !== undefined) { + if (descriptor.set) { + descriptor.set.call(this, value); + } else { + if (descriptor.writable === false) { + throw `Cannot assign to read only property '${key}' of object '${this}'`; + } + + descriptor.value = value; + } + return; + } + } + + metatable = getmetatable(metatable); + } + + rawset(this, key, value); +} diff --git a/src/lualib/Error.ts b/src/lualib/Error.ts index 197ff816b..ae5deb05b 100644 --- a/src/lualib/Error.ts +++ b/src/lualib/Error.ts @@ -1,9 +1,14 @@ +import { __TS__New } from "./New"; + interface ErrorType { name: string; new (...args: any[]): Error; } -function __TS__GetErrorStack(constructor: () => any): string { +function getErrorStack(constructor: () => any): string | undefined { + // If debug module is not available in this environment, don't bother trying to get stack trace + if (debug === undefined) return undefined; + let level = 1; while (true) { const info = debug.getinfo(level, "f"); @@ -17,14 +22,26 @@ function __TS__GetErrorStack(constructor: () => any): string { } } - return debug.traceback(undefined, level); + if (_VERSION.includes("Lua 5.0")) { + return debug.traceback(`[Level ${level}]`); + // @ts-ignore Fails when compiled with Lua 5.0 types + } else if (_VERSION === "Lua 5.1") { + // Lua 5.1 and LuaJIT have a bug where it's not possible to specify the level without a message. + // @ts-ignore Fails when compiled with Lua 5.0 types + return string.sub(debug.traceback("", level), 2); + } else { + // @ts-ignore Fails when compiled with Lua 5.0 types + return debug.traceback(undefined, level); + } } -function __TS__WrapErrorToString(getDescription: (this: T) => string): (this: T) => string { +function wrapErrorToString(getDescription: (this: T) => string): (this: T) => string { return function (this: Error): string { - const description = getDescription.call(this); + const description = getDescription.call(this as T); const caller = debug.getinfo(3, "f"); - if (_VERSION === "Lua 5.1" || (caller && caller.func !== error)) { + // @ts-ignore Fails when compiled with Lua 5.0 types + const isClassicLua = _VERSION.includes("Lua 5.0"); + if (isClassicLua || (caller && caller.func !== error)) { return description; } else { return `${description}\n${this.stack}`; @@ -32,24 +49,24 @@ function __TS__WrapErrorToString(getDescription: (this: T) => s }; } -function __TS__InitErrorClass(Type: ErrorType, name: string): any { +function initErrorClass(Type: ErrorType, name: string): any { Type.name = name; return setmetatable(Type, { __call: (_self: any, message: string) => new Type(message), }); } -Error = __TS__InitErrorClass( +export const Error: ErrorConstructor = initErrorClass( class implements Error { public name = "Error"; - public stack: string; + public stack?: string; constructor(public message = "") { - this.stack = __TS__GetErrorStack((this.constructor as any).new); + this.stack = getErrorStack(__TS__New as any); const metatable = getmetatable(this); - if (!metatable.__errorToStringPatched) { + if (metatable && !metatable.__errorToStringPatched) { metatable.__errorToStringPatched = true; - metatable.__tostring = __TS__WrapErrorToString(metatable.__tostring); + metatable.__tostring = wrapErrorToString(metatable.__tostring); } } @@ -60,11 +77,17 @@ Error = __TS__InitErrorClass( "Error" ); -for (const errorName of ["RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError"]) { - globalThis[errorName] = __TS__InitErrorClass( +function createErrorClass(name: string) { + return initErrorClass( class extends Error { - public name = errorName; + public name = name; }, - errorName + name ); } + +export const RangeError = createErrorClass("RangeError"); +export const ReferenceError = createErrorClass("ReferenceError"); +export const SyntaxError = createErrorClass("SyntaxError"); +export const TypeError = createErrorClass("TypeError"); +export const URIError = createErrorClass("URIError"); diff --git a/src/lualib/FunctionBind.ts b/src/lualib/FunctionBind.ts index e12fbe37c..f36e9f6e5 100644 --- a/src/lualib/FunctionBind.ts +++ b/src/lualib/FunctionBind.ts @@ -1,13 +1,10 @@ -function __TS__FunctionBind( +export function __TS__FunctionBind( this: void, fn: (this: void, ...argArray: any[]) => any, - thisArg: any, ...boundArgs: any[] ): (...args: any[]) => any { return (...args: any[]) => { - for (let i = 0; i < boundArgs.length; ++i) { - table.insert(args, i + 1, boundArgs[i]); - } - return fn(thisArg, ...args); + args.unshift(...boundArgs); + return fn(...args); }; } diff --git a/src/lualib/Generator.ts b/src/lualib/Generator.ts index 1573f5ec3..c17aab8c7 100644 --- a/src/lualib/Generator.ts +++ b/src/lualib/Generator.ts @@ -1,14 +1,12 @@ -interface GeneratorIterator { - ____coroutine: LuaThread; - [Symbol.iterator](): GeneratorIterator; - next: typeof __TS__GeneratorNext; -} +import { __TS__CountVarargs } from "./CountVarargs"; +import { GeneratorIterator } from "./GeneratorIterator"; +import { __TS__Unpack } from "./Unpack"; -function __TS__GeneratorIterator(this: GeneratorIterator) { +function generatorIterator(this: GeneratorIterator) { return this; } -function __TS__GeneratorNext(this: GeneratorIterator, ...args: any[]) { +function generatorNext(this: GeneratorIterator, ...args: any[]) { const co = this.____coroutine; if (coroutine.status(co) === "dead") return { done: true }; @@ -18,14 +16,14 @@ function __TS__GeneratorNext(this: GeneratorIterator, ...args: any[]) { return { value, done: coroutine.status(co) === "dead" }; } -function __TS__Generator(this: void, fn: (this: void, ...args: any[]) => any) { +export function __TS__Generator(this: void, fn: (this: void, ...args: any[]) => any) { return function (this: void, ...args: any[]): GeneratorIterator { - const argsLength = select("#", ...args); + const argsLength = __TS__CountVarargs(...args); return { // Using explicit this there, since we don't pass arguments after the first nil and context is likely to be nil - ____coroutine: coroutine.create(() => fn((unpack || table.unpack)(args, 1, argsLength))), - [Symbol.iterator]: __TS__GeneratorIterator, - next: __TS__GeneratorNext, + ____coroutine: coroutine.create(() => fn(...__TS__Unpack(args, 1, argsLength))), + [Symbol.iterator]: generatorIterator, + next: generatorNext, }; }; } diff --git a/src/lualib/GeneratorIterator.d.ts b/src/lualib/GeneratorIterator.d.ts new file mode 100644 index 000000000..2509046b5 --- /dev/null +++ b/src/lualib/GeneratorIterator.d.ts @@ -0,0 +1,5 @@ +export interface GeneratorIterator { + ____coroutine: LuaThread; + [Symbol.iterator](): GeneratorIterator; + next: typeof generatorNext; +} diff --git a/src/lualib/InstanceOf.ts b/src/lualib/InstanceOf.ts index a4d80c881..317c0b794 100644 --- a/src/lualib/InstanceOf.ts +++ b/src/lualib/InstanceOf.ts @@ -1,11 +1,11 @@ -function __TS__InstanceOf(this: void, obj: LuaClassInstance, classTbl: LuaClass): boolean { +export function __TS__InstanceOf(this: void, obj: LuaClassInstance, classTbl: LuaClass): boolean { if (typeof classTbl !== "object") { throw "Right-hand side of 'instanceof' is not an object"; } if (classTbl[Symbol.hasInstance] !== undefined) { // eslint-disable-next-line no-implicit-coercion - return !!classTbl[Symbol.hasInstance](obj); + return !!classTbl[Symbol.hasInstance]!(obj); } if (typeof obj === "object") { @@ -14,7 +14,7 @@ function __TS__InstanceOf(this: void, obj: LuaClassInstance, classTbl: LuaClass) if (luaClass === classTbl) { return true; } - luaClass = luaClass.____super; + luaClass = luaClass.____super!; } } return false; diff --git a/src/lualib/InstanceOfObject.ts b/src/lualib/InstanceOfObject.ts index 681e243c5..28e32150e 100644 --- a/src/lualib/InstanceOfObject.ts +++ b/src/lualib/InstanceOfObject.ts @@ -1,4 +1,4 @@ -function __TS__InstanceOfObject(this: void, value: unknown): boolean { +export function __TS__InstanceOfObject(this: void, value: unknown): boolean { const valueType = type(value); return valueType === "table" || valueType === "function"; } diff --git a/src/lualib/Iterator.ts b/src/lualib/Iterator.ts index d6112cd88..6991ea888 100644 --- a/src/lualib/Iterator.ts +++ b/src/lualib/Iterator.ts @@ -1,40 +1,38 @@ -/** @tupleReturn */ -function __TS__IteratorGeneratorStep(this: GeneratorIterator): [true, any] | [] { +import { GeneratorIterator } from "./GeneratorIterator"; + +function iteratorGeneratorStep(this: GeneratorIterator): LuaMultiReturn<[true, any] | []> { const co = this.____coroutine; const [status, value] = coroutine.resume(co); if (!status) throw value; - if (coroutine.status(co) === "dead") return []; - return [true, value]; + if (coroutine.status(co) === "dead") return $multi(); + return $multi(true, value); } -/** @tupleReturn */ -function __TS__IteratorIteratorStep(this: Iterator): [true, T] | [] { +function iteratorIteratorStep(this: Iterator): LuaMultiReturn<[true, T] | []> { const result = this.next(); - if (result.done) return []; - return [true, result.value]; + if (result.done) return $multi(); + return $multi(true, result.value); } -/** @tupleReturn */ -function __TS__IteratorStringStep(this: string, index: number): [number, string] | [] { +function iteratorStringStep(this: string, index: number): LuaMultiReturn<[number, string] | []> { index += 1; - if (index > this.length) return []; - return [index, string.sub(this, index, index)]; + if (index > this.length) return $multi(); + return $multi(index, string.sub(this, index, index)); } -/** @tupleReturn */ -function __TS__Iterator( +export function __TS__Iterator( this: void, iterable: string | GeneratorIterator | Iterable | readonly T[] -): [(...args: any[]) => [any, any] | [], ...any[]] | LuaIterable> { +): LuaMultiReturn<[(...args: any[]) => [any, any] | [], ...any[]]> | LuaIterable> { if (typeof iterable === "string") { - return [__TS__IteratorStringStep, iterable, 0]; + return $multi(iteratorStringStep, iterable, 0); } else if ("____coroutine" in iterable) { - return [__TS__IteratorGeneratorStep, iterable]; + return $multi(iteratorGeneratorStep, iterable); } else if (iterable[Symbol.iterator]) { const iterator = iterable[Symbol.iterator](); - return [__TS__IteratorIteratorStep, iterator]; + return $multi(iteratorIteratorStep, iterator); } else { return ipairs(iterable as readonly T[]); } diff --git a/src/lualib/LuaIteratorSpread.ts b/src/lualib/LuaIteratorSpread.ts new file mode 100644 index 000000000..b846bc535 --- /dev/null +++ b/src/lualib/LuaIteratorSpread.ts @@ -0,0 +1,13 @@ +export function __TS__LuaIteratorSpread( + this: (this: void, state: TState, key: TKey) => LuaMultiReturn<[TKey, TValue]>, + state: TState, + firstKey: TKey +): LuaMultiReturn> { + const results = []; + let [key, value] = this(state, firstKey); + while (key) { + results.push([key, value]); + [key, value] = this(state, key); + } + return $multi(...results) as LuaMultiReturn>; +} diff --git a/src/lualib/Map.ts b/src/lualib/Map.ts index e442b6132..c2bcb43a4 100644 --- a/src/lualib/Map.ts +++ b/src/lualib/Map.ts @@ -1,4 +1,4 @@ -Map = class Map { +export class Map { public static [Symbol.species] = Map; public [Symbol.toStringTag] = "Map"; @@ -52,24 +52,24 @@ Map = class Map { // Do order bookkeeping const next = this.nextKey.get(key); const previous = this.previousKey.get(key); - if (next && previous) { + if (next !== undefined && previous !== undefined) { this.nextKey.set(previous, next); this.previousKey.set(next, previous); - } else if (next) { + } else if (next !== undefined) { this.firstKey = next; - this.previousKey.set(next, undefined); - } else if (previous) { + this.previousKey.set(next, undefined!); + } else if (previous !== undefined) { this.lastKey = previous; - this.nextKey.set(previous, undefined); + this.nextKey.set(previous, undefined!); } else { this.firstKey = undefined; this.lastKey = undefined; } - this.nextKey.set(key, undefined); - this.previousKey.set(key, undefined); + this.nextKey.set(key, undefined!); + this.previousKey.set(key, undefined!); } - this.items.set(key, undefined); + this.items.set(key, undefined!); return contains; } @@ -100,8 +100,8 @@ Map = class Map { this.firstKey = key; this.lastKey = key; } else if (isNewValue) { - this.nextKey.set(this.lastKey, key); - this.previousKey.set(key, this.lastKey); + this.nextKey.set(this.lastKey!, key); + this.previousKey.set(key, this.lastKey!); this.lastKey = key; } @@ -113,47 +113,65 @@ Map = class Map { } public entries(): IterableIterator<[K, V]> { + const getFirstKey = () => this.firstKey; const { items, nextKey } = this; - let key = this.firstKey; + let key: K | undefined; + let started = false; return { [Symbol.iterator](): IterableIterator<[K, V]> { return this; }, next(): IteratorResult<[K, V]> { - const result = { done: !key, value: [key, items.get(key)] as [K, V] }; - key = nextKey.get(key); - return result; + if (!started) { + started = true; + key = getFirstKey(); + } else { + key = nextKey.get(key!); + } + return { done: !key, value: [key!, items.get(key!)] as [K, V] }; }, }; } public keys(): IterableIterator { + const getFirstKey = () => this.firstKey; const nextKey = this.nextKey; - let key = this.firstKey; + let key: K | undefined; + let started = false; return { [Symbol.iterator](): IterableIterator { return this; }, next(): IteratorResult { - const result = { done: !key, value: key }; - key = nextKey.get(key); - return result; + if (!started) { + started = true; + key = getFirstKey(); + } else { + key = nextKey.get(key!); + } + return { done: !key, value: key! }; }, }; } public values(): IterableIterator { + const getFirstKey = () => this.firstKey; const { items, nextKey } = this; - let key = this.firstKey; + let key: K | undefined; + let started = false; return { [Symbol.iterator](): IterableIterator { return this; }, next(): IteratorResult { - const result = { done: !key, value: items.get(key) }; - key = nextKey.get(key); - return result; + if (!started) { + started = true; + key = getFirstKey(); + } else { + key = nextKey.get(key!); + } + return { done: !key, value: items.get(key!) }; }, }; } -}; +} diff --git a/src/lualib/MapGroupBy.ts b/src/lualib/MapGroupBy.ts new file mode 100644 index 000000000..d3e079192 --- /dev/null +++ b/src/lualib/MapGroupBy.ts @@ -0,0 +1,22 @@ +export function __TS__MapGroupBy( + this: void, + items: Iterable, + keySelector: (item: T, index: number) => K +): Map { + const result = new Map(); + + let i = 0; + for (const item of items) { + const key = keySelector(item, i); + + if (result.has(key)) { + result.get(key)!.push(item); + } else { + result.set(key, [item]); + } + + i++; + } + + return result; +} diff --git a/src/lualib/MathAtan2.ts b/src/lualib/MathAtan2.ts index fdf322664..185cfe678 100644 --- a/src/lualib/MathAtan2.ts +++ b/src/lualib/MathAtan2.ts @@ -1 +1 @@ -const __TS__MathAtan2 = math.atan2 || math.atan; +export const __TS__MathAtan2 = math.atan2 || math.atan; diff --git a/src/lualib/MathSign.ts b/src/lualib/MathSign.ts new file mode 100644 index 000000000..629895a11 --- /dev/null +++ b/src/lualib/MathSign.ts @@ -0,0 +1,15 @@ +// https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-math.sign + +import { __TS__NumberIsNaN } from "./NumberIsNaN"; + +export function __TS__MathSign(this: void, val: number) { + if (__TS__NumberIsNaN(val) || val === 0) { + return val; + } + + if (val < 0) { + return -1; + } + + return 1; +} diff --git a/src/lualib/MathTrunc.ts b/src/lualib/MathTrunc.ts new file mode 100644 index 000000000..f244c2147 --- /dev/null +++ b/src/lualib/MathTrunc.ts @@ -0,0 +1,10 @@ +// https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-math.trunc + +import { __TS__NumberIsFinite } from "./NumberIsFinite"; +export function __TS__MathTrunc(this: void, val: number) { + if (!__TS__NumberIsFinite(val) || val === 0) { + return val; + } + + return val > 0 ? math.floor(val) : math.ceil(val); +} diff --git a/src/lualib/New.ts b/src/lualib/New.ts index 8b33ec837..b54890917 100644 --- a/src/lualib/New.ts +++ b/src/lualib/New.ts @@ -1,4 +1,4 @@ -function __TS__New(this: void, target: LuaClass, ...args: any[]): any { +export function __TS__New(this: void, target: LuaClass, ...args: any[]): any { const instance: any = setmetatable({}, target.prototype); instance.____constructor(...args); return instance; diff --git a/src/lualib/Number.ts b/src/lualib/Number.ts index 0b19a1600..b258b48f6 100644 --- a/src/lualib/Number.ts +++ b/src/lualib/Number.ts @@ -1,4 +1,4 @@ -function __TS__Number(this: void, value: unknown): number { +export function __TS__Number(this: void, value: unknown): number { const valueType = type(value); if (valueType === "number") { return value as number; diff --git a/src/lualib/NumberIsFinite.ts b/src/lualib/NumberIsFinite.ts index 98df30790..f225b8c5c 100644 --- a/src/lualib/NumberIsFinite.ts +++ b/src/lualib/NumberIsFinite.ts @@ -1,3 +1,3 @@ -function __TS__NumberIsFinite(this: void, value: unknown): boolean { +export function __TS__NumberIsFinite(this: void, value: unknown): boolean { return typeof value === "number" && value === value && value !== Infinity && value !== -Infinity; } diff --git a/src/lualib/NumberIsInteger.ts b/src/lualib/NumberIsInteger.ts new file mode 100644 index 000000000..275fcae03 --- /dev/null +++ b/src/lualib/NumberIsInteger.ts @@ -0,0 +1,6 @@ +import { __TS__NumberIsFinite } from "./NumberIsFinite"; + +/// https://tc39.es/ecma262/multipage/numbers-and-dates.html#sec-number.isinteger +export function __TS__NumberIsInteger(this: void, value: unknown): boolean { + return __TS__NumberIsFinite(value) && math.floor(value as number) === (value as number); +} diff --git a/src/lualib/NumberIsNaN.ts b/src/lualib/NumberIsNaN.ts index b040f5e5b..e57631cc1 100644 --- a/src/lualib/NumberIsNaN.ts +++ b/src/lualib/NumberIsNaN.ts @@ -1,3 +1,3 @@ -function __TS__NumberIsNaN(this: void, value: unknown): boolean { +export function __TS__NumberIsNaN(this: void, value: unknown): boolean { return value !== value; } diff --git a/src/lualib/NumberToFixed.ts b/src/lualib/NumberToFixed.ts new file mode 100644 index 000000000..355da13f0 --- /dev/null +++ b/src/lualib/NumberToFixed.ts @@ -0,0 +1,14 @@ +/// https://www.ecma-international.org/ecma-262/10.0/index.html#sec-number.prototype.tofixed +export function __TS__NumberToFixed(this: number, fractionDigits?: number): string { + if (Math.abs(this) >= 1e21 || this !== this) { + return this.toString(); + } + const f = Math.floor(fractionDigits ?? 0); + // reduced to 99 as string.format only supports 2-digit numbers + if (f < 0 || f > 99) { + throw "toFixed() digits argument must be between 0 and 99"; + } + // throws "invalid format (width or precision too long)" if strlen > 99 + // if (f < 80) return fmt; else try {return fmt} catch(_) { throw "toFixed() digits argument..." } + return string.format(`%.${f}f`, this); +} diff --git a/src/lualib/NumberToString.ts b/src/lualib/NumberToString.ts index 52da58d7c..6cdbaadc3 100644 --- a/src/lualib/NumberToString.ts +++ b/src/lualib/NumberToString.ts @@ -1,7 +1,9 @@ -const ____radixChars = "0123456789abcdefghijklmnopqrstuvwxyz"; +import { __TS__MathModf } from "./MathModf"; + +const radixChars = "0123456789abcdefghijklmnopqrstuvwxyz"; // https://www.ecma-international.org/ecma-262/10.0/index.html#sec-number.prototype.tostring -function __TS__NumberToString(this: number, radix?: number): string { +export function __TS__NumberToString(this: number, radix?: number): string { if (radix === undefined || radix === 10 || this === Infinity || this === -Infinity || this !== this) { return this.toString(); } @@ -11,7 +13,7 @@ function __TS__NumberToString(this: number, radix?: number): string { throw "toString() radix argument must be between 2 and 36"; } - let [integer, fraction] = math.modf(Math.abs(this)); + let [integer, fraction] = __TS__MathModf(Math.abs(this)); let result = ""; if (radix === 8) { @@ -20,7 +22,7 @@ function __TS__NumberToString(this: number, radix?: number): string { result = string.format("%x", integer); } else { do { - result = ____radixChars[integer % radix] + result; + result = radixChars[integer % radix] + result; integer = Math.floor(integer / radix); } while (integer !== 0); } @@ -33,7 +35,7 @@ function __TS__NumberToString(this: number, radix?: number): string { fraction *= radix; delta *= radix; const digit = Math.floor(fraction); - result += ____radixChars[digit]; + result += radixChars[digit]; fraction -= digit; // TODO: Round to even } while (fraction >= delta); diff --git a/src/lualib/ObjectAssign.ts b/src/lualib/ObjectAssign.ts index bcf521c07..7d0a3a9b1 100644 --- a/src/lualib/ObjectAssign.ts +++ b/src/lualib/ObjectAssign.ts @@ -1,15 +1,13 @@ // https://tc39.github.io/ecma262/#sec-object.assign -// eslint-disable-next-line @typescript-eslint/ban-types -function __TS__ObjectAssign(this: void, to: T, ...sources: object[]): T { - if (to === undefined) { - return to; - } - - for (const source of sources) { - for (const key in source) { - to[key] = source[key]; +export function __TS__ObjectAssign(this: void, target: T, ...sources: T[]): T { + for (const i of $range(1, sources.length)) { + const source = sources[i - 1]; + if (type(source) === "table") { + for (const key in source) { + target[key] = source[key]; + } } } - return to; + return target; } diff --git a/src/lualib/ObjectDefineProperty.ts b/src/lualib/ObjectDefineProperty.ts index e9aa599d5..c0dc40a1f 100644 --- a/src/lualib/ObjectDefineProperty.ts +++ b/src/lualib/ObjectDefineProperty.ts @@ -1,5 +1,13 @@ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty -function __TS__ObjectDefineProperty(this: void, target: T, key: any, desc: PropertyDescriptor): T { + +import { __TS__SetDescriptor } from "./SetDescriptor"; + +export function __TS__ObjectDefineProperty( + this: void, + target: T, + key: any, + desc: PropertyDescriptor +): T { const luaKey = typeof key === "number" ? key + 1 : key; const value = rawget(target, luaKey); @@ -17,9 +25,9 @@ function __TS__ObjectDefineProperty(this: void, target: T, key: any, desc: Pr descriptor = { set: desc.set, get: desc.get, - configurable: desc.configurable !== undefined ? desc.configurable : valueExists, - enumerable: desc.enumerable !== undefined ? desc.enumerable : valueExists, - writable: desc.writable !== undefined ? desc.writable : valueExists, + configurable: desc.configurable ?? valueExists, + enumerable: desc.enumerable ?? valueExists, + writable: desc.writable ?? valueExists, value: desc.value !== undefined ? desc.value : value, }; } diff --git a/src/lualib/ObjectEntries.ts b/src/lualib/ObjectEntries.ts index f491bd039..20fde96ad 100644 --- a/src/lualib/ObjectEntries.ts +++ b/src/lualib/ObjectEntries.ts @@ -1,7 +1,12 @@ -function __TS__ObjectEntries(this: void, obj: any): Array { - const result = []; +export function __TS__ObjectEntries( + this: void, + obj: Record +): Array<[TKey, TValue]> { + const result: Array<[TKey, TValue]> = []; + let len = 0; for (const key in obj) { - result[result.length] = [key, obj[key]]; + len++; + result[len - 1] = [key, obj[key]]; } return result; } diff --git a/src/lualib/ObjectFromEntries.ts b/src/lualib/ObjectFromEntries.ts index efdf7dd90..88c224cf7 100644 --- a/src/lualib/ObjectFromEntries.ts +++ b/src/lualib/ObjectFromEntries.ts @@ -1,4 +1,4 @@ -function __TS__ObjectFromEntries( +export function __TS__ObjectFromEntries( this: void, entries: ReadonlyArray<[string, T]> | Iterable<[string, T]> ): Record { diff --git a/src/lualib/ObjectGetOwnPropertyDescriptor.ts b/src/lualib/ObjectGetOwnPropertyDescriptor.ts index 84dab33df..975de6712 100644 --- a/src/lualib/ObjectGetOwnPropertyDescriptor.ts +++ b/src/lualib/ObjectGetOwnPropertyDescriptor.ts @@ -1,6 +1,10 @@ -function __TS__ObjectGetOwnPropertyDescriptor(this: void, object: any, key: any): PropertyDescriptor | undefined { +export function __TS__ObjectGetOwnPropertyDescriptor( + this: void, + object: any, + key: any +): PropertyDescriptor | undefined { const metatable = getmetatable(object); if (!metatable) return; if (!rawget(metatable, "_descriptors")) return; - return rawget(metatable, "_descriptors")[key]; + return rawget(metatable, "_descriptors")![key]; } diff --git a/src/lualib/ObjectGetOwnPropertyDescriptors.ts b/src/lualib/ObjectGetOwnPropertyDescriptors.ts index 04aee8b51..69d23af06 100644 --- a/src/lualib/ObjectGetOwnPropertyDescriptors.ts +++ b/src/lualib/ObjectGetOwnPropertyDescriptors.ts @@ -1,5 +1,8 @@ -function __TS__ObjectGetOwnPropertyDescriptors(this: void, object: any): Record { +export function __TS__ObjectGetOwnPropertyDescriptors( + this: void, + object: any +): Record { const metatable = getmetatable(object); if (!metatable) return {}; - return rawget(metatable, "_descriptors") || {}; + return rawget(metatable, "_descriptors") ?? {}; } diff --git a/src/lualib/ObjectGroupBy.ts b/src/lualib/ObjectGroupBy.ts new file mode 100644 index 000000000..6328ad2aa --- /dev/null +++ b/src/lualib/ObjectGroupBy.ts @@ -0,0 +1,22 @@ +export function __TS__ObjectGroupBy( + this: void, + items: Iterable, + keySelector: (item: T, index: number) => K +): Partial> { + const result: Partial> = {}; + + let i = 0; + for (const item of items) { + const key = keySelector(item, i); + + if (key in result) { + result[key]!.push(item); + } else { + result[key] = [item]; + } + + i++; + } + + return result; +} diff --git a/src/lualib/ObjectKeys.ts b/src/lualib/ObjectKeys.ts index 8163a620a..c7a0c0208 100644 --- a/src/lualib/ObjectKeys.ts +++ b/src/lualib/ObjectKeys.ts @@ -1,7 +1,9 @@ -function __TS__ObjectKeys(this: void, obj: any): Array { +export function __TS__ObjectKeys(this: void, obj: any): Array { const result = []; + let len = 0; for (const key in obj) { - result[result.length] = key; + len++; + result[len - 1] = key; } return result; } diff --git a/src/lualib/ObjectRest.ts b/src/lualib/ObjectRest.ts index 7da14b2fb..276135ce5 100644 --- a/src/lualib/ObjectRest.ts +++ b/src/lualib/ObjectRest.ts @@ -1,4 +1,4 @@ -function __TS__ObjectRest( +export function __TS__ObjectRest( this: void, target: Record, usedProperties: Partial> diff --git a/src/lualib/ObjectValues.ts b/src/lualib/ObjectValues.ts index e850f6a31..925a61ab3 100644 --- a/src/lualib/ObjectValues.ts +++ b/src/lualib/ObjectValues.ts @@ -1,7 +1,9 @@ -function __TS__ObjectValues(this: void, obj: any): Array { +export function __TS__ObjectValues(this: void, obj: any): Array { const result = []; + let len = 0; for (const key in obj) { - result[result.length] = obj[key]; + len++; + result[len - 1] = obj[key]; } return result; } diff --git a/src/lualib/ParseFloat.ts b/src/lualib/ParseFloat.ts index 1cd069111..7720acb6b 100644 --- a/src/lualib/ParseFloat.ts +++ b/src/lualib/ParseFloat.ts @@ -1,11 +1,13 @@ -function __TS__ParseFloat(this: void, numberString: string): number { +import { __TS__Match } from "./Match"; + +export function __TS__ParseFloat(this: void, numberString: string): number { // Check if string is infinity - const infinityMatch = string.match(numberString, "^%s*(-?Infinity)"); - if (infinityMatch) { + const [infinityMatch] = __TS__Match(numberString, "^%s*(-?Infinity)"); + if (infinityMatch !== undefined) { // eslint-disable-next-line @typescript-eslint/prefer-string-starts-ends-with return infinityMatch[0] === "-" ? -Infinity : Infinity; } - const number = tonumber(string.match(numberString, "^%s*(-?%d+%.?%d*)")); + const number = tonumber(__TS__Match(numberString, "^%s*(-?%d+%.?%d*)")[0]); return number ?? NaN; } diff --git a/src/lualib/ParseInt.ts b/src/lualib/ParseInt.ts index b1d5710a0..c27d62662 100644 --- a/src/lualib/ParseInt.ts +++ b/src/lualib/ParseInt.ts @@ -1,15 +1,17 @@ -const __TS__parseInt_base_pattern = "0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTvVwWxXyYzZ"; +import { __TS__Match } from "./Match"; -function __TS__ParseInt(this: void, numberString: string, base?: number): number { +const parseIntBasePattern = "0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTvVwWxXyYzZ"; + +export function __TS__ParseInt(this: void, numberString: string, base?: number): number { // Check which base to use if none specified if (base === undefined) { base = 10; - const hexMatch = string.match(numberString, "^%s*-?0[xX]"); - if (hexMatch) { + const [hexMatch] = __TS__Match(numberString, "^%s*-?0[xX]"); + if (hexMatch !== undefined) { base = 16; - numberString = string.match(hexMatch, "-") - ? "-" + numberString.substr(hexMatch.length) - : numberString.substr(hexMatch.length); + numberString = __TS__Match(hexMatch, "-")[0] + ? "-" + numberString.substring(hexMatch.length) + : numberString.substring(hexMatch.length); } } @@ -20,13 +22,11 @@ function __TS__ParseInt(this: void, numberString: string, base?: number): number // Calculate string match pattern to use const allowedDigits = - base <= 10 - ? __TS__parseInt_base_pattern.substring(0, base) - : __TS__parseInt_base_pattern.substr(0, 10 + 2 * (base - 10)); + base <= 10 ? parseIntBasePattern.substring(0, base) : parseIntBasePattern.substring(0, 10 + 2 * (base - 10)); const pattern = `^%s*(-?[${allowedDigits}]*)`; // Try to parse with Lua tonumber - const number = tonumber(string.match(numberString, pattern), base); + const number = tonumber(__TS__Match(numberString, pattern)[0], base); if (number === undefined) { return NaN; diff --git a/src/lualib/Promise.ts b/src/lualib/Promise.ts new file mode 100644 index 000000000..e8121f1b3 --- /dev/null +++ b/src/lualib/Promise.ts @@ -0,0 +1,236 @@ +/* eslint-disable @typescript-eslint/promise-function-async */ + +// Promises implemented based on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise +// and https://promisesaplus.com/ + +export const enum PromiseState { + Pending, + Fulfilled, + Rejected, +} + +type PromiseExecutor = ConstructorParameters>[0]; +type PromiseResolve = Parameters>[0]; +type PromiseReject = Parameters>[1]; +type PromiseResolveCallback = (value: TValue) => TResult | PromiseLike; +type PromiseRejectCallback = (reason: any) => TResult | PromiseLike; + +function makeDeferredPromiseFactory(this: void) { + let resolve: PromiseResolve; + let reject: PromiseReject; + const executor: PromiseExecutor = (res, rej) => { + resolve = res; + reject = rej; + }; + return function (this: void) { + const promise = new Promise(executor); + return $multi(promise, resolve, reject); + }; +} + +const makeDeferredPromise = makeDeferredPromiseFactory(); + +function isPromiseLike(this: void, value: unknown): value is PromiseLike { + return value instanceof __TS__Promise; +} + +function doNothing(): void {} + +const pcall = _G.pcall; + +export class __TS__Promise implements Promise { + public state = PromiseState.Pending; + public value?: T; + public rejectionReason?: any; + + private fulfilledCallbacks: Array> = []; + private rejectedCallbacks: PromiseReject[] = []; + + // @ts-ignore + public [Symbol.toStringTag]: string; // Required to implement interface, no output Lua + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/resolve + public static resolve(this: void, value: T | PromiseLike): __TS__Promise> { + if (value instanceof __TS__Promise) { + return value; + } + // Create and return a promise instance that is already resolved + const promise = new __TS__Promise>(doNothing); + promise.state = PromiseState.Fulfilled; + promise.value = value as Awaited; + return promise; + } + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/reject + public static reject(this: void, reason?: any): __TS__Promise { + // Create and return a promise instance that is already rejected + const promise = new __TS__Promise(doNothing); + promise.state = PromiseState.Rejected; + promise.rejectionReason = reason; + return promise; + } + + constructor(executor: PromiseExecutor) { + // Avoid unnecessary local functions allocations by using `pcall` explicitly + const [success, error] = pcall( + executor, + undefined, + v => this.resolve(v), + err => this.reject(err) + ); + if (!success) { + // When a promise executor throws, the promise should be rejected with the thrown object as reason + this.reject(error); + } + } + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/then + public then( + onFulfilled?: PromiseResolveCallback, + onRejected?: PromiseRejectCallback + ): Promise { + const [promise, resolve, reject] = makeDeferredPromise(); + + this.addCallbacks( + // We always want to resolve our child promise if this promise is resolved, even if we have no handler + onFulfilled ? this.createPromiseResolvingCallback(onFulfilled, resolve, reject) : resolve, + // We always want to reject our child promise if this promise is rejected, even if we have no handler + onRejected ? this.createPromiseResolvingCallback(onRejected, resolve, reject) : reject + ); + + return promise as Promise; + } + + // Both callbacks should never throw! + public addCallbacks(fulfilledCallback: (value: T) => void, rejectedCallback: (rejectionReason: any) => void): void { + if (this.state === PromiseState.Fulfilled) { + // If promise already resolved, immediately call callback. We don't even need to store rejected callback + // Tail call return is important! + return fulfilledCallback(this.value!); + } + if (this.state === PromiseState.Rejected) { + // Similar thing + return rejectedCallback(this.rejectionReason); + } + + this.fulfilledCallbacks.push(fulfilledCallback as any); + this.rejectedCallbacks.push(rejectedCallback); + } + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/catch + public catch(onRejected?: (reason: any) => TResult | PromiseLike): Promise { + return this.then(undefined, onRejected); + } + + // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/finally + // Delegates to .then() so that a new Promise is returned (per ES spec §27.2.5.3) + // and the original fulfillment value / rejection reason is preserved. + // reference: https://github.com/tc39/proposal-promise-finally/blob/fd934c0b42d59bf8d9446e737ba14d50a9067216/polyfill.js#L34-L41 + public finally(onFinally?: () => void): Promise { + if (typeof onFinally !== "function") { + return this.then(onFinally, onFinally); + } + return this.then( + x => new __TS__Promise(resolve => resolve(onFinally())).then(() => x), + e => + new __TS__Promise(resolve => resolve(onFinally())).then(() => { + throw e; + }) + ); + } + + private resolve(value: T | PromiseLike): void { + if (isPromiseLike(value)) { + // Tail call return is important! + return (value as __TS__Promise).addCallbacks( + v => this.resolve(v), + err => this.reject(err) + ); + } + + // Resolve this promise, if it is still pending. This function is passed to the constructor function. + if (this.state === PromiseState.Pending) { + this.state = PromiseState.Fulfilled; + this.value = value; + + // Tail call return is important! + return this.invokeCallbacks(this.fulfilledCallbacks, value); + } + } + + private reject(reason: any): void { + // Reject this promise, if it is still pending. This function is passed to the constructor function. + if (this.state === PromiseState.Pending) { + this.state = PromiseState.Rejected; + this.rejectionReason = reason; + + // Tail call return is important! + return this.invokeCallbacks(this.rejectedCallbacks, reason); + } + } + + private invokeCallbacks(callbacks: ReadonlyArray<(value: T) => void>, value: T): void { + const callbacksLength = callbacks.length; + + if (callbacksLength !== 0) { + for (const i of $range(1, callbacksLength - 1)) { + callbacks[i - 1](value); + } + // Tail call optimization for a common case. + return callbacks[callbacksLength - 1](value); + } + } + + private createPromiseResolvingCallback( + f: PromiseResolveCallback | PromiseRejectCallback, + resolve: (data: TResult1 | TResult2) => void, + reject: (reason: any) => void + ) { + return (value: T): void => { + const [success, resultOrError] = pcall< + undefined, + [T], + TResult1 | PromiseLike | TResult2 | PromiseLike + >(f, undefined, value); + if (!success) { + // Tail call return is important! + return reject(resultOrError); + } + // Tail call return is important! + return this.handleCallbackValue(resultOrError, resolve, reject); + }; + } + + private handleCallbackValue( + value: TResult | PromiseLike, + resolve: (data: TResult1 | TResult2) => void, + reject: (reason: any) => void + ): void { + if (isPromiseLike(value)) { + const nextpromise = value as __TS__Promise; + if (nextpromise.state === PromiseState.Fulfilled) { + // If a handler function returns an already fulfilled promise, + // the promise returned by then gets fulfilled with that promise's value. + // Tail call return is important! + return resolve(nextpromise.value!); + } else if (nextpromise.state === PromiseState.Rejected) { + // If a handler function returns an already rejected promise, + // the promise returned by then gets fulfilled with that promise's value. + // Tail call return is important! + return reject(nextpromise.rejectionReason); + } else { + // If a handler function returns another pending promise object, the resolution/rejection + // of the promise returned by then will be subsequent to the resolution/rejection of + // the promise returned by the handler. + // We cannot use `then` because we need to do tail call, and `then` returns a Promise. + // `resolve` and `reject` should never throw. + return nextpromise.addCallbacks(resolve, reject); + } + } else { + // If a handler returns a value, the promise returned by then gets resolved with the returned value as its value + // If a handler doesn't return anything, the promise returned by then gets resolved with undefined + // Tail call return is important! + return resolve(value); + } + } +} diff --git a/src/lualib/PromiseAll.ts b/src/lualib/PromiseAll.ts new file mode 100644 index 000000000..c9263b645 --- /dev/null +++ b/src/lualib/PromiseAll.ts @@ -0,0 +1,56 @@ +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all +import { __TS__Promise, PromiseState } from "./Promise"; + +// eslint-disable-next-line @typescript-eslint/promise-function-async +export function __TS__PromiseAll(this: void, iterable: Iterable>): Promise { + const results: T[] = []; + + const toResolve = new LuaTable>(); + let numToResolve = 0; + + let i = 0; + for (const item of iterable) { + if (item instanceof __TS__Promise) { + if (item.state === PromiseState.Fulfilled) { + // If value is a resolved promise, add its value to our results array + results[i] = item.value; + } else if (item.state === PromiseState.Rejected) { + // If value is a rejected promise, return a rejected promise with the rejection reason + return Promise.reject(item.rejectionReason); + } else { + // If value is a pending promise, add it to the list of pending promises + numToResolve++; + toResolve.set(i, item); + } + } else { + // If value is not a promise, add it to the results array + results[i] = item as T; + } + i++; + } + + // If there are no remaining pending promises, return a resolved promise with the results + if (numToResolve === 0) { + return Promise.resolve(results); + } + + return new Promise((resolve, reject) => { + for (const [index, promise] of pairs(toResolve)) { + promise.then( + data => { + // When resolved, store value in results array + results[index] = data; + numToResolve--; + if (numToResolve === 0) { + // If there are no more promises to resolve, resolve with our filled results array + resolve(results); + } + }, + reason => { + // When rejected, immediately reject the returned promise + reject(reason); + } + ); + } + }); +} diff --git a/src/lualib/PromiseAllSettled.ts b/src/lualib/PromiseAllSettled.ts new file mode 100644 index 000000000..4bcd13123 --- /dev/null +++ b/src/lualib/PromiseAllSettled.ts @@ -0,0 +1,64 @@ +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled +import { __TS__Promise, PromiseState } from "./Promise"; + +// eslint-disable-next-line @typescript-eslint/promise-function-async +export function __TS__PromiseAllSettled( + this: void, + iterable: Iterable +): Promise ? U : T>>> { + const results: Array ? U : T>> = []; + + const toResolve = new LuaTable>(); + let numToResolve = 0; + + let i = 0; + for (const item of iterable) { + if (item instanceof __TS__Promise) { + if (item.state === PromiseState.Fulfilled) { + // If value is a resolved promise, add a fulfilled PromiseSettledResult + results[i] = { status: "fulfilled", value: item.value }; + } else if (item.state === PromiseState.Rejected) { + // If value is a rejected promise, add a rejected PromiseSettledResult + results[i] = { status: "rejected", reason: item.rejectionReason }; + } else { + // If value is a pending promise, add it to the list of pending promises + numToResolve++; + toResolve.set(i, item); + } + } else { + // If value is not a promise, add it to the results as fulfilled PromiseSettledResult + results[i] = { status: "fulfilled", value: item as any }; + } + i++; + } + + // If there are no remaining pending promises, return a resolved promise with the results + if (numToResolve === 0) { + return Promise.resolve(results); + } + + return new Promise(resolve => { + for (const [index, promise] of pairs(toResolve)) { + promise.then( + data => { + // When resolved, add a fulfilled PromiseSettledResult + results[index] = { status: "fulfilled", value: data as any }; + numToResolve--; + if (numToResolve === 0) { + // If there are no more promises to resolve, resolve with our filled results array + resolve(results); + } + }, + reason => { + // When resolved, add a rejected PromiseSettledResult + results[index] = { status: "rejected", reason }; + numToResolve--; + if (numToResolve === 0) { + // If there are no more promises to resolve, resolve with our filled results array + resolve(results); + } + } + ); + } + }); +} diff --git a/src/lualib/PromiseAny.ts b/src/lualib/PromiseAny.ts new file mode 100644 index 000000000..17c77f273 --- /dev/null +++ b/src/lualib/PromiseAny.ts @@ -0,0 +1,57 @@ +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/any +import { __TS__Promise, PromiseState } from "./Promise"; + +// eslint-disable-next-line @typescript-eslint/promise-function-async +export function __TS__PromiseAny(this: void, iterable: Iterable>): Promise { + const rejections: string[] = []; + const pending: Array> = []; + + for (const item of iterable) { + if (item instanceof __TS__Promise) { + if (item.state === PromiseState.Fulfilled) { + // If value is a resolved promise, return a new resolved promise with its value + return Promise.resolve(item.value); + } else if (item.state === PromiseState.Rejected) { + // If value is a rejected promise, add its value to our list of rejections + rejections.push(item.rejectionReason); + } else { + // If value is a pending promise, add it to the list of pending promises + pending.push(item); + } + } else { + // If value is not a promise, return a resolved promise with it as its value + return Promise.resolve(item); + } + } + + // If we have not returned yet and there are no pending promises, reject + if (pending.length === 0) { + return Promise.reject("No promises to resolve with .any()"); + } + + let numResolved = 0; + + return new Promise((resolve, reject) => { + for (const promise of pending) { + promise.then( + data => { + // If any pending promise resolves, resolve this promise with its data + resolve(data); + }, + reason => { + // If a pending promise rejects, add its rejection to our rejection list + rejections.push(reason); + numResolved++; + if (numResolved === pending.length) { + // If there are no more pending promises, reject with the list of rejections + reject({ + name: "AggregateError", + message: "All Promises rejected", + errors: rejections, + }); + } + } + ); + } + }); +} diff --git a/src/lualib/PromiseRace.ts b/src/lualib/PromiseRace.ts new file mode 100644 index 000000000..238aaf33b --- /dev/null +++ b/src/lualib/PromiseRace.ts @@ -0,0 +1,36 @@ +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/race +import { PromiseState, __TS__Promise } from "./Promise"; + +// eslint-disable-next-line @typescript-eslint/promise-function-async +export function __TS__PromiseRace(this: void, iterable: Iterable>): Promise { + const pending: Array> = []; + + for (const item of iterable) { + if (item instanceof __TS__Promise) { + if (item.state === PromiseState.Fulfilled) { + // If value is a fulfilled promise, return a resolved promise with its value + return Promise.resolve(item.value); + } else if (item.state === PromiseState.Rejected) { + // If value is a rejected promise, return rejected promise with its value + return Promise.reject(item.rejectionReason); + } else { + // If value is a pending promise, add it to the list of pending promises + pending.push(item); + } + } else { + // If value is not a promise, return a promise resolved with it as its value + return Promise.resolve(item); + } + } + + // If not yet returned, wait for any pending promise to resolve or reject. + // If there are no pending promise, this promise will be pending forever as per specification. + return new Promise((resolve, reject) => { + for (const promise of pending) { + promise.then( + value => resolve(value), + reason => reject(reason) + ); + } + }); +} diff --git a/src/lualib/Set.ts b/src/lualib/Set.ts index 71a8265ff..e302e4094 100644 --- a/src/lualib/Set.ts +++ b/src/lualib/Set.ts @@ -1,4 +1,4 @@ -Set = class Set { +export class Set { public static [Symbol.species] = Set; public [Symbol.toStringTag] = "Set"; @@ -42,8 +42,8 @@ Set = class Set { this.firstKey = value; this.lastKey = value; } else if (isNewValue) { - this.nextKey.set(this.lastKey, value); - this.previousKey.set(value, this.lastKey); + this.nextKey.set(this.lastKey!, value); + this.previousKey.set(value, this.lastKey!); this.lastKey = value; } @@ -66,22 +66,22 @@ Set = class Set { // Do order bookkeeping const next = this.nextKey.get(value); const previous = this.previousKey.get(value); - if (next && previous) { + if (next !== undefined && previous !== undefined) { this.nextKey.set(previous, next); this.previousKey.set(next, previous); - } else if (next) { + } else if (next !== undefined) { this.firstKey = next; - this.previousKey.set(next, undefined); - } else if (previous) { + this.previousKey.set(next, undefined!); + } else if (previous !== undefined) { this.lastKey = previous; - this.nextKey.set(previous, undefined); + this.nextKey.set(previous, undefined!); } else { this.firstKey = undefined; this.lastKey = undefined; } - this.nextKey.set(value, undefined); - this.previousKey.set(value, undefined); + this.nextKey.set(value, undefined!); + this.previousKey.set(value, undefined!); } return contains; @@ -102,47 +102,151 @@ Set = class Set { } public entries(): IterableIterator<[T, T]> { + const getFirstKey = () => this.firstKey; const nextKey = this.nextKey; - let key: T = this.firstKey; + let key: T | undefined; + let started = false; return { [Symbol.iterator](): IterableIterator<[T, T]> { return this; }, next(): IteratorResult<[T, T]> { - const result = { done: !key, value: [key, key] as [T, T] }; - key = nextKey.get(key); - return result; + if (!started) { + started = true; + key = getFirstKey(); + } else { + key = nextKey.get(key!); + } + return { done: !key, value: [key!, key!] as [T, T] }; }, }; } public keys(): IterableIterator { + const getFirstKey = () => this.firstKey; const nextKey = this.nextKey; - let key: T = this.firstKey; + let key: T | undefined; + let started = false; return { [Symbol.iterator](): IterableIterator { return this; }, next(): IteratorResult { - const result = { done: !key, value: key }; - key = nextKey.get(key); - return result; + if (!started) { + started = true; + key = getFirstKey(); + } else { + key = nextKey.get(key!); + } + return { done: !key, value: key! }; }, }; } public values(): IterableIterator { + const getFirstKey = () => this.firstKey; const nextKey = this.nextKey; - let key: T = this.firstKey; + let key: T | undefined; + let started = false; return { [Symbol.iterator](): IterableIterator { return this; }, next(): IteratorResult { - const result = { done: !key, value: key }; - key = nextKey.get(key); - return result; + if (!started) { + started = true; + key = getFirstKey(); + } else { + key = nextKey.get(key!); + } + return { done: !key, value: key! }; }, }; } -}; + + /** + * @returns a new Set containing all the elements in this Set and also all the elements in the argument. + */ + public union(other: ReadonlySet): Set { + const result = new Set(this); + for (const item of other) { + result.add(item); + } + return result; + } + + /** + * @returns a new Set containing all the elements which are both in this Set and in the argument. + */ + public intersection(other: ReadonlySet) { + const result = new Set(); + for (const item of this) { + if (other.has(item)) { + result.add(item); + } + } + return result; + } + + /** + * @returns a new Set containing all the elements in this Set which are not also in the argument. + */ + public difference(other: ReadonlySet): Set { + const result = new Set(this); + for (const item of other) { + result.delete(item); + } + return result; + } + + /** + * @returns a new Set containing all the elements which are in either this Set or in the argument, but not in both. + */ + public symmetricDifference(other: ReadonlySet): Set { + const result = new Set(this); + for (const item of other) { + if (this.has(item)) { + result.delete(item); + } else { + result.add(item); + } + } + return result; + } + + /** + * @returns a boolean indicating whether all the elements in this Set are also in the argument. + */ + public isSubsetOf(other: ReadonlySet): boolean { + for (const item of this) { + if (!other.has(item)) { + return false; + } + } + return true; + } + + /** + * @returns a boolean indicating whether all the elements in the argument are also in this Set. + */ + public isSupersetOf(other: ReadonlySet): boolean { + for (const item of other) { + if (!this.has(item as T)) { + return false; + } + } + return true; + } + + /** + * @returns a boolean indicating whether this Set has no elements in common with the argument. + */ + public isDisjointFrom(other: ReadonlySetLike): boolean { + for (const item of this) { + if (other.has(item)) { + return false; + } + } + return true; + } +} diff --git a/src/lualib/SetDescriptor.ts b/src/lualib/SetDescriptor.ts index 49c7a0bf2..ccb4bf3f0 100644 --- a/src/lualib/SetDescriptor.ts +++ b/src/lualib/SetDescriptor.ts @@ -1,72 +1,47 @@ -function ____descriptorIndex(this: any, key: string): void { - const value = rawget(this, key); - if (value !== null) { - return value; - } - - let metatable = getmetatable(this); - while (metatable) { - const rawResult = rawget(metatable, key); - if (rawResult !== undefined) { - return rawResult; - } - - const descriptors = rawget(metatable, "_descriptors"); - if (descriptors) { - const descriptor: PropertyDescriptor = descriptors[key]; - if (descriptor) { - if (descriptor.get) { - return descriptor.get.call(this); - } +import { __TS__CloneDescriptor } from "./CloneDescriptor"; +import { __TS__DescriptorGet } from "./DescriptorGet"; +import { __TS__DescriptorSet } from "./DescriptorSet"; - return descriptor.value; - } - } +const getmetatable = _G.getmetatable; - metatable = getmetatable(metatable); - } +function descriptorIndex(this: any, key: string): void { + return __TS__DescriptorGet.call(this, getmetatable(this), key); } -function ____descriptorNewindex(this: any, key: string, value: any): void { - let metatable = getmetatable(this); - while (metatable) { - const descriptors = rawget(metatable, "_descriptors"); - if (descriptors) { - const descriptor: PropertyDescriptor = descriptors[key]; - if (descriptor) { - if (descriptor.set) { - descriptor.set.call(this, value); - } else { - if (descriptor.writable === false) { - throw `Cannot assign to read only property '${key}' of object '${this}'`; - } - - descriptor.value = value; - } - return; - } - } - - metatable = getmetatable(metatable); - } - - rawset(this, key, value); +function descriptorNewIndex(this: any, key: string, value: any): void { + return __TS__DescriptorSet.call(this, getmetatable(this), key, value); } // It's also used directly in class transform to add descriptors to the prototype -function __TS__SetDescriptor(this: void, target: any, key: any, desc: PropertyDescriptor, isPrototype = false): void { +export function __TS__SetDescriptor( + this: void, + target: any, + key: any, + desc: PropertyDescriptor, + isPrototype = false +): void { let metatable = isPrototype ? target : getmetatable(target); if (!metatable) { metatable = {}; setmetatable(target, metatable); } + // When setting a descriptor on an instance (not a prototype), ensure it has + // its own metatable so descriptors are not shared across instances. + // See: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1625 + if (!isPrototype && !rawget(metatable, "_isOwnDescriptorMetatable")) { + const instanceMetatable: any = {}; + instanceMetatable._isOwnDescriptorMetatable = true; + setmetatable(instanceMetatable, metatable); + setmetatable(target, instanceMetatable); + metatable = instanceMetatable; + } + const value = rawget(target, key); if (value !== undefined) rawset(target, key, undefined); if (!rawget(metatable, "_descriptors")) metatable._descriptors = {}; - const descriptor = __TS__CloneDescriptor(desc); - metatable._descriptors[key] = descriptor; - metatable.__index = ____descriptorIndex; - metatable.__newindex = ____descriptorNewindex; + metatable._descriptors[key] = __TS__CloneDescriptor(desc); + metatable.__index = descriptorIndex; + metatable.__newindex = descriptorNewIndex; } diff --git a/src/lualib/SourceMapTraceBack.ts b/src/lualib/SourceMapTraceBack.ts index 3a264ead9..4597fa3dd 100644 --- a/src/lualib/SourceMapTraceBack.ts +++ b/src/lualib/SourceMapTraceBack.ts @@ -1,32 +1,80 @@ // TODO: In the future, change this to __TS__RegisterFileInfo and provide tstl interface to // get some metadata about transpilation. -function __TS__SourceMapTraceBack(this: void, fileName: string, sourceMap: { [line: number]: number }): void { + +import { __TS__Match } from "./Match"; + +interface SourceMap { + [line: string]: number | { line: number; file: string }; +} + +declare global { + function __TS__originalTraceback(this: void, thread?: LuaThread, message?: string, level?: number): void; + var __TS__sourcemap: Record; +} + +export function __TS__SourceMapTraceBack(this: void, fileName: string, sourceMap: SourceMap): void { globalThis.__TS__sourcemap = globalThis.__TS__sourcemap || {}; globalThis.__TS__sourcemap[fileName] = sourceMap; if (globalThis.__TS__originalTraceback === undefined) { - globalThis.__TS__originalTraceback = debug.traceback; - debug.traceback = (thread, message, level) => { + const originalTraceback = debug.traceback; + globalThis.__TS__originalTraceback = originalTraceback as typeof __TS__originalTraceback; + debug.traceback = ((thread?: LuaThread, message?: string, level?: number) => { let trace: string; if (thread === undefined && message === undefined && level === undefined) { - trace = globalThis.__TS__originalTraceback(); + trace = originalTraceback(); + } else if (_VERSION.includes("Lua 5.0")) { + trace = originalTraceback(`[Level ${level}] ${message}`); } else { - trace = globalThis.__TS__originalTraceback(thread, message, level); + // @ts-ignore Fails when compiled with Lua 5.0 types + trace = originalTraceback(thread, message, level); } if (typeof trace !== "string") { return trace; } - const [result] = string.gsub(trace, "(%S+).lua:(%d+)", (file, line) => { - const fileSourceMap = globalThis.__TS__sourcemap[file + ".lua"]; - if (fileSourceMap && fileSourceMap[line]) { - return `${file}.ts:${fileSourceMap[line]}`; + const replacer = (file: string, srcFile: string, line: string) => { + const fileSourceMap: SourceMap = globalThis.__TS__sourcemap[file]; + if (fileSourceMap !== undefined && fileSourceMap[line] !== undefined) { + const data = fileSourceMap[line]; + if (typeof data === "number") { + return `${srcFile}:${data}`; + } + + return `${data.file}:${data.line}`; } - return `${file}.lua:${line}`; - }); + + return `${file}:${line}`; + }; + + // Rewrite stack trace frames from "{PATH}.lua:{LINE}" to "{PATH}.ts:{ORIGINAL_LINE}". + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1665 + // Avoid matching anonymous function stack entries like `in function <...>` + // by excluding `<` before the file path. + // TODO: This will still fail for paths containing spaces. + let [result] = string.gsub(trace, "([^%s<]+)%.lua:(%d+)", (file, line) => + replacer(`${file}.lua`, `${file}.ts`, line) + ); + + const stringReplacer = (file: string, line: string) => { + const fileSourceMap: SourceMap = globalThis.__TS__sourcemap[file]; + if (fileSourceMap !== undefined && fileSourceMap[line] !== undefined) { + const chunkName = __TS__Match(file, '%[string "([^"]+)"%]')[0]; + const [sourceName] = string.gsub(chunkName, ".lua$", ".ts"); + const data = fileSourceMap[line]; + if (typeof data === "number") { + return `${sourceName}:${data}`; + } + + return `${data.file}:${data.line}`; + } + + return `${file}:${line}`; + }; + [result] = string.gsub(result, '(%[string "[^"]+"%]):(%d+)', (file, line) => stringReplacer(file, line)); return result; - }; + }) as typeof debug.traceback; } } diff --git a/src/lualib/SparseArray.d.ts b/src/lualib/SparseArray.d.ts new file mode 100644 index 000000000..768d93d7f --- /dev/null +++ b/src/lualib/SparseArray.d.ts @@ -0,0 +1 @@ +export type __TS__SparseArray = T[] & { sparseLength: number }; diff --git a/src/lualib/SparseArrayNew.ts b/src/lualib/SparseArrayNew.ts new file mode 100644 index 000000000..c6877b9c2 --- /dev/null +++ b/src/lualib/SparseArrayNew.ts @@ -0,0 +1,9 @@ +import { __TS__CountVarargs } from "./CountVarargs"; +import { __TS__SparseArray } from "./SparseArray"; + +export function __TS__SparseArrayNew(this: void, ...args: T[]): __TS__SparseArray { + const sparseArray = [...args] as __TS__SparseArray; + // Note that we're depending on vararg optimization to occur here. + sparseArray.sparseLength = __TS__CountVarargs(...args); + return sparseArray; +} diff --git a/src/lualib/SparseArrayPush.ts b/src/lualib/SparseArrayPush.ts new file mode 100644 index 000000000..253d6e223 --- /dev/null +++ b/src/lualib/SparseArrayPush.ts @@ -0,0 +1,11 @@ +import { __TS__CountVarargs } from "./CountVarargs"; +import { __TS__SparseArray } from "./SparseArray"; + +export function __TS__SparseArrayPush(this: void, sparseArray: __TS__SparseArray, ...args: T[]): void { + const argsLen = __TS__CountVarargs(...args); + const listLen = sparseArray.sparseLength; + for (const i of $range(1, argsLen)) { + sparseArray[listLen + i - 1] = args[i - 1]; + } + sparseArray.sparseLength = listLen + argsLen; +} diff --git a/src/lualib/Spread.ts b/src/lualib/Spread.ts index 2bc96395b..3c736060d 100644 --- a/src/lualib/Spread.ts +++ b/src/lualib/Spread.ts @@ -1,14 +1,15 @@ -function __TS__Spread(this: void, iterable: string | Iterable): T[] { - const arr = []; +export function __TS__Spread(this: void, iterable: string | Iterable): LuaMultiReturn { + const arr: T[] = []; if (typeof iterable === "string") { - // eslint-disable-next-line @typescript-eslint/prefer-for-of - for (let i = 0; i < iterable.length; i += 1) { - arr[arr.length] = iterable[i]; + for (const i of $range(0, iterable.length - 1)) { + arr[i] = iterable[i] as T; } } else { + let len = 0; for (const item of iterable) { - arr[arr.length] = item; + len++; + arr[len - 1] = item; } } - return __TS__Unpack(arr); + return $multi(...arr); } diff --git a/src/lualib/StringAccess.ts b/src/lualib/StringAccess.ts index 8695fd4e7..c273506c2 100644 --- a/src/lualib/StringAccess.ts +++ b/src/lualib/StringAccess.ts @@ -1,4 +1,4 @@ -function __TS__StringAccess(this: string, index: number) { +export function __TS__StringAccess(this: string, index: number) { if (index >= 0 && index < this.length) { return string.sub(this, index + 1, index + 1); } diff --git a/src/lualib/StringCharAt.ts b/src/lualib/StringCharAt.ts index 3c0e5161f..d76e94654 100644 --- a/src/lualib/StringCharAt.ts +++ b/src/lualib/StringCharAt.ts @@ -1,4 +1,4 @@ -function __TS__StringCharAt(this: string, pos: number): string { +export function __TS__StringCharAt(this: string, pos: number): string { if (pos !== pos) pos = 0; if (pos < 0) return ""; return string.sub(this, pos + 1, pos + 1); diff --git a/src/lualib/StringCharCodeAt.ts b/src/lualib/StringCharCodeAt.ts index 0044cc6cf..a3c722b9d 100644 --- a/src/lualib/StringCharCodeAt.ts +++ b/src/lualib/StringCharCodeAt.ts @@ -1,4 +1,4 @@ -function __TS__StringCharCodeAt(this: string, index: number): number { +export function __TS__StringCharCodeAt(this: string, index: number): number { if (index !== index) index = 0; if (index < 0) return NaN; return string.byte(this, index + 1) ?? NaN; diff --git a/src/lualib/StringConcat.ts b/src/lualib/StringConcat.ts deleted file mode 100644 index 657ae5c24..000000000 --- a/src/lualib/StringConcat.ts +++ /dev/null @@ -1,7 +0,0 @@ -function __TS__StringConcat(this: void, str1: string, ...args: string[]): string { - let out = str1; - for (const arg of args) { - out += arg; - } - return out; -} diff --git a/src/lualib/StringEndsWith.ts b/src/lualib/StringEndsWith.ts index b519a77eb..ea7d9020e 100644 --- a/src/lualib/StringEndsWith.ts +++ b/src/lualib/StringEndsWith.ts @@ -1,4 +1,4 @@ -function __TS__StringEndsWith(this: string, searchString: string, endPosition?: number): boolean { +export function __TS__StringEndsWith(this: string, searchString: string, endPosition?: number): boolean { if (endPosition === undefined || endPosition > this.length) { endPosition = this.length; } diff --git a/src/lualib/StringIncludes.ts b/src/lualib/StringIncludes.ts index 7508a2a42..3d896c8fa 100644 --- a/src/lualib/StringIncludes.ts +++ b/src/lualib/StringIncludes.ts @@ -1,4 +1,4 @@ -function __TS__StringIncludes(this: string, searchString: string, position?: number): boolean { +export function __TS__StringIncludes(this: string, searchString: string, position?: number): boolean { // http://lua-users.org/wiki/StringLibraryTutorial if (!position) { position = 1; diff --git a/src/lualib/StringPadEnd.ts b/src/lualib/StringPadEnd.ts index 24c8d48da..1f1c30e2e 100644 --- a/src/lualib/StringPadEnd.ts +++ b/src/lualib/StringPadEnd.ts @@ -1,4 +1,4 @@ -function __TS__StringPadEnd(this: string, maxLength: number, fillString = " "): string { +export function __TS__StringPadEnd(this: string, maxLength: number, fillString = " "): string { if (maxLength !== maxLength) maxLength = 0; if (maxLength === -Infinity || maxLength === Infinity) { throw "Invalid string length"; diff --git a/src/lualib/StringPadStart.ts b/src/lualib/StringPadStart.ts index 0e73f06fc..a6cb8e601 100644 --- a/src/lualib/StringPadStart.ts +++ b/src/lualib/StringPadStart.ts @@ -1,4 +1,4 @@ -function __TS__StringPadStart(this: string, maxLength: number, fillString = " "): string { +export function __TS__StringPadStart(this: string, maxLength: number, fillString = " "): string { if (maxLength !== maxLength) maxLength = 0; if (maxLength === -Infinity || maxLength === Infinity) { throw "Invalid string length"; diff --git a/src/lualib/StringReplace.ts b/src/lualib/StringReplace.ts index 51a093bef..9a2da017b 100644 --- a/src/lualib/StringReplace.ts +++ b/src/lualib/StringReplace.ts @@ -1,22 +1,17 @@ -function __TS__StringReplace( +const sub = string.sub; +export function __TS__StringReplace( this: void, source: string, searchValue: string, - replaceValue: string | ((substring: string) => string) + replaceValue: string | ((match: string, offset: number, string: string) => string) ): string { - [searchValue] = string.gsub(searchValue, "[%%%(%)%.%+%-%*%?%[%^%$]", "%%%1"); - - if (typeof replaceValue === "string") { - [replaceValue] = string.gsub(replaceValue, "%%", "%%%%"); - const [result] = string.gsub(source, searchValue, replaceValue, 1); - return result; - } else { - const [result] = string.gsub( - source, - searchValue, - match => (replaceValue as (substring: string) => string)(match), - 1 - ); - return result; + const [startPos, endPos] = string.find(source, searchValue, undefined, true); + if (!startPos) { + return source; } + const before = sub(source, 1, startPos - 1); + const replacement = + typeof replaceValue === "string" ? replaceValue : replaceValue(searchValue, startPos - 1, source); + const after = sub(source, endPos + 1); + return before + replacement + after; } diff --git a/src/lualib/StringReplaceAll.ts b/src/lualib/StringReplaceAll.ts new file mode 100644 index 000000000..c110512e9 --- /dev/null +++ b/src/lualib/StringReplaceAll.ts @@ -0,0 +1,41 @@ +const sub = string.sub; +const find = string.find; +export function __TS__StringReplaceAll( + this: void, + source: string, + searchValue: string, + replaceValue: string | ((match: string, offset: number, string: string) => string) +): string { + if (typeof replaceValue === "string") { + const concat = table.concat(source.split(searchValue), replaceValue); + if (searchValue.length === 0) { + return replaceValue + concat + replaceValue; + } + return concat; + } + const parts: string[] = []; + let partsIndex = 1; + + if (searchValue.length === 0) { + parts[0] = replaceValue("", 0, source); + partsIndex = 2; + for (const i of $range(1, source.length)) { + parts[partsIndex - 1] = sub(source, i, i); + parts[partsIndex] = replaceValue("", i, source); + partsIndex += 2; + } + } else { + let currentPos = 1; + while (true) { + const [startPos, endPos] = find(source, searchValue, currentPos, true); + if (!startPos) break; + parts[partsIndex - 1] = sub(source, currentPos, startPos - 1); + parts[partsIndex] = replaceValue(searchValue, startPos - 1, source); + partsIndex += 2; + + currentPos = endPos + 1; + } + parts[partsIndex - 1] = sub(source, currentPos); + } + return table.concat(parts); +} diff --git a/src/lualib/StringSlice.ts b/src/lualib/StringSlice.ts index 42355d9e1..307d86894 100644 --- a/src/lualib/StringSlice.ts +++ b/src/lualib/StringSlice.ts @@ -1,4 +1,4 @@ -function __TS__StringSlice(this: string, start?: number, end?: number): string { +export function __TS__StringSlice(this: string, start?: number, end?: number): string { if (start === undefined || start !== start) start = 0; if (end !== end) end = 0; diff --git a/src/lualib/StringSplit.ts b/src/lualib/StringSplit.ts index 734dac38c..f89ffb57d 100644 --- a/src/lualib/StringSplit.ts +++ b/src/lualib/StringSplit.ts @@ -1,36 +1,34 @@ -function __TS__StringSplit(this: void, source: string, separator?: string, limit?: number): string[] { - if (limit === undefined) { - limit = 4294967295; - } +const sub = string.sub; +const find = string.find; +export function __TS__StringSplit(this: void, source: string, separator?: string, limit?: number): string[] { + limit ??= 4294967295; if (limit === 0) { return []; } - const out = []; - let index = 0; - let count = 0; + const result = []; + let resultIndex = 1; if (separator === undefined || separator === "") { - while (index < source.length - 1 && count < limit) { - out[count] = source[index]; - count++; - index++; + for (const i of $range(1, source.length)) { + result[resultIndex - 1] = sub(source, i, i); + resultIndex++; } } else { - const separatorLength = separator.length; - let nextIndex = source.indexOf(separator); - while (nextIndex >= 0 && count < limit) { - out[count] = source.substring(index, nextIndex); - count++; - index = nextIndex + separatorLength; - nextIndex = source.indexOf(separator, index); + let currentPos = 1; + while (resultIndex <= limit) { + const [startPos, endPos] = find(source, separator, currentPos, true); + if (!startPos) break; + result[resultIndex - 1] = sub(source, currentPos, startPos - 1); + resultIndex++; + currentPos = endPos + 1; } - } - if (count < limit) { - out[count] = source.substring(index); + if (resultIndex <= limit) { + result[resultIndex - 1] = sub(source, currentPos); + } } - return out; + return result; } diff --git a/src/lualib/StringStartsWith.ts b/src/lualib/StringStartsWith.ts index 8de8fa8ec..82f542143 100644 --- a/src/lualib/StringStartsWith.ts +++ b/src/lualib/StringStartsWith.ts @@ -1,4 +1,4 @@ -function __TS__StringStartsWith(this: string, searchString: string, position?: number): boolean { +export function __TS__StringStartsWith(this: string, searchString: string, position?: number): boolean { if (position === undefined || position < 0) { position = 0; } diff --git a/src/lualib/StringSubstr.ts b/src/lualib/StringSubstr.ts index e14c3c4a5..1940daf4d 100644 --- a/src/lualib/StringSubstr.ts +++ b/src/lualib/StringSubstr.ts @@ -1,4 +1,4 @@ -function __TS__StringSubstr(this: string, from: number, length?: number): string { +export function __TS__StringSubstr(this: string, from: number, length?: number): string { if (from !== from) from = 0; if (length !== undefined) { diff --git a/src/lualib/StringSubstring.ts b/src/lualib/StringSubstring.ts index df8210555..f3480081a 100644 --- a/src/lualib/StringSubstring.ts +++ b/src/lualib/StringSubstring.ts @@ -1,4 +1,4 @@ -function __TS__StringSubstring(this: string, start: number, end?: number): string { +export function __TS__StringSubstring(this: string, start: number, end?: number): string { if (end !== end) end = 0; if (end !== undefined && start > end) { diff --git a/src/lualib/StringTrim.ts b/src/lualib/StringTrim.ts index 8a231118a..f1d71d712 100644 --- a/src/lualib/StringTrim.ts +++ b/src/lualib/StringTrim.ts @@ -1,4 +1,4 @@ -function __TS__StringTrim(this: string): string { +export function __TS__StringTrim(this: string): string { // http://lua-users.org/wiki/StringTrim const [result] = string.gsub(this, "^[%s\xA0\uFEFF]*(.-)[%s\xA0\uFEFF]*$", "%1"); return result; diff --git a/src/lualib/StringTrimEnd.ts b/src/lualib/StringTrimEnd.ts index c45238235..711c91538 100644 --- a/src/lualib/StringTrimEnd.ts +++ b/src/lualib/StringTrimEnd.ts @@ -1,4 +1,4 @@ -function __TS__StringTrimEnd(this: string): string { +export function __TS__StringTrimEnd(this: string): string { // http://lua-users.org/wiki/StringTrim const [result] = string.gsub(this, "[%s\xA0\uFEFF]*$", ""); return result; diff --git a/src/lualib/StringTrimStart.ts b/src/lualib/StringTrimStart.ts index 47e6e8a66..258ea4f8b 100644 --- a/src/lualib/StringTrimStart.ts +++ b/src/lualib/StringTrimStart.ts @@ -1,4 +1,4 @@ -function __TS__StringTrimStart(this: string): string { +export function __TS__StringTrimStart(this: string): string { // http://lua-users.org/wiki/StringTrim const [result] = string.gsub(this, "^[%s\xA0\uFEFF]*", ""); return result; diff --git a/src/lualib/Symbol.ts b/src/lualib/Symbol.ts index 61a5c0423..5c1d7076b 100644 --- a/src/lualib/Symbol.ts +++ b/src/lualib/Symbol.ts @@ -1,14 +1,16 @@ -const ____symbolMetatable = { +const symbolMetatable = { __tostring(this: symbol): string { - return `Symbol(${this.description || ""})`; + return `Symbol(${this.description ?? ""})`; }, }; -function __TS__Symbol(this: void, description?: string | number): symbol { - return setmetatable({ description }, ____symbolMetatable) as any; +export function __TS__Symbol(this: void, description?: string | number): symbol { + return setmetatable({ description }, symbolMetatable) as unknown as symbol; } -Symbol = { +export const Symbol = { + asyncDispose: __TS__Symbol("Symbol.asyncDispose"), + dispose: __TS__Symbol("Symbol.dispose"), iterator: __TS__Symbol("Symbol.iterator"), hasInstance: __TS__Symbol("Symbol.hasInstance"), diff --git a/src/lualib/SymbolRegistry.ts b/src/lualib/SymbolRegistry.ts index 460edcc86..4deaa0993 100644 --- a/src/lualib/SymbolRegistry.ts +++ b/src/lualib/SymbolRegistry.ts @@ -1,15 +1,18 @@ -const ____symbolRegistry: Record = {}; +import { __TS__Symbol } from "./Symbol"; -function __TS__SymbolRegistryFor(this: void, key: string): symbol { - if (!____symbolRegistry[key]) { - ____symbolRegistry[key] = __TS__Symbol(key); +const symbolRegistry: Record = {}; +export function __TS__SymbolRegistryFor(this: void, key: string): symbol { + if (!symbolRegistry[key]) { + symbolRegistry[key] = __TS__Symbol(key); } - return ____symbolRegistry[key]; + return symbolRegistry[key]; } -function __TS__SymbolRegistryKeyFor(this: void, sym: symbol): string { - for (const key in ____symbolRegistry) { - if (____symbolRegistry[key] === sym) return key; +export function __TS__SymbolRegistryKeyFor(this: void, sym: symbol): string | undefined { + for (const key in symbolRegistry) { + if (symbolRegistry[key] === sym) return key; } + + return undefined; } diff --git a/src/lualib/TypeOf.ts b/src/lualib/TypeOf.ts index 158062cd4..5afbbde04 100644 --- a/src/lualib/TypeOf.ts +++ b/src/lualib/TypeOf.ts @@ -1,4 +1,4 @@ -function __TS__TypeOf(this: void, value: unknown): string { +export function __TS__TypeOf(this: void, value: unknown): string { const luaType = type(value); if (luaType === "table") { return "object"; diff --git a/src/lualib/Unpack.ts b/src/lualib/Unpack.ts deleted file mode 100644 index 99aedf253..000000000 --- a/src/lualib/Unpack.ts +++ /dev/null @@ -1 +0,0 @@ -const __TS__Unpack = table.unpack || unpack; diff --git a/src/lualib/Using.ts b/src/lualib/Using.ts new file mode 100644 index 000000000..a720f6e60 --- /dev/null +++ b/src/lualib/Using.ts @@ -0,0 +1,22 @@ +export function __TS__Using( + this: undefined, + cb: (this: void, ...args: TArgs) => TReturn, + ...args: TArgs +): TReturn { + let thrownError; + const [ok, result] = xpcall( + () => cb(...args), + err => (thrownError = err) + ); + + const argArray = [...args]; + for (let i = argArray.length - 1; i >= 0; i--) { + argArray[i][Symbol.dispose](); + } + + if (!ok) { + throw thrownError; + } + + return result; +} diff --git a/src/lualib/UsingAsync.ts b/src/lualib/UsingAsync.ts new file mode 100644 index 000000000..e4b7593f9 --- /dev/null +++ b/src/lualib/UsingAsync.ts @@ -0,0 +1,27 @@ +export async function __TS__UsingAsync, TReturn>( + this: undefined, + cb: (...args: TArgs) => TReturn, + ...args: TArgs +): Promise { + let thrownError; + const [ok, result] = xpcall( + () => cb(...args), + err => (thrownError = err) + ); + + const argArray = [...args]; + for (let i = argArray.length - 1; i >= 0; i--) { + if (Symbol.dispose in argArray[i]) { + (argArray[i] as Disposable)[Symbol.dispose](); + } + if (Symbol.asyncDispose in argArray[i]) { + await (argArray[i] as AsyncDisposable)[Symbol.asyncDispose](); + } + } + + if (!ok) { + throw thrownError; + } + + return result; +} diff --git a/src/lualib/WeakMap.ts b/src/lualib/WeakMap.ts index 7b7df1e99..65d1ae4da 100644 --- a/src/lualib/WeakMap.ts +++ b/src/lualib/WeakMap.ts @@ -1,4 +1,4 @@ -WeakMap = class WeakMap { +export class WeakMap { public static [Symbol.species] = WeakMap; public [Symbol.toStringTag] = "WeakMap"; @@ -30,7 +30,7 @@ WeakMap = class WeakMap { public delete(key: K): boolean { const contains = this.has(key); - this.items.set(key, undefined); + this.items.set(key, undefined!); return contains; } @@ -46,4 +46,4 @@ WeakMap = class WeakMap { this.items.set(key, value); return this; } -}; +} diff --git a/src/lualib/WeakSet.ts b/src/lualib/WeakSet.ts index 670b3c695..b921efc17 100644 --- a/src/lualib/WeakSet.ts +++ b/src/lualib/WeakSet.ts @@ -1,4 +1,4 @@ -WeakSet = class WeakSet { +export class WeakSet { public static [Symbol.species] = WeakSet; public [Symbol.toStringTag] = "WeakSet"; @@ -33,11 +33,11 @@ WeakSet = class WeakSet { public delete(value: T): boolean { const contains = this.has(value); - this.items.set(value, undefined); + this.items.set(value, undefined!); return contains; } public has(value: T): boolean { return this.items.get(value) === true; } -}; +} diff --git a/src/lualib/declarations/coroutine.d.ts b/src/lualib/declarations/coroutine.d.ts deleted file mode 100644 index 1e648d6c8..000000000 --- a/src/lualib/declarations/coroutine.d.ts +++ /dev/null @@ -1,19 +0,0 @@ -/** @noSelfInFile */ - -interface LuaThread { - readonly __internal__: unique symbol; -} - -declare namespace coroutine { - function create(f: (...args: any[]) => any): LuaThread; - - /** @tupleReturn */ - function resume(co: LuaThread, ...val: any[]): [true, ...any[]] | [false, string]; - - function status(co: LuaThread): "running" | "suspended" | "normal" | "dead"; - - function wrap(f: (...args: any[]) => any): /** @tupleReturn */ (...args: any[]) => any[]; - - /** @tupleReturn */ - function yield(...args: any[]): any[]; -} diff --git a/src/lualib/declarations/debug.d.ts b/src/lualib/declarations/debug.d.ts deleted file mode 100644 index 2333f95d4..000000000 --- a/src/lualib/declarations/debug.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** @noSelfInFile */ - -declare const _VERSION: string; -declare function error(...args: any[]): never; - -declare namespace debug { - function traceback(...args: any[]): string; - - interface FunctionInfo any = () => any> { - func: T; - name?: string; - namewhat: "global" | "local" | "method" | "field" | ""; - source: string; - short_src: string; - linedefined: number; - lastlinedefined: number; - what: "Lua" | "C" | "main"; - currentline: number; - nups: number; - } - - function getinfo(i: number, what?: string): Partial; -} diff --git a/src/lualib/declarations/global.d.ts b/src/lualib/declarations/global.d.ts deleted file mode 100644 index 488586275..000000000 --- a/src/lualib/declarations/global.d.ts +++ /dev/null @@ -1,32 +0,0 @@ -/* eslint-disable no-var */ -/** @noSelfInFile */ - -type AnyTable = Record; -// eslint-disable-next-line -type AnyNotNil = {}; - -declare var __TS__sourcemap: Record | undefined; -declare var __TS__originalTraceback: - | ((this: void, thread?: any, message?: string, level?: number) => string) - | undefined; - -// Override next declaration so we can omit extra return values -declare type NextEmptyCheck = (this: void, table: any, index: undefined) => unknown | undefined; - -declare function tonumber(value: any, base?: number): number | undefined; -declare function type( - value: any -): "nil" | "number" | "string" | "boolean" | "table" | "function" | "thread" | "userdata"; -declare function setmetatable(table: T, metatable: any): T; -declare function getmetatable(table: T): any; -declare function rawget(table: T, key: K): T[K]; -declare function rawset(table: T, key: K, val: T[K]): void; -/** @tupleReturn */ -declare function next(table: Record, index?: K): [K, V]; -declare function pcall(func: () => any): any; -declare function unpack(list: T[], i?: number, j?: number): T[]; - -declare function select(index: number, ...args: T[]): T; -declare function select(index: "#", ...args: T[]): number; - -declare function ipairs(t: Record): LuaIterable, Record>; diff --git a/src/lualib/declarations/math.d.ts b/src/lualib/declarations/math.d.ts deleted file mode 100644 index 54568c031..000000000 --- a/src/lualib/declarations/math.d.ts +++ /dev/null @@ -1,14 +0,0 @@ -/** @noSelf */ -declare namespace math { - /** @tupleReturn */ - function modf(x: number): [number, number]; - - function atan(x: number): number; - // eslint-disable-next-line @typescript-eslint/unified-signatures - function atan(y: number, x?: number): number; - - function atan2(y: number, x: number): number; - - function ceil(x: number): number; - function floor(x: number): number; -} diff --git a/src/lualib/declarations/string.d.ts b/src/lualib/declarations/string.d.ts deleted file mode 100644 index ed5aa27e5..000000000 --- a/src/lualib/declarations/string.d.ts +++ /dev/null @@ -1,23 +0,0 @@ -/** @noSelf */ -declare namespace string { - function byte(s: string, i?: number): number | undefined; - /** @tupleReturn */ - function byte(s: string, i?: number, j?: number): number[]; - - /** @tupleReturn */ - function gsub( - source: string, - searchValue: string, - replaceValue: string | ((...groups: string[]) => string), - n?: number - ): [string, number]; - function sub(s: string, i: number, j?: number): string; - function format(formatstring: string, ...args: any[]): string; - function match(string: string, pattern: string): string; - function find( - string: string, - pattern: string, - start?: number, - plainflag?: boolean - ): LuaMultiReturn<[number, number] | [undefined]>; -} diff --git a/src/lualib/declarations/table.d.ts b/src/lualib/declarations/table.d.ts deleted file mode 100644 index 776fe72b0..000000000 --- a/src/lualib/declarations/table.d.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** @noSelfInFile */ - -declare namespace table { - function sort(list: T[], compareFn?: (a: T, b: T) => boolean): void; - function remove(list: T[], idx: number): T; - function insert(list: T[], idx: number, val: T): void; - function unpack(list: T[], i?: number, j?: number): T[]; -} diff --git a/src/lualib/declarations/tstl.d.ts b/src/lualib/declarations/tstl.d.ts index f68237190..9de44be80 100644 --- a/src/lualib/declarations/tstl.d.ts +++ b/src/lualib/declarations/tstl.d.ts @@ -1,18 +1,24 @@ /** @noSelfInFile */ -interface Metatable { +interface LuaMetatable { _descriptors?: Record; __index?: any; __newindex?: any; __tostring?: any; + __errorToStringPatched?: boolean; } -interface LuaClass extends Metatable { +interface LuaClass extends LuaMetatable { prototype: LuaClassInstance; [Symbol.hasInstance]?(instance: LuaClassInstance): any; ____super?: LuaClass; } -interface LuaClassInstance extends Metatable { +interface LuaClassInstance extends LuaMetatable { constructor: LuaClass; } + +// Declare math atan2 for versions that have it instead of math.atan +declare namespace math { + const atan2: typeof atan; +} diff --git a/src/lualib/declarations/universal/unpack.d.ts b/src/lualib/declarations/universal/unpack.d.ts new file mode 100644 index 000000000..c199c09df --- /dev/null +++ b/src/lualib/declarations/universal/unpack.d.ts @@ -0,0 +1,4 @@ +/** @noSelfInFile */ + +// Declare unpack for versions that have it instead of table.unpack +declare const unpack: typeof table.unpack | undefined; diff --git a/src/lualib/tsconfig.json b/src/lualib/tsconfig.json index f16968dc1..0f0874d0d 100644 --- a/src/lualib/tsconfig.json +++ b/src/lualib/tsconfig.json @@ -1,16 +1,18 @@ { "compilerOptions": { - "outDir": "../../dist/lualib", + "outDir": "../../dist/lualib/universal", "target": "esnext", "lib": ["esnext"], - "types": ["../../language-extensions"], + "types": ["lua-types/5.4"], "skipLibCheck": true, + "rootDirs": [".", "universal"], - "noUnusedLocals": true, - "noUnusedParameters": true + "strict": true }, "tstl": { "luaLibImport": "none", - "noHeader": true - } + "noHeader": true, + "luaPlugins": [{ "name": "../../dist/lualib-build/plugin.js" }] + }, + "include": ["*.ts", "universal/**/*.ts", "declarations/**/*.ts", "../../language-extensions/index.d.ts"] } diff --git a/src/lualib/tsconfig.lua50.json b/src/lualib/tsconfig.lua50.json new file mode 100644 index 000000000..a0b5aee4b --- /dev/null +++ b/src/lualib/tsconfig.lua50.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "outDir": "../../dist/lualib/5.0", + "target": "esnext", + "lib": ["esnext"], + "types": ["lua-types/5.0"], + "skipLibCheck": true, + "rootDirs": [".", "5.0"], + + "strict": true + }, + "tstl": { + "luaLibImport": "none", + "noHeader": true, + "luaTarget": "5.0", + "luaPlugins": [{ "name": "../../dist/lualib-build/plugin.js" }] + }, + "include": ["*.ts", "5.0/**/*.ts", "declarations/**/*.ts", "../../language-extensions/index.d.ts"] +} diff --git a/src/lualib/universal/CountVarargs.ts b/src/lualib/universal/CountVarargs.ts new file mode 100644 index 000000000..dc6107bb5 --- /dev/null +++ b/src/lualib/universal/CountVarargs.ts @@ -0,0 +1,6 @@ +/** @noSelfInFile */ + +export function __TS__CountVarargs(...args: T[]): number { + // Note that we need vararg optimization for this call. + return select("#", ...args); +} diff --git a/src/lualib/universal/Match.ts b/src/lualib/universal/Match.ts new file mode 100644 index 000000000..de9420b59 --- /dev/null +++ b/src/lualib/universal/Match.ts @@ -0,0 +1 @@ +export const __TS__Match = string.match; diff --git a/src/lualib/universal/MathModf.ts b/src/lualib/universal/MathModf.ts new file mode 100644 index 000000000..139ae8f6a --- /dev/null +++ b/src/lualib/universal/MathModf.ts @@ -0,0 +1 @@ +export const __TS__MathModf = math.modf; diff --git a/src/lualib/universal/SparseArraySpread.ts b/src/lualib/universal/SparseArraySpread.ts new file mode 100644 index 000000000..99871cb89 --- /dev/null +++ b/src/lualib/universal/SparseArraySpread.ts @@ -0,0 +1,6 @@ +import { __TS__SparseArray } from "./SparseArray"; + +export function __TS__SparseArraySpread(this: void, sparseArray: __TS__SparseArray): LuaMultiReturn { + const _unpack = unpack ?? table.unpack; + return _unpack(sparseArray, 1, sparseArray.sparseLength); +} diff --git a/src/lualib/universal/Unpack.ts b/src/lualib/universal/Unpack.ts new file mode 100644 index 000000000..f138f2d7d --- /dev/null +++ b/src/lualib/universal/Unpack.ts @@ -0,0 +1 @@ +export const __TS__Unpack = table.unpack || unpack; diff --git a/src/measure-performance.ts b/src/measure-performance.ts new file mode 100644 index 000000000..1d66af7e8 --- /dev/null +++ b/src/measure-performance.ts @@ -0,0 +1,83 @@ +import { performance } from "perf_hooks"; + +// We use our own performance hooks implementation for easier use, but also call node's performance hooks, so it shows up in the profiler. + +let enabled = false; +const marks = new Map(); +const durations = new Map(); + +function timestamp() { + return performance.now(); +} + +/** + * Marks a performance event, with the given markName. + */ +function mark(markName: string) { + if (enabled) { + marks.set(markName, timestamp()); + performance.mark(markName); + } +} + +/** + * Adds a performance measurement with the specified name. + * + * @param measureName The name of the performance measurement. + * @param startMarkName The name of the starting mark + * @param endMarkName The name of the ending mark + */ +function measure(measureName: string, startMarkName: string, endMarkName: string) { + if (enabled) { + const end = marks.get(endMarkName) ?? timestamp(); + const start = marks.get(startMarkName) ?? performance.timeOrigin; + const previousDuration = durations.get(measureName) ?? 0; + durations.set(measureName, previousDuration + (end - start)); + performance.measure(measureName, startMarkName, endMarkName); + } +} + +/** + * Starts a performance measurement section. + * @param name name of the measurement + */ +export function startSection(name: string) { + mark("start " + name); +} + +/** + * Ends a performance measurement section. + * @param name name of the measurement + */ +export function endSection(name: string) { + mark("end " + name); + measure(name, "start " + name, "end " + name); +} + +export function isMeasurementEnabled() { + return enabled; +} + +export function enableMeasurement() { + if (!enabled) { + enabled = true; + } +} + +export function disableMeasurement() { + if (enabled) { + enabled = false; + marks.clear(); + durations.clear(); + } +} + +export function forEachMeasure(callback: (measureName: string, duration: number) => void) { + durations.forEach((duration, measureName) => callback(measureName, duration)); +} + +export function getTotalDuration() { + let total = 0; + forEachMeasure((_, duration) => (total += duration)); + return total; +} diff --git a/src/transformation/builtins/array.ts b/src/transformation/builtins/array.ts index d8bd4dd3b..b22bff01d 100644 --- a/src/transformation/builtins/array.ts +++ b/src/transformation/builtins/array.ts @@ -1,49 +1,127 @@ import * as ts from "typescript"; +import { LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; -import { isStringType, isNumberType } from "../utils/typescript"; +import { transformArguments, transformCallAndArguments } from "../visitors/call"; +import { expressionResultIsUsed, typeAlwaysHasSomeOfFlags } from "../utils/typescript"; +import { moveToPrecedingTemp } from "../visitors/expression-list"; +import { isUnpackCall, wrapInTable } from "../utils/lua-ast"; export function transformArrayConstructorCall( context: TransformationContext, - node: PropertyCallExpression -): lua.CallExpression | undefined { - const expression = node.expression; + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression +): lua.Expression | undefined { const signature = context.checker.getResolvedSignature(node); const params = transformArguments(context, node.arguments, signature); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { + case "from": + return transformLuaLibFunction(context, LuaLibFeature.ArrayFrom, node, ...params); case "isArray": return transformLuaLibFunction(context, LuaLibFeature.ArrayIsArray, node, ...params); + case "of": + return wrapInTable(...params); default: - context.diagnostics.push(unsupportedProperty(expression.name, "Array", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Array", expressionName)); } } +function createTableLengthExpression(context: TransformationContext, expression: lua.Expression, node?: ts.Expression) { + if (context.luaTarget === LuaTarget.Lua50) { + const tableGetn = lua.createTableIndexExpression( + lua.createIdentifier("table"), + lua.createStringLiteral("getn") + ); + return lua.createCallExpression(tableGetn, [expression], node); + } else { + return lua.createUnaryExpression(expression, lua.SyntaxKind.LengthOperator, node); + } +} + +/** + * Optimized single element Array.push + * + * array[#array+1] = el + * return (#array + 1) + */ +function transformSingleElementArrayPush( + context: TransformationContext, + node: ts.CallExpression, + caller: lua.Expression, + param: lua.Expression +): lua.Expression { + const arrayIdentifier = lua.isIdentifier(caller) ? caller : moveToPrecedingTemp(context, caller); + + // #array + 1 + let lengthExpression: lua.Expression = lua.createBinaryExpression( + createTableLengthExpression(context, arrayIdentifier), + lua.createNumericLiteral(1), + lua.SyntaxKind.AdditionOperator + ); + + const expressionIsUsed = expressionResultIsUsed(node); + if (expressionIsUsed) { + // store length in a temp + lengthExpression = moveToPrecedingTemp(context, lengthExpression); + } + + const pushStatement = lua.createAssignmentStatement( + lua.createTableIndexExpression(arrayIdentifier, lengthExpression), + param, + node + ); + context.addPrecedingStatements(pushStatement); + return expressionIsUsed ? lengthExpression : lua.createNilLiteral(); +} + export function transformArrayPrototypeCall( context: TransformationContext, - node: PropertyCallExpression -): lua.CallExpression | undefined { - const expression = node.expression; + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression +): lua.Expression | undefined { const signature = context.checker.getResolvedSignature(node); - const params = transformArguments(context, node.arguments, signature); - const caller = context.transformExpression(expression.expression); + const [caller, params] = transformCallAndArguments(context, calledMethod.expression, node.arguments, signature); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { + case "at": + return transformLuaLibFunction(context, LuaLibFeature.ArrayAt, node, caller, ...params); case "concat": return transformLuaLibFunction(context, LuaLibFeature.ArrayConcat, node, caller, ...params); case "entries": return transformLuaLibFunction(context, LuaLibFeature.ArrayEntries, node, caller); + case "fill": + return transformLuaLibFunction(context, LuaLibFeature.ArrayFill, node, caller, ...params); case "push": + if (node.arguments.length === 1) { + const param = params[0] ?? lua.createNilLiteral(); + if (isUnpackCall(param)) { + return transformLuaLibFunction( + context, + LuaLibFeature.ArrayPushArray, + node, + caller, + (param as lua.CallExpression).params[0] ?? lua.createNilLiteral() + ); + } + if (!lua.isDotsLiteral(param)) { + return transformSingleElementArrayPush(context, node, caller, param); + } + } + return transformLuaLibFunction(context, LuaLibFeature.ArrayPush, node, caller, ...params); case "reverse": return transformLuaLibFunction(context, LuaLibFeature.ArrayReverse, node, caller); case "shift": - return transformLuaLibFunction(context, LuaLibFeature.ArrayShift, node, caller); + return lua.createCallExpression( + lua.createTableIndexExpression(lua.createIdentifier("table"), lua.createStringLiteral("remove")), + [caller, lua.createNumericLiteral(1)], + node + ); case "unshift": return transformLuaLibFunction(context, LuaLibFeature.ArrayUnshift, node, caller, ...params); case "sort": @@ -81,15 +159,21 @@ export function transformArrayPrototypeCall( case "splice": return transformLuaLibFunction(context, LuaLibFeature.ArraySplice, node, caller, ...params); case "join": - const callerType = context.checker.getTypeAtLocation(expression.expression); + const callerType = context.checker.getTypeAtLocation(calledMethod.expression); const elementType = context.checker.getElementTypeOfArrayType(callerType); - if (elementType && (isStringType(context, elementType) || isNumberType(context, elementType))) { + if ( + elementType && + typeAlwaysHasSomeOfFlags(context, elementType, ts.TypeFlags.StringLike | ts.TypeFlags.NumberLike) + ) { const defaultSeparatorLiteral = lua.createStringLiteral(","); + const param = params[0] ?? lua.createNilLiteral(); const parameters = [ caller, node.arguments.length === 0 ? defaultSeparatorLiteral - : lua.createBinaryExpression(params[0], defaultSeparatorLiteral, lua.SyntaxKind.OrOperator), + : lua.isStringLiteral(param) + ? param + : lua.createBinaryExpression(param, defaultSeparatorLiteral, lua.SyntaxKind.OrOperator), ]; return lua.createCallExpression( @@ -104,19 +188,27 @@ export function transformArrayPrototypeCall( return transformLuaLibFunction(context, LuaLibFeature.ArrayFlat, node, caller, ...params); case "flatMap": return transformLuaLibFunction(context, LuaLibFeature.ArrayFlatMap, node, caller, ...params); + case "toReversed": + return transformLuaLibFunction(context, LuaLibFeature.ArrayToReversed, node, caller, ...params); + case "toSorted": + return transformLuaLibFunction(context, LuaLibFeature.ArrayToSorted, node, caller, ...params); + case "toSpliced": + return transformLuaLibFunction(context, LuaLibFeature.ArrayToSpliced, node, caller, ...params); + case "with": + return transformLuaLibFunction(context, LuaLibFeature.ArrayWith, node, caller, ...params); default: - context.diagnostics.push(unsupportedProperty(expression.name, "array", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "array", expressionName)); } } export function transformArrayProperty( context: TransformationContext, node: ts.PropertyAccessExpression -): lua.UnaryExpression | undefined { +): lua.Expression | undefined { switch (node.name.text) { case "length": const expression = context.transformExpression(node.expression); - return lua.createUnaryExpression(expression, lua.SyntaxKind.LengthOperator, node); + return createTableLengthExpression(context, expression, node); default: return undefined; } diff --git a/src/transformation/builtins/console.ts b/src/transformation/builtins/console.ts index af7ceb87e..a32f547da 100644 --- a/src/transformation/builtins/console.ts +++ b/src/transformation/builtins/console.ts @@ -2,25 +2,25 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments } from "../visitors/call"; const isStringFormatTemplate = (node: ts.Expression) => ts.isStringLiteral(node) && node.text.includes("%"); export function transformConsoleCall( context: TransformationContext, - expression: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const method = expression.expression; - const methodName = method.name.text; - const signature = context.checker.getResolvedSignature(expression); - const parameters = transformArguments(context, expression.arguments, signature); + const methodName = calledMethod.name.text; + const signature = context.checker.getResolvedSignature(node); + const parameters = transformArguments(context, node.arguments, signature); switch (methodName) { case "error": case "info": case "log": case "warn": - if (expression.arguments.length > 0 && isStringFormatTemplate(expression.arguments[0])) { + if (node.arguments.length > 0 && isStringFormatTemplate(node.arguments[0])) { // print(string.format([arguments])) const stringFormatCall = lua.createCallExpression( lua.createTableIndexExpression(lua.createIdentifier("string"), lua.createStringLiteral("format")), @@ -31,7 +31,7 @@ export function transformConsoleCall( // print([arguments]) return lua.createCallExpression(lua.createIdentifier("print"), parameters); case "assert": - if (expression.arguments.length > 1 && isStringFormatTemplate(expression.arguments[1])) { + if (node.arguments.length > 1 && isStringFormatTemplate(node.arguments[1])) { // assert([condition], string.format([arguments])) const stringFormatCall = lua.createCallExpression( lua.createTableIndexExpression(lua.createIdentifier("string"), lua.createStringLiteral("format")), @@ -42,7 +42,7 @@ export function transformConsoleCall( // assert() return lua.createCallExpression(lua.createIdentifier("assert"), parameters); case "trace": - if (expression.arguments.length > 0 && isStringFormatTemplate(expression.arguments[0])) { + if (node.arguments.length > 0 && isStringFormatTemplate(node.arguments[0])) { // print(debug.traceback(string.format([arguments]))) const stringFormatCall = lua.createCallExpression( lua.createTableIndexExpression(lua.createIdentifier("string"), lua.createStringLiteral("format")), @@ -61,6 +61,6 @@ export function transformConsoleCall( ); return lua.createCallExpression(lua.createIdentifier("print"), [debugTracebackCall]); default: - context.diagnostics.push(unsupportedProperty(method.name, "console", methodName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "console", methodName)); } } diff --git a/src/transformation/builtins/function.ts b/src/transformation/builtins/function.ts index f6cb8a6a0..8b219b6ba 100644 --- a/src/transformation/builtins/function.ts +++ b/src/transformation/builtins/function.ts @@ -4,37 +4,33 @@ import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedForTarget, unsupportedProperty, unsupportedSelfFunctionConversion } from "../utils/diagnostics"; import { ContextType, getFunctionContextType } from "../utils/function-context"; -import { createUnpackCall } from "../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformCallAndArguments } from "../visitors/call"; +import { createUnpackCall } from "../utils/lua-ast"; export function transformFunctionPrototypeCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.CallExpression | undefined { - const expression = node.expression; - const callerType = context.checker.getTypeAtLocation(expression.expression); + const callerType = context.checker.getTypeAtLocation(calledMethod.expression); if (getFunctionContextType(context, callerType) === ContextType.Void) { context.diagnostics.push(unsupportedSelfFunctionConversion(node)); } const signature = context.checker.getResolvedSignature(node); - const params = transformArguments(context, node.arguments, signature); - const caller = context.transformExpression(expression.expression); - const expressionName = expression.name.text; + const [caller, params] = transformCallAndArguments(context, calledMethod.expression, node.arguments, signature); + const expressionName = calledMethod.name.text; switch (expressionName) { case "apply": - return lua.createCallExpression( - caller, - [params[0], createUnpackCall(context, params[1], node.arguments[1])], - node - ); + const nonContextArgs = params.length > 1 ? [createUnpackCall(context, params[1], node.arguments[1])] : []; + return lua.createCallExpression(caller, [params[0], ...nonContextArgs], node); case "bind": return transformLuaLibFunction(context, LuaLibFeature.FunctionBind, node, caller, ...params); case "call": return lua.createCallExpression(caller, params, node); - default: - context.diagnostics.push(unsupportedProperty(expression.name, "function", expressionName)); + case "toString": + context.diagnostics.push(unsupportedProperty(calledMethod.name, "function", expressionName)); } } @@ -44,8 +40,12 @@ export function transformFunctionProperty( ): lua.Expression | undefined { switch (node.name.text) { case "length": - if (context.luaTarget === LuaTarget.Lua51 || context.luaTarget === LuaTarget.Universal) { - context.diagnostics.push(unsupportedForTarget(node, "function.length", LuaTarget.Lua51)); + if ( + context.luaTarget === LuaTarget.Lua50 || + context.luaTarget === LuaTarget.Lua51 || + context.luaTarget === LuaTarget.Universal + ) { + context.diagnostics.push(unsupportedForTarget(node, "function.length", context.luaTarget)); } // debug.getinfo(fn) @@ -61,7 +61,10 @@ export function transformFunctionProperty( ? lua.createBinaryExpression(nparams, lua.createNumericLiteral(1), lua.SyntaxKind.SubtractionOperator) : nparams; - default: + case "arguments": + case "caller": + case "displayName": + case "name": context.diagnostics.push(unsupportedProperty(node.name, "function", node.name.text)); } } diff --git a/src/transformation/builtins/global.ts b/src/transformation/builtins/global.ts index 79f359bbd..288414a60 100644 --- a/src/transformation/builtins/global.ts +++ b/src/transformation/builtins/global.ts @@ -4,25 +4,31 @@ import { TransformationContext } from "../context"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; import { isNumberType } from "../utils/typescript"; import { transformArguments } from "../visitors/call"; +import { transformStringConstructorCall } from "./string"; -export function transformGlobalCall( +export function tryTransformBuiltinGlobalCall( context: TransformationContext, - node: ts.CallExpression + node: ts.CallExpression, + expressionType: ts.Type ): lua.Expression | undefined { - const signature = context.checker.getResolvedSignature(node); - const parameters = transformArguments(context, node.arguments, signature); - const expressionType = context.checker.getTypeAtLocation(node.expression); + function getParameters() { + const signature = context.checker.getResolvedSignature(node); + return transformArguments(context, node.arguments, signature); + } + const name = expressionType.symbol.name; switch (name) { case "SymbolConstructor": - return transformLuaLibFunction(context, LuaLibFeature.Symbol, node, ...parameters); + return transformLuaLibFunction(context, LuaLibFeature.Symbol, node, ...getParameters()); case "NumberConstructor": - return transformLuaLibFunction(context, LuaLibFeature.Number, node, ...parameters); + return transformLuaLibFunction(context, LuaLibFeature.Number, node, ...getParameters()); + case "StringConstructor": + return transformStringConstructorCall(node, ...getParameters()); case "isNaN": case "isFinite": const numberParameters = isNumberType(context, expressionType) - ? parameters - : [transformLuaLibFunction(context, LuaLibFeature.Number, undefined, ...parameters)]; + ? getParameters() + : [transformLuaLibFunction(context, LuaLibFeature.Number, undefined, ...getParameters())]; return transformLuaLibFunction( context, @@ -31,8 +37,8 @@ export function transformGlobalCall( ...numberParameters ); case "parseFloat": - return transformLuaLibFunction(context, LuaLibFeature.ParseFloat, node, ...parameters); + return transformLuaLibFunction(context, LuaLibFeature.ParseFloat, node, ...getParameters()); case "parseInt": - return transformLuaLibFunction(context, LuaLibFeature.ParseInt, node, ...parameters); + return transformLuaLibFunction(context, LuaLibFeature.ParseInt, node, ...getParameters()); } } diff --git a/src/transformation/builtins/index.ts b/src/transformation/builtins/index.ts index 6d7f0bd8b..3eefd64c9 100644 --- a/src/transformation/builtins/index.ts +++ b/src/transformation/builtins/index.ts @@ -1,29 +1,24 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; -import { assume } from "../../utils"; import { TransformationContext } from "../context"; import { createNaN } from "../utils/lua-ast"; import { importLuaLibFeature, LuaLibFeature } from "../utils/lualib"; import { getIdentifierSymbolId } from "../utils/symbols"; -import { - hasStandardLibrarySignature, - isArrayType, - isFunctionType, - isNumberType, - isStandardLibraryType, - isStringType, -} from "../utils/typescript"; -import { PropertyCallExpression } from "../visitors/call"; -import { checkForLuaLibType } from "../visitors/class/new"; +import { isStandardLibraryType, isStringType, isArrayType, isFunctionType } from "../utils/typescript"; +import { getCalledExpression } from "../visitors/call"; import { transformArrayConstructorCall, transformArrayProperty, transformArrayPrototypeCall } from "./array"; import { transformConsoleCall } from "./console"; import { transformFunctionPrototypeCall, transformFunctionProperty } from "./function"; -import { transformGlobalCall } from "./global"; +import { tryTransformBuiltinGlobalCall } from "./global"; import { transformMathCall, transformMathProperty } from "./math"; -import { transformNumberConstructorCall, transformNumberPrototypeCall } from "./number"; -import { transformObjectConstructorCall, transformObjectPrototypeCall } from "./object"; -import { transformStringConstructorCall, transformStringProperty, transformStringPrototypeCall } from "./string"; +import { transformNumberConstructorCall, transformNumberPrototypeCall, transformNumberProperty } from "./number"; +import { transformObjectConstructorCall, tryTransformObjectPrototypeCall } from "./object"; +import { transformPromiseConstructorCall } from "./promise"; +import { transformStringConstructorMethodCall, transformStringProperty, transformStringPrototypeCall } from "./string"; import { transformSymbolConstructorCall } from "./symbol"; +import { unsupportedBuiltinOptionalCall } from "../utils/diagnostics"; +import { LuaTarget } from "../../CompilerOptions"; +import { transformMapConstructorCall } from "./map"; export function transformBuiltinPropertyAccessExpression( context: TransformationContext, @@ -31,6 +26,17 @@ export function transformBuiltinPropertyAccessExpression( ): lua.Expression | undefined { const ownerType = context.checker.getTypeAtLocation(node.expression); + if (ts.isIdentifier(node.expression) && isStandardLibraryType(context, ownerType, undefined)) { + switch (ownerType.symbol.name) { + case "NumberConstructor": + return transformNumberProperty(context, node); + case "Math": + return transformMathProperty(context, node); + case "SymbolConstructor": + importLuaLibFeature(context, LuaLibFeature.Symbol); + } + } + if (isStringType(context, ownerType)) { return transformStringProperty(context, node); } @@ -39,18 +45,9 @@ export function transformBuiltinPropertyAccessExpression( return transformArrayProperty(context, node); } - if (isFunctionType(context, ownerType)) { + if (isFunctionType(ownerType)) { return transformFunctionProperty(context, node); } - - if (ts.isIdentifier(node.expression) && isStandardLibraryType(context, ownerType, undefined)) { - switch (node.expression.text) { - case "Math": - return transformMathProperty(context, node); - case "Symbol": - importLuaLibFeature(context, LuaLibFeature.Symbol); - } - } } export function transformBuiltinCallExpression( @@ -59,79 +56,183 @@ export function transformBuiltinCallExpression( ): lua.Expression | undefined { const expressionType = context.checker.getTypeAtLocation(node.expression); if (ts.isIdentifier(node.expression) && isStandardLibraryType(context, expressionType, undefined)) { - // TODO: checkForLuaLibType(context, expressionType); - const result = transformGlobalCall(context, node); - if (result) { - return result; - } + const result = tryTransformBuiltinGlobalCall(context, node, expressionType); + if (result) return result; } - if (!ts.isPropertyAccessExpression(node.expression)) { - return; - } - - assume(node); - - // If the function being called is of type owner.func, get the type of owner - const ownerType = context.checker.getTypeAtLocation(node.expression.expression); - - if (isStandardLibraryType(context, ownerType, undefined)) { - const symbol = ownerType.getSymbol(); - switch (symbol?.name) { - case "ArrayConstructor": - return transformArrayConstructorCall(context, node); - case "Console": - return transformConsoleCall(context, node); - case "Math": - return transformMathCall(context, node); - case "StringConstructor": - return transformStringConstructorCall(context, node); - case "ObjectConstructor": - return transformObjectConstructorCall(context, node); - case "SymbolConstructor": - return transformSymbolConstructorCall(context, node); - case "NumberConstructor": - return transformNumberConstructorCall(context, node); - } - } + const calledMethod = ts.getOriginalNode(getCalledExpression(node)); + if (ts.isPropertyAccessExpression(calledMethod)) { + const globalResult = tryTransformBuiltinGlobalMethodCall(context, node, calledMethod); + if (globalResult) return globalResult; - if (isStringType(context, ownerType) && hasStandardLibrarySignature(context, node)) { - return transformStringPrototypeCall(context, node); - } + const prototypeResult = tryTransformBuiltinPropertyCall(context, node, calledMethod); + if (prototypeResult) return prototypeResult; - if (isNumberType(context, ownerType) && hasStandardLibrarySignature(context, node)) { - return transformNumberPrototypeCall(context, node); + // object prototype call may work even without resolved signature/type (which the other builtin calls use) + // e.g. (foo as any).toString() + // prototype methods take precedence (e.g. number.toString(2)) + const objectResult = tryTransformObjectPrototypeCall(context, node, calledMethod); + if (objectResult) return objectResult; } +} - if (isArrayType(context, ownerType) && hasStandardLibrarySignature(context, node)) { - return transformArrayPrototypeCall(context, node); +function tryTransformBuiltinGlobalMethodCall( + context: TransformationContext, + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression +) { + const ownerType = context.checker.getTypeAtLocation(calledMethod.expression); + const ownerSymbol = tryGetStandardLibrarySymbolOfType(context, ownerType); + if (!ownerSymbol || ownerSymbol.parent) return; + + let result: lua.Expression | undefined; + switch (ownerSymbol.name) { + case "ArrayConstructor": + result = transformArrayConstructorCall(context, node, calledMethod); + break; + case "Console": + result = transformConsoleCall(context, node, calledMethod); + break; + case "MapConstructor": + result = transformMapConstructorCall(context, node, calledMethod); + break; + case "Math": + result = transformMathCall(context, node, calledMethod); + break; + case "StringConstructor": + result = transformStringConstructorMethodCall(context, node, calledMethod); + break; + case "ObjectConstructor": + result = transformObjectConstructorCall(context, node, calledMethod); + break; + case "SymbolConstructor": + result = transformSymbolConstructorCall(context, node, calledMethod); + break; + case "NumberConstructor": + result = transformNumberConstructorCall(context, node, calledMethod); + break; + case "PromiseConstructor": + result = transformPromiseConstructorCall(context, node, calledMethod); + break; } - - if (isFunctionType(context, ownerType) && hasStandardLibrarySignature(context, node)) { - return transformFunctionPrototypeCall(context, node); + if (result && calledMethod.questionDotToken) { + // e.g. console?.log() + context.diagnostics.push(unsupportedBuiltinOptionalCall(calledMethod)); } + return result; +} - const objectResult = transformObjectPrototypeCall(context, node); - if (objectResult) { - return objectResult; +function tryTransformBuiltinPropertyCall( + context: TransformationContext, + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression +) { + const functionType = context.checker.getTypeAtLocation(node.expression); + const callSymbol = tryGetStandardLibrarySymbolOfType(context, functionType); + if (!callSymbol) return; + const ownerSymbol = callSymbol.parent; + if (!ownerSymbol || ownerSymbol.parent) return; + + switch (ownerSymbol.name) { + case "String": + return transformStringPrototypeCall(context, node, calledMethod); + case "Number": + return transformNumberPrototypeCall(context, node, calledMethod); + case "Array": + case "ReadonlyArray": + return transformArrayPrototypeCall(context, node, calledMethod); + case "Function": + case "CallableFunction": + case "NewableFunction": + return transformFunctionPrototypeCall(context, node, calledMethod); } } export function transformBuiltinIdentifierExpression( context: TransformationContext, - node: ts.Identifier + node: ts.Identifier, + symbol: ts.Symbol | undefined ): lua.Expression | undefined { switch (node.text) { case "NaN": return createNaN(node); case "Infinity": - const math = lua.createIdentifier("math"); - const huge = lua.createStringLiteral("huge"); - return lua.createTableIndexExpression(math, huge, node); - + if (context.luaTarget === LuaTarget.Lua50) { + const one = lua.createNumericLiteral(1); + const zero = lua.createNumericLiteral(0); + return lua.createBinaryExpression(one, zero, lua.SyntaxKind.DivisionOperator); + } else { + const math = lua.createIdentifier("math"); + const huge = lua.createStringLiteral("huge"); + return lua.createTableIndexExpression(math, huge, node); + } case "globalThis": - return lua.createIdentifier("_G", node, getIdentifierSymbolId(context, node), "globalThis"); + return lua.createIdentifier("_G", node, getIdentifierSymbolId(context, node, symbol), "globalThis"); } } + +const builtinErrorTypeNames = new Set([ + "Error", + "ErrorConstructor", + "RangeError", + "RangeErrorConstructor", + "ReferenceError", + "ReferenceErrorConstructor", + "SyntaxError", + "SyntaxErrorConstructor", + "TypeError", + "TypeErrorConstructor", + "URIError", + "URIErrorConstructor", +]); + +export function checkForLuaLibType(context: TransformationContext, type: ts.Type): void { + const symbol = type.symbol; + if (!symbol || symbol.parent) return; + const name = symbol.name; + + switch (name) { + case "Map": + case "MapConstructor": + importLuaLibFeature(context, LuaLibFeature.Map); + return; + case "Set": + case "SetConstructor": + importLuaLibFeature(context, LuaLibFeature.Set); + return; + case "WeakMap": + case "WeakMapConstructor": + importLuaLibFeature(context, LuaLibFeature.WeakMap); + return; + case "WeakSet": + case "WeakSetConstructor": + importLuaLibFeature(context, LuaLibFeature.WeakSet); + return; + case "Promise": + case "PromiseConstructor": + importLuaLibFeature(context, LuaLibFeature.Promise); + return; + } + + if (builtinErrorTypeNames.has(name)) { + importLuaLibFeature(context, LuaLibFeature.Error); + } +} + +export function tryGetStandardLibrarySymbolOfType( + context: TransformationContext, + type: ts.Type +): ts.Symbol | undefined { + if (type.isUnionOrIntersection()) { + for (const subType of type.types) { + const symbol = tryGetStandardLibrarySymbolOfType(context, subType); + if (symbol) return symbol; + } + } else if (isStandardLibraryType(context, type, undefined)) { + return type.symbol; + } + + return undefined; +} diff --git a/src/transformation/builtins/map.ts b/src/transformation/builtins/map.ts new file mode 100644 index 000000000..556578591 --- /dev/null +++ b/src/transformation/builtins/map.ts @@ -0,0 +1,22 @@ +import * as lua from "../../LuaAST"; +import * as ts from "typescript"; +import { TransformationContext } from "../context"; +import { unsupportedProperty } from "../utils/diagnostics"; +import { transformArguments } from "../visitors/call"; +import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; + +export function transformMapConstructorCall( + context: TransformationContext, + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression +): lua.Expression | undefined { + const args = transformArguments(context, node.arguments); + const methodName = calledMethod.name.text; + + switch (methodName) { + case "groupBy": + return transformLuaLibFunction(context, LuaLibFeature.MapGroupBy, node, ...args); + default: + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Map", methodName)); + } +} diff --git a/src/transformation/builtins/math.ts b/src/transformation/builtins/math.ts index af742214f..82a118ec1 100644 --- a/src/transformation/builtins/math.ts +++ b/src/transformation/builtins/math.ts @@ -4,7 +4,7 @@ import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments } from "../visitors/call"; export function transformMathProperty( context: TransformationContext, @@ -33,23 +33,29 @@ export function transformMathProperty( export function transformMathCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; const signature = context.checker.getResolvedSignature(node); const params = transformArguments(context, node.arguments, signature); const math = lua.createIdentifier("math"); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { - // Lua 5.3: math.atan(y, x) + // Lua 5.3+: math.atan(y, x) // Otherwise: math.atan2(y, x) case "atan2": { if (context.luaTarget === LuaTarget.Universal) { return transformLuaLibFunction(context, LuaLibFeature.MathAtan2, node, ...params); } - const method = lua.createStringLiteral(context.luaTarget === LuaTarget.Lua53 ? "atan" : "atan2"); + const useAtan2 = + context.luaTarget === LuaTarget.Lua50 || + context.luaTarget === LuaTarget.Lua51 || + context.luaTarget === LuaTarget.Lua52 || + context.luaTarget === LuaTarget.LuaJIT || + context.luaTarget === LuaTarget.Luau; + const method = lua.createStringLiteral(useAtan2 ? "atan2" : "atan"); return lua.createCallExpression(lua.createTableIndexExpression(math, method), params, node); } @@ -66,18 +72,44 @@ export function transformMathCall( case "log1p": { const log = lua.createStringLiteral("log"); const one = lua.createNumericLiteral(1); - const add = lua.createBinaryExpression(one, params[0], lua.SyntaxKind.AdditionOperator); + const add = lua.createBinaryExpression( + one, + params[0] ?? lua.createNilLiteral(), + lua.SyntaxKind.AdditionOperator + ); return lua.createCallExpression(lua.createTableIndexExpression(math, log), [add], node); } + case "pow": { + // Translate to base ^ power + return lua.createBinaryExpression( + params[0] ?? lua.createNilLiteral(), + params[1] ?? lua.createNilLiteral(), + lua.SyntaxKind.PowerOperator, + node + ); + } + // math.floor(x + 0.5) case "round": { const floor = lua.createStringLiteral("floor"); const half = lua.createNumericLiteral(0.5); - const add = lua.createBinaryExpression(params[0], half, lua.SyntaxKind.AdditionOperator); + const add = lua.createBinaryExpression( + params[0] ?? lua.createNilLiteral(), + half, + lua.SyntaxKind.AdditionOperator + ); return lua.createCallExpression(lua.createTableIndexExpression(math, floor), [add], node); } + case "sign": { + return transformLuaLibFunction(context, LuaLibFeature.MathSign, node, ...params); + } + + case "trunc": { + return transformLuaLibFunction(context, LuaLibFeature.MathTrunc, node, ...params); + } + case "abs": case "acos": case "asin": @@ -89,7 +121,6 @@ export function transformMathCall( case "log": case "max": case "min": - case "pow": case "random": case "sin": case "sqrt": @@ -99,6 +130,6 @@ export function transformMathCall( } default: - context.diagnostics.push(unsupportedProperty(expression.name, "Math", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Math", expressionName)); } } diff --git a/src/transformation/builtins/number.ts b/src/transformation/builtins/number.ts index 227d82b23..ecb98da64 100644 --- a/src/transformation/builtins/number.ts +++ b/src/transformation/builtins/number.ts @@ -1,42 +1,165 @@ +import ts = require("typescript"); import * as lua from "../../LuaAST"; +import { createNaN } from "../utils/lua-ast"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments } from "../visitors/call"; +import { LuaTarget } from "../../CompilerOptions"; export function transformNumberPrototypeCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; const signature = context.checker.getResolvedSignature(node); const params = transformArguments(context, node.arguments, signature); - const caller = context.transformExpression(expression.expression); + const caller = context.transformExpression(calledMethod.expression); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { case "toString": return params.length === 0 ? lua.createCallExpression(lua.createIdentifier("tostring"), [caller], node) : transformLuaLibFunction(context, LuaLibFeature.NumberToString, node, caller, ...params); + case "toFixed": + return transformLuaLibFunction(context, LuaLibFeature.NumberToFixed, node, caller, ...params); default: - context.diagnostics.push(unsupportedProperty(expression.name, "number", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "number", expressionName)); + } +} + +export function transformNumberProperty( + context: TransformationContext, + node: ts.PropertyAccessExpression +): lua.Expression | undefined { + const name = node.name.text; + + /* + Read the docs on https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number for further info about what these numbers entail. + Most of them should be fairly straight forward base on their name(s) though. + */ + + switch (name) { + case "POSITIVE_INFINITY": + if (context.luaTarget === LuaTarget.Lua50) { + const one = lua.createNumericLiteral(1); + const zero = lua.createNumericLiteral(0); + return lua.createBinaryExpression(one, zero, lua.SyntaxKind.DivisionOperator); + } else { + const math = lua.createIdentifier("math"); + const huge = lua.createStringLiteral("huge"); + return lua.createTableIndexExpression(math, huge, node); + } + case "NEGATIVE_INFINITY": + if (context.luaTarget === LuaTarget.Lua50) { + const one = lua.createNumericLiteral(1); + const zero = lua.createNumericLiteral(0); + return lua.createUnaryExpression( + lua.createBinaryExpression(one, zero, lua.SyntaxKind.DivisionOperator), + lua.SyntaxKind.NegationOperator + ); + } else { + const math = lua.createIdentifier("math"); + const huge = lua.createStringLiteral("huge"); + return lua.createUnaryExpression( + lua.createTableIndexExpression(math, huge, node), + lua.SyntaxKind.NegationOperator + ); + } + case "NaN": + return createNaN(node); + case "EPSILON": + return lua.createBinaryExpression( + lua.createNumericLiteral(2), + lua.createNumericLiteral(-52), + lua.SyntaxKind.PowerOperator, + node + ); + case "MIN_VALUE": + // 2 ^ -1074 = 5e-324 (smallest positive double) + return lua.createBinaryExpression( + lua.createNumericLiteral(2), + lua.createNumericLiteral(-1074), + lua.SyntaxKind.PowerOperator, + node + ); + case "MIN_SAFE_INTEGER": + // -(2 ^ 53 - 1) = -9007199254740991 + return lua.createUnaryExpression( + lua.createParenthesizedExpression( + lua.createBinaryExpression( + lua.createBinaryExpression( + lua.createNumericLiteral(2), + lua.createNumericLiteral(53), + lua.SyntaxKind.PowerOperator + ), + lua.createNumericLiteral(1), + lua.SyntaxKind.SubtractionOperator + ) + ), + lua.SyntaxKind.NegationOperator, + node + ); + case "MAX_SAFE_INTEGER": + // 2 ^ 53 - 1 = 9007199254740991 + return lua.createBinaryExpression( + lua.createBinaryExpression( + lua.createNumericLiteral(2), + lua.createNumericLiteral(53), + lua.SyntaxKind.PowerOperator + ), + lua.createNumericLiteral(1), + lua.SyntaxKind.SubtractionOperator, + node + ); + case "MAX_VALUE": + // (2 - 2 ^ -52) * 2 ^ 1023 = 1.7976931348623157e+308 + return lua.createBinaryExpression( + lua.createParenthesizedExpression( + lua.createBinaryExpression( + lua.createNumericLiteral(2), + lua.createBinaryExpression( + lua.createNumericLiteral(2), + lua.createNumericLiteral(-52), + lua.SyntaxKind.PowerOperator + ), + lua.SyntaxKind.SubtractionOperator + ) + ), + lua.createBinaryExpression( + lua.createNumericLiteral(2), + lua.createNumericLiteral(1023), + lua.SyntaxKind.PowerOperator + ), + lua.SyntaxKind.MultiplicationOperator, + node + ); + + default: + context.diagnostics.push(unsupportedProperty(node.name, "Number", name)); } } export function transformNumberConstructorCall( context: TransformationContext, - expression: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.CallExpression | undefined { - const method = expression.expression; - const parameters = transformArguments(context, expression.arguments); - const methodName = method.name.text; + const parameters = transformArguments(context, node.arguments); + const methodName = calledMethod.name.text; switch (methodName) { + case "isInteger": + return transformLuaLibFunction(context, LuaLibFeature.NumberIsInteger, node, ...parameters); case "isNaN": - return transformLuaLibFunction(context, LuaLibFeature.NumberIsNaN, expression, ...parameters); + return transformLuaLibFunction(context, LuaLibFeature.NumberIsNaN, node, ...parameters); case "isFinite": - return transformLuaLibFunction(context, LuaLibFeature.NumberIsFinite, expression, ...parameters); + return transformLuaLibFunction(context, LuaLibFeature.NumberIsFinite, node, ...parameters); + case "parseInt": + return transformLuaLibFunction(context, LuaLibFeature.NumberParseInt, node, ...parameters); + case "parseFloat": + return transformLuaLibFunction(context, LuaLibFeature.NumberParseFloat, node, ...parameters); default: - context.diagnostics.push(unsupportedProperty(method.name, "Number", methodName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Number", methodName)); } } diff --git a/src/transformation/builtins/object.ts b/src/transformation/builtins/object.ts index 2db793f91..43dd8a165 100644 --- a/src/transformation/builtins/object.ts +++ b/src/transformation/builtins/object.ts @@ -1,57 +1,59 @@ import * as lua from "../../LuaAST"; +import * as ts from "typescript"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments } from "../visitors/call"; export function transformObjectConstructorCall( context: TransformationContext, - expression: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const method = expression.expression; - const args = transformArguments(context, expression.arguments); - const methodName = method.name.text; + const args = transformArguments(context, node.arguments); + const methodName = calledMethod.name.text; switch (methodName) { case "assign": - return transformLuaLibFunction(context, LuaLibFeature.ObjectAssign, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectAssign, node, ...args); case "defineProperty": - return transformLuaLibFunction(context, LuaLibFeature.ObjectDefineProperty, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectDefineProperty, node, ...args); case "entries": - return transformLuaLibFunction(context, LuaLibFeature.ObjectEntries, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectEntries, node, ...args); case "fromEntries": - return transformLuaLibFunction(context, LuaLibFeature.ObjectFromEntries, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectFromEntries, node, ...args); case "getOwnPropertyDescriptor": - return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptor, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptor, node, ...args); case "getOwnPropertyDescriptors": - return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptors, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectGetOwnPropertyDescriptors, node, ...args); + case "groupBy": + return transformLuaLibFunction(context, LuaLibFeature.ObjectGroupBy, node, ...args); case "keys": - return transformLuaLibFunction(context, LuaLibFeature.ObjectKeys, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectKeys, node, ...args); case "values": - return transformLuaLibFunction(context, LuaLibFeature.ObjectValues, expression, ...args); + return transformLuaLibFunction(context, LuaLibFeature.ObjectValues, node, ...args); default: - context.diagnostics.push(unsupportedProperty(method.name, "Object", methodName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Object", methodName)); } } -export function transformObjectPrototypeCall( +export function tryTransformObjectPrototypeCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; - const signature = context.checker.getResolvedSignature(node); - - const name = expression.name.text; + const name = calledMethod.name.text; switch (name) { case "toString": const toStringIdentifier = lua.createIdentifier("tostring"); return lua.createCallExpression( toStringIdentifier, - [context.transformExpression(expression.expression)], + [context.transformExpression(calledMethod.expression)], node ); case "hasOwnProperty": - const expr = context.transformExpression(expression.expression); + const expr = context.transformExpression(calledMethod.expression); + const signature = context.checker.getResolvedSignature(node); const parameters = transformArguments(context, node.arguments, signature); const rawGetIdentifier = lua.createIdentifier("rawget"); const rawGetCall = lua.createCallExpression(rawGetIdentifier, [expr, ...parameters]); diff --git a/src/transformation/builtins/promise.ts b/src/transformation/builtins/promise.ts new file mode 100644 index 000000000..19c7284e7 --- /dev/null +++ b/src/transformation/builtins/promise.ts @@ -0,0 +1,54 @@ +import * as ts from "typescript"; +import * as lua from "../../LuaAST"; +import { TransformationContext } from "../context"; +import { unsupportedProperty } from "../utils/diagnostics"; +import { importLuaLibFeature, LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; +import { transformArguments } from "../visitors/call"; +import { isStandardLibraryType } from "../utils/typescript"; + +export function isPromiseClass(context: TransformationContext, node: ts.Identifier) { + if (node.text !== "Promise") return false; + const type = context.checker.getTypeAtLocation(node); + return isStandardLibraryType(context, type, undefined); +} + +export function createPromiseIdentifier(original: ts.Node) { + return lua.createIdentifier("__TS__Promise", original); +} + +export function transformPromiseConstructorCall( + context: TransformationContext, + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression +): lua.Expression | undefined { + const signature = context.checker.getResolvedSignature(node); + const params = transformArguments(context, node.arguments, signature); + + const expressionName = calledMethod.name.text; + switch (expressionName) { + case "all": + return transformLuaLibFunction(context, LuaLibFeature.PromiseAll, node, ...params); + case "allSettled": + return transformLuaLibFunction(context, LuaLibFeature.PromiseAllSettled, node, ...params); + case "any": + return transformLuaLibFunction(context, LuaLibFeature.PromiseAny, node, ...params); + case "race": + return transformLuaLibFunction(context, LuaLibFeature.PromiseRace, node, ...params); + case "resolve": + importLuaLibFeature(context, LuaLibFeature.Promise); + return lua.createCallExpression(createStaticPromiseFunctionAccessor("resolve", calledMethod), params, node); + case "reject": + importLuaLibFeature(context, LuaLibFeature.Promise); + return lua.createCallExpression(createStaticPromiseFunctionAccessor("reject", calledMethod), params, node); + default: + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Promise", expressionName)); + } +} + +export function createStaticPromiseFunctionAccessor(functionName: string, node: ts.Node) { + return lua.createTableIndexExpression( + lua.createIdentifier("__TS__Promise"), + lua.createStringLiteral(functionName), + node + ); +} diff --git a/src/transformation/builtins/string.ts b/src/transformation/builtins/string.ts index 4cb21ea93..98bd62303 100644 --- a/src/transformation/builtins/string.ts +++ b/src/transformation/builtins/string.ts @@ -1,10 +1,11 @@ import * as ts from "typescript"; +import { LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; -import { addToNumericExpression, createNaN, getNumberLiteralValue } from "../utils/lua-ast"; +import { addToNumericExpression, createNaN, getNumberLiteralValue, wrapInTable } from "../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments, transformCallAndArguments } from "../visitors/call"; function createStringCall(methodName: string, tsOriginal: ts.Node, ...params: lua.Expression[]): lua.CallExpression { const stringIdentifier = lua.createIdentifier("string"); @@ -17,26 +18,31 @@ function createStringCall(methodName: string, tsOriginal: ts.Node, ...params: lu export function transformStringPrototypeCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; const signature = context.checker.getResolvedSignature(node); - const params = transformArguments(context, node.arguments, signature); - const caller = context.transformExpression(expression.expression); + const [caller, params] = transformCallAndArguments(context, calledMethod.expression, node.arguments, signature); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { case "replace": return transformLuaLibFunction(context, LuaLibFeature.StringReplace, node, caller, ...params); + case "replaceAll": + return transformLuaLibFunction(context, LuaLibFeature.StringReplaceAll, node, caller, ...params); case "concat": - return transformLuaLibFunction(context, LuaLibFeature.StringConcat, node, caller, ...params); + return lua.createCallExpression( + lua.createTableIndexExpression(lua.createIdentifier("table"), lua.createStringLiteral("concat")), + [wrapInTable(caller, ...params)], + node + ); case "indexOf": { const stringExpression = createStringCall( "find", node, caller, - params[0], + params[0] ?? lua.createNilLiteral(), params[1] ? // string.find handles negative indexes by making it relative to string end, but for indexOf it's the same as 0 lua.createCallExpression( @@ -104,7 +110,7 @@ export function transformStringPrototypeCall( const literalValue = getNumberLiteralValue(params[0]); // Inline string.sub call if we know that parameter is pure and isn't negative if (literalValue !== undefined && literalValue >= 0) { - const firstParamPlusOne = addToNumericExpression(params[0], 1); + const firstParamPlusOne = addToNumericExpression(params[0] ?? lua.createNilLiteral(), 1); return createStringCall("sub", node, caller, firstParamPlusOne, firstParamPlusOne); } @@ -116,7 +122,12 @@ export function transformStringPrototypeCall( // Inline string.sub call if we know that parameter is pure and isn't negative if (literalValue !== undefined && literalValue >= 0) { return lua.createBinaryExpression( - createStringCall("byte", node, caller, addToNumericExpression(params[0], 1)), + createStringCall( + "byte", + node, + caller, + addToNumericExpression(params[0] ?? lua.createNilLiteral(), 1) + ), createNaN(), lua.SyntaxKind.OrOperator ); @@ -134,26 +145,30 @@ export function transformStringPrototypeCall( case "repeat": const math = lua.createIdentifier("math"); const floor = lua.createStringLiteral("floor"); - const parameter = lua.createCallExpression(lua.createTableIndexExpression(math, floor), [params[0]]); + const parameter = lua.createCallExpression(lua.createTableIndexExpression(math, floor), [ + params[0] ?? lua.createNilLiteral(), + ]); return createStringCall("rep", node, caller, parameter); case "padStart": return transformLuaLibFunction(context, LuaLibFeature.StringPadStart, node, caller, ...params); case "padEnd": return transformLuaLibFunction(context, LuaLibFeature.StringPadEnd, node, caller, ...params); + case "toString": + return; // will be handled by transformObjectPrototypeCall default: - context.diagnostics.push(unsupportedProperty(expression.name, "string", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "string", expressionName)); } } -export function transformStringConstructorCall( +export function transformStringConstructorMethodCall( context: TransformationContext, - node: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.Expression | undefined { - const expression = node.expression; const signature = context.checker.getResolvedSignature(node); const params = transformArguments(context, node.arguments, signature); - const expressionName = expression.name.text; + const expressionName = calledMethod.name.text; switch (expressionName) { case "fromCharCode": return lua.createCallExpression( @@ -163,19 +178,35 @@ export function transformStringConstructorCall( ); default: - context.diagnostics.push(unsupportedProperty(expression.name, "String", expressionName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "String", expressionName)); } } export function transformStringProperty( context: TransformationContext, node: ts.PropertyAccessExpression -): lua.UnaryExpression | undefined { +): lua.Expression | undefined { switch (node.name.text) { case "length": const expression = context.transformExpression(node.expression); - return lua.createUnaryExpression(expression, lua.SyntaxKind.LengthOperator, node); + if (context.luaTarget === LuaTarget.Lua50) { + const stringLen = lua.createTableIndexExpression( + lua.createIdentifier("string"), + lua.createStringLiteral("len") + ); + return lua.createCallExpression(stringLen, [expression], node); + } else { + return lua.createUnaryExpression(expression, lua.SyntaxKind.LengthOperator, node); + } default: context.diagnostics.push(unsupportedProperty(node.name, "string", node.name.text)); } } + +export function transformStringConstructorCall( + originalNode: ts.CallExpression, + ...args: lua.Expression[] +): lua.Expression | undefined { + const tostring = lua.createIdentifier("tostring", originalNode.expression); + return lua.createCallExpression(tostring, args, originalNode); +} diff --git a/src/transformation/builtins/symbol.ts b/src/transformation/builtins/symbol.ts index 04329e059..4c5b7cbde 100644 --- a/src/transformation/builtins/symbol.ts +++ b/src/transformation/builtins/symbol.ts @@ -1,25 +1,26 @@ +import ts = require("typescript"); import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; import { unsupportedProperty } from "../utils/diagnostics"; import { importLuaLibFeature, LuaLibFeature } from "../utils/lualib"; -import { PropertyCallExpression, transformArguments } from "../visitors/call"; +import { transformArguments } from "../visitors/call"; export function transformSymbolConstructorCall( context: TransformationContext, - expression: PropertyCallExpression + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression ): lua.CallExpression | undefined { - const method = expression.expression; - const signature = context.checker.getResolvedSignature(expression); - const parameters = transformArguments(context, expression.arguments, signature); - const methodName = method.name.text; + const signature = context.checker.getResolvedSignature(node); + const parameters = transformArguments(context, node.arguments, signature); + const methodName = calledMethod.name.text; switch (methodName) { case "for": case "keyFor": importLuaLibFeature(context, LuaLibFeature.SymbolRegistry); const upperMethodName = methodName[0].toUpperCase() + methodName.slice(1); const functionIdentifier = lua.createIdentifier(`__TS__SymbolRegistry${upperMethodName}`); - return lua.createCallExpression(functionIdentifier, parameters, expression); + return lua.createCallExpression(functionIdentifier, parameters, node); default: - context.diagnostics.push(unsupportedProperty(method.name, "Symbol", methodName)); + context.diagnostics.push(unsupportedProperty(calledMethod.name, "Symbol", methodName)); } } diff --git a/src/transformation/context/context.ts b/src/transformation/context/context.ts index 4ee4515c4..ad40d48c5 100644 --- a/src/transformation/context/context.ts +++ b/src/transformation/context/context.ts @@ -1,14 +1,20 @@ import * as ts from "typescript"; import { CompilerOptions, LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; -import { castArray } from "../../utils"; +import { assert, castArray } from "../../utils"; import { unsupportedNodeKind } from "../utils/diagnostics"; -import { unwrapVisitorResult } from "../utils/lua-ast"; -import { ExpressionLikeNode, ObjectVisitor, StatementLikeNode, VisitorMap } from "./visitors"; +import { unwrapVisitorResult, OneToManyVisitorResult } from "../utils/lua-ast"; +import { createSafeName } from "../utils/safe-names"; +import { ExpressionLikeNode, StatementLikeNode, VisitorMap, FunctionVisitor } from "./visitors"; +import { SymbolInfo } from "../utils/symbols"; +import { LuaLibFeature } from "../../LuaLib"; +import { Scope, ScopeType } from "../utils/scope"; +import { ClassSuperInfo } from "../visitors/class"; + +export const tempSymbolId = -1 as lua.SymbolId; export interface AllAccessorDeclarations { firstAccessor: ts.AccessorDeclaration; - secondAccessor: ts.AccessorDeclaration | undefined; getAccessor: ts.GetAccessorDeclaration | undefined; setAccessor: ts.SetAccessorDeclaration | undefined; } @@ -17,24 +23,23 @@ export interface EmitResolver { isValueAliasDeclaration(node: ts.Node): boolean; isReferencedAliasDeclaration(node: ts.Node, checkChildren?: boolean): boolean; isTopLevelValueImportEqualsWithEntityName(node: ts.ImportEqualsDeclaration): boolean; - moduleExportsSomeValue(moduleReferenceExpression: ts.Expression): boolean; - getAllAccessorDeclarations(declaration: ts.AccessorDeclaration): AllAccessorDeclarations; } -export interface DiagnosticsProducingTypeChecker extends ts.TypeChecker { +export interface TypeCheckerWithEmitResolver extends ts.TypeChecker { getEmitResolver(sourceFile?: ts.SourceFile, cancellationToken?: ts.CancellationToken): EmitResolver; } export class TransformationContext { public readonly diagnostics: ts.Diagnostic[] = []; - public readonly checker: DiagnosticsProducingTypeChecker = (this - .program as any).getDiagnosticsProducingTypeChecker(); + public readonly checker = this.program.getTypeChecker() as TypeCheckerWithEmitResolver; public readonly resolver: EmitResolver; + public readonly precedingStatementsStack: lua.Statement[][] = []; public readonly options: CompilerOptions = this.program.getCompilerOptions(); public readonly luaTarget = this.options.luaTarget ?? LuaTarget.Universal; public readonly isModule = ts.isExternalModule(this.sourceFile); public readonly isStrict = + // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing (this.options.alwaysStrict ?? this.options.strict) || (this.isModule && this.options.target !== undefined && this.options.target >= ts.ScriptTarget.ES2015); @@ -46,69 +51,219 @@ export class TransformationContext { this.resolver = this.checker.getEmitResolver(originalSourceFile); } - private currentNodeVisitors: Array> = []; + private currentNodeVisitors: ReadonlyArray> = []; + private currentNodeVisitorsIndex = 0; + + private nextTempId = 0; + + public transformNode(node: ts.Node): lua.Node[] { + return unwrapVisitorResult(this.transformNodeRaw(node)); + } - public transformNode(node: ts.Node): lua.Node[]; /** @internal */ - // eslint-disable-next-line @typescript-eslint/unified-signatures - public transformNode(node: ts.Node, isExpression?: boolean): lua.Node[]; - public transformNode(node: ts.Node, isExpression?: boolean): lua.Node[] { + public transformNodeRaw(node: ts.Node, isExpression?: boolean) { // TODO: Move to visitors? - if (node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.DeclareKeyword)) { + if ( + ts.canHaveModifiers(node) && + node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.DeclareKeyword) + ) { return []; } const nodeVisitors = this.visitorMap.get(node.kind); - if (!nodeVisitors || nodeVisitors.length === 0) { + if (!nodeVisitors) { this.diagnostics.push(unsupportedNodeKind(node, node.kind)); return isExpression ? [lua.createNilLiteral()] : []; } const previousNodeVisitors = this.currentNodeVisitors; - this.currentNodeVisitors = [...nodeVisitors]; + const previousNodeVisitorsIndex = this.currentNodeVisitorsIndex; + this.currentNodeVisitors = nodeVisitors; + this.currentNodeVisitorsIndex = nodeVisitors.length - 1; - const visitor = this.currentNodeVisitors.pop()!; - const result = unwrapVisitorResult(visitor.transform(node, this)); + const visitor = this.currentNodeVisitors[this.currentNodeVisitorsIndex]; + const result = visitor(node, this); this.currentNodeVisitors = previousNodeVisitors; + this.currentNodeVisitorsIndex = previousNodeVisitorsIndex; return result; } public superTransformNode(node: ts.Node): lua.Node[] { - if (this.currentNodeVisitors.length === 0) { + return unwrapVisitorResult(this.doSuperTransformNode(node)); + } + + private doSuperTransformNode(node: ts.Node): OneToManyVisitorResult { + if (--this.currentNodeVisitorsIndex < 0) { throw new Error(`There is no super transform for ${ts.SyntaxKind[node.kind]} visitor`); } - const visitor = this.currentNodeVisitors.pop()!; - return unwrapVisitorResult(visitor.transform(node, this)); + const visitor = this.currentNodeVisitors[this.currentNodeVisitorsIndex]; + return unwrapVisitorResult(visitor(node, this)); } public transformExpression(node: ExpressionLikeNode): lua.Expression { - const [result] = this.transformNode(node, true); + const result = this.transformNodeRaw(node, true); + return this.assertIsExpression(node, result); + } + private assertIsExpression(node: ExpressionLikeNode, result: OneToManyVisitorResult): lua.Expression { if (result === undefined) { throw new Error(`Expression visitor for node type ${ts.SyntaxKind[node.kind]} did not return any result.`); } - + if (Array.isArray(result)) { + return result[0] as lua.Expression; + } return result as lua.Expression; } public superTransformExpression(node: ExpressionLikeNode): lua.Expression { - const [result] = this.superTransformNode(node); + const result = this.doSuperTransformNode(node); + return this.assertIsExpression(node, result); + } - if (result === undefined) { - throw new Error(`Expression visitor for node type ${ts.SyntaxKind[node.kind]} did not return any result.`); + public transformStatements(node: StatementLikeNode | readonly StatementLikeNode[]): lua.Statement[] { + return castArray(node).flatMap(n => { + this.pushPrecedingStatements(); + const statements = this.transformNode(n) as lua.Statement[]; + const result = this.popPrecedingStatements(); + result.push(...statements); + return result; + }); + } + + public superTransformStatements(node: StatementLikeNode | readonly StatementLikeNode[]): lua.Statement[] { + return castArray(node).flatMap(n => { + this.pushPrecedingStatements(); + const statements = this.superTransformNode(n) as lua.Statement[]; + const result = this.popPrecedingStatements(); + result.push(...statements); + return result; + }); + } + + public pushPrecedingStatements() { + this.precedingStatementsStack.push([]); + } + + public popPrecedingStatements() { + const precedingStatements = this.precedingStatementsStack.pop(); + assert(precedingStatements); + return precedingStatements; + } + + public addPrecedingStatements(statements: lua.Statement | lua.Statement[]) { + const precedingStatements = this.precedingStatementsStack[this.precedingStatementsStack.length - 1]; + assert(precedingStatements); + if (Array.isArray(statements)) { + precedingStatements.push(...statements); + } else { + precedingStatements.push(statements); } + } - return result as lua.Expression; + public prependPrecedingStatements(statements: lua.Statement | lua.Statement[]) { + const precedingStatements = this.precedingStatementsStack[this.precedingStatementsStack.length - 1]; + assert(precedingStatements); + if (Array.isArray(statements)) { + precedingStatements.unshift(...statements); + } else { + precedingStatements.unshift(statements); + } } - public transformStatements(node: StatementLikeNode | readonly StatementLikeNode[]): lua.Statement[] { - return castArray(node).flatMap(n => this.transformNode(n) as lua.Statement[]); + public createTempName(prefix = "temp") { + prefix = prefix.replace(/^_*/, ""); // Strip leading underscores because createSafeName will add them again + return createSafeName(`${prefix}_${this.nextTempId++}`); } - public superTransformStatements(node: StatementLikeNode | readonly StatementLikeNode[]): lua.Statement[] { - return castArray(node).flatMap(n => this.superTransformNode(n) as lua.Statement[]); + private getTempNameForLuaExpression(expression: lua.Expression): string | undefined { + if (lua.isStringLiteral(expression)) { + return expression.value; + } else if (lua.isNumericLiteral(expression)) { + return `_${expression.value.toString()}`; + } else if (lua.isIdentifier(expression)) { + return expression.text; + } else if (lua.isCallExpression(expression)) { + const name = this.getTempNameForLuaExpression(expression.expression); + if (name) { + return `${name}_result`; + } + } else if (lua.isTableIndexExpression(expression)) { + const tableName = this.getTempNameForLuaExpression(expression.table); + const indexName = this.getTempNameForLuaExpression(expression.index); + if (tableName || indexName) { + return `${tableName ?? "table"}_${indexName ?? "index"}`; + } + } + } + + public createTempNameForLuaExpression(expression: lua.Expression) { + const name = this.getTempNameForLuaExpression(expression); + const identifier = lua.createIdentifier(this.createTempName(name), undefined, tempSymbolId); + lua.setNodePosition(identifier, lua.getOriginalPos(expression)); + return identifier; + } + + private getTempNameForNode(node: ts.Node): string | undefined { + if (ts.isStringLiteral(node) || ts.isIdentifier(node) || ts.isMemberName(node)) { + return node.text; + } else if (ts.isNumericLiteral(node)) { + return `_${node.text}`; + } else if (ts.isCallExpression(node)) { + const name = this.getTempNameForNode(node.expression); + if (name) { + return `${name}_result`; + } + } else if (ts.isElementAccessExpression(node) || ts.isPropertyAccessExpression(node)) { + const tableName = this.getTempNameForNode(node.expression); + const indexName = ts.isElementAccessExpression(node) + ? this.getTempNameForNode(node.argumentExpression) + : node.name.text; + if (tableName || indexName) { + return `${tableName ?? "table"}_${indexName ?? "index"}`; + } + } + } + + public createTempNameForNode(node: ts.Node) { + const name = this.getTempNameForNode(node); + return lua.createIdentifier(this.createTempName(name), node, tempSymbolId); + } + + // other utils + + private lastSymbolId = 0; + public readonly symbolInfoMap = new Map(); + public readonly symbolIdMaps = new Map(); + + public nextSymbolId(): lua.SymbolId { + return ++this.lastSymbolId as lua.SymbolId; + } + + public readonly usedLuaLibFeatures = new Set(); + + public readonly scopeStack: Scope[] = []; + private lastScopeId = 0; + + public pushScope(type: ScopeType, node: ts.Node): Scope { + const scope: Scope = { type, id: ++this.lastScopeId, node }; + this.scopeStack.push(scope); + return scope; } + + public popScope(): Scope { + const scope = this.scopeStack.pop(); + assert(scope); + return scope; + } + + // Static context -> namespace dictionary keeping the current namespace for each transformation context + // see visitors/namespace.ts + /** @internal */ + public currentNamespaces: ts.ModuleDeclaration | undefined; + + /** @internal */ + public classSuperInfos: ClassSuperInfo[] = []; } diff --git a/src/transformation/context/visitors.ts b/src/transformation/context/visitors.ts index df7cd7acb..429cce1b4 100644 --- a/src/transformation/context/visitors.ts +++ b/src/transformation/context/visitors.ts @@ -61,6 +61,7 @@ interface NodesBySyntaxKind { [ts.SyntaxKind.AsExpression]: ts.AsExpression; [ts.SyntaxKind.NonNullExpression]: ts.NonNullExpression; [ts.SyntaxKind.MetaProperty]: ts.MetaProperty; + [ts.SyntaxKind.SatisfiesExpression]: ts.SatisfiesExpression; [ts.SyntaxKind.TemplateSpan]: ts.TemplateSpan; [ts.SyntaxKind.SemicolonClassElement]: ts.SemicolonClassElement; [ts.SyntaxKind.Block]: ts.Block; @@ -148,6 +149,7 @@ export type VisitorResult = T extends ExpressionLikeNode export type Visitor = FunctionVisitor | ObjectVisitor; export type FunctionVisitor = (node: T, context: TransformationContext) => VisitorResult; + export interface ObjectVisitor { transform: FunctionVisitor; @@ -162,4 +164,4 @@ export interface ObjectVisitor { } export type Visitors = { [P in keyof NodesBySyntaxKind]?: Visitor }; -export type VisitorMap = Map>>; +export type VisitorMap = Map>>; diff --git a/src/transformation/index.ts b/src/transformation/index.ts index 5b6fca647..b9e1f6241 100644 --- a/src/transformation/index.ts +++ b/src/transformation/index.ts @@ -3,16 +3,17 @@ import * as lua from "../LuaAST"; import { getOrUpdate } from "../utils"; import { ObjectVisitor, TransformationContext, VisitorMap, Visitors } from "./context"; import { standardVisitors } from "./visitors"; +import { usingTransformer } from "./pre-transformers/using-transformer"; export function createVisitorMap(customVisitors: Visitors[]): VisitorMap { - const visitorMap: VisitorMap = new Map(); + const objectVisitorMap: Map>> = new Map(); for (const visitors of [standardVisitors, ...customVisitors]) { const priority = visitors === standardVisitors ? -Infinity : 0; for (const [syntaxKindKey, visitor] of Object.entries(visitors)) { if (!visitor) continue; const syntaxKind = Number(syntaxKindKey) as ts.SyntaxKind; - const nodeVisitors = getOrUpdate(visitorMap, syntaxKind, () => []); + const nodeVisitors = getOrUpdate(objectVisitorMap, syntaxKind, () => []); const objectVisitor: ObjectVisitor = typeof visitor === "function" ? { transform: visitor, priority } : visitor; @@ -20,16 +21,25 @@ export function createVisitorMap(customVisitors: Visitors[]): VisitorMap { } } - for (const nodeVisitors of visitorMap.values()) { - nodeVisitors.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0)); + const result: VisitorMap = new Map(); + for (const [kind, nodeVisitors] of objectVisitorMap) { + result.set( + kind, + nodeVisitors.sort((a, b) => (a.priority ?? 0) - (b.priority ?? 0)).map(visitor => visitor.transform) + ); } - - return visitorMap; + return result; } export function transformSourceFile(program: ts.Program, sourceFile: ts.SourceFile, visitorMap: VisitorMap) { const context = new TransformationContext(program, sourceFile, visitorMap); - const [file] = context.transformNode(sourceFile) as [lua.File]; + + // TS -> TS pre-transformation + const preTransformers = [usingTransformer(context)]; + const result = ts.transform(sourceFile, preTransformers); + + // TS -> Lua transformation + const [file] = context.transformNode(result.transformed[0]) as [lua.File]; return { file, diagnostics: context.diagnostics }; } diff --git a/src/transformation/pre-transformers/using-transformer.ts b/src/transformation/pre-transformers/using-transformer.ts new file mode 100644 index 000000000..1229b71c2 --- /dev/null +++ b/src/transformation/pre-transformers/using-transformer.ts @@ -0,0 +1,139 @@ +import * as ts from "typescript"; +import { TransformationContext } from "../context"; +import { LuaLibFeature, importLuaLibFeature } from "../utils/lualib"; + +export function usingTransformer(context: TransformationContext): ts.TransformerFactory { + return ctx => sourceFile => { + function visit(node: ts.Node): ts.Node { + if (ts.isBlock(node) || ts.isSourceFile(node)) { + const [hasUsings, newStatements] = transformBlockWithUsing(context, node.statements, node); + if (hasUsings) { + // Recurse visitor into updated block to find further usings + const updatedBlock = ts.isBlock(node) + ? ts.factory.updateBlock(node, newStatements) + : ts.factory.updateSourceFile(node, newStatements); + const result = ts.visitEachChild(updatedBlock, visit, ctx); + + // Set all the synthetic node parents to something that makes sense + const parent: ts.Node[] = [updatedBlock]; + function setParent(node2: ts.Node): ts.Node { + ts.setParent(node2, parent[parent.length - 1]); + parent.push(node2); + ts.visitEachChild(node2, setParent, ctx); + parent.pop(); + return node2; + } + ts.visitEachChild(updatedBlock, setParent, ctx); + ts.setParent(updatedBlock, node.parent); + + return result; + } + } + return ts.visitEachChild(node, visit, ctx); + } + const transformedSourceFile = ts.visitEachChild(sourceFile, visit, ctx); + return visit(transformedSourceFile) as ts.SourceFile; + }; +} + +function isUsingDeclarationList(node: ts.Node): node is ts.VariableStatement { + return ts.isVariableStatement(node) && (node.declarationList.flags & ts.NodeFlags.Using) !== 0; +} + +function transformBlockWithUsing( + context: TransformationContext, + statements: ts.NodeArray | ts.Statement[], + block: ts.Block | ts.SourceFile +): [true, ts.Statement[]] | [false] { + const newStatements: ts.Statement[] = []; + + for (let i = 0; i < statements.length; i++) { + const statement = statements[i]; + if (isUsingDeclarationList(statement)) { + const isAwaitUsing = (statement.declarationList.flags & ts.NodeFlags.AwaitContext) !== 0; + + if (isAwaitUsing) { + importLuaLibFeature(context, LuaLibFeature.UsingAsync); + } else { + importLuaLibFeature(context, LuaLibFeature.Using); + } + + // Make declared using variables callback function parameters + const variableNames = statement.declarationList.declarations.map(d => + ts.factory.createParameterDeclaration(undefined, undefined, d.name) + ); + // Add this: void as first parameter + variableNames.unshift(createThisVoidParameter(context.checker)); + + // Put all following statements in the callback body + const followingStatements = statements.slice(i + 1); + const [followingHasUsings, replacedFollowingStatements] = transformBlockWithUsing( + context, + followingStatements, + block + ); + const callbackBody = ts.factory.createBlock( + followingHasUsings ? replacedFollowingStatements : followingStatements + ); + + const callback = ts.factory.createFunctionExpression( + // Put async keyword in front of callback when we are in an async using + isAwaitUsing ? [ts.factory.createModifier(ts.SyntaxKind.AsyncKeyword)] : undefined, + undefined, + undefined, + undefined, + variableNames, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.AnyKeyword), // Required for TS to not freak out trying to infer the type of synthetic nodes + callbackBody + ); + + // Replace using variable list with call to lualib function with callback and followed by all variable initializers + const functionIdentifier = ts.factory.createIdentifier(isAwaitUsing ? "__TS__UsingAsync" : "__TS__Using"); + let call: ts.Expression = ts.factory.createCallExpression( + functionIdentifier, + [], + [ + callback, + ...statement.declarationList.declarations.map( + d => d.initializer ?? ts.factory.createIdentifier("unidentified") + ), + ] + ); + + // If this is an 'await using ...', add an await statement here + if (isAwaitUsing) { + call = ts.factory.createAwaitExpression(call); + } + + if (ts.isSourceFile(block)) { + // If block is a sourcefile, don't insert a return statement into root code + newStatements.push(ts.factory.createExpressionStatement(call)); + } else if ( + block.parent && + ts.isBlock(block.parent) && + block.parent.statements[block.parent.statements.length - 1] !== block + ) { + // If this is a free-standing block in a function (not the last statement), dont return the value + newStatements.push(ts.factory.createExpressionStatement(call)); + } else { + newStatements.push(ts.factory.createReturnStatement(call)); + } + + return [true, newStatements]; + } else { + newStatements.push(statement); + } + } + return [false]; +} + +function createThisVoidParameter(checker: ts.TypeChecker) { + const voidType = checker.typeToTypeNode(checker.getVoidType(), undefined, undefined); + return ts.factory.createParameterDeclaration( + undefined, + undefined, + ts.factory.createIdentifier("this"), + undefined, + voidType + ); +} diff --git a/src/transformation/utils/annotations.ts b/src/transformation/utils/annotations.ts index 212f30416..b66704c97 100644 --- a/src/transformation/utils/annotations.ts +++ b/src/transformation/utils/annotations.ts @@ -1,188 +1,112 @@ import * as ts from "typescript"; -import { TransformationContext } from "../context"; -import { findFirstNodeAbove, inferAssignedType } from "./typescript"; export enum AnnotationKind { - Extension = "extension", - MetaExtension = "metaExtension", CustomConstructor = "customConstructor", CompileMembersOnly = "compileMembersOnly", NoResolution = "noResolution", - PureAbstract = "pureAbstract", - Phantom = "phantom", - TupleReturn = "tupleReturn", - LuaIterator = "luaIterator", - LuaTable = "luaTable", NoSelf = "noSelf", + CustomName = "customName", NoSelfInFile = "noSelfInFile", - Vararg = "vararg", - ForRange = "forRange", } +const annotationValues = new Map(Object.values(AnnotationKind).map(k => [k.toLowerCase(), k])); + export interface Annotation { kind: AnnotationKind; args: string[]; } -function createAnnotation(name: string, args: string[]): Annotation | undefined { - const kind = Object.values(AnnotationKind).find(k => k.toLowerCase() === name.toLowerCase()); - if (kind !== undefined) { - return { kind, args }; - } -} - export type AnnotationsMap = Map; function collectAnnotations(source: ts.Symbol | ts.Signature, annotationsMap: AnnotationsMap): void { for (const tag of source.getJsDocTags()) { - const annotation = createAnnotation(tag.name, tag.text?.map(p => p.text) ?? []); - if (annotation) { - annotationsMap.set(annotation.kind, annotation); - } + const tagName = annotationValues.get(tag.name.toLowerCase()); + if (!tagName) continue; + const annotation: Annotation = { + kind: tag.name as AnnotationKind, + args: tag.text?.map(p => p.text) ?? [], + }; + annotationsMap.set(tagName, annotation); } } +const symbolAnnotations = new WeakMap(); + export function getSymbolAnnotations(symbol: ts.Symbol): AnnotationsMap { + const known = symbolAnnotations.get(symbol); + if (known) return known; + const annotationsMap: AnnotationsMap = new Map(); collectAnnotations(symbol, annotationsMap); + + symbolAnnotations.set(symbol, annotationsMap); return annotationsMap; } export function getTypeAnnotations(type: ts.Type): AnnotationsMap { + // types are not frequently repeatedly polled for annotations, so it's not worth caching them const annotationsMap: AnnotationsMap = new Map(); - - if (type.symbol) collectAnnotations(type.symbol, annotationsMap); - if (type.aliasSymbol) collectAnnotations(type.aliasSymbol, annotationsMap); - + if (type.symbol) { + getSymbolAnnotations(type.symbol).forEach((value, key) => { + annotationsMap.set(key, value); + }); + } + if (type.aliasSymbol) { + getSymbolAnnotations(type.aliasSymbol).forEach((value, key) => { + annotationsMap.set(key, value); + }); + } return annotationsMap; } +const nodeAnnotations = new WeakMap(); export function getNodeAnnotations(node: ts.Node): AnnotationsMap { - const annotationsMap: AnnotationsMap = new Map(); + const known = nodeAnnotations.get(node); + if (known) return known; - for (const tag of ts.getJSDocTags(node)) { - const tagName = tag.tagName.text; - const annotation = createAnnotation(tagName, getTagArgsFromComment(tag)); - if (annotation) { - annotationsMap.set(annotation.kind, annotation); - } - } + const annotationsMap: AnnotationsMap = new Map(); + collectAnnotationsFromTags(annotationsMap, ts.getAllJSDocTags(node, ts.isJSDocUnknownTag)); + nodeAnnotations.set(node, annotationsMap); return annotationsMap; } +function collectAnnotationsFromTags(annotationsMap: AnnotationsMap, tags: readonly ts.JSDocTag[]) { + for (const tag of tags) { + const tagName = annotationValues.get(tag.tagName.text.toLowerCase()); + if (!tagName) continue; + annotationsMap.set(tagName, { kind: tagName, args: getTagArgsFromComment(tag) }); + } +} + +const fileAnnotations = new WeakMap(); export function getFileAnnotations(sourceFile: ts.SourceFile): AnnotationsMap { + const known = fileAnnotations.get(sourceFile); + if (known) return known; + const annotationsMap: AnnotationsMap = new Map(); if (sourceFile.statements.length > 0) { // Manually collect jsDoc because `getJSDocTags` includes tags only from closest comment const jsDoc = sourceFile.statements[0].jsDoc; if (jsDoc) { - for (const tag of jsDoc.flatMap(x => x.tags ?? [])) { - const tagName = tag.tagName.text; - const annotation = createAnnotation(tagName, getTagArgsFromComment(tag)); - if (annotation) { - annotationsMap.set(annotation.kind, annotation); + for (const jsDocElement of jsDoc) { + if (jsDocElement.tags) { + collectAnnotationsFromTags(annotationsMap, jsDocElement.tags); } } } } + fileAnnotations.set(sourceFile, annotationsMap); return annotationsMap; } -export function getSignatureAnnotations(context: TransformationContext, signature: ts.Signature): AnnotationsMap { - const annotationsMap: AnnotationsMap = new Map(); - collectAnnotations(signature, annotationsMap); - - // Function properties on interfaces have the JSDoc tags on the parent PropertySignature - const declaration = signature.getDeclaration(); - if (declaration?.parent && ts.isPropertySignature(declaration.parent)) { - const symbol = context.checker.getSymbolAtLocation(declaration.parent.name); - if (symbol) { - collectAnnotations(symbol, annotationsMap); - } - } - - return annotationsMap; -} - -export function isTupleReturnCall(context: TransformationContext, node: ts.Node): boolean { - if (!ts.isCallExpression(node)) { - return false; - } - - const signature = context.checker.getResolvedSignature(node); - if (signature) { - if (getSignatureAnnotations(context, signature).has(AnnotationKind.TupleReturn)) { - return true; - } - - // Only check function type for directive if it is declared as an interface or type alias - const declaration = signature.getDeclaration(); - const isInterfaceOrAlias = - declaration?.parent && - ((ts.isInterfaceDeclaration(declaration.parent) && ts.isCallSignatureDeclaration(declaration)) || - ts.isTypeAliasDeclaration(declaration.parent)); - if (!isInterfaceOrAlias) { - return false; - } - } - - const type = context.checker.getTypeAtLocation(node.expression); - return getTypeAnnotations(type).has(AnnotationKind.TupleReturn); -} - -export function isInTupleReturnFunction(context: TransformationContext, node: ts.Node): boolean { - const declaration = findFirstNodeAbove(node, ts.isFunctionLike); - if (!declaration) { - return false; - } - - let functionType: ts.Type | undefined; - if (ts.isFunctionExpression(declaration) || ts.isArrowFunction(declaration)) { - functionType = inferAssignedType(context, declaration); - } else if (ts.isMethodDeclaration(declaration) && ts.isObjectLiteralExpression(declaration.parent)) { - // Manually lookup type for object literal properties declared with method syntax - const interfaceType = inferAssignedType(context, declaration.parent); - const propertySymbol = interfaceType.getProperty(declaration.name.getText()); - if (propertySymbol) { - functionType = context.checker.getTypeOfSymbolAtLocation(propertySymbol, declaration); - } - } - - if (functionType === undefined) { - functionType = context.checker.getTypeAtLocation(declaration); - } - - // Check all overloads for directive - const signatures = functionType.getCallSignatures(); - if (signatures?.some(s => getSignatureAnnotations(context, s).has(AnnotationKind.TupleReturn))) { - return true; - } - - return getTypeAnnotations(functionType).has(AnnotationKind.TupleReturn); -} - -export function isLuaIteratorType(context: TransformationContext, node: ts.Node): boolean { - const type = context.checker.getTypeAtLocation(node); - return getTypeAnnotations(type).has(AnnotationKind.LuaIterator); -} - -export function isVarargType(context: TransformationContext, node: ts.Node): boolean { - const type = context.checker.getTypeAtLocation(node); - return getTypeAnnotations(type).has(AnnotationKind.Vararg); -} - -export function isForRangeType(context: TransformationContext, node: ts.Node): boolean { - const type = context.checker.getTypeAtLocation(node); - return getTypeAnnotations(type).has(AnnotationKind.ForRange); -} - -export function getTagArgsFromComment(tag: ts.JSDocTag): string[] { +function getTagArgsFromComment(tag: ts.JSDocTag): string[] { if (tag.comment) { if (typeof tag.comment === "string") { - return tag.comment.split(" "); + const firstLine = tag.comment.split("\n")[0]; + return firstLine.trim().split(" "); } else { return tag.comment.map(part => part.text); } diff --git a/src/transformation/utils/assignment-validation.ts b/src/transformation/utils/assignment-validation.ts index e62045248..d0f42d90a 100644 --- a/src/transformation/utils/assignment-validation.ts +++ b/src/transformation/utils/assignment-validation.ts @@ -34,15 +34,10 @@ export function validateAssignment( validateFunctionAssignment(context, node, fromType, toType, toName); - const fromTypeNode = context.checker.typeToTypeNode(fromType, undefined, undefined); - const toTypeNode = context.checker.typeToTypeNode(toType, undefined, undefined); - if (!fromTypeNode || !toTypeNode) { - return; - } - + const checker = context.checker; if ( - (ts.isArrayTypeNode(toTypeNode) || ts.isTupleTypeNode(toTypeNode)) && - (ts.isArrayTypeNode(fromTypeNode) || ts.isTupleTypeNode(fromTypeNode)) + (checker.isTupleType(toType) || checker.isArrayType(toType)) && + (checker.isTupleType(fromType) || checker.isArrayType(fromType)) ) { // Recurse into arrays/tuples const fromTypeArguments = (fromType as ts.TypeReference).typeArguments; @@ -58,32 +53,39 @@ export function validateAssignment( } } - if ( - (toType.flags & ts.TypeFlags.Object) !== 0 && - ((toType as ts.ObjectType).objectFlags & ts.ObjectFlags.ClassOrInterface) !== 0 && - toType.symbol && - toType.symbol.members && - fromType.symbol && - fromType.symbol.members - ) { + const fromMembers = fromType.symbol?.members; + const toMembers = toType.symbol?.members; + + if (fromMembers && toMembers) { // Recurse into interfaces - toType.symbol.members.forEach((toMember, escapedMemberName) => { - if (fromType.symbol.members) { - const fromMember = fromType.symbol.members.get(escapedMemberName); + if (toMembers.size < fromMembers.size) { + toMembers.forEach((toMember, escapedMemberName) => { + const fromMember = fromMembers.get(escapedMemberName); if (fromMember) { - const toMemberType = context.checker.getTypeOfSymbolAtLocation(toMember, node); - const fromMemberType = context.checker.getTypeOfSymbolAtLocation(fromMember, node); - const memberName = ts.unescapeLeadingUnderscores(escapedMemberName); - validateAssignment( - context, - node, - fromMemberType, - toMemberType, - toName ? `${toName}.${memberName}` : memberName - ); + validateMember(toMember, fromMember, escapedMemberName); } - } - }); + }); + } else { + fromMembers.forEach((fromMember, escapedMemberName) => { + const toMember = toMembers.get(escapedMemberName); + if (toMember) { + validateMember(toMember, fromMember, escapedMemberName); + } + }); + } + } + + function validateMember(toMember: ts.Symbol, fromMember: ts.Symbol, escapedMemberName: ts.__String): void { + const toMemberType = context.checker.getTypeOfSymbolAtLocation(toMember, node); + const fromMemberType = context.checker.getTypeOfSymbolAtLocation(fromMember, node); + const memberName = ts.unescapeLeadingUnderscores(escapedMemberName); + validateAssignment( + context, + node, + fromMemberType, + toMemberType, + toName ? `${toName}.${memberName}` : memberName + ); } } diff --git a/src/transformation/utils/diagnostics.ts b/src/transformation/utils/diagnostics.ts index fcc37b6f0..e8d77c169 100644 --- a/src/transformation/utils/diagnostics.ts +++ b/src/transformation/utils/diagnostics.ts @@ -1,5 +1,6 @@ import * as ts from "typescript"; -import { LuaTarget } from "../../CompilerOptions"; +import * as lua from "../../LuaAST"; +import { LuaTarget, TypeScriptToLuaOptions } from "../../CompilerOptions"; import { createSerialDiagnosticFactory } from "../../utils"; import { AnnotationKind } from "./annotations"; @@ -10,9 +11,9 @@ const createDiagnosticFactory = ( message: MessageProvider ) => createSerialDiagnosticFactory((node: ts.Node, ...args: TArgs) => ({ - file: node.getSourceFile(), - start: node.getStart(), - length: node.getWidth(), + file: ts.getOriginalNode(node).getSourceFile(), + start: ts.getOriginalNode(node).getStart(), + length: ts.getOriginalNode(node).getWidth(), messageText: typeof message === "string" ? message : message(...args), category, })); @@ -72,20 +73,33 @@ export const invalidMultiIterableWithoutDestructuring = createErrorDiagnosticFac "LuaIterable with a LuaMultiReturn return value type must be destructured." ); +export const invalidPairsIterableWithoutDestructuring = createErrorDiagnosticFactory( + "LuaPairsIterable type must be destructured in a for...of statement." +); + export const unsupportedAccessorInObjectLiteral = createErrorDiagnosticFactory( "Accessors in object literal are not supported." ); export const unsupportedRightShiftOperator = createErrorDiagnosticFactory( - "Right shift operator is not supported for target Lua 5.3. Use `>>>` instead." + "Signed right shift `>>` is not supported on Lua 5.3+: Lua's native `>>` is logical (zero-fill) on 64-bit integers, with no built-in arithmetic shift. Use `>>>` if you don't need sign extension, or write your own helper." ); const getLuaTargetName = (version: LuaTarget) => (version === LuaTarget.LuaJIT ? "LuaJIT" : `Lua ${version}`); export const unsupportedForTarget = createErrorDiagnosticFactory( - (functionality: string, version: Exclude) => + (functionality: string, version: LuaTarget) => `${functionality} is/are not supported for target ${getLuaTargetName(version)}.` ); +export const unsupportedForTargetButOverrideAvailable = createErrorDiagnosticFactory( + (functionality: string, version: LuaTarget, optionName: keyof TypeScriptToLuaOptions) => + `As a precaution, ${functionality} is/are not supported for target ${getLuaTargetName( + version + )} due to language features/limitations. ` + + `However "--${optionName}" can be used to bypass this precaution. ` + + "See https://typescripttolua.github.io/docs/configuration for more information." +); + export const unsupportedProperty = createErrorDiagnosticFactory( (parentName: string, property: string) => `${parentName}.${property} is unsupported.` ); @@ -106,42 +120,63 @@ export const invalidMultiFunctionReturnType = createErrorDiagnosticFactory( "The $multi function cannot be cast to a non-LuaMultiReturn type." ); -export const invalidMultiTypeToNonArrayLiteral = createErrorDiagnosticFactory("Expected an array literal."); +export const invalidMultiReturnAccess = createErrorDiagnosticFactory( + "The LuaMultiReturn type can only be accessed via an element access expression of a numeric type." +); -export const invalidMultiTypeToEmptyPatternOrArrayLiteral = createErrorDiagnosticFactory( - "There must be one or more elements specified here." +export const invalidCallExtensionUse = createErrorDiagnosticFactory( + "This function must be called directly and cannot be referred to." +); +export const annotationDeprecated = createWarningDiagnosticFactory( + (kind: AnnotationKind) => + `'@${kind}' is deprecated and will be removed in a future update. Please update your code before upgrading to the next release, otherwise your project will no longer compile. ` + + `See https://typescripttolua.github.io/docs/advanced/compiler-annotations#${kind.toLowerCase()} for more information.` ); -export const invalidMultiReturnAccess = createErrorDiagnosticFactory( - "The LuaMultiReturn type can only be accessed via an element access expression of a numeric type." +export const truthyOnlyConditionalValue = createWarningDiagnosticFactory( + "Only false and nil evaluate to 'false' in Lua, everything else is considered 'true'. Explicitly compare the value with ===." ); -export const invalidOperatorMappingUse = createErrorDiagnosticFactory( - "This function must always be directly called and cannot be referred to." +export const notAllowedOptionalAssignment = createErrorDiagnosticFactory( + "The left-hand side of an assignment expression may not be an optional property access." ); -export const invalidTableExtensionUse = createErrorDiagnosticFactory( - "This function must be called directly and cannot be referred to." +export const awaitMustBeInAsyncFunction = createErrorDiagnosticFactory( + "Await can only be used inside async functions." ); -export const invalidTableDeleteExpression = createErrorDiagnosticFactory( - "Table delete extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement." +export const unsupportedAsyncGenerator = createErrorDiagnosticFactory("Async generator functions are not supported."); + +export const unsupportedForAwaitOf = createErrorDiagnosticFactory("'for await...of' loops are not supported."); + +export const unsupportedBuiltinOptionalCall = createErrorDiagnosticFactory( + "Optional calls are not supported for builtin or language extension functions." ); -export const invalidTableSetExpression = createErrorDiagnosticFactory( - "Table set extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement." +export const unsupportedOptionalCompileMembersOnly = createErrorDiagnosticFactory( + "Optional calls are not supported on enums marked with @compileMembersOnly." ); -export const annotationRemoved = createErrorDiagnosticFactory( - (kind: AnnotationKind) => - `'@${kind}' has been removed and will no longer have any effect.` + - `See https://typescripttolua.github.io/docs/advanced/compiler-annotations#${kind.toLowerCase()} for more information.` +export const undefinedInArrayLiteral = createErrorDiagnosticFactory( + "Array literals may not contain undefined or null." ); -export const annotationDeprecated = createWarningDiagnosticFactory( - (kind: AnnotationKind) => - `'@${kind}' is deprecated and will be removed in a future update. Please update your code before upgrading to the next release, otherwise your project will no longer compile. ` + - `See https://typescripttolua.github.io/docs/advanced/compiler-annotations#${kind.toLowerCase()} for more information.` +export const invalidMethodCallExtensionUse = createErrorDiagnosticFactory( + "This language extension must be called as a method." ); -export const optionalChainingNotSupported = createErrorDiagnosticFactory("Optional chaining is not supported yet."); +export const invalidSpreadInCallExtension = createErrorDiagnosticFactory( + "Spread elements are not supported in call extensions." +); + +export const cannotAssignToNodeOfKind = createErrorDiagnosticFactory( + (kind: lua.SyntaxKind) => `Cannot create assignment assigning to a node of type ${lua.SyntaxKind[kind]}.` +); + +export const incompleteFieldDecoratorWarning = createWarningDiagnosticFactory( + "You are using a class field decorator, note that tstl ignores returned value initializers!" +); + +export const unsupportedArrayWithLengthConstructor = createErrorDiagnosticFactory( + `Constructing new Array with length is not supported.` +); diff --git a/src/transformation/utils/export.ts b/src/transformation/utils/export.ts index 1edee936c..f0ef437e5 100644 --- a/src/transformation/utils/export.ts +++ b/src/transformation/utils/export.ts @@ -1,19 +1,34 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { TransformationContext } from "../context"; -import { createModuleLocalNameIdentifier } from "../visitors/namespace"; +import { createModuleLocalName } from "../visitors/namespace"; import { createExportsIdentifier } from "./lua-ast"; import { getSymbolInfo } from "./symbols"; import { findFirstNodeAbove } from "./typescript"; export function hasDefaultExportModifier(node: ts.Node): boolean { - return (node.modifiers ?? []).some(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword); + return ( + ts.canHaveModifiers(node) && + node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.DefaultKeyword) === true + ); +} + +export function hasExportModifier(node: ts.Node): boolean { + return ( + ts.canHaveModifiers(node) && + node.modifiers?.some(modifier => modifier.kind === ts.SyntaxKind.ExportKeyword) === true + ); } -export const createDefaultExportIdentifier = (original: ts.Node): lua.Identifier => - lua.createIdentifier("default", original); +export function shouldBeExported(node: ts.Node): boolean { + if (hasExportModifier(node)) { + // Don't export if we're inside a namespace (module declaration) + return ts.findAncestor(node, ts.isModuleDeclaration) === undefined; + } + return false; +} -export const createDefaultExportStringLiteral = (original: ts.Node): lua.StringLiteral => +export const createDefaultExportStringLiteral = (original?: ts.Node): lua.StringLiteral => lua.createStringLiteral("default", original); export function getExportedSymbolDeclaration(symbol: ts.Symbol): ts.Declaration | undefined { @@ -137,8 +152,12 @@ export function createExportedIdentifier( const exportTable = exportScope && ts.isModuleDeclaration(exportScope) - ? createModuleLocalNameIdentifier(context, exportScope) + ? createModuleLocalName(context, exportScope) : createExportsIdentifier(); return lua.createTableIndexExpression(exportTable, lua.createStringLiteral(identifier.text)); } + +export function createDefaultExportExpression(node: ts.Node): lua.AssignmentLeftHandSideExpression { + return lua.createTableIndexExpression(createExportsIdentifier(), createDefaultExportStringLiteral(node), node); +} diff --git a/src/transformation/utils/function-context.ts b/src/transformation/utils/function-context.ts index 8bf05255c..4fce80f49 100644 --- a/src/transformation/utils/function-context.ts +++ b/src/transformation/utils/function-context.ts @@ -5,10 +5,10 @@ import { AnnotationKind, getFileAnnotations, getNodeAnnotations } from "./annota import { findFirstNodeAbove, getAllCallSignatures, inferAssignedType } from "./typescript"; export enum ContextType { - None, - Void, - NonVoid, - Mixed, + None = 0, + Void = 1 << 0, + NonVoid = 1 << 1, + Mixed = Void | NonVoid, } function hasNoSelfAncestor(declaration: ts.Declaration): boolean { @@ -29,21 +29,86 @@ function hasNoSelfAncestor(declaration: ts.Declaration): boolean { } function getExplicitThisParameter(signatureDeclaration: ts.SignatureDeclaration): ts.ParameterDeclaration | undefined { - return signatureDeclaration.parameters.find( - param => ts.isIdentifier(param.name) && param.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword - ); + const param = signatureDeclaration.parameters[0]; + if (param && ts.isIdentifier(param.name) && ts.identifierToKeywordKind(param.name) === ts.SyntaxKind.ThisKeyword) { + return param; + } } -export function getDeclarationContextType( - { program }: TransformationContext, +const callContextTypes = new WeakMap(); + +export function getCallContextType(context: TransformationContext, callExpression: ts.CallLikeExpression): ContextType { + const known = callContextTypes.get(callExpression); + if (known !== undefined) return known; + + const signature = context.checker.getResolvedSignature(callExpression); + const signatureDeclaration = signature?.getDeclaration(); + + let contextType = ContextType.None; + + if (signatureDeclaration) { + contextType = computeDeclarationContextType(context, signatureDeclaration); + } else { + // No signature declaration could be resolved, so instead try to see if the declaration is in a + // noSelfInFile file + const declarations = findRootDeclarations(context, callExpression); + contextType = declarations.some(d => getFileAnnotations(d.getSourceFile()).has(AnnotationKind.NoSelfInFile)) + ? ContextType.Void + : context.options.noImplicitSelf + ? ContextType.Void + : ContextType.NonVoid; + } + + callContextTypes.set(callExpression, contextType); + return contextType; +} + +const signatureDeclarationContextTypes = new WeakMap(); + +function getSignatureContextType( + context: TransformationContext, signatureDeclaration: ts.SignatureDeclaration ): ContextType { + const known = signatureDeclarationContextTypes.get(signatureDeclaration); + if (known !== undefined) return known; + const contextType = computeDeclarationContextType(context, signatureDeclaration); + signatureDeclarationContextTypes.set(signatureDeclaration, contextType); + return contextType; +} + +function findRootDeclarations(context: TransformationContext, callExpression: ts.CallLikeExpression): ts.Declaration[] { + const calledExpression = ts.isTaggedTemplateExpression(callExpression) + ? callExpression.tag + : ts.isJsxSelfClosingElement(callExpression) + ? callExpression.tagName + : ts.isJsxOpeningElement(callExpression) + ? callExpression.tagName + : ts.isCallExpression(callExpression) + ? callExpression.expression + : undefined; + + if (!calledExpression) return []; + + const calledSymbol = context.checker.getSymbolAtLocation(calledExpression); + if (calledSymbol === undefined) return []; + + return ( + calledSymbol.getDeclarations()?.flatMap(d => { + if (ts.isImportSpecifier(d)) { + const aliasSymbol = context.checker.getAliasedSymbol(calledSymbol); + return aliasSymbol.getDeclarations() ?? []; + } else { + return [d]; + } + }) ?? [] + ); +} + +function computeDeclarationContextType(context: TransformationContext, signatureDeclaration: ts.SignatureDeclaration) { const thisParameter = getExplicitThisParameter(signatureDeclaration); if (thisParameter) { // Explicit 'this' - return thisParameter.type && thisParameter.type.kind === ts.SyntaxKind.VoidKeyword - ? ContextType.Void - : ContextType.NonVoid; + return thisParameter.type?.kind === ts.SyntaxKind.VoidKeyword ? ContextType.Void : ContextType.NonVoid; } // noSelf declaration on function signature @@ -57,7 +122,8 @@ export function getDeclarationContextType( ts.isConstructSignatureDeclaration(signatureDeclaration) || ts.isConstructorDeclaration(signatureDeclaration) || (signatureDeclaration.parent && ts.isPropertyDeclaration(signatureDeclaration.parent)) || - (signatureDeclaration.parent && ts.isPropertySignature(signatureDeclaration.parent)) + (signatureDeclaration.parent && ts.isPropertySignature(signatureDeclaration.parent)) || + (signatureDeclaration.parent && ts.isIndexSignatureDeclaration(signatureDeclaration.parent)) ) { // Class/interface methods only respect @noSelf on their parent const scopeDeclaration = findFirstNodeAbove( @@ -66,21 +132,42 @@ export function getDeclarationContextType( ts.isClassDeclaration(n) || ts.isClassExpression(n) || ts.isInterfaceDeclaration(n) ); - if (scopeDeclaration === undefined) { - return ContextType.NonVoid; - } - - if (getNodeAnnotations(scopeDeclaration).has(AnnotationKind.NoSelf)) { + if (scopeDeclaration !== undefined && getNodeAnnotations(scopeDeclaration).has(AnnotationKind.NoSelf)) { return ContextType.Void; } return ContextType.NonVoid; } + if (signatureDeclaration.parent && ts.isTypeParameterDeclaration(signatureDeclaration.parent)) { + return ContextType.NonVoid; + } + + // Call signature inside a class or interface respects @noSelf on the enclosing class/interface + if (ts.isCallSignatureDeclaration(signatureDeclaration)) { + const scopeDeclaration = findFirstNodeAbove( + signatureDeclaration, + (n): n is ts.ClassLikeDeclaration | ts.InterfaceDeclaration => + ts.isClassDeclaration(n) || ts.isClassExpression(n) || ts.isInterfaceDeclaration(n) + ); + + if (scopeDeclaration !== undefined && getNodeAnnotations(scopeDeclaration).has(AnnotationKind.NoSelf)) { + return ContextType.Void; + } + } + // When using --noImplicitSelf and the signature is defined in a file targeted by the program apply the @noSelf rule. + const program = context.program; const options = program.getCompilerOptions() as CompilerOptions; - if (options.noImplicitSelf && program.getRootFileNames().includes(signatureDeclaration.getSourceFile().fileName)) { - return ContextType.Void; + if (options.noImplicitSelf) { + const sourceFile = program.getSourceFile(signatureDeclaration.getSourceFile().fileName); + if ( + sourceFile !== undefined && + !program.isSourceFileDefaultLibrary(sourceFile) && + !program.isSourceFileFromExternalLibrary(sourceFile) + ) { + return ContextType.Void; + } } // Walk up to find @noSelf or @noSelfInFile @@ -92,52 +179,63 @@ export function getDeclarationContextType( } function reduceContextTypes(contexts: ContextType[]): ContextType { - const reducer = (a: ContextType, b: ContextType) => { - if (a === ContextType.None) { - return b; - } else if (b === ContextType.None) { - return a; - } else if (a !== b) { - return ContextType.Mixed; - } else { - return a; - } - }; - - return contexts.reduce(reducer, ContextType.None); + let type = ContextType.None; + for (const context of contexts) { + type |= context; + if (type === ContextType.Mixed) break; + } + return type; } -function getSignatureDeclarations( - context: TransformationContext, - signatures: readonly ts.Signature[] -): ts.SignatureDeclaration[] { - return signatures.flatMap(signature => { - const signatureDeclaration = signature.getDeclaration(); - if ( - (ts.isFunctionExpression(signatureDeclaration) || ts.isArrowFunction(signatureDeclaration)) && - !getExplicitThisParameter(signatureDeclaration) - ) { - // Infer type of function expressions/arrow functions - const inferredType = inferAssignedType(context, signatureDeclaration); - if (inferredType) { - const inferredSignatures = getAllCallSignatures(inferredType); - if (inferredSignatures.length > 0) { - return inferredSignatures.map(s => s.getDeclaration()); - } - } +function getSignatureDeclarations(context: TransformationContext, signature: ts.Signature): ts.SignatureDeclaration[] { + if (signature.compositeSignatures) { + return signature.compositeSignatures.flatMap(s => getSignatureDeclarations(context, s)); + } + + const signatureDeclaration = signature.getDeclaration(); + if (signatureDeclaration === undefined) { + return []; + } + + let inferredType: ts.Type | undefined; + if ( + ts.isMethodDeclaration(signatureDeclaration) && + ts.isObjectLiteralExpression(signatureDeclaration.parent) && + !getExplicitThisParameter(signatureDeclaration) + ) { + inferredType = context.checker.getContextualTypeForObjectLiteralElement(signatureDeclaration); + } else if ( + (ts.isFunctionExpression(signatureDeclaration) || ts.isArrowFunction(signatureDeclaration)) && + !getExplicitThisParameter(signatureDeclaration) + ) { + // Infer type of function expressions/arrow functions + inferredType = inferAssignedType(context, signatureDeclaration); + } + + if (inferredType) { + const inferredSignatures = getAllCallSignatures(inferredType); + if (inferredSignatures.length > 0) { + return inferredSignatures.map(s => s.getDeclaration()); } + } - return signatureDeclaration; - }); + return [signatureDeclaration]; } +const typeContextTypes = new WeakMap(); + export function getFunctionContextType(context: TransformationContext, type: ts.Type): ContextType { - if (type.isTypeParameter()) { - type = type.getConstraint() ?? type; - } + const known = typeContextTypes.get(type); + if (known !== undefined) return known; + const contextType = computeFunctionContextType(context, type); + typeContextTypes.set(type, contextType); + return contextType; +} - if (type.isUnion()) { - return reduceContextTypes(type.types.map(t => getFunctionContextType(context, t))); +function computeFunctionContextType(context: TransformationContext, type: ts.Type): ContextType { + if (type.isTypeParameter()) { + const constraint = type.getConstraint(); + if (constraint) return getFunctionContextType(context, constraint); } const signatures = context.checker.getSignaturesOfType(type, ts.SignatureKind.Call); @@ -145,6 +243,7 @@ export function getFunctionContextType(context: TransformationContext, type: ts. return ContextType.None; } - const signatureDeclarations = getSignatureDeclarations(context, signatures); - return reduceContextTypes(signatureDeclarations.map(s => getDeclarationContextType(context, s))); + return reduceContextTypes( + signatures.flatMap(s => getSignatureDeclarations(context, s)).map(s => getSignatureContextType(context, s)) + ); } diff --git a/src/transformation/utils/language-extensions.ts b/src/transformation/utils/language-extensions.ts index 3861cbf57..6da38837a 100644 --- a/src/transformation/utils/language-extensions.ts +++ b/src/transformation/utils/language-extensions.ts @@ -1,130 +1,178 @@ import * as ts from "typescript"; import { TransformationContext } from "../context"; +import { invalidMethodCallExtensionUse, invalidSpreadInCallExtension } from "./diagnostics"; export enum ExtensionKind { MultiFunction = "MultiFunction", - MultiType = "MultiType", RangeFunction = "RangeFunction", VarargConstant = "VarargConstant", - IterableType = "IterableType", - AdditionOperatorType = "AdditionOperatorType", - AdditionOperatorMethodType = "AdditionOperatorMethodType", - SubtractionOperatorType = "SubtractionOperatorType", - SubtractionOperatorMethodType = "SubtractionOperatorMethodType", - MultiplicationOperatorType = "MultiplicationOperatorType", - MultiplicationOperatorMethodType = "MultiplicationOperatorMethodType", - DivisionOperatorType = "DivisionOperatorType", - DivisionOperatorMethodType = "DivisionOperatorMethodType", - ModuloOperatorType = "ModuloOperatorType", - ModuloOperatorMethodType = "ModuloOperatorMethodType", - PowerOperatorType = "PowerOperatorType", - PowerOperatorMethodType = "PowerOperatorMethodType", - FloorDivisionOperatorType = "FloorDivisionOperatorType", - FloorDivisionOperatorMethodType = "FloorDivisionOperatorMethodType", - BitwiseAndOperatorType = "BitwiseAndOperatorType", - BitwiseAndOperatorMethodType = "BitwiseAndOperatorMethodType", - BitwiseOrOperatorType = "BitwiseOrOperatorType", - BitwiseOrOperatorMethodType = "BitwiseOrOperatorMethodType", - BitwiseExclusiveOrOperatorType = "BitwiseExclusiveOrOperatorType", - BitwiseExclusiveOrOperatorMethodType = "BitwiseExclusiveOrOperatorMethodType", - BitwiseLeftShiftOperatorType = "BitwiseLeftShiftOperatorType", - BitwiseLeftShiftOperatorMethodType = "BitwiseLeftShiftOperatorMethodType", - BitwiseRightShiftOperatorType = "BitwiseRightShiftOperatorType", - BitwiseRightShiftOperatorMethodType = "BitwiseRightShiftOperatorMethodType", - ConcatOperatorType = "ConcatOperatorType", - ConcatOperatorMethodType = "ConcatOperatorMethodType", - LessThanOperatorType = "LessThanOperatorType", - LessThanOperatorMethodType = "LessThanOperatorMethodType", - GreaterThanOperatorType = "GreaterThanOperatorType", - GreaterThanOperatorMethodType = "GreaterThanOperatorMethodType", - NegationOperatorType = "NegationOperatorType", - NegationOperatorMethodType = "NegationOperatorMethodType", - BitwiseNotOperatorType = "BitwiseNotOperatorType", - BitwiseNotOperatorMethodType = "BitwiseNotOperatorMethodType", - LengthOperatorType = "LengthOperatorType", - LengthOperatorMethodType = "LengthOperatorMethodType", - TableNewType = "TableNewType", - TableDeleteType = "TableDeleteType", - TableDeleteMethodType = "TableDeleteMethodType", - TableGetType = "TableGetType", - TableGetMethodType = "TableGetMethodType", - TableHasType = "TableHasType", - TableHasMethodType = "TableHasMethodType", - TableSetType = "TableSetType", - TableSetMethodType = "TableSetMethodType", + AdditionOperatorType = "Addition", + AdditionOperatorMethodType = "AdditionMethod", + SubtractionOperatorType = "Subtraction", + SubtractionOperatorMethodType = "SubtractionMethod", + MultiplicationOperatorType = "Multiplication", + MultiplicationOperatorMethodType = "MultiplicationMethod", + DivisionOperatorType = "Division", + DivisionOperatorMethodType = "DivisionMethod", + ModuloOperatorType = "Modulo", + ModuloOperatorMethodType = "ModuloMethod", + PowerOperatorType = "Power", + PowerOperatorMethodType = "PowerMethod", + FloorDivisionOperatorType = "FloorDivision", + FloorDivisionOperatorMethodType = "FloorDivisionMethod", + BitwiseAndOperatorType = "BitwiseAnd", + BitwiseAndOperatorMethodType = "BitwiseAndMethod", + BitwiseOrOperatorType = "BitwiseOr", + BitwiseOrOperatorMethodType = "BitwiseOrMethod", + BitwiseExclusiveOrOperatorType = "BitwiseExclusiveOr", + BitwiseExclusiveOrOperatorMethodType = "BitwiseExclusiveOrMethod", + BitwiseLeftShiftOperatorType = "BitwiseLeftShift", + BitwiseLeftShiftOperatorMethodType = "BitwiseLeftShiftMethod", + BitwiseRightShiftOperatorType = "BitwiseRightShift", + BitwiseRightShiftOperatorMethodType = "BitwiseRightShiftMethod", + ConcatOperatorType = "Concat", + ConcatOperatorMethodType = "ConcatMethod", + LessThanOperatorType = "LessThan", + LessThanOperatorMethodType = "LessThanMethod", + GreaterThanOperatorType = "GreaterThan", + GreaterThanOperatorMethodType = "GreaterThanMethod", + NegationOperatorType = "Negation", + NegationOperatorMethodType = "NegationMethod", + BitwiseNotOperatorType = "BitwiseNot", + BitwiseNotOperatorMethodType = "BitwiseNotMethod", + LengthOperatorType = "Length", + LengthOperatorMethodType = "LengthMethod", + TableNewType = "TableNew", + TableDeleteType = "TableDelete", + TableDeleteMethodType = "TableDeleteMethod", + TableGetType = "TableGet", + TableGetMethodType = "TableGetMethod", + TableHasType = "TableHas", + TableHasMethodType = "TableHasMethod", + TableSetType = "TableSet", + TableSetMethodType = "TableSetMethod", + TableAddKeyType = "TableAddKey", + TableAddKeyMethodType = "TableAddKeyMethod", + TableIsEmptyType = "TableIsEmpty", + TableIsEmptyMethodType = "TableIsEmptyMethod", } -const extensionKindToValueName: { [T in ExtensionKind]?: string } = { - [ExtensionKind.MultiFunction]: "$multi", - [ExtensionKind.RangeFunction]: "$range", - [ExtensionKind.VarargConstant]: "$vararg", -}; +const extensionValues: Set = new Set(Object.values(ExtensionKind)); -const extensionKindToTypeBrand: { [T in ExtensionKind]: string } = { - [ExtensionKind.MultiFunction]: "__luaMultiFunctionBrand", - [ExtensionKind.MultiType]: "__luaMultiReturnBrand", - [ExtensionKind.RangeFunction]: "__luaRangeFunctionBrand", - [ExtensionKind.VarargConstant]: "__luaVarargConstantBrand", - [ExtensionKind.IterableType]: "__luaIterableBrand", - [ExtensionKind.AdditionOperatorType]: "__luaAdditionBrand", - [ExtensionKind.AdditionOperatorMethodType]: "__luaAdditionMethodBrand", - [ExtensionKind.SubtractionOperatorType]: "__luaSubtractionBrand", - [ExtensionKind.SubtractionOperatorMethodType]: "__luaSubtractionMethodBrand", - [ExtensionKind.MultiplicationOperatorType]: "__luaMultiplicationBrand", - [ExtensionKind.MultiplicationOperatorMethodType]: "__luaMultiplicationMethodBrand", - [ExtensionKind.DivisionOperatorType]: "__luaDivisionBrand", - [ExtensionKind.DivisionOperatorMethodType]: "__luaDivisionMethodBrand", - [ExtensionKind.ModuloOperatorType]: "__luaModuloBrand", - [ExtensionKind.ModuloOperatorMethodType]: "__luaModuloMethodBrand", - [ExtensionKind.PowerOperatorType]: "__luaPowerBrand", - [ExtensionKind.PowerOperatorMethodType]: "__luaPowerMethodBrand", - [ExtensionKind.FloorDivisionOperatorType]: "__luaFloorDivisionBrand", - [ExtensionKind.FloorDivisionOperatorMethodType]: "__luaFloorDivisionMethodBrand", - [ExtensionKind.BitwiseAndOperatorType]: "__luaBitwiseAndBrand", - [ExtensionKind.BitwiseAndOperatorMethodType]: "__luaBitwiseAndMethodBrand", - [ExtensionKind.BitwiseOrOperatorType]: "__luaBitwiseOrBrand", - [ExtensionKind.BitwiseOrOperatorMethodType]: "__luaBitwiseOrMethodBrand", - [ExtensionKind.BitwiseExclusiveOrOperatorType]: "__luaBitwiseExclusiveOrBrand", - [ExtensionKind.BitwiseExclusiveOrOperatorMethodType]: "__luaBitwiseExclusiveOrMethodBrand", - [ExtensionKind.BitwiseLeftShiftOperatorType]: "__luaBitwiseLeftShiftBrand", - [ExtensionKind.BitwiseLeftShiftOperatorMethodType]: "__luaBitwiseLeftShiftMethodBrand", - [ExtensionKind.BitwiseRightShiftOperatorType]: "__luaBitwiseRightShiftBrand", - [ExtensionKind.BitwiseRightShiftOperatorMethodType]: "__luaBitwiseRightShiftMethodBrand", - [ExtensionKind.ConcatOperatorType]: "__luaConcatBrand", - [ExtensionKind.ConcatOperatorMethodType]: "__luaConcatMethodBrand", - [ExtensionKind.LessThanOperatorType]: "__luaLessThanBrand", - [ExtensionKind.LessThanOperatorMethodType]: "__luaLessThanMethodBrand", - [ExtensionKind.GreaterThanOperatorType]: "__luaGreaterThanBrand", - [ExtensionKind.GreaterThanOperatorMethodType]: "__luaGreaterThanMethodBrand", - [ExtensionKind.NegationOperatorType]: "__luaNegationBrand", - [ExtensionKind.NegationOperatorMethodType]: "__luaNegationMethodBrand", - [ExtensionKind.BitwiseNotOperatorType]: "__luaBitwiseNotBrand", - [ExtensionKind.BitwiseNotOperatorMethodType]: "__luaBitwiseNotMethodBrand", - [ExtensionKind.LengthOperatorType]: "__luaLengthBrand", - [ExtensionKind.LengthOperatorMethodType]: "__luaLengthMethodBrand", - [ExtensionKind.TableNewType]: "__luaTableNewBrand", - [ExtensionKind.TableDeleteType]: "__luaTableDeleteBrand", - [ExtensionKind.TableDeleteMethodType]: "__luaTableDeleteMethodBrand", - [ExtensionKind.TableGetType]: "__luaTableGetBrand", - [ExtensionKind.TableGetMethodType]: "__luaTableGetMethodBrand", - [ExtensionKind.TableHasType]: "__luaTableHasBrand", - [ExtensionKind.TableHasMethodType]: "__luaTableHasMethodBrand", - [ExtensionKind.TableSetType]: "__luaTableSetBrand", - [ExtensionKind.TableSetMethodType]: "__luaTableSetMethodBrand", -}; +export function getExtensionKindForType(context: TransformationContext, type: ts.Type): ExtensionKind | undefined { + const value = getPropertyValue(context, type, "__tstlExtension"); + if (value && extensionValues.has(value)) { + return value as ExtensionKind; + } +} + +const excludedTypeFlags: ts.TypeFlags = + ((1 << 18) - 1) | // All flags from Any...Never + ts.TypeFlags.Index | + ts.TypeFlags.NonPrimitive; + +function getPropertyValue(context: TransformationContext, type: ts.Type, propertyName: string): string | undefined { + if (type.flags & excludedTypeFlags) return; + const property = type.getProperty(propertyName); + if (!property) return undefined; + const propertyType = context.checker.getTypeOfSymbolAtLocation(property, context.sourceFile); + if (propertyType.isStringLiteral()) return propertyType.value; +} + +export function getExtensionKindForNode(context: TransformationContext, node: ts.Node): ExtensionKind | undefined { + const originalNode = ts.getOriginalNode(node); + let type = context.checker.getTypeAtLocation(originalNode); + if (ts.isOptionalChain(originalNode)) { + type = context.checker.getNonNullableType(type); + } + return getExtensionKindForType(context, type); +} + +export function getExtensionKindForSymbol( + context: TransformationContext, + symbol: ts.Symbol +): ExtensionKind | undefined { + const type = context.checker.getTypeOfSymbolAtLocation(symbol, context.sourceFile); + return getExtensionKindForType(context, type); +} + +export enum IterableExtensionKind { + Iterable = "Iterable", + Pairs = "Pairs", + PairsKey = "PairsKey", +} + +export function isLuaIterable(context: TransformationContext, type: ts.Type): boolean { + return getPropertyValue(context, type, "__tstlIterable") !== undefined; +} + +export function getIterableExtensionTypeForType( + context: TransformationContext, + type: ts.Type +): IterableExtensionKind | undefined { + const value = getPropertyValue(context, type, "__tstlIterable"); + if (value && value in IterableExtensionKind) { + return value as IterableExtensionKind; + } +} + +export function getIterableExtensionKindForNode( + context: TransformationContext, + node: ts.Node +): IterableExtensionKind | undefined { + const type = context.checker.getTypeAtLocation(node); + return getIterableExtensionTypeForType(context, type); +} + +export const methodExtensionKinds: ReadonlySet = new Set( + Object.values(ExtensionKind).filter(key => key.endsWith("Method")) +); + +export function getNaryCallExtensionArgs( + context: TransformationContext, + node: ts.CallExpression, + kind: ExtensionKind, + numArgs: number +): readonly ts.Expression[] | undefined { + let expressions: readonly ts.Expression[]; + if (node.arguments.some(ts.isSpreadElement)) { + context.diagnostics.push(invalidSpreadInCallExtension(node)); + return undefined; + } + if (methodExtensionKinds.has(kind)) { + if (!(ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression))) { + context.diagnostics.push(invalidMethodCallExtensionUse(node)); + return undefined; + } + if (node.arguments.length < numArgs - 1) { + // assumed to be TS error + return undefined; + } + expressions = [node.expression.expression, ...node.arguments]; + } else { + if (node.arguments.length < numArgs) { + // assumed to be TS error + return undefined; + } + expressions = node.arguments; + } + return expressions; +} -export function isExtensionType(type: ts.Type, extensionKind: ExtensionKind): boolean { - const typeBrand = extensionKindToTypeBrand[extensionKind]; - return typeBrand !== undefined && type.getProperty(typeBrand) !== undefined; +export function getUnaryCallExtensionArg( + context: TransformationContext, + node: ts.CallExpression, + kind: ExtensionKind +): ts.Expression | undefined { + return getNaryCallExtensionArgs(context, node, kind, 1)?.[0]; } -export function isExtensionValue( +export function getBinaryCallExtensionArgs( context: TransformationContext, - symbol: ts.Symbol, - extensionKind: ExtensionKind -): boolean { - return ( - symbol.getName() === extensionKindToValueName[extensionKind] && - symbol.declarations?.some(d => isExtensionType(context.checker.getTypeAtLocation(d), extensionKind)) === true - ); + node: ts.CallExpression, + kind: ExtensionKind +): readonly [ts.Expression, ts.Expression] | undefined { + const expressions = getNaryCallExtensionArgs(context, node, kind, 2); + if (expressions === undefined) return undefined; + return [expressions[0], expressions[1]]; } diff --git a/src/transformation/utils/lua-ast.ts b/src/transformation/utils/lua-ast.ts index 5f3a21e4a..59f6cffd7 100644 --- a/src/transformation/utils/lua-ast.ts +++ b/src/transformation/utils/lua-ast.ts @@ -4,7 +4,7 @@ import * as lua from "../../LuaAST"; import { assert, castArray } from "../../utils"; import { TransformationContext } from "../context"; import { createExportedIdentifier, getIdentifierExportScope } from "./export"; -import { peekScope, ScopeType, Scope } from "./scope"; +import { peekScope, ScopeType, Scope, addScopeVariableDeclaration } from "./scope"; import { transformLuaLibFunction } from "./lualib"; import { LuaLibFeature } from "../../LuaLib"; @@ -62,34 +62,56 @@ export function getNumberLiteralValue(expression?: lua.Expression) { return undefined; } -// Prefer use of transformToImmediatelyInvokedFunctionExpression to maintain correct scope. If you use this directly, -// ensure you push/pop a function scope appropriately to avoid incorrect vararg optimization. -export function createImmediatelyInvokedFunctionExpression( - statements: lua.Statement[], - result: lua.Expression | lua.Expression[], +export function createUnpackCall( + context: TransformationContext, + expression: lua.Expression, tsOriginal?: ts.Node -): lua.CallExpression { - const body = [...statements, lua.createReturnStatement(castArray(result))]; - const flags = statements.length === 0 ? lua.FunctionExpressionFlags.Inline : lua.FunctionExpressionFlags.None; - const iife = lua.createFunctionExpression(lua.createBlock(body), undefined, undefined, flags); - return lua.createCallExpression(iife, [], tsOriginal); +): lua.Expression { + if (context.luaTarget === LuaTarget.Universal) { + return transformLuaLibFunction(context, LuaLibFeature.Unpack, tsOriginal, expression); + } + + const unpack = + context.luaTarget === LuaTarget.Lua50 || + context.luaTarget === LuaTarget.Lua51 || + context.luaTarget === LuaTarget.LuaJIT + ? lua.createIdentifier("unpack") + : lua.createTableIndexExpression(lua.createIdentifier("table"), lua.createStringLiteral("unpack")); + + return lua.setNodeFlags(lua.createCallExpression(unpack, [expression], tsOriginal), lua.NodeFlags.TableUnpackCall); } -export function createUnpackCall( +export function createBoundedUnpackCall( context: TransformationContext, expression: lua.Expression, + maxUnpackItem: number, tsOriginal?: ts.Node ): lua.Expression { if (context.luaTarget === LuaTarget.Universal) { return transformLuaLibFunction(context, LuaLibFeature.Unpack, tsOriginal, expression); } + // Lua 5.0 does not support this signature, so don't add the arguments there + const extraArgs = + context.luaTarget === LuaTarget.Lua50 + ? [] + : [lua.createNumericLiteral(1), lua.createNumericLiteral(maxUnpackItem)]; + const unpack = - context.luaTarget === LuaTarget.Lua51 || context.luaTarget === LuaTarget.LuaJIT + context.luaTarget === LuaTarget.Lua50 || + context.luaTarget === LuaTarget.Lua51 || + context.luaTarget === LuaTarget.LuaJIT ? lua.createIdentifier("unpack") : lua.createTableIndexExpression(lua.createIdentifier("table"), lua.createStringLiteral("unpack")); - return lua.createCallExpression(unpack, [expression], tsOriginal); + return lua.setNodeFlags( + lua.createCallExpression(unpack, [expression, ...extraArgs], tsOriginal), + lua.NodeFlags.TableUnpackCall + ); +} + +export function isUnpackCall(node: lua.Node): boolean { + return lua.isCallExpression(node) && (node.flags & lua.NodeFlags.TableUnpackCall) !== 0; } export function wrapInTable(...expressions: lua.Expression[]): lua.TableExpression { @@ -119,12 +141,7 @@ export function createHoistableVariableDeclarationStatement( if (identifier.symbolId !== undefined) { const scope = peekScope(context); assert(scope.type !== ScopeType.Switch); - - if (!scope.variableDeclarations) { - scope.variableDeclarations = []; - } - - scope.variableDeclarations.push(declaration); + addScopeVariableDeclaration(scope, declaration); } return declaration; @@ -151,6 +168,8 @@ export function createLocalOrExportedOrGlobalDeclaration( let declaration: lua.VariableDeclarationStatement | undefined; let assignment: lua.AssignmentStatement | undefined; + const noImplicitGlobalVariables = context.options.noImplicitGlobalVariables === true; + const isFunctionDeclaration = tsOriginal !== undefined && ts.isFunctionDeclaration(tsOriginal); const identifiers = castArray(lhs); @@ -174,26 +193,27 @@ export function createLocalOrExportedOrGlobalDeclaration( const scope = peekScope(context); const isTopLevelVariable = scope.type === ScopeType.File; - if (context.isModule || !isTopLevelVariable) { - if (scope.type === ScopeType.Switch || (!isFunctionDeclaration && hasMultipleReferences(scope, lhs))) { - // Split declaration and assignment of identifiers that reference themselves in their declaration - declaration = lua.createVariableDeclarationStatement(lhs, undefined, tsOriginal); + if (context.isModule || !isTopLevelVariable || noImplicitGlobalVariables) { + const isLuaFunctionExpression = rhs && !Array.isArray(rhs) && lua.isFunctionExpression(rhs); + const isSafeRecursiveFunctionDeclaration = isFunctionDeclaration && isLuaFunctionExpression; + if (!isSafeRecursiveFunctionDeclaration && hasMultipleReferences(scope, lhs)) { + // Split declaration and assignment of identifiers that reference themselves in their declaration. + // Put declaration above preceding statements in case the identifier is referenced in those. + const precedingDeclaration = lua.createVariableDeclarationStatement(lhs, undefined, tsOriginal); + context.prependPrecedingStatements(precedingDeclaration); if (rhs) { assignment = lua.createAssignmentStatement(lhs, rhs, tsOriginal); } + + // Remember local variable declarations for hoisting later + addScopeVariableDeclaration(scope, precedingDeclaration); } else { declaration = lua.createVariableDeclarationStatement(lhs, rhs, tsOriginal); - } - - // Remember local variable declarations for hoisting later - if (!scope.variableDeclarations) { - scope.variableDeclarations = []; - } - scope.variableDeclarations.push(declaration); - - if (scope.type === ScopeType.Switch) { - declaration = undefined; + if (!isFunctionDeclaration) { + // Remember local variable declarations for hoisting later + addScopeVariableDeclaration(scope, declaration); + } } } else if (rhs) { // global @@ -215,6 +235,8 @@ export function createLocalOrExportedOrGlobalDeclaration( } } + setJSDocComments(context, tsOriginal, declaration, assignment); + if (declaration && assignment) { return [declaration, assignment]; } else if (declaration) { @@ -226,6 +248,105 @@ export function createLocalOrExportedOrGlobalDeclaration( } } +/** + * Apply JSDoc comments to the newly-created Lua statement, if present. + * https://stackoverflow.com/questions/47429792/is-it-possible-to-get-comments-as-nodes-in-the-ast-using-the-typescript-compiler + */ +function setJSDocComments( + context: TransformationContext, + tsOriginal: ts.Node | undefined, + declaration: lua.VariableDeclarationStatement | undefined, + assignment: lua.AssignmentStatement | undefined +) { + // Respect the vanilla TypeScript option of "removeComments": + // https://www.typescriptlang.org/tsconfig#removeComments + if (context.options.removeComments) { + return; + } + + const docCommentArray = getJSDocCommentFromTSNode(context, tsOriginal); + if (docCommentArray === undefined) { + return; + } + + if (declaration && assignment) { + declaration.leadingComments = docCommentArray; + } else if (declaration) { + declaration.leadingComments = docCommentArray; + } else if (assignment) { + assignment.leadingComments = docCommentArray; + } +} + +function getJSDocCommentFromTSNode( + context: TransformationContext, + tsOriginal: ts.Node | undefined +): string[] | undefined { + if (tsOriginal === undefined) { + return undefined; + } + + // The "name" property is only on a subset of node types; we want to be permissive and get the + // comments from as many nodes as possible. + const node = tsOriginal as any; + if (node.name === undefined) { + return undefined; + } + + const symbol = context.checker.getSymbolAtLocation(node.name); + if (symbol === undefined) { + return undefined; + } + + // The TypeScript compiler separates JSDoc comments into the "documentation comment" and the + // "tags". The former is conventionally at the top of the comment, and the bottom is + // conventionally at the bottom. We need to get both from the TypeScript API and then combine + // them into one block of text. + const docCommentArray = symbol.getDocumentationComment(context.checker); + const docCommentText = ts.displayPartsToString(docCommentArray).trim(); + + const jsDocTagInfoArray = symbol.getJsDocTags(context.checker); + const jsDocTagsTextLines = jsDocTagInfoArray.map(jsDocTagInfo => { + let text = "@" + jsDocTagInfo.name; + if (jsDocTagInfo.text !== undefined) { + const tagDescriptionTextArray = jsDocTagInfo.text + .filter(symbolDisplayPart => symbolDisplayPart.text.trim() !== "") + .map(symbolDisplayPart => symbolDisplayPart.text.trim()); + const tagDescriptionText = tagDescriptionTextArray.join(" "); + text += " " + tagDescriptionText; + } + return text; + }); + const jsDocTagsText = jsDocTagsTextLines.join("\n"); + + const combined = (docCommentText + "\n\n" + jsDocTagsText).trim(); + if (combined === "") { + return undefined; + } + + // By default, TSTL will display comments immediately next to the "--" characters. We can make + // the comments look better if we separate them by a space (similar to what Prettier does in + // JavaScript/TypeScript). + const linesWithoutSpace = combined.split("\n"); + const lines = linesWithoutSpace.map(line => ` ${line}`); + + // We want to JSDoc comments to map on to LDoc comments: + // https://stevedonovan.github.io/ldoc/manual/doc.md.html + // LDoc comments require that the first line starts with three hyphens. + // Thus, need to add a hyphen to the first line. + if (lines.length > 0) { + const firstLine = lines[0]; + if (firstLine.startsWith(" @")) { + lines.unshift("-"); + } else { + lines.shift(); + lines.unshift("-" + firstLine); + } + + return lines; + } +} + export const createNaN = (tsOriginal?: ts.Node) => lua.createBinaryExpression( lua.createNumericLiteral(0), diff --git a/src/transformation/utils/lualib.ts b/src/transformation/utils/lualib.ts index 3c2d0ec1b..ee828d1b9 100644 --- a/src/transformation/utils/lualib.ts +++ b/src/transformation/utils/lualib.ts @@ -1,18 +1,12 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { LuaLibFeature } from "../../LuaLib"; -import { getOrUpdate } from "../../utils"; import { TransformationContext } from "../context"; export { LuaLibFeature }; -const luaLibFeatures = new WeakMap>(); -export function getUsedLuaLibFeatures(context: TransformationContext): Set { - return getOrUpdate(luaLibFeatures, context, () => new Set()); -} - export function importLuaLibFeature(context: TransformationContext, feature: LuaLibFeature): void { - getUsedLuaLibFeatures(context).add(feature); + context.usedLuaLibFeatures.add(feature); } export function transformLuaLibFunction( diff --git a/src/transformation/utils/preceding-statements.ts b/src/transformation/utils/preceding-statements.ts new file mode 100644 index 000000000..16cefac12 --- /dev/null +++ b/src/transformation/utils/preceding-statements.ts @@ -0,0 +1,18 @@ +import * as lua from "../../LuaAST"; +import { TransformationContext } from "../context"; + +export interface WithPrecedingStatements< + T extends lua.Statement | lua.Statement[] | lua.Expression | lua.Expression[] +> { + precedingStatements: lua.Statement[]; + result: T; +} + +export function transformInPrecedingStatementScope< + TReturn extends lua.Statement | lua.Statement[] | lua.Expression | lua.Expression[] +>(context: TransformationContext, transformer: () => TReturn): WithPrecedingStatements { + context.pushPrecedingStatements(); + const statementOrStatements = transformer(); + const precedingStatements = context.popPrecedingStatements(); + return { precedingStatements, result: statementOrStatements }; +} diff --git a/src/transformation/utils/safe-names.ts b/src/transformation/utils/safe-names.ts index 877196ba4..0772d71a8 100644 --- a/src/transformation/utils/safe-names.ts +++ b/src/transformation/utils/safe-names.ts @@ -1,12 +1,23 @@ import * as ts from "typescript"; +import { CompilerOptions, LuaTarget } from "../.."; import { TransformationContext } from "../context"; import { invalidAmbientIdentifierName } from "./diagnostics"; import { isSymbolExported } from "./export"; import { isAmbientNode } from "./typescript"; -export const isValidLuaIdentifier = (name: string) => !luaKeywords.has(name) && /^[a-zA-Z_][a-zA-Z0-9_]*$/.test(name); +export const shouldAllowUnicode = (options: CompilerOptions) => options.luaTarget === LuaTarget.LuaJIT; + +export const isValidLuaIdentifier = (name: string, options: CompilerOptions) => + !luaKeywords.has(name) && + (shouldAllowUnicode(options) + ? /^[a-zA-Z_\u007F-\uFFFD][a-zA-Z0-9_\u007F-\uFFFD]*$/ + : /^[a-zA-Z_][a-zA-Z0-9_]*$/ + ).test(name); + export const luaKeywords: ReadonlySet = new Set([ "and", + "bit", + "bit32", "break", "do", "else", @@ -52,10 +63,11 @@ const luaBuiltins: ReadonlySet = new Set([ "unpack", ]); -export const isUnsafeName = (name: string) => !isValidLuaIdentifier(name) || luaBuiltins.has(name); +export const isUnsafeName = (name: string, options: CompilerOptions) => + !isValidLuaIdentifier(name, options) || luaBuiltins.has(name); function checkName(context: TransformationContext, name: string, node: ts.Node): boolean { - const isInvalid = !isValidLuaIdentifier(name); + const isInvalid = !isValidLuaIdentifier(name, context.options); if (isInvalid) { // Empty identifier is a TypeScript error @@ -80,19 +92,16 @@ export function hasUnsafeSymbolName( } // only unsafe when non-ambient and not exported - return isUnsafeName(symbol.name) && !isAmbient && !isSymbolExported(context, symbol); + return isUnsafeName(symbol.name, context.options) && !isAmbient && !isSymbolExported(context, symbol); } export function hasUnsafeIdentifierName( context: TransformationContext, identifier: ts.Identifier, - checkSymbol = true + symbol: ts.Symbol | undefined ): boolean { - if (checkSymbol) { - const symbol = context.checker.getSymbolAtLocation(identifier); - if (symbol) { - return hasUnsafeSymbolName(context, symbol, identifier); - } + if (symbol) { + return hasUnsafeSymbolName(context, symbol, identifier); } return checkName(context, identifier.text, identifier); diff --git a/src/transformation/utils/scope.ts b/src/transformation/utils/scope.ts index 72b267e2a..314f1d795 100644 --- a/src/transformation/utils/scope.ts +++ b/src/transformation/utils/scope.ts @@ -14,6 +14,7 @@ export enum ScopeType { Block = 1 << 5, Try = 1 << 6, Catch = 1 << 7, + LoopInitializer = 1 << 8, } interface FunctionDefinitionInfo { @@ -21,25 +22,35 @@ interface FunctionDefinitionInfo { definition?: lua.VariableDeclarationStatement | lua.AssignmentStatement; } +export enum LoopContinued { + WithGoto, + WithRepeatBreak, + WithContinue, +} + export interface Scope { type: ScopeType; id: number; - node?: ts.Node; + node: ts.Node; referencedSymbols?: Map; variableDeclarations?: lua.VariableDeclarationStatement[]; functionDefinitions?: Map; importStatements?: lua.Statement[]; - loopContinued?: boolean; + loopContinued?: LoopContinued; functionReturned?: boolean; + asyncTryHasReturn?: boolean; + asyncTryHasBreak?: boolean; + asyncTryHasContinue?: LoopContinued; } -const scopeStacks = new WeakMap(); -function getScopeStack(context: TransformationContext): Scope[] { - return getOrUpdate(scopeStacks, context, () => []); +export interface HoistingResult { + statements: lua.Statement[]; + hoistedStatements: lua.Statement[]; + hoistedIdentifiers: lua.Identifier[]; } export function* walkScopesUp(context: TransformationContext): IterableIterator { - const scopeStack = getScopeStack(context); + const scopeStack = context.scopeStack; for (let i = scopeStack.length - 1; i >= 0; --i) { const scope = scopeStack[i]; yield scope; @@ -51,10 +62,8 @@ export function markSymbolAsReferencedInCurrentScopes( symbolId: lua.SymbolId, identifier: ts.Identifier ): void { - for (const scope of getScopeStack(context)) { - if (!scope.referencedSymbols) { - scope.referencedSymbols = new Map(); - } + for (const scope of context.scopeStack) { + scope.referencedSymbols ??= new Map(); const references = getOrUpdate(scope.referencedSymbols, symbolId, () => []); references.push(identifier); @@ -62,7 +71,7 @@ export function markSymbolAsReferencedInCurrentScopes( } export function peekScope(context: TransformationContext): Scope { - const scopeStack = getScopeStack(context); + const scopeStack = context.scopeStack; const scope = scopeStack[scopeStack.length - 1]; assert(scope); @@ -70,36 +79,47 @@ export function peekScope(context: TransformationContext): Scope { } export function findScope(context: TransformationContext, scopeTypes: ScopeType): Scope | undefined { - return [...getScopeStack(context)].reverse().find(s => scopeTypes & s.type); + for (let i = context.scopeStack.length - 1; i >= 0; --i) { + const scope = context.scopeStack[i]; + if (scopeTypes & scope.type) { + return scope; + } + } } -const scopeIdCounters = new WeakMap(); -export function pushScope(context: TransformationContext, scopeType: ScopeType): Scope { - const nextScopeId = (scopeIdCounters.get(context) ?? 0) + 1; - scopeIdCounters.set(context, nextScopeId); +export function findAsyncTryScopeInStack(context: TransformationContext): Scope | undefined { + for (const scope of walkScopesUp(context)) { + if (scope.type === ScopeType.Function) return undefined; + if (scope.type === ScopeType.Try || scope.type === ScopeType.Catch) return scope; + } + return undefined; +} - const scopeStack = getScopeStack(context); - const scope: Scope = { type: scopeType, id: nextScopeId }; - scopeStack.push(scope); - return scope; +/** Like findAsyncTryScopeInStack, but also stops at Loop boundaries. */ +export function findAsyncTryScopeBeforeLoop(context: TransformationContext): Scope | undefined { + for (const scope of walkScopesUp(context)) { + if (scope.type === ScopeType.Function || scope.type === ScopeType.Loop) return undefined; + if (scope.type === ScopeType.Try || scope.type === ScopeType.Catch) return scope; + } + return undefined; } -export function popScope(context: TransformationContext): Scope { - const scopeStack = getScopeStack(context); - const scope = scopeStack.pop(); - assert(scope); +export function addScopeVariableDeclaration(scope: Scope, declaration: lua.VariableDeclarationStatement) { + scope.variableDeclarations ??= []; - return scope; + scope.variableDeclarations.push(declaration); } -function isDeclaredInScope(symbol: ts.Symbol, scopeNode: ts.Node) { - return symbol?.declarations?.some(d => findFirstNodeAbove(d, (n): n is ts.Node => n === scopeNode)); +function isHoistableFunctionDeclaredInScope(symbol: ts.Symbol, scopeNode: ts.Node) { + return symbol?.declarations?.some( + d => ts.isFunctionDeclaration(d) && findFirstNodeAbove(d, (n): n is ts.Node => n === scopeNode) + ); } // Checks for references to local functions which haven't been defined yet, // and thus will be hoisted above the current position. export function hasReferencedUndefinedLocalFunction(context: TransformationContext, scope: Scope) { - if (!scope.referencedSymbols || !scope.node) { + if (!scope.referencedSymbols) { return false; } for (const [symbolId, nodes] of scope.referencedSymbols) { @@ -107,7 +127,7 @@ export function hasReferencedUndefinedLocalFunction(context: TransformationConte if ( !scope.functionDefinitions?.has(symbolId) && type.getCallSignatures().length > 0 && - isDeclaredInScope(type.symbol, scope.node) + isHoistableFunctionDeclaredInScope(type.symbol, scope.node) ) { return true; } @@ -127,20 +147,47 @@ export function hasReferencedSymbol(context: TransformationContext, scope: Scope return false; } -export function isFunctionScopeWithDefinition(scope: Scope): scope is Scope & { node: ts.SignatureDeclaration } { - return scope.node !== undefined && ts.isFunctionLike(scope.node); +export function separateHoistedStatements(context: TransformationContext, statements: lua.Statement[]): HoistingResult { + const scope = peekScope(context); + const allHoistedStatments: lua.Statement[] = []; + const allHoistedIdentifiers: lua.Identifier[] = []; + + let { unhoistedStatements, hoistedStatements, hoistedIdentifiers } = hoistFunctionDefinitions( + context, + scope, + statements + ); + allHoistedStatments.push(...hoistedStatements); + allHoistedIdentifiers.push(...hoistedIdentifiers); + + ({ unhoistedStatements, hoistedIdentifiers } = hoistVariableDeclarations(context, scope, unhoistedStatements)); + allHoistedIdentifiers.push(...hoistedIdentifiers); + + ({ unhoistedStatements, hoistedStatements } = hoistImportStatements(scope, unhoistedStatements)); + allHoistedStatments.unshift(...hoistedStatements); + + return { + statements: unhoistedStatements, + hoistedStatements: allHoistedStatments, + hoistedIdentifiers: allHoistedIdentifiers, + }; } export function performHoisting(context: TransformationContext, statements: lua.Statement[]): lua.Statement[] { - const scope = peekScope(context); - let result = statements; - result = hoistFunctionDefinitions(context, scope, result); - result = hoistVariableDeclarations(context, scope, result); - result = hoistImportStatements(scope, result); - return result; + const result = separateHoistedStatements(context, statements); + const modifiedStatements = [...result.hoistedStatements, ...result.statements]; + if (result.hoistedIdentifiers.length > 0) { + modifiedStatements.unshift(lua.createVariableDeclarationStatement(result.hoistedIdentifiers)); + } + return modifiedStatements; } function shouldHoistSymbol(context: TransformationContext, symbolId: lua.SymbolId, scope: Scope): boolean { + // Always hoist in top-level of switch statements + if (scope.type === ScopeType.Switch) { + return true; + } + const symbolInfo = getSymbolInfo(context, symbolId); if (!symbolInfo) { return false; @@ -181,65 +228,80 @@ function hoistVariableDeclarations( context: TransformationContext, scope: Scope, statements: lua.Statement[] -): lua.Statement[] { +): { unhoistedStatements: lua.Statement[]; hoistedIdentifiers: lua.Identifier[] } { if (!scope.variableDeclarations) { - return statements; + return { unhoistedStatements: statements, hoistedIdentifiers: [] }; } - const result = [...statements]; - const hoistedLocals: lua.Identifier[] = []; + const unhoistedStatements = [...statements]; + const hoistedIdentifiers: lua.Identifier[] = []; for (const declaration of scope.variableDeclarations) { const symbols = declaration.left.map(i => i.symbolId).filter(isNonNull); if (symbols.some(s => shouldHoistSymbol(context, s, scope))) { - const index = result.indexOf(declaration); - assert(index > -1); + const index = unhoistedStatements.indexOf(declaration); + if (index < 0) { + continue; // statements array may not contain all statements in the scope (switch-case) + } if (declaration.right) { const assignment = lua.createAssignmentStatement(declaration.left, declaration.right); lua.setNodePosition(assignment, declaration); // Preserve position info for sourcemap - result.splice(index, 1, assignment); + unhoistedStatements.splice(index, 1, assignment); } else { - result.splice(index, 1); + unhoistedStatements.splice(index, 1); } - hoistedLocals.push(...declaration.left); - } else if (scope.type === ScopeType.Switch) { - assert(!declaration.right); - hoistedLocals.push(...declaration.left); + hoistedIdentifiers.push(...declaration.left); } } - if (hoistedLocals.length > 0) { - result.unshift(lua.createVariableDeclarationStatement(hoistedLocals)); - } - - return result; + return { unhoistedStatements, hoistedIdentifiers }; } function hoistFunctionDefinitions( context: TransformationContext, scope: Scope, statements: lua.Statement[] -): lua.Statement[] { +): { unhoistedStatements: lua.Statement[]; hoistedStatements: lua.Statement[]; hoistedIdentifiers: lua.Identifier[] } { if (!scope.functionDefinitions) { - return statements; + return { unhoistedStatements: statements, hoistedStatements: [], hoistedIdentifiers: [] }; } - const result = [...statements]; - const hoistedFunctions: Array = []; + const unhoistedStatements = [...statements]; + const hoistedStatements: lua.Statement[] = []; + const hoistedIdentifiers: lua.Identifier[] = []; for (const [functionSymbolId, functionDefinition] of scope.functionDefinitions) { assert(functionDefinition.definition); if (shouldHoistSymbol(context, functionSymbolId, scope)) { - const index = result.indexOf(functionDefinition.definition); - result.splice(index, 1); - hoistedFunctions.push(functionDefinition.definition); + const index = unhoistedStatements.indexOf(functionDefinition.definition); + if (index < 0) { + continue; // statements array may not contain all statements in the scope (switch-case) + } + unhoistedStatements.splice(index, 1); + + if (lua.isVariableDeclarationStatement(functionDefinition.definition)) { + // Separate function definition and variable declaration + assert(functionDefinition.definition.right); + hoistedIdentifiers.push(...functionDefinition.definition.left); + hoistedStatements.push( + lua.createAssignmentStatement( + functionDefinition.definition.left, + functionDefinition.definition.right + ) + ); + } else { + hoistedStatements.push(functionDefinition.definition); + } } } - return [...hoistedFunctions, ...result]; + return { unhoistedStatements, hoistedStatements, hoistedIdentifiers }; } -function hoistImportStatements(scope: Scope, statements: lua.Statement[]): lua.Statement[] { - return scope.importStatements ? [...scope.importStatements, ...statements] : statements; +function hoistImportStatements( + scope: Scope, + statements: lua.Statement[] +): { unhoistedStatements: lua.Statement[]; hoistedStatements: lua.Statement[] } { + return { unhoistedStatements: statements, hoistedStatements: scope.importStatements ?? [] }; } diff --git a/src/transformation/utils/symbols.ts b/src/transformation/utils/symbols.ts index f800eea86..d79b68da8 100644 --- a/src/transformation/utils/symbols.ts +++ b/src/transformation/utils/symbols.ts @@ -1,31 +1,20 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; -import { getOrUpdate } from "../../utils"; import { TransformationContext } from "../context"; import { isOptimizedVarArgSpread } from "../visitors/spread"; import { markSymbolAsReferencedInCurrentScopes } from "./scope"; -const symbolIdCounters = new WeakMap(); -function nextSymbolId(context: TransformationContext): lua.SymbolId { - const symbolId = (symbolIdCounters.get(context) ?? 0) + 1; - symbolIdCounters.set(context, symbolId); - return symbolId as lua.SymbolId; -} - export interface SymbolInfo { symbol: ts.Symbol; firstSeenAtPos: number; } -const symbolInfoMap = new WeakMap>(); -const symbolIdMaps = new WeakMap>(); - export function getSymbolInfo(context: TransformationContext, symbolId: lua.SymbolId): SymbolInfo | undefined { - return getOrUpdate(symbolInfoMap, context, () => new Map()).get(symbolId); + return context.symbolInfoMap.get(symbolId); } export function getSymbolIdOfSymbol(context: TransformationContext, symbol: ts.Symbol): lua.SymbolId | undefined { - return getOrUpdate(symbolIdMaps, context, () => new Map()).get(symbol); + return context.symbolIdMaps.get(symbol); } export function trackSymbolReference( @@ -33,16 +22,13 @@ export function trackSymbolReference( symbol: ts.Symbol, identifier: ts.Identifier ): lua.SymbolId | undefined { - const symbolIds = getOrUpdate(symbolIdMaps, context, () => new Map()); - // Track first time symbols are seen - let symbolId = symbolIds.get(symbol); + let symbolId = context.symbolIdMaps.get(symbol); if (symbolId === undefined) { - symbolId = nextSymbolId(context); + symbolId = context.nextSymbolId(); - symbolIds.set(symbol, symbolId); - const symbolInfo = getOrUpdate(symbolInfoMap, context, () => new Map()); - symbolInfo.set(symbolId, { symbol, firstSeenAtPos: identifier.pos }); + context.symbolIdMaps.set(symbol, symbolId); + context.symbolInfoMap.set(symbolId, { symbol, firstSeenAtPos: identifier.pos }); } // If isOptimizedVarArgSpread returns true, the identifier will not appear in the resulting Lua. @@ -56,9 +42,9 @@ export function trackSymbolReference( export function getIdentifierSymbolId( context: TransformationContext, - identifier: ts.Identifier + identifier: ts.Identifier, + symbol: ts.Symbol | undefined ): lua.SymbolId | undefined { - const symbol = context.checker.getSymbolAtLocation(identifier); if (symbol) { return trackSymbolReference(context, symbol, identifier); } diff --git a/src/transformation/utils/transform.ts b/src/transformation/utils/transform.ts deleted file mode 100644 index 215a018d5..000000000 --- a/src/transformation/utils/transform.ts +++ /dev/null @@ -1,22 +0,0 @@ -import * as ts from "typescript"; -import * as lua from "../../LuaAST"; -import { castArray } from "../../utils"; -import { TransformationContext } from "../context"; -import { createImmediatelyInvokedFunctionExpression } from "./lua-ast"; -import { ScopeType, pushScope, popScope } from "./scope"; - -export interface ImmediatelyInvokedFunctionParameters { - statements: lua.Statement | lua.Statement[]; - result: lua.Expression | lua.Expression[]; -} - -export function transformToImmediatelyInvokedFunctionExpression( - context: TransformationContext, - transformFunction: () => ImmediatelyInvokedFunctionParameters, - tsOriginal?: ts.Node -): lua.CallExpression { - pushScope(context, ScopeType.Function); - const { statements, result } = transformFunction(); - popScope(context); - return createImmediatelyInvokedFunctionExpression(castArray(statements), result, tsOriginal); -} diff --git a/src/transformation/utils/typescript/index.ts b/src/transformation/utils/typescript/index.ts index 6d7bfa68c..f12bf4721 100644 --- a/src/transformation/utils/typescript/index.ts +++ b/src/transformation/utils/typescript/index.ts @@ -14,16 +14,30 @@ export function hasExportEquals(sourceFile: ts.SourceFile): boolean { * Search up until finding a node satisfying the callback */ export function findFirstNodeAbove(node: ts.Node, callback: (n: ts.Node) => n is T): T | undefined { - let current = node; + // Synthetic nodes (created by pre-transformers like usingTransformer) may have an unset .parent. + // Fall back to ts.getOriginalNode so we can still walk the source-parsed parent chain. + let current = ts.getOriginalNode(node); while (current.parent) { if (callback(current.parent)) { return current.parent; } else { - current = current.parent; + current = ts.getOriginalNode(current.parent); } } } +export function findFirstNonOuterParent(node: ts.Node): ts.Node { + let current = ts.getOriginalNode(node).parent; + while (ts.isOuterExpression(current)) { + current = ts.getOriginalNode(current).parent; + } + return current; +} + +export function expressionResultIsUsed(node: ts.Expression): boolean { + return !ts.isExpressionStatement(findFirstNonOuterParent(node)); +} + export function getFirstDeclarationInFile(symbol: ts.Symbol, sourceFile: ts.SourceFile): ts.Declaration | undefined { const originalSourceFile = ts.getParseTreeNode(sourceFile) ?? sourceFile; const declarations = (symbol.getDeclarations() ?? []).filter(d => d.getSourceFile() === originalSourceFile); @@ -31,7 +45,7 @@ export function getFirstDeclarationInFile(symbol: ts.Symbol, sourceFile: ts.Sour return declarations.length > 0 ? declarations.reduce((p, c) => (p.pos < c.pos ? p : c)) : undefined; } -function isStandardLibraryDeclaration(context: TransformationContext, declaration: ts.Declaration): boolean { +export function isStandardLibraryDeclaration(context: TransformationContext, declaration: ts.Declaration): boolean { const parseTreeNode = ts.getParseTreeNode(declaration) ?? declaration; const sourceFile = parseTreeNode.getSourceFile(); if (!sourceFile) { @@ -83,7 +97,7 @@ export function isExpressionWithEvaluationEffect(node: ts.Expression): boolean { export function getFunctionTypeForCall(context: TransformationContext, node: ts.CallExpression) { const signature = context.checker.getResolvedSignature(node); - if (!signature || !signature.declaration) { + if (!signature?.declaration) { return; } const typeDeclaration = findFirstNodeAbove(signature.declaration, ts.isTypeAliasDeclaration); @@ -92,3 +106,20 @@ export function getFunctionTypeForCall(context: TransformationContext, node: ts. } return context.checker.getTypeFromTypeNode(typeDeclaration.type); } + +export function isConstIdentifier(context: TransformationContext, node: ts.Node) { + let identifier = node; + if (ts.isComputedPropertyName(identifier)) { + identifier = identifier.expression; + } + if (!ts.isIdentifier(identifier)) { + return false; + } + const symbol = context.checker.getSymbolAtLocation(identifier); + if (!symbol?.declarations) { + return false; + } + return symbol.declarations.some( + d => ts.isVariableDeclarationList(d.parent) && (d.parent.flags & ts.NodeFlags.Const) !== 0 + ); +} diff --git a/src/transformation/utils/typescript/nodes.ts b/src/transformation/utils/typescript/nodes.ts index 3007c4d7d..09e2791c7 100644 --- a/src/transformation/utils/typescript/nodes.ts +++ b/src/transformation/utils/typescript/nodes.ts @@ -1,4 +1,5 @@ import * as ts from "typescript"; +import { findFirstNodeAbove } from "."; import { TransformationContext } from "../../context"; export function isAssignmentPattern(node: ts.Node): node is ts.AssignmentPattern { @@ -25,6 +26,30 @@ export function isInDestructingAssignment(node: ts.Node): boolean { ); } +export function isInAsyncFunction(node: ts.Node): boolean { + // Check if node is in function declaration with `async` + const declaration = findFirstNodeAbove(node, ts.isFunctionLike); + if (!declaration) { + return false; + } + + if (ts.canHaveModifiers(declaration)) { + return ts.getModifiers(declaration)?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false; + } else { + return false; + } +} + +export function isInGeneratorFunction(node: ts.Node): boolean { + // Check if node is in function declaration with `async` + const declaration = findFirstNodeAbove(node, ts.isFunctionDeclaration); + if (!declaration) { + return false; + } + + return declaration.asteriskToken !== undefined; +} + /** * Quite hacky, avoid unless absolutely necessary! */ diff --git a/src/transformation/utils/typescript/types.ts b/src/transformation/utils/typescript/types.ts index 2c6e69dcb..4a356b972 100644 --- a/src/transformation/utils/typescript/types.ts +++ b/src/transformation/utils/typescript/types.ts @@ -1,69 +1,75 @@ import * as ts from "typescript"; import { TransformationContext } from "../../context"; -export function isTypeWithFlags(context: TransformationContext, type: ts.Type, flags: ts.TypeFlags): boolean { - const predicate = (type: ts.Type) => { - if (type.symbol) { - const baseConstraint = context.checker.getBaseConstraintOfType(type); - if (baseConstraint && baseConstraint !== type) { - return isTypeWithFlags(context, baseConstraint, flags); - } - } - return (type.flags & flags) !== 0; - }; - - return typeAlwaysSatisfies(context, type, predicate); -} +export function typeAlwaysHasSomeOfFlags(context: TransformationContext, type: ts.Type, flags: ts.TypeFlags): boolean { + const baseConstraint = context.checker.getBaseConstraintOfType(type); + if (baseConstraint) { + type = baseConstraint; + } -export function typeAlwaysSatisfies( - context: TransformationContext, - type: ts.Type, - predicate: (type: ts.Type) => boolean -): boolean { - if (predicate(type)) { + if (type.flags & flags) { return true; } if (type.isUnion()) { - return type.types.every(t => typeAlwaysSatisfies(context, t, predicate)); + return type.types.every(t => typeAlwaysHasSomeOfFlags(context, t, flags)); } if (type.isIntersection()) { - return type.types.some(t => typeAlwaysSatisfies(context, t, predicate)); + return type.types.some(t => typeAlwaysHasSomeOfFlags(context, t, flags)); } return false; } -export function typeCanSatisfy( - context: TransformationContext, - type: ts.Type, - predicate: (type: ts.Type) => boolean -): boolean { - if (predicate(type)) { +export function typeCanHaveSomeOfFlags(context: TransformationContext, type: ts.Type, flags: ts.TypeFlags): boolean { + const baseConstraint = context.checker.getBaseConstraintOfType(type); + if (!baseConstraint) { + // type parameter with no constraint can be anything, assume it might satisfy predicate + if (type.isTypeParameter()) return true; + } else { + type = baseConstraint; + } + + if (type.flags & flags) { return true; } if (type.isUnion()) { - return type.types.some(t => typeCanSatisfy(context, t, predicate)); + return type.types.some(t => typeCanHaveSomeOfFlags(context, t, flags)); } if (type.isIntersection()) { - return type.types.some(t => typeCanSatisfy(context, t, predicate)); + return type.types.some(t => typeCanHaveSomeOfFlags(context, t, flags)); } return false; } export function isStringType(context: TransformationContext, type: ts.Type): boolean { - return isTypeWithFlags(context, type, ts.TypeFlags.String | ts.TypeFlags.StringLike | ts.TypeFlags.StringLiteral); + return typeAlwaysHasSomeOfFlags(context, type, ts.TypeFlags.StringLike); } export function isNumberType(context: TransformationContext, type: ts.Type): boolean { - return isTypeWithFlags(context, type, ts.TypeFlags.Number | ts.TypeFlags.NumberLike | ts.TypeFlags.NumberLiteral); + return typeAlwaysHasSomeOfFlags(context, type, ts.TypeFlags.NumberLike); } function isExplicitArrayType(context: TransformationContext, type: ts.Type): boolean { + if (context.checker.isArrayType(type) || context.checker.isTupleType(type)) return true; + + if (type.isUnionOrIntersection()) { + if (type.types.some(t => isExplicitArrayType(context, t))) { + return true; + } + } + + const baseTypes = type.getBaseTypes(); + if (baseTypes) { + if (baseTypes.some(t => isExplicitArrayType(context, t))) { + return true; + } + } + if (type.symbol) { const baseConstraint = context.checker.getBaseConstraintOfType(type); if (baseConstraint && baseConstraint !== type) { @@ -71,17 +77,23 @@ function isExplicitArrayType(context: TransformationContext, type: ts.Type): boo } } - if (type.isUnionOrIntersection()) { - return type.types.some(t => isExplicitArrayType(context, t)); + return false; +} + +function isAlwaysExplicitArrayType(context: TransformationContext, type: ts.Type): boolean { + if (context.checker.isArrayType(type) || context.checker.isTupleType(type)) return true; + if (type.symbol) { + const baseConstraint = context.checker.getBaseConstraintOfType(type); + if (baseConstraint && baseConstraint !== type) { + return isAlwaysExplicitArrayType(context, baseConstraint); + } } - const flags = ts.NodeBuilderFlags.InTypeAlias | ts.NodeBuilderFlags.AllowEmptyTuple; - let typeNode = context.checker.typeToTypeNode(type, undefined, flags); - if (typeNode && ts.isTypeOperatorNode(typeNode) && typeNode.operator === ts.SyntaxKind.ReadonlyKeyword) { - typeNode = typeNode.type; + if (type.isUnionOrIntersection()) { + return type.types.every(t => isAlwaysExplicitArrayType(context, t)); } - return typeNode !== undefined && (ts.isArrayTypeNode(typeNode) || ts.isTupleTypeNode(typeNode)); + return false; } /** @@ -100,14 +112,45 @@ export function forTypeOrAnySupertype( type = context.checker.getDeclaredTypeOfSymbol(type.symbol); } - return (type.getBaseTypes() ?? []).some(superType => forTypeOrAnySupertype(context, superType, predicate)); + const baseTypes = type.getBaseTypes(); + if (!baseTypes) return false; + return baseTypes.some(superType => forTypeOrAnySupertype(context, superType, predicate)); } export function isArrayType(context: TransformationContext, type: ts.Type): boolean { return forTypeOrAnySupertype(context, type, t => isExplicitArrayType(context, t)); } -export function isFunctionType(context: TransformationContext, type: ts.Type): boolean { - const typeNode = context.checker.typeToTypeNode(type, undefined, ts.NodeBuilderFlags.InTypeAlias); - return typeNode !== undefined && ts.isFunctionTypeNode(typeNode); +export function isAlwaysArrayType(context: TransformationContext, type: ts.Type): boolean { + return forTypeOrAnySupertype(context, type, t => isAlwaysExplicitArrayType(context, t)); +} + +export function isFunctionType(type: ts.Type): boolean { + return type.getCallSignatures().length > 0; +} + +export function canBeFalsy(context: TransformationContext, type: ts.Type): boolean { + const strictNullChecks = context.options.strict === true || context.options.strictNullChecks === true; + if (!strictNullChecks && !type.isLiteral()) return true; + const falsyFlags = + ts.TypeFlags.Boolean | + ts.TypeFlags.BooleanLiteral | + ts.TypeFlags.Never | + ts.TypeFlags.Void | + ts.TypeFlags.Unknown | + ts.TypeFlags.Any | + ts.TypeFlags.Undefined | + ts.TypeFlags.Null; + return typeCanHaveSomeOfFlags(context, type, falsyFlags); +} + +export function canBeFalsyWhenNotNull(context: TransformationContext, type: ts.Type): boolean { + const falsyFlags = + ts.TypeFlags.Boolean | + ts.TypeFlags.BooleanLiteral | + ts.TypeFlags.Never | + ts.TypeFlags.Void | + ts.TypeFlags.Unknown | + ts.TypeFlags.Any; + return typeCanHaveSomeOfFlags(context, type, falsyFlags); } diff --git a/src/transformation/visitors/access.ts b/src/transformation/visitors/access.ts index a41c4462f..7c96bdcd8 100644 --- a/src/transformation/visitors/access.ts +++ b/src/transformation/visitors/access.ts @@ -3,104 +3,217 @@ import * as lua from "../../LuaAST"; import { transformBuiltinPropertyAccessExpression } from "../builtins"; import { FunctionVisitor, TransformationContext } from "../context"; import { AnnotationKind, getTypeAnnotations } from "../utils/annotations"; -import { annotationRemoved, invalidMultiReturnAccess, optionalChainingNotSupported } from "../utils/diagnostics"; -import { addToNumericExpression } from "../utils/lua-ast"; +import { + invalidCallExtensionUse, + invalidMultiReturnAccess, + unsupportedOptionalCompileMembersOnly, +} from "../utils/diagnostics"; +import { getExtensionKindForNode } from "../utils/language-extensions"; +import { addToNumericExpression, createExportsIdentifier } from "../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; import { isArrayType, isNumberType, isStringType } from "../utils/typescript"; import { tryGetConstEnumValue } from "./enum"; -import { returnsMultiType } from "./language-extensions/multi"; - -export function transformElementAccessArgument( +import { transformOrderedExpressions } from "./expression-list"; +import { callExtensions } from "./language-extensions/call-extension"; +import { isMultiReturnCall, returnsMultiType } from "./language-extensions/multi"; +import { + transformOptionalChainWithCapture, + ExpressionWithThisValue, + isOptionalContinuation, + captureThisValue, +} from "./optional-chaining"; +import { SyntaxKind } from "typescript"; +import { getCustomNameFromSymbol } from "./identifier"; +import { getSymbolExportScope, isSymbolExported } from "../utils/export"; + +function addOneToArrayAccessArgument( context: TransformationContext, - node: ts.ElementAccessExpression + node: ts.ElementAccessExpression, + index: lua.Expression ): lua.Expression { - const index = context.transformExpression(node.argumentExpression); - const type = context.checker.getTypeAtLocation(node.expression); const argumentType = context.checker.getTypeAtLocation(node.argumentExpression); if (isArrayType(context, type) && isNumberType(context, argumentType)) { return addToNumericExpression(index, 1); } - return index; } -export const transformElementAccessExpression: FunctionVisitor = (node, context) => { +export function transformElementAccessArgument( + context: TransformationContext, + node: ts.ElementAccessExpression +): lua.Expression { + const index = context.transformExpression(node.argumentExpression); + return addOneToArrayAccessArgument(context, node, index); +} + +export const transformElementAccessExpression: FunctionVisitor = (node, context) => + transformElementAccessExpressionWithCapture(context, node, undefined).expression; +export function transformElementAccessExpressionWithCapture( + context: TransformationContext, + node: ts.ElementAccessExpression, + thisValueCapture: lua.Identifier | undefined +): ExpressionWithThisValue { const constEnumValue = tryGetConstEnumValue(context, node); if (constEnumValue) { - return constEnumValue; + return { expression: constEnumValue }; } - const table = context.transformExpression(node.expression); + if (ts.isOptionalChain(node)) { + return transformOptionalChainWithCapture(context, node, thisValueCapture); + } + + const [table, accessExpression] = transformOrderedExpressions(context, [node.expression, node.argumentExpression]); const type = context.checker.getTypeAtLocation(node.expression); const argumentType = context.checker.getTypeAtLocation(node.argumentExpression); if (isStringType(context, type) && isNumberType(context, argumentType)) { - const index = context.transformExpression(node.argumentExpression); - return transformLuaLibFunction(context, LuaLibFeature.StringAccess, node, table, index); + // strings are not callable, so ignore thisValueCapture + return { + expression: transformLuaLibFunction(context, LuaLibFeature.StringAccess, node, table, accessExpression), + }; } - const accessExpression = transformElementAccessArgument(context, node); + const updatedAccessExpression = addOneToArrayAccessArgument(context, node, accessExpression); - if (ts.isCallExpression(node.expression) && returnsMultiType(context, node.expression)) { + if (isMultiReturnCall(context, node.expression)) { const accessType = context.checker.getTypeAtLocation(node.argumentExpression); if (!isNumberType(context, accessType)) { context.diagnostics.push(invalidMultiReturnAccess(node)); } + const canOmitSelect = ts.isNumericLiteral(node.argumentExpression) && node.argumentExpression.text === "0"; + if (canOmitSelect) { + // wrapping in parenthesis ensures only the first return value is used + // https://www.lua.org/manual/5.1/manual.html#2.5 + return { expression: lua.createParenthesizedExpression(table) }; + } + const selectIdentifier = lua.createIdentifier("select"); - const selectCall = lua.createCallExpression(selectIdentifier, [accessExpression, table]); - return selectCall; + return { expression: lua.createCallExpression(selectIdentifier, [updatedAccessExpression, table]) }; } - return lua.createTableIndexExpression(table, accessExpression, node); -}; + if (thisValueCapture) { + const thisValue = captureThisValue(context, table, thisValueCapture, node.expression); + return { + expression: lua.createTableIndexExpression(thisValue, updatedAccessExpression, node), + thisValue, + }; + } + return { expression: lua.createTableIndexExpression(table, updatedAccessExpression, node) }; +} -export const transformPropertyAccessExpression: FunctionVisitor = (node, context) => { - const property = node.name.text; +export const transformPropertyAccessExpression: FunctionVisitor = (node, context) => + transformPropertyAccessExpressionWithCapture(context, node, undefined).expression; +export function transformPropertyAccessExpressionWithCapture( + context: TransformationContext, + node: ts.PropertyAccessExpression, + thisValueCapture: lua.Identifier | undefined +): ExpressionWithThisValue { const type = context.checker.getTypeAtLocation(node.expression); + const isOptionalLeft = isOptionalContinuation(node.expression); - const annotations = getTypeAnnotations(type); - - if (annotations.has(AnnotationKind.LuaTable)) { - context.diagnostics.push(annotationRemoved(node, AnnotationKind.LuaTable)); - } - - if (ts.isOptionalChain(node)) { - context.diagnostics.push(optionalChainingNotSupported(node)); + let property = node.name.text; + const symbol = context.checker.getSymbolAtLocation(node.name); + const customName = getCustomNameFromSymbol(context, symbol); + if (customName) { + property = customName; } const constEnumValue = tryGetConstEnumValue(context, node); if (constEnumValue) { - return constEnumValue; - } - - const builtinResult = transformBuiltinPropertyAccessExpression(context, node); - if (builtinResult) { - return builtinResult; + return { expression: constEnumValue }; } if (ts.isCallExpression(node.expression) && returnsMultiType(context, node.expression)) { context.diagnostics.push(invalidMultiReturnAccess(node)); } + if (ts.isOptionalChain(node)) { + return transformOptionalChainWithCapture(context, node, thisValueCapture); + } + // Do not output path for member only enums + const annotations = getTypeAnnotations(type); if (annotations.has(AnnotationKind.CompileMembersOnly)) { + if (isOptionalLeft) { + context.diagnostics.push(unsupportedOptionalCompileMembersOnly(node)); + } + if (ts.isPropertyAccessExpression(node.expression)) { // in case of ...x.enum.y transform to ...x.y - return lua.createTableIndexExpression( + const expression = lua.createTableIndexExpression( context.transformExpression(node.expression.expression), lua.createStringLiteral(property), node ); + return { expression }; } else { - return lua.createIdentifier(property, node); + // Check if we need to account for enum being exported int his file + if ( + isSymbolExported(context, type.symbol) && + getSymbolExportScope(context, type.symbol) === node.expression.getSourceFile() + ) { + return { + expression: lua.createTableIndexExpression( + createExportsIdentifier(), + lua.createStringLiteral(property), + node + ), + }; + } else { + return { expression: lua.createIdentifier(property, node) }; + } } } - const callPath = context.transformExpression(node.expression); - return lua.createTableIndexExpression(callPath, lua.createStringLiteral(property), node); -}; + const builtinResult = transformBuiltinPropertyAccessExpression(context, node); + if (builtinResult) { + // Ignore thisValueCapture. + // This assumes that nothing returned by builtin property accesses are callable. + // If this assumption is no longer true, this may need to be updated. + return { expression: builtinResult }; + } + + if ( + ts.isIdentifier(node.expression) && + node.parent && + (!ts.isCallExpression(node.parent) || node.parent.expression !== node) + ) { + // Check if this is a method call extension that is not used as a call + const extensionType = getExtensionKindForNode(context, node); + if (extensionType && callExtensions.has(extensionType)) { + context.diagnostics.push(invalidCallExtensionUse(node)); + } + } + + const table = context.transformExpression(node.expression); + + if (thisValueCapture) { + const thisValue = captureThisValue(context, table, thisValueCapture, node.expression); + const expression = lua.createTableIndexExpression(thisValue, lua.createStringLiteral(property), node); + return { + expression, + thisValue, + }; + } + if (node.expression.kind === SyntaxKind.SuperKeyword) { + const symbol = context.checker.getSymbolAtLocation(node); + if (symbol && symbol.flags & ts.SymbolFlags.GetAccessor) { + return { + expression: transformLuaLibFunction( + context, + LuaLibFeature.DescriptorGet, + node, + lua.createIdentifier("self"), + table, + lua.createStringLiteral(property) + ), + }; + } + } + return { expression: lua.createTableIndexExpression(table, lua.createStringLiteral(property), node) }; +} export const transformQualifiedName: FunctionVisitor = (node, context) => { const right = lua.createStringLiteral(node.right.text, node.right); diff --git a/src/transformation/visitors/async-await.ts b/src/transformation/visitors/async-await.ts new file mode 100644 index 000000000..8063312c0 --- /dev/null +++ b/src/transformation/visitors/async-await.ts @@ -0,0 +1,34 @@ +import * as ts from "typescript"; +import * as lua from "../../LuaAST"; +import { FunctionVisitor, TransformationContext } from "../context"; +import { awaitMustBeInAsyncFunction } from "../utils/diagnostics"; +import { importLuaLibFeature, LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; +import { isInAsyncFunction } from "../utils/typescript"; + +export const transformAwaitExpression: FunctionVisitor = (node, context) => { + // Check if await is inside an async function, it is not allowed at top level or in non-async functions + if (!isInAsyncFunction(node)) { + context.diagnostics.push(awaitMustBeInAsyncFunction(node)); + } + + const expression = context.transformExpression(node.expression); + return transformLuaLibFunction(context, LuaLibFeature.Await, node, expression); +}; + +export function isAsyncFunction(declaration: ts.FunctionLikeDeclaration): boolean { + return declaration.modifiers?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword) ?? false; +} + +export function wrapInAsyncAwaiter( + context: TransformationContext, + statements: lua.Statement[], + includeResolveParameter = true +): lua.CallExpression { + importLuaLibFeature(context, LuaLibFeature.Await); + + const parameters = includeResolveParameter ? [lua.createIdentifier("____awaiter_resolve")] : []; + + return lua.createCallExpression(lua.createIdentifier("__TS__AsyncAwaiter"), [ + lua.createFunctionExpression(lua.createBlock(statements), parameters), + ]); +} diff --git a/src/transformation/visitors/binary-expression/assignments.ts b/src/transformation/visitors/binary-expression/assignments.ts index c8a1cd907..73e6d9dd1 100644 --- a/src/transformation/visitors/binary-expression/assignments.ts +++ b/src/transformation/visitors/binary-expression/assignments.ts @@ -1,32 +1,52 @@ import * as ts from "typescript"; +import { SyntaxKind } from "typescript"; import * as lua from "../../../LuaAST"; -import { cast } from "../../../utils"; import { TransformationContext } from "../../context"; -import { isTupleReturnCall } from "../../utils/annotations"; import { validateAssignment } from "../../utils/assignment-validation"; import { createExportedIdentifier, getDependenciesOfSymbol, isSymbolExported } from "../../utils/export"; -import { createUnpackCall, wrapInTable } from "../../utils/lua-ast"; +import { createBoundedUnpackCall, wrapInTable } from "../../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; import { isArrayType, isDestructuringAssignment } from "../../utils/typescript"; -import { transformElementAccessArgument } from "../access"; import { isArrayLength, transformDestructuringAssignment } from "./destructuring-assignments"; import { isMultiReturnCall } from "../language-extensions/multi"; -import { popScope, pushScope, ScopeType } from "../../utils/scope"; -import { - ImmediatelyInvokedFunctionParameters, - transformToImmediatelyInvokedFunctionExpression, -} from "../../utils/transform"; +import { cannotAssignToNodeOfKind, notAllowedOptionalAssignment } from "../../utils/diagnostics"; +import { transformElementAccessArgument } from "../access"; +import { moveToPrecedingTemp, transformExpressionList } from "../expression-list"; +import { transformInPrecedingStatementScope } from "../../utils/preceding-statements"; export function transformAssignmentLeftHandSideExpression( context: TransformationContext, - node: ts.Expression + node: ts.Expression, + rightHasPrecedingStatements?: boolean ): lua.AssignmentLeftHandSideExpression { + // Access expressions need the components of the left side cached in temps before the right side's preceding statements + if (rightHasPrecedingStatements && (ts.isElementAccessExpression(node) || ts.isPropertyAccessExpression(node))) { + let table = context.transformExpression(node.expression); + table = moveToPrecedingTemp(context, table, node.expression); + + let index: lua.Expression; + if (ts.isElementAccessExpression(node)) { + index = transformElementAccessArgument(context, node); + index = moveToPrecedingTemp(context, index, node.argumentExpression); + } else { + index = lua.createStringLiteral(node.name.text, node.name); + } + return lua.createTableIndexExpression(table, index, node); + } + const symbol = context.checker.getSymbolAtLocation(node); const left = context.transformExpression(node); - return lua.isIdentifier(left) && symbol && isSymbolExported(context, symbol) - ? createExportedIdentifier(context, left) - : cast(left, lua.isAssignmentLeftHandSideExpression); + if (lua.isIdentifier(left) && symbol && isSymbolExported(context, symbol)) { + return createExportedIdentifier(context, left); + } + + if (lua.isAssignmentLeftHandSideExpression(left)) { + return left; + } else { + context.diagnostics.push(cannotAssignToNodeOfKind(node, left.kind)); + return lua.createAnonymousIdentifier(); + } } export function transformAssignment( @@ -34,8 +54,14 @@ export function transformAssignment( // TODO: Change type to ts.LeftHandSideExpression? lhs: ts.Expression, right: lua.Expression, + rightHasPrecedingStatements?: boolean, parent?: ts.Expression ): lua.Statement[] { + if (ts.isOptionalChain(lhs)) { + context.diagnostics.push(notAllowedOptionalAssignment(lhs)); + return []; + } + if (isArrayLength(context, lhs)) { const arrayLengthAssignment = lua.createExpressionStatement( transformLuaLibFunction( @@ -50,13 +76,37 @@ export function transformAssignment( return [arrayLengthAssignment]; } - const symbol = ts.isShorthandPropertyAssignment(lhs.parent) - ? context.checker.getShorthandAssignmentValueSymbol(lhs.parent) - : context.checker.getSymbolAtLocation(lhs); + if (ts.isPropertyAccessExpression(lhs) || ts.isElementAccessExpression(lhs)) { + if (lhs.expression.kind === SyntaxKind.SuperKeyword) { + const symbol = context.checker.getSymbolAtLocation(lhs); + if (symbol && symbol.flags & ts.SymbolFlags.SetAccessor) { + return [ + lua.createExpressionStatement( + transformLuaLibFunction( + context, + LuaLibFeature.DescriptorSet, + parent, + lua.createIdentifier("self"), + context.transformExpression(lhs.expression), + ts.isPropertyAccessExpression(lhs) + ? lua.createStringLiteral(lhs.name.text) + : context.transformExpression(lhs.argumentExpression), + right + ) + ), + ]; + } + } + } + + const symbol = + lhs.parent && ts.isShorthandPropertyAssignment(lhs.parent) + ? context.checker.getShorthandAssignmentValueSymbol(lhs.parent) + : context.checker.getSymbolAtLocation(lhs); const dependentSymbols = symbol ? getDependenciesOfSymbol(context, symbol) : []; - const left = transformAssignmentLeftHandSideExpression(context, lhs); + const left = transformAssignmentLeftHandSideExpression(context, lhs, rightHasPrecedingStatements); const rootAssignment = lua.createAssignmentStatement(left, right, lhs.parent); @@ -70,23 +120,41 @@ export function transformAssignment( ]; } +export function transformAssignmentWithRightPrecedingStatements( + context: TransformationContext, + lhs: ts.Expression, + right: lua.Expression, + rightPrecedingStatements: lua.Statement[], + parent?: ts.Expression +): lua.Statement[] { + return [ + ...rightPrecedingStatements, + ...transformAssignment(context, lhs, right, rightPrecedingStatements.length > 0, parent), + ]; +} + function transformDestructuredAssignmentExpression( context: TransformationContext, expression: ts.DestructuringAssignment -): ImmediatelyInvokedFunctionParameters { - const rootIdentifier = lua.createAnonymousIdentifier(expression.left); - - let right = context.transformExpression(expression.right); - if (isTupleReturnCall(context, expression.right) || isMultiReturnCall(context, expression.right)) { +) { + let { precedingStatements: rightPrecedingStatements, result: right } = transformInPrecedingStatementScope( + context, + () => context.transformExpression(expression.right) + ); + context.addPrecedingStatements(rightPrecedingStatements); + if (isMultiReturnCall(context, expression.right)) { right = wrapInTable(right); } - const statements = [ - lua.createVariableDeclarationStatement(rootIdentifier, right), - ...transformDestructuringAssignment(context, expression, rootIdentifier), - ]; + const rightExpr = moveToPrecedingTemp(context, right, expression.right); + const statements = transformDestructuringAssignment( + context, + expression, + rightExpr, + rightPrecedingStatements.length > 0 + ); - return { statements, result: rootIdentifier }; + return { statements, result: rightExpr }; } export function transformAssignmentExpression( @@ -110,56 +178,33 @@ export function transformAssignmentExpression( } if (isDestructuringAssignment(expression)) { - return transformToImmediatelyInvokedFunctionExpression( - context, - () => transformDestructuredAssignmentExpression(context, expression), - expression - ); + const { statements, result } = transformDestructuredAssignmentExpression(context, expression); + context.addPrecedingStatements(statements); + return result; } if (ts.isPropertyAccessExpression(expression.left) || ts.isElementAccessExpression(expression.left)) { - // Left is property/element access: cache result while maintaining order of evaluation - // (function(o, i, v) o[i] = v; return v end)(${objExpression}, ${indexExpression}, ${right}) - const objParameter = lua.createIdentifier("o"); - const indexParameter = lua.createIdentifier("i"); - const valueParameter = lua.createIdentifier("v"); - const indexStatement = lua.createTableIndexExpression(objParameter, indexParameter); - const statements: lua.Statement[] = [ - lua.createAssignmentStatement(indexStatement, valueParameter), - lua.createReturnStatement([valueParameter]), - ]; - const iife = lua.createFunctionExpression(lua.createBlock(statements), [ - objParameter, - indexParameter, - valueParameter, - ]); - pushScope(context, ScopeType.Function); - const objExpression = context.transformExpression(expression.left.expression); - let indexExpression: lua.Expression; - if (ts.isPropertyAccessExpression(expression.left)) { - // Property access - indexExpression = lua.createStringLiteral(expression.left.name.text); - } else { - // Element access - indexExpression = transformElementAccessArgument(context, expression.left); - } + const { precedingStatements, result: right } = transformInPrecedingStatementScope(context, () => + context.transformExpression(expression.right) + ); - const args = [objExpression, indexExpression, context.transformExpression(expression.right)]; - popScope(context); - return lua.createCallExpression(iife, args, expression); - } else { - return transformToImmediatelyInvokedFunctionExpression( + const left = transformAssignmentLeftHandSideExpression( context, - () => { - // Simple assignment - // (function() ${left} = ${right}; return ${left} end)() - const left = context.transformExpression(expression.left); - const right = context.transformExpression(expression.right); - const statements = transformAssignment(context, expression.left, right); - return { statements, result: left }; - }, - expression + expression.left, + precedingStatements.length > 0 ); + + context.addPrecedingStatements(precedingStatements); + const rightExpr = moveToPrecedingTemp(context, right, expression.right); + context.addPrecedingStatements(lua.createAssignmentStatement(left, rightExpr, expression.left)); + return rightExpr; + } else { + // Simple assignment + // ${left} = ${right}; return ${left} + const left = context.transformExpression(expression.left); + const right = context.transformExpression(expression.right); + context.addPrecedingStatements(transformAssignment(context, expression.left, right)); + return left; } } @@ -174,7 +219,9 @@ const canBeTransformedToLuaAssignmentStatement = ( } if (ts.isPropertyAccessExpression(element) || ts.isElementAccessExpression(element)) { - return true; + // Lua's execution order for multi-assignments is not the same as JS's, so we should always + // break these down when the left side may have side effects. + return false; } if (ts.isIdentifier(element)) { @@ -198,13 +245,15 @@ export function transformAssignmentStatement( if (isDestructuringAssignment(expression)) { if (canBeTransformedToLuaAssignmentStatement(context, expression)) { const rightType = context.checker.getTypeAtLocation(expression.right); - let right = context.transformExpression(expression.right); + let right: lua.Expression | lua.Expression[]; - if ( - !(isTupleReturnCall(context, expression.right) || isMultiReturnCall(context, expression.right)) && - isArrayType(context, rightType) - ) { - right = createUnpackCall(context, right, expression.right); + if (ts.isArrayLiteralExpression(expression.right)) { + right = transformExpressionList(context, expression.right.elements); + } else { + right = context.transformExpression(expression.right); + if (!isMultiReturnCall(context, expression.right) && isArrayType(context, rightType)) { + right = createBoundedUnpackCall(context, right, expression.left.elements.length, expression.right); + } } const left = expression.left.elements.map(e => transformAssignmentLeftHandSideExpression(context, e)); @@ -212,17 +261,12 @@ export function transformAssignmentStatement( return [lua.createAssignmentStatement(left, right, expression)]; } - let right = context.transformExpression(expression.right); - if (isTupleReturnCall(context, expression.right) || isMultiReturnCall(context, expression.right)) { - right = wrapInTable(right); - } - - const rootIdentifier = lua.createAnonymousIdentifier(expression.left); - return [ - lua.createVariableDeclarationStatement(rootIdentifier, right), - ...transformDestructuringAssignment(context, expression, rootIdentifier), - ]; + const { statements } = transformDestructuredAssignmentExpression(context, expression); + return statements; } else { - return transformAssignment(context, expression.left, context.transformExpression(expression.right)); + const { precedingStatements, result: right } = transformInPrecedingStatementScope(context, () => + context.transformExpression(expression.right) + ); + return transformAssignmentWithRightPrecedingStatements(context, expression.left, right, precedingStatements); } } diff --git a/src/transformation/visitors/binary-expression/bit.ts b/src/transformation/visitors/binary-expression/bit.ts index e284b2ea0..5ae876630 100644 --- a/src/transformation/visitors/binary-expression/bit.ts +++ b/src/transformation/visitors/binary-expression/bit.ts @@ -49,6 +49,7 @@ function transformBitOperatorToLuaOperator( return lua.SyntaxKind.BitwiseLeftShiftOperator; case ts.SyntaxKind.GreaterThanGreaterThanToken: context.diagnostics.push(unsupportedRightShiftOperator(node)); + return lua.SyntaxKind.BitwiseRightShiftOperator; case ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken: return lua.SyntaxKind.BitwiseRightShiftOperator; } @@ -63,8 +64,10 @@ export function transformBinaryBitOperation( ): lua.Expression { switch (context.luaTarget) { case LuaTarget.Universal: + case LuaTarget.Lua50: case LuaTarget.Lua51: - context.diagnostics.push(unsupportedForTarget(node, "Bitwise operations", LuaTarget.Lua51)); + context.diagnostics.push(unsupportedForTarget(node, "Bitwise operations", context.luaTarget)); + return transformBinaryBitLibOperation(node, left, right, operator, "bit"); case LuaTarget.LuaJIT: return transformBinaryBitLibOperation(node, left, right, operator, "bit"); @@ -72,6 +75,22 @@ export function transformBinaryBitOperation( case LuaTarget.Lua52: return transformBinaryBitLibOperation(node, left, right, operator, "bit32"); default: + // Lua 5.3+ `>>` is arithmetic (sign-extending), but TS `>>>` is logical (zero-fill). + // Emit `(left & 0xFFFFFFFF) >> right` to convert to unsigned 32-bit first. + if (operator === ts.SyntaxKind.GreaterThanGreaterThanGreaterThanToken) { + const mask = lua.createBinaryExpression( + left, + lua.createNumericLiteral(0xffffffff, node), + lua.SyntaxKind.BitwiseAndOperator, + node + ); + return lua.createBinaryExpression( + lua.createParenthesizedExpression(mask, node), + right, + lua.SyntaxKind.BitwiseRightShiftOperator, + node + ); + } const luaOperator = transformBitOperatorToLuaOperator(context, node, operator); return lua.createBinaryExpression(left, right, luaOperator, node); } @@ -107,8 +126,10 @@ export function transformUnaryBitOperation( ): lua.Expression { switch (context.luaTarget) { case LuaTarget.Universal: + case LuaTarget.Lua50: case LuaTarget.Lua51: - context.diagnostics.push(unsupportedForTarget(node, "Bitwise operations", LuaTarget.Lua51)); + context.diagnostics.push(unsupportedForTarget(node, "Bitwise operations", context.luaTarget)); + return transformUnaryBitLibOperation(node, expression, operator, "bit"); case LuaTarget.LuaJIT: return transformUnaryBitLibOperation(node, expression, operator, "bit"); diff --git a/src/transformation/visitors/binary-expression/compound.ts b/src/transformation/visitors/binary-expression/compound.ts index febd7681e..4532fe56c 100644 --- a/src/transformation/visitors/binary-expression/compound.ts +++ b/src/transformation/visitors/binary-expression/compound.ts @@ -1,39 +1,27 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; -import { cast, assertNever } from "../../../utils"; +import { assertNever } from "../../../utils"; import { TransformationContext } from "../../context"; -import { - ImmediatelyInvokedFunctionParameters, - transformToImmediatelyInvokedFunctionExpression, -} from "../../utils/transform"; -import { isArrayType, isExpressionWithEvaluationEffect } from "../../utils/typescript"; -import { transformBinaryOperation } from "../binary-expression"; -import { transformAssignment } from "./assignments"; - -// If expression is property/element access with possible effects from being evaluated, returns separated object and index expressions. -export function parseAccessExpressionWithEvaluationEffects( - context: TransformationContext, - node: ts.Expression -): [ts.Expression, ts.Expression] | [] { - if ( - ts.isElementAccessExpression(node) && - (isExpressionWithEvaluationEffect(node.expression) || isExpressionWithEvaluationEffect(node.argumentExpression)) - ) { - const type = context.checker.getTypeAtLocation(node.expression); - if (isArrayType(context, type)) { - // Offset arrays by one - const oneLit = ts.factory.createNumericLiteral("1"); - const exp = ts.factory.createParenthesizedExpression(node.argumentExpression); - const addExp = ts.factory.createBinaryExpression(exp, ts.SyntaxKind.PlusToken, oneLit); - return [node.expression, addExp]; - } else { - return [node.expression, node.argumentExpression]; - } - } else if (ts.isPropertyAccessExpression(node) && isExpressionWithEvaluationEffect(node.expression)) { - return [node.expression, ts.factory.createStringLiteral(node.name.text)]; - } +import { transformInPrecedingStatementScope, WithPrecedingStatements } from "../../utils/preceding-statements"; +import { transformBinaryOperation } from "./index"; +import { transformAssignmentWithRightPrecedingStatements } from "./assignments"; +import { isArrayLength } from "./destructuring-assignments"; +import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; +import { cannotAssignToNodeOfKind } from "../../utils/diagnostics"; - return []; +function isLuaExpressionWithSideEffect(expression: lua.Expression) { + return !(lua.isLiteral(expression) || lua.isIdentifier(expression)); +} + +function shouldCacheTableIndexExpressions( + expression: lua.TableIndexExpression, + rightPrecedingStatements: lua.Statement[] +) { + return ( + isLuaExpressionWithSideEffect(expression.table) || + isLuaExpressionWithSideEffect(expression.index) || + rightPrecedingStatements.length > 0 + ); } // TODO: `as const` doesn't work on enum members @@ -78,82 +66,153 @@ export const isCompoundAssignmentToken = (token: ts.BinaryOperator): token is ts export const unwrapCompoundAssignmentToken = (token: ts.CompoundAssignmentOperator): CompoundAssignmentToken => compoundToAssignmentTokens[token]; -export function transformCompoundAssignment( +function transformCompoundAssignment( context: TransformationContext, expression: ts.Expression, lhs: ts.Expression, rhs: ts.Expression, operator: CompoundAssignmentToken, isPostfix: boolean -): ImmediatelyInvokedFunctionParameters { - const left = cast(context.transformExpression(lhs), lua.isAssignmentLeftHandSideExpression); - const right = context.transformExpression(rhs); +): WithPrecedingStatements { + if (isArrayLength(context, lhs)) { + const { precedingStatements, result: lengthSetterStatement } = transformCompoundLengthSetter( + context, + expression, + lhs, + rhs, + operator + ); + + return { precedingStatements, result: lengthSetterStatement.expression }; + } + + const left = context.transformExpression(lhs); + if (!lua.isAssignmentLeftHandSideExpression(left)) { + context.diagnostics.push(cannotAssignToNodeOfKind(expression, left.kind)); + return { precedingStatements: [], result: left }; + } + + const { precedingStatements: rightPrecedingStatements, result: right } = transformInPrecedingStatementScope( + context, + () => context.transformExpression(rhs) + ); - const [objExpression, indexExpression] = parseAccessExpressionWithEvaluationEffects(context, lhs); - if (objExpression && indexExpression) { + if (lua.isTableIndexExpression(left)) { // Complex property/element accesses need to cache object/index expressions to avoid repeating side-effects // local __obj, __index = ${objExpression}, ${indexExpression}; - const obj = lua.createIdentifier("____obj"); - const index = lua.createIdentifier("____index"); - const objAndIndexDeclaration = lua.createVariableDeclarationStatement( - [obj, index], - [context.transformExpression(objExpression), context.transformExpression(indexExpression)] - ); + const obj = context.createTempNameForLuaExpression(left.table); + const index = context.createTempNameForLuaExpression(left.index); + + const objAndIndexDeclaration = lua.createVariableDeclarationStatement([obj, index], [left.table, left.index]); const accessExpression = lua.createTableIndexExpression(obj, index); - const tmp = lua.createIdentifier("____tmp"); - let tmpDeclaration: lua.VariableDeclarationStatement; - let assignStatement: lua.AssignmentStatement; + const tmp = context.createTempNameForLuaExpression(left); if (isPostfix) { // local ____tmp = ____obj[____index]; // ____obj[____index] = ____tmp ${replacementOperator} ${right}; - tmpDeclaration = lua.createVariableDeclarationStatement(tmp, accessExpression); - const operatorExpression = transformBinaryOperation(context, tmp, right, operator, expression); - assignStatement = lua.createAssignmentStatement(accessExpression, operatorExpression); + // return ____tmp + const tmpDeclaration = lua.createVariableDeclarationStatement(tmp, accessExpression); + const { precedingStatements, result: operatorExpression } = transformBinaryOperation( + context, + tmp, + right, + rightPrecedingStatements, + operator, + expression + ); + const assignStatement = lua.createAssignmentStatement(accessExpression, operatorExpression); + return { + precedingStatements: [objAndIndexDeclaration, ...precedingStatements, tmpDeclaration, assignStatement], + result: tmp, + }; } else { + if (isSetterSkippingCompoundAssignmentOperator(operator)) { + return { + precedingStatements: [ + objAndIndexDeclaration, + ...transformSetterSkippingCompoundAssignment( + accessExpression, + operator, + right, + rightPrecedingStatements + ), + ], + result: left, + }; + } // local ____tmp = ____obj[____index] ${replacementOperator} ${right}; // ____obj[____index] = ____tmp; - const operatorExpression = transformBinaryOperation(context, accessExpression, right, operator, expression); - tmpDeclaration = lua.createVariableDeclarationStatement(tmp, operatorExpression); - assignStatement = lua.createAssignmentStatement(accessExpression, tmp); + // return ____tmp + const { precedingStatements, result: operatorExpression } = transformBinaryOperation( + context, + accessExpression, + right, + rightPrecedingStatements, + operator, + expression + ); + const tmpDeclaration = lua.createVariableDeclarationStatement(tmp, operatorExpression); + const assignStatement = lua.createAssignmentStatement(accessExpression, tmp); + return { + precedingStatements: [objAndIndexDeclaration, ...precedingStatements, tmpDeclaration, assignStatement], + result: tmp, + }; } - // return ____tmp - return { statements: [objAndIndexDeclaration, tmpDeclaration, assignStatement], result: tmp }; } else if (isPostfix) { // Postfix expressions need to cache original value in temp // local ____tmp = ${left}; // ${left} = ____tmp ${replacementOperator} ${right}; // return ____tmp - const tmpIdentifier = lua.createIdentifier("____tmp"); + const tmpIdentifier = context.createTempNameForLuaExpression(left); const tmpDeclaration = lua.createVariableDeclarationStatement(tmpIdentifier, left); - const operatorExpression = transformBinaryOperation(context, tmpIdentifier, right, operator, expression); - const assignStatements = transformAssignment(context, lhs, operatorExpression); - return { statements: [tmpDeclaration, ...assignStatements], result: tmpIdentifier }; - } else if (ts.isPropertyAccessExpression(lhs) || ts.isElementAccessExpression(lhs)) { - // Simple property/element access expressions need to cache in temp to avoid double-evaluation - // local ____tmp = ${left} ${replacementOperator} ${right}; - // ${left} = ____tmp; - // return ____tmp - const tmpIdentifier = lua.createIdentifier("____tmp"); - const operatorExpression = transformBinaryOperation(context, left, right, operator, expression); - const tmpDeclaration = lua.createVariableDeclarationStatement(tmpIdentifier, operatorExpression); - const assignStatements = transformAssignment(context, lhs, tmpIdentifier); - - if (isSetterSkippingCompoundAssignmentOperator(operator)) { - const statements = [ - tmpDeclaration, - ...transformSetterSkippingCompoundAssignment(context, tmpIdentifier, operator, rhs), - ]; - return { statements, result: tmpIdentifier }; + const { precedingStatements, result: operatorExpression } = transformBinaryOperation( + context, + tmpIdentifier, + right, + rightPrecedingStatements, + operator, + expression + ); + const assignStatements = transformAssignmentWithRightPrecedingStatements( + context, + lhs, + operatorExpression, + rightPrecedingStatements + ); + return { + precedingStatements: [tmpDeclaration, ...precedingStatements, ...assignStatements], + result: tmpIdentifier, + }; + } else { + if (rightPrecedingStatements.length > 0 && isSetterSkippingCompoundAssignmentOperator(operator)) { + return { + precedingStatements: transformSetterSkippingCompoundAssignment( + left, + operator, + right, + rightPrecedingStatements + ), + result: left, + }; } - return { statements: [tmpDeclaration, ...assignStatements], result: tmpIdentifier }; - } else { // Simple expressions - // ${left} = ${right}; return ${right} - const operatorExpression = transformBinaryOperation(context, left, right, operator, expression); - const statements = transformAssignment(context, lhs, operatorExpression); - return { statements, result: left }; + // ${left} = ${left} ${operator} ${right} + const { precedingStatements, result: operatorExpression } = transformBinaryOperation( + context, + left, + right, + rightPrecedingStatements, + operator, + expression + ); + const statements = transformAssignmentWithRightPrecedingStatements( + context, + lhs, + operatorExpression, + precedingStatements + ); + return { precedingStatements: statements, result: left }; } } @@ -165,12 +224,17 @@ export function transformCompoundAssignmentExpression( rhs: ts.Expression, operator: CompoundAssignmentToken, isPostfix: boolean -): lua.CallExpression { - return transformToImmediatelyInvokedFunctionExpression( +): lua.Expression { + const { precedingStatements, result } = transformCompoundAssignment( context, - () => transformCompoundAssignment(context, expression, lhs, rhs, operator, isPostfix), - expression + expression, + lhs, + rhs, + operator, + isPostfix ); + context.addPrecedingStatements(precedingStatements); + return result; } export function transformCompoundAssignmentStatement( @@ -180,42 +244,83 @@ export function transformCompoundAssignmentStatement( rhs: ts.Expression, operator: CompoundAssignmentToken ): lua.Statement[] { - const left = cast(context.transformExpression(lhs), lua.isAssignmentLeftHandSideExpression); - const right = context.transformExpression(rhs); + if (isArrayLength(context, lhs)) { + const { precedingStatements, result: lengthSetterStatement } = transformCompoundLengthSetter( + context, + node, + lhs, + rhs, + operator + ); - const [objExpression, indexExpression] = parseAccessExpressionWithEvaluationEffects(context, lhs); - if (objExpression && indexExpression) { + return [...precedingStatements, lengthSetterStatement]; + } + + const left = context.transformExpression(lhs); + if (!lua.isAssignmentLeftHandSideExpression(left)) { + context.diagnostics.push(cannotAssignToNodeOfKind(node, left.kind)); + return []; + } + + const { precedingStatements: rightPrecedingStatements, result: right } = transformInPrecedingStatementScope( + context, + () => context.transformExpression(rhs) + ); + + if (lua.isTableIndexExpression(left) && shouldCacheTableIndexExpressions(left, rightPrecedingStatements)) { // Complex property/element accesses need to cache object/index expressions to avoid repeating side-effects // local __obj, __index = ${objExpression}, ${indexExpression}; // ____obj[____index] = ____obj[____index] ${replacementOperator} ${right}; - const obj = lua.createIdentifier("____obj"); - const index = lua.createIdentifier("____index"); - const objAndIndexDeclaration = lua.createVariableDeclarationStatement( - [obj, index], - [context.transformExpression(objExpression), context.transformExpression(indexExpression)] - ); + const obj = context.createTempNameForLuaExpression(left.table); + const index = context.createTempNameForLuaExpression(left.index); + + const objAndIndexDeclaration = lua.createVariableDeclarationStatement([obj, index], [left.table, left.index]); const accessExpression = lua.createTableIndexExpression(obj, index); if (isSetterSkippingCompoundAssignmentOperator(operator)) { return [ objAndIndexDeclaration, - ...transformSetterSkippingCompoundAssignment(context, accessExpression, operator, rhs, node), + ...transformSetterSkippingCompoundAssignment( + accessExpression, + operator, + right, + rightPrecedingStatements, + node + ), ]; } - const operatorExpression = transformBinaryOperation(context, accessExpression, right, operator, node); + const { precedingStatements: rightPrecedingStatements2, result: operatorExpression } = transformBinaryOperation( + context, + accessExpression, + right, + rightPrecedingStatements, + operator, + node + ); const assignStatement = lua.createAssignmentStatement(accessExpression, operatorExpression); - return [objAndIndexDeclaration, assignStatement]; + return [objAndIndexDeclaration, ...rightPrecedingStatements2, assignStatement]; } else { if (isSetterSkippingCompoundAssignmentOperator(operator)) { - const luaLhs = context.transformExpression(lhs) as lua.AssignmentLeftHandSideExpression; - return transformSetterSkippingCompoundAssignment(context, luaLhs, operator, rhs, node); + return transformSetterSkippingCompoundAssignment(left, operator, right, rightPrecedingStatements, node); } // Simple statements // ${left} = ${left} ${replacementOperator} ${right} - const operatorExpression = transformBinaryOperation(context, left, right, operator, node); - return transformAssignment(context, lhs, operatorExpression); + const { precedingStatements: rightPrecedingStatements2, result: operatorExpression } = transformBinaryOperation( + context, + left, + right, + rightPrecedingStatements, + operator, + node + ); + return transformAssignmentWithRightPrecedingStatements( + context, + lhs, + operatorExpression, + rightPrecedingStatements2 + ); } } @@ -237,10 +342,10 @@ function isSetterSkippingCompoundAssignmentOperator( } function transformSetterSkippingCompoundAssignment( - context: TransformationContext, lhs: lua.AssignmentLeftHandSideExpression, operator: SetterSkippingCompoundAssignmentOperator, - rhs: ts.Expression, + right: lua.Expression, + rightPrecedingStatements: lua.Statement[], node?: ts.Node ): lua.Statement[] { // These assignments have the form 'if x then y = z', figure out what condition x is first. @@ -260,9 +365,38 @@ function transformSetterSkippingCompoundAssignment( return [ lua.createIfStatement( condition, - lua.createBlock([lua.createAssignmentStatement(lhs, context.transformExpression(rhs))]), + lua.createBlock([...rightPrecedingStatements, lua.createAssignmentStatement(lhs, right, node)]), undefined, node ), ]; } + +function transformCompoundLengthSetter( + context: TransformationContext, + node: ts.Node, + lhs: ts.PropertyAccessExpression | ts.ElementAccessExpression, + rhs: ts.Expression, + operator: CompoundAssignmentToken +): WithPrecedingStatements { + const { precedingStatements: rightPrecedingStatements, result: right } = transformInPrecedingStatementScope( + context, + () => context.transformExpression(rhs) + ); + const table = context.transformExpression(lhs.expression); + const lengthExpression = lua.createUnaryExpression(table, lua.SyntaxKind.LengthOperator, lhs); + const { precedingStatements, result: operatorExpression } = transformBinaryOperation( + context, + lengthExpression, + right, + rightPrecedingStatements, + operator, + node + ); + + const arrayLengthAssignment = lua.createExpressionStatement( + transformLuaLibFunction(context, LuaLibFeature.ArraySetLength, node, table, operatorExpression) + ); + + return { precedingStatements, result: arrayLengthAssignment }; +} diff --git a/src/transformation/visitors/binary-expression/destructuring-assignments.ts b/src/transformation/visitors/binary-expression/destructuring-assignments.ts index 567de4e6f..aa7042727 100644 --- a/src/transformation/visitors/binary-expression/destructuring-assignments.ts +++ b/src/transformation/visitors/binary-expression/destructuring-assignments.ts @@ -1,9 +1,12 @@ import * as ts from "typescript"; +import { transformBinaryOperation } from "."; import * as lua from "../../../LuaAST"; -import { assertNever } from "../../../utils"; +import { assertNever, cast } from "../../../utils"; import { TransformationContext } from "../../context"; import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; +import { transformInPrecedingStatementScope } from "../../utils/preceding-statements"; import { isArrayType, isAssignmentPattern } from "../../utils/typescript"; +import { moveToPrecedingTemp } from "../expression-list"; import { transformPropertyName } from "../literal"; import { transformAssignment, @@ -36,28 +39,31 @@ export function isArrayLength( export function transformDestructuringAssignment( context: TransformationContext, node: ts.DestructuringAssignment, - root: lua.Expression + root: lua.Expression, + rightHasPrecedingStatements: boolean ): lua.Statement[] { - return transformAssignmentPattern(context, node.left, root); + return transformAssignmentPattern(context, node.left, root, rightHasPrecedingStatements); } export function transformAssignmentPattern( context: TransformationContext, node: ts.AssignmentPattern, - root: lua.Expression + root: lua.Expression, + rightHasPrecedingStatements: boolean ): lua.Statement[] { switch (node.kind) { case ts.SyntaxKind.ObjectLiteralExpression: - return transformObjectLiteralAssignmentPattern(context, node, root); + return transformObjectLiteralAssignmentPattern(context, node, root, rightHasPrecedingStatements); case ts.SyntaxKind.ArrayLiteralExpression: - return transformArrayLiteralAssignmentPattern(context, node, root); + return transformArrayLiteralAssignmentPattern(context, node, root, rightHasPrecedingStatements); } } function transformArrayLiteralAssignmentPattern( context: TransformationContext, node: ts.ArrayLiteralExpression, - root: lua.Expression + root: lua.Expression, + rightHasPrecedingStatements: boolean ): lua.Statement[] { return node.elements.flatMap((element, index) => { const indexedRoot = lua.createTableIndexExpression(root, lua.createNumericLiteral(index + 1), element); @@ -67,16 +73,18 @@ function transformArrayLiteralAssignmentPattern( return transformObjectLiteralAssignmentPattern( context, element as ts.ObjectLiteralExpression, - indexedRoot + indexedRoot, + rightHasPrecedingStatements ); case ts.SyntaxKind.ArrayLiteralExpression: return transformArrayLiteralAssignmentPattern( context, element as ts.ArrayLiteralExpression, - indexedRoot + indexedRoot, + rightHasPrecedingStatements ); case ts.SyntaxKind.BinaryExpression: - const assignedVariable = lua.createIdentifier("____bindingAssignmentValue"); + const assignedVariable = context.createTempNameForLuaExpression(indexedRoot); const assignedVariableDeclaration = lua.createVariableDeclarationStatement( assignedVariable, @@ -89,11 +97,17 @@ function transformArrayLiteralAssignmentPattern( lua.SyntaxKind.EqualityOperator ); - const defaultAssignmentStatements = transformAssignment( - context, - (element as ts.BinaryExpression).left, - context.transformExpression((element as ts.BinaryExpression).right) - ); + const { precedingStatements: defaultPrecedingStatements, result: defaultAssignmentStatements } = + transformInPrecedingStatementScope(context, () => + transformAssignment( + context, + (element as ts.BinaryExpression).left, + context.transformExpression((element as ts.BinaryExpression).right) + ) + ); + + // Keep preceding statements inside if block + defaultAssignmentStatements.unshift(...defaultPrecedingStatements); const elseAssignmentStatements = transformAssignment( context, @@ -111,7 +125,10 @@ function transformArrayLiteralAssignmentPattern( case ts.SyntaxKind.Identifier: case ts.SyntaxKind.PropertyAccessExpression: case ts.SyntaxKind.ElementAccessExpression: - return transformAssignment(context, element, indexedRoot); + const { precedingStatements, result: statements } = transformInPrecedingStatementScope(context, () => + transformAssignment(context, element, indexedRoot, rightHasPrecedingStatements) + ); + return [...precedingStatements, ...statements]; // Keep preceding statements in order case ts.SyntaxKind.SpreadElement: if (index !== node.elements.length - 1) { // TypeScript error @@ -126,7 +143,16 @@ function transformArrayLiteralAssignmentPattern( lua.createNumericLiteral(index) ); - return transformAssignment(context, (element as ts.SpreadElement).expression, restElements); + const { precedingStatements: spreadPrecedingStatements, result: spreadStatements } = + transformInPrecedingStatementScope(context, () => + transformAssignment( + context, + (element as ts.SpreadElement).expression, + restElements, + rightHasPrecedingStatements + ) + ); + return [...spreadPrecedingStatements, ...spreadStatements]; // Keep preceding statements in order case ts.SyntaxKind.OmittedExpression: return []; default: @@ -139,7 +165,8 @@ function transformArrayLiteralAssignmentPattern( function transformObjectLiteralAssignmentPattern( context: TransformationContext, node: ts.ObjectLiteralExpression, - root: lua.Expression + root: lua.Expression, + rightHasPrecedingStatements: boolean ): lua.Statement[] { const result: lua.Statement[] = []; @@ -149,7 +176,7 @@ function transformObjectLiteralAssignmentPattern( result.push(...transformShorthandPropertyAssignment(context, property, root)); break; case ts.SyntaxKind.PropertyAssignment: - result.push(...transformPropertyAssignment(context, property, root)); + result.push(...transformPropertyAssignment(context, property, root, rightHasPrecedingStatements)); break; case ts.SyntaxKind.SpreadAssignment: result.push(...transformSpreadAssignment(context, property, root, node.properties)); @@ -207,7 +234,8 @@ function transformShorthandPropertyAssignment( function transformPropertyAssignment( context: TransformationContext, node: ts.PropertyAssignment, - root: lua.Expression + root: lua.Expression, + rightHasPrecedingStatements: boolean ): lua.Statement[] { const result: lua.Statement[] = []; @@ -216,38 +244,94 @@ function transformPropertyAssignment( const newRootAccess = lua.createTableIndexExpression(root, propertyAccessString); if (ts.isObjectLiteralExpression(node.initializer)) { - return transformObjectLiteralAssignmentPattern(context, node.initializer, newRootAccess); + return transformObjectLiteralAssignmentPattern( + context, + node.initializer, + newRootAccess, + rightHasPrecedingStatements + ); } if (ts.isArrayLiteralExpression(node.initializer)) { - return transformArrayLiteralAssignmentPattern(context, node.initializer, newRootAccess); + return transformArrayLiteralAssignmentPattern( + context, + node.initializer, + newRootAccess, + rightHasPrecedingStatements + ); } } - const leftExpression = ts.isBinaryExpression(node.initializer) ? node.initializer.left : node.initializer; - const variableToExtract = transformPropertyName(context, node.name); - const extractingExpression = lua.createTableIndexExpression(root, variableToExtract); - - const destructureAssignmentStatements = transformAssignment(context, leftExpression, extractingExpression); + context.pushPrecedingStatements(); - result.push(...destructureAssignmentStatements); + let variableToExtract = transformPropertyName(context, node.name); + // Must be evaluated before left's preceding statements + variableToExtract = moveToPrecedingTemp(context, variableToExtract, node.name); + const extractingExpression = lua.createTableIndexExpression(root, variableToExtract); + let destructureAssignmentStatements: lua.Statement[]; if (ts.isBinaryExpression(node.initializer)) { - const assignmentLeftHandSide = context.transformExpression(node.initializer.left); - - const nilCondition = lua.createBinaryExpression( - assignmentLeftHandSide, - lua.createNilLiteral(), - lua.SyntaxKind.EqualityOperator - ); - - const ifBlock = lua.createBlock( - transformAssignmentStatement(context, node.initializer as ts.AssignmentExpression) + if ( + ts.isPropertyAccessExpression(node.initializer.left) || + ts.isElementAccessExpression(node.initializer.left) + ) { + // Access expressions need their table and index expressions cached to preserve execution order + const left = cast(context.transformExpression(node.initializer.left), lua.isTableIndexExpression); + + const rightExpression = node.initializer.right; + const { precedingStatements: defaultPrecedingStatements, result: defaultExpression } = + transformInPrecedingStatementScope(context, () => context.transformExpression(rightExpression)); + + const tableTemp = context.createTempNameForLuaExpression(left.table); + const indexTemp = context.createTempNameForLuaExpression(left.index); + + const tempsDeclaration = lua.createVariableDeclarationStatement( + [tableTemp, indexTemp], + [left.table, left.index] + ); + + // obj[index] = extractingExpression ?? defaultExpression + const { precedingStatements: rightPrecedingStatements, result: rhs } = transformBinaryOperation( + context, + extractingExpression, + defaultExpression, + defaultPrecedingStatements, + ts.SyntaxKind.QuestionQuestionToken, + node.initializer + ); + const assignStatement = lua.createAssignmentStatement( + lua.createTableIndexExpression(tableTemp, indexTemp), + rhs + ); + + destructureAssignmentStatements = [tempsDeclaration, ...rightPrecedingStatements, assignStatement]; + } else { + const assignmentLeftHandSide = context.transformExpression(node.initializer.left); + + const nilCondition = lua.createBinaryExpression( + assignmentLeftHandSide, + lua.createNilLiteral(), + lua.SyntaxKind.EqualityOperator + ); + + const ifBlock = lua.createBlock( + transformAssignmentStatement(context, node.initializer as ts.AssignmentExpression) + ); + + destructureAssignmentStatements = [lua.createIfStatement(nilCondition, ifBlock, undefined, node)]; + } + } else { + destructureAssignmentStatements = transformAssignment( + context, + node.initializer, + extractingExpression, + rightHasPrecedingStatements ); - - result.push(lua.createIfStatement(nilCondition, ifBlock, undefined, node)); } + result.push(...context.popPrecedingStatements()); + result.push(...destructureAssignmentStatements); + return result; } diff --git a/src/transformation/visitors/binary-expression/index.ts b/src/transformation/visitors/binary-expression/index.ts index 0b4533a4a..f2b0acb16 100644 --- a/src/transformation/visitors/binary-expression/index.ts +++ b/src/transformation/visitors/binary-expression/index.ts @@ -1,9 +1,10 @@ import * as ts from "typescript"; +import { LuaTarget } from "../../../CompilerOptions"; import * as lua from "../../../LuaAST"; import { FunctionVisitor, TransformationContext } from "../../context"; import { wrapInToStringForConcat } from "../../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; -import { isStandardLibraryType, isStringType, typeCanSatisfy } from "../../utils/typescript"; +import { canBeFalsyWhenNotNull, isStandardLibraryType, isStringType } from "../../utils/typescript"; import { transformTypeOfBinaryExpression } from "../typeof"; import { transformAssignmentExpression, transformAssignmentStatement } from "./assignments"; import { BitOperator, isBitOperator, transformBinaryBitOperation } from "./bit"; @@ -14,7 +15,18 @@ import { unwrapCompoundAssignmentToken, } from "./compound"; import { assert } from "../../../utils"; -import { transformToImmediatelyInvokedFunctionExpression } from "../../utils/transform"; +import { transformOrderedExpressions } from "../expression-list"; +import { transformInPrecedingStatementScope, WithPrecedingStatements } from "../../utils/preceding-statements"; + +type ShortCircuitOperator = + | ts.SyntaxKind.AmpersandAmpersandToken + | ts.SyntaxKind.BarBarToken + | ts.SyntaxKind.QuestionQuestionToken; + +const isShortCircuitOperator = (value: unknown): value is ShortCircuitOperator => + value === ts.SyntaxKind.AmpersandAmpersandToken || + value === ts.SyntaxKind.BarBarToken || + value === ts.SyntaxKind.QuestionQuestionToken; type SimpleOperator = | ts.AdditiveOperatorOrHigher @@ -41,7 +53,7 @@ const simpleOperatorsToLua: Record = { [ts.SyntaxKind.ExclamationEqualsEqualsToken]: lua.SyntaxKind.InequalityOperator, }; -export function transformBinaryOperation( +function transformBinaryOperationWithNoPrecedingStatements( context: TransformationContext, left: lua.Expression, right: lua.Expression, @@ -54,7 +66,12 @@ export function transformBinaryOperation( if (operator === ts.SyntaxKind.QuestionQuestionToken) { assert(ts.isBinaryExpression(node)); - return transformNullishCoalescingExpression(context, node); + return transformNullishCoalescingOperationNoPrecedingStatements(context, node, left, right); + } + + if (operator === ts.SyntaxKind.PercentToken && context.luaTarget === LuaTarget.Lua50) { + const mathMod = lua.createTableIndexExpression(lua.createIdentifier("math"), lua.createStringLiteral("mod")); + return lua.createCallExpression(mathMod, [left, right], node); } let luaOperator = simpleOperatorsToLua[operator]; @@ -63,9 +80,12 @@ export function transformBinaryOperation( if (operator === ts.SyntaxKind.PlusToken && ts.isBinaryExpression(node)) { const typeLeft = context.checker.getTypeAtLocation(node.left); const typeRight = context.checker.getTypeAtLocation(node.right); - if (isStringType(context, typeLeft) || isStringType(context, typeRight)) { - left = wrapInToStringForConcat(left); - right = wrapInToStringForConcat(right); + + const isLeftString = isStringType(context, typeLeft); + const isRightString = isStringType(context, typeRight); + if (isLeftString || isRightString) { + left = isLeftString ? left : wrapInToStringForConcat(left); + right = isRightString ? right : wrapInToStringForConcat(right); luaOperator = lua.SyntaxKind.ConcatOperator; } } @@ -73,6 +93,86 @@ export function transformBinaryOperation( return lua.createBinaryExpression(left, right, luaOperator, node); } +export function createShortCircuitBinaryExpressionPrecedingStatements( + context: TransformationContext, + lhs: lua.Expression, + rhs: lua.Expression, + rightPrecedingStatements: lua.Statement[], + operator: ShortCircuitOperator, + node?: ts.BinaryExpression +): WithPrecedingStatements { + const conditionIdentifier = context.createTempNameForLuaExpression(lhs); + const assignmentStatement = lua.createVariableDeclarationStatement(conditionIdentifier, lhs, node?.left); + + let condition: lua.Expression; + switch (operator) { + case ts.SyntaxKind.BarBarToken: + condition = lua.createUnaryExpression( + lua.cloneIdentifier(conditionIdentifier), + lua.SyntaxKind.NotOperator, + node + ); + break; + case ts.SyntaxKind.AmpersandAmpersandToken: + condition = lua.cloneIdentifier(conditionIdentifier); + break; + case ts.SyntaxKind.QuestionQuestionToken: + condition = lua.createBinaryExpression( + lua.cloneIdentifier(conditionIdentifier), + lua.createNilLiteral(), + lua.SyntaxKind.EqualityOperator, + node + ); + break; + } + + const ifStatement = lua.createIfStatement( + condition, + lua.createBlock([...rightPrecedingStatements, lua.createAssignmentStatement(conditionIdentifier, rhs)]), + undefined, + node?.left + ); + return { precedingStatements: [assignmentStatement, ifStatement], result: conditionIdentifier }; +} + +function transformShortCircuitBinaryExpression( + context: TransformationContext, + node: ts.BinaryExpression, + operator: ShortCircuitOperator +): WithPrecedingStatements { + const lhs = context.transformExpression(node.left); + const { precedingStatements, result } = transformInPrecedingStatementScope(context, () => + context.transformExpression(node.right) + ); + return transformBinaryOperation(context, lhs, result, precedingStatements, operator, node); +} + +export function transformBinaryOperation( + context: TransformationContext, + left: lua.Expression, + right: lua.Expression, + rightPrecedingStatements: lua.Statement[], + operator: BitOperator | SimpleOperator | ts.SyntaxKind.QuestionQuestionToken, + node: ts.Node +): WithPrecedingStatements { + if (rightPrecedingStatements.length > 0 && isShortCircuitOperator(operator)) { + assert(ts.isBinaryExpression(node)); + return createShortCircuitBinaryExpressionPrecedingStatements( + context, + left, + right, + rightPrecedingStatements, + operator, + node + ); + } + + return { + precedingStatements: rightPrecedingStatements, + result: transformBinaryOperationWithNoPrecedingStatements(context, left, right, operator, node), + }; +} + export const transformBinaryExpression: FunctionVisitor = (node, context) => { const operator = node.operatorToken.kind; @@ -115,25 +215,41 @@ export const transformBinaryExpression: FunctionVisitor = ( } case ts.SyntaxKind.CommaToken: { - return transformToImmediatelyInvokedFunctionExpression( - context, - () => ({ - statements: context.transformStatements(ts.factory.createExpressionStatement(node.left)), - result: context.transformExpression(node.right), - }), - node + const statements = context.transformStatements(ts.factory.createExpressionStatement(node.left)); + const { precedingStatements, result } = transformInPrecedingStatementScope(context, () => + context.transformExpression(node.right) ); + statements.push(...precedingStatements); + context.addPrecedingStatements(statements); + return result; } - default: - return transformBinaryOperation( - context, - context.transformExpression(node.left), - context.transformExpression(node.right), - operator, - node - ); + case ts.SyntaxKind.QuestionQuestionToken: + case ts.SyntaxKind.AmpersandAmpersandToken: + case ts.SyntaxKind.BarBarToken: { + const { precedingStatements, result } = transformShortCircuitBinaryExpression(context, node, operator); + context.addPrecedingStatements(precedingStatements); + return result; + } } + + const { + precedingStatements: orderedExpressionPrecedingStatements, + result: [lhs, rhs], + } = transformInPrecedingStatementScope(context, () => + transformOrderedExpressions(context, [node.left, node.right]) + ); + + const { precedingStatements, result } = transformBinaryOperation( + context, + lhs, + rhs, + orderedExpressionPrecedingStatements, + operator, + node + ); + context.addPrecedingStatements(precedingStatements); + return result; }; export function transformBinaryExpressionStatement( @@ -160,41 +276,29 @@ export function transformBinaryExpressionStatement( } } -function transformNullishCoalescingExpression( +function transformNullishCoalescingOperationNoPrecedingStatements( context: TransformationContext, - node: ts.BinaryExpression + node: ts.BinaryExpression, + transformedLeft: lua.Expression, + transformedRight: lua.Expression ): lua.Expression { const lhsType = context.checker.getTypeAtLocation(node.left); // Check if we can take a shortcut to 'lhs or rhs' if the left-hand side cannot be 'false'. - const typeCanBeFalse = (type: ts.Type) => - (type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Boolean)) !== 0 || - (type.flags & ts.TypeFlags.BooleanLiteral & ts.TypeFlags.PossiblyFalsy) !== 0; - if (typeCanSatisfy(context, lhsType, typeCanBeFalse)) { - // lhs can be false, transform to IIFE - const lhsIdentifier = lua.createIdentifier("____lhs"); - const nilComparison = lua.createBinaryExpression( - lua.cloneIdentifier(lhsIdentifier), - lua.createNilLiteral(), - lua.SyntaxKind.EqualityOperator - ); - // if ____ == nil then return rhs else return ____ end - const ifStatement = lua.createIfStatement( - nilComparison, - lua.createBlock([lua.createReturnStatement([context.transformExpression(node.right)])]), - lua.createBlock([lua.createReturnStatement([lua.cloneIdentifier(lhsIdentifier)])]) + if (canBeFalsyWhenNotNull(context, lhsType)) { + // reuse logic from case with preceding statements + const { precedingStatements, result } = createShortCircuitBinaryExpressionPrecedingStatements( + context, + transformedLeft, + transformedRight, + [], + ts.SyntaxKind.QuestionQuestionToken, + node ); - // (function(lhs') if lhs' == nil then return rhs else return lhs' end)(lhs) - return lua.createCallExpression(lua.createFunctionExpression(lua.createBlock([ifStatement]), [lhsIdentifier]), [ - context.transformExpression(node.left), - ]); + context.addPrecedingStatements(precedingStatements); + return result; } else { // lhs or rhs - return lua.createBinaryExpression( - context.transformExpression(node.left), - context.transformExpression(node.right), - lua.SyntaxKind.OrOperator, - node - ); + return lua.createBinaryExpression(transformedLeft, transformedRight, lua.SyntaxKind.OrOperator, node); } } diff --git a/src/transformation/visitors/block.ts b/src/transformation/visitors/block.ts index c26cc1cea..4eef4b6f8 100644 --- a/src/transformation/visitors/block.ts +++ b/src/transformation/visitors/block.ts @@ -1,7 +1,7 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { FunctionVisitor, TransformationContext } from "../context"; -import { performHoisting, popScope, pushScope, Scope, ScopeType } from "../utils/scope"; +import { performHoisting, Scope, ScopeType } from "../utils/scope"; export function transformBlockOrStatement(context: TransformationContext, statement: ts.Statement): lua.Statement[] { return context.transformStatements(ts.isBlock(statement) ? statement.statements : statement); @@ -12,15 +12,15 @@ export function transformScopeBlock( node: ts.Block, scopeType: ScopeType ): [lua.Block, Scope] { - pushScope(context, scopeType); + context.pushScope(scopeType, node); const statements = performHoisting(context, context.transformStatements(node.statements)); - const scope = popScope(context); + const scope = context.popScope(); return [lua.createBlock(statements, node), scope]; } export const transformBlock: FunctionVisitor = (node, context) => { - pushScope(context, ScopeType.Block); + context.pushScope(ScopeType.Block, node); const statements = performHoisting(context, context.transformStatements(node.statements)); - popScope(context); + context.popScope(); return lua.createDoStatement(statements, node); }; diff --git a/src/transformation/visitors/break-continue.ts b/src/transformation/visitors/break-continue.ts index 9211d4165..e41a7934b 100644 --- a/src/transformation/visitors/break-continue.ts +++ b/src/transformation/visitors/break-continue.ts @@ -2,28 +2,70 @@ import * as ts from "typescript"; import { LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; import { FunctionVisitor } from "../context"; -import { unsupportedForTarget } from "../utils/diagnostics"; -import { findScope, ScopeType } from "../utils/scope"; +import { findAsyncTryScopeBeforeLoop, findScope, LoopContinued, ScopeType } from "../utils/scope"; +import { isInAsyncFunction } from "../utils/typescript"; export const transformBreakStatement: FunctionVisitor = (breakStatement, context) => { - const breakableScope = findScope(context, ScopeType.Loop | ScopeType.Switch); - if (breakableScope?.type === ScopeType.Switch) { - return lua.createGotoStatement(`____switch${breakableScope.id}_end`); - } else { - return lua.createBreakStatement(breakStatement); + const tryScope = isInAsyncFunction(breakStatement) ? findAsyncTryScopeBeforeLoop(context) : undefined; + if (tryScope) { + tryScope.asyncTryHasBreak = true; + return [ + lua.createAssignmentStatement( + lua.createIdentifier("____hasBroken"), + lua.createBooleanLiteral(true), + breakStatement + ), + lua.createReturnStatement([], breakStatement), + ]; } + return lua.createBreakStatement(breakStatement); }; export const transformContinueStatement: FunctionVisitor = (statement, context) => { - if (context.luaTarget === LuaTarget.Universal || context.luaTarget === LuaTarget.Lua51) { - context.diagnostics.push(unsupportedForTarget(statement, "Continue statement", LuaTarget.Lua51)); - } - const scope = findScope(context, ScopeType.Loop); + const continuedWith = { + [LuaTarget.Universal]: LoopContinued.WithRepeatBreak, + [LuaTarget.Lua50]: LoopContinued.WithRepeatBreak, + [LuaTarget.Lua51]: LoopContinued.WithRepeatBreak, + [LuaTarget.Lua52]: LoopContinued.WithGoto, + [LuaTarget.Lua53]: LoopContinued.WithGoto, + [LuaTarget.Lua54]: LoopContinued.WithGoto, + [LuaTarget.Lua55]: LoopContinued.WithGoto, + [LuaTarget.LuaJIT]: LoopContinued.WithGoto, + [LuaTarget.Luau]: LoopContinued.WithContinue, + }[context.luaTarget]; + if (scope) { - scope.loopContinued = true; + scope.loopContinued = continuedWith; + } + + const tryScope = isInAsyncFunction(statement) ? findAsyncTryScopeBeforeLoop(context) : undefined; + if (tryScope) { + tryScope.asyncTryHasContinue = continuedWith; + return [ + lua.createAssignmentStatement( + lua.createIdentifier("____hasContinued"), + lua.createBooleanLiteral(true), + statement + ), + lua.createReturnStatement([], statement), + ]; } - return lua.createGotoStatement(`__continue${scope?.id ?? ""}`, statement); + const label = `__continue${scope?.id ?? ""}`; + + switch (continuedWith) { + case LoopContinued.WithGoto: + return lua.createGotoStatement(label, statement); + + case LoopContinued.WithContinue: + return lua.createContinueStatement(statement); + + case LoopContinued.WithRepeatBreak: + return [ + lua.createAssignmentStatement(lua.createIdentifier(label), lua.createBooleanLiteral(true), statement), + lua.createBreakStatement(statement), + ]; + } }; diff --git a/src/transformation/visitors/call.ts b/src/transformation/visitors/call.ts index f7a1b004e..c4a74135c 100644 --- a/src/transformation/visitors/call.ts +++ b/src/transformation/visitors/call.ts @@ -2,290 +2,253 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { transformBuiltinCallExpression } from "../builtins"; import { FunctionVisitor, TransformationContext } from "../context"; -import { AnnotationKind, getTypeAnnotations, isInTupleReturnFunction, isTupleReturnCall } from "../utils/annotations"; import { validateAssignment } from "../utils/assignment-validation"; -import { ContextType, getDeclarationContextType } from "../utils/function-context"; -import { createUnpackCall, wrapInTable } from "../utils/lua-ast"; -import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; +import { ContextType, getCallContextType } from "../utils/function-context"; +import { wrapInTable } from "../utils/lua-ast"; import { isValidLuaIdentifier } from "../utils/safe-names"; -import { isExpressionWithEvaluationEffect, isInDestructingAssignment } from "../utils/typescript"; +import { isExpressionWithEvaluationEffect } from "../utils/typescript"; import { transformElementAccessArgument } from "./access"; -import { shouldMultiReturnCallBeWrapped } from "./language-extensions/multi"; -import { isOperatorMapping, transformOperatorMappingExpression } from "./language-extensions/operators"; -import { - isTableDeleteCall, - isTableGetCall, - isTableHasCall, - isTableSetCall, - transformTableDeleteExpression, - transformTableGetExpression, - transformTableHasExpression, - transformTableSetExpression, -} from "./language-extensions/table"; -import { annotationRemoved, invalidTableDeleteExpression, invalidTableSetExpression } from "../utils/diagnostics"; -import { - ImmediatelyInvokedFunctionParameters, - transformToImmediatelyInvokedFunctionExpression, -} from "../utils/transform"; - -export type PropertyCallExpression = ts.CallExpression & { expression: ts.PropertyAccessExpression }; - -function getExpressionsBeforeAndAfterFirstSpread( - expressions: readonly ts.Expression[] -): [readonly ts.Expression[], readonly ts.Expression[]] { - // [a, b, ...c, d, ...e] --> [a, b] and [...c, d, ...e] - const index = expressions.findIndex(ts.isSpreadElement); - const hasSpreadElement = index !== -1; - const before = hasSpreadElement ? expressions.slice(0, index) : expressions; - const after = hasSpreadElement ? expressions.slice(index) : []; - return [before, after]; -} - -function transformSpreadableExpressionsIntoArrayConcatArguments( +import { isMultiReturnCall, shouldMultiReturnCallBeWrapped } from "./language-extensions/multi"; +import { unsupportedBuiltinOptionalCall } from "../utils/diagnostics"; +import { moveToPrecedingTemp, transformExpressionList } from "./expression-list"; +import { transformInPrecedingStatementScope } from "../utils/preceding-statements"; +import { getOptionalContinuationData, transformOptionalChain } from "./optional-chaining"; +import { transformImportExpression } from "./modules/import"; +import { transformLanguageExtensionCallExpression } from "./language-extensions/call-extension"; +import { getCustomNameFromSymbol } from "./identifier"; + +export function validateArguments( context: TransformationContext, - expressions: readonly ts.Expression[] | ts.NodeArray -): lua.Expression[] { - // [...array, a, b, ...tuple()] --> [ [...array], [a, b], [...tuple()] ] - // chunk non-spread arguments together so they don't concat - const chunks: ts.Expression[][] = []; - for (const [index, expression] of expressions.entries()) { - if (ts.isSpreadElement(expression)) { - chunks.push([expression]); - const next = expressions[index + 1]; - if (next && !ts.isSpreadElement(next)) { - chunks.push([]); - } - } else { - let lastChunk = chunks[chunks.length - 1]; - if (!lastChunk) { - lastChunk = []; - chunks.push(lastChunk); - } - lastChunk.push(expression); + params: readonly ts.Expression[], + signature?: ts.Signature +) { + if (!signature || signature.parameters.length < params.length) { + return; + } + for (const [index, param] of params.entries()) { + const signatureParameter = signature.parameters[index]; + if (signatureParameter.valueDeclaration !== undefined) { + const signatureType = context.checker.getTypeAtLocation(signatureParameter.valueDeclaration); + const paramType = context.checker.getTypeAtLocation(param); + validateAssignment(context, param, paramType, signatureType, signatureParameter.name); } } - - return chunks.map(chunk => wrapInTable(...chunk.map(expression => context.transformExpression(expression)))); } -export function flattenSpreadExpressions( +export function transformArguments( context: TransformationContext, - expressions: readonly ts.Expression[] + params: readonly ts.Expression[], + signature?: ts.Signature, + callContext?: ts.Expression ): lua.Expression[] { - const [preSpreadExpressions, postSpreadExpressions] = getExpressionsBeforeAndAfterFirstSpread(expressions); - const transformedPreSpreadExpressions = preSpreadExpressions.map(a => context.transformExpression(a)); + validateArguments(context, params, signature); + return transformExpressionList(context, callContext ? [callContext, ...params] : params); +} + +function transformCallWithArguments( + context: TransformationContext, + callExpression: ts.Expression, + transformedArguments: lua.Expression[], + argPrecedingStatements: lua.Statement[], + callContext?: ts.Expression +): [lua.Expression, lua.Expression[]] { + let call = context.transformExpression(callExpression); - // Nothing special required - if (postSpreadExpressions.length === 0) { - return transformedPreSpreadExpressions; + let transformedContext: lua.Expression | undefined; + if (callContext) { + transformedContext = context.transformExpression(callContext); } - // Only one spread element at the end? Will work as expected - if (postSpreadExpressions.length === 1) { - return [...transformedPreSpreadExpressions, context.transformExpression(postSpreadExpressions[0])]; + if (argPrecedingStatements.length > 0) { + if (transformedContext) { + transformedContext = moveToPrecedingTemp(context, transformedContext, callContext); + } + call = moveToPrecedingTemp(context, call, callExpression); + context.addPrecedingStatements(argPrecedingStatements); } - // Use Array.concat and unpack the result of that as the last Expression - const concatArguments = transformSpreadableExpressionsIntoArrayConcatArguments(context, postSpreadExpressions); - const lastExpression = createUnpackCall( - context, - transformLuaLibFunction(context, LuaLibFeature.ArrayConcat, undefined, ...concatArguments) - ); + if (transformedContext) { + transformedArguments.unshift(transformedContext); + } - return [...transformedPreSpreadExpressions, lastExpression]; + return [call, transformedArguments]; } -export function transformArguments( +export function transformCallAndArguments( context: TransformationContext, + callExpression: ts.Expression, params: readonly ts.Expression[], signature?: ts.Signature, callContext?: ts.Expression -): lua.Expression[] { - const parameters = flattenSpreadExpressions(context, params); - - // Add context as first param if present - if (callContext) { - parameters.unshift(context.transformExpression(callContext)); - } - - if (signature && signature.parameters.length >= params.length) { - for (const [index, param] of params.entries()) { - const signatureParameter = signature.parameters[index]; - const paramType = context.checker.getTypeAtLocation(param); - if (signatureParameter.valueDeclaration !== undefined) { - const signatureType = context.checker.getTypeAtLocation(signatureParameter.valueDeclaration); - validateAssignment(context, param, paramType, signatureType, signatureParameter.name); - } - } - } - - return parameters; +): [lua.Expression, lua.Expression[]] { + const { precedingStatements: argPrecedingStatements, result: transformedArguments } = + transformInPrecedingStatementScope(context, () => transformArguments(context, params, signature, callContext)); + return transformCallWithArguments(context, callExpression, transformedArguments, argPrecedingStatements); } function transformElementAccessCall( context: TransformationContext, left: ts.PropertyAccessExpression | ts.ElementAccessExpression, - args: ts.Expression[] | ts.NodeArray, - signature?: ts.Signature -): ImmediatelyInvokedFunctionParameters { - const transformedArguments = transformArguments(context, args, signature, ts.factory.createIdentifier("____self")); - + transformedArguments: lua.Expression[], + argPrecedingStatements: lua.Statement[] +) { // Cache left-side if it has effects - // (function() local ____self = context; return ____self[argument](parameters); end)() + // local ____self = context; return ____self[argument](parameters); + const selfIdentifier = lua.createIdentifier(context.createTempName("self")); + const callContext = context.transformExpression(left.expression); + const selfAssignment = lua.createVariableDeclarationStatement(selfIdentifier, callContext); + context.addPrecedingStatements(selfAssignment); + const argument = ts.isElementAccessExpression(left) ? transformElementAccessArgument(context, left) : lua.createStringLiteral(left.name.text); - const selfIdentifier = lua.createIdentifier("____self"); - const callContext = context.transformExpression(left.expression); - const selfAssignment = lua.createVariableDeclarationStatement(selfIdentifier, callContext); - const index = lua.createTableIndexExpression(selfIdentifier, argument); - const callExpression = lua.createCallExpression(index, transformedArguments); - return { statements: selfAssignment, result: callExpression }; + + let index: lua.Expression = lua.createTableIndexExpression(selfIdentifier, argument); + + if (argPrecedingStatements.length > 0) { + // Cache index in temp if args had preceding statements + index = moveToPrecedingTemp(context, index); + context.addPrecedingStatements(argPrecedingStatements); + } + + return lua.createCallExpression(index, [selfIdentifier, ...transformedArguments]); } export function transformContextualCallExpression( context: TransformationContext, node: ts.CallExpression | ts.TaggedTemplateExpression, - args: ts.Expression[] | ts.NodeArray, - signature?: ts.Signature + args: ts.Expression[] | ts.NodeArray ): lua.Expression { - const left = ts.isCallExpression(node) ? node.expression : node.tag; - if (ts.isPropertyAccessExpression(left) && ts.isIdentifier(left.name) && isValidLuaIdentifier(left.name.text)) { + if (ts.isOptionalChain(node)) { + return transformOptionalChain(context, node); + } + const left = ts.isCallExpression(node) ? getCalledExpression(node) : node.tag; + + let { precedingStatements: argPrecedingStatements, result: transformedArguments } = + transformInPrecedingStatementScope(context, () => transformArguments(context, args)); + + if ( + ts.isPropertyAccessExpression(left) && + ts.isIdentifier(left.name) && + isValidLuaIdentifier(left.name.text, context.options) && + argPrecedingStatements.length === 0 + ) { // table:name() const table = context.transformExpression(left.expression); + let name = left.name.text; - return lua.createMethodCallExpression( - table, - lua.createIdentifier(left.name.text, left.name), - transformArguments(context, args, signature), - node - ); + const symbol = context.checker.getSymbolAtLocation(left); + const customName = getCustomNameFromSymbol(context, symbol); + + if (customName) { + name = customName; + } + + return lua.createMethodCallExpression(table, lua.createIdentifier(name, left.name), transformedArguments, node); } else if (ts.isElementAccessExpression(left) || ts.isPropertyAccessExpression(left)) { if (isExpressionWithEvaluationEffect(left.expression)) { - return transformToImmediatelyInvokedFunctionExpression( + return transformElementAccessCall(context, left, transformedArguments, argPrecedingStatements); + } else { + let expression: lua.Expression; + [expression, transformedArguments] = transformCallWithArguments( context, - () => transformElementAccessCall(context, left, args, signature), - node + left, + transformedArguments, + argPrecedingStatements, + left.expression ); - } else { - const callContext = context.transformExpression(left.expression); - const expression = context.transformExpression(left); - const transformedArguments = transformArguments(context, args, signature); - return lua.createCallExpression(expression, [callContext, ...transformedArguments]); + return lua.createCallExpression(expression, transformedArguments, node); } - } else if (ts.isIdentifier(left)) { + } else if (ts.isIdentifier(left) || ts.isCallExpression(left)) { const callContext = context.isStrict ? ts.factory.createNull() : ts.factory.createIdentifier("_G"); - const transformedArguments = transformArguments(context, args, signature, callContext); - const expression = context.transformExpression(left); + let expression: lua.Expression; + [expression, transformedArguments] = transformCallWithArguments( + context, + left, + transformedArguments, + argPrecedingStatements, + callContext + ); return lua.createCallExpression(expression, transformedArguments, node); } else { throw new Error(`Unsupported LeftHandSideExpression kind: ${ts.SyntaxKind[left.kind]}`); } } -function transformPropertyCall(context: TransformationContext, node: PropertyCallExpression): lua.Expression { +function transformPropertyCall( + context: TransformationContext, + node: ts.CallExpression, + calledMethod: ts.PropertyAccessExpression +): lua.Expression { const signature = context.checker.getResolvedSignature(node); - if (node.expression.expression.kind === ts.SyntaxKind.SuperKeyword) { + if (calledMethod.expression.kind === ts.SyntaxKind.SuperKeyword) { // Super calls take the format of super.call(self,...) const parameters = transformArguments(context, node.arguments, signature, ts.factory.createThis()); - return lua.createCallExpression(context.transformExpression(node.expression), parameters); + return lua.createCallExpression(context.transformExpression(node.expression), parameters, node); } - const signatureDeclaration = signature?.getDeclaration(); - if (!signatureDeclaration || getDeclarationContextType(context, signatureDeclaration) !== ContextType.Void) { + if (getCallContextType(context, node) !== ContextType.Void) { // table:name() - return transformContextualCallExpression(context, node, node.arguments, signature); + return transformContextualCallExpression(context, node, node.arguments); } else { - const table = context.transformExpression(node.expression.expression); - // table.name() - const name = node.expression.name.text; - const callPath = lua.createTableIndexExpression(table, lua.createStringLiteral(name), node.expression); - const parameters = transformArguments(context, node.arguments, signature); + const [callPath, parameters] = transformCallAndArguments(context, node.expression, node.arguments, signature); + return lua.createCallExpression(callPath, parameters, node); } } function transformElementCall(context: TransformationContext, node: ts.CallExpression): lua.Expression { - const signature = context.checker.getResolvedSignature(node); - const signatureDeclaration = signature?.getDeclaration(); - if (!signatureDeclaration || getDeclarationContextType(context, signatureDeclaration) !== ContextType.Void) { + if (getCallContextType(context, node) !== ContextType.Void) { // A contextual parameter must be given to this call expression - return transformContextualCallExpression(context, node, node.arguments, signature); + return transformContextualCallExpression(context, node, node.arguments); } else { // No context - const expression = context.transformExpression(node.expression); - const parameters = transformArguments(context, node.arguments, signature); - return lua.createCallExpression(expression, parameters); + const [expression, parameters] = transformCallAndArguments(context, node.expression, node.arguments); + return lua.createCallExpression(expression, parameters, node); } } export const transformCallExpression: FunctionVisitor = (node, context) => { - const isTupleReturn = isTupleReturnCall(context, node); - const isTupleReturnForward = - node.parent && ts.isReturnStatement(node.parent) && isInTupleReturnFunction(context, node); - const isInSpread = node.parent && ts.isSpreadElement(node.parent); - const returnValueIsUsed = node.parent && !ts.isExpressionStatement(node.parent); - const wrapTupleReturn = - isTupleReturn && !isTupleReturnForward && !isInDestructingAssignment(node) && !isInSpread && returnValueIsUsed; - const wrapResult = wrapTupleReturn || shouldMultiReturnCallBeWrapped(context, node); - - const builtinResult = transformBuiltinCallExpression(context, node); - if (builtinResult) { - return wrapResult ? wrapInTable(builtinResult) : builtinResult; - } + const calledExpression = getCalledExpression(node); - if (isOperatorMapping(context, node)) { - return transformOperatorMappingExpression(context, node); + if (calledExpression.kind === ts.SyntaxKind.ImportKeyword) { + return transformImportExpression(node, context); } - if (isTableDeleteCall(context, node)) { - context.diagnostics.push(invalidTableDeleteExpression(node)); - return transformToImmediatelyInvokedFunctionExpression( - context, - () => ({ statements: transformTableDeleteExpression(context, node), result: lua.createNilLiteral() }), - node - ); - } - - if (isTableGetCall(context, node)) { - return transformTableGetExpression(context, node); + if (ts.isOptionalChain(node)) { + return transformOptionalChain(context, node); } - if (isTableHasCall(context, node)) { - return transformTableHasExpression(context, node); - } - - if (isTableSetCall(context, node)) { - context.diagnostics.push(invalidTableSetExpression(node)); - return transformToImmediatelyInvokedFunctionExpression( - context, - () => ({ statements: transformTableSetExpression(context, node), result: lua.createNilLiteral() }), - node - ); - } + const optionalContinuation = ts.isIdentifier(calledExpression) + ? getOptionalContinuationData(calledExpression) + : undefined; + const wrapResultInTable = isMultiReturnCall(context, node) && shouldMultiReturnCallBeWrapped(context, node); - if (ts.isPropertyAccessExpression(node.expression)) { - const ownerType = context.checker.getTypeAtLocation(node.expression.expression); - const annotations = getTypeAnnotations(ownerType); - if (annotations.has(AnnotationKind.LuaTable)) { - context.diagnostics.push(annotationRemoved(node, AnnotationKind.LuaTable)); + const builtinOrExtensionResult = + transformBuiltinCallExpression(context, node) ?? transformLanguageExtensionCallExpression(context, node); + if (builtinOrExtensionResult) { + if (optionalContinuation !== undefined) { + context.diagnostics.push(unsupportedBuiltinOptionalCall(node)); } + return wrapResultInTable ? wrapInTable(builtinOrExtensionResult) : builtinOrExtensionResult; + } - const result = transformPropertyCall(context, node as PropertyCallExpression); - return wrapResult ? wrapInTable(result) : result; + if (ts.isPropertyAccessExpression(calledExpression)) { + const result = transformPropertyCall(context, node, calledExpression); + return wrapResultInTable ? wrapInTable(result) : result; } - if (ts.isElementAccessExpression(node.expression)) { + if (ts.isElementAccessExpression(calledExpression)) { const result = transformElementCall(context, node); - return wrapResult ? wrapInTable(result) : result; + return wrapResultInTable ? wrapInTable(result) : result; } const signature = context.checker.getResolvedSignature(node); // Handle super calls properly - if (node.expression.kind === ts.SyntaxKind.SuperKeyword) { + if (calledExpression.kind === ts.SyntaxKind.SuperKeyword) { const parameters = transformArguments(context, node.arguments, signature, ts.factory.createThis()); return lua.createCallExpression( @@ -293,21 +256,38 @@ export const transformCallExpression: FunctionVisitor = (node context.transformExpression(ts.factory.createSuper()), lua.createStringLiteral("____constructor") ), - parameters + parameters, + node ); } - const callPath = context.transformExpression(node.expression); - const signatureDeclaration = signature?.getDeclaration(); + let callPath: lua.Expression; + let parameters: lua.Expression[]; - let parameters: lua.Expression[] = []; - if (signatureDeclaration && getDeclarationContextType(context, signatureDeclaration) === ContextType.Void) { - parameters = transformArguments(context, node.arguments, signature); + const isContextualCall = getCallContextType(context, node) !== ContextType.Void; + + if (!isContextualCall) { + [callPath, parameters] = transformCallAndArguments(context, calledExpression, node.arguments, signature); } else { - const callContext = context.isStrict ? ts.factory.createNull() : ts.factory.createIdentifier("_G"); - parameters = transformArguments(context, node.arguments, signature, callContext); + // if is optionalContinuation, context will be handled by transformOptionalChain. + const useGlobalContext = !context.isStrict && optionalContinuation === undefined; + const callContext = useGlobalContext ? ts.factory.createIdentifier("_G") : ts.factory.createNull(); + [callPath, parameters] = transformCallAndArguments( + context, + calledExpression, + node.arguments, + signature, + callContext + ); } const callExpression = lua.createCallExpression(callPath, parameters, node); - return wrapResult ? wrapInTable(callExpression) : callExpression; + if (optionalContinuation && isContextualCall) { + optionalContinuation.contextualCall = callExpression; + } + return wrapResultInTable ? wrapInTable(callExpression) : callExpression; }; + +export function getCalledExpression(node: ts.CallExpression): ts.Expression { + return ts.skipOuterExpressions(node.expression); +} diff --git a/src/transformation/visitors/class/decorators.ts b/src/transformation/visitors/class/decorators.ts index 4b913485c..11e4b75c7 100644 --- a/src/transformation/visitors/class/decorators.ts +++ b/src/transformation/visitors/class/decorators.ts @@ -1,9 +1,13 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; import { TransformationContext } from "../../context"; -import { decoratorInvalidContext } from "../../utils/diagnostics"; +import { decoratorInvalidContext, incompleteFieldDecoratorWarning } from "../../utils/diagnostics"; import { ContextType, getFunctionContextType } from "../../utils/function-context"; import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; +import { isNonNull } from "../../../utils"; +import { transformMemberExpressionOwnerName, transformMethodName } from "./members/method"; +import { transformPropertyName } from "../literal"; +import { isPrivateNode, isStaticNode } from "./utils"; export function transformDecoratorExpression(context: TransformationContext, decorator: ts.Decorator): lua.Expression { const expression = decorator.expression; @@ -16,7 +20,161 @@ export function transformDecoratorExpression(context: TransformationContext, dec return context.transformExpression(expression); } -export function createDecoratingExpression( +export function createClassDecoratingExpression( + context: TransformationContext, + classDeclaration: ts.ClassDeclaration | ts.ClassExpression, + className: lua.Expression +): lua.Expression { + const classDecorators = + ts.getDecorators(classDeclaration)?.map(d => transformDecoratorExpression(context, d)) ?? []; + + // If experimentalDecorators flag is set, decorate with legacy decorator logic + if (context.options.experimentalDecorators) { + return createLegacyDecoratingExpression(context, classDeclaration.kind, classDecorators, className); + } + + // Else: TypeScript 5.0 decorator + return createDecoratingExpression(context, className, className, classDecorators, { + kind: lua.createStringLiteral("class"), + name: lua.createStringLiteral(classDeclaration.name?.getText() ?? ""), + }); +} + +export function createClassMethodDecoratingExpression( + context: TransformationContext, + methodDeclaration: ts.MethodDeclaration, + originalMethod: lua.Expression, + className: lua.Identifier +): lua.Expression { + const parameterDecorators = getParameterDecorators(context, methodDeclaration); + const methodDecorators = + ts.getDecorators(methodDeclaration)?.map(d => transformDecoratorExpression(context, d)) ?? []; + + const methodName = transformMethodName(context, methodDeclaration); + + // If experimentalDecorators flag is set, decorate with legacy decorator logic + if (context.options.experimentalDecorators) { + const methodTable = transformMemberExpressionOwnerName(methodDeclaration, className); + return createLegacyDecoratingExpression( + context, + methodDeclaration.kind, + [...methodDecorators, ...parameterDecorators], + methodTable, + methodName + ); + } + + // Else: TypeScript 5.0 decorator + return createDecoratingExpression(context, className, originalMethod, methodDecorators, { + kind: lua.createStringLiteral("method"), + name: methodName, + private: lua.createBooleanLiteral(isPrivateNode(methodDeclaration)), + static: lua.createBooleanLiteral(isStaticNode(methodDeclaration)), + }); +} + +export function createClassAccessorDecoratingExpression( + context: TransformationContext, + accessor: ts.AccessorDeclaration, + originalAccessor: lua.Expression, + className: lua.Identifier +): lua.Expression { + const accessorDecorators = ts.getDecorators(accessor)?.map(d => transformDecoratorExpression(context, d)) ?? []; + const propertyName = transformPropertyName(context, accessor.name); + + // If experimentalDecorators flag is set, decorate with legacy decorator logic + if (context.options.experimentalDecorators) { + const propertyOwnerTable = transformMemberExpressionOwnerName(accessor, className); + + return createLegacyDecoratingExpression( + context, + accessor.kind, + accessorDecorators, + propertyOwnerTable, + propertyName + ); + } + + // Else: TypeScript 5.0 decorator + return createDecoratingExpression(context, className, originalAccessor, accessorDecorators, { + kind: lua.createStringLiteral(accessor.kind === ts.SyntaxKind.SetAccessor ? "setter" : "getter"), + name: propertyName, + private: lua.createBooleanLiteral(isPrivateNode(accessor)), + static: lua.createBooleanLiteral(isStaticNode(accessor)), + }); +} + +export function createClassPropertyDecoratingExpression( + context: TransformationContext, + property: ts.PropertyDeclaration, + className: lua.Identifier +): lua.Expression { + const decorators = ts.getDecorators(property) ?? []; + const propertyDecorators = decorators.map(d => transformDecoratorExpression(context, d)); + + // If experimentalDecorators flag is set, decorate with legacy decorator logic + if (context.options.experimentalDecorators) { + const propertyName = transformPropertyName(context, property.name); + const propertyOwnerTable = transformMemberExpressionOwnerName(property, className); + + return createLegacyDecoratingExpression( + context, + property.kind, + propertyDecorators, + propertyOwnerTable, + propertyName + ); + } + + // Else: TypeScript 5.0 decorator + + // Add a diagnostic when something is returned from a field decorator + for (const decorator of decorators) { + const signature = context.checker.getResolvedSignature(decorator); + const decoratorReturnType = signature?.getReturnType(); + // If return type of decorator is NOT void + if (decoratorReturnType && (decoratorReturnType.flags & ts.TypeFlags.Void) === 0) { + context.diagnostics.push(incompleteFieldDecoratorWarning(property)); + } + } + + return createDecoratingExpression(context, className, lua.createNilLiteral(), propertyDecorators, { + kind: lua.createStringLiteral("field"), + name: lua.createStringLiteral(property.name.getText()), + private: lua.createBooleanLiteral(isPrivateNode(property)), + static: lua.createBooleanLiteral(isStaticNode(property)), + }); +} + +function createDecoratingExpression( + context: TransformationContext, + className: lua.Expression, + originalValue: TValue, + decorators: lua.Expression[], + decoratorContext: Record +): lua.Expression { + const decoratorTable = lua.createTableExpression(decorators.map(d => lua.createTableFieldExpression(d))); + const decoratorContextTable = objectToLuaTableLiteral(decoratorContext); + + return transformLuaLibFunction( + context, + LuaLibFeature.Decorate, + undefined, + className, + originalValue, + decoratorTable, + decoratorContextTable + ); +} + +function objectToLuaTableLiteral(obj: Record): lua.Expression { + return lua.createTableExpression( + Object.entries(obj).map(([key, value]) => lua.createTableFieldExpression(value, lua.createStringLiteral(key))) + ); +} + +// Legacy decorators: +function createLegacyDecoratingExpression( context: TransformationContext, kind: ts.SyntaxKind, decorators: lua.Expression[], @@ -35,5 +193,39 @@ export function createDecoratingExpression( trailingExpressions.push(isMethodOrAccessor ? lua.createBooleanLiteral(true) : lua.createNilLiteral()); } - return transformLuaLibFunction(context, LuaLibFeature.Decorate, undefined, ...trailingExpressions); + return transformLuaLibFunction(context, LuaLibFeature.DecorateLegacy, undefined, ...trailingExpressions); +} + +function getParameterDecorators( + context: TransformationContext, + node: ts.FunctionLikeDeclarationBase +): lua.CallExpression[] { + return node.parameters + .flatMap((parameter, index) => + ts + .getDecorators(parameter) + ?.map(decorator => + transformLuaLibFunction( + context, + LuaLibFeature.DecorateParam, + node, + lua.createNumericLiteral(index), + transformDecoratorExpression(context, decorator) + ) + ) + ) + .filter(isNonNull); +} + +export function createConstructorDecoratingExpression( + context: TransformationContext, + node: ts.ConstructorDeclaration, + className: lua.Identifier +): lua.Statement | undefined { + const parameterDecorators = getParameterDecorators(context, node); + + if (parameterDecorators.length > 0) { + const decorateMethod = createLegacyDecoratingExpression(context, node.kind, parameterDecorators, className); + return lua.createExpressionStatement(decorateMethod); + } } diff --git a/src/transformation/visitors/class/index.ts b/src/transformation/visitors/class/index.ts index 98d74f84a..f2024babe 100644 --- a/src/transformation/visitors/class/index.ts +++ b/src/transformation/visitors/class/index.ts @@ -1,38 +1,37 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; -import { getOrUpdate } from "../../../utils"; -import { FunctionVisitor, TransformationContext } from "../../context"; -import { AnnotationKind, getTypeAnnotations } from "../../utils/annotations"; -import { annotationRemoved } from "../../utils/diagnostics"; +import { AllAccessorDeclarations, FunctionVisitor, TransformationContext } from "../../context"; import { - createDefaultExportIdentifier, + createDefaultExportExpression, createExportedIdentifier, hasDefaultExportModifier, isSymbolExported, + shouldBeExported, } from "../../utils/export"; -import { createSelfIdentifier, unwrapVisitorResult } from "../../utils/lua-ast"; +import { createSelfIdentifier } from "../../utils/lua-ast"; import { createSafeName, isUnsafeName } from "../../utils/safe-names"; -import { transformToImmediatelyInvokedFunctionExpression } from "../../utils/transform"; import { transformIdentifier } from "../identifier"; -import { createDecoratingExpression, transformDecoratorExpression } from "./decorators"; +import { createClassDecoratingExpression, createConstructorDecoratingExpression } from "./decorators"; import { transformAccessorDeclarations } from "./members/accessors"; import { createConstructorName, transformConstructorDeclaration } from "./members/constructor"; -import { - createPropertyDecoratingExpression, - transformClassInstanceFields, - transformStaticPropertyDeclaration, -} from "./members/fields"; -import { createMethodDecoratingExpression, transformMethodDeclaration } from "./members/method"; -import { checkForLuaLibType } from "./new"; -import { createClassSetup } from "./setup"; +import { transformClassInstanceFields, transformStaticPropertyDeclaration } from "./members/fields"; +import { transformMethodDeclaration } from "./members/method"; import { getExtendedNode, getExtendedType, isStaticNode } from "./utils"; +import { createClassSetup } from "./setup"; +import { LuaTarget } from "../../../CompilerOptions"; +import { transformInPrecedingStatementScope } from "../../utils/preceding-statements"; +import { createClassPropertyDecoratingExpression } from "./decorators"; +import { findFirstNodeAbove } from "../../utils/typescript"; export const transformClassDeclaration: FunctionVisitor = (declaration, context) => { // If declaration is a default export, transform to export variable assignment instead if (hasDefaultExportModifier(declaration)) { - const left = createExportedIdentifier(context, createDefaultExportIdentifier(declaration)); - const right = transformClassAsExpression(declaration, context); - return [lua.createAssignmentStatement(left, right, declaration)]; + // Class declaration including assignment to ____exports.default are in preceding statements + const { precedingStatements } = transformInPrecedingStatementScope(context, () => { + transformClassAsExpression(declaration, context); + return []; + }); + return precedingStatements; } const { statements } = transformClassLikeDeclaration(declaration, context); @@ -45,18 +44,13 @@ export function transformClassAsExpression( expression: ts.ClassLikeDeclaration, context: TransformationContext ): lua.Expression { - return transformToImmediatelyInvokedFunctionExpression( - context, - () => { - const { statements, name } = transformClassLikeDeclaration(expression, context); - return { statements: unwrapVisitorResult(statements), result: name }; - }, - expression - ); + const { statements, name } = transformClassLikeDeclaration(expression, context); + context.addPrecedingStatements(statements); + return name; } -const classSuperInfos = new WeakMap(); -interface ClassSuperInfo { +/** @internal */ +export interface ClassSuperInfo { className: lua.Identifier; extendedTypeNode?: ts.ExpressionWithTypeArguments; } @@ -72,29 +66,14 @@ function transformClassLikeDeclaration( } else if (classDeclaration.name !== undefined) { className = transformIdentifier(context, classDeclaration.name); } else { - // TypeScript error - className = lua.createAnonymousIdentifier(); - } - - const annotations = getTypeAnnotations(context.checker.getTypeAtLocation(classDeclaration)); - - if (annotations.has(AnnotationKind.Extension)) { - context.diagnostics.push(annotationRemoved(classDeclaration, AnnotationKind.Extension)); - } - if (annotations.has(AnnotationKind.MetaExtension)) { - context.diagnostics.push(annotationRemoved(classDeclaration, AnnotationKind.MetaExtension)); + className = lua.createIdentifier(context.createTempName("class"), classDeclaration); } // Get type that is extended - const extendedTypeNode = getExtendedNode(context, classDeclaration); + const extendedTypeNode = getExtendedNode(classDeclaration); const extendedType = getExtendedType(context, classDeclaration); - const superInfo = getOrUpdate(classSuperInfos, context, () => []); - superInfo.push({ className, extendedTypeNode }); - - if (extendedType) { - checkForLuaLibType(context, extendedType); - } + context.classSuperInfos.push({ className, extendedTypeNode }); // Get all properties with value const properties = classDeclaration.members.filter(ts.isPropertyDeclaration).filter(member => member.initializer); @@ -105,7 +84,7 @@ function transformClassLikeDeclaration( const result: lua.Statement[] = []; let localClassName: lua.Identifier; - if (isUnsafeName(className.text)) { + if (isUnsafeName(className.text, context.options)) { localClassName = lua.createIdentifier( createSafeName(className.text), undefined, @@ -135,11 +114,15 @@ function transformClassLikeDeclaration( ); if (constructorResult) result.push(constructorResult); + + // Legacy constructor decorator + const decoratingExpression = createConstructorDecoratingExpression(context, constructor, localClassName); + if (decoratingExpression) result.push(decoratingExpression); } else if (!extendedType) { // Generate a constructor if none was defined in a base class const constructorResult = transformConstructorDeclaration( context, - ts.factory.createConstructorDeclaration([], [], [], ts.factory.createBlock([], true)), + ts.factory.createConstructorDeclaration([], [], ts.factory.createBlock([], true)), localClassName, instanceFields, classDeclaration @@ -149,16 +132,20 @@ function transformClassLikeDeclaration( } else if (instanceFields.length > 0) { // Generate a constructor if none was defined in a class with instance fields that need initialization // localClassName.prototype.____constructor = function(self, ...) - // baseClassName.prototype.____constructor(self, ...) + // baseClassName.prototype.____constructor(self, ...) // or unpack(arg) for Lua 5.0 // ... const constructorBody = transformClassInstanceFields(context, instanceFields); + const argsExpression = + context.luaTarget === LuaTarget.Lua50 + ? lua.createCallExpression(lua.createIdentifier("unpack"), [lua.createArgLiteral()]) + : lua.createDotsLiteral(); const superCall = lua.createExpressionStatement( lua.createCallExpression( lua.createTableIndexExpression( context.transformExpression(ts.factory.createSuper()), lua.createStringLiteral("____constructor") ), - [createSelfIdentifier(), lua.createDotsLiteral()] + [createSelfIdentifier(), argsExpression] ) ); constructorBody.unshift(superCall); @@ -166,69 +153,109 @@ function transformClassLikeDeclaration( lua.createBlock(constructorBody), [createSelfIdentifier()], lua.createDotsLiteral(), - lua.FunctionExpressionFlags.Declaration + lua.NodeFlags.Declaration ); result.push( lua.createAssignmentStatement(createConstructorName(localClassName), constructorFunction, classDeclaration) ); } - // Transform accessors - for (const member of classDeclaration.members) { - if (!ts.isAccessor(member)) continue; - const accessors = context.resolver.getAllAccessorDeclarations(member); - if (accessors.firstAccessor !== member) continue; + // Transform class members - const accessorsResult = transformAccessorDeclarations(context, accessors, localClassName); - if (accessorsResult) { - result.push(accessorsResult); + // First transform the methods, in case the static properties call them + for (const member of classDeclaration.members) { + if (ts.isMethodDeclaration(member)) { + // Methods + const statements = transformMethodDeclaration(context, member, localClassName); + result.push(...statements); } } - const decorationStatements: lua.Statement[] = []; - + // Then transform the rest for (const member of classDeclaration.members) { if (ts.isAccessor(member)) { - const expression = createPropertyDecoratingExpression(context, member, localClassName); - if (expression) decorationStatements.push(lua.createExpressionStatement(expression)); - } else if (ts.isMethodDeclaration(member)) { - const statement = transformMethodDeclaration(context, member, localClassName); - if (statement) result.push(statement); - if (member.body) { - const statement = createMethodDecoratingExpression(context, member, localClassName); - if (statement) decorationStatements.push(statement); + // Accessors + const symbol = context.checker.getSymbolAtLocation(member.name); + if (!symbol) continue; + const accessors = getAllAccessorDeclarations(classDeclaration, symbol, context); + if (accessors.firstAccessor !== member) continue; + + const accessorsResult = transformAccessorDeclarations(context, accessors, localClassName); + if (accessorsResult) { + result.push(accessorsResult); } } else if (ts.isPropertyDeclaration(member)) { + // Properties if (isStaticNode(member)) { const statement = transformStaticPropertyDeclaration(context, member, localClassName); - if (statement) decorationStatements.push(statement); + if (statement) result.push(statement); + } + + if (ts.getDecorators(member)?.length) { + result.push( + lua.createExpressionStatement(createClassPropertyDecoratingExpression(context, member, className)) + ); + } + } else if (ts.isClassStaticBlockDeclaration(member)) { + if (member.body.statements.length > 0) { + const bodyStatements = context.transformStatements(member.body.statements); + const iif = lua.createFunctionExpression(lua.createBlock(bodyStatements), [ + lua.createIdentifier("self"), + ]); + const iife = lua.createCallExpression(iif, [localClassName]); + result.push(lua.createExpressionStatement(iife, member)); } - const expression = createPropertyDecoratingExpression(context, member, localClassName); - if (expression) decorationStatements.push(lua.createExpressionStatement(expression)); } } - result.push(...decorationStatements); - // Decorate the class - if (classDeclaration.decorators) { - const decoratingExpression = createDecoratingExpression( - context, - classDeclaration.kind, - classDeclaration.decorators.map(d => transformDecoratorExpression(context, d)), - localClassName - ); + if (ts.canHaveDecorators(classDeclaration) && ts.getDecorators(classDeclaration)) { + const decoratingExpression = createClassDecoratingExpression(context, classDeclaration, localClassName); const decoratingStatement = lua.createAssignmentStatement(localClassName, decoratingExpression); result.push(decoratingStatement); + + if (shouldBeExported(classDeclaration)) { + const exportExpression = hasDefaultExportModifier(classDeclaration) + ? createDefaultExportExpression(classDeclaration) + : createExportedIdentifier(context, localClassName); + + const classAssignment = lua.createAssignmentStatement(exportExpression, localClassName); + result.push(classAssignment); + } } - superInfo.pop(); + context.classSuperInfos.pop(); return { statements: result, name: className }; } +function getAllAccessorDeclarations( + classDeclaration: ts.ClassLikeDeclaration, + symbol: ts.Symbol, + context: TransformationContext +): AllAccessorDeclarations { + const getAccessor = classDeclaration.members.find( + (m): m is ts.GetAccessorDeclaration => + ts.isGetAccessor(m) && context.checker.getSymbolAtLocation(m.name) === symbol + ); + const setAccessor = classDeclaration.members.find( + (m): m is ts.SetAccessorDeclaration => + ts.isSetAccessor(m) && context.checker.getSymbolAtLocation(m.name) === symbol + ); + + // Get the first of the two (that is not undefined) + const firstAccessor = + getAccessor && (!setAccessor || getAccessor.pos < setAccessor.pos) ? getAccessor : setAccessor!; + + return { + firstAccessor, + setAccessor, + getAccessor, + }; +} + export const transformSuperExpression: FunctionVisitor = (expression, context) => { - const superInfos = getOrUpdate(classSuperInfos, context, () => []); + const superInfos = context.classSuperInfos; const superInfo = superInfos[superInfos.length - 1]; if (!superInfo) return lua.createAnonymousIdentifier(expression); const { className, extendedTypeNode } = superInfo; @@ -245,10 +272,14 @@ export const transformSuperExpression: FunctionVisitor = (ex } } - if (!baseClassName) { - // Use "className.____super" if the base is not a simple identifier - baseClassName = lua.createTableIndexExpression(className, lua.createStringLiteral("____super"), expression); - } + // Use "className.____super" if the base is not a simple identifier + baseClassName ??= lua.createTableIndexExpression(className, lua.createStringLiteral("____super"), expression); - return lua.createTableIndexExpression(baseClassName, lua.createStringLiteral("prototype")); + const f = findFirstNodeAbove(expression, ts.isFunctionLike); + if (f && ts.canHaveModifiers(f) && isStaticNode(f)) { + // In static method, don't add prototype to super call + return baseClassName; + } else { + return lua.createTableIndexExpression(baseClassName, lua.createStringLiteral("prototype")); + } }; diff --git a/src/transformation/visitors/class/members/accessors.ts b/src/transformation/visitors/class/members/accessors.ts index f774b9645..017100056 100644 --- a/src/transformation/visitors/class/members/accessors.ts +++ b/src/transformation/visitors/class/members/accessors.ts @@ -7,11 +7,27 @@ import { transformFunctionBody, transformParameters } from "../../function"; import { transformPropertyName } from "../../literal"; import { isStaticNode } from "../utils"; import { createPrototypeName } from "./constructor"; +import { createClassAccessorDecoratingExpression } from "../decorators"; -function transformAccessor(context: TransformationContext, node: ts.AccessorDeclaration): lua.FunctionExpression { +function transformAccessor( + context: TransformationContext, + node: ts.AccessorDeclaration, + className: lua.Identifier +): lua.Expression { const [params, dot, restParam] = transformParameters(context, node.parameters, createSelfIdentifier()); - const body = node.body ? transformFunctionBody(context, node.parameters, node.body, restParam)[0] : []; - return lua.createFunctionExpression(lua.createBlock(body), params, dot, lua.FunctionExpressionFlags.Declaration); + const body = node.body ? transformFunctionBody(context, node.parameters, node.body, node, restParam)[0] : []; + const accessorFunction = lua.createFunctionExpression( + lua.createBlock(body), + params, + dot, + lua.NodeFlags.Declaration + ); + + if (ts.getDecorators(node)?.length) { + return createClassAccessorDecoratingExpression(context, node, accessorFunction, className); + } else { + return accessorFunction; + } } export function transformAccessorDeclarations( @@ -23,12 +39,12 @@ export function transformAccessorDeclarations( const descriptor = lua.createTableExpression([]); if (getAccessor) { - const getterFunction = transformAccessor(context, getAccessor); + const getterFunction = transformAccessor(context, getAccessor, className); descriptor.fields.push(lua.createTableFieldExpression(getterFunction, lua.createStringLiteral("get"))); } if (setAccessor) { - const setterFunction = transformAccessor(context, setAccessor); + const setterFunction = transformAccessor(context, setAccessor, className); descriptor.fields.push(lua.createTableFieldExpression(setterFunction, lua.createStringLiteral("set"))); } diff --git a/src/transformation/visitors/class/members/constructor.ts b/src/transformation/visitors/class/members/constructor.ts index 11766d2a8..8bff2f4fc 100644 --- a/src/transformation/visitors/class/members/constructor.ts +++ b/src/transformation/visitors/class/members/constructor.ts @@ -2,7 +2,7 @@ import * as ts from "typescript"; import * as lua from "../../../../LuaAST"; import { TransformationContext } from "../../../context"; import { createSelfIdentifier } from "../../../utils/lua-ast"; -import { popScope, pushScope, ScopeType } from "../../../utils/scope"; +import { ScopeType } from "../../../utils/scope"; import { transformFunctionBodyContent, transformFunctionBodyHeader, transformParameters } from "../../function"; import { transformIdentifier } from "../../identifier"; import { transformClassInstanceFields } from "./fields"; @@ -28,7 +28,7 @@ export function transformConstructorDeclaration( } // Transform body - const scope = pushScope(context, ScopeType.Function); + const scope = context.pushScope(ScopeType.Function, statement); const body = transformFunctionBodyContent(context, statement.body); const [params, dotsLiteral, restParamName] = transformParameters( @@ -45,23 +45,22 @@ export function transformConstructorDeclaration( const classInstanceFields = transformClassInstanceFields(context, instanceFields); - // If there are field initializers and the first statement is a super call, - // move super call between default assignments and initializers + // If there are field initializers and there is a super call somewhere, + // move super call and everything before it to between default assignments and initializers if ( (constructorFieldsDeclarations.length > 0 || classInstanceFields.length > 0) && statement.body && statement.body.statements.length > 0 ) { - const firstStatement = statement.body.statements[0]; - if ( - ts.isExpressionStatement(firstStatement) && - ts.isCallExpression(firstStatement.expression) && - firstStatement.expression.expression.kind === ts.SyntaxKind.SuperKeyword - ) { - const superCall = body.shift(); - if (superCall) { - bodyWithFieldInitializers.push(superCall); - } + const superIndex = statement.body.statements.findIndex( + s => + ts.isExpressionStatement(s) && + ts.isCallExpression(s.expression) && + s.expression.expression.kind === ts.SyntaxKind.SuperKeyword + ); + + if (superIndex !== -1) { + bodyWithFieldInitializers.push(...body.splice(0, superIndex + 1)); } } @@ -86,11 +85,11 @@ export function transformConstructorDeclaration( const constructorWasGenerated = statement.pos === -1; - popScope(context); + context.popScope(); return lua.createAssignmentStatement( createConstructorName(className), - lua.createFunctionExpression(block, params, dotsLiteral, lua.FunctionExpressionFlags.Declaration), + lua.createFunctionExpression(block, params, dotsLiteral, lua.NodeFlags.Declaration), constructorWasGenerated ? classDeclaration : statement ); } diff --git a/src/transformation/visitors/class/members/fields.ts b/src/transformation/visitors/class/members/fields.ts index 980c6f851..2308eaac8 100644 --- a/src/transformation/visitors/class/members/fields.ts +++ b/src/transformation/visitors/class/members/fields.ts @@ -2,26 +2,8 @@ import * as ts from "typescript"; import * as lua from "../../../../LuaAST"; import { TransformationContext } from "../../../context"; import { createSelfIdentifier } from "../../../utils/lua-ast"; +import { transformInPrecedingStatementScope } from "../../../utils/preceding-statements"; import { transformPropertyName } from "../../literal"; -import { createDecoratingExpression, transformDecoratorExpression } from "../decorators"; -import { transformMemberExpressionOwnerName } from "./method"; - -export function createPropertyDecoratingExpression( - context: TransformationContext, - node: ts.PropertyDeclaration | ts.AccessorDeclaration, - className: lua.Identifier -): lua.Expression | undefined { - if (!node.decorators) return; - const propertyName = transformPropertyName(context, node.name); - const propertyOwnerTable = transformMemberExpressionOwnerName(node, className); - return createDecoratingExpression( - context, - node.kind, - node.decorators.map(d => transformDecoratorExpression(context, d)), - propertyOwnerTable, - propertyName - ); -} export function transformClassInstanceFields( context: TransformationContext, @@ -30,18 +12,22 @@ export function transformClassInstanceFields( const statements: lua.Statement[] = []; for (const f of instanceFields) { - // Get identifier - const fieldName = transformPropertyName(context, f.name); + const { precedingStatements, result: statement } = transformInPrecedingStatementScope(context, () => { + // Get identifier + const fieldName = transformPropertyName(context, f.name); + + const value = f.initializer ? context.transformExpression(f.initializer) : undefined; - const value = f.initializer ? context.transformExpression(f.initializer) : undefined; + // self[fieldName] + const selfIndex = lua.createTableIndexExpression(createSelfIdentifier(), fieldName); - // self[fieldName] - const selfIndex = lua.createTableIndexExpression(createSelfIdentifier(), fieldName); + // self[fieldName] = value + const assignClassField = lua.createAssignmentStatement(selfIndex, value, f); - // self[fieldName] = value - const assignClassField = lua.createAssignmentStatement(selfIndex, value, f); + return assignClassField; + }); - statements.push(assignClassField); + statements.push(...precedingStatements, statement); } return statements; @@ -56,5 +42,6 @@ export function transformStaticPropertyDeclaration( const fieldName = transformPropertyName(context, field.name); const value = context.transformExpression(field.initializer); const classField = lua.createTableIndexExpression(lua.cloneIdentifier(className), fieldName); + return lua.createAssignmentStatement(classField, value); } diff --git a/src/transformation/visitors/class/members/method.ts b/src/transformation/visitors/class/members/method.ts index c902ea8de..adfe2c352 100644 --- a/src/transformation/visitors/class/members/method.ts +++ b/src/transformation/visitors/class/members/method.ts @@ -4,10 +4,8 @@ import { TransformationContext } from "../../../context"; import { transformFunctionToExpression } from "../../function"; import { transformPropertyName } from "../../literal"; import { isStaticNode } from "../utils"; -import { createDecoratingExpression, transformDecoratorExpression } from "../decorators"; import { createPrototypeName } from "./constructor"; -import { transformLuaLibFunction, LuaLibFeature } from "../../../utils/lualib"; -import { isNonNull } from "../../../../utils"; +import { createClassMethodDecoratingExpression } from "../decorators"; export function transformMemberExpressionOwnerName( node: ts.PropertyDeclaration | ts.MethodDeclaration | ts.AccessorDeclaration, @@ -28,53 +26,45 @@ export function transformMethodDeclaration( context: TransformationContext, node: ts.MethodDeclaration, className: lua.Identifier -): lua.Statement | undefined { +): lua.Statement[] { // Don't transform methods without body (overload declarations) - if (!node.body) return; + if (!node.body) return []; const methodTable = transformMemberExpressionOwnerName(node, className); const methodName = transformMethodName(context, node); const [functionExpression] = transformFunctionToExpression(context, node); - return lua.createAssignmentStatement( - lua.createTableIndexExpression(methodTable, methodName), - functionExpression, - node - ); -} - -export function createMethodDecoratingExpression( - context: TransformationContext, - node: ts.MethodDeclaration, - className: lua.Identifier -): lua.Statement | undefined { - const methodTable = transformMemberExpressionOwnerName(node, className); - const methodName = transformMethodName(context, node); - - const parameterDecorators = node.parameters - .flatMap((parameter, index) => - parameter.decorators?.map(decorator => - transformLuaLibFunction( - context, - LuaLibFeature.DecorateParam, - node, - lua.createNumericLiteral(index), - transformDecoratorExpression(context, decorator) - ) - ) - ) - .filter(isNonNull); - - const methodDecorators = node.decorators?.map(d => transformDecoratorExpression(context, d)) ?? []; + const methodHasDecorators = (ts.getDecorators(node)?.length ?? 0) > 0; + const methodHasParameterDecorators = node.parameters.some(p => (ts.getDecorators(p)?.length ?? 0) > 0); // Legacy decorators - if (methodDecorators.length > 0 || parameterDecorators.length > 0) { - const decorateMethod = createDecoratingExpression( - context, - node.kind, - [...methodDecorators, ...parameterDecorators], - methodTable, - methodName - ); - return lua.createExpressionStatement(decorateMethod); + if (methodHasDecorators || methodHasParameterDecorators) { + if (context.options.experimentalDecorators) { + // Legacy decorator statement + return [ + lua.createAssignmentStatement( + lua.createTableIndexExpression(methodTable, methodName), + functionExpression + ), + lua.createExpressionStatement( + createClassMethodDecoratingExpression(context, node, functionExpression, className) + ), + ]; + } else { + return [ + lua.createAssignmentStatement( + lua.createTableIndexExpression(methodTable, methodName), + createClassMethodDecoratingExpression(context, node, functionExpression, className), + node + ), + ]; + } + } else { + return [ + lua.createAssignmentStatement( + lua.createTableIndexExpression(methodTable, methodName), + functionExpression, + node + ), + ]; } } diff --git a/src/transformation/visitors/class/new.ts b/src/transformation/visitors/class/new.ts index 721cc26e5..0c5733451 100644 --- a/src/transformation/visitors/class/new.ts +++ b/src/transformation/visitors/class/new.ts @@ -1,73 +1,48 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; -import { FunctionVisitor, TransformationContext } from "../../context"; +import { FunctionVisitor } from "../../context"; import { AnnotationKind, getTypeAnnotations } from "../../utils/annotations"; -import { annotationInvalidArgumentCount, annotationRemoved } from "../../utils/diagnostics"; -import { importLuaLibFeature, LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; -import { transformArguments } from "../call"; +import { annotationInvalidArgumentCount, unsupportedArrayWithLengthConstructor } from "../../utils/diagnostics"; +import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; +import { transformArguments, transformCallAndArguments } from "../call"; import { isTableNewCall } from "../language-extensions/table"; - -const builtinErrorTypeNames = new Set([ - "Error", - "ErrorConstructor", - "RangeError", - "RangeErrorConstructor", - "ReferenceError", - "ReferenceErrorConstructor", - "SyntaxError", - "SyntaxErrorConstructor", - "TypeError", - "TypeErrorConstructor", - "URIError", - "URIErrorConstructor", -]); - -// TODO: Do it in identifier? -export function checkForLuaLibType(context: TransformationContext, type: ts.Type): void { - if (!type.symbol) return; - - const name = context.checker.getFullyQualifiedName(type.symbol); - switch (name) { - case "Map": - importLuaLibFeature(context, LuaLibFeature.Map); - return; - case "Set": - importLuaLibFeature(context, LuaLibFeature.Set); - return; - case "WeakMap": - importLuaLibFeature(context, LuaLibFeature.WeakMap); - return; - case "WeakSet": - importLuaLibFeature(context, LuaLibFeature.WeakSet); - return; - } - - if (builtinErrorTypeNames.has(name)) { - importLuaLibFeature(context, LuaLibFeature.Error); - } -} +import { tryGetStandardLibrarySymbolOfType } from "../../builtins"; export const transformNewExpression: FunctionVisitor = (node, context) => { - const type = context.checker.getTypeAtLocation(node); - - const annotations = getTypeAnnotations(type); - - if (annotations.has(AnnotationKind.LuaTable)) { - context.diagnostics.push(annotationRemoved(node, AnnotationKind.LuaTable)); - } - if (isTableNewCall(context, node)) { return lua.createTableExpression(undefined, node); } - const name = context.transformExpression(node.expression); - const signature = context.checker.getResolvedSignature(node); - const params = node.arguments - ? transformArguments(context, node.arguments, signature) - : [lua.createBooleanLiteral(true)]; + const constructorType = context.checker.getTypeAtLocation(node.expression); + if (tryGetStandardLibrarySymbolOfType(context, constructorType)?.name === "ArrayConstructor") { + if (node.arguments === undefined || node.arguments.length === 0) { + // turn new Array<>() into a simple {} + return lua.createTableExpression([], node); + } else { + // More than one argument, check if items constructor + const signature = context.checker.getResolvedSignature(node); + const signatureDeclaration = signature?.getDeclaration(); + if ( + signatureDeclaration?.parameters.length === 1 && + signatureDeclaration.parameters[0].dotDotDotToken === undefined + ) { + context.diagnostics.push(unsupportedArrayWithLengthConstructor(node)); + return lua.createTableExpression([], node); + } else { + const callArguments = transformArguments(context, node.arguments, signature); + return lua.createTableExpression( + callArguments.map(e => lua.createTableFieldExpression(e)), + node + ); + } + } + } - checkForLuaLibType(context, type); + const signature = context.checker.getResolvedSignature(node); + const [name, params] = transformCallAndArguments(context, node.expression, node.arguments ?? [], signature); + const type = context.checker.getTypeAtLocation(node); + const annotations = getTypeAnnotations(type); const customConstructorAnnotation = annotations.get(AnnotationKind.CustomConstructor); if (customConstructorAnnotation) { if (customConstructorAnnotation.args.length === 1) { diff --git a/src/transformation/visitors/class/setup.ts b/src/transformation/visitors/class/setup.ts index 151e68a77..ab6eeea92 100644 --- a/src/transformation/visitors/class/setup.ts +++ b/src/transformation/visitors/class/setup.ts @@ -55,13 +55,13 @@ export function createClassSetup( result.push( lua.createAssignmentStatement( lua.createTableIndexExpression(lua.cloneIdentifier(localClassName), lua.createStringLiteral("name")), - getReflectionClassName(context, statement, className), + getReflectionClassName(statement, className), statement ) ); if (extendsType) { - const extendedNode = getExtendedNode(context, statement); + const extendedNode = getExtendedNode(statement); assert(extendedNode); result.push( lua.createExpressionStatement( @@ -80,7 +80,6 @@ export function createClassSetup( } export function getReflectionClassName( - context: TransformationContext, declaration: ts.ClassLikeDeclarationBase, className: lua.Identifier ): lua.Expression { @@ -92,7 +91,7 @@ export function getReflectionClassName( return lua.createStringLiteral("default"); } - if (getExtendedNode(context, declaration)) { + if (getExtendedNode(declaration)) { return lua.createTableIndexExpression(className, lua.createStringLiteral("name")); } diff --git a/src/transformation/visitors/class/utils.ts b/src/transformation/visitors/class/utils.ts index aea06680a..0cd4384bb 100644 --- a/src/transformation/visitors/class/utils.ts +++ b/src/transformation/visitors/class/utils.ts @@ -1,30 +1,22 @@ import * as ts from "typescript"; import { TransformationContext } from "../../context"; -import { AnnotationKind, getTypeAnnotations } from "../../utils/annotations"; -import { annotationRemoved } from "../../utils/diagnostics"; -export function isStaticNode(node: ts.Node): boolean { - return (node.modifiers ?? []).some(m => m.kind === ts.SyntaxKind.StaticKeyword); +export function isPrivateNode(node: ts.HasModifiers): boolean { + return node.modifiers?.some(m => m.kind === ts.SyntaxKind.PrivateKeyword) === true; +} + +export function isStaticNode(node: ts.HasModifiers): boolean { + return node.modifiers?.some(m => m.kind === ts.SyntaxKind.StaticKeyword) === true; } export function getExtendsClause(node: ts.ClassLikeDeclarationBase): ts.HeritageClause | undefined { - return (node.heritageClauses ?? []).find(clause => clause.token === ts.SyntaxKind.ExtendsKeyword); + return node.heritageClauses?.find(clause => clause.token === ts.SyntaxKind.ExtendsKeyword); } -export function getExtendedNode( - context: TransformationContext, - node: ts.ClassLikeDeclarationBase -): ts.ExpressionWithTypeArguments | undefined { +export function getExtendedNode(node: ts.ClassLikeDeclarationBase): ts.ExpressionWithTypeArguments | undefined { const extendsClause = getExtendsClause(node); if (!extendsClause) return; - const superType = context.checker.getTypeAtLocation(extendsClause.types[0]); - const annotations = getTypeAnnotations(superType); - - if (annotations.has(AnnotationKind.PureAbstract)) { - context.diagnostics.push(annotationRemoved(extendsClause, AnnotationKind.PureAbstract)); - } - return extendsClause.types[0]; } @@ -32,6 +24,6 @@ export function getExtendedType( context: TransformationContext, node: ts.ClassLikeDeclarationBase ): ts.Type | undefined { - const extendedNode = getExtendedNode(context, node); + const extendedNode = getExtendedNode(node); return extendedNode && context.checker.getTypeAtLocation(extendedNode); } diff --git a/src/transformation/visitors/conditional.ts b/src/transformation/visitors/conditional.ts index 2fab7494c..6285a488d 100644 --- a/src/transformation/visitors/conditional.ts +++ b/src/transformation/visitors/conditional.ts @@ -1,92 +1,118 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { FunctionVisitor, TransformationContext } from "../context"; -import { performHoisting, popScope, pushScope, ScopeType } from "../utils/scope"; +import { transformInPrecedingStatementScope, WithPrecedingStatements } from "../utils/preceding-statements"; +import { performHoisting, ScopeType } from "../utils/scope"; import { transformBlockOrStatement } from "./block"; - -function canBeFalsy(context: TransformationContext, type: ts.Type): boolean { - const strictNullChecks = context.options.strict === true || context.options.strictNullChecks === true; - - const falsyFlags = - ts.TypeFlags.Boolean | - ts.TypeFlags.BooleanLiteral | - ts.TypeFlags.Undefined | - ts.TypeFlags.Null | - ts.TypeFlags.Never | - ts.TypeFlags.Void | - ts.TypeFlags.Any; - - if (type.flags & falsyFlags) { - return true; - } else if (!strictNullChecks && !type.isLiteral()) { - return true; - } else if (type.isUnion()) { - return type.types.some(subType => canBeFalsy(context, subType)); - } else { - return false; - } -} - -function wrapInFunctionCall(expression: lua.Expression): lua.FunctionExpression { - const returnStatement = lua.createReturnStatement([expression]); - - return lua.createFunctionExpression( - lua.createBlock([returnStatement]), - undefined, - undefined, - lua.FunctionExpressionFlags.Inline - ); -} +import { canBeFalsy } from "../utils/typescript"; +import { truthyOnlyConditionalValue } from "../utils/diagnostics"; +import { LuaTarget } from "../../CompilerOptions"; function transformProtectedConditionalExpression( context: TransformationContext, - expression: ts.ConditionalExpression -): lua.CallExpression { - const condition = context.transformExpression(expression.condition); - const val1 = context.transformExpression(expression.whenTrue); - const val2 = context.transformExpression(expression.whenFalse); + expression: ts.ConditionalExpression, + condition: WithPrecedingStatements, + whenTrue: WithPrecedingStatements, + whenFalse: WithPrecedingStatements +): lua.Expression { + const tempVar = context.createTempNameForNode(expression.condition); - const val1Function = wrapInFunctionCall(val1); - const val2Function = wrapInFunctionCall(val2); + const trueStatements = whenTrue.precedingStatements.concat( + lua.createAssignmentStatement(lua.cloneIdentifier(tempVar), whenTrue.result, expression.whenTrue) + ); + + const falseStatements = whenFalse.precedingStatements.concat( + lua.createAssignmentStatement(lua.cloneIdentifier(tempVar), whenFalse.result, expression.whenFalse) + ); - // (condition and (() => v1) or (() => v2))() - const conditionAnd = lua.createBinaryExpression(condition, val1Function, lua.SyntaxKind.AndOperator); - const orExpression = lua.createBinaryExpression(conditionAnd, val2Function, lua.SyntaxKind.OrOperator); - return lua.createCallExpression(orExpression, [], expression); + context.addPrecedingStatements([ + lua.createVariableDeclarationStatement(tempVar, undefined, expression.condition), + ...condition.precedingStatements, + lua.createIfStatement( + condition.result, + lua.createBlock(trueStatements, expression.whenTrue), + lua.createBlock(falseStatements, expression.whenFalse), + expression + ), + ]); + return lua.cloneIdentifier(tempVar); } export const transformConditionalExpression: FunctionVisitor = (expression, context) => { - if (canBeFalsy(context, context.checker.getTypeAtLocation(expression.whenTrue))) { - return transformProtectedConditionalExpression(context, expression); + if (context.luaTarget === LuaTarget.Luau) { + // Luau's ternary operator doesn't have these issues + return lua.createConditionalExpression( + context.transformExpression(expression.condition), + context.transformExpression(expression.whenTrue), + context.transformExpression(expression.whenFalse), + expression + ); } - const condition = context.transformExpression(expression.condition); - const val1 = context.transformExpression(expression.whenTrue); - const val2 = context.transformExpression(expression.whenFalse); + // Check if we need to add diagnostic about Lua truthiness + checkOnlyTruthyCondition(expression.condition, context); + + const condition = transformInPrecedingStatementScope(context, () => + context.transformExpression(expression.condition) + ); + const whenTrue = transformInPrecedingStatementScope(context, () => + context.transformExpression(expression.whenTrue) + ); + const whenFalse = transformInPrecedingStatementScope(context, () => + context.transformExpression(expression.whenFalse) + ); + if ( + whenTrue.precedingStatements.length > 0 || + whenFalse.precedingStatements.length > 0 || + canBeFalsy(context, context.checker.getTypeAtLocation(expression.whenTrue)) + ) { + return transformProtectedConditionalExpression(context, expression, condition, whenTrue, whenFalse); + } // condition and v1 or v2 - const conditionAnd = lua.createBinaryExpression(condition, val1, lua.SyntaxKind.AndOperator); - return lua.createBinaryExpression(conditionAnd, val2, lua.SyntaxKind.OrOperator, expression); + context.addPrecedingStatements(condition.precedingStatements); + const conditionAnd = lua.createBinaryExpression(condition.result, whenTrue.result, lua.SyntaxKind.AndOperator); + return lua.createBinaryExpression(conditionAnd, whenFalse.result, lua.SyntaxKind.OrOperator, expression); }; export function transformIfStatement(statement: ts.IfStatement, context: TransformationContext): lua.IfStatement { - pushScope(context, ScopeType.Conditional); + context.pushScope(ScopeType.Conditional, statement); + + // Check if we need to add diagnostic about Lua truthiness + checkOnlyTruthyCondition(statement.expression, context); + const condition = context.transformExpression(statement.expression); const statements = performHoisting(context, transformBlockOrStatement(context, statement.thenStatement)); - popScope(context); + context.popScope(); const ifBlock = lua.createBlock(statements); if (statement.elseStatement) { if (ts.isIfStatement(statement.elseStatement)) { - const elseStatement = transformIfStatement(statement.elseStatement, context); - return lua.createIfStatement(condition, ifBlock, elseStatement); + const tsElseStatement = statement.elseStatement; + const { precedingStatements, result: elseStatement } = transformInPrecedingStatementScope(context, () => + transformIfStatement(tsElseStatement, context) + ); + // If else-if condition generates preceding statements, we can't use elseif, we have to break it down: + // if conditionA then + // ... + // else + // conditionB's preceding statements + // if conditionB then + // end + // end + if (precedingStatements.length > 0) { + const elseBlock = lua.createBlock([...precedingStatements, elseStatement]); + return lua.createIfStatement(condition, ifBlock, elseBlock); + } else { + return lua.createIfStatement(condition, ifBlock, elseStatement); + } } else { - pushScope(context, ScopeType.Conditional); + context.pushScope(ScopeType.Conditional, statement); const elseStatements = performHoisting( context, transformBlockOrStatement(context, statement.elseStatement) ); - popScope(context); + context.popScope(); const elseBlock = lua.createBlock(elseStatements); return lua.createIfStatement(condition, ifBlock, elseBlock); } @@ -94,3 +120,12 @@ export function transformIfStatement(statement: ts.IfStatement, context: Transfo return lua.createIfStatement(condition, ifBlock); } + +export function checkOnlyTruthyCondition(condition: ts.Expression, context: TransformationContext) { + if (context.options.strictNullChecks === false) return; // This check is not valid if everything could implicitly be nil + if (ts.isElementAccessExpression(condition)) return; // Array index could always implicitly return nil + + if (!canBeFalsy(context, context.checker.getTypeAtLocation(condition))) { + context.diagnostics.push(truthyOnlyConditionalValue(condition)); + } +} diff --git a/src/transformation/visitors/delete.ts b/src/transformation/visitors/delete.ts index 39bbf1d57..efa879c2e 100644 --- a/src/transformation/visitors/delete.ts +++ b/src/transformation/visitors/delete.ts @@ -5,8 +5,13 @@ import { transformLuaLibFunction, LuaLibFeature } from "../utils/lualib"; import { unsupportedProperty } from "../utils/diagnostics"; import { isArrayType, isNumberType } from "../utils/typescript"; import { addToNumericExpression } from "../utils/lua-ast"; +import { transformOptionalDeleteExpression } from "./optional-chaining"; export const transformDeleteExpression: FunctionVisitor = (node, context) => { + if (ts.isOptionalChain(node.expression)) { + return transformOptionalDeleteExpression(context, node, node.expression); + } + let ownerExpression: lua.Expression | undefined; let propertyExpression: lua.Expression | undefined; diff --git a/src/transformation/visitors/enum.ts b/src/transformation/visitors/enum.ts index 918e685f6..9de658a45 100644 --- a/src/transformation/visitors/enum.ts +++ b/src/transformation/visitors/enum.ts @@ -2,7 +2,7 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { FunctionVisitor, TransformationContext } from "../context"; import { AnnotationKind, getTypeAnnotations } from "../utils/annotations"; -import { getSymbolExportScope } from "../utils/export"; +import { addExportToIdentifier, getSymbolExportScope } from "../utils/export"; import { createLocalOrExportedOrGlobalDeclaration } from "../utils/lua-ast"; import { isFirstDeclaration } from "../utils/typescript"; import { transformIdentifier } from "./identifier"; @@ -32,7 +32,7 @@ export const transformEnumDeclaration: FunctionVisitor = (no if (!membersOnly && isFirstDeclaration(context, node)) { const name = transformIdentifier(context, node.name); const table = lua.createBinaryExpression( - lua.cloneIdentifier(name), + addExportToIdentifier(context, name), lua.createTableExpression(), lua.SyntaxKind.OrOperator ); @@ -60,9 +60,7 @@ export const transformEnumDeclaration: FunctionVisitor = (no } } - if (!valueExpression) { - valueExpression = context.transformExpression(member.initializer); - } + valueExpression ??= context.transformExpression(member.initializer); } else { valueExpression = lua.createNilLiteral(); } diff --git a/src/transformation/visitors/errors.ts b/src/transformation/visitors/errors.ts index e05ed55f3..77067dda8 100644 --- a/src/transformation/visitors/errors.ts +++ b/src/transformation/visitors/errors.ts @@ -1,22 +1,174 @@ import * as ts from "typescript"; +import { LuaLibFeature, LuaTarget } from "../.."; import * as lua from "../../LuaAST"; -import { FunctionVisitor } from "../context"; -import { isInTupleReturnFunction } from "../utils/annotations"; +import { FunctionVisitor, TransformationContext } from "../context"; +import { unsupportedForTarget, unsupportedForTargetButOverrideAvailable } from "../utils/diagnostics"; import { createUnpackCall } from "../utils/lua-ast"; -import { findScope, ScopeType } from "../utils/scope"; +import { transformLuaLibFunction } from "../utils/lualib"; +import { findScope, LoopContinued, Scope, ScopeType } from "../utils/scope"; +import { isInAsyncFunction, isInGeneratorFunction } from "../utils/typescript"; +import { wrapInAsyncAwaiter } from "./async-await"; import { transformScopeBlock } from "./block"; import { transformIdentifier } from "./identifier"; import { isInMultiReturnFunction } from "./language-extensions/multi"; +import { createReturnStatement } from "./return"; + +const transformAsyncTry: FunctionVisitor = (statement, context) => { + const [tryBlock, tryScope] = transformScopeBlock(context, statement.tryBlock, ScopeType.Try); + + if ( + (context.options.luaTarget === LuaTarget.Lua50 || context.options.luaTarget === LuaTarget.Lua51) && + !context.options.lua51AllowTryCatchInAsyncAwait + ) { + context.diagnostics.push( + unsupportedForTargetButOverrideAvailable( + statement, + "try/catch inside async functions", + LuaTarget.Lua51, + "lua51AllowTryCatchInAsyncAwait" + ) + ); + return tryBlock.statements; + } + + // __TS__AsyncAwaiter() + const awaiter = wrapInAsyncAwaiter(context, tryBlock.statements, false); + const awaiterIdentifier = lua.createIdentifier("____try"); + const awaiterDefinition = lua.createVariableDeclarationStatement(awaiterIdentifier, awaiter); + + // Transform catch/finally and collect scope info before building the result + let catchScope: Scope | undefined; + const chainCalls: lua.Statement[] = []; + + if (statement.catchClause) { + // ____try = ____try.catch() + const [catchFunction, cScope] = transformCatchClause(context, statement.catchClause); + catchScope = cScope; + if (catchFunction.params) { + catchFunction.params.unshift(lua.createAnonymousIdentifier()); + } + + const catchBodyStatements = catchFunction.body ? catchFunction.body.statements : []; + const asyncWrappedCatch = wrapInAsyncAwaiter(context, [...catchBodyStatements], false); + catchFunction.body = lua.createBlock([lua.createReturnStatement([asyncWrappedCatch])]); + + const awaiterCatch = lua.createTableIndexExpression(awaiterIdentifier, lua.createStringLiteral("catch")); + const catchCall = lua.createCallExpression(awaiterCatch, [awaiterIdentifier, catchFunction]); + chainCalls.push(lua.createAssignmentStatement(lua.cloneIdentifier(awaiterIdentifier), catchCall)); + } + + if (statement.finallyBlock) { + // ____try = ____try.finally() + const finallyStatements = context.transformStatements(statement.finallyBlock.statements); + const asyncWrappedFinally = wrapInAsyncAwaiter(context, finallyStatements, false); + const finallyFunction = lua.createFunctionExpression( + lua.createBlock([lua.createReturnStatement([asyncWrappedFinally])]) + ); + + const awaiterFinally = lua.createTableIndexExpression(awaiterIdentifier, lua.createStringLiteral("finally")); + const finallyCall = lua.createCallExpression( + awaiterFinally, + [awaiterIdentifier, finallyFunction], + statement.finallyBlock + ); + chainCalls.push(lua.createAssignmentStatement(lua.cloneIdentifier(awaiterIdentifier), finallyCall)); + } + + // __TS__Await(____try) + const promiseAwait = transformLuaLibFunction(context, LuaLibFeature.Await, statement, awaiterIdentifier); + chainCalls.push(lua.createExpressionStatement(promiseAwait, statement)); + + const hasReturn = tryScope.asyncTryHasReturn ?? catchScope?.asyncTryHasReturn; + const hasBreak = tryScope.asyncTryHasBreak ?? catchScope?.asyncTryHasBreak; + const hasContinue = tryScope.asyncTryHasContinue ?? catchScope?.asyncTryHasContinue; + + // Build result in output order: flag declarations, awaiter, chain calls, post-checks + const result: lua.Statement[] = []; + + if (hasReturn || hasBreak || hasContinue !== undefined) { + const flagDecls: lua.Identifier[] = []; + if (hasReturn) { + flagDecls.push(lua.createIdentifier("____hasReturned")); + flagDecls.push(lua.createIdentifier("____returnValue")); + } + if (hasBreak) { + flagDecls.push(lua.createIdentifier("____hasBroken")); + } + if (hasContinue !== undefined) { + flagDecls.push(lua.createIdentifier("____hasContinued")); + } + result.push(lua.createVariableDeclarationStatement(flagDecls)); + } + + result.push(awaiterDefinition); + result.push(...chainCalls); + + if (hasReturn) { + result.push( + lua.createIfStatement( + lua.createIdentifier("____hasReturned"), + lua.createBlock([createReturnStatement(context, [lua.createIdentifier("____returnValue")], statement)]) + ) + ); + } + + if (hasBreak) { + result.push( + lua.createIfStatement(lua.createIdentifier("____hasBroken"), lua.createBlock([lua.createBreakStatement()])) + ); + } + + if (hasContinue !== undefined) { + const loopScope = findScope(context, ScopeType.Loop); + const label = `__continue${loopScope?.id ?? ""}`; + + const continueStatements: lua.Statement[] = []; + switch (hasContinue) { + case LoopContinued.WithGoto: + continueStatements.push(lua.createGotoStatement(label)); + break; + case LoopContinued.WithContinue: + continueStatements.push(lua.createContinueStatement()); + break; + case LoopContinued.WithRepeatBreak: + continueStatements.push( + lua.createAssignmentStatement(lua.createIdentifier(label), lua.createBooleanLiteral(true)) + ); + continueStatements.push(lua.createBreakStatement()); + break; + } + + result.push( + lua.createIfStatement(lua.createIdentifier("____hasContinued"), lua.createBlock(continueStatements)) + ); + } + + return result; +}; export const transformTryStatement: FunctionVisitor = (statement, context) => { + if (isInAsyncFunction(statement)) { + return transformAsyncTry(statement, context); + } + const [tryBlock, tryScope] = transformScopeBlock(context, statement.tryBlock, ScopeType.Try); + if ( + (context.options.luaTarget === LuaTarget.Lua50 || context.options.luaTarget === LuaTarget.Lua51) && + isInGeneratorFunction(statement) + ) { + context.diagnostics.push( + unsupportedForTarget(statement, "try/catch inside generator functions", LuaTarget.Lua51) + ); + return tryBlock.statements; + } + const tryResultIdentifier = lua.createIdentifier("____try"); const returnValueIdentifier = lua.createIdentifier("____returnValue"); const result: lua.Statement[] = []; - let returnedIdentifier: lua.Identifier | undefined; + const returnedIdentifier = lua.createIdentifier("____hasReturned"); let returnCondition: lua.Expression | undefined; const pCall = lua.createIdentifier("pcall"); @@ -24,41 +176,38 @@ export const transformTryStatement: FunctionVisitor = (statemen if (statement.catchClause && statement.catchClause.block.statements.length > 0) { // try with catch - let [catchBlock, catchScope] = transformScopeBlock(context, statement.catchClause.block, ScopeType.Catch); - if (statement.catchClause.variableDeclaration) { - // Replace ____returned with catch variable - returnedIdentifier = transformIdentifier( - context, - statement.catchClause.variableDeclaration.name as ts.Identifier - ); - } else if (tryScope.functionReturned || catchScope.functionReturned) { - returnedIdentifier = lua.createIdentifier("____returned"); - } + const [catchFunction, catchScope] = transformCatchClause(context, statement.catchClause); + const catchIdentifier = lua.createIdentifier("____catch"); + result.push(lua.createVariableDeclarationStatement(catchIdentifier, catchFunction)); + + const hasReturn = tryScope.functionReturned ?? catchScope.functionReturned; const tryReturnIdentifiers = [tryResultIdentifier]; // ____try - if (returnedIdentifier) { - tryReturnIdentifiers.push(returnedIdentifier); // ____returned or catch variable - if (tryScope.functionReturned || catchScope.functionReturned) { + if (hasReturn || statement.catchClause.variableDeclaration) { + tryReturnIdentifiers.push(returnedIdentifier); // ____returned + if (hasReturn) { tryReturnIdentifiers.push(returnValueIdentifier); // ____returnValue returnCondition = lua.cloneIdentifier(returnedIdentifier); } } result.push(lua.createVariableDeclarationStatement(tryReturnIdentifiers, tryCall)); - if ((tryScope.functionReturned || catchScope.functionReturned) && returnedIdentifier) { - // Wrap catch in function if try or catch has return - const catchCall = lua.createCallExpression(lua.createFunctionExpression(catchBlock), []); - const catchAssign = lua.createAssignmentStatement( - [lua.cloneIdentifier(returnedIdentifier), lua.cloneIdentifier(returnValueIdentifier)], - catchCall - ); - catchBlock = lua.createBlock([catchAssign]); - } + const catchCall = lua.createCallExpression( + catchIdentifier, + statement.catchClause.variableDeclaration ? [lua.cloneIdentifier(returnedIdentifier)] : [] + ); + const catchCallStatement = hasReturn + ? lua.createAssignmentStatement( + [lua.cloneIdentifier(returnedIdentifier), lua.cloneIdentifier(returnValueIdentifier)], + catchCall + ) + : lua.createExpressionStatement(catchCall); + const notTryCondition = lua.createUnaryExpression(tryResultIdentifier, lua.SyntaxKind.NotOperator); - result.push(lua.createIfStatement(notTryCondition, catchBlock)); + result.push(lua.createIfStatement(notTryCondition, lua.createBlock([catchCallStatement]))); } else if (tryScope.functionReturned) { // try with return, but no catch - returnedIdentifier = lua.createIdentifier("____returned"); + // returnedIdentifier = lua.createIdentifier("____returned"); const returnedVariables = [tryResultIdentifier, returnedIdentifier, returnValueIdentifier]; result.push(lua.createVariableDeclarationStatement(returnedVariables, tryCall)); @@ -68,6 +217,10 @@ export const transformTryStatement: FunctionVisitor = (statemen returnedIdentifier, lua.SyntaxKind.AndOperator ); + } else if (statement.finallyBlock) { + // try without catch, but with finally — need to capture error for re-throw + const errorIdentifier = lua.createIdentifier("____error"); + result.push(lua.createVariableDeclarationStatement([tryResultIdentifier, errorIdentifier], tryCall)); } else { // try without return or catch result.push(lua.createExpressionStatement(tryCall)); @@ -77,25 +230,33 @@ export const transformTryStatement: FunctionVisitor = (statemen result.push(...context.transformStatements(statement.finallyBlock)); } + // Re-throw error if try had no catch but had a finally. + // On pcall failure the error is the second return value, which lands in + // ____hasReturned (when functionReturned) or ____error (otherwise). + if (!statement.catchClause && statement.finallyBlock) { + const notTryCondition = lua.createUnaryExpression( + lua.cloneIdentifier(tryResultIdentifier), + lua.SyntaxKind.NotOperator + ); + const errorIdentifier = tryScope.functionReturned + ? lua.cloneIdentifier(returnedIdentifier) + : lua.createIdentifier("____error"); + const rethrow = lua.createExpressionStatement( + lua.createCallExpression(lua.createIdentifier("error"), [errorIdentifier, lua.createNumericLiteral(0)]) + ); + result.push(lua.createIfStatement(notTryCondition, lua.createBlock([rethrow]))); + } + if (returnCondition && returnedIdentifier) { - // With catch clause: - // if ____returned then return ____returnValue end - // No catch clause: - // if ____try and ____returned then return ____returnValue end const returnValues: lua.Expression[] = []; - const parentTryCatch = findScope(context, ScopeType.Function | ScopeType.Try | ScopeType.Catch); - if (parentTryCatch && parentTryCatch.type !== ScopeType.Function) { - // Nested try/catch needs to prefix a 'true' return value - returnValues.push(lua.createBooleanLiteral(true)); - } - if (isInTupleReturnFunction(context, statement) || isInMultiReturnFunction(context, statement)) { + if (isInMultiReturnFunction(context, statement)) { returnValues.push(createUnpackCall(context, lua.cloneIdentifier(returnValueIdentifier))); } else { returnValues.push(lua.cloneIdentifier(returnValueIdentifier)); } - const returnStatement = lua.createReturnStatement(returnValues); + const returnStatement = createReturnStatement(context, returnValues, statement); const ifReturnedStatement = lua.createIfStatement(returnCondition, lua.createBlock([returnStatement])); result.push(ifReturnedStatement); } @@ -116,3 +277,20 @@ export const transformThrowStatement: FunctionVisitor = (stat statement ); }; + +function transformCatchClause( + context: TransformationContext, + catchClause: ts.CatchClause +): [lua.FunctionExpression, Scope] { + const [catchBlock, catchScope] = transformScopeBlock(context, catchClause.block, ScopeType.Catch); + + const catchParameter = catchClause.variableDeclaration + ? transformIdentifier(context, catchClause.variableDeclaration.name as ts.Identifier) + : undefined; + const catchFunction = lua.createFunctionExpression( + catchBlock, + catchParameter ? [lua.cloneIdentifier(catchParameter)] : [] + ); + + return [catchFunction, catchScope]; +} diff --git a/src/transformation/visitors/expression-list.ts b/src/transformation/visitors/expression-list.ts new file mode 100644 index 000000000..617d09d4a --- /dev/null +++ b/src/transformation/visitors/expression-list.ts @@ -0,0 +1,197 @@ +import assert = require("assert"); +import * as ts from "typescript"; +import * as lua from "../../LuaAST"; +import { TransformationContext, tempSymbolId } from "../context"; +import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; +import { transformInPrecedingStatementScope } from "../utils/preceding-statements"; +import { isConstIdentifier } from "../utils/typescript"; +import { isOptionalContinuation } from "./optional-chaining"; + +export function shouldMoveToTemp(context: TransformationContext, expression: lua.Expression, tsOriginal?: ts.Node) { + return ( + !lua.isLiteral(expression) && + !(lua.isIdentifier(expression) && expression.symbolId === tempSymbolId) && // Treat generated temps as consts + !( + tsOriginal && + (isConstIdentifier(context, tsOriginal) || + isOptionalContinuation(tsOriginal) || + tsOriginal.kind === ts.SyntaxKind.ThisKeyword) + ) + ); +} + +// Cache an expression in a preceding statement and return the temp identifier +export function moveToPrecedingTemp( + context: TransformationContext, + expression: lua.Expression, + tsOriginal?: ts.Node +): lua.Expression { + if (!shouldMoveToTemp(context, expression, tsOriginal)) { + return expression; + } + const tempIdentifier = context.createTempNameForLuaExpression(expression); + const tempDeclaration = lua.createVariableDeclarationStatement(tempIdentifier, expression, tsOriginal); + context.addPrecedingStatements(tempDeclaration); + return lua.cloneIdentifier(tempIdentifier, tsOriginal); +} + +function transformExpressions( + context: TransformationContext, + expressions: readonly ts.Expression[] +): { + transformedExpressions: lua.Expression[]; + precedingStatements: lua.Statement[][]; + lastPrecedingStatementsIndex: number; +} { + const precedingStatements: lua.Statement[][] = []; + const transformedExpressions: lua.Expression[] = []; + let lastPrecedingStatementsIndex = -1; + for (let i = 0; i < expressions.length; ++i) { + const { precedingStatements: expressionPrecedingStatements, result: expression } = + transformInPrecedingStatementScope(context, () => context.transformExpression(expressions[i])); + transformedExpressions.push(expression); + if (expressionPrecedingStatements.length > 0) { + lastPrecedingStatementsIndex = i; + } + precedingStatements.push(expressionPrecedingStatements); + } + return { transformedExpressions, precedingStatements, lastPrecedingStatementsIndex }; +} + +function transformExpressionsUsingTemps( + context: TransformationContext, + expressions: readonly ts.Expression[], + transformedExpressions: lua.Expression[], + precedingStatements: lua.Statement[][], + lastPrecedingStatementsIndex: number +) { + for (let i = 0; i < transformedExpressions.length; ++i) { + context.addPrecedingStatements(precedingStatements[i]); + if (i < lastPrecedingStatementsIndex) { + transformedExpressions[i] = moveToPrecedingTemp(context, transformedExpressions[i], expressions[i]); + } + } + return transformedExpressions; +} + +function pushToSparseArray( + context: TransformationContext, + arrayIdentifier: lua.Identifier | undefined, + expressions: lua.Expression[] +) { + if (!arrayIdentifier) { + arrayIdentifier = lua.createIdentifier(context.createTempName("array")); + const libCall = transformLuaLibFunction(context, LuaLibFeature.SparseArrayNew, undefined, ...expressions); + const declaration = lua.createVariableDeclarationStatement(arrayIdentifier, libCall); + context.addPrecedingStatements(declaration); + } else { + const libCall = transformLuaLibFunction( + context, + LuaLibFeature.SparseArrayPush, + undefined, + arrayIdentifier, + ...expressions + ); + context.addPrecedingStatements(lua.createExpressionStatement(libCall)); + } + return arrayIdentifier; +} + +function transformExpressionsUsingSparseArray( + context: TransformationContext, + expressions: readonly ts.Expression[], + transformedExpressions: lua.Expression[], + precedingStatements: lua.Statement[][] +) { + let arrayIdentifier: lua.Identifier | undefined; + + let expressionBatch: lua.Expression[] = []; + for (let i = 0; i < expressions.length; ++i) { + // Expressions with preceding statements should always be at the start of a batch + if (precedingStatements[i].length > 0 && expressionBatch.length > 0) { + arrayIdentifier = pushToSparseArray(context, arrayIdentifier, expressionBatch); + expressionBatch = []; + } + + context.addPrecedingStatements(precedingStatements[i]); + expressionBatch.push(transformedExpressions[i]); + + // Spread expressions should always be at the end of a batch + if (ts.isSpreadElement(expressions[i])) { + arrayIdentifier = pushToSparseArray(context, arrayIdentifier, expressionBatch); + expressionBatch = []; + } + } + + if (expressionBatch.length > 0) { + arrayIdentifier = pushToSparseArray(context, arrayIdentifier, expressionBatch); + } + + assert(arrayIdentifier); + return [transformLuaLibFunction(context, LuaLibFeature.SparseArraySpread, undefined, arrayIdentifier)]; +} + +function countNeededTemps( + context: TransformationContext, + expressions: readonly ts.Expression[], + transformedExpressions: lua.Expression[], + lastPrecedingStatementsIndex: number +) { + if (lastPrecedingStatementsIndex < 0) { + return 0; + } + return transformedExpressions + .slice(0, lastPrecedingStatementsIndex) + .filter((e, i) => shouldMoveToTemp(context, e, expressions[i])).length; +} + +// Transforms a list of expressions while flattening spreads and maintaining execution order +export function transformExpressionList( + context: TransformationContext, + expressions: readonly ts.Expression[] +): lua.Expression[] { + const { transformedExpressions, precedingStatements, lastPrecedingStatementsIndex } = transformExpressions( + context, + expressions + ); + + // If more than this number of temps are required to preserve execution order, we'll fall back to using the + // sparse array lib functions instead to prevent excessive locals. + const maxTemps = 2; + + // Use sparse array lib if there are spreads before the last expression + // or if too many temps are needed to preserve order + const lastSpread = expressions.findIndex(e => ts.isSpreadElement(e)); + if ( + (lastSpread >= 0 && lastSpread < expressions.length - 1) || + countNeededTemps(context, expressions, transformedExpressions, lastPrecedingStatementsIndex) > maxTemps + ) { + return transformExpressionsUsingSparseArray(context, expressions, transformedExpressions, precedingStatements); + } else { + return transformExpressionsUsingTemps( + context, + expressions, + transformedExpressions, + precedingStatements, + lastPrecedingStatementsIndex + ); + } +} + +// Transforms a series of expressions while maintaining execution order +export function transformOrderedExpressions( + context: TransformationContext, + expressions: readonly ts.Expression[] +): lua.Expression[] { + const { transformedExpressions, precedingStatements, lastPrecedingStatementsIndex } = transformExpressions( + context, + expressions + ); + return transformExpressionsUsingTemps( + context, + expressions, + transformedExpressions, + precedingStatements, + lastPrecedingStatementsIndex + ); +} diff --git a/src/transformation/visitors/expression-statement.ts b/src/transformation/visitors/expression-statement.ts index 228307f25..a247fd29b 100644 --- a/src/transformation/visitors/expression-statement.ts +++ b/src/transformation/visitors/expression-statement.ts @@ -1,26 +1,10 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; -import { FunctionVisitor } from "../context"; +import { FunctionVisitor, tempSymbolId } from "../context"; import { transformBinaryExpressionStatement } from "./binary-expression"; -import { - isTableDeleteCall, - isTableSetCall, - transformTableDeleteExpression, - transformTableSetExpression, -} from "./language-extensions/table"; import { transformUnaryExpressionStatement } from "./unary-expression"; export const transformExpressionStatement: FunctionVisitor = (node, context) => { - const expression = node.expression; - - if (ts.isCallExpression(expression) && isTableDeleteCall(context, expression)) { - return transformTableDeleteExpression(context, expression); - } - - if (ts.isCallExpression(expression) && isTableSetCall(context, expression)) { - return transformTableSetExpression(context, expression); - } - const unaryExpressionResult = transformUnaryExpressionStatement(context, node); if (unaryExpressionResult) { return unaryExpressionResult; @@ -31,9 +15,22 @@ export const transformExpressionStatement: FunctionVisitor context.transformExpression(value) + ); + if (!lua.isNilLiteral(parameterValue)) { + statements.push(lua.createAssignmentStatement(parameterName, parameterValue)); + } + if (statements.length === 0) return undefined; const nilCondition = lua.createBinaryExpression( parameterName, @@ -32,7 +43,7 @@ function transformParameterDefaultValueDeclaration( lua.SyntaxKind.EqualityOperator ); - const ifBlock = lua.createBlock([assignment]); + const ifBlock = lua.createBlock(statements, tsOriginal); return lua.createIfStatement(nilCondition, ifBlock, undefined, tsOriginal); } @@ -48,10 +59,48 @@ function isRestParameterReferenced(identifier: lua.Identifier, scope: Scope): bo return references !== undefined && references.length > 0; } +export function createCallableTable(functionExpression: lua.Expression): lua.Expression { + // __call metamethod receives the table as the first argument, so we need to add a dummy parameter + if (lua.isFunctionExpression(functionExpression)) { + functionExpression.params?.unshift(lua.createAnonymousIdentifier()); + } else { + // functionExpression may have been replaced (lib functions, etc...), + // so we create a forwarding function to eat the extra argument + functionExpression = lua.createFunctionExpression( + lua.createBlock([ + lua.createReturnStatement([lua.createCallExpression(functionExpression, [lua.createDotsLiteral()])]), + ]), + [lua.createAnonymousIdentifier()], + lua.createDotsLiteral(), + lua.NodeFlags.Inline + ); + } + return lua.createCallExpression(lua.createIdentifier("setmetatable"), [ + lua.createTableExpression(), + lua.createTableExpression([ + lua.createTableFieldExpression(functionExpression, lua.createStringLiteral("__call")), + ]), + ]); +} + +export function isFunctionTypeWithProperties(context: TransformationContext, functionType: ts.Type): boolean { + if (functionType.isUnion()) { + return functionType.types.some(t => isFunctionTypeWithProperties(context, t)); + } else { + return ( + isFunctionType(functionType) && + functionType.getProperties().length > 0 && + getExtensionKindForType(context, functionType) === undefined // ignore TSTL extension functions like $range + ); + } +} + export function transformFunctionBodyContent(context: TransformationContext, body: ts.ConciseBody): lua.Statement[] { if (!ts.isBlock(body)) { - const returnStatement = transformExpressionBodyToReturnStatement(context, body); - return [returnStatement]; + const { precedingStatements, result: returnStatement } = transformInPrecedingStatementScope(context, () => + transformExpressionBodyToReturnStatement(context, body) + ); + return [...precedingStatements, returnStatement]; } const bodyStatements = performHoisting(context, context.transformStatements(body.statements)); @@ -64,7 +113,7 @@ export function transformFunctionBodyHeader( parameters: ts.NodeArray, spreadIdentifier?: lua.Identifier ): lua.Statement[] { - const headerStatements = []; + const headerStatements: lua.Statement[] = []; // Add default parameters and object binding patterns const bindingPatternDeclarations: lua.Statement[] = []; @@ -74,28 +123,35 @@ export function transformFunctionBodyHeader( const identifier = lua.createIdentifier(`____bindingPattern${bindPatternIndex++}`); if (declaration.initializer !== undefined) { // Default binding parameter - headerStatements.push( - transformParameterDefaultValueDeclaration(context, identifier, declaration.initializer) + const initializer = transformParameterDefaultValueDeclaration( + context, + identifier, + declaration.initializer ); + if (initializer) headerStatements.push(initializer); } // Binding pattern - bindingPatternDeclarations.push(...transformBindingPattern(context, declaration.name, identifier)); + const name = declaration.name; + const { precedingStatements, result: bindings } = transformInPrecedingStatementScope(context, () => + transformBindingPattern(context, name, identifier) + ); + bindingPatternDeclarations.push(...precedingStatements, ...bindings); } else if (declaration.initializer !== undefined) { // Default parameter - headerStatements.push( - transformParameterDefaultValueDeclaration( - context, - transformIdentifier(context, declaration.name), - declaration.initializer - ) + const initializer = transformParameterDefaultValueDeclaration( + context, + transformIdentifier(context, declaration.name), + declaration.initializer ); + if (initializer) headerStatements.push(initializer); } } // Push spread operator here if (spreadIdentifier && isRestParameterReferenced(spreadIdentifier, bodyScope)) { - const spreadTable = wrapInTable(lua.createDotsLiteral()); + const spreadTable = + context.luaTarget === LuaTarget.Lua50 ? lua.createArgLiteral() : wrapInTable(lua.createDotsLiteral()); headerStatements.push(lua.createVariableDeclarationStatement(spreadIdentifier, spreadTable)); } @@ -109,14 +165,16 @@ export function transformFunctionBody( context: TransformationContext, parameters: ts.NodeArray, body: ts.ConciseBody, - spreadIdentifier?: lua.Identifier, - node?: ts.FunctionLikeDeclaration + node: ts.FunctionLikeDeclaration, + spreadIdentifier?: lua.Identifier ): [lua.Statement[], Scope] { - const scope = pushScope(context, ScopeType.Function); - scope.node = node; - const bodyStatements = transformFunctionBodyContent(context, body); + const scope = context.pushScope(ScopeType.Function, node); + let bodyStatements = transformFunctionBodyContent(context, body); + if (node && isAsyncFunction(node)) { + bodyStatements = [lua.createReturnStatement([wrapInAsyncAwaiter(context, bodyStatements)])]; + } const headerStatements = transformFunctionBodyHeader(context, scope, parameters, spreadIdentifier); - popScope(context); + context.popScope(); return [[...headerStatements, ...bodyStatements], scope]; } @@ -137,7 +195,7 @@ export function transformParameters( // Only push parameter name to paramName array if it isn't a spread parameter for (const param of parameters) { - if (ts.isIdentifier(param.name) && param.name.originalKeywordKind === ts.SyntaxKind.ThisKeyword) { + if (ts.isIdentifier(param.name) && ts.identifierToKeywordKind(param.name) === ts.SyntaxKind.ThisKeyword) { continue; } @@ -167,9 +225,21 @@ export function transformFunctionToExpression( ): [lua.Expression, Scope] { assert(node.body); + if (node.asteriskToken && isAsyncFunction(node)) { + context.diagnostics.push(unsupportedAsyncGenerator(node)); + } + const type = context.checker.getTypeAtLocation(node); let functionContext: lua.Identifier | undefined; - if (getFunctionContextType(context, type) !== ContextType.Void) { + + const firstParam = node.parameters[0]; + const hasThisVoidParameter = + firstParam && + ts.isIdentifier(firstParam.name) && + ts.identifierToKeywordKind(firstParam.name) === ts.SyntaxKind.ThisKeyword && + firstParam.type?.kind === ts.SyntaxKind.VoidKeyword; + + if (!hasThisVoidParameter && getFunctionContextType(context, type) !== ContextType.Void) { if (ts.isArrowFunction(node)) { // dummy context for arrow functions with parameters if (node.parameters.length > 0) { @@ -181,10 +251,10 @@ export function transformFunctionToExpression( } } - let flags = lua.FunctionExpressionFlags.None; - if (!ts.isBlock(node.body)) flags |= lua.FunctionExpressionFlags.Inline; + let flags = lua.NodeFlags.None; + if (!ts.isBlock(node.body)) flags |= lua.NodeFlags.Inline; if (ts.isFunctionDeclaration(node) || ts.isMethodDeclaration(node)) { - flags |= lua.FunctionExpressionFlags.Declaration; + flags |= lua.NodeFlags.Declaration; } const [paramNames, dotsLiteral, spreadIdentifier] = transformParameters(context, node.parameters, functionContext); @@ -192,9 +262,10 @@ export function transformFunctionToExpression( context, node.parameters, node.body, - spreadIdentifier, - node + node, + spreadIdentifier ); + const functionExpression = lua.createFunctionExpression( lua.createBlock(transformedBody), paramNames, @@ -222,8 +293,9 @@ export function transformFunctionLikeDeclaration( const [functionExpression, functionScope] = transformFunctionToExpression(context, node); + const isNamedFunctionExpression = ts.isFunctionExpression(node) && node.name; // Handle named function expressions which reference themselves - if (ts.isFunctionExpression(node) && node.name && functionScope.referencedSymbols) { + if (isNamedFunctionExpression && functionScope.referencedSymbols) { const symbol = context.checker.getSymbolAtLocation(node.name); if (symbol) { // TODO: Not using symbol ids because of https://github.com/microsoft/TypeScript/issues/37131 @@ -231,22 +303,27 @@ export function transformFunctionLikeDeclaration( nodes.some(n => context.checker.getSymbolAtLocation(n)?.valueDeclaration === symbol.valueDeclaration) ); - // Only wrap if the name is actually referenced inside the function + // Only handle if the name is actually referenced inside the function if (isReferenced) { const nameIdentifier = transformIdentifier(context, node.name); - // We cannot use transformToImmediatelyInvokedFunctionExpression() here because we need to transpile - // the function first to determine if it's self-referencing. Fortunately, this does not cause issues - // with var-arg optimization because the IIFE is just wrapping another function which will already push - // another scope. - return createImmediatelyInvokedFunctionExpression( - [lua.createVariableDeclarationStatement(nameIdentifier, functionExpression)], - lua.cloneIdentifier(nameIdentifier) - ); + if (isFunctionTypeWithProperties(context, context.checker.getTypeAtLocation(node))) { + context.addPrecedingStatements([ + lua.createVariableDeclarationStatement(nameIdentifier), + lua.createAssignmentStatement(nameIdentifier, createCallableTable(functionExpression)), + ]); + } else { + context.addPrecedingStatements( + lua.createVariableDeclarationStatement(nameIdentifier, functionExpression) + ); + } + return lua.cloneIdentifier(nameIdentifier); } } } - return functionExpression; + return isNamedFunctionExpression && isFunctionTypeWithProperties(context, context.checker.getTypeAtLocation(node)) + ? createCallableTable(functionExpression) + : functionExpression; } export const transformFunctionDeclaration: FunctionVisitor = (node, context) => { @@ -270,15 +347,19 @@ export const transformFunctionDeclaration: FunctionVisitor = (expression, context) => { diff --git a/src/transformation/visitors/identifier.ts b/src/transformation/visitors/identifier.ts index 17706848c..3b2e016e4 100644 --- a/src/transformation/visitors/identifier.ts +++ b/src/transformation/visitors/identifier.ts @@ -1,80 +1,134 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; -import { transformBuiltinIdentifierExpression } from "../builtins"; -import { FunctionVisitor, TransformationContext } from "../context"; -import { AnnotationKind, isForRangeType } from "../utils/annotations"; -import { - invalidMultiFunctionUse, - invalidOperatorMappingUse, - invalidRangeUse, - invalidVarargUse, - invalidTableExtensionUse, - annotationRemoved, -} from "../utils/diagnostics"; +import { transformBuiltinIdentifierExpression, checkForLuaLibType } from "../builtins"; +import { isPromiseClass, createPromiseIdentifier } from "../builtins/promise"; +import { FunctionVisitor, tempSymbolId, TransformationContext } from "../context"; +import { invalidCallExtensionUse } from "../utils/diagnostics"; import { createExportedIdentifier, getSymbolExportScope } from "../utils/export"; import { createSafeName, hasUnsafeIdentifierName } from "../utils/safe-names"; import { getIdentifierSymbolId } from "../utils/symbols"; -import { isMultiFunctionNode } from "./language-extensions/multi"; -import { isOperatorMapping } from "./language-extensions/operators"; -import { isRangeFunctionNode } from "./language-extensions/range"; -import { isTableExtensionIdentifier } from "./language-extensions/table"; -import { isVarargConstantNode } from "./language-extensions/vararg"; +import { getOptionalContinuationData, isOptionalContinuation } from "./optional-chaining"; +import { isStandardLibraryType } from "../utils/typescript"; +import { getExtensionKindForNode, getExtensionKindForSymbol } from "../utils/language-extensions"; +import { callExtensions } from "./language-extensions/call-extension"; +import { isIdentifierExtensionValue, reportInvalidExtensionValue } from "./language-extensions/identifier"; +import { Annotation, AnnotationKind, getNodeAnnotations } from "../utils/annotations"; export function transformIdentifier(context: TransformationContext, identifier: ts.Identifier): lua.Identifier { - if (isMultiFunctionNode(context, identifier)) { - context.diagnostics.push(invalidMultiFunctionUse(identifier)); - return lua.createAnonymousIdentifier(identifier); - } + return transformNonValueIdentifier(context, identifier, context.checker.getSymbolAtLocation(identifier)); +} - if (isOperatorMapping(context, identifier)) { - context.diagnostics.push(invalidOperatorMappingUse(identifier)); - } +export function getCustomNameFromSymbol(context: TransformationContext, symbol?: ts.Symbol): undefined | string { + let retVal: undefined | string; + + if (symbol) { + const declarations = symbol.getDeclarations(); + if (declarations) { + let customNameAnnotation: undefined | Annotation = undefined; + for (const declaration of declarations) { + const nodeAnnotations = getNodeAnnotations(declaration); + const foundAnnotation = nodeAnnotations.get(AnnotationKind.CustomName); + + if (foundAnnotation) { + customNameAnnotation = foundAnnotation; + break; + } + + // If the symbol is an imported value, check the original declaration + // beware of declaration.propertyName, this is the import name alias and should not be renamed! + if (ts.isImportSpecifier(declaration) && !declaration.propertyName) { + const importedType = context.checker.getTypeAtLocation(declaration); + if (importedType.symbol) { + const importedCustomName = getCustomNameFromSymbol(context, importedType.symbol); + if (importedCustomName) { + return importedCustomName; + } + } + } + } - if (isTableExtensionIdentifier(context, identifier)) { - context.diagnostics.push(invalidTableExtensionUse(identifier)); + if (customNameAnnotation) { + retVal = customNameAnnotation.args[0]; + } + } } - if (isRangeFunctionNode(context, identifier)) { - context.diagnostics.push(invalidRangeUse(identifier)); - return lua.createAnonymousIdentifier(identifier); + return retVal; +} + +function transformNonValueIdentifier( + context: TransformationContext, + identifier: ts.Identifier, + symbol: ts.Symbol | undefined +) { + if (isOptionalContinuation(identifier)) { + const result = lua.createIdentifier(identifier.text, undefined, tempSymbolId); + getOptionalContinuationData(identifier)!.usedIdentifiers.push(result); + return result; } - if (isVarargConstantNode(context, identifier)) { - context.diagnostics.push(invalidVarargUse(identifier)); - return lua.createAnonymousIdentifier(identifier); + const extensionKind = symbol + ? getExtensionKindForSymbol(context, symbol) + : getExtensionKindForNode(context, identifier); + + if (extensionKind) { + if (callExtensions.has(extensionKind)) { + // Avoid putting duplicate diagnostic on the name of a variable declaration, due to the inferred type + if (!(ts.isVariableDeclaration(identifier.parent) && identifier.parent.name === identifier)) { + context.diagnostics.push(invalidCallExtensionUse(identifier)); + } + // fall through + } else if (isIdentifierExtensionValue(symbol, extensionKind)) { + reportInvalidExtensionValue(context, identifier, extensionKind); + return lua.createAnonymousIdentifier(identifier); + } } - if (isForRangeType(context, identifier)) { - context.diagnostics.push(annotationRemoved(identifier, AnnotationKind.ForRange)); + const type = context.checker.getTypeAtLocation(identifier); + if (isStandardLibraryType(context, type, undefined)) { + checkForLuaLibType(context, type); + if (isPromiseClass(context, identifier)) { + return createPromiseIdentifier(identifier); + } } - const text = hasUnsafeIdentifierName(context, identifier) ? createSafeName(identifier.text) : identifier.text; + let text = hasUnsafeIdentifierName(context, identifier, symbol) ? createSafeName(identifier.text) : identifier.text; - const symbolId = getIdentifierSymbolId(context, identifier); + const customName = getCustomNameFromSymbol(context, symbol); + if (customName) text = customName; + + const symbolId = getIdentifierSymbolId(context, identifier, symbol); return lua.createIdentifier(text, identifier, symbolId, identifier.text); } -export const transformIdentifierExpression: FunctionVisitor = (node, context) => { - const symbol = context.checker.getSymbolAtLocation(node); +export function transformIdentifierWithSymbol( + context: TransformationContext, + node: ts.Identifier, + symbol: ts.Symbol | undefined +): lua.Expression { if (symbol) { const exportScope = getSymbolExportScope(context, symbol); if (exportScope) { const name = symbol.name; - const text = hasUnsafeIdentifierName(context, node) ? createSafeName(name) : name; - const symbolId = getIdentifierSymbolId(context, node); + const text = hasUnsafeIdentifierName(context, node, symbol) ? createSafeName(name) : name; + const symbolId = getIdentifierSymbolId(context, node, symbol); const identifier = lua.createIdentifier(text, node, symbolId, name); return createExportedIdentifier(context, identifier, exportScope); } } - - if (node.originalKeywordKind === ts.SyntaxKind.UndefinedKeyword) { - return lua.createNilLiteral(); - } - - const builtinResult = transformBuiltinIdentifierExpression(context, node); + const builtinResult = transformBuiltinIdentifierExpression(context, node, symbol); if (builtinResult) { return builtinResult; } - return transformIdentifier(context, node); + return transformNonValueIdentifier(context, node, symbol); +} + +export const transformIdentifierExpression: FunctionVisitor = (node, context) => { + if (ts.identifierToKeywordKind(node) === ts.SyntaxKind.UndefinedKeyword) { + return lua.createNilLiteral(node); + } + + const symbol = context.checker.getSymbolAtLocation(node); + return transformIdentifierWithSymbol(context, node, symbol); }; diff --git a/src/transformation/visitors/index.ts b/src/transformation/visitors/index.ts index b9983ae31..466f5ed35 100644 --- a/src/transformation/visitors/index.ts +++ b/src/transformation/visitors/index.ts @@ -40,6 +40,8 @@ import { transformTypeOfExpression } from "./typeof"; import { typescriptVisitors } from "./typescript"; import { transformPostfixUnaryExpression, transformPrefixUnaryExpression } from "./unary-expression"; import { transformVariableStatement } from "./variable-declaration"; +import { transformAwaitExpression } from "./async-await"; +import { transformVoidExpression } from "./void"; const transformEmptyStatement: FunctionVisitor = () => undefined; const transformParenthesizedExpression: FunctionVisitor = (node, context) => @@ -49,6 +51,7 @@ export const standardVisitors: Visitors = { ...literalVisitors, ...typescriptVisitors, [ts.SyntaxKind.ArrowFunction]: transformFunctionLikeDeclaration, + [ts.SyntaxKind.AwaitExpression]: transformAwaitExpression, [ts.SyntaxKind.BinaryExpression]: transformBinaryExpression, [ts.SyntaxKind.Block]: transformBlock, [ts.SyntaxKind.BreakStatement]: transformBreakStatement, @@ -96,4 +99,5 @@ export const standardVisitors: Visitors = { [ts.SyntaxKind.VariableStatement]: transformVariableStatement, [ts.SyntaxKind.WhileStatement]: transformWhileStatement, [ts.SyntaxKind.YieldExpression]: transformYieldExpression, + [ts.SyntaxKind.VoidExpression]: transformVoidExpression, }; diff --git a/src/transformation/visitors/language-extensions/call-extension.ts b/src/transformation/visitors/language-extensions/call-extension.ts new file mode 100644 index 000000000..49c05b72e --- /dev/null +++ b/src/transformation/visitors/language-extensions/call-extension.ts @@ -0,0 +1,34 @@ +import { TransformationContext } from "../../context"; +import * as ts from "typescript"; +import { ExtensionKind, getExtensionKindForNode } from "../../utils/language-extensions"; +import * as lua from "../../../LuaAST"; +import { operatorExtensionTransformers } from "./operators"; +import { tableExtensionTransformers, tableNewExtensions } from "./table"; + +const allCallExtensionHandlers: LanguageExtensionCallTransformerMap = { + ...operatorExtensionTransformers, + ...tableExtensionTransformers, +}; +export const callExtensions = new Set(Object.keys(allCallExtensionHandlers) as ExtensionKind[]); +tableNewExtensions.forEach(kind => callExtensions.add(kind)); + +export type LanguageExtensionCallTransformer = ( + context: TransformationContext, + node: ts.CallExpression, + extensionKind: ExtensionKind +) => lua.Expression; + +export type LanguageExtensionCallTransformerMap = { + [P in ExtensionKind]?: LanguageExtensionCallTransformer; +}; +export function transformLanguageExtensionCallExpression( + context: TransformationContext, + node: ts.CallExpression +): lua.Expression | undefined { + const extensionKind = getExtensionKindForNode(context, node.expression); + if (!extensionKind) return; + const transformer = allCallExtensionHandlers[extensionKind]; + if (transformer) { + return transformer(context, node, extensionKind); + } +} diff --git a/src/transformation/visitors/language-extensions/identifier.ts b/src/transformation/visitors/language-extensions/identifier.ts new file mode 100644 index 000000000..945126e6d --- /dev/null +++ b/src/transformation/visitors/language-extensions/identifier.ts @@ -0,0 +1,27 @@ +import * as ts from "typescript"; +import { ExtensionKind } from "../../utils/language-extensions"; +import { TransformationContext } from "../../context"; +import { invalidMultiFunctionUse, invalidRangeUse, invalidVarargUse } from "../../utils/diagnostics"; + +const extensionKindToValueName: { [T in ExtensionKind]?: string } = { + [ExtensionKind.MultiFunction]: "$multi", + [ExtensionKind.RangeFunction]: "$range", + [ExtensionKind.VarargConstant]: "$vararg", +}; +export function isIdentifierExtensionValue(symbol: ts.Symbol | undefined, extensionKind: ExtensionKind): boolean { + return symbol !== undefined && extensionKindToValueName[extensionKind] === symbol.name; +} + +export function reportInvalidExtensionValue( + context: TransformationContext, + identifier: ts.Identifier, + extensionKind: ExtensionKind +): void { + if (extensionKind === ExtensionKind.MultiFunction) { + context.diagnostics.push(invalidMultiFunctionUse(identifier)); + } else if (extensionKind === ExtensionKind.RangeFunction) { + context.diagnostics.push(invalidRangeUse(identifier)); + } else if (extensionKind === ExtensionKind.VarargConstant) { + context.diagnostics.push(invalidVarargUse(identifier)); + } +} diff --git a/src/transformation/visitors/language-extensions/iterable.ts b/src/transformation/visitors/language-extensions/iterable.ts index 769e15093..ee1ded7be 100644 --- a/src/transformation/visitors/language-extensions/iterable.ts +++ b/src/transformation/visitors/language-extensions/iterable.ts @@ -1,34 +1,23 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; -import * as extensions from "../../utils/language-extensions"; import { TransformationContext } from "../../context"; import { getVariableDeclarationBinding, transformForInitializer } from "../loops/utils"; import { transformArrayBindingElement } from "../variable-declaration"; -import { invalidMultiIterableWithoutDestructuring } from "../../utils/diagnostics"; +import { + invalidMultiIterableWithoutDestructuring, + invalidPairsIterableWithoutDestructuring, +} from "../../utils/diagnostics"; import { cast } from "../../../utils"; import { isMultiReturnType } from "./multi"; -export function isIterableType(type: ts.Type): boolean { - return extensions.isExtensionType(type, extensions.ExtensionKind.IterableType); -} - -export function returnsIterableType(context: TransformationContext, node: ts.CallExpression): boolean { - const signature = context.checker.getResolvedSignature(node); - const type = signature?.getReturnType(); - return type ? isIterableType(type) : false; -} - -export function isIterableExpression(context: TransformationContext, expression: ts.Expression): boolean { - const type = context.checker.getTypeAtLocation(expression); - return isIterableType(type); -} - function transformForOfMultiIterableStatement( context: TransformationContext, statement: ts.ForOfStatement, - block: lua.Block + block: lua.Block, + luaIterator: lua.Expression, + invalidMultiUseDiagnostic: (node: ts.Node) => ts.Diagnostic ): lua.Statement { - const luaIterator = context.transformExpression(statement.expression); + context.pushPrecedingStatements(); let identifiers: lua.Identifier[] = []; if (ts.isVariableDeclarationList(statement.initializer)) { @@ -38,7 +27,7 @@ function transformForOfMultiIterableStatement( if (ts.isArrayBindingPattern(binding)) { identifiers = binding.elements.map(e => transformArrayBindingElement(context, e)); } else { - context.diagnostics.push(invalidMultiIterableWithoutDestructuring(binding)); + context.diagnostics.push(invalidMultiUseDiagnostic(binding)); } } else if (ts.isArrayLiteralExpression(statement.initializer)) { // Variables NOT declared in for loop - catch iterator values in temps and assign @@ -56,13 +45,15 @@ function transformForOfMultiIterableStatement( ); } } else { - context.diagnostics.push(invalidMultiIterableWithoutDestructuring(statement.initializer)); + context.diagnostics.push(invalidMultiUseDiagnostic(statement.initializer)); } if (identifiers.length === 0) { identifiers.push(lua.createAnonymousIdentifier()); } + block.statements.unshift(...context.popPrecedingStatements()); + return lua.createForInStatement(block, identifiers, [luaIterator], statement); } @@ -71,12 +62,53 @@ export function transformForOfIterableStatement( statement: ts.ForOfStatement, block: lua.Block ): lua.Statement { - const type = context.checker.getTypeAtLocation(statement.expression); - if (type.aliasTypeArguments?.length === 2 && isMultiReturnType(type.aliasTypeArguments[0])) { - return transformForOfMultiIterableStatement(context, statement, block); + const iteratedExpressionType = context.checker.getTypeAtLocation(statement.expression); + const iterableType = + iteratedExpressionType.isIntersection() && + iteratedExpressionType.types.find(t => t.symbol.escapedName === "Iterable"); + const iterableTypeArguments = (iterableType as ts.TypeReference)?.typeArguments; + + if (iterableTypeArguments && iterableTypeArguments.length > 0 && isMultiReturnType(iterableTypeArguments[0])) { + const luaIterator = context.transformExpression(statement.expression); + return transformForOfMultiIterableStatement( + context, + statement, + block, + luaIterator, + invalidMultiIterableWithoutDestructuring + ); } const luaIterator = context.transformExpression(statement.expression); const identifier = transformForInitializer(context, statement.initializer, block); return lua.createForInStatement(block, [identifier], [luaIterator], statement); } + +export function transformForOfPairsIterableStatement( + context: TransformationContext, + statement: ts.ForOfStatement, + block: lua.Block +): lua.Statement { + const pairsCall = lua.createCallExpression(lua.createIdentifier("pairs"), [ + context.transformExpression(statement.expression), + ]); + return transformForOfMultiIterableStatement( + context, + statement, + block, + pairsCall, + invalidPairsIterableWithoutDestructuring + ); +} + +export function transformForOfPairsKeyIterableStatement( + context: TransformationContext, + statement: ts.ForOfStatement, + block: lua.Block +): lua.Statement { + const pairsCall = lua.createCallExpression(lua.createIdentifier("pairs"), [ + context.transformExpression(statement.expression), + ]); + const identifier = transformForInitializer(context, statement.initializer, block); + return lua.createForInStatement(block, [identifier], [pairsCall], statement); +} diff --git a/src/transformation/visitors/language-extensions/multi.ts b/src/transformation/visitors/language-extensions/multi.ts index 85b3c6a29..d9d7cbc02 100644 --- a/src/transformation/visitors/language-extensions/multi.ts +++ b/src/transformation/visitors/language-extensions/multi.ts @@ -1,12 +1,24 @@ import * as ts from "typescript"; import * as extensions from "../../utils/language-extensions"; +import { + getExtensionKindForNode, + getIterableExtensionKindForNode, + IterableExtensionKind, +} from "../../utils/language-extensions"; import { TransformationContext } from "../../context"; -import { findFirstNodeAbove } from "../../utils/typescript"; -import { isIterableExpression } from "./iterable"; -import { invalidMultiFunctionUse } from "../../utils/diagnostics"; +import { findFirstNodeAbove, findFirstNonOuterParent } from "../../utils/typescript"; +const multiReturnExtensionName = "__tstlMultiReturn"; export function isMultiReturnType(type: ts.Type): boolean { - return extensions.isExtensionType(type, extensions.ExtensionKind.MultiType); + return type.getProperty(multiReturnExtensionName) !== undefined; +} + +export function canBeMultiReturnType(type: ts.Type): boolean { + return ( + (type.flags & ts.TypeFlags.Any) !== 0 || + isMultiReturnType(type) || + (type.isUnion() && type.types.some(t => canBeMultiReturnType(t))) + ); } export function isMultiFunctionCall(context: TransformationContext, expression: ts.CallExpression): boolean { @@ -24,8 +36,11 @@ export function isMultiReturnCall(context: TransformationContext, expression: ts } export function isMultiFunctionNode(context: TransformationContext, node: ts.Node): boolean { - const symbol = context.checker.getSymbolAtLocation(node); - return symbol ? extensions.isExtensionValue(context, symbol, extensions.ExtensionKind.MultiFunction) : false; + return ( + ts.isIdentifier(node) && + node.text === "$multi" && + getExtensionKindForNode(context, node) === extensions.ExtensionKind.MultiFunction + ); } export function isInMultiReturnFunction(context: TransformationContext, node: ts.Node) { @@ -43,67 +58,52 @@ export function shouldMultiReturnCallBeWrapped(context: TransformationContext, n return false; } + const parent = findFirstNonOuterParent(node); + // Variable declaration with destructuring - if (ts.isVariableDeclaration(node.parent) && ts.isArrayBindingPattern(node.parent.name)) { + if (ts.isVariableDeclaration(parent) && ts.isArrayBindingPattern(parent.name)) { return false; } // Variable assignment with destructuring if ( - ts.isBinaryExpression(node.parent) && - node.parent.operatorToken.kind === ts.SyntaxKind.EqualsToken && - ts.isArrayLiteralExpression(node.parent.left) + ts.isBinaryExpression(parent) && + parent.operatorToken.kind === ts.SyntaxKind.EqualsToken && + ts.isArrayLiteralExpression(parent.left) ) { return false; } // Spread operator - if (ts.isSpreadElement(node.parent)) { + if (ts.isSpreadElement(parent)) { return false; } // Stand-alone expression - if (ts.isExpressionStatement(node.parent)) { + if (ts.isExpressionStatement(parent)) { return false; } // Forwarded multi-return call if ( - (ts.isReturnStatement(node.parent) || ts.isArrowFunction(node.parent)) && // Body-less arrow func + (ts.isReturnStatement(parent) || ts.isArrowFunction(parent)) && // Body-less arrow func isInMultiReturnFunction(context, node) ) { return false; } // Element access expression 'foo()[0]' will be optimized using 'select' - if (ts.isElementAccessExpression(node.parent)) { + if (ts.isElementAccessExpression(parent)) { return false; } // LuaIterable in for...of - if (ts.isForOfStatement(node.parent) && isIterableExpression(context, node)) { + if ( + ts.isForOfStatement(parent) && + getIterableExtensionKindForNode(context, node) === IterableExtensionKind.Iterable + ) { return false; } return true; } - -export function findMultiAssignmentViolations( - context: TransformationContext, - node: ts.ObjectLiteralExpression -): ts.Node[] { - const result: ts.Node[] = []; - - for (const element of node.properties) { - if (!ts.isShorthandPropertyAssignment(element)) continue; - const valueSymbol = context.checker.getShorthandAssignmentValueSymbol(element); - if (valueSymbol) { - if (extensions.isExtensionValue(context, valueSymbol, extensions.ExtensionKind.MultiFunction)) { - context.diagnostics.push(invalidMultiFunctionUse(element)); - result.push(element); - } - } - } - - return result; -} diff --git a/src/transformation/visitors/language-extensions/operators.ts b/src/transformation/visitors/language-extensions/operators.ts index 5cc039d31..4cb08abc2 100644 --- a/src/transformation/visitors/language-extensions/operators.ts +++ b/src/transformation/visitors/language-extensions/operators.ts @@ -1,138 +1,125 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; import { TransformationContext } from "../../context"; -import * as extensions from "../../utils/language-extensions"; import { assert } from "../../../utils"; -import { getFunctionTypeForCall } from "../../utils/typescript"; import { LuaTarget } from "../../../CompilerOptions"; import { unsupportedForTarget } from "../../utils/diagnostics"; +import { ExtensionKind, getBinaryCallExtensionArgs, getUnaryCallExtensionArg } from "../../utils/language-extensions"; +import { LanguageExtensionCallTransformerMap } from "./call-extension"; +import { transformOrderedExpressions } from "../expression-list"; -const binaryOperatorMappings = new Map([ - [extensions.ExtensionKind.AdditionOperatorType, lua.SyntaxKind.AdditionOperator], - [extensions.ExtensionKind.AdditionOperatorMethodType, lua.SyntaxKind.AdditionOperator], - [extensions.ExtensionKind.SubtractionOperatorType, lua.SyntaxKind.SubtractionOperator], - [extensions.ExtensionKind.SubtractionOperatorMethodType, lua.SyntaxKind.SubtractionOperator], - [extensions.ExtensionKind.MultiplicationOperatorType, lua.SyntaxKind.MultiplicationOperator], - [extensions.ExtensionKind.MultiplicationOperatorMethodType, lua.SyntaxKind.MultiplicationOperator], - [extensions.ExtensionKind.DivisionOperatorType, lua.SyntaxKind.DivisionOperator], - [extensions.ExtensionKind.DivisionOperatorMethodType, lua.SyntaxKind.DivisionOperator], - [extensions.ExtensionKind.ModuloOperatorType, lua.SyntaxKind.ModuloOperator], - [extensions.ExtensionKind.ModuloOperatorMethodType, lua.SyntaxKind.ModuloOperator], - [extensions.ExtensionKind.PowerOperatorType, lua.SyntaxKind.PowerOperator], - [extensions.ExtensionKind.PowerOperatorMethodType, lua.SyntaxKind.PowerOperator], - [extensions.ExtensionKind.FloorDivisionOperatorType, lua.SyntaxKind.FloorDivisionOperator], - [extensions.ExtensionKind.FloorDivisionOperatorMethodType, lua.SyntaxKind.FloorDivisionOperator], - [extensions.ExtensionKind.BitwiseAndOperatorType, lua.SyntaxKind.BitwiseAndOperator], - [extensions.ExtensionKind.BitwiseAndOperatorMethodType, lua.SyntaxKind.BitwiseAndOperator], - [extensions.ExtensionKind.BitwiseOrOperatorType, lua.SyntaxKind.BitwiseOrOperator], - [extensions.ExtensionKind.BitwiseOrOperatorMethodType, lua.SyntaxKind.BitwiseOrOperator], - [extensions.ExtensionKind.BitwiseExclusiveOrOperatorType, lua.SyntaxKind.BitwiseExclusiveOrOperator], - [extensions.ExtensionKind.BitwiseExclusiveOrOperatorMethodType, lua.SyntaxKind.BitwiseExclusiveOrOperator], - [extensions.ExtensionKind.BitwiseLeftShiftOperatorType, lua.SyntaxKind.BitwiseLeftShiftOperator], - [extensions.ExtensionKind.BitwiseLeftShiftOperatorMethodType, lua.SyntaxKind.BitwiseLeftShiftOperator], - [extensions.ExtensionKind.BitwiseRightShiftOperatorType, lua.SyntaxKind.BitwiseRightShiftOperator], - [extensions.ExtensionKind.BitwiseRightShiftOperatorMethodType, lua.SyntaxKind.BitwiseRightShiftOperator], - [extensions.ExtensionKind.ConcatOperatorType, lua.SyntaxKind.ConcatOperator], - [extensions.ExtensionKind.ConcatOperatorMethodType, lua.SyntaxKind.ConcatOperator], - [extensions.ExtensionKind.LessThanOperatorType, lua.SyntaxKind.LessThanOperator], - [extensions.ExtensionKind.LessThanOperatorMethodType, lua.SyntaxKind.LessThanOperator], - [extensions.ExtensionKind.GreaterThanOperatorType, lua.SyntaxKind.GreaterThanOperator], - [extensions.ExtensionKind.GreaterThanOperatorMethodType, lua.SyntaxKind.GreaterThanOperator], +const binaryOperatorMappings = new Map([ + [ExtensionKind.AdditionOperatorType, lua.SyntaxKind.AdditionOperator], + [ExtensionKind.AdditionOperatorMethodType, lua.SyntaxKind.AdditionOperator], + [ExtensionKind.SubtractionOperatorType, lua.SyntaxKind.SubtractionOperator], + [ExtensionKind.SubtractionOperatorMethodType, lua.SyntaxKind.SubtractionOperator], + [ExtensionKind.MultiplicationOperatorType, lua.SyntaxKind.MultiplicationOperator], + [ExtensionKind.MultiplicationOperatorMethodType, lua.SyntaxKind.MultiplicationOperator], + [ExtensionKind.DivisionOperatorType, lua.SyntaxKind.DivisionOperator], + [ExtensionKind.DivisionOperatorMethodType, lua.SyntaxKind.DivisionOperator], + [ExtensionKind.ModuloOperatorType, lua.SyntaxKind.ModuloOperator], + [ExtensionKind.ModuloOperatorMethodType, lua.SyntaxKind.ModuloOperator], + [ExtensionKind.PowerOperatorType, lua.SyntaxKind.PowerOperator], + [ExtensionKind.PowerOperatorMethodType, lua.SyntaxKind.PowerOperator], + [ExtensionKind.FloorDivisionOperatorType, lua.SyntaxKind.FloorDivisionOperator], + [ExtensionKind.FloorDivisionOperatorMethodType, lua.SyntaxKind.FloorDivisionOperator], + [ExtensionKind.BitwiseAndOperatorType, lua.SyntaxKind.BitwiseAndOperator], + [ExtensionKind.BitwiseAndOperatorMethodType, lua.SyntaxKind.BitwiseAndOperator], + [ExtensionKind.BitwiseOrOperatorType, lua.SyntaxKind.BitwiseOrOperator], + [ExtensionKind.BitwiseOrOperatorMethodType, lua.SyntaxKind.BitwiseOrOperator], + [ExtensionKind.BitwiseExclusiveOrOperatorType, lua.SyntaxKind.BitwiseExclusiveOrOperator], + [ExtensionKind.BitwiseExclusiveOrOperatorMethodType, lua.SyntaxKind.BitwiseExclusiveOrOperator], + [ExtensionKind.BitwiseLeftShiftOperatorType, lua.SyntaxKind.BitwiseLeftShiftOperator], + [ExtensionKind.BitwiseLeftShiftOperatorMethodType, lua.SyntaxKind.BitwiseLeftShiftOperator], + [ExtensionKind.BitwiseRightShiftOperatorType, lua.SyntaxKind.BitwiseRightShiftOperator], + [ExtensionKind.BitwiseRightShiftOperatorMethodType, lua.SyntaxKind.BitwiseRightShiftOperator], + [ExtensionKind.ConcatOperatorType, lua.SyntaxKind.ConcatOperator], + [ExtensionKind.ConcatOperatorMethodType, lua.SyntaxKind.ConcatOperator], + [ExtensionKind.LessThanOperatorType, lua.SyntaxKind.LessThanOperator], + [ExtensionKind.LessThanOperatorMethodType, lua.SyntaxKind.LessThanOperator], + [ExtensionKind.GreaterThanOperatorType, lua.SyntaxKind.GreaterThanOperator], + [ExtensionKind.GreaterThanOperatorMethodType, lua.SyntaxKind.GreaterThanOperator], ]); -const unaryOperatorMappings = new Map([ - [extensions.ExtensionKind.NegationOperatorType, lua.SyntaxKind.NegationOperator], - [extensions.ExtensionKind.NegationOperatorMethodType, lua.SyntaxKind.NegationOperator], - [extensions.ExtensionKind.BitwiseNotOperatorType, lua.SyntaxKind.BitwiseNotOperator], - [extensions.ExtensionKind.BitwiseNotOperatorMethodType, lua.SyntaxKind.BitwiseNotOperator], - [extensions.ExtensionKind.LengthOperatorType, lua.SyntaxKind.LengthOperator], - [extensions.ExtensionKind.LengthOperatorMethodType, lua.SyntaxKind.LengthOperator], +const unaryOperatorMappings = new Map([ + [ExtensionKind.NegationOperatorType, lua.SyntaxKind.NegationOperator], + [ExtensionKind.NegationOperatorMethodType, lua.SyntaxKind.NegationOperator], + [ExtensionKind.BitwiseNotOperatorType, lua.SyntaxKind.BitwiseNotOperator], + [ExtensionKind.BitwiseNotOperatorMethodType, lua.SyntaxKind.BitwiseNotOperator], + [ExtensionKind.LengthOperatorType, lua.SyntaxKind.LengthOperator], + [ExtensionKind.LengthOperatorMethodType, lua.SyntaxKind.LengthOperator], ]); -const operatorMapExtensions = [...binaryOperatorMappings.keys(), ...unaryOperatorMappings.keys()]; +const bitwiseOperatorMapExtensions = new Set([ + ExtensionKind.BitwiseAndOperatorType, + ExtensionKind.BitwiseAndOperatorMethodType, + ExtensionKind.BitwiseOrOperatorType, + ExtensionKind.BitwiseOrOperatorMethodType, + ExtensionKind.BitwiseExclusiveOrOperatorType, + ExtensionKind.BitwiseExclusiveOrOperatorMethodType, + ExtensionKind.BitwiseLeftShiftOperatorType, + ExtensionKind.BitwiseLeftShiftOperatorMethodType, + ExtensionKind.BitwiseRightShiftOperatorType, + ExtensionKind.BitwiseRightShiftOperatorMethodType, + ExtensionKind.BitwiseNotOperatorType, + ExtensionKind.BitwiseNotOperatorMethodType, +]); -const bitwiseOperatorMapExtensions = new Set([ - extensions.ExtensionKind.BitwiseAndOperatorType, - extensions.ExtensionKind.BitwiseAndOperatorMethodType, - extensions.ExtensionKind.BitwiseOrOperatorType, - extensions.ExtensionKind.BitwiseOrOperatorMethodType, - extensions.ExtensionKind.BitwiseExclusiveOrOperatorType, - extensions.ExtensionKind.BitwiseExclusiveOrOperatorMethodType, - extensions.ExtensionKind.BitwiseLeftShiftOperatorType, - extensions.ExtensionKind.BitwiseLeftShiftOperatorMethodType, - extensions.ExtensionKind.BitwiseRightShiftOperatorType, - extensions.ExtensionKind.BitwiseRightShiftOperatorMethodType, - extensions.ExtensionKind.BitwiseNotOperatorType, - extensions.ExtensionKind.BitwiseNotOperatorMethodType, +const requiresLua53 = new Set([ + ...bitwiseOperatorMapExtensions, + ExtensionKind.FloorDivisionOperatorType, + ExtensionKind.FloorDivisionOperatorMethodType, ]); -function getOperatorMapExtensionKindForCall(context: TransformationContext, node: ts.CallExpression) { - const type = getFunctionTypeForCall(context, node); - return type && operatorMapExtensions.find(extensionKind => extensions.isExtensionType(type, extensionKind)); +export const operatorExtensionTransformers: LanguageExtensionCallTransformerMap = {}; +for (const kind of binaryOperatorMappings.keys()) { + operatorExtensionTransformers[kind] = transformBinaryOperator; +} +for (const kind of unaryOperatorMappings.keys()) { + operatorExtensionTransformers[kind] = transformUnaryOperator; } -export function isOperatorMapping(context: TransformationContext, node: ts.CallExpression | ts.Identifier) { - if (ts.isCallExpression(node)) { - return getOperatorMapExtensionKindForCall(context, node) !== undefined; - } else { - const type = context.checker.getTypeAtLocation(node); - return operatorMapExtensions.some(extensionKind => extensions.isExtensionType(type, extensionKind)); - } +function transformBinaryOperator(context: TransformationContext, node: ts.CallExpression, kind: ExtensionKind) { + if (requiresLua53.has(kind)) checkHasLua53(context, node, kind); + + const args = getBinaryCallExtensionArgs(context, node, kind); + if (!args) return lua.createNilLiteral(); + + const [left, right] = transformOrderedExpressions(context, args); + + const luaOperator = binaryOperatorMappings.get(kind); + assert(luaOperator); + return lua.createBinaryExpression(left, right, luaOperator); } -export function transformOperatorMappingExpression( - context: TransformationContext, - node: ts.CallExpression -): lua.Expression { - const extensionKind = getOperatorMapExtensionKindForCall(context, node); - assert(extensionKind); +function transformUnaryOperator(context: TransformationContext, node: ts.CallExpression, kind: ExtensionKind) { + if (requiresLua53.has(kind)) checkHasLua53(context, node, kind); + const arg = getUnaryCallExtensionArg(context, node, kind); + if (!arg) return lua.createNilLiteral(); + + const luaOperator = unaryOperatorMappings.get(kind); + assert(luaOperator); + return lua.createUnaryExpression(context.transformExpression(arg), luaOperator); +} + +function checkHasLua53(context: TransformationContext, node: ts.CallExpression, kind: ExtensionKind) { const isBefore53 = + context.luaTarget === LuaTarget.Lua50 || context.luaTarget === LuaTarget.Lua51 || context.luaTarget === LuaTarget.Lua52 || context.luaTarget === LuaTarget.LuaJIT || context.luaTarget === LuaTarget.Universal; if (isBefore53) { const luaTarget = context.luaTarget === LuaTarget.Universal ? LuaTarget.Lua51 : context.luaTarget; - if (bitwiseOperatorMapExtensions.has(extensionKind)) { - context.diagnostics.push(unsupportedForTarget(node, "Native bitwise operations", luaTarget)); - } else if ( - extensionKind === extensions.ExtensionKind.FloorDivisionOperatorType || - extensionKind === extensions.ExtensionKind.FloorDivisionOperatorMethodType - ) { - context.diagnostics.push(unsupportedForTarget(node, "Floor division operator", luaTarget)); - } - } - - const args = node.arguments.slice(); - if (binaryOperatorMappings.has(extensionKind)) { if ( - args.length === 1 && - (ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression)) + kind === ExtensionKind.FloorDivisionOperatorType || + kind === ExtensionKind.FloorDivisionOperatorMethodType ) { - args.unshift(node.expression.expression); - } - - const luaOperator = binaryOperatorMappings.get(extensionKind); - assert(luaOperator); - return lua.createBinaryExpression( - context.transformExpression(args[0]), - context.transformExpression(args[1]), - luaOperator - ); - } else { - let arg: ts.Expression; - if ( - args.length === 0 && - (ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression)) - ) { - arg = node.expression.expression; + context.diagnostics.push(unsupportedForTarget(node, "Floor division operator", luaTarget)); } else { - arg = args[0]; + // is bitwise operator + context.diagnostics.push(unsupportedForTarget(node, "Native bitwise operations", luaTarget)); } - - const luaOperator = unaryOperatorMappings.get(extensionKind); - assert(luaOperator); - return lua.createUnaryExpression(context.transformExpression(arg), luaOperator); } } diff --git a/src/transformation/visitors/language-extensions/range.ts b/src/transformation/visitors/language-extensions/range.ts index 43f297a9a..00647180c 100644 --- a/src/transformation/visitors/language-extensions/range.ts +++ b/src/transformation/visitors/language-extensions/range.ts @@ -7,14 +7,18 @@ import { transformIdentifier } from "../identifier"; import { transformArguments } from "../call"; import { assert } from "../../../utils"; import { invalidRangeControlVariable } from "../../utils/diagnostics"; +import { getExtensionKindForNode } from "../../utils/language-extensions"; export function isRangeFunction(context: TransformationContext, expression: ts.CallExpression): boolean { return isRangeFunctionNode(context, expression.expression); } export function isRangeFunctionNode(context: TransformationContext, node: ts.Node): boolean { - const symbol = context.checker.getSymbolAtLocation(node); - return symbol ? extensions.isExtensionValue(context, symbol, extensions.ExtensionKind.RangeFunction) : false; + return ( + ts.isIdentifier(node) && + node.text === "$range" && + getExtensionKindForNode(context, node) === extensions.ExtensionKind.RangeFunction + ); } function getControlVariable(context: TransformationContext, statement: ts.ForOfStatement) { diff --git a/src/transformation/visitors/language-extensions/table.ts b/src/transformation/visitors/language-extensions/table.ts index 695bcd3b1..e42e3f8c7 100644 --- a/src/transformation/visitors/language-extensions/table.ts +++ b/src/transformation/visitors/language-extensions/table.ts @@ -1,124 +1,83 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; import { TransformationContext } from "../../context"; -import * as extensions from "../../utils/language-extensions"; -import { getFunctionTypeForCall } from "../../utils/typescript"; -import { assert } from "../../../utils"; - -const tableDeleteExtensions = [ - extensions.ExtensionKind.TableDeleteType, - extensions.ExtensionKind.TableDeleteMethodType, -]; - -const tableGetExtensions = [extensions.ExtensionKind.TableGetType, extensions.ExtensionKind.TableGetMethodType]; - -const tableHasExtensions = [extensions.ExtensionKind.TableHasType, extensions.ExtensionKind.TableHasMethodType]; - -const tableSetExtensions = [extensions.ExtensionKind.TableSetType, extensions.ExtensionKind.TableSetMethodType]; - -const tableExtensions = [ - extensions.ExtensionKind.TableNewType, - ...tableDeleteExtensions, - ...tableGetExtensions, - ...tableHasExtensions, - ...tableSetExtensions, -]; - -function getTableExtensionKindForCall( - context: TransformationContext, - node: ts.CallExpression, - validExtensions: extensions.ExtensionKind[] -) { - const type = getFunctionTypeForCall(context, node); - return type && validExtensions.find(extensionKind => extensions.isExtensionType(type, extensionKind)); -} - -export function isTableExtensionIdentifier(context: TransformationContext, node: ts.Identifier) { - const type = context.checker.getTypeAtLocation(node); - return tableExtensions.some(extensionKind => extensions.isExtensionType(type, extensionKind)); -} - -export function isTableDeleteCall(context: TransformationContext, node: ts.CallExpression) { - return getTableExtensionKindForCall(context, node, tableDeleteExtensions) !== undefined; -} - -export function isTableGetCall(context: TransformationContext, node: ts.CallExpression) { - return getTableExtensionKindForCall(context, node, tableGetExtensions) !== undefined; -} - -export function isTableHasCall(context: TransformationContext, node: ts.CallExpression) { - return getTableExtensionKindForCall(context, node, tableHasExtensions) !== undefined; -} - -export function isTableSetCall(context: TransformationContext, node: ts.CallExpression) { - return getTableExtensionKindForCall(context, node, tableSetExtensions) !== undefined; -} +import { + ExtensionKind, + getBinaryCallExtensionArgs, + getExtensionKindForNode, + getNaryCallExtensionArgs, + getUnaryCallExtensionArg, +} from "../../utils/language-extensions"; +import { transformOrderedExpressions } from "../expression-list"; +import { LanguageExtensionCallTransformerMap } from "./call-extension"; export function isTableNewCall(context: TransformationContext, node: ts.NewExpression) { - const type = context.checker.getTypeAtLocation(node.expression); - return extensions.isExtensionType(type, extensions.ExtensionKind.TableNewType); + return getExtensionKindForNode(context, node.expression) === ExtensionKind.TableNewType; } -export function transformTableDeleteExpression(context: TransformationContext, node: ts.CallExpression): lua.Statement { - const extensionKind = getTableExtensionKindForCall(context, node, tableDeleteExtensions); - assert(extensionKind); - - const args = node.arguments.slice(); - if ( - args.length === 1 && - (ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression)) - ) { - // In case of method (no table argument), push method owner to front of args list - args.unshift(node.expression.expression); +export const tableNewExtensions = [ExtensionKind.TableNewType]; + +export const tableExtensionTransformers: LanguageExtensionCallTransformerMap = { + [ExtensionKind.TableDeleteType]: transformTableDeleteExpression, + [ExtensionKind.TableDeleteMethodType]: transformTableDeleteExpression, + [ExtensionKind.TableGetType]: transformTableGetExpression, + [ExtensionKind.TableGetMethodType]: transformTableGetExpression, + [ExtensionKind.TableHasType]: transformTableHasExpression, + [ExtensionKind.TableHasMethodType]: transformTableHasExpression, + [ExtensionKind.TableSetType]: transformTableSetExpression, + [ExtensionKind.TableSetMethodType]: transformTableSetExpression, + [ExtensionKind.TableAddKeyType]: transformTableAddKeyExpression, + [ExtensionKind.TableAddKeyMethodType]: transformTableAddKeyExpression, + [ExtensionKind.TableIsEmptyType]: transformTableIsEmptyExpression, + [ExtensionKind.TableIsEmptyMethodType]: transformTableIsEmptyExpression, +}; + +function transformTableDeleteExpression( + context: TransformationContext, + node: ts.CallExpression, + extensionKind: ExtensionKind +): lua.Expression { + const args = getBinaryCallExtensionArgs(context, node, extensionKind); + if (!args) { + return lua.createNilLiteral(); } + const [table, key] = transformOrderedExpressions(context, args); // arg0[arg1] = nil - return lua.createAssignmentStatement( - lua.createTableIndexExpression(context.transformExpression(args[0]), context.transformExpression(args[1])), - lua.createNilLiteral(), - node + context.addPrecedingStatements( + lua.createAssignmentStatement(lua.createTableIndexExpression(table, key), lua.createNilLiteral(), node) ); + return lua.createBooleanLiteral(true); } -export function transformTableGetExpression(context: TransformationContext, node: ts.CallExpression): lua.Expression { - const extensionKind = getTableExtensionKindForCall(context, node, tableGetExtensions); - assert(extensionKind); - - const args = node.arguments.slice(); - if ( - args.length === 1 && - (ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression)) - ) { - // In case of method (no table argument), push method owner to front of args list - args.unshift(node.expression.expression); +function transformTableGetExpression( + context: TransformationContext, + node: ts.CallExpression, + extensionKind: ExtensionKind +): lua.Expression { + const args = getBinaryCallExtensionArgs(context, node, extensionKind); + if (!args) { + return lua.createNilLiteral(); } + const [table, key] = transformOrderedExpressions(context, args); // arg0[arg1] - return lua.createTableIndexExpression( - context.transformExpression(args[0]), - context.transformExpression(args[1]), - node - ); + return lua.createTableIndexExpression(table, key, node); } -export function transformTableHasExpression(context: TransformationContext, node: ts.CallExpression): lua.Expression { - const extensionKind = getTableExtensionKindForCall(context, node, tableHasExtensions); - assert(extensionKind); - - const args = node.arguments.slice(); - if ( - args.length === 1 && - (ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression)) - ) { - // In case of method (no table argument), push method owner to front of args list - args.unshift(node.expression.expression); +function transformTableHasExpression( + context: TransformationContext, + node: ts.CallExpression, + extensionKind: ExtensionKind +): lua.Expression { + const args = getBinaryCallExtensionArgs(context, node, extensionKind); + if (!args) { + return lua.createNilLiteral(); } + const [table, key] = transformOrderedExpressions(context, args); // arg0[arg1] - const tableIndexExpression = lua.createTableIndexExpression( - context.transformExpression(args[0]), - context.transformExpression(args[1]) - ); + const tableIndexExpression = lua.createTableIndexExpression(table, key); // arg0[arg1] ~= nil return lua.createBinaryExpression( @@ -129,23 +88,58 @@ export function transformTableHasExpression(context: TransformationContext, node ); } -export function transformTableSetExpression(context: TransformationContext, node: ts.CallExpression): lua.Statement { - const extensionKind = getTableExtensionKindForCall(context, node, tableSetExtensions); - assert(extensionKind); - - const args = node.arguments.slice(); - if ( - args.length === 2 && - (ts.isPropertyAccessExpression(node.expression) || ts.isElementAccessExpression(node.expression)) - ) { - // In case of method (no table argument), push method owner to front of args list - args.unshift(node.expression.expression); +function transformTableSetExpression( + context: TransformationContext, + node: ts.CallExpression, + extensionKind: ExtensionKind +): lua.Expression { + const args = getNaryCallExtensionArgs(context, node, extensionKind, 3); + if (!args) { + return lua.createNilLiteral(); } + const [table, key, value] = transformOrderedExpressions(context, args); // arg0[arg1] = arg2 - return lua.createAssignmentStatement( - lua.createTableIndexExpression(context.transformExpression(args[0]), context.transformExpression(args[1])), - context.transformExpression(args[2]), + context.addPrecedingStatements( + lua.createAssignmentStatement(lua.createTableIndexExpression(table, key), value, node) + ); + return lua.createNilLiteral(); +} + +function transformTableAddKeyExpression( + context: TransformationContext, + node: ts.CallExpression, + extensionKind: ExtensionKind +): lua.Expression { + const args = getNaryCallExtensionArgs(context, node, extensionKind, 2); + if (!args) { + return lua.createNilLiteral(); + } + + const [table, key] = transformOrderedExpressions(context, args); + // arg0[arg1] = true + context.addPrecedingStatements( + lua.createAssignmentStatement(lua.createTableIndexExpression(table, key), lua.createBooleanLiteral(true), node) + ); + return lua.createNilLiteral(); +} + +function transformTableIsEmptyExpression( + context: TransformationContext, + node: ts.CallExpression, + extensionKind: ExtensionKind +): lua.Expression { + const args = getUnaryCallExtensionArg(context, node, extensionKind); + if (!args) { + return lua.createNilLiteral(); + } + + const table = context.transformExpression(args); + // next(arg0) == nil + return lua.createBinaryExpression( + lua.createCallExpression(lua.createIdentifier("next"), [table], node), + lua.createNilLiteral(), + lua.SyntaxKind.EqualityOperator, node ); } diff --git a/src/transformation/visitors/language-extensions/vararg.ts b/src/transformation/visitors/language-extensions/vararg.ts index 99eea82a9..4576737c8 100644 --- a/src/transformation/visitors/language-extensions/vararg.ts +++ b/src/transformation/visitors/language-extensions/vararg.ts @@ -1,16 +1,20 @@ import * as ts from "typescript"; import { TransformationContext } from "../../context"; import * as extensions from "../../utils/language-extensions"; +import { getExtensionKindForSymbol } from "../../utils/language-extensions"; import { Scope, ScopeType } from "../../utils/scope"; export function isGlobalVarargConstant(context: TransformationContext, symbol: ts.Symbol, scope: Scope) { + return scope.type === ScopeType.File && isVarargConstantSymbol(context, symbol); +} +function isVarargConstantSymbol(context: TransformationContext, symbol: ts.Symbol) { return ( - scope.type === ScopeType.File && - extensions.isExtensionValue(context, symbol, extensions.ExtensionKind.VarargConstant) + symbol.getName() === "$vararg" && + getExtensionKindForSymbol(context, symbol) === extensions.ExtensionKind.VarargConstant ); } export function isVarargConstantNode(context: TransformationContext, node: ts.Node): boolean { const symbol = context.checker.getSymbolAtLocation(node); - return symbol ? extensions.isExtensionValue(context, symbol, extensions.ExtensionKind.VarargConstant) : false; + return symbol !== undefined && isVarargConstantSymbol(context, symbol); } diff --git a/src/transformation/visitors/literal.ts b/src/transformation/visitors/literal.ts index 8fa85e4e4..1d91c89d9 100644 --- a/src/transformation/visitors/literal.ts +++ b/src/transformation/visitors/literal.ts @@ -2,15 +2,14 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { assertNever } from "../../utils"; import { FunctionVisitor, TransformationContext, Visitors } from "../context"; -import { unsupportedAccessorInObjectLiteral, invalidMultiFunctionUse } from "../utils/diagnostics"; -import { createExportedIdentifier, getSymbolExportScope } from "../utils/export"; +import { undefinedInArrayLiteral, unsupportedAccessorInObjectLiteral } from "../utils/diagnostics"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { createSafeName, hasUnsafeIdentifierName, hasUnsafeSymbolName } from "../utils/safe-names"; -import { getSymbolIdOfSymbol, trackSymbolReference } from "../utils/symbols"; +import { trackSymbolReference } from "../utils/symbols"; import { isArrayType } from "../utils/typescript"; import { transformFunctionLikeDeclaration } from "./function"; -import { flattenSpreadExpressions } from "./call"; -import { findMultiAssignmentViolations } from "./language-extensions/multi"; +import { moveToPrecedingTemp, transformExpressionList } from "./expression-list"; +import { transformIdentifierWithSymbol } from "./identifier"; +import { LuaTarget } from "../../CompilerOptions"; // TODO: Move to object-literal.ts? export function transformPropertyName(context: TransformationContext, node: ts.PropertyName): lua.Expression { @@ -30,54 +29,53 @@ export function createShorthandIdentifier( valueSymbol: ts.Symbol | undefined, propertyIdentifier: ts.Identifier ): lua.Expression { - const propertyName = propertyIdentifier.text; - - const isUnsafeName = valueSymbol - ? hasUnsafeSymbolName(context, valueSymbol, propertyIdentifier) - : hasUnsafeIdentifierName(context, propertyIdentifier, false); - - const name = isUnsafeName ? createSafeName(propertyName) : propertyName; - - let identifier = context.transformExpression(ts.factory.createIdentifier(name)); - lua.setNodeOriginal(identifier, propertyIdentifier); - if (valueSymbol !== undefined && lua.isIdentifier(identifier)) { - identifier.symbolId = getSymbolIdOfSymbol(context, valueSymbol); - - const exportScope = getSymbolExportScope(context, valueSymbol); - if (exportScope) { - identifier = createExportedIdentifier(context, identifier, exportScope); - } - } - - return identifier; + return transformIdentifierWithSymbol(context, propertyIdentifier, valueSymbol); } -const transformNumericLiteralExpression: FunctionVisitor = expression => { +const transformNumericLiteralExpression: FunctionVisitor = (expression, context) => { if (expression.text === "Infinity") { - const math = lua.createIdentifier("math"); - const huge = lua.createStringLiteral("huge"); - return lua.createTableIndexExpression(math, huge, expression); + if (context.luaTarget === LuaTarget.Lua50) { + const one = lua.createNumericLiteral(1); + const zero = lua.createNumericLiteral(0); + return lua.createBinaryExpression(one, zero, lua.SyntaxKind.DivisionOperator); + } else { + const math = lua.createIdentifier("math"); + const huge = lua.createStringLiteral("huge"); + return lua.createTableIndexExpression(math, huge, expression); + } } return lua.createNumericLiteral(Number(expression.text), expression); }; const transformObjectLiteralExpression: FunctionVisitor = (expression, context) => { - const violations = findMultiAssignmentViolations(context, expression); - if (violations.length > 0) { - context.diagnostics.push(...violations.map(e => invalidMultiFunctionUse(e))); - return lua.createNilLiteral(expression); - } + const properties: lua.Expression[] = []; + const initializers: ts.Node[] = []; + const keyPrecedingStatements: lua.Statement[][] = []; + const valuePrecedingStatements: lua.Statement[][] = []; + let lastPrecedingStatementsIndex = -1; - let properties: lua.TableFieldExpression[] = []; - const tableExpressions: lua.Expression[] = []; + for (let i = 0; i < expression.properties.length; ++i) { + const element = expression.properties[i]; + + // Transform key and cache preceding statements + context.pushPrecedingStatements(); - for (const element of expression.properties) { const name = element.name ? transformPropertyName(context, element.name) : undefined; + let precedingStatements = context.popPrecedingStatements(); + keyPrecedingStatements.push(precedingStatements); + if (precedingStatements.length > 0) { + lastPrecedingStatementsIndex = i; + } + + // Transform value and cache preceding statements + context.pushPrecedingStatements(); + if (ts.isPropertyAssignment(element)) { const expression = context.transformExpression(element.initializer); properties.push(lua.createTableFieldExpression(expression, name, element)); + initializers.push(element.initializer); } else if (ts.isShorthandPropertyAssignment(element)) { const valueSymbol = context.checker.getShorthandAssignmentValueSymbol(element); if (valueSymbol) { @@ -86,18 +84,12 @@ const transformObjectLiteralExpression: FunctionVisitor __TS__ObjectAssign({x = 0}, {y = 2}, {y = 1, z = 2}) - if (properties.length > 0) { - const tableExpression = lua.createTableExpression(properties, expression); - tableExpressions.push(tableExpression); - properties = []; - } - const type = context.checker.getTypeAtLocation(element.expression); let tableExpression: lua.Expression; if (isArrayType(context, type)) { @@ -111,39 +103,115 @@ const transformObjectLiteralExpression: FunctionVisitor 0) { + lastPrecedingStatementsIndex = i; + } + } + + // Expressions referenced before others that produced preceding statements need to be cached in temps + if (lastPrecedingStatementsIndex >= 0) { + for (let i = 0; i < properties.length; ++i) { + const property = properties[i]; + + // Bubble up key's preceding statements + context.addPrecedingStatements(keyPrecedingStatements[i]); + + // Cache computed property name in temp if before the last expression that generated preceding statements + if (i <= lastPrecedingStatementsIndex && lua.isTableFieldExpression(property) && property.key) { + property.key = moveToPrecedingTemp(context, property.key, expression.properties[i].name); + } + + // Bubble up value's preceding statements + context.addPrecedingStatements(valuePrecedingStatements[i]); + + // Cache property value in temp if before the last expression that generated preceding statements + if (i < lastPrecedingStatementsIndex) { + if (lua.isTableFieldExpression(property)) { + property.value = moveToPrecedingTemp(context, property.value, initializers[i]); + } else { + properties[i] = moveToPrecedingTemp(context, property, initializers[i]); + } + } + } + } + + // Sort into field expressions and tables to pass into __TS__ObjectAssign + let fields: lua.TableFieldExpression[] = []; + const tableExpressions: lua.Expression[] = []; + for (const property of properties) { + if (lua.isTableFieldExpression(property)) { + fields.push(property); + } else { + if (fields.length > 0) { + tableExpressions.push(lua.createTableExpression(fields)); + } + tableExpressions.push(property); + fields = []; + } } if (tableExpressions.length === 0) { - return lua.createTableExpression(properties, expression); + return lua.createTableExpression(fields, expression); } else { - if (properties.length > 0) { - const tableExpression = lua.createTableExpression(properties, expression); + if (fields.length > 0) { + const tableExpression = lua.createTableExpression(fields, expression); tableExpressions.push(tableExpression); } if (tableExpressions[0].kind !== lua.SyntaxKind.TableExpression) { tableExpressions.unshift(lua.createTableExpression(undefined, expression)); } - return transformLuaLibFunction(context, LuaLibFeature.ObjectAssign, expression, ...tableExpressions); } }; const transformArrayLiteralExpression: FunctionVisitor = (expression, context) => { + // Disallow using undefined/null in array literals + checkForUndefinedOrNullInArrayLiteral(expression, context); + const filteredElements = expression.elements.map(e => ts.isOmittedExpression(e) ? ts.factory.createIdentifier("undefined") : e ); - const values = flattenSpreadExpressions(context, filteredElements).map(e => lua.createTableFieldExpression(e)); + const values = transformExpressionList(context, filteredElements).map(e => lua.createTableFieldExpression(e)); return lua.createTableExpression(values, expression); }; +function checkForUndefinedOrNullInArrayLiteral(array: ts.ArrayLiteralExpression, context: TransformationContext) { + // Look for last non-nil element in literal + let lastNonUndefinedIndex = array.elements.length - 1; + for (; lastNonUndefinedIndex >= 0; lastNonUndefinedIndex--) { + if (!isUndefinedOrNull(array.elements[lastNonUndefinedIndex])) { + break; + } + } + + // Add diagnostics for non-trailing nil elements in array literal + for (let i = 0; i < array.elements.length; i++) { + if (i < lastNonUndefinedIndex && isUndefinedOrNull(array.elements[i])) { + context.diagnostics.push(undefinedInArrayLiteral(array.elements[i])); + } + } +} + +function isUndefinedOrNull(node: ts.Node) { + return ( + node.kind === ts.SyntaxKind.UndefinedKeyword || + node.kind === ts.SyntaxKind.NullKeyword || + (ts.isIdentifier(node) && node.text === "undefined") + ); +} + export const literalVisitors: Visitors = { [ts.SyntaxKind.NullKeyword]: node => lua.createNilLiteral(node), [ts.SyntaxKind.TrueKeyword]: node => lua.createBooleanLiteral(true, node), diff --git a/src/transformation/visitors/loops/do-while.ts b/src/transformation/visitors/loops/do-while.ts index 0fafc710a..f4c714c71 100644 --- a/src/transformation/visitors/loops/do-while.ts +++ b/src/transformation/visitors/loops/do-while.ts @@ -1,23 +1,77 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; import { FunctionVisitor } from "../../context"; -import { transformLoopBody } from "./utils"; +import { transformInPrecedingStatementScope } from "../../utils/preceding-statements"; +import { checkOnlyTruthyCondition } from "../conditional"; +import { invertCondition, transformLoopBody } from "./utils"; -export const transformWhileStatement: FunctionVisitor = (statement, context) => - lua.createWhileStatement( - lua.createBlock(transformLoopBody(context, statement)), - context.transformExpression(statement.expression), - statement +export const transformWhileStatement: FunctionVisitor = (statement, context) => { + // Check if we need to add diagnostic about Lua truthiness + checkOnlyTruthyCondition(statement.expression, context); + + const body = transformLoopBody(context, statement); + + let { precedingStatements: conditionPrecedingStatements, result: condition } = transformInPrecedingStatementScope( + context, + () => context.transformExpression(statement.expression) ); + // If condition has preceding statements, ensure they are executed every iteration by using the form: + // + // while true do + // condition's preceding statements + // if not condition then + // break + // end + // ... + // end + if (conditionPrecedingStatements.length > 0) { + conditionPrecedingStatements.push( + lua.createIfStatement( + invertCondition(condition), + lua.createBlock([lua.createBreakStatement()]), + undefined, + statement.expression + ) + ); + body.unshift(...conditionPrecedingStatements); + condition = lua.createBooleanLiteral(true); + } + + return lua.createWhileStatement(lua.createBlock(body), condition, statement); +}; + export const transformDoStatement: FunctionVisitor = (statement, context) => { + // Check if we need to add diagnostic about Lua truthiness + checkOnlyTruthyCondition(statement.expression, context); + const body = lua.createDoStatement(transformLoopBody(context, statement)); - let condition = context.transformExpression(statement.expression); - if (lua.isUnaryExpression(condition) && condition.operator === lua.SyntaxKind.NotOperator) { - condition = condition.operand; - } else { - condition = lua.createUnaryExpression(condition, lua.SyntaxKind.NotOperator); + + let { precedingStatements: conditionPrecedingStatements, result: condition } = transformInPrecedingStatementScope( + context, + () => invertCondition(context.transformExpression(statement.expression)) + ); + + // If condition has preceding statements, ensure they are executed every iteration by using the form: + // + // repeat + // ... + // condition's preceding statements + // if condition then + // break + // end + // end + if (conditionPrecedingStatements.length > 0) { + conditionPrecedingStatements.push( + lua.createIfStatement( + condition, + lua.createBlock([lua.createBreakStatement()]), + undefined, + statement.expression + ) + ); + condition = lua.createBooleanLiteral(false); } - return lua.createRepeatStatement(lua.createBlock([body]), condition, statement); + return lua.createRepeatStatement(lua.createBlock([body, ...conditionPrecedingStatements]), condition, statement); }; diff --git a/src/transformation/visitors/loops/for-of.ts b/src/transformation/visitors/loops/for-of.ts index 64d2c211b..5071623db 100644 --- a/src/transformation/visitors/loops/for-of.ts +++ b/src/transformation/visitors/loops/for-of.ts @@ -1,13 +1,18 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; import { FunctionVisitor, TransformationContext } from "../../context"; -import { AnnotationKind, isForRangeType, isLuaIteratorType } from "../../utils/annotations"; -import { annotationRemoved } from "../../utils/diagnostics"; import { LuaLibFeature, transformLuaLibFunction } from "../../utils/lualib"; import { isArrayType } from "../../utils/typescript"; -import { isIterableExpression, transformForOfIterableStatement } from "../language-extensions/iterable"; +import { + transformForOfIterableStatement, + transformForOfPairsIterableStatement, + transformForOfPairsKeyIterableStatement, +} from "../language-extensions/iterable"; import { isRangeFunction, transformRangeStatement } from "../language-extensions/range"; import { transformForInitializer, transformLoopBody } from "./utils"; +import { getIterableExtensionKindForNode, IterableExtensionKind } from "../../utils/language-extensions"; +import { unsupportedForAwaitOf } from "../../utils/diagnostics"; +import { assertNever } from "../../../utils"; function transformForOfArrayStatement( context: TransformationContext, @@ -39,19 +44,30 @@ function transformForOfIteratorStatement( } export const transformForOfStatement: FunctionVisitor = (node, context) => { + if (node.awaitModifier) { + context.diagnostics.push(unsupportedForAwaitOf(node.awaitModifier)); + } + const body = lua.createBlock(transformLoopBody(context, node)); if (ts.isCallExpression(node.expression) && isRangeFunction(context, node.expression)) { return transformRangeStatement(context, node, body); - } else if (ts.isCallExpression(node.expression) && isForRangeType(context, node.expression.expression)) { - context.diagnostics.push(annotationRemoved(node.expression, AnnotationKind.ForRange)); - } else if (isIterableExpression(context, node.expression)) { - return transformForOfIterableStatement(context, node, body); - } else if (isLuaIteratorType(context, node.expression)) { - context.diagnostics.push(annotationRemoved(node.expression, AnnotationKind.LuaIterator)); - } else if (isArrayType(context, context.checker.getTypeAtLocation(node.expression))) { + } + const iterableExtensionType = getIterableExtensionKindForNode(context, node.expression); + if (iterableExtensionType) { + if (iterableExtensionType === IterableExtensionKind.Iterable) { + return transformForOfIterableStatement(context, node, body); + } else if (iterableExtensionType === IterableExtensionKind.Pairs) { + return transformForOfPairsIterableStatement(context, node, body); + } else if (iterableExtensionType === IterableExtensionKind.PairsKey) { + return transformForOfPairsKeyIterableStatement(context, node, body); + } else { + assertNever(iterableExtensionType); + } + } + if (isArrayType(context, context.checker.getTypeAtLocation(node.expression))) { return transformForOfArrayStatement(context, node, body); - } else { - return transformForOfIteratorStatement(context, node, body); } + + return transformForOfIteratorStatement(context, node, body); }; diff --git a/src/transformation/visitors/loops/for.ts b/src/transformation/visitors/loops/for.ts index 98864082e..5ae3bf0ea 100644 --- a/src/transformation/visitors/loops/for.ts +++ b/src/transformation/visitors/loops/for.ts @@ -1,12 +1,16 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; import { FunctionVisitor } from "../../context"; +import { transformInPrecedingStatementScope } from "../../utils/preceding-statements"; import { checkVariableDeclarationList, transformVariableDeclaration } from "../variable-declaration"; -import { transformLoopBody } from "./utils"; +import { invertCondition, transformLoopBody } from "./utils"; +import { ScopeType } from "../../utils/scope"; export const transformForStatement: FunctionVisitor = (statement, context) => { const result: lua.Statement[] = []; + context.pushScope(ScopeType.Loop, statement); + if (statement.initializer) { if (ts.isVariableDeclarationList(statement.initializer)) { checkVariableDeclarationList(context, statement.initializer); @@ -17,19 +21,50 @@ export const transformForStatement: FunctionVisitor = (statemen } } - const condition = statement.condition - ? context.transformExpression(statement.condition) - : lua.createBooleanLiteral(true); - - // Add body const body: lua.Statement[] = transformLoopBody(context, statement); + let condition: lua.Expression; + if (statement.condition) { + const tsCondition = statement.condition; + const { precedingStatements: conditionPrecedingStatements, result } = transformInPrecedingStatementScope( + context, + () => context.transformExpression(tsCondition) + ); + condition = result; + + // If condition has preceding statements, ensure they are executed every iteration by using the form: + // + // while true do + // condition's preceding statements + // if not condition then + // break + // end + // ... + // end + if (conditionPrecedingStatements.length > 0) { + conditionPrecedingStatements.push( + lua.createIfStatement( + invertCondition(condition), + lua.createBlock([lua.createBreakStatement()]), + undefined, + statement.condition + ) + ); + body.unshift(...conditionPrecedingStatements); + condition = lua.createBooleanLiteral(true); + } + } else { + condition = lua.createBooleanLiteral(true); + } + if (statement.incrementor) { body.push(...context.transformStatements(ts.factory.createExpressionStatement(statement.incrementor))); } // while (condition) do ... end - result.push(lua.createWhileStatement(lua.createBlock(body), condition)); + result.push(lua.createWhileStatement(lua.createBlock(body), condition, statement)); + + context.popScope(); return lua.createDoStatement(result, statement); }; diff --git a/src/transformation/visitors/loops/utils.ts b/src/transformation/visitors/loops/utils.ts index a7402b653..d7e4a3093 100644 --- a/src/transformation/visitors/loops/utils.ts +++ b/src/transformation/visitors/loops/utils.ts @@ -1,7 +1,8 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; import { TransformationContext } from "../../context"; -import { performHoisting, popScope, pushScope, ScopeType } from "../../utils/scope"; +import { transformInPrecedingStatementScope } from "../../utils/preceding-statements"; +import { LoopContinued, performHoisting, ScopeType } from "../../utils/scope"; import { isAssignmentPattern } from "../../utils/typescript"; import { transformAssignment } from "../binary-expression/assignments"; import { transformAssignmentPattern } from "../binary-expression/destructuring-assignments"; @@ -13,20 +14,49 @@ export function transformLoopBody( context: TransformationContext, loop: ts.WhileStatement | ts.DoStatement | ts.ForStatement | ts.ForOfStatement | ts.ForInOrOfStatement ): lua.Statement[] { - pushScope(context, ScopeType.Loop); + context.pushScope(ScopeType.Loop, loop); const body = performHoisting(context, transformBlockOrStatement(context, loop.statement)); - const scope = popScope(context); + const scope = context.popScope(); const scopeId = scope.id; - if (!scope.loopContinued) { - return body; - } + switch (scope.loopContinued) { + case undefined: + case LoopContinued.WithContinue: + return body; + + case LoopContinued.WithGoto: + return [lua.createDoStatement(body), lua.createLabelStatement(`__continue${scopeId}`)]; + + case LoopContinued.WithRepeatBreak: + const identifier = lua.createIdentifier(`__continue${scopeId}`); + const literalTrue = lua.createBooleanLiteral(true); - const baseResult: lua.Statement[] = [lua.createDoStatement(body)]; - const continueLabel = lua.createLabelStatement(`__continue${scopeId}`); - baseResult.push(continueLabel); + // If there is a break in the body statements, do not include any code afterwards + const transformedBodyStatements = []; + let bodyBroken = false; + for (const statement of body) { + transformedBodyStatements.push(statement); + if (lua.isBreakStatement(statement)) { + bodyBroken = true; + break; + } + } + if (!bodyBroken) { + // Tell loop to continue if not broken + transformedBodyStatements.push(lua.createAssignmentStatement(identifier, literalTrue)); + } - return baseResult; + return [ + lua.createDoStatement([ + lua.createVariableDeclarationStatement(identifier), + lua.createRepeatStatement(lua.createBlock(transformedBodyStatements), literalTrue), + lua.createIfStatement( + lua.createUnaryExpression(identifier, lua.SyntaxKind.NotOperator), + lua.createBlock([lua.createBreakStatement()]) + ), + ]), + ]; + } } export function getVariableDeclarationBinding( @@ -49,14 +79,20 @@ export function transformForInitializer( ): lua.Identifier { const valueVariable = lua.createIdentifier("____value"); + context.pushScope(ScopeType.LoopInitializer, initializer); + if (ts.isVariableDeclarationList(initializer)) { // Declaration of new variable const binding = getVariableDeclarationBinding(context, initializer); if (ts.isArrayBindingPattern(binding) || ts.isObjectBindingPattern(binding)) { - block.statements.unshift(...transformBindingPattern(context, binding, valueVariable)); + const { precedingStatements, result: bindings } = transformInPrecedingStatementScope(context, () => + transformBindingPattern(context, binding, valueVariable) + ); + block.statements.unshift(...precedingStatements, ...bindings); } else { // Single variable declared in for loop + context.popScope(); return transformIdentifier(context, binding); } } else { @@ -64,10 +100,21 @@ export function transformForInitializer( block.statements.unshift( ...(isAssignmentPattern(initializer) - ? transformAssignmentPattern(context, initializer, valueVariable) + ? transformAssignmentPattern(context, initializer, valueVariable, false) : transformAssignment(context, initializer, valueVariable)) ); } + context.popScope(); return valueVariable; } + +export function invertCondition(expression: lua.Expression) { + if (lua.isUnaryExpression(expression) && expression.operator === lua.SyntaxKind.NotOperator) { + return expression.operand; + } else { + const notExpression = lua.createUnaryExpression(expression, lua.SyntaxKind.NotOperator); + lua.setNodePosition(notExpression, lua.getOriginalPos(expression)); + return notExpression; + } +} diff --git a/src/transformation/visitors/modules/export.ts b/src/transformation/visitors/modules/export.ts index 955f56866..c814b8257 100644 --- a/src/transformation/visitors/modules/export.ts +++ b/src/transformation/visitors/modules/export.ts @@ -2,17 +2,12 @@ import * as ts from "typescript"; import * as lua from "../../../LuaAST"; import { assert } from "../../../utils"; import { FunctionVisitor, TransformationContext } from "../../context"; -import { - createDefaultExportIdentifier, - createDefaultExportStringLiteral, - createExportedIdentifier, -} from "../../utils/export"; +import { createDefaultExportExpression, createDefaultExportStringLiteral } from "../../utils/export"; import { createExportsIdentifier } from "../../utils/lua-ast"; -import { ScopeType } from "../../utils/scope"; -import { transformScopeBlock } from "../block"; -import { transformIdentifier } from "../identifier"; -import { createShorthandIdentifier } from "../literal"; +import { createShorthandIdentifier, transformPropertyName } from "../literal"; import { createModuleRequire } from "./import"; +import { createSafeName } from "../../utils/safe-names"; +import * as path from "path"; export const transformExportAssignment: FunctionVisitor = (node, context) => { if (!context.resolver.isValueAliasDeclaration(node)) { @@ -39,10 +34,6 @@ export const transformExportAssignment: FunctionVisitor = ( function transformExportAll(context: TransformationContext, node: ts.ExportDeclaration): lua.Statement | undefined { assert(node.moduleSpecifier); - if (!context.resolver.moduleExportsSomeValue(node.moduleSpecifier)) { - return undefined; - } - const moduleRequire = createModuleRequire(context, node.moduleSpecifier); // export * as ns from "..."; @@ -105,21 +96,37 @@ function transformExportAll(context: TransformationContext, node: ts.ExportDecla } const isDefaultExportSpecifier = (node: ts.ExportSpecifier) => - (node.name && node.name.originalKeywordKind === ts.SyntaxKind.DefaultKeyword) || - (node.propertyName && node.propertyName.originalKeywordKind === ts.SyntaxKind.DefaultKeyword); + (node.name && + ts.isIdentifier(node.name) && + ts.identifierToKeywordKind(node.name) === ts.SyntaxKind.DefaultKeyword) || + (node.propertyName && + ts.isIdentifier(node.propertyName) && + ts.identifierToKeywordKind(node.propertyName) === ts.SyntaxKind.DefaultKeyword); function transformExportSpecifier(context: TransformationContext, node: ts.ExportSpecifier): lua.AssignmentStatement { - const exportedSymbol = context.checker.getExportSpecifierLocalTargetSymbol(node); - const exportedIdentifier = node.propertyName ? node.propertyName : node.name; - const exportedExpression = createShorthandIdentifier(context, exportedSymbol, exportedIdentifier); + const exportedName = node.name; + const exportedValue = node.propertyName ?? node.name; + let rhs: lua.Expression; + if (ts.isIdentifier(exportedValue)) { + const exportedSymbol = context.checker.getExportSpecifierLocalTargetSymbol(node); + rhs = createShorthandIdentifier(context, exportedSymbol, exportedValue); + } else { + rhs = lua.createStringLiteral(exportedName.text, exportedValue); + } - const isDefault = isDefaultExportSpecifier(node); - const identifierToExport = isDefault - ? createDefaultExportIdentifier(node) - : transformIdentifier(context, node.name); - const exportAssignmentLeftHandSide = createExportedIdentifier(context, identifierToExport); + if (isDefaultExportSpecifier(node)) { + const lhs = createDefaultExportExpression(node); + return lua.createAssignmentStatement(lhs, rhs, node); + } else { + const exportsTable = createExportsIdentifier(); + const lhs = lua.createTableIndexExpression( + exportsTable, + lua.createStringLiteral(exportedName.text), + exportedName + ); - return lua.createAssignmentStatement(exportAssignmentLeftHandSide, exportedExpression, node); + return lua.createAssignmentStatement(lhs, rhs, node); + } } function transformExportSpecifiersFrom( @@ -128,37 +135,32 @@ function transformExportSpecifiersFrom( moduleSpecifier: ts.Expression, exportSpecifiers: ts.ExportSpecifier[] ): lua.Statement { - // First transpile as import clause - const importClause = ts.factory.createImportClause( - false, - undefined, - ts.factory.createNamedImports( - exportSpecifiers.map(s => ts.factory.createImportSpecifier(s.propertyName, s.name)) - ) - ); + const result: lua.Statement[] = []; - const importDeclaration = ts.factory.createImportDeclaration( - statement.decorators, - statement.modifiers, - importClause, - moduleSpecifier - ); + const importPath = ts.isStringLiteral(moduleSpecifier) ? moduleSpecifier.text.replace(/"/g, "") : "module"; - // Wrap in block to prevent imports from hoisting out of `do` statement - const [block] = transformScopeBlock(context, ts.factory.createBlock([importDeclaration]), ScopeType.Block); - const result = block.statements; + // Create the require statement to extract values. + // local ____module = require("module") + const importUniqueName = lua.createIdentifier(createSafeName(path.basename(importPath))); + const requireCall = createModuleRequire(context, moduleSpecifier); + result.push(lua.createVariableDeclarationStatement(importUniqueName, requireCall, statement)); - // Now the module is imported, add the imports to the export table for (const specifier of exportSpecifiers) { - result.push( - lua.createAssignmentStatement( - createExportedIdentifier(context, transformIdentifier(context, specifier.name)), - transformIdentifier(context, specifier.name) - ) + // Assign to exports table + const exportsTable = createExportsIdentifier(); + const exportedName = specifier.name; + const exportedNameTransformed = transformPropertyName(context, exportedName); + const lhs = lua.createTableIndexExpression(exportsTable, exportedNameTransformed, exportedName); + + const exportedValue = specifier.propertyName ?? specifier.name; + const rhs = lua.createTableIndexExpression( + lua.cloneIdentifier(importUniqueName), + transformPropertyName(context, exportedValue), + specifier ); + result.push(lua.createAssignmentStatement(lhs, rhs, specifier)); } - // Wrap this in a DoStatement to prevent polluting the scope. return lua.createDoStatement(result, statement); } diff --git a/src/transformation/visitors/modules/import.ts b/src/transformation/visitors/modules/import.ts index d8daba8c9..7c6a597ac 100644 --- a/src/transformation/visitors/modules/import.ts +++ b/src/transformation/visitors/modules/import.ts @@ -1,13 +1,15 @@ import * as path from "path"; import * as ts from "typescript"; import * as lua from "../../../LuaAST"; +import { createStaticPromiseFunctionAccessor } from "../../builtins/promise"; import { FunctionVisitor, TransformationContext } from "../../context"; import { AnnotationKind, getSymbolAnnotations } from "../../utils/annotations"; import { createDefaultExportStringLiteral } from "../../utils/export"; import { createHoistableVariableDeclarationStatement } from "../../utils/lua-ast"; +import { importLuaLibFeature, LuaLibFeature } from "../../utils/lualib"; import { createSafeName } from "../../utils/safe-names"; import { peekScope } from "../../utils/scope"; -import { transformIdentifier } from "../identifier"; +import { getCustomNameFromSymbol, transformIdentifier } from "../identifier"; import { transformPropertyName } from "../literal"; function isNoResolutionPath(context: TransformationContext, moduleSpecifier: ts.Expression): boolean { @@ -44,11 +46,15 @@ function transformImportSpecifier( importSpecifier: ts.ImportSpecifier, moduleTableName: lua.Identifier ): lua.VariableDeclarationStatement { + const type = context.checker.getTypeAtLocation(importSpecifier.name); + const leftIdentifier = transformIdentifier(context, importSpecifier.name); - const propertyName = transformPropertyName( - context, - importSpecifier.propertyName ? importSpecifier.propertyName : importSpecifier.name - ); + + // If imported value has a customName annotation use that, otherwise use regular property + const customName = getCustomNameFromSymbol(context, type.getSymbol()); + const propertyName = customName + ? lua.createStringLiteral(customName, importSpecifier.propertyName ?? importSpecifier.name) + : transformPropertyName(context, importSpecifier.propertyName ?? importSpecifier.name); return lua.createVariableDeclarationStatement( leftIdentifier, @@ -60,9 +66,7 @@ function transformImportSpecifier( export const transformImportDeclaration: FunctionVisitor = (statement, context) => { const scope = peekScope(context); - if (!scope.importStatements) { - scope.importStatements = []; - } + scope.importStatements ??= []; const result: lua.Statement[] = []; const requireCall = createModuleRequire(context, statement.moduleSpecifier); @@ -72,12 +76,8 @@ export const transformImportDeclaration: FunctionVisitor = if (statement.importClause === undefined) { result.push(lua.createExpressionStatement(requireCall)); - if (scope.importStatements) { - scope.importStatements.push(...result); - return undefined; - } else { - return result; - } + scope.importStatements.push(...result); + return undefined; } const importPath = ts.isStringLiteral(statement.moduleSpecifier) @@ -144,12 +144,8 @@ export const transformImportDeclaration: FunctionVisitor = result.unshift(lua.createVariableDeclarationStatement(importUniqueName, requireCall, statement)); } - if (scope.importStatements) { - scope.importStatements.push(...result); - return undefined; - } else { - return result; - } + scope.importStatements.push(...result); + return undefined; }; export const transformExternalModuleReference: FunctionVisitor = (node, context) => @@ -169,3 +165,11 @@ export const transformImportEqualsDeclaration: FunctionVisitor = (node, context) => { + importLuaLibFeature(context, LuaLibFeature.Promise); + + const moduleRequire = + node.arguments.length > 0 ? createModuleRequire(context, node.arguments[0], node) : lua.createNilLiteral(); + return lua.createCallExpression(createStaticPromiseFunctionAccessor("resolve", node), [moduleRequire], node); +}; diff --git a/src/transformation/visitors/namespace.ts b/src/transformation/visitors/namespace.ts index d41aefd75..49a3f2289 100644 --- a/src/transformation/visitors/namespace.ts +++ b/src/transformation/visitors/namespace.ts @@ -1,24 +1,32 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { FunctionVisitor, TransformationContext } from "../context"; -import { AnnotationKind, getTypeAnnotations } from "../utils/annotations"; -import { annotationRemoved } from "../utils/diagnostics"; import { addExportToIdentifier, createExportedIdentifier, getIdentifierExportScope } from "../utils/export"; import { createHoistableVariableDeclarationStatement, createLocalOrExportedOrGlobalDeclaration, } from "../utils/lua-ast"; import { createSafeName, isUnsafeName } from "../utils/safe-names"; -import { performHoisting, popScope, pushScope, ScopeType } from "../utils/scope"; +import { performHoisting, ScopeType } from "../utils/scope"; import { getSymbolIdOfSymbol } from "../utils/symbols"; import { transformIdentifier } from "./identifier"; +export function createModuleLocalName(context: TransformationContext, module: ts.ModuleDeclaration): lua.Expression { + if (!ts.isSourceFile(module.parent) && ts.isModuleDeclaration(module.parent)) { + const parentDeclaration = createModuleLocalName(context, module.parent); + const name = createModuleLocalNameIdentifier(context, module); + return lua.createTableIndexExpression(parentDeclaration, lua.createStringLiteral(name.text), module.name); + } + + return createModuleLocalNameIdentifier(context, module); +} + export function createModuleLocalNameIdentifier( context: TransformationContext, declaration: ts.ModuleDeclaration ): lua.Identifier { const moduleSymbol = context.checker.getSymbolAtLocation(declaration.name); - if (moduleSymbol !== undefined && isUnsafeName(moduleSymbol.name)) { + if (moduleSymbol !== undefined && isUnsafeName(moduleSymbol.name, context.options)) { return lua.createIdentifier( createSafeName(declaration.name.text), declaration.name, @@ -27,7 +35,6 @@ export function createModuleLocalNameIdentifier( ); } - // TODO: Should synthetic name nodes be escaped as well? return transformIdentifier(context, declaration.name as ts.Identifier); } @@ -47,17 +54,8 @@ function moduleHasEmittedBody( return false; } -// Static context -> namespace dictionary keeping the current namespace for each transformation context -const currentNamespaces = new WeakMap(); - export const transformModuleDeclaration: FunctionVisitor = (node, context) => { - const annotations = getTypeAnnotations(context.checker.getTypeAtLocation(node)); - // If phantom namespace elide the declaration and return the body - if (annotations.has(AnnotationKind.Phantom)) { - context.diagnostics.push(annotationRemoved(node, AnnotationKind.Phantom)); - } - - const currentNamespace = currentNamespaces.get(context); + const currentNamespace = context.currentNamespaces; const result: lua.Statement[] = []; const symbol = context.checker.getSymbolAtLocation(node.name); @@ -115,20 +113,21 @@ export const transformModuleDeclaration: FunctionVisitor = // Set current namespace for nested NS // Keep previous namespace to reset after block transpilation - currentNamespaces.set(context, node); + + context.currentNamespaces = node; // Transform moduleblock to block and visit it if (moduleHasEmittedBody(node)) { - pushScope(context, ScopeType.Block); + context.pushScope(ScopeType.Block, node); const statements = performHoisting( context, context.transformStatements(ts.isModuleBlock(node.body) ? node.body.statements : node.body) ); - popScope(context); + context.popScope(); result.push(lua.createDoStatement(statements)); } - currentNamespaces.set(context, currentNamespace); + context.currentNamespaces = currentNamespace; return result; }; diff --git a/src/transformation/visitors/optional-chaining.ts b/src/transformation/visitors/optional-chaining.ts new file mode 100644 index 000000000..6d6deeb8d --- /dev/null +++ b/src/transformation/visitors/optional-chaining.ts @@ -0,0 +1,286 @@ +import * as ts from "typescript"; +import * as lua from "../../LuaAST"; +import { tempSymbolId, TransformationContext } from "../context"; +import { assert, assertNever } from "../../utils"; +import { transformInPrecedingStatementScope } from "../utils/preceding-statements"; +import { transformElementAccessExpressionWithCapture, transformPropertyAccessExpressionWithCapture } from "./access"; +import { shouldMoveToTemp } from "./expression-list"; +import { canBeFalsyWhenNotNull, expressionResultIsUsed } from "../utils/typescript"; +import { wrapInStatement } from "./expression-statement"; + +type NormalOptionalChain = ts.PropertyAccessChain | ts.ElementAccessChain | ts.CallChain; + +function skipNonNullChains(chain: ts.OptionalChain): NormalOptionalChain { + while (ts.isNonNullChain(chain)) { + chain = chain.expression as ts.OptionalChain; + } + return chain; +} + +function flattenChain(chain: ts.OptionalChain) { + chain = skipNonNullChains(chain); + const links: NormalOptionalChain[] = [chain]; + while (!chain.questionDotToken && !ts.isTaggedTemplateExpression(chain)) { + const nextLink: ts.Expression = chain.expression; + assert(ts.isOptionalChain(nextLink)); + chain = skipNonNullChains(nextLink); + links.unshift(chain); + } + return { expression: chain.expression, chain: links }; +} + +export interface ExpressionWithThisValue { + expression: lua.Expression; + thisValue?: lua.Expression; +} + +function transformExpressionWithThisValueCapture( + context: TransformationContext, + node: ts.Expression, + thisValueCapture: lua.Identifier +): ExpressionWithThisValue { + if (ts.isParenthesizedExpression(node)) { + return transformExpressionWithThisValueCapture(context, node.expression, thisValueCapture); + } + if (ts.isPropertyAccessExpression(node)) { + return transformPropertyAccessExpressionWithCapture(context, node, thisValueCapture); + } + if (ts.isElementAccessExpression(node)) { + return transformElementAccessExpressionWithCapture(context, node, thisValueCapture); + } + return { expression: context.transformExpression(node) }; +} + +// returns thisValueCapture exactly if a temp variable was used. +export function captureThisValue( + context: TransformationContext, + expression: lua.Expression, + thisValueCapture: lua.Identifier, + tsOriginal: ts.Node +): lua.Expression { + if (!shouldMoveToTemp(context, expression, tsOriginal)) { + return expression; + } + const tempAssignment = lua.createAssignmentStatement(thisValueCapture, expression, tsOriginal); + context.addPrecedingStatements(tempAssignment); + return thisValueCapture; +} + +export interface OptionalContinuation { + contextualCall?: lua.CallExpression; + usedIdentifiers: lua.Identifier[]; +} + +const optionalContinuations = new WeakMap(); + +// should be translated verbatim to lua +function createOptionalContinuationIdentifier(text: string, tsOriginal: ts.Expression): ts.Identifier { + const identifier = ts.factory.createIdentifier(text); + ts.setOriginalNode(identifier, tsOriginal); + optionalContinuations.set(identifier, { + usedIdentifiers: [], + }); + return identifier; +} + +export function isOptionalContinuation(node: ts.Node): boolean { + return ts.isIdentifier(node) && optionalContinuations.has(node); +} + +export function getOptionalContinuationData(identifier: ts.Identifier): OptionalContinuation | undefined { + return optionalContinuations.get(identifier); +} + +export function transformOptionalChain(context: TransformationContext, node: ts.OptionalChain): lua.Expression { + return transformOptionalChainWithCapture(context, node, undefined).expression; +} + +export function transformOptionalChainWithCapture( + context: TransformationContext, + tsNode: ts.OptionalChain, + thisValueCapture: lua.Identifier | undefined, + isDelete?: ts.DeleteExpression +): ExpressionWithThisValue { + const luaTempName = context.createTempName("opt"); + + const { expression: tsLeftExpression, chain } = flattenChain(tsNode); + + // build temp.b.c.d + const tsTemp = createOptionalContinuationIdentifier(luaTempName, tsLeftExpression); + let tsRightExpression: ts.Expression = tsTemp; + for (const link of chain) { + if (ts.isPropertyAccessExpression(link)) { + tsRightExpression = ts.factory.createPropertyAccessExpression(tsRightExpression, link.name); + } else if (ts.isElementAccessExpression(link)) { + tsRightExpression = ts.factory.createElementAccessExpression(tsRightExpression, link.argumentExpression); + } else if (ts.isCallExpression(link)) { + tsRightExpression = ts.factory.createCallExpression(tsRightExpression, undefined, link.arguments); + } else { + assertNever(link); + } + ts.setOriginalNode(tsRightExpression, link); + } + if (isDelete) { + tsRightExpression = ts.factory.createDeleteExpression(tsRightExpression); + ts.setOriginalNode(tsRightExpression, isDelete); + } + + // transform right expression first to check if thisValue capture is needed + // capture and return thisValue if requested from outside + let returnThisValue: lua.Expression | undefined; + const { precedingStatements: rightPrecedingStatements, result: rightExpression } = + transformInPrecedingStatementScope(context, () => { + if (!thisValueCapture) { + return context.transformExpression(tsRightExpression); + } + + const { expression: result, thisValue } = transformExpressionWithThisValueCapture( + context, + tsRightExpression, + thisValueCapture + ); + returnThisValue = thisValue; + return result; + }); + + // transform left expression, handle thisValue if needed by rightExpression + const thisValueCaptureName = context.createTempName("this"); + const leftThisValueTemp = lua.createIdentifier(thisValueCaptureName, undefined, tempSymbolId); + let capturedThisValue: lua.Expression | undefined; + + const optionalContinuationData = getOptionalContinuationData(tsTemp); + const rightContextualCall = optionalContinuationData?.contextualCall; + const { precedingStatements: leftPrecedingStatements, result: leftExpression } = transformInPrecedingStatementScope( + context, + () => { + let result: lua.Expression; + if (rightContextualCall) { + ({ expression: result, thisValue: capturedThisValue } = transformExpressionWithThisValueCapture( + context, + tsLeftExpression, + leftThisValueTemp + )); + } else { + result = context.transformExpression(tsLeftExpression); + } + return result; + } + ); + + // handle super calls by passing self as context + function getLeftMostChainItem(node: ts.Node): ts.Node { + if (ts.isPropertyAccessExpression(node)) { + return getLeftMostChainItem(node.expression); + } else { + return node; + } + } + if (getLeftMostChainItem(tsLeftExpression).kind === ts.SyntaxKind.SuperKeyword) { + capturedThisValue = lua.createIdentifier("self"); + } + + // handle context + if (rightContextualCall) { + if (capturedThisValue) { + rightContextualCall.params[0] = capturedThisValue; + if (capturedThisValue === leftThisValueTemp) { + context.addPrecedingStatements(lua.createVariableDeclarationStatement(leftThisValueTemp)); + } + } else { + if (context.isStrict) { + rightContextualCall.params[0] = lua.createNilLiteral(); + } else { + const identifier = lua.createIdentifier("_G"); + if (rightPrecedingStatements.length === 0) { + rightContextualCall.params[0] = identifier; + } else { + const tempContext = context.createTempNameForLuaExpression(identifier); + rightPrecedingStatements.unshift(lua.createVariableDeclarationStatement(tempContext, identifier)); + rightContextualCall.params[0] = tempContext; + } + } + } + } + + // evaluate optional chain + context.addPrecedingStatements(leftPrecedingStatements); + + // try use existing variable instead of creating new one, if possible + let leftIdentifier: lua.Identifier | undefined; + const usedLuaIdentifiers = optionalContinuationData?.usedIdentifiers; + const reuseLeftIdentifier = + usedLuaIdentifiers && + usedLuaIdentifiers.length > 0 && + lua.isIdentifier(leftExpression) && + (rightPrecedingStatements.length === 0 || !shouldMoveToTemp(context, leftExpression, tsLeftExpression)); + if (reuseLeftIdentifier) { + leftIdentifier = leftExpression; + for (const usedIdentifier of usedLuaIdentifiers) { + usedIdentifier.text = leftIdentifier.text; + } + } else { + leftIdentifier = lua.createIdentifier(luaTempName, undefined, tempSymbolId); + context.addPrecedingStatements(lua.createVariableDeclarationStatement(leftIdentifier, leftExpression)); + } + + if (!expressionResultIsUsed(tsNode) || isDelete) { + // if left ~= nil then + // + // + // end + + const innerExpression = wrapInStatement(rightExpression); + const innerStatements = rightPrecedingStatements; + if (innerExpression) innerStatements.push(innerExpression); + + context.addPrecedingStatements( + lua.createIfStatement( + lua.createBinaryExpression(leftIdentifier, lua.createNilLiteral(), lua.SyntaxKind.InequalityOperator), + lua.createBlock(innerStatements) + ) + ); + return { expression: lua.createNilLiteral(), thisValue: returnThisValue }; + } else if ( + rightPrecedingStatements.length === 0 && + !canBeFalsyWhenNotNull(context, context.checker.getTypeAtLocation(tsLeftExpression)) + ) { + // return a && a.b + return { + expression: lua.createBinaryExpression(leftIdentifier, rightExpression, lua.SyntaxKind.AndOperator, tsNode), + thisValue: returnThisValue, + }; + } else { + let resultIdentifier: lua.Identifier; + if (!reuseLeftIdentifier) { + // reuse temp variable for output + resultIdentifier = leftIdentifier; + } else { + resultIdentifier = lua.createIdentifier(context.createTempName("opt_result"), undefined, tempSymbolId); + context.addPrecedingStatements(lua.createVariableDeclarationStatement(resultIdentifier)); + } + // if left ~= nil then + // + // result = + // end + // return result + context.addPrecedingStatements( + lua.createIfStatement( + lua.createBinaryExpression(leftIdentifier, lua.createNilLiteral(), lua.SyntaxKind.InequalityOperator), + lua.createBlock([ + ...rightPrecedingStatements, + lua.createAssignmentStatement(resultIdentifier, rightExpression), + ]) + ) + ); + return { expression: resultIdentifier, thisValue: returnThisValue }; + } +} + +export function transformOptionalDeleteExpression( + context: TransformationContext, + node: ts.DeleteExpression, + innerExpression: ts.OptionalChain +) { + transformOptionalChainWithCapture(context, innerExpression, undefined, node); + return lua.createBooleanLiteral(true, node); +} diff --git a/src/transformation/visitors/return.ts b/src/transformation/visitors/return.ts index f309874f7..3a61314ff 100644 --- a/src/transformation/visitors/return.ts +++ b/src/transformation/visitors/return.ts @@ -1,11 +1,9 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { FunctionVisitor, TransformationContext } from "../context"; -import { isInTupleReturnFunction, isTupleReturnCall } from "../utils/annotations"; import { validateAssignment } from "../utils/assignment-validation"; import { createUnpackCall, wrapInTable } from "../utils/lua-ast"; -import { ScopeType, walkScopesUp } from "../utils/scope"; -import { isArrayType } from "../utils/typescript"; +import { findAsyncTryScopeInStack, ScopeType, walkScopesUp } from "../utils/scope"; import { transformArguments } from "./call"; import { returnsMultiType, @@ -13,88 +11,67 @@ import { isMultiFunctionCall, isMultiReturnType, isInMultiReturnFunction, + canBeMultiReturnType, } from "./language-extensions/multi"; import { invalidMultiFunctionReturnType } from "../utils/diagnostics"; +import { isInAsyncFunction } from "../utils/typescript"; -function transformExpressionsInReturn( - context: TransformationContext, - node: ts.Expression, - insideTryCatch: boolean -): lua.Expression[] { +function transformExpressionsInReturn(context: TransformationContext, node: ts.Expression): lua.Expression[] { const expressionType = context.checker.getTypeAtLocation(node); - if (ts.isCallExpression(node)) { + // skip type assertions + // don't skip parenthesis as it may arise confusion with lua behavior (where parenthesis are significant) + const innerNode = ts.skipOuterExpressions(node, ts.OuterExpressionKinds.Assertions); + + if (ts.isCallExpression(innerNode)) { // $multi(...) - if (isMultiFunctionCall(context, node)) { + if (isMultiFunctionCall(context, innerNode)) { // Don't allow $multi to be implicitly cast to something other than LuaMultiReturn const type = context.checker.getContextualType(node); - if (type && !isMultiReturnType(type)) { - context.diagnostics.push(invalidMultiFunctionReturnType(node)); + if (type && !canBeMultiReturnType(type)) { + context.diagnostics.push(invalidMultiFunctionReturnType(innerNode)); } - let returnValues = transformArguments(context, node.arguments); - if (insideTryCatch) { - returnValues = [wrapInTable(...returnValues)]; // Wrap results when returning inside try/catch - } - return returnValues; + return transformArguments(context, innerNode.arguments); } - - // Force-wrap LuaMultiReturn when returning inside try/catch - if (insideTryCatch && returnsMultiType(context, node) && !shouldMultiReturnCallBeWrapped(context, node)) { - return [wrapInTable(context.transformExpression(node))]; - } - } else if (isInMultiReturnFunction(context, node) && isMultiReturnType(expressionType)) { + } else if (isInMultiReturnFunction(context, innerNode) && isMultiReturnType(expressionType)) { // Unpack objects typed as LuaMultiReturn - return [createUnpackCall(context, context.transformExpression(node), node)]; - } - - if (!isInTupleReturnFunction(context, node)) { - return [context.transformExpression(node)]; - } - - let results: lua.Expression[]; - - // Parent function is a TupleReturn function - if (ts.isArrayLiteralExpression(node)) { - // If return expression is an array literal, leave out brackets. - results = node.elements.map(e => context.transformExpression(e)); - } else if (!isTupleReturnCall(context, node) && isArrayType(context, expressionType)) { - // If return expression is an array-type and not another TupleReturn call, unpack it - results = [createUnpackCall(context, context.transformExpression(node), node)]; - } else { - results = [context.transformExpression(node)]; - } - - // Wrap tupleReturn results when returning inside try/catch - if (insideTryCatch) { - results = [wrapInTable(...results)]; + return [createUnpackCall(context, context.transformExpression(innerNode), innerNode)]; } - return results; + return [context.transformExpression(node)]; } export function transformExpressionBodyToReturnStatement( context: TransformationContext, node: ts.Expression ): lua.Statement { - const expressions = transformExpressionsInReturn(context, node, false); - return lua.createReturnStatement(expressions, node); + const expressions = transformExpressionsInReturn(context, node); + return createReturnStatement(context, expressions, node); } -export const transformReturnStatement: FunctionVisitor = (statement, context) => { - // Bubble up explicit return flag and check if we're inside a try/catch block - let insideTryCatch = false; - for (const scope of walkScopesUp(context)) { - scope.functionReturned = true; +function transformReturnExpressionForTryCatch(context: TransformationContext, node: ts.Expression): lua.Expression { + const innerNode = ts.skipOuterExpressions(node, ts.OuterExpressionKinds.Assertions); - if (scope.type === ScopeType.Function) { - break; + if (ts.isCallExpression(innerNode)) { + if (isMultiFunctionCall(context, innerNode)) { + const type = context.checker.getContextualType(node); + if (type && !canBeMultiReturnType(type)) { + context.diagnostics.push(invalidMultiFunctionReturnType(innerNode)); + } + return wrapInTable(...transformArguments(context, innerNode.arguments)); } - insideTryCatch = insideTryCatch || scope.type === ScopeType.Try || scope.type === ScopeType.Catch; + if (returnsMultiType(context, innerNode) && !shouldMultiReturnCallBeWrapped(context, innerNode)) { + return wrapInTable(context.transformExpression(node)); + } } - let results: lua.Expression[]; + return context.transformExpression(node); +} + +export const transformReturnStatement: FunctionVisitor = (statement, context) => { + const asyncTryScope = isInAsyncFunction(statement) ? findAsyncTryScopeInStack(context) : undefined; if (statement.expression) { const expressionType = context.checker.getTypeAtLocation(statement.expression); @@ -102,16 +79,68 @@ export const transformReturnStatement: FunctionVisitor = (st if (returnType) { validateAssignment(context, statement, expressionType, returnType); } + } - results = transformExpressionsInReturn(context, statement.expression, insideTryCatch); - } else { - // Empty return - results = []; + if (asyncTryScope) { + asyncTryScope.asyncTryHasReturn = true; + const stmts: lua.Statement[] = [ + lua.createAssignmentStatement( + lua.createIdentifier("____hasReturned"), + lua.createBooleanLiteral(true), + statement + ), + ]; + if (statement.expression) { + const returnValue = transformReturnExpressionForTryCatch(context, statement.expression); + stmts.push(lua.createAssignmentStatement(lua.createIdentifier("____returnValue"), returnValue, statement)); + } + stmts.push(lua.createReturnStatement([], statement)); + return stmts; } - if (insideTryCatch) { - results.unshift(lua.createBooleanLiteral(true)); + let results: lua.Expression[]; + if (!statement.expression) { + results = []; + } else if (isInTryCatch(context)) { + results = [transformReturnExpressionForTryCatch(context, statement.expression)]; + } else { + results = transformExpressionsInReturn(context, statement.expression); } - return lua.createReturnStatement(results, statement); + return createReturnStatement(context, results, statement); }; + +export function createReturnStatement( + context: TransformationContext, + values: lua.Expression[], + node: ts.Node +): lua.ReturnStatement { + if (isInAsyncFunction(node)) { + return lua.createReturnStatement([ + lua.createCallExpression(lua.createIdentifier("____awaiter_resolve"), [lua.createNilLiteral(), ...values]), + ]); + } + + if (isInTryCatch(context)) { + // Bubble up explicit return flag and check if we're inside a try/catch block + values = [lua.createBooleanLiteral(true), ...values]; + } + + return lua.createReturnStatement(values, node); +} + +function isInTryCatch(context: TransformationContext): boolean { + // Check if context is in a try or catch + let insideTryCatch = false; + for (const scope of walkScopesUp(context)) { + scope.functionReturned = true; + + if (scope.type === ScopeType.Function) { + break; + } + + insideTryCatch = insideTryCatch || scope.type === ScopeType.Try || scope.type === ScopeType.Catch; + } + + return insideTryCatch; +} diff --git a/src/transformation/visitors/sourceFile.ts b/src/transformation/visitors/sourceFile.ts index 604aa0c7e..2fe860f53 100644 --- a/src/transformation/visitors/sourceFile.ts +++ b/src/transformation/visitors/sourceFile.ts @@ -3,8 +3,8 @@ import * as lua from "../../LuaAST"; import { assert } from "../../utils"; import { FunctionVisitor } from "../context"; import { createExportsIdentifier } from "../utils/lua-ast"; -import { getUsedLuaLibFeatures } from "../utils/lualib"; -import { performHoisting, popScope, pushScope, ScopeType } from "../utils/scope"; +import { transformInPrecedingStatementScope } from "../utils/preceding-statements"; +import { performHoisting, ScopeType } from "../utils/scope"; import { hasExportEquals } from "../utils/typescript"; export const transformSourceFileNode: FunctionVisitor = (node, context) => { @@ -13,7 +13,11 @@ export const transformSourceFileNode: FunctionVisitor = (node, co const [statement] = node.statements; if (statement) { assert(ts.isExpressionStatement(statement)); - statements.push(lua.createReturnStatement([context.transformExpression(statement.expression)])); + const { precedingStatements, result: expression } = transformInPrecedingStatementScope(context, () => + context.transformExpression(statement.expression) + ); + statements.push(...precedingStatements); + statements.push(lua.createReturnStatement([expression])); } else { const errorCall = lua.createCallExpression(lua.createIdentifier("error"), [ lua.createStringLiteral("Unexpected end of JSON input"), @@ -22,9 +26,10 @@ export const transformSourceFileNode: FunctionVisitor = (node, co statements.push(lua.createExpressionStatement(errorCall)); } } else { - pushScope(context, ScopeType.File); + context.pushScope(ScopeType.File, node); + statements = performHoisting(context, context.transformStatements(node.statements)); - popScope(context); + context.popScope(); if (context.isModule) { // If export equals was not used. Create the exports table. @@ -41,5 +46,5 @@ export const transformSourceFileNode: FunctionVisitor = (node, co } const trivia = node.getFullText().match(/^#!.*\r?\n/)?.[0] ?? ""; - return lua.createFile(statements, getUsedLuaLibFeatures(context), trivia, node); + return lua.createFile(statements, context.usedLuaLibFeatures, trivia, node); }; diff --git a/src/transformation/visitors/spread.ts b/src/transformation/visitors/spread.ts index d9da3ce44..b10a0ae6e 100644 --- a/src/transformation/visitors/spread.ts +++ b/src/transformation/visitors/spread.ts @@ -1,23 +1,18 @@ import * as ts from "typescript"; +import { LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; +import { assertNever } from "../../utils"; import { FunctionVisitor, TransformationContext } from "../context"; -import { AnnotationKind, isTupleReturnCall, isVarargType } from "../utils/annotations"; +import { getIterableExtensionKindForNode, IterableExtensionKind } from "../utils/language-extensions"; import { createUnpackCall } from "../utils/lua-ast"; import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; -import { - findScope, - hasReferencedSymbol, - hasReferencedUndefinedLocalFunction, - isFunctionScopeWithDefinition, - ScopeType, -} from "../utils/scope"; -import { isArrayType } from "../utils/typescript"; -import { returnsMultiType } from "./language-extensions/multi"; -import { annotationRemoved } from "../utils/diagnostics"; +import { findScope, hasReferencedSymbol, hasReferencedUndefinedLocalFunction, ScopeType } from "../utils/scope"; +import { findFirstNonOuterParent, isAlwaysArrayType } from "../utils/typescript"; +import { isMultiReturnCall } from "./language-extensions/multi"; import { isGlobalVarargConstant } from "./language-extensions/vararg"; export function isOptimizedVarArgSpread(context: TransformationContext, symbol: ts.Symbol, identifier: ts.Identifier) { - if (!ts.isSpreadElement(identifier.parent)) { + if (!ts.isSpreadElement(findFirstNonOuterParent(identifier))) { return false; } @@ -33,7 +28,15 @@ export function isOptimizedVarArgSpread(context: TransformationContext, symbol: } // Scope must be a function scope associated with a real ts function - if (!isFunctionScopeWithDefinition(scope)) { + if (!ts.isFunctionLike(scope.node)) { + return false; + } + + // Scope cannot be an async function + if ( + ts.canHaveModifiers(scope.node) && + ts.getModifiers(scope.node)?.some(m => m.kind === ts.SyntaxKind.AsyncKeyword) + ) { return false; } @@ -58,22 +61,37 @@ export function isOptimizedVarArgSpread(context: TransformationContext, symbol: // TODO: Currently it's also used as an array member export const transformSpreadElement: FunctionVisitor = (node, context) => { - if (ts.isIdentifier(node.expression)) { - if (isVarargType(context, node.expression)) { - context.diagnostics.push(annotationRemoved(node, AnnotationKind.Vararg)); - } - const symbol = context.checker.getSymbolAtLocation(node.expression); - if (symbol && isOptimizedVarArgSpread(context, symbol, node.expression)) { - return lua.createDotsLiteral(node); + const tsInnerExpression = ts.skipOuterExpressions(node.expression); + if (ts.isIdentifier(tsInnerExpression)) { + const symbol = context.checker.getSymbolAtLocation(tsInnerExpression); + if (symbol && isOptimizedVarArgSpread(context, symbol, tsInnerExpression)) { + return context.luaTarget === LuaTarget.Lua50 + ? createUnpackCall(context, lua.createArgLiteral(), node) + : lua.createDotsLiteral(node); } } const innerExpression = context.transformExpression(node.expression); - if (isTupleReturnCall(context, node.expression)) return innerExpression; - if (ts.isCallExpression(node.expression) && returnsMultiType(context, node.expression)) return innerExpression; + if (isMultiReturnCall(context, tsInnerExpression)) return innerExpression; + + const iterableExtensionType = getIterableExtensionKindForNode(context, node.expression); + if (iterableExtensionType) { + if (iterableExtensionType === IterableExtensionKind.Iterable) { + return transformLuaLibFunction(context, LuaLibFeature.LuaIteratorSpread, node, innerExpression); + } else if (iterableExtensionType === IterableExtensionKind.Pairs) { + const objectEntries = transformLuaLibFunction(context, LuaLibFeature.ObjectEntries, node, innerExpression); + return createUnpackCall(context, objectEntries, node); + } else if (iterableExtensionType === IterableExtensionKind.PairsKey) { + const objectKeys = transformLuaLibFunction(context, LuaLibFeature.ObjectKeys, node, innerExpression); + return createUnpackCall(context, objectKeys, node); + } else { + assertNever(iterableExtensionType); + } + } - const type = context.checker.getTypeAtLocation(node.expression); - if (isArrayType(context, type)) { + const type = context.checker.getTypeAtLocation(node.expression); // not ts-inner expression, in case of casts + if (isAlwaysArrayType(context, type)) { + // All union members must be arrays to be able to shortcut to unpack call return createUnpackCall(context, innerExpression, node); } diff --git a/src/transformation/visitors/switch.ts b/src/transformation/visitors/switch.ts index a82ff0bf0..cee8b7f57 100644 --- a/src/transformation/visitors/switch.ts +++ b/src/transformation/visitors/switch.ts @@ -1,61 +1,269 @@ import * as ts from "typescript"; -import { LuaTarget } from "../../CompilerOptions"; import * as lua from "../../LuaAST"; -import { FunctionVisitor } from "../context"; -import { unsupportedForTarget } from "../utils/diagnostics"; -import { performHoisting, popScope, pushScope, ScopeType } from "../utils/scope"; +import { FunctionVisitor, TransformationContext } from "../context"; +import { transformInPrecedingStatementScope, WithPrecedingStatements } from "../utils/preceding-statements"; +import { ScopeType, separateHoistedStatements } from "../utils/scope"; +import { createShortCircuitBinaryExpressionPrecedingStatements } from "./binary-expression"; -export const transformSwitchStatement: FunctionVisitor = (statement, context) => { - if (context.luaTarget === LuaTarget.Universal || context.luaTarget === LuaTarget.Lua51) { - context.diagnostics.push(unsupportedForTarget(statement, "Switch statements", LuaTarget.Lua51)); +const containsBreakOrReturn = (nodes: Iterable): boolean => { + for (const s of nodes) { + if (ts.isBreakStatement(s) || ts.isReturnStatement(s)) { + return true; + } else if (ts.isBlock(s) && containsBreakOrReturn(s.statements)) { + return true; + } else if (s.kind === ts.SyntaxKind.SyntaxList) { + // We cannot use getChildren() because that breaks when using synthetic nodes from transformers + // So get children the long way + const children: ts.Node[] = []; + ts.forEachChild(s, c => children.push(c)); + if (containsBreakOrReturn(children)) { + return true; + } + } + } + + return false; +}; + +const createOrExpression = ( + context: TransformationContext, + left: lua.Expression, + right: lua.Expression, + rightPrecedingStatements: lua.Statement[] +): WithPrecedingStatements => { + if (rightPrecedingStatements.length > 0) { + return createShortCircuitBinaryExpressionPrecedingStatements( + context, + left, + right, + rightPrecedingStatements, + ts.SyntaxKind.BarBarToken + ); + } else { + return { + precedingStatements: rightPrecedingStatements, + result: lua.createBinaryExpression(left, right, lua.SyntaxKind.OrOperator), + }; } +}; - const scope = pushScope(context, ScopeType.Switch); +const coalesceCondition = ( + condition: lua.Expression | undefined, + conditionPrecedingStatements: lua.Statement[], + switchVariable: lua.Identifier, + expression: ts.Expression, + context: TransformationContext +): WithPrecedingStatements => { + const { precedingStatements, result: transformedExpression } = transformInPrecedingStatementScope(context, () => + context.transformExpression(expression) + ); - // Give the switch a unique name to prevent nested switches from acting up. + // Coalesce skipped statements + const comparison = lua.createBinaryExpression( + switchVariable, + transformedExpression, + lua.SyntaxKind.EqualityOperator + ); + if (condition) { + return createOrExpression(context, condition, comparison, precedingStatements); + } + + // Next condition + return { precedingStatements: [...conditionPrecedingStatements, ...precedingStatements], result: comparison }; +}; + +export const transformSwitchStatement: FunctionVisitor = (statement, context) => { + const scope = context.pushScope(ScopeType.Switch, statement); + + // Give the switch and condition accumulator a unique name to prevent nested switches from acting up. const switchName = `____switch${scope.id}`; + const conditionName = `____cond${scope.id}`; const switchVariable = lua.createIdentifier(switchName); + const conditionVariable = lua.createIdentifier(conditionName); - let statements: lua.Statement[] = []; + // If the switch only has a default clause, wrap it in a single do. + // Otherwise, we need to generate a set of if statements to emulate the switch. + const statements: lua.Statement[] = []; + const hoistedStatements: lua.Statement[] = []; + const hoistedIdentifiers: lua.Identifier[] = []; + const clauses = statement.caseBlock.clauses; + if (clauses.length === 1 && ts.isDefaultClause(clauses[0])) { + const defaultClause = clauses[0].statements; + if (defaultClause.length) { + const { + statements: defaultStatements, + hoistedStatements: defaultHoistedStatements, + hoistedIdentifiers: defaultHoistedIdentifiers, + } = separateHoistedStatements(context, context.transformStatements(defaultClause)); + hoistedStatements.push(...defaultHoistedStatements); + hoistedIdentifiers.push(...defaultHoistedIdentifiers); + statements.push(lua.createDoStatement(defaultStatements)); + } + } else { + // Build up the condition for each if statement + let defaultTransformed = false; + let isInitialCondition = true; + let condition: lua.Expression | undefined = undefined; + let conditionPrecedingStatements: lua.Statement[] = []; + for (let i = 0; i < clauses.length; i++) { + const clause = clauses[i]; + const previousClause: ts.CaseOrDefaultClause | undefined = clauses[i - 1]; - // Starting from the back, concatenating ifs into one big if/elseif statement - const concatenatedIf = statement.caseBlock.clauses.reduceRight((previousCondition, clause, index) => { - if (ts.isDefaultClause(clause)) { - // Skip default clause here (needs to be included to ensure index lines up with index later) - return previousCondition; + // Skip redundant default clauses, will be handled in final default case + if (i === 0 && ts.isDefaultClause(clause)) continue; + if (ts.isDefaultClause(clause) && previousClause && containsBreakOrReturn(previousClause.statements)) { + continue; + } + + // Compute the condition for the if statement + if (!ts.isDefaultClause(clause)) { + const { precedingStatements, result } = coalesceCondition( + condition, + conditionPrecedingStatements, + switchVariable, + clause.expression, + context + ); + conditionPrecedingStatements = precedingStatements; + condition = result; + + // Skip empty clauses unless final clause (i.e side-effects) + if (i !== clauses.length - 1 && clause.statements.length === 0) continue; + + // Declare or assign condition variable + if (isInitialCondition) { + statements.push( + ...conditionPrecedingStatements, + lua.createVariableDeclarationStatement(conditionVariable, condition) + ); + } else { + const { precedingStatements, result } = createOrExpression( + context, + conditionVariable, + condition, + conditionPrecedingStatements + ); + conditionPrecedingStatements = precedingStatements; + condition = result; + + statements.push( + ...conditionPrecedingStatements, + lua.createAssignmentStatement(conditionVariable, condition) + ); + } + isInitialCondition = false; + } else { + // If the default is proceeded by empty clauses and will be emitted we may need to initialize the condition + if (isInitialCondition) { + statements.push( + ...conditionPrecedingStatements, + lua.createVariableDeclarationStatement( + conditionVariable, + condition ?? lua.createBooleanLiteral(false) + ) + ); + + // Clear condition ot ensure it is not evaluated twice + condition = undefined; + conditionPrecedingStatements = []; + isInitialCondition = false; + } + + // Allow default to fallthrough to final default clause + if (i === clauses.length - 1) { + // Evaluate the final condition that we may be skipping + if (condition) { + const { precedingStatements, result } = createOrExpression( + context, + conditionVariable, + condition, + conditionPrecedingStatements + ); + conditionPrecedingStatements = precedingStatements; + condition = result; + statements.push( + ...conditionPrecedingStatements, + lua.createAssignmentStatement(conditionVariable, condition) + ); + } + continue; + } + } + + // Transform the clause and append the final break statement if necessary + const { + statements: clauseStatements, + hoistedStatements: clauseHoistedStatements, + hoistedIdentifiers: clauseHoistedIdentifiers, + } = separateHoistedStatements(context, context.transformStatements(clause.statements)); + if (i === clauses.length - 1 && !containsBreakOrReturn(clause.statements)) { + clauseStatements.push(lua.createBreakStatement()); + } + hoistedStatements.push(...clauseHoistedStatements); + hoistedIdentifiers.push(...clauseHoistedIdentifiers); + + // Remember that we transformed default clause so we don't duplicate hoisted statements later + if (ts.isDefaultClause(clause)) { + defaultTransformed = true; + } + + // Push if statement for case + statements.push(lua.createIfStatement(conditionVariable, lua.createBlock(clauseStatements))); + + // Clear condition for next clause + condition = undefined; + conditionPrecedingStatements = []; } - // If the clause condition holds, go to the correct label - const condition = lua.createBinaryExpression( - switchVariable, - context.transformExpression(clause.expression), - lua.SyntaxKind.EqualityOperator - ); + // If no conditions above match, we need to create the final default case code-path, + // as we only handle fallthrough into defaults in the previous if statement chain + const start = clauses.findIndex(c => ts.isDefaultClause(c)); + if (start >= 0) { + // Find the last clause that we can fallthrough to + const end = clauses.findIndex( + (clause, index) => index >= start && containsBreakOrReturn(clause.statements) + ); - const goto = lua.createGotoStatement(`${switchName}_case_${index}`); - return lua.createIfStatement(condition, lua.createBlock([goto]), previousCondition); - }, undefined as lua.IfStatement | undefined); + const { + statements: defaultStatements, + hoistedStatements: defaultHoistedStatements, + hoistedIdentifiers: defaultHoistedIdentifiers, + } = separateHoistedStatements(context, context.transformStatements(clauses[start].statements)); - if (concatenatedIf) { - statements.push(concatenatedIf); - } + // Only push hoisted statements if this is the first time we're transforming the default clause + if (!defaultTransformed) { + hoistedStatements.push(...defaultHoistedStatements); + hoistedIdentifiers.push(...defaultHoistedIdentifiers); + } - const hasDefaultCase = statement.caseBlock.clauses.some(ts.isDefaultClause); - statements.push(lua.createGotoStatement(`${switchName}_${hasDefaultCase ? "case_default" : "end"}`)); + // Combine the fallthrough statements + for (const clause of clauses.slice(start + 1, end >= 0 ? end + 1 : undefined)) { + let statements = context.transformStatements(clause.statements); + // Drop hoisted statements as they were already added when clauses were initially transformed above + ({ statements } = separateHoistedStatements(context, statements)); + defaultStatements.push(...statements); + } - for (const [index, clause] of statement.caseBlock.clauses.entries()) { - const labelName = `${switchName}_case_${ts.isCaseClause(clause) ? index : "default"}`; - statements.push(lua.createLabelStatement(labelName)); - statements.push(lua.createDoStatement(context.transformStatements(clause.statements))); + // Add the default clause if it has any statements + // The switch will always break on the final clause and skip execution if valid to do so + if (defaultStatements.length) { + statements.push(lua.createDoStatement(defaultStatements)); + } + } } - statements.push(lua.createLabelStatement(`${switchName}_end`)); + // Hoist the variable, function, and import statements to the top of the switch + statements.unshift(...hoistedStatements); + if (hoistedIdentifiers.length > 0) { + statements.unshift(lua.createVariableDeclarationStatement(hoistedIdentifiers)); + } - statements = performHoisting(context, statements); - popScope(context); + context.popScope(); + // Add the switch expression after hoisting const expression = context.transformExpression(statement.expression); statements.unshift(lua.createVariableDeclarationStatement(switchVariable, expression)); - return statements; + // Wrap the statements in a repeat until true statement to facilitate dynamic break/returns + return lua.createRepeatStatement(lua.createBlock(statements), lua.createBooleanLiteral(true)); }; diff --git a/src/transformation/visitors/template.ts b/src/transformation/visitors/template.ts index 9f9a19b33..291c703d5 100644 --- a/src/transformation/visitors/template.ts +++ b/src/transformation/visitors/template.ts @@ -1,10 +1,11 @@ import * as ts from "typescript"; import * as lua from "../../LuaAST"; import { FunctionVisitor } from "../context"; -import { ContextType, getDeclarationContextType } from "../utils/function-context"; +import { ContextType, getCallContextType } from "../utils/function-context"; import { wrapInToStringForConcat } from "../utils/lua-ast"; -import { isStringType } from "../utils/typescript/types"; +import { isStringType } from "../utils/typescript"; import { transformArguments, transformContextualCallExpression } from "./call"; +import { transformOrderedExpressions } from "./expression-list"; // TODO: Source positions function getRawLiteral(node: ts.LiteralLikeNode): string { @@ -24,8 +25,13 @@ export const transformTemplateExpression: FunctionVisitor parts.push(lua.createStringLiteral(head, node.head)); } - for (const span of node.templateSpans) { - const expression = context.transformExpression(span.expression); + const transformedExpressions = transformOrderedExpressions( + context, + node.templateSpans.map(s => s.expression) + ); + for (let i = 0; i < node.templateSpans.length; ++i) { + const span = node.templateSpans[i]; + const expression = transformedExpressions[i]; const spanType = context.checker.getTypeAtLocation(span.expression); if (isStringType(context, spanType)) { parts.push(expression); @@ -82,17 +88,14 @@ export const transformTaggedTemplateExpression: FunctionVisitor undefined, [ts.SyntaxKind.NonNullExpression]: (node, context) => context.transformExpression(node.expression), + [ts.SyntaxKind.ExpressionWithTypeArguments]: (node, context) => context.transformExpression(node.expression), + [ts.SyntaxKind.SatisfiesExpression]: (node, context) => context.transformExpression(node.expression), [ts.SyntaxKind.AsExpression]: transformAssertionExpression, [ts.SyntaxKind.TypeAssertionExpression]: transformAssertionExpression, [ts.SyntaxKind.NotEmittedStatement]: () => undefined, diff --git a/src/transformation/visitors/unary-expression.ts b/src/transformation/visitors/unary-expression.ts index 5960c2200..88b512757 100644 --- a/src/transformation/visitors/unary-expression.ts +++ b/src/transformation/visitors/unary-expression.ts @@ -7,6 +7,8 @@ import { transformCompoundAssignmentExpression, transformCompoundAssignmentStatement, } from "./binary-expression/compound"; +import { isNumberType } from "../utils/typescript"; +import { LuaLibFeature, transformLuaLibFunction } from "../utils/lualib"; export function transformUnaryExpressionStatement( context: TransformationContext, @@ -92,16 +94,29 @@ export const transformPrefixUnaryExpression: FunctionVisitor( + const tableExpression = propertyAccessStack.reduce( (path, property) => lua.createTableIndexExpression(path, transformPropertyName(context, property)), table ); @@ -64,7 +70,11 @@ export function transformBindingPattern( // The identifier of the new variable const variableName = transformIdentifier(context, element.name); // The field to extract - const propertyName = transformPropertyName(context, element.propertyName ?? element.name); + const elementName = element.propertyName ?? element.name; + const { precedingStatements, result: propertyName } = transformInPrecedingStatementScope(context, () => + transformPropertyName(context, elementName) + ); + result.push(...precedingStatements); // Keep property's preceding statements in order let expression: lua.Expression; if (element.dotDotDotToken) { @@ -124,11 +134,15 @@ export function transformBindingPattern( result.push(...createLocalOrExportedOrGlobalDeclaration(context, variableName, expression)); if (element.initializer) { const identifier = addExportToIdentifier(context, variableName); + const tsInitializer = element.initializer; + const { precedingStatements: initializerPrecedingStatements, result: initializer } = + transformInPrecedingStatementScope(context, () => context.transformExpression(tsInitializer)); result.push( lua.createIfStatement( lua.createBinaryExpression(identifier, lua.createNilLiteral(), lua.SyntaxKind.EqualityOperator), lua.createBlock([ - lua.createAssignmentStatement(identifier, context.transformExpression(element.initializer)), + ...initializerPrecedingStatements, + lua.createAssignmentStatement(identifier, initializer), ]) ) ); @@ -151,19 +165,21 @@ export function transformBindingVariableDeclaration( ts.isBindingElement(e) && (!ts.isIdentifier(e.name) || e.dotDotDotToken); if (ts.isObjectBindingPattern(bindingPattern) || bindingPattern.elements.some(isComplexBindingElement)) { - let table: lua.Identifier; - if (initializer !== undefined && ts.isIdentifier(initializer)) { - table = transformIdentifier(context, initializer); - } else { + let table: lua.Expression; + if (initializer) { // Contain the expression in a temporary variable - table = lua.createAnonymousIdentifier(); - if (initializer) { - let expression = context.transformExpression(initializer); - if (isTupleReturnCall(context, initializer) || isMultiReturnCall(context, initializer)) { - expression = wrapInTable(expression); - } - statements.push(lua.createVariableDeclarationStatement(table, expression)); + let expression = context.transformExpression(initializer); + if (isMultiReturnCall(context, initializer)) { + expression = wrapInTable(expression); } + const { precedingStatements: moveStatements, result: movedExpr } = transformInPrecedingStatementScope( + context, + () => moveToPrecedingTemp(context, expression, initializer) + ); + statements.push(...moveStatements); + table = movedExpr; + } else { + table = lua.createAnonymousIdentifier(); } statements.push(...transformBindingPattern(context, bindingPattern, table)); return statements; @@ -175,8 +191,8 @@ export function transformBindingVariableDeclaration( : lua.createAnonymousIdentifier(); if (initializer) { - if (isTupleReturnCall(context, initializer) || isMultiReturnCall(context, initializer)) { - // Don't unpack @tupleReturn or LuaMultiReturn functions + if (isMultiReturnCall(context, initializer)) { + // Don't unpack LuaMultiReturn functions statements.push( ...createLocalOrExportedOrGlobalDeclaration( context, @@ -189,14 +205,15 @@ export function transformBindingVariableDeclaration( // Don't unpack array literals const values = initializer.elements.length > 0 - ? initializer.elements.map(e => context.transformExpression(e)) + ? transformExpressionList(context, initializer.elements) : lua.createNilLiteral(); statements.push(...createLocalOrExportedOrGlobalDeclaration(context, vars, values, initializer)); } else { - // local vars = this.transpileDestructingAssignmentValue(node.initializer); - const unpackedInitializer = createUnpackCall( + // use unpack(thing, 1, #bindingItems) to unpack + const unpackedInitializer = createBoundedUnpackCall( context, context.transformExpression(initializer), + bindingPattern.elements.length, initializer ); statements.push( @@ -242,17 +259,32 @@ export function transformVariableDeclaration( // Find variable identifier const identifierName = transformIdentifier(context, statement.name); const value = statement.initializer && context.transformExpression(statement.initializer); - return createLocalOrExportedOrGlobalDeclaration(context, identifierName, value, statement); + + // Wrap functions being assigned to a type that contains additional properties in a callable table + // This catches 'const foo = function() {}; foo.bar = "FOOBAR";' + const wrappedValue = value && shouldWrapInitializerInCallableTable() ? createCallableTable(value) : value; + + return createLocalOrExportedOrGlobalDeclaration(context, identifierName, wrappedValue, statement); } else if (ts.isArrayBindingPattern(statement.name) || ts.isObjectBindingPattern(statement.name)) { return transformBindingVariableDeclaration(context, statement.name, statement.initializer); } else { return assertNever(statement.name); } + + function shouldWrapInitializerInCallableTable() { + assert(statement.initializer); + const initializer = ts.skipOuterExpressions(statement.initializer); + // do not wrap if not a function expression + if (!ts.isFunctionExpression(initializer) && !ts.isArrowFunction(initializer)) return false; + // Skip named function expressions because they will have been wrapped already + if (ts.isFunctionExpression(initializer) && initializer.name) return false; + return isFunctionTypeWithProperties(context, context.checker.getTypeAtLocation(statement.name)); + } } export function checkVariableDeclarationList(context: TransformationContext, node: ts.VariableDeclarationList): void { - if ((node.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const)) === 0) { - const token = node.getFirstToken(); + if ((node.flags & (ts.NodeFlags.Let | ts.NodeFlags.Const | ts.NodeFlags.Using | ts.NodeFlags.AwaitUsing)) === 0) { + const token = ts.getOriginalNode(node).getFirstToken(); assert(token); context.diagnostics.push(unsupportedVarDeclaration(token)); } diff --git a/src/transformation/visitors/void.ts b/src/transformation/visitors/void.ts new file mode 100644 index 000000000..c942e859c --- /dev/null +++ b/src/transformation/visitors/void.ts @@ -0,0 +1,15 @@ +import * as ts from "typescript"; +import * as lua from "../../LuaAST"; +import { FunctionVisitor } from "../context"; +import { wrapInStatement } from "./expression-statement"; + +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void +export const transformVoidExpression: FunctionVisitor = (node, context) => { + // If content is a literal it is safe to replace the entire expression with nil + if (!ts.isLiteralExpression(node.expression)) { + const statements = wrapInStatement(context.transformExpression(node.expression)); + if (statements) context.addPrecedingStatements(statements); + } + + return lua.createNilLiteral(); +}; diff --git a/src/transpilation/bundle.ts b/src/transpilation/bundle.ts index 31d58b27b..b030bddfe 100644 --- a/src/transpilation/bundle.ts +++ b/src/transpilation/bundle.ts @@ -1,28 +1,36 @@ import * as path from "path"; import { SourceNode } from "source-map"; import * as ts from "typescript"; -import { CompilerOptions } from "../CompilerOptions"; +import { CompilerOptions, LuaTarget } from "../CompilerOptions"; import { escapeString, tstlHeader } from "../LuaPrinter"; -import { cast, formatPathToLuaPath, isNonNull, normalizeSlashes, trimExtension } from "../utils"; +import { cast, formatPathToLuaPath, isNonNull, trimExtension } from "../utils"; import { couldNotFindBundleEntryPoint } from "./diagnostics"; -import { getEmitOutDir, getEmitPathRelativeToOutDir, getSourceDir } from "./transpiler"; +import { getEmitOutDir, getEmitPathRelativeToOutDir, getProjectRoot } from "./transpiler"; import { EmitFile, ProcessedFile } from "./utils"; const createModulePath = (pathToResolve: string, program: ts.Program) => escapeString(formatPathToLuaPath(trimExtension(getEmitPathRelativeToOutDir(pathToResolve, program)))); // Override `require` to read from ____modules table. -const requireOverride = ` +function requireOverride(options: CompilerOptions) { + const runModule = + options.luaTarget === LuaTarget.Lua50 + ? "if (table.getn(arg) > 0) then value = module(unpack(arg)) else value = module(file) end" + : 'if (select("#", ...) > 0) then value = module(...) else value = module(file) end'; + return ` local ____modules = {} local ____moduleCache = {} local ____originalRequire = require -local function require(file) +local function require(file, ...) if ____moduleCache[file] then - return ____moduleCache[file] + return ____moduleCache[file].value end if ____modules[file] then - ____moduleCache[file] = ____modules[file]() - return ____moduleCache[file] + local module = ____modules[file] + local value = nil + ${runModule} + ____moduleCache[file] = { value = value } + return value else if ____originalRequire then return ____originalRequire(file) @@ -32,6 +40,50 @@ local function require(file) end end `; +} + +export const sourceMapTracebackBundlePlaceholder = "{#SourceMapTracebackBundle}"; + +type SourceMapLineData = number | { line: number; file: string }; + +export function printStackTraceBundleOverride(rootNode: SourceNode): string { + const map: Record = {}; + const getLineNumber = (line: number, fallback: number) => { + const data: SourceMapLineData | undefined = map[line]; + if (data === undefined) { + return fallback; + } + if (typeof data === "number") { + return data; + } + return data.line; + }; + const transformLineData = (data: SourceMapLineData) => { + if (typeof data === "number") { + return data; + } + return `{line = ${data.line}, file = "${data.file}"}`; + }; + + let currentLine = 1; + rootNode.walk((chunk, mappedPosition) => { + if (mappedPosition.line !== undefined && mappedPosition.line > 0) { + const line = getLineNumber(currentLine, mappedPosition.line); + + map[currentLine] = { + line, + file: path.basename(mappedPosition.source), + }; + } + + currentLine += chunk.split("\n").length - 1; + }); + + const mapItems = Object.entries(map).map(([line, original]) => `["${line}"] = ${transformLineData(original)}`); + const mapString = "{" + mapItems.join(",") + "}"; + + return `__TS__SourceMapTraceBack(debug.getinfo(1).short_src, ${mapString});`; +} export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [ts.Diagnostic[], EmitFile] { const diagnostics: ts.Diagnostic[] = []; @@ -41,10 +93,12 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t const entryModule = cast(options.luaBundleEntry, isNonNull); // Resolve project settings relative to project file. - const resolvedEntryModule = path.resolve(getSourceDir(program), entryModule); - const outputPath = normalizeSlashes(path.resolve(getEmitOutDir(program), bundleFile)); + const resolvedEntryModule = path.resolve(getProjectRoot(program), entryModule); + const outputPath = path.resolve(getEmitOutDir(program), bundleFile); + const entryModuleFilePath = + program.getSourceFile(entryModule)?.fileName ?? program.getSourceFile(resolvedEntryModule)?.fileName; - if (program.getSourceFile(resolvedEntryModule) === undefined && program.getSourceFile(entryModule) === undefined) { + if (entryModuleFilePath === undefined) { diagnostics.push(couldNotFindBundleEntryPoint(entryModule)); } @@ -55,16 +109,27 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t const moduleTable = createModuleTableNode(moduleTableEntries); // return require("") - const entryPoint = `return require(${createModulePath(entryModule, program)})\n`; + const args = options.luaTarget === LuaTarget.Lua50 ? "unpack(arg == nil and {} or arg)" : "..."; + // Avoid producing a tail-call (which removes the bundle's stack frame) by assigning the `require` result to a local and returning it. + const entryPath = createModulePath(entryModuleFilePath ?? entryModule, program); + const entryPoint = `local ____entry = require(${entryPath}, ${args})\nreturn ____entry\n`; + + const footers: string[] = []; + if (options.sourceMapTraceback) { + // Generates SourceMapTraceback for the entire file + footers.push('local __TS__SourceMapTraceBack = require("lualib_bundle").__TS__SourceMapTraceBack\n'); + footers.push(`${sourceMapTracebackBundlePlaceholder}\n`); + } - const sourceChunks = [requireOverride, moduleTable, entryPoint]; + const sourceChunks = [requireOverride(options), moduleTable, ...footers, entryPoint]; if (!options.noHeader) { sourceChunks.unshift(tstlHeader); } const bundleNode = joinSourceChunks(sourceChunks); - const { code, map } = bundleNode.toStringWithSourceMap(); + let { code, map } = bundleNode.toStringWithSourceMap(); + code = code.replace(sourceMapTracebackBundlePlaceholder, printStackTraceBundleOverride(bundleNode)); return [ diagnostics, @@ -78,7 +143,7 @@ export function getBundleResult(program: ts.Program, files: ProcessedFile[]): [t } function moduleSourceNode({ code, sourceMapNode }: ProcessedFile, modulePath: string): SourceNode { - const tableEntryHead = `[${modulePath}] = function() `; + const tableEntryHead = `[${modulePath}] = function(...) \n`; const tableEntryTail = " end,\n"; return joinSourceChunks([tableEntryHead, sourceMapNode ?? code, tableEntryTail]); @@ -92,6 +157,7 @@ function createModuleTableNode(fileChunks: SourceChunk[]): SourceNode { } type SourceChunk = string | SourceNode; + function joinSourceChunks(chunks: SourceChunk[]): SourceNode { return new SourceNode(null, null, null, chunks); } diff --git a/src/transpilation/diagnostics.ts b/src/transpilation/diagnostics.ts index bfae6b140..1ee589708 100644 --- a/src/transpilation/diagnostics.ts +++ b/src/transpilation/diagnostics.ts @@ -8,7 +8,7 @@ const createDiagnosticFactory = ( export const couldNotResolveRequire = createDiagnosticFactory( (requirePath: string, containingFile: string) => - `Could not resolve require path '${requirePath}' in file ${containingFile}.` + `Could not resolve lua source files for require path '${requirePath}' in file ${containingFile}.` ); export const couldNotReadDependency = createDiagnosticFactory( @@ -48,3 +48,16 @@ export const usingLuaBundleWithInlineMightGenerateDuplicateCode = createSerialDi "Using 'luaBundle' with 'luaLibImport: \"inline\"' might generate duplicate code. " + "It is recommended to use 'luaLibImport: \"require\"'.", })); + +export const cannotBundleLibrary = createDiagnosticFactory( + () => + 'Cannot bundle projects with "buildmode": "library". Projects including the library can still bundle (which will include external library files).' +); + +export const unsupportedJsxEmit = createDiagnosticFactory(() => 'JSX is only supported with "react" jsx option.'); + +export const emitPathCollision = createDiagnosticFactory( + (outputPath: string, file1: string, file2: string) => + `Output path '${outputPath}' is used by both '${file1}' and '${file2}'. ` + + `Dots in file/directory names are replaced with underscores for Lua module resolution.` +); diff --git a/src/transpilation/find-lua-requires.ts b/src/transpilation/find-lua-requires.ts new file mode 100644 index 000000000..90be465e6 --- /dev/null +++ b/src/transpilation/find-lua-requires.ts @@ -0,0 +1,168 @@ +export interface LuaRequire { + from: number; + to: number; + requirePath: string; +} + +export function findLuaRequires(lua: string): LuaRequire[] { + return findRequire(lua, 0); +} + +function findRequire(lua: string, offset: number): LuaRequire[] { + const result = []; + + while (offset < lua.length) { + const c = lua[offset]; + if ( + c === "r" && + (offset === 0 || + isWhitespace(lua[offset - 1]) || + lua[offset - 1] === "]" || + lua[offset - 1] === "(" || + lua[offset - 1] === "[") + ) { + const m = matchRequire(lua, offset); + if (m.matched) { + offset = m.match.to + 1; + result.push(m.match); + } else { + offset = m.end; + } + } else if (c === '"' || c === "'") { + offset = readString(lua, offset, c).offset; // Skip string and surrounding quotes + } else if (c === "-" && offset + 1 < lua.length && lua[offset + 1] === "-") { + offset = skipComment(lua, offset); + } else { + offset++; + } + } + + return result; +} + +type MatchResult = { matched: true; match: T } | { matched: false; end: number }; + +function matchRequire(lua: string, offset: number): MatchResult { + const start = offset; + for (const c of "require") { + if (offset > lua.length) { + return { matched: false, end: offset }; + } + + if (lua[offset] !== c) { + return { matched: false, end: offset }; + } + offset++; + } + + offset = skipWhitespace(lua, offset); + + let hasParentheses = false; + + if (offset > lua.length) { + return { matched: false, end: offset }; + } else { + if (lua[offset] === "(") { + hasParentheses = true; + offset++; + offset = skipWhitespace(lua, offset); + } else if (lua[offset] === '"' || lua[offset] === "'") { + // require without parentheses + } else { + // otherwise fail match + return { matched: false, end: offset }; + } + } + + if (offset > lua.length || (lua[offset] !== '"' && lua[offset] !== "'")) { + return { matched: false, end: offset }; + } + + const { value: requireString, offset: offsetAfterString } = readString(lua, offset, lua[offset]); + offset = offsetAfterString; // Skip string and surrounding quotes + + if (hasParentheses) { + offset = skipWhitespace(lua, offset); + + if (offset > lua.length || lua[offset] !== ")") { + return { matched: false, end: offset }; + } + + offset++; + } + + return { matched: true, match: { from: start, to: offset - 1, requirePath: requireString } }; +} + +function readString(lua: string, offset: number, delimiter: string): { value: string; offset: number } { + expect(lua, offset, delimiter); + offset++; + + let start = offset; + let result = ""; + + let escaped = false; + while (offset < lua.length && (lua[offset] !== delimiter || escaped)) { + if (lua[offset] === "\\" && !escaped) { + escaped = true; + } else { + if (lua[offset] === delimiter) { + result += lua.slice(start, offset - 1); + start = offset; + } + escaped = false; + } + + offset++; + } + + if (offset < lua.length) { + expect(lua, offset, delimiter); + } + + result += lua.slice(start, offset); + return { value: result, offset: offset + 1 }; +} + +function skipWhitespace(lua: string, offset: number): number { + while (offset < lua.length && isWhitespace(lua[offset])) { + offset++; + } + return offset; +} + +function isWhitespace(c: string): boolean { + return c === " " || c === "\t" || c === "\r" || c === "\n"; +} + +function skipComment(lua: string, offset: number): number { + expect(lua, offset, "-"); + expect(lua, offset + 1, "-"); + offset += 2; + + if (offset + 1 < lua.length && lua[offset] === "[" && lua[offset + 1] === "[") { + return skipMultiLineComment(lua, offset); + } else { + return skipSingleLineComment(lua, offset); + } +} + +function skipMultiLineComment(lua: string, offset: number): number { + while (offset < lua.length && !(lua[offset] === "]" && lua[offset - 1] === "]")) { + offset++; + } + return offset + 1; +} + +function skipSingleLineComment(lua: string, offset: number): number { + while (offset < lua.length && lua[offset] !== "\n") { + offset++; + } + return offset + 1; +} + +function expect(lua: string, offset: number, char: string) { + if (lua[offset] !== char) { + throw new Error(`Expected ${char} at position ${offset} but found ${lua[offset]}`); + } +} diff --git a/src/transpilation/index.ts b/src/transpilation/index.ts index 59e83233a..d4e890719 100644 --- a/src/transpilation/index.ts +++ b/src/transpilation/index.ts @@ -3,6 +3,7 @@ import * as path from "path"; import * as ts from "typescript"; import { parseConfigFileWithSystem } from "../cli/tsconfig"; import { CompilerOptions } from "../CompilerOptions"; +import { normalizeSlashes } from "../utils"; import { createEmitOutputCollector, TranspiledFile } from "./output-collector"; import { EmitResult, Transpiler } from "./transpiler"; @@ -18,11 +19,9 @@ export function transpileFiles( writeFile?: ts.WriteFileCallback ): EmitResult { const program = ts.createProgram(rootNames, options); + const preEmitDiagnostics = ts.getPreEmitDiagnostics(program); const { diagnostics: transpileDiagnostics, emitSkipped } = new Transpiler().emit({ program, writeFile }); - const diagnostics = ts.sortAndDeduplicateDiagnostics([ - ...ts.getPreEmitDiagnostics(program), - ...transpileDiagnostics, - ]); + const diagnostics = ts.sortAndDeduplicateDiagnostics([...preEmitDiagnostics, ...transpileDiagnostics]); return { diagnostics: [...diagnostics], emitSkipped }; } @@ -44,8 +43,12 @@ const libCache: { [key: string]: ts.SourceFile } = {}; /** @internal */ export function createVirtualProgram(input: Record, options: CompilerOptions = {}): ts.Program { + const normalizedFiles: Record = {}; + for (const [path, file] of Object.entries(input)) { + normalizedFiles[normalizeSlashes(path)] = file; + } const compilerHost: ts.CompilerHost = { - fileExists: fileName => fileName in input || ts.sys.fileExists(fileName), + fileExists: fileName => fileName in normalizedFiles || ts.sys.fileExists(fileName), getCanonicalFileName: fileName => fileName, getCurrentDirectory: () => "", getDefaultLibFileName: ts.getDefaultLibFileName, @@ -55,8 +58,8 @@ export function createVirtualProgram(input: Record, options: Com writeFile() {}, getSourceFile(fileName) { - if (fileName in input) { - return ts.createSourceFile(fileName, input[fileName], ts.ScriptTarget.Latest, false); + if (fileName in normalizedFiles) { + return ts.createSourceFile(fileName, normalizedFiles[fileName], ts.ScriptTarget.Latest, false); } let filePath: string | undefined; @@ -80,7 +83,7 @@ export function createVirtualProgram(input: Record, options: Com }, }; - return ts.createProgram(Object.keys(input), options, compilerHost); + return ts.createProgram(Object.keys(normalizedFiles), options, compilerHost); } export interface TranspileVirtualProjectResult { @@ -93,12 +96,10 @@ export function transpileVirtualProject( options: CompilerOptions = {} ): TranspileVirtualProjectResult { const program = createVirtualProgram(files, options); + const preEmitDiagnostics = ts.getPreEmitDiagnostics(program); const collector = createEmitOutputCollector(); const { diagnostics: transpileDiagnostics } = new Transpiler().emit({ program, writeFile: collector.writeFile }); - const diagnostics = ts.sortAndDeduplicateDiagnostics([ - ...ts.getPreEmitDiagnostics(program), - ...transpileDiagnostics, - ]); + const diagnostics = ts.sortAndDeduplicateDiagnostics([...preEmitDiagnostics, ...transpileDiagnostics]); return { diagnostics: [...diagnostics], transpiledFiles: collector.files }; } diff --git a/src/transpilation/output-collector.ts b/src/transpilation/output-collector.ts index 866c1f79a..1458b2403 100644 --- a/src/transpilation/output-collector.ts +++ b/src/transpilation/output-collector.ts @@ -14,7 +14,7 @@ export interface TranspiledFile { jsSourceMap?: string; } -export function createEmitOutputCollector() { +export function createEmitOutputCollector(luaExtension = ".lua") { const files: TranspiledFile[] = []; const writeFile: ts.WriteFileCallback = (fileName, data, _bom, _onError, sourceFiles = []) => { let file = files.find(f => intersection(f.sourceFiles, sourceFiles).length > 0); @@ -25,9 +25,9 @@ export function createEmitOutputCollector() { file.sourceFiles = union(file.sourceFiles, sourceFiles); } - if (fileName.endsWith(".lua")) { + if (fileName.endsWith(luaExtension)) { file.lua = data; - } else if (fileName.endsWith(".lua.map")) { + } else if (fileName.endsWith(`${luaExtension}.map`)) { file.luaSourceMap = data; } else if (fileName.endsWith(".js")) { file.js = data; diff --git a/src/transpilation/plugins.ts b/src/transpilation/plugins.ts index 0311efbbd..f84af2bc9 100644 --- a/src/transpilation/plugins.ts +++ b/src/transpilation/plugins.ts @@ -1,8 +1,10 @@ import * as ts from "typescript"; +import { EmitHost } from ".."; import { CompilerOptions } from "../CompilerOptions"; import { Printer } from "../LuaPrinter"; import { Visitors } from "../transformation/context"; -import { getConfigDirectory, resolvePlugin } from "./utils"; +import { EmitFile, getConfigDirectory, ProcessedFile, resolvePlugin } from "./utils"; +import * as performance from "../measure-performance"; export interface Plugin { /** @@ -18,29 +20,91 @@ export interface Plugin { * At most one custom printer can be provided across all plugins. */ printer?: Printer; + + /** + * This function is called before transpilation of the TypeScript program starts. + */ + beforeTransform?: (program: ts.Program, options: CompilerOptions, emitHost: EmitHost) => ts.Diagnostic[] | void; + + /** + * This function is called after translating the input program to Lua, but before resolving dependencies or bundling. + */ + afterPrint?: ( + program: ts.Program, + options: CompilerOptions, + emitHost: EmitHost, + result: ProcessedFile[] + ) => ts.Diagnostic[] | void; + + /** + * This function is called after translating the input program to Lua, after resolving dependencies and after bundling. + */ + beforeEmit?: ( + program: ts.Program, + options: CompilerOptions, + emitHost: EmitHost, + result: EmitFile[] + ) => ts.Diagnostic[] | void; + + /** + * This function is called after translating the input program to Lua, after resolving dependencies, after bundling and writing files to disk. + */ + afterEmit?: ( + program: ts.Program, + options: CompilerOptions, + emitHost: EmitHost, + result: EmitFile[] + ) => ts.Diagnostic[] | void; + + /** + * This function is called when trying to resolve the .lua file corresponding to a Lua require statement. Allows you to provide + * your own module resolution logic. If return value is undefined, regular module resolution is done. + */ + moduleResolution?: ( + moduleIdentifier: string, + requiringFile: string, + options: CompilerOptions, + emitHost: EmitHost + ) => string | undefined; } -export function getPlugins(program: ts.Program, diagnostics: ts.Diagnostic[], customPlugins: Plugin[]): Plugin[] { +export function getPlugins(program: ts.Program): { diagnostics: ts.Diagnostic[]; plugins: Plugin[] } { + performance.startSection("getPlugins"); + const diagnostics: ts.Diagnostic[] = []; const pluginsFromOptions: Plugin[] = []; const options = program.getCompilerOptions() as CompilerOptions; for (const [index, pluginOption] of (options.luaPlugins ?? []).entries()) { const optionName = `tstl.luaPlugins[${index}]`; - const { error: resolveError, result: factory } = resolvePlugin( - "plugin", - `${optionName}.name`, - getConfigDirectory(options), - pluginOption.name, - pluginOption.import - ); + const factory = (() => { + if ("plugin" in pluginOption) { + return pluginOption.plugin; + } else { + const { error: resolveError, result: factory } = resolvePlugin( + "plugin", + `${optionName}.name`, + getConfigDirectory(options), + pluginOption.name, + pluginOption.import + ); + + if (resolveError) diagnostics.push(resolveError); + return factory; + } + })(); - if (resolveError) diagnostics.push(resolveError); if (factory === undefined) continue; const plugin = typeof factory === "function" ? factory(pluginOption) : factory; pluginsFromOptions.push(plugin); } - return [...customPlugins, ...pluginsFromOptions]; + if (options.tstlVerbose) { + console.log(`Loaded ${pluginsFromOptions.length} plugins`); + } + + performance.endSection("getPlugins"); + + return { diagnostics, plugins: pluginsFromOptions }; } diff --git a/src/transpilation/resolve.ts b/src/transpilation/resolve.ts index 3eca25e2e..af7981ffc 100644 --- a/src/transpilation/resolve.ts +++ b/src/transpilation/resolve.ts @@ -5,15 +5,20 @@ import * as fs from "fs"; import { EmitHost, ProcessedFile } from "./utils"; import { SourceNode } from "source-map"; import { getEmitPathRelativeToOutDir, getProjectRoot, getSourceDir } from "./transpiler"; -import { formatPathToLuaPath, trimExtension } from "../utils"; +import { formatPathToLuaPath, normalizeSlashes, trimExtension } from "../utils"; import { couldNotReadDependency, couldNotResolveRequire } from "./diagnostics"; -import { BuildMode } from "../CompilerOptions"; +import { BuildMode, CompilerOptions } from "../CompilerOptions"; +import { findLuaRequires, LuaRequire } from "./find-lua-requires"; +import { Plugin } from "./plugins"; +import * as picomatch from "picomatch"; const resolver = resolve.ResolverFactory.createResolver({ extensions: [".lua"], enforceExtension: true, // Resolved file must be a lua file - fileSystem: { ...new resolve.CachedInputFileSystem(fs) }, + fileSystem: { ...new resolve.CachedInputFileSystem(fs, 0) }, useSyncFileSystemCalls: true, + conditionNames: ["require", "node", "tstl", "default"], + symlinks: false, // Do not resolve symlinks to their original paths (that breaks node_modules detection) }); interface ResolutionResult { @@ -21,166 +26,398 @@ interface ResolutionResult { diagnostics: ts.Diagnostic[]; } -export function resolveDependencies(program: ts.Program, files: ProcessedFile[], emitHost: EmitHost): ResolutionResult { - const outFiles: ProcessedFile[] = [...files]; - const diagnostics: ts.Diagnostic[] = []; +class ResolutionContext { + private noResolvePaths: picomatch.Glob[]; - // Resolve dependencies for all processed files - for (const file of files) { - const resolutionResult = resolveFileDependencies(file, program, emitHost); - outFiles.push(...resolutionResult.resolvedFiles); - diagnostics.push(...resolutionResult.diagnostics); + public diagnostics: ts.Diagnostic[] = []; + public resolvedFiles = new Map(); + + constructor( + public readonly program: ts.Program, + public readonly options: CompilerOptions, + private readonly emitHost: EmitHost, + private readonly plugins: Plugin[] + ) { + this.noResolvePaths = [...new Set(options.noResolvePaths)]; } - return { resolvedFiles: outFiles, diagnostics }; -} + public addAndResolveDependencies(file: ProcessedFile): void { + if (this.resolvedFiles.has(file.fileName)) return; + this.resolvedFiles.set(file.fileName, file); + + // Do this backwards so the replacements do not mess with the positions of the previous requires + for (const required of findLuaRequires(file.code).reverse()) { + // Do not resolve noResolution paths + if (required.requirePath.startsWith("@NoResolution:")) { + // Remove @NoResolution prefix if not building in library mode + if (!isBuildModeLibrary(this.program)) { + const path = required.requirePath.replace("@NoResolution:", ""); + replaceRequireInCode(file, required, path, this.options.extension); + replaceRequireInSourceMap(file, required, path, this.options.extension); + } -function resolveFileDependencies(file: ProcessedFile, program: ts.Program, emitHost: EmitHost): ResolutionResult { - const dependencies: ProcessedFile[] = []; - const diagnostics: ts.Diagnostic[] = []; + // Skip + continue; + } + // Try to resolve the import starting from the directory `file` is in + this.resolveImport(file, required); + } + } - for (const required of findRequiredPaths(file.code)) { - // Do no resolve lualib - if (required === "lualib_bundle") { - continue; + public resolveImport(file: ProcessedFile, required: LuaRequire): void { + // Do no resolve lualib - always use the lualib of the application entry point, not the lualib from external packages + if (required.requirePath === "lualib_bundle") { + this.resolvedFiles.set("lualib_bundle", { fileName: "lualib_bundle", code: "" }); + return; } - // Do not resolve noResolution paths - if (required.startsWith("@NoResolution:")) { - const path = required.replace("@NoResolution:", ""); - replaceRequireInCode(file, required, path); - replaceRequireInSourceMap(file, required, path); - continue; + if (this.noResolvePaths.find(glob => picomatch.isMatch(required.requirePath, glob))) { + if (this.options.tstlVerbose) { + console.log( + `Skipping module resolution of ${required.requirePath} as it is in the tsconfig noResolvePaths.` + ); + } + return; } - // Try to resolve the import starting from the directory `file` is in - const fileDir = path.dirname(file.fileName); - const resolvedDependency = resolveDependency(fileDir, required, program, emitHost); - if (resolvedDependency) { - // Figure out resolved require path and dependency output path - const resolvedRequire = getEmitPathRelativeToOutDir(resolvedDependency, program); + const dependencyPath = + this.resolveDependencyPathsWithPlugins(file, required.requirePath) ?? + this.resolveDependencyPath(file, required.requirePath); - if (shouldRewriteRequires(resolvedDependency, program)) { - replaceRequireInCode(file, required, resolvedRequire); - replaceRequireInSourceMap(file, required, resolvedRequire); - } + if (!dependencyPath) return this.couldNotResolveImport(required, file); - // If dependency is not part of project, add dependency to output and resolve its dependencies recursively - if (shouldIncludeDependency(resolvedDependency, program)) { - // If dependency resolved successfully, read its content - const dependencyContent = emitHost.readFile(resolvedDependency); - if (dependencyContent === undefined) { - diagnostics.push(couldNotReadDependency(resolvedDependency)); - continue; + if (this.options.tstlVerbose) { + console.log(`Resolved ${required.requirePath} to ${normalizeSlashes(dependencyPath)}`); + } + + this.processDependency(dependencyPath); + // Figure out resolved require path and dependency output path + if (shouldRewriteRequires(dependencyPath, this.program)) { + const resolvedRequire = getEmitPathRelativeToOutDir(dependencyPath, this.program); + replaceRequireInCode(file, required, resolvedRequire, this.options.extension); + replaceRequireInSourceMap(file, required, resolvedRequire, this.options.extension); + } + } + + private resolveDependencyPathsWithPlugins(requiringFile: ProcessedFile, dependency: string) { + const requiredFromLuaFile = requiringFile.fileName.endsWith(".lua"); + for (const plugin of this.plugins) { + if (plugin.moduleResolution != null) { + const pluginResolvedPath = plugin.moduleResolution( + dependency, + requiringFile.fileName, + this.options, + this.emitHost + ); + if (pluginResolvedPath !== undefined) { + // If resolved path is absolute no need to further resolve it + if (path.isAbsolute(pluginResolvedPath)) { + return pluginResolvedPath; + } + + // If lua file is in node_module + if (requiredFromLuaFile && isNodeModulesFile(requiringFile.fileName)) { + // If requiring file is in lua module, try to resolve sibling in that file first + const resolvedNodeModulesFile = this.resolveLuaDependencyPathFromNodeModules( + requiringFile, + pluginResolvedPath + ); + if (resolvedNodeModulesFile) { + if (this.options.tstlVerbose) { + console.log( + `Resolved file path for module ${dependency} to path ${pluginResolvedPath} using plugin.` + ); + } + return resolvedNodeModulesFile; + } + } + + const resolvedPath = this.formatPathToFile(pluginResolvedPath, requiringFile); + const fileFromPath = this.getFileFromPath(resolvedPath); + + if (fileFromPath) { + if (this.options.tstlVerbose) { + console.log( + `Resolved file path for module ${dependency} to path ${pluginResolvedPath} using plugin.` + ); + } + return fileFromPath; + } } + } + } + } + + public processedDependencies = new Set(); + + private formatPathToFile(targetPath: string, required: ProcessedFile) { + const isRelative = ["/", "./", "../"].some(p => targetPath.startsWith(p)); - const dependency = { - fileName: resolvedDependency, - code: dependencyContent, - }; - const nestedDependencies = resolveFileDependencies(dependency, program, emitHost); - dependencies.push(dependency, ...nestedDependencies.resolvedFiles); - diagnostics.push(...nestedDependencies.diagnostics); + // // If the import is relative, always resolve it relative to the requiring file + // // If the import is not relative, resolve it relative to options.baseUrl if it is set + const fileDirectory = path.dirname(required.fileName); + const relativeTo = isRelative ? fileDirectory : this.options.baseUrl ?? fileDirectory; + + // // Check if file is a file in the project + const resolvedPath = path.join(relativeTo, targetPath); + return resolvedPath; + } + + private processDependency(dependencyPath: string): void { + if (this.processedDependencies.has(dependencyPath)) return; + this.processedDependencies.add(dependencyPath); + + if (!shouldIncludeDependency(dependencyPath, this.program)) return; + + // If dependency is not part of project, add dependency to output and resolve its dependencies recursively + const dependencyContent = this.emitHost.readFile(dependencyPath); + if (dependencyContent === undefined) { + this.diagnostics.push(couldNotReadDependency(dependencyPath)); + return; + } + + const dependency = { + fileName: dependencyPath, + code: dependencyContent, + }; + this.addAndResolveDependencies(dependency); + } + + private couldNotResolveImport(required: LuaRequire, file: ProcessedFile): void { + const fallbackRequire = fallbackResolve(required, getSourceDir(this.program), path.dirname(file.fileName)); + replaceRequireInCode(file, required, fallbackRequire, this.options.extension); + replaceRequireInSourceMap(file, required, fallbackRequire, this.options.extension); + + this.diagnostics.push( + couldNotResolveRequire(required.requirePath, path.relative(getProjectRoot(this.program), file.fileName)) + ); + } + + private resolveDependencyPath(requiringFile: ProcessedFile, dependency: string): string | undefined { + const fileDirectory = path.dirname(requiringFile.fileName); + if (this.options.tstlVerbose) { + console.log(`Resolving "${dependency}" from ${normalizeSlashes(requiringFile.fileName)}`); + } + + const requiredFromLuaFile = requiringFile.fileName.endsWith(".lua"); + const dependencyPath = requiredFromLuaFile ? luaRequireToPath(dependency) : dependency; + + if (requiredFromLuaFile && isNodeModulesFile(requiringFile.fileName)) { + // If requiring file is in lua module, try to resolve sibling in that file first + const resolvedNodeModulesFile = this.resolveLuaDependencyPathFromNodeModules(requiringFile, dependencyPath); + if (resolvedNodeModulesFile) return resolvedNodeModulesFile; + } + + // Bare specifiers: check paths mappings first, matching TypeScript's resolution order. + // TS never applies paths to relative imports, so skip for those. + if (!ts.isExternalModuleNameRelative(dependencyPath) && this.options.paths) { + // When baseUrl is not set, resolve paths relative to the tsconfig directory (TS 6.0+ behavior) + const pathsBase = + this.options.baseUrl ?? + (this.options.configFilePath ? path.dirname(this.options.configFilePath) : undefined); + if (pathsBase) { + const fileFromPaths = this.tryGetModuleNameFromPaths(dependencyPath, this.options.paths, pathsBase); + if (fileFromPaths) return fileFromPaths; } - } else { - // Could not resolve dependency, add a diagnostic and make some fallback path - diagnostics.push(couldNotResolveRequire(required, path.relative(getProjectRoot(program), file.fileName))); + } - const fallbackRequire = fallbackResolve(required, getSourceDir(program), fileDir); - replaceRequireInCode(file, required, fallbackRequire); - replaceRequireInSourceMap(file, required, fallbackRequire); + // Check if file is a file in the project + const resolvedPath = this.formatPathToFile(dependencyPath, requiringFile); + const fileFromPath = this.getFileFromPath(resolvedPath); + if (fileFromPath) return fileFromPath; + + // Not a TS file in our project sources, use resolver to check if we can find dependency + try { + const resolveResult = resolver.resolveSync({}, fileDirectory, dependencyPath); + if (resolveResult) return resolveResult; + } catch (e: any) { + // resolveSync errors if it fails to resolve + if (this.options.tstlVerbose && e.details) { + // Output resolver log + console.log(e.details); + } } + + return undefined; } - return { resolvedFiles: dependencies, diagnostics }; -} -function resolveDependency( - fileDirectory: string, - dependency: string, - program: ts.Program, - emitHost: EmitHost -): string | undefined { - // Check if file is a file in the project - const resolvedPath = path.join(fileDirectory, dependency); + private resolveLuaDependencyPathFromNodeModules( + requiringFile: ProcessedFile, + dependency: string + ): string | undefined { + // We don't know for sure where the lua root is, so guess it is at package root + const splitPath = path.normalize(requiringFile.fileName).split(path.sep); + let packageRootIndex = splitPath.lastIndexOf("node_modules") + 2; + let packageRoot = splitPath.slice(0, packageRootIndex).join(path.sep); + + while (packageRootIndex < splitPath.length) { + // Try to find lua file relative to currently guessed Lua root + const resolvedPath = path.join(packageRoot, dependency); + const fileFromPath = this.getFileFromPath(resolvedPath); + if (fileFromPath) { + return fileFromPath; + } else { + // Did not find file at current root, try again one directory deeper + packageRoot = path.join(packageRoot, splitPath[packageRootIndex++]); + } + } - if (isProjectFile(resolvedPath, program)) { - // JSON files need their extension as part of the import path, caught by this branch - return resolvedPath; + return undefined; } - const resolvedFile = resolvedPath + ".ts"; - if (isProjectFile(resolvedFile, program)) { - return resolvedFile; + // value is false if already searched but not found + private pathToFile = new Map(); + + private getFileFromPath(resolvedPath: string): string | undefined { + const existingFile = this.pathToFile.get(resolvedPath); + if (existingFile) return existingFile; + if (existingFile === false) return undefined; + + const file = this.searchForFileFromPath(resolvedPath); + this.pathToFile.set(resolvedPath, file ?? false); + return file; } - const projectIndexPath = path.resolve(resolvedPath, "index.ts"); - if (isProjectFile(projectIndexPath, program)) { - return projectIndexPath; + private searchForFileFromPath(resolvedPath: string): string | undefined { + const possibleProjectFiles = [ + resolvedPath, // JSON files need their extension as part of the import path, caught by this branch, + resolvedPath + ".ts", // Regular ts file + path.join(resolvedPath, "index.ts"), // Index ts file, + resolvedPath + ".tsx", // tsx file + path.join(resolvedPath, "index.tsx"), // tsx index + ]; + + for (const possibleFile of possibleProjectFiles) { + if (isProjectFile(possibleFile, this.program)) { + return possibleFile; + } + } + + // Check if this is a lua file in the project sources + const possibleLuaProjectFiles = [ + resolvedPath + ".lua", // lua file in sources + path.join(resolvedPath, "index.lua"), // lua index file in sources + path.join(resolvedPath, "init.lua"), // lua looks for /init.lua if it cannot find .lua + ]; + + for (const possibleFile of possibleLuaProjectFiles) { + if (this.emitHost.fileExists(possibleFile)) { + return possibleFile; + } + } } - // Check if this is a sibling of a required lua file - const luaFilePath = path.resolve(fileDirectory, dependency + ".lua"); - if (emitHost.fileExists(luaFilePath)) { - return luaFilePath; + // Taken from TS and modified: https://github.com/microsoft/TypeScript/blob/88a1e3a1dd8d2d86e844ff1c16d5f041cebcfdb9/src/compiler/moduleSpecifiers.ts#L562 + private tryGetModuleNameFromPaths(relativeToBaseUrl: string, paths: ts.MapLike, baseUrl: string) { + const relativeImport = removeTrailingDirectorySeparator(normalizeSlashes(relativeToBaseUrl)); + for (const [importPattern, targetPatterns] of Object.entries(paths)) { + const pattern = removeFileExtension(normalizeSlashes(importPattern)); + const indexOfStar = pattern.indexOf("*"); + if (indexOfStar !== -1) { + // Try to match * to relativeImport + const prefix = pattern.substring(0, indexOfStar); + const suffix = pattern.substring(indexOfStar + 1); + if ( + (relativeImport.length >= prefix.length + suffix.length && + relativeImport.startsWith(prefix) && + relativeImport.endsWith(suffix)) || + (!suffix && relativeImport === removeTrailingDirectorySeparator(prefix)) + ) { + // If import matches *, extract the matched * path + const matchedStar = relativeImport.substring(prefix.length, relativeImport.length - suffix.length); + // Try to resolve to the target patterns with filled in * pattern + for (const target of targetPatterns) { + const file = this.getFileFromPath(path.join(baseUrl, target.replace("*", matchedStar))); + if (file) return file; + } + } + } else if (pattern === relativeImport) { + // If there is no * pattern, check for exact matches and try those targets + for (const target of targetPatterns) { + const file = this.getFileFromPath(path.join(baseUrl, target)); + if (file) return file; + } + } + } } +} - // Not a TS file in our project sources, use resolver to check if we can find dependency - try { - const resolveResult = resolver.resolveSync({}, fileDirectory, dependency); - if (resolveResult) { - return resolveResult; +export function resolveDependencies( + program: ts.Program, + files: ProcessedFile[], + emitHost: EmitHost, + plugins: Plugin[] +): ResolutionResult { + const options = program.getCompilerOptions() as CompilerOptions; + + const resolutionContext = new ResolutionContext(program, options, emitHost, plugins); + + // Resolve dependencies for all processed files + for (const file of files) { + if (options.tstlVerbose) { + console.log(`Resolving dependencies for ${normalizeSlashes(file.fileName)}`); } - } catch (e) { - // resolveSync errors if it fails to resolve + resolutionContext.addAndResolveDependencies(file); } - return undefined; + return { resolvedFiles: [...resolutionContext.resolvedFiles.values()], diagnostics: resolutionContext.diagnostics }; } function shouldRewriteRequires(resolvedDependency: string, program: ts.Program) { - return !isNodeModulesFile(resolvedDependency) || !isBuildModeLibrary(program); + return !isBuildModeLibrary(program) || !isNodeModulesFile(resolvedDependency); } function shouldIncludeDependency(resolvedDependency: string, program: ts.Program) { // Never include lua files (again) that are transpiled from project sources - if (!hasSourceFileInProject(resolvedDependency, program)) { - // Always include lua files not in node_modules (internal lua sources) - if (!isNodeModulesFile(resolvedDependency)) { - return true; - } else { - // Only include node_modules files if not in library mode - return !isBuildModeLibrary(program); - } - } - return false; + if (hasSourceFileInProject(resolvedDependency, program)) return false; + // Always include lua files not in node_modules (internal lua sources) + if (!isNodeModulesFile(resolvedDependency)) return true; + // Only include node_modules files if not in library mode + return !isBuildModeLibrary(program); } function isBuildModeLibrary(program: ts.Program) { return program.getCompilerOptions().buildMode === BuildMode.Library; } -function findRequiredPaths(code: string): string[] { - // Find all require("") paths in a lua code string - const paths: string[] = []; - const pattern = /require\("(.+)"\)/g; - // eslint-disable-next-line @typescript-eslint/ban-types - let match: RegExpExecArray | null; - while ((match = pattern.exec(code))) { - paths.push(match[1]); - } - - return paths; +function replaceRequireInCode( + file: ProcessedFile, + originalRequire: LuaRequire, + newRequire: string, + extension: string | undefined +): void { + const requirePath = requirePathForFile(newRequire, extension); + file.code = file.code = + file.code.substring(0, originalRequire.from) + + `require("${requirePath}")` + + file.code.substring(originalRequire.to + 1); } -function replaceRequireInCode(file: ProcessedFile, originalRequire: string, newRequire: string): void { - const requirePath = formatPathToLuaPath(newRequire.replace(".lua", "")); - file.code = file.code.replace(`require("${originalRequire}")`, `require("${requirePath}")`); +function replaceRequireInSourceMap( + file: ProcessedFile, + originalRequire: LuaRequire, + newRequire: string, + extension?: string | undefined +): void { + const requirePath = requirePathForFile(newRequire, extension); + if (file.sourceMapNode) { + replaceInSourceMap( + file.sourceMapNode, + file.sourceMapNode, + `"${originalRequire.requirePath}"`, + `"${requirePath}"` + ); + } } -function replaceRequireInSourceMap(file: ProcessedFile, originalRequire: string, newRequire: string): void { - const requirePath = formatPathToLuaPath(newRequire.replace(".lua", "")); - if (file.sourceMapNode) { - replaceInSourceMap(file.sourceMapNode, file.sourceMapNode, `"${originalRequire}"`, `"${requirePath}"`); +function requirePathForFile(filePath: string, extension = ".lua"): string { + if (!extension.startsWith(".")) { + extension = `.${extension}`; + } + if (filePath.endsWith(extension)) { + return formatPathToLuaPath(filePath.substring(0, filePath.length - extension.length)); + } else { + return formatPathToLuaPath(filePath); } } @@ -215,17 +452,31 @@ function isProjectFile(file: string, program: ts.Program): boolean { function hasSourceFileInProject(filePath: string, program: ts.Program) { const pathWithoutExtension = trimExtension(filePath); return ( - isProjectFile(pathWithoutExtension + ".ts", program) || isProjectFile(pathWithoutExtension + ".json", program) + isProjectFile(pathWithoutExtension + ".ts", program) || + isProjectFile(pathWithoutExtension + ".tsx", program) || + isProjectFile(pathWithoutExtension + ".json", program) ); } // Transform an import path to a lua require that is probably not correct, but can be used as fallback when regular resolution fails -function fallbackResolve(required: string, sourceRootDir: string, fileDir: string): string { +function fallbackResolve(required: LuaRequire, sourceRootDir: string, fileDir: string): string { return formatPathToLuaPath( path - .normalize(path.join(path.relative(sourceRootDir, fileDir), required)) + .normalize(path.join(path.relative(sourceRootDir, fileDir), required.requirePath)) .split(path.sep) .filter(s => s !== "." && s !== "..") .join(path.sep) ); } + +function luaRequireToPath(requirePath: string): string { + return requirePath.replace(/\./g, path.sep); +} + +function removeFileExtension(path: string) { + return path.includes(".") ? trimExtension(path) : path; +} + +function removeTrailingDirectorySeparator(path: string) { + return path.endsWith("/") || path.endsWith("\\") ? path.substring(0, -1) : path; +} diff --git a/src/transpilation/transformers.ts b/src/transpilation/transformers.ts index e043704b8..0080b6e4e 100644 --- a/src/transpilation/transformers.ts +++ b/src/transpilation/transformers.ts @@ -36,6 +36,7 @@ export function getTransformers( ...(transformersFromOptions.after ?? []), ...(customTransformers.after ?? []), + stripParenthesisExpressionsTransformer, luaTransformer, ], }; @@ -53,6 +54,34 @@ export const noImplicitSelfTransformer: ts.TransformerFactory = context => sourceFile => { + // Remove parenthesis expressions before transforming to Lua, so transpiler is not hindered by extra ParenthesizedExpression nodes + function unwrapParentheses(node: ts.Expression) { + while (ts.isParenthesizedExpression(node) && !ts.isOptionalChain(node.expression)) { + node = node.expression; + } + return node; + } + function visit(node: ts.Node): ts.Node { + // For now only call expressions strip their expressions of parentheses, there could be more cases where this is required + if (ts.isCallExpression(node)) { + return ts.factory.updateCallExpression( + node, + unwrapParentheses(node.expression), + node.typeArguments, + node.arguments + ); + } else if (ts.isVoidExpression(node)) { + return ts.factory.updateVoidExpression(node, unwrapParentheses(node.expression)); + } else if (ts.isDeleteExpression(node)) { + return ts.factory.updateDeleteExpression(node, unwrapParentheses(node.expression)); + } + + return ts.visitEachChild(node, visit, context); + } + return ts.visitEachChild(sourceFile, visit, context); +}; + function loadTransformersFromOptions(program: ts.Program, diagnostics: ts.Diagnostic[]): ts.CustomTransformers { const customTransformers: Required = { before: [], @@ -61,40 +90,53 @@ function loadTransformersFromOptions(program: ts.Program, diagnostics: ts.Diagno }; const options = program.getCompilerOptions() as CompilerOptions; - if (!options.plugins) return customTransformers; - - for (const [index, transformerImport] of options.plugins.entries()) { - if (!("transform" in transformerImport)) continue; - const optionName = `compilerOptions.plugins[${index}]`; - - const { error: resolveError, result: factory } = resolvePlugin( - "transformer", - `${optionName}.transform`, - getConfigDirectory(options), - transformerImport.transform, - transformerImport.import - ); - - if (resolveError) diagnostics.push(resolveError); - if (factory === undefined) continue; - - const { error: loadError, transformer } = loadTransformer(optionName, program, factory, transformerImport); - if (loadError) diagnostics.push(loadError); - if (transformer === undefined) continue; - - if (transformer.before) { - customTransformers.before.push(transformer.before); - } - - if (transformer.after) { - customTransformers.after.push(transformer.after); - } - - if (transformer.afterDeclarations) { - customTransformers.afterDeclarations.push(transformer.afterDeclarations); + if (options.plugins) { + for (const [index, transformerImport] of options.plugins.entries()) { + if (!("transform" in transformerImport)) continue; + const optionName = `compilerOptions.plugins[${index}]`; + + const { error: resolveError, result: factory } = resolvePlugin( + "transformer", + `${optionName}.transform`, + getConfigDirectory(options), + transformerImport.transform, + transformerImport.import + ); + + if (resolveError) diagnostics.push(resolveError); + if (factory === undefined) continue; + + const { error: loadError, transformer } = loadTransformer(optionName, program, factory, transformerImport); + if (loadError) diagnostics.push(loadError); + if (transformer === undefined) continue; + + if (transformer.before) { + customTransformers.before.push(transformer.before); + } + + if (transformer.after) { + customTransformers.after.push(transformer.after); + } + + if (transformer.afterDeclarations) { + customTransformers.afterDeclarations.push(transformer.afterDeclarations); + } } } - + if (options.jsx === ts.JsxEmit.React) { + customTransformers.before.push(context => { + // if target < ES2017, typescript generates some unnecessary additional transformations in transformJSX. + // We can't control the target compiler option, so we override here. + const patchedContext: ts.TransformationContext = { + ...context, + getCompilerOptions: () => ({ + ...context.getCompilerOptions(), + target: ts.ScriptTarget.ESNext, + }), + }; + return ts.transformJsx(patchedContext); + }); + } return customTransformers; } diff --git a/src/transpilation/transpile.ts b/src/transpilation/transpile.ts index 0d3071f73..6adfa5fc4 100644 --- a/src/transpilation/transpile.ts +++ b/src/transpilation/transpile.ts @@ -4,9 +4,10 @@ import { CompilerOptions, validateOptions } from "../CompilerOptions"; import { createPrinter } from "../LuaPrinter"; import { createVisitorMap, transformSourceFile } from "../transformation"; import { isNonNull } from "../utils"; -import { getPlugins, Plugin } from "./plugins"; +import { Plugin } from "./plugins"; import { getTransformers } from "./transformers"; import { EmitHost, ProcessedFile } from "./utils"; +import * as performance from "../measure-performance"; export interface TranspileOptions { program: ts.Program; @@ -23,10 +24,16 @@ export interface TranspileResult { export function getProgramTranspileResult( emitHost: EmitHost, writeFileResult: ts.WriteFileCallback, - { program, sourceFiles: targetSourceFiles, customTransformers = {}, plugins: customPlugins = [] }: TranspileOptions + { program, sourceFiles: targetSourceFiles, customTransformers = {}, plugins = [] }: TranspileOptions ): TranspileResult { + performance.startSection("beforeTransform"); + const options = program.getCompilerOptions() as CompilerOptions; + if (options.tstlVerbose) { + console.log("Parsing project settings"); + } + const diagnostics = validateOptions(options); let transpiledFiles: ProcessedFile[] = []; @@ -52,18 +59,39 @@ export function getProgramTranspileResult( } if (preEmitDiagnostics.length > 0) { + performance.endSection("beforeTransform"); return { diagnostics: preEmitDiagnostics, transpiledFiles }; } } - const plugins = getPlugins(program, diagnostics, customPlugins); + for (const plugin of plugins) { + if (plugin.beforeTransform) { + const pluginDiagnostics = plugin.beforeTransform(program, options, emitHost) ?? []; + diagnostics.push(...pluginDiagnostics); + } + } + const visitorMap = createVisitorMap(plugins.map(p => p.visitors).filter(isNonNull)); const printer = createPrinter(plugins.map(p => p.printer).filter(isNonNull)); + const processSourceFile = (sourceFile: ts.SourceFile) => { - const { file, diagnostics: transformDiagnostics } = transformSourceFile(program, sourceFile, visitorMap); + if (options.tstlVerbose) { + console.log(`Transforming ${sourceFile.fileName}`); + } + performance.startSection("transpile"); + + const { file, diagnostics: transformDiagnostics } = transformSourceFile(program, sourceFile, visitorMap); diagnostics.push(...transformDiagnostics); + + performance.endSection("transpile"); + if (!options.noEmit && !options.emitDeclarationOnly) { + performance.startSection("print"); + if (options.tstlVerbose) { + console.log(`Printing ${sourceFile.fileName}`); + } + const printResult = printer(program, emitHost, sourceFile.fileName, file); transpiledFiles.push({ sourceFiles: [sourceFile], @@ -71,6 +99,7 @@ export function getProgramTranspileResult( luaAst: file, ...printResult, }); + performance.endSection("print"); } }; @@ -86,11 +115,13 @@ export function getProgramTranspileResult( options.noEmit = false; const writeFile: ts.WriteFileCallback = (fileName, ...rest) => { - if (!fileName.endsWith(".js") && !fileName.endsWith(".js.map")) { + if (!fileName.endsWith(".js") && !fileName.endsWith(".js.map") && !fileName.endsWith(".json")) { writeFileResult(fileName, ...rest); } }; + performance.endSection("beforeTransform"); + if (targetSourceFiles) { for (const file of targetSourceFiles) { if (isEmittableJsonFile(file)) { @@ -106,11 +137,22 @@ export function getProgramTranspileResult( program.getSourceFiles().filter(isEmittableJsonFile).forEach(processSourceFile); } + performance.startSection("afterPrint"); + options.noEmit = oldNoEmit; if (options.noEmit || (options.noEmitOnError && diagnostics.length > 0)) { transpiledFiles = []; } + for (const plugin of plugins) { + if (plugin.afterPrint) { + const pluginDiagnostics = plugin.afterPrint(program, options, emitHost, transpiledFiles) ?? []; + diagnostics.push(...pluginDiagnostics); + } + } + + performance.endSection("afterPrint"); + return { diagnostics, transpiledFiles }; } diff --git a/src/transpilation/transpiler.ts b/src/transpilation/transpiler.ts index 7b5eb0f3f..7f79af35d 100644 --- a/src/transpilation/transpiler.ts +++ b/src/transpilation/transpiler.ts @@ -1,12 +1,15 @@ import * as path from "path"; import * as ts from "typescript"; -import { isBundleEnabled } from "../CompilerOptions"; -import { getLuaLibBundle } from "../LuaLib"; +import { CompilerOptions, isBundleEnabled, LuaLibImportKind, LuaTarget } from "../CompilerOptions"; +import { buildMinimalLualibBundle, findUsedLualibFeatures, getLuaLibBundle } from "../LuaLib"; import { normalizeSlashes, trimExtension } from "../utils"; import { getBundleResult } from "./bundle"; +import { emitPathCollision } from "./diagnostics"; +import { getPlugins, Plugin } from "./plugins"; import { resolveDependencies } from "./resolve"; import { getProgramTranspileResult, TranspileOptions } from "./transpile"; import { EmitFile, EmitHost, ProcessedFile } from "./utils"; +import * as performance from "../measure-performance"; export interface TranspilerOptions { emitHost?: EmitHost; @@ -23,64 +26,155 @@ export interface EmitResult { export class Transpiler { protected emitHost: EmitHost; + constructor({ emitHost = ts.sys }: TranspilerOptions = {}) { this.emitHost = emitHost; } public emit(emitOptions: EmitOptions): EmitResult { - const { program, writeFile = this.emitHost.writeFile } = emitOptions; - const { diagnostics, transpiledFiles: freshFiles } = getProgramTranspileResult( + const { program, writeFile = this.emitHost.writeFile, plugins: optionsPlugins = [] } = emitOptions; + + const { diagnostics: getPluginsDiagnostics, plugins: configPlugins } = getPlugins(program); + const plugins = [...optionsPlugins, ...configPlugins]; + + const { diagnostics: transpileDiagnostics, transpiledFiles: freshFiles } = getProgramTranspileResult( this.emitHost, writeFile, - emitOptions + { + ...emitOptions, + plugins, + } ); - const { emitPlan } = this.getEmitPlan(program, diagnostics, freshFiles); + const { emitPlan } = this.getEmitPlan(program, transpileDiagnostics, freshFiles, plugins); + + const emitDiagnostics = this.emitFiles(program, plugins, emitPlan, writeFile); + + return { + diagnostics: getPluginsDiagnostics.concat(transpileDiagnostics, emitDiagnostics), + emitSkipped: emitPlan.length === 0, + }; + } + + private emitFiles( + program: ts.Program, + plugins: Plugin[], + emitPlan: EmitFile[], + writeFile: ts.WriteFileCallback + ): ts.Diagnostic[] { + performance.startSection("emit"); + + const options = program.getCompilerOptions() as CompilerOptions; + + if (options.tstlVerbose) { + console.log("Emitting output"); + } + + const diagnostics: ts.Diagnostic[] = []; + + for (const plugin of plugins) { + if (plugin.beforeEmit) { + const beforeEmitPluginDiagnostics = plugin.beforeEmit(program, options, this.emitHost, emitPlan) ?? []; + diagnostics.push(...beforeEmitPluginDiagnostics); + } + } - const options = program.getCompilerOptions(); const emitBOM = options.emitBOM ?? false; for (const { outputPath, code, sourceMap, sourceFiles } of emitPlan) { + if (options.tstlVerbose) { + console.log(`Emitting ${normalizeSlashes(outputPath)}`); + } + writeFile(outputPath, code, emitBOM, undefined, sourceFiles); if (options.sourceMap && sourceMap !== undefined) { writeFile(outputPath + ".map", sourceMap, emitBOM, undefined, sourceFiles); } } - return { diagnostics, emitSkipped: emitPlan.length === 0 }; + for (const plugin of plugins) { + if (plugin.afterEmit) { + const afterEmitPluginDiagnostics = plugin.afterEmit(program, options, this.emitHost, emitPlan) ?? []; + diagnostics.push(...afterEmitPluginDiagnostics); + } + } + + if (options.tstlVerbose) { + console.log("Emit finished!"); + } + + performance.endSection("emit"); + + return diagnostics; } protected getEmitPlan( program: ts.Program, diagnostics: ts.Diagnostic[], - files: ProcessedFile[] + files: ProcessedFile[], + plugins: Plugin[] ): { emitPlan: EmitFile[] } { - const options = program.getCompilerOptions(); + performance.startSection("getEmitPlan"); + const options = program.getCompilerOptions() as CompilerOptions; - const lualibRequired = files.some(f => f.code.includes('require("lualib_bundle")')); - if (lualibRequired) { - // Add lualib bundle to source dir 'virtually', will be moved to correct output dir in emitPlan - const fileName = normalizeSlashes(path.resolve(getSourceDir(program), "lualib_bundle.lua")); - files.unshift({ fileName, code: getLuaLibBundle(this.emitHost) }); + if (options.tstlVerbose) { + console.log("Constructing emit plan"); } // Resolve imported modules and modify output Lua requires - const resolutionResult = resolveDependencies(program, files, this.emitHost); + const resolutionResult = resolveDependencies(program, files, this.emitHost, plugins); diagnostics.push(...resolutionResult.diagnostics); + const lualibRequired = resolutionResult.resolvedFiles.some(f => f.fileName === "lualib_bundle"); + if (lualibRequired) { + // Remove lualib placeholders from resolution result + resolutionResult.resolvedFiles = resolutionResult.resolvedFiles.filter(f => f.fileName !== "lualib_bundle"); + + if (options.tstlVerbose) { + console.log("Including lualib bundle"); + } + // Add lualib bundle to source dir 'virtually', will be moved to correct output dir in emitPlan + const fileName = normalizeSlashes(path.resolve(getSourceDir(program), "lualib_bundle.lua")); + const code = this.getLuaLibBundleContent(options, resolutionResult.resolvedFiles); + resolutionResult.resolvedFiles.unshift({ fileName, code }); + } + let emitPlan: EmitFile[]; if (isBundleEnabled(options)) { const [bundleDiagnostics, bundleFile] = getBundleResult(program, resolutionResult.resolvedFiles); diagnostics.push(...bundleDiagnostics); emitPlan = [bundleFile]; } else { - emitPlan = resolutionResult.resolvedFiles.map(file => ({ - ...file, - outputPath: getEmitPath(file.fileName, program), - })); + const outputPathMap = new Map(); + emitPlan = resolutionResult.resolvedFiles.map(file => { + const outputPath = getEmitPath(file.fileName, program); + const existing = outputPathMap.get(outputPath); + if (existing) { + diagnostics.push(emitPathCollision(outputPath, existing, file.fileName)); + } else { + outputPathMap.set(outputPath, file.fileName); + } + return { ...file, outputPath }; + }); } + performance.endSection("getEmitPlan"); + return { emitPlan }; } + + private getLuaLibBundleContent(options: CompilerOptions, resolvedFiles: ProcessedFile[]) { + const luaTarget = options.luaTarget ?? LuaTarget.Universal; + if (options.luaLibImport === LuaLibImportKind.RequireMinimal) { + const usedFeatures = findUsedLualibFeatures( + luaTarget, + this.emitHost, + resolvedFiles.map(f => f.code) + ); + return buildMinimalLualibBundle(usedFeatures, luaTarget, this.emitHost); + } else { + return getLuaLibBundle(luaTarget, this.emitHost); + } + } } export function getEmitPath(file: string, program: ts.Program): string { @@ -103,8 +197,17 @@ export function getEmitPathRelativeToOutDir(fileName: string, program: ts.Progra emitPathSplits[0] = "lua_modules"; } - // Make extension lua - emitPathSplits[emitPathSplits.length - 1] = trimExtension(emitPathSplits[emitPathSplits.length - 1]) + ".lua"; + // Replace dots with underscores in path segments so that Lua's require() + // resolves correctly. Dots are path separators in Lua's module system, so + // "Foo.Bar/index.lua" would be unreachable via require("Foo.Bar.index") + // since Lua interprets it as "Foo/Bar/index.lua". + emitPathSplits[emitPathSplits.length - 1] = trimExtension(emitPathSplits[emitPathSplits.length - 1]); + emitPathSplits = emitPathSplits.map(segment => segment.replace(/\./g, "_")); + + // Set extension + const extension = ((program.getCompilerOptions() as CompilerOptions).extension ?? "lua").trim(); + const trimmedExtension = extension.startsWith(".") ? extension.substring(1) : extension; + emitPathSplits[emitPathSplits.length - 1] += "." + trimmedExtension; return path.join(...emitPathSplits); } @@ -114,7 +217,9 @@ export function getSourceDir(program: ts.Program): string { if (rootDir && rootDir.length > 0) { return path.isAbsolute(rootDir) ? rootDir : path.resolve(getProjectRoot(program), rootDir); } - return program.getCommonSourceDirectory(); + + // If no rootDir is given, source is relative to the project root + return getProjectRoot(program); } export function getEmitOutDir(program: ts.Program): string { @@ -122,7 +227,9 @@ export function getEmitOutDir(program: ts.Program): string { if (outDir && outDir.length > 0) { return path.isAbsolute(outDir) ? outDir : path.resolve(getProjectRoot(program), outDir); } - return program.getCommonSourceDirectory(); + + // If no outDir is provided, emit in project root + return getProjectRoot(program); } export function getProjectRoot(program: ts.Program): string { diff --git a/src/transpilation/utils.ts b/src/transpilation/utils.ts index a4acdfbb4..ce03fd854 100644 --- a/src/transpilation/utils.ts +++ b/src/transpilation/utils.ts @@ -8,6 +8,7 @@ import * as lua from "../LuaAST"; import * as diagnosticFactories from "./diagnostics"; export interface EmitHost { + directoryExists(path: string): boolean; fileExists(path: string): boolean; getCurrentDirectory(): string; readFile(path: string): string | undefined; @@ -23,7 +24,6 @@ interface BaseFile { export interface ProcessedFile extends BaseFile { fileName: string; luaAst?: lua.File; - /** @internal */ sourceMapNode?: SourceNode; } @@ -34,33 +34,37 @@ export interface EmitFile extends BaseFile { export const getConfigDirectory = (options: ts.CompilerOptions) => options.configFilePath ? path.dirname(options.configFilePath) : process.cwd(); +const getTstlDirectory = () => path.dirname(__dirname); + export function resolvePlugin( kind: string, optionName: string, basedir: string, - query: string, + query: unknown, importName = "default" ): { error?: ts.Diagnostic; result?: unknown } { if (typeof query !== "string") { return { error: cliDiagnostics.compilerOptionRequiresAValueOfType(optionName, "string") }; } + const isModuleNotFoundError = (error: any) => error.code === "MODULE_NOT_FOUND"; + let resolved: string; try { resolved = resolve.sync(query, { basedir, extensions: [".js", ".ts", ".tsx"] }); } catch (err) { - if (err.code !== "MODULE_NOT_FOUND") throw err; + if (!isModuleNotFoundError(err)) throw err; return { error: diagnosticFactories.couldNotResolveFrom(kind, query, basedir) }; } const hasNoRequireHook = require.extensions[".ts"] === undefined; if (hasNoRequireHook && (resolved.endsWith(".ts") || resolved.endsWith(".tsx"))) { try { - const tsNodePath = resolve.sync("ts-node", { basedir }); + const tsNodePath = resolve.sync("ts-node", { basedir: getTstlDirectory() }); const tsNode: typeof import("ts-node") = require(tsNodePath); tsNode.register({ transpileOnly: true }); } catch (err) { - if (err.code !== "MODULE_NOT_FOUND") throw err; + if (!isModuleNotFoundError(err)) throw err; return { error: diagnosticFactories.toLoadItShouldBeTranspiled(kind, query) }; } } diff --git a/src/tstl.ts b/src/tstl.ts index fb011be3c..813347a21 100644 --- a/src/tstl.ts +++ b/src/tstl.ts @@ -7,6 +7,7 @@ import { parseCommandLine } from "./cli/parse"; import { createDiagnosticReporter } from "./cli/report"; import { createConfigFileUpdater, locateConfigFile, parseConfigFileWithSystem } from "./cli/tsconfig"; import { isBundleEnabled } from "./CompilerOptions"; +import * as performance from "./measure-performance"; const shouldBePretty = ({ pretty }: ts.CompilerOptions = {}) => pretty !== undefined ? (pretty as boolean) : ts.sys.writeOutputIsTTY?.() ?? false; @@ -95,23 +96,29 @@ function performCompilation( options: tstl.CompilerOptions, configFileParsingDiagnostics?: readonly ts.Diagnostic[] ): void { + if (options.measurePerformance) performance.enableMeasurement(); + + performance.startSection("createProgram"); + const program = ts.createProgram({ rootNames, options, projectReferences, configFileParsingDiagnostics, }); + const preEmitDiagnostics = ts.getPreEmitDiagnostics(program); - const { diagnostics: transpileDiagnostics, emitSkipped } = new tstl.Transpiler().emit({ program }); + performance.endSection("createProgram"); - const diagnostics = ts.sortAndDeduplicateDiagnostics([ - ...ts.getPreEmitDiagnostics(program), - ...transpileDiagnostics, - ]); + const { diagnostics: transpileDiagnostics, emitSkipped } = new tstl.Transpiler().emit({ program }); + const diagnostics = ts.sortAndDeduplicateDiagnostics([...preEmitDiagnostics, ...transpileDiagnostics]); diagnostics.forEach(reportDiagnostic); + + if (options.measurePerformance) reportPerformance(); + const exitCode = - diagnostics.length === 0 + diagnostics.filter(d => d.category === ts.DiagnosticCategory.Error).length === 0 ? ts.ExitStatus.Success : emitSkipped ? ts.ExitStatus.DiagnosticsPresent_OutputsSkipped @@ -159,6 +166,9 @@ function updateWatchCompilationHost( host.afterProgramCreate = builderProgram => { const program = builderProgram.getProgram(); const options = builderProgram.getCompilerOptions() as tstl.CompilerOptions; + + if (options.measurePerformance) performance.enableMeasurement(); + const configFileParsingDiagnostics: ts.Diagnostic[] = updateConfigFile(options); let sourceFiles: ts.SourceFile[] | undefined; @@ -189,6 +199,8 @@ function updateWatchCompilationHost( diagnostics.forEach(reportDiagnostic); + if (options.measurePerformance) reportPerformance(); + const errors = diagnostics.filter(d => d.category === ts.DiagnosticCategory.Error); hadErrorLastTime = errors.length > 0; @@ -196,6 +208,17 @@ function updateWatchCompilationHost( }; } +function reportPerformance() { + if (performance.isMeasurementEnabled()) { + console.log("Performance measurements: "); + performance.forEachMeasure((name, duration) => { + console.log(` ${name}: ${duration.toFixed(2)}ms`); + }); + console.log(`Total: ${performance.getTotalDuration().toFixed(2)}ms`); + performance.disableMeasurement(); + } +} + function checkNodeVersion(): void { const [major, minor] = process.version.slice(1).split(".").map(Number); const isValid = major > 12 || (major === 12 && minor >= 13); diff --git a/src/typescript-internal.d.ts b/src/typescript-internal.d.ts index 7444d89d7..5333df95a 100644 --- a/src/typescript-internal.d.ts +++ b/src/typescript-internal.d.ts @@ -23,5 +23,40 @@ declare module "typescript" { interface TypeChecker { getElementTypeOfArrayType(type: Type): Type | undefined; + getContextualTypeForObjectLiteralElement(element: ObjectLiteralElementLike): Type | undefined; + + isTupleType(type: Type): boolean; + isArrayType(type: Type): boolean; + } + + interface Symbol { + parent?: Symbol; } + + interface Signature { + compositeSignatures?: Signature[]; + } + + function transformJsx(context: TransformationContext): (x: SourceFile) => SourceFile; + + export type OuterExpression = + | ParenthesizedExpression + | TypeAssertion + | AsExpression + | NonNullExpression + | PartiallyEmittedExpression; + + function skipOuterExpressions(node: Expression, kinds?: OuterExpressionKinds): Expression; + export function isOuterExpression(node: Node, kinds?: OuterExpressionKinds): node is OuterExpression; + + export function nodeNextJsonConfigResolver( + moduleName: string, + containingFile: string, + host: ModuleResolutionHost + ): ResolvedModuleWithFailedLookupLocations; + + export function pathIsAbsolute(path: string): boolean; + export function pathIsRelative(path: string): boolean; + + export function setParent(child: T, parent: T["parent"] | undefined): T; } diff --git a/src/utils.ts b/src/utils.ts index 72e6ee168..70cdb2940 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -5,7 +5,7 @@ import * as path from "path"; export function castArray(value: T | T[]): T[]; export function castArray(value: T | readonly T[]): readonly T[]; export function castArray(value: T | readonly T[]): readonly T[] { - return Array.isArray(value) ? value : [value]; + return Array.isArray(value) ? value : [value as T]; } export const intersperse = (values: readonly T[], separator: T): T[] => @@ -50,7 +50,6 @@ export function formatPathToLuaPath(filePath: string): string { type NoInfer = [T][T extends any ? 0 : never]; export function getOrUpdate( - // eslint-disable-next-line @typescript-eslint/ban-types map: Map | (K extends object ? WeakMap : never), key: K, getDefaultValue: () => NoInfer @@ -62,7 +61,6 @@ export function getOrUpdate( return map.get(key)!; } -// eslint-disable-next-line @typescript-eslint/ban-types export function isNonNull(value: T | null | undefined): value is T { return value != null; } @@ -79,7 +77,7 @@ export function cast( } export function assert(value: any, message?: string | Error): asserts value { - nativeAssert(value, message); + nativeAssert.ok(value, message); } export function assertNever(_value: never): never { diff --git a/test/cli/parse.spec.ts b/test/cli/parse.spec.ts index f66b97766..c33af1208 100644 --- a/test/cli/parse.spec.ts +++ b/test/cli/parse.spec.ts @@ -59,6 +59,14 @@ describe("command line", () => { }); }); + describe("array-of-objects options", () => { + test.each([["{"], ["{}"], ["0"], ["''"]])("should error on invalid value (%s)", value => { + const result = tstl.parseCommandLine(["--luaPlugins", value]); + + expect(result.errors).toHaveDiagnostics(); + }); + }); + describe("boolean options", () => { test.each([true, false])("should parse booleans (%p)", value => { const result = tstl.parseCommandLine(["--noHeader", value.toString()]); @@ -104,12 +112,13 @@ describe("command line", () => { ["noHeader", "true", { noHeader: true }], ["sourceMapTraceback", "false", { sourceMapTraceback: false }], ["sourceMapTraceback", "true", { sourceMapTraceback: true }], + ["tstlVerbose", "true", { tstlVerbose: true }], + ["tstlVerbose", "false", { tstlVerbose: false }], ["buildMode", "default", { buildMode: tstl.BuildMode.Default }], ["buildMode", "library", { buildMode: tstl.BuildMode.Library }], ["luaLibImport", "none", { luaLibImport: tstl.LuaLibImportKind.None }], - ["luaLibImport", "always", { luaLibImport: tstl.LuaLibImportKind.Always }], ["luaLibImport", "inline", { luaLibImport: tstl.LuaLibImportKind.Inline }], ["luaLibImport", "require", { luaLibImport: tstl.LuaLibImportKind.Require }], @@ -121,6 +130,15 @@ describe("command line", () => { ["luaBundle", "foo", { luaBundle: "foo" }], ["luaBundleEntry", "bar", { luaBundleEntry: "bar" }], + + ["extension", ".lua", { extension: ".lua" }], + ["extension", "scar", { extension: "scar" }], + + ["luaPlugins", '[{ "name": "a" }]', { luaPlugins: [{ name: "a" }] }], + ["luaPlugins", '[{ "name": "a" },{ "name": "b" }]', { luaPlugins: [{ name: "a" }, { name: "b" }] }], + + ["noResolvePaths", "path1", { noResolvePaths: ["path1"] }], + ["noResolvePaths", "path1,path2", { noResolvePaths: ["path1", "path2"] }], ])("--%s %s", (optionName, value, expected) => { const result = tstl.parseCommandLine([`--${optionName}`, value]); @@ -220,19 +238,28 @@ describe("tsconfig", () => { ["buildMode", "library", { buildMode: tstl.BuildMode.Library }], ["luaLibImport", "none", { luaLibImport: tstl.LuaLibImportKind.None }], - ["luaLibImport", "always", { luaLibImport: tstl.LuaLibImportKind.Always }], ["luaLibImport", "inline", { luaLibImport: tstl.LuaLibImportKind.Inline }], ["luaLibImport", "require", { luaLibImport: tstl.LuaLibImportKind.Require }], ["luaTarget", "universal", { luaTarget: tstl.LuaTarget.Universal }], + ["luaTarget", "5.0", { luaTarget: tstl.LuaTarget.Lua50 }], ["luaTarget", "5.1", { luaTarget: tstl.LuaTarget.Lua51 }], ["luaTarget", "5.2", { luaTarget: tstl.LuaTarget.Lua52 }], ["luaTarget", "5.3", { luaTarget: tstl.LuaTarget.Lua53 }], ["luaTarget", "5.4", { luaTarget: tstl.LuaTarget.Lua54 }], + ["luaTarget", "5.5", { luaTarget: tstl.LuaTarget.Lua55 }], ["luaTarget", "jit", { luaTarget: tstl.LuaTarget.LuaJIT }], + ["luaTarget", "luau", { luaTarget: tstl.LuaTarget.Luau }], ["luaBundle", "foo", { luaBundle: "foo" }], ["luaBundleEntry", "bar", { luaBundleEntry: "bar" }], + + ["noImplicitSelf", true, { noImplicitSelf: true }], + + ["tstlVerbose", true, { tstlVerbose: true }], + + ["noResolvePaths", [], { noResolvePaths: [] }], + ["noResolvePaths", ["path1", "path2"], { noResolvePaths: ["path1", "path2"] }], ])("{ %p: %p }", (optionName, value, expected) => { const result = parseConfigFileContent({ tstl: { [optionName]: value } }); diff --git a/test/cli/run.ts b/test/cli/run.ts index cee7e19b2..732d71bcb 100644 --- a/test/cli/run.ts +++ b/test/cli/run.ts @@ -1,7 +1,7 @@ import { ChildProcess, fork } from "child_process"; import * as path from "path"; -jest.setTimeout(20000); +jest.setTimeout(30000); const cliPath = path.join(__dirname, "../../src/tstl.ts"); @@ -26,6 +26,6 @@ export async function runCli(args: string[]): Promise { child.stderr!.on("data", (data: Buffer) => (output += data.toString())); return new Promise(resolve => { - child.on("close", exitCode => resolve({ exitCode, output })); + child.on("close", exitCode => resolve({ exitCode: exitCode ?? 1, output })); }); } diff --git a/test/cli/tsconfig.spec.ts b/test/cli/tsconfig.spec.ts index 1608ac973..e5d1b946c 100644 --- a/test/cli/tsconfig.spec.ts +++ b/test/cli/tsconfig.spec.ts @@ -1,7 +1,7 @@ import * as fs from "fs-extra"; import * as os from "os"; import * as path from "path"; -import { locateConfigFile } from "../../src/cli/tsconfig"; +import { locateConfigFile, parseConfigFileWithSystem } from "../../src/cli/tsconfig"; import { normalizeSlashes } from "../../src/utils"; let temp: string; @@ -20,7 +20,7 @@ afterEach(async () => { const locate = (project: string | undefined, fileNames: string[] = []) => locateConfigFile({ errors: [], fileNames, options: { project } }); -const normalize = (name: string) => normalizeSlashes(path.resolve(temp, name)); +const normalize = (name: string) => normalizeSlashes(fs.realpathSync(path.resolve(temp, name))); describe("specified", () => { for (const separator of process.platform === "win32" ? ["/", "\\"] : ["/"]) { @@ -91,3 +91,25 @@ describe("errors", () => { expect([locate("tsconfig.json", [""])]).toHaveDiagnostics(); }); }); + +describe("tsconfig extends", () => { + test("correctly merges extended tsconfig files", () => { + const parsedConfig = parseConfigFileWithSystem(path.join(__dirname, "tsconfig", "tsconfig.json")); + expect(parsedConfig.options).toMatchObject({ luaTarget: "5.3", noHeader: true }); + }); + + test("can handle multiple extends", () => { + const parsedConfig = parseConfigFileWithSystem(path.join(__dirname, "tsconfig", "tsconfig.multi-extends.json")); + expect(parsedConfig.options).toMatchObject({ luaTarget: "5.4", sourceMapTraceback: true }); + }); + + test("can handle cycles in configs", () => { + const parsedConfig = parseConfigFileWithSystem(path.join(__dirname, "tsconfig", "tsconfig-cycle1.json")); + expect(parsedConfig.options).toMatchObject({ luaTarget: "5.4" }); + }); + + test("can handle tsconfig files with comments", () => { + const parsedConfig = parseConfigFileWithSystem(path.join(__dirname, "tsconfig", "tsconfig.with-comments.json")); + expect(parsedConfig.options).toMatchObject({ luaTarget: "5.3" }); + }); +}); diff --git a/test/cli/tsconfig/tsconfig-cycle1.json b/test/cli/tsconfig/tsconfig-cycle1.json new file mode 100644 index 000000000..6383b5ccf --- /dev/null +++ b/test/cli/tsconfig/tsconfig-cycle1.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig-cycle2.json", + "tstl": { + "luaTarget": "5.4" + } +} diff --git a/test/cli/tsconfig/tsconfig-cycle2.json b/test/cli/tsconfig/tsconfig-cycle2.json new file mode 100644 index 000000000..14c3edcd1 --- /dev/null +++ b/test/cli/tsconfig/tsconfig-cycle2.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig-cycle1.json", + "tstl": { + "luaTarget": "5.3" + } +} diff --git a/test/cli/tsconfig/tsconfig.base.json b/test/cli/tsconfig/tsconfig.base.json new file mode 100644 index 000000000..6b296a8de --- /dev/null +++ b/test/cli/tsconfig/tsconfig.base.json @@ -0,0 +1,6 @@ +{ + "tstl": { + "noHeader": true, + "luaTarget": "jit" + } +} diff --git a/test/cli/tsconfig/tsconfig.json b/test/cli/tsconfig/tsconfig.json new file mode 100644 index 000000000..7e76044a2 --- /dev/null +++ b/test/cli/tsconfig/tsconfig.json @@ -0,0 +1,6 @@ +{ + "extends": "./tsconfig.base.json", + "tstl": { + "luaTarget": "5.3" + } +} diff --git a/test/cli/tsconfig/tsconfig.multi-extends.json b/test/cli/tsconfig/tsconfig.multi-extends.json new file mode 100644 index 000000000..7404a6266 --- /dev/null +++ b/test/cli/tsconfig/tsconfig.multi-extends.json @@ -0,0 +1,6 @@ +{ + "extends": ["./tsconfig.json", "./tsconfig2.json"], + "tstl": { + "sourceMapTraceback": true + } +} diff --git a/test/cli/tsconfig/tsconfig.with-comments.json b/test/cli/tsconfig/tsconfig.with-comments.json new file mode 100644 index 000000000..9baf9adda --- /dev/null +++ b/test/cli/tsconfig/tsconfig.with-comments.json @@ -0,0 +1,8 @@ +{ + "extends": "./tsconfig.base.json", + "tstl": { + // Can handle comments + /* also of this kind */ + "luaTarget": "5.3" + } +} diff --git a/test/cli/tsconfig/tsconfig2.json b/test/cli/tsconfig/tsconfig2.json new file mode 100644 index 000000000..9cfef77e0 --- /dev/null +++ b/test/cli/tsconfig/tsconfig2.json @@ -0,0 +1,5 @@ +{ + "tstl": { + "luaTarget": "5.4" + } +} diff --git a/test/json.50.lua b/test/json.50.lua new file mode 100644 index 000000000..8e4109c8f --- /dev/null +++ b/test/json.50.lua @@ -0,0 +1,147 @@ +-- +-- json.lua +-- +-- Copyright (c) 2015 rxi +-- +-- This library is free software; you can redistribute it and/or modify it +-- under the terms of the MIT license. See LICENSE for details. +-- +-- +-- LICENSE CONTENTS: +-- +-- Copyright (c) 2015 rxi +-- +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy of +-- this software and associated documentation files (the "Software"), to deal in +-- the Software without restriction, including without limitation the rights to +-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +-- of the Software, and to permit persons to whom the Software is furnished to do +-- so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in all +-- copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. + +local json = { _version = "0.1.0" } + +------------------------------------------------------------------------------- +-- Encode +------------------------------------------------------------------------------- + +local encode + +local escape_char_map = { + [ "\\" ] = "\\\\", + [ "\"" ] = "\\\"", + [ "\b" ] = "\\b", + [ "\f" ] = "\\f", + [ "\n" ] = "\\n", + [ "\r" ] = "\\r", + [ "\t" ] = "\\t", +} + +local escape_char_map_inv = { [ "\\/" ] = "/" } +for k, v in pairs(escape_char_map) do + escape_char_map_inv[v] = k +end + + +local function escape_char(c) + return escape_char_map[c] or string.format("\\u%04x", c:byte()) +end + + +local function encode_nil(val) + return "null" +end + + +local function encode_table(val, stack) + local res = {} + stack = stack or {} + + -- Circular reference? + if stack[val] then error("circular reference") end + + stack[val] = true + + if val[1] ~= nil or next(val) == nil then + -- Treat as array -- check keys are valid and it is not sparse + local n = 0 + for k in pairs(val) do + if k ~= "n" then + if type(k) ~= "number" then + error("invalid table: mixed or invalid key types") + end + n = n + 1 + end + end + if n ~= table.getn(val) then + error("invalid table: sparse array") + end + -- Encode + for i, v in ipairs(val) do + table.insert(res, encode(v, stack)) + end + stack[val] = nil + return "[" .. table.concat(res, ",") .. "]" + + else + -- Treat as an object + for k, v in pairs(val) do + if type(k) ~= "string" then + error("invalid table: mixed or invalid key types") + end + table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) + end + stack[val] = nil + return "{" .. table.concat(res, ",") .. "}" + end +end + + +local function encode_string(val) + return '"' .. string.gsub(val, '[%z\1-\31\\"]', escape_char) .. '"' +end + + +local function encode_number(val) + if val ~= val then + return "NaN" + elseif val >= 1 / 0 then + return "Infinity" + elseif val <= 0 / 0 then + return "-Infinity" + else + return string.format("%.17g", val) + end +end + + +local type_func_map = { + [ "nil" ] = encode_nil, + [ "table" ] = encode_table, + [ "string" ] = encode_string, + [ "number" ] = encode_number, + [ "boolean" ] = tostring, +} + + +encode = function(val, stack) + local t = type(val) + local f = type_func_map[t] + if f then + return f(val, stack) + end + error("unexpected type '" .. t .. "'") +end + +return {stringify = function(val) return ( encode(val) ) end} diff --git a/test/setup.ts b/test/setup.ts index de78ad24b..704c59bed 100644 --- a/test/setup.ts +++ b/test/setup.ts @@ -3,7 +3,6 @@ import * as ts from "typescript"; import * as tstl from "../src"; declare global { - // eslint-disable-next-line @typescript-eslint/no-namespace namespace jest { // eslint-disable-next-line @typescript-eslint/naming-convention interface Matchers { @@ -14,7 +13,7 @@ declare global { expect.extend({ toHaveDiagnostics(diagnostics: ts.Diagnostic[], expected?: number[]): jest.CustomMatcherResult { - assert(Array.isArray(diagnostics)); + assert.ok(Array.isArray(diagnostics)); // @ts-ignore const matcherHint = this.utils.matcherHint("toHaveDiagnostics", undefined, "", this); @@ -37,7 +36,9 @@ expect.extend({ const message = this.isNot ? diagnosticMessages : expected - ? `Expected:\n${expected.join("\n")}\nReceived:\n${diagnostics.map(diag => diag.code).join("\n")}\n` + ? `Expected:\n${expected.join("\n")}\nReceived:\n${diagnostics + .map(diag => diag.code) + .join("\n")}\n\n${diagnosticMessages}\n` : `Received: ${this.utils.printReceived([])}\n`; return matcherHint + "\n\n" + message; diff --git a/test/translation/__snapshots__/transformation.spec.ts.snap b/test/translation/__snapshots__/transformation.spec.ts.snap index 7fd968b00..e9fccb4ae 100644 --- a/test/translation/__snapshots__/transformation.spec.ts.snap +++ b/test/translation/__snapshots__/transformation.spec.ts.snap @@ -1,53 +1,74 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Luau-specific Transformation (luauSpecificTransformations) 1`] = ` +"t = if true then "is true" else "is false" +while false do + continue +end +repeat + do + continue + end +until not false" +`; + exports[`Transformation (blockScopeVariables) 1`] = ` "do local a = 1 local b = 1 - local ____ = {c = 1} - local c = ____.c + local ____temp_0 = {c = 1} + local c = ____temp_0.c end" `; exports[`Transformation (characterEscapeSequence) 1`] = ` -"quoteInDoubleQuotes = \\"' ' '\\" -quoteInTemplateString = \\"' ' '\\" -doubleQuoteInQuotes = \\"\\\\\\" \\\\\\" \\\\\\"\\" -doubleQuoteInDoubleQuotes = \\"\\\\\\" \\\\\\" \\\\\\"\\" -doubleQuoteInTemplateString = \\"\\\\\\" \\\\\\" \\\\\\"\\" -backQuoteInQuotes = \\"\` \` \`\\" -backQuoteInDoubleQuotes = \\"\` \` \`\\" -backQuoteInTemplateString = \\"\` \` \`\\" -escapedCharsInQuotes = \\"\\\\\\\\ \\\\0 \\\\b \\\\t \\\\n \\\\v \\\\f \\\\\\" ' \`\\" -escapedCharsInDoubleQuotes = \\"\\\\\\\\ \\\\0 \\\\b \\\\t \\\\n \\\\v \\\\f \\\\\\" ' \`\\" -escapedCharsInTemplateString = \\"\\\\\\\\ \\\\0 \\\\b \\\\t \\\\n \\\\v \\\\f \\\\\\" ' \`\\" -nonEmptyTemplateString = (\\"Level 0: \\\\n\\\\t \\" .. ((\\"Level 1: \\\\n\\\\t\\\\t \\" .. ((\\"Level 3: \\\\n\\\\t\\\\t\\\\t \\" .. \\"Last level \\\\n --\\") .. \\" \\\\n --\\")) .. \\" \\\\n --\\")) .. \\" \\\\n --\\"" +"quoteInDoubleQuotes = "' ' '" +quoteInTemplateString = "' ' '" +doubleQuoteInQuotes = "\\" \\" \\"" +doubleQuoteInDoubleQuotes = "\\" \\" \\"" +doubleQuoteInTemplateString = "\\" \\" \\"" +backQuoteInQuotes = "\` \` \`" +backQuoteInDoubleQuotes = "\` \` \`" +backQuoteInTemplateString = "\` \` \`" +escapedCharsInQuotes = "\\\\ \\0 \\b \\t \\n \\v \\f \\" ' \`" +escapedCharsInDoubleQuotes = "\\\\ \\0 \\b \\t \\n \\v \\f \\" ' \`" +escapedCharsInTemplateString = "\\\\ \\0 \\b \\t \\n \\v \\f \\" ' \`" +nonEmptyTemplateString = ("Level 0: \\n\\t " .. ("Level 1: \\n\\t\\t " .. ("Level 3: \\n\\t\\t\\t " .. "Last level \\n --") .. " \\n --") .. " \\n --") .. " \\n --"" `; +exports[`Transformation (customNameWithExtraComment) 1`] = `"TestNamespace.pass()"`; + +exports[`Transformation (customNameWithNoSelf) 1`] = `"TestNamespace.pass()"`; + exports[`Transformation (exportStatement) 1`] = ` "local ____exports = {} local xyz = 4 ____exports.xyz = xyz ____exports.uwv = xyz do - local ____export = require(\\"xyz\\") + local ____export = require("xyz") for ____exportKey, ____exportValue in pairs(____export) do - if ____exportKey ~= \\"default\\" then + if ____exportKey ~= "default" then ____exports[____exportKey] = ____exportValue end end end do - local ____xyz = require(\\"xyz\\") - local abc = ____xyz.abc - local def = ____xyz.def - ____exports.abc = abc - ____exports.def = def + local ____xyz = require("xyz") + ____exports.abc = ____xyz.abc + ____exports.def = ____xyz.def end do - local ____xyz = require(\\"xyz\\") - local def = ____xyz.abc - ____exports.def = def + local ____xyz = require("xyz") + ____exports.def = ____xyz.abc +end +do + local ____bla = require("bla") + ____exports.bar = ____bla["123"] +end +do + local ____bla = require("bla") + ____exports["123"] = ____bla.foo end return ____exports" `; @@ -58,9 +79,10 @@ return ____exports" `; exports[`Transformation (methodRestArguments) 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require("lualib_bundle") +local __TS__Class = ____lualib.__TS__Class MyClass = __TS__Class() -MyClass.name = \\"MyClass\\" +MyClass.name = "MyClass" function MyClass.prototype.____constructor(self) end function MyClass.prototype.varargsFunction(self, a, ...) @@ -74,22 +96,24 @@ return ____exports" `; exports[`Transformation (modulesClassExport) 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require("lualib_bundle") +local __TS__Class = ____lualib.__TS__Class local ____exports = {} ____exports.TestClass = __TS__Class() local TestClass = ____exports.TestClass -TestClass.name = \\"TestClass\\" +TestClass.name = "TestClass" function TestClass.prototype.____constructor(self) end return ____exports" `; exports[`Transformation (modulesClassWithMemberExport) 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require("lualib_bundle") +local __TS__Class = ____lualib.__TS__Class local ____exports = {} ____exports.TestClass = __TS__Class() local TestClass = ____exports.TestClass -TestClass.name = \\"TestClass\\" +TestClass.name = "TestClass" function TestClass.prototype.____constructor(self) end function TestClass.prototype.memberFunc(self) @@ -111,14 +135,14 @@ end" exports[`Transformation (modulesImportAll) 1`] = ` "local ____exports = {} -local Test = require(\\"test\\") +local Test = require("test") local ____ = Test return ____exports" `; exports[`Transformation (modulesImportNamed) 1`] = ` "local ____exports = {} -local ____test = require(\\"test\\") +local ____test = require("test") local TestClass = ____test.TestClass local ____ = TestClass return ____exports" @@ -126,15 +150,15 @@ return ____exports" exports[`Transformation (modulesImportNamedSpecialChars) 1`] = ` "local ____exports = {} -local ____kebab_2Dmodule = require(\\"kebab-module\\") +local ____kebab_2Dmodule = require("kebab-module") local TestClass1 = ____kebab_2Dmodule.TestClass1 -local ____dollar_24module = require(\\"dollar$module\\") +local ____dollar_24module = require("dollar$module") local TestClass2 = ____dollar_24module.TestClass2 -local ____singlequote_27module = require(\\"singlequote'module\\") +local ____singlequote_27module = require("singlequote'module") local TestClass3 = ____singlequote_27module.TestClass3 -local ____hash_23module = require(\\"hash#module\\") +local ____hash_23module = require("hash#module") local TestClass4 = ____hash_23module.TestClass4 -local ____space_20module = require(\\"space module\\") +local ____space_20module = require("space module") local TestClass5 = ____space_20module.TestClass5 local ____ = TestClass1 local ____ = TestClass2 @@ -146,7 +170,7 @@ return ____exports" exports[`Transformation (modulesImportRenamed) 1`] = ` "local ____exports = {} -local ____test = require(\\"test\\") +local ____test = require("test") local RenamedClass = ____test.TestClass local ____ = RenamedClass return ____exports" @@ -154,15 +178,15 @@ return ____exports" exports[`Transformation (modulesImportRenamedSpecialChars) 1`] = ` "local ____exports = {} -local ____kebab_2Dmodule = require(\\"kebab-module\\") +local ____kebab_2Dmodule = require("kebab-module") local RenamedClass1 = ____kebab_2Dmodule.TestClass -local ____dollar_24module = require(\\"dollar$module\\") +local ____dollar_24module = require("dollar$module") local RenamedClass2 = ____dollar_24module.TestClass -local ____singlequote_27module = require(\\"singlequote'module\\") +local ____singlequote_27module = require("singlequote'module") local RenamedClass3 = ____singlequote_27module.TestClass -local ____hash_23module = require(\\"hash#module\\") +local ____hash_23module = require("hash#module") local RenamedClass4 = ____hash_23module.TestClass -local ____space_20module = require(\\"space module\\") +local ____space_20module = require("space module") local RenamedClass5 = ____space_20module.TestClass local ____ = RenamedClass1 local ____ = RenamedClass2 @@ -174,7 +198,7 @@ return ____exports" exports[`Transformation (modulesImportWithoutFromClause) 1`] = ` "local ____exports = {} -require(\\"test\\") +require("test") return ____exports" `; @@ -224,11 +248,35 @@ return ____exports" exports[`Transformation (modulesVariableExport) 1`] = ` "local ____exports = {} -____exports.foo = \\"bar\\" +____exports.foo = "bar" return ____exports" `; -exports[`Transformation (modulesVariableNoExport) 1`] = `"foo = \\"bar\\""`; +exports[`Transformation (modulesVariableNoExport) 1`] = `"foo = "bar""`; + +exports[`Transformation (printFormat) 1`] = ` +"stringConcat = (("a" .. "b" .. "c") .. "d") .. "e" +numbers = 2 * 2 + 3 + 4 * (5 + 6) ~= 7 +function func(...) +end +func(function() + local b = "A function" +end) +func(func()) +array = {func()} +array2 = { + func(), + func() +} +object = {a = 1, b = 2, c = 3} +bigObject = { + a = 1, + b = 2, + c = 3, + d = "value1", + e = "value2" +}" +`; exports[`Transformation (returnDefault) 1`] = ` "function myFunc(self) @@ -242,7 +290,8 @@ value1 = obj.value1 value2 = obj.value2 obj2 = {value3 = 1, value4 = 2} value3 = obj2.value3 -value4 = obj2.value4 +local ____obj2_0 = obj2 +value4 = ____obj2_0.value4 function fun1(self) end fun2 = function() @@ -251,7 +300,7 @@ end" exports[`Transformation (unusedDefaultWithNamespaceImport) 1`] = ` "local ____exports = {} -local x = require(\\"module\\") +local x = require("module") local ____ = x return ____exports" `; diff --git a/test/translation/transformation.spec.ts b/test/translation/transformation.spec.ts index 42a4b4355..5f9df58fe 100644 --- a/test/translation/transformation.spec.ts +++ b/test/translation/transformation.spec.ts @@ -19,3 +19,18 @@ test.each(fixtures)("Transformation (%s)", (_name, content) => { .disableSemanticCheck() .expectLuaToMatchSnapshot(); }); + +const luauFixturesPath = path.join(fixturesPath, "luau"); +const luauFixtures = fs + .readdirSync(luauFixturesPath) + .filter(f => path.extname(f) === ".ts") + .sort() + .map(f => [path.parse(f).name, fs.readFileSync(path.join(luauFixturesPath, f), "utf8")]); + +test.each(luauFixtures)("Luau-specific Transformation (%s)", (_name, content) => { + util.testModule(content) + .setOptions({ luaLibImport: tstl.LuaLibImportKind.Require, luaTarget: tstl.LuaTarget.Luau }) + .ignoreDiagnostics([annotationDeprecated.code, couldNotResolveRequire.code]) + .disableSemanticCheck() + .expectLuaToMatchSnapshot(); +}); diff --git a/test/translation/transformation/customNameWithExtraComment.ts b/test/translation/transformation/customNameWithExtraComment.ts new file mode 100644 index 000000000..459512ded --- /dev/null +++ b/test/translation/transformation/customNameWithExtraComment.ts @@ -0,0 +1,10 @@ +/** @noSelf */ +declare namespace TestNamespace { + /** + * @customName pass + * The first word should not be included. + **/ + function fail(): void; +} + +TestNamespace.fail(); diff --git a/test/translation/transformation/customNameWithNoSelf.ts b/test/translation/transformation/customNameWithNoSelf.ts new file mode 100644 index 000000000..73175ccdc --- /dev/null +++ b/test/translation/transformation/customNameWithNoSelf.ts @@ -0,0 +1,7 @@ +/** @noSelf */ +declare namespace TestNamespace { + /** @customName pass */ + function fail(): void; +} + +TestNamespace.fail(); diff --git a/test/translation/transformation/exportStatement.ts b/test/translation/transformation/exportStatement.ts index 7aa965422..cf227da3d 100644 --- a/test/translation/transformation/exportStatement.ts +++ b/test/translation/transformation/exportStatement.ts @@ -4,3 +4,5 @@ export { xyz as uwv }; export * from "xyz"; export { abc, def } from "xyz"; export { abc as def } from "xyz"; +export { "123" as bar } from "bla"; +export { foo as "123" } from "bla"; diff --git a/test/translation/transformation/luau/luauSpecificTransformations.ts b/test/translation/transformation/luau/luauSpecificTransformations.ts new file mode 100644 index 000000000..b4c9fdec4 --- /dev/null +++ b/test/translation/transformation/luau/luauSpecificTransformations.ts @@ -0,0 +1,9 @@ +const t = true ? "is true" : "is false"; + +while (false) { + continue; +} + +do { + continue; +} while (false); diff --git a/test/translation/transformation/printFormat.ts b/test/translation/transformation/printFormat.ts new file mode 100644 index 000000000..3374952c5 --- /dev/null +++ b/test/translation/transformation/printFormat.ts @@ -0,0 +1,27 @@ +const stringConcat = "a" + ("b" + "c") + "d" + "e"; +const numbers = 2 * 2 + 3 + 4 * (5 + 6) !== 7; + +function func(this: void, ...args: any) {} + +func(() => { + const b = "A function"; +}); + +func(func()); + +const array = [func()]; +const array2 = [func(), func()]; + +const object = { + a: 1, + b: 2, + c: 3, +}; + +const bigObject = { + a: 1, + b: 2, + c: 3, + d: "value1", + e: "value2", +}; diff --git a/test/transpile/__snapshots__/directories.spec.ts.snap b/test/transpile/__snapshots__/directories.spec.ts.snap index eaf179a6c..d214dbba7 100644 --- a/test/transpile/__snapshots__/directories.spec.ts.snap +++ b/test/transpile/__snapshots__/directories.spec.ts.snap @@ -1,33 +1,29 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`should be able to resolve ({"name": "basic", "options": [Object]}) 1`] = ` -Array [ +[ "directories/basic/src/lib/file.lua", - "directories/basic/src/lualib_bundle.lua", "directories/basic/src/main.lua", ] `; exports[`should be able to resolve ({"name": "basic", "options": [Object]}) 2`] = ` -Array [ +[ "directories/basic/out/lib/file.lua", - "directories/basic/out/lualib_bundle.lua", "directories/basic/out/main.lua", ] `; exports[`should be able to resolve ({"name": "basic", "options": [Object]}) 3`] = ` -Array [ +[ "directories/basic/src/lib/file.lua", - "directories/basic/src/lualib_bundle.lua", "directories/basic/src/main.lua", ] `; exports[`should be able to resolve ({"name": "basic", "options": [Object]}) 4`] = ` -Array [ +[ "directories/basic/out/lib/file.lua", - "directories/basic/out/lualib_bundle.lua", "directories/basic/out/main.lua", ] `; diff --git a/test/transpile/__snapshots__/module-resolution.spec.ts.snap b/test/transpile/__snapshots__/module-resolution.spec.ts.snap new file mode 100644 index 000000000..e87569ded --- /dev/null +++ b/test/transpile/__snapshots__/module-resolution.spec.ts.snap @@ -0,0 +1,17 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`supports complicated paths configuration 1`] = ` +[ + "/paths-base-tsconfig/dist/packages/tstl-program/packages/mypackage/src/bar.lua", + "/paths-base-tsconfig/dist/packages/tstl-program/packages/mypackage/src/index.lua", + "/paths-base-tsconfig/dist/packages/tstl-program/packages/myprogram/src/main.lua", +] +`; + +exports[`supports paths configuration 1`] = ` +[ + "/paths-simple/myprogram/dist/mypackage/bar.lua", + "/paths-simple/myprogram/dist/mypackage/index.lua", + "/paths-simple/myprogram/dist/myprogram/main.lua", +] +`; diff --git a/test/transpile/__snapshots__/project.spec.ts.snap b/test/transpile/__snapshots__/project.spec.ts.snap index 623480405..227e004f8 100644 --- a/test/transpile/__snapshots__/project.spec.ts.snap +++ b/test/transpile/__snapshots__/project.spec.ts.snap @@ -1,8 +1,28 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`should give verbose output 1`] = ` +[ + "Loaded 0 plugins", + "Parsing project settings", + "Transforming /test/transpile/project/otherFile.ts", + "Printing /test/transpile/project/otherFile.ts", + "Transforming /test/transpile/project/index.ts", + "Printing /test/transpile/project/index.ts", + "Constructing emit plan", + "Resolving dependencies for /test/transpile/project/otherFile.ts", + "Resolving dependencies for /test/transpile/project/index.ts", + "Resolving "./otherFile" from /test/transpile/project/index.ts", + "Resolved ./otherFile to /test/transpile/project/otherFile.ts", + "Emitting output", + "Emitting /test/transpile/project/otherFile.lua", + "Emitting /test/transpile/project/index.lua", + "Emit finished!", +] +`; + exports[`should transpile 1`] = ` -Array [ - Object { +[ + { "filePath": "otherFile.lua", "lua": "local ____exports = {} function ____exports.getNumber(self) @@ -11,10 +31,10 @@ end return ____exports ", }, - Object { + { "filePath": "index.lua", "lua": "local ____exports = {} -local ____otherFile = require(\\"otherFile\\") +local ____otherFile = require("otherFile") local getNumber = ____otherFile.getNumber local myNumber = getNumber(nil) setAPIValue(myNumber * 5) diff --git a/test/transpile/bundle.spec.ts b/test/transpile/bundle.spec.ts index 69c86406e..3834bbb2f 100644 --- a/test/transpile/bundle.spec.ts +++ b/test/transpile/bundle.spec.ts @@ -1,19 +1,132 @@ import * as path from "path"; import * as util from "../util"; +import { TranspileVirtualProjectResult } from "../../src"; +import { lineAndColumnOf } from "../unit/printer/utils"; +import * as fs from "fs"; -const projectDir = path.join(__dirname, "bundle"); -const inputProject = path.join(projectDir, "tsconfig.json"); +describe("bundle two files", () => { + const projectDir = path.join(__dirname, "bundle", "bundle-two-files"); + const inputProject = path.join(projectDir, "tsconfig.json"); -test("should transpile into one file", () => { - const { diagnostics, transpiledFiles } = util.testProject(inputProject).getLuaResult(); + let transpileResult: TranspileVirtualProjectResult = { + transpiledFiles: [], + diagnostics: [], + }; - expect(diagnostics).not.toHaveDiagnostics(); - expect(transpiledFiles).toHaveLength(1); + beforeAll(() => { + transpileResult = util.testProject(inputProject).getLuaResult(); + }); + + test("should transpile into one file (with no errors)", () => { + expect(transpileResult.diagnostics).not.toHaveDiagnostics(); + expect(transpileResult.transpiledFiles).toHaveLength(1); + }); - const { outPath, lua } = transpiledFiles[0]; // Verify the name is as specified in tsconfig - expect(outPath.endsWith("bundle/bundle.lua")).toBe(true); + test("should have name, specified in tsconfig.json", () => { + const { outPath } = transpileResult.transpiledFiles[0]; + expect(outPath.endsWith(path.join(projectDir, "bundle.lua"))).toBe(true); + }); + + // Verify exported module by executing + // Use an empty TS string because we already transpiled the TS project + test("executing should act correctly", () => { + const { lua } = transpileResult.transpiledFiles[0]; + util.testModule("").setLuaHeader(lua!).expectToEqual({ myNumber: 3 }); + }); +}); + +describe("bundle with source maps", () => { + const projectDir = path.join(__dirname, "bundle", "bundle-source-maps"); + const inputProject = path.join(projectDir, "tsconfig.json"); + + let transpileResult: TranspileVirtualProjectResult = { + transpiledFiles: [], + diagnostics: [], + }; + + beforeAll(() => { + transpileResult = util.testProject(inputProject).getLuaResult(); + }); + + // See https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1109 + test('the result file should not contain "{#SourceMapTraceback}" macro-string', () => { + const { lua } = transpileResult.transpiledFiles[0]; + expect(lua).toBeDefined(); + expect(lua!).not.toContain("{#SourceMapTraceback}"); + }); + // Verify exported module by executing // Use an empty TS string because we already transpiled the TS project - util.testModule("").setLuaHeader(lua!).expectToEqual({ myNumber: 3 }); + test("executing should act correctly", () => { + const { lua } = transpileResult.transpiledFiles[0]; + const result = util.testModule("").setLuaHeader(lua!).getLuaExecutionResult(); + + expect(result.myNumber).toEqual(3 * 4 * (5 + 6)); + }); + + test("sourceMapTraceback saves correct sourcemap", () => { + const code = { + index: fs.readFileSync(path.join(projectDir, "index.ts"), "utf8"), + largeFile: fs.readFileSync(path.join(projectDir, "largeFile.ts"), "utf8"), + }; + + const { lua } = transpileResult.transpiledFiles[0]; + const builder = util.testModule("").setLuaHeader(lua!); + const result = builder.getLuaExecutionResult(); + const sourceMap = result.sourceMap; + + expect(sourceMap).toEqual(expect.any(Object)); + const sourceMapFiles = Object.keys(sourceMap); + expect(sourceMapFiles).toHaveLength(1); + const mainSourceMap = sourceMap[sourceMapFiles[0]]; + + const transpiledLua = builder.getMainLuaCodeChunk(); + + const assertPatterns: Array<{ + file: keyof typeof code; + luaPattern: string; + typeScriptPattern: string; + }> = [ + { + file: "index", + luaPattern: "____exports.myNumber = getNumber(", + typeScriptPattern: "const myNumber = getNumber(", + }, + { + file: "largeFile", + luaPattern: "local Calculator = __TS__Class()", + typeScriptPattern: "abstract class Calculator", + }, + { + file: "largeFile", + luaPattern: "local CalculatorMul = __TS__Class()", + typeScriptPattern: "class CalculatorMul extends Calculator {", + }, + { + file: "largeFile", + luaPattern: "local function resolveCalculatorClass(", + typeScriptPattern: "function resolveCalculatorClass(", + }, + { + file: "largeFile", + luaPattern: "function ____exports.getNumber(", + typeScriptPattern: "export function getNumber(", + }, + { + file: "largeFile", + luaPattern: 'Error,\n "Unknown operation "', + typeScriptPattern: "throw new Error(", + }, + ]; + + for (const { file: currentFile, luaPattern, typeScriptPattern } of assertPatterns) { + const luaPosition = lineAndColumnOf(transpiledLua, luaPattern); + const mappedLine: { file: string; line: number } = mainSourceMap[luaPosition.line.toString()]; + + const typescriptPosition = lineAndColumnOf(code[currentFile], typeScriptPattern); + expect(mappedLine.line).toEqual(typescriptPosition.line); + expect(mappedLine.file).toBe(`${currentFile}.ts`); + } + }); }); diff --git a/test/transpile/bundle/bundle-source-maps/index.ts b/test/transpile/bundle/bundle-source-maps/index.ts new file mode 100644 index 000000000..22a2f499e --- /dev/null +++ b/test/transpile/bundle/bundle-source-maps/index.ts @@ -0,0 +1,8 @@ +import { getNumber, Operation } from "./largeFile"; + +// Local variables +const left = getNumber(3, 4, Operation.MUL); +const right = getNumber(5, 6, Operation.SUM); + +export const myNumber = getNumber(left, right, Operation.MUL); +export const sourceMap = (globalThis as any).__TS__sourcemap; diff --git a/test/transpile/bundle/bundle-source-maps/largeFile.ts b/test/transpile/bundle/bundle-source-maps/largeFile.ts new file mode 100644 index 000000000..ae0a9be7a --- /dev/null +++ b/test/transpile/bundle/bundle-source-maps/largeFile.ts @@ -0,0 +1,53 @@ +// Some comments here to check source map correctness +// Some comments here to check source map correctness + +abstract class Calculator { + protected left: number; + protected right: number; + + constructor(left: number, right: number) { + this.left = left; + this.right = right; + } + + public abstract calc(): number; +} + +/** + * Sums two numbers + */ +class CalculatorSum extends Calculator { + public calc(): number { + return this.left + this.right; + } +} + +class CalculatorMul extends Calculator { + public calc(): number { + return this.left * this.right; + } +} + +// Some comments here to check source map correctness + +export const enum Operation { + SUM = "SUM", + MUL = "MUL", +} + +// Local internal function +function resolveCalculatorClass(left: number, right: number, operation: Operation): Calculator { + if (operation === Operation.MUL) { + return new CalculatorMul(left, right); + } + if (operation === Operation.SUM) { + return new CalculatorSum(left, right); + } + + throw new Error(`Unknown operation ${operation}`); +} + +// Some comments here to check source map correctness +export function getNumber(left: number, right: number, operation: Operation): number { + return resolveCalculatorClass(left, right, operation).calc(); +} diff --git a/test/transpile/bundle/bundle-source-maps/tsconfig.json b/test/transpile/bundle/bundle-source-maps/tsconfig.json new file mode 100644 index 000000000..6614ac5d7 --- /dev/null +++ b/test/transpile/bundle/bundle-source-maps/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "target": "esnext", + "lib": ["esnext"], + "types": ["lua-types/5.3"], + "rootDir": "." + }, + "tstl": { + "luaBundle": "bundle.lua", + "luaBundleEntry": "index.ts", + "sourceMapTraceback": true + }, + "include": ["."] +} diff --git a/test/transpile/bundle/index.ts b/test/transpile/bundle/bundle-two-files/index.ts similarity index 100% rename from test/transpile/bundle/index.ts rename to test/transpile/bundle/bundle-two-files/index.ts diff --git a/test/transpile/bundle/otherFile.ts b/test/transpile/bundle/bundle-two-files/otherFile.ts similarity index 100% rename from test/transpile/bundle/otherFile.ts rename to test/transpile/bundle/bundle-two-files/otherFile.ts diff --git a/test/transpile/bundle/tsconfig.json b/test/transpile/bundle/bundle-two-files/tsconfig.json similarity index 100% rename from test/transpile/bundle/tsconfig.json rename to test/transpile/bundle/bundle-two-files/tsconfig.json diff --git a/test/transpile/directories.spec.ts b/test/transpile/directories.spec.ts index 1c0bba994..9572c1e0d 100644 --- a/test/transpile/directories.spec.ts +++ b/test/transpile/directories.spec.ts @@ -19,7 +19,7 @@ test.each([ const config = { compilerOptions: { ...compilerOptions, types: [], skipLibCheck: true }, - tstl: { luaTarget: tstl.LuaTarget.LuaJIT, luaLibImport: tstl.LuaLibImportKind.Always }, + tstl: { luaTarget: tstl.LuaTarget.LuaJIT }, }; const { fileNames, options } = tstl.updateParsedConfigFile( diff --git a/test/transpile/lualib.spec.ts b/test/transpile/lualib.spec.ts new file mode 100644 index 000000000..20288bd8a --- /dev/null +++ b/test/transpile/lualib.spec.ts @@ -0,0 +1,31 @@ +import * as ts from "typescript"; +import { LuaLibFeature, LuaTarget } from "../../src"; +import { readLuaLibFeature } from "../../src/LuaLib"; +import * as util from "../util"; + +test.each(Object.entries(LuaLibFeature))("Lualib does not use ____exports (%p)", (_, feature) => { + const lualibCode = readLuaLibFeature(feature, LuaTarget.Lua54, ts.sys); + + const exportsOccurrences = lualibCode.match(/____exports/g); + expect(exportsOccurrences).toBeNull(); +}); + +test("Lualib bundle does not assign globals", () => { + // language=TypeScript + util.testModule` + declare const _G: LuaTable; + declare const require: (this: void, module: string) => any; + const globalKeys = new LuaTable(); + for (const [key] of _G) { + globalKeys[key] = true; + } + require("lualib_bundle"); + for (const [key] of _G) { + if (!globalKeys[key]) { + error("Global was assigned: " + key); + } + } + ` + .withLanguageExtensions() + .expectNoExecutionError(); +}); diff --git a/test/transpile/module-resolution.spec.ts b/test/transpile/module-resolution.spec.ts index c386e2d33..35e79ed3d 100644 --- a/test/transpile/module-resolution.spec.ts +++ b/test/transpile/module-resolution.spec.ts @@ -2,7 +2,8 @@ import * as path from "path"; import * as tstl from "../../src"; import * as util from "../util"; import * as ts from "typescript"; -import { transpileProject } from "../../src"; +import { BuildMode } from "../../src"; +import { normalizeSlashes } from "../../src/utils"; describe("basic module resolution", () => { const projectPath = path.resolve(__dirname, "module-resolution", "project-with-node-modules"); @@ -49,7 +50,7 @@ describe("basic module resolution", () => { test("can resolve package depencency with a dependency on another package", () => { // Declarations in the node_modules directory - expect(projectWithNodeModules.getLuaExecutionResult().moduleWithDependencyResult).toEqual( + expect(projectWithNodeModules.getLuaExecutionResult().moduleWithDependencyResult).toBe( "Calling dependency: foo from lua module with decls" ); }); @@ -172,6 +173,22 @@ describe("module resolution with sourceDir", () => { .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) .expectToEqual(expectedResult); }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1394 + test("can resolve files with non-standard extension (#1394)", () => { + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "src", "main.ts")) + .setOptions({ outDir: "tstl-out", extension: ".script" }) + .expectToEqual(expectedResult); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1394 + test("can resolve files with non-standard extension without separator (#1394)", () => { + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "src", "main.ts")) + .setOptions({ outDir: "tstl-out", extension: "script" }) + .expectToEqual(expectedResult); + }); }); describe("module resolution project with lua sources", () => { @@ -230,53 +247,502 @@ describe("module resolution in library mode", () => { expect(file.lua).not.toContain('require("lua_modules'); } }); +}); - test("bundle works in library mode because no external dependencies", () => { - const projectPath = path.resolve(__dirname, "module-resolution", "project-with-lua-sources"); - const mainFile = path.join(projectPath, "main.ts"); +describe("module resolution project with dependencies built by tstl library mode", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-tstl-library-modules"); - const { transpiledFiles } = util + // First compile dependencies into node_modules. NOTE: Actually writing to disk, very slow + tstl.transpileProject(path.join(projectPath, "dependency1-ts", "tsconfig.json")); + tstl.transpileProject(path.join(projectPath, "dependency2-ts", "tsconfig.json")); + + const expectedResult = { + dependency1IndexResult: "function in dependency 1 index: dependency1OtherFileFunc in dependency1/d1otherfile", + dependency1OtherFileFuncResult: "dependency1OtherFileFunc in dependency1/d1otherfile", + dependency2MainResult: "dependency 2 main", + dependency2OtherFileResult: "Dependency 2 func: my string argument", + }; + + test("can resolve lua dependencies", () => { + const transpileResult = util .testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "main.ts")) - .setOptions({ buildMode: tstl.BuildMode.Library, luaBundle: "bundle.lua", luaBundleEntry: mainFile }) - .expectToEqual({ - funcFromLuaFile: "lua file in subdir", - funcFromSubDirLuaFile: "lua file in subdir", - }) + .setOptions({ outDir: "tstl-out" }) + .expectToEqual(expectedResult) .getLuaResult(); - for (const file of transpiledFiles) { - expect(file.lua).not.toContain('require("lua_modules'); - } + // Assert node_modules file requires the correct lualib_bundle + const requiringLuaFile = path.join("lua_modules", "dependency1", "index.lua"); + const lualibRequiringFile = transpileResult.transpiledFiles.find(f => f.outPath.endsWith(requiringLuaFile)); + expect(lualibRequiringFile).toBeDefined(); + expect(lualibRequiringFile?.lua).toContain('require("lualib_bundle")'); + }); + + test("can resolve dependencies and bundle", () => { + const mainFile = path.join(projectPath, "main.ts"); + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual(expectedResult); }); }); -describe("module resolution project with dependencies built by tstl library mode", () => { - const projectPath = path.resolve(__dirname, "module-resolution", "project-with-tstl-library-modules"); +describe("module resolution project with dependencies built by tstl library mode and has exports field", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-tstl-library-has-exports-field"); // First compile dependencies into node_modules. NOTE: Actually writing to disk, very slow - transpileProject(path.join(projectPath, "dependency1-ts", "tsconfig.json")); - transpileProject(path.join(projectPath, "dependency2-ts", "tsconfig.json")); + const dependency1Path = path.join(projectPath, "node_modules", "dependency1"); + tstl.transpileProject(path.join(dependency1Path, "tsconfig.json")); const expectedResult = { dependency1IndexResult: "function in dependency 1 index: dependency1OtherFileFunc in dependency1/d1otherfile", dependency1OtherFileFuncResult: "dependency1OtherFileFunc in dependency1/d1otherfile", - dependency2MainResult: "dependency 2 main", - dependency2OtherFileResult: "Dependency 2 func: my string argument", }; test("can resolve lua dependencies", () => { - util.testProject(path.join(projectPath, "tsconfig.json")) + const transpileResult = util + .testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(path.join(projectPath, "main.ts")) - .setOptions({ outDir: "tstl-out" }) - .expectToEqual(expectedResult); + .setOptions({ + outDir: "tstl-out", + moduleResolution: ts.ModuleResolutionKind.Node16, + module: ts.ModuleKind.Node16, + }) + .expectToEqual(expectedResult) + .getLuaResult(); + + // Assert node_modules file requires the correct lualib_bundle + const requiringLuaFile = path.join("lua_modules", "dependency1", "dist", "index.lua"); + const lualibRequiringFile = transpileResult.transpiledFiles.find(f => f.outPath.endsWith(requiringLuaFile)); + expect(lualibRequiringFile).toBeDefined(); + expect(lualibRequiringFile?.lua).toContain('require("lualib_bundle")'); }); test("can resolve dependencies and bundle", () => { const mainFile = path.join(projectPath, "main.ts"); util.testProject(path.join(projectPath, "tsconfig.json")) .setMainFileName(mainFile) - .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .setOptions({ + luaBundle: "bundle.lua", + luaBundleEntry: mainFile, + moduleResolution: ts.ModuleResolutionKind.Node16, + module: ts.ModuleKind.Node16, + }) .expectToEqual(expectedResult); }); }); + +// Test fix for https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1037 +describe("module resolution with tsx", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-tsx"); + + test("project with tsx files", () => { + util.testProject(path.join(projectPath, "tsconfig.json")) + .setMainFileName(path.join(projectPath, "main.tsx")) + .expectToEqual({ + result: "hello from other.tsx", + indexResult: "hello from dir/index.tsx", + }); + }); +}); + +describe("dependency with complicated inner structure", () => { + const projectPath = path.resolve(__dirname, "module-resolution", "project-with-complicated-dependency"); + const tsConfigPath = path.join(projectPath, "tsconfig.json"); + const mainFilePath = path.join(projectPath, "main.ts"); + + const expectedResult = { + otherFileResult: "someFunc from otherfile.lua", + otherFileUtil: "util", + subsubresult: "result from subsub dir", + utilResult: "util", + subdirwithInitResult: "a", + }; + + // Test fix for https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1055 + test("bundle should not contain duplicate files", () => { + const mainFile = path.join(projectPath, "main.ts"); + const { transpiledFiles } = util + .testProject(tsConfigPath) + .setMainFileName(mainFilePath) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual(expectedResult) + .getLuaResult(); + + expect(transpiledFiles).toHaveLength(1); + const lua = transpiledFiles[0].lua!; + // util is used in 2 places, but should only occur once in the bundle + const utilModuleOccurrences = (lua.match(/\["lua_modules\.dependency1\.util"\]/g) ?? []).length; + expect(utilModuleOccurrences).toBe(1); + }); + + // Test fix for https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1054 + test("should be able to resolve dependency files in subdirectories", () => { + util.testProject(tsConfigPath).setMainFileName(mainFilePath).expectToEqual(expectedResult); + }); +}); + +test("module resolution should not try to resolve @noResolution annotation", () => { + util.testModule` + import * as json from "json"; + const test = json.decode("{}"); + ` + .addExtraFile( + "json.d.ts", + ` + /** @noResolution */ + declare module "json" { + function encode(this: void, data: unknown): string; + function decode(this: void, data: string): unknown; + } + ` + ) + .expectToHaveNoDiagnostics(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1101 +test("module resolution inline require of environment library workaround", () => { + util.testModule` + declare function require(this: void, module: string): any; + + const test = require("@NoResolution:luasource"); + test.foo(); + `.expectToHaveNoDiagnostics(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1118 +describe("module resolution should not try to resolve modules in noResolvePaths", () => { + test("as used in direct import", () => { + util.testModule` + import * as lua from "directimport"; + lua.foo(); + ` + .addExtraFile( + "directimport.d.ts", + `declare module "directimport" { + export function foo(): void; + }` + ) + .setOptions({ noResolvePaths: ["directimport"] }) + .expectToHaveNoDiagnostics(); + }); + + test("as used in imported lua sources", () => { + util.testModule` + import * as lua from "./luasource"; + lua.foo(); + ` + .addExtraFile("luasource.d.ts", "export function foo(): void;") + .addExtraFile( + "luasource.lua", + ` + require("dontResolveThis") + require("a.b.c.foo") + + return { foo = function() return "bar" end } + ` + ) + .setOptions({ noResolvePaths: ["a.b.c.foo", "somethingExtra", "dontResolveThis"] }) + .expectToHaveNoDiagnostics(); + }); + + test("can ignore specific files with glob pattern", () => { + util.testModule` + // Pre-Load as to not error out at runtime + import "preload"; + + import "ignoreme"; + import * as b from "./actualfile"; + + export const result = b.foo(); + ` + .addExtraFile("preload.d.ts", `declare module "preload" {}`) + .addExtraFile("preload.lua", 'package.preload["ignoreme"] = function() return nil end') + .addExtraFile( + "actualfile.ts", + `export function foo() + { + return 'foo'; + }` + ) + .addExtraFile( + "ignoreme.d.ts", + `declare module "ignoreme" { + export function foo(): void; + }` + ) + .setOptions({ noResolvePaths: ["ignore*"] }) + .expectToHaveNoDiagnostics() + .expectToEqual({ result: "foo" }); + }); + + test("can ignore all files with glob pattern in require", () => { + util.testModule` + declare function require(this: void, module: string): any; + + const a = require("a") + const b = require("b/b") + const c = require("c/c/c") + const d = require("!:?somefile") + ` + .setOptions({ noResolvePaths: ["**"] }) + .expectToHaveNoDiagnostics(); + }); + + test("can ignore all files with glob pattern as used in imported lua sources", () => { + util.testModule` + import * as lua from "./luasource"; + lua.foo(); + ` + .addExtraFile("luasource.d.ts", "export function foo(): void;") + .addExtraFile( + "luasource.lua", + ` + require("ignoreme!") + require("i.g.n.o.r.e") + ` + ) + .setOptions({ noResolvePaths: ["**"] }) + .expectToHaveNoDiagnostics(); + }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1062 +test("module resolution should not rewrite @NoResolution requires in library mode", () => { + const { transpiledFiles } = util.testModule` + import * as json from "json"; + const test = json.decode("{}"); + ` + .addExtraFile( + "json.d.ts", + ` + /** @noResolution */ + declare module "json" { + function encode(this: void, data: unknown): string; + function decode(this: void, data: string): unknown; + } + ` + ) + .setOptions({ buildMode: BuildMode.Library }) + .getLuaResult(); + + expect(transpiledFiles).toHaveLength(1); + expect(transpiledFiles[0].lua).toContain('require("@NoResolution:'); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1050 +test("module resolution should not try to resolve require-like functions", () => { + util.testModule` + function custom_require(this: void, value: string) { + return value; + } + + namespace ns { + export function require(this: void, value: string) { + return value; + } + } + + class MyClass { + require(value: string) { + return value; + } + } + const inst = new MyClass(); + + export const result = [ + custom_require("value 1"), + ns.require("value 2"), + inst.require("value 3") + ]; + + ` + .expectToHaveNoDiagnostics() + .expectToEqual({ + result: ["value 1", "value 2", "value 3"], + }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1050 +test("module resolution uses baseURL to resolve imported files", () => { + util.testModule` + import { foo } from "dep1"; + import { bar } from "dep2"; + import { baz } from "luadep"; + + export const fooResult = foo(); + export const barResult = bar(); + export const bazResult = baz(); + ` + .addExtraFile( + "myproject/mydeps/dep1.ts", + ` + export function foo() { return "foo"; } + ` + ) + .addExtraFile( + "myproject/mydeps/dep2.ts", + ` + export function bar() { return "bar"; } + ` + ) + .addExtraFile( + "myproject/mydeps/luadep.d.ts", + ` + export function baz(): string; + ` + ) + .addExtraFile( + "myproject/mydeps/luadep.lua", + ` + return { baz = function() return "baz" end } + ` + ) + .setOptions({ baseUrl: "./myproject/mydeps", ignoreDeprecations: "6.0" }) + .expectToEqual({ + fooResult: "foo", + barResult: "bar", + bazResult: "baz", + }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1071 +test("includes lualib_bundle when external lua requests it", () => { + util.testModule` + export { foo } from "./lualibuser"; + ` + .addExtraFile( + "lualibuser.d.ts", + ` + export const foo: string[]; + ` + ) + .addExtraFile( + "lualibuser.lua", + ` + local ____lualib = require("lualib_bundle") + local __TS__ArrayPush = ____lualib.__TS__ArrayPush + + local result = {} + __TS__ArrayPush(result, "foo") + __TS__ArrayPush(result, "bar") + + return { foo = result } + ` + ) + .expectToEqual({ + foo: ["foo", "bar"], + }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1146 +test("require matches correct pattern", () => { + util.testModule` + declare function require(this: void, module: string): any; + export const addResult = require("a").foo + require("b").foo; + export const callResult = require("c")("foo"); + ` + .addExtraFile("a.lua", "return { foo = 3 }") + .addExtraFile("b.lua", "return { foo = 5 }") + .addExtraFile("c.lua", "return function(self, a) return a end") + .expectToEqual({ addResult: 3 + 5, callResult: "foo" }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1307 +test("lualib_module with parent directory import (#1307)", () => { + const projectDir = path.join(__dirname, "module-resolution", "project-with-dependency-with-same-file-names"); + const inputProject = path.join(projectDir, "tsconfig.json"); + + util.testProject(inputProject).setMainFileName(path.join(projectDir, "index.ts")).expectToEqual({ + // eslint-disable-next-line @typescript-eslint/naming-convention + BASE_CONSTANT: 123, + // eslint-disable-next-line @typescript-eslint/naming-convention + FEATURE_CONSTANT: 456, + }); +}); + +test("supports paths configuration", () => { + // Package root + const baseProjectPath = path.resolve(__dirname, "module-resolution", "paths-simple"); + // myprogram package + const projectPath = path.join(baseProjectPath, "myprogram"); + const projectTsConfig = path.join(projectPath, "tsconfig.json"); + const mainFile = path.join(projectPath, "main.ts"); + + const luaResult = util + .testProject(projectTsConfig) + .setMainFileName(mainFile) + .expectToHaveNoDiagnostics() + .getLuaResult(); + + expect(snapshotPaths(luaResult.transpiledFiles)).toMatchSnapshot(); + + // Bundle to have all files required to execute and check result + util.testProject(projectTsConfig) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual({ foo: 314, bar: 271 }); +}); + +test("supports complicated paths configuration", () => { + // Package root + const baseProjectPath = path.resolve(__dirname, "module-resolution", "paths-base-tsconfig"); + // myprogram package + const projectPath = path.join(baseProjectPath, "packages", "myprogram"); + const projectTsConfig = path.join(projectPath, "tsconfig.json"); + const mainFile = path.join(projectPath, "src", "main.ts"); + + const luaResult = util + .testProject(projectTsConfig) + .setMainFileName(mainFile) + .expectToHaveNoDiagnostics() + .getLuaResult(); + + expect(snapshotPaths(luaResult.transpiledFiles)).toMatchSnapshot(); + + // Bundle to have all files required to execute + util.testProject(projectTsConfig) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual({ foo: 314, bar: 271 }); +}); + +test("paths mapping wins over sibling project file (TS resolution order)", () => { + const baseProjectPath = path.resolve(__dirname, "module-resolution", "paths-vs-project-file"); + const projectPath = path.join(baseProjectPath, "program"); + const projectTsConfig = path.join(projectPath, "tsconfig.json"); + const mainFile = path.join(projectPath, "main.ts"); + + util.testProject(projectTsConfig) + .setMainFileName(mainFile) + .setOptions({ luaBundle: "bundle.lua", luaBundleEntry: mainFile }) + .expectToEqual({ value: "paths-mapped" }); +}); + +test("module resolution using plugin", () => { + const baseProjectPath = path.resolve(__dirname, "module-resolution", "project-with-module-resolution-plugin"); + const projectTsConfig = path.join(baseProjectPath, "tsconfig.json"); + const mainFile = path.join(baseProjectPath, "main.ts"); + + const testBuilder = util + .testProject(projectTsConfig) + .setMainFileName(mainFile) + .setOptions({ + luaPlugins: [ + { + name: path.join(__dirname, "./plugins/moduleResolution.ts"), + }, + ], + }) + .expectToHaveNoDiagnostics(); + + const luaResult = testBuilder.getLuaResult(); + + expect(luaResult.transpiledFiles).toHaveLength(3); + + testBuilder.expectToEqual({ result: ["foo", "absolutefoo"] }); +}); + +function snapshotPaths(files: tstl.TranspiledFile[]) { + return files.map(f => normalizeSlashes(f.outPath).split("module-resolution")[1]).sort(); +} diff --git a/test/transpile/module-resolution/paths-base-tsconfig/packages/mypackage/src/bar.ts b/test/transpile/module-resolution/paths-base-tsconfig/packages/mypackage/src/bar.ts new file mode 100644 index 000000000..fef4e9ed6 --- /dev/null +++ b/test/transpile/module-resolution/paths-base-tsconfig/packages/mypackage/src/bar.ts @@ -0,0 +1 @@ +export const bar = 271; diff --git a/test/transpile/module-resolution/paths-base-tsconfig/packages/mypackage/src/index.ts b/test/transpile/module-resolution/paths-base-tsconfig/packages/mypackage/src/index.ts new file mode 100644 index 000000000..c932fa9f5 --- /dev/null +++ b/test/transpile/module-resolution/paths-base-tsconfig/packages/mypackage/src/index.ts @@ -0,0 +1 @@ +export const foo = 314; diff --git a/test/transpile/module-resolution/paths-base-tsconfig/packages/mypackage/tsconfig.json b/test/transpile/module-resolution/paths-base-tsconfig/packages/mypackage/tsconfig.json new file mode 100644 index 000000000..5ac343336 --- /dev/null +++ b/test/transpile/module-resolution/paths-base-tsconfig/packages/mypackage/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "../../dist/packages/tstl-module" + }, + "include": ["./src/**/*.ts"] +} diff --git a/test/transpile/module-resolution/paths-base-tsconfig/packages/myprogram/src/main.ts b/test/transpile/module-resolution/paths-base-tsconfig/packages/myprogram/src/main.ts new file mode 100644 index 000000000..973b93922 --- /dev/null +++ b/test/transpile/module-resolution/paths-base-tsconfig/packages/myprogram/src/main.ts @@ -0,0 +1,4 @@ +import { foo } from "mypackage"; +import { bar } from "mypackage/bar"; + +export { foo, bar }; diff --git a/test/transpile/module-resolution/paths-base-tsconfig/packages/myprogram/tsconfig.json b/test/transpile/module-resolution/paths-base-tsconfig/packages/myprogram/tsconfig.json new file mode 100644 index 000000000..a0ba7c18e --- /dev/null +++ b/test/transpile/module-resolution/paths-base-tsconfig/packages/myprogram/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "outDir": "../../dist/packages/tstl-program" + }, + "include": ["./src/**/*.ts"] +} diff --git a/test/transpile/module-resolution/paths-base-tsconfig/tsconfig.base.json b/test/transpile/module-resolution/paths-base-tsconfig/tsconfig.base.json new file mode 100644 index 000000000..4d3934b0d --- /dev/null +++ b/test/transpile/module-resolution/paths-base-tsconfig/tsconfig.base.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "rootDir": ".", + "baseUrl": ".", + "ignoreDeprecations": "6.0", + "paths": { + "mypackage": ["packages/mypackage/src/index.ts"], + "mypackage/*": ["packages/mypackage/src/*"] + } + } +} diff --git a/test/transpile/module-resolution/paths-simple/mypackage/bar.ts b/test/transpile/module-resolution/paths-simple/mypackage/bar.ts new file mode 100644 index 000000000..fef4e9ed6 --- /dev/null +++ b/test/transpile/module-resolution/paths-simple/mypackage/bar.ts @@ -0,0 +1 @@ +export const bar = 271; diff --git a/test/transpile/module-resolution/paths-simple/mypackage/index.ts b/test/transpile/module-resolution/paths-simple/mypackage/index.ts new file mode 100644 index 000000000..c932fa9f5 --- /dev/null +++ b/test/transpile/module-resolution/paths-simple/mypackage/index.ts @@ -0,0 +1 @@ +export const foo = 314; diff --git a/test/transpile/module-resolution/paths-simple/mypackage/tsconfig.json b/test/transpile/module-resolution/paths-simple/mypackage/tsconfig.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/test/transpile/module-resolution/paths-simple/mypackage/tsconfig.json @@ -0,0 +1 @@ +{} diff --git a/test/transpile/module-resolution/paths-simple/myprogram/main.ts b/test/transpile/module-resolution/paths-simple/myprogram/main.ts new file mode 100644 index 000000000..e7687877a --- /dev/null +++ b/test/transpile/module-resolution/paths-simple/myprogram/main.ts @@ -0,0 +1,4 @@ +import { foo } from "myOtherPackage"; +import { bar } from "myOtherPackage/bar"; + +export { foo, bar }; diff --git a/test/transpile/module-resolution/paths-simple/myprogram/tsconfig.json b/test/transpile/module-resolution/paths-simple/myprogram/tsconfig.json new file mode 100644 index 000000000..a21584fac --- /dev/null +++ b/test/transpile/module-resolution/paths-simple/myprogram/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "rootDir": "..", + "outDir": "dist", + "paths": { + "myOtherPackage": ["../mypackage"], + "myOtherPackage/*": ["../mypackage/*"] + } + } +} diff --git a/test/transpile/module-resolution/paths-vs-project-file/other/alias.ts b/test/transpile/module-resolution/paths-vs-project-file/other/alias.ts new file mode 100644 index 000000000..b09331910 --- /dev/null +++ b/test/transpile/module-resolution/paths-vs-project-file/other/alias.ts @@ -0,0 +1 @@ +export const value = "paths-mapped"; diff --git a/test/transpile/module-resolution/paths-vs-project-file/program/alias.ts b/test/transpile/module-resolution/paths-vs-project-file/program/alias.ts new file mode 100644 index 000000000..565154b68 --- /dev/null +++ b/test/transpile/module-resolution/paths-vs-project-file/program/alias.ts @@ -0,0 +1 @@ +export const value = "project-file"; diff --git a/test/transpile/module-resolution/paths-vs-project-file/program/main.ts b/test/transpile/module-resolution/paths-vs-project-file/program/main.ts new file mode 100644 index 000000000..7b4bc6587 --- /dev/null +++ b/test/transpile/module-resolution/paths-vs-project-file/program/main.ts @@ -0,0 +1,3 @@ +import { value } from "alias"; + +export { value }; diff --git a/test/transpile/module-resolution/paths-vs-project-file/program/tsconfig.json b/test/transpile/module-resolution/paths-vs-project-file/program/tsconfig.json new file mode 100644 index 000000000..26c1a151f --- /dev/null +++ b/test/transpile/module-resolution/paths-vs-project-file/program/tsconfig.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "rootDir": "..", + "outDir": "dist", + "paths": { + "alias": ["../other/alias"] + } + } +} diff --git a/test/transpile/module-resolution/project-with-complicated-dependency/main.ts b/test/transpile/module-resolution/project-with-complicated-dependency/main.ts new file mode 100644 index 000000000..70d8123fa --- /dev/null +++ b/test/transpile/module-resolution/project-with-complicated-dependency/main.ts @@ -0,0 +1,7 @@ +import * as dependency1 from "dependency1"; + +export const otherFileResult = dependency1.otherFileFromDependency1(); +export const utilResult = dependency1.callUtil(); +export const otherFileUtil = dependency1.otherFileUtil(); +export const subsubresult = dependency1.subsubdirfileResult; +export const subdirwithInitResult = dependency1.subdirWithInitResult; diff --git a/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/index.d.ts b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/index.d.ts new file mode 100644 index 000000000..28f4f5baa --- /dev/null +++ b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/index.d.ts @@ -0,0 +1,6 @@ +/** @noSelfInFile */ +export declare function otherFileFromDependency1(): string; +export declare function callUtil(): string; +export declare function otherFileUtil(): string; +export const subsubdirfileResult: string; +export const subdirWithInitResult: string; \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/index.lua b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/index.lua new file mode 100644 index 000000000..c3838b1f4 --- /dev/null +++ b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/index.lua @@ -0,0 +1,18 @@ +local otherfile = require("otherfile") +local util = require("util") +local subdirfile = require("subdir.subdirfile") +local othersubdirfile = require("subdir.othersubdirfile") +local subsubdirfile = require("subdir.subsubdir.subsubdirfile") +local subdirwithinit = require("subdirwithinit") + +return { + otherFileFromDependency1 = otherfile.someFunc, + callUtil = function() + return util.utilf() + end, + otherFileUtil = otherfile.callUtil, + subdirfileResult = subdirfile.subdirfileResult, + othersubdirfileResult = othersubdirfile.othersubdirfileResult, + subsubdirfileResult = subsubdirfile.subsubresult, + subdirWithInitResult = subdirwithinit.a() +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/otherfile.lua b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/otherfile.lua new file mode 100644 index 000000000..0fb8719ac --- /dev/null +++ b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/otherfile.lua @@ -0,0 +1,10 @@ +local util = require("util") + +return { + someFunc = function() + return "someFunc from otherfile.lua" + end, + callUtil = function() + return util.utilf() + end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdir/othersubdirfile.lua b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdir/othersubdirfile.lua new file mode 100644 index 000000000..45e5c985f --- /dev/null +++ b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdir/othersubdirfile.lua @@ -0,0 +1,3 @@ +return { + result = "result from other subdirectory file" +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdir/subdirfile.lua b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdir/subdirfile.lua new file mode 100644 index 000000000..fe26400a1 --- /dev/null +++ b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdir/subdirfile.lua @@ -0,0 +1,5 @@ +local othersubdirfile = require("subdir.othersubdirfile") +return { + subdirfileResult = "result from subdirectory file!", + othersubdirfileResult = othersubdirfile.result +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdir/subsubdir/subsubdirfile.lua b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdir/subsubdir/subsubdirfile.lua new file mode 100644 index 000000000..581c8f858 --- /dev/null +++ b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdir/subsubdir/subsubdirfile.lua @@ -0,0 +1,3 @@ +return { + subsubresult = "result from subsub dir" +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdirwithinit/init.lua b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdirwithinit/init.lua new file mode 100644 index 000000000..f26612a6f --- /dev/null +++ b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/subdirwithinit/init.lua @@ -0,0 +1,3 @@ +return { + a = function() return "a" end +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/util.lua b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/util.lua new file mode 100644 index 000000000..e4e812406 --- /dev/null +++ b/test/transpile/module-resolution/project-with-complicated-dependency/node_modules/dependency1/util.lua @@ -0,0 +1,3 @@ +return { + utilf = function() return "util" end, +} \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-complicated-dependency/tsconfig.json b/test/transpile/module-resolution/project-with-complicated-dependency/tsconfig.json new file mode 100644 index 000000000..b76533290 --- /dev/null +++ b/test/transpile/module-resolution/project-with-complicated-dependency/tsconfig.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "strict": true, + "moduleResolution": "Node", + "target": "esnext", + "lib": ["esnext"], + "types": [], + "rootDir": "." + } +} diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/index.ts b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/index.ts new file mode 100644 index 000000000..0ab21dc4e --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/index.ts @@ -0,0 +1 @@ +export * from "mymodule"; diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/constants.lua b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/constants.lua new file mode 100644 index 000000000..de5917dd7 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/constants.lua @@ -0,0 +1,3 @@ +local ____exports = {} +____exports.BASE_CONSTANT = 123 +return ____exports diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/constants.lua b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/constants.lua new file mode 100644 index 000000000..9d3bd6110 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/constants.lua @@ -0,0 +1,3 @@ +local ____exports = {} +____exports.FEATURE_CONSTANT = 456 +return ____exports diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/feature.lua b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/feature.lua new file mode 100644 index 000000000..103496eaa --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/feature/feature.lua @@ -0,0 +1,8 @@ +local ____exports = {} +local ____constants = require("constants") +local BASE_CONSTANT = ____constants.BASE_CONSTANT +local ____constants = require("feature.constants") +local FEATURE_CONSTANT = ____constants.FEATURE_CONSTANT +____exports.BASE_CONSTANT = BASE_CONSTANT +____exports.FEATURE_CONSTANT = FEATURE_CONSTANT +return ____exports diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.d.ts b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.d.ts new file mode 100644 index 000000000..90401efad --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.d.ts @@ -0,0 +1,2 @@ +export declare const BASE_CONSTANT: number; +export declare const FEATURE_CONSTANT: number; diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.lua b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.lua new file mode 100644 index 000000000..4e57411d3 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/node_modules/mymodule/index.lua @@ -0,0 +1,10 @@ +local ____exports = {} +do + local ____export = require("feature.feature") + for ____exportKey, ____exportValue in pairs(____export) do + if ____exportKey ~= "default" then + ____exports[____exportKey] = ____exportValue + end + end +end +return ____exports diff --git a/test/transpile/module-resolution/project-with-dependency-with-same-file-names/tsconfig.json b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/tsconfig.json new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/test/transpile/module-resolution/project-with-dependency-with-same-file-names/tsconfig.json @@ -0,0 +1 @@ +{} diff --git a/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/absolutebar.lua b/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/absolutebar.lua new file mode 100644 index 000000000..35718ddea --- /dev/null +++ b/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/absolutebar.lua @@ -0,0 +1,5 @@ +local ____exports = {} +function ____exports.absolutefoo(self) + return "absolutefoo" +end +return ____exports \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/absolutefoo.d.ts b/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/absolutefoo.d.ts new file mode 100644 index 000000000..9df109b2e --- /dev/null +++ b/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/absolutefoo.d.ts @@ -0,0 +1 @@ +export declare function absolutefoo(): string; diff --git a/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/bar.lua b/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/bar.lua new file mode 100644 index 000000000..b821a15b5 --- /dev/null +++ b/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/bar.lua @@ -0,0 +1,5 @@ +local ____exports = {} +function ____exports.foo(self) + return "foo" +end +return ____exports \ No newline at end of file diff --git a/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/foo.d.ts b/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/foo.d.ts new file mode 100644 index 000000000..27e069e94 --- /dev/null +++ b/test/transpile/module-resolution/project-with-module-resolution-plugin/lua_sources/foo.d.ts @@ -0,0 +1 @@ +export declare function foo(): string; diff --git a/test/transpile/module-resolution/project-with-module-resolution-plugin/main.ts b/test/transpile/module-resolution/project-with-module-resolution-plugin/main.ts new file mode 100644 index 000000000..e3e6bc616 --- /dev/null +++ b/test/transpile/module-resolution/project-with-module-resolution-plugin/main.ts @@ -0,0 +1,3 @@ +import { foo } from "./lua_sources/foo"; +import { absolutefoo } from "./lua_sources/absolutefoo"; +export const result = [foo(), absolutefoo()]; diff --git a/test/transpile/module-resolution/project-with-module-resolution-plugin/tsconfig.json b/test/transpile/module-resolution/project-with-module-resolution-plugin/tsconfig.json new file mode 100644 index 000000000..06ed18c9f --- /dev/null +++ b/test/transpile/module-resolution/project-with-module-resolution-plugin/tsconfig.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "strict": true, + "target": "esnext", + "lib": ["esnext"], + "types": [] + } +} diff --git a/test/transpile/module-resolution/project-with-node-modules/tsconfig.json b/test/transpile/module-resolution/project-with-node-modules/tsconfig.json index 935b64af6..faa93f0e3 100644 --- a/test/transpile/module-resolution/project-with-node-modules/tsconfig.json +++ b/test/transpile/module-resolution/project-with-node-modules/tsconfig.json @@ -7,6 +7,7 @@ "target": "esnext", "lib": ["esnext"], "types": [], - "rootDir": "." + "rootDir": ".", + "noUncheckedSideEffectImports": false } } diff --git a/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/main.ts b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/main.ts new file mode 100644 index 000000000..3421911f2 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/main.ts @@ -0,0 +1,5 @@ +import { dependency1IndexFunc } from "dependency1/sub"; +import { dependency1OtherFileFunc } from "dependency1/sub/d1otherfile"; + +export const dependency1IndexResult = dependency1IndexFunc(); +export const dependency1OtherFileFuncResult = dependency1OtherFileFunc(); diff --git a/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/package.json b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/package.json new file mode 100644 index 000000000..c74b862a1 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/package.json @@ -0,0 +1,14 @@ +{ + "name": "dependency1", + "version": "0.0.1", + "exports": { + "./sub": { + "require": "./dist/index", + "types": "./dist/index.d.ts" + }, + "./sub/*": { + "require": "./dist/*", + "types": "./dist/*.d.ts" + } + } +} diff --git a/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/src/d1otherfile.ts b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/src/d1otherfile.ts new file mode 100644 index 000000000..3877a1c52 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/src/d1otherfile.ts @@ -0,0 +1,3 @@ +export function dependency1OtherFileFunc() { + return "dependency1OtherFileFunc in dependency1/d1otherfile"; +} diff --git a/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/src/index.ts b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/src/index.ts new file mode 100644 index 000000000..03cff47c5 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/src/index.ts @@ -0,0 +1,10 @@ +import { dependency1OtherFileFunc } from "./d1otherfile"; + +export function dependency1IndexFunc() { + return "function in dependency 1 index: " + dependency1OtherFileFunc(); +} + +export function squares(nums: number[]) { + // Require lualib functionality + return nums.map(n => n * n); +} diff --git a/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/tsconfig.json b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/tsconfig.json new file mode 100644 index 000000000..d6ffcdc0c --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/node_modules/dependency1/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "declaration": true, + "declarationMap": true, + "sourceMap": true, + "composite": true + }, + "tstl": { + "buildMode": "library" + } +} diff --git a/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/package.json b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/package.json new file mode 100644 index 000000000..b88dc4bc0 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/package.json @@ -0,0 +1,7 @@ +{ + "name": "app", + "version": "0.0.1", + "dependencies": { + "dependency1": "file:../dependency1-ts" + } +} diff --git a/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/tsconfig.json b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/tsconfig.json new file mode 100644 index 000000000..7b0969155 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tstl-library-has-exports-field/tsconfig.json @@ -0,0 +1,6 @@ +{ + "compilerOptions": { + "moduleResolution": "node16" + }, + "references": [{ "path": "./node_modules/dependency1" }] +} diff --git a/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/index.ts b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/index.ts index fc1cdddec..03cff47c5 100644 --- a/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/index.ts +++ b/test/transpile/module-resolution/project-with-tstl-library-modules/dependency1-ts/index.ts @@ -3,3 +3,8 @@ import { dependency1OtherFileFunc } from "./d1otherfile"; export function dependency1IndexFunc() { return "function in dependency 1 index: " + dependency1OtherFileFunc(); } + +export function squares(nums: number[]) { + // Require lualib functionality + return nums.map(n => n * n); +} diff --git a/test/transpile/module-resolution/project-with-tsx/dir/index.tsx b/test/transpile/module-resolution/project-with-tsx/dir/index.tsx new file mode 100644 index 000000000..20c981b8a --- /dev/null +++ b/test/transpile/module-resolution/project-with-tsx/dir/index.tsx @@ -0,0 +1,3 @@ +export function indexf() { + return "hello from dir/index.tsx"; +} diff --git a/test/transpile/module-resolution/project-with-tsx/main.tsx b/test/transpile/module-resolution/project-with-tsx/main.tsx new file mode 100644 index 000000000..d17e2a7e4 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tsx/main.tsx @@ -0,0 +1,5 @@ +import { f } from "./other"; +import { indexf } from "./dir"; + +export const result = f(); +export const indexResult = indexf(); diff --git a/test/transpile/module-resolution/project-with-tsx/other.tsx b/test/transpile/module-resolution/project-with-tsx/other.tsx new file mode 100644 index 000000000..514705822 --- /dev/null +++ b/test/transpile/module-resolution/project-with-tsx/other.tsx @@ -0,0 +1,3 @@ +export function f() { + return "hello from other.tsx"; +} diff --git a/test/transpile/module-resolution/project-with-tsx/tsconfig.json b/test/transpile/module-resolution/project-with-tsx/tsconfig.json new file mode 100644 index 000000000..b5fbd521b --- /dev/null +++ b/test/transpile/module-resolution/project-with-tsx/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "jsx": "react" + } +} diff --git a/test/transpile/paths.spec.ts b/test/transpile/paths.spec.ts new file mode 100644 index 000000000..f98b6e635 --- /dev/null +++ b/test/transpile/paths.spec.ts @@ -0,0 +1,160 @@ +import * as path from "path"; +import * as ts from "typescript"; +import { getEmitPath, getSourceDir } from "../../src"; +import * as util from "../util"; + +const cwd = process.cwd(); + +// Path for project tsconfig.json to resolve for +const configFilePath = path.join(cwd, "tsconfig.json"); + +describe("getSourceDir", () => { + test("with rootDir", () => { + const program = ts.createProgram(["main.ts", "src/otherfile.ts"], { configFilePath, rootDir: "src" }); + + // If rootdir is specified, rootDir is the sourceDir + expect(getSourceDir(program)).toBe(path.join(cwd, "src")); + }); + + test("without rootDir", () => { + const program = ts.createProgram(["main.ts", "src/otherfile.ts"], { configFilePath }); + + // If rootDir is not specified, root dir is where the config file is + expect(normalize(getSourceDir(program))).toBe(cwd); + }); + + test("without config file in src dir", () => { + const program = ts.createProgram([path.join(cwd, "src", "main.ts"), path.join(cwd, "src", "otherfile.ts")], {}); + + // getCommonSourceDirectory does not work right so mock it + jest.spyOn(program, "getCommonSourceDirectory").mockReturnValue(path.join(cwd, "src")); + + // If there is no config file, return the common source directory + expect(normalize(getSourceDir(program))).toBe(path.join(cwd, "src")); + }); +}); + +describe("getEmitPath", () => { + test("puts files next to input without options", () => { + const { transpiledFiles } = util.testModule`` + .setMainFileName("main.ts") + .addExtraFile("dir/extra.ts", "") + .expectToHaveNoDiagnostics() + .getLuaResult(); + + const fileNames = transpiledFiles.map(f => f.outPath); + expect(fileNames).toContain("main.lua"); + expect(fileNames).toContain(path.join("dir", "extra.lua")); + }); + + test("puts files in outdir", () => { + const outDir = path.join(cwd, "tstl-out"); + const { transpiledFiles } = util.testModule`` + .setMainFileName("main.ts") + .addExtraFile("dir/extra.ts", "") + .setOptions({ outDir }) + .expectToHaveNoDiagnostics() + .getLuaResult(); + + const fileNames = transpiledFiles.map(f => f.outPath); + expect(fileNames).toContain(path.join(outDir, "main.lua")); + expect(fileNames).toContain(path.join(outDir, "dir", "extra.lua")); + }); + + test("puts files from rootDir in outdir", () => { + const outDir = path.join(cwd, "tstl-out"); + const { transpiledFiles } = util.testModule`` + .setMainFileName("src/main.ts") + .addExtraFile("src/extra.ts", "") + .setOptions({ rootDir: "src", outDir }) + .expectToHaveNoDiagnostics() + .getLuaResult(); + + const fileNames = transpiledFiles.map(f => f.outPath); + expect(fileNames).toContain(path.join(outDir, "main.lua")); + expect(fileNames).toContain(path.join(outDir, "extra.lua")); + }); + + test("puts bundle relative to project root", () => { + const { transpiledFiles } = util.testModule`` + .setMainFileName("src/main.ts") + .addExtraFile("src/extra.ts", "") + .setOptions({ configFilePath, rootDir: "src", luaBundle: "out/bundle.lua", luaBundleEntry: "src/main.ts" }) + .expectToHaveNoDiagnostics() + .getLuaResult(); + + const fileNames = transpiledFiles.map(f => f.outPath); + expect(fileNames).toHaveLength(1); + expect(fileNames).toContain(path.join(cwd, "out", "bundle.lua")); + }); + + test("puts bundle relative to outdir", () => { + const { transpiledFiles } = util.testModule`` + .setMainFileName("src/main.ts") + .addExtraFile("src/extra.ts", "") + .setOptions({ + configFilePath, + rootDir: "src", + outDir: "out1", + luaBundle: "out2/bundle.lua", + luaBundleEntry: "src/main.ts", + }) + .expectToHaveNoDiagnostics() + .getLuaResult(); + + const fileNames = transpiledFiles.map(f => f.outPath); + expect(fileNames).toHaveLength(1); + expect(fileNames).toContain(path.join(cwd, "out1", "out2", "bundle.lua")); + }); + + test.each([".scar", "scar"])("uses config extension (%p)", extension => { + const { transpiledFiles } = util.testModule`` + .setMainFileName("main.ts") + .addExtraFile("dir/extra.ts", "") + .setOptions({ extension }) + .expectToHaveNoDiagnostics() + .getLuaResult(); + + const fileNames = transpiledFiles.map(f => f.outPath); + expect(fileNames).toContain("main.scar"); + expect(fileNames).toContain(path.join("dir", "extra.scar")); + }); + + test("bundle with different extension", () => { + const { transpiledFiles } = util.testModule`` + .setMainFileName("src/main.ts") + .addExtraFile("src/extra.ts", "") + .setOptions({ + configFilePath, + rootDir: "src", + outDir: "out1", + luaBundle: "out2/bundle.scar", + luaBundleEntry: "src/main.ts", + }) + .expectToHaveNoDiagnostics() + .getLuaResult(); + + const fileNames = transpiledFiles.map(f => f.outPath); + expect(fileNames).toHaveLength(1); + expect(fileNames).toContain(path.join(cwd, "out1", "out2", "bundle.scar")); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1540 + test("puts files next to their source if no config is given (#1540)", () => { + const file1 = path.join("src", "main.ts"); + const file2 = path.join("src", "otherfile.ts"); + const file3 = path.join("src", "nested", "nestedfile.ts"); + const program = ts.createProgram([file1, file2, file3], { configFilePath }); + + // If rootDir is not specified, root dir is where the config file is + const configRoot = path.dirname(configFilePath); + const replaceExtension = (f: string) => f.replace(/\.ts$/, ".lua"); + expect(getEmitPath(file1, program)).toBe(replaceExtension(path.join(configRoot, file1))); + expect(getEmitPath(file2, program)).toBe(replaceExtension(path.join(configRoot, file2))); + expect(getEmitPath(file3, program)).toBe(replaceExtension(path.join(configRoot, file3))); + }); +}); + +function normalize(path: string) { + return path.endsWith("/") ? path.slice(0, path.length - 1) : path; +} diff --git a/test/transpile/plugins/__snapshots__/plugins.spec.ts.snap b/test/transpile/plugins/__snapshots__/plugins.spec.ts.snap index 9902416ab..7e3be754d 100644 --- a/test/transpile/plugins/__snapshots__/plugins.spec.ts.snap +++ b/test/transpile/plugins/__snapshots__/plugins.spec.ts.snap @@ -5,11 +5,11 @@ exports[`statement comments 1`] = ` function ____exports.__main(self) --This comment on variable declaration was added by a plugin! --- This one too! - local foo = \\"bar\\" + local foo = "bar" -- This trailing comment was also added by a plugin! --- Example luadoc comment ---@param paramName ParamClass - foo = \\"baz\\" + foo = "baz" --[[ This plugin can also (kinda) create multiline comments. -- Line 2 --]] diff --git a/test/transpile/plugins/add-comments.ts b/test/transpile/plugins/add-comments.ts index 9beb01432..2e9c56a5f 100644 --- a/test/transpile/plugins/add-comments.ts +++ b/test/transpile/plugins/add-comments.ts @@ -58,5 +58,4 @@ const plugin: tstl.Plugin = { }, }; -// eslint-disable-next-line import/no-default-export export default plugin; diff --git a/test/transpile/plugins/afterEmit.ts b/test/transpile/plugins/afterEmit.ts new file mode 100644 index 000000000..277258f25 --- /dev/null +++ b/test/transpile/plugins/afterEmit.ts @@ -0,0 +1,23 @@ +import * as ts from "typescript"; +import * as tstl from "../../../src"; + +const plugin: tstl.Plugin = { + afterEmit(program: ts.Program, options: tstl.CompilerOptions, emitHost: tstl.EmitHost, result: tstl.EmitFile[]) { + void program; + void options; + void emitHost; + void result; + + const diagnostic = { + category: ts.DiagnosticCategory.Message, + messageText: "After emit diagnostic message!", + code: 1234, + file: program.getSourceFiles()[0], + start: undefined, + length: undefined, + } satisfies ts.Diagnostic; + return [diagnostic]; + }, +}; + +export default plugin; diff --git a/test/transpile/plugins/afterPrint.ts b/test/transpile/plugins/afterPrint.ts new file mode 100644 index 000000000..cb548218b --- /dev/null +++ b/test/transpile/plugins/afterPrint.ts @@ -0,0 +1,21 @@ +import * as ts from "typescript"; +import * as tstl from "../../../src"; + +const plugin: tstl.Plugin = { + afterPrint( + program: ts.Program, + options: tstl.CompilerOptions, + emitHost: tstl.EmitHost, + result: tstl.ProcessedFile[] + ) { + void program; + void options; + void emitHost; + + for (const file of result) { + file.code = "-- Comment added by afterPrint plugin\n" + file.code; + } + }, +}; + +export default plugin; diff --git a/test/transpile/plugins/arguments.ts b/test/transpile/plugins/arguments.ts index f6aa2ba6e..e90afae3b 100644 --- a/test/transpile/plugins/arguments.ts +++ b/test/transpile/plugins/arguments.ts @@ -6,7 +6,6 @@ interface Options { option: boolean; } -// eslint-disable-next-line import/no-default-export export default function plugin(options: Options): tstl.Plugin { return { visitors: { diff --git a/test/transpile/plugins/beforeEmit.ts b/test/transpile/plugins/beforeEmit.ts new file mode 100644 index 000000000..2deb029e4 --- /dev/null +++ b/test/transpile/plugins/beforeEmit.ts @@ -0,0 +1,16 @@ +import * as ts from "typescript"; +import * as tstl from "../../../src"; + +const plugin: tstl.Plugin = { + beforeEmit(program: ts.Program, options: tstl.CompilerOptions, emitHost: tstl.EmitHost, result: tstl.EmitFile[]) { + void program; + void options; + void emitHost; + + for (const file of result) { + file.code = "-- Comment added by beforeEmit plugin\n" + file.code; + } + }, +}; + +export default plugin; diff --git a/test/transpile/plugins/beforeTransform.ts b/test/transpile/plugins/beforeTransform.ts new file mode 100644 index 000000000..c93c20f91 --- /dev/null +++ b/test/transpile/plugins/beforeTransform.ts @@ -0,0 +1,14 @@ +import * as ts from "typescript"; +import * as tstl from "../../../src"; + +const plugin: tstl.Plugin = { + beforeTransform(program: ts.Program, options: tstl.CompilerOptions, emitHost: tstl.EmitHost) { + void program; + void emitHost; + + // Modify settings + options.outDir = "plugin/beforeTransform/outdir"; + }, +}; + +export default plugin; diff --git a/test/transpile/plugins/moduleResolution.ts b/test/transpile/plugins/moduleResolution.ts new file mode 100644 index 000000000..705345a5b --- /dev/null +++ b/test/transpile/plugins/moduleResolution.ts @@ -0,0 +1,22 @@ +import path = require("path"); +import type * as tstl from "../../../src"; + +const plugin: tstl.Plugin = { + moduleResolution(moduleIdentifier) { + if (moduleIdentifier.includes("absolutefoo")) { + return path.join( + path.dirname(__dirname), + "module-resolution", + "project-with-module-resolution-plugin", + "lua_sources", + "absolutebar.lua" + ); + } + + if (moduleIdentifier.includes("foo")) { + return moduleIdentifier.replace("foo", "bar"); + } + }, +}; + +export default plugin; diff --git a/test/transpile/plugins/plugins.spec.ts b/test/transpile/plugins/plugins.spec.ts index eaaee3dca..ed25ae748 100644 --- a/test/transpile/plugins/plugins.spec.ts +++ b/test/transpile/plugins/plugins.spec.ts @@ -1,10 +1,35 @@ import * as path from "path"; import * as util from "../../util"; +import { Plugin } from "../../../src/transpilation/plugins"; +import * as ts from "typescript"; test("printer", () => { util.testModule`` .setOptions({ luaPlugins: [{ name: path.join(__dirname, "printer.ts") }] }) - .tap(builder => expect(builder.getMainLuaCodeChunk()).toMatch("Plugin")); + .tap(builder => expect(builder.getMainLuaCodeChunk()).toMatch("-- Custom printer plugin:")); +}); + +test("printer in bundle", () => { + const { transpiledFiles } = util.testBundle` + import "./otherfile"; + + const foo = "foo"; + ` + .addExtraFile( + "otherfile.ts", + ` + const bar = "bar"; + ` + ) + .setOptions({ luaPlugins: [{ name: path.join(__dirname, "printer.ts") }] }) + .expectToHaveNoDiagnostics() + .getLuaResult(); + + expect(transpiledFiles).toHaveLength(1); + const lua = transpiledFiles[0].lua!; + + expect(lua).toContain("-- Custom printer plugin: main.lua"); + expect(lua).toContain("-- Custom printer plugin: otherfile.lua"); }); test("visitor", () => { @@ -55,8 +80,164 @@ test.each(["namespace", "module"])("%s with TS transformer plugin", moduleOrName return false; } } - ` + ` ) .setOptions({ plugins: [{ transform: path.join(__dirname, "transformer-plugin.ts") }] }) .expectNoExecutionError(); }); + +test("beforeTransform plugin", () => { + const { transpiledFiles } = util.testModule` + console.log("Hello, World!"); + ` + .setOptions({ luaPlugins: [{ name: path.join(__dirname, "beforeTransform.ts") }] }) + .getLuaResult(); + + expect(transpiledFiles).toHaveLength(1); + // Expect emitted to output path set by the plugin + expect(transpiledFiles[0].outPath).toContain(path.join("plugin", "beforeTransform", "outdir")); +}); + +test("afterPrint plugin", () => { + const { transpiledFiles } = util.testModule` + console.log("Hello, World!"); + ` + .addExtraFile( + "extrafile.ts", + ` + console.log("Hello, Mars!"); + ` + ) + .setOptions({ luaPlugins: [{ name: path.join(__dirname, "afterPrint.ts") }] }) + .getLuaResult(); + + expect(transpiledFiles).toHaveLength(2); + for (const f of transpiledFiles) { + // Expect plugin inserted extra lua at start of file + expect(f.lua).toContain("-- Comment added by afterPrint plugin"); + } +}); + +test("beforeEmit plugin", () => { + const { transpiledFiles } = util.testModule` + console.log("Hello, World!"); + [].push(1,2,3); // Use lualib code + ` + .addExtraFile( + "extrafile.ts", + ` + console.log("Hello, Mars!"); + ` + ) + .setOptions({ luaPlugins: [{ name: path.join(__dirname, "beforeEmit.ts") }] }) + .getLuaResult(); + + // 2 input files + 1 lualib_bundle + expect(transpiledFiles).toHaveLength(3); + expect(transpiledFiles.find(f => f.outPath.endsWith("lualib_bundle.lua"))).toBeDefined(); + for (const f of transpiledFiles) { + // Expect plugin inserted extra lua at start of all files including lualib bundle + expect(f.lua).toContain("-- Comment added by beforeEmit plugin"); + } +}); + +test("beforeEmit plugin bundle", () => { + const { transpiledFiles } = util.testBundle` + console.log("Hello, World!"); + [].push(1,2,3); // Use lualib code + ` + .addExtraFile( + "extrafile.ts", + ` + console.log("Hello, Mars!"); + ` + ) + .setOptions({ luaPlugins: [{ name: path.join(__dirname, "beforeEmit.ts") }] }) + .getLuaResult(); + + // 1 lua bundle output + expect(transpiledFiles).toHaveLength(1); + for (const f of transpiledFiles) { + // Expect bundle to be affected by plugin + expect(f.lua).toContain("-- Comment added by beforeEmit plugin"); + } +}); + +test("afterEmit plugin", () => { + const { diagnostics } = util.testModule` + console.log("Hello, World!"); + [].push(1,2,3); // Use lualib code + ` + .addExtraFile( + "extrafile.ts", + ` + console.log("Hello, Mars!"); + ` + ) + .setOptions({ luaPlugins: [{ name: path.join(__dirname, "afterEmit.ts") }] }) + .getLuaResult(); + + // Expect to see the diagnostic returned by the plugin in the output + const diagnostic = diagnostics.find(d => d.code === 1234); + expect(diagnostic).toBeDefined(); + expect(diagnostic?.category).toBe(ts.DiagnosticCategory.Message); + expect(diagnostic?.messageText).toContain("After emit"); +}); + +test("in memory plugin", () => { + const { diagnostics } = util.testModule`` + .setOptions({ + luaPlugins: [ + { + plugin: { + afterEmit(program: ts.Program) { + return [ + { + category: ts.DiagnosticCategory.Message, + messageText: "In memory plugin diagnostic message!", + code: 1234, + file: program.getSourceFiles()[0], + start: undefined, + length: undefined, + } satisfies ts.Diagnostic, + ]; + }, + } satisfies Plugin, + }, + ], + }) + .getLuaResult(); + + expect(diagnostics).toHaveLength(1); + expect(diagnostics[0].code).toBe(1234); +}); + +test("in memory plugin with factory", () => { + const { diagnostics } = util.testModule`` + .setOptions({ + luaPlugins: [ + { + code: 1234, + plugin: options => + ({ + afterEmit(program: ts.Program) { + return [ + { + category: ts.DiagnosticCategory.Message, + messageText: "In memory plugin diagnostic message!", + code: options.code, + file: program.getSourceFiles()[0], + start: undefined, + length: undefined, + } satisfies ts.Diagnostic, + ]; + }, + } satisfies Plugin), + }, + ], + }) + .getLuaResult(); + + expect(diagnostics).toHaveLength(1); + expect(diagnostics[0].code).toBe(1234); +}); diff --git a/test/transpile/plugins/printer.ts b/test/transpile/plugins/printer.ts index 7ae55ca01..37b6d84f7 100644 --- a/test/transpile/plugins/printer.ts +++ b/test/transpile/plugins/printer.ts @@ -1,12 +1,23 @@ +import { SourceNode } from "source-map"; import * as tstl from "../../../src"; +class CustomPrinter extends tstl.LuaPrinter { + /* Override printFile */ + protected printFile(file: tstl.File): SourceNode { + const originalResult = super.printFile(file); + // Add header comment at the top of the file + return this.createSourceNode(file, [`-- Custom printer plugin: ${this.luaFile}\n`, originalResult]); + } + + /* Override printBoolean */ + public printBooleanLiteral(expression: tstl.BooleanLiteral): SourceNode { + // Print any boolean as 'true' + return this.createSourceNode(expression, "true"); + } +} + const plugin: tstl.Plugin = { - printer(program, emitHost, fileName, ...args) { - const result = new tstl.LuaPrinter(emitHost, program, fileName).print(...args); - result.code = `-- Plugin\n${result.code}`; - return result; - }, + printer: (program, emitHost, fileName, file) => new CustomPrinter(emitHost, program, fileName).print(file), }; -// eslint-disable-next-line import/no-default-export export default plugin; diff --git a/test/transpile/plugins/visitor-super.ts b/test/transpile/plugins/visitor-super.ts index 9a8bee5f1..3ece9458b 100644 --- a/test/transpile/plugins/visitor-super.ts +++ b/test/transpile/plugins/visitor-super.ts @@ -15,5 +15,4 @@ const plugin: tstl.Plugin = { }, }; -// eslint-disable-next-line import/no-default-export export default plugin; diff --git a/test/transpile/plugins/visitor.ts b/test/transpile/plugins/visitor.ts index 507b9fa3d..15da18ded 100644 --- a/test/transpile/plugins/visitor.ts +++ b/test/transpile/plugins/visitor.ts @@ -7,5 +7,4 @@ const plugin: tstl.Plugin = { }, }; -// eslint-disable-next-line import/no-default-export export default plugin; diff --git a/test/transpile/project.spec.ts b/test/transpile/project.spec.ts index 965c834d7..49234975b 100644 --- a/test/transpile/project.spec.ts +++ b/test/transpile/project.spec.ts @@ -1,4 +1,5 @@ import * as path from "path"; +import { normalizeSlashes } from "../../src/utils"; import * as util from "../util"; test("should transpile", () => { @@ -13,3 +14,22 @@ test("should transpile", () => { transpiledFiles.map(f => ({ filePath: path.relative(projectDir, f.outPath), lua: f.lua })) ).toMatchSnapshot(); }); + +test("should give verbose output", () => { + // Capture console logs + const consoleLogs: string[] = []; + const originalLog = console.log; + console.log = (...args: any[]) => { + consoleLogs.push(args.map(a => a.toString().replace(normalizeSlashes(process.cwd()), "")).join(",")); + }; + + const projectDir = path.join(__dirname, "project"); + util.testProject(path.join(projectDir, "tsconfig.json")) + .setMainFileName(path.join(projectDir, "index.ts")) + .setOptions({ tstlVerbose: true }) + .expectToHaveNoDiagnostics(); + + console.log = originalLog; + + expect(consoleLogs).toMatchSnapshot(); +}); diff --git a/test/transpile/resolve-plugin/ts.ts b/test/transpile/resolve-plugin/ts.ts index 60757f0a4..ff3177bab 100644 --- a/test/transpile/resolve-plugin/ts.ts +++ b/test/transpile/resolve-plugin/ts.ts @@ -1,2 +1 @@ -// eslint-disable-next-line import/no-default-export export default true; diff --git a/test/transpile/transformers/fixtures.ts b/test/transpile/transformers/fixtures.ts index a69829d5d..4bb890135 100644 --- a/test/transpile/transformers/fixtures.ts +++ b/test/transpile/transformers/fixtures.ts @@ -6,22 +6,25 @@ import { visitAndReplace } from "./utils"; export const program = (program: ts.Program, options: { value: any }): ts.TransformerFactory => checker(program.getTypeChecker(), options); -export const config = ({ value }: { value: any }): ts.TransformerFactory => context => file => - visitAndReplace(context, file, node => { - if (!ts.isReturnStatement(node)) return; - return ts.factory.updateReturnStatement(node, value ? ts.factory.createTrue() : ts.factory.createFalse()); - }); +export const config = + ({ value }: { value: any }): ts.TransformerFactory => + context => + file => + visitAndReplace(context, file, node => { + if (!ts.isReturnStatement(node)) return; + return ts.factory.updateReturnStatement(node, value ? ts.factory.createTrue() : ts.factory.createFalse()); + }); -export const checker = ( - checker: ts.TypeChecker, - { value }: { value: any } -): ts.TransformerFactory => context => file => - visitAndReplace(context, file, node => { - if (!ts.isReturnStatement(node) || !node.expression) return; - const type = checker.getTypeAtLocation(node.expression); - if ((type.flags & ts.TypeFlags.BooleanLiteral) === 0) return; - return ts.factory.updateReturnStatement(node, value ? ts.factory.createTrue() : ts.factory.createFalse()); - }); +export const checker = + (checker: ts.TypeChecker, { value }: { value: any }): ts.TransformerFactory => + context => + file => + visitAndReplace(context, file, node => { + if (!ts.isReturnStatement(node) || !node.expression) return; + const type = checker.getTypeAtLocation(node.expression); + if ((type.flags & ts.TypeFlags.BooleanLiteral) === 0) return; + return ts.factory.updateReturnStatement(node, value ? ts.factory.createTrue() : ts.factory.createFalse()); + }); export const raw: ts.TransformerFactory = context => file => visitAndReplace(context, file, node => { @@ -29,12 +32,13 @@ export const raw: ts.TransformerFactory = context => file => return ts.factory.updateReturnStatement(node, ts.factory.createTrue()); }); -export const compilerOptions = ( - options: tstl.CompilerOptions -): ts.TransformerFactory => context => file => { - assert(options.plugins?.length === 1); - return visitAndReplace(context, file, node => { - if (!ts.isReturnStatement(node)) return; - return ts.factory.updateReturnStatement(node, ts.factory.createTrue()); - }); -}; +export const compilerOptions = + (options: tstl.CompilerOptions): ts.TransformerFactory => + context => + file => { + assert.ok(options.plugins?.length === 1); + return visitAndReplace(context, file, node => { + if (!ts.isReturnStatement(node)) return; + return ts.factory.updateReturnStatement(node, ts.factory.createTrue()); + }); + }; diff --git a/test/transpile/transformers/transformers.spec.ts b/test/transpile/transformers/transformers.spec.ts index 02290d9d1..34dd9dc15 100644 --- a/test/transpile/transformers/transformers.spec.ts +++ b/test/transpile/transformers/transformers.spec.ts @@ -35,3 +35,22 @@ describe("factory types", () => { .expectToEqual(true); }); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1464 +test("transformer with switch does not break (#1464)", () => { + util.testFunction` + const foo: number = 3; + switch (foo) { + case 2: { + return 10; + } + case 3: { + return false; + } + } + ` + .setOptions({ + plugins: [{ transform: path.join(__dirname, "fixtures.ts"), import: "program", value: true }], + }) + .expectToEqual(true); +}); diff --git a/test/transpile/transformers/utils.ts b/test/transpile/transformers/utils.ts index 3e1503ba0..819454530 100644 --- a/test/transpile/transformers/utils.ts +++ b/test/transpile/transformers/utils.ts @@ -2,5 +2,5 @@ import * as ts from "typescript"; export function visitAndReplace(context: ts.TransformationContext, node: T, visitor: ts.Visitor): T { const visit: ts.Visitor = node => visitor(node) ?? ts.visitEachChild(node, visit, context); - return ts.visitNode(node, visit); + return ts.visitEachChild(node, visit, context); } diff --git a/test/unit/__snapshots__/bundle.spec.ts.snap b/test/unit/__snapshots__/bundle.spec.ts.snap index 84c10f3d5..852b6a668 100644 --- a/test/unit/__snapshots__/bundle.spec.ts.snap +++ b/test/unit/__snapshots__/bundle.spec.ts.snap @@ -1,6 +1,8 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`LuaLibImportKind.Inline generates a warning: diagnostics 1`] = `"warning TSTL: Using 'luaBundle' with 'luaLibImport: \\"inline\\"' might generate duplicate code. It is recommended to use 'luaLibImport: \\"require\\"'."`; +exports[`LuaLibImportKind.Inline generates a warning: diagnostics 1`] = `"warning TSTL: Using 'luaBundle' with 'luaLibImport: "inline"' might generate duplicate code. It is recommended to use 'luaLibImport: "require"'."`; + +exports[`bundling not allowed for buildmode library: diagnostics 1`] = `"error TSTL: Cannot bundle projects with "buildmode": "library". Projects including the library can still bundle (which will include external library files)."`; exports[`luaEntry doesn't exist: diagnostics 1`] = `"error TSTL: Could not find bundle entry point 'entry.ts'. It should be a file in the project."`; diff --git a/test/unit/__snapshots__/comments.spec.ts.snap b/test/unit/__snapshots__/comments.spec.ts.snap new file mode 100644 index 000000000..55052f60e --- /dev/null +++ b/test/unit/__snapshots__/comments.spec.ts.snap @@ -0,0 +1,50 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`JSDoc is copied on a function with tags: code 1`] = ` +"--- This is a function comment. +-- It has multiple lines. +-- +-- @param arg1 This is the first argument. +-- @param arg2 This is the second argument. +-- @returns A very powerful string. +function foo(self, arg1, arg2) + return "bar" +end" +`; + +exports[`JSDoc is copied on a function with tags: diagnostics 1`] = `""`; + +exports[`JSDoc is copied on a variable: code 1`] = ` +"--- This is a variable comment. +foo = 123" +`; + +exports[`JSDoc is copied on a variable: diagnostics 1`] = `""`; + +exports[`Multi-line JSDoc with one block is copied on a function: code 1`] = ` +"--- This is a function comment. +-- It has more than one line. +function foo(self) +end" +`; + +exports[`Multi-line JSDoc with one block is copied on a function: diagnostics 1`] = `""`; + +exports[`Multi-line JSDoc with two blocks is copied on a function: code 1`] = ` +"--- This is a function comment. +-- It has more than one line. +-- +-- It also has more than one block. +function foo(self) +end" +`; + +exports[`Multi-line JSDoc with two blocks is copied on a function: diagnostics 1`] = `""`; + +exports[`Single-line JSDoc is copied on a function: code 1`] = ` +"--- This is a function comment. +function foo(self) +end" +`; + +exports[`Single-line JSDoc is copied on a function: diagnostics 1`] = `""`; diff --git a/test/unit/__snapshots__/enum.spec.ts.snap b/test/unit/__snapshots__/enum.spec.ts.snap new file mode 100644 index 000000000..181b66a45 --- /dev/null +++ b/test/unit/__snapshots__/enum.spec.ts.snap @@ -0,0 +1,12 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`enum nested in namespace 1`] = ` +"A = A or ({}) +do + A.TestEnum = A.TestEnum or ({}) + A.TestEnum.B = 0 + A.TestEnum[A.TestEnum.B] = "B" + A.TestEnum.C = 1 + A.TestEnum[A.TestEnum.C] = "C" +end" +`; diff --git a/test/unit/__snapshots__/expressions.spec.ts.snap b/test/unit/__snapshots__/expressions.spec.ts.snap index 03bb46230..7eeb5961a 100644 --- a/test/unit/__snapshots__/expressions.spec.ts.snap +++ b/test/unit/__snapshots__/expressions.spec.ts.snap @@ -14,13 +14,13 @@ return ____exports" exports[`Binary expressions ordering parentheses ("1*(3+4*2)") 1`] = ` "local ____exports = {} -____exports.__result = 1 * (3 + (4 * 2)) +____exports.__result = 1 * (3 + 4 * 2) return ____exports" `; exports[`Binary expressions ordering parentheses ("1*30+4") 1`] = ` "local ____exports = {} -____exports.__result = (1 * 30) + 4 +____exports.__result = 1 * 30 + 4 return ____exports" `; @@ -36,6 +36,116 @@ ____exports.__result = 10 - (4 + 5) return ____exports" `; +exports[`Bitop [5.0] ("~a"): code 1`] = ` +"local ____exports = {} +____exports.__result = bit.bnot(a) +return ____exports" +`; + +exports[`Bitop [5.0] ("~a"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`Bitop [5.0] ("a&=b"): code 1`] = ` +"local ____exports = {} +a = bit.band(a, b) +____exports.__result = a +return ____exports" +`; + +exports[`Bitop [5.0] ("a&=b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`Bitop [5.0] ("a&b"): code 1`] = ` +"local ____exports = {} +____exports.__result = bit.band(a, b) +return ____exports" +`; + +exports[`Bitop [5.0] ("a&b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`Bitop [5.0] ("a<<=b"): code 1`] = ` +"local ____exports = {} +a = bit.lshift(a, b) +____exports.__result = a +return ____exports" +`; + +exports[`Bitop [5.0] ("a<<=b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`Bitop [5.0] ("a<>=b"): code 1`] = ` +"local ____exports = {} +a = bit.arshift(a, b) +____exports.__result = a +return ____exports" +`; + +exports[`Bitop [5.0] ("a>>=b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`Bitop [5.0] ("a>>>=b"): code 1`] = ` +"local ____exports = {} +a = bit.rshift(a, b) +____exports.__result = a +return ____exports" +`; + +exports[`Bitop [5.0] ("a>>>=b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`Bitop [5.0] ("a>>>b"): code 1`] = ` +"local ____exports = {} +____exports.__result = bit.rshift(a, b) +return ____exports" +`; + +exports[`Bitop [5.0] ("a>>>b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`Bitop [5.0] ("a>>b"): code 1`] = ` +"local ____exports = {} +____exports.__result = bit.arshift(a, b) +return ____exports" +`; + +exports[`Bitop [5.0] ("a>>b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`Bitop [5.0] ("a^=b"): code 1`] = ` +"local ____exports = {} +a = bit.bxor(a, b) +____exports.__result = a +return ____exports" +`; + +exports[`Bitop [5.0] ("a^=b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`Bitop [5.0] ("a^b"): code 1`] = ` +"local ____exports = {} +____exports.__result = bit.bxor(a, b) +return ____exports" +`; + +exports[`Bitop [5.0] ("a^b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`Bitop [5.0] ("a|=b"): code 1`] = ` +"local ____exports = {} +a = bit.bor(a, b) +____exports.__result = a +return ____exports" +`; + +exports[`Bitop [5.0] ("a|=b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`Bitop [5.0] ("a|b"): code 1`] = ` +"local ____exports = {} +____exports.__result = bit.bor(a, b) +return ____exports" +`; + +exports[`Bitop [5.0] ("a|b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitwise operations is/are not supported for target Lua 5.0."`; + exports[`Bitop [5.1] ("~a"): code 1`] = ` "local ____exports = {} ____exports.__result = bit.bnot(a) @@ -46,10 +156,8 @@ exports[`Bitop [5.1] ("~a"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bitw exports[`Bitop [5.1] ("a&=b"): code 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.band(a, b) - return a -end)() +a = bit.band(a, b) +____exports.__result = a return ____exports" `; @@ -65,10 +173,8 @@ exports[`Bitop [5.1] ("a&b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bit exports[`Bitop [5.1] ("a<<=b"): code 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.lshift(a, b) - return a -end)() +a = bit.lshift(a, b) +____exports.__result = a return ____exports" `; @@ -84,10 +190,8 @@ exports[`Bitop [5.1] ("a<>=b"): code 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.arshift(a, b) - return a -end)() +a = bit.arshift(a, b) +____exports.__result = a return ____exports" `; @@ -95,10 +199,8 @@ exports[`Bitop [5.1] ("a>>=b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: B exports[`Bitop [5.1] ("a>>>=b"): code 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.rshift(a, b) - return a -end)() +a = bit.rshift(a, b) +____exports.__result = a return ____exports" `; @@ -122,10 +224,8 @@ exports[`Bitop [5.1] ("a>>b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bi exports[`Bitop [5.1] ("a^=b"): code 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.bxor(a, b) - return a -end)() +a = bit.bxor(a, b) +____exports.__result = a return ____exports" `; @@ -141,10 +241,8 @@ exports[`Bitop [5.1] ("a^b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Bit exports[`Bitop [5.1] ("a|=b"): code 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.bor(a, b) - return a -end)() +a = bit.bor(a, b) +____exports.__result = a return ____exports" `; @@ -166,10 +264,8 @@ return ____exports" exports[`Bitop [5.2] ("a&=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit32.band(a, b) - return a -end)() +a = bit32.band(a, b) +____exports.__result = a return ____exports" `; @@ -181,10 +277,8 @@ return ____exports" exports[`Bitop [5.2] ("a<<=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit32.lshift(a, b) - return a -end)() +a = bit32.lshift(a, b) +____exports.__result = a return ____exports" `; @@ -196,19 +290,15 @@ return ____exports" exports[`Bitop [5.2] ("a>>=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit32.arshift(a, b) - return a -end)() +a = bit32.arshift(a, b) +____exports.__result = a return ____exports" `; exports[`Bitop [5.2] ("a>>>=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit32.rshift(a, b) - return a -end)() +a = bit32.rshift(a, b) +____exports.__result = a return ____exports" `; @@ -226,10 +316,8 @@ return ____exports" exports[`Bitop [5.2] ("a^=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit32.bxor(a, b) - return a -end)() +a = bit32.bxor(a, b) +____exports.__result = a return ____exports" `; @@ -241,10 +329,8 @@ return ____exports" exports[`Bitop [5.2] ("a|=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit32.bor(a, b) - return a -end)() +a = bit32.bor(a, b) +____exports.__result = a return ____exports" `; @@ -262,10 +348,8 @@ return ____exports" exports[`Bitop [5.3] ("a&=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a & b - return a -end)() +a = a & b +____exports.__result = a return ____exports" `; @@ -277,10 +361,8 @@ return ____exports" exports[`Bitop [5.3] ("a<<=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a << b - return a -end)() +a = a << b +____exports.__result = a return ____exports" `; @@ -292,25 +374,21 @@ return ____exports" exports[`Bitop [5.3] ("a>>>=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a >> b - return a -end)() +a = (a & 4294967295) >> b +____exports.__result = a return ____exports" `; exports[`Bitop [5.3] ("a>>>b") 1`] = ` "local ____exports = {} -____exports.__result = a >> b +____exports.__result = (a & 4294967295) >> b return ____exports" `; exports[`Bitop [5.3] ("a^=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a ~ b - return a -end)() +a = a ~ b +____exports.__result = a return ____exports" `; @@ -322,10 +400,8 @@ return ____exports" exports[`Bitop [5.3] ("a|=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a | b - return a -end)() +a = a | b +____exports.__result = a return ____exports" `; @@ -343,10 +419,8 @@ return ____exports" exports[`Bitop [5.4] ("a&=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a & b - return a -end)() +a = a & b +____exports.__result = a return ____exports" `; @@ -358,10 +432,8 @@ return ____exports" exports[`Bitop [5.4] ("a<<=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a << b - return a -end)() +a = a << b +____exports.__result = a return ____exports" `; @@ -373,25 +445,21 @@ return ____exports" exports[`Bitop [5.4] ("a>>>=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a >> b - return a -end)() +a = (a & 4294967295) >> b +____exports.__result = a return ____exports" `; exports[`Bitop [5.4] ("a>>>b") 1`] = ` "local ____exports = {} -____exports.__result = a >> b +____exports.__result = (a & 4294967295) >> b return ____exports" `; exports[`Bitop [5.4] ("a^=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a ~ b - return a -end)() +a = a ~ b +____exports.__result = a return ____exports" `; @@ -403,10 +471,8 @@ return ____exports" exports[`Bitop [5.4] ("a|=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a | b - return a -end)() +a = a | b +____exports.__result = a return ____exports" `; @@ -416,6 +482,77 @@ ____exports.__result = a | b return ____exports" `; +exports[`Bitop [5.5] ("~a") 1`] = ` +"local ____exports = {} +____exports.__result = ~a +return ____exports" +`; + +exports[`Bitop [5.5] ("a&=b") 1`] = ` +"local ____exports = {} +a = a & b +____exports.__result = a +return ____exports" +`; + +exports[`Bitop [5.5] ("a&b") 1`] = ` +"local ____exports = {} +____exports.__result = a & b +return ____exports" +`; + +exports[`Bitop [5.5] ("a<<=b") 1`] = ` +"local ____exports = {} +a = a << b +____exports.__result = a +return ____exports" +`; + +exports[`Bitop [5.5] ("a<>>=b") 1`] = ` +"local ____exports = {} +a = (a & 4294967295) >> b +____exports.__result = a +return ____exports" +`; + +exports[`Bitop [5.5] ("a>>>b") 1`] = ` +"local ____exports = {} +____exports.__result = (a & 4294967295) >> b +return ____exports" +`; + +exports[`Bitop [5.5] ("a^=b") 1`] = ` +"local ____exports = {} +a = a ~ b +____exports.__result = a +return ____exports" +`; + +exports[`Bitop [5.5] ("a^b") 1`] = ` +"local ____exports = {} +____exports.__result = a ~ b +return ____exports" +`; + +exports[`Bitop [5.5] ("a|=b") 1`] = ` +"local ____exports = {} +a = a | b +____exports.__result = a +return ____exports" +`; + +exports[`Bitop [5.5] ("a|b") 1`] = ` +"local ____exports = {} +____exports.__result = a | b +return ____exports" +`; + exports[`Bitop [JIT] ("~a") 1`] = ` "local ____exports = {} ____exports.__result = bit.bnot(a) @@ -424,10 +561,8 @@ return ____exports" exports[`Bitop [JIT] ("a&=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.band(a, b) - return a -end)() +a = bit.band(a, b) +____exports.__result = a return ____exports" `; @@ -439,10 +574,8 @@ return ____exports" exports[`Bitop [JIT] ("a<<=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.lshift(a, b) - return a -end)() +a = bit.lshift(a, b) +____exports.__result = a return ____exports" `; @@ -454,19 +587,15 @@ return ____exports" exports[`Bitop [JIT] ("a>>=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.arshift(a, b) - return a -end)() +a = bit.arshift(a, b) +____exports.__result = a return ____exports" `; exports[`Bitop [JIT] ("a>>>=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.rshift(a, b) - return a -end)() +a = bit.rshift(a, b) +____exports.__result = a return ____exports" `; @@ -484,10 +613,8 @@ return ____exports" exports[`Bitop [JIT] ("a^=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.bxor(a, b) - return a -end)() +a = bit.bxor(a, b) +____exports.__result = a return ____exports" `; @@ -499,10 +626,8 @@ return ____exports" exports[`Bitop [JIT] ("a|=b") 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = bit.bor(a, b) - return a -end)() +a = bit.bor(a, b) +____exports.__result = a return ____exports" `; @@ -535,9 +660,11 @@ return ____exports" `; exports[`Unary expressions basic ("+a") 1`] = ` -"local ____exports = {} +"local ____lualib = require("lualib_bundle") +local __TS__Number = ____lualib.__TS__Number +local ____exports = {} function ____exports.__main(self) - local ____ = a + __TS__Number(a) end return ____exports" `; @@ -551,27 +678,31 @@ return ____exports" `; exports[`Unary expressions basic ("-a") 1`] = ` -"local ____exports = {} +"local ____lualib = require("lualib_bundle") +local __TS__Number = ____lualib.__TS__Number +local ____exports = {} function ____exports.__main(self) - local ____ = -a + __TS__Number(-a) end return ____exports" `; exports[`Unary expressions basic ("delete tbl.test") 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require("lualib_bundle") +local __TS__Delete = ____lualib.__TS__Delete local ____exports = {} function ____exports.__main(self) - __TS__Delete(tbl, \\"test\\") + __TS__Delete(tbl, "test") end return ____exports" `; exports[`Unary expressions basic ("delete tbl['test']") 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require("lualib_bundle") +local __TS__Delete = ____lualib.__TS__Delete local ____exports = {} function ____exports.__main(self) - __TS__Delete(tbl, \\"test\\") + __TS__Delete(tbl, "test") end return ____exports" `; @@ -593,19 +724,21 @@ return ____exports" `; exports[`Unary expressions basic ("let a = delete tbl.test") 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require("lualib_bundle") +local __TS__Delete = ____lualib.__TS__Delete local ____exports = {} function ____exports.__main(self) - local a = __TS__Delete(tbl, \\"test\\") + local a = __TS__Delete(tbl, "test") end return ____exports" `; exports[`Unary expressions basic ("let a = delete tbl['test']") 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require("lualib_bundle") +local __TS__Delete = ____lualib.__TS__Delete local ____exports = {} function ____exports.__main(self) - local a = __TS__Delete(tbl, \\"test\\") + local a = __TS__Delete(tbl, "test") end return ____exports" `; @@ -618,14 +751,12 @@ return ____exports" exports[`Unsupported bitop 5.3 ("a>>=b"): code 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a >> b - return a -end)() +a = a >> b +____exports.__result = a return ____exports" `; -exports[`Unsupported bitop 5.3 ("a>>=b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Right shift operator is not supported for target Lua 5.3. Use \`>>>\` instead."`; +exports[`Unsupported bitop 5.3 ("a>>=b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Signed right shift \`>>\` is not supported on Lua 5.3+: Lua's native \`>>\` is logical (zero-fill) on 64-bit integers, with no built-in arithmetic shift. Use \`>>>\` if you don't need sign extension, or write your own helper."`; exports[`Unsupported bitop 5.3 ("a>>b"): code 1`] = ` "local ____exports = {} @@ -633,18 +764,16 @@ ____exports.__result = a >> b return ____exports" `; -exports[`Unsupported bitop 5.3 ("a>>b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Right shift operator is not supported for target Lua 5.3. Use \`>>>\` instead."`; +exports[`Unsupported bitop 5.3 ("a>>b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Signed right shift \`>>\` is not supported on Lua 5.3+: Lua's native \`>>\` is logical (zero-fill) on 64-bit integers, with no built-in arithmetic shift. Use \`>>>\` if you don't need sign extension, or write your own helper."`; exports[`Unsupported bitop 5.4 ("a>>=b"): code 1`] = ` "local ____exports = {} -____exports.__result = (function() - a = a >> b - return a -end)() +a = a >> b +____exports.__result = a return ____exports" `; -exports[`Unsupported bitop 5.4 ("a>>=b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Right shift operator is not supported for target Lua 5.3. Use \`>>>\` instead."`; +exports[`Unsupported bitop 5.4 ("a>>=b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Signed right shift \`>>\` is not supported on Lua 5.3+: Lua's native \`>>\` is logical (zero-fill) on 64-bit integers, with no built-in arithmetic shift. Use \`>>>\` if you don't need sign extension, or write your own helper."`; exports[`Unsupported bitop 5.4 ("a>>b"): code 1`] = ` "local ____exports = {} @@ -652,4 +781,4 @@ ____exports.__result = a >> b return ____exports" `; -exports[`Unsupported bitop 5.4 ("a>>b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Right shift operator is not supported for target Lua 5.3. Use \`>>>\` instead."`; +exports[`Unsupported bitop 5.4 ("a>>b"): diagnostics 1`] = `"main.ts(1,25): error TSTL: Signed right shift \`>>\` is not supported on Lua 5.3+: Lua's native \`>>\` is logical (zero-fill) on 64-bit integers, with no built-in arithmetic shift. Use \`>>>\` if you don't need sign extension, or write your own helper."`; diff --git a/test/unit/__snapshots__/identifiers.spec.ts.snap b/test/unit/__snapshots__/identifiers.spec.ts.snap index d955068e5..cfd11a893 100644 --- a/test/unit/__snapshots__/identifiers.spec.ts.snap +++ b/test/unit/__snapshots__/identifiers.spec.ts.snap @@ -52,18 +52,14 @@ exports[`ambient identifier must be a valid lua identifier ("enum $$ {}"): code exports[`ambient identifier must be a valid lua identifier ("enum $$ {}"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier ("function $$();"): code 1`] = `"local ____ = _____24_24_24"`; +exports[`ambient identifier must be a valid lua identifier ("function $$(): void;"): code 1`] = `"local ____ = _____24_24_24"`; -exports[`ambient identifier must be a valid lua identifier ("function $$();"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`; +exports[`ambient identifier must be a valid lua identifier ("function $$(): void;"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`; exports[`ambient identifier must be a valid lua identifier ("let $$: any;"): code 1`] = `"local ____ = _____24_24_24"`; exports[`ambient identifier must be a valid lua identifier ("let $$: any;"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier ("module $$ { export const bar: any; }"): code 1`] = `"local ____ = _____24_24_24"`; - -exports[`ambient identifier must be a valid lua identifier ("module $$ { export const bar: any; }"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`; - exports[`ambient identifier must be a valid lua identifier ("namespace $$ { export const bar: any; }"): code 1`] = `"local ____ = _____24_24_24"`; exports[`ambient identifier must be a valid lua identifier ("namespace $$ { export const bar: any; }"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`; @@ -72,59 +68,59 @@ exports[`ambient identifier must be a valid lua identifier ("var $$: any;"): cod exports[`ambient identifier must be a valid lua identifier ("var $$: any;"): diagnostics 1`] = `"main.ts(3,9): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("$$"): code 1`] = `"foo = {[\\"$$$\\"] = _____24_24_24}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("$$"): code 1`] = `"foo = {["$$$"] = _____24_24_24}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("$$"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("_̀ः٠‿"): code 1`] = `"foo = {[\\"_̀ः٠‿\\"] = ______300_903_660_203F}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("_̀ः٠‿"): code 1`] = `"foo = {["_̀ः٠‿"] = ______300_903_660_203F}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("_̀ः٠‿"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name '_̀ः٠‿'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("and"): code 1`] = `"foo = {[\\"and\\"] = ____and}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("and"): code 1`] = `"foo = {["and"] = ____and}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("and"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'and'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("elseif"): code 1`] = `"foo = {[\\"elseif\\"] = ____elseif}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("elseif"): code 1`] = `"foo = {["elseif"] = ____elseif}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("elseif"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'elseif'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("end"): code 1`] = `"foo = {[\\"end\\"] = ____end}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("end"): code 1`] = `"foo = {["end"] = ____end}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("end"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'end'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("goto"): code 1`] = `"foo = {[\\"goto\\"] = ____goto}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("goto"): code 1`] = `"foo = {["goto"] = ____goto}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("goto"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'goto'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("local"): code 1`] = `"foo = {[\\"local\\"] = ____local}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("local"): code 1`] = `"foo = {["local"] = ____local}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("local"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'local'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("nil"): code 1`] = `"foo = {[\\"nil\\"] = ____nil}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("nil"): code 1`] = `"foo = {["nil"] = ____nil}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("nil"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'nil'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("not"): code 1`] = `"foo = {[\\"not\\"] = ____not}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("not"): code 1`] = `"foo = {["not"] = ____not}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("not"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'not'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("or"): code 1`] = `"foo = {[\\"or\\"] = ____or}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("or"): code 1`] = `"foo = {["or"] = ____or}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("or"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'or'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("repeat"): code 1`] = `"foo = {[\\"repeat\\"] = ____repeat}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("repeat"): code 1`] = `"foo = {["repeat"] = ____repeat}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("repeat"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'repeat'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("then"): code 1`] = `"foo = {[\\"then\\"] = ____then}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("then"): code 1`] = `"foo = {["then"] = ____then}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("then"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'then'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("until"): code 1`] = `"foo = {[\\"until\\"] = ____until}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("until"): code 1`] = `"foo = {["until"] = ____until}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("until"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'until'. Ambient identifiers must be valid lua identifiers."`; -exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("ɥɣɎɌͼƛಠ"): code 1`] = `"foo = {[\\"ɥɣɎɌͼƛಠ\\"] = _____265_263_24E_24C_37C_19B_CA0}"`; +exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("ɥɣɎɌͼƛಠ"): code 1`] = `"foo = {["ɥɣɎɌͼƛಠ"] = _____265_263_24E_24C_37C_19B_CA0}"`; exports[`ambient identifier must be a valid lua identifier (object literal shorthand) ("ɥɣɎɌͼƛಠ"): diagnostics 1`] = `"main.ts(3,27): error TSTL: Invalid ambient identifier name 'ɥɣɎɌͼƛಠ'. Ambient identifiers must be valid lua identifiers."`; @@ -192,58 +188,253 @@ exports[`undeclared identifier must be a valid lua identifier ("ɥɣɎɌͼƛಠ" exports[`undeclared identifier must be a valid lua identifier ("ɥɣɎɌͼƛಠ"): diagnostics 1`] = `"main.ts(2,21): error TSTL: Invalid ambient identifier name 'ɥɣɎɌͼƛಠ'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("$$"): code 1`] = `"foo = {[\\"$$$\\"] = _____24_24_24}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("$$"): code 1`] = `"foo = {["$$$"] = _____24_24_24}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("$$"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name '$$$'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("_̀ः٠‿"): code 1`] = `"foo = {[\\"_̀ः٠‿\\"] = ______300_903_660_203F}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("_̀ः٠‿"): code 1`] = `"foo = {["_̀ः٠‿"] = ______300_903_660_203F}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("_̀ः٠‿"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name '_̀ः٠‿'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("and"): code 1`] = `"foo = {[\\"and\\"] = ____and}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("and"): code 1`] = `"foo = {["and"] = ____and}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("and"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'and'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("elseif"): code 1`] = `"foo = {[\\"elseif\\"] = ____elseif}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("elseif"): code 1`] = `"foo = {["elseif"] = ____elseif}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("elseif"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'elseif'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("end"): code 1`] = `"foo = {[\\"end\\"] = ____end}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("end"): code 1`] = `"foo = {["end"] = ____end}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("end"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'end'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("goto"): code 1`] = `"foo = {[\\"goto\\"] = ____goto}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("goto"): code 1`] = `"foo = {["goto"] = ____goto}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("goto"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'goto'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("local"): code 1`] = `"foo = {[\\"local\\"] = ____local}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("local"): code 1`] = `"foo = {["local"] = ____local}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("local"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'local'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("nil"): code 1`] = `"foo = {[\\"nil\\"] = ____nil}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("nil"): code 1`] = `"foo = {["nil"] = ____nil}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("nil"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'nil'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("not"): code 1`] = `"foo = {[\\"not\\"] = ____not}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("not"): code 1`] = `"foo = {["not"] = ____not}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("not"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'not'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("or"): code 1`] = `"foo = {[\\"or\\"] = ____or}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("or"): code 1`] = `"foo = {["or"] = ____or}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("or"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'or'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("repeat"): code 1`] = `"foo = {[\\"repeat\\"] = ____repeat}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("repeat"): code 1`] = `"foo = {["repeat"] = ____repeat}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("repeat"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'repeat'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("then"): code 1`] = `"foo = {[\\"then\\"] = ____then}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("then"): code 1`] = `"foo = {["then"] = ____then}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("then"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'then'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("until"): code 1`] = `"foo = {[\\"until\\"] = ____until}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("until"): code 1`] = `"foo = {["until"] = ____until}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("until"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'until'. Ambient identifiers must be valid lua identifiers."`; -exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("ɥɣɎɌͼƛಠ"): code 1`] = `"foo = {[\\"ɥɣɎɌͼƛಠ\\"] = _____265_263_24E_24C_37C_19B_CA0}"`; +exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("ɥɣɎɌͼƛಠ"): code 1`] = `"foo = {["ɥɣɎɌͼƛಠ"] = _____265_263_24E_24C_37C_19B_CA0}"`; exports[`undeclared identifier must be a valid lua identifier (object literal shorthand) ("ɥɣɎɌͼƛಠ"): diagnostics 1`] = `"main.ts(2,27): error TSTL: Invalid ambient identifier name 'ɥɣɎɌͼƛಠ'. Ambient identifiers must be valid lua identifiers."`; + +exports[`unicode identifiers in supporting environments (luajit) destructuring property name ("_̀ः٠‿") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local ____temp_0 = {foo = "foobar"} + local _̀ः٠‿ = ____temp_0.foo + return _̀ः٠‿ +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) destructuring property name ("ɥɣɎɌͼƛಠ") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local ____temp_0 = {foo = "foobar"} + local ɥɣɎɌͼƛಠ = ____temp_0.foo + return ɥɣɎɌͼƛಠ +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) destructuring property name ("𝛼𝛽𝚫") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local ____temp_0 = {foo = "foobar"} + local 𝛼𝛽𝚫 = ____temp_0.foo + return 𝛼𝛽𝚫 +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) destructuring shorthand ("_̀ः٠‿") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local ____temp_0 = {_̀ः٠‿ = "foobar"} + local _̀ः٠‿ = ____temp_0._̀ः٠‿ + return _̀ः٠‿ +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) destructuring shorthand ("ɥɣɎɌͼƛಠ") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local ____temp_0 = {ɥɣɎɌͼƛಠ = "foobar"} + local ɥɣɎɌͼƛಠ = ____temp_0.ɥɣɎɌͼƛಠ + return ɥɣɎɌͼƛಠ +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) destructuring shorthand ("𝛼𝛽𝚫") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local ____temp_0 = {𝛼𝛽𝚫 = "foobar"} + local 𝛼𝛽𝚫 = ____temp_0.𝛼𝛽𝚫 + return 𝛼𝛽𝚫 +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) function ("_̀ः٠‿") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function _̀ः٠‿(self, arg) + return "foo" .. arg + end + return _̀ः٠‿(nil, "bar") +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) function ("ɥɣɎɌͼƛಠ") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function ɥɣɎɌͼƛಠ(self, arg) + return "foo" .. arg + end + return ɥɣɎɌͼƛಠ(nil, "bar") +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) function ("𝛼𝛽𝚫") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function 𝛼𝛽𝚫(self, arg) + return "foo" .. arg + end + return 𝛼𝛽𝚫(nil, "bar") +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) identifier name ("_̀ः٠‿") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local _̀ः٠‿ = "foobar" + return _̀ः٠‿ +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) identifier name ("ɥɣɎɌͼƛಠ") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local ɥɣɎɌͼƛಠ = "foobar" + return ɥɣɎɌͼƛಠ +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) identifier name ("𝛼𝛽𝚫") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local 𝛼𝛽𝚫 = "foobar" + return 𝛼𝛽𝚫 +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) method ("_̀ः٠‿") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local foo = {_̀ः٠‿ = function(self, arg) + return "foo" .. arg + end} + return foo:_̀ः٠‿("bar") +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) method ("ɥɣɎɌͼƛಠ") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local foo = {ɥɣɎɌͼƛಠ = function(self, arg) + return "foo" .. arg + end} + return foo:ɥɣɎɌͼƛಠ("bar") +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) method ("𝛼𝛽𝚫") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local foo = {𝛼𝛽𝚫 = function(self, arg) + return "foo" .. arg + end} + return foo:𝛼𝛽𝚫("bar") +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) property name ("_̀ः٠‿") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local x = {_̀ः٠‿ = "foobar"} + return x._̀ः٠‿ +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) property name ("ɥɣɎɌͼƛಠ") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local x = {ɥɣɎɌͼƛಠ = "foobar"} + return x.ɥɣɎɌͼƛಠ +end +return ____exports" +`; + +exports[`unicode identifiers in supporting environments (luajit) property name ("𝛼𝛽𝚫") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local x = {𝛼𝛽𝚫 = "foobar"} + return x.𝛼𝛽𝚫 +end +return ____exports" +`; + +exports[`unicode static initialization block (#1645) 1`] = ` +"local ____lualib = require("lualib_bundle") +local __TS__Class = ____lualib.__TS__Class +local ____exports = {} +____exports.default = __TS__Class() +local _____81EA_5B9A_4E49_5F02_80FD = ____exports.default +_____81EA_5B9A_4E49_5F02_80FD.name = "自定义异能" +function _____81EA_5B9A_4E49_5F02_80FD.prototype.____constructor(self) +end; +(function(self) + local a = 1 +end)(_____81EA_5B9A_4E49_5F02_80FD) +return ____exports" +`; diff --git a/test/unit/__snapshots__/loops.spec.ts.snap b/test/unit/__snapshots__/loops.spec.ts.snap index e9b6be991..886964ebc 100644 --- a/test/unit/__snapshots__/loops.spec.ts.snap +++ b/test/unit/__snapshots__/loops.spec.ts.snap @@ -3,7 +3,7 @@ exports[`forin[Array]: code 1`] = ` "local ____exports = {} function ____exports.__main(self) - local array = {} + local array = {1, 2, 3} for key in pairs(array) do end end @@ -11,121 +11,3 @@ return ____exports" `; exports[`forin[Array]: diagnostics 1`] = `"main.ts(3,9): error TSTL: Iterating over arrays with 'for ... in' is not allowed."`; - -exports[`loop continue (do { continue; } while (false)) [5.1]: code 1`] = ` -"repeat - do - do - goto __continue2 - end - ::__continue2:: - end -until not false" -`; - -exports[`loop continue (do { continue; } while (false)) [5.1]: diagnostics 1`] = `"main.ts(1,6): error TSTL: Continue statement is/are not supported for target Lua 5.1."`; - -exports[`loop continue (do { continue; } while (false)) [universal]: code 1`] = ` -"repeat - do - do - goto __continue2 - end - ::__continue2:: - end -until not false" -`; - -exports[`loop continue (do { continue; } while (false)) [universal]: diagnostics 1`] = `"main.ts(1,6): error TSTL: Continue statement is/are not supported for target Lua 5.1."`; - -exports[`loop continue (for (;;) { continue; }) [5.1]: code 1`] = ` -"do - while true do - do - goto __continue2 - end - ::__continue2:: - end -end" -`; - -exports[`loop continue (for (;;) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,12): error TSTL: Continue statement is/are not supported for target Lua 5.1."`; - -exports[`loop continue (for (;;) { continue; }) [universal]: code 1`] = ` -"do - while true do - do - goto __continue2 - end - ::__continue2:: - end -end" -`; - -exports[`loop continue (for (;;) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,12): error TSTL: Continue statement is/are not supported for target Lua 5.1."`; - -exports[`loop continue (for (const a in {}) { continue; }) [5.1]: code 1`] = ` -"for a in pairs({}) do - do - goto __continue2 - end - ::__continue2:: -end" -`; - -exports[`loop continue (for (const a in {}) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.1."`; - -exports[`loop continue (for (const a in {}) { continue; }) [universal]: code 1`] = ` -"for a in pairs({}) do - do - goto __continue2 - end - ::__continue2:: -end" -`; - -exports[`loop continue (for (const a in {}) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.1."`; - -exports[`loop continue (for (const a of []) { continue; }) [5.1]: code 1`] = ` -"for ____, a in ipairs({}) do - do - goto __continue2 - end - ::__continue2:: -end" -`; - -exports[`loop continue (for (const a of []) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.1."`; - -exports[`loop continue (for (const a of []) { continue; }) [universal]: code 1`] = ` -"for ____, a in ipairs({}) do - do - goto __continue2 - end - ::__continue2:: -end" -`; - -exports[`loop continue (for (const a of []) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,23): error TSTL: Continue statement is/are not supported for target Lua 5.1."`; - -exports[`loop continue (while (false) { continue; }) [5.1]: code 1`] = ` -"while false do - do - goto __continue2 - end - ::__continue2:: -end" -`; - -exports[`loop continue (while (false) { continue; }) [5.1]: diagnostics 1`] = `"main.ts(1,17): error TSTL: Continue statement is/are not supported for target Lua 5.1."`; - -exports[`loop continue (while (false) { continue; }) [universal]: code 1`] = ` -"while false do - do - goto __continue2 - end - ::__continue2:: -end" -`; - -exports[`loop continue (while (false) { continue; }) [universal]: diagnostics 1`] = `"main.ts(1,17): error TSTL: Continue statement is/are not supported for target Lua 5.1."`; diff --git a/test/unit/__snapshots__/optionalChaining.spec.ts.snap b/test/unit/__snapshots__/optionalChaining.spec.ts.snap new file mode 100644 index 000000000..19fa2a309 --- /dev/null +++ b/test/unit/__snapshots__/optionalChaining.spec.ts.snap @@ -0,0 +1,276 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Unsupported optional chains Builtin global method: code 1`] = ` +"local ____lualib = require("lualib_bundle") +local __TS__Number = ____lualib.__TS__Number +local ____opt_0 = Number +if ____opt_0 ~= nil then + __TS__Number("3") +end" +`; + +exports[`Unsupported optional chains Builtin global method: diagnostics 1`] = `"main.ts(2,17): error TSTL: Optional calls are not supported for builtin or language extension functions."`; + +exports[`Unsupported optional chains Builtin global property: code 1`] = ` +"local ____opt_0 = console +if ____opt_0 ~= nil then + print("3") +end" +`; + +exports[`Unsupported optional chains Builtin global property: diagnostics 1`] = `"main.ts(2,17): error TSTL: Optional calls are not supported for builtin or language extension functions."`; + +exports[`Unsupported optional chains Builtin prototype method: code 1`] = ` +"local ____lualib = require("lualib_bundle") +local __TS__ArrayForEach = ____lualib.__TS__ArrayForEach +local ____opt_0 = ({1, 2, 3, 4}).forEach +if ____opt_0 ~= nil then + __TS__ArrayForEach( + {1, 2, 3, 4}, + function() + end + ) +end" +`; + +exports[`Unsupported optional chains Builtin prototype method: diagnostics 1`] = `"main.ts(2,17): error TSTL: Optional calls are not supported for builtin or language extension functions."`; + +exports[`Unsupported optional chains Compile members only: code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + --- + -- @compileMembersOnly + local A = 0 + --- + -- @compileMembersOnly + local B = 2 + --- + -- @compileMembersOnly + local C = 3 + --- + -- @compileMembersOnly + local D = "D" + local ____opt_0 = TestEnum + if ____opt_0 ~= nil then + local ____ = B + end +end +return ____exports" +`; + +exports[`Unsupported optional chains Compile members only: diagnostics 1`] = `"main.ts(10,17): error TSTL: Optional calls are not supported on enums marked with @compileMembersOnly."`; + +exports[`Unsupported optional chains Language extensions: code 1`] = ` +"local ____opt_0 = ({}).has +if ____opt_0 ~= nil then +end" +`; + +exports[`Unsupported optional chains Language extensions: diagnostics 1`] = ` +"main.ts(2,17): error TSTL: Optional calls are not supported for builtin or language extension functions. +main.ts(2,17): error TSTL: This language extension must be called as a method." +`; + +exports[`long optional chain 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local a = {b = {c = {d = {e = {f = "hello!"}}}}} + local ____opt_2 = a.b + local ____opt_0 = ____opt_2 and ____opt_2.c + return ____opt_0 and ____opt_0.d.e.f +end +return ____exports" +`; + +exports[`optional chaining ("{ foo: \\"foo\\" }") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local obj = {foo = "foo"} + return obj and obj.foo +end +return ____exports" +`; + +exports[`optional chaining ("null") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local obj = nil + return obj and obj.foo +end +return ____exports" +`; + +exports[`optional chaining ("undefined") 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local obj = nil + return obj and obj.foo +end +return ____exports" +`; + +exports[`optional element function calls 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local obj = { + value = "foobar", + foo = function(v) return v + 10 end + } + local fooKey = "foo" + local barKey = "bar" + local ____opt_0 = obj[barKey] + local ____temp_4 = ____opt_0 and ____opt_0(5) + if ____temp_4 == nil then + local ____opt_2 = obj[fooKey] + ____temp_4 = ____opt_2 and ____opt_2(15) + end + return ____temp_4 +end +return ____exports" +`; + +exports[`unused call 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local result + local obj = {foo = function(self) + result = "bar" + end} + if obj ~= nil then + obj:foo() + end + return result +end +return ____exports" +`; + +exports[`unused expression 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local obj = {foo = "bar"} + if obj ~= nil then + local ____ = obj.foo + end +end +return ____exports" +`; + +exports[`unused result with preceding statements on right side 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local i = 0 + local obj = nil + if obj ~= nil then + local ____opt_0_foo_2 = obj.foo + local ____i_1 = i + i = ____i_1 + 1 + ____opt_0_foo_2(obj, ____i_1) + end + return i +end +return ____exports" +`; + +exports[`unused result with preceding statements on right side 2`] = ` +"local ____exports = {} +function ____exports.__main(self) + local i = 0 + local obj = {foo = function(self, val) + return val + end} + if obj ~= nil then + local ____opt_0_foo_2 = obj.foo + local ____i_1 = i + i = ____i_1 + 1 + ____opt_0_foo_2(obj, ____i_1) + end + return i +end +return ____exports" +`; + +exports[`with preceding statements on right side 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local i = 0 + local obj = nil + local ____opt_result_4 + if obj ~= nil then + local ____opt_0_foo_2 = obj.foo + local ____i_1 = i + i = ____i_1 + 1 + ____opt_result_4 = ____opt_0_foo_2(obj, ____i_1) + end + return {result = ____opt_result_4, i = i} +end +return ____exports" +`; + +exports[`with preceding statements on right side 2`] = ` +"local ____exports = {} +function ____exports.__main(self) + local i = 0 + local obj = {foo = function(____, v) return v end} + local ____opt_result_4 + if obj ~= nil then + local ____opt_0_foo_2 = obj.foo + local ____i_1 = i + i = ____i_1 + 1 + ____opt_result_4 = ____opt_0_foo_2(obj, ____i_1) + end + return {result = ____opt_result_4, i = i} +end +return ____exports" +`; + +exports[`with preceding statements on right side modifying left 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local i = 0 + local obj = nil + local function bar(self) + if obj then + obj.foo = nil + end + obj = nil + return 1 + end + local ____opt_0 = obj + if ____opt_0 ~= nil then + local ____opt_0_foo_3 = ____opt_0.foo + local ____bar_result_2 = bar(nil) + local ____i_1 = i + i = ____i_1 + 1 + ____opt_0 = ____opt_0_foo_3(____opt_0, ____bar_result_2, ____i_1) + end + return {result = ____opt_0, obj = obj, i = i} +end +return ____exports" +`; + +exports[`with preceding statements on right side modifying left 2`] = ` +"local ____exports = {} +function ____exports.__main(self) + local i = 0 + local obj = {foo = function(self, v) + return v + end} + local function bar(self) + if obj then + obj.foo = nil + end + obj = nil + return 1 + end + local ____opt_0 = obj + if ____opt_0 ~= nil then + local ____opt_0_foo_3 = ____opt_0.foo + local ____bar_result_2 = bar(nil) + local ____i_1 = i + i = ____i_1 + 1 + ____opt_0 = ____opt_0_foo_3(____opt_0, ____bar_result_2, ____i_1) + end + return {result = ____opt_0, obj = obj, i = i} +end +return ____exports" +`; diff --git a/test/unit/__snapshots__/spread.spec.ts.snap b/test/unit/__snapshots__/spread.spec.ts.snap index f16bc746a..c1fba08eb 100644 --- a/test/unit/__snapshots__/spread.spec.ts.snap +++ b/test/unit/__snapshots__/spread.spec.ts.snap @@ -12,116 +12,7 @@ function ____exports.__main(self) multi(nil, ...) ) end - return test(nil, \\"a\\", \\"b\\", \\"c\\") -end -return ____exports" -`; - -exports[`vararg spread optimization basic use 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function pick(self, ...) - local args = {...} - return args[2] - end - local function test(self, ...) - return pick(nil, ...) - end - return test(nil, \\"a\\", \\"b\\", \\"c\\") -end -return ____exports" -`; - -exports[`vararg spread optimization block statement 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function pick(self, ...) - local args = {...} - return args[2] - end - local function test(self, ...) - local result - do - result = pick(nil, ...) - end - return result - end - return test(nil, \\"a\\", \\"b\\", \\"c\\") -end -return ____exports" -`; - -exports[`vararg spread optimization body-less arrow function 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function pick(self, ...) - local args = {...} - return args[2] - end - local function test(____, ...) - return pick(nil, ...) - end - return test(nil, \\"a\\", \\"b\\", \\"c\\") -end -return ____exports" -`; - -exports[`vararg spread optimization finally clause 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function pick(self, ...) - local args = {...} - return args[2] - end - local function test(self, ...) - do - pcall( - function() - error(\\"foobar\\", 0) - end - ) - do - return pick(nil, ...) - end - end - end - return test(nil, \\"a\\", \\"b\\", \\"c\\") -end -return ____exports" -`; - -exports[`vararg spread optimization if statement 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function pick(self, ...) - local args = {...} - return args[2] - end - local function test(self, ...) - if true then - return pick(nil, ...) - end - end - return test(nil, \\"a\\", \\"b\\", \\"c\\") -end -return ____exports" -`; - -exports[`vararg spread optimization loop statement 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local function pick(self, ...) - local args = {...} - return args[2] - end - local function test(self, ...) - repeat - do - return pick(nil, ...) - end - until not false - end - return test(nil, \\"a\\", \\"b\\", \\"c\\") + return test(nil, "a", "b", "c") end return ____exports" `; diff --git a/test/unit/__snapshots__/switch.spec.ts.snap b/test/unit/__snapshots__/switch.spec.ts.snap index e7d003fa5..a64e3f77d 100644 --- a/test/unit/__snapshots__/switch.spec.ts.snap +++ b/test/unit/__snapshots__/switch.spec.ts.snap @@ -1,53 +1,147 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`array 1`] = ` +exports[`switch empty fallthrough to default (0) 1`] = ` "local ____exports = {} function ____exports.__main(self) - local result = -1 - local ____switch3 = 2 - if ____switch3 == 0 then - goto ____switch3_case_0 - elseif ____switch3 == 1 then - goto ____switch3_case_1 - elseif ____switch3 == 2 then - goto ____switch3_case_2 - end - goto ____switch3_end - ::____switch3_case_0:: - do + local out = {} + repeat + local ____switch3 = 0 + local ____cond3 = ____switch3 == 1 do - result = 200 - goto ____switch3_end + out[#out + 1] = "default" end - end - ::____switch3_case_1:: - do + until true + return out +end +return ____exports" +`; + +exports[`switch empty fallthrough to default (1) 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local out = {} + repeat + local ____switch3 = 1 + local ____cond3 = ____switch3 == 1 do - result = 100 - goto ____switch3_end + out[#out + 1] = "default" + end + until true + return out +end +return ____exports" +`; + +exports[`switch hoisting hoisting from default clause is not duplicated when falling through 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local x = 1 + local result = "" + repeat + local ____switch3 = x + local hoisted + function hoisted(self) + return "hoisted" + end + local ____cond3 = ____switch3 == 1 + if ____cond3 then + result = hoisted(nil) + break + end + ____cond3 = ____cond3 or ____switch3 == 2 + if ____cond3 then + result = "2" + end + if ____cond3 then + result = "default" + end + ____cond3 = ____cond3 or ____switch3 == 3 + if ____cond3 then + result = "3" + break end - end - ::____switch3_case_2:: - do do - result = 1 - goto ____switch3_end + result = "default" + result = "3" end - end - ::____switch3_end:: + until true return result end return ____exports" `; -exports[`switch not allowed in 5.1: code 1`] = ` +exports[`switch hoisting hoisting from fallthrough clause after default is not duplicated 1`] = ` "local ____exports = {} function ____exports.__main(self) - local ____switch3 = \\"abc\\" - goto ____switch3_end - ::____switch3_end:: + local x = 1 + local result = "" + repeat + local ____switch3 = x + local hoisted + function hoisted(self) + return "hoisted" + end + local ____cond3 = ____switch3 == 1 + if ____cond3 then + result = hoisted(nil) + break + end + ____cond3 = ____cond3 or ____switch3 == 2 + if ____cond3 then + result = "2" + end + if ____cond3 then + result = "default" + end + ____cond3 = ____cond3 or ____switch3 == 3 + if ____cond3 then + result = "3" + break + end + do + result = "default" + result = "3" + end + until true + return result end return ____exports" `; -exports[`switch not allowed in 5.1: diagnostics 1`] = `"main.ts(2,9): error TSTL: Switch statements is/are not supported for target Lua 5.1."`; +exports[`switch produces optimal output 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local x = 0 + local out = {} + repeat + local ____switch3 = 0 + local ____cond3 = ____switch3 == 0 or ____switch3 == 1 or ____switch3 == 2 + if ____cond3 then + out[#out + 1] = "0,1,2" + break + end + ____cond3 = ____cond3 or ____switch3 == 3 + if ____cond3 then + do + out[#out + 1] = "3" + break + end + end + ____cond3 = ____cond3 or ____switch3 == 4 + if ____cond3 then + break + end + do + x = x + 1 + out[#out + 1] = "default = " .. tostring(x) + do + out[#out + 1] = "3" + break + end + end + until true + out[#out + 1] = tostring(x) + return out +end +return ____exports" +`; diff --git a/test/unit/annotations/__snapshots__/customConstructor.spec.ts.snap b/test/unit/annotations/__snapshots__/customConstructor.spec.ts.snap index c283485bf..11cab115c 100644 --- a/test/unit/annotations/__snapshots__/customConstructor.spec.ts.snap +++ b/test/unit/annotations/__snapshots__/customConstructor.spec.ts.snap @@ -1,11 +1,15 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`IncorrectUsage: code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require("lualib_bundle") +local __TS__Class = ____lualib.__TS__Class +local __TS__New = ____lualib.__TS__New local ____exports = {} function ____exports.__main(self) + --- + -- @customConstructor local Point2D = __TS__Class() - Point2D.name = \\"Point2D\\" + Point2D.name = "Point2D" function Point2D.prototype.____constructor(self) end __TS__New(Point2D) diff --git a/test/unit/annotations/__snapshots__/deprecated.spec.ts.snap b/test/unit/annotations/__snapshots__/deprecated.spec.ts.snap deleted file mode 100644 index d612bc478..000000000 --- a/test/unit/annotations/__snapshots__/deprecated.spec.ts.snap +++ /dev/null @@ -1,109 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`LuaTable deprecation warning property access set: code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - return tbl:set(\\"foo\\", 0) -end -return ____exports" -`; - -exports[`LuaTable deprecation warning property access set: diagnostics 1`] = `"main.ts(12,16): error TSTL: '@luaTable' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#luatable for more information."`; - -exports[`LuaTable removed warning constructor: code 1`] = ` -"require(\\"lualib_bundle\\"); -____table = __TS__New(Table)" -`; - -exports[`LuaTable removed warning constructor: diagnostics 1`] = `"main.ts(11,15): error TSTL: '@luaTable' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#luatable for more information."`; - -exports[`LuaTable removed warning property access get: code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - return tbl:get(\\"foo\\") -end -return ____exports" -`; - -exports[`LuaTable removed warning property access get: diagnostics 1`] = `"main.ts(12,16): error TSTL: '@luaTable' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#luatable for more information."`; - -exports[`LuaTable removed warning property access length: code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - return tbl.length -end -return ____exports" -`; - -exports[`LuaTable removed warning property access length: diagnostics 1`] = `"main.ts(12,16): error TSTL: '@luaTable' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#luatable for more information."`; - -exports[`extension removed: code 1`] = ` -"require(\\"lualib_bundle\\"); -B = __TS__Class() -B.name = \\"B\\" -__TS__ClassExtends(B, A)" -`; - -exports[`extension removed: code 2`] = ` -"require(\\"lualib_bundle\\"); -B = __TS__Class() -B.name = \\"B\\" -__TS__ClassExtends(B, A)" -`; - -exports[`extension removed: diagnostics 1`] = `"main.ts(4,9): error TSTL: '@extension' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#extension for more information."`; - -exports[`extension removed: diagnostics 2`] = `"main.ts(4,9): error TSTL: '@metaExtension' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#metaextension for more information."`; - -exports[`forRange removed: code 1`] = `""`; - -exports[`forRange removed: diagnostics 1`] = `"main.ts(4,25): error TSTL: '@forRange' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#forrange for more information."`; - -exports[`luaiterator removed: code 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local arr = {\\"a\\", \\"b\\", \\"c\\"} - local function luaIter(self) - local i = 0 - return function() return arr[(function() - local ____tmp = i - i = ____tmp + 1 - return ____tmp - end)() + 1] end - end - local result = \\"\\" - return result -end -return ____exports" -`; - -exports[`luaiterator removed: diagnostics 1`] = `"main.ts(10,23): error TSTL: '@luaIterator' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#luaiterator for more information."`; - -exports[`phantom removed: code 1`] = ` -"A = A or ({}) -do - local function nsMember(self) - end -end" -`; - -exports[`phantom removed: diagnostics 1`] = `"main.ts(3,9): error TSTL: '@phantom' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#phantom for more information."`; - -exports[`pureAbstract removed: code 1`] = ` -"require(\\"lualib_bundle\\"); -ClassB = __TS__Class() -ClassB.name = \\"ClassB\\" -__TS__ClassExtends(ClassB, ClassA)" -`; - -exports[`pureAbstract removed: diagnostics 1`] = `"main.ts(4,22): error TSTL: '@pureAbstract' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#pureabstract for more information."`; - -exports[`vararg removed: code 1`] = ` -"function foo(self, ...) -end -function vararg(self, ...) - foo(_G, ...) -end" -`; - -exports[`vararg removed: diagnostics 1`] = `"main.ts(6,17): error TSTL: '@vararg' has been removed and will no longer have any effect.See https://typescripttolua.github.io/docs/advanced/compiler-annotations#vararg for more information."`; diff --git a/test/unit/annotations/customConstructor.spec.ts b/test/unit/annotations/customConstructor.spec.ts index 86858f700..c9ddcf739 100644 --- a/test/unit/annotations/customConstructor.spec.ts +++ b/test/unit/annotations/customConstructor.spec.ts @@ -11,8 +11,8 @@ test("CustomCreate", () => { const tsHeader = ` /** @customConstructor Point2DCreate */ class Point2D { - public x: number; - public y: number; + public x!: number; + public y!: number; constructor(x: number, y: number) { // No values assigned } diff --git a/test/unit/annotations/deprecated.spec.ts b/test/unit/annotations/deprecated.spec.ts deleted file mode 100644 index 2f0edb105..000000000 --- a/test/unit/annotations/deprecated.spec.ts +++ /dev/null @@ -1,103 +0,0 @@ -import { annotationRemoved } from "../../../src/transformation/utils/diagnostics"; -import * as util from "../../util"; - -test.each(["extension", "metaExtension"])("extension removed", extensionType => { - util.testModule` - declare class A {} - /** @${extensionType} **/ - class B extends A {} - `.expectDiagnosticsToMatchSnapshot([annotationRemoved.code]); -}); - -test("phantom removed", () => { - util.testModule` - /** @phantom **/ - namespace A { - function nsMember() {} - } - `.expectDiagnosticsToMatchSnapshot([annotationRemoved.code]); -}); - -test("pureAbstract removed", () => { - util.testModule` - /** @pureAbstract */ - declare class ClassA {} - class ClassB extends ClassA {} - `.expectDiagnosticsToMatchSnapshot([annotationRemoved.code]); -}); - -test("forRange removed", () => { - util.testModule` - /** @forRange */ - declare function forRange(start: number, limit: number, step?: number): number[]; - for (const i of forRange(1, 10)) {} - `.expectDiagnosticsToMatchSnapshot([annotationRemoved.code]); -}); - -test("vararg removed", () => { - util.testModule` - /** @vararg */ - type VarArg = T & { readonly __brand: unique symbol }; - function foo(...args: any[]) {} - function vararg(...args: VarArg) { - foo(...args); - } - `.expectDiagnosticsToMatchSnapshot([annotationRemoved.code]); -}); - -test("luaiterator removed", () => { - util.testFunction` - const arr = ["a", "b", "c"]; - /** @luaIterator */ - interface Iter extends Iterable {} - function luaIter(): Iter { - let i = 0; - return (() => arr[i++]) as any; - } - let result = ""; - for (let e of luaIter()) { result += e; } - return result; - `.expectDiagnosticsToMatchSnapshot([annotationRemoved.code]); -}); - -const tableLibClass = ` -/** @luaTable */ -declare class Table { - length: number; - constructor(notAllowed?: any); - set(key?: K, value?: V, notAllowed?: any): void; - get(key?: K, notAllowed?: any): V; - other(): void; -} -declare let tbl: Table; -`; - -test("LuaTable removed warning constructor", () => { - util.testModule("const table = new Table();") - .setTsHeader(tableLibClass) - .expectDiagnosticsToMatchSnapshot([annotationRemoved.code]); -}); - -test("LuaTable removed warning property access length", () => { - util.testFunction` - return tbl.length; - ` - .setTsHeader(tableLibClass) - .expectDiagnosticsToMatchSnapshot([annotationRemoved.code]); -}); - -test("LuaTable removed warning property access get", () => { - util.testFunction` - return tbl.get("foo"); - ` - .setTsHeader(tableLibClass) - .expectDiagnosticsToMatchSnapshot([annotationRemoved.code]); -}); - -test("LuaTable deprecation warning property access set", () => { - util.testFunction` - return tbl.set("foo", 0); - ` - .setTsHeader(tableLibClass) - .expectDiagnosticsToMatchSnapshot([annotationRemoved.code]); -}); diff --git a/test/unit/annotations/tupleReturn.spec.ts b/test/unit/annotations/tupleReturn.spec.ts deleted file mode 100644 index 337e52c37..000000000 --- a/test/unit/annotations/tupleReturn.spec.ts +++ /dev/null @@ -1,431 +0,0 @@ -import * as util from "../../util"; - -const expectNoUnpack: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).not.toContain("unpack"); - -test("Tuple Return Access", () => { - util.testFunction` - /** @tupleReturn */ - function tuple(): [number, number, number] { return [3, 5, 1]; } - return tuple()[2]; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Destruct Declaration", () => { - util.testFunction` - /** @tupleReturn */ - function tuple(): [number, number, number] { return [3,5,1]; } - const [,b,c] = tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Destruct Assignment", () => { - util.testFunction` - /** @tupleReturn */ - function tuple(): [number, number] { return [3,6]; } - let [a,b] = [1,2]; - [b,a] = tuple(); - return a - b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Static Method Return Destruct", () => { - util.testFunction` - class Test { - /** @tupleReturn */ - static tuple(): [number, number, number] { return [3,5,1]; } - } - const [a,b,c] = Test.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Static Function Property Return Destruct", () => { - util.testFunction` - class Test { - /** @tupleReturn */ - static tuple: () => [number, number, number] = () => [3,5,1]; - } - const [a,b,c] = Test.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Non-Static Method Return Destruct", () => { - util.testFunction` - class Test { - /** @tupleReturn */ - tuple(): [number, number, number] { return [3,5,1]; } - } - const t = new Test(); - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Non-Static Function Property Return Destruct", () => { - util.testFunction` - class Test { - /** @tupleReturn */ - tuple: () => [number, number, number] = () => [3,5,1]; - } - const t = new Test(); - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Interface Method Return Destruct", () => { - util.testFunction` - interface Test { - /** @tupleReturn */ - tuple(): [number, number, number]; - } - const t: Test = { - tuple() { return [3,5,1]; } - }; - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Interface Function Property Return Destruct", () => { - util.testFunction` - interface Test { - /** @tupleReturn */ - tuple: () => [number, number, number]; - } - const t: Test = { - tuple: () => [3,5,1] - }; - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Object Literal Method Return Destruct", () => { - util.testFunction` - const t = { - /** @tupleReturn */ - tuple() { return [3,5,1]; } - }; - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Object Literal Function Property Return Destruct", () => { - util.testFunction` - const t = { - /** @tupleReturn */ - tuple: () => [3,5,1] - }; - const [a,b,c] = t.tuple(); - return b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Arrow Function", () => { - util.testFunction` - const fn = /** @tupleReturn */ (s: string) => [s, "bar"]; - const [a, b] = fn("foo"); - return a + b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Inference", () => { - util.testFunction` - /** @tupleReturn */ interface Fn { (s: string): [string, string] } - const fn: Fn = s => [s, "bar"]; - const [a, b] = fn("foo"); - return a + b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Inference as Argument", () => { - util.testFunction` - /** @tupleReturn */ interface Fn { (s: string): [string, string] } - function foo(fn: Fn) { - const [a, b] = fn("foo"); - return a + b; - } - return foo(s => [s, "bar"]); - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Inference as Elipsis Argument", () => { - util.testFunction` - /** @tupleReturn */ interface Fn { (s: string): [string, string] } - function foo(_: number, ...fn: Fn[]) { - const [a, b] = fn[0]("foo"); - return a + b; - } - return foo(0, s => [s, "bar"]); - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return Inference as Elipsis Tuple Argument", () => { - util.testFunction` - /** @tupleReturn */ interface Fn { (s: string): [string, string] } - function foo(_: number, ...fn: [number, Fn]) { - const [a, b] = fn[1]("foo"); - return a + b; - } - return foo(0, 0, s => [s, "bar"]); - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return in Spread", () => { - util.testFunction` - /** @tupleReturn */ function foo(): [string, string] { - return ["foo", "bar"]; - } - function bar(a: string, b: string) { - return a + b; - } - return bar(...foo()); - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Type Alias", () => { - util.testFunction` - /** @tupleReturn */ type Fn = () => [number, number]; - const fn: Fn = () => [1, 2]; - const [a, b] = fn(); - return a + b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Interface", () => { - util.testFunction` - /** @tupleReturn */ interface Fn { (): [number, number]; } - const fn: Fn = () => [1, 2]; - const [a, b] = fn(); - return a + b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Interface Signature", () => { - util.testFunction` - interface Fn { - /** @tupleReturn */ (): [number, number]; - } - const fn: Fn = () => [1, 2]; - const [a, b] = fn(); - return a + b; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Overload", () => { - util.testFunction` - function fn(a: number): number; - /** @tupleReturn */ function fn(a: string, b: string): [string, string]; - function fn(a: number | string, b?: string): number | [string, string] { - if (typeof a === "number") { - return a; - } else { - return [a, b as string]; - } - } - const a = fn(3); - const [b, c] = fn("foo", "bar"); - return a + b + c - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Interface Overload", () => { - util.testFunction` - interface Fn { - (a: number): number; - /** @tupleReturn */ (a: string, b: string): [string, string]; - } - const fn = ((a: number | string, b?: string): number | [string, string] => { - if (typeof a === "number") { - return a; - } else { - return [a, b as string]; - } - }) as Fn; - const a = fn(3); - const [b, c] = fn("foo", "bar"); - return a + b + c - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return on Interface Method Overload", () => { - util.testFunction` - interface Foo { - foo(a: number): number; - /** @tupleReturn */ foo(a: string, b: string): [string, string]; - } - const bar = { - foo: (a: number | string, b?: string): number | [string, string] => { - if (typeof a === "number") { - return a; - } else { - return [a, b as string]; - } - } - } as Foo; - const a = bar.foo(3); - const [b, c] = bar.foo("foo", "bar"); - return a + b + c; - ` - .tap(expectNoUnpack) - .expectToMatchJsResult(); -}); - -test("Tuple Return vs Non-Tuple Return Overload", () => { - const luaHeader = ` - function fn(a, b) - if type(a) == "number" then - return {a, a + 1} - else - return a, b - end - end - `; - - const tsHeader = ` - declare function fn(this: void, a: number): [number, number]; - /** @tupleReturn */ declare function fn(this: void, a: string, b: string): [string, string]; - `; - - util.testFunction` - const [a, b] = fn(3); - const [c, d] = fn("foo", "bar"); - return (a + b) + c + d; - ` - .setTsHeader(tsHeader) - .setLuaHeader(luaHeader) - .expectToEqual("7foobar"); -}); - -test("TupleReturn assignment", () => { - util.testFunction` - /** @tupleReturn */ - function abc(this: void): number[] { - return [3, 5]; - } - let [a, b] = abc(); - return { a, b }; - `.expectToMatchJsResult(); -}); - -test("TupleReturn Single assignment", () => { - util.testFunction` - /** @tupleReturn */ - function abc(this: void): [number, string] { - return [3, "foo"]; - } - let a = abc(); - a = abc(); - return a; - `.expectToMatchJsResult(); -}); - -test("TupleReturn interface assignment", () => { - const lua = util.testFunction` - interface def { - /** @tupleReturn */ - abc(); - } declare const jkl : def; - let [a,b] = jkl.abc(); - `.getMainLuaCodeChunk(); - - expect(lua).toContain("local a, b = jkl:abc()"); -}); - -test("TupleReturn namespace assignment", () => { - const lua = util.testFunction` - declare namespace def { - /** @tupleReturn */ - function abc(this: void) {} - } - let [a,b] = def.abc(); - `.getMainLuaCodeChunk(); - - expect(lua).toContain("local a, b = def.abc()"); -}); - -test("TupleReturn method assignment", () => { - const lua = util.testFunction` - declare class def { - /** @tupleReturn */ - abc() { return [1,2,3]; } - } const jkl = new def(); - let [a,b] = jkl.abc(); - `.getMainLuaCodeChunk(); - - expect(lua).toContain("local a, b = jkl:abc()"); -}); - -test("TupleReturn functional", () => { - util.testFunction` - /** @tupleReturn */ - function abc(): [number, string] { return [3, "a"]; } - const [a, b] = abc(); - return b + a; - `.expectToMatchJsResult(); -}); - -test("TupleReturn single", () => { - util.testFunction` - /** @tupleReturn */ - function abc(): [number, string] { return [3, "a"]; } - const res = abc(); - return res.length - `.expectToMatchJsResult(); -}); - -test("TupleReturn in expression", () => { - util.testFunction` - /** @tupleReturn */ - function abc(): [number, string] { return [3, "a"]; } - return abc()[1] + abc()[0]; - `.expectToMatchJsResult(); -}); diff --git a/test/unit/assignments.spec.ts b/test/unit/assignments.spec.ts index 5f9209c41..3ad7da149 100644 --- a/test/unit/assignments.spec.ts +++ b/test/unit/assignments.spec.ts @@ -94,16 +94,17 @@ test.each([ "[x[1], x[0]] = tr()", "x = [y[1], y[0]]", "[x[0], x[1]] = [y[1], y[0]]", -])("Tuple assignment expressions (%p)", expression => { +])("multi return assignment expressions (%p)", expression => { util.testFunction` let x: [string, string] = ["x0", "x1"]; let y: [string, string] = ["y0", "y1"]; function t(): [string, string] { return ["t0", "t1"] }; - /** @tupleReturn */ - function tr(): [string, string] { return ["tr0", "tr1"] }; + function tr(): LuaMultiReturn<[string, string]> { return $multi("tr0", "tr1"); }; const r = ${expression}; return \`\${r[0]},\${r[1]}\` - `.expectToMatchJsResult(); + ` + .withLanguageExtensions() + .expectToMatchJsResult(); }); test.each([ @@ -134,6 +135,17 @@ test.each([ `.expectToMatchJsResult(); }); +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1277 +test("Compound assignment as expression (#1277)", () => { + util.testFunction` + let foo = { + bar: false as any + } + const result = foo.bar ||= true; + return { result, foo }; + `.expectToMatchJsResult(); +}); + test.each([ "++o.p", "o.p++", @@ -330,7 +342,7 @@ test.each([ test("local variable declaration referencing self indirectly", () => { util.testFunction` - let cb: () => void; + let cb: () => void = () => { throw "Expecting this to be overwritten"; }; function foo(newCb: () => void) { cb = newCb; @@ -348,7 +360,7 @@ test("local variable declaration referencing self indirectly", () => { test("local multiple variable declaration referencing self indirectly", () => { util.testFunction` - let cb: () => void; + let cb: () => void = () => { throw "Expecting this to be overwritten"; }; function foo(newCb: () => void) { cb = newCb; @@ -383,7 +395,7 @@ describe.each(["x &&= y", "x ||= y"])("boolean compound assignment (%p)", assign test.each([undefined, 3])("nullish coalescing compound assignment", initialValue => { util.testFunction` - let x: number = ${util.formatCode(initialValue)}; + let x: number | undefined = ${util.formatCode(initialValue)}; x ??= 5; return x; `.expectToMatchJsResult(); @@ -416,7 +428,7 @@ test.each([ * x.y ||= z is translated to x.y || (x.y = z). * x.y &&= z is translated to x.y && (x.y = z). * x.y ||= z is translated to x.y !== undefined && (x.y = z). - + Test if setter in Lua is called same nr of times as in JS. */ util.testModule` @@ -448,7 +460,7 @@ test.each([ * x.y ||= z is translated to x.y || (x.y = z). * x.y &&= z is translated to x.y && (x.y = z). * x.y ||= z is translated to x.y !== undefined && (x.y = z). - + Test if setter in Lua is called same nr of times as in JS. */ util.testModule` @@ -494,3 +506,13 @@ test.each([ return [obj, objGot]; `.expectToMatchJsResult(); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1412 +test("invalid assignment to call expression (#1412)", () => { + util.testFunction` + function foo(): number { + return 3; + } + foo() += 4; + `.expectNoTranspileException(); +}); diff --git a/test/unit/builtins/__snapshots__/console.spec.ts.snap b/test/unit/builtins/__snapshots__/console.spec.ts.snap index f06d99dae..2c9d8578c 100644 --- a/test/unit/builtins/__snapshots__/console.spec.ts.snap +++ b/test/unit/builtins/__snapshots__/console.spec.ts.snap @@ -13,7 +13,7 @@ exports[`console.assert ("console.assert(false, \\"message %%s\\", \\"info\\")") function ____exports.__main(self) assert( false, - string.format(\\"message %%s\\", \\"info\\") + string.format("message %%s", "info") ) end return ____exports" @@ -24,7 +24,7 @@ exports[`console.assert ("console.assert(false, \\"message %s\\", \\"info\\")") function ____exports.__main(self) assert( false, - string.format(\\"message %s\\", \\"info\\") + string.format("message %s", "info") ) end return ____exports" @@ -33,7 +33,7 @@ return ____exports" exports[`console.assert ("console.assert(false, \\"message\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - assert(false, \\"message\\") + assert(false, "message") end return ____exports" `; @@ -41,7 +41,7 @@ return ____exports" exports[`console.assert ("console.assert(false, \\"message\\", \\"more\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - assert(false, \\"message\\", \\"more\\") + assert(false, "message", "more") end return ____exports" `; @@ -57,9 +57,7 @@ return ____exports" exports[`console.error ("console.error(\\"Hello %%s\\", \\"there\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - string.format(\\"Hello %%s\\", \\"there\\") - ) + print(string.format("Hello %%s", "there")) end return ____exports" `; @@ -67,9 +65,7 @@ return ____exports" exports[`console.error ("console.error(\\"Hello %s\\", \\"there\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - string.format(\\"Hello %s\\", \\"there\\") - ) + print(string.format("Hello %s", "there")) end return ____exports" `; @@ -77,7 +73,7 @@ return ____exports" exports[`console.error ("console.error(\\"Hello\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print(\\"Hello\\") + print("Hello") end return ____exports" `; @@ -85,7 +81,7 @@ return ____exports" exports[`console.error ("console.error(\\"Hello\\", \\"There\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print(\\"Hello\\", \\"There\\") + print("Hello", "There") end return ____exports" `; @@ -101,9 +97,7 @@ return ____exports" exports[`console.info ("console.info(\\"Hello %%s\\", \\"there\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - string.format(\\"Hello %%s\\", \\"there\\") - ) + print(string.format("Hello %%s", "there")) end return ____exports" `; @@ -111,9 +105,7 @@ return ____exports" exports[`console.info ("console.info(\\"Hello %s\\", \\"there\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - string.format(\\"Hello %s\\", \\"there\\") - ) + print(string.format("Hello %s", "there")) end return ____exports" `; @@ -121,7 +113,7 @@ return ____exports" exports[`console.info ("console.info(\\"Hello\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print(\\"Hello\\") + print("Hello") end return ____exports" `; @@ -129,7 +121,7 @@ return ____exports" exports[`console.info ("console.info(\\"Hello\\", \\"There\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print(\\"Hello\\", \\"There\\") + print("Hello", "There") end return ____exports" `; @@ -145,9 +137,7 @@ return ____exports" exports[`console.log ("console.log(\\"Hello %%s\\", \\"there\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - string.format(\\"Hello %%s\\", \\"there\\") - ) + print(string.format("Hello %%s", "there")) end return ____exports" `; @@ -155,9 +145,7 @@ return ____exports" exports[`console.log ("console.log(\\"Hello %s\\", \\"there\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - string.format(\\"Hello %s\\", \\"there\\") - ) + print(string.format("Hello %s", "there")) end return ____exports" `; @@ -165,7 +153,7 @@ return ____exports" exports[`console.log ("console.log(\\"Hello\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print(\\"Hello\\") + print("Hello") end return ____exports" `; @@ -173,7 +161,7 @@ return ____exports" exports[`console.log ("console.log(\\"Hello\\", \\"There\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print(\\"Hello\\", \\"There\\") + print("Hello", "There") end return ____exports" `; @@ -181,9 +169,7 @@ return ____exports" exports[`console.trace ("console.trace()") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - debug.traceback() - ) + print(debug.traceback()) end return ____exports" `; @@ -191,11 +177,7 @@ return ____exports" exports[`console.trace ("console.trace(\\"Hello %%s\\", \\"there\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - debug.traceback( - string.format(\\"Hello %%s\\", \\"there\\") - ) - ) + print(debug.traceback(string.format("Hello %%s", "there"))) end return ____exports" `; @@ -203,11 +185,7 @@ return ____exports" exports[`console.trace ("console.trace(\\"Hello %s\\", \\"there\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - debug.traceback( - string.format(\\"Hello %s\\", \\"there\\") - ) - ) + print(debug.traceback(string.format("Hello %s", "there"))) end return ____exports" `; @@ -215,9 +193,7 @@ return ____exports" exports[`console.trace ("console.trace(\\"Hello\\", \\"there\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - debug.traceback(\\"Hello\\", \\"there\\") - ) + print(debug.traceback("Hello", "there")) end return ____exports" `; @@ -225,9 +201,7 @@ return ____exports" exports[`console.trace ("console.trace(\\"message\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - debug.traceback(\\"message\\") - ) + print(debug.traceback("message")) end return ____exports" `; @@ -243,9 +217,7 @@ return ____exports" exports[`console.warn ("console.warn(\\"Hello %%s\\", \\"there\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - string.format(\\"Hello %%s\\", \\"there\\") - ) + print(string.format("Hello %%s", "there")) end return ____exports" `; @@ -253,9 +225,7 @@ return ____exports" exports[`console.warn ("console.warn(\\"Hello %s\\", \\"there\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print( - string.format(\\"Hello %s\\", \\"there\\") - ) + print(string.format("Hello %s", "there")) end return ____exports" `; @@ -263,7 +233,7 @@ return ____exports" exports[`console.warn ("console.warn(\\"Hello\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print(\\"Hello\\") + print("Hello") end return ____exports" `; @@ -271,7 +241,7 @@ return ____exports" exports[`console.warn ("console.warn(\\"Hello\\", \\"There\\")") 1`] = ` "local ____exports = {} function ____exports.__main(self) - print(\\"Hello\\", \\"There\\") + print("Hello", "There") end return ____exports" `; diff --git a/test/unit/builtins/__snapshots__/math.spec.ts.snap b/test/unit/builtins/__snapshots__/math.spec.ts.snap deleted file mode 100644 index cb324c9c9..000000000 --- a/test/unit/builtins/__snapshots__/math.spec.ts.snap +++ /dev/null @@ -1,81 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Math.PI 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local ____ = math.pi -end -return ____exports" -`; - -exports[`Math.cos() 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - math.cos() -end -return ____exports" -`; - -exports[`Math.log1p(3) 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - math.log(1 + 3) -end -return ____exports" -`; - -exports[`Math.log2(3) 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local ____ = math.log(3) / 0.6931471805599453 -end -return ____exports" -`; - -exports[`Math.log10(3) 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local ____ = math.log(3) / 2.302585092994046 -end -return ____exports" -`; - -exports[`Math.min() 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - math.min() -end -return ____exports" -`; - -exports[`Math.round(3.3) 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - math.floor(3.3 + 0.5) -end -return ____exports" -`; - -exports[`Math.sin() 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - math.sin() -end -return ____exports" -`; - -exports[`const x = Math.log2(3) 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local x = math.log(3) / 0.6931471805599453 -end -return ____exports" -`; - -exports[`const x = Math.log10(3) 1`] = ` -"local ____exports = {} -function ____exports.__main(self) - local x = math.log(3) / 2.302585092994046 -end -return ____exports" -`; diff --git a/test/unit/builtins/array.spec.ts b/test/unit/builtins/array.spec.ts index 037ebe2a7..5e3cd8887 100644 --- a/test/unit/builtins/array.spec.ts +++ b/test/unit/builtins/array.spec.ts @@ -1,3 +1,7 @@ +import { + undefinedInArrayLiteral, + unsupportedArrayWithLengthConstructor, +} from "../../../src/transformation/utils/diagnostics"; import * as util from "../../util"; test("omitted expression", () => { @@ -175,6 +179,23 @@ describe("array.length", () => { `.expectToEqual(new util.ExecutionError(`invalid array length: ${luaSpecialValueString}`)); }); + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1395 + test("in compound assignment (#1395)", () => { + util.testFunction` + const arr = [1,2,3,4]; + const returnVal = arr.length -= 2; + return { arr, returnVal }; + `.expectToMatchJsResult(); + }); + + test("as standalone compound assignment (#1395)", () => { + util.testFunction` + const arr = [1,2,3,4]; + arr.length -= 2; + return arr; + `.expectToMatchJsResult(); + }); + test("in array destructuring", () => { util.testFunction` const array = [0, 1, 2]; @@ -254,6 +275,36 @@ test("tuple.forEach", () => { `.expectToMatchJsResult(); }); +describe("at", () => { + test("valid index", () => { + util.testFunction` + const array = [1, 2, 3, 4]; + return array.at(2); + `.expectToMatchJsResult(); + }); + + test("invalid index", () => { + util.testFunction` + const array = [1, 2, 3, 4]; + return array.at(10); + `.expectToMatchJsResult(); + }); + + test("valid negative index", () => { + util.testFunction` + const array = [1, 2, 3, 4]; + return array.at(-2); + `.expectToMatchJsResult(); + }); + + test("invalid negative index", () => { + util.testFunction` + const array = [1, 2, 3, 4]; + return array.at(-10); + `.expectToMatchJsResult(); + }); +}); + test("array.forEach (%p)", () => { util.testFunction` const array = [0, 1, 2, 3]; @@ -274,7 +325,7 @@ test.each([ { array: [0, 2, 4, 8], predicate: "false" }, ])("array.find (%p)", ({ array, predicate }) => { util.testFunction` - const array = ${util.formatCode(array)}; + const array: number[] = ${util.formatCode(array)}; return array.find((elem, index, arr) => ${predicate} && arr[index] === elem); `.expectToMatchJsResult(); }); @@ -286,7 +337,7 @@ test.each([ { array: [0, 2, 4, 8], searchElement: 8 }, ])("array.findIndex (%p)", ({ array, searchElement }) => { util.testFunction` - const array = ${util.formatCode(array)}; + const array: number[] = ${util.formatCode(array)}; return array.findIndex((elem, index, arr) => elem === ${searchElement} && arr[index] === elem); `.expectToMatchJsResult(); }); @@ -370,9 +421,16 @@ test.each([ { array: [0, 1, 2, 3], start: 1, deleteCount: undefined }, { array: [0, 1, 2, 3], start: 1, deleteCount: null }, ])("array.splice (%p)", ({ array, start, deleteCount, newElements = [] }) => { + const deleteCountCode = + deleteCount === undefined + ? "undefined as any" + : deleteCount === null + ? "null as any" + : util.formatCode(deleteCount); + const newElementsCode = newElements.length > 0 ? ", " + util.formatCode(...newElements) : ""; util.testFunction` - const array = ${util.formatCode(array)}; - array.splice(${util.formatCode(start, deleteCount, ...newElements)}); + const array: number[] = ${util.formatCode(array)}; + array.splice(${util.formatCode(start)}, ${deleteCountCode}${newElementsCode}); return array; `.expectToMatchJsResult(); }); @@ -459,8 +517,11 @@ test("array.join without separator argument", () => { util.testExpression`["test1", "test2"].join()`.expectToMatchJsResult(); }); +test("array.indexOf empty array", () => { + util.testExpression`([] as string[]).indexOf("test1")`.expectToMatchJsResult(); +}); + test.each([ - { array: [], args: ["test1"] }, { array: ["test1"], args: ["test1"] }, { array: ["test1", "test2"], args: ["test2"] }, { array: ["test1", "test2", "test3"], args: ["test3", 1] }, @@ -471,14 +532,40 @@ test.each([ util.testExpression`${util.formatCode(array)}.indexOf(${util.formatCode(...args)})`.expectToMatchJsResult(); }); -test.each([{ args: [1] }, { args: [1, 2, 3] }])("array.push (%p)", ({ args }) => { +test.each([ + { args: "1" }, + { args: "1, 2, 3" }, + { args: "...[1, 2, 3]" }, + { args: "1, 2, ...[3, 4]" }, + { args: "1, 2, ...[3, 4], 5" }, +])("array.push (%p)", ({ args }) => { util.testFunction` const array = [0]; - const value = array.push(${util.formatCode(...args)}); + const value = array.push(${args}); return { array, value }; `.expectToMatchJsResult(); }); +test("array.push(...)", () => { + util.testFunction` + const array = [0]; + function push(...args: any[]) { + return array.push(...args); + } + const value = push(1, 2, 3); + return { array, value }; + `.expectToMatchJsResult(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1256 +test.each(["[1, 2, 3]", "undefined"])("array push on optional array", value => { + util.testFunction` + const arr = ${value} as number[] | undefined; + arr?.push(4); + return arr + `.expectToMatchJsResult(); +}); + test.each([ { array: [1, 2, 3], expected: [3, 2] }, { array: [1, 2, 3, null], expected: [3, 2] }, @@ -494,7 +581,7 @@ test.each([{ array: [1, 2, 3] }, { array: [1, 2, 3, 4] }, { array: [1] }, { arra "array.reverse (%p)", ({ array }) => { util.testFunction` - const array = ${util.formatCode(array)}; + const array: number[] = ${util.formatCode(array)}; array.reverse(); return array; `.expectToMatchJsResult(); @@ -503,7 +590,7 @@ test.each([{ array: [1, 2, 3] }, { array: [1, 2, 3, 4] }, { array: [1] }, { arra test.each([{ array: [1, 2, 3] }, { array: [1] }, { array: [] }])("array.shift (%p)", ({ array }) => { util.testFunction` - const array = ${util.formatCode(array)}; + const array: number[] = ${util.formatCode(array)}; const value = array.shift(); return { array, value }; `.expectToMatchJsResult(); @@ -516,7 +603,7 @@ test.each([ { array: [], args: [1] }, ])("array.unshift (%p)", ({ array, args }) => { util.testFunction` - const array = ${util.formatCode(array)}; + const array: number[] = ${util.formatCode(array)}; const value = array.unshift(${util.formatCode(...args)}); return { array, value }; `.expectToMatchJsResult(); @@ -524,7 +611,7 @@ test.each([ test.each([{ array: [4, 5, 3, 2, 1] }, { array: [1] }, { array: [] }])("array.sort (%p)", ({ array }) => { util.testFunctionTemplate` - const array = ${array}; + const array: number[] = ${array}; array.sort(); return array; `.expectToMatchJsResult(); @@ -582,7 +669,7 @@ describe.each(["reduce", "reduceRight"])("array.%s", reduce => { }); test("empty no initial", () => { - util.testExpression`[].${reduce}(() => {})`.expectToMatchJsResult(true); + util.testExpression`([] as any[]).${reduce}(() => {})`.expectToMatchJsResult(true); }); test("undefined returning callback", () => { @@ -598,7 +685,7 @@ test.each([{ array: [] }, { array: ["a", "b", "c"] }, { array: [{ foo: "foo" }, "array.entries (%p)", ({ array }) => { util.testFunction` - const array = ${util.formatCode(array)}; + const array: any[] = ${util.formatCode(array)}; const result = []; for (const [i, v] of array.entries()) { result.push([i, v]); @@ -670,7 +757,186 @@ test("Array.isArray returns true for empty objects", () => { util.testExpression`Array.isArray({})`.expectToEqual(true); }); +test.each([ + "[1, 2, 3]", + "(new Set([1, 2, 3])).values()", + "[1, 2, 3], value => value * 2", + "{ length: 3 }, (_, index) => index + 1", +])("Array.from(%p)", valueString => { + util.testExpression`Array.from(${valueString})`.expectToMatchJsResult(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1576 +test("array.from with non-array iterable (#1576)", () => { + util.testFunction` + const map = new Map().set(1, 2); + return Array.from(map, ([v,k]) => ({k,v})); + `.expectToMatchJsResult(); +}); + +// Array.of +test.each(["1, 2, 3", "", "...[1, 2, 3], 4, 5, 6"])("Array.of(%p)", valueString => { + util.testExpression`Array.of(${valueString})`.expectToMatchJsResult(); +}); + // Test fix for https://github.com/TypeScriptToLua/TypeScriptToLua/issues/738 test("array.prototype.concat issue #738", () => { util.testExpression`([] as any[]).concat(13, 323, {x: 3}, [2, 3])`.expectToMatchJsResult(); }); + +test.each(["[1, undefined, 3]", "[1, null, 3]", "[1, undefined, 2, null]"])( + "not allowed to use null or undefined in array literals (%p)", + literal => { + util.testExpression(literal).expectToHaveDiagnostics([undefinedInArrayLiteral.code]); + } +); + +test("not allowed to use null or undefined in array literals ([undefined, null, 1])", () => { + util.testExpression`[undefined, null, 1]`.expectToHaveDiagnostics([ + undefinedInArrayLiteral.code, + undefinedInArrayLiteral.code, + ]); +}); + +test.each([ + "[]", + "[1, 2, undefined]", + "[1, 2, null]", + "[1, undefined, undefined, null]", + "[undefined]", + "[undefined, null]", +])("trailing undefined or null are allowed in array literal (%p)", literal => { + util.testExpression(literal).expectToHaveNoDiagnostics(); +}); + +describe("array.fill", () => { + test.each(["([] as number[])", "[1]", "[1,2,3,4]"])( + "Fills full length of array without other parameters (%p)", + arr => { + util.testExpression`${arr}.fill(5)`.expectToMatchJsResult(); + } + ); + + test.each(["[1,2,3]", "[1,2,3,4,5,6]"])("Fills starting from start parameter (%p)", arr => { + util.testExpression`${arr}.fill(5, 3)`.expectToMatchJsResult(); + }); + + test("handles negative start parameter", () => { + util.testExpression`[1,2,3,4,5,6,7].fill(8, -3)`.expectToMatchJsResult(); + }); + + test("handles negative end parameter", () => { + util.testExpression`[1,2,3,4,5,6,7].fill(8, -5, -2)`.expectToMatchJsResult(); + }); + + test("Fills starting from start parameter, up to ending parameter", () => { + util.testExpression`[1,2,3,4,5,6,7,8].fill(5, 2, 6)`.expectToMatchJsResult(); + }); + + // NOTE: This is different from the default ECMAScript specification for the behavior, but for Lua this is much more useful + test("Extends size of the array if ending size is larger than array", () => { + util.testExpression`[1,2,3].fill(5, 0, 6)`.expectToEqual([5, 5, 5, 5, 5, 5]); + }); +}); + +// Issue #1218: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1218 +test.each(["[1, 2, 3]", "undefined"])("prototype call on nullable array (%p)", value => { + util.testFunction` + function find(arr?: number[]) { + return arr?.indexOf(2); + } + return find(${value}); + ` + .setOptions({ strictNullChecks: true }) + .expectToMatchJsResult(); +}); + +describe("copying array methods", () => { + test("toReversed", () => { + util.testFunction` + const original = [1,2,3,4,5]; + const reversed = original.toReversed(); + + return {original, reversed}; + `.expectToEqual({ + original: [1, 2, 3, 4, 5], + reversed: [5, 4, 3, 2, 1], + }); + }); + + test("toSorted", () => { + util.testFunction` + const original = [5,2,1,4,3]; + const sorted = original.toSorted(); + + return {original, sorted}; + `.expectToEqual({ + original: [5, 2, 1, 4, 3], + sorted: [1, 2, 3, 4, 5], + }); + }); + + test("toSpliced", () => { + util.testFunction` + const original = [1,2,3,4,5]; + const spliced = original.toSpliced(2, 2, 10, 11); + + return {original, spliced}; + `.expectToEqual({ + original: [1, 2, 3, 4, 5], + spliced: [1, 2, 10, 11, 5], + }); + }); + + test("with", () => { + util.testFunction` + const original = [1,2,3,4,5]; + const updated = original.with(2, 10); + + return {original, updated}; + `.expectToEqual({ + original: [1, 2, 3, 4, 5], + updated: [1, 2, 10, 4, 5], + }); + }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1605 +test("array indexing in optional chain (#1605)", () => { + util.testModule` + interface Foo extends Array {} + const b: {arr?: Foo} = {arr:[1,2]}; + export const t = b.arr?.[1]; + + const c: Foo | undefined = b.arr; + export const u = c?.[1]; + ` + .setOptions({ strict: true }) // crucial to reproducing for some reason + .expectToMatchJsResult(); +}); + +test("new Array()", () => { + util.testFunction` + const arr = new Array(); + arr.push(1,2,3); + return arr; + `.expectToMatchJsResult(); +}); + +test("new Array()", () => { + util.testFunction` + const arr = new Array(); + arr.push(1,2,3); + return arr; + `.expectToMatchJsResult(); +}); + +test("new Array(length)", () => { + util.testFunction` + const arr = new Array(10); + `.expectToHaveDiagnostics([unsupportedArrayWithLengthConstructor.code]); +}); + +test("new Array(...items)", () => { + util.testExpression`new Array(1,2,3,4,5) `.expectToMatchJsResult(); +}); diff --git a/test/unit/builtins/async-await.spec.ts b/test/unit/builtins/async-await.spec.ts new file mode 100644 index 000000000..1c5d23018 --- /dev/null +++ b/test/unit/builtins/async-await.spec.ts @@ -0,0 +1,1252 @@ +import { ModuleKind, ScriptTarget } from "typescript"; +import { LuaTarget } from "../../../src"; +import { + awaitMustBeInAsyncFunction, + unsupportedAsyncGenerator, + unsupportedForAwaitOf, + unsupportedForTargetButOverrideAvailable, +} from "../../../src/transformation/utils/diagnostics"; +import * as util from "../../util"; + +const promiseTestLib = ` +// Some logging utility, useful for asserting orders of operations + +const allLogs: any[] = []; +function log(...values: any[]) { + allLogs.push(...values); +} + +// Create a promise and store its resolve and reject functions, useful for creating pending promises + +function defer() { + let resolve: (data: any) => void = () => {}; + let reject: (reason: string) => void = () => {}; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; +}`; + +test("high amount of chained awaits doesn't cause stack overflow", () => { + util.testFunction` + let result = "not executed"; + + async function delay() { + return; + } + + async function loop() { + try { + for (let i = 0; i < 500000; ++i) { + await delay(); + } + result = "success"; + } catch (e: any) { + result = e; + } + } + + loop(); + + return result; + `.expectToEqual("success"); +}); + +test("can await already resolved promise", () => { + util.testFunction` + const result: unknown[] = []; + async function abc() { + return await Promise.resolve(30); + } + abc().then(value => result.push(value)); + + return result; + `.expectToEqual([30]); +}); + +test("can await already rejected promise", () => { + util.testFunction` + const result: unknown[] = []; + async function abc() { + return await Promise.reject("test rejection"); + } + abc().catch(reason => result.push(reason)); + + return result; + `.expectToEqual(["test rejection"]); +}); + +test("can await pending promise", () => { + util.testFunction` + const { promise, resolve } = defer(); + promise.then(data => log("resolving original promise", data)); + + async function abc() { + return await promise; + } + + const awaitingPromise = abc(); + awaitingPromise.then(data => log("resolving awaiting promise", data)); + + resolve("resolved data"); + + return allLogs; + + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["resolving original promise", "resolved data", "resolving awaiting promise", "resolved data"]); +}); + +test("can await non-promise values", () => { + util.testFunction` + async function foo() { + return await "foo"; + } + + async function bar() { + return await { foo: await foo(), bar: "bar" }; + } + + async function baz() { + return (await bar()).foo + (await bar()).bar; + } + + const { state, value } = baz() as any; + return { state, value }; + `.expectToEqual({ + state: 1, // __TS__PromiseState.Fulfilled + value: "foobar", + }); +}); + +test.each(["async function abc() {", "const abc = async () => {"])( + "can return non-promise from async function (%p)", + functionHeader => { + util.testFunction` + const { promise, resolve } = defer(); + promise.then(data => log("resolving original promise", data)); + + ${functionHeader} + await promise; + return "abc return data" + } + + const awaitingPromise = abc(); + awaitingPromise.then(data => log("resolving awaiting promise", data)); + + resolve("resolved data"); + + return allLogs; + + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + "resolving original promise", + "resolved data", + "resolving awaiting promise", + "abc return data", + ]); + } +); + +test.each(["async function abc() {", "const abc = async () => {"])( + "can have multiple awaits in async function (%p)", + functionHeader => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + const { promise: promise2, resolve: resolve2 } = defer(); + const { promise: promise3, resolve: resolve3 } = defer(); + promise1.then(data => log("resolving promise1", data)); + promise2.then(data => log("resolving promise2", data)); + promise3.then(data => log("resolving promise3", data)); + + ${functionHeader} + const result1 = await promise1; + const result2 = await promise2; + const result3 = await promise3; + return [result1, result2, result3]; + } + + const awaitingPromise = abc(); + awaitingPromise.then(data => log("resolving awaiting promise", data)); + + resolve1("data1"); + resolve2("data2"); + resolve3("data3"); + + return allLogs; + + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + "resolving promise1", + "data1", + "resolving promise2", + "data2", + "resolving promise3", + "data3", + "resolving awaiting promise", + ["data1", "data2", "data3"], + ]); + } +); + +test("can make inline async functions", () => { + util.testFunction` + const foo = async function() { return "foo"; }; + const bar = async function() { return await foo(); }; + + const { state, value } = bar() as any; + return { state, value }; + `.expectToEqual({ + state: 1, // __TS__PromiseState.Fulfilled + value: "foo", + }); +}); + +test("can make async lambdas with expression body", () => { + util.testFunction` + const foo = async () => "foo"; + const bar = async () => await foo(); + + const { state, value } = bar() as any; + return { state, value }; + `.expectToEqual({ + state: 1, // __TS__PromiseState.Fulfilled + value: "foo", + }); +}); + +test("can await async function from async function", () => { + util.testFunction` + const { promise, resolve } = defer(); + promise.then(data => log("resolving original promise", data)); + + async function abc() { + return await promise; + } + + async function def() { + return await abc(); + } + + const awaitingPromise = def(); + awaitingPromise.then(data => log("resolving awaiting promise", data)); + + resolve("resolved data"); + + return allLogs; + + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["resolving original promise", "resolved data", "resolving awaiting promise", "resolved data"]); +}); + +test("async function returning value is same as non-async function returning promise", () => { + util.testFunction` + function f(): Promise { + return Promise.resolve(42); + } + + async function fAsync(): Promise { + return 42; + } + + const { state: state1, value: value1 } = f() as any; + const { state: state2, value: value2 } = fAsync() as any; + + return { + state1, value1, + state2, value2 + }; + `.expectToEqual({ + state1: 1, // __TS__PromiseState.Fulfilled + value1: 42, + state2: 1, // __TS__PromiseState.Fulfilled + value2: 42, + }); +}); + +test("correctly handles awaited functions rejecting", () => { + util.testFunction` + const { promise: promise1, reject } = defer(); + const { promise: promise2 } = defer(); + + promise1.then(data => log("resolving promise1", data), reason => log("rejecting promise1", reason)); + promise2.then(data => log("resolving promise2", data)); + + async function abc() { + const result1 = await promise1; + const result2 = await promise2; + return [result1, result2]; + } + + const awaitingPromise = abc(); + awaitingPromise.catch(reason => log("awaiting promise was rejected because:", reason)); + + reject("test reject"); + + return allLogs; + + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["rejecting promise1", "test reject", "awaiting promise was rejected because:", "test reject"]); +}); + +test("can call async function at top-level", () => { + util.testModule` + export let aStarted = false; + async function a() { + aStarted = true; + return 42; + } + + a(); // Call async function (but cannot await) + ` + .setOptions({ module: ModuleKind.ESNext, target: ScriptTarget.ES2017 }) + .expectToEqual({ + aStarted: true, + }); +}); + +test("async function throws error", () => { + util.testFunction` + async function a() { + throw "test throw"; + } + + const { state, rejectionReason } = a() as any; + return { state, rejectionReason }; + `.expectToEqual({ + state: 2, // __TS__PromiseState.Rejected + rejectionReason: "test throw", + }); +}); + +test("async lambda throws error", () => { + util.testFunction` + const a = async () => { + throw "test throw"; + } + + const { state, rejectionReason } = a() as any; + return { state, rejectionReason }; + `.expectToEqual({ + state: 2, // __TS__PromiseState.Rejected + rejectionReason: "test throw", + }); +}); + +test("async function throws object", () => { + util.testFunction` + async function a() { + throw new Error("test throw"); + } + + const { state, rejectionReason } = a() as any; + return { state, rejectionReason }; + `.expectToEqual({ + state: 2, // __TS__PromiseState.Rejected + rejectionReason: { + message: "test throw", + name: "Error", + stack: expect.stringContaining("stack traceback"), + }, + }); +}); + +test.each([ + "await a();", + "const b = await a();", + "export const b = await a();", + "declare function foo(n: number): number; foo(await a());", + "declare function foo(n: number): number; const b = foo(await a());", + "const b = [await a()];", + "const b = [4, await a()];", + "const b = true ? 4 : await a();", +])("cannot await at top-level (%p)", awaitUsage => { + util.testModule` + async function a() { + return 42; + } + + ${awaitUsage} + export {} // Required to make TS happy, cannot await without import/exports + ` + .setOptions({ module: ModuleKind.ESNext, target: ScriptTarget.ES2017 }) + .expectToHaveDiagnostics([awaitMustBeInAsyncFunction.code]); +}); + +test("async function can access varargs", () => { + util.testFunction` + const { promise, resolve } = defer(); + + async function a(...args: string[]) { + log(await promise); + log(args[1]); + } + + const awaitingPromise = a("A", "B", "C"); + resolve("resolved"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["resolved", "B"]); +}); + +test("async function can forward varargs", () => { + util.testFunction` + const { promise, resolve } = defer(); + + async function a(...args: string[]) { + log(await promise); + log(...args); + } + + const awaitingPromise = a("A", "B", "C"); + resolve("resolved"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["resolved", "A", "B", "C"]); +}); + +test("async function adopts pending promise", () => { + util.testFunction` + let resolve: (v: string) => void = () => {}; + async function receive(): Promise { + return new Promise(res => { + resolve = res; + }); + } + + receive().then(v => { + log(v); + }); + + resolve("delayed resolve"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["delayed resolve"]); +}); + +test("async function adopts resolved promise", () => { + util.testFunction` + async function receive(): Promise { + return new Promise(resolve => { + resolve("resolved!"); + }); + } + + receive().then(v => { + log(v); + }); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["resolved!"]); +}); + +test("async function adopts rejected promise", () => { + util.testFunction` + async function receive(): Promise { + return new Promise((_, reject) => { + reject("rejected"); + }); + } + + receive().catch(err => { + log(err); + }); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["rejected"]); +}); + +test("return in catch aborts async method", () => { + util.testFunction` + async function asyncFunc() { + try { + await Promise.reject("rejection"); + log("Should not be seen"); + } catch { + log("C"); + return "result"; + } + log("Should also not be seen"); + } + + asyncFunc().then(v => log("resolved", v)); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["C", "resolved", "result"]); +}); + +test.each(["async function abc() {", "const abc = async () => {"])( + "can throw error after await in async function (%p)", + functionHeader => { + util.testFunction` + const { promise, resolve } = defer(); + promise.then(data => log("resolving first promise", data)); + + ${functionHeader} + await promise; + log("run abc"); + throw "test throw"; + } + + const awaitingPromise = abc(); + awaitingPromise.catch(error => log("caught error", error)); + + resolve("resolved data"); + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["resolving first promise", "resolved data", "run abc", "caught error", "test throw"]); + } +); + +test.each(["async function abc() {", "const abc = async () => {"])( + "can throw object after await in async function (%p)", + functionHeader => { + util.testFunction` + const { promise, resolve } = defer(); + promise.then(data => log("resolving first promise", data)); + + ${functionHeader} + await promise; + log("run abc"); + throw new Error("test throw"); + } + + const awaitingPromise = abc(); + awaitingPromise.catch(error => log("caught error", error)); + + resolve("resolved data"); + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + "resolving first promise", + "resolved data", + "run abc", + "caught error", + { + message: "test throw", + name: "Error", + stack: expect.stringContaining("stack traceback"), + }, + ]); + } +); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1105 +describe("try/catch in async function", () => { + util.testEachVersion( + "await inside try/catch returns inside async function", + () => util.testModule` + export let result = 0; + async function foo(): Promise { + try { + return await new Promise(resolve => resolve(4)); + } catch { + throw "an error occurred in the async function" + } + } + foo().then(value => { + result = value; + }); + `, + // Cannot execute LuaJIT with test runner + { + ...util.expectEachVersionExceptJit(builder => builder.expectToEqual({ result: 4 })), + [LuaTarget.Lua50]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), + [LuaTarget.Lua51]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), + } + ); + + util.testEachVersion( + "await inside try/catch throws inside async function", + () => util.testModule` + export let reason = ""; + async function foo(): Promise { + try { + return await new Promise((resolve, reject) => reject("test error")); + } catch (e) { + throw "an error occurred in the async function: " + e; + } + } + foo().catch(e => { + reason = e; + }); + `, + { + ...util.expectEachVersionExceptJit(builder => + builder.expectToEqual({ reason: "an error occurred in the async function: test error" }) + ), + [LuaTarget.Lua50]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), + [LuaTarget.Lua51]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), + } + ); + + util.testEachVersion( + "await inside try/catch deferred rejection uses catch clause", + () => + util.testModule` + export let reason = ""; + let reject: (reason: string) => void = () => { throw "should be overridden!"; } + + async function foo(): Promise { + try { + return await new Promise((res, rej) => { reject = rej; }); + } catch (e) { + throw "an error occurred in the async function: " + e; + } + } + foo().catch(e => { + reason = e; + }); + reject("test error"); + `, + { + ...util.expectEachVersionExceptJit(builder => + builder.expectToEqual({ reason: "an error occurred in the async function: test error" }) + ), + [LuaTarget.Lua50]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), + [LuaTarget.Lua51]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), + } + ); + + test("try/finally in async function", () => { + util.testModule` + const foo = async () => { + throw "foo error"; + }; + + let finallyCalled = false; + + const run = async () => { + try { + await foo(); + } finally { + finallyCalled = true; + } + return 10; + }; + + let succeeded: any = false; + let rejected: any = false; + + run() + .then(_ => { succeeded = true }) + .catch(e => { rejected = e }); + + export const result = { finallyCalled, succeeded, rejected }; + `.expectToEqual({ + result: { + finallyCalled: true, + succeeded: false, + rejected: "foo error", + }, + }); + }); + + test("async function adopts pending promise in try", () => { + util.testFunction` + let resolve: (v: string) => void = () => {}; + async function receive(): Promise { + try + { + return new Promise(res => { + resolve = res; + }); + } + catch + { + return "catch"; + } + } + + receive().then(v => { + log(v); + }); + + resolve("delayed resolve"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["delayed resolve"]); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1272 + test("Awaiting a rejected promise should halt function execution (#1272)", () => { + util.testModule` + const foo = async () => { + throw new Error('foo error'); + }; + + let halted = true; + let caught = ""; + + const run = async () => { + try { + await foo(); + halted = false; + } catch (err) { + caught = 'catch err: ' + err; + } + }; + + let succeeded: any = false; + let rejected: any = false; + + run() + .then(_ => { succeeded = true }, + e => { rejected = e }); + + export const result = { halted, caught, succeeded, rejected }; + `.expectToEqual({ + result: { + halted: true, + caught: "catch err: Error: foo error", + succeeded: true, + rejected: false, + }, + }); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1272 + test("Awaiting a rejecting promise should halt function execution (#1272)", () => { + util.testModule` + let rej: (error: any) => void = () => {}; + const foo = () => new Promise((_, reject) => { + rej = reject; + }); + + let halted = true; + let caught = ""; + + const run = async () => { + try { + await foo(); + halted = false; + } catch (err) { + caught = 'catch err: ' + err; + } + }; + + let succeeded: any = false; + let rejected: any = false; + + run() + .then(_ => { succeeded = true }, + e => { rejected = e }); + + rej("rejection message"); + + export const result = { halted, caught, succeeded, rejected }; + `.expectToEqual({ + result: { + halted: true, + caught: "catch err: rejection message", + succeeded: true, + rejected: false, + }, + }); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1272 + test("Async try/catch complicated case (#1272)", () => { + util.testModule` + const foo = async () => { + throw new Error('foo error'); + }; + + let halted = true; + let caught = ""; + + const run = async () => { + try { + await foo(); // This throws + } catch (err) { + caught = 'catch err: ' + err; // This catches the async thrown error, function continues + } + try { + throw "throw 2"; // Another throw + } catch (err) { + try { + await foo(); + } catch (err2) { + return \`caught: \${err}, then: \${err2}\`; // Async function should exit here + } + } + halted = false; // This code should not be called + return 10; + }; + + let succeeded: any = false; + let rejected: any = false; + let value: any = undefined; + + run() + .then(v => { succeeded = true; value = v }, + e => { rejected = e }); + + export const result = { halted, caught, succeeded, rejected, value }; + `.expectToEqual({ + result: { + halted: true, + caught: "catch err: Error: foo error", + succeeded: true, + rejected: false, + value: "caught: throw 2, then: Error: foo error", + }, + }); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1659 + test("await inside catch handler resolves correctly (#1659)", () => { + util.testFunction` + let reject: (reason: string) => void = () => {}; + + async function failing() { + return new Promise((_, rej) => { reject = rej; }); + } + + async function run() { + try { + await failing(); + } catch (e) { + log("catch"); + const a = await Promise.resolve(true); + log("a", a); + } + } + + run(); + reject("error"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["catch", "a", true]); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1659 + test("await inside finally handler resolves correctly (#1659)", () => { + util.testFunction` + let reject: (reason: string) => void = () => {}; + + async function failing() { + return new Promise((_, rej) => { reject = rej; }); + } + + async function run() { + try { + await failing(); + } finally { + log("finally"); + const a = await Promise.resolve(true); + log("a", a); + } + } + + run().catch(() => {}); + reject("error"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["finally", "a", true]); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1659 + test("await inside both catch and finally handlers (#1659)", () => { + util.testFunction` + let reject: (reason: string) => void = () => {}; + + async function failing() { + return new Promise((_, rej) => { reject = rej; }); + } + + async function run() { + try { + await failing(); + } catch (e) { + log("catch"); + const a = await Promise.resolve("caught"); + log("a", a); + } finally { + log("finally"); + const b = await Promise.resolve("done"); + log("b", b); + } + } + + run(); + reject("error"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["catch", "a", "caught", "finally", "b", "done"]); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1659 + test("awaited value in catch is returned from async function (#1659)", () => { + util.testFunction` + const failing = defer(); + const recovery = defer(); + + async function run() { + try { + await failing.promise; + return "succeeded"; + } catch (e) { + return await recovery.promise; + } + } + + run().then(value => log("result", value)); + + failing.reject("error"); + recovery.resolve("recovered"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["result", "recovered"]); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1706 + test("return inside try with deferred promise (#1706)", () => { + util.testFunction` + let resolveLater!: (value: string) => void; + + function deferredPromise(): Promise { + return new Promise(resolve => { + resolveLater = (v) => resolve(v); + }); + } + + async function fn(): Promise { + try { + return await deferredPromise(); + } catch { + return 'caught'; + } + log('unreachable!'); + } + + const promise = fn(); + resolveLater('ok'); + promise.then(v => log(v)); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["ok"]); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1706 + test("return inside try in loop with deferred promise (#1706)", () => { + util.testFunction` + let resolveLater!: (value: string) => void; + + function deferredPromise(): Promise { + return new Promise(resolve => { + resolveLater = (v) => resolve(v); + }); + } + + async function fn(): Promise { + while (true) { + try { + return await deferredPromise(); + } catch { + return 'caught'; + } + log('unreachable!'); + } + } + + const promise = fn(); + resolveLater('ok'); + promise.then(v => log(v)); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["ok"]); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1706 + test("return from catch with deferred promise (#1706)", () => { + util.testFunction` + let rejectLater!: (reason: string) => void; + + function deferredPromise(): Promise { + return new Promise((_, reject) => { + rejectLater = (r) => reject(r); + }); + } + + async function fn(): Promise { + try { + return await deferredPromise(); + } catch (e) { + return 'caught: ' + e; + } + log('unreachable!'); + } + + const promise = fn(); + rejectLater('oops'); + promise.then(v => log(v)); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["caught: oops"]); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1706 + util.testEachVersion( + "break inside try in async loop (#1706)", + () => util.testModule` + export let result = "not set"; + async function fn(): Promise { + while (true) { + try { + await Promise.resolve(); + break; + } catch {} + } + result = "done"; + } + fn(); + `, + { + ...util.expectEachVersionExceptJit(builder => builder.expectToEqual({ result: "done" })), + [LuaTarget.Lua50]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), + [LuaTarget.Lua51]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), + } + ); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1706 + util.testEachVersion( + "continue inside try in async loop (#1706)", + () => util.testModule` + export const results: number[] = []; + async function fn(): Promise { + for (let i = 0; i < 3; i++) { + try { + await Promise.resolve(); + if (i === 1) continue; + } catch {} + results.push(i); + } + } + fn(); + `, + { + ...util.expectEachVersionExceptJit(builder => builder.expectToEqual({ results: [0, 2] })), + [LuaTarget.Lua50]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), + [LuaTarget.Lua51]: builder => + builder.expectToHaveDiagnostics([unsupportedForTargetButOverrideAvailable.code]), + } + ); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1706 + test("multi return from try in async function (#1706)", () => { + util.testFunction` + async function fn(): Promise> { + try { + await Promise.resolve(); + return $multi("foo", "bar"); + } catch { + return $multi("err", "err"); + } + } + + let result: string[] = []; + fn().then(v => { const [a, b] = v; result = [a, b]; }); + + return result; + ` + .withLanguageExtensions() + .expectToEqual(["foo", "bar"]); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1706 + test("return inside try with finally (#1706)", () => { + util.testFunction` + let resolveLater!: (value: string) => void; + + function deferredPromise(): Promise { + return new Promise(resolve => { + resolveLater = (v) => resolve(v); + }); + } + + async function fn(): Promise { + try { + return await deferredPromise(); + } finally { + log('finally'); + } + } + + const promise = fn(); + resolveLater('ok'); + promise.then(v => log(v)); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["finally", "ok"]); + }); + + test.each([0, 1, 2])("async re-throw (%p)", i => { + util.testModule` + const i: number = ${i}; + async function foo() { + try { + try { + if (i === 0) { throw "z"; } + } catch (e) { + throw "a"; + } finally { + if (i === 1) { throw "b"; } + } + } catch (e) { + throw (e as string).toUpperCase(); + } finally { + throw "C"; + } + } + export let result: string = "x"; + async function run() { + try { + await foo(); + } catch (e) { + result = (e as string)[(e as string).length - 1]; + } + } + run(); + `.expectToEqual({ result: "C" }); + }); + + test("async: catch re-throws, finally still runs", () => { + util.testModule` + const foo = async () => { + throw "original"; + }; + + let finallyCalled = false; + let caughtError: any = false; + + const run = async () => { + try { + await foo(); + } catch (e) { + throw "re-thrown: " + e; + } finally { + finallyCalled = true; + } + }; + + run().catch(e => { caughtError = e; }); + + export const result = { finallyCalled, caughtError }; + `.expectToEqual({ + result: { + finallyCalled: true, + caughtError: "re-thrown: original", + }, + }); + }); + + test("async: finally throw overrides catch throw", () => { + util.testModule` + const run = async () => { + try { + throw "try-error"; + } catch (e) { + throw "catch-error"; + } finally { + throw "finally-error"; + } + }; + + let caughtError: any = false; + run().catch(e => { caughtError = e; }); + + export const result = caughtError; + `.expectToEqual({ result: "finally-error" }); + }); + + test("async: finally return overrides catch throw", () => { + util.testFunction` + async function run() { + try { + throw "try-error"; + } catch (e) { + throw "catch-error"; + } finally { + return "finally-return"; + } + } + + let result: any; + run().then(v => { result = v; }).catch(e => { result = "rejected: " + e; }); + + return result; + `.expectToEqual("finally-return"); + }); +}); + +describe("async generators are unsupported", () => { + test.each([ + "async function* gen() { yield 1; }", + "const gen = async function*() { yield 1; };", + "const gen = { async *m() { yield 1; } };", + "class C { async *m() { yield 1; } }", + ])("diagnoses %p", source => { + util.testModule` + ${source} + export {} + `.expectToHaveDiagnostics([unsupportedAsyncGenerator.code]); + }); +}); + +test("for-await-of is unsupported", () => { + util.testModule` + async function run(items: AsyncIterable) { + for await (const x of items) { + void x; + } + } + export {} + `.expectToHaveDiagnostics([unsupportedForAwaitOf.code]); +}); diff --git a/test/unit/builtins/loading.spec.ts b/test/unit/builtins/loading.spec.ts index fa213d03f..563870f6d 100644 --- a/test/unit/builtins/loading.spec.ts +++ b/test/unit/builtins/loading.spec.ts @@ -4,40 +4,80 @@ import * as util from "../../util"; describe("luaLibImport", () => { test("inline", () => { - util.testExpression`[0].push(1)` + util.testExpression`[0].indexOf(1)` .setOptions({ luaLibImport: tstl.LuaLibImportKind.Inline }) .tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain('require("lualib_bundle")')) .expectToMatchJsResult(); }); test("require", () => { - util.testExpression`[0].push(1)` + util.testExpression`[0].indexOf(1)` .setOptions({ luaLibImport: tstl.LuaLibImportKind.Require }) .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain('require("lualib_bundle")')) .expectToMatchJsResult(); }); - test("always", () => { - util.testModule`` - .setOptions({ luaLibImport: tstl.LuaLibImportKind.Always }) + function testLualibOnlyHasArrayIndexOf(builder: util.TestBuilder) { + const lualibBundle = builder.getLuaResult().transpiledFiles.find(f => f.outPath.endsWith("lualib_bundle.lua"))!; + expect(lualibBundle).toBeDefined(); + expect(lualibBundle.lua).toEqual(expect.stringMatching("^local function __TS__ArrayIndexOf")); + expect(lualibBundle.lua).not.toContain("__TS__ArrayConcat"); + expect(lualibBundle.lua).not.toContain("Error"); + } + + test("require-minimal", () => { + util.testExpression`[0].indexOf(1)` + .setOptions({ luaLibImport: tstl.LuaLibImportKind.RequireMinimal }) .tap(builder => expect(builder.getMainLuaCodeChunk()).toContain('require("lualib_bundle")')) - .expectToEqual(undefined); + .tap(testLualibOnlyHasArrayIndexOf) + .expectToMatchJsResult(); + }); + + test("require-minimal with lualib in another file", () => { + util.testModule` + import "./other"; + ` + .setOptions({ luaLibImport: tstl.LuaLibImportKind.RequireMinimal }) + .addExtraFile( + "other.lua", + ` +local ____lualib = require("lualib_bundle") +local __TS__ArrayIndexOf = ____lualib.__TS__ArrayIndexOf +__TS__ArrayIndexOf({}, 1) + ` + ) + // note: indent matters in above code, because searching for lualib checks for start & end of line + .tap(testLualibOnlyHasArrayIndexOf) + .expectNoExecutionError(); }); }); -test.each([tstl.LuaLibImportKind.Inline, tstl.LuaLibImportKind.None, tstl.LuaLibImportKind.Require])( - "should not include lualib without code (%p)", - luaLibImport => { - util.testModule``.setOptions({ luaLibImport }).tap(builder => expect(builder.getMainLuaCodeChunk()).toBe("")); - } -); +test.each([ + tstl.LuaLibImportKind.Inline, + tstl.LuaLibImportKind.None, + tstl.LuaLibImportKind.Require, + tstl.LuaLibImportKind.RequireMinimal, +])("should not include lualib without code (%p)", luaLibImport => { + util.testModule``.setOptions({ luaLibImport }).tap(builder => expect(builder.getMainLuaCodeChunk()).toBe("")); +}); test("lualib should not include tstl header", () => { - util.testExpression`[0].push(1)`.tap(builder => + util.testExpression`[0].indexOf(1)`.tap(builder => expect(builder.getMainLuaCodeChunk()).not.toContain("Generated with") ); }); +test("using lualib does not crash when coroutine is not defined", () => { + util.testModule` + declare const _G: any; + declare function require(this: void, name: string): any + + _G.coroutine = undefined; + require("lualib_bundle"); + export const result = 1 + `.expectToEqual({ result: 1 }); +}); + describe("Unknown builtin property", () => { test("access", () => { util.testExpression`Math.unknownProperty` diff --git a/test/unit/builtins/map.spec.ts b/test/unit/builtins/map.spec.ts index 7eec8844d..c9f6c1a2b 100644 --- a/test/unit/builtins/map.spec.ts +++ b/test/unit/builtins/map.spec.ts @@ -209,3 +209,85 @@ describe.each(iterationMethods)("map.%s() preserves insertion order", iterationM `.expectToMatchJsResult(); }); }); + +describe.each(iterationMethods)("map.%s() handles mutation", iterationMethod => { + test("iterator persists after delete", () => { + util.testFunction` + const map = new Map([[1, "a"], [2, "b"]]); + const iter = map.${iterationMethod}(); + map.delete(1); + return iter.next().value; + `.expectToMatchJsResult(); + }); + + test("iterator with delete and add between iterations", () => { + util.testFunction` + const map = new Map([[1, "a"], [2, "b"], [3, "c"]]); + const iter = map.${iterationMethod}(); + iter.next(); // 1 + map.delete(2); + map.set(4, "d"); + const results: IteratorResult[] = []; + let r = iter.next(); + while (!r.done) { results.push({ done: r.done, value: r.value }); r = iter.next(); } + return results; + `.expectToMatchJsResult(); + }); + + test("iterator does not restart after exhaustion", () => { + util.testFunction` + const map = new Map([[1, "a"], [2, "b"]]); + const iter = map.${iterationMethod}(); + const results: boolean[] = []; + results.push(iter.next().done!); + results.push(iter.next().done!); + results.push(iter.next().done!); // should be done + results.push(iter.next().done!); // should still be done, not restart + return results; + `.expectToMatchJsResult(); + }); +}); + +describe("Map.groupBy", () => { + test("empty", () => { + util.testFunction` + const array: number[] = []; + + const map = Map.groupBy(array, (num, index) => { + return num % 2 === 0 ? "even": "odd"; + }); + + return Object.fromEntries(map.entries()); + `.expectToEqual([]); + }); + + test("groupBy", () => { + util.testFunction` + const array = [0, 1, 2, 3, 4, 5]; + + const map = Map.groupBy(array, (num, index) => { + return num % 2 === 0 ? "even": "odd"; + }); + + return Object.fromEntries(map.entries()); + `.expectToEqual({ + even: [0, 2, 4], + odd: [1, 3, 5], + }); + }); + + test("groupBy index", () => { + util.testFunction` + const array = [0, 1, 2, 3, 4, 5]; + + const map = Map.groupBy(array, (num, index) => { + return index < 3 ? "low": "high"; + }); + + return Object.fromEntries(map.entries()); + `.expectToEqual({ + low: [0, 1, 2], + high: [3, 4, 5], + }); + }); +}); diff --git a/test/unit/builtins/math.spec.ts b/test/unit/builtins/math.spec.ts index dd928c68d..d09b331cd 100644 --- a/test/unit/builtins/math.spec.ts +++ b/test/unit/builtins/math.spec.ts @@ -1,28 +1,88 @@ import * as tstl from "../../../src"; import * as util from "../../util"; +// These test are kept to a minimum, +// because we just want to confirm translation is correct +// and not test Lua's built in math functions. +// Differences in math implementations between JS & Lua cause inaccuracies +// therefore test input numbers are "carefully" selected to always match accuratly. +// Lualib implementations are tested separately. test.each([ - "Math.cos()", - "Math.sin()", - "Math.min()", - "Math.log2(3)", - "Math.log10(3)", - "const x = Math.log2(3)", - "const x = Math.log10(3)", - "Math.log1p(3)", - "Math.round(3.3)", + // log + "Math.log(42)", + "Math.log10(10)", + "Math.log2(42)", + "Math.log1p(42)", + // round + "Math.round(0.1)", + "Math.round(0.9)", + "Math.round(0.5)", + // abs + "Math.abs(-42)", + "Math.abs(42)", + // trigometric + "Math.acos(0.42)", + "Math.asin(0.42)", + "Math.atan(0.42)", + "Math.cos(42)", + "Math.sin(42)", + "Math.tan(42)", "Math.PI", + // ceil & floor + "Math.ceil(42.42)", + "Math.floor(42.42)", + // exp + "Math.exp(42)", + // max & min + "Math.max(-42, 42)", + "Math.max(42, -42)", + "Math.max(42, 42)", + "Math.max(-42, -42)", + "Math.min(42, -42)", + "Math.min(-42, 42)", + "Math.min(42, 42)", + "Math.min(-42, -42)", + // pow + "Math.pow(4.2, 4.2)", + "Math.pow(4.2, -4.2)", + "Math.pow(-4.2, -4.2)", + // random + // "Math.random()", + // sqrt + "Math.sqrt(2)", + "Math.sqrt(-2)", + // trunc + "Math.trunc(42.42)", + "Math.trunc(-42.42)", + "Math.trunc(0)", + "Math.trunc(Infinity)", + "Math.trunc(-Infinity)", ])("%s", code => { - // TODO: Remove? - util.testFunction(code).disableSemanticCheck().expectLuaToMatchSnapshot(); + util.testExpression(code).expectToMatchJsResult(); }); +// Hard to test properly +util.testExpression("Math.random()").expectNoExecutionError(); + test.each(["E", "LN10", "LN2", "LOG10E", "LOG2E", "SQRT1_2", "SQRT2"])("Math.%s", constant => { util.testExpression`Math.${constant}`.tap(builder => { expect(builder.getLuaExecutionResult()).toBeCloseTo(builder.getJsExecutionResult()); }); }); +// LuaLib MathSign +test.each([ + "Math.sign(-42)", + "Math.sign(42)", + "Math.sign(-4.2)", + "Math.sign(4.2)", + "Math.sign(0)", + "Math.sign(-Infinity)", +])("%s", code => { + util.testExpression(code).expectToMatchJsResult(); +}); + +// LuaLib Atan2 const expectMathAtan2: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toContain("math.atan2("); const expectMathAtan: util.TapCallback = builder => expect(builder.getMainLuaCodeChunk()).toContain("math.atan("); const expectLualibMathAtan2: util.TapCallback = builder => @@ -31,17 +91,23 @@ const expectLualibMathAtan2: util.TapCallback = builder => util.testEachVersion("Math.atan2", () => util.testExpression`Math.atan2(4, 5)`, { [tstl.LuaTarget.Universal]: builder => builder.tap(expectLualibMathAtan2), [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectMathAtan2), + [tstl.LuaTarget.Lua50]: builder => builder.tap(expectMathAtan2), [tstl.LuaTarget.Lua51]: builder => builder.tap(expectMathAtan2), [tstl.LuaTarget.Lua52]: builder => builder.tap(expectMathAtan2), [tstl.LuaTarget.Lua53]: builder => builder.tap(expectMathAtan), - [tstl.LuaTarget.Lua54]: builder => builder.tap(expectMathAtan2), + [tstl.LuaTarget.Lua54]: builder => builder.tap(expectMathAtan), + [tstl.LuaTarget.Lua55]: builder => builder.tap(expectMathAtan), + [tstl.LuaTarget.Luau]: builder => builder.tap(expectMathAtan2), }); -util.testEachVersion("Math.atan2(4, 5)", () => util.testExpression`Math.atan2(4, 5)`, { - [tstl.LuaTarget.Universal]: builder => builder.expectToMatchJsResult(), - [tstl.LuaTarget.LuaJIT]: false, - [tstl.LuaTarget.Lua51]: builder => builder.expectToMatchJsResult(), - [tstl.LuaTarget.Lua52]: builder => builder.expectToMatchJsResult(), - [tstl.LuaTarget.Lua53]: builder => builder.expectToMatchJsResult(), - [tstl.LuaTarget.Lua54]: builder => builder.expectToMatchJsResult(), -}); +util.testEachVersion( + "Math.atan2(4, 5)", + () => util.testExpression`Math.atan2(4, 5)`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) +); + +util.testEachVersion( + "Math.pow(3, 5)", + () => util.testExpression`Math.pow(3, 5)`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) +); diff --git a/test/unit/builtins/numbers.spec.ts b/test/unit/builtins/numbers.spec.ts index f58209c31..9570e4af2 100644 --- a/test/unit/builtins/numbers.spec.ts +++ b/test/unit/builtins/numbers.spec.ts @@ -1,8 +1,6 @@ import * as util from "../../util"; test.each([ - "NaN === NaN", - "NaN !== NaN", "NaN + NaN", "NaN - NaN", "NaN * NaN", @@ -22,6 +20,8 @@ test.each([ util.testExpression(code).expectToMatchJsResult(); }); +// Current implementation does not allow this +// eslint-disable-next-line jest/no-disabled-tests test.skip.each(["NaN", "Infinity"])("%s reassignment", name => { util.testFunction` const ${name} = 1; @@ -46,6 +46,10 @@ describe("Number", () => { test.each(cases)("isFinite(%p)", value => { util.testExpressionTemplate`Number.isFinite(${value} as any)`.expectToMatchJsResult(); }); + + test.each(cases)("isInteger(%p)", value => { + util.testExpressionTemplate`Number.isInteger(${value} as any)`.expectToMatchJsResult(); + }); }); const toStringRadixes = [undefined, 10, 2, 8, 9, 16, 17, 36, 36.9]; @@ -69,6 +73,26 @@ test.each([ util.testExpressionTemplate`(${value}).toString(2)`.expectToEqual(luaNativeSpecialNumString); }); +const toFixedFractions = [undefined, 0, 1, 2, Math.PI, 5, 99]; +// 1.5, 1.25 and 1.125 fails as rounding differ +const toFixedValues = [-1, 0, 1, Math.PI, -1.1234, -9.99e19, 1e22]; +const toFixedPairs = toFixedValues.flatMap(value => toFixedFractions.map(frac => [value, frac] as const)); +test.each(toFixedPairs)("(%p).toFixed(%p)", (value, frac) => { + util.testExpressionTemplate`(${value}).toFixed(${frac})`.expectToMatchJsResult(); +}); + +test.each([ + [NaN, "(0/0)"], + [Infinity, "(1/0)"], + [-Infinity, "(-(1/0))"], +])("%p.toFixed(2)", (value, luaNativeSpecialNum) => { + // Need to get the actual lua tostring version of inf/nan + // this is platform dependent so we can/should not hardcode it + const luaNativeSpecialNumString = util.testExpression`${luaNativeSpecialNum}.toString()`.getLuaExecutionResult(); + // Cannot use expectToMatchJsResult because this actually wont be the same in JS in Lua + util.testExpressionTemplate`(${value}).toFixed(2)`.expectToEqual(luaNativeSpecialNumString); +}); + test.each(cases)("isNaN(%p)", value => { util.testExpressionTemplate`isNaN(${value} as any)`.expectToMatchJsResult(); }); @@ -88,29 +112,32 @@ test("numbers overflowing the float limit become math.huge", () => { util.testExpression`1e309`.expectToMatchJsResult(); }); -describe.each(["parseInt", "parseFloat"])("parse numbers with %s", parseFunction => { - const numberStrings = ["3", "3.0", "9", "42", "239810241", "-20391", "3.1415", "2.7182", "-34910.3"]; +describe.each(["parseInt", "parseFloat", "Number.parseInt", "Number.parseFloat"])( + "parse numbers with %s", + parseFunction => { + const numberStrings = ["3", "3.0", "9", "42", "239810241", "-20391", "3.1415", "2.7182", "-34910.3"]; - test.each(numberStrings)("parses (%s)", numberString => { - util.testExpression`${parseFunction}("${numberString}")`.expectToMatchJsResult(); - }); + test.each(numberStrings)("parses (%s)", numberString => { + util.testExpression`${parseFunction}("${numberString}")`.expectToMatchJsResult(); + }); - test("empty string", () => { - util.testExpression`${parseFunction}("")`.expectToMatchJsResult(); - }); + test("empty string", () => { + util.testExpression`${parseFunction}("")`.expectToMatchJsResult(); + }); - test("invalid string", () => { - util.testExpression`${parseFunction}("bla")`.expectToMatchJsResult(); - }); + test("invalid string", () => { + util.testExpression`${parseFunction}("bla")`.expectToMatchJsResult(); + }); - test.each(["1px", "2300m", "3,4", "452adkfl"])("trailing text (%s)", numberString => { - util.testExpression`${parseFunction}("${numberString}")`.expectToMatchJsResult(); - }); + test.each(["1px", "2300m", "3,4", "452adkfl"])("trailing text (%s)", numberString => { + util.testExpression`${parseFunction}("${numberString}")`.expectToMatchJsResult(); + }); - test.each([" 3", " 4", " -231", " 1px"])("leading whitespace (%s)", numberString => { - util.testExpression`${parseFunction}("${numberString}")`.expectToMatchJsResult(); - }); -}); + test.each([" 3", " 4", " -231", " 1px"])("leading whitespace (%s)", numberString => { + util.testExpression`${parseFunction}("${numberString}")`.expectToMatchJsResult(); + }); + } +); test.each(["Infinity", "-Infinity", " -Infinity"])("parseFloat handles Infinity", numberString => { util.testExpression`parseFloat("${numberString}")`.expectToMatchJsResult(); @@ -141,3 +168,80 @@ test.each([ ])("parseInt with base and trailing text (%p)", ({ numberString, base }) => { util.testExpression`parseInt("${numberString}", ${base})`.expectToMatchJsResult(); }); + +test.each(["Infinity", "-Infinity", " -Infinity"])("Number.parseFloat handles Infinity", numberString => { + util.testExpression`Number.parseFloat("${numberString}")`.expectToMatchJsResult(); +}); + +test.each([ + { numberString: "36", base: 8 }, + { numberString: "-36", base: 8 }, + { numberString: "100010101101", base: 2 }, + { numberString: "-100010101101", base: 2 }, + { numberString: "3F", base: 16 }, +])("Number.parseInt with base (%p)", ({ numberString, base }) => { + util.testExpression`Number.parseInt("${numberString}", ${base})`.expectToMatchJsResult(); +}); + +test.each(["0x4A", "-0x42", "0X42", " 0x391", " -0x8F"])("Number.parseInt detects hexadecimal", numberString => { + util.testExpression`Number.parseInt("${numberString}")`.expectToMatchJsResult(); +}); + +test.each([1, 37, -100])("Number.parseInt with invalid base (%p)", base => { + util.testExpression`Number.parseInt("11111", ${base})`.expectToMatchJsResult(); +}); + +test.each([ + { numberString: "36px", base: 8 }, + { numberString: "10001010110231", base: 2 }, + { numberString: "3Fcolor", base: 16 }, +])("Number.parseInt with base and trailing text (%p)", ({ numberString, base }) => { + util.testExpression`Number.parseInt("${numberString}", ${base})`.expectToMatchJsResult(); +}); + +// Issue #1218: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1218 +test.each(["42", "undefined"])("prototype call on nullable number (%p)", value => { + util.testFunction` + function toString(n?: number) { + return n?.toString(); + } + return toString(${value}); + ` + .setOptions({ strictNullChecks: true }) + .expectToMatchJsResult(); +}); + +test.each([ + // Full ordering: NEG_INF < MIN_SAFE < 0 < MIN_VALUE < EPSILON < MAX_SAFE < MAX_VALUE < POS_INF + "Number.NEGATIVE_INFINITY < Number.MIN_SAFE_INTEGER", + "Number.MIN_SAFE_INTEGER < 0", + "0 < Number.MIN_VALUE", + "Number.MIN_VALUE < Number.EPSILON", + "Number.EPSILON < Number.MAX_SAFE_INTEGER", + "Number.MAX_SAFE_INTEGER < Number.MAX_VALUE", + "Number.MAX_VALUE < Number.POSITIVE_INFINITY", + + // Verify specific values + "Number.MIN_VALUE > 0", + "Number.MIN_SAFE_INTEGER === -(2**53 - 1)", + "Number.MAX_SAFE_INTEGER === 2**53 - 1", + "Number.MAX_SAFE_INTEGER + 1 !== Number.MAX_SAFE_INTEGER", + "Number.MIN_SAFE_INTEGER - 1 !== Number.MIN_SAFE_INTEGER", +])("Number constants have correct relative sizes (%p)", comparison => { + util.testExpression(comparison).expectToMatchJsResult(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1629 +test("unary + casting to number (#1629)", () => { + util.testFunction` + let abc = "123"; + return [+abc, +"456"]; + `.expectToEqual([123, 456]); +}); + +test("unary - casting to number", () => { + util.testFunction` + let abc = "123"; + return [-abc, -"456"]; + `.expectToEqual([-123, -456]); +}); diff --git a/test/unit/builtins/object.spec.ts b/test/unit/builtins/object.spec.ts index fb0fe26ba..411da903b 100644 --- a/test/unit/builtins/object.spec.ts +++ b/test/unit/builtins/object.spec.ts @@ -10,6 +10,16 @@ test.each([ util.testExpression`Object.assign(${util.formatCode(initial)}, ${argsString})`.expectToMatchJsResult(); }); +test.each([ + "Object.assign({}, false)", + "Object.assign({}, null)", + "Object.assign({}, undefined)", + "Object.assign({}, null, undefined)", + "Object.assign({ a: 1 }, false, { b: 2 })", +])("Object.assign skips non-object sources (%p)", expression => { + util.testExpression(expression).expectToMatchJsResult(); +}); + test.each([{}, { abc: 3 }, { abc: 3, def: "xyz" }])("Object.entries (%p)", obj => { const testBuilder = util.testExpressionTemplate`Object.entries(${obj})`; // Need custom matcher because order is not guaranteed in neither JS nor Lua @@ -147,7 +157,7 @@ describe("Object.defineProperty", () => { test.each(trueFalseTests)("configurable (%p)", value => { util.testFunction` - const foo = { bar: true }; + const foo: { bar?: boolean } = { bar: true }; Object.defineProperty(foo, "bar", { configurable: ${value} }); try { delete foo.bar } catch {}; return foo.bar; @@ -196,6 +206,81 @@ describe("Object.defineProperty", () => { return { prop: foo.bar, err }; `.expectToMatchJsResult(); }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1625 + test("instance isolation", () => { + util.testFunction` + class Test { + declare obj: object; + constructor() { + Object.defineProperty(this, "obj", { value: {}, writable: true, configurable: true }); + } + } + const t1 = new Test(); + const t2 = new Test(); + return t1.obj === t2.obj; + `.expectToMatchJsResult(); + }); + + test("instance isolation with three instances", () => { + util.testFunction` + class Test { + declare obj: object; + constructor() { + Object.defineProperty(this, "obj", { value: {}, writable: true, configurable: true }); + } + } + const t1 = new Test(); + const t2 = new Test(); + const t3 = new Test(); + return [t1.obj === t2.obj, t1.obj === t3.obj, t2.obj === t3.obj]; + `.expectToMatchJsResult(); + }); + + test("instance isolation with mutation", () => { + util.testFunction` + class Test { + declare value: number; + constructor(v: number) { + Object.defineProperty(this, "value", { value: v, writable: true, configurable: true }); + } + } + const t1 = new Test(1); + const t2 = new Test(2); + return [t1.value, t2.value]; + `.expectToMatchJsResult(); + }); + + test("instance isolation with multiple properties", () => { + util.testFunction` + class Test { + declare a: string; + declare b: string; + constructor(a: string, b: string) { + Object.defineProperty(this, "a", { value: a, writable: true, configurable: true }); + Object.defineProperty(this, "b", { value: b, writable: true, configurable: true }); + } + } + const t1 = new Test("x", "y"); + const t2 = new Test("p", "q"); + return [t1.a, t1.b, t2.a, t2.b]; + `.expectToMatchJsResult(); + }); + + test("instance isolation preserves prototype methods", () => { + util.testFunction` + class Test { + declare val: number; + constructor(v: number) { + Object.defineProperty(this, "val", { value: v, writable: true, configurable: true }); + } + getVal() { return this.val; } + } + const t1 = new Test(10); + const t2 = new Test(20); + return [t1.getVal(), t2.getVal()]; + `.expectToMatchJsResult(); + }); }); describe("Object.getOwnPropertyDescriptor", () => { @@ -221,23 +306,84 @@ describe("Object.getOwnPropertyDescriptors", () => { describe("delete from object", () => { test("delete from object", () => { util.testFunction` - const obj = { foo: "bar", bar: "baz" }; - delete obj["foo"]; - return obj; - ` - .setTsHeader("declare function setmetatable(this: void, table: T, metatable: any): T;") - .expectToEqual({ bar: "baz" }); + const obj: { foo?: string, bar: string } = { foo: "bar", bar: "baz" }; + return [delete obj["foo"], obj]; + `.expectToMatchJsResult(); + }); + + test("delete nonexistent property", () => { + util.testFunction` + const obj = {} as any + return [delete obj["foo"], obj]; + `.expectToMatchJsResult(); }); // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/993 test("delete from object with metatable", () => { util.testFunction` - const obj = { foo: "bar", bar: "baz" }; + const obj: { foo?: string, bar: string } = { foo: "bar", bar: "baz" }; setmetatable(obj, {}); - delete obj["foo"]; - return obj; + return [delete obj["foo"], obj]; ` .setTsHeader("declare function setmetatable(this: void, table: T, metatable: any): T;") - .expectToEqual({ bar: "baz" }); + .expectToEqual([true, { bar: "baz" }]); + }); + + test("delete nonconfigurable own property", () => { + util.testFunction` + const obj = {} as any + Object.defineProperty(obj, "foo", { + configurable: false, + value: true + }) + try { + delete obj["foo"] + return "deleted" + } catch (e) { + return e instanceof TypeError + } + ` + .setOptions({ + strict: true, + }) + .expectToMatchJsResult(); + }); +}); + +describe("Object.groupBy", () => { + test("empty", () => { + util.testFunction` + const array: number[] = []; + + return Object.groupBy(array, (num, index) => { + return num % 2 === 0 ? "even": "odd"; + }); + `.expectToEqual([]); + }); + + test("groupBy", () => { + util.testFunction` + const array = [0, 1, 2, 3, 4, 5]; + + return Object.groupBy(array, (num, index) => { + return num % 2 === 0 ? "even": "odd"; + }); + `.expectToEqual({ + even: [0, 2, 4], + odd: [1, 3, 5], + }); + }); + + test("groupBy index", () => { + util.testFunction` + const array = [0, 1, 2, 3, 4, 5]; + + return Object.groupBy(array, (num, index) => { + return index < 3 ? "low": "high"; + }); + `.expectToEqual({ + low: [0, 1, 2], + high: [3, 4, 5], + }); }); }); diff --git a/test/unit/builtins/promise.spec.ts b/test/unit/builtins/promise.spec.ts new file mode 100644 index 000000000..63272c8ab --- /dev/null +++ b/test/unit/builtins/promise.spec.ts @@ -0,0 +1,1413 @@ +import * as util from "../../util"; + +const promiseTestLib = ` +// Some logging utility, useful for asserting orders of operations + +const allLogs: any[] = []; +function log(...values: any[]) { + allLogs.push(...values); +} + +// Create a promise and store its resolve and reject functions, useful for creating pending promises + +function defer() { + let resolve: (data: any) => void = () => {}; + let reject: (reason: string) => void = () => {}; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + return { promise, resolve, reject }; +}`; + +test("can create resolved promise", () => { + util.testFunction` + const { state, value } = Promise.resolve(42) as any; + return { state, value }; + `.expectToEqual({ + state: 1, // __TS__PromiseState.Fulfilled + value: 42, + }); +}); + +test("can create rejected promise", () => { + util.testFunction` + const { state, rejectionReason } = Promise.reject("test rejection") as any; + return { state, rejectionReason }; + `.expectToEqual({ + state: 2, // __TS__PromiseState.Rejected + rejectionReason: "test rejection", + }); +}); + +test("promise constructor executor throwing rejects promise", () => { + util.testFunction` + const { state, rejectionReason } = new Promise(() => { throw "executor exception"; }) as any; + return { state, rejectionReason }; + `.expectToEqual({ + state: 2, // __TS__PromiseState.Rejected + rejectionReason: "executor exception", + }); +}); + +test("promise can be resolved", () => { + util.testFunction` + const { promise, resolve } = defer(); + + let result: string | undefined; + let rejectResult: string | undefined; + + promise.then( + data => { result = data; }, + reason => { rejectResult = reason; } + ); + + const beforeResolve = result; + + resolve("Hello!"); + + const afterResolve = result; + + return { beforeResolve, afterResolve, rejectResult }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + beforeResolve: undefined, + afterResolve: "Hello!", + rejectResult: undefined, + }); +}); + +test("promise can be rejected", () => { + util.testFunction` + const { promise, reject } = defer(); + + let resolveResult: string | undefined; + let rejectResult: string | undefined; + + promise.then( + data => { resolveResult = data; }, + reason => { rejectResult = reason; } + ); + + const beforeReject = rejectResult; + + reject("Hello!"); + + const afterReject = rejectResult; + + return { beforeReject, afterReject, resolveResult }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + beforeReject: undefined, + afterReject: "Hello!", + resolveResult: undefined, + }); +}); + +test("promise cannot be resolved more than once", () => { + util.testFunction` + const { promise, resolve } = defer(); + + promise.then( + data => { log(data); } + ); + + resolve("Hello!"); + resolve("World!"); // Second resolve should be ignored + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["Hello!"]); +}); + +test("promise cannot be rejected more than once", () => { + util.testFunction` + const { promise, reject } = defer(); + + promise.then( + _ => {}, + reason => { log(reason); } + ); + + reject("Hello!"); + reject("World!"); // Second reject should be ignored + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["Hello!"]); +}); + +test("promise cannot be rejected after resolving", () => { + util.testFunction` + const { promise, resolve, reject } = defer(); + + promise.then( + data => { log(data); }, + reason => { log(reason); } + ); + + resolve("Hello!"); + reject("World!"); // should be ignored because already resolved + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["Hello!"]); +}); + +test("promise cannot be resolved after rejecting", () => { + util.testFunction` + const { promise, resolve, reject } = defer(); + + promise.then( + data => { log(data); }, + reason => { log(reason); } + ); + + reject("Hello!"); + resolve("World!"); // should be ignored because already rejected + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["Hello!"]); +}); + +test("promise can be (then-resolve) observed more than once", () => { + util.testFunction` + const { promise, resolve } = defer(); + + promise.then( + data => { log("then 1: " + data); } + ); + + promise.then( + data => { log("then 2: " + data); } + ); + + resolve("Hello!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["then 1: Hello!", "then 2: Hello!"]); +}); + +test("promise can be (then-reject) observed more than once", () => { + util.testFunction` + const { promise, reject } = defer(); + + promise.then( + undefined, + reason => { log("then 1: " + reason); } + ); + + promise.then( + undefined, + reason => { log("then 2: " + reason); }, + ); + + reject("Hello!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["then 1: Hello!", "then 2: Hello!"]); +}); + +test("promise can be (catch) observed more than once", () => { + util.testFunction` + const { promise, reject } = defer(); + + promise.catch( + reason => { log("catch 1: " + reason); } + ); + + promise.catch( + reason => { log("catch 2: " + reason); }, + ); + + reject("Hello!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["catch 1: Hello!", "catch 2: Hello!"]); +}); + +test("promise chained resolve resolves all", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + const { promise: promise2, resolve: resolve2 } = defer(); + const { promise: promise3, resolve: resolve3 } = defer(); + + promise3.then(data => { + log("promise3: " + data); + resolve2(data); + }); + promise2.then(data => { + log("promise2: " + data); + resolve1(data); + }); + promise1.then(data => { + log("promise1: " + data); + }); + + resolve3("Hello!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise3: Hello!", "promise2: Hello!", "promise1: Hello!"]); +}); + +test("promise then returns a literal", () => { + util.testFunction` + const { promise, resolve } = defer(); + + const promise2 = promise.then(data => { + log("promise resolved with: " + data); + return "promise 1 resolved: " + data; + }); + + promise2.then(data => { + log("promise2 resolved with: " + data); + }); + + resolve("Hello!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise resolved with: Hello!", "promise2 resolved with: promise 1 resolved: Hello!"]); +}); + +test("promise then returns a resolved promise", () => { + util.testFunction` + const { promise, resolve } = defer(); + + const promise2 = promise.then(data => { + log("promise resolved with: " + data); + return Promise.resolve("promise 1 resolved: " + data); + }); + + promise2.then(data => { + log("promise2 resolved with: " + data); + }); + + resolve("Hello!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise resolved with: Hello!", "promise2 resolved with: promise 1 resolved: Hello!"]); +}); + +test("promise then returns a rejected promise", () => { + util.testFunction` + const { promise, resolve } = defer(); + + const promise2 = promise.then(data => { + log("promise resolved with: " + data); + return Promise.reject("promise 1: reject!"); + }); + + promise2.catch(reason => { + log("promise2 rejected with: " + reason); + }); + + resolve("Hello!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise resolved with: Hello!", "promise2 rejected with: promise 1: reject!"]); +}); + +test("promise then returns a pending promise (resolves)", () => { + util.testFunction` + const { promise, resolve } = defer(); + + let resolve2: any; + + const promise2 = promise.then(data => { + log("promise resolved with: " + data); + + const promise3 = new Promise((res) => { + resolve2 = res; + }); + + promise3.then(data2 => { + log("promise3 resolved with: " + data2); + }); + + return promise3; + }); + + promise2.then(data => { + log("promise2 resolved with: " + data); + }); + + // Resolve promise 1 + resolve("Hello!"); + + // Resolve promise 2 and 3 + resolve2("World!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + "promise resolved with: Hello!", + "promise3 resolved with: World!", + "promise2 resolved with: World!", + ]); +}); + +test("promise then returns a pending promise (rejects)", () => { + util.testFunction` + const { promise, resolve } = defer(); + + let reject: any; + + const promise2 = promise.then(data => { + log("promise resolved with: " + data); + + const promise3 = new Promise((_, rej) => { + reject = rej; + }); + + promise3.catch(reason => { + log("promise3 rejected with: " + reason); + }); + + return promise3; + }); + + promise2.catch(reason => { + log("promise2 rejected with: " + reason); + }); + + // Resolve promise 1 + resolve("Hello!"); + + // Reject promise 2 and 3 + reject("World!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + "promise resolved with: Hello!", + "promise3 rejected with: World!", + "promise2 rejected with: World!", + ]); +}); + +test("promise then onFulfilled throws", () => { + util.testFunction` + const { promise, resolve } = defer(); + + const promise2 = promise.then(data => { + throw "fulfill exception!" + }); + + promise2.catch(reason => { + log("promise2 rejected with: " + reason); + }); + + resolve("Hello!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise2 rejected with: fulfill exception!"]); +}); + +test("promise then onRejected throws", () => { + util.testFunction` + const { promise, reject } = defer(); + + const promise2 = promise.then( + _ => {}, + reason => { throw "fulfill exception from onReject!" } + ); + + promise2.catch(reason => { + log("promise2 rejected with: " + reason); + }); + + reject("Hello!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise2 rejected with: fulfill exception from onReject!"]); +}); + +test("then on resolved promise immediately calls callback", () => { + util.testFunction` + Promise.resolve(42).then(data => { log(data); }); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([42]); +}); + +test("then on rejected promise immediately calls callback", () => { + util.testFunction` + Promise.reject("already rejected").then(data => { log("resolved", data); }, reason => { log("rejected", reason); }); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["rejected", "already rejected"]); +}); + +test("second then throws", () => { + util.testFunction` + const { promise, resolve } = defer(); + + promise.then(data => { + // nothing + log("then1", data) + }); + + promise.then(data => { + log("then2", data) + throw "test throw"; + }).catch(err => { + // caught + log("rejected: ", err) + }); + + resolve("mydata"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["then1", "mydata", "then2", "mydata", "rejected: ", "test throw"]); +}); + +test("chained then throws", () => { + util.testFunction` + const { promise, resolve } = defer(); + + promise.then(data => { + // nothing + log("then1", data) + }).then(data => { + log("then2", data) + throw "test throw"; + }).catch(err => { + // caught + log("rejected: ", err) + }); + + resolve("mydata"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + "then1", + "mydata", + "then2", // Does not have data because first then returned undefined + "rejected: ", + "test throw", + ]); +}); + +test("empty then resolves", () => { + util.testFunction` + const { promise, resolve } = defer(); + + promise.then().then(v => { log("then2", v) }); + + resolve("mydata"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["then2", "mydata"]); +}); + +test("empty then rejects", () => { + util.testFunction` + const { promise, reject } = defer(); + + promise.then().catch(err => { log("catch", err) }); + + reject("my error"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["catch", "my error"]); +}); + +test("catch on rejected promise immediately calls callback", () => { + util.testFunction` + Promise.reject("already rejected").catch(reason => { log(reason); }); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["already rejected"]); +}); + +test("finally on resolved promise immediately calls callback", () => { + util.testFunction` + Promise.resolve(42).finally(() => { log("finally"); }); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["finally"]); +}); + +test("finally on rejected promise immediately calls callback", () => { + util.testFunction` + Promise.reject("already rejected").finally(() => { log("finally"); }); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["finally"]); +}); + +test("direct chaining", () => { + util.testFunction` + const { promise, resolve } = defer(); + + promise + .then(data => { + log("resolving then1", data); + return "then 1 data"; + }).then(data => { + log("resolving then2", data); + throw "test throw"; + }).catch(reason => { + log("handling catch", reason); + }); + + resolve("test data"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + "resolving then1", + "test data", + "resolving then2", + "then 1 data", + "handling catch", + "test throw", + ]); +}); + +describe("finally behaves same as then/catch", () => { + const thenCatchPromise = ` + const { promise, resolve, reject } = defer(); + promise + .then(data => { + log("do something", data); + log("final code"); + }) + .catch(reason => { + log("handling error", reason); + log("final code"); + }); + `; + + const finallyPromise = ` + const { promise, resolve, reject } = defer(); + promise + .then(data => { + log("do something", data); + }) + .catch(reason => { + log("handling error", reason); + }) + .finally(() => { + log("final code"); + }); + `; + + test("when resolving", () => { + const thenResult = util.testFunction` + ${thenCatchPromise} + resolve("test data"); + return allLogs; + ` + .setTsHeader(promiseTestLib) + .getLuaExecutionResult(); + + const finallyResult = util.testFunction` + ${finallyPromise} + resolve("test data"); + return allLogs; + ` + .setTsHeader(promiseTestLib) + .getLuaExecutionResult(); + + expect(finallyResult).toEqual(thenResult); + }); + + test("when rejecting", () => { + const thenResult = util.testFunction` + ${thenCatchPromise} + reject("test rejection reason"); + return allLogs; + ` + .setTsHeader(promiseTestLib) + .getLuaExecutionResult(); + + const finallyResult = util.testFunction` + ${finallyPromise} + reject("test rejection reason"); + return allLogs; + ` + .setTsHeader(promiseTestLib) + .getLuaExecutionResult(); + + expect(finallyResult).toEqual(thenResult); + }); +}); + +test("example: asynchronous web request", () => { + const testHarness = ` + interface UserData { name: string, age: number} + const requests = new Map void>(); + function getUserData(id: number, callback: (userData: UserData) => void) { + requests.set(id, callback); + } + function emulateRequestReturn(id: number, data: UserData) { + requests.get(id)!(data); + } + `; + + util.testFunction` + // Wrap function getUserData(id: number, callback: (userData: UserData) => void) into a Promise + function getUserDataAsync(id: number): Promise { + return new Promise(resolve => { + getUserData(id, resolve); + }); + } + + const user1DataPromise = getUserDataAsync(1); + const user2DataPromise = getUserDataAsync(2); + + user1DataPromise.then(() => log("received data for user 1")); + user2DataPromise.then(() => log("received data for user 2")); + + const allDataPromise = Promise.all([user1DataPromise, user2DataPromise]); + + allDataPromise.then(([user1data, user2data]) => { + log("all requests completed", user1data, user2data); + }); + + emulateRequestReturn(2, { name: "bar", age: 42 }); + emulateRequestReturn(1, { name: "foo", age: 35 }); + + return allLogs; + ` + .setTsHeader(testHarness + promiseTestLib) + .expectToEqual([ + "received data for user 2", + "received data for user 1", + "all requests completed", + { + name: "foo", + age: 35, + }, + { + name: "bar", + age: 42, + }, + ]); +}); + +test("promise is instanceof promise", () => { + util.testExpression`Promise.resolve(4) instanceof Promise`.expectToMatchJsResult(); +}); + +test("chained then on resolved promise", () => { + util.testFunction` + Promise.resolve("result1").then(undefined, () => {}).then(value => log(value)); + Promise.resolve("result2").then(value => "then1", () => {}).then(value => log(value)); + Promise.resolve("result3").then(value => undefined, () => {}).then(value => log(value ?? "undefined")); + Promise.resolve("result4").then(value => "then2").then(value => [value, "then3"]).then(([v1, v2]) => log(v1, v2)); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["result1", "then1", "undefined", "then2", "then3"]); +}); + +test("chained catch on rejected promise", () => { + util.testFunction` + Promise.reject("reason1").then(() => {}).then(v => log("resolved", v), reason => log("rejected", reason)); + Promise.reject("reason2").then(() => {}, () => "reason3").then(v => log("resolved", v)); + Promise.reject("reason4").then(() => {}, () => undefined).then(v => log("resolved", v ?? "undefined")); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["rejected", "reason1", "resolved", "reason3", "resolved", "undefined"]); +}); + +// Issue 2 from https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1105 +test("catch after then catches rejected promise", () => { + util.testFunction` + Promise.reject('test error') + .then(result => { + log("then", result); + }) + .catch(e => { + log("catch", e); + }) + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["catch", "test error"]); +}); + +test("promise unwraps resolved promise result", () => { + util.testFunction` + const { promise, resolve } = defer(); + promise.then(v => log(v)); + + resolve(Promise.resolve("result")); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["result"]); +}); + +test("resolving promise with rejected promise rejects the promise", () => { + util.testFunction` + const { promise, resolve } = defer(); + promise.catch(err => log(err)); + + resolve(Promise.reject("reject")); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["reject"]); +}); + +test("resolving promise with pending promise will keep pending until promise2 resolved", () => { + util.testFunction` + const { promise, resolve } = defer(); + promise.then(v => log("promise 1", v)); + + const { promise: promise2, resolve: resolve2 } = defer(); + promise2.then(v => log("promise 2", v)); + + resolve(promise2); + resolve2("result"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise 2", "result", "promise 1", "result"]); +}); + +test("resolving promise with pending promise will keep pending until promise2 rejects", () => { + util.testFunction` + const { promise, resolve } = defer(); + promise.catch(v => log("promise 1", v)); + + const { promise: promise2, reject: reject2 } = defer(); + promise2.catch(v => log("promise 2", v)); + + resolve(promise2); + reject2("rejection"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise 2", "rejection", "promise 1", "rejection"]); +}); + +describe("Promise.resolve", () => { + test("returns the given resolved promise", () => { + util.testFunction` + let result = 0; + Promise.resolve(Promise.resolve(4)) + .then((value) => result = value); + return result; + `.expectToEqual(4); + }); + + test("returns the given rejected promise", () => { + util.testFunction` + let result = 0; + Promise.resolve(Promise.reject(4)) + .catch((value) => result = value); + return result; + `.expectToEqual(4); + }); +}); + +describe("Promise.all", () => { + test("resolves once all arguments are resolved", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + const { promise: promise2, resolve: resolve2 } = defer(); + const { promise: promise3, resolve: resolve3 } = defer(); + + const promise = Promise.all([promise1, promise2, promise3]); + promise.then(([result1, result2, result3]) => { + log(result1, result2, result3); + }); + + resolve1("promise 1 result!"); + resolve2("promise 2 result!"); + resolve3("promise 3 result!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise 1 result!", "promise 2 result!", "promise 3 result!"]); + }); + + test("rejects on first rejection", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + const { promise: promise2, reject: reject2 } = defer(); + const { promise: promise3, resolve: resolve3 } = defer(); + + const promise = Promise.all([promise1, promise2, promise3]); + promise.then( + ([result1, result2, result3]) => { + log(result1, result2, result3); + }, + reason => { + log(reason); + } + ); + + resolve1("promise 1 result!"); + reject2("promise 2 rejects!"); + resolve3("promise 3 result!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise 2 rejects!"]); + }); + + test("handles already-resolved promises", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + + const promise = Promise.all([promise1, Promise.resolve("already resolved!")]); + promise.then(([result1, result2]) => { + log(result1, result2); + }); + + resolve1("promise 1 result!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise 1 result!", "already resolved!"]); + }); + + test("handles non-promise data", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + + const promise = Promise.all([42, promise1, "foo"]); + promise.then(([result1, result2, result3]) => { + log(result1, result2, result3); + }); + + resolve1("promise 1 result!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([42, "promise 1 result!", "foo"]); + }); + + test("returns already-resolved promise if no pending promises in arguments", () => { + util.testFunction` + const { state, value } = Promise.all([42, Promise.resolve("already resolved!"), "foo"]) as any; + return { state, value }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + state: 1, // __TS__PromiseState.Fulfilled + value: [42, "already resolved!", "foo"], + }); + }); + + test("returns already-rejected promise if already rejected promise in arguments", () => { + util.testFunction` + const { state, rejectionReason } = Promise.all([42, Promise.reject("already rejected!")]) as any; + return { state, rejectionReason }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + state: 2, // __TS__PromiseState.Rejected + rejectionReason: "already rejected!", + }); + }); +}); + +describe("Promise.allSettled", () => { + test("resolves once all arguments are resolved", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + const { promise: promise2, resolve: resolve2 } = defer(); + const { promise: promise3, resolve: resolve3 } = defer(); + + const promise = Promise.allSettled([promise1, promise2, promise3]); + promise.then(([result1, result2, result3]) => { + log(result1, result2, result3); + }); + + resolve3("promise 3 result!"); + resolve1("promise 1 result!"); + resolve2("promise 2 result!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + { status: "fulfilled", value: "promise 1 result!" }, + { status: "fulfilled", value: "promise 2 result!" }, + { status: "fulfilled", value: "promise 3 result!" }, + ]); + }); + + test("resolves once all arguments are rejected", () => { + util.testFunction` + const { promise: promise1, reject: reject1 } = defer(); + const { promise: promise2, reject: reject2 } = defer(); + const { promise: promise3, reject: reject3 } = defer(); + + const promise = Promise.allSettled([promise1, promise2, promise3]); + promise.then(([result1, result2, result3]) => { + log(result1, result2, result3); + }); + + reject2("promise 2 rejected!"); + reject1("promise 1 rejected!"); + reject3("promise 3 rejected!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + { status: "rejected", reason: "promise 1 rejected!" }, + { status: "rejected", reason: "promise 2 rejected!" }, + { status: "rejected", reason: "promise 3 rejected!" }, + ]); + }); + + test("resolves once all arguments are rejected or resolved", () => { + util.testFunction` + const { promise: promise1, reject: reject1 } = defer(); + const { promise: promise2, resolve: resolve2 } = defer(); + const { promise: promise3, reject: reject3 } = defer(); + + const promise = Promise.allSettled([promise1, promise2, promise3]); + promise.then(([result1, result2, result3]) => { + log(result1, result2, result3); + }); + + resolve2("promise 2 resolved!"); + reject1("promise 1 rejected!"); + reject3("promise 3 rejected!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + { status: "rejected", reason: "promise 1 rejected!" }, + { status: "fulfilled", value: "promise 2 resolved!" }, + { status: "rejected", reason: "promise 3 rejected!" }, + ]); + }); + + test("handles already resolved promises", () => { + util.testFunction` + const { promise, resolve } = defer(); + + const returnedPromise = Promise.allSettled([Promise.resolve("already resolved"), promise]); + returnedPromise.then(([result1, result2]) => { + log(result1, result2); + }); + + resolve("promise resolved!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + { status: "fulfilled", value: "already resolved" }, + { status: "fulfilled", value: "promise resolved!" }, + ]); + }); + + test("handles already rejected promises", () => { + util.testFunction` + const { promise, resolve } = defer(); + + const returnedPromise = Promise.allSettled([Promise.reject("already rejected"), promise]); + returnedPromise.then(([result1, result2]) => { + log(result1, result2); + }); + + resolve("promise resolved!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + { status: "rejected", reason: "already rejected" }, + { status: "fulfilled", value: "promise resolved!" }, + ]); + }); + + test("handles literal arguments", () => { + util.testFunction` + const { promise, resolve } = defer(); + + const returnedPromise = Promise.allSettled(["my literal", promise]); + returnedPromise.then(([result1, result2]) => { + log(result1, result2); + }); + + resolve("promise resolved!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + { status: "fulfilled", value: "my literal" }, + { status: "fulfilled", value: "promise resolved!" }, + ]); + }); + + test("returns resolved promise for empty argument list", () => { + util.testFunction` + const { state, value } = Promise.allSettled([]) as any; + return { state, value }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + state: 1, // __TS__PromiseState.Fulfilled + value: [], + }); + }); +}); + +describe("Promise.any", () => { + test("resolves once first promise resolves", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + const { promise: promise2, resolve: resolve2 } = defer(); + const { promise: promise3, resolve: resolve3 } = defer(); + + const promise = Promise.any([promise1, promise2, promise3]); + promise.then(data => { + log(data); + }); + + resolve2("promise 2 result!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise 2 result!"]); + }); + + test("rejects once all promises reject", () => { + util.testFunction` + const { promise: promise1, reject: reject1 } = defer(); + const { promise: promise2, reject: reject2 } = defer(); + const { promise: promise3, reject: reject3 } = defer(); + + const promise = Promise.any([promise1, promise2, promise3]); + promise.catch(reason => { + log(reason); + }); + + reject2("promise 2 rejected!"); + reject3("promise 3 rejected!"); + reject1("promise 1 rejected!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + { + name: "AggregateError", + message: "All Promises rejected", + errors: ["promise 2 rejected!", "promise 3 rejected!", "promise 1 rejected!"], + }, + ]); + }); + + test("handles already rejected promises", () => { + util.testFunction` + const { promise: promise1, reject: reject1 } = defer(); + const { promise: promise2, reject: reject2 } = defer(); + + const promise = Promise.any([promise1, Promise.reject("already rejected!"), promise2]); + promise.catch(reason => { + log(reason); + }); + + reject2("promise 2 rejected!"); + reject1("promise 1 rejected!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual([ + { + name: "AggregateError", + message: "All Promises rejected", + errors: ["already rejected!", "promise 2 rejected!", "promise 1 rejected!"], + }, + ]); + }); + + test("rejects if iterable is empty", () => { + util.testFunction` + const { state, rejectionReason } = Promise.any([]) as any; + return { state, rejectionReason }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + state: 2, // __TS__PromiseState.Rejected + rejectionReason: "No promises to resolve with .any()", + }); + }); + + test("immediately resolves with literal", () => { + util.testFunction` + const { promise, resolve } = defer(); + + const { state, value } = Promise.any([promise, "my literal"]) as any; + return { state, value }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + state: 1, // __TS__PromiseState.Fulfilled + value: "my literal", + }); + }); + + test("immediately resolves with resolved promise", () => { + util.testFunction` + const { promise, resolve } = defer(); + + const { state, value } = Promise.any([promise, Promise.resolve("my resolved promise")]) as any; + return { state, value }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + state: 1, // __TS__PromiseState.Fulfilled + value: "my resolved promise", + }); + }); +}); + +describe("Promise.race", () => { + test("resolves once first promise resolves", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + const { promise: promise2, resolve: resolve2 } = defer(); + const { promise: promise3, resolve: resolve3 } = defer(); + + const promise = Promise.race([promise1, promise2, promise3]); + promise.then(data => { + log(data); + }); + + resolve2("promise 2 result!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise 2 result!"]); + }); + + test("rejects once first promise rejects", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + const { promise: promise2, reject: reject2 } = defer(); + const { promise: promise3, resolve: resolve3 } = defer(); + + const promise = Promise.race([promise1, promise2, promise3]); + promise.catch(reason => { + log(reason); + }); + + reject2("promise 2 rejected!"); + + return allLogs; + ` + .setTsHeader(promiseTestLib) + .expectToEqual(["promise 2 rejected!"]); + }); + + test("returns resolved promise if arguments contain resolved promise", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + const { promise: promise2, resolve: resolve2 } = defer(); + + const { state, value } = Promise.race([promise1, Promise.resolve("already resolved!"), promise2]) as any; + return { state, value }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + state: 1, // __TS__PromiseState.Fulfilled + value: "already resolved!", + }); + }); + + test("returns resolved promise if arguments contain literal", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + const { promise: promise2, resolve: resolve2 } = defer(); + + const { state, value } = Promise.race([promise1, "my literal", promise2]) as any; + return { state, value }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + state: 1, // __TS__PromiseState.Fulfilled + value: "my literal", + }); + }); + + test("returns rejected promise if arguments contain rejected promise", () => { + util.testFunction` + const { promise: promise1, resolve: resolve1 } = defer(); + const { promise: promise2, resolve: resolve2 } = defer(); + + const { state, rejectionReason } = Promise.race([promise1, Promise.reject("already rejected!"), promise2]) as any; + return { state, rejectionReason }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + state: 2, // __TS__PromiseState.Rejected + rejectionReason: "already rejected!", + }); + }); + + test("returns forever pending promise if argument array is empty", () => { + util.testFunction` + const { state } = Promise.race([]) as any; + return { state }; + ` + .setTsHeader(promiseTestLib) + .expectToEqual({ + state: 0, // __TS__PromiseState.Pending + }); + }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1660 +describe("Promise.finally", () => { + test("returns a different promise instance", () => { + util.testFunction` + const p1 = new Promise(() => {}); + const p2 = p1.finally(); + return p1 === p2; + `.expectToMatchJsResult(); + }); + + test("preserves fulfillment value", () => { + util.testFunction` + const result = Promise.resolve(42).finally(() => {}) as any; + return result.value; + `.expectToEqual(42); + }); + + test("preserves rejection reason", () => { + util.testFunction` + const result = Promise.reject("err").finally(() => {}) as any; + return result.rejectionReason; + `.expectToEqual("err"); + }); + + test("callback executes on fulfillment", () => { + util.testFunction` + let called = false; + Promise.resolve(1).finally(() => { called = true; }); + return called; + `.expectToEqual(true); + }); + + test("callback executes on rejection", () => { + util.testFunction` + let called = false; + Promise.reject("err").finally(() => { called = true; }); + return called; + `.expectToEqual(true); + }); + + test("finally with undefined callback", () => { + util.testFunction` + const result = Promise.resolve(99).finally(undefined) as any; + return result.value; + `.expectToEqual(99); + }); + + test("finally throw overrides fulfillment value", () => { + util.testModule` + let result: any; + Promise.resolve("ok") + .finally(() => { throw "finally-error"; }) + .then(v => { result = v; }, e => { result = e; }); + export const output = result; + `.expectToEqual({ output: "finally-error" }); + }); + + test("finally throw overrides rejection reason", () => { + util.testModule` + let result: any; + Promise.reject("original") + .finally(() => { throw "finally-error"; }) + .then(v => { result = v; }, e => { result = e; }); + export const output = result; + `.expectToEqual({ output: "finally-error" }); + }); + + test("finally returning rejected promise overrides fulfillment", () => { + util.testModule` + let result: any; + Promise.resolve("ok") + .finally(() => Promise.reject("finally-rejected") as any) + .then(v => { result = v; }, e => { result = e; }); + export const output = result; + `.expectToEqual({ output: "finally-rejected" }); + }); + + test("finally returning rejected promise overrides rejection", () => { + util.testModule` + let result: any; + Promise.reject("original") + .finally(() => Promise.reject("finally-rejected") as any) + .then(v => { result = v; }, e => { result = e; }); + export const output = result; + `.expectToEqual({ output: "finally-rejected" }); + }); +}); diff --git a/test/unit/builtins/set.spec.ts b/test/unit/builtins/set.spec.ts index 8f81edfe8..234be91a7 100644 --- a/test/unit/builtins/set.spec.ts +++ b/test/unit/builtins/set.spec.ts @@ -100,7 +100,7 @@ test("set has false", () => { test("set has null", () => { util.testFunction` - let myset = new Set(["a", "c"]); + let myset = new Set(["a", "c"]); return myset.has(null); `.expectToMatchJsResult(); }); @@ -194,3 +194,270 @@ describe.each(iterationMethods)("set.%s() preserves insertion order", iterationM `.expectToMatchJsResult(); }); }); + +describe.each(iterationMethods)("set.%s() handles mutation", iterationMethod => { + test("iterator persists after delete", () => { + util.testFunction` + const set1 = new Set(); + set1.add(42); + set1.add("forty two"); + + const iterator1 = set1.${iterationMethod}(); + set1.delete(42); + + return iterator1.next().value; + `.expectToMatchJsResult(); + }); + + test("iterator with delete and add between iterations", () => { + util.testFunction` + const set = new Set([1, 2, 3]); + const iter = set.${iterationMethod}(); + iter.next(); // 1 + set.delete(2); + set.add(4); + const results: IteratorResult[] = []; + let r = iter.next(); + while (!r.done) { results.push({ done: r.done, value: r.value }); r = iter.next(); } + return results; + `.expectToMatchJsResult(); + }); + + test("iterator does not restart after exhaustion", () => { + util.testFunction` + const set = new Set([1, 2]); + const iter = set.${iterationMethod}(); + const results: boolean[] = []; + results.push(iter.next().done!); + results.push(iter.next().done!); + results.push(iter.next().done!); // should be done + results.push(iter.next().done!); // should still be done, not restart + return results; + `.expectToMatchJsResult(); + }); +}); + +test("instanceof Set without creating set", () => { + util.testFunction` + const myset = 3 as any; + return myset instanceof Set; + `.expectToMatchJsResult(); +}); + +describe("new ECMAScript Set methods", () => { + test("union", () => { + util.testFunction` + const set1 = new Set([1,2,3,4,5,6]); + const set2 = new Set([4,5,6,7,8,9]); + + const intersection = set1.union(set2); + return [...intersection]; + `.expectToEqual([1, 2, 3, 4, 5, 6, 7, 8, 9]); + }); + + test("union with empty sets", () => { + util.testFunction` + const set1 = new Set([1,2,3,4,5,6]); + const set2 = new Set([]); + + const intersection = set1.union(set2); + return [...intersection]; + `.expectToEqual([1, 2, 3, 4, 5, 6]); + + util.testFunction` + const set1 = new Set([]); + const set2 = new Set([4,5,6,7,8,9]); + + const intersection = set1.union(set2); + return [...intersection]; + `.expectToEqual([4, 5, 6, 7, 8, 9]); + }); + + test("intersection", () => { + util.testFunction` + const set1 = new Set([1,2,3,4,5,6]); + const set2 = new Set([4,5,6,7,8,9]); + + const intersection = set1.intersection(set2); + return [...intersection]; + `.expectToEqual([4, 5, 6]); + }); + + test("intersection with empty sets", () => { + util.testFunction` + const set1 = new Set([1,2,3,4,5,6]); + const set2 = new Set([]); + + const intersection = set1.intersection(set2); + return [...intersection]; + `.expectToEqual([]); + + util.testFunction` + const set1 = new Set([]); + const set2 = new Set([4,5,6,7,8,9]); + + const intersection = set1.intersection(set2); + return [...intersection]; + `.expectToEqual([]); + }); + + test("difference", () => { + util.testFunction` + const set1 = new Set([1,2,3,4,5,6]); + const set2 = new Set([4,5,6,7,8,9]); + + const intersection = set1.difference(set2); + return [...intersection]; + `.expectToEqual([1, 2, 3]); + }); + + test("symmetricDifference", () => { + util.testFunction` + const set1 = new Set([1,2,3,4,5,6]); + const set2 = new Set([4,5,6,7,8,9]); + + const intersection = set1.symmetricDifference(set2); + return [...intersection]; + `.expectToEqual([1, 2, 3, 7, 8, 9]); + }); + + test("isSubsetOf", () => { + util.testFunction` + const set1 = new Set([3,4,5,6]); + const set2 = new Set([1,2,3,4,5,6,7,8,9]); + + return { + set1SubsetOfSet2: set1.isSubsetOf(set2), + set2SubsetOfSet1: set2.isSubsetOf(set1), + }; + `.expectToEqual({ + set1SubsetOfSet2: true, + set2SubsetOfSet1: false, + }); + }); + + test("isSubsetOf equal", () => { + util.testFunction` + const set1 = new Set([1,2,3,4,5,6,7,8,9]); + const set2 = new Set([1,2,3,4,5,6,7,8,9]); + + return { + set1SubsetOfSet2: set1.isSubsetOf(set2), + set2SubsetOfSet1: set2.isSubsetOf(set1), + }; + `.expectToEqual({ + set1SubsetOfSet2: true, + set2SubsetOfSet1: true, + }); + }); + + test("isSubsetOf empty", () => { + util.testFunction` + const set1 = new Set([]); + const set2 = new Set([1,2,3]); + + return { + set1SubsetOfSet2: set1.isSubsetOf(set2), + set2SubsetOfSet1: set2.isSubsetOf(set1), + }; + `.expectToEqual({ + set1SubsetOfSet2: true, + set2SubsetOfSet1: false, + }); + }); + + test("isSupersetOf", () => { + util.testFunction` + const set1 = new Set([3,4,5,6]); + const set2 = new Set([1,2,3,4,5,6,7,8,9]); + + return { + set1SupersetOfSet2: set1.isSupersetOf(set2), + set2SupersetOfSet1: set2.isSupersetOf(set1), + }; + `.expectToEqual({ + set1SupersetOfSet2: false, + set2SupersetOfSet1: true, + }); + }); + + test("isSupersetOf equal", () => { + util.testFunction` + const set1 = new Set([1,2,3,4,5,6,7,8,9]); + const set2 = new Set([1,2,3,4,5,6,7,8,9]); + + return { + set1SupersetOfSet2: set1.isSupersetOf(set2), + set2SupersetOfSet1: set2.isSupersetOf(set1), + }; + `.expectToEqual({ + set1SupersetOfSet2: true, + set2SupersetOfSet1: true, + }); + }); + + test("isSupersetOf empty", () => { + util.testFunction` + const set1 = new Set([]); + const set2 = new Set([1,2,3]); + + return { + set1SupersetOfSet2: set1.isSupersetOf(set2), + set2SupersetOfSet1: set2.isSupersetOf(set1), + }; + `.expectToEqual({ + set1SupersetOfSet2: false, + set2SupersetOfSet1: true, + }); + }); + + test("isDisjointFrom", () => { + util.testFunction` + const set1 = new Set([3,4,5,6]); + const set2 = new Set([7,8,9]); + const set3 = new Set([1,2,3,4]); + + return { + set1DisjointFromSet2: set1.isDisjointFrom(set2), + set2DisjointFromSet1: set2.isDisjointFrom(set1), + set1DisjointFromSet3: set1.isDisjointFrom(set3), + set3DisjointFromSet1: set3.isDisjointFrom(set1), + }; + `.expectToEqual({ + set1DisjointFromSet2: true, + set2DisjointFromSet1: true, + set1DisjointFromSet3: false, + set3DisjointFromSet1: false, + }); + }); + + test("isDisjointFrom equal", () => { + util.testFunction` + const set1 = new Set([1,2,3,4,5,6,7,8,9]); + const set2 = new Set([1,2,3,4,5,6,7,8,9]); + + return { + set1DisjointFromSet2: set1.isDisjointFrom(set2), + set2DisjointFromSet1: set2.isDisjointFrom(set1), + }; + `.expectToEqual({ + set1DisjointFromSet2: false, + set2DisjointFromSet1: false, + }); + }); + + test("isDisjointFrom empty", () => { + util.testFunction` + const set1 = new Set([]); + const set2 = new Set([1,2,3]); + + return { + set1DisjointFromSet2: set1.isDisjointFrom(set2), + set2DisjointFromSet1: set2.isDisjointFrom(set1), + }; + `.expectToEqual({ + set1DisjointFromSet2: true, + set2DisjointFromSet1: true, + }); + }); +}); diff --git a/test/unit/builtins/string.spec.ts b/test/unit/builtins/string.spec.ts index c31ef012f..7f2ad312d 100644 --- a/test/unit/builtins/string.spec.ts +++ b/test/unit/builtins/string.spec.ts @@ -13,6 +13,10 @@ test("Supported lua string function", () => { util.testExpression`"test".upper()`.setTsHeader(tsHeader).expectToEqual("TEST"); }); +test("string.toString()", () => { + util.testExpression`"test".toString()`.expectToEqual("test"); +}); + test.each([[], [65], [65, 66], [65, 66, 67]])("String.fromCharCode (%p)", (...args) => { util.testExpression`String.fromCharCode(${util.formatCode(...args)})`.expectToMatchJsResult(); }); @@ -54,22 +58,43 @@ test("string index (side effect)", () => { `.expectToMatchJsResult(); }); -test.each([ - { inp: "hello test", searchValue: "", replaceValue: "" }, - { inp: "hello test", searchValue: " ", replaceValue: "" }, - { inp: "hello test", searchValue: "hello", replaceValue: "" }, - { inp: "hello test", searchValue: "test", replaceValue: "" }, - { inp: "hello test", searchValue: "test", replaceValue: "world" }, - { inp: "hello test", searchValue: "test", replaceValue: "%world" }, - { inp: "hello test", searchValue: "test", replaceValue: "." }, - { inp: "hello %test", searchValue: "test", replaceValue: "world" }, - { inp: "hello %test", searchValue: "%test", replaceValue: "world" }, - { inp: "hello test.", searchValue: ".", replaceValue: "$" }, - { inp: "hello test", searchValue: "test", replaceValue: () => "a" }, - { inp: "hello test", searchValue: "test", replaceValue: () => "%a" }, - { inp: "aaa", searchValue: "a", replaceValue: "b" }, -])("string.replace (%p)", ({ inp, searchValue, replaceValue }) => { - util.testExpression`"${inp}".replace(${util.formatCode(searchValue, replaceValue)})`.expectToMatchJsResult(); +describe.each(["replace", "replaceAll"])("string.%s", method => { + const testCases = [ + { inp: "hello test", searchValue: "", replaceValue: "" }, + { inp: "hello test", searchValue: "", replaceValue: "_" }, + { inp: "hello test", searchValue: " ", replaceValue: "" }, + { inp: "hello test", searchValue: "hello", replaceValue: "" }, + { inp: "hello test", searchValue: "test", replaceValue: "" }, + { inp: "hello test", searchValue: "test", replaceValue: "world" }, + { inp: "hello test", searchValue: "test", replaceValue: "%world" }, + { inp: "hello test", searchValue: "test", replaceValue: "." }, + { inp: "hello %test", searchValue: "test", replaceValue: "world" }, + { inp: "hello %test", searchValue: "%test", replaceValue: "world" }, + { inp: "hello test.", searchValue: ".", replaceValue: "$" }, + { inp: "aaa", searchValue: "a", replaceValue: "b" }, + ]; + + test.each(testCases)("string replacer %p", ({ inp, searchValue, replaceValue }) => { + util.testExpression`"${inp}${inp}".${method}(${util.formatCode( + searchValue, + replaceValue + )})`.expectToMatchJsResult(); + }); + + test.each(testCases)("function replacer %p", ({ inp, searchValue, replaceValue }) => { + util.testFunction` + const result = { + args: [] as string[], + string: "" + } + function replacer(...args: any[]): string { + result.args.push(...args) + return ${util.formatCode(replaceValue)} + } + result.string = "${inp}${inp}".${method}(${util.formatCode(searchValue)}, replacer) + return result + `.expectToMatchJsResult(); + }); }); test.each([ @@ -346,3 +371,55 @@ test("string intersected method", () => { return ({ abc: () => "a" } as Vector).abc(); `.expectToMatchJsResult(); }); + +test("tostring number with String constructor", () => { + util.testFunction` + const n = 123 + return "abc:" + String(n); + `.expectToEqual("abc:123"); +}); + +test("tostring table with String constructor", () => { + const result = util.testFunction` + const t = {} + return "abc:" + String(t); + `.getLuaExecutionResult(); + expect(result).toContain("abc:table: 0x"); +}); + +// Issue #1218: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1218 +test.each(['"foo"', "undefined"])("prototype call on nullable string (%p)", value => { + util.testFunction` + function toUpper(str?: string) { + return str?.toUpperCase(); + } + return toUpper(${value}); + ` + .setOptions({ strictNullChecks: true }) + .expectToMatchJsResult(); +}); + +// Issue #1218: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1218 +test.each(["string | undefined", "string | null", "null | string", "null | undefined | string"])( + "prototype call on nullable string type (%p)", + type => { + util.testFunction` + function toUpper(str: ${type}) { + return str?.toUpperCase(); + } + return toUpper("foo"); + ` + .setOptions({ strictNullChecks: true }) + .expectToMatchJsResult(); + } +); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1406 +test("string.indexOf without arguments (#1406)", () => { + util.testExpression`"".indexOf()`.expectNoTranspileException(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1406 +test("string.repeat without arguments (#1406)", () => { + util.testExpression`"".repeat()`.expectNoTranspileException(); +}); diff --git a/test/unit/builtins/weakMap.spec.ts b/test/unit/builtins/weakMap.spec.ts index 812e43622..8ff5d4c2e 100644 --- a/test/unit/builtins/weakMap.spec.ts +++ b/test/unit/builtins/weakMap.spec.ts @@ -74,7 +74,7 @@ test("weakMap has null", () => { util.testFunction` ${initRefsTs} let mymap = new WeakMap([[{}, true]]); - return mymap.has(null); + return mymap.has(null as any); `.expectToMatchJsResult(); }); diff --git a/test/unit/bundle.spec.ts b/test/unit/bundle.spec.ts index f961dddb0..b8179363a 100644 --- a/test/unit/bundle.spec.ts +++ b/test/unit/bundle.spec.ts @@ -1,4 +1,4 @@ -import { LuaLibImportKind } from "../../src"; +import { BuildMode, LuaLibImportKind } from "../../src"; import * as diagnosticFactories from "../../src/transpilation/diagnostics"; import * as util from "../util"; @@ -109,6 +109,29 @@ test("cyclic imports", () => { .expectToEqual(new util.ExecutionError("stack overflow")); }); +test("does not evaluate files multiple times", () => { + util.testBundle` + import "./countingfile"; + import "./otherfile"; + + export const count = _count; + ` + .addExtraFile( + "otherfile.ts", + ` + import "./countingfile"; + ` + ) + .addExtraFile( + "countingfile.ts", + ` + declare var _count: number | undefined; + _count = (_count ?? 0) + 1; + ` + ) + .expectToEqual({ count: 1 }); +}); + test("no entry point", () => { util.testBundle`` .setOptions({ luaBundleEntry: undefined }) @@ -120,3 +143,9 @@ test("luaEntry doesn't exist", () => { .setEntryPoint("entry.ts") .expectDiagnosticsToMatchSnapshot([diagnosticFactories.couldNotFindBundleEntryPoint.code], true); }); + +test("bundling not allowed for buildmode library", () => { + util.testBundle`` + .setOptions({ buildMode: BuildMode.Library }) + .expectDiagnosticsToMatchSnapshot([diagnosticFactories.cannotBundleLibrary.code], true); +}); diff --git a/test/unit/classes/__snapshots__/classes.spec.ts.snap b/test/unit/classes/__snapshots__/classes.spec.ts.snap index a900582b5..9154d1911 100644 --- a/test/unit/classes/__snapshots__/classes.spec.ts.snap +++ b/test/unit/classes/__snapshots__/classes.spec.ts.snap @@ -1,10 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`missing declaration name: code 1`] = ` -"require(\\"lualib_bundle\\"); -____ = __TS__Class() -____.name = \\"\\" -function ____.prototype.____constructor(self) +"local ____lualib = require("lualib_bundle") +local __TS__Class = ____lualib.__TS__Class +____class_0 = __TS__Class() +____class_0.name = "" +function ____class_0.prototype.____constructor(self) end" `; diff --git a/test/unit/classes/__snapshots__/decorators.spec.ts.snap b/test/unit/classes/__snapshots__/decorators.spec.ts.snap index cbc983bd5..681e4fa3d 100644 --- a/test/unit/classes/__snapshots__/decorators.spec.ts.snap +++ b/test/unit/classes/__snapshots__/decorators.spec.ts.snap @@ -1,18 +1,62 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`Throws error if decorator function has void context: code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require("lualib_bundle") +local __TS__Class = ____lualib.__TS__Class +local __TS__Decorate = ____lualib.__TS__Decorate local ____exports = {} function ____exports.__main(self) - local function decorator(constructor) + local function decorator(constructor, context) end local TestClass = __TS__Class() - TestClass.name = \\"TestClass\\" + TestClass.name = "TestClass" function TestClass.prototype.____constructor(self) end - TestClass = __TS__Decorate({decorator}, TestClass) + TestClass = __TS__Decorate(TestClass, TestClass, {decorator}, {kind = "class", name = "TestClass"}) end return ____exports" `; exports[`Throws error if decorator function has void context: diagnostics 1`] = `"main.ts(4,9): error TSTL: Decorator function cannot have 'this: void'."`; + +exports[`class field decorator warns the return value is ignored: code 1`] = ` +"local ____lualib = require("lualib_bundle") +local __TS__Class = ____lualib.__TS__Class +local __TS__Decorate = ____lualib.__TS__Decorate +local ____exports = {} +function ____exports.__main(self) + local fieldDecoratorContext + local function fieldDecorator(self, _, context) + fieldDecoratorContext = context + return function(____, initialValue) return initialValue * 12 end + end + local TestClass = __TS__Class() + TestClass.name = "TestClass" + function TestClass.prototype.____constructor(self) + self.value = 22 + end + __TS__Decorate(TestClass, nil, {fieldDecorator}, {kind = "field", name = "value", private = false, static = false}) +end +return ____exports" +`; + +exports[`class field decorator warns the return value is ignored: diagnostics 1`] = `"main.ts(11,13): warning TSTL: You are using a class field decorator, note that tstl ignores returned value initializers!"`; + +exports[`legacy experimentalDecorators Throws error if decorator function has void context: code 1`] = ` +"local ____lualib = require("lualib_bundle") +local __TS__Class = ____lualib.__TS__Class +local __TS__DecorateLegacy = ____lualib.__TS__DecorateLegacy +local ____exports = {} +function ____exports.__main(self) + local function decorator(constructor) + end + local TestClass = __TS__Class() + TestClass.name = "TestClass" + function TestClass.prototype.____constructor(self) + end + TestClass = __TS__DecorateLegacy({decorator}, TestClass) +end +return ____exports" +`; + +exports[`legacy experimentalDecorators Throws error if decorator function has void context: diagnostics 1`] = `"main.ts(4,13): error TSTL: Decorator function cannot have 'this: void'."`; diff --git a/test/unit/classes/accessors.spec.ts b/test/unit/classes/accessors.spec.ts index 8a39fb21b..db17fe67d 100644 --- a/test/unit/classes/accessors.spec.ts +++ b/test/unit/classes/accessors.spec.ts @@ -11,6 +11,19 @@ test("get accessor", () => { `.expectToMatchJsResult(); }); +test("multiple get accessors", () => { + util.testFunction` + class Foo { + _foo = "foo"; + get foo() { return this._foo; } + _bar = "bar"; + get bar() { return this._bar; } + } + const f = new Foo(); + return f.foo + f.bar; + `.expectToMatchJsResult(); +}); + test("get accessor in base class", () => { util.testFunction` class Foo { @@ -142,6 +155,26 @@ test("get/set accessors", () => { `.expectToMatchJsResult(); }); +test("multiple get/set accessors", () => { + util.testFunction` + class Foo { + _foo = "foo"; + get foo() { return this._foo; } + set foo(val: string) { this._foo = val; } + + _bar = "bar"; + get bar() { return this._bar; } + set bar(val: string) { this._bar = val; } + } + const f = new Foo(); + const fooOriginal = f.foo; + f.foo = "fizz"; + const barOriginal = f.bar; + f.bar = "buzz"; + return [fooOriginal, f.foo, barOriginal, f.bar]; + `.expectToMatchJsResult(); +}); + test("get/set accessors in base class", () => { util.testFunction` class Foo { @@ -327,3 +360,44 @@ test("static get/set accessors in base class", () => { return fooOriginal + Bar.foo; `.expectToMatchJsResult(); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1437 +test("super class get accessor (#1437)", () => { + util.testFunction` + class A { + get foo() { + return "A"; + } + } + + class B extends A { + override get foo() { + return super.foo + "B"; + } + } + + return new B().foo; + `.expectToMatchJsResult(); +}); + +test("super class set accessor", () => { + util.testFunction` + let result = "unset"; + + class A { + set result(value: string) { + result = "foo" + value; + } + } + + class B extends A { + override set result(value: string) { + super.result = "bar" + value; + } + } + + new B().result = "baz"; + + return result; + `.expectToMatchJsResult(); +}); diff --git a/test/unit/classes/classes.spec.ts b/test/unit/classes/classes.spec.ts index f4c14df34..e7b8cc304 100644 --- a/test/unit/classes/classes.spec.ts +++ b/test/unit/classes/classes.spec.ts @@ -181,6 +181,27 @@ test("SubclassConstructor", () => { `.expectToMatchJsResult(); }); +test("SubclassConstructorPropertyInitiailizationSuperOrder", () => { + util.testFunction` + class a { + field: number; + constructor(field: number) { + this.field = field; + } + } + class b extends a { + fieldDouble = this.field * 2; + constructor(field: number) { + const newField = field + 1; + super(newField); + } + } + + const result = new b(10); + return [result.field, result.fieldDouble]; + `.expectToMatchJsResult(); +}); + test("Subclass constructor across merged namespace", () => { util.testModule` namespace NS { @@ -749,9 +770,9 @@ test("default exported anonymous class has 'default' name property", () => { }); // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/584 -test("constructor class name available with constructor", () => { +test("constructor class name available with decorator", () => { util.testModule` - const decorator = any>(constructor: T) => class extends constructor {}; + const decorator = any>(constructor: T, context: ClassDecoratorContext) => class extends constructor {}; @decorator class MyClass {} @@ -761,3 +782,142 @@ test("constructor class name available with constructor", () => { .setReturnExport("className") .expectToEqual("MyClass"); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/959 +test("methods accessed via this index pass correct context", () => { + util.testModule` + class Example { + baz = 3; + foo() { + this["bar"]() + } + + bar() { + return this.baz; + } + } + + const inst = new Example(); + export const result = inst.foo(); + `.expectToMatchJsResult(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/959 +test.each(['(this["bar"])', '((((this["bar"]))))'])("methods in parentheses pass correct context %s", callPath => { + util.testModule` + class Example { + baz = 3; + foo() { + ${callPath}() + } + + bar() { + return this.baz; + } + } + + const inst = new Example(); + export const result = inst.foo(); + `.expectToMatchJsResult(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1447 +test("static initialization block (#1447)", () => { + util.testModule` + class A { + private static staticProperty1 = 3; + public static staticProperty2; + static { + this.staticProperty2 = this.staticProperty1 + 5; + } + public static staticProperty3; + static { + this.staticProperty3 = this.staticProperty1 + this.staticProperty2; + } + } + export const result1 = A.staticProperty2; + export const result2 = A.staticProperty3; + `.expectToMatchJsResult(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1457 +test("static member definition order (#1457)", () => { + util.testFunction` + class A { + static a = A.foo() + + static foo(): number { + return 5 + } + } + + return A.a; + `.expectToMatchJsResult(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1504 +test("Calling static inherited functions works (#1504)", () => { + util.testFunction` + class A { + static Get() { + return "A"; + } + } + + class B extends A { + static Get() { + return super.Get() + "B"; + } + } + + return B.Get(); + `.expectToMatchJsResult(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1537 +test("get inherted __index member from super (DotA 2 inheritance) (#1537)", () => { + util.testFunction` + // Inherit 'connected' class + class C extends Connected { + bar() { + return super.foo(); + } + } + + return new C().bar();` + .setTsHeader( + `interface I { + foo(): string; + } + + // Hacky interface/class merging + interface Connected extends I {} + class Connected {} + + declare function setmetatable(this: void, t: any, mt: any): void; + + const A = { + foo() { + return "foo"; + } + }; + + // Connect class 'Connected' to 'traditional' class A + setmetatable(Connected.prototype, { + __index: A + });` + ) + .expectToEqual("foo"); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1673 +test("vararg spread optimization in class constructor (#1673)", () => { + const lua = util.testModule` + class C { + constructor(...args: any[]) { + console.log(...args); + } + }`.getMainLuaCodeChunk(); + + expect(lua).not.toContain("table.unpack"); +}); diff --git a/test/unit/classes/decorators.spec.ts b/test/unit/classes/decorators.spec.ts index dadb73655..095bd8e3a 100644 --- a/test/unit/classes/decorators.spec.ts +++ b/test/unit/classes/decorators.spec.ts @@ -1,27 +1,37 @@ -import { decoratorInvalidContext } from "../../../src/transformation/utils/diagnostics"; +import { + decoratorInvalidContext, + incompleteFieldDecoratorWarning, +} from "../../../src/transformation/utils/diagnostics"; import * as util from "../../util"; test("Class decorator with no parameters", () => { util.testFunction` - function setBool {}>(constructor: T) { + let classDecoratorContext; + + function classDecorator {}>(constructor: T, context: ClassDecoratorContext) { + classDecoratorContext = context; + return class extends constructor { decoratorBool = true; } } - @setBool + @classDecorator class TestClass { public decoratorBool = false; } - return new TestClass(); + return { decoratedClass: new TestClass(), context: { + kind: classDecoratorContext!.kind, + name: classDecoratorContext!.name, + } }; `.expectToMatchJsResult(); }); test("Class decorator with parameters", () => { util.testFunction` function setNum(numArg: number) { - return {}>(constructor: T) => { + return {}>(constructor: T, context: ClassDecoratorContext) => { return class extends constructor { decoratorNum = numArg; }; @@ -30,7 +40,7 @@ test("Class decorator with parameters", () => { @setNum(420) class TestClass { - public decoratorNum; + public decoratorNum?: number; } return new TestClass(); @@ -39,13 +49,13 @@ test("Class decorator with parameters", () => { test("Multiple class decorators", () => { util.testFunction` - function setTen {}>(constructor: T) { + function setTen {}>(constructor: T, context: ClassDecoratorContext) { return class extends constructor { decoratorTen = 10; } } - function setNum {}>(constructor: T) { + function setNum {}>(constructor: T, context: ClassDecoratorContext) { return class extends constructor { decoratorNum = 410; } @@ -54,8 +64,8 @@ test("Multiple class decorators", () => { @setTen @setNum class TestClass { - public decoratorTen; - public decoratorNum; + public decoratorTen?: number; + public decoratorNum?: number; } return new TestClass(); @@ -64,7 +74,7 @@ test("Multiple class decorators", () => { test("Class decorator with inheritance", () => { util.testFunction` - function setTen {}>(constructor: T) { + function setTen {}>(constructor: T, context: ClassDecoratorContext) { return class extends constructor { decoratorTen = 10; } @@ -85,11 +95,11 @@ test("Class decorator with inheritance", () => { test("Class decorators are applied in order and executed in reverse order", () => { util.testFunction` - const order = []; + const order: string[] = []; function pushOrder(index: number) { order.push("eval " + index); - return (constructor: new (...args: any[]) => {}) => { + return (constructor: new (...args: any[]) => {}, context: ClassDecoratorContext) => { order.push("execute " + index); }; } @@ -105,7 +115,7 @@ test("Class decorators are applied in order and executed in reverse order", () = test("Throws error if decorator function has void context", () => { util.testFunction` - function decorator(this: void, constructor: new (...args: any[]) => {}) {} + function decorator(this: void, constructor: new (...args: any[]) => {}, context: ClassDecoratorContext) {} @decorator class TestClass {} @@ -114,7 +124,7 @@ test("Throws error if decorator function has void context", () => { test("Exported class decorator", () => { util.testModule` - function decorator any>(Class: T): T { + function decorator any>(Class: T, context: ClassDecoratorContext): T { return class extends Class { public bar = "foobar"; }; @@ -127,60 +137,512 @@ test("Exported class decorator", () => { .expectToMatchJsResult(); }); -test.each([ - ["@decorator method() {}"], - ["@decorator property;"], - ["@decorator propertyWithInitializer = () => {};"], - ["@decorator ['evaluated property'];"], - ["@decorator get getter() { return 5 }"], - ["@decorator set setter(value) {}"], - ["@decorator static method() {}"], - ["@decorator static property;"], - ["@decorator static propertyWithInitializer = () => {}"], - ["@decorator static get getter() { return 5 }"], - ["@decorator static set setter(value) {}"], - ["@decorator static ['evaluated property'];"], - ["method(@decorator a) {}"], - ["static method(@decorator a) {}"], -])("Decorate class member (%p)", classMember => { +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1149 +test("exported class with decorator (#1149)", () => { + util.testModule` + import { MyClass } from "./other"; + const inst = new MyClass(); + export const result = inst.foo(); + ` + .addExtraFile( + "other.ts", + `function myDecorator(target: {new(): any}, context: ClassDecoratorContext) { + return class extends target { + foo() { + return "overridden"; + } + } + } + + @myDecorator + export class MyClass { + foo() { + return "foo"; + } + }` + ) + .expectToEqual({ result: "overridden" }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1634 +test("namespaced exported class with decorator (#1634)", () => { + util.testModule` + function myDecorator(target: {new(): any}, context: ClassDecoratorContext) { + return class extends target { + foo() { + return "overridden"; + } + } + } + + namespace ns { + @myDecorator + export class MyClass { + foo() { + return "foo"; + } + } + } + `.expectNoExecutionError(); +}); + +test("default exported class with decorator", () => { + util.testModule` + import MyClass from "./other"; + const inst = new MyClass(); + export const result = inst.foo(); + ` + .addExtraFile( + "other.ts", + `function myDecorator(target: {new(): any}, context: ClassDecoratorContext) { + return class extends target { + foo() { + return "overridden"; + } + } + } + + @myDecorator + export default class { + foo() { + return "foo"; + } + }` + ) + .expectToEqual({ result: "overridden" }); +}); + +test("class method decorator", () => { + util.testFunction` + let methodDecoratorContext; + + function methodDecorator(method: (v: number) => number, context: ClassMethodDecoratorContext) { + methodDecoratorContext = context; + + return (v: number) => { + return method(v) + 10; + }; + } + + class TestClass { + @methodDecorator + public myMethod(x: number) { + return x * 23; + } + } + + return { result: new TestClass().myMethod(4), context: { + kind: methodDecoratorContext!.kind, + name: methodDecoratorContext!.name, + private: methodDecoratorContext!.private, + static: methodDecoratorContext!.static + } }; + `.expectToMatchJsResult(); +}); + +test("this in decorator points to class being decorated", () => { + util.testFunction` + function methodDecorator(method: (v: number) => number, context: ClassMethodDecoratorContext) { + return function(this: TestClass) { + const thisCallTime = this.myInstanceVariable; + return thisCallTime; + }; + } + + class TestClass { + constructor(protected myInstanceVariable: number) { } + + @methodDecorator + public myMethod() { + return 0; + } + } + + return new TestClass(5).myMethod(); + `.expectToMatchJsResult(); +}); + +test("class getter decorator", () => { + util.testFunction` + let getterDecoratorContext; + + function getterDecorator(getter: () => number, context: ClassGetterDecoratorContext) { + getterDecoratorContext = context; + + return () => { + return getter() + 10; + }; + } + + class TestClass { + @getterDecorator + get getterValue() { return 10; } + } + + return { result: new TestClass().getterValue, context: { + kind: getterDecoratorContext!.kind, + name: getterDecoratorContext!.name, + private: getterDecoratorContext!.private, + static: getterDecoratorContext!.static + } }; + `.expectToMatchJsResult(); +}); + +test("class setter decorator", () => { util.testFunction` - let decoratorParameters: any; + let setterDecoratorContext; - const decorator = (target, key, index?) => { - const targetKind = target === Foo ? "Foo" : target === Foo.prototype ? "Foo.prototype" : "unknown"; - decoratorParameters = { targetKind, key, index: typeof index }; - }; + function setterDecorator(setter: (v: number) => void, context: ClassSetterDecoratorContext) { + setterDecoratorContext = context; - class Foo { - ${classMember} + return function(this: TestClass, v: number) { + setter.call(this, v + 15); + }; + } + + class TestClass { + public value?: number; + + @setterDecorator + set valueSetter(v: number) { this.value = v; } } - return decoratorParameters; + const instance = new TestClass(); + instance.valueSetter = 23; + return { result: instance.value, context: { + kind: setterDecoratorContext!.kind, + name: setterDecoratorContext!.name, + private: setterDecoratorContext!.private, + static: setterDecoratorContext!.static + } }; `.expectToMatchJsResult(); }); -describe("Decorators /w descriptors", () => { +test("class field decorator", () => { + util.testFunction` + let fieldDecoratorContext; + + function fieldDecorator(_: undefined, context: ClassFieldDecoratorContext) { + fieldDecoratorContext = context; + } + + class TestClass { + @fieldDecorator + public value: number = 22; + } + + return { result: new TestClass(), context: { + kind: fieldDecoratorContext!.kind, + name: fieldDecoratorContext!.name, + private: fieldDecoratorContext!.private, + static: fieldDecoratorContext!.static, + } }; + `.expectToEqual({ + result: { + value: 22, // Different from JS because we ignore the value initializer + }, + context: { + kind: "field", + name: "value", + private: false, + static: false, + }, + }); +}); + +test("class field decorator warns the return value is ignored", () => { + util.testFunction` + let fieldDecoratorContext; + + function fieldDecorator(_: undefined, context: ClassFieldDecoratorContext) { + fieldDecoratorContext = context; + + return (initialValue: number) => initialValue * 12; + } + + class TestClass { + @fieldDecorator + public value: number = 22; + } + `.expectDiagnosticsToMatchSnapshot([incompleteFieldDecoratorWarning.code]); +}); + +describe("legacy experimentalDecorators", () => { + test("Class decorator with no parameters", () => { + util.testFunction` + function setBool {}>(constructor: T) { + return class extends constructor { + decoratorBool = true; + } + } + + @setBool + class TestClass { + public decoratorBool = false; + } + + return new TestClass(); + ` + .setOptions({ experimentalDecorators: true }) + .expectToMatchJsResult(); + }); + + test("Class decorator with parameters", () => { + util.testFunction` + function setNum(numArg: number) { + return {}>(constructor: T) => { + return class extends constructor { + decoratorNum = numArg; + }; + }; + } + + @setNum(420) + class TestClass { + public decoratorNum?: number; + } + + return new TestClass(); + ` + .setOptions({ experimentalDecorators: true }) + .expectToMatchJsResult(); + }); + + test("Multiple class decorators", () => { + util.testFunction` + function setTen {}>(constructor: T) { + return class extends constructor { + decoratorTen = 10; + } + } + + function setNum {}>(constructor: T) { + return class extends constructor { + decoratorNum = 410; + } + } + + @setTen + @setNum + class TestClass { + public decoratorTen?: number; + public decoratorNum?: number; + } + + return new TestClass(); + ` + .setOptions({ experimentalDecorators: true }) + .expectToMatchJsResult(); + }); + + test("Class decorator with inheritance", () => { + util.testFunction` + function setTen {}>(constructor: T) { + return class extends constructor { + decoratorTen = 10; + } + } + + class TestClass { + public decoratorTen = 0; + } + + @setTen + class SubTestClass extends TestClass { + public decoratorTen = 5; + } + + return new SubTestClass(); + ` + .setOptions({ experimentalDecorators: true }) + .expectToMatchJsResult(); + }); + + test("Class decorators are applied in order and executed in reverse order", () => { + util.testFunction` + const order: string[] = []; + + function pushOrder(index: number) { + order.push("eval " + index); + return (constructor: new (...args: any[]) => {}) => { + order.push("execute " + index); + }; + } + + @pushOrder(1) + @pushOrder(2) + @pushOrder(3) + class TestClass {} + + return order; + ` + .setOptions({ experimentalDecorators: true }) + .expectToMatchJsResult(); + }); + + test("Throws error if decorator function has void context", () => { + util.testFunction` + function decorator(this: void, constructor: new (...args: any[]) => {}) {} + + @decorator + class TestClass {} + ` + .setOptions({ experimentalDecorators: true }) + .expectDiagnosticsToMatchSnapshot([decoratorInvalidContext.code]); + }); + + test("Exported class decorator", () => { + util.testModule` + function decorator any>(Class: T): T { + return class extends Class { + public bar = "foobar"; + }; + } + + @decorator + export class Foo {} + ` + .setReturnExport("Foo", "bar") + .setOptions({ experimentalDecorators: true }) + .expectToMatchJsResult(); + }); + test.each([ - ["return { writable: true }", "return { configurable: true }"], - ["desc.writable = true", "return { configurable: true }"], - ])("Combine decorators (%p + %p)", (decorateA, decorateB) => { + ["@decorator method() {}"], + ["@decorator property: any;"], + ["@decorator propertyWithInitializer = () => {};"], + ["@decorator ['evaluated property']: any;"], + ["@decorator get getter() { return 5 }"], + ["@decorator set setter(value: any) {}"], + ["@decorator static method() {}"], + ["@decorator static property: any;"], + ["@decorator static propertyWithInitializer = () => {}"], + ["@decorator static get getter() { return 5 }"], + ["@decorator static set setter(value: any) {}"], + ["@decorator static ['evaluated property']: any;"], + ["method(@decorator a: any) {}"], + ["static method(@decorator a: any) {}"], + ["constructor(@decorator a: any) {}"], + ])("Decorate class member (%p)", classMember => { util.testFunction` - const A = (target, key, desc): any => { ${decorateA} }; - const B = (target, key, desc): any => { ${decorateB} }; - class Foo { @A @B static method() {} } - const { value, ...rest } = Object.getOwnPropertyDescriptor(Foo, "method"); - return rest; - `.expectToMatchJsResult(); + let decoratorParameters: any; + + const decorator = (target: any, key: any, index?: any) => { + const targetKind = target === Foo ? "Foo" : target === Foo.prototype ? "Foo.prototype" : "unknown"; + decoratorParameters = { targetKind, key, index: typeof index }; + }; + + class Foo { + ${classMember} + } + + return decoratorParameters; + ` + .setOptions({ experimentalDecorators: true }) + .expectToMatchJsResult(); }); - test.each(["return { value: true }", "desc.value = true"])( - "Use decorator to override method value", - overrideStatement => { + describe("Decorators /w descriptors", () => { + test.each([ + ["return { writable: true }", "return { configurable: true }"], + ["desc.writable = true", "return { configurable: true }"], + ])("Combine decorators (%p + %p)", (decorateA, decorateB) => { util.testFunction` - const decorator = (target, key, desc): any => { ${overrideStatement} }; - class Foo { @decorator static method() {} } - return Foo.method; - `.expectToMatchJsResult(); - } - ); + const A = (target: any, key: any, desc: any): any => { ${decorateA} }; + const B = (target: any, key: any, desc: any): any => { ${decorateB} }; + class Foo { @A @B static method() {} } + const { value, ...rest } = Object.getOwnPropertyDescriptor(Foo, "method")!; + return rest; + ` + .setOptions({ experimentalDecorators: true }) + .expectToMatchJsResult(); + }); + + test.each(["return { value: true }", "desc.value = true"])( + "Use decorator to override method value %s", + overrideStatement => { + util.testFunction` + const decorator = (target: any, key: any, desc: any): any => { ${overrideStatement} }; + class Foo { @decorator static method() {} } + return Foo.method; + ` + .setOptions({ experimentalDecorators: true }) + .expectToMatchJsResult(); + } + ); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1149 + test("exported class with decorator", () => { + util.testModule` + import { MyClass } from "./other"; + const inst = new MyClass(); + export const result = inst.foo(); + ` + .addExtraFile( + "other.ts", + `function myDecorator(target: {new(): any}) { + return class extends target { + foo() { + return "overridden"; + } + } + } + + @myDecorator + export class MyClass { + foo() { + return "foo"; + } + }` + ) + .setOptions({ experimentalDecorators: true }) + .expectToEqual({ result: "overridden" }); + }); + + test("default exported class with decorator", () => { + util.testModule` + import MyClass from "./other"; + const inst = new MyClass(); + export const result = inst.foo(); + ` + .addExtraFile( + "other.ts", + `function myDecorator(target: {new(): any}) { + return class extends target { + foo() { + return "overridden"; + } + } + } + + @myDecorator + export default class { + foo() { + return "foo"; + } + }` + ) + .setOptions({ experimentalDecorators: true }) + .expectToEqual({ result: "overridden" }); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1453 + test("Class methods with legacy decorators can still be called ($1453)", () => { + util.testFunction` + function decorator( + target: Class, + propertyKey: keyof Class, + ): void {} + + class Foo { + @decorator + public method2() { return 4; } + } + + return new Foo().method2(); + ` + .setOptions({ experimentalDecorators: true }) + .expectToMatchJsResult(); + }); }); diff --git a/test/unit/classes/instanceof.spec.ts b/test/unit/classes/instanceof.spec.ts index 4e4000153..3869bfa1c 100644 --- a/test/unit/classes/instanceof.spec.ts +++ b/test/unit/classes/instanceof.spec.ts @@ -84,7 +84,7 @@ test("instanceof export", () => { test("instanceof Symbol.hasInstance", () => { util.testFunction` class myClass { - static [Symbol.hasInstance]() { + static [Symbol.hasInstance](instance: unknown) { return false; } } diff --git a/test/unit/comments.spec.ts b/test/unit/comments.spec.ts new file mode 100644 index 000000000..2efb92b53 --- /dev/null +++ b/test/unit/comments.spec.ts @@ -0,0 +1,115 @@ +import * as util from "../util"; + +test("Single-line JSDoc is copied on a function", () => { + const builder = util.testModule` + /** This is a function comment. */ + function foo() {} + ` + .expectToHaveNoDiagnostics() + .expectDiagnosticsToMatchSnapshot(); + + const transpiledFile = builder.getLuaResult().transpiledFiles[0]; + expect(transpiledFile).toBeDefined(); + const { lua } = transpiledFile; + expect(lua).toBeDefined(); + expect(lua).toContain("This is a function comment."); +}); + +test("Multi-line JSDoc with one block is copied on a function", () => { + const builder = util.testModule` + /** + * This is a function comment. + * It has more than one line. + */ + function foo() {} + ` + .expectToHaveNoDiagnostics() + .expectDiagnosticsToMatchSnapshot(); + + const transpiledFile = builder.getLuaResult().transpiledFiles[0]; + expect(transpiledFile).toBeDefined(); + const { lua } = transpiledFile; + expect(lua).toBeDefined(); + expect(lua).toContain("It has more than one line."); +}); + +test("Multi-line JSDoc with two blocks is copied on a function", () => { + const builder = util.testModule` + /** + * This is a function comment. + * It has more than one line. + * + * It also has more than one block. + */ + function foo() {} + ` + .expectToHaveNoDiagnostics() + .expectDiagnosticsToMatchSnapshot(); + + const transpiledFile = builder.getLuaResult().transpiledFiles[0]; + expect(transpiledFile).toBeDefined(); + const { lua } = transpiledFile; + expect(lua).toBeDefined(); + expect(lua).toContain("It also has more than one block."); +}); + +test("JSDoc is copied on a function with tags", () => { + const builder = util.testModule` + /** + * This is a function comment. + * It has multiple lines. + * + * @param arg1 This is the first argument. + * @param arg2 This is the second argument. + * @returns A very powerful string. + */ + function foo(arg1: boolean, arg2: number): string { + return "bar"; + } + ` + .expectToHaveNoDiagnostics() + .expectDiagnosticsToMatchSnapshot(); + + const transpiledFile = builder.getLuaResult().transpiledFiles[0]; + expect(transpiledFile).toBeDefined(); + const { lua } = transpiledFile; + expect(lua).toBeDefined(); + expect(lua).toContain("This is the first argument."); + expect(lua).toContain("This is the second argument."); + expect(lua).toContain("A very powerful string."); +}); + +test("JSDoc is copied on a variable", () => { + const builder = util.testModule` + /** This is a variable comment. */ + const foo = 123; + ` + .expectToHaveNoDiagnostics() + .expectDiagnosticsToMatchSnapshot(); + + const transpiledFile = builder.getLuaResult().transpiledFiles[0]; + expect(transpiledFile).toBeDefined(); + const { lua } = transpiledFile; + expect(lua).toBeDefined(); + expect(lua).toContain("This is a variable comment."); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1299 +test("tsdoc comment does not cause a diagnostic (#1299)", () => { + util.testModule` + interface LuaBootstrap { + /** + * @remarks + * + * Links can point to a URL: {@link https://github.com/microsoft/tsdoc} + */ + on_init(f: (() => void) ): void + } + + const script : LuaBootstrap = { + on_init: (x) => x() + } + + script.on_init(() => {}) + `.expectToHaveNoDiagnostics(); +}); diff --git a/test/unit/conditionals.spec.ts b/test/unit/conditionals.spec.ts index c9717d0c0..59ca587bc 100644 --- a/test/unit/conditionals.spec.ts +++ b/test/unit/conditionals.spec.ts @@ -1,5 +1,6 @@ import * as tstl from "../../src"; import * as util from "../util"; +import { truthyOnlyConditionalValue } from "../../src/transformation/utils/diagnostics"; test.each([0, 1])("if (%p)", inp => { util.testFunction` @@ -74,7 +75,7 @@ test.each([ ])("Ternary operator (%p)", ({ input, options }) => { util.testFunction` const literalValue = "literal"; - let variableValue: string; + let variableValue: string = "variable"; let maybeBooleanValue: string | boolean = false; let maybeUndefinedValue: string | undefined; return ${input}; @@ -88,7 +89,12 @@ test.each([ { condition: false, lhs: 4, rhs: 5 }, { condition: 3, lhs: 4, rhs: 5 }, ])("Ternary Conditional (%p)", ({ condition, lhs, rhs }) => { - util.testExpressionTemplate`${condition} ? ${lhs} : ${rhs}`.expectToMatchJsResult(); + util.testExpressionTemplate`${condition} ? ${lhs} : ${rhs}` + .ignoreDiagnostics([ + truthyOnlyConditionalValue.code, + 2872 /* TS2872: This kind of expression is always truthy. */, + ]) + .expectToMatchJsResult(); }); test.each(["true", "false", "a < 4", "a == 8"])("Ternary Conditional Delayed (%p)", condition => { @@ -99,3 +105,104 @@ test.each(["true", "false", "a < 4", "a == 8"])("Ternary Conditional Delayed (%p return delay(); `.expectToMatchJsResult(); }); + +test.each([false, true, null])("Ternary conditional with generic whenTrue branch (%p)", trueVal => { + util.testFunction` + function ternary(a: boolean, b: B, c: C) { + return a ? b : c + } + return ternary(true, ${trueVal}, "wasFalse") + ` + .setOptions({ + strictNullChecks: true, + }) + .expectToMatchJsResult(); +}); + +test.each([false, true])("Ternary conditional with preceding statements in true branch (%p)", trueVal => { + // language=TypeScript + util.testFunction` + let i = 0; + const result = ${trueVal} ? i += 1 : i; + return { result, i }; + ` + .setOptions({ + strictNullChecks: true, + }) + .expectToMatchJsResult(); +}); + +test.each([false, true])("Ternary conditional with preceding statements in false branch (%p)", trueVal => { + // language=TypeScript + util.testFunction` + let i = 0; + const result = ${trueVal} ? i : i += 2; + return { result, i }; + ` + .setOptions({ + strictNullChecks: true, + }) + .expectToMatchJsResult(); +}); + +test.each(["string", "number", "string | number"])( + "Warning when using if statement that cannot evaluate to false undefined or null (%p)", + type => { + util.testFunction` + if (condition) {} + ` + .setTsHeader(`declare var condition: ${type};`) + .setOptions({ strict: true }) + .expectToHaveDiagnostics([truthyOnlyConditionalValue.code]); + } +); + +test.each(["string", "number", "string | number"])("Warning can be disabled when strict is true (%p)", type => { + util.testFunction` + if (condition) {} + ` + .setTsHeader(`declare var condition: ${type};`) + .setOptions({ strict: true, strictNullChecks: false }) + .expectToHaveNoDiagnostics(); +}); + +test.each(["string", "number", "string | number"])( + "Warning when using while statement that cannot evaluate to false undefined or null (%p)", + type => { + util.testFunction` + while (condition) {} + ` + .setTsHeader(`declare var condition: ${type};`) + .setOptions({ strict: true }) + .expectToHaveDiagnostics([truthyOnlyConditionalValue.code]); + } +); + +test.each(["string", "number", "string | number"])( + "Warning when using do while statement that cannot evaluate to false undefined or null (%p)", + type => { + util.testFunction` + do {} while (condition) + ` + .setTsHeader(`declare var condition: ${type};`) + .setOptions({ strict: true }) + .expectToHaveDiagnostics([truthyOnlyConditionalValue.code]); + } +); + +test.each(["string", "number", "string | number"])( + "Warning when using ternary that cannot evaluate to false undefined or null (%p)", + type => { + util.testExpression`condition ? 1 : 0` + .setTsHeader(`declare var condition: ${type};`) + .setOptions({ strict: true }) + .expectToHaveDiagnostics([truthyOnlyConditionalValue.code]); + } +); + +test.each(["string", "number", "string | number"])("No warning when using element index in condition (%p)", type => { + util.testExpression`condition[0] ? 1 : 0` + .setTsHeader(`declare var condition: ${type}[];`) + .setOptions({ strict: true }) + .expectToHaveNoDiagnostics(); +}); diff --git a/test/unit/destructuring.spec.ts b/test/unit/destructuring.spec.ts index 2ec12c7d1..36b73daaf 100644 --- a/test/unit/destructuring.spec.ts +++ b/test/unit/destructuring.spec.ts @@ -1,41 +1,52 @@ +import { cannotAssignToNodeOfKind, invalidMultiReturnAccess } from "../../src/transformation/utils/diagnostics"; import * as util from "../util"; const allBindings = "x, y, z, rest"; const testCases = [ - { binding: "{ x }", value: { x: true } }, - { binding: "{ x, y }", value: { x: false, y: true } }, - { binding: "{ x: z, y }", value: { x: true, y: false } }, - { binding: "{ x: { x, y }, z }", value: { x: { x: true, y: false }, z: false } }, - { binding: "{ x, y = true }", value: { x: false, y: false } }, - { binding: "{ x = true }", value: {} }, - { binding: "{ x, y = true }", value: { x: false } }, - { binding: "{ ...rest }", value: {} }, - { binding: "{ x, ...rest }", value: { x: "x" } }, - { binding: "{ x, ...rest }", value: { x: "x", y: "y", z: "z" } }, - { binding: "{ x, ...y }", value: { x: "x", y: "y", z: "z" } }, - { binding: "{ x: y, ...z }", value: { x: "x", y: "y", z: "z" } }, - - { binding: "[]", value: [] }, - { binding: "[x, y]", value: ["x", "y"] }, - { binding: "[x, , y]", value: ["x", "", "y"] }, - { binding: "[x = true]", value: [false] }, - { binding: "[[x, y]]", value: [["x", "y"]] }, - { binding: "[x, ...rest]", value: ["x"] }, - { binding: "[x, ...rest]", value: ["x", "y", "z"] }, - - { binding: "{ y: [z = true] }", value: { y: [false] } }, - { binding: "{ x: [x, y] }", value: { x: ["x", "y"] } }, - { binding: "{ x: [{ y }] }", value: { x: [{ y: "y" }] } }, -].map(({ binding, value }) => ({ binding, value: util.formatCode(value) })); + { binding: "{ x }", type: "{ x: boolean }", value: { x: true } }, + { binding: "{ x, y }", type: "{ x: boolean, y: boolean }", value: { x: false, y: true } }, + { binding: "{ x: z, y }", type: "{ x: boolean, y?: boolean, z?: boolean }", value: { x: true, y: false } }, + { + binding: "{ x: { x, y }, z }", + type: "{ x: { x: boolean, y: boolean }, z: boolean }", + value: { x: { x: true, y: false }, z: false }, + }, + { binding: "{ x, y = true }", type: "{ x: boolean, y?: boolean }", value: { x: false, y: false } }, + { binding: "{ x = true }", type: "{ x?: boolean }", value: {} }, + { binding: "{ x, y = true }", type: "{ x: boolean, y?: boolean }", value: { x: false } }, + { binding: "{ ...rest }", type: "any", value: {} }, + { binding: "{ x, ...rest }", type: "any", value: { x: "x" } }, + { binding: "{ x, ...rest }", type: "any", value: { x: "x", y: "y", z: "z" } }, + { binding: "{ x, ...y }", type: "any", value: { x: "x", y: "y", z: "z" } }, + { binding: "{ x: y, ...z }", type: "any", value: { x: "x", y: "y", z: "z" } }, + + { binding: "[]", type: "boolean[]", value: [] }, + { binding: "[x, y]", type: "[string, string]", value: ["x", "y"] }, + { binding: "[x, , y]", type: "string[]", value: ["x", "", "y"] }, + { binding: "[x = true]", type: "boolean[]", value: [false] }, + { binding: "[x = true]", type: "boolean[]", value: [] }, + { binding: "[[x, y]]", type: "Array", value: [["x", "y"]] }, + { binding: "[x, ...rest]", type: "string[]", value: ["x"] }, + { binding: "[x, ...rest]", type: "string[]", value: ["x", "y", "z"] }, + + { binding: "{ y: [z = true] }", type: "{ y: boolean[] }", value: { y: [false] } }, + { binding: "{ y: [z = true] }", type: "{ y: boolean[] }", value: { y: [] } }, + { binding: "{ x: [x, y] }", type: "{ x: [string, string] }", value: { x: ["x", "y"] } }, + { binding: "{ x: [{ y }] }", type: "{ x: [{ y: string }] }", value: { x: [{ y: "y" }] } }, +].map(({ binding, type, value }) => ({ binding, type, value: util.formatCode(value) })); test.each([ ...testCases, - { binding: "{ x, y }, z", value: "{ x: false, y: false }, true" }, - { binding: "{ x, y }, { z }", value: "{ x: false, y: false }, { z: true }" }, -])("in function parameter (%p)", ({ binding, value }) => { + { binding: "{ x, y }", type: "{ x: boolean, y: boolean }, z: boolean", value: "{ x: false, y: false }, true" }, + { + binding: "{ x, y }", + type: "{ x: boolean, y: boolean }, { z }: { z: boolean }", + value: "{ x: false, y: false }, { z: true }", + }, +])("in function parameter (%p)", ({ binding, type, value }) => { util.testFunction` let ${allBindings}; - function test(${binding}) { + function test(${binding}: ${type}) { return { ${allBindings} }; } @@ -53,6 +64,17 @@ test("in function parameter creates local variables", () => { expect(code).toContain("local b ="); }); +test("in function parameter creates local variables in correct scope", () => { + util.testFunction` + let x = 7; + function foo([x]: [number]) { + x *= 2; + } + foo([1]); + return x; + `.expectToMatchJsResult(); +}); + test.each(testCases)("in variable declaration (%p)", ({ binding, value }) => { util.testFunction` let ${allBindings}; @@ -63,6 +85,28 @@ test.each(testCases)("in variable declaration (%p)", ({ binding, value }) => { `.expectToMatchJsResult(); }); +test.each(testCases)("in variable declaration from const variable (%p)", ({ binding, value }) => { + util.testFunction` + let ${allBindings}; + { + const v: any = ${value}; + const ${binding} = v; + return { ${allBindings} }; + } + `.expectToMatchJsResult(); +}); + +test.each(testCases)("in variable declaration from this (%p)", ({ binding, value }) => { + util.testFunction` + let ${allBindings}; + function test(this: any) { + const ${binding} = this; + return { ${allBindings} }; + } + return test.call(${value}); + `.expectToMatchJsResult(); +}); + test.each(testCases)("in exported variable declaration (%p)", ({ binding, value }) => { util.testModule` export const ${binding} = ${value}; @@ -90,6 +134,28 @@ test.each(assignmentTestCases)("in assignment expression (%p)", ({ binding, valu `.expectToMatchJsResult(); }); +test.each(assignmentTestCases)("in assignment expression from const variable (%p)", ({ binding, value }) => { + util.testFunction` + let ${allBindings}; + const obj = { prop: false }; + const v: any = ${value}; + const expressionResult = (${binding} = v); + return { ${allBindings}, expressionResult }; + `.expectToMatchJsResult(); +}); + +test.each(assignmentTestCases)("in assignment expression from this (%p)", ({ binding, value }) => { + util.testFunction` + let ${allBindings}; + const obj = { prop: false }; + function test(this: any) { + const expressionResult = (${binding} = this); + return { ${allBindings}, obj, expressionResult }; + } + return test.call(${value}); + `.expectToMatchJsResult(); +}); + test.each(["[]", "{}"])("empty binding pattern", bindingPattern => { util.testFunction` let i = 1; @@ -139,6 +205,17 @@ describe("array destructuring optimization", () => { .expectToMatchJsResult(); }); + util.testEachVersion( + "array versions", + () => + util.testFunction` + const array = [3, 5, 1]; + const [a, b, c] = array; + return { a, b, c }; + `, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); + test("array literal", () => { util.testFunction` const [a, b, c] = [3, 5, 1]; @@ -159,6 +236,33 @@ describe("array destructuring optimization", () => { .expectToMatchJsResult(); }); + test("array literal with side effects in elements", () => { + util.testFunction` + const arr = [1, 2]; + let i = 0; + let [v1, v2] = [arr[i], arr[++i]]; + return { v1, v2 }; + `.expectToMatchJsResult(); + }); + + test("array literal with many side effects in elements", () => { + util.testFunction` + const arr = [10, 20, 30, 40]; + let i = 0; + let [v1, v2, v3, v4] = [arr[i++], arr[i++], arr[i++], arr[i++]]; + return { v1, v2, v3, v4 }; + `.expectToMatchJsResult(); + }); + + test("array literal with mixed pure and impure elements", () => { + util.testFunction` + const arr = [10, 20, 30]; + let i = 0; + let [v1, v2, v3] = [1, arr[++i], 2]; + return { v1, v2, v3, i }; + `.expectToMatchJsResult(); + }); + test("array union", () => { util.testFunction` const array: [string] | [] = ["bar"]; @@ -170,3 +274,14 @@ describe("array destructuring optimization", () => { .expectToMatchJsResult(); }); }); + +test("no exception from semantically invalid TS", () => { + util.testModule` + declare function testFunc(value: number): LuaMultiReturn<[number, number]>; + let [a, b] = testFunc(5) // Missing ; + [a, b] = testFunc(b) // Interpreted as testFunc(5)[a, b] + ` + .withLanguageExtensions() + .disableSemanticCheck() + .expectToHaveDiagnostics([invalidMultiReturnAccess.code, cannotAssignToNodeOfKind.code]); +}); diff --git a/test/unit/enum.spec.ts b/test/unit/enum.spec.ts index f421be2c5..ae552b9c1 100644 --- a/test/unit/enum.spec.ts +++ b/test/unit/enum.spec.ts @@ -4,7 +4,7 @@ import * as util from "../util"; const serializeEnum = (identifier: string) => `(() => { const mappedTestEnum: any = {}; for (const key in ${identifier}) { - mappedTestEnum[(key as any).toString()] = ${identifier}[key]; + mappedTestEnum[(key as any).toString()] = (${identifier} as any)[key]; } return mappedTestEnum; })()`; @@ -203,3 +203,14 @@ test("enum merging multiple files", () => { ) .expectToMatchJsResult(); }); + +test("enum nested in namespace", () => { + util.testModule` + namespace A { + export enum TestEnum { + B, + C + } + } + `.expectLuaToMatchSnapshot(); +}); diff --git a/test/unit/error.spec.ts b/test/unit/error.spec.ts index 9174f941c..8204cc36a 100644 --- a/test/unit/error.spec.ts +++ b/test/unit/error.spec.ts @@ -1,4 +1,5 @@ import * as util from "../util"; +import * as tstl from "../../src"; test("throwString", () => { util.testFunction` @@ -6,6 +7,8 @@ test("throwString", () => { `.expectToEqual(new util.ExecutionError("Some Error")); }); +// TODO: Finally does not behave like it should, see #1137 +// eslint-disable-next-line jest/no-disabled-tests test.skip.each([0, 1, 2])("re-throw (%p)", i => { util.testFunction` const i: number = ${i}; @@ -77,18 +80,17 @@ test("return nil from try", () => { `.expectToMatchJsResult(); }); -test("tuple return from try", () => { +test("multi return from try", () => { const testBuilder = util.testFunction` - /** @tupleReturn */ function foobar() { try { - return ["foo", "bar"]; + return $multi("foo", "bar"); } catch { } } - const [foo, bar] = foobar(); + const [foo, bar] = foobar()!; return foo + bar; - `; + `.withLanguageExtensions(); expect(testBuilder.getMainLuaCodeChunk()).not.toMatch("unpack(foobar"); testBuilder.expectToMatchJsResult(); }); @@ -122,19 +124,18 @@ test("return nil from catch", () => { `.expectToMatchJsResult(); }); -test("tuple return from catch", () => { +test("multi return from catch", () => { const testBuilder = util.testFunction` - /** @tupleReturn */ - function foobar(): [string, string] { + function foobar(): LuaMultiReturn<[string, string]> { try { throw "foobar"; - } catch (e) { - return [e.toString(), " catch"]; + } catch (e: any) { + return $multi(e.toString(), " catch"); } } const [foo, bar] = foobar(); return foo + bar; - `; + `.withLanguageExtensions(); expect(testBuilder.getMainLuaCodeChunk()).not.toMatch("unpack(foobar"); testBuilder.expectToMatchJsResult(); }); @@ -210,46 +211,70 @@ test("return from catch->finally", () => { `.expectToMatchJsResult(); }); -test("tuple return from try->finally", () => { +test("multi return from try->finally", () => { util.testFunction` let x = "unevaluated"; function evaluate(arg: string) { x = "evaluated"; return arg; } - /** @tupleReturn */ function foobar() { try { - return [evaluate("foo"), "bar"]; + return $multi(evaluate("foo"), "bar"); } catch { } finally { - return ["final", "ly"]; + return $multi("final", "ly"); } } const [foo, bar] = foobar(); return foo + bar + " " + x; - `.expectToMatchJsResult(); + ` + .withLanguageExtensions() + .expectToMatchJsResult(); }); -test("tuple return from catch->finally", () => { +test("multi return from catch->finally", () => { util.testFunction` let x = "unevaluated"; function evaluate(arg: string) { x = "evaluated"; return arg; } - /** @tupleReturn */ function foobar() { try { throw "foo"; - } catch (e) { - return [evaluate(e), "bar"]; + } catch (e: any) { + return $multi(evaluate(e), "bar"); } finally { - return ["final", "ly"]; + return $multi("final", "ly"); } } const [foo, bar] = foobar(); return foo + bar + " " + x; + ` + .withLanguageExtensions() + .expectToMatchJsResult(); +}); + +test("throw propagates through finally to outer catch", () => { + util.testFunction` + function thrower() { + try { + throw "Error"; + } finally { + } + } + + function caller() { + try { + thrower(); + return "NoCatch"; + } catch (e) { + return e; + } + } + + return caller(); `.expectToMatchJsResult(); }); @@ -322,3 +347,172 @@ test.each([...builtinErrors, "CustomError"])("get stack from %s", errorType => { expect(stack).toMatch("innerFunction"); expect(stack).toMatch("outerFunction"); }); + +test("still works without debug module", () => { + util.testFunction` + try + { + throw new Error("hello, world"); + } + catch (e) + { + return e; + } + ` + .setLuaHeader("debug = nil") + .expectToEqual({ + message: "hello, world", + name: "Error", + stack: undefined, + }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1665 +test("sourceMapTraceback maps anonymous function locations in .lua files (#1665)", () => { + // Nested IIFEs produce anonymous function notation in traceback. + // Old pattern (%S+)%.lua:(%d+) captures "", + // failing the sourcemap lookup. Fix: ([^%s<]+) excludes "<". + + // mapping is copied from the emitted Lua, not invented. + const mapping = `{["5"] = 1,["6"] = 2,["7"] = 3,["8"] = 4,["9"] = 3,["10"] = 2,["11"] = 1}`; + + // Test harness executes via luaL_dostring (chunk names are [string "..."]), so we mock a file-based traceback. + const fakeTraceback = [ + "stack traceback:", + "\tmain.lua:8: in function ", + "\tmain.lua:7: in function ", + "\t[C]: in ?", + ].join("\n"); + + // Inject sourcemap for "main.lua" and mock debug.traceback to return file-based frames. + const header = ` + __TS__sourcemap = { ["main.lua"] = ${mapping} }; + local __real_tb = debug.traceback + debug.traceback = function() return ${JSON.stringify(fakeTraceback)} end + `; + + const builder = util.testFunction` + return (() => { + return (() => { + return (debug.traceback as (this: void) => string)(); + })(); + })(); + ` + .setLuaHeader(header) + .setOptions({ sourceMapTraceback: true }); + + const lua = builder.getMainLuaCodeChunk(); + // Sanity check: emitted code registers the same mapping literal we inject above. + expect(lua).toContain(`__TS__SourceMapTraceBack(debug.getinfo(1).short_src, ${mapping});`); + + const result = builder.getLuaExecutionResult(); + expect(result).toEqual(expect.any(String)); + // Both `main.lua:N` and `` frames should be rewritten using the sourcemap. + expect(result).not.toContain("main.lua"); + // Regular line references + expect(result).toContain("\tmain.ts:4:"); + expect(result).toContain("\tmain.ts:3:"); + // Anonymous function references must keep <> format + expect(result).toContain(""); + expect(result).toContain(""); +}); + +test("try/finally rethrow preserves error value", () => { + util.testFunction` + function foo() { + try { + throw "oops"; + } finally { + } + } + try { foo(); return "no error"; } catch(e) { return e; } + `.expectToMatchJsResult(); +}); + +test("try/finally with return and throw paths", () => { + util.testFunction` + function foo(shouldReturn: boolean) { + try { + if (shouldReturn) return "returned"; + throw "thrown"; + } finally { + } + } + const results: any[] = []; + results.push(foo(true)); + try { foo(false); } catch(e) { results.push(e); } + return results; + `.expectToMatchJsResult(); +}); + +test("try/finally runs finally side effect before rethrow", () => { + util.testFunction` + let sideEffect = false; + function foo() { + try { + throw "err"; + } finally { + sideEffect = true; + } + } + try { foo(); } catch(e) {} + return sideEffect; + `.expectToMatchJsResult(); +}); + +test("try/finally with return and throw paths and non-empty finally body", () => { + util.testFunction` + let sideEffect = false; + function foo(shouldReturn: boolean) { + try { + if (shouldReturn) return "ok"; + throw "err"; + } finally { + sideEffect = true; + } + } + const results: any[] = []; + results.push(foo(true)); + results.push(sideEffect); + sideEffect = false; + try { foo(false); } catch(e) { results.push(e); } + results.push(sideEffect); + return results; + `.expectToMatchJsResult(); +}); + +test("try/finally rethrow with non-string error", () => { + util.testFunction` + function foo() { + try { + throw 42; + } finally { + } + } + try { foo(); return "no error"; } catch(e) { return e; } + `.expectToMatchJsResult(); +}); + +util.testEachVersion( + "error stacktrace omits constructor and __TS_New", + () => util.testFunction` + const e = new Error(); + return e.stack; + `, + { + ...util.expectEachVersionExceptJit(builder => { + builder.expectToHaveNoDiagnostics(); + const luaResult = builder.getLuaExecutionResult(); + // eslint-disable-next-line jest/no-standalone-expect + expect(luaResult.split("\n")).toHaveLength(4); + }), + + // 5.0 debug.traceback doesn't support levels + [tstl.LuaTarget.Lua50](builder) { + builder.expectToHaveNoDiagnostics(); + const luaResult = builder.getLuaExecutionResult(); + // eslint-disable-next-line jest/no-standalone-expect + expect(luaResult).toContain("Level 4"); + }, + } +); diff --git a/test/unit/expressions.spec.ts b/test/unit/expressions.spec.ts index 71c014fda..12fd85be1 100644 --- a/test/unit/expressions.spec.ts +++ b/test/unit/expressions.spec.ts @@ -18,9 +18,13 @@ test.each([ util.testFunction(input).disableSemanticCheck().expectLuaToMatchSnapshot(); }); -test.each(["3+4", "5-2", "6*3", "6**3", "20/5", "15/10", "15%3"])("Binary expressions basic numeric (%p)", input => { - util.testExpression(input).expectToMatchJsResult(); -}); +for (const expression of ["3+4", "5-2", "6*3", "6**3", "20/5", "15/10", "15%3"]) { + util.testEachVersion( + `Binary expressions basic numeric (${expression})`, + () => util.testExpression(expression), + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); +} test.each(["1==1", "1===1", "1!=1", "1!==1", "1>1", "1>=1", "1<1", "1<=1", "1&&1", "1||1"])( "Binary expressions basic boolean (%p)", @@ -48,6 +52,14 @@ test.each(["a+=b", "a-=b", "a*=b", "a/=b", "a%=b", "a**=b"])("Binary expressions const supportedInAll = ["~a", "a&b", "a&=b", "a|b", "a|=b", "a^b", "a^=b", "a<>>b", "a>>>=b"]; const unsupportedIn53And54 = ["a>>b", "a>>=b"]; const allBinaryOperators = [...supportedInAll, ...unsupportedIn53And54]; +test.each(allBinaryOperators)("Bitop [5.0] (%p)", input => { + // Bit operations not supported in 5.0, expect an exception + util.testExpression(input) + .setOptions({ luaTarget: tstl.LuaTarget.Lua50, luaLibImport: tstl.LuaLibImportKind.None }) + .disableSemanticCheck() + .expectDiagnosticsToMatchSnapshot([unsupportedForTarget.code]); +}); + test.each(allBinaryOperators)("Bitop [5.1] (%p)", input => { // Bit operations not supported in 5.1, expect an exception util.testExpression(input) @@ -84,6 +96,13 @@ test.each(supportedInAll)("Bitop [5.4] (%p)", input => { .expectLuaToMatchSnapshot(); }); +test.each(supportedInAll)("Bitop [5.5] (%p)", input => { + util.testExpression(input) + .setOptions({ luaTarget: tstl.LuaTarget.Lua55, luaLibImport: tstl.LuaLibImportKind.None }) + .disableSemanticCheck() + .expectLuaToMatchSnapshot(); +}); + test.each(unsupportedIn53And54)("Unsupported bitop 5.3 (%p)", input => { util.testExpression(input) .setOptions({ luaTarget: tstl.LuaTarget.Lua53, luaLibImport: tstl.LuaLibImportKind.None }) @@ -98,6 +117,35 @@ test.each(unsupportedIn53And54)("Unsupported bitop 5.4 (%p)", input => { .expectDiagnosticsToMatchSnapshot([unsupportedRightShiftOperator.code]); }); +// Execution tests: verify >>> produces correct results matching JS semantics +for (const expression of ["-5 >>> 0", "-1 >>> 0", "1 >>> 0", "-1 >>> 16", "255 >>> 4"]) { + util.testEachVersion(`Unsigned right shift execution (${expression})`, () => util.testExpression(expression), { + [tstl.LuaTarget.Universal]: false, + [tstl.LuaTarget.Lua50]: false, // No bit library in WASM runtime + [tstl.LuaTarget.Lua51]: false, // No bit library in WASM runtime + [tstl.LuaTarget.Lua52]: builder => builder.expectToMatchJsResult(), + [tstl.LuaTarget.Lua53]: builder => builder.expectToMatchJsResult(), + [tstl.LuaTarget.Lua54]: builder => builder.expectToMatchJsResult(), + [tstl.LuaTarget.Lua55]: builder => builder.expectToMatchJsResult(), + [tstl.LuaTarget.LuaJIT]: false, // Can't execute LuaJIT in tests + [tstl.LuaTarget.Luau]: false, + }); +} + +for (const code of ["let a = -5; a >>>= 0; return a;", "let a = -1; a >>>= 16; return a;"]) { + util.testEachVersion(`Unsigned right shift assignment execution (${code})`, () => util.testFunction(code), { + [tstl.LuaTarget.Universal]: false, + [tstl.LuaTarget.Lua50]: false, // No bit library in WASM runtime + [tstl.LuaTarget.Lua51]: false, // No bit library in WASM runtime + [tstl.LuaTarget.Lua52]: builder => builder.expectToMatchJsResult(), + [tstl.LuaTarget.Lua53]: builder => builder.expectToMatchJsResult(), + [tstl.LuaTarget.Lua54]: builder => builder.expectToMatchJsResult(), + [tstl.LuaTarget.Lua55]: builder => builder.expectToMatchJsResult(), + [tstl.LuaTarget.LuaJIT]: false, // Can't execute LuaJIT in tests + [tstl.LuaTarget.Luau]: false, + }); +} + test.each(["1+1", "-1+1", "1*30+4", "1*(3+4)", "1*(3+4*2)", "10-(4+5)"])( "Binary expressions ordering parentheses (%p)", input => { @@ -153,6 +201,21 @@ test("Non-null expression", () => { `.expectToMatchJsResult(); }); +test("Typescript 4.7 instantiation expression", () => { + util.testFunction` + function foo(x: T): T { return x; } + const bar = foo; + return bar(3); + `.expectToMatchJsResult(); +}); + +test("Typescript 4.9 satisfies expression", () => { + util.testFunction` + const foo = { a: 1 } satisfies { a: number }; + return foo.a; + `.expectToMatchJsResult(); +}); + test.each([ '"foobar"', "17", diff --git a/test/unit/find-lua-requires.spec.ts b/test/unit/find-lua-requires.spec.ts new file mode 100644 index 000000000..bb879d642 --- /dev/null +++ b/test/unit/find-lua-requires.spec.ts @@ -0,0 +1,159 @@ +import { findLuaRequires, LuaRequire } from "../../src/transpilation/find-lua-requires"; + +test("empty string", () => { + expect(findLuaRequires("")).toEqual([]); +}); + +test("can find requires", () => { + const lua = ` + require("req1") + require('req2') + local r3 = require("req3") + `; + expect(requirePaths(findLuaRequires(lua))).toEqual(["req1", "req2", "req3"]); +}); + +test("handles requires with spacing", () => { + const lua = 'require \t ( \t "req" )'; + expect(requirePaths(findLuaRequires(lua))).toEqual(["req"]); +}); + +test("handles requires without parentheses", () => { + const lua = 'require "req"'; + expect(requirePaths(findLuaRequires(lua))).toEqual(["req"]); +}); + +test("has correct offsets", () => { + const lua = ` + require("req1") + require('req2') + local r3 = require("req3") + `; + const requires = findLuaRequires(lua); + expect(requires).toHaveLength(3); + expect(lua.substring(requires[0].from, requires[0].to + 1)).toBe('require("req1")'); + expect(lua.substring(requires[1].from, requires[1].to + 1)).toBe("require('req2')"); + expect(lua.substring(requires[2].from, requires[2].to + 1)).toBe('require("req3")'); +}); + +test("has correct offsets for offsets without parentheses", () => { + const lua = ` + require"req1" + require 'req2' + local r3 = require"req3" + `; + const requires = findLuaRequires(lua); + expect(requires).toHaveLength(3); + expect(lua.substring(requires[0].from, requires[0].to + 1)).toBe('require"req1"'); + expect(lua.substring(requires[1].from, requires[1].to + 1)).toBe("require 'req2'"); + expect(lua.substring(requires[2].from, requires[2].to + 1)).toBe('require"req3"'); +}); + +test("ignores requires that should not be included", () => { + const lua = ` + require("req1") + local a = "require('This should not be included')" + require('req2') + local b = 'require("This should not be included")' + require("req3") + -- require("this should not be included") + require("req4") + --[[ require("this should not be included") ]] + require("req5") + `; + expect(requirePaths(findLuaRequires(lua))).toEqual(["req1", "req2", "req3", "req4", "req5"]); +}); + +test("non-terminated require", () => { + expect(findLuaRequires("require('abc")).toEqual([]); +}); + +describe.each(['"', "'"])("strings with delimiter %p", delimiter => { + test("escaped delimiter", () => { + const lua = ` + require(${delimiter}req1${delimiter}); + local a = ${delimiter}require(excludeThis\\${delimiter})${delimiter} + require(${delimiter}req\\${delimiter}2${delimiter}); + `; + expect(requirePaths(findLuaRequires(lua))).toEqual(["req1", `req${delimiter}2`]); + }); + + test("multiple escaped delimiters", () => { + const lua = ` + require(${delimiter}r\\${delimiter}e.- q%\\${delimiter}1${delimiter}) + `; + expect(requirePaths(findLuaRequires(lua))).toEqual([`r${delimiter}e.- q%${delimiter}1`]); + }); + + test("handles other escaped characters", () => { + expect(requirePaths(findLuaRequires(`require(${delimiter}req\\n\\\\${delimiter})`))).toEqual(["req\\n\\\\"]); + }); + + test("handles non-delimiter quote", () => { + const oppositeDelimiter = delimiter === "'" ? '"' : "'"; + const lua = ` + require(${delimiter}req1${delimiter}); + local a = ${delimiter}require(excludeThis${oppositeDelimiter})${delimiter}; + require(${delimiter}req2${oppositeDelimiter}${delimiter}); + `; + expect(requirePaths(findLuaRequires(lua))).toEqual(["req1", `req2${oppositeDelimiter}`]); + }); + + test("non-terminated string", () => { + const lua = ` + require(${delimiter}myRequire${delimiter}); + local a = ${delimiter}require("excludeThis") + `; + expect(requirePaths(findLuaRequires(lua))).toEqual(["myRequire"]); + }); +}); + +describe("single-line comments", () => { + test("comment at end of file", () => { + expect(findLuaRequires("--")).toEqual([]); + }); + + test("require before and after comment", () => { + const lua = ` + require("req1")-- comment\nrequire("req2") + `; + expect(requirePaths(findLuaRequires(lua))).toEqual(["req1", "req2"]); + }); + + test("require before and after empty comment", () => { + const lua = ` + require("req1")--\nrequire("req2") + `; + expect(requirePaths(findLuaRequires(lua))).toEqual(["req1", "req2"]); + }); +}); + +describe("multi-line comments", () => { + test("comment at end of file", () => { + expect(findLuaRequires("--[[]]")).toEqual([]); + }); + + test("unterminated comment", () => { + expect(findLuaRequires("--[[")).toEqual([]); + }); + + test("require before and after comment", () => { + const lua = ` + require("req1")--[[ + ml comment require("this should be excluded") + ]]require("req2") + `; + expect(requirePaths(findLuaRequires(lua))).toEqual(["req1", "req2"]); + }); + + test("require before and after empty comment", () => { + const lua = ` + require("req1")--[[]]require("req2") + `; + expect(requirePaths(findLuaRequires(lua))).toEqual(["req1", "req2"]); + }); +}); + +function requirePaths(matches: LuaRequire[]): string[] { + return matches.map(m => m.requirePath); +} diff --git a/test/unit/functions/__snapshots__/functions.spec.ts.snap b/test/unit/functions/__snapshots__/functions.spec.ts.snap index 345e31731..83345e13b 100644 --- a/test/unit/functions/__snapshots__/functions.spec.ts.snap +++ b/test/unit/functions/__snapshots__/functions.spec.ts.snap @@ -1,5 +1,39 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`Function default parameter with value "null" 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function foo(self, x) + return x + end + return foo(nil) +end +return ____exports" +`; + +exports[`Function default parameter with value "undefined" 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function foo(self, x) + return x + end + return foo(nil) +end +return ____exports" +`; + +exports[`function.length unsupported ("5.0"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local function fn(self) + end + return debug.getinfo(fn).nparams - 1 +end +return ____exports" +`; + +exports[`function.length unsupported ("5.0"): diagnostics 1`] = `"main.ts(3,20): error TSTL: function.length is/are not supported for target Lua 5.0."`; + exports[`function.length unsupported ("5.1"): code 1`] = ` "local ____exports = {} function ____exports.__main(self) @@ -10,7 +44,7 @@ end return ____exports" `; -exports[`function.length unsupported ("5.1"): diagnostics 1`] = `"main.ts(3,16): error TSTL: function.length is/are not supported for target Lua 5.1."`; +exports[`function.length unsupported ("5.1"): diagnostics 1`] = `"main.ts(3,20): error TSTL: function.length is/are not supported for target Lua 5.1."`; exports[`function.length unsupported ("universal"): code 1`] = ` "local ____exports = {} @@ -22,7 +56,7 @@ end return ____exports" `; -exports[`function.length unsupported ("universal"): diagnostics 1`] = `"main.ts(3,16): error TSTL: function.length is/are not supported for target Lua 5.1."`; +exports[`function.length unsupported ("universal"): diagnostics 1`] = `"main.ts(3,20): error TSTL: function.length is/are not supported for target Lua universal."`; exports[`missing declaration name: code 1`] = ` "function ____(self) diff --git a/test/unit/functions/__snapshots__/noSelfAnnotation.spec.ts.snap b/test/unit/functions/__snapshots__/noSelfAnnotation.spec.ts.snap index f4e5fdd6d..64eb65457 100644 --- a/test/unit/functions/__snapshots__/noSelfAnnotation.spec.ts.snap +++ b/test/unit/functions/__snapshots__/noSelfAnnotation.spec.ts.snap @@ -13,3 +13,8 @@ exports[`@noSelf on parent class declaration removes context argument 1`] = `"ho exports[`@noSelf on parent interface declaration removes context argument 1`] = `"holder.myMethod()"`; exports[`@noSelf on parent namespace declaration removes context argument 1`] = `"MyNamespace.myMethod()"`; + +exports[`@noSelf on static class methods with string key access 1`] = ` +"TestClass.myMethod() +TestClass.myKey()" +`; diff --git a/test/unit/functions/functionProperties.spec.ts b/test/unit/functions/functionProperties.spec.ts new file mode 100644 index 000000000..85b3ec155 --- /dev/null +++ b/test/unit/functions/functionProperties.spec.ts @@ -0,0 +1,202 @@ +import * as util from "../../util"; + +test("property on function", () => { + util.testFunction` + function foo(s: string) { return s; } + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("property on void function", () => { + util.testFunction` + function foo(this: void, s: string) { return s; } + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("property on recursively referenced function", () => { + util.testFunction` + function foo(s: string) { return s + foo.bar; } + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("property on hoisted function", () => { + util.testFunction` + foo.bar = "bar"; + function foo(s: string) { return s; } + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("function merged with namespace", () => { + util.testModule` + function foo(s: string) { return s; } + namespace foo { + export let bar = "bar"; + } + export const result = foo("foo") + foo.bar; + ` + .setReturnExport("result") + .expectToEqual("foobar"); +}); + +test("function with property assigned to variable", () => { + util.testFunction` + const foo = function(s: string) { return s; }; + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("void function with property assigned to variable", () => { + util.testFunction` + const foo = function(this: void, s: string) { return s; }; + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("named function with property assigned to variable", () => { + util.testFunction` + const foo = function baz(s: string) { return s; } + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("recursively referenced function with property assigned to variable", () => { + util.testFunction` + const foo = function(s: string) { return s + foo.bar; }; + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("named recursively referenced function with property assigned to variable", () => { + util.testFunction` + const foo = function baz(s: string) { return s + foo.bar + baz.bar; }; + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("arrow function with property assigned to variable", () => { + util.testFunction` + const foo: { (s: string): string; bar: string; } = s => s; + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("void arrow function with property assigned to variable", () => { + util.testFunction` + const foo: { (this: void, s: string): string; bar: string; } = s => s; + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("recursively referenced arrow function with property assigned to variable", () => { + util.testFunction` + const foo: { (s: string): string; bar: string; } = s => s + foo.bar; + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); + +test("property on generator function", () => { + util.testFunction` + function *foo(s: string) { yield s; } + foo.bar = "bar"; + for (const s of foo("foo")) { + return s + foo.bar; + } + `.expectToMatchJsResult(); +}); + +test("generator function assigned to variable", () => { + util.testFunction` + const foo = function *(s: string) { yield s; } + foo.bar = "bar"; + for (const s of foo("foo")) { + return s + foo.bar; + } + `.expectToMatchJsResult(); +}); + +test("property on async function", () => { + util.testFunction` + let result = ""; + async function foo(s: string) { result = s + foo.bar; } + foo.bar = "bar"; + void foo("foo"); + return result; + `.expectToMatchJsResult(); +}); + +test("async function with property assigned to variable", () => { + util.testFunction` + let result = ""; + const foo = async function(s: string) { result = s + foo.bar; } + foo.bar = "bar"; + void foo("foo"); + return result; + `.expectToMatchJsResult(); +}); + +test("async arrow function with property assigned to variable", () => { + util.testFunction` + let result = ""; + const foo: { (s: string): Promise; bar: string; } = async s => { result = s + foo.bar; }; + foo.bar = "bar"; + void foo("foo"); + return result; + `.expectToMatchJsResult(); +}); + +test("call function with property using call method", () => { + util.testFunction` + function foo(this: string, s: string) { return this + s; } + foo.baz = "baz"; + return foo.call("foo", "bar") + foo.baz; + `.expectToMatchJsResult(); +}); + +test("call function with property using apply method", () => { + util.testFunction` + function foo(this: string, s: string) { return this + s; } + foo.baz = "baz"; + return foo.apply("foo", ["bar"]) + foo.baz; + `.expectToMatchJsResult(); +}); + +test("call function with property using bind method", () => { + util.testFunction` + function foo(this: string, s: string) { return this + s; } + foo.baz = "baz"; + return foo.bind("foo", "bar")() + foo.baz; + `.expectToMatchJsResult(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1196 +test("Does not wrap simple variable declaration", () => { + util.testFunction` + function foo(s: string) { return s; } + foo.bar = "bar"; + const foo2 = foo; + return foo2("foo") + foo2.bar; + `.expectToMatchJsResult(); +}); + +test("Wraps function in inner expression", () => { + util.testFunction` + type Foo = { (s: string): string; bar: string; } + const foo = (s => s)! as Foo + foo.bar = "bar"; + return foo("foo") + foo.bar; + `.expectToMatchJsResult(); +}); diff --git a/test/unit/functions/functions.spec.ts b/test/unit/functions/functions.spec.ts index 2d9d8f1e3..27b48a1ee 100644 --- a/test/unit/functions/functions.spec.ts +++ b/test/unit/functions/functions.spec.ts @@ -1,10 +1,11 @@ +import * as ts from "typescript"; import * as tstl from "../../../src"; import * as util from "../../util"; import { unsupportedForTarget } from "../../../src/transformation/utils/diagnostics"; test("Arrow Function Expression", () => { util.testFunction` - const add = (a, b) => a + b; + const add = (a: number, b: number) => a + b; return add(1, 2); `.expectToMatchJsResult(); }); @@ -24,17 +25,22 @@ test.each(["i++", "i--", "++i", "--i"])("Arrow function unary expression (%p)", `.expectToMatchJsResult(); }); -test.each(["b => a = b", "b => a += b", "b => a -= b", "b => a *= b", "b => a /= b", "b => a **= b", "b => a %= b"])( - "Arrow function assignment (%p)", - lambda => { - util.testFunction` +test.each([ + "(b: number) => a = b", + "(b: number) => a += b", + "(b: number) => a -= b", + "(b: number) => a *= b", + "(b: number) => a /= b", + "(b: number) => a **= b", + "(b: number) => a %= b", +])("Arrow function assignment (%p)", lambda => { + util.testFunction` let a = 10; let lambda = ${lambda}; lambda(5); return a; `.expectToMatchJsResult(); - } -); +}); test.each([{ args: [] }, { args: [1] }, { args: [1, 2] }])("Arrow default values (%p)", ({ args }) => { util.testFunction` @@ -45,7 +51,7 @@ test.each([{ args: [] }, { args: [1] }, { args: [1, 2] }])("Arrow default values test("Function Expression", () => { util.testFunction` - let add = function(a, b) {return a+b}; + let add = function(a: number, b: number) {return a+b}; return add(1,2); `.expectToMatchJsResult(); }); @@ -106,6 +112,48 @@ test("Function default binding parameter maintains order", () => { `.expectToMatchJsResult(); }); +test.each(["undefined", "null"])("Function default parameter with value %p", defaultValue => { + util.testFunction` + function foo(x = ${defaultValue}) { + return x; + } + return foo(); + ` + .expectToMatchJsResult() + .tap(builder => { + const lua = builder.getMainLuaCodeChunk(); + expect(lua).not.toMatch("if x == nil then"); + }) + .expectLuaToMatchSnapshot(); +}); + +test("Function default parameter with preceding statements", () => { + util.testFunction` + let i = 1 + function foo(x = i++) { + return x; + } + return [i, foo(), i]; + `.expectToMatchJsResult(); +}); + +test("Function default parameter with nil value and preceding statements", () => { + util.testFunction` + const a = new LuaTable() + a.set("foo", "bar") + function foo(x: any = a.set("foo", "baz")) { + return x ?? "nil"; + } + return [a.get("foo"), foo(), a.get("foo")]; + ` + .withLanguageExtensions() + .tap(builder => { + const lua = builder.getMainLuaCodeChunk(); + expect(lua).not.toMatch(" x = nil"); + }) + .expectToEqual(["bar", "nil", "baz"]); +}); + test("Class method call", () => { util.testFunction` class TestClass { @@ -159,35 +207,62 @@ test("Class static dot method with parameter", () => { `.expectToMatchJsResult(); }); -test("Function bind", () => { +const functionTypeDeclarations = [ + ["arrow", ": (...args: any) => any"], + ["call signature", ": { (...args: any): any; }"], + ["generic", ": Function"], + ["inferred", ""], +]; + +test.each(functionTypeDeclarations)("Function bind (%s)", (_, type) => { util.testFunction` - const abc = function (this: { a: number }, a: string, b: string) { return this.a + a + b; } + const abc${type} = function (this: { a: number }, a: string, b: string) { return this.a + a + b; } return abc.bind({ a: 4 }, "b")("c"); `.expectToMatchJsResult(); }); -test("Function apply", () => { +test.each(functionTypeDeclarations)("Function apply (%s)", (_, type) => { util.testFunction` - const abc = function (this: { a: number }, a: string) { return this.a + a; } + const abc${type} = function (this: { a: number }, a: string) { return this.a + a; } return abc.apply({ a: 4 }, ["b"]); `.expectToMatchJsResult(); }); -test("Function call", () => { +// Fix #1226: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1226 +test.each(functionTypeDeclarations)("function apply without arguments should not lead to exception (%s)", (_, type) => { + util.testFunction` + const f${type} = function (this: number) { return this + 3; } + return f.apply(4); + `.expectToMatchJsResult(); +}); + +// Issue #1218: https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1218 +test.each(["() => 4", "undefined"])("prototype call on nullable function (%p)", value => { util.testFunction` - const abc = function (this: { a: number }, a: string) { return this.a + a; } + function call(f?: () => number) { + return f?.apply(3); + } + return call(${value}); + ` + .setOptions({ strictNullChecks: true }) + .expectToMatchJsResult(); +}); + +test.each(functionTypeDeclarations)("Function call (%s)", (_, type) => { + util.testFunction` + const abc${type} = function (this: { a: number }, a: string) { return this.a + a; } return abc.call({ a: 4 }, "b"); `.expectToMatchJsResult(); }); test.each([ - "function fn() {}", - "function fn(x, y, z) {}", - "function fn(x, y, z, ...args) {}", - "function fn(...args) {}", - "function fn(this: void) {}", - "function fn(this: void, x, y, z) {}", - "function fnReference(x, y, z) {} const fn = fnReference;", + "function fn(): void {}", + "function fn(x: any, y: any, z: any): void {}", + "function fn(x: any, y: any, z: any, ...args: any[]): void {}", + "function fn(...args: any[]): void {}", + "function fn(this: void): void {}", + "function fn(this: void, x: any, y: any, z: any): void {}", + "function fnReference(x: any, y: any, z: any): void {} const fn = fnReference;", "const wrap = (fn: (...args: any[]) => any) => (...args: any[]) => fn(...args); const fn = wrap((x, y, z) => {});", ])("function.length (%p)", declaration => { util.testFunction` @@ -196,14 +271,17 @@ test.each([ `.expectToMatchJsResult(); }); -test.each([tstl.LuaTarget.Lua51, tstl.LuaTarget.Universal])("function.length unsupported (%p)", luaTarget => { - util.testFunction` - function fn() {} - return fn.length; - ` - .setOptions({ luaTarget }) - .expectDiagnosticsToMatchSnapshot([unsupportedForTarget.code]); -}); +test.each([tstl.LuaTarget.Lua50, tstl.LuaTarget.Lua51, tstl.LuaTarget.Universal])( + "function.length unsupported (%p)", + luaTarget => { + util.testFunction` + function fn() {} + return fn.length; + ` + .setOptions({ luaTarget }) + .expectDiagnosticsToMatchSnapshot([unsupportedForTarget.code]); + } +); test("Recursive function definition", () => { util.testFunction` @@ -248,6 +326,14 @@ test("Object method declaration", () => { `.expectToMatchJsResult(); }); +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1266 +test("Object method declaration used with non-null operator (#1266)", () => { + util.testFunction` + let o = { v: 4, m(i: number): number { return this.v * i; } }; + return o.m!(3); + `.expectToMatchJsResult(); +}); + test.each([ { args: ["bar"], expected: "foobar" }, { args: ["baz", "bar"], expected: "bazbar" }, @@ -351,7 +437,7 @@ test("Complex element access call no args", () => { test("Complex element access call statement", () => { util.testFunction` - let foo: string; + let foo: string | undefined; class C { prop = "bar"; method(s: string) { foo = s + this.prop; } @@ -477,3 +563,15 @@ test("top-level function declaration is global", () => { .addExtraFile("a.ts", 'function foo() { return "foo" }') .expectToEqual({ result: "foo" }); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1325 +test("call expression should not throw (#1325)", () => { + util.testModule` + function test(iterator:Iterator) { + iterator.return?.(); + } + ` + // Note: does not reproduce without strict=true + .setOptions({ target: ts.ScriptTarget.ESNext, strict: true }) + .expectToHaveNoDiagnostics(); +}); diff --git a/test/unit/functions/generators.spec.ts b/test/unit/functions/generators.spec.ts index 7065aecf6..6cf935528 100644 --- a/test/unit/functions/generators.spec.ts +++ b/test/unit/functions/generators.spec.ts @@ -1,3 +1,5 @@ +import { LuaTarget } from "../../../src/CompilerOptions"; +import { unsupportedForTarget } from "../../../src/transformation/utils/diagnostics"; import * as util from "../../util"; test("generator parameters", () => { @@ -25,7 +27,7 @@ test(".next()", () => { test(".next() with parameters", () => { util.testFunction` - function* generator() { + function* generator(): Generator { return yield 0; } @@ -147,3 +149,23 @@ test("hoisting", () => { } `.expectToMatchJsResult(); }); + +util.testEachVersion( + "generator yield inside try/catch", + () => util.testFunction` + function* generator() { + try { + yield 4; + } catch { + throw "something went wrong"; + } + } + return generator().next(); + `, + // Cannot execute LuaJIT with test runner + { + ...util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()), + [LuaTarget.Lua50]: builder => builder.expectToHaveDiagnostics([unsupportedForTarget.code]), + [LuaTarget.Lua51]: builder => builder.expectToHaveDiagnostics([unsupportedForTarget.code]), + } +); diff --git a/test/unit/functions/noImplicitGlobalVariables.spec.ts b/test/unit/functions/noImplicitGlobalVariables.spec.ts new file mode 100644 index 000000000..00bd6195a --- /dev/null +++ b/test/unit/functions/noImplicitGlobalVariables.spec.ts @@ -0,0 +1,30 @@ +import * as util from "../../util"; + +test("normal TSTL creates global variables", () => { + const builder = util.testModule` + function foo() {} + const bar = 123; + `.expectToHaveNoDiagnostics(); + + const transpiledFile = builder.getLuaResult().transpiledFiles[0]; + expect(transpiledFile).toBeDefined(); + const { lua } = transpiledFile; + expect(lua).toBeDefined(); + expect(lua).not.toContain("local"); +}); + +test("noImplicitGlobalVariables does not create any global variables", () => { + const builder = util.testModule` + function foo() {} + const bar = 123; + ` + .setOptions({ noImplicitGlobalVariables: true }) + .expectToHaveNoDiagnostics(); + + const transpiledFile = builder.getLuaResult().transpiledFiles[0]; + expect(transpiledFile).toBeDefined(); + const { lua } = transpiledFile; + expect(lua).toBeDefined(); + expect(lua).toContain("local function foo("); + expect(lua).toContain("local bar ="); +}); diff --git a/test/unit/functions/noImplicitSelfOption.spec.ts b/test/unit/functions/noImplicitSelfOption.spec.ts index 2da1d9722..ef788c922 100644 --- a/test/unit/functions/noImplicitSelfOption.spec.ts +++ b/test/unit/functions/noImplicitSelfOption.spec.ts @@ -1,3 +1,5 @@ +import * as path from "path"; +import { transpileFiles } from "../../../src"; import { couldNotResolveRequire } from "../../../src/transpilation/diagnostics"; import * as util from "../../util"; @@ -5,11 +7,46 @@ test("enables noSelfInFile behavior for functions", () => { util.testFunction` function fooBar() {} const test: (this: void) => void = fooBar; + fooBar(); ` .setOptions({ noImplicitSelf: true }) .expectToHaveNoDiagnostics(); }); +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1084 +test.each(["\\", "/"])("transpileFiles handles paths with noImplicitSelf and %s separator (#1084)", separator => { + const projectDir = `${path.dirname(path.dirname(__dirname))}${separator}transpile${separator}project`; + const emittedFiles: Record = {}; + const { diagnostics } = transpileFiles( + [ + `${projectDir}${separator}index.ts`, + `${projectDir}${separator}api.d.ts`, + `${projectDir}${separator}otherFile.ts`, + ], + { noImplicitSelf: true }, + (fileName, text) => (emittedFiles[fileName] = text) + ); + expect(diagnostics).toHaveLength(0); + expect(Object.keys(emittedFiles)).not.toHaveLength(0); + for (const fileContent of Object.values(emittedFiles)) { + expect(fileContent).toContain("getNumber()"); + expect(fileContent).not.toContain("getNumber(self)"); + expect(fileContent).not.toContain("getNumber(_G)"); + } +}); + +test("noImplicitSelf does not affect functions in default libraries", () => { + util.testFunction` + const array = [1, 2, 3]; + const items = array.filter(x => x > 1); // array.filter is in external library + return items; + ` + .setOptions({ + noImplicitSelf: true, + }) + .expectToMatchJsResult(); +}); + test("enables noSelfInFile behavior for methods", () => { util.testFunction` class FooBar { @@ -42,3 +79,17 @@ test("generates declaration files with @noSelfInFile", () => { .ignoreDiagnostics([couldNotResolveRequire.code]) // no foo implementation in the project to create foo.lua .expectToHaveNoDiagnostics(); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1292 +test("explicit this parameter respected over noImplicitSelf", () => { + util.testModule` + function foo(this: unknown, arg: any) { + return {self: this, arg}; + } + export const result = foo(1); + ` + .setOptions({ + noImplicitSelf: true, + }) + .expectToMatchJsResult(); +}); diff --git a/test/unit/functions/noSelfAnnotation.spec.ts b/test/unit/functions/noSelfAnnotation.spec.ts index ab77ec03e..e9fbb853d 100644 --- a/test/unit/functions/noSelfAnnotation.spec.ts +++ b/test/unit/functions/noSelfAnnotation.spec.ts @@ -51,3 +51,126 @@ test("@noSelf on parent namespace declaration removes context argument", () => { MyNamespace.myMethod(); `.expectLuaToMatchSnapshot(); }); + +test("@noSelf on static class methods with string key access", () => { + util.testModule` + /** @noSelf */ + declare class TestClass { + static [key: string]: () => void; + static myMethod(): void; + } + + TestClass.myMethod(); + TestClass.myKey(); + `.expectLuaToMatchSnapshot(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1661 +// A Lua-side function observes the actual argc, so a missing @noSelf would +// surface as a phantom leading nil (argc 2 instead of 1). +const argcProbeHeader = ` + function probe(...) + return select("#", ...) + end +`; + +test("@noSelf on interface call signature: Lua probe sees correct argc", () => { + util.testModule` + /** @noSelf */ + interface Probe { (a: string): number; } + declare const probe: Probe; + export const result = probe("hi"); + ` + .setLuaHeader(argcProbeHeader) + .expectToEqual({ result: 1 }); +}); + +test("@noSelf parent interface, property typed by call-signature interface: Lua probe sees correct argc", () => { + util.testModule` + /** @noSelf */ + interface CallSignature { (a: string): number; } + /** @noSelf */ + interface Holder { fn: CallSignature; } + declare const holder: Holder; + export const result = holder.fn("hi"); + ` + .setLuaHeader(`${argcProbeHeader}\nholder = { fn = probe }`) + .expectToEqual({ result: 1 }); +}); + +test("@noSelf parent interface, property typed by type-literal call signature: Lua probe sees correct argc", () => { + util.testModule` + /** @noSelf */ + interface Holder { fn: { (a: string): number }; } + declare const holder: Holder; + export const result = holder.fn("hi"); + ` + .setLuaHeader(`${argcProbeHeader}\nholder = { fn = probe }`) + .expectToEqual({ result: 1 }); +}); + +// additional coverage for https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1292 +test("explicit this parameter respected over @noSelf", () => { + util.testModule` + /** @noSelfInFile */ + function foo(this: unknown, arg: any) { + return {self: this, arg}; + } + export const result = foo(1); + `.expectToMatchJsResult(); +}); + +test("respect noSelfInFile over noImplicitSelf", () => { + const result = util.testModule` + /** @noSelfInFile **/ + const func: Function = () => 1; + export const result = func(1); + ` + .expectToMatchJsResult() + .getLuaResult(); + + expect(result.transpiledFiles).not.toHaveLength(0); + + const mainFile = result.transpiledFiles.find(f => f.outPath === "main.lua"); + expect(mainFile).toBeDefined(); + + // avoid ts error "not defined", even though toBeDefined is being checked above + if (!mainFile) return; + + expect(mainFile.lua).toBeDefined(); + expect(mainFile.lua).toContain("func(1)"); +}); + +test("respect noSelfInFile over noImplicitSelf (func declared in other file)", () => { + const result = util.testModule` + import { func, result } from "./functions"; + + export const result1 = result; + export const result2 = func(1); + ` + .addExtraFile( + "functions.ts", + ` + /** @noSelfInFile **/ + export const func: Function = () => 1; + export const result = func(2); + ` + ) + .expectToMatchJsResult() + .getLuaResult(); + + expect(result.transpiledFiles).not.toHaveLength(0); + + const mainFile = result.transpiledFiles.find(f => f.outPath.includes("main.lua")); + expect(mainFile).toBeDefined(); + const functionFile = result.transpiledFiles.find(f => f.outPath.includes("functions.lua")); + expect(functionFile).toBeDefined(); + + // avoid ts error "not defined", even though toBeDefined is being checked above + if (!mainFile || !functionFile) return; + + expect(mainFile.lua).toBeDefined(); + expect(mainFile.lua).toContain("func(1)"); + expect(mainFile.lua).toBeDefined(); + expect(functionFile.lua).toContain("func(2)"); +}); diff --git a/test/unit/functions/validation/__snapshots__/invalidFunctionAssignments.spec.ts.snap b/test/unit/functions/validation/__snapshots__/invalidFunctionAssignments.spec.ts.snap index 7ed8e7560..87cb07b9d 100644 --- a/test/unit/functions/validation/__snapshots__/invalidFunctionAssignments.spec.ts.snap +++ b/test/unit/functions/validation/__snapshots__/invalidFunctionAssignments.spec.ts.snap @@ -179,15 +179,15 @@ exports[`Invalid function argument ({"definition": "class NoSelfAnonFuncNSMerged exports[`Invalid function argument ({"definition": "class NoSelfAnonFuncNSMergedClass { method(s: string): string { return s; } } /** @noSelf */ namespace NoSelfAnonFuncNSMergedClass { export function nsFunc(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedClass.nsFunc"}): diagnostics 2`] = `"main.ts(5,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function argument ({"definition": "class NoSelfMethodClass { +exports[`Invalid function argument ({"definition": "class NoSelfMethodClass { /** @noSelf */ - noSelfMethod(s: string): string { return s; } + noSelfMethod(s: string): string { return s; } } const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}): diagnostics 1`] = `"main.ts(8,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function argument ({"definition": "class NoSelfMethodClass { +exports[`Invalid function argument ({"definition": "class NoSelfMethodClass { /** @noSelf */ - noSelfMethod(s: string): string { return s; } + noSelfMethod(s: string): string { return s; } } const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}): diagnostics 2`] = `"main.ts(8,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; @@ -249,16 +249,16 @@ exports[`Invalid function argument ({"definition": "interface FuncPropInterface exports[`Invalid function argument ({"definition": "interface MethodInterface { method(this: any, s: string): string; } const methodInterface: MethodInterface = { method: function(this: any, s: string): string { return s; } };", "value": "methodInterface.method"}): diagnostics 1`] = `"main.ts(5,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function argument ({"definition": "interface NoSelfMethodInterface { - /** @noSelf */ +exports[`Invalid function argument ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ noSelfMethod(s: string): string; } const noSelfMethodInterface: NoSelfMethodInterface = { noSelfMethod: function(s: string): string { return s; } };", "value": "noSelfMethodInterface.noSelfMethod"}): diagnostics 1`] = `"main.ts(10,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function argument ({"definition": "interface NoSelfMethodInterface { - /** @noSelf */ +exports[`Invalid function argument ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ noSelfMethod(s: string): string; } const noSelfMethodInterface: NoSelfMethodInterface = { @@ -340,17 +340,17 @@ exports[`Invalid function argument ({"definition": "namespace NoSelfFuncNs { exports[`Invalid function argument ({"definition": "namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncSelf(s: string): string { return s; } } /** @noSelf */ namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "SelfAnonFuncNSMergedNoSelfNS.nsFuncSelf"}): diagnostics 1`] = `"main.ts(5,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function argument ({"value": "(function(this: any, s) { return s; })"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; +exports[`Invalid function argument ({"value": "(function(this: any, s: string) { return s; })"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function argument ({"value": "(function(this: void, s) { return s; })"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function argument ({"value": "(function(this: void, s: string) { return s; })"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function argument ({"value": "(function(this: void, s) { return s; })"}): diagnostics 2`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function argument ({"value": "(function(this: void, s: string) { return s; })"}): diagnostics 2`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function argument ({"value": "function(this: any, s) { return s; }"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; +exports[`Invalid function argument ({"value": "function(this: any, s: string) { return s; }"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function argument ({"value": "function(this: void, s) { return s; }"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function argument ({"value": "function(this: void, s: string) { return s; }"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function argument ({"value": "function(this: void, s) { return s; }"}): diagnostics 2`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function argument ({"value": "function(this: void, s: string) { return s; }"}): diagnostics 2`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; exports[`Invalid function argument with cast ({"definition": "/** @noSelfInFile */ let noSelfInFileFunc: {(s: string): string} = function(s) { return s; };", "value": "noSelfInFileFunc"}): diagnostics 1`] = ` "main.ts(4,23): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'. @@ -571,15 +571,15 @@ exports[`Invalid function assignment ({"definition": "class NoSelfAnonFuncNSMerg exports[`Invalid function assignment ({"definition": "class NoSelfAnonFuncNSMergedClass { method(s: string): string { return s; } } /** @noSelf */ namespace NoSelfAnonFuncNSMergedClass { export function nsFunc(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedClass.nsFunc"}): diagnostics 2`] = `"main.ts(5,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function assignment ({"definition": "class NoSelfMethodClass { +exports[`Invalid function assignment ({"definition": "class NoSelfMethodClass { /** @noSelf */ - noSelfMethod(s: string): string { return s; } + noSelfMethod(s: string): string { return s; } } const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}): diagnostics 1`] = `"main.ts(8,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function assignment ({"definition": "class NoSelfMethodClass { +exports[`Invalid function assignment ({"definition": "class NoSelfMethodClass { /** @noSelf */ - noSelfMethod(s: string): string { return s; } + noSelfMethod(s: string): string { return s; } } const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}): diagnostics 2`] = `"main.ts(8,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; @@ -641,16 +641,16 @@ exports[`Invalid function assignment ({"definition": "interface FuncPropInterfac exports[`Invalid function assignment ({"definition": "interface MethodInterface { method(this: any, s: string): string; } const methodInterface: MethodInterface = { method: function(this: any, s: string): string { return s; } };", "value": "methodInterface.method"}): diagnostics 1`] = `"main.ts(5,18): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function assignment ({"definition": "interface NoSelfMethodInterface { - /** @noSelf */ +exports[`Invalid function assignment ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ noSelfMethod(s: string): string; } const noSelfMethodInterface: NoSelfMethodInterface = { noSelfMethod: function(s: string): string { return s; } };", "value": "noSelfMethodInterface.noSelfMethod"}): diagnostics 1`] = `"main.ts(10,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function assignment ({"definition": "interface NoSelfMethodInterface { - /** @noSelf */ +exports[`Invalid function assignment ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ noSelfMethod(s: string): string; } const noSelfMethodInterface: NoSelfMethodInterface = { @@ -732,17 +732,17 @@ exports[`Invalid function assignment ({"definition": "namespace NoSelfFuncNs { exports[`Invalid function assignment ({"definition": "namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncSelf(s: string): string { return s; } } /** @noSelf */ namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "SelfAnonFuncNSMergedNoSelfNS.nsFuncSelf"}): diagnostics 1`] = `"main.ts(5,18): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function assignment ({"value": "(function(this: any, s) { return s; })"}): diagnostics 1`] = `"main.ts(4,18): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; +exports[`Invalid function assignment ({"value": "(function(this: any, s: string) { return s; })"}): diagnostics 1`] = `"main.ts(4,18): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function assignment ({"value": "(function(this: void, s) { return s; })"}): diagnostics 1`] = `"main.ts(4,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function assignment ({"value": "(function(this: void, s: string) { return s; })"}): diagnostics 1`] = `"main.ts(4,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function assignment ({"value": "(function(this: void, s) { return s; })"}): diagnostics 2`] = `"main.ts(4,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function assignment ({"value": "(function(this: void, s: string) { return s; })"}): diagnostics 2`] = `"main.ts(4,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function assignment ({"value": "function(this: any, s) { return s; }"}): diagnostics 1`] = `"main.ts(4,18): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; +exports[`Invalid function assignment ({"value": "function(this: any, s: string) { return s; }"}): diagnostics 1`] = `"main.ts(4,18): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function assignment ({"value": "function(this: void, s) { return s; }"}): diagnostics 1`] = `"main.ts(4,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function assignment ({"value": "function(this: void, s: string) { return s; }"}): diagnostics 1`] = `"main.ts(4,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function assignment ({"value": "function(this: void, s) { return s; }"}): diagnostics 2`] = `"main.ts(4,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function assignment ({"value": "function(this: void, s: string) { return s; }"}): diagnostics 2`] = `"main.ts(4,18): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; exports[`Invalid function assignment with cast ({"definition": "/** @noSelfInFile */ let noSelfInFileFunc: {(s: string): string} = function(s) { return s; };", "value": "noSelfInFileFunc"}): diagnostics 1`] = ` "main.ts(4,14): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'. @@ -963,15 +963,15 @@ exports[`Invalid function generic argument ({"definition": "class NoSelfAnonFunc exports[`Invalid function generic argument ({"definition": "class NoSelfAnonFuncNSMergedClass { method(s: string): string { return s; } } /** @noSelf */ namespace NoSelfAnonFuncNSMergedClass { export function nsFunc(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedClass.nsFunc"}): diagnostics 2`] = `"main.ts(5,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function generic argument ({"definition": "class NoSelfMethodClass { +exports[`Invalid function generic argument ({"definition": "class NoSelfMethodClass { /** @noSelf */ - noSelfMethod(s: string): string { return s; } + noSelfMethod(s: string): string { return s; } } const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}): diagnostics 1`] = `"main.ts(8,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function generic argument ({"definition": "class NoSelfMethodClass { +exports[`Invalid function generic argument ({"definition": "class NoSelfMethodClass { /** @noSelf */ - noSelfMethod(s: string): string { return s; } + noSelfMethod(s: string): string { return s; } } const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}): diagnostics 2`] = `"main.ts(8,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; @@ -1033,16 +1033,16 @@ exports[`Invalid function generic argument ({"definition": "interface FuncPropIn exports[`Invalid function generic argument ({"definition": "interface MethodInterface { method(this: any, s: string): string; } const methodInterface: MethodInterface = { method: function(this: any, s: string): string { return s; } };", "value": "methodInterface.method"}): diagnostics 1`] = `"main.ts(5,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function generic argument ({"definition": "interface NoSelfMethodInterface { - /** @noSelf */ +exports[`Invalid function generic argument ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ noSelfMethod(s: string): string; } const noSelfMethodInterface: NoSelfMethodInterface = { noSelfMethod: function(s: string): string { return s; } };", "value": "noSelfMethodInterface.noSelfMethod"}): diagnostics 1`] = `"main.ts(10,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function generic argument ({"definition": "interface NoSelfMethodInterface { - /** @noSelf */ +exports[`Invalid function generic argument ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ noSelfMethod(s: string): string; } const noSelfMethodInterface: NoSelfMethodInterface = { @@ -1124,17 +1124,17 @@ exports[`Invalid function generic argument ({"definition": "namespace NoSelfFunc exports[`Invalid function generic argument ({"definition": "namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncSelf(s: string): string { return s; } } /** @noSelf */ namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "SelfAnonFuncNSMergedNoSelfNS.nsFuncSelf"}): diagnostics 1`] = `"main.ts(5,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function generic argument ({"value": "(function(this: any, s) { return s; })"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; +exports[`Invalid function generic argument ({"value": "(function(this: any, s: string) { return s; })"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function generic argument ({"value": "(function(this: void, s) { return s; })"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function generic argument ({"value": "(function(this: void, s: string) { return s; })"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function generic argument ({"value": "(function(this: void, s) { return s; })"}): diagnostics 2`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function generic argument ({"value": "(function(this: void, s: string) { return s; })"}): diagnostics 2`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function generic argument ({"value": "function(this: any, s) { return s; }"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; +exports[`Invalid function generic argument ({"value": "function(this: any, s: string) { return s; }"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function generic argument ({"value": "function(this: void, s) { return s; }"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function generic argument ({"value": "function(this: void, s: string) { return s; }"}): diagnostics 1`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function generic argument ({"value": "function(this: void, s) { return s; }"}): diagnostics 2`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function generic argument ({"value": "function(this: void, s: string) { return s; }"}): diagnostics 2`] = `"main.ts(4,27): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; exports[`Invalid function overload assignment ("(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(7,52): error TSTL: Unsupported assignment of function with different overloaded types for 'this'. Overloads should all have the same type for 'this'."`; @@ -1323,15 +1323,15 @@ exports[`Invalid function return ({"definition": "class NoSelfAnonFuncNSMergedCl exports[`Invalid function return ({"definition": "class NoSelfAnonFuncNSMergedClass { method(s: string): string { return s; } } /** @noSelf */ namespace NoSelfAnonFuncNSMergedClass { export function nsFunc(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedClass.nsFunc"}): diagnostics 2`] = `"main.ts(5,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function return ({"definition": "class NoSelfMethodClass { +exports[`Invalid function return ({"definition": "class NoSelfMethodClass { /** @noSelf */ - noSelfMethod(s: string): string { return s; } + noSelfMethod(s: string): string { return s; } } const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}): diagnostics 1`] = `"main.ts(8,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function return ({"definition": "class NoSelfMethodClass { +exports[`Invalid function return ({"definition": "class NoSelfMethodClass { /** @noSelf */ - noSelfMethod(s: string): string { return s; } + noSelfMethod(s: string): string { return s; } } const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}): diagnostics 2`] = `"main.ts(8,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; @@ -1393,16 +1393,16 @@ exports[`Invalid function return ({"definition": "interface FuncPropInterface { exports[`Invalid function return ({"definition": "interface MethodInterface { method(this: any, s: string): string; } const methodInterface: MethodInterface = { method: function(this: any, s: string): string { return s; } };", "value": "methodInterface.method"}): diagnostics 1`] = `"main.ts(5,17): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function return ({"definition": "interface NoSelfMethodInterface { - /** @noSelf */ +exports[`Invalid function return ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ noSelfMethod(s: string): string; } const noSelfMethodInterface: NoSelfMethodInterface = { noSelfMethod: function(s: string): string { return s; } };", "value": "noSelfMethodInterface.noSelfMethod"}): diagnostics 1`] = `"main.ts(10,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function return ({"definition": "interface NoSelfMethodInterface { - /** @noSelf */ +exports[`Invalid function return ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ noSelfMethod(s: string): string; } const noSelfMethodInterface: NoSelfMethodInterface = { @@ -1484,17 +1484,17 @@ exports[`Invalid function return ({"definition": "namespace NoSelfFuncNs { exports[`Invalid function return ({"definition": "namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncSelf(s: string): string { return s; } } /** @noSelf */ namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "SelfAnonFuncNSMergedNoSelfNS.nsFuncSelf"}): diagnostics 1`] = `"main.ts(5,17): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function return ({"value": "(function(this: any, s) { return s; })"}): diagnostics 1`] = `"main.ts(4,17): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; +exports[`Invalid function return ({"value": "(function(this: any, s: string) { return s; })"}): diagnostics 1`] = `"main.ts(4,17): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function return ({"value": "(function(this: void, s) { return s; })"}): diagnostics 1`] = `"main.ts(4,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function return ({"value": "(function(this: void, s: string) { return s; })"}): diagnostics 1`] = `"main.ts(4,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function return ({"value": "(function(this: void, s) { return s; })"}): diagnostics 2`] = `"main.ts(4,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function return ({"value": "(function(this: void, s: string) { return s; })"}): diagnostics 2`] = `"main.ts(4,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function return ({"value": "function(this: any, s) { return s; }"}): diagnostics 1`] = `"main.ts(4,17): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; +exports[`Invalid function return ({"value": "function(this: any, s: string) { return s; }"}): diagnostics 1`] = `"main.ts(4,17): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function return ({"value": "function(this: void, s) { return s; }"}): diagnostics 1`] = `"main.ts(4,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function return ({"value": "function(this: void, s: string) { return s; }"}): diagnostics 1`] = `"main.ts(4,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function return ({"value": "function(this: void, s) { return s; }"}): diagnostics 2`] = `"main.ts(4,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function return ({"value": "function(this: void, s: string) { return s; }"}): diagnostics 2`] = `"main.ts(4,17): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; exports[`Invalid function return with cast ({"definition": "/** @noSelfInFile */ let noSelfInFileFunc: {(s: string): string} = function(s) { return s; };", "value": "noSelfInFileFunc"}): diagnostics 1`] = ` "main.ts(4,17): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'. @@ -1538,9 +1538,10 @@ main.ts(4,24): error TSTL: Unable to convert function with no 'this' parameter t exports[`Invalid function tuple assignment: diagnostics 1`] = ` "main.ts(5,13): error TS2322: Type '[number, Meth]' is not assignable to type '[number, Func]'. - Type 'Meth' is not assignable to type 'Func'. - The 'this' types of each signature are incompatible. - Type 'void' is not assignable to type '{}'. + Type at position 1 in source is not compatible with type at position 1 in target. + Type 'Meth' is not assignable to type 'Func'. + The 'this' types of each signature are incompatible. + Type 'void' is not assignable to type '{}'. main.ts(5,38): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'." `; @@ -1723,15 +1724,15 @@ exports[`Invalid function variable declaration ({"definition": "class NoSelfAnon exports[`Invalid function variable declaration ({"definition": "class NoSelfAnonFuncNSMergedClass { method(s: string): string { return s; } } /** @noSelf */ namespace NoSelfAnonFuncNSMergedClass { export function nsFunc(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedClass.nsFunc"}): diagnostics 2`] = `"main.ts(4,58): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function variable declaration ({"definition": "class NoSelfMethodClass { +exports[`Invalid function variable declaration ({"definition": "class NoSelfMethodClass { /** @noSelf */ - noSelfMethod(s: string): string { return s; } + noSelfMethod(s: string): string { return s; } } const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}): diagnostics 1`] = `"main.ts(7,47): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function variable declaration ({"definition": "class NoSelfMethodClass { +exports[`Invalid function variable declaration ({"definition": "class NoSelfMethodClass { /** @noSelf */ - noSelfMethod(s: string): string { return s; } + noSelfMethod(s: string): string { return s; } } const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}): diagnostics 2`] = `"main.ts(7,58): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; @@ -1793,16 +1794,16 @@ exports[`Invalid function variable declaration ({"definition": "interface FuncPr exports[`Invalid function variable declaration ({"definition": "interface MethodInterface { method(this: any, s: string): string; } const methodInterface: MethodInterface = { method: function(this: any, s: string): string { return s; } };", "value": "methodInterface.method"}): diagnostics 1`] = `"main.ts(4,59): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function variable declaration ({"definition": "interface NoSelfMethodInterface { - /** @noSelf */ +exports[`Invalid function variable declaration ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ noSelfMethod(s: string): string; } const noSelfMethodInterface: NoSelfMethodInterface = { noSelfMethod: function(s: string): string { return s; } };", "value": "noSelfMethodInterface.noSelfMethod"}): diagnostics 1`] = `"main.ts(9,47): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function variable declaration ({"definition": "interface NoSelfMethodInterface { - /** @noSelf */ +exports[`Invalid function variable declaration ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ noSelfMethod(s: string): string; } const noSelfMethodInterface: NoSelfMethodInterface = { @@ -1884,20 +1885,1167 @@ exports[`Invalid function variable declaration ({"definition": "namespace NoSelf exports[`Invalid function variable declaration ({"definition": "namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncSelf(s: string): string { return s; } } /** @noSelf */ namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "SelfAnonFuncNSMergedNoSelfNS.nsFuncSelf"}): diagnostics 1`] = `"main.ts(4,59): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function variable declaration ({"value": "(function(this: any, s) { return s; })"}): diagnostics 1`] = `"main.ts(3,59): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; +exports[`Invalid function variable declaration ({"value": "(function(this: any, s: string) { return s; })"}): diagnostics 1`] = `"main.ts(3,59): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function variable declaration ({"value": "(function(this: void, s) { return s; })"}): diagnostics 1`] = `"main.ts(3,47): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function variable declaration ({"value": "(function(this: void, s: string) { return s; })"}): diagnostics 1`] = `"main.ts(3,47): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function variable declaration ({"value": "(function(this: void, s) { return s; })"}): diagnostics 2`] = `"main.ts(3,58): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function variable declaration ({"value": "(function(this: void, s: string) { return s; })"}): diagnostics 2`] = `"main.ts(3,58): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function variable declaration ({"value": "function(this: any, s) { return s; }"}): diagnostics 1`] = `"main.ts(3,59): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; +exports[`Invalid function variable declaration ({"value": "function(this: any, s: string) { return s; }"}): diagnostics 1`] = `"main.ts(3,59): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; -exports[`Invalid function variable declaration ({"value": "function(this: void, s) { return s; }"}): diagnostics 1`] = `"main.ts(3,47): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function variable declaration ({"value": "function(this: void, s: string) { return s; }"}): diagnostics 1`] = `"main.ts(3,47): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; -exports[`Invalid function variable declaration ({"value": "function(this: void, s) { return s; }"}): diagnostics 2`] = `"main.ts(3,58): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; +exports[`Invalid function variable declaration ({"value": "function(this: void, s: string) { return s; }"}): diagnostics 2`] = `"main.ts(3,58): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; exports[`Invalid interface method assignment: diagnostics 1`] = `"main.ts(5,22): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; exports[`Invalid lua lib function argument: diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'callbackfn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; exports[`Invalid method tuple assignment: diagnostics 1`] = `"main.ts(5,38): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class AnonFuncNSMergedNoSelfClass { method(s: string): string { return s; } } + namespace AnonFuncNSMergedNoSelfClass { export function nsFunc(s: string) { return s; } }", "value": "AnonFuncNSMergedNoSelfClass.nsFunc"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class AnonFunctionNestedInNoSelfClass { + method() { return function(s: string) { return s; } } + } + const anonFunctionNestedInNoSelfClass = (new AnonFunctionNestedInNoSelfClass).method();", "value": "anonFunctionNestedInNoSelfClass"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class NoSelfAnonMethodClassMergedNS { method(s: string): string { return s; } } + namespace NoSelfAnonMethodClassMergedNS { export function nsFunc(s: string) { return s; } } + const noSelfAnonMethodClassMergedNS = new NoSelfAnonMethodClassMergedNS();", "value": "noSelfAnonMethodClassMergedNS.method"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class NoSelfAnonMethodClassMergedNS { method(s: string): string { return s; } } + namespace NoSelfAnonMethodClassMergedNS { export function nsFunc(s: string) { return s; } } + const noSelfAnonMethodClassMergedNS = new NoSelfAnonMethodClassMergedNS();", "value": "noSelfAnonMethodClassMergedNS.method"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class NoSelfFuncPropClass { noSelfFuncProp: (s: string) => string = s => s; } + const noSelfFuncPropClass = new NoSelfFuncPropClass();", "value": "noSelfFuncPropClass.noSelfFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class NoSelfFuncPropClass { noSelfFuncProp: (s: string) => string = s => s; } + const noSelfFuncPropClass = new NoSelfFuncPropClass();", "value": "noSelfFuncPropClass.noSelfFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class NoSelfMethodClass { noSelfMethod(s: string): string { return s; } } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class NoSelfMethodClass { noSelfMethod(s: string): string { return s; } } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class NoSelfStaticFuncPropClass { + static noSelfStaticFuncProp: (s: string) => string = s => s; + }", "value": "NoSelfStaticFuncPropClass.noSelfStaticFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class NoSelfStaticFuncPropClass { + static noSelfStaticFuncProp: (s: string) => string = s => s; + }", "value": "NoSelfStaticFuncPropClass.noSelfStaticFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class NoSelfStaticMethodClass { + static noSelfStaticMethod(s: string): string { return s; } + }", "value": "NoSelfStaticMethodClass.noSelfStaticMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ class NoSelfStaticMethodClass { + static noSelfStaticMethod(s: string): string { return s; } + }", "value": "NoSelfStaticMethodClass.noSelfStaticMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ const NoSelfMethodClassExpression = class { + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClassExpression = new NoSelfMethodClassExpression();", "value": "noSelfMethodClassExpression.noSelfMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ const NoSelfMethodClassExpression = class { + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClassExpression = new NoSelfMethodClassExpression();", "value": "noSelfMethodClassExpression.noSelfMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ interface NoSelfFuncPropInterface { noSelfFuncProp(s: string): string; } + const noSelfFuncPropInterface: NoSelfFuncPropInterface = { + noSelfFuncProp: (s: string): string => s + };", "value": "noSelfFuncPropInterface.noSelfFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ interface NoSelfFuncPropInterface { noSelfFuncProp(s: string): string; } + const noSelfFuncPropInterface: NoSelfFuncPropInterface = { + noSelfFuncProp: (s: string): string => s + };", "value": "noSelfFuncPropInterface.noSelfFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ interface NoSelfMethodInterface { noSelfMethod(s: string): string; } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ interface NoSelfMethodInterface { noSelfMethod(s: string): string; } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace AnonFunctionNestedInClassInNoSelfNs { + export class AnonFunctionNestedInClass { + method() { return function(s: string) { return s; } } + } + } + const anonFunctionNestedInClassInNoSelfNs = + (new AnonFunctionNestedInClassInNoSelfNs.AnonFunctionNestedInClass).method();", "value": "anonFunctionNestedInClassInNoSelfNs"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(10,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace AnonFunctionNestedInClassInNoSelfNs { + export class AnonFunctionNestedInClass { + method() { return function(s: string) { return s; } } + } + } + const anonFunctionNestedInClassInNoSelfNs = + (new AnonFunctionNestedInClassInNoSelfNs.AnonFunctionNestedInClass).method();", "value": "anonFunctionNestedInClassInNoSelfNs"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(10,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace AnonMethodClassInNoSelfNs { + export class MethodClass { + method(s: string): string { return s; } + } + } + const anonMethodClassInNoSelfNs = new AnonMethodClassInNoSelfNs.MethodClass();", "value": "anonMethodClassInNoSelfNs.method"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(9,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace AnonMethodInterfaceInNoSelfNs { + export interface MethodInterface { + method(s: string): string; + } + } + const anonMethodInterfaceInNoSelfNs: AnonMethodInterfaceInNoSelfNs.MethodInterface = { + method: function(s: string): string { return s; } + };", "value": "anonMethodInterfaceInNoSelfNs.method"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(11,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace NoSelfFuncNestedNs { + export namespace NestedNs { export function noSelfNestedNsFunc(s: string) { return s; } } + }", "value": "NoSelfFuncNestedNs.NestedNs.noSelfNestedNsFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace NoSelfFuncNestedNs { + export namespace NestedNs { export function noSelfNestedNsFunc(s: string) { return s; } } + }", "value": "NoSelfFuncNestedNs.NestedNs.noSelfNestedNsFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace NoSelfFuncNs { export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace NoSelfFuncNs { export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace NoSelfLambdaNestedNs { + export namespace NestedNs { export let noSelfNestedNsLambda: (s: string) => string = s => s } + }", "value": "NoSelfLambdaNestedNs.NestedNs.noSelfNestedNsLambda"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace NoSelfLambdaNestedNs { + export namespace NestedNs { export let noSelfNestedNsLambda: (s: string) => string = s => s } + }", "value": "NoSelfLambdaNestedNs.NestedNs.noSelfNestedNsLambda"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace NoSelfLambdaNs { + export let noSelfNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfLambdaNs.noSelfNsLambda"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelf */ namespace NoSelfLambdaNs { + export let noSelfNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfLambdaNs.noSelfNsLambda"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelfInFile */ class NoSelfInFileFuncNestedInClass { + method() { return function(s: string) { return s; } } + } + const noSelfInFileFuncNestedInClass = (new NoSelfInFileFuncNestedInClass).method();", "value": "noSelfInFileFuncNestedInClass"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelfInFile */ class NoSelfInFileFuncNestedInClass { + method() { return function(s: string) { return s; } } + } + const noSelfInFileFuncNestedInClass = (new NoSelfInFileFuncNestedInClass).method();", "value": "noSelfInFileFuncNestedInClass"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelfInFile */ let noSelfInFileFunc: {(s: string): string} = function(s) { return s; };", "value": "noSelfInFileFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelfInFile */ let noSelfInFileFunc: {(s: string): string} = function(s) { return s; };", "value": "noSelfInFileFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelfInFile */ let noSelfInFileLambda: (s: string) => string = s => s;", "value": "noSelfInFileLambda"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelfInFile */ let noSelfInFileLambda: (s: string) => string = s => s;", "value": "noSelfInFileLambda"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileFuncNs { + export function noSelfInFileNsFunc(s: string) { return s; } + }", "value": "NoSelfInFileFuncNs.noSelfInFileNsFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileFuncNs { + export function noSelfInFileNsFunc(s: string) { return s; } + }", "value": "NoSelfInFileFuncNs.noSelfInFileNsFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileLambdaNs { + export let noSelfInFileNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfInFileLambdaNs.noSelfInFileNsLambda"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileLambdaNs { + export let noSelfInFileNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfInFileLambdaNs.noSelfInFileNsLambda"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class AnonFuncPropClass { anonFuncProp: (s: string) => string = s => s; } + const anonFuncPropClass = new AnonFuncPropClass();", "value": "anonFuncPropClass.anonFuncProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "class AnonMethodClass { anonMethod(s: string): string { return s; } } + const anonMethodClass = new AnonMethodClass();", "value": "anonMethodClass.anonMethod"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "class AnonMethodClassMergedNoSelfNS { method(s: string): string { return s; } } + /** @noSelf */ namespace AnonMethodClassMergedNoSelfNS { export function nsFunc(s: string) { return s; } } + const anonMethodClassMergedNoSelfNS = new AnonMethodClassMergedNoSelfNS();", "value": "anonMethodClassMergedNoSelfNS.method"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "class AnonStaticFuncPropClass { + static anonStaticFuncProp: (s: string) => string = s => s; + }", "value": "AnonStaticFuncPropClass.anonStaticFuncProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "class AnonStaticMethodClass { static anonStaticMethod(s: string): string { return s; } }", "value": "AnonStaticMethodClass.anonStaticMethod"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "class FuncPropClass { funcProp: (this: any, s: string) => string = s => s; } + const funcPropClass = new FuncPropClass();", "value": "funcPropClass.funcProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "class MethodClass { method(this: any, s: string): string { return s; } } + const methodClass = new MethodClass();", "value": "methodClass.method"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "class NoSelfAnonFuncNSMergedClass { method(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedClass { export function nsFunc(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedClass.nsFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class NoSelfAnonFuncNSMergedClass { method(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedClass { export function nsFunc(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedClass.nsFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class NoSelfMethodClass { + /** @noSelf */ + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(8,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class NoSelfMethodClass { + /** @noSelf */ + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(8,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class StaticFuncPropClass { + static staticFuncProp: (this: any, s: string) => string = s => s; + }", "value": "StaticFuncPropClass.staticFuncProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "class StaticMethodClass { + static staticMethod(this: any, s: string): string { return s; } + }", "value": "StaticMethodClass.staticMethod"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "class StaticVoidFuncPropClass { + static staticVoidFuncProp: (this: void, s: string) => string = s => s; + }", "value": "StaticVoidFuncPropClass.staticVoidFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class StaticVoidFuncPropClass { + static staticVoidFuncProp: (this: void, s: string) => string = s => s; + }", "value": "StaticVoidFuncPropClass.staticVoidFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class StaticVoidMethodClass { + static staticVoidMethod(this: void, s: string): string { return s; } + }", "value": "StaticVoidMethodClass.staticVoidMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class StaticVoidMethodClass { + static staticVoidMethod(this: void, s: string): string { return s; } + }", "value": "StaticVoidMethodClass.staticVoidMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class VoidFuncPropClass { + voidFuncProp: (this: void, s: string) => string = s => s; + } + const voidFuncPropClass = new VoidFuncPropClass();", "value": "voidFuncPropClass.voidFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class VoidFuncPropClass { + voidFuncProp: (this: void, s: string) => string = s => s; + } + const voidFuncPropClass = new VoidFuncPropClass();", "value": "voidFuncPropClass.voidFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class VoidMethodClass { + voidMethod(this: void, s: string): string { return s; } + } + const voidMethodClass = new VoidMethodClass();", "value": "voidMethodClass.voidMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "class VoidMethodClass { + voidMethod(this: void, s: string): string { return s; } + } + const voidMethodClass = new VoidMethodClass();", "value": "voidMethodClass.voidMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "interface AnonFuncPropInterface { anonFuncProp: (s: string) => string; } + const anonFuncPropInterface: AnonFuncPropInterface = { anonFuncProp: (s: string): string => s };", "value": "anonFuncPropInterface.anonFuncProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "interface AnonMethodInterface { anonMethod(s: string): string; } + const anonMethodInterface: AnonMethodInterface = { + anonMethod: function(this: any, s: string): string { return s; } + };", "value": "anonMethodInterface.anonMethod"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(7,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "interface FuncPropInterface { funcProp: (this: any, s: string) => string; } + const funcPropInterface: FuncPropInterface = { funcProp: function(this: any, s: string) { return s; } };", "value": "funcPropInterface.funcProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "interface MethodInterface { method(this: any, s: string): string; } + const methodInterface: MethodInterface = { method: function(this: any, s: string): string { return s; } };", "value": "methodInterface.method"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ + noSelfMethod(s: string): string; + } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(10,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ + noSelfMethod(s: string): string; + } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(10,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "interface VoidFuncPropInterface { + voidFuncProp: (this: void, s: string) => string; + } + const voidFuncPropInterface: VoidFuncPropInterface = { + voidFuncProp: function(this: void, s: string): string { return s; } + };", "value": "voidFuncPropInterface.voidFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(9,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "interface VoidFuncPropInterface { + voidFuncProp: (this: void, s: string) => string; + } + const voidFuncPropInterface: VoidFuncPropInterface = { + voidFuncProp: function(this: void, s: string): string { return s; } + };", "value": "voidFuncPropInterface.voidFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(9,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "interface VoidMethodInterface { + voidMethod(this: void, s: string): string; + } + const voidMethodInterface: VoidMethodInterface = { + voidMethod(this: void, s: string): string { return s; } + };", "value": "voidMethodInterface.voidMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(9,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "interface VoidMethodInterface { + voidMethod(this: void, s: string): string; + } + const voidMethodInterface: VoidMethodInterface = { + voidMethod(this: void, s: string): string { return s; } + };", "value": "voidMethodInterface.voidMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(9,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "let anonFunc: {(s: string): string} = function(s) { return s; };", "value": "anonFunc"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "let anonLambda: (s: string) => string = s => s;", "value": "anonLambda"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "let selfFunc: {(this: any, s: string): string} = function(s) { return s; };", "value": "selfFunc"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "let selfLambda: (this: any, s: string) => string = s => s;", "value": "selfLambda"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "let voidFunc: {(this: void, s: string): string} = function(s) { return s; };", "value": "voidFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "let voidFunc: {(this: void, s: string): string} = function(s) { return s; };", "value": "voidFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "let voidLambda: (this: void, s: string) => string = s => s;", "value": "voidLambda"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "let voidLambda: (this: void, s: string) => string = s => s;", "value": "voidLambda"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "namespace FuncNestedNs { + export namespace NestedNs { export function nestedNsFunc(s: string) { return s; } } + }", "value": "FuncNestedNs.NestedNs.nestedNsFunc"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "namespace FuncNs { export function nsFunc(s: string) { return s; } }", "value": "FuncNs.nsFunc"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "namespace LambdaNestedNs { + export namespace NestedNs { export let nestedNsLambda: (s: string) => string = s => s } + }", "value": "LambdaNestedNs.NestedNs.nestedNsLambda"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "namespace LambdaNs { + export let nsLambda: (s: string) => string = s => s; + }", "value": "LambdaNs.nsLambda"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"definition": "namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncSelf(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedSelfNS.nsFuncNoSelf"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncSelf(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedSelfNS.nsFuncNoSelf"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "namespace NoSelfFuncNs { + /** @noSelf */ + export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "namespace NoSelfFuncNs { + /** @noSelf */ + export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"definition": "namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncSelf(s: string): string { return s; } } + /** @noSelf */ namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "SelfAnonFuncNSMergedNoSelfNS.nsFuncSelf"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"value": "(function(this: any, s: string) { return s; })"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"value": "(function(this: void, s: string) { return s; })"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"value": "(function(this: void, s: string) { return s; })"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"value": "function(this: any, s: string) { return s; }"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with a 'this' parameter to function 'obj.fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method argument ({"value": "function(this: void, s: string) { return s; }"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method argument ({"value": "function(this: void, s: string) { return s; }"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,35): error TSTL: Unable to convert function with no 'this' parameter to function 'obj.fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class AnonFuncNSMergedNoSelfClass { method(s: string): string { return s; } } + namespace AnonFuncNSMergedNoSelfClass { export function nsFunc(s: string) { return s; } }", "value": "AnonFuncNSMergedNoSelfClass.nsFunc"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class AnonFunctionNestedInNoSelfClass { + method() { return function(s: string) { return s; } } + } + const anonFunctionNestedInNoSelfClass = (new AnonFunctionNestedInNoSelfClass).method();", "value": "anonFunctionNestedInNoSelfClass"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class NoSelfAnonMethodClassMergedNS { method(s: string): string { return s; } } + namespace NoSelfAnonMethodClassMergedNS { export function nsFunc(s: string) { return s; } } + const noSelfAnonMethodClassMergedNS = new NoSelfAnonMethodClassMergedNS();", "value": "noSelfAnonMethodClassMergedNS.method"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class NoSelfAnonMethodClassMergedNS { method(s: string): string { return s; } } + namespace NoSelfAnonMethodClassMergedNS { export function nsFunc(s: string) { return s; } } + const noSelfAnonMethodClassMergedNS = new NoSelfAnonMethodClassMergedNS();", "value": "noSelfAnonMethodClassMergedNS.method"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class NoSelfFuncPropClass { noSelfFuncProp: (s: string) => string = s => s; } + const noSelfFuncPropClass = new NoSelfFuncPropClass();", "value": "noSelfFuncPropClass.noSelfFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class NoSelfFuncPropClass { noSelfFuncProp: (s: string) => string = s => s; } + const noSelfFuncPropClass = new NoSelfFuncPropClass();", "value": "noSelfFuncPropClass.noSelfFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class NoSelfMethodClass { noSelfMethod(s: string): string { return s; } } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class NoSelfMethodClass { noSelfMethod(s: string): string { return s; } } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class NoSelfStaticFuncPropClass { + static noSelfStaticFuncProp: (s: string) => string = s => s; + }", "value": "NoSelfStaticFuncPropClass.noSelfStaticFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class NoSelfStaticFuncPropClass { + static noSelfStaticFuncProp: (s: string) => string = s => s; + }", "value": "NoSelfStaticFuncPropClass.noSelfStaticFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class NoSelfStaticMethodClass { + static noSelfStaticMethod(s: string): string { return s; } + }", "value": "NoSelfStaticMethodClass.noSelfStaticMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ class NoSelfStaticMethodClass { + static noSelfStaticMethod(s: string): string { return s; } + }", "value": "NoSelfStaticMethodClass.noSelfStaticMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ const NoSelfMethodClassExpression = class { + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClassExpression = new NoSelfMethodClassExpression();", "value": "noSelfMethodClassExpression.noSelfMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ const NoSelfMethodClassExpression = class { + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClassExpression = new NoSelfMethodClassExpression();", "value": "noSelfMethodClassExpression.noSelfMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ interface NoSelfFuncPropInterface { noSelfFuncProp(s: string): string; } + const noSelfFuncPropInterface: NoSelfFuncPropInterface = { + noSelfFuncProp: (s: string): string => s + };", "value": "noSelfFuncPropInterface.noSelfFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ interface NoSelfFuncPropInterface { noSelfFuncProp(s: string): string; } + const noSelfFuncPropInterface: NoSelfFuncPropInterface = { + noSelfFuncProp: (s: string): string => s + };", "value": "noSelfFuncPropInterface.noSelfFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ interface NoSelfMethodInterface { noSelfMethod(s: string): string; } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ interface NoSelfMethodInterface { noSelfMethod(s: string): string; } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace AnonFunctionNestedInClassInNoSelfNs { + export class AnonFunctionNestedInClass { + method() { return function(s: string) { return s; } } + } + } + const anonFunctionNestedInClassInNoSelfNs = + (new AnonFunctionNestedInClassInNoSelfNs.AnonFunctionNestedInClass).method();", "value": "anonFunctionNestedInClassInNoSelfNs"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(10,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace AnonFunctionNestedInClassInNoSelfNs { + export class AnonFunctionNestedInClass { + method() { return function(s: string) { return s; } } + } + } + const anonFunctionNestedInClassInNoSelfNs = + (new AnonFunctionNestedInClassInNoSelfNs.AnonFunctionNestedInClass).method();", "value": "anonFunctionNestedInClassInNoSelfNs"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(10,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace AnonMethodClassInNoSelfNs { + export class MethodClass { + method(s: string): string { return s; } + } + } + const anonMethodClassInNoSelfNs = new AnonMethodClassInNoSelfNs.MethodClass();", "value": "anonMethodClassInNoSelfNs.method"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(9,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace AnonMethodInterfaceInNoSelfNs { + export interface MethodInterface { + method(s: string): string; + } + } + const anonMethodInterfaceInNoSelfNs: AnonMethodInterfaceInNoSelfNs.MethodInterface = { + method: function(s: string): string { return s; } + };", "value": "anonMethodInterfaceInNoSelfNs.method"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(11,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace NoSelfFuncNestedNs { + export namespace NestedNs { export function noSelfNestedNsFunc(s: string) { return s; } } + }", "value": "NoSelfFuncNestedNs.NestedNs.noSelfNestedNsFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace NoSelfFuncNestedNs { + export namespace NestedNs { export function noSelfNestedNsFunc(s: string) { return s; } } + }", "value": "NoSelfFuncNestedNs.NestedNs.noSelfNestedNsFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace NoSelfFuncNs { export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace NoSelfFuncNs { export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace NoSelfLambdaNestedNs { + export namespace NestedNs { export let noSelfNestedNsLambda: (s: string) => string = s => s } + }", "value": "NoSelfLambdaNestedNs.NestedNs.noSelfNestedNsLambda"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace NoSelfLambdaNestedNs { + export namespace NestedNs { export let noSelfNestedNsLambda: (s: string) => string = s => s } + }", "value": "NoSelfLambdaNestedNs.NestedNs.noSelfNestedNsLambda"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace NoSelfLambdaNs { + export let noSelfNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfLambdaNs.noSelfNsLambda"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelf */ namespace NoSelfLambdaNs { + export let noSelfNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfLambdaNs.noSelfNsLambda"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelfInFile */ class NoSelfInFileFuncNestedInClass { + method() { return function(s: string) { return s; } } + } + const noSelfInFileFuncNestedInClass = (new NoSelfInFileFuncNestedInClass).method();", "value": "noSelfInFileFuncNestedInClass"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelfInFile */ class NoSelfInFileFuncNestedInClass { + method() { return function(s: string) { return s; } } + } + const noSelfInFileFuncNestedInClass = (new NoSelfInFileFuncNestedInClass).method();", "value": "noSelfInFileFuncNestedInClass"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelfInFile */ let noSelfInFileFunc: {(s: string): string} = function(s) { return s; };", "value": "noSelfInFileFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelfInFile */ let noSelfInFileFunc: {(s: string): string} = function(s) { return s; };", "value": "noSelfInFileFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelfInFile */ let noSelfInFileLambda: (s: string) => string = s => s;", "value": "noSelfInFileLambda"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelfInFile */ let noSelfInFileLambda: (s: string) => string = s => s;", "value": "noSelfInFileLambda"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileFuncNs { + export function noSelfInFileNsFunc(s: string) { return s; } + }", "value": "NoSelfInFileFuncNs.noSelfInFileNsFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileFuncNs { + export function noSelfInFileNsFunc(s: string) { return s; } + }", "value": "NoSelfInFileFuncNs.noSelfInFileNsFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileLambdaNs { + export let noSelfInFileNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfInFileLambdaNs.noSelfInFileNsLambda"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileLambdaNs { + export let noSelfInFileNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfInFileLambdaNs.noSelfInFileNsLambda"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class AnonFuncPropClass { anonFuncProp: (s: string) => string = s => s; } + const anonFuncPropClass = new AnonFuncPropClass();", "value": "anonFuncPropClass.anonFuncProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "class AnonMethodClass { anonMethod(s: string): string { return s; } } + const anonMethodClass = new AnonMethodClass();", "value": "anonMethodClass.anonMethod"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "class AnonMethodClassMergedNoSelfNS { method(s: string): string { return s; } } + /** @noSelf */ namespace AnonMethodClassMergedNoSelfNS { export function nsFunc(s: string) { return s; } } + const anonMethodClassMergedNoSelfNS = new AnonMethodClassMergedNoSelfNS();", "value": "anonMethodClassMergedNoSelfNS.method"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "class AnonStaticFuncPropClass { + static anonStaticFuncProp: (s: string) => string = s => s; + }", "value": "AnonStaticFuncPropClass.anonStaticFuncProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "class AnonStaticMethodClass { static anonStaticMethod(s: string): string { return s; } }", "value": "AnonStaticMethodClass.anonStaticMethod"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "class FuncPropClass { funcProp: (this: any, s: string) => string = s => s; } + const funcPropClass = new FuncPropClass();", "value": "funcPropClass.funcProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "class MethodClass { method(this: any, s: string): string { return s; } } + const methodClass = new MethodClass();", "value": "methodClass.method"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "class NoSelfAnonFuncNSMergedClass { method(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedClass { export function nsFunc(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedClass.nsFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class NoSelfAnonFuncNSMergedClass { method(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedClass { export function nsFunc(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedClass.nsFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class NoSelfMethodClass { + /** @noSelf */ + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(8,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class NoSelfMethodClass { + /** @noSelf */ + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(8,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class StaticFuncPropClass { + static staticFuncProp: (this: any, s: string) => string = s => s; + }", "value": "StaticFuncPropClass.staticFuncProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "class StaticMethodClass { + static staticMethod(this: any, s: string): string { return s; } + }", "value": "StaticMethodClass.staticMethod"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "class StaticVoidFuncPropClass { + static staticVoidFuncProp: (this: void, s: string) => string = s => s; + }", "value": "StaticVoidFuncPropClass.staticVoidFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class StaticVoidFuncPropClass { + static staticVoidFuncProp: (this: void, s: string) => string = s => s; + }", "value": "StaticVoidFuncPropClass.staticVoidFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class StaticVoidMethodClass { + static staticVoidMethod(this: void, s: string): string { return s; } + }", "value": "StaticVoidMethodClass.staticVoidMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class StaticVoidMethodClass { + static staticVoidMethod(this: void, s: string): string { return s; } + }", "value": "StaticVoidMethodClass.staticVoidMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class VoidFuncPropClass { + voidFuncProp: (this: void, s: string) => string = s => s; + } + const voidFuncPropClass = new VoidFuncPropClass();", "value": "voidFuncPropClass.voidFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class VoidFuncPropClass { + voidFuncProp: (this: void, s: string) => string = s => s; + } + const voidFuncPropClass = new VoidFuncPropClass();", "value": "voidFuncPropClass.voidFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class VoidMethodClass { + voidMethod(this: void, s: string): string { return s; } + } + const voidMethodClass = new VoidMethodClass();", "value": "voidMethodClass.voidMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "class VoidMethodClass { + voidMethod(this: void, s: string): string { return s; } + } + const voidMethodClass = new VoidMethodClass();", "value": "voidMethodClass.voidMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "interface AnonFuncPropInterface { anonFuncProp: (s: string) => string; } + const anonFuncPropInterface: AnonFuncPropInterface = { anonFuncProp: (s: string): string => s };", "value": "anonFuncPropInterface.anonFuncProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "interface AnonMethodInterface { anonMethod(s: string): string; } + const anonMethodInterface: AnonMethodInterface = { + anonMethod: function(this: any, s: string): string { return s; } + };", "value": "anonMethodInterface.anonMethod"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(7,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "interface FuncPropInterface { funcProp: (this: any, s: string) => string; } + const funcPropInterface: FuncPropInterface = { funcProp: function(this: any, s: string) { return s; } };", "value": "funcPropInterface.funcProp"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "interface MethodInterface { method(this: any, s: string): string; } + const methodInterface: MethodInterface = { method: function(this: any, s: string): string { return s; } };", "value": "methodInterface.method"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ + noSelfMethod(s: string): string; + } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(10,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ + noSelfMethod(s: string): string; + } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(10,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "interface VoidFuncPropInterface { + voidFuncProp: (this: void, s: string) => string; + } + const voidFuncPropInterface: VoidFuncPropInterface = { + voidFuncProp: function(this: void, s: string): string { return s; } + };", "value": "voidFuncPropInterface.voidFuncProp"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(9,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "interface VoidFuncPropInterface { + voidFuncProp: (this: void, s: string) => string; + } + const voidFuncPropInterface: VoidFuncPropInterface = { + voidFuncProp: function(this: void, s: string): string { return s; } + };", "value": "voidFuncPropInterface.voidFuncProp"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(9,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "interface VoidMethodInterface { + voidMethod(this: void, s: string): string; + } + const voidMethodInterface: VoidMethodInterface = { + voidMethod(this: void, s: string): string { return s; } + };", "value": "voidMethodInterface.voidMethod"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(9,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "interface VoidMethodInterface { + voidMethod(this: void, s: string): string; + } + const voidMethodInterface: VoidMethodInterface = { + voidMethod(this: void, s: string): string { return s; } + };", "value": "voidMethodInterface.voidMethod"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(9,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "let anonFunc: {(s: string): string} = function(s) { return s; };", "value": "anonFunc"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "let anonLambda: (s: string) => string = s => s;", "value": "anonLambda"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "let selfFunc: {(this: any, s: string): string} = function(s) { return s; };", "value": "selfFunc"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "let selfLambda: (this: any, s: string) => string = s => s;", "value": "selfLambda"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "let voidFunc: {(this: void, s: string): string} = function(s) { return s; };", "value": "voidFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "let voidFunc: {(this: void, s: string): string} = function(s) { return s; };", "value": "voidFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "let voidLambda: (this: void, s: string) => string = s => s;", "value": "voidLambda"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "let voidLambda: (this: void, s: string) => string = s => s;", "value": "voidLambda"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "namespace FuncNestedNs { + export namespace NestedNs { export function nestedNsFunc(s: string) { return s; } } + }", "value": "FuncNestedNs.NestedNs.nestedNsFunc"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "namespace FuncNs { export function nsFunc(s: string) { return s; } }", "value": "FuncNs.nsFunc"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "namespace LambdaNestedNs { + export namespace NestedNs { export let nestedNsLambda: (s: string) => string = s => s } + }", "value": "LambdaNestedNs.NestedNs.nestedNsLambda"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "namespace LambdaNs { + export let nsLambda: (s: string) => string = s => s; + }", "value": "LambdaNs.nsLambda"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"definition": "namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncSelf(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedSelfNS.nsFuncNoSelf"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncSelf(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedSelfNS.nsFuncNoSelf"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "namespace NoSelfFuncNs { + /** @noSelf */ + export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "namespace NoSelfFuncNs { + /** @noSelf */ + export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(6,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"definition": "namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncSelf(s: string): string { return s; } } + /** @noSelf */ namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "SelfAnonFuncNSMergedNoSelfNS.nsFuncSelf"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(5,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"value": "(function(this: any, s: string) { return s; })"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"value": "(function(this: void, s: string) { return s; })"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"value": "(function(this: void, s: string) { return s; })"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"value": "function(this: any, s: string) { return s; }"}, "(this: void, s: string) => string", false): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method assignment ({"value": "function(this: void, s: string) { return s; }"}, "(s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment ({"value": "function(this: void, s: string) { return s; }"}, "(this: any, s: string) => string", true): diagnostics 1`] = `"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method assignment with cast ({"definition": "/** @noSelfInFile */ let noSelfInFileFunc: {(s: string): string} = function(s) { return s; };", "value": "noSelfInFileFunc"}, "(noSelfInFileFunc) as ((this: any, s: string) => string)", false): diagnostics 1`] = ` +"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'. +main.ts(4,24): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'." +`; + +exports[`Invalid object with method assignment with cast ({"definition": "/** @noSelfInFile */ let noSelfInFileFunc: {(s: string): string} = function(s) { return s; };", "value": "noSelfInFileFunc"}, "<(this: any, s: string) => string>(noSelfInFileFunc)", false): diagnostics 1`] = ` +"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'. +main.ts(4,24): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'." +`; + +exports[`Invalid object with method assignment with cast ({"definition": "let selfFunc: {(this: any, s: string): string} = function(s) { return s; };", "value": "selfFunc"}, "(selfFunc) as ((this: void, s: string) => string)", true): diagnostics 1`] = ` +"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'. +main.ts(4,24): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'." +`; + +exports[`Invalid object with method assignment with cast ({"definition": "let selfFunc: {(this: any, s: string): string} = function(s) { return s; };", "value": "selfFunc"}, "<(this: void, s: string) => string>(selfFunc)", true): diagnostics 1`] = ` +"main.ts(4,19): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'. +main.ts(4,24): error TSTL: Unable to convert function with a 'this' parameter to function with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'." +`; + +exports[`Invalid object with method assignment with cast ({"definition": "let voidFunc: {(this: void, s: string): string} = function(s) { return s; };", "value": "voidFunc"}, "(voidFunc) as ((s: string) => string)", false): diagnostics 1`] = ` +"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'. +main.ts(4,24): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'." +`; + +exports[`Invalid object with method assignment with cast ({"definition": "let voidFunc: {(this: void, s: string): string} = function(s) { return s; };", "value": "voidFunc"}, "(voidFunc) as ((this: any, s: string) => string)", false): diagnostics 1`] = ` +"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'. +main.ts(4,24): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'." +`; + +exports[`Invalid object with method assignment with cast ({"definition": "let voidFunc: {(this: void, s: string): string} = function(s) { return s; };", "value": "voidFunc"}, "<(s: string) => string>(voidFunc)", false): diagnostics 1`] = ` +"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'. +main.ts(4,24): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'." +`; + +exports[`Invalid object with method assignment with cast ({"definition": "let voidFunc: {(this: void, s: string): string} = function(s) { return s; };", "value": "voidFunc"}, "<(this: any, s: string) => string>(voidFunc)", false): diagnostics 1`] = ` +"main.ts(4,19): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'. +main.ts(4,24): error TSTL: Unable to convert function with no 'this' parameter to function with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'." +`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class AnonFuncNSMergedNoSelfClass { method(s: string): string { return s; } } + namespace AnonFuncNSMergedNoSelfClass { export function nsFunc(s: string) { return s; } }", "value": "AnonFuncNSMergedNoSelfClass.nsFunc"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(4,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class AnonFunctionNestedInNoSelfClass { + method() { return function(s: string) { return s; } } + } + const anonFunctionNestedInNoSelfClass = (new AnonFunctionNestedInNoSelfClass).method();", "value": "anonFunctionNestedInNoSelfClass"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(6,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class NoSelfAnonMethodClassMergedNS { method(s: string): string { return s; } } + namespace NoSelfAnonMethodClassMergedNS { export function nsFunc(s: string) { return s; } } + const noSelfAnonMethodClassMergedNS = new NoSelfAnonMethodClassMergedNS();", "value": "noSelfAnonMethodClassMergedNS.method"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(5,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class NoSelfAnonMethodClassMergedNS { method(s: string): string { return s; } } + namespace NoSelfAnonMethodClassMergedNS { export function nsFunc(s: string) { return s; } } + const noSelfAnonMethodClassMergedNS = new NoSelfAnonMethodClassMergedNS();", "value": "noSelfAnonMethodClassMergedNS.method"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(5,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class NoSelfFuncPropClass { noSelfFuncProp: (s: string) => string = s => s; } + const noSelfFuncPropClass = new NoSelfFuncPropClass();", "value": "noSelfFuncPropClass.noSelfFuncProp"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(4,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class NoSelfFuncPropClass { noSelfFuncProp: (s: string) => string = s => s; } + const noSelfFuncPropClass = new NoSelfFuncPropClass();", "value": "noSelfFuncPropClass.noSelfFuncProp"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(4,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class NoSelfMethodClass { noSelfMethod(s: string): string { return s; } } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(4,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class NoSelfMethodClass { noSelfMethod(s: string): string { return s; } } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(4,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class NoSelfStaticFuncPropClass { + static noSelfStaticFuncProp: (s: string) => string = s => s; + }", "value": "NoSelfStaticFuncPropClass.noSelfStaticFuncProp"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(5,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class NoSelfStaticFuncPropClass { + static noSelfStaticFuncProp: (s: string) => string = s => s; + }", "value": "NoSelfStaticFuncPropClass.noSelfStaticFuncProp"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(5,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class NoSelfStaticMethodClass { + static noSelfStaticMethod(s: string): string { return s; } + }", "value": "NoSelfStaticMethodClass.noSelfStaticMethod"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(5,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ class NoSelfStaticMethodClass { + static noSelfStaticMethod(s: string): string { return s; } + }", "value": "NoSelfStaticMethodClass.noSelfStaticMethod"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(5,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ const NoSelfMethodClassExpression = class { + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClassExpression = new NoSelfMethodClassExpression();", "value": "noSelfMethodClassExpression.noSelfMethod"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(6,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ const NoSelfMethodClassExpression = class { + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClassExpression = new NoSelfMethodClassExpression();", "value": "noSelfMethodClassExpression.noSelfMethod"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(6,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ interface NoSelfFuncPropInterface { noSelfFuncProp(s: string): string; } + const noSelfFuncPropInterface: NoSelfFuncPropInterface = { + noSelfFuncProp: (s: string): string => s + };", "value": "noSelfFuncPropInterface.noSelfFuncProp"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(6,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ interface NoSelfFuncPropInterface { noSelfFuncProp(s: string): string; } + const noSelfFuncPropInterface: NoSelfFuncPropInterface = { + noSelfFuncProp: (s: string): string => s + };", "value": "noSelfFuncPropInterface.noSelfFuncProp"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(6,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ interface NoSelfMethodInterface { noSelfMethod(s: string): string; } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(6,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ interface NoSelfMethodInterface { noSelfMethod(s: string): string; } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(6,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace AnonFunctionNestedInClassInNoSelfNs { + export class AnonFunctionNestedInClass { + method() { return function(s: string) { return s; } } + } + } + const anonFunctionNestedInClassInNoSelfNs = + (new AnonFunctionNestedInClassInNoSelfNs.AnonFunctionNestedInClass).method();", "value": "anonFunctionNestedInClassInNoSelfNs"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(9,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace AnonFunctionNestedInClassInNoSelfNs { + export class AnonFunctionNestedInClass { + method() { return function(s: string) { return s; } } + } + } + const anonFunctionNestedInClassInNoSelfNs = + (new AnonFunctionNestedInClassInNoSelfNs.AnonFunctionNestedInClass).method();", "value": "anonFunctionNestedInClassInNoSelfNs"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(9,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace AnonMethodClassInNoSelfNs { + export class MethodClass { + method(s: string): string { return s; } + } + } + const anonMethodClassInNoSelfNs = new AnonMethodClassInNoSelfNs.MethodClass();", "value": "anonMethodClassInNoSelfNs.method"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(8,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace AnonMethodInterfaceInNoSelfNs { + export interface MethodInterface { + method(s: string): string; + } + } + const anonMethodInterfaceInNoSelfNs: AnonMethodInterfaceInNoSelfNs.MethodInterface = { + method: function(s: string): string { return s; } + };", "value": "anonMethodInterfaceInNoSelfNs.method"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(10,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace NoSelfFuncNestedNs { + export namespace NestedNs { export function noSelfNestedNsFunc(s: string) { return s; } } + }", "value": "NoSelfFuncNestedNs.NestedNs.noSelfNestedNsFunc"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(5,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace NoSelfFuncNestedNs { + export namespace NestedNs { export function noSelfNestedNsFunc(s: string) { return s; } } + }", "value": "NoSelfFuncNestedNs.NestedNs.noSelfNestedNsFunc"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(5,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace NoSelfFuncNs { export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(3,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace NoSelfFuncNs { export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(3,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace NoSelfLambdaNestedNs { + export namespace NestedNs { export let noSelfNestedNsLambda: (s: string) => string = s => s } + }", "value": "NoSelfLambdaNestedNs.NestedNs.noSelfNestedNsLambda"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(5,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace NoSelfLambdaNestedNs { + export namespace NestedNs { export let noSelfNestedNsLambda: (s: string) => string = s => s } + }", "value": "NoSelfLambdaNestedNs.NestedNs.noSelfNestedNsLambda"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(5,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace NoSelfLambdaNs { + export let noSelfNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfLambdaNs.noSelfNsLambda"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(5,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelf */ namespace NoSelfLambdaNs { + export let noSelfNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfLambdaNs.noSelfNsLambda"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(5,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelfInFile */ class NoSelfInFileFuncNestedInClass { + method() { return function(s: string) { return s; } } + } + const noSelfInFileFuncNestedInClass = (new NoSelfInFileFuncNestedInClass).method();", "value": "noSelfInFileFuncNestedInClass"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(6,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelfInFile */ class NoSelfInFileFuncNestedInClass { + method() { return function(s: string) { return s; } } + } + const noSelfInFileFuncNestedInClass = (new NoSelfInFileFuncNestedInClass).method();", "value": "noSelfInFileFuncNestedInClass"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(6,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelfInFile */ let noSelfInFileFunc: {(s: string): string} = function(s) { return s; };", "value": "noSelfInFileFunc"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(3,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelfInFile */ let noSelfInFileFunc: {(s: string): string} = function(s) { return s; };", "value": "noSelfInFileFunc"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(3,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelfInFile */ let noSelfInFileLambda: (s: string) => string = s => s;", "value": "noSelfInFileLambda"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(3,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelfInFile */ let noSelfInFileLambda: (s: string) => string = s => s;", "value": "noSelfInFileLambda"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(3,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileFuncNs { + export function noSelfInFileNsFunc(s: string) { return s; } + }", "value": "NoSelfInFileFuncNs.noSelfInFileNsFunc"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(5,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileFuncNs { + export function noSelfInFileNsFunc(s: string) { return s; } + }", "value": "NoSelfInFileFuncNs.noSelfInFileNsFunc"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(5,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileLambdaNs { + export let noSelfInFileNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfInFileLambdaNs.noSelfInFileNsLambda"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(5,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "/** @noSelfInFile */ namespace NoSelfInFileLambdaNs { + export let noSelfInFileNsLambda: (s: string) => string = s => s; + }", "value": "NoSelfInFileLambdaNs.noSelfInFileNsLambda"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(5,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class AnonFuncPropClass { anonFuncProp: (s: string) => string = s => s; } + const anonFuncPropClass = new AnonFuncPropClass();", "value": "anonFuncPropClass.anonFuncProp"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(4,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class AnonMethodClass { anonMethod(s: string): string { return s; } } + const anonMethodClass = new AnonMethodClass();", "value": "anonMethodClass.anonMethod"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(4,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class AnonMethodClassMergedNoSelfNS { method(s: string): string { return s; } } + /** @noSelf */ namespace AnonMethodClassMergedNoSelfNS { export function nsFunc(s: string) { return s; } } + const anonMethodClassMergedNoSelfNS = new AnonMethodClassMergedNoSelfNS();", "value": "anonMethodClassMergedNoSelfNS.method"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(5,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class AnonStaticFuncPropClass { + static anonStaticFuncProp: (s: string) => string = s => s; + }", "value": "AnonStaticFuncPropClass.anonStaticFuncProp"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(5,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class AnonStaticMethodClass { static anonStaticMethod(s: string): string { return s; } }", "value": "AnonStaticMethodClass.anonStaticMethod"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(3,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class FuncPropClass { funcProp: (this: any, s: string) => string = s => s; } + const funcPropClass = new FuncPropClass();", "value": "funcPropClass.funcProp"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(4,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class MethodClass { method(this: any, s: string): string { return s; } } + const methodClass = new MethodClass();", "value": "methodClass.method"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(4,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class NoSelfAnonFuncNSMergedClass { method(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedClass { export function nsFunc(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedClass.nsFunc"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(4,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class NoSelfAnonFuncNSMergedClass { method(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedClass { export function nsFunc(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedClass.nsFunc"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(4,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class NoSelfMethodClass { + /** @noSelf */ + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(7,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class NoSelfMethodClass { + /** @noSelf */ + noSelfMethod(s: string): string { return s; } + } + const noSelfMethodClass = new NoSelfMethodClass();", "value": "noSelfMethodClass.noSelfMethod"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(7,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class StaticFuncPropClass { + static staticFuncProp: (this: any, s: string) => string = s => s; + }", "value": "StaticFuncPropClass.staticFuncProp"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(5,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class StaticMethodClass { + static staticMethod(this: any, s: string): string { return s; } + }", "value": "StaticMethodClass.staticMethod"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(5,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class StaticVoidFuncPropClass { + static staticVoidFuncProp: (this: void, s: string) => string = s => s; + }", "value": "StaticVoidFuncPropClass.staticVoidFuncProp"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(5,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class StaticVoidFuncPropClass { + static staticVoidFuncProp: (this: void, s: string) => string = s => s; + }", "value": "StaticVoidFuncPropClass.staticVoidFuncProp"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(5,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class StaticVoidMethodClass { + static staticVoidMethod(this: void, s: string): string { return s; } + }", "value": "StaticVoidMethodClass.staticVoidMethod"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(5,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class StaticVoidMethodClass { + static staticVoidMethod(this: void, s: string): string { return s; } + }", "value": "StaticVoidMethodClass.staticVoidMethod"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(5,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class VoidFuncPropClass { + voidFuncProp: (this: void, s: string) => string = s => s; + } + const voidFuncPropClass = new VoidFuncPropClass();", "value": "voidFuncPropClass.voidFuncProp"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(6,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class VoidFuncPropClass { + voidFuncProp: (this: void, s: string) => string = s => s; + } + const voidFuncPropClass = new VoidFuncPropClass();", "value": "voidFuncPropClass.voidFuncProp"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(6,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class VoidMethodClass { + voidMethod(this: void, s: string): string { return s; } + } + const voidMethodClass = new VoidMethodClass();", "value": "voidMethodClass.voidMethod"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(6,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "class VoidMethodClass { + voidMethod(this: void, s: string): string { return s; } + } + const voidMethodClass = new VoidMethodClass();", "value": "voidMethodClass.voidMethod"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(6,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "interface AnonFuncPropInterface { anonFuncProp: (s: string) => string; } + const anonFuncPropInterface: AnonFuncPropInterface = { anonFuncProp: (s: string): string => s };", "value": "anonFuncPropInterface.anonFuncProp"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(4,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "interface AnonMethodInterface { anonMethod(s: string): string; } + const anonMethodInterface: AnonMethodInterface = { + anonMethod: function(this: any, s: string): string { return s; } + };", "value": "anonMethodInterface.anonMethod"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(6,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "interface FuncPropInterface { funcProp: (this: any, s: string) => string; } + const funcPropInterface: FuncPropInterface = { funcProp: function(this: any, s: string) { return s; } };", "value": "funcPropInterface.funcProp"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(4,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "interface MethodInterface { method(this: any, s: string): string; } + const methodInterface: MethodInterface = { method: function(this: any, s: string): string { return s; } };", "value": "methodInterface.method"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(4,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ + noSelfMethod(s: string): string; + } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(9,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "interface NoSelfMethodInterface { + /** @noSelf */ + noSelfMethod(s: string): string; + } + const noSelfMethodInterface: NoSelfMethodInterface = { + noSelfMethod: function(s: string): string { return s; } + };", "value": "noSelfMethodInterface.noSelfMethod"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(9,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "interface VoidFuncPropInterface { + voidFuncProp: (this: void, s: string) => string; + } + const voidFuncPropInterface: VoidFuncPropInterface = { + voidFuncProp: function(this: void, s: string): string { return s; } + };", "value": "voidFuncPropInterface.voidFuncProp"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(8,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "interface VoidFuncPropInterface { + voidFuncProp: (this: void, s: string) => string; + } + const voidFuncPropInterface: VoidFuncPropInterface = { + voidFuncProp: function(this: void, s: string): string { return s; } + };", "value": "voidFuncPropInterface.voidFuncProp"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(8,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "interface VoidMethodInterface { + voidMethod(this: void, s: string): string; + } + const voidMethodInterface: VoidMethodInterface = { + voidMethod(this: void, s: string): string { return s; } + };", "value": "voidMethodInterface.voidMethod"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(8,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "interface VoidMethodInterface { + voidMethod(this: void, s: string): string; + } + const voidMethodInterface: VoidMethodInterface = { + voidMethod(this: void, s: string): string { return s; } + };", "value": "voidMethodInterface.voidMethod"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(8,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "let anonFunc: {(s: string): string} = function(s) { return s; };", "value": "anonFunc"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(3,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "let anonLambda: (s: string) => string = s => s;", "value": "anonLambda"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(3,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "let selfFunc: {(this: any, s: string): string} = function(s) { return s; };", "value": "selfFunc"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(3,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "let selfLambda: (this: any, s: string) => string = s => s;", "value": "selfLambda"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(3,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "let voidFunc: {(this: void, s: string): string} = function(s) { return s; };", "value": "voidFunc"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(3,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "let voidFunc: {(this: void, s: string): string} = function(s) { return s; };", "value": "voidFunc"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(3,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "let voidLambda: (this: void, s: string) => string = s => s;", "value": "voidLambda"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(3,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "let voidLambda: (this: void, s: string) => string = s => s;", "value": "voidLambda"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(3,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "namespace FuncNestedNs { + export namespace NestedNs { export function nestedNsFunc(s: string) { return s; } } + }", "value": "FuncNestedNs.NestedNs.nestedNsFunc"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(5,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "namespace FuncNs { export function nsFunc(s: string) { return s; } }", "value": "FuncNs.nsFunc"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(3,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "namespace LambdaNestedNs { + export namespace NestedNs { export let nestedNsLambda: (s: string) => string = s => s } + }", "value": "LambdaNestedNs.NestedNs.nestedNsLambda"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(5,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "namespace LambdaNs { + export let nsLambda: (s: string) => string = s => s; + }", "value": "LambdaNs.nsLambda"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(5,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"definition": "namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncSelf(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedSelfNS.nsFuncNoSelf"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(4,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncSelf(s: string): string { return s; } } + /** @noSelf */ namespace NoSelfAnonFuncNSMergedSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "NoSelfAnonFuncNSMergedSelfNS.nsFuncNoSelf"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(4,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "namespace NoSelfFuncNs { + /** @noSelf */ + export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(5,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "namespace NoSelfFuncNs { + /** @noSelf */ + export function noSelfNsFunc(s: string) { return s; } }", "value": "NoSelfFuncNs.noSelfNsFunc"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(5,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"definition": "namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncSelf(s: string): string { return s; } } + /** @noSelf */ namespace SelfAnonFuncNSMergedNoSelfNS { export function nsFuncNoSelf(s: string) { return s; } }", "value": "SelfAnonFuncNSMergedNoSelfNS.nsFuncSelf"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(4,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"value": "(function(this: any, s: string) { return s; })"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(3,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"value": "(function(this: void, s: string) { return s; })"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(3,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"value": "(function(this: void, s: string) { return s; })"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(3,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"value": "function(this: any, s: string) { return s; }"}, "(this: void, s: string) => string"): diagnostics 1`] = `"main.ts(3,68): error TSTL: Unable to convert function with a 'this' parameter to function 'fn' with no 'this'. To fix, wrap in an arrow function, or declare with 'this: void'."`; + +exports[`Invalid object with method variable declaration ({"value": "function(this: void, s: string) { return s; }"}, "(s: string) => string"): diagnostics 1`] = `"main.ts(3,56): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; + +exports[`Invalid object with method variable declaration ({"value": "function(this: void, s: string) { return s; }"}, "(this: any, s: string) => string"): diagnostics 1`] = `"main.ts(3,67): error TSTL: Unable to convert function with no 'this' parameter to function 'fn' with 'this'. To fix, wrap in an arrow function, or declare with 'this: any'."`; diff --git a/test/unit/functions/validation/functionExpressionTypeInference.spec.ts b/test/unit/functions/validation/functionExpressionTypeInference.spec.ts index abac4e483..9220902b2 100644 --- a/test/unit/functions/validation/functionExpressionTypeInference.spec.ts +++ b/test/unit/functions/validation/functionExpressionTypeInference.spec.ts @@ -79,7 +79,7 @@ test.each([ test("Function expression type inference in object literal assigned to narrower type", () => { util.testFunction` - let foo: {} = {bar: s => s}; + let foo: {} = {bar: (s: string) => s}; return (foo as {bar: (a: any) => any}).bar("foobar"); `.expectToMatchJsResult(); }); diff --git a/test/unit/functions/validation/functionPermutations.ts b/test/unit/functions/validation/functionPermutations.ts index f95af8ab3..4947bc9a4 100644 --- a/test/unit/functions/validation/functionPermutations.ts +++ b/test/unit/functions/validation/functionPermutations.ts @@ -221,9 +221,9 @@ export const noSelfTestFunctions: TestFunction[] = [ }, { value: "noSelfMethodClass.noSelfMethod", - definition: `class NoSelfMethodClass { + definition: `class NoSelfMethodClass { /** @noSelf */ - noSelfMethod(s: string): string { return s; } + noSelfMethod(s: string): string { return s; } } const noSelfMethodClass = new NoSelfMethodClass();`, }, @@ -271,8 +271,8 @@ export const noSelfTestFunctions: TestFunction[] = [ }, { value: "noSelfMethodInterface.noSelfMethod", - definition: `interface NoSelfMethodInterface { - /** @noSelf */ + definition: `interface NoSelfMethodInterface { + /** @noSelf */ noSelfMethod(s: string): string; } const noSelfMethodInterface: NoSelfMethodInterface = { @@ -352,20 +352,20 @@ const noSelfInFileTestFunctions: TestFunction[] = [ ]; export const anonTestFunctionExpressions: TestFunction[] = [ - { value: "s => s" }, - { value: "(s => s)" }, - { value: "function(s) { return s; }" }, - { value: "(function(s) { return s; })" }, + { value: "(s: string) => s" }, + { value: "((s: string) => s)" }, + { value: "function(s: string) { return s; }" }, + { value: "(function(s: string) { return s; })" }, ]; export const selfTestFunctionExpressions: TestFunction[] = [ - { value: "function(this: any, s) { return s; }" }, - { value: "(function(this: any, s) { return s; })" }, + { value: "function(this: any, s: string) { return s; }" }, + { value: "(function(this: any, s: string) { return s; })" }, ]; export const noSelfTestFunctionExpressions: TestFunction[] = [ - { value: "function(this: void, s) { return s; }" }, - { value: "(function(this: void, s) { return s; })" }, + { value: "function(this: void, s: string) { return s; }" }, + { value: "(function(this: void, s: string) { return s; })" }, ]; export const anonTestFunctionType = "(s: string) => string"; @@ -377,18 +377,21 @@ type TestFunctionCast = [ /* castedFunction: */ string, /* isSelfConversion?: */ boolean? ]; -export const validTestFunctionCasts: TestFunctionCast[] = [ +export const validTestMethodCasts: TestFunctionCast[] = [ [selfTestFunctions[0], `<${anonTestFunctionType}>(${selfTestFunctions[0].value})`], [selfTestFunctions[0], `(${selfTestFunctions[0].value}) as (${anonTestFunctionType})`], [selfTestFunctions[0], `<${selfTestFunctionType}>(${selfTestFunctions[0].value})`], [selfTestFunctions[0], `(${selfTestFunctions[0].value}) as (${selfTestFunctionType})`], [noSelfTestFunctions[0], `<${noSelfTestFunctionType}>(${noSelfTestFunctions[0].value})`], [noSelfTestFunctions[0], `(${noSelfTestFunctions[0].value}) as (${noSelfTestFunctionType})`], - [noSelfInFileTestFunctions[0], `<${anonTestFunctionType}>(${noSelfInFileTestFunctions[0].value})`], - [noSelfInFileTestFunctions[0], `(${noSelfInFileTestFunctions[0].value}) as (${anonTestFunctionType})`], [noSelfInFileTestFunctions[0], `<${noSelfTestFunctionType}>(${noSelfInFileTestFunctions[0].value})`], [noSelfInFileTestFunctions[0], `(${noSelfInFileTestFunctions[0].value}) as (${noSelfTestFunctionType})`], ]; +export const validTestFunctionCasts: TestFunctionCast[] = [ + ...validTestMethodCasts, + [noSelfInFileTestFunctions[0], `<${anonTestFunctionType}>(${noSelfInFileTestFunctions[0].value})`], + [noSelfInFileTestFunctions[0], `(${noSelfInFileTestFunctions[0].value}) as (${anonTestFunctionType})`], +]; export const invalidTestFunctionCasts: TestFunctionCast[] = [ [noSelfTestFunctions[0], `<${anonTestFunctionType}>(${noSelfTestFunctions[0].value})`, false], [noSelfTestFunctions[0], `(${noSelfTestFunctions[0].value}) as (${anonTestFunctionType})`, false], @@ -405,11 +408,10 @@ export type TestFunctionAssignment = [ /* functionType: */ string, /* isSelfConversion?: */ boolean? ]; -export const validTestFunctionAssignments: TestFunctionAssignment[] = [ +export const validTestMethodAssignments: TestFunctionAssignment[] = [ ...selfTestFunctions.map((f): TestFunctionAssignment => [f, anonTestFunctionType]), ...selfTestFunctions.map((f): TestFunctionAssignment => [f, selfTestFunctionType]), ...noSelfTestFunctions.map((f): TestFunctionAssignment => [f, noSelfTestFunctionType]), - ...noSelfInFileTestFunctions.map((f): TestFunctionAssignment => [f, anonTestFunctionType]), ...noSelfInFileTestFunctions.map((f): TestFunctionAssignment => [f, noSelfTestFunctionType]), ...anonTestFunctionExpressions.map((f): TestFunctionAssignment => [f, anonTestFunctionType]), ...anonTestFunctionExpressions.map((f): TestFunctionAssignment => [f, selfTestFunctionType]), @@ -418,6 +420,10 @@ export const validTestFunctionAssignments: TestFunctionAssignment[] = [ ...selfTestFunctionExpressions.map((f): TestFunctionAssignment => [f, selfTestFunctionType]), ...noSelfTestFunctionExpressions.map((f): TestFunctionAssignment => [f, noSelfTestFunctionType]), ]; +export const validTestFunctionAssignments: TestFunctionAssignment[] = [ + ...validTestMethodAssignments, + ...noSelfInFileTestFunctions.map((f): TestFunctionAssignment => [f, anonTestFunctionType]), +]; export const invalidTestFunctionAssignments: TestFunctionAssignment[] = [ ...selfTestFunctions.map((f): TestFunctionAssignment => [f, noSelfTestFunctionType, false]), ...noSelfTestFunctions.map((f): TestFunctionAssignment => [f, anonTestFunctionType, true]), @@ -427,3 +433,7 @@ export const invalidTestFunctionAssignments: TestFunctionAssignment[] = [ ...noSelfTestFunctionExpressions.map((f): TestFunctionAssignment => [f, anonTestFunctionType, true]), ...noSelfTestFunctionExpressions.map((f): TestFunctionAssignment => [f, selfTestFunctionType, true]), ]; +export const invalidTestMethodAssignments: TestFunctionAssignment[] = [ + ...invalidTestFunctionAssignments, + ...noSelfInFileTestFunctions.map((f): TestFunctionAssignment => [f, anonTestFunctionType, true]), +]; diff --git a/test/unit/functions/validation/invalidFunctionAssignments.spec.ts b/test/unit/functions/validation/invalidFunctionAssignments.spec.ts index cd12b4ba8..2b9fd61db 100644 --- a/test/unit/functions/validation/invalidFunctionAssignments.spec.ts +++ b/test/unit/functions/validation/invalidFunctionAssignments.spec.ts @@ -4,7 +4,11 @@ import { unsupportedSelfFunctionConversion, } from "../../../../src/transformation/utils/diagnostics"; import * as util from "../../../util"; -import { invalidTestFunctionAssignments, invalidTestFunctionCasts } from "./functionPermutations"; +import { + invalidTestFunctionAssignments, + invalidTestFunctionCasts, + invalidTestMethodAssignments, +} from "./functionPermutations"; test.each(invalidTestFunctionAssignments)( "Invalid function variable declaration (%p)", @@ -19,6 +23,19 @@ test.each(invalidTestFunctionAssignments)( } ); +test.each(invalidTestMethodAssignments)( + "Invalid object with method variable declaration (%p, %p)", + (testFunction, functionType, isSelfConversion) => { + util.testModule` + ${testFunction.definition ?? ""} + const obj: { fn: ${functionType} } = {fn: ${testFunction.value}}; + `.expectDiagnosticsToMatchSnapshot( + [isSelfConversion ? unsupportedSelfFunctionConversion.code : unsupportedNoSelfFunctionConversion.code], + true + ); + } +); + test.each(invalidTestFunctionAssignments)( "Invalid function assignment (%p)", (testFunction, functionType, isSelfConversion) => { @@ -33,6 +50,20 @@ test.each(invalidTestFunctionAssignments)( } ); +test.each(invalidTestMethodAssignments)( + "Invalid object with method assignment (%p, %p, %p)", + (testFunction, functionType, isSelfConversion) => { + util.testModule` + ${testFunction.definition ?? ""} + let obj: { fn: ${functionType} }; + obj = {fn: ${testFunction.value}}; + `.expectDiagnosticsToMatchSnapshot( + [isSelfConversion ? unsupportedSelfFunctionConversion.code : unsupportedNoSelfFunctionConversion.code], + true + ); + } +); + test.each(invalidTestFunctionCasts)("Invalid function assignment with cast (%p)", (testFunction, castedFunction) => { util.testModule` ${testFunction.definition ?? ""} @@ -44,12 +75,28 @@ test.each(invalidTestFunctionCasts)("Invalid function assignment with cast (%p)" ); }); +test.each(invalidTestFunctionCasts)( + "Invalid object with method assignment with cast (%p, %p, %p)", + (testFunction, castedFunction, isSelfConversion) => { + util.testModule` + ${testFunction.definition ?? ""} + let obj: { fn: typeof ${testFunction.value} }; + obj = {fn: ${castedFunction}}; + `.expectDiagnosticsToMatchSnapshot( + isSelfConversion + ? [unsupportedSelfFunctionConversion.code, unsupportedNoSelfFunctionConversion.code] + : [unsupportedNoSelfFunctionConversion.code, unsupportedSelfFunctionConversion.code], + true + ); + } +); + test.each(invalidTestFunctionAssignments)( "Invalid function argument (%p)", (testFunction, functionType, isSelfConversion) => { util.testModule` ${testFunction.definition ?? ""} - declare function takesFunction(fn: ${functionType}); + declare function takesFunction(fn: ${functionType}): void; takesFunction(${testFunction.value}); `.expectDiagnosticsToMatchSnapshot( [isSelfConversion ? unsupportedSelfFunctionConversion.code : unsupportedNoSelfFunctionConversion.code], @@ -58,6 +105,20 @@ test.each(invalidTestFunctionAssignments)( } ); +test.each(invalidTestMethodAssignments)( + "Invalid object with method argument (%p, %p, %p)", + (testFunction, functionType, isSelfConversion) => { + util.testModule` + ${testFunction.definition ?? ""} + declare function takesObjectWithMethod(obj: { fn: ${functionType} }): void; + takesObjectWithMethod({fn: ${testFunction.value}}); + `.expectDiagnosticsToMatchSnapshot( + [isSelfConversion ? unsupportedSelfFunctionConversion.code : unsupportedNoSelfFunctionConversion.code], + true + ); + } +); + test("Invalid lua lib function argument", () => { util.testModule` declare function foo(this: void, value: string): void; @@ -69,7 +130,7 @@ test("Invalid lua lib function argument", () => { test.each(invalidTestFunctionCasts)("Invalid function argument with cast (%p)", (testFunction, castedFunction) => { util.testModule` ${testFunction.definition ?? ""} - declare function takesFunction(fn: typeof ${testFunction.value}); + declare function takesFunction(fn: typeof ${testFunction.value}): void; takesFunction(${castedFunction}); `.expectDiagnosticsToMatchSnapshot( [unsupportedNoSelfFunctionConversion.code, unsupportedSelfFunctionConversion.code], @@ -82,7 +143,7 @@ test.each(invalidTestFunctionAssignments)( (testFunction, functionType, isSelfConversion) => { util.testModule` ${testFunction.definition ?? ""} - declare function takesFunction(fn: T); + declare function takesFunction(fn: T): void; takesFunction(${testFunction.value}); `.expectDiagnosticsToMatchSnapshot( [isSelfConversion ? unsupportedSelfFunctionConversion.code : unsupportedNoSelfFunctionConversion.code], @@ -165,3 +226,18 @@ test.each([ let f: ${assignType} = o; `.expectDiagnosticsToMatchSnapshot([unsupportedOverloadAssignment.code], true); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/896 +test("Does not fail on union type signatures (#896)", () => { + util.testExpression`foo<'a'>(() => {});` + .setTsHeader( + ` + declare interface Events { + a(): void; + [key: string]: (this: void) => void; + } + declare function foo(callback: Events[T]): void; + ` + ) + .expectToHaveDiagnostics([unsupportedOverloadAssignment.code]); +}); diff --git a/test/unit/functions/validation/validFunctionAssignments.spec.ts b/test/unit/functions/validation/validFunctionAssignments.spec.ts index d9613692d..525b95929 100644 --- a/test/unit/functions/validation/validFunctionAssignments.spec.ts +++ b/test/unit/functions/validation/validFunctionAssignments.spec.ts @@ -12,6 +12,8 @@ import { TestFunctionAssignment, validTestFunctionAssignments, validTestFunctionCasts, + validTestMethodAssignments, + validTestMethodCasts, } from "./functionPermutations"; test.each(validTestFunctionAssignments)("Valid function variable declaration (%p)", (testFunction, functionType) => { @@ -23,6 +25,15 @@ test.each(validTestFunctionAssignments)("Valid function variable declaration (%p .expectToMatchJsResult(); }); +test.each(validTestMethodAssignments)("Valid object wit method declaration (%p, %p)", (testFunction, functionType) => { + util.testFunction` + const obj: { fn: ${functionType} } = {fn: ${testFunction.value}}; + return obj.fn("foobar"); + ` + .setTsHeader(testFunction.definition ?? "") + .expectToMatchJsResult(); +}); + test.each(validTestFunctionAssignments)("Valid function assignment (%p)", (testFunction, functionType) => { util.testFunction` let fn: ${functionType}; @@ -43,6 +54,19 @@ test.each(validTestFunctionCasts)("Valid function assignment with cast (%p)", (t .expectToMatchJsResult(); }); +test.each(validTestMethodCasts)( + "Valid object with method assignment with cast (%p, %p)", + (testFunction, castedFunction) => { + util.testFunction` + let obj: { fn: typeof ${testFunction.value} }; + obj = {fn: ${castedFunction}}; + return obj.fn("foobar"); + ` + .setTsHeader(testFunction.definition ?? "") + .expectToMatchJsResult(); + } +); + test.each(validTestFunctionAssignments)("Valid function argument (%p)", (testFunction, functionType) => { util.testFunction` function takesFunction(fn: ${functionType}) { @@ -54,6 +78,17 @@ test.each(validTestFunctionAssignments)("Valid function argument (%p)", (testFun .expectToMatchJsResult(); }); +test.each(validTestMethodAssignments)("Valid object with method argument (%p, %p)", (testFunction, functionType) => { + util.testFunction` + function takesObjectWithMethod(obj: { fn: ${functionType} }) { + return obj.fn("foobar"); + } + return takesObjectWithMethod({fn: ${testFunction.value}}); + ` + .setTsHeader(testFunction.definition ?? "") + .expectToMatchJsResult(); +}); + test("Valid lua lib function argument", () => { util.testFunction` let result = ""; @@ -198,3 +233,35 @@ test.each([ return f(${args.map(a => '"' + a + '"').join(", ")}); `.expectToMatchJsResult(); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/896 +test("Does not fail on union type signatures (#896)", () => { + util.testExpression`foo<'a'>(() => {});` + .setTsHeader( + ` + declare interface Events { + a(): void; + [key: string]: Function; + } + declare function foo(callback: Events[T]): void; + ` + ) + .expectToHaveNoDiagnostics(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1568 +test("No false positives when using generic functions (#1568)", () => { + util.testModule` + /** @noSelf */ + declare namespace Test { + export function testCallback void>( + callback: T, + ): void; + } + + Test.testCallback(() => {}); + + const f = () => {}; + Test.testCallback(f); + `.expectToHaveNoDiagnostics(); +}); diff --git a/test/unit/hoisting.spec.ts b/test/unit/hoisting.spec.ts index 144460067..e08a7c083 100644 --- a/test/unit/hoisting.spec.ts +++ b/test/unit/hoisting.spec.ts @@ -254,7 +254,7 @@ test("Hoisting variable without initializer", () => { function foo() { return x; } - let x: number; + let x: number | undefined; return foo(); `.expectToMatchJsResult(); }); diff --git a/test/unit/identifiers.spec.ts b/test/unit/identifiers.spec.ts index 9f72ad671..ddac778fc 100644 --- a/test/unit/identifiers.spec.ts +++ b/test/unit/identifiers.spec.ts @@ -1,3 +1,4 @@ +import { LuaTarget } from "../../src"; import { invalidAmbientIdentifierName } from "../../src/transformation/utils/diagnostics"; import { luaKeywords } from "../../src/transformation/utils/safe-names"; import * as util from "../util"; @@ -92,9 +93,8 @@ test.each([ "const foo: any, bar: any, $$$: any;", "class $$$ {}", "namespace $$$ { export const bar: any; }", - "module $$$ { export const bar: any; }", "enum $$$ {}", - "function $$$();", + "function $$$(): void;", ])("ambient identifier must be a valid lua identifier (%p)", statement => { util.testModule` declare ${statement} @@ -195,7 +195,7 @@ test.each(validTsInvalidLuaNames)("class with invalid lua name has correct name test.each(validTsInvalidLuaNames)("decorated class with invalid lua name", name => { util.testFunction` - function decorator any>(Class: T): T { + function decorator any>(Class: T, context: ClassDecoratorContext): T { return class extends Class { public bar = "foobar"; }; @@ -209,7 +209,7 @@ test.each(validTsInvalidLuaNames)("decorated class with invalid lua name", name test.each(validTsInvalidLuaNames)("exported decorated class with invalid lua name", name => { util.testModule` - function decorator any>(Class: T): T { + function decorator any>(Class: T, context: ClassDecoratorContext): T { return class extends Class { public bar = "foobar"; }; @@ -222,6 +222,111 @@ test.each(validTsInvalidLuaNames)("exported decorated class with invalid lua nam .expectToMatchJsResult(); }); +describe("unicode identifiers in supporting environments (luajit)", () => { + const unicodeNames = ["𝛼𝛽𝚫", "ɥɣɎɌͼƛಠ", "_̀ः٠‿"]; + + test.each(unicodeNames)("identifier name (%p)", name => { + util.testFunction` + const ${name} = "foobar"; + return ${name}; + ` + .setOptions({ luaTarget: LuaTarget.LuaJIT }) + .expectLuaToMatchSnapshot(); + }); + + test.each(unicodeNames)("property name (%p)", name => { + util.testFunction` + const x = { ${name}: "foobar" }; + return x.${name}; + ` + .setOptions({ luaTarget: LuaTarget.LuaJIT }) + .expectLuaToMatchSnapshot(); + }); + + test.each(unicodeNames)("destructuring property name (%p)", name => { + util.testFunction` + const { foo: ${name} } = { foo: "foobar" }; + return ${name}; + ` + .setOptions({ luaTarget: LuaTarget.LuaJIT }) + .expectLuaToMatchSnapshot(); + }); + + test.each(unicodeNames)("destructuring shorthand (%p)", name => { + util.testFunction` + const { ${name} } = { ${name}: "foobar" }; + return ${name}; + ` + .setOptions({ luaTarget: LuaTarget.LuaJIT }) + .expectLuaToMatchSnapshot(); + }); + + test.each(unicodeNames)("function (%p)", name => { + util.testFunction` + function ${name}(arg: string) { + return "foo" + arg; + } + return ${name}("bar"); + ` + .setOptions({ luaTarget: LuaTarget.LuaJIT }) + .expectLuaToMatchSnapshot(); + }); + + test.each(unicodeNames)("method (%p)", name => { + util.testFunction` + const foo = { + ${name}(arg: string) { return "foo" + arg; } + }; + return foo.${name}("bar"); + ` + .setOptions({ luaTarget: LuaTarget.LuaJIT }) + .expectLuaToMatchSnapshot(); + }); +}); + +test("unicode export class", () => { + util.testModule` + import { 你好 } from "./utfclass"; + export const result = new 你好().hello(); + ` + .addExtraFile( + "utfclass.ts", + `export class 你好 { + hello() { + return "你好"; + } + }` + ) + .expectToEqual({ result: "你好" }); +}); + +test("unicode export default class", () => { + util.testModule` + import c from "./utfclass"; + export const result = new c().hello(); + ` + .addExtraFile( + "utfclass.ts", + `export default class 你好 { + hello() { + return "你好"; + } + }` + ) + .expectToEqual({ result: "你好" }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1645 +test("unicode static initialization block (#1645)", () => { + util.testModule` + export default class 自定义异能 { + static { + let a = 1; + } + } + `.expectLuaToMatchSnapshot(); +}); + describe("lua keyword as identifier doesn't interfere with lua's value", () => { test("variable (nil)", () => { util.testFunction` @@ -239,7 +344,7 @@ describe("lua keyword as identifier doesn't interfere with lua's value", () => { test("variable (elseif)", () => { util.testFunction` - const elseif = "foobar"; + const elseif: string | undefined = "foobar"; if (false) { } else if (elseif) { return elseif; @@ -287,7 +392,7 @@ describe("lua keyword as identifier doesn't interfere with lua's value", () => { test("variable (then)", () => { util.testFunction` - const then = "foobar"; + const then: string | undefined = "foobar"; if (then) { return then; } @@ -482,6 +587,15 @@ describe("lua keyword as identifier doesn't interfere with lua's value", () => { expect(luaResult).toBe("foobar"); }); + test("variable (bit32)", () => { + util.testFunction` + const bit32 = 1; + return bit32 << 1; + ` + .setOptions({ luaTarget: LuaTarget.Lua52 }) + .expectToMatchJsResult(); + }); + test("variable (_G)", () => { util.testFunction` const _G = "bar"; @@ -750,3 +864,193 @@ test("lua built-in as in constructor assignment", () => { export const result = new A("42").error; `.expectToMatchJsResult(); }); + +test("customName rename function", () => { + const result = util.testModule` + /** @customName test2 **/ + function test(this: void): number { return 3; } + export const result: number = test(); + ` + .expectToEqual({ result: 3 }) + .getLuaResult(); + + expect(result.transpiledFiles).not.toHaveLength(0); + + const mainFile = result.transpiledFiles.find(f => f.outPath === "main.lua"); + expect(mainFile).toBeDefined(); + + // avoid ts error "not defined", even though toBeDefined is being checked above + if (!mainFile) return; + + expect(mainFile.lua).toBeDefined(); + expect(mainFile.lua).toContain("function test2()"); + expect(mainFile.lua).not.toContain("test()"); +}); + +test("customName rename variable", () => { + const result = util.testModule` + /** @customName result2 **/ + export const result: number = 3; + ` + .expectToEqual({ result2: 3 }) + .getLuaResult(); + + const mainFile = result.transpiledFiles.find(f => f.outPath === "main.lua"); + expect(mainFile).toBeDefined(); + + // avoid ts error "not defined", even though toBeDefined is being checked above + if (!mainFile) return; + + expect(mainFile.lua).toBeDefined(); + expect(mainFile.lua).toContain("result2 ="); + expect(mainFile.lua).not.toContain("result ="); +}); + +test("customName rename classes", () => { + const testModule = util.testModule` + /** @customName Class2 **/ + class Class { + test: string; + + constructor(test: string) { + this.test = test; + } + } + + export const result = new Class("hello world"); + `; + + const executionResult = testModule.getLuaExecutionResult(); + expect(executionResult.result).toBeDefined(); + expect(executionResult.result).toMatchObject({ test: "hello world" }); + + const result = testModule.getLuaResult(); + expect(result.transpiledFiles).not.toHaveLength(0); + + const mainFile = result.transpiledFiles.find(f => f.outPath === "main.lua"); + expect(mainFile).toBeDefined(); + + // avoid ts error "not defined", even though toBeDefined is being checked above + if (!mainFile) return; + + expect(mainFile.lua).toBeDefined(); + expect(mainFile.lua).toContain("local Class2 ="); + expect(mainFile.lua).not.toContain("local Class ="); +}); + +test("customName rename namespace", () => { + const testModule = util.testModule` + /** @customName Test2 **/ + namespace Test { + /** @customName Func2 **/ + export function Func(): string { + return "hi"; + } + } + + export const result = Test.Func(); + `; + + testModule.expectToEqual({ result: "hi" }); + + const result = testModule.getLuaResult(); + expect(result.transpiledFiles).not.toHaveLength(0); + + const mainFile = result.transpiledFiles.find(f => f.outPath === "main.lua"); + expect(mainFile).toBeDefined(); + + // avoid ts error "not defined", even though toBeDefined is being checked above + if (!mainFile) return; + + expect(mainFile.lua).toBeDefined(); + expect(mainFile.lua).toContain("Test2 ="); + expect(mainFile.lua).toContain("Test2.Func2"); + expect(mainFile.lua).not.toContain("Test ="); + expect(mainFile.lua).not.toContain("Func("); +}); + +test("customName rename declared function", () => { + const testModule = util.testModule` + /** @customName Test2 **/ + declare function Test(this: void): void; + + Test(); + `; + + const result = testModule.getLuaResult(); + expect(result.transpiledFiles).not.toHaveLength(0); + + const mainFile = result.transpiledFiles.find(f => f.outPath === "main.lua"); + expect(mainFile).toBeDefined(); + + // avoid ts error "not defined", even though toBeDefined is being checked above + if (!mainFile) return; + + expect(mainFile.lua).toBeDefined(); + expect(mainFile.lua).toContain("Test2("); + expect(mainFile.lua).not.toContain("Test("); +}); + +test("customName rename import specifier", () => { + const testModule = util.testModule` + import { Test } from "./myimport"; + import { Test as Aliased } from "./myimport"; + Test(); + Aliased(); + `.addExtraFile( + "myimport.ts", + ` + /** @customName Test2 **/ + export function Test(this: void): void {} + ` + ); + + testModule.expectToHaveNoDiagnostics(); + const result = testModule.getLuaResult(); + expect(result.transpiledFiles).not.toHaveLength(0); + + const mainFile = result.transpiledFiles.find(f => f.outPath === "main.lua"); + expect(mainFile).toBeDefined(); + + // avoid ts error "not defined", even though toBeDefined is being checked above + if (!mainFile) return; + + expect(mainFile.lua).toBeDefined(); + expect(mainFile.lua).toContain("Test2("); + expect(mainFile.lua).toContain("myimport.Test2"); + expect(mainFile.lua).not.toContain("Test("); + + testModule.expectNoExecutionError(); +}); + +test("customName import specifier from declarations", () => { + const testModule = util.testModule` + import { Test } from "./myimport"; + import { Test as Aliased } from "./myimport"; + Test(); + Aliased(); + ` + .addExtraFile( + "myimport.d.ts", + ` + /** @customName Test2 **/ + export declare function Test(this: void): void; + ` + ) + .setOptions({ noResolvePaths: ["./myimport"] }); + + testModule.expectToHaveNoDiagnostics(); + const result = testModule.getLuaResult(); + expect(result.transpiledFiles).not.toHaveLength(0); + + const mainFile = result.transpiledFiles.find(f => f.outPath === "main.lua"); + expect(mainFile).toBeDefined(); + + // avoid ts error "not defined", even though toBeDefined is being checked above + if (!mainFile) return; + + expect(mainFile.lua).toBeDefined(); + expect(mainFile.lua).toContain("Test2("); + expect(mainFile.lua).toContain("myimport.Test2"); + expect(mainFile.lua).not.toContain("Test("); +}); diff --git a/test/unit/jsx.spec.ts b/test/unit/jsx.spec.ts new file mode 100644 index 000000000..a72d0d7f9 --- /dev/null +++ b/test/unit/jsx.spec.ts @@ -0,0 +1,375 @@ +import * as util from "../util"; +import { TestBuilder } from "../util"; +import { JsxEmit } from "typescript"; +import { unsupportedJsxEmit } from "../../src/transpilation/diagnostics"; +import { unsupportedNodeKind } from "../../src/transformation/utils/diagnostics"; + +// language=TypeScript +const reactLib = ` + export class Component { + static isClass = true + } + + namespace React { + const isLua = typeof Component === "object" + + export function createElement( + type: any, + props?: any, + ...children: any[] + ) { + let typeStr: string + if (isLua) { + typeStr = + typeof type === "function" ? "<< function >>" : + typeof type === "object" ? \`<< class \${type.name} >>\` : + type + } else { + typeStr = typeof type === "function" ? (type.isClass + ? \`<< class \${type.name} >>\` + : "<< function >>") + : type + } + + return { + type: typeStr, + props, + children + }; + } + + export class Fragment extends Component { + } + } + export default React; +`; +// language=TypeScript +const jsxTypings = `declare namespace JSX { + interface IntrinsicElements { + a: any + b: any + foo: any + bar: any + div: any + "with-dash": any + } +} +`; + +function testJsx(...args: [string] | [TemplateStringsArray, ...any[]]): TestBuilder { + return util + .testFunction(...args) + .setOptions({ + jsx: JsxEmit.React, + }) + .setMainFileName("main.tsx") + .addExtraFile("react.ts", reactLib) + .addExtraFile("jsx.d.ts", jsxTypings) + .setTsHeader('import React, { Component } from "./react";'); +} + +describe("jsx", () => { + test("element", () => { + testJsx` + return + `.expectToMatchJsResult(); + }); + test("self closing element", () => { + testJsx` + return + `.expectToMatchJsResult(); + }); + test("element with dash name", () => { + testJsx` + return + `.expectToMatchJsResult(); + }); + test("custom element", () => { + testJsx` + function Foo() {} + return + `.expectToMatchJsResult(); + }); + test("fragment", () => { + testJsx` + return <> + `.expectToMatchJsResult(); + }); + test("fragment with children", () => { + testJsx` + return <> + `.expectToMatchJsResult(); + }); + test("esoteric component names", () => { + testJsx` + class _Foo extends Component {} + return <_Foo /> + `.expectToMatchJsResult(); + + testJsx` + class $ extends Component {} + return <$ /> + `.expectToMatchJsResult(); + + testJsx` + class é extends Component {} + return <é /> + `.expectToMatchJsResult(); + }); + test("nested elements", () => { + testJsx` + return + `.expectToMatchJsResult(); + }); + test("many nested elements", () => { + testJsx` + return + `.expectToMatchJsResult(); + }); + test("interpolated children", () => { + testJsx` + const x = 3 + return {x} + `.expectToMatchJsResult(); + }); + test("string prop", () => { + testJsx` + return + `.expectToMatchJsResult(); + }); + test("value prop", () => { + testJsx` + const x = 5 + return + `.expectToMatchJsResult(); + }); + test("quoted prop", () => { + testJsx` + return + `.expectToMatchJsResult(); + }); + test("shorthand prop", () => { + testJsx` + return + `.expectToMatchJsResult(); + }); + test("spaces in jsxText", () => { + testJsx` + return this + is somemultiline + text thing. + + + `.expectToMatchJsResult(); + }); + test("multiline string jsxText", () => { + testJsx` + return + foo bar + baz + + `.expectToMatchJsResult(); + }); + + test("access tag value", () => { + testJsx` + const a = { b(){} }; + return + `.expectToMatchJsResult(); + testJsx` + const a = { b: { c: { d(){} } } }; + return + `.expectToMatchJsResult(); + }); + + test("spread props", () => { + testJsx` + const x = {c: "d", e: "f"} + return + `.expectToMatchJsResult(); + testJsx` + const x = {c: "d", e: "no"} + return + `.expectToMatchJsResult(); + }); + + test("comment children", () => { + testJsx` + return + {/* comment */} + {/* another comment */} + + `.expectToMatchJsResult(); + testJsx` + return + + {/* comment */} + + + `.expectToMatchJsResult(); + }); + + test("multiline string prop value", () => { + testJsx` + return
+ `.expectToMatchJsResult(); + testJsx` + return
+ `.expectToMatchJsResult(); + }); + + test("prop strings with entities", () => { + testJsx` + return
+ `.expectToMatchJsResult(); + }); + test("jsxText with entities", () => { + testJsx` + return 9+10<21 + `.expectToMatchJsResult(); + }); + + test("Spread children", () => { + // doesn't actually "spread" (typescript's current behavior) + testJsx` + const children = [, ] + return {...children} + `.expectToMatchJsResult(); + }); + + test("complex", () => { + testJsx` + const x = 3 + const props = {one: "two", three: 4} + return + `.expectToMatchJsResult(); + }); + + // language=TypeScript + const customJsxLib = `export namespace MyLib { + export function myCreate( + type: any, + props: any, + ...children: any[] + ) { + return { type: typeof type, props, children, myThing: true }; + } + + export function MyFragment() { + } + } + `; + + test("custom JSX factory", () => { + testJsx` + return c + ` + .setTsHeader('import { MyLib } from "./myJsx";') + .setOptions({ jsxFactory: "MyLib.myCreate" }) + .addExtraFile("myJsx.ts", customJsxLib) + .expectToMatchJsResult(); + testJsx` + return c + ` + .setTsHeader('import { MyLib } from "./myJsx";const myCreate2 = MyLib.myCreate;') + .setOptions({ jsxFactory: "myCreate2" }) + .addExtraFile("myJsx.ts", customJsxLib) + .expectToMatchJsResult(); + }); + test("custom JSX factory with noImplicitSelf", () => { + testJsx` + return c + ` + .setTsHeader( + `function createElement(tag: string | Function, props: { [key: string]: string | boolean }, ...children: any[]) { + return { tag, children }; + }` + ) + .setOptions({ jsxFactory: "createElement", noImplicitSelf: true }) + .expectToMatchJsResult(); + }); + test("custom fragment factory", () => { + testJsx` + return <>c + ` + .setTsHeader('import { MyLib } from "./myJsx";') + .setOptions({ jsxFactory: "MyLib.myCreate", jsxFragmentFactory: "MyLib.MyFragment" }) + .addExtraFile("myJsx.ts", customJsxLib) + .expectToMatchJsResult(); + testJsx` + return <>c + ` + .setTsHeader('import { MyLib } from "./myJsx";function MyFragment2(){};') + .setOptions({ jsxFactory: "MyLib.myCreate", jsxFragmentFactory: "MyFragment2" }) + .addExtraFile("myJsx.ts", customJsxLib) + .expectToMatchJsResult(); + }); + test("custom JSX pragma", () => { + testJsx` + return c + ` + .setTsHeader('/** @jsx MyLib.myCreate */\nimport { MyLib } from "./myJsx";') + .addExtraFile("myJsx.ts", customJsxLib) + .expectToMatchJsResult(); + testJsx` + return c + ` + .setTsHeader('/** @jsx myCreate2 */import { MyLib } from "./myJsx";const myCreate2 = MyLib.myCreate;') + .addExtraFile("myJsx.ts", customJsxLib) + .expectToMatchJsResult(); + }); + test("custom fragment pragma", () => { + testJsx` + return <>c + ` + .setTsHeader( + '/** @jsx MyLib.myCreate */\n/** @jsxFrag MyLib.MyFragment */\nimport { MyLib } from "./myJsx";' + ) + .addExtraFile("myJsx.ts", customJsxLib) + .expectToMatchJsResult(); + testJsx` + return <>c + ` + .setTsHeader( + "/** @jsx MyLib.myCreate */\n/** @jsxFrag MyFragment2 */\n" + + 'import { MyLib } from "./myJsx";function MyFragment2(){};' + ) + .setOptions({ jsxFactory: "MyLib.myCreate", jsxFragmentFactory: "MyFragment" }) + .addExtraFile("myJsx.ts", customJsxLib) + .expectToMatchJsResult(); + }); + + test("forward declare components", () => { + testJsx` + const foo = + + function Foo(){} + + return foo + `.expectToMatchJsResult(); + }); + + test("invalid jsx config", () => { + testJsx(` + return + `) + .setOptions({ + jsx: JsxEmit.Preserve, + }) + .expectToHaveDiagnostics([unsupportedJsxEmit.code, unsupportedNodeKind.code]); + }); +}); diff --git a/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap b/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap index 1f76110a4..e3c25d948 100644 --- a/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap +++ b/test/unit/language-extensions/__snapshots__/iterable.spec.ts.snap @@ -4,14 +4,12 @@ exports[`LuaIterable with LuaMultiReturn value type invalid LuaIterable {}): code 1`] = ` "local function ____(____, ____bindingPattern0) if ____bindingPattern0 == nil then - ____bindingPattern0 = { - ____(_G, 1) - } + ____bindingPattern0 = {____(_G, 1)} end local a = ____bindingPattern0[1] end" @@ -52,7 +48,7 @@ end" exports[`invalid $multi call (([a] = $multi(1)) => {}): diagnostics 1`] = `"main.ts(2,16): error TSTL: The $multi function must be called in a return statement."`; -exports[`invalid $multi call (({ $multi });): code 1`] = `"local ____ = nil"`; +exports[`invalid $multi call (({ $multi });): code 1`] = `"local ____ = {["$multi"] = ____}"`; exports[`invalid $multi call (({ $multi });): diagnostics 1`] = `"main.ts(2,12): error TSTL: The $multi function must be called in a return statement."`; @@ -65,27 +61,17 @@ end" exports[`invalid $multi call (const [a = 0] = $multi()): diagnostics 1`] = `"main.ts(2,25): error TSTL: The $multi function must be called in a return statement."`; -exports[`invalid $multi call (const {} = $multi();): code 1`] = ` -"local ____ = { - { - ____(_G) - } -}" -`; +exports[`invalid $multi call (const {} = $multi();): code 1`] = `"local ____temp_0 = {{____(_G)}}"`; exports[`invalid $multi call (const {} = $multi();): diagnostics 1`] = `"main.ts(2,20): error TSTL: The $multi function must be called in a return statement."`; -exports[`invalid $multi call (const a = $multi();): code 1`] = ` -"a = { - ____(_G) -}" -`; +exports[`invalid $multi call (const a = $multi();): code 1`] = `"a = {____(_G)}"`; exports[`invalid $multi call (const a = $multi();): diagnostics 1`] = `"main.ts(2,19): error TSTL: The $multi function must be called in a return statement."`; exports[`invalid $multi implicit cast: code 1`] = ` "function badMulti(self) - return \\"foo\\", 42 + return "foo", 42 end" `; @@ -174,35 +160,34 @@ return ____exports" exports[`invalid direct $multi function use (const ar = [1]; const [a] = $multi(...ar)): diagnostics 1`] = `"main.ts(7,37): error TSTL: The $multi function must be called in a return statement."`; -exports[`invalid direct $multi function use (let a; [a] = $multi()): code 1`] = ` +exports[`invalid direct $multi function use (let a: any; for ([a] = $multi(1, 2); false; 1) {}): code 1`] = ` "local ____exports = {} local function multi(self, ...) return ... end local a -local ____ = { - ____(nil) -} -a = ____[1] -____exports.a = a +do + local ____temp_0 = {____(nil, 1, 2)} + a = ____temp_0[1] + ____exports.a = a + while false do + local ____ = 1 + end +end ____exports.a = a return ____exports" `; -exports[`invalid direct $multi function use (let a; [a] = $multi()): diagnostics 1`] = `"main.ts(7,22): error TSTL: The $multi function must be called in a return statement."`; +exports[`invalid direct $multi function use (let a: any; for ([a] = $multi(1, 2); false; 1) {}): diagnostics 1`] = `"main.ts(7,32): error TSTL: The $multi function must be called in a return statement."`; -exports[`invalid direct $multi function use (let a; for ([a] = $multi(1, 2); false; 1) {}): code 1`] = ` +exports[`invalid direct $multi function use (let a: any; for (const [a] = $multi(1, 2); false; 1) {}): code 1`] = ` "local ____exports = {} local function multi(self, ...) return ... end local a do - local ____ = { - ____(nil, 1, 2) - } - a = ____[1] - ____exports.a = a + local a = ____(nil, 1, 2) while false do local ____ = 1 end @@ -211,45 +196,38 @@ ____exports.a = a return ____exports" `; -exports[`invalid direct $multi function use (let a; for ([a] = $multi(1, 2); false; 1) {}): diagnostics 1`] = `"main.ts(7,27): error TSTL: The $multi function must be called in a return statement."`; +exports[`invalid direct $multi function use (let a: any; for (const [a] = $multi(1, 2); false; 1) {}): diagnostics 1`] = `"main.ts(7,38): error TSTL: The $multi function must be called in a return statement."`; -exports[`invalid direct $multi function use (let a; for (const [a] = $multi(1, 2); false; 1) {}): code 1`] = ` +exports[`invalid direct $multi function use (let a: any; if ([a] = $multi(1)) { ++a; }): code 1`] = ` "local ____exports = {} local function multi(self, ...) return ... end local a -do - local a = ____(nil, 1, 2) - while false do - local ____ = 1 - end +local ____temp_0 = {____(nil, 1)} +a = ____temp_0[1] +____exports.a = a +if ____temp_0 then + a = a + 1 + ____exports.a = a end ____exports.a = a return ____exports" `; -exports[`invalid direct $multi function use (let a; for (const [a] = $multi(1, 2); false; 1) {}): diagnostics 1`] = `"main.ts(7,33): error TSTL: The $multi function must be called in a return statement."`; +exports[`invalid direct $multi function use (let a: any; if ([a] = $multi(1)) { ++a; }): diagnostics 1`] = `"main.ts(7,31): error TSTL: The $multi function must be called in a return statement."`; -exports[`invalid direct $multi function use (let a; if ([a] = $multi(1)) { ++a; }): code 1`] = ` +exports[`invalid direct $multi function use (let a; [a] = $multi()): code 1`] = ` "local ____exports = {} local function multi(self, ...) return ... end local a -if (function() - local ____ = { - ____(nil, 1) - } - a = ____[1] - ____exports.a = a - return ____ -end)() then - a = a + 1 - ____exports.a = a -end +local ____temp_0 = {____(nil)} +a = ____temp_0[1] +____exports.a = a ____exports.a = a return ____exports" `; -exports[`invalid direct $multi function use (let a; if ([a] = $multi(1)) { ++a; }): diagnostics 1`] = `"main.ts(7,26): error TSTL: The $multi function must be called in a return statement."`; +exports[`invalid direct $multi function use (let a; [a] = $multi()): diagnostics 1`] = `"main.ts(7,22): error TSTL: The $multi function must be called in a return statement."`; diff --git a/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap b/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap index cfeef91c8..a4554a8a1 100644 --- a/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap +++ b/test/unit/language-extensions/__snapshots__/operators.spec.ts.snap @@ -1,24 +1,60 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`does not crash on invalid operator use global function: code 1`] = `""`; + +exports[`does not crash on invalid operator use global function: diagnostics 1`] = `"main.ts(3,13): error TS2554: Expected 2 arguments, but got 1."`; + +exports[`does not crash on invalid operator use method: code 1`] = `"left = {}"`; + +exports[`does not crash on invalid operator use method: diagnostics 1`] = `"main.ts(5,18): error TS2554: Expected 1 arguments, but got 0."`; + +exports[`does not crash on invalid operator use unary operator: code 1`] = `"op(_G)"`; + +exports[`does not crash on invalid operator use unary operator: diagnostics 1`] = `"main.ts(2,31): error TS2304: Cannot find name 'LuaUnaryMinus'."`; + exports[`operator mapping - invalid use (const foo = (op as any)(1, 2);): code 1`] = `"foo = op(_G, 1, 2)"`; -exports[`operator mapping - invalid use (const foo = (op as any)(1, 2);): diagnostics 1`] = `"main.ts(3,22): error TSTL: This function must always be directly called and cannot be referred to."`; +exports[`operator mapping - invalid use (const foo = (op as any)(1, 2);): diagnostics 1`] = `"main.ts(3,22): error TSTL: This function must be called directly and cannot be referred to."`; exports[`operator mapping - invalid use (const foo = [op];): code 1`] = `"foo = {op}"`; -exports[`operator mapping - invalid use (const foo = [op];): diagnostics 1`] = `"main.ts(3,22): error TSTL: This function must always be directly called and cannot be referred to."`; +exports[`operator mapping - invalid use (const foo = [op];): diagnostics 1`] = `"main.ts(3,22): error TSTL: This function must be called directly and cannot be referred to."`; exports[`operator mapping - invalid use (const foo = \`\${op}\`): code 1`] = `"foo = tostring(op)"`; -exports[`operator mapping - invalid use (const foo = \`\${op}\`): diagnostics 1`] = `"main.ts(3,24): error TSTL: This function must always be directly called and cannot be referred to."`; +exports[`operator mapping - invalid use (const foo = \`\${op}\`): diagnostics 1`] = `"main.ts(3,24): error TSTL: This function must be called directly and cannot be referred to."`; exports[`operator mapping - invalid use (const foo: unknown = op;): code 1`] = `"foo = op"`; -exports[`operator mapping - invalid use (const foo: unknown = op;): diagnostics 1`] = `"main.ts(3,30): error TSTL: This function must always be directly called and cannot be referred to."`; +exports[`operator mapping - invalid use (const foo: unknown = op;): diagnostics 1`] = `"main.ts(3,30): error TSTL: This function must be called directly and cannot be referred to."`; exports[`operator mapping - invalid use (declare function foo(op: LuaAddition): void; foo(op);): code 1`] = `"foo(_G, op)"`; -exports[`operator mapping - invalid use (declare function foo(op: LuaAddition): void; foo(op);): diagnostics 1`] = `"main.ts(3,82): error TSTL: This function must always be directly called and cannot be referred to."`; +exports[`operator mapping - invalid use (declare function foo(op: LuaAddition): void; foo(op);): diagnostics 1`] = `"main.ts(3,82): error TSTL: This function must be called directly and cannot be referred to."`; + +exports[`unsupported binary operator mapping (5.0 LuaBitwiseAnd): code 1`] = `"local ____ = left & right"`; + +exports[`unsupported binary operator mapping (5.0 LuaBitwiseAnd): diagnostics 1`] = `"main.ts(6,9): error TSTL: Native bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`unsupported binary operator mapping (5.0 LuaBitwiseExclusiveOr): code 1`] = `"local ____ = left ~ right"`; + +exports[`unsupported binary operator mapping (5.0 LuaBitwiseExclusiveOr): diagnostics 1`] = `"main.ts(6,9): error TSTL: Native bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`unsupported binary operator mapping (5.0 LuaBitwiseLeftShift): code 1`] = `"local ____ = left << right"`; + +exports[`unsupported binary operator mapping (5.0 LuaBitwiseLeftShift): diagnostics 1`] = `"main.ts(6,9): error TSTL: Native bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`unsupported binary operator mapping (5.0 LuaBitwiseOr): code 1`] = `"local ____ = left | right"`; + +exports[`unsupported binary operator mapping (5.0 LuaBitwiseOr): diagnostics 1`] = `"main.ts(6,9): error TSTL: Native bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`unsupported binary operator mapping (5.0 LuaBitwiseRightShift): code 1`] = `"local ____ = left >> right"`; + +exports[`unsupported binary operator mapping (5.0 LuaBitwiseRightShift): diagnostics 1`] = `"main.ts(6,9): error TSTL: Native bitwise operations is/are not supported for target Lua 5.0."`; + +exports[`unsupported binary operator mapping (5.0 LuaFloorDivision): code 1`] = `"local ____ = left // right"`; + +exports[`unsupported binary operator mapping (5.0 LuaFloorDivision): diagnostics 1`] = `"main.ts(6,9): error TSTL: Floor division operator is/are not supported for target Lua 5.0."`; exports[`unsupported binary operator mapping (5.1 LuaBitwiseAnd): code 1`] = `"local ____ = left & right"`; @@ -116,6 +152,10 @@ exports[`unsupported binary operator mapping (universal LuaFloorDivision): code exports[`unsupported binary operator mapping (universal LuaFloorDivision): diagnostics 1`] = `"main.ts(6,9): error TSTL: Floor division operator is/are not supported for target Lua 5.1."`; +exports[`unsupported unary operator mapping (5.0 LuaBitwiseNot): code 1`] = `"local ____ = ~operand"`; + +exports[`unsupported unary operator mapping (5.0 LuaBitwiseNot): diagnostics 1`] = `"main.ts(5,9): error TSTL: Native bitwise operations is/are not supported for target Lua 5.0."`; + exports[`unsupported unary operator mapping (5.1 LuaBitwiseNot): code 1`] = `"local ____ = ~operand"`; exports[`unsupported unary operator mapping (5.1 LuaBitwiseNot): diagnostics 1`] = `"main.ts(5,9): error TSTL: Native bitwise operations is/are not supported for target Lua 5.1."`; diff --git a/test/unit/language-extensions/__snapshots__/pairsIterable.spec.ts.snap b/test/unit/language-extensions/__snapshots__/pairsIterable.spec.ts.snap new file mode 100644 index 000000000..f38856514 --- /dev/null +++ b/test/unit/language-extensions/__snapshots__/pairsIterable.spec.ts.snap @@ -0,0 +1,26 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`invalid LuaPairsIterable without destructuring ("for (const s of testIterable) {}"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local testIterable = {a1 = "a2", b1 = "b2", c1 = "c2"} + for ____ in pairs(testIterable) do + end +end +return ____exports" +`; + +exports[`invalid LuaPairsIterable without destructuring ("for (const s of testIterable) {}"): diagnostics 1`] = `"main.ts(5,20): error TSTL: LuaPairsIterable type must be destructured in a for...of statement."`; + +exports[`invalid LuaPairsIterable without destructuring ("let s; for (s of testIterable) {}"): code 1`] = ` +"local ____exports = {} +function ____exports.__main(self) + local testIterable = {a1 = "a2", b1 = "b2", c1 = "c2"} + local s + for ____ in pairs(testIterable) do + end +end +return ____exports" +`; + +exports[`invalid LuaPairsIterable without destructuring ("let s; for (s of testIterable) {}"): diagnostics 1`] = `"main.ts(5,21): error TSTL: LuaPairsIterable type must be destructured in a for...of statement."`; diff --git a/test/unit/language-extensions/__snapshots__/range.spec.ts.snap b/test/unit/language-extensions/__snapshots__/range.spec.ts.snap index f066c1260..73a56890b 100644 --- a/test/unit/language-extensions/__snapshots__/range.spec.ts.snap +++ b/test/unit/language-extensions/__snapshots__/range.spec.ts.snap @@ -33,14 +33,11 @@ return ____exports" exports[`$range invalid use ("const x = $range(1, 10);"): diagnostics 1`] = `"main.ts(2,19): error TSTL: $range can only be used in a for...of loop."`; exports[`$range invalid use ("const y = [...$range(1, 10)];"): code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require("lualib_bundle") +local __TS__Spread = ____lualib.__TS__Spread local ____exports = {} function ____exports.__main(self) - local y = { - __TS__Spread( - ____(nil, 1, 10) - ) - } + local y = {__TS__Spread(____(nil, 1, 10))} end return ____exports" `; @@ -50,9 +47,7 @@ exports[`$range invalid use ("const y = [...$range(1, 10)];"): diagnostics 1`] = exports[`$range invalid use ("for (const i in $range(1, 10, 2)) {}"): code 1`] = ` "local ____exports = {} function ____exports.__main(self) - for i in pairs( - ____(nil, 1, 10, 2) - ) do + for i in pairs(____(nil, 1, 10, 2)) do end end return ____exports" @@ -63,9 +58,7 @@ exports[`$range invalid use ("for (const i in $range(1, 10, 2)) {}"): diagnostic exports[`$range invalid use ("for (const i of $range(1, 10, 2) as number[]) {}"): code 1`] = ` "local ____exports = {} function ____exports.__main(self) - for ____, i in ipairs( - ____(nil, 1, 10, 2) - ) do + for ____, i in ipairs(____(nil, 1, 10, 2)) do end end return ____exports" @@ -74,12 +67,11 @@ return ____exports" exports[`$range invalid use ("for (const i of $range(1, 10, 2) as number[]) {}"): diagnostics 1`] = `"main.ts(2,25): error TSTL: $range can only be used in a for...of loop."`; exports[`$range invalid use ("for (const i of ($range(1, 10, 2))) {}"): code 1`] = ` -"require(\\"lualib_bundle\\"); +"local ____lualib = require("lualib_bundle") +local __TS__Iterator = ____lualib.__TS__Iterator local ____exports = {} function ____exports.__main(self) - for ____, i in __TS__Iterator( - ____(nil, 1, 10, 2) - ) do + for ____, i in __TS__Iterator(____(nil, 1, 10, 2)) do end end return ____exports" diff --git a/test/unit/language-extensions/__snapshots__/table.spec.ts.snap b/test/unit/language-extensions/__snapshots__/table.spec.ts.snap index 5deed95c3..24abd6e71 100644 --- a/test/unit/language-extensions/__snapshots__/table.spec.ts.snap +++ b/test/unit/language-extensions/__snapshots__/table.spec.ts.snap @@ -46,92 +46,6 @@ return ____exports" exports[`LuaTable extension interface LuaTable in strict mode does not accept key type that could be nil ("unknown"): diagnostics 1`] = `"main.ts(1,38): error TS2344: Type 'unknown' does not satisfy the constraint 'AnyNotNil'."`; -exports[`LuaTableDelete extension LuaTableDelete invalid use as expression ("const foo = [tableDelete({}, \\"foo\\")];"): code 1`] = ` -"foo = { - (function() - ({}).foo = nil - return nil - end)() -}" -`; - -exports[`LuaTableDelete extension LuaTableDelete invalid use as expression ("const foo = [tableDelete({}, \\"foo\\")];"): diagnostics 1`] = `"main.ts(3,26): error TSTL: Table delete extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement."`; - -exports[`LuaTableDelete extension LuaTableDelete invalid use as expression ("const foo = \`\${tableDelete({}, \\"foo\\")}\`;"): code 1`] = ` -"foo = tostring( - (function() - ({}).foo = nil - return nil - end)() -)" -`; - -exports[`LuaTableDelete extension LuaTableDelete invalid use as expression ("const foo = \`\${tableDelete({}, \\"foo\\")}\`;"): diagnostics 1`] = `"main.ts(3,28): error TSTL: Table delete extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement."`; - -exports[`LuaTableDelete extension LuaTableDelete invalid use as expression ("const foo = tableDelete({}, \\"foo\\");"): code 1`] = ` -"foo = (function() - ({}).foo = nil - return nil -end)()" -`; - -exports[`LuaTableDelete extension LuaTableDelete invalid use as expression ("const foo = tableDelete({}, \\"foo\\");"): diagnostics 1`] = `"main.ts(3,25): error TSTL: Table delete extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement."`; - -exports[`LuaTableDelete extension LuaTableDelete invalid use as expression ("declare function foo(arg: any): void; foo(tableDelete({}, \\"foo\\"));"): code 1`] = ` -"foo( - _G, - (function() - ({}).foo = nil - return nil - end)() -)" -`; - -exports[`LuaTableDelete extension LuaTableDelete invalid use as expression ("declare function foo(arg: any): void; foo(tableDelete({}, \\"foo\\"));"): diagnostics 1`] = `"main.ts(3,55): error TSTL: Table delete extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement."`; - -exports[`LuaTableGet & LuaTableSet extensions LuaTableSet invalid use as expression ("const foo = [setTable({}, \\"foo\\", 3)];"): code 1`] = ` -"foo = { - (function() - ({}).foo = 3 - return nil - end)() -}" -`; - -exports[`LuaTableGet & LuaTableSet extensions LuaTableSet invalid use as expression ("const foo = [setTable({}, \\"foo\\", 3)];"): diagnostics 1`] = `"main.ts(3,26): error TSTL: Table set extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement."`; - -exports[`LuaTableGet & LuaTableSet extensions LuaTableSet invalid use as expression ("const foo = \`\${setTable({}, \\"foo\\", 3)}\`;"): code 1`] = ` -"foo = tostring( - (function() - ({}).foo = 3 - return nil - end)() -)" -`; - -exports[`LuaTableGet & LuaTableSet extensions LuaTableSet invalid use as expression ("const foo = \`\${setTable({}, \\"foo\\", 3)}\`;"): diagnostics 1`] = `"main.ts(3,28): error TSTL: Table set extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement."`; - -exports[`LuaTableGet & LuaTableSet extensions LuaTableSet invalid use as expression ("const foo = setTable({}, \\"foo\\", 3);"): code 1`] = ` -"foo = (function() - ({}).foo = 3 - return nil -end)()" -`; - -exports[`LuaTableGet & LuaTableSet extensions LuaTableSet invalid use as expression ("const foo = setTable({}, \\"foo\\", 3);"): diagnostics 1`] = `"main.ts(3,25): error TSTL: Table set extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement."`; - -exports[`LuaTableGet & LuaTableSet extensions LuaTableSet invalid use as expression ("declare function foo(arg: any): void; foo(setTable({}, \\"foo\\", 3));"): code 1`] = ` -"foo( - _G, - (function() - ({}).foo = 3 - return nil - end)() -)" -`; - -exports[`LuaTableGet & LuaTableSet extensions LuaTableSet invalid use as expression ("declare function foo(arg: any): void; foo(setTable({}, \\"foo\\", 3));"): diagnostics 1`] = `"main.ts(3,55): error TSTL: Table set extension can only be called as a stand-alone statement. It cannot be used as an expression in another statement."`; - exports[`LuaTableGet & LuaTableSet extensions invalid use ("const foo = (getTable as any)(1, 2);"): code 1`] = `"foo = getTable(_G, 1, 2)"`; exports[`LuaTableGet & LuaTableSet extensions invalid use ("const foo = (getTable as any)(1, 2);"): diagnostics 1`] = `"main.ts(3,26): error TSTL: This function must be called directly and cannot be referred to."`; @@ -171,3 +85,59 @@ exports[`LuaTableHas extension invalid use ("const foo: unknown = tableHas;"): d exports[`LuaTableHas extension invalid use ("declare function foo(tableHas: LuaTableHas<{}, string>): void; foo(tableHas);"): code 1`] = `"foo(_G, tableHas)"`; exports[`LuaTableHas extension invalid use ("declare function foo(tableHas: LuaTableHas<{}, string>): void; foo(tableHas);"): diagnostics 1`] = `"main.ts(3,80): error TSTL: This function must be called directly and cannot be referred to."`; + +exports[`LuaTableHas extension invalid use method assignment ("LuaMap"): code 1`] = ` +"____table = {} +has = ____table.has" +`; + +exports[`LuaTableHas extension invalid use method assignment ("LuaMap"): diagnostics 1`] = `"main.ts(3,29): error TSTL: This function must be called directly and cannot be referred to."`; + +exports[`LuaTableHas extension invalid use method assignment ("LuaSet"): code 1`] = ` +"____table = {} +has = ____table.has" +`; + +exports[`LuaTableHas extension invalid use method assignment ("LuaSet"): diagnostics 1`] = `"main.ts(3,29): error TSTL: This function must be called directly and cannot be referred to."`; + +exports[`LuaTableHas extension invalid use method assignment ("LuaTable"): code 1`] = ` +"____table = {} +has = ____table.has" +`; + +exports[`LuaTableHas extension invalid use method assignment ("LuaTable"): diagnostics 1`] = `"main.ts(3,29): error TSTL: This function must be called directly and cannot be referred to."`; + +exports[`LuaTableHas extension invalid use method expression ("LuaMap"): code 1`] = ` +"local ____lualib = require("lualib_bundle") +local __TS__ArrayMap = ____lualib.__TS__ArrayMap +____table = {} +__TS__ArrayMap({"a", "b", "c"}, ____table.has)" +`; + +exports[`LuaTableHas extension invalid use method expression ("LuaMap"): diagnostics 1`] = `"main.ts(3,37): error TSTL: This function must be called directly and cannot be referred to."`; + +exports[`LuaTableHas extension invalid use method expression ("LuaSet"): code 1`] = ` +"local ____lualib = require("lualib_bundle") +local __TS__ArrayMap = ____lualib.__TS__ArrayMap +____table = {} +__TS__ArrayMap({"a", "b", "c"}, ____table.has)" +`; + +exports[`LuaTableHas extension invalid use method expression ("LuaSet"): diagnostics 1`] = `"main.ts(3,37): error TSTL: This function must be called directly and cannot be referred to."`; + +exports[`LuaTableHas extension invalid use method expression ("LuaTable"): code 1`] = ` +"local ____lualib = require("lualib_bundle") +local __TS__ArrayMap = ____lualib.__TS__ArrayMap +____table = {} +__TS__ArrayMap({"a", "b", "c"}, ____table.has)" +`; + +exports[`LuaTableHas extension invalid use method expression ("LuaTable"): diagnostics 1`] = `"main.ts(3,37): error TSTL: This function must be called directly and cannot be referred to."`; + +exports[`does not crash on invalid extension use global function: code 1`] = `""`; + +exports[`does not crash on invalid extension use global function: diagnostics 1`] = `"main.ts(3,9): error TS2554: Expected 2 arguments, but got 1."`; + +exports[`does not crash on invalid extension use method: code 1`] = `"left = {}"`; + +exports[`does not crash on invalid extension use method: diagnostics 1`] = `"main.ts(5,14): error TS2554: Expected 2 arguments, but got 0."`; diff --git a/test/unit/language-extensions/__snapshots__/vararg.spec.ts.snap b/test/unit/language-extensions/__snapshots__/vararg.spec.ts.snap index 819cc27a0..e2cf42298 100644 --- a/test/unit/language-extensions/__snapshots__/vararg.spec.ts.snap +++ b/test/unit/language-extensions/__snapshots__/vararg.spec.ts.snap @@ -24,12 +24,14 @@ f(_G, ____)" exports[`$vararg invalid use ("function f(s: string[]) {} f($vararg);"): diagnostics 1`] = `"main.ts(2,38): error TSTL: $vararg can only be used in a spread element ('...$vararg') in global scope."`; exports[`$vararg invalid use ("function foo(...args: string[]) {} function bar() { foo(...$vararg); }"): code 1`] = ` -"function foo(self, ...) +"local ____lualib = require("lualib_bundle") +local __TS__Spread = ____lualib.__TS__Spread +function foo(self, ...) end function bar(self) foo( _G, - table.unpack(____) + __TS__Spread(____) ) end" `; diff --git a/test/unit/language-extensions/iterable.spec.ts b/test/unit/language-extensions/iterable.spec.ts index b6f7ab141..30e602f62 100644 --- a/test/unit/language-extensions/iterable.spec.ts +++ b/test/unit/language-extensions/iterable.spec.ts @@ -1,12 +1,6 @@ -import * as path from "path"; import * as util from "../../util"; -import * as tstl from "../../../src"; import { invalidMultiIterableWithoutDestructuring } from "../../../src/transformation/utils/diagnostics"; -const iterableProjectOptions: tstl.CompilerOptions = { - types: [path.resolve(__dirname, "../../../language-extensions")], -}; - describe("simple LuaIterable", () => { const testIterable = ` function testIterable(this: void): LuaIterable { @@ -26,7 +20,7 @@ describe("simple LuaIterable", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -39,7 +33,7 @@ describe("simple LuaIterable", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -53,7 +47,7 @@ describe("simple LuaIterable", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -67,7 +61,7 @@ describe("simple LuaIterable", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -81,7 +75,7 @@ describe("simple LuaIterable", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -95,7 +89,7 @@ describe("simple LuaIterable", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -113,7 +107,7 @@ describe("simple LuaIterable", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); }); @@ -136,7 +130,7 @@ describe("LuaIterable using state", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -149,7 +143,7 @@ describe("LuaIterable using state", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -163,7 +157,7 @@ describe("LuaIterable using state", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -177,7 +171,7 @@ describe("LuaIterable using state", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -191,7 +185,7 @@ describe("LuaIterable using state", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -205,7 +199,7 @@ describe("LuaIterable using state", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -223,7 +217,7 @@ describe("LuaIterable using state", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); }); @@ -245,27 +239,27 @@ describe("LuaIterable with array value type", () => { test("basic destructuring", () => { util.testFunction` ${testIterable} - const results: Array = []; + const results = []; for (const [x, y] of testIterable()) { results.push([x, y]); } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); test("destructure with external control variable", () => { util.testFunction` ${testIterable} - const results: Array = []; + const results = [] let x: string, y: string; for ([x, y] of testIterable()) { results.push([x, y]); } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -279,7 +273,7 @@ describe("LuaIterable with array value type", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -293,7 +287,7 @@ describe("LuaIterable with array value type", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -307,7 +301,7 @@ describe("LuaIterable with array value type", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -325,15 +319,15 @@ describe("LuaIterable with array value type", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); }); describe("LuaIterable with LuaMultiReturn value type", () => { const testIterable = ` - function testIterable(this: void): LuaIterable> { - const strsArray = [["a1", "a2"], ["b1", "b2"], ["c1", "c2"]]; + function testIterable(this: void): LuaIterable> { + const strsArray = [["a1", {a: "a"}], ["b1", {a: "b"}], ["c1", {a: "c"}]]; let i = 0; return (() => { const strs = strsArray[i++]; @@ -344,35 +338,35 @@ describe("LuaIterable with LuaMultiReturn value type", () => { } `; const testResults = [ - ["a1", "a2"], - ["b1", "b2"], - ["c1", "c2"], + ["a1", { a: "a" }], + ["b1", { a: "b" }], + ["c1", { a: "c" }], ]; test("basic destructuring", () => { util.testFunction` ${testIterable} - const results: Array = []; + const results = []; for (const [x, y] of testIterable()) { results.push([x, y]); } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); test("destructure with external control variable", () => { util.testFunction` ${testIterable} - const results: Array = []; - let x: string, y: string; + const results = []; + let x: string, y: any; for ([x, y] of testIterable()) { results.push([x, y]); } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -380,13 +374,13 @@ describe("LuaIterable with LuaMultiReturn value type", () => { util.testFunction` ${testIterable} function forward() { return testIterable(); } - const results: Array = []; + const results = []; for (const [x, y] of forward()) { results.push([x, y]); } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -394,13 +388,13 @@ describe("LuaIterable with LuaMultiReturn value type", () => { util.testFunction` ${testIterable} function forward() { const iter = testIterable(); return iter; } - const results: Array = []; + const results = []; for (const [x, y] of forward()) { results.push([x, y]); } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -408,20 +402,20 @@ describe("LuaIterable with LuaMultiReturn value type", () => { util.testFunction` ${testIterable} const forward = () => testIterable(); - const results: Array = []; + const results = []; for (const [x, y] of forward()) { results.push([x, y]); } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); test("destructure manual use", () => { util.testFunction` ${testIterable} - const results: Array = []; + const results = []; const iter = testIterable(); while (true) { const [x, y] = iter(); @@ -432,10 +426,27 @@ describe("LuaIterable with LuaMultiReturn value type", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); + test("nested destructuring", () => { + util.testFunction` + ${testIterable} + const results = []; + for (const [x, {a}] of testIterable()) { + results.push([x, a]); + } + return results; + ` + .withLanguageExtensions() + .expectToEqual([ + ["a1", "a"], + ["b1", "b"], + ["c1", "c"], + ]); + }); + test.each(["for (const s of testIterable()) {}", "let s; for (s of testIterable()) {}"])( "invalid LuaIterable without destructuring (%p)", statement => { @@ -443,7 +454,7 @@ describe("LuaIterable with LuaMultiReturn value type", () => { ${testIterable} ${statement} ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectDiagnosticsToMatchSnapshot([invalidMultiIterableWithoutDestructuring.code]); } ); @@ -472,7 +483,7 @@ describe("LuaIterable property", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -486,7 +497,7 @@ describe("LuaIterable property", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -500,7 +511,7 @@ describe("LuaIterable property", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -514,7 +525,7 @@ describe("LuaIterable property", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -528,7 +539,7 @@ describe("LuaIterable property", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); @@ -546,7 +557,7 @@ describe("LuaIterable property", () => { } return results; ` - .setOptions(iterableProjectOptions) + .withLanguageExtensions() .expectToEqual(testResults); }); }); diff --git a/test/unit/language-extensions/multi.spec.ts b/test/unit/language-extensions/multi.spec.ts index 5d59aa4d0..90fc54200 100644 --- a/test/unit/language-extensions/multi.spec.ts +++ b/test/unit/language-extensions/multi.spec.ts @@ -1,16 +1,10 @@ -import * as path from "path"; import * as util from "../../util"; -import * as tstl from "../../../src"; import { invalidMultiFunctionUse, invalidMultiReturnAccess, invalidMultiFunctionReturnType, } from "../../../src/transformation/utils/diagnostics"; -const multiProjectOptions: tstl.CompilerOptions = { - types: [path.resolve(__dirname, "../../../language-extensions")], -}; - test("multi example use case", () => { util.testModule` function multiReturn(): LuaMultiReturn<[string, number]> { @@ -20,7 +14,7 @@ test("multi example use case", () => { const [a, b] = multiReturn(); export { a, b }; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual({ a: "foo", b: 5 }); }); @@ -34,7 +28,7 @@ test("Destructuring assignment of LuaMultiReturn", () => { const [a, ...b] = multiReturn(); export {a, b}; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual({ a: 1, b: [2, 3] }); }); @@ -47,7 +41,8 @@ test("Destructuring assignment of LuaMultiReturn returning nil", () => { const [a, ...b] = multiReturn(); export {a, b}; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() + .setOptions({ strict: false }) .expectToEqual({ a: undefined, b: [] }); }); @@ -59,12 +54,12 @@ test.each<[string, any]>([ util.testFunction` return ${expression}; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual(result); }); const multiFunction = ` -function multi(...args) { +function multi(...args: unknown[]) { return $multi(...args); } `; @@ -77,9 +72,9 @@ const createCasesThatCall = (name: string): Array<[string, any]> => [ [`const [a = 1] = ${name}(2)`, 2], [`const ar = [1]; const [a] = ${name}(...ar)`, 1], [`const _ = null, [a] = ${name}(1)`, 1], - [`let a; for (const [a] = ${name}(1, 2); false; 1) {}`, undefined], - [`let a; for ([a] = ${name}(1, 2); false; 1) {}`, 1], - [`let a; if ([a] = ${name}(1)) { ++a; }`, 2], + [`let a: any; for (const [a] = ${name}(1, 2); false; 1) {}`, undefined], + [`let a: any; for ([a] = ${name}(1, 2); false; 1) {}`, 1], + [`let a: any; if ([a] = ${name}(1)) { ++a; }`, 2], ]; test.each<[string, any]>(createCasesThatCall("$multi"))("invalid direct $multi function use (%s)", statement => { @@ -88,7 +83,7 @@ test.each<[string, any]>(createCasesThatCall("$multi"))("invalid direct $multi f ${statement} export { a }; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .setReturnExport("a") .expectDiagnosticsToMatchSnapshot([invalidMultiFunctionUse.code]); }); @@ -101,7 +96,7 @@ test.each<[string, any]>(createCasesThatCall("multi"))( ${statement} export { a }; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .setReturnExport("a") .expectToEqual(result); } @@ -119,7 +114,7 @@ test.each<[string, number[]]>([ util.testModule` ${statement} ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectDiagnosticsToMatchSnapshot(diagnostics); }); @@ -131,7 +126,7 @@ test("function to spread multi type result from multi type function", () => { } return m(); ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual(true); }); @@ -142,7 +137,7 @@ test("$multi call with destructuring assignment side effects", () => { export { a }; [a] = multi(1); ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .setReturnExport("a") .expectToEqual(1); }); @@ -153,7 +148,7 @@ test("allow $multi call in ArrowFunction body", () => { const [result] = call(); return result; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual(1); }); @@ -164,7 +159,7 @@ test("forward $multi call", () => { const [resultA, resultB] = call(); return [resultA, resultB]; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual([1, 2]); }); @@ -175,7 +170,7 @@ test("forward $multi call indirect", () => { const [resultA, resultB] = call(); return [resultA, resultB]; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual([1, 2]); }); @@ -186,17 +181,47 @@ test("forward $multi call in ArrowFunction body", () => { const [resultA, resultB] = call(); return [resultA, resultB]; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual([1, 2]); }); +test("$multi call in function typed as any", () => { + util.testFunction` + function foo(): any { return $multi(1, 2); } + return foo() + ` + .withLanguageExtensions() + .expectToHaveNoDiagnostics() + .expectToEqual(1); +}); + +test("$multi call cast to any", () => { + util.testFunction` + function foo() { return $multi(1, 2) as any; } + return foo() + ` + .withLanguageExtensions() + .expectToHaveNoDiagnostics() + .expectToEqual(1); +}); + +test("$multi call cast to MultiReturn type", () => { + util.testFunction` + function foo() { return $multi(1, 2) as unknown as LuaMultiReturn; } + return foo() + ` + .withLanguageExtensions() + .expectToHaveNoDiagnostics() + .expectToEqual(1); +}); + test.each(["0", "i"])("allow LuaMultiReturn numeric access (%s)", expression => { util.testFunction` ${multiFunction} const i = 0; return multi(1)[${expression}]; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual(1); }); @@ -205,7 +230,7 @@ test.each(["multi()['forEach']", "multi().forEach"])("disallow LuaMultiReturn no ${multiFunction} return ${expression}; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectDiagnosticsToMatchSnapshot([invalidMultiReturnAccess.code]); }); @@ -215,7 +240,7 @@ test("invalid $multi implicit cast", () => { return $multi("foo", 42); } ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectDiagnosticsToMatchSnapshot([invalidMultiFunctionReturnType.code]); }); @@ -236,7 +261,7 @@ test.each([ const [x, y] = multiOverload(${flag}); return y; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual(result); }); @@ -248,10 +273,10 @@ test("return $multi from try", () => { } catch { } } - const [_, a] = multiTest(); + const [_, a] = multiTest()!; return a; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual(2); }); @@ -267,7 +292,7 @@ test("return $multi from catch", () => { const [_, a] = multiTest(); return a; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual(2); }); @@ -280,10 +305,10 @@ test("return LuaMultiReturn from try", () => { } catch { } } - const [_, a] = multiTest(); + const [_, a] = multiTest()!; return a; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual(2); }); @@ -300,6 +325,80 @@ test("return LuaMultiReturn from catch", () => { const [_, a] = multiTest(); return a; ` - .setOptions(multiProjectOptions) + .withLanguageExtensions() .expectToEqual(2); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1404 +test("LuaMultiReturn applies after casting a function (#1404)", () => { + util.testFunction` + let swap: any = (a: number, b: number) => $multi(b, a); + let [a, b] = (swap as (...args: any) => LuaMultiReturn<[number, number]>)(4, 3); + return [a, b]; + ` + .withLanguageExtensions() + .expectToEqual([3, 4]); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1411 +describe("LuaMultiReturn returns all values even when indexed with [0] #1411", () => { + const sharedCode = ` + declare function select(this:void, index: '#', ...args: T[]): number; + + function foo(this: void): LuaMultiReturn<[number, number]> { + return $multi(123, 456); + } + + function bar(this: void): number { + return foo()[0]; + } + `; + test("Returns the correct value", () => { + util.testFunction` + return bar(); + ` + .setTsHeader(sharedCode) + .withLanguageExtensions() + .expectToEqual(123); + }); + + // We require an extra test since the test above would succeed even if bar() returned more than one value. + // This is because our test helper always returns just the first lua value returned from the testFunction. + // Here we need to test explicitly that only one value is returned. + test("Does not return multiple values", () => { + util.testFunction` + return select("#", bar()); + ` + .setTsHeader(sharedCode) + .withLanguageExtensions() + .expectToEqual(1); + }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1591 +test("LuaMultiReturn in LuaIterable (#1591)", () => { + const lua = util.testModule` + type IterableAlias = LuaIterable>; + + declare const iterable: IterableAlias; + + for (const [a, b] of iterable) {} + ` + .withLanguageExtensions() + .getMainLuaCodeChunk(); + + expect(lua).toContain("a, b"); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1591 +test("LuaMultiReturn in LuaIterable intersection (#1591)", () => { + const lua = util.testModule` + declare function iterator(): { a: string } & LuaIterable>; + + for (const [a, b] of iterator()) {} + ` + .withLanguageExtensions() + .getMainLuaCodeChunk(); + + expect(lua).toContain("a, b"); +}); diff --git a/test/unit/language-extensions/operators.spec.ts b/test/unit/language-extensions/operators.spec.ts index c84f8aac9..303a5d0be 100644 --- a/test/unit/language-extensions/operators.spec.ts +++ b/test/unit/language-extensions/operators.spec.ts @@ -1,9 +1,12 @@ import * as path from "path"; import * as util from "../../util"; import * as tstl from "../../../src"; -import { invalidOperatorMappingUse } from "../../../src/transformation/utils/diagnostics"; import { LuaTarget } from "../../../src"; -import { unsupportedForTarget } from "../../../src/transformation/utils/diagnostics"; +import { + unsupportedForTarget, + invalidCallExtensionUse, + invalidSpreadInCallExtension, +} from "../../../src/transformation/utils/diagnostics"; const operatorsProjectOptions: tstl.CompilerOptions = { luaTarget: LuaTarget.Lua54, @@ -114,7 +117,7 @@ test.each(binaryMathOperatorTests)( } ); -const luaTargetsPre53 = [LuaTarget.Lua51, LuaTarget.Lua52, LuaTarget.LuaJIT, LuaTarget.Universal]; +const luaTargetsPre53 = [LuaTarget.Lua50, LuaTarget.Lua51, LuaTarget.Lua52, LuaTarget.LuaJIT, LuaTarget.Universal]; const operatorTypesPost53 = [ "LuaFloorDivision", @@ -388,5 +391,43 @@ test.each([ ${invalidCode} ` .setOptions(operatorsProjectOptions) - .expectDiagnosticsToMatchSnapshot([invalidOperatorMappingUse.code]); + .expectDiagnosticsToMatchSnapshot([invalidCallExtensionUse.code]); +}); + +describe("does not crash on invalid operator use", () => { + test("global function", () => { + util.testModule` + declare const op: LuaAddition; + op(1) + ` + .setOptions(operatorsProjectOptions) + .expectDiagnosticsToMatchSnapshot(); + }); + test("unary operator", () => { + util.testModule` + declare const op: LuaUnaryMinus; + op() + ` + .setOptions(operatorsProjectOptions) + .expectDiagnosticsToMatchSnapshot(); + }); + test("method", () => { + util.testModule` + const left = {} as { + op: LuaAdditionMethod; + } + left.op() + ` + .setOptions(operatorsProjectOptions) + .expectDiagnosticsToMatchSnapshot(); + }); +}); + +test("does not allow spread", () => { + util.testModule` + declare const op: LuaAddition; + op(...[1, 2] as const); + ` + .setOptions(operatorsProjectOptions) + .expectToHaveDiagnostics([invalidSpreadInCallExtension.code]); }); diff --git a/test/unit/language-extensions/pairsIterable.spec.ts b/test/unit/language-extensions/pairsIterable.spec.ts new file mode 100644 index 000000000..51c7e0b4e --- /dev/null +++ b/test/unit/language-extensions/pairsIterable.spec.ts @@ -0,0 +1,219 @@ +import * as util from "../../util"; +import { invalidPairsIterableWithoutDestructuring } from "../../../src/transformation/utils/diagnostics"; +import { LuaTarget } from "../../../src"; + +const testIterable = ` +const testIterable = {a1: "a2", b1: "b2", c1: "c2"} as unknown as LuaPairsIterable; +`; + +const testResults = { + a1: "a2", + b1: "b2", + c1: "c2", +}; + +test("pairs iterable", () => { + util.testFunction` + ${testIterable} + const results: Record = {}; + for (const [k, v] of testIterable) { + results[k] = v; + } + return results; + ` + .withLanguageExtensions() + .expectToEqual(testResults); +}); + +test("pairs iterable with external control variable", () => { + util.testFunction` + ${testIterable} + const results: Record = {}; + let k: string, v: string; + for ([k, v] of testIterable) { + results[k] = v; + } + return results; + ` + .withLanguageExtensions() + .expectToEqual(testResults); +}); + +test("pairs iterable function forward", () => { + util.testFunction` + ${testIterable} + function forward() { return testIterable; } + const results: Record = {}; + for (const [k, v] of forward()) { + results[k] = v; + } + return results; + ` + .withLanguageExtensions() + .expectToEqual(testResults); +}); + +test("pairs iterable function indirect forward", () => { + util.testFunction` + ${testIterable} + function forward() { const iter = testIterable; return iter; } + const results: Record = {}; + for (const [k, v] of forward()) { + results[k] = v; + } + return results; + ` + .withLanguageExtensions() + .expectToEqual(testResults); +}); + +test("pairs iterable arrow function forward", () => { + util.testFunction` + ${testIterable} + const forward = () => testIterable; + const results: Record = {}; + for (const [k, v] of forward()) { + results[k] = v; + } + return results; + ` + .withLanguageExtensions() + .expectToEqual(testResults); +}); + +test("pairs iterable with __pairs metamethod", () => { + util.testFunction` + class PairsTest { + __pairs() { + const kvp = [ ["a1", "a2"], ["b1", "b2"], ["c1", "c2"] ]; + let i = 0; + return () => { + if (i < kvp.length) { + const [k, v] = kvp[i++]; + return $multi(k, v); + } + }; + } + } + const tester = new PairsTest() as PairsTest & LuaPairsIterable; + const results: Record = {}; + for (const [k, v] of tester) { + results[k] = v; + } + return results; + ` + .withLanguageExtensions() + .setOptions({ luaTarget: LuaTarget.Lua53 }) + .expectToEqual(testResults); +}); + +test.each(["for (const s of testIterable) {}", "let s; for (s of testIterable) {}"])( + "invalid LuaPairsIterable without destructuring (%p)", + statement => { + util.testFunction` + ${testIterable} + ${statement} + ` + .withLanguageExtensions() + .expectDiagnosticsToMatchSnapshot([invalidPairsIterableWithoutDestructuring.code]); + } +); + +const testKeyIterable = ` +const testKeyIterable = {a1: true, b1: true, c1: true} as unknown as LuaPairsKeyIterable; +`; + +test("pairs key iterable", () => { + util.testFunction` + ${testKeyIterable} + const results: Record = {}; + for (const k of testKeyIterable) { + results[k] = true; + } + return results; + ` + .withLanguageExtensions() + .expectToEqual({ a1: true, b1: true, c1: true }); +}); + +test("pairs key iterable with external control variable", () => { + util.testFunction` + ${testKeyIterable} + const results: Record = {}; + let k: string; + for (k of testKeyIterable) { + results[k] = true; + } + return results; + ` + .withLanguageExtensions() + .expectToEqual({ a1: true, b1: true, c1: true }); +}); + +test("pairs key iterable function forward", () => { + util.testFunction` + ${testKeyIterable} + function forward() { return testKeyIterable; } + const results: Record = {}; + for (const k of forward()) { + results[k] = true; + } + return results; + ` + .withLanguageExtensions() + .expectToEqual({ a1: true, b1: true, c1: true }); +}); + +test("pairs key iterable function indirect forward", () => { + util.testFunction` + ${testKeyIterable} + function forward() { const iter = testKeyIterable; return iter; } + const results: Record = {}; + for (const k of forward()) { + results[k] = true; + } + return results; + ` + .withLanguageExtensions() + .expectToEqual({ a1: true, b1: true, c1: true }); +}); + +test("pairs key iterable arrow function forward", () => { + util.testFunction` + ${testKeyIterable} + const forward = () => testKeyIterable; + const results: Record = {}; + for (const k of forward()) { + results[k] = true; + } + return results; + ` + .withLanguageExtensions() + .expectToEqual({ a1: true, b1: true, c1: true }); +}); + +test("pairs key iterable with __pairs metamethod", () => { + util.testFunction` + class PairsTest { + __pairs() { + const kvp = [ ["a1", true], ["b1", true], ["c1", true] ]; + let i = 0; + return () => { + if (i < kvp.length) { + const [k, v] = kvp[i++]; + return $multi(k, v); + } + }; + } + } + const tester = new PairsTest() as PairsTest & LuaPairsKeyIterable; + const results: Record = {}; + for (const k of tester) { + results[k] = true; + } + return results; + ` + .withLanguageExtensions() + .setOptions({ luaTarget: LuaTarget.Lua53 }) + .expectToEqual({ a1: true, b1: true, c1: true }); +}); diff --git a/test/unit/language-extensions/range.spec.ts b/test/unit/language-extensions/range.spec.ts index 56ac65251..3fc5539f6 100644 --- a/test/unit/language-extensions/range.spec.ts +++ b/test/unit/language-extensions/range.spec.ts @@ -1,12 +1,6 @@ -import * as path from "path"; import * as util from "../../util"; -import * as tstl from "../../../src"; import { invalidRangeControlVariable, invalidRangeUse } from "../../../src/transformation/utils/diagnostics"; -const rangeProjectOptions: tstl.CompilerOptions = { - types: [path.resolve(__dirname, "../../../language-extensions")], -}; - test("$range basic use", () => { util.testFunction` const result: number[] = []; @@ -15,7 +9,7 @@ test("$range basic use", () => { } return result; ` - .setOptions(rangeProjectOptions) + .withLanguageExtensions() .expectToEqual([1, 2, 3, 4, 5]); }); @@ -27,7 +21,7 @@ test("$range basic use with step", () => { } return result; ` - .setOptions(rangeProjectOptions) + .withLanguageExtensions() .expectToEqual([1, 3, 5, 7, 9]); }); @@ -39,7 +33,7 @@ test("$range basic use reverse", () => { } return result; ` - .setOptions(rangeProjectOptions) + .withLanguageExtensions() .expectToEqual([5, 4, 3, 2, 1]); }); @@ -48,7 +42,7 @@ test("$range invalid control variable", () => { let i: number; for (i of $range(1, 5)) {} ` - .setOptions(rangeProjectOptions) + .withLanguageExtensions() .expectDiagnosticsToMatchSnapshot([invalidRangeControlVariable.code]); }); @@ -63,6 +57,6 @@ test.each([ util.testFunction` ${statement} ` - .setOptions(rangeProjectOptions) + .withLanguageExtensions() .expectDiagnosticsToMatchSnapshot([invalidRangeUse.code]); }); diff --git a/test/unit/language-extensions/table.spec.ts b/test/unit/language-extensions/table.spec.ts index e6d485a04..13af766fa 100644 --- a/test/unit/language-extensions/table.spec.ts +++ b/test/unit/language-extensions/table.spec.ts @@ -1,15 +1,5 @@ -import * as path from "path"; import * as util from "../../util"; -import * as tstl from "../../../src"; -import { - invalidTableDeleteExpression, - invalidTableExtensionUse, - invalidTableSetExpression, -} from "../../../src/transformation/utils/diagnostics"; - -const tableProjectOptions: tstl.CompilerOptions = { - types: [path.resolve(__dirname, "../../../language-extensions")], -}; +import { invalidCallExtensionUse } from "../../../src/transformation/utils/diagnostics"; describe("LuaTableGet & LuaTableSet extensions", () => { test("stand-alone function", () => { @@ -21,11 +11,29 @@ describe("LuaTableGet & LuaTableSet extensions", () => { const result = getTable(tbl, "foo"); export { result } ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .setReturnExport("result") .expectToEqual(3); }); + test("stand-alone function with preceding statements", () => { + util.testModule` + declare const setTable: LuaTableSet<{}, string, string>; + const values: any[] = [] + const tbl: any = {}; + const get = (value: any) => { + values.push(value) + return value + } + let x = "a" + setTable(tbl, x += "b", x += "c") + export const result = tbl.ab + ` + .withLanguageExtensions() + .setReturnExport("result") + .expectToEqual("abc"); + }); + test("namespace function", () => { util.testModule` declare namespace Table { @@ -37,7 +45,7 @@ describe("LuaTableGet & LuaTableSet extensions", () => { const result = Table.get(tbl, "foo"); export { result } ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .setReturnExport("result") .expectToEqual(3); }); @@ -53,7 +61,7 @@ describe("LuaTableGet & LuaTableSet extensions", () => { const result = tbl.get("foo"); export { result } ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .setReturnExport("result") .expectToEqual(3); }); @@ -69,22 +77,8 @@ describe("LuaTableGet & LuaTableSet extensions", () => { declare const getTable: LuaTableGet<{}, string, number>; ${statement} ` - .setOptions(tableProjectOptions) - .expectDiagnosticsToMatchSnapshot([invalidTableExtensionUse.code]); - }); - - test.each([ - 'const foo = setTable({}, "foo", 3);', - 'const foo = `${setTable({}, "foo", 3)}`;', - 'declare function foo(arg: any): void; foo(setTable({}, "foo", 3));', - 'const foo = [setTable({}, "foo", 3)];', - ])("LuaTableSet invalid use as expression (%p)", expression => { - util.testModule` - declare const setTable: LuaTableSet<{}, string, number>; - ${expression} - ` - .setOptions(tableProjectOptions) - .expectDiagnosticsToMatchSnapshot([invalidTableSetExpression.code]); + .withLanguageExtensions() + .expectDiagnosticsToMatchSnapshot([invalidCallExtensionUse.code]); }); }); @@ -97,7 +91,7 @@ describe("LuaTableHas extension", () => { export const hasFoo = tableHas(table, "foo"); export const hasBaz = tableHas(table, "baz"); ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual({ hasFoo: true, hasBaz: false }); }); @@ -108,7 +102,7 @@ describe("LuaTableHas extension", () => { export const result = \`table has foo: \${tableHas(table, "foo")}\`; ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual({ result: "table has foo: true" }); }); @@ -122,7 +116,7 @@ describe("LuaTableHas extension", () => { export const hasFoo = Table.tableHas(table, "foo"); export const hasBaz = Table.tableHas(table, "baz"); ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual({ hasFoo: true, hasBaz: false }); }); @@ -138,7 +132,7 @@ describe("LuaTableHas extension", () => { export const hasFoo = table.has("foo"); export const hasBaz = table.has("baz"); ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual({ hasFoo: true, hasBaz: false }); }); @@ -158,7 +152,7 @@ describe("LuaTableHas extension", () => { export const numCalls = count; ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual({ numCalls: 1 }); }); @@ -173,9 +167,33 @@ describe("LuaTableHas extension", () => { declare const tableHas: LuaTableHas<{}, string>; ${statement} ` - .setOptions(tableProjectOptions) - .expectDiagnosticsToMatchSnapshot([invalidTableExtensionUse.code]); + .withLanguageExtensions() + .expectDiagnosticsToMatchSnapshot([invalidCallExtensionUse.code]); }); + + test.each(["LuaTable", "LuaMap", "LuaSet"])( + "invalid use method assignment (%p)", + type => { + util.testModule` + const table = new ${type}(); + const has = table.has; + ` + .withLanguageExtensions() + .expectDiagnosticsToMatchSnapshot([invalidCallExtensionUse.code]); + } + ); + + test.each(["LuaTable", "LuaMap", "LuaSet"])( + "invalid use method expression (%p)", + type => { + util.testModule` + const table = new ${type}(); + ["a", "b", "c"].map(table.has); + ` + .withLanguageExtensions() + .expectDiagnosticsToMatchSnapshot([invalidCallExtensionUse.code]); + } + ); }); describe("LuaTableDelete extension", () => { @@ -186,7 +204,7 @@ describe("LuaTableDelete extension", () => { export const table = { foo: "bar", baz: "baz" }; tableDelete(table, "foo"); ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual({ table: { baz: "baz" } }); }); @@ -199,11 +217,11 @@ describe("LuaTableDelete extension", () => { export const table = { foo: "bar", baz: "baz" }; Table.tableDelete(table, "foo"); ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual({ table: { baz: "baz" } }); }); - test("LuaTableHasMethod method", () => { + test("LuaTableDeleteMethod method", () => { util.testModule` interface TableWithDelete { delete: LuaTableDeleteMethod; @@ -214,22 +232,129 @@ describe("LuaTableDelete extension", () => { table.set("bar", 12); table.delete("foo"); ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual({ table: { bar: 12 } }); }); +}); + +describe("LuaTableAddKey extension", () => { + test("LuaTableAddKey standalone function", () => { + util.testModule` + declare const tableAddKey: LuaTableAddKey<{}, string>; + export const table = { foo: "bar" }; + tableAddKey(table, "baz"); + ` + .withLanguageExtensions() + .expectToEqual({ table: { foo: "bar", baz: true } }); + }); + + test("LuaTableAddKey namespace function", () => { + util.testModule` + declare namespace Table { + export const tableAddKey: LuaTableAddKey<{}, string>; + } + export const table = { foo: "bar" }; + Table.tableAddKey(table, "baz"); + ` + .withLanguageExtensions() + .expectToEqual({ table: { foo: "bar", baz: true } }); + }); + + test("LuaTableAddKey method", () => { + util.testModule` + interface TableWithAddKey { + addKey: LuaTableAddKeyMethod; + } + export const table = {} as TableWithAddKey; + table.addKey("bar"); + ` + .withLanguageExtensions() + .expectToEqual({ table: { bar: true } }); + }); +}); + +describe("LuaIsEmpty extension", () => { + test("LuaIsEmpty standalone function", () => { + util.testModule` + declare const isTableEmpty: LuaTableIsEmpty<{}>; + + const table = { foo: "bar", baz: "baz" }; + const emptyTable = {}; + + export const result = [isTableEmpty(table), isTableEmpty(emptyTable)]; + ` + .withLanguageExtensions() + .expectToEqual({ result: [false, true] }); + }); + + test("LuaIsEmpty namespace function", () => { + util.testModule` + declare namespace Table { + export const isTableEmpty: LuaTableIsEmpty<{}>; + } + + const table = { foo: "bar", baz: "baz" }; + const emptyTable = {}; + + export const result = [Table.isTableEmpty(table), Table.isTableEmpty(emptyTable)]; + ` + .withLanguageExtensions() + .expectToEqual({ result: [false, true] }); + }); + + test("LuaTableIsEmptyMethod method", () => { + util.testModule` + interface TableWithIsEmpty { + isEmpty: LuaTableIsEmptyMethod; + set: LuaTableSetMethod; + } + const table = {} as TableWithIsEmpty; + table.set("foo", 42); + table.set("bar", 12); + + const emptyTable = {} as TableWithIsEmpty; + + export const result = [table.isEmpty(), emptyTable.isEmpty()]; + ` + .withLanguageExtensions() + .expectToEqual({ result: [false, true] }); + }); +}); +describe("Table extensions use as expression", () => { test.each([ - 'const foo = tableDelete({}, "foo");', - 'const foo = `${tableDelete({}, "foo")}`;', - 'declare function foo(arg: any): void; foo(tableDelete({}, "foo"));', - 'const foo = [tableDelete({}, "foo")];', - ])("LuaTableDelete invalid use as expression (%p)", expression => { + ["LuaTableAddKey<{}, string>", 'func({}, "foo")', undefined], + ["LuaTableAddKey<{}, string>", '"truthy" && func({}, "foo")', undefined], + ["LuaTableDelete<{}, string>", 'func({}, "foo")', true], + ["LuaTableDelete<{}, string>", '"truthy" && func({}, "foo")', true], + ["LuaTableSet<{}, string, number>", 'func({}, "foo", 3)', undefined], + ["LuaTableIsEmpty<{}>", "func({})", true], + ["LuaTableIsEmpty<{}>", 'func({ foo: "bar", baz: "baz" })', false], + ["LuaTableIsEmpty<{}>", '"truthy" && func({})', true], + ])("functions used as expression", (funcType, expression, value) => { util.testModule` - declare const tableDelete: LuaTableDelete<{}, string>; - ${expression} + declare const func: ${funcType} + export const result = ${expression} ` - .setOptions(tableProjectOptions) - .expectDiagnosticsToMatchSnapshot([invalidTableDeleteExpression.code]); + .withLanguageExtensions() + .setReturnExport("result") + .ignoreDiagnostics([2872 /* TS2872: This kind of expression is always truthy. */]) + .expectToEqual(value); + }); + + test.each([ + ["LuaTableDeleteMethod", 'tbl.func("foo")', true], + ["LuaTableSetMethod", 'tbl.func("foo", 3)', undefined], + ["LuaTableAddKeyMethod", 'tbl.func("foo")', undefined], + ["LuaTableIsEmpty<{}>", "tbl.func({})", true], + ])("methods used as expression", (funcType, expression, value) => { + util.testModule` + const tbl = {} as { func: ${funcType} } + export const result = ${expression} + ` + .withLanguageExtensions() + .setReturnExport("result") + .expectToEqual(value); }); }); @@ -240,7 +365,7 @@ describe("LuaTable extension interface", () => { tbl.set("foo", 3); return tbl.get("foo"); ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual(3); }); @@ -250,7 +375,7 @@ describe("LuaTable extension interface", () => { tbl.set("foo", 3); return tbl.get("foo"); ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual(3); }); @@ -263,7 +388,7 @@ describe("LuaTable extension interface", () => { } return fill(new LuaTable()).get("foo"); ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual(3); }); @@ -271,7 +396,8 @@ describe("LuaTable extension interface", () => { "LuaTable in strict mode does not accept key type that could be nil (%p)", keyType => { util.testExpression`new LuaTable<${keyType}, unknown>()` - .setOptions({ ...tableProjectOptions, strict: true }) + .withLanguageExtensions() + .setOptions({ strict: true }) .expectToHaveDiagnostics() .expectDiagnosticsToMatchSnapshot(); } @@ -285,7 +411,7 @@ describe("LuaTable extension interface", () => { tbl.set(key, 3); return tbl.get(key); ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual(3); }); @@ -296,7 +422,7 @@ describe("LuaTable extension interface", () => { tbl.set(3, "bar"); return tbl.length(); ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual(1); }); @@ -306,7 +432,7 @@ describe("LuaTable extension interface", () => { tbl.set(3, "foo"); return [tbl.has(1), tbl.has(3)]; ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual([false, true]); }); @@ -320,14 +446,94 @@ describe("LuaTable extension interface", () => { tbl.delete("foo"); return tbl; ` - .setOptions(tableProjectOptions) + .withLanguageExtensions() .expectToEqual({ baz: 5 }); }); + test("table isEmpty", () => { + util.testFunction` + const tbl = new LuaTable(); + tbl.set("foo", 1); + + const emptyTbl = new LuaTable(); + + return [tbl.isEmpty(), emptyTbl.isEmpty()]; + ` + .withLanguageExtensions() + .expectToEqual([false, true]); + }); + + test("table add", () => { + util.testFunction` + const tbl = new LuaSet(); + tbl.add("foo"); + return tbl + ` + .withLanguageExtensions() + .expectToEqual({ foo: true }); + }); + test.each(['new LuaTable().get("foo");', 'new LuaTable().set("foo", "bar");'])( "table immediate access (%p)", statement => { - util.testFunction(statement).setOptions(tableProjectOptions).expectToHaveNoDiagnostics(); + util.testFunction(statement).withLanguageExtensions().expectToHaveNoDiagnostics(); } ); + + test("table pairs iterate", () => { + util.testFunction` + const tbl = new LuaTable(); + tbl.set("foo", 1); + tbl.set("bar", 3); + tbl.set("baz", 5); + const results: Record = {}; + for (const [k, v] of tbl) { + results[k] = v; + } + return results; + ` + .withLanguageExtensions() + .expectToEqual({ foo: 1, bar: 3, baz: 5 }); + }); +}); + +test.each([ + [undefined, undefined], + ["new LuaSet()", true], +])("call on optional table with strictNullChecks (%s)", (value, expected) => { + util.testFunction` + function getFoo(): LuaSet | undefined { + return ${value} + } + const foo = getFoo() + foo?.add("foo") + return foo?.has("foo") + ` + .setOptions({ + strictNullChecks: true, + }) + .withLanguageExtensions() + .expectToEqual(expected); +}); + +describe("does not crash on invalid extension use", () => { + test("global function", () => { + util.testModule` + declare const op: LuaTableGet<{}, string, any> + op({}) + ` + .withLanguageExtensions() + .expectDiagnosticsToMatchSnapshot(); + }); + + test("method", () => { + util.testModule` + const left = {} as { + op: LuaTableGet<{}, string, any> + } + left.op() + ` + .withLanguageExtensions() + .expectDiagnosticsToMatchSnapshot(); + }); }); diff --git a/test/unit/language-extensions/vararg.spec.ts b/test/unit/language-extensions/vararg.spec.ts index f92cc0f96..d5aa9fc69 100644 --- a/test/unit/language-extensions/vararg.spec.ts +++ b/test/unit/language-extensions/vararg.spec.ts @@ -1,23 +1,18 @@ -import * as path from "path"; import * as util from "../../util"; -import * as tstl from "../../../src"; import { invalidVarargUse } from "../../../src/transformation/utils/diagnostics"; -const varargProjectOptions: tstl.CompilerOptions = { - types: [path.resolve(__dirname, "../../../language-extensions")], -}; - test.each([ 'const result = [...$vararg].join("")', 'let result: string; { result = [...$vararg].join(""); }', 'let result: string; if (true) { result = [...$vararg].join(""); }', 'let result: string; do { result = [...$vararg].join(""); } while (false);', + 'function foo(...args: unknown[]) { return args.join(""); } const result = foo(...$vararg);', ])("$vararg valid use (%p)", statement => { util.testModule` ${statement} export { result }; ` - .setOptions(varargProjectOptions) + .withLanguageExtensions() .setLuaFactory(code => `return (function(...) ${code} end)("A", "B", "C", "D")`) .tap(builder => expect(builder.getMainLuaCodeChunk()).not.toMatch("unpack")) .expectToEqual({ result: "ABCD" }); @@ -33,6 +28,28 @@ test.each([ util.testModule` ${statement} ` - .setOptions(varargProjectOptions) + .withLanguageExtensions() .expectDiagnosticsToMatchSnapshot([invalidVarargUse.code]); }); + +test("$vararg in bundle entry point", () => { + util.testModule` + export const result = [...$vararg].join(""); + ` + .setMainFileName("src/main.ts") + .setOptions({ rootDir: "src", luaBundle: "bundle.lua", luaBundleEntry: "src/main.ts" }) + .withLanguageExtensions() + .setLuaFactory(code => `return (function(...) ${code} end)("A", "B", "C", "D")`) + .expectToEqual({ result: "ABCD" }); +}); + +test("$vararg in bundle sub-module", () => { + util.testModule` + export { result } from "./module"; + ` + .setMainFileName("src/main.ts") + .addExtraFile("src/module.ts", 'export const result = [...$vararg].join("")') + .setOptions({ rootDir: "src", luaBundle: "bundle.lua", luaBundleEntry: "src/main.ts" }) + .withLanguageExtensions() + .expectToEqual({ result: "module" }); +}); diff --git a/test/unit/loops.spec.ts b/test/unit/loops.spec.ts index a3d4db239..e6ceab5db 100644 --- a/test/unit/loops.spec.ts +++ b/test/unit/loops.spec.ts @@ -1,5 +1,6 @@ import * as tstl from "../../src"; -import { forbiddenForIn, unsupportedForTarget } from "../../src/transformation/utils/diagnostics"; +import { LuaTarget } from "../../src"; +import { forbiddenForIn } from "../../src/transformation/utils/diagnostics"; import * as util from "../util"; test("while", () => { @@ -14,8 +15,9 @@ test("while", () => { `.expectToMatchJsResult(); }); -test("while with continue", () => { - util.testFunction` +util.testEachVersion( + "while with continue", + () => util.testFunction` let arrTest = [0, 1, 2, 3, 4]; let i = 0; while (i < arrTest.length) { @@ -36,11 +38,13 @@ test("while with continue", () => { i++; } return arrTest; - `.expectToMatchJsResult(); -}); + `, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) +); -test("dowhile with continue", () => { - util.testFunction` +util.testEachVersion( + "dowhile with continue", + () => util.testFunction` let arrTest = [0, 1, 2, 3, 4]; let i = 0; do { @@ -61,8 +65,9 @@ test("dowhile with continue", () => { i++; } while (i < arrTest.length) return arrTest; - `.expectToMatchJsResult(); -}); + `, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) +); test("for", () => { util.testFunction` @@ -85,8 +90,9 @@ test("for with expression", () => { `.expectToMatchJsResult(); }); -test("for with continue", () => { - util.testFunction` +util.testEachVersion( + "for with continue", + () => util.testFunction` let arrTest = [0, 1, 2, 3, 4]; for (let i = 0; i < arrTest.length; i++) { if (i % 2 == 0) { @@ -101,8 +107,9 @@ test("for with continue", () => { } } return arrTest; - `.expectToMatchJsResult(); -}); + `, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) +); test("forMirror", () => { util.testFunction` @@ -201,7 +208,7 @@ test.each([ }, ])("forin[Object] (%p)", ({ inp }) => { util.testFunctionTemplate` - let objTest = ${inp}; + let objTest: Record = ${inp}; for (let key in objTest) { objTest[key] = objTest[key] + 1; } @@ -211,14 +218,27 @@ test.each([ test("forin[Array]", () => { util.testFunction` - const array = []; + const array = [1,2,3]; for (const key in array) {} `.expectDiagnosticsToMatchSnapshot([forbiddenForIn.code]); }); -test.each([{ inp: { a: 0, b: 1, c: 2, d: 3, e: 4 } }])("forin with continue (%p)", ({ inp }) => { +const luaTargetsExceptJit = [ + LuaTarget.Lua50, + LuaTarget.Lua51, + LuaTarget.Lua52, + LuaTarget.Lua53, + LuaTarget.Lua54, + LuaTarget.Universal, +]; + +test.each( + luaTargetsExceptJit.flatMap(target => + [{ inp: { a: 0, b: 1, c: 2, d: 3, e: 4 } }].map(testCase => [target, testCase] as const) + ) +)("forin with continue (%s %p)", (luaTarget, { inp }) => { util.testFunctionTemplate` - let obj = ${inp}; + let obj: Record = ${inp}; for (let i in obj) { if (obj[i] % 2 == 0) { continue; @@ -227,7 +247,9 @@ test.each([{ inp: { a: 0, b: 1, c: 2, d: 3, e: 4 } }])("forin with continue (%p) obj[i] = 0; } return obj; - `.expectToMatchJsResult(); + ` + .setOptions({ luaTarget }) + .expectToMatchJsResult(); }); test.each([{ inp: [0, 1, 2] }])("forof (%p)", ({ inp }) => { @@ -279,6 +301,39 @@ test("forof destructing", () => { `.expectToMatchJsResult(); }); +test("forof destructing scope", () => { + util.testFunction` + let x = 7; + for (let [x] of [[1], [2], [3]]) { + x *= 2; + } + return x; + `.expectToMatchJsResult(); +}); + +// This catches the case where x is falsely seen as globally scoped and the 'local' is stripped out +test("forof destructing scope (global)", () => { + util.testModule` + let x = 7; + for (let [x] of [[1], [2], [3]]) { + x *= 2; + } + if (x !== 7) throw x; + `.expectNoExecutionError(); +}); + +test("forof nested destructing", () => { + util.testFunction` + const obj = { a: [3], b: [5] }; + let result = 0; + + for(const [k, [v]] of Object.entries(obj)){ + result += v; + } + return result; + `.expectToMatchJsResult(); +}); + test("forof destructing with existing variables", () => { const input = [ [1, 2], @@ -298,8 +353,9 @@ test("forof destructing with existing variables", () => { `.expectToMatchJsResult(); }); -test("forof with continue", () => { - util.testFunction` +util.testEachVersion( + "forof with continue", + () => util.testFunction` let testArr = [0, 1, 2, 3, 4]; let a = 0; for (let i of testArr) { @@ -317,8 +373,9 @@ test("forof with continue", () => { a++; } return testArr; - `.expectToMatchJsResult(); -}); + `, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) +); test("forof with iterator", () => { util.testFunction` @@ -467,8 +524,12 @@ describe("for...of empty destructuring", () => { }); }; - describe("declaration", () => declareTests("const ")); - describe("assignment", () => declareTests("")); + describe("declaration", () => { + declareTests("const "); + }); + describe("assignment", () => { + declareTests(""); + }); }); for (const testCase of [ @@ -478,19 +539,42 @@ for (const testCase of [ "for (const a in {}) { continue; }", "for (const a of []) { continue; }", ]) { + const expectContinueVariable: util.TapCallback = builder => + expect(builder.getMainLuaCodeChunk()).toMatch(/local __continue\d+/); + const expectContinueGotoLabel: util.TapCallback = builder => - expect(builder.getMainLuaCodeChunk()).toMatch("::__continue2::"); + expect(builder.getMainLuaCodeChunk()).toMatch(/::__continue\d+::/); + + const expectContinueStatement: util.TapCallback = builder => + expect(builder.getMainLuaCodeChunk()).toMatch("continue;"); util.testEachVersion(`loop continue (${testCase})`, () => util.testModule(testCase), { - [tstl.LuaTarget.Universal]: builder => builder.expectDiagnosticsToMatchSnapshot([unsupportedForTarget.code]), - [tstl.LuaTarget.Lua51]: builder => builder.expectDiagnosticsToMatchSnapshot([unsupportedForTarget.code]), + [tstl.LuaTarget.Universal]: expectContinueVariable, + [tstl.LuaTarget.Lua50]: expectContinueVariable, + [tstl.LuaTarget.Lua51]: expectContinueVariable, [tstl.LuaTarget.Lua52]: expectContinueGotoLabel, [tstl.LuaTarget.Lua53]: expectContinueGotoLabel, [tstl.LuaTarget.Lua54]: expectContinueGotoLabel, + [tstl.LuaTarget.Lua55]: expectContinueGotoLabel, [tstl.LuaTarget.LuaJIT]: expectContinueGotoLabel, + [tstl.LuaTarget.Luau]: () => expectContinueStatement, }); } +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1638 +test.each([tstl.LuaTarget.Universal, tstl.LuaTarget.Lua50, tstl.LuaTarget.Lua51])( + "no unreachable code when using continue for target %s (#1638)", + target => { + util.testFunction` + let i = 0; + while(++i < 10) continue; + return i; + ` + .setOptions({ luaTarget: target }) + .expectToMatchJsResult(); + } +); + test("do...while", () => { util.testFunction` let result = 0; @@ -555,3 +639,12 @@ test("for...in with pre-defined variable keeps last value", () => { // Need custom matcher because order is not guaranteed in neither JS nor Lua expect([keyX, keyFoo]).toContain(result); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1631 +test("loop variables should not be global (#1631)", () => { + const code = util.testModule` + for (let val = 0; val < 2; ++val) {} + `.getMainLuaCodeChunk(); + + expect(code).toContain("local val"); +}); diff --git a/test/unit/modules/__snapshots__/resolution.spec.ts.snap b/test/unit/modules/__snapshots__/resolution.spec.ts.snap index 4db74c57f..8c40101e5 100644 --- a/test/unit/modules/__snapshots__/resolution.spec.ts.snap +++ b/test/unit/modules/__snapshots__/resolution.spec.ts.snap @@ -2,9 +2,9 @@ exports[`doesn't resolve paths out of root dir: code 1`] = ` "local ____exports = {} -local module = require(\\"module\\") +local module = require("module") local ____ = module return ____exports" `; -exports[`doesn't resolve paths out of root dir: diagnostics 1`] = `"error TSTL: Could not resolve require path '../module' in file main.ts."`; +exports[`doesn't resolve paths out of root dir: diagnostics 1`] = `"error TSTL: Could not resolve lua source files for require path '../module' in file main.ts."`; diff --git a/test/unit/modules/modules.spec.ts b/test/unit/modules/modules.spec.ts index ffdc36ce9..1969806d0 100644 --- a/test/unit/modules/modules.spec.ts +++ b/test/unit/modules/modules.spec.ts @@ -94,8 +94,7 @@ test("Default Import and Export Expression", () => { test("Import and Export Assignment", () => { util.testModule` - // @ts-ignore - import * as m from "./module"; + import m = require("./module"); export const value = m; ` .setOptions({ module: ts.ModuleKind.CommonJS }) @@ -280,3 +279,36 @@ test("namespace export with unsafe Lua name", () => { .addExtraFile("module.ts", moduleFile) .expectToMatchJsResult(); }); + +test("import expression", () => { + util.testModule` + let result; + import("./module").then(m => { result = m.foo(); }); + export { result }; + ` + .addExtraFile("module.ts", 'export function foo() { return "foo"; }') + .setOptions({ module: ts.ModuleKind.ESNext }) + .expectToEqual({ result: "foo" }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1572 +test("correctly exports @compileMembersOnly enums (#1572)", () => { + util.testModule` + export { val } from "./otherfile"; + ` + .addExtraFile( + "otherfile.ts", + ` + // Would put this in the main file, but we cannot transfer enum types over lua/js boundary + // but we still need to have an exported enum, hence it is in another file + /** @compileMembersOnly */ + export enum MyEnum { + A = 0, + B = 1, + C = 2 + } + export const val = MyEnum.B | MyEnum.C; + ` + ) + .expectToEqual({ val: 3 }); +}); diff --git a/test/unit/modules/resolution.spec.ts b/test/unit/modules/resolution.spec.ts index 791ae06b5..c6f97d5f9 100644 --- a/test/unit/modules/resolution.spec.ts +++ b/test/unit/modules/resolution.spec.ts @@ -1,12 +1,15 @@ +import * as path from "path"; import * as ts from "typescript"; -import { couldNotResolveRequire } from "../../../src/transpilation/diagnostics"; +import { couldNotResolveRequire, emitPathCollision } from "../../../src/transpilation/diagnostics"; import * as util from "../../util"; const requireRegex = /require\("(.*?)"\)/; -const expectToRequire = (expected: string): util.TapCallback => builder => { - const [, requiredPath] = builder.getMainLuaCodeChunk().match(requireRegex) ?? []; - expect(requiredPath).toBe(expected); -}; +const expectToRequire = + (expected: string): util.TapCallback => + builder => { + const [, requiredPath] = builder.getMainLuaCodeChunk().match(requireRegex) ?? []; + expect(requiredPath).toBe(expected); + }; test.each([ { @@ -85,6 +88,37 @@ test("doesn't resolve paths out of root dir", () => { .expectDiagnosticsToMatchSnapshot([couldNotResolveRequire.code]); }); +test("resolves non-standard requires", () => { + const { transpiledFiles } = util.testModule` + export * from "./externalLua"; + ` + .addExtraFile("externalLua.d.ts", "export const foo = 3;") + .addExtraFile( + "externalLua.lua", + ` + require("requiredLuaFile1") -- standard + require('requiredLuaFile2') -- single quote + require'requiredLuaFile3' -- no parentheses + require"requiredLuaFile4" -- no parentheses double quote + require "requiredLuaFile5" -- no parentheses and space + require "requiredLua'File6" -- no parentheses and space + require 'requiredLua"File7' -- no parentheses and space + ` + ) + .addExtraFile("requiredLuaFile1.lua", "") + .addExtraFile("requiredLuaFile2.lua", "") + .addExtraFile("requiredLuaFile3.lua", "") + .addExtraFile("requiredLuaFile4.lua", "") + .addExtraFile("requiredLuaFile5.lua", "") + .addExtraFile("requiredLua'File6.lua", "") + .addExtraFile('requiredLua"File7.lua', "") + .expectToHaveNoDiagnostics() + .getLuaResult(); + + // Expect main.lua, externalLua.lua and all 7 required lua files in there + expect(transpiledFiles.map(f => f.outPath)).toHaveLength(9); +}); + test.each([ { declarationStatement: ` @@ -133,6 +167,52 @@ test.each([ .tap(expectToRequire(expectedPath)); }); +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1445 +// Can't test this via execution because the test harness uses package.preload +// instead of real filesystem resolution, so require() always finds the module +// regardless of output path. We check the output path directly instead. +test("dots in directory names are replaced with underscores in output", () => { + const { transpiledFiles } = util.testModule` + import { answer } from "./Foo.Bar"; + export const result = answer; + ` + .addExtraFile("Foo.Bar/index.ts", "export const answer = 42;") + .setOptions({ rootDir: "." }) + .getLuaResult(); + + const dottedFile = transpiledFiles.find(f => f.lua?.includes("answer = 42")); + expect(dottedFile).toBeDefined(); + expect(dottedFile!.outPath).toContain(path.join("Foo_Bar", "index.lua")); + expect(dottedFile!.outPath).not.toContain("Foo.Bar"); +}); + +test("dots in file names are replaced with underscores in output", () => { + const { transpiledFiles } = util.testModule` + import { answer } from "./foo.test"; + export const result = answer; + ` + .addExtraFile("foo.test.ts", "export const answer = 42;") + .setOptions({ rootDir: "." }) + .getLuaResult(); + + const dottedFile = transpiledFiles.find(f => f.lua?.includes("answer = 42")); + expect(dottedFile).toBeDefined(); + expect(dottedFile!.outPath).toContain("foo_test.lua"); + expect(dottedFile!.outPath).not.toContain("foo.test"); +}); + +test("dots in paths that collide with existing paths produce a diagnostic", () => { + util.testModule` + import { a } from "./Foo.Bar"; + import { b } from "./Foo_Bar"; + export const result = a + b; + ` + .addExtraFile("Foo.Bar/index.ts", "export const a = 1;") + .addExtraFile("Foo_Bar/index.ts", "export const b = 2;") + .setOptions({ rootDir: "." }) + .expectToHaveDiagnostics([emitPathCollision.code]); +}); + test("import = require", () => { util.testModule` import foo = require("./foo/bar"); diff --git a/test/unit/namespaces.spec.ts b/test/unit/namespaces.spec.ts index 762152d67..7c98e7ef9 100644 --- a/test/unit/namespaces.spec.ts +++ b/test/unit/namespaces.spec.ts @@ -1,15 +1,5 @@ import * as util from "../util"; -test("legacy internal module syntax", () => { - util.testModule` - module Foo { - export const foo = "bar"; - } - - export const foo = Foo.foo; - `.expectToMatchJsResult(); -}); - test("global scoping", () => { util.testFunction("return a.foo();") .setTsHeader('namespace a { export function foo() { return "bar"; } }') @@ -42,7 +32,7 @@ test("context in namespace function", () => { util.testModule` namespace a { export const foo = "foo"; - export function bar() { return this.foo + "bar"; } + export function bar(this: typeof a) { return this.foo + "bar"; } } export const result = a.bar(); @@ -144,3 +134,15 @@ test("enum in a namespace", () => { export const result = Test.TestEnum.A; `.expectToMatchJsResult(); }); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1431 +test("nested namespaces (#1431)", () => { + util.testModule` + namespace Foo.Bar {} + namespace Foo.Bar { + export const baz = 32; + } + + export { Foo } + `.expectToMatchJsResult(); +}); diff --git a/test/unit/nullishCoalescing.spec.ts b/test/unit/nullishCoalescing.spec.ts index 67dbc1cec..56db55332 100644 --- a/test/unit/nullishCoalescing.spec.ts +++ b/test/unit/nullishCoalescing.spec.ts @@ -1,18 +1,26 @@ import * as util from "../util"; test.each(["null", "undefined"])("nullish-coalesing operator returns rhs", nullishValue => { - util.testExpression`${nullishValue} ?? "Hello, World!"`.expectToMatchJsResult(); + util.testExpression`${nullishValue} ?? "Hello, World!"` + .ignoreDiagnostics([2871 /* TS2871: This expression is always nullish. */]) + .expectToMatchJsResult(); }); test.each([3, "foo", {}, [], true, false])("nullish-coalesing operator returns lhs", value => { - util.testExpression`${util.formatCode(value)} ?? "Hello, World!"`.expectToMatchJsResult(); + util.testExpression`${util.formatCode(value)} ?? "Hello, World!"` + .ignoreDiagnostics([ + 2869 /* TS2869: Right operand of ?? is unreachable because the left operand is never nullish. */, + ]) + .expectToMatchJsResult(); }); test.each(["any", "unknown"])("nullish-coalesing operator with any/unknown type", type => { util.testFunction` const unknownType = false as ${type}; return unknownType ?? "This should not be returned!"; - `.expectToMatchJsResult(); + ` + .ignoreDiagnostics([2871 /* TS2871: This expression is always nullish. */]) + .expectToMatchJsResult(); }); test.each(["boolean | string", "number | false", "undefined | true"])( @@ -38,5 +46,30 @@ test("nullish-coalescing operator with side effect rhs", () => { let i = 0; const incI = () => ++i; return [i, undefined ?? incI(), i]; + ` + .ignoreDiagnostics([2871 /* TS2871: This expression is always nullish. */]) + .expectToMatchJsResult(); +}); + +test("nullish-coalescing operator with vararg", () => { + util.testFunction` + + function foo(...args: any[]){ + return args + } + function bar(...args: any[]) { + let x: boolean | undefined = false + const y = x ?? foo(...args) + } + return bar(1, 2) + `.expectToMatchJsResult(); +}); + +test.each([true, false, null])("nullish-coalescing with generic lhs (%p)", lhs => { + util.testFunction` + function coalesce(a: A, b: B) { + return a ?? b + } + return coalesce(${lhs}, "wasNull") `.expectToMatchJsResult(); }); diff --git a/test/unit/objectLiteral.spec.ts b/test/unit/objectLiteral.spec.ts index 819143e1f..16e7582d0 100644 --- a/test/unit/objectLiteral.spec.ts +++ b/test/unit/objectLiteral.spec.ts @@ -63,3 +63,66 @@ test.each(['{x: "foobar"}.x', '{x: "foobar"}["x"]', '{x: () => "foobar"}.x()', ' util.testExpression(expression).expectToMatchJsResult(); } ); + +describe("noSelf in functions", () => { + test("Explicit this: void parameter", () => { + // language=TypeScript + util.testFunction` + const obj: Record string> = { + method(a) { + return a; + }, + func: function(a) { + return a; + }, + arrow: (a) => { + return a; + } + }; + return [obj.method("a") ?? "nil", obj.func("b") ?? "nil", obj.arrow("c") ?? "nil"]; + `.expectToMatchJsResult(); + }); + + test("No self annotation", () => { + // language=TypeScript + util.testFunction` + const obj: Record string> = { + method(a) { + return a; + }, + func: function(a) { + return a; + }, + arrow: (a) => { + return a; + } + }; + return [obj.method("a") ?? "nil", obj.func("b") ?? "nil", obj.arrow("c") ?? "nil"]; + `.expectToMatchJsResult(); + }); + + test("individual function types", () => { + // language=TypeScript + util.testFunction` + interface FunctionContainer { + method: (this: void, a: string) => string + func: (this: void, a: string) => string + arrow: (this: void, a: string) => string + } + + const obj: FunctionContainer = { + method(a) { + return a + }, + func: function(a) { + return a + }, + arrow: (a) => { + return a + } + } + + return [obj.method("a") ?? "nil", obj.func("b") ?? "nil", obj.arrow("c") ?? "nil"]; + `.expectToMatchJsResult(); + }); +}); diff --git a/test/unit/optionalChaining.spec.ts b/test/unit/optionalChaining.spec.ts index 33d0802db..10de7e792 100644 --- a/test/unit/optionalChaining.spec.ts +++ b/test/unit/optionalChaining.spec.ts @@ -1,9 +1,460 @@ -import { optionalChainingNotSupported } from "../../src/transformation/utils/diagnostics"; +import { notAllowedOptionalAssignment } from "../../src/transformation/utils/diagnostics"; import * as util from "../util"; -test("Diagnostic optional chaining is not supported yet", () => { +test.each(["null", "undefined", '{ foo: "foo" }'])("optional chaining (%p)", value => { util.testFunction` - let func = (value: number) => value != 0 ? {value} : undefined; - return func(1)?.value; - `.expectToHaveDiagnostics([optionalChainingNotSupported.code]); + const obj = ${value} as {foo: string} | null | undefined; + return obj?.foo; + ` + .expectToMatchJsResult() + .expectLuaToMatchSnapshot(); + // should use "and" expression +}); + +test("long optional chain", () => { + util.testFunction` + const a = { b: { c: { d: { e: { f: "hello!"}}}}}; + return a.b?.c?.d.e.f; + ` + .expectToMatchJsResult() + .expectLuaToMatchSnapshot(); + // should use "and" expression +}); + +test.each(["undefined", "{}", "{ foo: {} }", "{ foo: {bar: 'baz'}}"])("nested optional chaining (%p)", value => { + util.testFunction` + const obj = ${value} as { foo?: { bar?: string } } | undefined; + return obj?.foo?.bar; + `.expectToMatchJsResult(); +}); + +test.each(["undefined", "{}", "{ foo: {} }", "{ foo: {bar: 'baz'}}"])( + "nested optional chaining combined with coalescing (%p)", + value => { + util.testFunction` + const obj = ${value} as { foo?: { bar?: string } } | undefined; + return obj?.foo?.bar ?? "not found"; + `.expectToMatchJsResult(); + } +); + +test.each(["[1, 2, 3, 4]", "undefined"])("optional array access (%p)", value => { + util.testFunction` + const arr: number[] | undefined = ${value}; + return arr?.[2]; + `.expectToMatchJsResult(); +}); + +test.each(["[1, [2, [3, [4, 5]]]]", "[1, [2, [3, undefined]]] ", "[1, undefined]"])( + "optional element access nested (%p)", + value => { + util.testFunction` + const arr: [number, [number, [number, [number, number] | undefined]]] | [number, undefined] = ${value}; + return arr[1]?.[1][1]?.[0]; + `.expectToMatchJsResult(); + } +); + +test.each(["{ }", "{ a: { } }", "{ a: { b: [{ c: 10 }] } }"])( + "optional nested element access properties (%p)", + value => { + util.testFunction` + const obj: {a?: {b?: Array<{c: number }> } } = ${value}; + return [obj["a"]?.["b"]?.[0]?.["c"] ?? "not found", obj["a"]?.["b"]?.[2]?.["c"] ?? "not found"]; + `.expectToMatchJsResult(); + } +); + +test("optional element function calls", () => { + util.testFunction` + const obj: { value: string; foo?(this: void, v: number): number; bar?(this: void, v: number): number; } = { + value: "foobar", + foo: (v: number) => v + 10 + } + const fooKey = "foo"; + const barKey = "bar"; + return obj[barKey]?.(5) ?? obj[fooKey]?.(15); + ` + .expectToMatchJsResult() + .expectLuaToMatchSnapshot(); + // should still use "and" statement, as functions have no self +}); + +test("unused expression", () => { + util.testFunction` + const obj = { foo: "bar" }; + obj?.foo; + ` + .expectToHaveNoDiagnostics() + .expectNoExecutionError() + .expectLuaToMatchSnapshot(); + // should use if statement, as result is not used +}); + +test("unused call", () => { + util.testFunction` + let result + const obj = { + foo() { + result = "bar" + } + }; + obj?.foo(); + return result; + ` + .expectToMatchJsResult() + .expectLuaToMatchSnapshot(); + // should use if statement, as result is not used +}); + +test.each(["undefined", "{ foo: (v: any)=>v }"])("with preceding statements on right side", value => { + util.testFunction` + let i = 0 + const obj: any = ${value}; + return {result: obj?.foo(i++), i}; + ` + .expectToMatchJsResult() + .expectLuaToMatchSnapshot(); + // should use if statement, as there are preceding statements +}); + +// unused, with preceding statements on right side +test.each(["undefined", "{ foo(val: any) {return val} }"])( + "unused result with preceding statements on right side", + value => { + util.testFunction` + let i = 0 + const obj = ${value} as any; + obj?.foo(i++); + return i + ` + .expectToHaveNoDiagnostics() + .expectLuaToMatchSnapshot(); + // should use if statement, as there are preceding statements + } +); + +test.each(["undefined", "{ foo(v: any) { return v} }"])( + "with preceding statements on right side modifying left", + value => { + util.testFunction` + let i = 0 + let obj: any = ${value}; + function bar() { + if(obj) obj.foo = undefined + obj = undefined + return 1 + } + + return {result: obj?.foo(bar(), i++), obj, i} + ` + .expectToMatchJsResult() + .expectLuaToMatchSnapshot(); + // should use if statement, as there are preceding statements + } +); + +test("does not suppress error if left side is false", () => { + const result = util.testFunction` + const obj: any = false + return obj?.foo + `.getLuaExecutionResult(); + expect(result).toBeInstanceOf(util.ExecutionError); +}); + +describe("optional access method calls", () => { + test("element access call", () => { + util.testFunction` + const obj: { value: string; foo?(prefix: string): string; bar?(prefix: string): string; } = { + value: "foobar", + foo(prefix: string) { return prefix + this.value; } + } + const fooKey = "foo"; + const barKey = "bar"; + return obj[barKey]?.("bar?") ?? obj[fooKey]?.("foo?"); + `.expectToMatchJsResult(); + }); + + test("property access call", () => { + util.testFunction` + const obj: { value: string; foo?(prefix: string): string; bar?(prefix: string): string; } = { + value: "foobar", + foo(prefix: string) { return prefix + this.value; } + } + return obj.foo?.("bar?") ?? obj.bar?.("foo?"); + `.expectToMatchJsResult(); + }); + + test("nested optional element access call", () => { + util.testFunction` + const obj: { value: string; foo?(prefix: string): string; bar?(prefix: string): string; } = { + value: "foobar", + foo(prefix: string) { return prefix + this.value; } + } + const fooKey = "foo"; + const barKey = "bar"; + return obj?.[barKey]?.("bar?") ?? obj?.[fooKey]?.("foo?"); + `.expectToMatchJsResult(); + }); + + test("nested optional property access call", () => { + util.testFunction` + const obj: { value: string; foo?(prefix: string): string; bar?(prefix: string): string; } = { + value: "foobar", + foo(prefix: string) { return prefix + this.value; } + } + return obj?.foo?.("bar?") ?? obj?.bar?.("foo?"); + `.expectToMatchJsResult(); + }); +}); + +test("no side effects", () => { + util.testFunction` + function getFoo(): { foo: number } | undefined { + return { foo: 42 }; + } + let barCalls = 0; + function getBar(): { bar: number } | undefined { + barCalls += 1; + return undefined; + } + const result = getFoo()?.foo ?? getBar()?.bar; + return { result, barCalls }; + `.expectToMatchJsResult(); +}); + +// Test for https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1044 +test("does not crash when incorrectly used in assignment (#1044)", () => { + const { diagnostics } = util.testFunction` + foo?.bar = "foo"; + `.getLuaResult(); + + expect(diagnostics.find(d => d.code === notAllowedOptionalAssignment.code)).toBeDefined(); +}); + +describe("optional chaining function calls", () => { + test.each(["() => 4", "undefined"])("stand-alone optional function (%p)", value => { + util.testFunction` + const f = (${value}) as (() => number) | undefined; + return f?.(); + `.expectToMatchJsResult(); + }); + + test("methods present", () => { + util.testFunction` + const objWithMethods = { + foo() { + return 3; + }, + bar(this: void) { + return 5; + } + }; + + return [objWithMethods?.foo(), objWithMethods?.bar()]; + `.expectToMatchJsResult(); + }); + + test("object with method can be undefined", () => { + util.testFunction` + const objWithMethods = undefined as { foo: () => number, bar: (this: void) => number } | undefined; + return [objWithMethods?.foo() ?? "no foo", objWithMethods?.bar() ?? "no bar"]; + `.expectToMatchJsResult(); + }); + + test("nested optional method call", () => { + util.testFunction` + type typeWithOptional = { a?: { b: { c: () => number } } }; + + const objWithMethods: typeWithOptional = {}; + const objWithMethods2: typeWithOptional = { a: { b: { c: () => 4 } } }; + + return { + expectNil: objWithMethods.a?.b.c(), + expectFour: objWithMethods2.a?.b.c() + }; + `.expectToMatchJsResult(); + }); + + test("methods are undefined", () => { + util.testFunction` + const objWithMethods: { foo?: () => number, bar?: (this: void) => number } = {}; + return [objWithMethods.foo?.() ?? "no foo", objWithMethods.bar?.() ?? "no bar"]; + `.expectToMatchJsResult(); + }); + + test("optional method of optional method result", () => { + util.testFunction` + const obj: { a?: () => {b: {c?: () => number }}} = {}; + const obj2: { a?: () => {b: {c?: () => number }}} = { a: () => ({b: {}})}; + const obj3: { a?: () => {b: {c?: () => number }}} = { a: () => ({b: { c: () => 5 }})}; + + return [obj.a?.().b.c?.() ?? "nil", obj2.a?.().b.c?.() ?? "nil", obj3.a?.().b.c?.() ?? "nil"]; + `.expectToMatchJsResult(); + }); + + // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1085 + test("incorrect type, method is not a function", () => { + util.testFunction` + const obj: any = {}; + obj?.foo(); + `.expectToEqual(new util.ExecutionError("attempt to call a nil value (method 'foo')")); + }); + + describe("builtins", () => { + test.each([ + ["undefined", undefined], + ["{foo: 0}", true], + ])("LuaTable: %p", (expr, value) => { + util.testFunction` + const table: LuaTable = ${expr} as any + const bar = table?.has("foo") + return bar + ` + .withLanguageExtensions() + .expectToEqual(value); + }); + + test.each(["undefined", "foo"])("Function call: %p", e => { + util.testFunction` + const result = [] + function foo(this: unknown, arg: unknown) { + return [this, arg] + } + const bar = ${e} as typeof foo | undefined + return bar?.call(1, 2) + `.expectToMatchJsResult(); + }); + + test.each([undefined, "[1, 2, 3, 4]"])("Array: %p", expr => { + util.testFunction` + const value = ${expr} as any[] | undefined; + return value?.map(x=>x+1); + `.expectToMatchJsResult(); + }); + }); + + test.each([true, false])("Default call context, strict %s", strict => { + util.testFunction` + function func(this: unknown, arg: unknown) { + return [this === globalThis ? "_G" : this === undefined ? "nil" : "neither", arg]; + } + let i = 0 + const result = func?.(i++); + ` + .setOptions({ + strict, + }) + .expectToMatchJsResult(); + }); +}); + +describe("Unsupported optional chains", () => { + test("Language extensions", () => { + util.testModule` + new LuaTable().has?.(3) + ` + .withLanguageExtensions() + .expectDiagnosticsToMatchSnapshot(); + }); + + test("Builtin prototype method", () => { + util.testModule` + [1,2,3,4].forEach?.(()=>{}) + `.expectDiagnosticsToMatchSnapshot(); + }); + + test("Builtin global method", () => { + util.testModule` + Number?.("3") + `.expectDiagnosticsToMatchSnapshot(); + }); + + test("Builtin global property", () => { + util.testModule` + console?.log("3") + ` + .setOptions({ + lib: ["lib.esnext.d.ts", "lib.dom.d.ts"], + }) + .expectDiagnosticsToMatchSnapshot(); + }); + + test("Compile members only", () => { + util.testFunction` + /** @compileMembersOnly */ + enum TestEnum { + A = 0, + B = 2, + C, + D = "D", + } + + TestEnum?.B + `.expectDiagnosticsToMatchSnapshot(); + }); +}); + +describe("optional delete", () => { + test("successful", () => { + util.testFunction` + const table: { bar?: number } = { + bar: 3 + } + return [delete table?.bar, table] + `.expectToMatchJsResult(); + }); + + test("unsuccessful", () => { + util.testFunction` + const table : { + bar?: number + } = {} + return [delete table?.bar, table] + `.expectToMatchJsResult(); + }); + + test("delete on undefined", () => { + util.testFunction` + const table = undefined as { + bar?: number + } | undefined; + return [delete table?.bar, table ?? "nil"] + `.expectToMatchJsResult(); + }); +}); + +describe("Non-null chain", () => { + test("Single non-null chain", () => { + util.testFunction` + const foo = {a: { b: 3} } + return foo?.a!.b + `.expectToMatchJsResult(); + }); + + test("Many non-null chains", () => { + util.testFunction` + const foo = {a: { b: 3} } + return foo?.a!!!.b!!! + `.expectToMatchJsResult(); + }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1585 +test("optional chaining of super call (#1585)", () => { + util.testFunction` + class Parent { + private name = "foo"; + M2() { return this.name; } + } + + class Child extends Parent { + M2() { + return super.M2?.(); + } + } + + const c = new Child(); + return c.M2(); + `.expectToMatchJsResult(); }); diff --git a/test/unit/overloads.spec.ts b/test/unit/overloads.spec.ts index d18daa000..85752a64a 100644 --- a/test/unit/overloads.spec.ts +++ b/test/unit/overloads.spec.ts @@ -67,8 +67,8 @@ test("overload method2", () => { test("constructor1", () => { util.testFunction` class myclass { - num: number; - str: string; + num?: number; + str?: string; constructor(def: number); constructor(def: string); @@ -88,8 +88,8 @@ test("constructor1", () => { test("constructor2", () => { util.testFunction` class myclass { - num: number; - str: string; + num?: number; + str?: string; constructor(def: number); constructor(def: string); diff --git a/test/unit/precedingStatements.spec.ts b/test/unit/precedingStatements.spec.ts new file mode 100644 index 000000000..6dd8c6473 --- /dev/null +++ b/test/unit/precedingStatements.spec.ts @@ -0,0 +1,659 @@ +import * as util from "../util"; + +const shortCircuitTests: Array<{ operator: string; testValue: unknown }> = [ + { operator: "&&", testValue: true }, + { operator: "&&", testValue: false }, + { operator: "&&", testValue: null }, + { operator: "&&=", testValue: true }, + { operator: "&&=", testValue: false }, + { operator: "&&=", testValue: null }, + { operator: "||", testValue: true }, + { operator: "||", testValue: false }, + { operator: "||", testValue: null }, + { operator: "||=", testValue: true }, + { operator: "||=", testValue: false }, + { operator: "||=", testValue: null }, + { operator: "??", testValue: true }, + { operator: "??", testValue: false }, + { operator: "??", testValue: null }, + { operator: "??=", testValue: true }, + { operator: "??=", testValue: false }, + { operator: "??=", testValue: null }, +]; + +test.each(shortCircuitTests)("short circuit operator (%p)", ({ operator, testValue }) => { + util.testFunction` + let x: unknown = ${testValue}; + let y = 1; + const z = x ${operator} y++; + return {x, y, z}; + `.expectToMatchJsResult(); +}); + +test.each(shortCircuitTests)("short circuit operator on property (%p)", ({ operator, testValue }) => { + util.testFunction` + let x: { foo: unknown } = { foo: ${testValue} }; + let y = 1; + const z = x.foo ${operator} y++; + return {x: x.foo, y, z}; + `.expectToMatchJsResult(); +}); + +test.each([true, false])("ternary operator (%p)", condition => { + util.testFunction` + let a = 0, b = 0; + let condition: boolean = ${condition}; + const c = condition ? a++ : b++; + return [a, b, c]; + `.expectToMatchJsResult(); +}); + +describe("execution order", () => { + const sequenceTests = [ + "i++, i", + "i, i++, i, i++", + "...a", + "i, ...a", + "...a, i", + "i, ...a, i++, i, ...a", + "i, ...a, i++, i, ...a, i", + "...[1, i++, 2]", + "...[1, i++, 2], i++", + "i, ...[1, i++, 2]", + "i, ...[1, i++, 2], i", + "i, ...[1, i++, 2], i++", + "i, ...[1, i++, 2], i++, ...[3, i++, 4]", + "i, ...a, i++, ...[1, i++, 2], i, i++, ...a", + "i, inc(), i++", + "i, ...[1, i++, inc(), 2], i++", + "i, ...'foo', i++", + "i, ...([1, i++, 2] as any), i++", + ]; + + test.each(sequenceTests)("array literal ([%p])", sequence => { + util.testFunction` + const a = [7, 8, 9]; + let i = 0; + function inc() { ++i; return i; } + return [${sequence}]; + `.expectToMatchJsResult(); + }); + + test.each(sequenceTests)("function arguments (foo(%p))", sequence => { + util.testFunction` + const a = [7, 8, 9]; + let i = 0; + function inc() { ++i; return i; } + function foo(...args: unknown[]) { return args; } + return foo(${sequence}); + `.expectToMatchJsResult(); + }); + + test.each([ + "{a: i, b: i++}", + "{a: i, b: i++, c: i}", + "{a: i, ...{b: i++}, c: i}", + "{a: i, ...o, b: i++}", + "{a: i, ...[i], b: i++}", + "{a: i, ...[i++], b: i++}", + "{a: i, ...o, b: i++, ...[i], ...{c: i++}, d: i++}", + ])("object literal (%p)", literal => { + util.testFunction` + const o = {a: "A", b: "B", c: "C"}; + let i = 0; + const literal: Record = ${literal}; + const result: Record = {}; + (Object.keys(result) as Array).forEach( + key => { result[key.toString()] = literal[key]; } + ); + return result; + ` + // TS2783: duplicate property in spread — intentional, testing execution order + .ignoreDiagnostics([2783]) + .expectToMatchJsResult(); + }); + + test("object literal with computed property names", () => { + util.testFunction` + let i = "A"; + const o = {[i += "B"]: i += "C", [i += "D"]: i += "E"}; + return [i, o]; + `.expectToMatchJsResult(); + }); + + test("comma operator", () => { + util.testFunction` + let a = 0, b = 0, c = 0; + const d = (a++, b += a, c += b); + return [a, b, c, d]; + `.expectToMatchJsResult(); + }); + + test("template expression", () => { + util.testFunction` + let i = 0; + return \`\${i}, \${i++}, \${i}\`; + `.expectToMatchJsResult(); + }); + + test("tagged template literal", () => { + util.testFunction` + let i = 0; + + function func(strings: TemplateStringsArray, ...expressions: any[]) { + const x = i > 0 ? "a" : "b"; + return { strings: [x, ...strings], raw: strings.raw, expressions }; + } + + return func\`hello \${i} \${i++}\`; + `.expectToMatchJsResult(); + }); + + test("binary operators", () => { + util.testFunction` + let i = 0; + return i + i++; + `.expectToMatchJsResult(); + }); + + test("index expression", () => { + util.testFunction` + let i = 0; + const a = [["A1", "A2"], ["B1", "B2"]]; + const result = a[i][i++]; + return [result, i]; + `.expectToMatchJsResult(); + }); + + test("void expression", () => { + util.testFunction` + function foo(x: number, y: number, z: number | undefined) { + return z; + } + let i = 0; + const result = foo(i, i++, void(i++)); + return {result, i}; + `.expectToMatchJsResult(); + }); +}); + +describe("assignment execution order", () => { + test("index assignment statement", () => { + util.testFunction` + let i = 0; + const a = [4, 5]; + a[i] = i++; + return [a, i]; + `.expectToMatchJsResult(); + }); + + test("index assignment expression", () => { + util.testFunction` + let i = 0; + const a = [9, 8]; + const result = a[i] = i++; + return [result, a, i]; + `.expectToMatchJsResult(); + }); + + test("indirect index assignment statement", () => { + util.testFunction` + let i = 0; + const a = [9, 8]; + const b = [7, 6]; + function foo(x: number) { return (x > 0) ? b : a; } + foo(i)[i] = i++; + return [a, b, i]; + `.expectToMatchJsResult(); + }); + + test("indirect index assignment expression", () => { + util.testFunction` + let i = 0; + const a = [9, 8]; + const b = [7, 6]; + function foo(x: number) { return (x > 0) ? b : a; } + const result = foo(i)[i] = i++; + return [result, a, b, i]; + `.expectToMatchJsResult(); + }); + + test("indirect property assignment statement", () => { + util.testFunction` + const a = {value: 10}; + const b = {value: 11}; + let i = 0; + function foo(x: number) { return (x > 0) ? b : a; } + foo(i).value = i++; + return [a, b, i]; + `.expectToMatchJsResult(); + }); + + test("indirect property assignment expression", () => { + util.testFunction` + const a = {value: 10}; + const b = {value: 11}; + let i = 0; + function foo(x: number) { return (x > 0) ? b : a; } + const result = foo(i).value = i++; + return [result, a, b, i]; + `.expectToMatchJsResult(); + }); + + test("compound index assignment statement", () => { + util.testFunction` + let i = 0; + const a = [9, 8]; + a[i] += i++; + return [a, i]; + `.expectToMatchJsResult(); + }); + + test("compound index assignment expression", () => { + util.testFunction` + let i = 0; + const a = [9, 8]; + const result = a[i] += i++; + return [result, a, i]; + `.expectToMatchJsResult(); + }); + + test("compound indirect index assignment statement", () => { + util.testFunction` + let i = 0; + const a = [9, 8]; + const b = [7, 6]; + function foo(x: number) { return (x > 0) ? b : a; } + foo(i)[i] += i++; + return [a, b, i]; + `.expectToMatchJsResult(); + }); + + test("compound indirect index assignment expression", () => { + util.testFunction` + let i = 1; + const a = [9, 8]; + const b = [7, 6]; + function foo(x: number) { return (x > 0) ? b : a; } + const result = foo(i)[i] += i++; + return [result, a, b, i]; + `.expectToMatchJsResult(); + }); + + test("array destructuring assignment statement", () => { + util.testFunction` + const a = [10, 9, 8, 7, 6, 5]; + let i = 0; + [a[i], a[i++]] = [i++, i++]; + return [a, i]; + `.expectToMatchJsResult(); + }); + + test("array destructuring assignment expression", () => { + util.testFunction` + const a = [10, 9, 8, 7, 6, 5]; + let i = 0; + const result = [a[i], a[i++]] = [i++, i++]; + return [a, i, result]; + `.expectToMatchJsResult(); + }); + + test("array destructuring assignment statement with default", () => { + util.testFunction` + const a = [10, 9, 8, 7, 6, 5]; + let i = 0; + [a[i] = i++, a[i++]] = [i++, i++]; + return [a, i]; + `.expectToMatchJsResult(); + }); + + test("array destructuring assignment expression with default", () => { + util.testFunction` + const a = [10, 9, 8, 7, 6, 5]; + let i = 0; + const result = [a[i] = i++, a[i++]] = [i++, i++]; + return [a, i, result]; + `.expectToMatchJsResult(); + }); + + test("array destructuring assignment statement with spread", () => { + util.testFunction` + let i = 0; + let a: number[][] = [[9, 9, 9], [9, 9, 9], [9, 9, 9]]; + [a[0][i], ...a[i++]] = [i++, i++]; + return [a, i]; + `.expectToMatchJsResult(); + }); + + test("array destructuring assignment expression with spread", () => { + util.testFunction` + let i = 0; + let a: number[][] = [[9, 9, 9], [9, 9, 9], [9, 9, 9]]; + const result = [a[0][i], ...a[i++]] = [i++, i++]; + return [a, i, result]; + `.expectToMatchJsResult(); + }); + + test("object destructuring assignment statement", () => { + util.testFunction` + let s = "A"; + const o: Record = {ABCDEFG: "success", result: ""}; + function c(x: string) { s = x + "C"; return o; } + function g(x: string) { s = x + "G"; return o; } + function e(x: string) { s = x + "E"; return s; } + ({ [e(s += "D")]: g(s += "F").result } = c(s += "B")); + return [s, o]; + `.expectToMatchJsResult(); + }); + + test("object destructuring assignment statement with default", () => { + util.testFunction` + let s = "A"; + const o: Record = {ABCDEFGHIJ: "success", result: ""}; + function c(x: string) { s = x + "C"; return o; } + function g(x: string) { s = x + "G"; return o; } + function i(x: string) { s = x + "I"; return o; } + function e(x: string): any { s = x + "E"; return undefined; } + ({ [e(s += "D")]: g(s += "F").result = i(s += "H")[s += "J"] } = c(s += "B")); + return [o, s]; + `.expectToMatchJsResult(); + }); + + test("object destructuring assignment expression", () => { + util.testFunction` + let s = "A"; + const o: Record = {ABCDEFG: "success", result: ""}; + function c(x: string) { s = x + "C"; return o; } + function g(x: string) { s = x + "G"; return o; } + function e(x: string) { s = x + "E"; return s; } + const result = ({ [e(s += "D")]: g(s += "F").result } = c(s += "B")); + return [s, o, result]; + `.expectToMatchJsResult(); + }); + + test("object destructuring assignment expression with default", () => { + util.testFunction` + let s = "A"; + const o: Record = {ABCDEFGHIJ: "success", result: ""}; + function c(x: string) { s = x + "C"; return o; } + function g(x: string) { s = x + "G"; return o; } + function i(x: string) { s = x + "I"; return o; } + function e(x: string): any { s = x + "E"; return undefined; } + const result = ({ [e(s += "D")]: g(s += "F").result = i(s += "H")[s += "J"] } = c(s += "B")); + return [o, s, result]; + `.expectToMatchJsResult(); + }); + + test("object destructuring declaration", () => { + util.testFunction` + let s = "A"; + const o: Record = {ABCDE: "success"}; + function c(x: string) { s = x + "C"; return o; } + function e(x: string) { s = x + "E"; return s; } + const { [e(s += "D")]: result } = c(s += "B"); + return [result, s]; + `.expectToMatchJsResult(); + }); + + test("object destructuring declaration with default", () => { + util.testFunction` + let s = "A"; + const o: Record = {ABCDEFGH: "success"}; + function c(x: string) { s = x + "C"; return o; } + function g(x: string) { s = x + "G"; return o; } + function e(x: string): any { s = x + "E"; return undefined; } + const { [e(s += "D")]: result = g(s += "F")[s += "H"]} = c(s += "B"); + return [result, s]; + `.expectToMatchJsResult(); + }); + + test("call expression", () => { + util.testFunction` + let i = 1; + function a(x: number) { return x * 10; } + function b(x: number) { return x * 100; } + function foo(x: number) { return (x > 0) ? b : a; } + const result = foo(i)(i++); + return [result, i]; + `.expectToMatchJsResult(); + }); + + test("call expression (function modified)", () => { + util.testFunction` + let i = 1; + let foo = (x: null, y: number) => { return y; }; + function changeFoo() { + foo = (x: null, y: number) => { return y * 10; }; + return null; + } + const result = foo(changeFoo(), i++); + return [result, i]; + `.expectToMatchJsResult(); + }); + + test("method call expression (method modified)", () => { + util.testFunction` + let i = 1; + let o = { + val: 3, + foo(x: null, y: number) { return y + this.val; } + }; + function changeFoo(this: void) { + o.foo = function(x: null, y: number) { return (y + this.val) * 10; }; + return null; + } + const result = o.foo(changeFoo(), i++); + return [result, i]; + `.expectToMatchJsResult(); + }); + + test("method element access call expression (method modified)", () => { + util.testFunction` + let i = 1; + let o = { + val: 3, + foo(x: null, y: number) { return y + this.val; } + }; + function changeFoo(this: void) { + o.foo = function(x: null, y: number) { return (y + this.val) * 10; }; + return null; + } + function getFoo() { return "foo" as const; } + function getO() { return o; } + const result = getO()[getFoo()](changeFoo(), i++); + return [result, i]; + `.expectToMatchJsResult(); + }); + + test("method call expression (object modified)", () => { + util.testFunction` + let i = 1; + let o = { + val: 3, + foo(x: null, y: number) { return y + this.val; } + }; + function changeO(this: void) { + o = { + val: 5, + foo: function(x: null, y: number) { return (y + this.val) * 10; } + }; + return null; + } + const result = o.foo(changeO(), i++); + return [result, i]; + `.expectToMatchJsResult(); + }); + + test("method element access call expression (object modified)", () => { + util.testFunction` + let i = 1; + let o = { + val: 3, + foo(x: null, y: number) { return y + this.val; } + }; + function changeO(this: void) { + o = { + val: 5, + foo: function(x: null, y: number) { return (y + this.val) * 10; } + }; + return null; + } + function getFoo() { return "foo" as const; } + function getO() { return o; } + const result = getO()[getFoo()](changeO(), i++); + return [result, i]; + `.expectToMatchJsResult(); + }); + + test("array method call", () => { + util.testFunction` + let a = [7]; + let b = [9]; + function foo(x: number) { return (x > 0) ? b : a; } + let i = 0; + foo(i).push(i, i++, i); + return [a, b, i]; + `.expectToMatchJsResult(); + }); + + test("function method call", () => { + util.testFunction` + let o = {val: 3}; + let a = function(this: typeof o, x: number) { return this.val + x; }; + let b = function(this: typeof o, x: number) { return (this.val + x) * 10; }; + function foo(x: number) { return (x > 0) ? b : a; } + let i = 0; + const result = foo(i).call(o, i++); + return [result, i]; + `.expectToMatchJsResult(); + }); + + test("string method call", () => { + util.testFunction` + function foo(x: number) { return (x > 0) ? "foo" : "bar"; } + let i = 0; + const result = foo(i).substr(++i); + return [result, i]; + `.expectToMatchJsResult(); + }); + + test("new call", () => { + util.testFunction` + class A { public val = 3; constructor(x: number) { this.val += x; } }; + class B { public val = 5; constructor(x: number) { this.val += (x * 10); } }; + function foo(x: number) { return (x > 0) ? B : A; } + let i = 0; + const result = new (foo(i))(i++).val; + return [result, i]; + `.expectToMatchJsResult(); + }); +}); + +describe("loop expressions", () => { + test("while loop", () => { + util.testFunction` + let i = 0, j = 0; + while (i++ < 5) { + ++j; + if (j >= 10) { + break; + } + } + return [i, j]; + `.expectToMatchJsResult(); + }); + + test("for loop", () => { + util.testFunction` + let j = 0; + for (let i = 0; i++ < 5;) { + ++j; + if (j >= 10) { + break; + } + } + return j; + `.expectToMatchJsResult(); + }); + + test("do while loop", () => { + util.testFunction` + let i = 0, j = 0; + do { + ++j; + if (j >= 10) { + break; + } + } while (i++ < 5); + return [i, j]; + `.expectToMatchJsResult(); + }); + + test("do while loop scoping", () => { + util.testFunction` + let x = 0; + let result = 0; + do { + let x = -10; + ++result; + } while (x++ >= 0 && result < 2); + return result; + `.expectToMatchJsResult(); + }); +}); + +test("switch", () => { + util.testFunction` + let i = 0; + let x = 0; + let result = ""; + switch (x) { + case i++: + result = "test"; + break; + case i++: + } + return [i, result]; + `.expectToMatchJsResult(); +}); + +test("else if", () => { + util.testFunction` + let i = 0; + if (i++ === 0) { + } else if (i++ === 1) { + } + return i; + `.expectToMatchJsResult(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1208 +test("class member initializers", () => { + util.testFunction` + class MyClass { + myField = false ?? true; + constructor(public foo: number = 0 ?? 5) {} + } + const inst = new MyClass(); + return [inst.myField, inst.foo]; + ` + .ignoreDiagnostics([ + 2869 /* TS2869: Right operand of ?? is unreachable because the left operand is never nullish. */, + ]) + .expectToMatchJsResult(); +}); + +test("class member initializers in extended class", () => { + util.testFunction` + class A {} + class MyClass extends A { + myField = false ?? true; + } + const inst = new MyClass(); + return inst.myField; + ` + .ignoreDiagnostics([ + 2869 /* TS2869: Right operand of ?? is unreachable because the left operand is never nullish. */, + ]) + .expectToMatchJsResult(); +}); diff --git a/test/unit/printer/__snapshots__/semicolons.spec.ts.snap b/test/unit/printer/__snapshots__/semicolons.spec.ts.snap index af1deb303..ac34dbdf8 100644 --- a/test/unit/printer/__snapshots__/semicolons.spec.ts.snap +++ b/test/unit/printer/__snapshots__/semicolons.spec.ts.snap @@ -3,9 +3,9 @@ exports[`semicolon insertion ("{}") 1`] = ` "local ____exports = {} function ____exports.__main(self) - local result = \\"\\" + local result = "" local function foo(self) - result = \\"foo\\" + result = "foo" end do end @@ -18,9 +18,9 @@ return ____exports" exports[`semicolon insertion ("const a = 1; const b = a;") 1`] = ` "local ____exports = {} function ____exports.__main(self) - local result = \\"\\" + local result = "" local function foo(self) - result = \\"foo\\" + result = "foo" end local a = 1 local b = a; @@ -33,9 +33,9 @@ return ____exports" exports[`semicolon insertion ("const a = 1; let b: number; b = a;") 1`] = ` "local ____exports = {} function ____exports.__main(self) - local result = \\"\\" + local result = "" local function foo(self) - result = \\"foo\\" + result = "foo" end local a = 1 local b @@ -49,9 +49,9 @@ return ____exports" exports[`semicolon insertion ("function bar() {} bar();") 1`] = ` "local ____exports = {} function ____exports.__main(self) - local result = \\"\\" + local result = "" local function foo(self) - result = \\"foo\\" + result = "foo" end local function bar(self) end diff --git a/test/unit/printer/deadCodeAfterBreak.spec.ts b/test/unit/printer/deadCodeAfterBreak.spec.ts new file mode 100644 index 000000000..fe1b9073c --- /dev/null +++ b/test/unit/printer/deadCodeAfterBreak.spec.ts @@ -0,0 +1,93 @@ +import * as tstl from "../../../src"; +import * as util from "../../util"; + +// In Lua 5.0, 5.1, and LuaJIT, break must be the last statement in a block. +// Any code after break is a syntax error (e.g. `while true do break; local b = 8 end` +// fails with "'end' expected near 'local'"). Lua 5.2+ relaxed this restriction. +// TSTL should strip dead code after break on all targets to avoid these errors. + +function expectNoDeadCode(builder: util.TestBuilder) { + const lua = builder.getMainLuaCodeChunk(); + expect(lua).not.toContain("local b = 8"); +} + +const affectedVersions: Record void) | boolean> = { + [tstl.LuaTarget.Universal]: false, + [tstl.LuaTarget.Lua50]: builder => builder.tap(expectNoDeadCode).expectToMatchJsResult(), + [tstl.LuaTarget.Lua51]: builder => builder.tap(expectNoDeadCode).expectToMatchJsResult(), + [tstl.LuaTarget.Lua52]: false, + [tstl.LuaTarget.Lua53]: false, + [tstl.LuaTarget.Lua54]: false, + [tstl.LuaTarget.Lua55]: false, + [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectNoDeadCode), + [tstl.LuaTarget.Luau]: false, +}; + +util.testEachVersion( + "for dead code after break", + () => util.testFunction` + let result = 0; + for (let i = 0; i < 10; i++) { result = i; break; const b = 8; } + return result; + `, + affectedVersions +); + +util.testEachVersion( + "for..in dead code after break", + () => util.testFunction` + let result = ""; + for (let a in {"a": 5, "b": 8}) { result = a; break; const b = 8; } + return result; + `, + affectedVersions +); + +util.testEachVersion( + "for..of dead code after break", + () => util.testFunction` + let result = 0; + for (let a of [1,2,4]) { result = a; break; const b = 8; } + return result; + `, + affectedVersions +); + +util.testEachVersion( + "while dead code after break", + () => util.testFunction` + let result = "done"; + while (true) { break; const b = 8; } + return result; + `, + affectedVersions +); + +util.testEachVersion( + "switch dead code after break", + () => util.testFunction` + let result = "none"; + switch ("abc" as string) { + case "def": + result = "def"; + break; + let abc = 4; + case "abc": + result = "abc"; + break; + let def = 6; + } + return result; + `, + affectedVersions +); + +util.testEachVersion( + "do-while dead code after break", + () => util.testFunction` + let result = "done"; + do { break; const b = 8; } while (true); + return result; + `, + affectedVersions +); diff --git a/test/unit/printer/semicolons.spec.ts b/test/unit/printer/semicolons.spec.ts index addb66e53..7be461d0f 100644 --- a/test/unit/printer/semicolons.spec.ts +++ b/test/unit/printer/semicolons.spec.ts @@ -10,6 +10,7 @@ test.each(["const a = 1; const b = a;", "const a = 1; let b: number; b = a;", "{ (undefined || foo)(); return result; ` + .ignoreDiagnostics([2873 /* TS2873: This kind of expression is always falsy. */]) .expectToMatchJsResult() .expectLuaToMatchSnapshot(); } diff --git a/test/unit/printer/sourcemaps.spec.ts b/test/unit/printer/sourcemaps.spec.ts index 371c50df9..478f482d6 100644 --- a/test/unit/printer/sourcemaps.spec.ts +++ b/test/unit/printer/sourcemaps.spec.ts @@ -1,7 +1,9 @@ -import { Position, SourceMapConsumer } from "source-map"; +import { SourceMapConsumer } from "source-map"; import * as tstl from "../../../src"; +import { LuaTarget, transpileString } from "../../../src"; import { couldNotResolveRequire } from "../../../src/transpilation/diagnostics"; import * as util from "../../util"; +import { lineAndColumnOf } from "./utils"; test.each([ { @@ -87,7 +89,7 @@ test.each([ assertPatterns: [ { luaPattern: "Bar = __TS__Class()", typeScriptPattern: "class Bar" }, { luaPattern: "Bar.name =", typeScriptPattern: "class Bar" }, - { luaPattern: "__TS__ClassExtends", typeScriptPattern: "extends" }, + { luaPattern: "__TS__ClassExtends(", typeScriptPattern: "extends" }, // find use of function, not import { luaPattern: "Foo", typeScriptPattern: "Foo" }, { luaPattern: "function Bar.prototype.____constructor", typeScriptPattern: "constructor" }, ], @@ -162,25 +164,35 @@ test.each([ }); test.each([ - { fileName: "/proj/foo.ts", config: {} }, + { + fileName: "/proj/foo.ts", + config: {}, + expectedSourcePath: "foo.ts", // ts and lua will be emitted to same directory + }, { fileName: "/proj/src/foo.ts", - config: { outDir: "/proj/dst" }, + config: { + outDir: "/proj/dst", + }, + expectedSourcePath: "../src/foo.ts", // path from proj/dst outDir to proj/src/foo.ts }, { fileName: "/proj/src/foo.ts", config: { rootDir: "/proj/src", outDir: "/proj/dst" }, + expectedSourcePath: "../src/foo.ts", // path from proj/dst outDir to proj/src/foo.ts }, { fileName: "/proj/src/sub/foo.ts", config: { rootDir: "/proj/src", outDir: "/proj/dst" }, + expectedSourcePath: "../../src/sub/foo.ts", // path from proj/dst/sub outDir to proj/src/sub/foo.ts }, { fileName: "/proj/src/sub/main.ts", - config: { rootDir: "/proj/src", outDir: "/proj/dst", sourceRoot: "bin" }, - fullSource: "bin/proj/src/sub/main.ts", + config: { rootDir: "/proj/src", outDir: "/proj/dst", sourceRoot: "bin/binsub/binsubsub" }, + expectedSourcePath: "../../src/sub/main.ts", // path from proj/dst/sub outDir to proj/src/sub/foo.ts + fullSource: "bin/src/sub/main.ts", }, -])("Source map has correct sources (%p)", async ({ fileName, config, fullSource }) => { +])("Source map has correct sources (%p)", async ({ fileName, config, fullSource, expectedSourcePath }) => { const file = util.testModule` const foo = "foo" ` @@ -190,11 +202,11 @@ test.each([ const sourceMap = JSON.parse(file.luaSourceMap); expect(sourceMap.sources).toHaveLength(1); - expect(sourceMap.sources[0]).toBe(fileName); + expect(sourceMap.sources[0]).toBe(expectedSourcePath); const consumer = await new SourceMapConsumer(file.luaSourceMap); expect(consumer.sources).toHaveLength(1); - expect(consumer.sources[0]).toBe(fullSource ?? fileName); + expect(consumer.sources[0]).toBe(fullSource ?? expectedSourcePath); }); test.each([ @@ -272,6 +284,24 @@ test("sourceMapTraceback saves sourcemap in _G", () => { } }); +test("sourceMapTraceback gives traceback", () => { + const builder = util.testFunction` + return (debug.traceback as (this: void)=>string)(); + `.setOptions({ sourceMapTraceback: true }); + + const traceback = builder.getLuaExecutionResult(); + expect(traceback).toEqual(expect.any(String)); +}); + +test("inline sourceMapTraceback gives traceback", () => { + const builder = util.testFunction` + return (debug.traceback as (this: void)=>string)(); + `.setOptions({ sourceMapTraceback: true, luaLibImport: tstl.LuaLibImportKind.Inline }); + + const traceback = builder.getLuaExecutionResult(); + expect(traceback).toEqual(expect.any(String)); +}); + test("Inline sourcemaps", () => { const code = ` function abc() { @@ -298,26 +328,23 @@ test("Inline sourcemaps", () => { expect(inlineSourceMap).toBe(file.luaSourceMap); }); -// Helper functions - -function lineAndColumnOf(text: string, pattern: string): Position { - const pos = text.indexOf(pattern); - if (pos === -1) { - return { line: -1, column: -1 }; - } - - const lineLengths = text.split("\n").map(s => s.length); - - let totalPos = 0; - for (let line = 1; line <= lineLengths.length; line++) { - // Add + 1 for the removed \n - const lineLength = lineLengths[line - 1] + 1; - if (pos < totalPos + lineLength) { - return { line, column: pos - totalPos }; +test("loadstring sourceMapTraceback gives traceback", () => { + const loadStrCode = transpileString( + `function bar() { + const trace = (debug.traceback as (this: void)=>string)(); + return trace; } + return bar();`, + { sourceMapTraceback: true, luaTarget: LuaTarget.Lua51 } + ).file?.lua; - totalPos += lineLengths[line - 1] + 1; - } + const builder = util.testModule` + const luaCode = \`${loadStrCode}\`; + return loadstring(luaCode, "foo.lua")(); + ` + .setTsHeader("declare function loadstring(this: void, string: string, chunkname: string): () => unknown") + .setOptions({ sourceMapTraceback: true, luaTarget: LuaTarget.Lua51 }); - return { line: -1, column: -1 }; -} + const traceback = builder.getLuaExecutionResult(); + expect(traceback).toContain("foo.ts"); +}); diff --git a/test/unit/printer/utils.ts b/test/unit/printer/utils.ts new file mode 100644 index 000000000..a9f58b44f --- /dev/null +++ b/test/unit/printer/utils.ts @@ -0,0 +1,25 @@ +// Helper functions + +import { Position } from "source-map"; + +export function lineAndColumnOf(text: string, pattern: string): Position { + const pos = text.indexOf(pattern); + if (pos === -1) { + return { line: -1, column: -1 }; + } + + const lineLengths = text.split("\n").map(s => s.length); + + let totalPos = 0; + for (let line = 1; line <= lineLengths.length; line++) { + // Add + 1 for the removed \n + const lineLength = lineLengths[line - 1] + 1; + if (pos < totalPos + lineLength) { + return { line, column: pos - totalPos }; + } + + totalPos += lineLengths[line - 1] + 1; + } + + return { line: -1, column: -1 }; +} diff --git a/test/unit/spread.spec.ts b/test/unit/spread.spec.ts index a395ef2bd..93936f29b 100644 --- a/test/unit/spread.spec.ts +++ b/test/unit/spread.spec.ts @@ -1,4 +1,3 @@ -import * as path from "path"; import * as tstl from "../../src"; import * as util from "../util"; import { formatCode } from "../util"; @@ -23,15 +22,16 @@ describe.each(["function call", "array literal"] as const)("in %s", kind => { util.testExpression(factory(expression)).expectToMatchJsResult(); }); - test.each(arrayLiteralCases)("of tuple return call (%p)", expression => { + test.each(arrayLiteralCases)("of multi return call (%p)", expression => { util.testFunction` - /** @tupleReturn */ function tuple(...args: any[]) { - return args; + return $multi(...args); } return ${factory(`...tuple(${expression})`)}; - `.expectToMatchJsResult(); + ` + .withLanguageExtensions() + .expectToMatchJsResult(); }); test("of multiple string literals", () => { @@ -76,10 +76,13 @@ describe("in function call", () => { { [tstl.LuaTarget.Universal]: builder => builder.tap(expectLualibUnpack).expectToMatchJsResult(), [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua50]: builder => builder.tap(expectUnpack).expectToMatchJsResult(), [tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack).expectToMatchJsResult(), [tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), [tstl.LuaTarget.Lua54]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), + [tstl.LuaTarget.Lua55]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), + [tstl.LuaTarget.Luau]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), } ); }); @@ -88,10 +91,13 @@ describe("in array literal", () => { util.testEachVersion(undefined, () => util.testExpression`[...[0, 1, 2]]`, { [tstl.LuaTarget.Universal]: builder => builder.tap(expectLualibUnpack).expectToMatchJsResult(), [tstl.LuaTarget.LuaJIT]: builder => builder.tap(expectUnpack), + [tstl.LuaTarget.Lua50]: builder => builder.tap(expectUnpack).expectToMatchJsResult(), [tstl.LuaTarget.Lua51]: builder => builder.tap(expectUnpack).expectToMatchJsResult(), [tstl.LuaTarget.Lua52]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), [tstl.LuaTarget.Lua53]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), [tstl.LuaTarget.Lua54]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), + [tstl.LuaTarget.Lua55]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), + [tstl.LuaTarget.Luau]: builder => builder.tap(expectTableUnpack).expectToMatchJsResult(), }); test("of array literal /w OmittedExpression", () => { @@ -110,6 +116,17 @@ describe("in object literal", () => { "{ ...{ x: false }, x: true }", "{ ...{ x: false }, x: false, ...{ x: true } }", ])("of object literal (%p)", expression => { + util.testExpression(expression) + .ignoreDiagnostics([2783]) // duplicate property in spread — intentional, testing spread override behavior + .expectToMatchJsResult(); + }); + + test.each([ + "{ ...((false && { a: 1 }) as any) }", + "{ ...((true && { a: 1 }) as any) }", + "{ a: 1, ...((false && { b: 2 }) as any) }", + "{ ...(null as any), ...(undefined as any) }", + ])("of short-circuited operand (%p)", expression => { util.testExpression(expression).expectToMatchJsResult(); }); @@ -133,58 +150,55 @@ describe("in object literal", () => { }); describe("vararg spread optimization", () => { - test("basic use", () => { - util.testFunction` + util.testEachVersion( + "basic use", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { return pick(...args); } - return test("a", "b", "c"); - ` - .expectLuaToMatchSnapshot() - .expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("body-less arrow function", () => { - util.testFunction` + util.testEachVersion( + "body-less arrow function", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } const test = (...args: string[]) => pick(...args); - return test("a", "b", "c"); - ` - .expectLuaToMatchSnapshot() - .expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("if statement", () => { - util.testFunction` + util.testEachVersion( + "if statement", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { if (true) { return pick(...args); } } - return test("a", "b", "c"); - ` - .expectLuaToMatchSnapshot() - .expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("loop statement", () => { - util.testFunction` + util.testEachVersion( + "loop statement", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { do { return pick(...args); } while (false); } - return test("a", "b", "c"); - ` - .expectLuaToMatchSnapshot() - .expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("block statement", () => { - util.testFunction` + util.testEachVersion( + "block statement", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { let result: string; @@ -193,14 +207,13 @@ describe("vararg spread optimization", () => { } return result; } - return test("a", "b", "c"); - ` - .expectLuaToMatchSnapshot() - .expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("finally clause", () => { - util.testFunction` + util.testEachVersion( + "finally clause", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { try { @@ -210,11 +223,9 @@ describe("vararg spread optimization", () => { return pick(...args); } } - return test("a" ,"b", "c"); - ` - .expectLuaToMatchSnapshot() - .expectToMatchJsResult(); - }); + return test("a" ,"b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); test("$multi", () => { util.testFunction` @@ -226,26 +237,71 @@ describe("vararg spread optimization", () => { } return test("a" ,"b", "c"); ` - .setOptions({ types: [path.resolve(__dirname, "../../language-extensions")] }) + .withLanguageExtensions() .expectLuaToMatchSnapshot() .expectToEqual("b"); }); + + util.testEachVersion( + "curry", + () => util.testFunction` + function test(fn: (...args: A) => void, ...args: A) { + return fn(...args); + } + return test((arg: string) => arg, "foobar");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); + + util.testEachVersion( + "curry with indirect type", + () => util.testFunction` + function test(obj: {fn: (...args: A) => void}, ...args: A) { + const fn = obj.fn; + return fn(...args); + } + return test({fn: (arg: string) => arg}, "foobar");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); + + util.testEachVersion( + "function type declared inside scope", + () => util.testFunction` + function test(...args: A) { + const fn: (...args: A) => A[0] = (...args) => args[0]; + return fn(...args); + } + test("foobar");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); + + util.testEachVersion( + "With cast", + () => util.testFunction` + function pick(...args: any[]) { return args[1]; } + function testany>(...args: Parameters) { + return pick(...(args as any[])); + } + return test<(...args: string[])=>void>("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); }); describe("vararg spread de-optimization", () => { - test("array modification", () => { - util.testFunction` + util.testEachVersion( + "array modification", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { args[1] = "foobar"; return pick(...args); } - return test("a", "b", "c"); - `.expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("array modification in hoisted function", () => { - util.testFunction` + util.testEachVersion( + "array modification in hoisted function", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { hoisted(); @@ -253,12 +309,13 @@ describe("vararg spread de-optimization", () => { function hoisted() { args[1] = "foobar"; } return result; } - return test("a", "b", "c"); - `.expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("array modification in secondary hoisted function", () => { - util.testFunction` + util.testEachVersion( + "array modification in secondary hoisted function", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { function triggersHoisted() { hoisted(); } @@ -267,103 +324,112 @@ describe("vararg spread de-optimization", () => { function hoisted() { args[1] = "foobar"; } return result; } - return test("a", "b", "c"); - `.expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); }); describe("vararg spread in IIFE", () => { - test("comma operator", () => { - util.testFunction` + util.testEachVersion( + "comma operator", + () => util.testFunction` function dummy() { return "foobar"; } function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { return (dummy(), pick(...args)); } - return test("a", "b", "c"); - `.expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("assignment expression", () => { - util.testFunction` + util.testEachVersion( + "assignment expression", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { let x: string; return (x = pick(...args)); } - return test("a", "b", "c"); - `.expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("destructured assignment expression", () => { - util.testFunction` + util.testEachVersion( + "destructured assignment expression", + () => util.testFunction` function pick(...args: string[]) { return [args[1]]; } function test(...args: string[]) { let x: string; return ([x] = pick(...args)); } - return test("a", "b", "c"); - `.expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("property-access assignment expression", () => { - util.testFunction` + util.testEachVersion( + "property-access assignment expression", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { let x: {val?: string} = {}; return (x.val = pick(...args)); } - return test("a", "b", "c"); - `.expectToMatchJsResult(); - }); + return test("a", "b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("binary compound assignment", () => { - util.testFunction` + util.testEachVersion( + "binary compound assignment", + () => util.testFunction` function pick(...args: number[]) { return args[1]; } function test(...args: number[]) { let x = 1; return x += pick(...args); } - return test(1, 2, 3); - `.expectToMatchJsResult(); - }); + return test(1, 2, 3);`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("postfix unary compound assignment", () => { - util.testFunction` + util.testEachVersion( + "postfix unary compound assignment", + () => util.testFunction` function pick(...args: number[]) { return args[1]; } function test(...args: number[]) { let x = [7, 8, 9]; return x[pick(...args)]++; } - return test(1, 2, 3); - `.expectToMatchJsResult(); - }); + return test(1, 2, 3);`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("prefix unary compound assignment", () => { - util.testFunction` + util.testEachVersion( + "prefix unary compound assignment", + () => util.testFunction` function pick(...args: number[]) { return args[1]; } function test(...args: number[]) { let x = [7, 8, 9]; return ++x[pick(...args)]; } - return test(1, 2, 3); - `.expectToMatchJsResult(); - }); + return test(1, 2, 3);`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("try clause", () => { - util.testFunction` + util.testEachVersion( + "try clause", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { try { return pick(...args) } catch {} } - return test("a" ,"b", "c"); - `.expectToMatchJsResult(); - }); + return test("a" ,"b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("catch clause", () => { - util.testFunction` + util.testEachVersion( + "catch clause", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { try { @@ -372,62 +438,153 @@ describe("vararg spread in IIFE", () => { return pick(...args) } } - return test("a" ,"b", "c"); - `.expectToMatchJsResult(); - }); + return test("a" ,"b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("class expression", () => { - util.testFunction` + util.testEachVersion( + "class expression", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } function test(...args: string[]) { const fooClass = class Foo { foo = pick(...args); }; return new fooClass().foo; } - return test("a" ,"b", "c"); - `.expectToMatchJsResult(); - }); + return test("a" ,"b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("self-referencing function expression", () => { - util.testFunction` + util.testEachVersion( + "self-referencing function expression", + () => util.testFunction` function pick(...args: string[]) { return args[1]; } const test = function testName(...args: string[]) { return \`\${typeof testName}:\${pick(...args)}\`; } - return test("a" ,"b", "c"); - `.expectToMatchJsResult(); - }); + return test("a" ,"b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("method indirect access (access args)", () => { - util.testFunction` + util.testEachVersion( + "method indirect access (access args)", + () => util.testFunction` const obj = { $method: () => obj.arg, arg: "foobar" }; function getObj(...args: string[]) { obj.arg = args[1]; return obj; } function test(...args: string[]) { return getObj(...args).$method(); } - return test("a" ,"b", "c"); - `.expectToMatchJsResult(); - }); + return test("a" ,"b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("method indirect access (method args)", () => { - util.testFunction` + util.testEachVersion( + "method indirect access (method args)", + () => util.testFunction` const obj = { $pick: (...args: string[]) => args[1] }; function getObj() { return obj; } function test(...args: string[]) { return getObj().$pick(...args); } - return test("a" ,"b", "c"); - `.expectToMatchJsResult(); - }); + return test("a" ,"b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); - test("tagged template method indirect access", () => { - util.testFunction` + util.testEachVersion( + "tagged template method indirect access", + () => util.testFunction` const obj = { $tag: (t: TemplateStringsArray, ...args: string[]) => args[1] }; function getObj() { return obj; } function pick(...args: string[]): string { return args[1]; } function test(...args: string[]) { return getObj().$tag\`FOO\${pick(...args)}BAR\`; } - return test("a" ,"b", "c"); - `.expectToMatchJsResult(); - }); + return test("a" ,"b", "c");`, + util.expectEachVersionExceptJit(builder => builder.expectToMatchJsResult()) + ); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1244 +test.each(["pairs", "ipairs"])("can spread %s (#1244)", func => { + util.testFunction` + const arr = ["a", "b", "c"]; + return [...${func}(arr)]; + ` + .withLanguageExtensions() + .setTsHeader( + ` + declare function ipairs(this: void, t: T): LuaIterable]>>; + declare function pairs(this: void, t: T): LuaIterable]>>; + ` + ) + .expectToEqual([ + [1, "a"], + [2, "b"], + [3, "c"], + ]); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1244 +test.each(["LuaTable", "LuaMap"])("can spread %s with pairs (#1244)", type => { + const result: Array<[string, string]> = util.testFunction` + const tbl = new ${type}(); + tbl.set("foo", "bar"); + tbl.set("fizz", "buzz"); + return [...pairs(tbl)]; + ` + .withLanguageExtensions() + .setTsHeader( + "declare function pairs(this: void, t: T): LuaIterable]>>;" + ) + .getLuaExecutionResult(); + + // We don't know the order so match like this + expect(result).toHaveLength(2); + expect(result).toContainEqual(["foo", "bar"]); + expect(result).toContainEqual(["fizz", "buzz"]); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1384 +test.each(["LuaTable", "LuaMap"])("can spread %s (#1384)", type => { + const result: Array<[string, string]> = util.testFunction` + const tbl = new ${type}(); + tbl.set("foo", "bar"); + tbl.set("fizz", "buzz"); + return [...tbl]; + ` + .withLanguageExtensions() + .getLuaExecutionResult(); + + // We don't know the order so match like this + expect(result).toHaveLength(2); + expect(result).toContainEqual(["foo", "bar"]); + expect(result).toContainEqual(["fizz", "buzz"]); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1384 +test("can spread LuaSet (#1384)", () => { + const result: Array<[string, string]> = util.testFunction` + const tbl = new LuaSet(); + tbl.add("foo"); + tbl.add("bar"); + return [...tbl]; + ` + .withLanguageExtensions() + .getLuaExecutionResult(); + + // We don't know the order so match like this + expect(result).toHaveLength(2); + expect(result).toContain("foo"); + expect(result).toContain("bar"); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1426 +test.each([true, false])("spread a decorator or array union type (#1426)", choice => { + util.testFunction` + function* things() { + yield "a"; + yield "b" + } + const c: boolean = ${util.formatCode(choice)}; + return [...(c ? things() : ["c", "d"])]; + `.expectToMatchJsResult(); }); diff --git a/test/unit/switch.spec.ts b/test/unit/switch.spec.ts index dd3e2a09c..1b8410a68 100644 --- a/test/unit/switch.spec.ts +++ b/test/unit/switch.spec.ts @@ -1,5 +1,3 @@ -import * as tstl from "../../src"; -import { unsupportedForTarget } from "../../src/transformation/utils/diagnostics"; import * as util from "../util"; test.each([0, 1, 2, 3])("switch (%p)", inp => { @@ -139,7 +137,7 @@ test("switch using variable re-declared in cases", () => { let foo: number = 0; switch (foo) { case 0: - let foo = true; + let foo: boolean | undefined = true; case 1: return foo; } @@ -209,7 +207,7 @@ test.each([0, 1, 2, 3])("switchWithBrackets (%p)", inp => { `.expectToMatchJsResult(); }); -test.each([0, 1, 2, 3])("switchWithBracketsBreakInConditional (%p)", inp => { +test.each([0, 1, 2, 3, 4])("switchWithBracketsBreakInConditional (%p)", inp => { util.testFunction` let result: number = -1; @@ -225,6 +223,11 @@ test.each([0, 1, 2, 3])("switchWithBracketsBreakInConditional (%p)", inp => { } case 2: { result = 2; + + if (result != 2) break; + } + case 3: { + result = 3; break; } } @@ -261,41 +264,27 @@ test.each([0, 1, 2, 3])("switchWithBracketsBreakInInternalLoop (%p)", inp => { `.expectToMatchJsResult(); }); -test("switch uses elseif", () => { - test("array", () => { - util.testFunction` - let result: number = -1; - - switch (2 as number) { - case 0: { - result = 200; - break; - } - - case 1: { - result = 100; - break; - } - - case 2: { - result = 1; - break; - } +test("switch executes only one clause", () => { + util.testFunction` + let result: number = -1; + switch (2 as number) { + case 0: { + result = 200; + break; } - return result; - ` - .expectLuaToMatchSnapshot() - .expectToMatchJsResult(); - }); -}); + case 1: { + result = 100; + break; + } -test("switch not allowed in 5.1", () => { - util.testFunction` - switch ("abc") {} - ` - .setOptions({ luaTarget: tstl.LuaTarget.Lua51 }) - .expectDiagnosticsToMatchSnapshot([unsupportedForTarget.code]); + case 2: { + result = 1; + break; + } + } + return result; + `.expectToMatchJsResult(); }); // https://github.com/TypeScriptToLua/TypeScriptToLua/issues/967 @@ -323,6 +312,17 @@ test("switch default case not last - second", () => { `.expectToMatchJsResult(); }); +test("switch default case only", () => { + util.testFunction` + let out = 0; + switch (4 as number) { + default: + out = 1 + } + return out; + `.expectToMatchJsResult(); +}); + test("switch fallthrough enters default", () => { util.testFunction` const out = []; @@ -361,3 +361,304 @@ test("switch fallthrough stops after default", () => { return out; `.expectToMatchJsResult(); }); + +test.each([0, 1])("switch empty fallthrough to default (%p)", inp => { + util.testFunction` + const out = []; + switch (${inp} as number) { + case 1: + default: + out.push("default"); + + } + return out; + ` + .expectLuaToMatchSnapshot() + .expectToMatchJsResult(); +}); + +test("switch does not pollute parent scope", () => { + util.testFunction` + let x: number = 0; + let y = 1; + switch (x) { + case 0: + let y = 2; + } + return y; + `.expectToMatchJsResult(); +}); + +test.each([0, 1, 2, 3, 4])("switch handles side-effects (%p)", inp => { + util.testFunction` + const out = []; + + let y = 0; + function foo() { + return y++; + } + + let x = ${inp} as number; + switch (x) { + case foo(): + out.push(1); + case foo(): + out.push(2); + case foo(): + out.push(3); + default: + out.push("default"); + case foo(): + } + + out.push(y); + return out; + `.expectToMatchJsResult(); +}); + +test.each([1, 2])("switch handles side-effects with empty fallthrough (%p)", inp => { + util.testFunction` + const out = []; + + let y = 0; + function foo() { + return y++; + } + + let x = 0 as number; + switch (x) { + // empty fallthrough 1 or many times + ${new Array(inp).fill("case foo():").join("\n")} + default: + out.push("default"); + + } + + out.push(y); + return out; + `.expectToMatchJsResult(); +}); + +test.each([1, 2])("switch handles side-effects with empty fallthrough (preceding clause) (%p)", inp => { + util.testFunction` + const out = []; + + let y = 0; + function foo() { + return y++; + } + + let x = 0 as number; + switch (x) { + case 1: + out.push(1); + // empty fallthrough 1 or many times + ${new Array(inp).fill("case foo():").join("\n")} + default: + out.push("default"); + + } + + out.push(y); + return out; + `.expectToMatchJsResult(); +}); + +test.each([0, 1, 2, 3, 4])("switch handles async side-effects (%p)", inp => { + util.testFunction` + (async () => { + const out = []; + + let y = 0; + async function foo() { + return new Promise((resolve) => y++ && resolve(0)); + } + + let x = ${inp} as number; + switch (x) { + case await foo(): + out.push(1); + case await foo(): + out.push(2); + case await foo(): + out.push(3); + default: + out.push("default"); + case await foo(): + } + + out.push(y); + return out; + })(); + `.expectToMatchJsResult(); +}); + +const optimalOutput = (c: number) => util.testFunction` + let x: number = 0; + const out = []; + switch (${c} as number) { + case 0: + case 1: + case 2: + out.push("0,1,2"); + break; + default: + x++; + out.push("default = " + x); + case 3: { + out.push("3"); + break; + } + case 4: + } + out.push(x.toString()); + return out; +`; + +test("switch produces optimal output", () => { + optimalOutput(0).expectLuaToMatchSnapshot(); +}); + +test.each([0, 1, 2, 3, 4, 5])("switch produces valid optimal output (%p)", inp => { + optimalOutput(inp).expectToMatchJsResult(); +}); + +describe("switch hoisting", () => { + test("hoisting between cases", () => { + util.testFunction` + let x = 1; + let result = ""; + switch (x) { + case 1: + result = hoisted(); + break; + case 2: + function hoisted() { + return "hoisted"; + } + break; + } + return result; + `.expectToMatchJsResult(); + }); + + test("indirect hoisting between cases", () => { + util.testFunction` + let x = 1; + let result = ""; + switch (x) { + case 1: + function callHoisted() { + return hoisted(); + } + result = callHoisted(); + break; + case 2: + function hoisted() { + return "hoisted"; + } + break; + } + return result; + `.expectToMatchJsResult(); + }); + + test("hoisting in case expression", () => { + util.testFunction` + let x = 1; + let result = ""; + switch (x) { + case hoisted(): + result = "hoisted"; + break; + case 2: + function hoisted() { + return 1; + } + break; + } + return result; + `.expectToMatchJsResult(); + }); + + test("hoisting from default clause", () => { + util.testFunction` + let x = 1; + let result = ""; + switch (x) { + case 1: + result = hoisted(); + break; + default: + function hoisted() { + return "hoisted"; + } + break; + } + return result; + `.expectToMatchJsResult(); + }); + + test("hoisting from default clause is not duplicated when falling through", () => { + util.testFunction` + let x = 1; + let result = ""; + switch (x) { + case 1: + result = hoisted(); + break; + case 2: + result = "2"; + default: + function hoisted() { + return "hoisted"; + } + result = "default"; + case 3: + result = "3"; + } + return result; + ` + .expectToMatchJsResult() + .expectLuaToMatchSnapshot(); + }); + + test("hoisting from fallthrough clause after default is not duplicated", () => { + util.testFunction` + let x = 1; + let result = ""; + switch (x) { + case 1: + result = hoisted(); + break; + case 2: + result = "2"; + default: + result = "default"; + case 3: + function hoisted() { + return "hoisted"; + } + result = "3"; + } + return result; + ` + .expectToMatchJsResult() + .expectLuaToMatchSnapshot(); + }); + + test("hoisting in a solo default clause", () => { + util.testFunction` + let x = 1; + let result = ""; + switch (x) { + default: + result = hoisted(); + function hoisted() { + return "hoisted"; + } + } + return result; + `.expectToMatchJsResult(); + }); +}); diff --git a/test/unit/templateLiterals.spec.ts b/test/unit/templateLiterals.spec.ts index d5dc519a5..318804ee9 100644 --- a/test/unit/templateLiterals.spec.ts +++ b/test/unit/templateLiterals.spec.ts @@ -83,3 +83,26 @@ test.each(["string", "'string literal type'", "string & unknown"])( .expectToMatchJsResult(); } ); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1637 +test("tagged template literal returned from function call (#1637)", () => { + util.testModule` + function templateFactory() { + return (template: TemplateStringsArray) => { + return "bar"; + } + } + export let result = templateFactory()\`foo\`; + `.expectToEqual({ result: "bar" }); +}); + +test("tagged template literal returned from function call, explicit no context (#1637)", () => { + util.testModule` + function templateFactory() { + return function(this: void, template: TemplateStringsArray) { + return "bar"; + } + } + export let result = templateFactory()\`foo\`; + `.expectToEqual({ result: "bar" }); +}); diff --git a/test/unit/using.spec.ts b/test/unit/using.spec.ts new file mode 100644 index 000000000..33749cb37 --- /dev/null +++ b/test/unit/using.spec.ts @@ -0,0 +1,255 @@ +import { LuaLibImportKind } from "../../src"; +import * as util from "../util"; + +const usingTestLib = ` + export const logs: string[] = []; + + function loggedDisposable(id: string): Disposable { + logs.push(\`Creating \${id}\`); + + return { + [Symbol.dispose]() { + logs.push(\`Disposing \${id}\`); + } + } + }`; + +test("using disposes object at end of function", () => { + util.testModule` + function func() { + using a = loggedDisposable("a"); + using b = loggedDisposable("b"); + + logs.push("function content"); + } + + func(); + ` + .setTsHeader(usingTestLib) + .expectToEqual({ logs: ["Creating a", "Creating b", "function content", "Disposing b", "Disposing a"] }); +}); + +test("handles multi-variable declarations", () => { + util.testModule` + function func() { + using a = loggedDisposable("a"), b = loggedDisposable("b"); + + logs.push("function content"); + } + + func(); + ` + .setTsHeader(usingTestLib) + .expectToEqual({ logs: ["Creating a", "Creating b", "function content", "Disposing b", "Disposing a"] }); +}); + +test("using disposes object at end of nested block", () => { + util.testModule` + function func() { + using a = loggedDisposable("a"); + + { + using b = loggedDisposable("b"); + logs.push("nested block"); + } + + logs.push("function content"); + } + + func(); + ` + .setTsHeader(usingTestLib) + .expectToEqual({ + logs: ["Creating a", "Creating b", "nested block", "Disposing b", "function content", "Disposing a"], + }); +}); + +test("using does not affect function return value", () => { + util.testModule` + function func() { + using a = loggedDisposable("a"); + using b = loggedDisposable("b"); + + logs.push("function content"); + + return "success"; + } + + export const result = func(); + ` + .setTsHeader(usingTestLib) + .expectToEqual({ + result: "success", + logs: ["Creating a", "Creating b", "function content", "Disposing b", "Disposing a"], + }); +}); + +test("using disposes even when error happens", () => { + util.testModule` + function func() { + using a = loggedDisposable("a"); + using b = loggedDisposable("b"); + + throw "test-induced exception"; + } + + try + { + func(); + } + catch (e) + { + logs.push(\`caught exception: \${e}\`); + } + ` + .setTsHeader(usingTestLib) + .expectToEqual({ + logs: [ + "Creating a", + "Creating b", + "Disposing b", + "Disposing a", + "caught exception: test-induced exception", + ], + }); +}); + +test("await using disposes object with await at end of function", () => { + util.testModule` + let disposeAsync: (() => void) | undefined; + + function loggedAsyncDisposable(id: string): AsyncDisposable { + logs.push(\`Creating \${id}\`); + + return { + [Symbol.asyncDispose]() { + logs.push(\`Disposing async \${id}\`); + return new Promise(resolve => { + disposeAsync = () => { + logs.push(\`Disposed \${id}\`); + resolve(); + }; + }); + } + } + } + + async function func() { + await using a = loggedAsyncDisposable("a"); + + logs.push("function content"); + return "function result"; + } + + const p = func().then(r => logs.push("promise resolved", r)); + + logs.push("function returned"); + + disposeAsync!(); + ` + .setTsHeader(usingTestLib) + .setOptions({ luaLibImport: LuaLibImportKind.Inline }) + .expectToEqual({ + logs: [ + "Creating a", + "function content", + "Disposing async a", + "function returned", + "Disposed a", + "promise resolved", + "function result", + ], + }); +}); + +test("await using can handle non-async disposables", () => { + util.testModule` + async function func() { + await using a = loggedDisposable("a"); + + logs.push("function content"); + } + + func(); + ` + .setTsHeader(usingTestLib) + .expectToEqual({ logs: ["Creating a", "function content", "Disposing a"] }); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1571 +test("await using no extra diagnostics (#1571)", () => { + util.testModule` + async function getResource(): Promise { + return { + [Symbol.asyncDispose]: async () => {} + }; + } + + async function someOtherAsync() {} + + async function main() { + await using resource = await getResource(); + await someOtherAsync(); + } + `.expectToHaveNoDiagnostics(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1622 +test("await-using with nested async arrow that also has await-using (runtime divergence)", () => { + util.testFunction` + const logs: any[] = []; + async function getA(): Promise { + return { [Symbol.asyncDispose]: async () => {} }; + } + async function outer(): Promise { + await using a = await getA(); + const inner = async (): Promise => { + await using b = await getA(); + return 42; + }; + return inner(); + } + outer().then(v => logs.push(v)); + return logs; + `.expectToEqual([42]); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1584 +test("works with disposable classes (#1584)", () => { + util.testFunction` + const log: string[] = []; + + class Scoped { + action(): void { + log.push("action") + } + [Symbol.dispose]() { + log.push("cleanup") + } + } + + function TestScoped(): void { + using s = new Scoped(); + s.action(); + } + + TestScoped(); + return log; + `.expectToEqual(["action", "cleanup"]); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1632 +test("works on root level (#1632)", () => { + util.testModule` + export let disposed = false; + + class A { + [Symbol.dispose] = function (this: A) { + disposed = true; + } + } + using a = new A(); + `.expectToEqual({ + disposed: true, + }); +}); diff --git a/test/unit/void.spec.ts b/test/unit/void.spec.ts new file mode 100644 index 000000000..024f0cf7e --- /dev/null +++ b/test/unit/void.spec.ts @@ -0,0 +1,34 @@ +import * as util from "../util"; + +test.each(["0", "1", '"a"'])("void evaluates to undefined (%p)", value => { + util.testExpression`void (${value})`.expectToMatchJsResult(); +}); + +test("void applies to function declarations", () => { + util.testFunction` + let result = 0; + void function setResult() { + result = 1; + }(); + return result; + `.expectToMatchJsResult(); +}); + +test("void used to ignore function return values", () => { + util.testFunction` + let result = 0; + function setResult() { + result = 1; + return 3 + }; + + void(setResult()); + + return result; + `.expectToMatchJsResult(); +}); + +// https://github.com/TypeScriptToLua/TypeScriptToLua/issues/1102 +test("void works with lambdas", () => { + util.testExpression`void (() => {})()`.expectToMatchJsResult(); +}); diff --git a/test/util.ts b/test/util.ts index 39259e784..1e191fb28 100644 --- a/test/util.ts +++ b/test/util.ts @@ -9,16 +9,27 @@ import * as ts from "typescript"; import * as vm from "vm"; import * as tstl from "../src"; import { createEmitOutputCollector } from "../src/transpilation/output-collector"; -import { getEmitOutDir, transpileProject } from "../src"; +import { EmitHost, getEmitOutDir, transpileProject } from "../src"; import { formatPathToLuaPath, normalizeSlashes } from "../src/utils"; +import { resolveLuaLibDir } from "../src/LuaLib"; -const jsonLib = fs.readFileSync(path.join(__dirname, "json.lua"), "utf8"); -const luaLib = fs.readFileSync(path.resolve(__dirname, "../dist/lualib/lualib_bundle.lua"), "utf8"); +function readLuaLib(target: tstl.LuaTarget) { + return fs.readFileSync(path.join(resolveLuaLibDir(target), "lualib_bundle.lua"), "utf8"); +} + +function jsonLib(target: tstl.LuaTarget): string { + const fileName = target === tstl.LuaTarget.Lua50 ? "json.50.lua" : "json.lua"; + return fs.readFileSync(path.join(__dirname, fileName), "utf8"); +} // Using `test` directly makes eslint-plugin-jest consider this file as a test const defineTest = test; function getLuaBindingsForVersion(target: tstl.LuaTarget): { lauxlib: LauxLib; lua: Lua; lualib: LuaLib } { + if (target === tstl.LuaTarget.Lua50) { + const { lauxlib, lua, lualib } = require("lua-wasm-bindings/dist/lua.50"); + return { lauxlib, lua, lualib }; + } if (target === tstl.LuaTarget.Lua51) { const { lauxlib, lua, lualib } = require("lua-wasm-bindings/dist/lua.51"); return { lauxlib, lua, lualib }; @@ -32,7 +43,7 @@ function getLuaBindingsForVersion(target: tstl.LuaTarget): { lauxlib: LauxLib; l return { lauxlib, lua, lualib }; } if (target === tstl.LuaTarget.LuaJIT) { - throw Error("Can't use executeLua() or expectToMatchJsResult() wit LuaJIT as target!"); + throw Error("Can't use executeLua() or expectToMatchJsResult() with LuaJIT as target!"); } const { lauxlib, lua, lualib } = require("lua-wasm-bindings/dist/lua.54"); @@ -40,7 +51,7 @@ function getLuaBindingsForVersion(target: tstl.LuaTarget): { lauxlib: LauxLib; l } export function assert(value: any, message?: string | Error): asserts value { - nativeAssert(value, message); + nativeAssert.ok(value, message); } export const formatCode = (...values: unknown[]) => values.map(e => stringify(e)).join(", "); @@ -52,7 +63,7 @@ export function testEachVersion( ): void { for (const version of Object.values(tstl.LuaTarget) as tstl.LuaTarget[]) { const specialBuilder = special?.[version]; - if (specialBuilder === false) return; + if (specialBuilder === false) continue; const testName = name === undefined ? version : `${name} [${version}]`; defineTest(testName, () => { @@ -65,17 +76,31 @@ export function testEachVersion( } } -const memoize: MethodDecorator = (_target, _propertyKey, descriptor) => { - const originalFunction = descriptor.value as any; +export function expectEachVersionExceptJit( + expectation: (builder: T) => void +): Record void) | boolean> { + return { + [tstl.LuaTarget.Universal]: expectation, + [tstl.LuaTarget.Lua50]: expectation, + [tstl.LuaTarget.Lua51]: expectation, + [tstl.LuaTarget.Lua52]: expectation, + [tstl.LuaTarget.Lua53]: expectation, + [tstl.LuaTarget.Lua54]: expectation, + [tstl.LuaTarget.Lua55]: expectation, + [tstl.LuaTarget.LuaJIT]: false, // Exclude JIT + [tstl.LuaTarget.Luau]: false, + }; +} + +const memoize = (originalFunction: any) => { const memoized = new WeakMap(); - descriptor.value = function (this: any, ...args: any[]): any { + return function (this: any, ...args: any[]): any { if (!memoized.has(this)) { memoized.set(this, originalFunction.apply(this, args)); } return memoized.get(this); - } as any; - return descriptor; + }; }; export class ExecutionError extends Error { @@ -97,77 +122,92 @@ export abstract class TestBuilder { // TODO: Use testModule in these cases? protected tsHeader = ""; public setTsHeader(tsHeader: string): this { - expect(this.hasProgram).toBe(false); + this.throwIfProgramExists("setTsHeader"); this.tsHeader = tsHeader; return this; } private luaHeader = ""; public setLuaHeader(luaHeader: string): this { - expect(this.hasProgram).toBe(false); + this.throwIfProgramExists("setLuaHeader"); this.luaHeader += luaHeader; return this; } protected jsHeader = ""; public setJsHeader(jsHeader: string): this { - expect(this.hasProgram).toBe(false); + this.throwIfProgramExists("setJsHeader"); this.jsHeader += jsHeader; return this; } protected abstract getLuaCodeWithWrapper(code: string): string; public setLuaFactory(luaFactory: (code: string) => string): this { - expect(this.hasProgram).toBe(false); + this.throwIfProgramExists("setLuaFactory"); this.getLuaCodeWithWrapper = luaFactory; return this; } private semanticCheck = true; public disableSemanticCheck(): this { - expect(this.hasProgram).toBe(false); + this.throwIfProgramExists("disableSemanticCheck"); this.semanticCheck = false; return this; } protected options: tstl.CompilerOptions = { - luaTarget: tstl.LuaTarget.Lua54, + luaTarget: tstl.LuaTarget.Lua55, noHeader: true, skipLibCheck: true, target: ts.ScriptTarget.ES2017, lib: ["lib.esnext.d.ts"], - moduleResolution: ts.ModuleResolutionKind.NodeJs, + moduleResolution: ts.ModuleResolutionKind.Bundler, resolveJsonModule: true, - experimentalDecorators: true, sourceMap: true, }; public setOptions(options: tstl.CompilerOptions = {}): this { - expect(this.hasProgram).toBe(false); + this.throwIfProgramExists("setOptions"); Object.assign(this.options, options); return this; } + public withLanguageExtensions(): this { + const langExtTypes = path.resolve(__dirname, "..", "language-extensions"); + this.options.types = this.options.types ? [...this.options.types, langExtTypes] : [langExtTypes]; + // Polyfill lualib for JS + this.setJsHeader(` + function $multi(...args) { return args; } + `); + return this; + } + protected mainFileName = "main.ts"; public setMainFileName(mainFileName: string): this { - expect(this.hasProgram).toBe(false); - this.mainFileName = normalizeSlashes(mainFileName); + this.throwIfProgramExists("setMainFileName"); + this.mainFileName = mainFileName; return this; } protected extraFiles: Record = {}; public addExtraFile(fileName: string, code: string): this { - expect(this.hasProgram).toBe(false); - this.extraFiles[fileName] = code; + this.throwIfProgramExists("addExtraFile"); + this.extraFiles[fileName] = normalizeSlashes(code); return this; } private customTransformers?: ts.CustomTransformers; public setCustomTransformers(customTransformers?: ts.CustomTransformers): this { - expect(this.hasProgram).toBe(false); + this.throwIfProgramExists("setCustomTransformers"); this.customTransformers = customTransformers; return this; } + private throwIfProgramExists(name: string) { + if (this.hasProgram) { + throw new Error(`${name}() should not be called after an .expect() or .debug()`); + } + } + // Transpilation and execution public getTsCode(): string { @@ -178,26 +218,38 @@ export abstract class TestBuilder { @memoize public getProgram(): ts.Program { this.hasProgram = true; - return tstl.createVirtualProgram( - { ...this.extraFiles, [normalizeSlashes(this.mainFileName)]: this.getTsCode() }, - this.options + + // Exclude lua files from TS program, but keep them in extraFiles so module resolution can find them + const nonLuaExtraFiles = Object.fromEntries( + Object.entries(this.extraFiles).filter(([fileName]) => !fileName.endsWith(".lua")) ); + + return tstl.createVirtualProgram({ ...nonLuaExtraFiles, [this.mainFileName]: this.getTsCode() }, this.options); + } + + private getEmitHost(): EmitHost { + return { + fileExists: (path: string) => normalizeSlashes(path) in this.extraFiles, + directoryExists: (path: string) => + Object.keys(this.extraFiles).some(f => f.startsWith(normalizeSlashes(path))), + getCurrentDirectory: () => ".", + readFile: (path: string) => this.extraFiles[normalizeSlashes(path)] ?? ts.sys.readFile(path), + writeFile() {}, + }; } @memoize public getLuaResult(): tstl.TranspileVirtualProjectResult { const program = this.getProgram(); - const collector = createEmitOutputCollector(); - const { diagnostics: transpileDiagnostics } = new tstl.Transpiler().emit({ + const preEmitDiagnostics = ts.getPreEmitDiagnostics(program); + const collector = createEmitOutputCollector(this.options.extension); + const { diagnostics: transpileDiagnostics } = new tstl.Transpiler({ emitHost: this.getEmitHost() }).emit({ program, customTransformers: this.customTransformers, writeFile: collector.writeFile, }); - const diagnostics = ts.sortAndDeduplicateDiagnostics([ - ...ts.getPreEmitDiagnostics(program), - ...transpileDiagnostics, - ]); + const diagnostics = ts.sortAndDeduplicateDiagnostics([...preEmitDiagnostics, ...transpileDiagnostics]); return { diagnostics: [...diagnostics], transpiledFiles: collector.files }; } @@ -205,11 +257,18 @@ export abstract class TestBuilder { @memoize public getMainLuaFileResult(): ExecutableTranspiledFile { const { transpiledFiles } = this.getLuaResult(); + const mainFileName = normalizeSlashes(this.mainFileName); const mainFile = this.options.luaBundle ? transpiledFiles[0] - : transpiledFiles.find(({ sourceFiles }) => - sourceFiles.some(f => normalizeSlashes(f.fileName) === this.mainFileName) - ); + : transpiledFiles.find(({ sourceFiles }) => sourceFiles.some(f => f.fileName === mainFileName)); + + if (mainFile === undefined) { + throw new Error( + `No source file could be found matching main file: ${mainFileName}.\nSource files in test:\n${transpiledFiles + .flatMap(f => f.sourceFiles.map(sf => sf.fileName)) + .join("\n")}` + ); + } expect(mainFile).toMatchObject({ lua: expect.any(String), luaSourceMap: expect.any(String) }); return mainFile as ExecutableTranspiledFile; @@ -231,7 +290,7 @@ export abstract class TestBuilder { const program = this.getProgram(); program.getCompilerOptions().module = ts.ModuleKind.CommonJS; - const collector = createEmitOutputCollector(); + const collector = createEmitOutputCollector(this.options.extension); const { diagnostics } = program.emit(undefined, collector.writeFile); return { transpiledFiles: collector.files, diagnostics: [...diagnostics] }; } @@ -239,8 +298,9 @@ export abstract class TestBuilder { @memoize public getMainJsCodeChunk(): string { const { transpiledFiles } = this.getJsResult(); - const code = transpiledFiles.find(({ sourceFiles }) => sourceFiles.some(f => f.fileName === this.mainFileName)) - ?.js; + const code = transpiledFiles.find(({ sourceFiles }) => + sourceFiles.some(f => f.fileName === this.mainFileName) + )?.js; assert(code !== undefined); const header = this.jsHeader ? `${this.jsHeader.trimRight()}\n` : ""; @@ -265,11 +325,24 @@ export abstract class TestBuilder { // Actions - public debug(): this { - const transpiledFiles = this.getLuaResult().transpiledFiles; - const luaCode = transpiledFiles.map(f => `[${f.outPath}]:\n${f.lua?.replace(/^/gm, " ")}`); - const value = prettyFormat(this.getLuaExecutionResult()).replace(/^/gm, " "); + public debug(includeLualib = false): this { + const { transpiledFiles, diagnostics } = this.getLuaResult(); + const luaCode = transpiledFiles + .filter(f => includeLualib || f.outPath !== "lualib_bundle.lua") + .map(f => `[${f.outPath}]:\n${f.lua?.replace(/^/gm, " ")}`); + const value = prettyFormat.format(this.getLuaExecutionResult()).replace(/^/gm, " "); console.log(`Lua Code:\n${luaCode.join("\n")}\n\nValue:\n${value}`); + + if (diagnostics.length > 0) { + console.log( + ts.formatDiagnostics(diagnostics.map(tstl.prepareDiagnosticForFormatting), { + getCurrentDirectory: () => "", + getCanonicalFileName: fileName => fileName, + getNewLine: () => "\n", + }) + ); + } + return this; } @@ -297,6 +370,11 @@ export abstract class TestBuilder { return this; } + public expectNoTranspileException(): this { + expect(() => this.getLuaResult()).not.toThrow(); + return this; + } + public expectNoExecutionError(): this { const luaResult = this.getLuaExecutionResult(); if (luaResult instanceof ExecutionError) { @@ -365,20 +443,21 @@ export abstract class TestBuilder { // Main file const mainFile = this.getMainLuaCodeChunk(); - const { lauxlib, lua, lualib } = getLuaBindingsForVersion(this.options.luaTarget ?? tstl.LuaTarget.Lua54); + const luaTarget = this.options.luaTarget ?? tstl.LuaTarget.Lua55; + const { lauxlib, lua, lualib } = getLuaBindingsForVersion(luaTarget); const L = lauxlib.luaL_newstate(); lualib.luaL_openlibs(L); // Load modules // Json - this.packagePreloadLuaFile(L, lua, lauxlib, "json", jsonLib); + this.injectLuaFile(L, lua, lauxlib, "json", jsonLib(luaTarget)); // Lua lib if ( this.options.luaLibImport === tstl.LuaLibImportKind.Require || mainFile.includes('require("lualib_bundle")') ) { - this.packagePreloadLuaFile(L, lua, lauxlib, "lualib_bundle", luaLib); + this.injectLuaFile(L, lua, lauxlib, "lualib_bundle", readLuaLib(luaTarget)); } // Load all transpiled files into Lua's package cache @@ -386,7 +465,7 @@ export abstract class TestBuilder { for (const transpiledFile of transpiledFiles) { if (transpiledFile.lua) { const filePath = path.relative(getEmitOutDir(this.getProgram()), transpiledFile.outPath); - this.packagePreloadLuaFile(L, lua, lauxlib, filePath, transpiledFile.lua); + this.injectLuaFile(L, lua, lauxlib, filePath, transpiledFile.lua); } } @@ -417,12 +496,26 @@ end)());`; } } - private packagePreloadLuaFile(state: LuaState, lua: Lua, lauxlib: LauxLib, fileName: string, fileContent: string) { - // Adding source Lua to the package.preload cache will allow require to find it - lua.lua_getglobal(state, "package"); - lua.lua_getfield(state, -1, "preload"); - lauxlib.luaL_loadstring(state, fileContent); - lua.lua_setfield(state, -2, formatPathToLuaPath(fileName.replace(".lua", ""))); + private injectLuaFile(state: LuaState, lua: Lua, lauxlib: LauxLib, fileName: string, fileContent: string) { + let extension = this.options.extension ?? ".lua"; + if (!extension.startsWith(".")) { + extension = `.${extension}`; + } + const modName = fileName.endsWith(extension) + ? formatPathToLuaPath(fileName.substring(0, fileName.length - extension.length)) + : fileName; + if (this.options.luaTarget === tstl.LuaTarget.Lua50) { + // Adding source Lua to the _LOADED cache will allow require to find it + lua.lua_getglobal(state, "_LOADED"); + lauxlib.luaL_dostring(state, fileContent); + lua.lua_setfield(state, -2, modName); + } else { + // Adding source Lua to the package.preload cache will allow require to find it + lua.lua_getglobal(state, "package"); + lua.lua_getfield(state, -1, "preload"); + lauxlib.luaL_loadstring(state, fileContent); + lua.lua_setfield(state, -2, modName); + } } private executeJs(): any { @@ -439,8 +532,9 @@ end)());`; const moduleExports = {}; globalContext.exports = moduleExports; globalContext.module = { exports: moduleExports }; + const baseName = fileName.replace("./", ""); const transpiledExtraFile = transpiledFiles.find(({ sourceFiles }) => - sourceFiles.some(f => f.fileName === fileName.replace("./", "") + ".ts") + sourceFiles.some(f => f.fileName === baseName + ".ts" || f.fileName === baseName + "/index.ts") ); if (transpiledExtraFile?.js) { @@ -462,6 +556,8 @@ end)());`; try { result = vm.runInContext(this.getJsCodeWithWrapper(), globalContext); } catch (error) { + const hasMessage = (error: any): error is { message: string } => error.message !== undefined; + assert(hasMessage(error)); return new ExecutionError(error.message); } @@ -500,7 +596,7 @@ class AccessorTestBuilder extends TestBuilder { protected accessor = ""; protected getLuaCodeWithWrapper(code: string) { - return `return (function()\n${code}\nend)()${this.accessor}`; + return `return (function(...)\n${code}\nend)()${this.accessor}`; } @memoize @@ -550,32 +646,31 @@ class ProjectTestBuilder extends ModuleTestBuilder { @memoize public getLuaResult(): tstl.TranspileVirtualProjectResult { // Override getLuaResult to use transpileProject with tsconfig.json instead - const collector = createEmitOutputCollector(); + const collector = createEmitOutputCollector(this.options.extension); const { diagnostics } = transpileProject(this.tsConfig, this.options, collector.writeFile); return { diagnostics: [...diagnostics], transpiledFiles: collector.files }; } } -const createTestBuilderFactory = ( - builder: new (_tsCode: string) => T, - serializeSubstitutions: boolean -) => (...args: [string] | [TemplateStringsArray, ...any[]]): T => { - let tsCode: string; - if (typeof args[0] === "string") { - expect(serializeSubstitutions).toBe(false); - tsCode = args[0]; - } else { - let [raw, ...substitutions] = args; - if (serializeSubstitutions) { - substitutions = substitutions.map(s => formatCode(s)); - } +const createTestBuilderFactory = + (builder: new (_tsCode: string) => T, serializeSubstitutions: boolean) => + (...args: [string] | [TemplateStringsArray, ...any[]]): T => { + let tsCode: string; + if (typeof args[0] === "string") { + expect(serializeSubstitutions).toBe(false); + tsCode = args[0]; + } else { + let [raw, ...substitutions] = args; + if (serializeSubstitutions) { + substitutions = substitutions.map(s => formatCode(s)); + } - tsCode = String.raw(Object.assign([], { raw }), ...substitutions); - } + tsCode = String.raw(Object.assign([], { raw }), ...substitutions); + } - return new builder(tsCode); -}; + return new builder(tsCode); + }; export const testBundle = createTestBuilderFactory(BundleTestBuilder, false); export const testModule = createTestBuilderFactory(ModuleTestBuilder, false); diff --git a/tsconfig-schema.json b/tsconfig-schema.json new file mode 100644 index 000000000..6bffd5e46 --- /dev/null +++ b/tsconfig-schema.json @@ -0,0 +1,114 @@ +{ + "title": "tsconfig.json with TSTL", + "description": "JSON schema for the TypeScript compiler's configuration file with TSTL", + "$schema": "http://json-schema.org/draft-07/schema", + "allOf": [ + { + "$ref": "https://json.schemastore.org/tsconfig" + } + ], + "properties": { + "tstl": { + "description": "TypeScriptToLua compiler options.", + "type": "object", + "definitions": { + "//": { + "reference": "https://typescripttolua.github.io/docs/configuration#custom-options" + } + }, + "properties": { + "buildMode": { + "description": "Use buildMode: \"library\" to build publishable library packages.", + "type": "string", + "default": "library", + "enum": ["default", "library"] + }, + "extension": { + "description": "File extension for the resulting Lua files. Defaults to \".lua\"", + "type": "string" + }, + "lua51AllowTryCatchInAsyncAwait": { + "description": "Disable the warning that try/catch is not allowed in async functions in Lua 5.1, in case you are using a patched 5.1 lua version that supports this.", + "type": "boolean", + "default": false + }, + "luaBundle": { + "description": "The name of the lua file to bundle output lua to. Requires luaBundleEntry.", + "type": "string" + }, + "luaBundleEntry": { + "description": "The entry *.ts file that will be executed when entering the luaBundle. Requires luaBundle.", + "type": "string" + }, + "luaLibImport": { + "description": "Specifies how js standard features missing in lua are imported.", + "type": "string", + "default": "require", + "enum": ["none", "inline", "require", "require-minimal"] + }, + "luaTarget": { + "description": "Specifies the Lua version you want to generate code for.", + "type": "string", + "default": "universal", + "enum": ["5.0", "universal", "5.1", "5.2", "5.3", "5.4", "5.5", "JIT", "Luau"] + }, + "noImplicitGlobalVariables": { + "description": "Always declare all root-level variables as local, even if the file is not a module and they would be global in TypeScript.", + "type": "boolean", + "default": false + }, + "noImplicitSelf": { + "description": "If true, treats all project files as if they were prefixed with\n/** @noSelfInFile **/.", + "type": "boolean", + "default": false + }, + "noHeader": { + "description": "Specify if a header will be added to compiled files.", + "type": "boolean", + "default": false + }, + "noResolvePaths": { + "description": "An array of import paths that should not be resolved but copied verbatim to output lua.", + "type": "array" + }, + "sourceMapTraceback": { + "description": "Applies the source map to show source TS files and lines in error tracebacks.", + "default": false, + "type": "boolean" + }, + "tstlVerbose": { + "description": "Give verbose tstl output, helpful when diagnosing tstl issues.", + "type": "boolean", + "default": false + }, + "luaPlugins": { + "description": "List of TypeScriptToLua plugins.", + "type": "array", + "items": { + "description": "Describes TypeScriptToLua plugin", + "type": "object", + "required": ["name"], + "properties": { + "name": { + "description": "Path to the JS file, that contains the plugin code", + "type": "string" + }, + "import": { + "type": "string" + } + } + } + }, + "measurePerformance": { + "description": "Measure and report performance of the tstl compiler.", + "type": "boolean" + } + }, + "dependencies": { + "luaBundle": ["luaBundleEntry"], + "luaBundleEntry": ["luaBundle"] + } + } + }, + "allowTrailingCommas": true +} diff --git a/tsconfig.json b/tsconfig.json index bf121bd20..1fcbb6d79 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -4,7 +4,6 @@ "lib": ["es2019"], "types": ["node"], "module": "commonjs", - "experimentalDecorators": true, "rootDir": "src", "outDir": "dist",