From 54424250b6da77e7105cfe894f5bbd051e44c4e3 Mon Sep 17 00:00:00 2001 From: Michael Arnaldi Date: Sun, 16 Jan 2022 16:59:35 +0000 Subject: [PATCH] chore: patch commit, summary of work. --- .eslintrc.json | 1 + .github/ISSUE_TEMPLATE/Bug_report.md | 75 - .github/ISSUE_TEMPLATE/Feature_request.md | 61 - .github/ISSUE_TEMPLATE/config.yml | 19 - .github/ISSUE_TEMPLATE/lib_change.md | 58 - .github/codeql/codeql-configuration.yml | 8 - .github/pr_owners.txt | 14 - .github/pull_request_template.md | 21 - .github/workflow-resources/.lsifrc.json | 6 - .../workflows/accept-baselines-fix-lints.yaml | 26 - .github/workflows/ci.yml | 189 - .github/workflows/codeql.yml | 64 - .../ensure-related-repos-run-crons.yml | 47 - .github/workflows/new-release-branch.yaml | 35 - .github/workflows/nightly.yaml | 32 - .../workflows/release-branch-artifact.yaml | 33 - .github/workflows/rich-navigation.yml | 33 - .github/workflows/set-version.yaml | 41 - .github/workflows/sync-branch.yaml | 32 - .github/workflows/sync-wiki.yml | 21 - .github/workflows/twoslash-repros.yaml | 38 - .github/workflows/update-lkg.yml | 24 - .github/workflows/update-package-lock.yaml | 32 - .gitignore | 1 + .gitpod.yml | 6 + .npmignore | 42 + .vscode/extensions.json | 10 - .vscode/launch.template.json | 58 - .vscode/settings.json | 20 + .vscode/settings.template.json | 23 - Herebyfile.mjs | 6 + effect/.gitignore | 1 + effect/package.json | 9 + effect/packages/package1/package.json | 23 + effect/packages/package1/src/class.js | 28 + effect/packages/package1/src/class.ts | 29 + .../packages/package1/src/companion-test.ts | 22 + effect/packages/package1/src/companion.ts | 9 + .../package1/src/companions-everywhere.ts | 15 + effect/packages/package1/src/export-bug/1.ts | 14 + effect/packages/package1/src/extensions.ts | 13 + effect/packages/package1/src/global.ts | 4 + effect/packages/package1/src/hello.ts | 4 + effect/packages/package1/src/import-bug/1.ts | 3 + effect/packages/package1/src/import-bug/2.ts | 4 + effect/packages/package1/src/index.ts | 179 + effect/packages/package1/src/inheritance.ts | 62 + .../package1/src/intersection-inheritance.ts | 51 + effect/packages/package1/src/jsdoc-bug.ts | 9 + effect/packages/package1/src/lazy.ts | 10 + effect/packages/package1/src/macros.ts | 27 + effect/packages/package1/src/map.ts | 25 + effect/packages/package1/src/mutable.ts | 28 + .../package1/src/namespace-collection/1.ts | 13 + effect/packages/package1/src/no-inherit.ts | 24 + effect/packages/package1/src/piped.ts | 6 + effect/packages/package1/src/prelude.ts | 3 + .../package1/src/prelude/definition/Cont.ts | 9 + .../package1/src/prelude/definition/Effect.ts | 55 + .../package1/src/prelude/definition/List.ts | 138 + .../package1/src/prelude/definition/Maybe.ts | 151 + .../package1/src/prelude/definition/Sync.ts | 86 + effect/packages/package1/src/prelude/index.ts | 2 + .../package1/src/prelude/operations/access.ts | 12 + .../package1/src/prelude/operations/bind.ts | 16 + .../package1/src/prelude/operations/do.ts | 9 + .../src/prelude/operations/failWith.ts | 13 + .../src/prelude/operations/flatMap.ts | 17 + .../package1/src/prelude/operations/index.ts | 10 + .../package1/src/prelude/operations/map.ts | 10 + .../package1/src/prelude/operations/orElse.ts | 14 + .../package1/src/prelude/operations/pipe.ts | 8 + .../package1/src/prelude/operations/run.ts | 11 + .../src/prelude/operations/succeedWith.ts | 34 + .../package1/src/prelude/operations/unit.ts | 9 + .../package1/src/prelude/operations/zip.ts | 14 + .../src/prelude/pipeable/pipeable-dep-arg.ts | 5 + .../src/prelude/pipeable/pipeable-index.ts | 23 + .../src/prelude/pipeable/pipeable-operator.ts | 26 + .../src/prelude/pipeable/pipeable-overload.ts | 21 + .../package1/src/prelude/pipeable/pipeable.ts | 55 + .../src/prelude/pipeable/type-resolution.ts | 36 + effect/packages/package1/src/primitives.ts | 447 ++ .../package1/src/scope-name-conflict.ts | 31 + .../package1/src/static-extension-class.ts | 20 + .../src/static-fluent-name-duplication.ts | 23 + effect/packages/package1/src/symbol-bug/1.ts | 18 + effect/packages/package1/src/symbol-bug/2.ts | 11 + effect/packages/package1/src/tailrec.ts | 46 + .../package1/src/type-and-static-repro/1.ts | 39 + .../package1/src/type-companion-global.ts | 1 + .../package1/src/type-driven-overloads.ts | 26 + .../package1/src/type-driven-overloads/T.ts | 27 + .../package1/src/type-driven-overloads/op1.ts | 20 + .../package1/src/type-driven-overloads/op2.ts | 15 + .../package1/src/union-inheritance.ts | 181 + .../package1/src/utils/LazyArgument.ts | 4 + effect/packages/package1/tsconfig.json | 14 + effect/packages/package2/fp-ts__core.json | 74 + effect/packages/package2/fp-ts__core_2.json | 40 + effect/packages/package2/package.json | 11 + .../packages/package2/src/derivation/auto.ts | 149 + .../package2/src/derivation/block-scoped.ts | 57 + .../packages/package2/src/derivation/brand.ts | 26 + .../packages/package2/src/derivation/check.ts | 66 + .../packages/package2/src/derivation/guard.ts | 156 + .../packages/package2/src/derivation/int.ts | 13 + .../package2/src/derivation/refinement.ts | 145 + .../packages/package2/src/derivation/show.ts | 169 + .../packages/package2/src/derivation/types.ts | 49 + .../packages/package2/src/derivation/use.ts | 55 + .../package2/src/external-definitions.ts | 6 + effect/packages/package2/src/globals.ts | 14 + effect/packages/package2/src/globals2.ts | 3 + .../package2/src/namespace-collection.ts | 5 + effect/packages/package2/src/prelude.ts | 10 + effect/packages/package2/src/service.ts | 17 + effect/packages/package2/src/test.ts | 24 + .../src/union-inheritance-repro/Equals.ts | 16 + .../src/union-inheritance-repro/exit-repro.ts | 30 + effect/packages/package2/src/via.ts | 2 + effect/packages/package2/tsconfig.json | 16 + effect/tsconfig.json | 12 + effect/tsplus.config.json | 12 + pack.sh | 6 + package.json | 2 +- scripts/configurePrerelease.mjs | 4 +- src/compiler/builderState.ts | 16 + src/compiler/checker.ts | 4547 ++++++++++++++++- src/compiler/commandLineParser.ts | 57 +- src/compiler/diagnosticMessages.json | 148 + src/compiler/factory/nodeFactory.ts | 38 +- src/compiler/factory/utilities.ts | 15 + src/compiler/hashing.ts | 376 ++ src/compiler/parser.ts | 757 ++- src/compiler/program.ts | 112 +- src/compiler/transformers/declarations.ts | 2 +- src/compiler/transformers/module/module.ts | 26 +- src/compiler/transformers/tailRec.ts | 240 + src/compiler/transformers/tsplus.ts | 1311 +++++ .../transformers/tsplusDeclaration.ts | 260 + src/compiler/transformers/utilities.ts | 82 +- src/compiler/types.ts | 526 +- src/compiler/utilities.ts | 5 +- src/compiler/utilitiesPublic.ts | 7 + src/lib/libs.json | 4 +- src/lib/tsplus.d.ts | 22 + src/server/protocol.ts | 2 + src/services/completions.ts | 39 + src/services/documentHighlights.ts | 13 + src/services/documentRegistry.ts | 4 +- src/services/findAllReferences.ts | 168 +- src/services/goToDefinition.ts | 142 +- src/services/services.ts | 308 +- src/services/signatureHelp.ts | 42 +- src/services/symbolDisplay.ts | 4 + src/tsc/_namespaces/ts.ts | 3 + src/tsc/tsconfig.json | 3 + testing/.gitignore | 1 + testing/package.json | 15 + testing/packages/.placeholder | 0 testing/packages/.placeolder | 0 testing/packages/demo/package.json | 20 + testing/packages/demo/src/bug-constant.ts | 4 + testing/packages/demo/src/do-bind.ts | 41 + testing/packages/demo/src/doc.ts | 65 + testing/packages/demo/src/example.ts | 11 + testing/packages/demo/src/fluent-guard.ts | 40 + testing/packages/demo/src/global.ts | 2 + testing/packages/demo/src/operator-repro.ts | 11 + testing/packages/demo/src/pipe-deps.ts | 15 + testing/packages/demo/src/pipe-repro.ts | 3 + .../packages/demo/src/pipeable-type-node.ts | 10 + testing/packages/demo/src/repro-pr-117.ts | 7 + testing/packages/demo/tsconfig.json | 12 + testing/prelude.d.ts | 483 ++ testing/tsconfig.json | 15 + testing/tsplus.config.json | 12 + utils/rebase.sh | 3 + 179 files changed, 13529 insertions(+), 1107 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/Bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/Feature_request.md delete mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/lib_change.md delete mode 100644 .github/codeql/codeql-configuration.yml delete mode 100644 .github/pr_owners.txt delete mode 100644 .github/pull_request_template.md delete mode 100644 .github/workflow-resources/.lsifrc.json delete mode 100644 .github/workflows/accept-baselines-fix-lints.yaml delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/codeql.yml delete mode 100644 .github/workflows/ensure-related-repos-run-crons.yml delete mode 100644 .github/workflows/new-release-branch.yaml delete mode 100644 .github/workflows/nightly.yaml delete mode 100644 .github/workflows/release-branch-artifact.yaml delete mode 100644 .github/workflows/rich-navigation.yml delete mode 100644 .github/workflows/set-version.yaml delete mode 100644 .github/workflows/sync-branch.yaml delete mode 100644 .github/workflows/sync-wiki.yml delete mode 100644 .github/workflows/twoslash-repros.yaml delete mode 100644 .github/workflows/update-lkg.yml delete mode 100644 .github/workflows/update-package-lock.yaml create mode 100644 .gitpod.yml create mode 100644 .npmignore delete mode 100644 .vscode/extensions.json delete mode 100644 .vscode/launch.template.json create mode 100644 .vscode/settings.json delete mode 100644 .vscode/settings.template.json create mode 100644 effect/.gitignore create mode 100644 effect/package.json create mode 100644 effect/packages/package1/package.json create mode 100644 effect/packages/package1/src/class.js create mode 100644 effect/packages/package1/src/class.ts create mode 100644 effect/packages/package1/src/companion-test.ts create mode 100644 effect/packages/package1/src/companion.ts create mode 100644 effect/packages/package1/src/companions-everywhere.ts create mode 100644 effect/packages/package1/src/export-bug/1.ts create mode 100644 effect/packages/package1/src/extensions.ts create mode 100644 effect/packages/package1/src/global.ts create mode 100644 effect/packages/package1/src/hello.ts create mode 100644 effect/packages/package1/src/import-bug/1.ts create mode 100644 effect/packages/package1/src/import-bug/2.ts create mode 100644 effect/packages/package1/src/index.ts create mode 100644 effect/packages/package1/src/inheritance.ts create mode 100644 effect/packages/package1/src/intersection-inheritance.ts create mode 100644 effect/packages/package1/src/jsdoc-bug.ts create mode 100644 effect/packages/package1/src/lazy.ts create mode 100644 effect/packages/package1/src/macros.ts create mode 100644 effect/packages/package1/src/map.ts create mode 100644 effect/packages/package1/src/mutable.ts create mode 100644 effect/packages/package1/src/namespace-collection/1.ts create mode 100644 effect/packages/package1/src/no-inherit.ts create mode 100644 effect/packages/package1/src/piped.ts create mode 100644 effect/packages/package1/src/prelude.ts create mode 100644 effect/packages/package1/src/prelude/definition/Cont.ts create mode 100644 effect/packages/package1/src/prelude/definition/Effect.ts create mode 100644 effect/packages/package1/src/prelude/definition/List.ts create mode 100644 effect/packages/package1/src/prelude/definition/Maybe.ts create mode 100644 effect/packages/package1/src/prelude/definition/Sync.ts create mode 100644 effect/packages/package1/src/prelude/index.ts create mode 100644 effect/packages/package1/src/prelude/operations/access.ts create mode 100644 effect/packages/package1/src/prelude/operations/bind.ts create mode 100644 effect/packages/package1/src/prelude/operations/do.ts create mode 100644 effect/packages/package1/src/prelude/operations/failWith.ts create mode 100644 effect/packages/package1/src/prelude/operations/flatMap.ts create mode 100644 effect/packages/package1/src/prelude/operations/index.ts create mode 100644 effect/packages/package1/src/prelude/operations/map.ts create mode 100644 effect/packages/package1/src/prelude/operations/orElse.ts create mode 100644 effect/packages/package1/src/prelude/operations/pipe.ts create mode 100644 effect/packages/package1/src/prelude/operations/run.ts create mode 100644 effect/packages/package1/src/prelude/operations/succeedWith.ts create mode 100644 effect/packages/package1/src/prelude/operations/unit.ts create mode 100644 effect/packages/package1/src/prelude/operations/zip.ts create mode 100644 effect/packages/package1/src/prelude/pipeable/pipeable-dep-arg.ts create mode 100644 effect/packages/package1/src/prelude/pipeable/pipeable-index.ts create mode 100644 effect/packages/package1/src/prelude/pipeable/pipeable-operator.ts create mode 100644 effect/packages/package1/src/prelude/pipeable/pipeable-overload.ts create mode 100644 effect/packages/package1/src/prelude/pipeable/pipeable.ts create mode 100644 effect/packages/package1/src/prelude/pipeable/type-resolution.ts create mode 100644 effect/packages/package1/src/primitives.ts create mode 100644 effect/packages/package1/src/scope-name-conflict.ts create mode 100644 effect/packages/package1/src/static-extension-class.ts create mode 100644 effect/packages/package1/src/static-fluent-name-duplication.ts create mode 100644 effect/packages/package1/src/symbol-bug/1.ts create mode 100644 effect/packages/package1/src/symbol-bug/2.ts create mode 100644 effect/packages/package1/src/tailrec.ts create mode 100644 effect/packages/package1/src/type-and-static-repro/1.ts create mode 100644 effect/packages/package1/src/type-companion-global.ts create mode 100644 effect/packages/package1/src/type-driven-overloads.ts create mode 100644 effect/packages/package1/src/type-driven-overloads/T.ts create mode 100644 effect/packages/package1/src/type-driven-overloads/op1.ts create mode 100644 effect/packages/package1/src/type-driven-overloads/op2.ts create mode 100644 effect/packages/package1/src/union-inheritance.ts create mode 100644 effect/packages/package1/src/utils/LazyArgument.ts create mode 100644 effect/packages/package1/tsconfig.json create mode 100644 effect/packages/package2/fp-ts__core.json create mode 100644 effect/packages/package2/fp-ts__core_2.json create mode 100644 effect/packages/package2/package.json create mode 100644 effect/packages/package2/src/derivation/auto.ts create mode 100644 effect/packages/package2/src/derivation/block-scoped.ts create mode 100644 effect/packages/package2/src/derivation/brand.ts create mode 100644 effect/packages/package2/src/derivation/check.ts create mode 100644 effect/packages/package2/src/derivation/guard.ts create mode 100644 effect/packages/package2/src/derivation/int.ts create mode 100644 effect/packages/package2/src/derivation/refinement.ts create mode 100644 effect/packages/package2/src/derivation/show.ts create mode 100644 effect/packages/package2/src/derivation/types.ts create mode 100644 effect/packages/package2/src/derivation/use.ts create mode 100644 effect/packages/package2/src/external-definitions.ts create mode 100644 effect/packages/package2/src/globals.ts create mode 100644 effect/packages/package2/src/globals2.ts create mode 100644 effect/packages/package2/src/namespace-collection.ts create mode 100644 effect/packages/package2/src/prelude.ts create mode 100644 effect/packages/package2/src/service.ts create mode 100644 effect/packages/package2/src/test.ts create mode 100644 effect/packages/package2/src/union-inheritance-repro/Equals.ts create mode 100644 effect/packages/package2/src/union-inheritance-repro/exit-repro.ts create mode 100644 effect/packages/package2/src/via.ts create mode 100644 effect/packages/package2/tsconfig.json create mode 100644 effect/tsconfig.json create mode 100644 effect/tsplus.config.json create mode 100644 pack.sh create mode 100644 src/compiler/hashing.ts create mode 100644 src/compiler/transformers/tailRec.ts create mode 100644 src/compiler/transformers/tsplus.ts create mode 100644 src/compiler/transformers/tsplusDeclaration.ts create mode 100644 src/lib/tsplus.d.ts create mode 100644 testing/.gitignore create mode 100644 testing/package.json create mode 100644 testing/packages/.placeholder create mode 100644 testing/packages/.placeolder create mode 100644 testing/packages/demo/package.json create mode 100644 testing/packages/demo/src/bug-constant.ts create mode 100644 testing/packages/demo/src/do-bind.ts create mode 100644 testing/packages/demo/src/doc.ts create mode 100644 testing/packages/demo/src/example.ts create mode 100644 testing/packages/demo/src/fluent-guard.ts create mode 100644 testing/packages/demo/src/global.ts create mode 100644 testing/packages/demo/src/operator-repro.ts create mode 100644 testing/packages/demo/src/pipe-deps.ts create mode 100644 testing/packages/demo/src/pipe-repro.ts create mode 100644 testing/packages/demo/src/pipeable-type-node.ts create mode 100644 testing/packages/demo/src/repro-pr-117.ts create mode 100644 testing/packages/demo/tsconfig.json create mode 100644 testing/prelude.d.ts create mode 100644 testing/tsconfig.json create mode 100644 testing/tsplus.config.json create mode 100644 utils/rebase.sh diff --git a/.eslintrc.json b/.eslintrc.json index 4d268386e14..9cf7ec4db1c 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,6 +1,7 @@ { "root": true, "parser": "@typescript-eslint/parser", + "ignorePatterns": ["src/**/*.ts", "effect/**/*.ts"], "parserOptions": { "warnOnUnsupportedTypeScriptVersion": false, "sourceType": "module" diff --git a/.github/ISSUE_TEMPLATE/Bug_report.md b/.github/ISSUE_TEMPLATE/Bug_report.md deleted file mode 100644 index d920d7ec3dd..00000000000 --- a/.github/ISSUE_TEMPLATE/Bug_report.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -name: Bug -about: Create a report to help us improve TypeScript -title: '' -labels: '' -assignees: '' - ---- - -# Bug Report - - - -### 🔎 Search Terms - - - -### 🕗 Version & Regression Information - - -- This is a crash -- This changed between versions ______ and _______ -- This is the behavior in every version I tried, and I reviewed the FAQ for entries about _________ -- I was unable to test this on prior versions because _______ - -### ⏯ Playground Link - - -[Playground link with relevant code](https://www.typescriptlang.org/play?#code/PTAEFkE9QYwewCYFNQHM5IM6gBZIE5JA) - -### đŸ’ģ Code - - -```ts -// We can quickly address your report if: -// - The code sample is short. Nearly all TypeScript bugs can be demonstrated in 20-30 lines of code! -// - It doesn't use external libraries. These are often issues with the type definitions rather than TypeScript bugs. -// - The incorrectness of the behavior is readily apparent from reading the sample. -// Reports are slower to investigate if: -// - We have to pare too much extraneous code. -// - We have to clone a large repo and validate that the problem isn't elsewhere. -// - The sample is confusing or doesn't clearly demonstrate what's wrong. -``` - -### 🙁 Actual behavior - - - -### 🙂 Expected behavior - - diff --git a/.github/ISSUE_TEMPLATE/Feature_request.md b/.github/ISSUE_TEMPLATE/Feature_request.md deleted file mode 100644 index c12cf9e5241..00000000000 --- a/.github/ISSUE_TEMPLATE/Feature_request.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -name: Feature Request -about: Suggest an idea -title: '' -labels: '' -assignees: '' - ---- - -# Suggestion - - - -## 🔍 Search Terms - - - -List of keywords you searched for before creating this issue. Write them down here so that others can find this suggestion more easily and help provide feedback. - -## ✅ Viability Checklist - - -My suggestion meets these guidelines: - -* [ ] This wouldn't be a breaking change in existing TypeScript/JavaScript code -* [ ] This wouldn't change the runtime behavior of existing JavaScript code -* [ ] This could be implemented without emitting different JS based on the types of the expressions -* [ ] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.) -* [ ] This feature would agree with the rest of [TypeScript's Design Goals](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals). - - -## ⭐ Suggestion - - - -## 📃 Motivating Example - - - -## đŸ’ģ Use Cases - - diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml deleted file mode 100644 index 69db3e0326a..00000000000 --- a/.github/ISSUE_TEMPLATE/config.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -blank_issues_enabled: false -contact_links: - - - about: "Please ask and answer usage questions on Stack Overflow." - name: Question - url: "https://stackoverflow.com/questions/tagged/typescript" - - - about: "Alternatively, you can use the TypeScript Community Discord." - name: Chat - url: "https://discord.gg/typescript" - - - about: "Please check the FAQ before filing new issues" - name: "TypeScript FAQ" - url: "https://github.com/microsoft/TypeScript/wiki/FAQ" - - - about: "Please raise issues about the site on its own repo." - name: Website - url: "https://github.com/microsoft/TypeScript-Website/issues/new" diff --git a/.github/ISSUE_TEMPLATE/lib_change.md b/.github/ISSUE_TEMPLATE/lib_change.md deleted file mode 100644 index 555da167425..00000000000 --- a/.github/ISSUE_TEMPLATE/lib_change.md +++ /dev/null @@ -1,58 +0,0 @@ ---- -name: Library change -about: Fix or improve issues with built-in type definitions like `lib.dom.d.ts`, `lib.es6.d.ts`, - etc. -title: '' -labels: '' -assignees: '' - ---- - -# lib Update Request - - - -## Configuration Check - - -My compilation *target* is `ES2015` and my *lib* is `the default`. - -## Missing / Incorrect Definition - - - -## Sample Code - - - -## Documentation Link - - diff --git a/.github/codeql/codeql-configuration.yml b/.github/codeql/codeql-configuration.yml deleted file mode 100644 index f94ac494391..00000000000 --- a/.github/codeql/codeql-configuration.yml +++ /dev/null @@ -1,8 +0,0 @@ -name : CodeQL Configuration - -paths: - - src - - scripts - - Herebyfile.mjs -paths-ignore: - - src/lib diff --git a/.github/pr_owners.txt b/.github/pr_owners.txt deleted file mode 100644 index 72f6d01d02f..00000000000 --- a/.github/pr_owners.txt +++ /dev/null @@ -1,14 +0,0 @@ -sandersn -weswigham -andrewbranch -RyanCavanaugh -sheetalkamat -rbuckton -ahejlsberg -amcasey -minestarks -armanio123 -gabritto -jakebailey -DanielRosenwasser -navya9singh diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md deleted file mode 100644 index 96f77fc18d1..00000000000 --- a/.github/pull_request_template.md +++ /dev/null @@ -1,21 +0,0 @@ - - -Fixes # diff --git a/.github/workflow-resources/.lsifrc.json b/.github/workflow-resources/.lsifrc.json deleted file mode 100644 index bec9a9dd7a4..00000000000 --- a/.github/workflow-resources/.lsifrc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "project": "../../src/tsconfig.json", - "source": "../../package.json", - "package": "../../package.json", - "out": "../../typescript.lsif" -} \ No newline at end of file diff --git a/.github/workflows/accept-baselines-fix-lints.yaml b/.github/workflows/accept-baselines-fix-lints.yaml deleted file mode 100644 index cb65de2c81e..00000000000 --- a/.github/workflows/accept-baselines-fix-lints.yaml +++ /dev/null @@ -1,26 +0,0 @@ -name: Accept Baselines and Fix Lints - -on: - workflow_dispatch: {} - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - - - name: Configure Git, Run Tests, Update Baselines, Apply Fixes - run: | - git config user.email "typescriptbot@microsoft.com" - git config user.name "TypeScript Bot" - npm install - git rm -r --quiet tests/baselines/reference :^tests/baselines/reference/docker :^tests/baselines/reference/user - npx hereby runtests-parallel --ci --fix || true - npx hereby baseline-accept - git add ./src - git add ./tests/baselines/reference - git diff --cached - git commit -m "Update Baselines and/or Applied Lint Fixes" - git push diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 692d6b83a94..00000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,189 +0,0 @@ -name: CI - -on: - push: - branches: - - main - - release-* - pull_request: - branches: - - main - - release-* - -jobs: - test: - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: - - "19" - - "18" - - "16" - - "14" - bundle: - - "true" - include: - - node-version: "*" - bundle: "false" - - name: Test Node ${{ matrix.node-version }} with --bundle=${{ matrix.bundle }} - - steps: - - uses: actions/checkout@v3 - - name: Use node version ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - check-latest: true - - run: npm ci - - - name: Tests - run: npm run test -- --bundle=${{ matrix.bundle }} - - lint: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: "*" - check-latest: true - - run: npm ci - - - name: Linter - run: npm run lint - - browser-integration: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: "*" - check-latest: true - - run: npm ci - - - name: Adding playwright - run: npm install --no-save --no-package-lock playwright - - - name: Validate the browser can import TypeScript - run: npx hereby test-browser-integration - - typecheck: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: "*" - check-latest: true - - run: npm ci - - - name: Build src - run: npx hereby build-src - - smoke: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: "*" - check-latest: true - - run: npm ci - - - run: npx hereby lkg - - run: | - npm pack - mv typescript*.tgz typescript.tgz - echo "PACKAGE=$PWD/typescript.tgz" >> $GITHUB_ENV - - - name: Smoke test - run: | - cd "$(mktemp -d)" - npm init --yes - npm install $PACKAGE tslib - - echo "Testing tsc..." - npx tsc --version - - echo "Testing tsserver..." - echo '{"seq": 1, "command": "status"}' | npx tsserver - - cat > smoke.js << 'EOF' - console.log(`Testing ${process.argv[2]}...`); - const { __importDefault, __importStar } = require("tslib"); - const ts = require(process.argv[2]); - - // See: https://github.com/microsoft/TypeScript/pull/51474#issuecomment-1310871623 - const fns = [ - [() => ts.version, true], - [() => ts.default.version, false], - [() => __importDefault(ts).version, false], - [() => __importDefault(ts).default.version, true], - [() => __importStar(ts).version, true], - [() => __importStar(ts).default.version, true], - ]; - - for (const [fn, shouldSucceed] of fns) { - let success = false; - try { - success = !!fn(); - } - catch {} - const status = success ? "succeeded" : "failed"; - if (success === shouldSucceed) { - console.log(`${fn.toString()} ${status} as expected.`); - } - else { - console.log(`${fn.toString()} unexpectedly ${status}.`); - process.exitCode = 1; - } - } - console.log("ok"); - EOF - - node ./smoke.js typescript - node ./smoke.js typescript/lib/tsserverlibrary - - misc: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: "*" - check-latest: true - - run: npm ci - - - name: Build scripts - run: npx hereby scripts - - - name: ESLint tests - run: npx hereby run-eslint-rules-tests - - self-check: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: "*" - check-latest: true - - run: npm ci - - - name: Build tsc - run: npx hereby tsc - - - name: Clean - run: npx hereby clean-src - - - name: Self build - run: npx hereby build-src --built diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml deleted file mode 100644 index 806de03ae48..00000000000 --- a/.github/workflows/codeql.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: "Code Scanning - Action" - -on: - push: - branches: - - main - - release-* - pull_request: - branches: - - main - - release-* - schedule: - # ┌───────────── minute (0 - 59) - # │ ┌───────────── hour (0 - 23) - # │ │ ┌───────────── day of the month (1 - 31) - # │ │ │ ┌───────────── month (1 - 12 or JAN-DEC) - # │ │ │ │ ┌───────────── day of the week (0 - 6 or SUN-SAT) - # │ │ │ │ │ - # │ │ │ │ │ - # │ │ │ │ │ - # * * * * * - - cron: '30 1 * * 0' - -jobs: - CodeQL-Build: - # CodeQL runs on ubuntu-latest, windows-latest, and macos-latest - runs-on: ubuntu-latest - if: github.repository == 'microsoft/TypeScript' - - permissions: - # required for all workflows - security-events: write - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - # Initializes the CodeQL tools for scanning. - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - with: - config-file: ./.github/codeql/codeql-configuration.yml - # Override language selection by uncommenting this and choosing your languages - # with: - # languages: go, javascript, csharp, python, cpp, java - - # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). - # If this step fails, then you should remove it and run the build manually (see below). - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - # ℹī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - # ✏ī¸ If the Autobuild fails above, remove it and uncomment the following - # three lines and modify them (or add more) to build your code if your - # project uses a compiled language - - #- run: | - # make bootstrap - # make release - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/ensure-related-repos-run-crons.yml b/.github/workflows/ensure-related-repos-run-crons.yml deleted file mode 100644 index 265e5028bdf..00000000000 --- a/.github/workflows/ensure-related-repos-run-crons.yml +++ /dev/null @@ -1,47 +0,0 @@ -# Ensures that repos which are related to TypeScript but may not have regular commits -# have their GitHub Actions scheduled jobs still active due to the 6 week timeout -# on OSS repos. This has already triggered a few times with microsoft/TypeScript-Make-Monaco-Builds -# so, better to automate keeping on top of it. - -name: Related Repo Commit Bumps - -on: - schedule: - # Monthly, https://crontab.guru/#0_0_*_1-12_* - - cron: '0 0 1 * *' - workflow_dispatch: {} - -jobs: - build: - runs-on: ubuntu-latest - if: github.repository == 'microsoft/TypeScript' - - steps: - - name: Configure git - run: | - git config --global user.email "typescriptbot@microsoft.com" - git config --global user.name "TypeScript Bot" - - - uses: actions/checkout@v3 - with: - repository: 'microsoft/TypeScript-Website' - path: 'ts-site' - - - name: Push Commit to TS Website - run: | - cd ts-site - git commit --allow-empty -m "Monthly Bump" - git config --unset-all http.https://github.com/.extraheader - git push https://${{ secrets.TS_BOT_GITHUB_TOKEN }}@github.com/microsoft/TypeScript-Website.git - - - uses: actions/checkout@v3 - with: - repository: 'microsoft/TypeScript-Make-Monaco-Builds' - path: 'monaco-builds' - - - name: Push Commit to TS Make Monaco Builds - run: | - cd monaco-builds - git commit --allow-empty -m "Monthly Bump" - git config --unset-all http.https://github.com/.extraheader - git push https://${{ secrets.TS_BOT_GITHUB_TOKEN }}@github.com/microsoft/TypeScript-Make-Monaco-Builds.git diff --git a/.github/workflows/new-release-branch.yaml b/.github/workflows/new-release-branch.yaml deleted file mode 100644 index 9b5414a935b..00000000000 --- a/.github/workflows/new-release-branch.yaml +++ /dev/null @@ -1,35 +0,0 @@ -name: New Release Branch - -on: - repository_dispatch: - types: new-release-branch - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/setup-node@v3 - - uses: actions/checkout@v3 - with: - fetch-depth: 5 - - run: | - git checkout -b ${{ github.event.client_payload.branch_name }} - sed -i -e 's/"version": ".*"/"version": "${{ github.event.client_payload.package_version }}"/g' package.json - sed -i -e 's/const versionMajorMinor = ".*"/const versionMajorMinor = "${{ github.event.client_payload.core_major_minor }}"/g' src/compiler/corePublic.ts - sed -i -e 's/const versionMajorMinor = ".*"/const versionMajorMinor = "${{ github.event.client_payload.core_major_minor }}"/g' tests/baselines/reference/api/typescript.d.ts - sed -i -e 's/const versionMajorMinor = ".*"/const versionMajorMinor = "${{ github.event.client_payload.core_major_minor }}"/g' tests/baselines/reference/api/tsserverlibrary.d.ts - sed -i -e 's/const version\(: string\)\{0,1\} = `${versionMajorMinor}.0-.*`/const version = `${versionMajorMinor}.0-${{ github.event.client_payload.core_tag || 'dev' }}`/g' src/compiler/corePublic.ts - npm ci - npx hereby LKG - npm test - git diff - git add package.json - git add src/compiler/corePublic.ts - git add tests/baselines/reference/api/typescript.d.ts - git add tests/baselines/reference/api/tsserverlibrary.d.ts - git add ./lib - git config user.email "typescriptbot@microsoft.com" - git config user.name "TypeScript Bot" - git commit -m 'Bump version to ${{ github.event.client_payload.package_version }} and LKG' - git push --set-upstream origin ${{ github.event.client_payload.branch_name }} diff --git a/.github/workflows/nightly.yaml b/.github/workflows/nightly.yaml deleted file mode 100644 index 39949c3e8ed..00000000000 --- a/.github/workflows/nightly.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: Publish Nightly - -on: - schedule: - - cron: '0 7 * * *' - # enable users to manually trigger with workflow_dispatch - workflow_dispatch: {} - repository_dispatch: - types: publish-nightly - -jobs: - build: - runs-on: ubuntu-latest - if: github.repository == 'microsoft/TypeScript' - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - # Use NODE_AUTH_TOKEN environment variable to authenticate to this registry. - registry-url: https://registry.npmjs.org/ - - name: Setup and publish nightly - run: | - npm whoami - npm ci - npx hereby configure-nightly - npx hereby LKG - npx hereby runtests-parallel - npx hereby clean - npm publish --tag next - env: - NODE_AUTH_TOKEN: ${{secrets.npm_token}} diff --git a/.github/workflows/release-branch-artifact.yaml b/.github/workflows/release-branch-artifact.yaml deleted file mode 100644 index bbe7fab646f..00000000000 --- a/.github/workflows/release-branch-artifact.yaml +++ /dev/null @@ -1,33 +0,0 @@ -name: Create Releasable Package Drop - -on: - push: - branches: - - release-* - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - - name: npm install and test - run: | - npm ci - npm test - - name: Adding playwright - run: npm install --no-save --no-package-lock playwright - - name: Validate the browser can import TypeScript - run: npx hereby test-browser-integration - - name: LKG, clean, and pack - run: | - npx hereby LKG - npx hereby clean - npm pack ./ - mv typescript-*.tgz typescript.tgz - - name: Upload built tarfile - uses: actions/upload-artifact@v3 - with: - name: tgz - path: typescript.tgz diff --git a/.github/workflows/rich-navigation.yml b/.github/workflows/rich-navigation.yml deleted file mode 100644 index 758faea4c7d..00000000000 --- a/.github/workflows/rich-navigation.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: "Rich Navigation Indexing" -on: - workflow_dispatch: - push: - branches: - - main - - release-* - pull_request: - branches: - - main - - release-* - -jobs: - richnav: - runs-on: windows-latest - - steps: - - uses: actions/checkout@v3 - with: - fetch-depth: 5 - - - uses: actions/setup-node@v3 - - - name: Install dependencies - run: npm ci - - - uses: microsoft/RichCodeNavIndexer@v0.1 - with: - languages: typescript - repo-token: ${{ secrets.GITHUB_TOKEN }} - typescriptVersion: 0.6.0-next.18 - configFiles: .github/workflow-resources/.lsifrc.json - continue-on-error: true diff --git a/.github/workflows/set-version.yaml b/.github/workflows/set-version.yaml deleted file mode 100644 index ce7da6726eb..00000000000 --- a/.github/workflows/set-version.yaml +++ /dev/null @@ -1,41 +0,0 @@ -name: Set branch version - -on: - repository_dispatch: - types: set-version - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/setup-node@v3 - - uses: actions/checkout@v3 - with: - ref: ${{ github.event.client_payload.branch_name }} - # notably, this is essentially the same script as `new-release-branch.yaml` (with fewer inputs), but it assumes the branch already exists - # do note that executing the transform below will prevent the `configurePrerelease` script from running on the source, as it makes the - # `version` identifier no longer match the regex it uses - # required client_payload members: - # branch_name - the target branch - # package_version - the full version string (eg, `3.9.1-rc` or `3.9.2`) - # core_major_minor - the major.minor pair associated with the desired package_version (eg, `3.9` for `3.9.3`) - - run: | - sed -i -e 's/"version": ".*"/"version": "${{ github.event.client_payload.package_version }}"/g' package.json - sed -i -e 's/const versionMajorMinor = ".*"/const versionMajorMinor = "${{ github.event.client_payload.core_major_minor }}"/g' src/compiler/corePublic.ts - sed -i -e 's/const versionMajorMinor = ".*"/const versionMajorMinor = "${{ github.event.client_payload.core_major_minor }}"/g' tests/baselines/reference/api/typescript.d.ts - sed -i -e 's/const versionMajorMinor = ".*"/const versionMajorMinor = "${{ github.event.client_payload.core_major_minor }}"/g' tests/baselines/reference/api/tsserverlibrary.d.ts - sed -i -e 's/const version\(: string\)\{0,1\} = .*;/const version = "${{ github.event.client_payload.package_version }}" as string;/g' src/compiler/corePublic.ts - npm ci - npx hereby LKG - npm test - git diff - git add package.json - git add src/compiler/corePublic.ts - git add tests/baselines/reference/api/typescript.d.ts - git add tests/baselines/reference/api/tsserverlibrary.d.ts - git add ./lib - git config user.email "typescriptbot@microsoft.com" - git config user.name "TypeScript Bot" - git commit -m 'Bump version to ${{ github.event.client_payload.package_version }} and LKG' - git push diff --git a/.github/workflows/sync-branch.yaml b/.github/workflows/sync-branch.yaml deleted file mode 100644 index ce15c88121d..00000000000 --- a/.github/workflows/sync-branch.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: Sync branch with master - -on: - repository_dispatch: - types: sync-branch - workflow_dispatch: - inputs: - branch_name: - description: 'Target Branch Name' - required: true - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/setup-node@v3 - - uses: actions/checkout@v3 - with: - ref: ${{ github.event.inputs.branch_name || github.event.client_payload.branch_name }} - fetch-depth: 0 - # This does a test post-merge and only pushes the result if the test succeeds - # required client_payload members: - # branch_name - the target branch - - run: | - git config user.email "typescriptbot@microsoft.com" - git config user.name "TypeScript Bot" - git fetch origin main - git merge origin/main --no-ff - npm ci - npm test - git push diff --git a/.github/workflows/sync-wiki.yml b/.github/workflows/sync-wiki.yml deleted file mode 100644 index 8054832be0a..00000000000 --- a/.github/workflows/sync-wiki.yml +++ /dev/null @@ -1,21 +0,0 @@ -name: Sync Two Wiki Repos - -on: [gollum] - -jobs: - sync: - runs-on: ubuntu-latest - steps: - - name: Get repo name - run: R=${GITHUB_REPOSITORY%?wiki}; echo "BASENAME=${R##*/}" >> $GITHUB_ENV - - name: Checkout ${{ env.BASENAME }}-wiki - uses: actions/checkout@v3 - with: - repository: "${{ GITHUB.repository_owner }}/${{ env.BASENAME }}-wiki" - token: ${{ secrets.TS_BOT_GITHUB_TOKEN }} - fetch-depth: 0 - - name: Run sync - run: ./.github/workflows/sync - env: - PUSHER: typescript-bot - AUTH: ${{ secrets.TS_BOT_GITHUB_TOKEN }} diff --git a/.github/workflows/twoslash-repros.yaml b/.github/workflows/twoslash-repros.yaml deleted file mode 100644 index 5f6fee2402d..00000000000 --- a/.github/workflows/twoslash-repros.yaml +++ /dev/null @@ -1,38 +0,0 @@ -name: Twoslash Code Sample Repros - -on: - push: - branches: - - orta-twoslash-repros - schedule: - - cron: '0 8 * * *' - repository_dispatch: - types: run-twoslash-repros - workflow_dispatch: - inputs: - issue: - description: Limits run to a single issue. - required: false - type: string - bisect: - description: If set, runs a git bisect on an existing repro. Requires 'issue' to be set. Value can be revision labels (e.g. `good v4.7.3 bad main`) or `true` to infer bisect range. - required: false - type: string - -jobs: - run: - if: ${{ github.repository == 'microsoft/TypeScript' }} - runs-on: ubuntu-latest - steps: - - if: ${{ github.event.inputs.bisect }} - uses: actions/checkout@v3 - with: - fetch-depth: 0 - - if: ${{ !github.event.inputs.bisect }} - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - - uses: microsoft/TypeScript-Twoslash-Repro-Action@master - with: - github-token: ${{ secrets.TS_BOT_GITHUB_TOKEN }} - issue: ${{ github.event.inputs.issue }} - bisect: ${{ github.event.inputs.bisect }} diff --git a/.github/workflows/update-lkg.yml b/.github/workflows/update-lkg.yml deleted file mode 100644 index b2fd19c02d9..00000000000 --- a/.github/workflows/update-lkg.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Update LKG - -on: - workflow_dispatch: {} - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - - - name: Configure Git and Update LKG - run: | - git config user.email "typescriptbot@microsoft.com" - git config user.name "TypeScript Bot" - npm ci - npx hereby LKG - npm test - git diff - git add ./lib - git commit -m "Update LKG" - git push diff --git a/.github/workflows/update-package-lock.yaml b/.github/workflows/update-package-lock.yaml deleted file mode 100644 index 18e60ff5e5c..00000000000 --- a/.github/workflows/update-package-lock.yaml +++ /dev/null @@ -1,32 +0,0 @@ -name: Update package-lock.json - -on: - schedule: - # This is probably 6am UTC, which is 10pm PST or 11pm PDT - # Alternatively, 6am local is also fine - - cron: '0 6 * * *' - workflow_dispatch: {} - -jobs: - build: - runs-on: ubuntu-latest - if: github.repository == 'microsoft/TypeScript' - - steps: - - uses: actions/checkout@v3 - with: - token: ${{ secrets.TS_BOT_GITHUB_TOKEN }} - - uses: actions/setup-node@v3 - with: - node-version: 16 - - - name: Configure git and update package-lock.json - run: | - git config user.email "typescriptbot@microsoft.com" - git config user.name "TypeScript Bot" - rm package-lock.json - npm install --package-lock-only --ignore-scripts # This is a no-op if package-lock.json is present. - git add -f package-lock.json - if git commit -m "Update package-lock.json"; then - git push - fi diff --git a/.gitignore b/.gitignore index c6f87d986b4..cd7417f30b8 100644 --- a/.gitignore +++ b/.gitignore @@ -50,6 +50,7 @@ internal/ **/.vscode/* !**/.vscode/tasks.json !**/.vscode/settings.template.json +!**/.vscode/settings.json !**/.vscode/launch.template.json !**/.vscode/extensions.json !tests/cases/projects/projectOption/**/node_modules diff --git a/.gitpod.yml b/.gitpod.yml new file mode 100644 index 00000000000..3f40e13abae --- /dev/null +++ b/.gitpod.yml @@ -0,0 +1,6 @@ +tasks: + - init: npm install && npm run build && npm run gulp local && cd effect && npm install && npm run build + +github: + prebuilds: + addCheck: true \ No newline at end of file diff --git a/.npmignore b/.npmignore new file mode 100644 index 00000000000..f2b04bd1d4e --- /dev/null +++ b/.npmignore @@ -0,0 +1,42 @@ +built +doc +Gulpfile.js +internal +jenkins.sh +lib/README.md +lib/enu +netci.groovy +scripts +src +tests +Jakefile.js +.devcontainer +.eslintrc +.eslintignore +.editorconfig +.failed-tests +.git +.git/ +.gitattributes +.github/ +.gitmodules +.settings/ +.travis.yml +.circleci +.vscode/ +.parallelperf.json +.mailmap +test.config +package-lock.json +yarn.lock +CONTRIBUTING.md +TEST-results.xml +.dockerignore +Dockerfile +.DS_Store +.eslintrc.json +.yarnrc +tmp +effect/ +dev/ +testing/ diff --git a/.vscode/extensions.json b/.vscode/extensions.json deleted file mode 100644 index c3ea200176d..00000000000 --- a/.vscode/extensions.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "recommendations": [ - "dbaeumer.vscode-eslint", - "rbuckton.tsserver-live-reload" - ], - - "unwantedRecommendations": [ - "ms-vscode.vscode-typescript-tslint-plugin" - ] -} \ No newline at end of file diff --git a/.vscode/launch.template.json b/.vscode/launch.template.json deleted file mode 100644 index 134216381d7..00000000000 --- a/.vscode/launch.template.json +++ /dev/null @@ -1,58 +0,0 @@ -/* - - Copy this file into '.vscode/launch.json' or merge its - contents into your existing configurations. - - If you want to remove the errors in comments for all JSON - files, add this to your settings in ~/.vscode/User/settings.json - - "files.associations": { - "*.json": "jsonc" - }, - -*/ - -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "Mocha Tests (currently opened test)", - "runtimeArgs": ["--nolazy"], - "program": "${workspaceRoot}/node_modules/mocha/bin/_mocha", - "args": [ - "-u", - "bdd", - "--no-timeouts", - "--colors", - "built/local/run.js", - "-f", - // You can change this to be the name of a specific test file (without the file extension) - // to consistently launch the same test - "${fileBasenameNoExtension}", - "--skip-percent", - "0" - ], - "env": { - "NODE_ENV": "testing" - }, - "sourceMaps": true, - "smartStep": true, - "preLaunchTask": "npm: build:tests", - "console": "integratedTerminal", - "customDescriptionGenerator": "'__tsDebuggerDisplay' in this ? this.__tsDebuggerDisplay(defaultValue) : defaultValue" - }, - { - // See: https://github.com/microsoft/TypeScript/wiki/Debugging-Language-Service-in-VS-Code - "type": "node", - "request": "attach", - "name": "Attach to VS Code TS Server via Port", - "processId": "${command:PickProcess}", - "customDescriptionGenerator": "'__tsDebuggerDisplay' in this ? this.__tsDebuggerDisplay(defaultValue) : defaultValue" - } - ] -} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..1152934979b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,20 @@ +// Rename this file 'settings.json' or merge its +// contents into your existing settings. +{ + "typescript.enablePromptUseWorkspaceTsdk": true, + "eslint.validate": [ + { + "language": "typescript", + "autoFix": false + } + ], + "eslint.options": { + "rulePaths": ["./scripts/eslint/built/rules/"], + "extensions": [".ts"] + }, + // To use the last-known-good (LKG) compiler version: + // "typescript.tsdk": "lib" + + // To use the locally built compiler, after 'npm run build': + "typescript.tsdk": "built/local" +} \ No newline at end of file diff --git a/.vscode/settings.template.json b/.vscode/settings.template.json deleted file mode 100644 index 4e85591e7eb..00000000000 --- a/.vscode/settings.template.json +++ /dev/null @@ -1,23 +0,0 @@ -// Rename this file 'settings.json' or merge its -// contents into your existing settings. -{ - // To use the last-known-good (LKG) compiler version: - // "typescript.tsdk": "lib" - - // To use the locally built compiler, after 'npm run build': - // "typescript.tsdk": "built/local" - - // To ignore commits listed in .git-blame-ignore-revs in GitLens: - // "gitlens.advanced.blame.customArguments": [ - // "--ignore-revs-file", - // ".git-blame-ignore-revs" - // ] - - // These options search the repo recursively and slow down - // the build task menu. We define our own in tasks.json. - "typescript.tsc.autoDetect": "off", - "npm.autoDetect": "off", - "grunt.autoDetect": "off", - "jake.autoDetect": "off", - "gulp.autoDetect": "off" -} diff --git a/Herebyfile.mjs b/Herebyfile.mjs index 34c8e65bb44..e0512064e01 100644 --- a/Herebyfile.mjs +++ b/Herebyfile.mjs @@ -871,6 +871,12 @@ export const configureExperimental = task({ run: () => exec(process.execPath, ["scripts/configurePrerelease.mjs", "experimental", "package.json", "src/compiler/corePublic.ts"]), }); +export const configureTsPlus = task({ + name: "configure-tsplus", + description: "Runs scripts/configurePrerelease.mjs to prepare a build for tsplus publishing", + run: () => exec(process.execPath, ["scripts/configurePrerelease.mjs", "tsplus", "package.json", "src/compiler/corePublic.ts"]), +}); + export const help = task({ name: "help", description: "Prints the top-level tasks.", diff --git a/effect/.gitignore b/effect/.gitignore new file mode 100644 index 00000000000..6f467448bd6 --- /dev/null +++ b/effect/.gitignore @@ -0,0 +1 @@ +**/build \ No newline at end of file diff --git a/effect/package.json b/effect/package.json new file mode 100644 index 00000000000..2c9d57ba6cf --- /dev/null +++ b/effect/package.json @@ -0,0 +1,9 @@ +{ + "scripts": { + "postinstall": "npm run build", + "build": "cd packages/package1 && npm run build:esm && cd ../package2 && npm run build:esm" + }, + "workspaces": [ + "packages/*" + ] +} diff --git a/effect/packages/package1/package.json b/effect/packages/package1/package.json new file mode 100644 index 00000000000..94122acc152 --- /dev/null +++ b/effect/packages/package1/package.json @@ -0,0 +1,23 @@ +{ + "name": "@tsplus-test/package1", + "type": "module", + "exports": { + "./*": { + "import": "./build/esm/*.js" + } + }, + "typesVersions": { + "*": { + "*": ["./build/dts/*"] + } + }, + "scripts": { + "build:esm": "node ../../../built/local/tsc.js -p ./tsconfig.json", + "build:cjs": "esbuild `find build/esm \\( -name '*.js' \\)` --platform=node --target=node10.4 --format=cjs --outdir=build/cjs --log-level=error", + "build": "rm -rf build; npm run build:esm; npm run build:cjs" + }, + "dependencies": { + "@effect-ts/core": "^0.52.1", + "esbuild": "^0.14.12" + } +} \ No newline at end of file diff --git a/effect/packages/package1/src/class.js b/effect/packages/package1/src/class.js new file mode 100644 index 00000000000..92df8cdf881 --- /dev/null +++ b/effect/packages/package1/src/class.js @@ -0,0 +1,28 @@ +import * as tsplus_module_1 from "@tsplus-test/package1/class"; +/** + * @tsplus type IO + * @tsplus companion IOOps + */ +export class IO { + constructor(io) { + this.io = io; + } +} +/** + * @tsplus static IOOps __call + */ +export function applyIO(f) { + return new IO(f); +} +/** + * @tsplus fluent IO map + */ +export function map(self, f) { + return new IO(() => f(self.io())); +} +tsplus_module_1.map(new IO(() => 0), (n) => n + 1); +export class ExtendedIO extends IO { + constructor(io) { + super(io); + } +} diff --git a/effect/packages/package1/src/class.ts b/effect/packages/package1/src/class.ts new file mode 100644 index 00000000000..f425b8a5843 --- /dev/null +++ b/effect/packages/package1/src/class.ts @@ -0,0 +1,29 @@ +/** + * @tsplus type IO + * @tsplus companion IOOps + */ +export class IO { + constructor(readonly io: () => A) {} +} + +/** + * @tsplus static IOOps __call + */ +export function applyIO(f: () => A): IO { + return new IO(f); +} + +/** + * @tsplus fluent IO map + */ +export function map(self: IO, f: (a: A) => B) { + return new IO(() => f(self.io())) +} + +new IO(() => 0).map((n) => n + 1) + +export class ExtendedIO extends IO { + constructor(io: () => A) { + super(io) + } +} \ No newline at end of file diff --git a/effect/packages/package1/src/companion-test.ts b/effect/packages/package1/src/companion-test.ts new file mode 100644 index 00000000000..76ff01d5e1e --- /dev/null +++ b/effect/packages/package1/src/companion-test.ts @@ -0,0 +1,22 @@ +import { Arr, Chunk } from "./companion.js"; + +/** + * @tsplus static ChunkOps empty + */ +export function empty(): Chunk { + return new Arr(); +} + +/** + * @tsplus fluent Chunk append + */ +export function append_(self: Chunk, a: B): Chunk { + return new Arr(); +} + +export function f() { + const a = Chunk.empty(); + return a.append(1); +} + +const x = f().append("1"); diff --git a/effect/packages/package1/src/companion.ts b/effect/packages/package1/src/companion.ts new file mode 100644 index 00000000000..46b9d7c9364 --- /dev/null +++ b/effect/packages/package1/src/companion.ts @@ -0,0 +1,9 @@ +/** + * @tsplus type Chunk + * @tsplus companion ChunkOps + */ +export abstract class Chunk { + readonly _A!: () => A; +} + +export class Arr extends Chunk {} diff --git a/effect/packages/package1/src/companions-everywhere.ts b/effect/packages/package1/src/companions-everywhere.ts new file mode 100644 index 00000000000..14908625911 --- /dev/null +++ b/effect/packages/package1/src/companions-everywhere.ts @@ -0,0 +1,15 @@ +/** + * @tsplus type companions-everwhere/A + * @tsplus companion companions-everywhere/AOps + */ +export type A = {} + +/** + * @tsplus static companions-everywhere/AOps get + */ +export const get: A = {} + +/** + * @tsplus static companions-everywhere/AOps __call + */ +export const callA = (): A => ({}) diff --git a/effect/packages/package1/src/export-bug/1.ts b/effect/packages/package1/src/export-bug/1.ts new file mode 100644 index 00000000000..0ec07d8deb5 --- /dev/null +++ b/effect/packages/package1/src/export-bug/1.ts @@ -0,0 +1,14 @@ +/** + * @tsplus type ets/Promise + */ +export interface Promise {} + +/** + * Retrieves the value of the promise, suspending the fiber running the action + * until the result is available. + * + * @tsplus fluent ets/Promise await + */ +export function _await(_self: Promise): void {} + +export { _await as await } \ No newline at end of file diff --git a/effect/packages/package1/src/extensions.ts b/effect/packages/package1/src/extensions.ts new file mode 100644 index 00000000000..1ffc529392b --- /dev/null +++ b/effect/packages/package1/src/extensions.ts @@ -0,0 +1,13 @@ +import { LazyArgument } from "./utils/LazyArgument.js"; + +export interface Base { + maybe: (a: LazyArgument) => A +} + +export class Hello implements Base{ + maybe = (a: LazyArgument) => { + return a() + } +} + +export const res = new Hello().maybe(0) \ No newline at end of file diff --git a/effect/packages/package1/src/global.ts b/effect/packages/package1/src/global.ts new file mode 100644 index 00000000000..9e514dcd344 --- /dev/null +++ b/effect/packages/package1/src/global.ts @@ -0,0 +1,4 @@ +/** + * @tsplus global + */ +import { A } from "@tsplus-test/package1/companions-everywhere"; \ No newline at end of file diff --git a/effect/packages/package1/src/hello.ts b/effect/packages/package1/src/hello.ts new file mode 100644 index 00000000000..ecedaa16909 --- /dev/null +++ b/effect/packages/package1/src/hello.ts @@ -0,0 +1,4 @@ +/** + * @tsplus implicit + */ +export const III: "Hello" = "Hello" \ No newline at end of file diff --git a/effect/packages/package1/src/import-bug/1.ts b/effect/packages/package1/src/import-bug/1.ts new file mode 100644 index 00000000000..bc0cb971a38 --- /dev/null +++ b/effect/packages/package1/src/import-bug/1.ts @@ -0,0 +1,3 @@ +import { Effect } from "../prelude"; + +export const anEffect = Effect.succeed(0) \ No newline at end of file diff --git a/effect/packages/package1/src/import-bug/2.ts b/effect/packages/package1/src/import-bug/2.ts new file mode 100644 index 00000000000..dcea345dac7 --- /dev/null +++ b/effect/packages/package1/src/import-bug/2.ts @@ -0,0 +1,4 @@ +import { Effect } from "../prelude"; +import { anEffect } from "./1"; + +export const y = anEffect.flatMap((x) => Effect.succeed(x + 1)) \ No newline at end of file diff --git a/effect/packages/package1/src/index.ts b/effect/packages/package1/src/index.ts new file mode 100644 index 00000000000..8a94388b854 --- /dev/null +++ b/effect/packages/package1/src/index.ts @@ -0,0 +1,179 @@ +import { Effect } from "./prelude.js"; +import { Maybe } from "./prelude.js"; +import { Nothing } from "./prelude/definition/Maybe.js"; +import { pipe } from "./primitives.js"; +import { LazyArgument } from "./utils/LazyArgument.js"; + +declare const n2: number; +export const n3 = n2 > 1 ? Maybe.just("positive2" as const) : n2 > 2 ? Maybe.just("positive3" as const) : n2 > 3 ? Maybe.just("positive4" as const) : Maybe.nothing(); + +export const isPositive = (n: number) => { + return n > 1 ? Maybe.just("positive2" as const) : Maybe.nothing(); +}; + +export const isPositiveEff = (n: number) => + n > 0 ? Effect("positive") : Effect.fail("negative"); + +export const resultEither = isPositive(0).match( + () => "nope", + () => "yeah" +); + +Effect.succeedNow("A"); + +export const prog = Effect.do + .bind("a", () => Effect(0)) + .bind("b", () => Effect(1)) + .bind("c", () => Effect(2)) + .bind("d", () => Effect(4) + Effect(5)) + .map( + ({ + a, + b, + c, + d: { + tuple: [e, f], + }, + }) => `result: ${a + b + c} ${e} ${f}` + ) + .flatMap((s) => Effect(console.log(s))); + +export const result = prog | Effect.fail("error"); + +export const zipped = Effect.succeed(0) + Effect.succeed(1); + +prog.unsafeRunPromise(); + +export const xxx = pipe( + 0, + (n) => n + 1, + (n) => `hello: ${n}` +); + +const x: Maybe = Maybe.just(0); + +const y = x.isJust() ? x.value : undefined; + +x.assertJust(); + +const z = x.value; + +const z2 = x.isJust() ? x.zip(Maybe.just("ok")) : 0; + +declare global { + /** + * @tsplus type Array + */ + export interface Array {} +} + +/** + * @tsplus fluent Array map0 + */ +export function arrayFunc( + self: LazyArgument>, + f: (a: A) => A +): Array { + return self().map(f); +} + +/** + * @tsplus getter Array getter + */ +export function arrayGetter(self: Array): Array { + return self; +} + +/** + * @tsplus getter Array head + */ +export function arrayHead(self: Array): A | undefined { + return self[0]; +} +const xxx6 = [1, 2, 3].map0((a) => a); +const a = [1, 2, 3].map0((a) => a).getter.map0((a) => a).getter.head; + +/** + * @tsplus fluent Array sum + */ +export function sum(self: Array): number { + return self.reduce((prev, cur) => prev + cur, 0); +} + +/** + * @tsplus getter Array getSum + */ +export function getSum(self: Array): number { + return self.reduce((prev, cur) => prev + cur, 0); +} + +/** + * @tsplus fluent Array average + */ +export function average(self: Array): number { + return self.sum() / self.length; +} + +export const arrAvg = [0, 1].average(); +export const arrSum = [0, 1].sum(); + +const x2 = Effect(0); + +function baseConstraint>(...xs: A): unknown[] { + return xs.map0((a) => a); +} + +function id2(a: A): A { + return a; +} + +const xxx2 = id2("aaa"); + +/** + * @tsplus fluent ets/Effect identity + * @tsplus fluent Maybe identity + * @tsplus getter ets/Effect identityGetter + * @tsplus getter Maybe identityGetter + * @tsplus static ets/EffectOps identity + * @tsplus static MaybeOps identity + */ +export function identity(self: A): A { + return self; +} + +Effect.succeed(0).identity(); + +Maybe.just(0).identity(); + +Effect.succeed(0).identityGetter; + +Maybe.just(0).identityGetter; + +Maybe.identity(0); + +Effect.identity(0); + +const maybe: Maybe = Maybe.just(0); + +if (maybe.isNothing()) { + const u = maybe; + const x = maybe.assertJust(); +} + +const xx3: Nothing = { + _tag: "Nothing", +}; + +const x4 = xx3.assertJust(); + +const zzz = 0 as any; + +const zzz2 = Effect.succeed(() => zzz); + +// @ts-expect-error +const zzz3 = Effect.succeed(zzz); + +Effect.do.bind("0", () => Effect(0)).bind("1", () => Effect(1)); + +// @ts-expect-error +export const x3 = Effect.succeed(0) | 0 \ No newline at end of file diff --git a/effect/packages/package1/src/inheritance.ts b/effect/packages/package1/src/inheritance.ts new file mode 100644 index 00000000000..e950e5e07f2 --- /dev/null +++ b/effect/packages/package1/src/inheritance.ts @@ -0,0 +1,62 @@ +declare global { + /** + * @tsplus type Iterable + */ + export interface Iterable {} +} + +/** + * @tsplus type List + * @tsplus companion ListOps + */ +export class List implements Iterable { + constructor(readonly arr: A[]) {} + [Symbol.iterator] = this.arr[Symbol.iterator] +} + +export declare namespace List {} + +/** + * @tsplus type List2 + */ +export interface List2 extends List {} + +function mkList2(arr: A[]): List2 { + return { + arr, + [Symbol.iterator]: arr[Symbol.iterator] + }; +} + +/** + * @tsplus fluent Iterable map + */ +export function iterableMap(xs: Iterable, f: (a: A) => B): Iterable { + return { + *[Symbol.iterator]() { + for (const a of xs) { + yield f(a); + } + } + } +} + +const l2 = mkList2([1, 2, 3]).map((n) => n.toString()) + +// + +/** + * @tsplus fluent List mapList + */ +export function mapList_(fa: List): List { + return fa as unknown as List +} + +/** + * @tsplus static ListOps mapList + */ +export function mapList(fa: List2, f: (a: A) => A): List2 { + return fa.mapList() +} + +List.mapList(mkList2([1, 2, 3]), (n) => n); \ No newline at end of file diff --git a/effect/packages/package1/src/intersection-inheritance.ts b/effect/packages/package1/src/intersection-inheritance.ts new file mode 100644 index 00000000000..6efab5d420a --- /dev/null +++ b/effect/packages/package1/src/intersection-inheritance.ts @@ -0,0 +1,51 @@ +// collection + +/** + * @tsplus type intersection-inheritance/A + */ +export interface A extends B { + readonly _A: unique symbol +} + +/** + * @tsplus type intersection-inheritance/B + */ +export interface B { + readonly _B: unique symbol +} + +/** + * @tsplus type intersection-inheritance/D + */ +export interface D extends B { + readonly _D: unique symbol +} + +/** + * @tsplus type intersection-inheritance/C + */ +export type C = A & D + +/** + * @tsplus fluent intersection-inheritance/A fn + */ +export declare function fnA(self: A): void + +/** + * @tsplus fluent intersection-inheritance/B fn + */ +export declare function fnB(self: B): void + +/** + * @tsplus fluent intersection-inheritance/C fn + */ +export declare function fnC(self: C): void + +/** + * @tsplus fluent intersection-inheritance/D fn + */ +export declare function fnD(self: D): void + +declare const c: C + +c.fn() \ No newline at end of file diff --git a/effect/packages/package1/src/jsdoc-bug.ts b/effect/packages/package1/src/jsdoc-bug.ts new file mode 100644 index 00000000000..cd66e8cdcbc --- /dev/null +++ b/effect/packages/package1/src/jsdoc-bug.ts @@ -0,0 +1,9 @@ +/** + * A comment + */ +/** + * More comment text + * @tsplus static x x + */ +export function x() { +} \ No newline at end of file diff --git a/effect/packages/package1/src/lazy.ts b/effect/packages/package1/src/lazy.ts new file mode 100644 index 00000000000..f7a0305e806 --- /dev/null +++ b/effect/packages/package1/src/lazy.ts @@ -0,0 +1,10 @@ +import { LazyArgument } from "./utils/LazyArgument"; + +export function lazyIn(_: LazyArgument) { + return _ +} + +lazyIn(1) + +// @ts-expect-error +lazyIn("ok") \ No newline at end of file diff --git a/effect/packages/package1/src/macros.ts b/effect/packages/package1/src/macros.ts new file mode 100644 index 00000000000..5c17014239e --- /dev/null +++ b/effect/packages/package1/src/macros.ts @@ -0,0 +1,27 @@ +/** + * @tsplus type Macros + */ +export interface Macros {} + +declare const macros: Macros + +/** + * @tsplus macro identity + * @tsplus getter Macros identity + */ +export function identity(a: A): A { + return a +} + +/** + * @tsplus macro remove + */ +function concrete(u: unknown): asserts u is any { + // +} + +macros.identity + +identity(0); + +concrete(0); \ No newline at end of file diff --git a/effect/packages/package1/src/map.ts b/effect/packages/package1/src/map.ts new file mode 100644 index 00000000000..7890c751749 --- /dev/null +++ b/effect/packages/package1/src/map.ts @@ -0,0 +1,25 @@ +import { Maybe } from "./prelude" + +/** + * @tsplus type HashMap + */ +export interface HashMap { + readonly _K: () => K + readonly _V: () => V +} + +/** + * @tsplus index HashMap + */ +export declare function get(self: HashMap, index: Key): Maybe + +export interface Key { + k: string +} + +export declare function key(s: string): Key + +export declare const myMap: HashMap + + +const ok = myMap[key("ok")].value \ No newline at end of file diff --git a/effect/packages/package1/src/mutable.ts b/effect/packages/package1/src/mutable.ts new file mode 100644 index 00000000000..45d53966e72 --- /dev/null +++ b/effect/packages/package1/src/mutable.ts @@ -0,0 +1,28 @@ +import { LazyArgument } from "./utils/LazyArgument" + +/** + * @tsplus type Chunk + */ +export interface Chunk { + _: () => A +} + +/** + * @tsplus type ChunkOps + */ +export interface ChunkOps { } +export const Chunk: ChunkOps = {} + +/** + * @tsplus operator Chunk + + */ +export declare function add(self: Chunk, that: LazyArgument>): Chunk + +/** + * @tsplus static ChunkOps __call + */ +export declare function make(...as: As): Chunk + + +let x = Chunk(0) +x = x + Chunk(1, 2) \ No newline at end of file diff --git a/effect/packages/package1/src/namespace-collection/1.ts b/effect/packages/package1/src/namespace-collection/1.ts new file mode 100644 index 00000000000..9df9c64c6f3 --- /dev/null +++ b/effect/packages/package1/src/namespace-collection/1.ts @@ -0,0 +1,13 @@ +export declare namespace A { + /** + * @tsplus type namespace-collection/T + */ + interface T {} +} + +/** + * @tsplus fluent namespace-collection/T func + */ +export function func(self: A.T): void { + return undefined; +} diff --git a/effect/packages/package1/src/no-inherit.ts b/effect/packages/package1/src/no-inherit.ts new file mode 100644 index 00000000000..d19ea4e94f1 --- /dev/null +++ b/effect/packages/package1/src/no-inherit.ts @@ -0,0 +1,24 @@ +/** + * @tsplus type no-inherit/A + */ +export interface A { + readonly _X: X +} + +/** + * @tsplus type no-inherit/B + * @tsplus no-inherit no-inherit/A + */ +export type B = A> + +/** + * @tsplus fluent no-inherit/A fnA + */ +export declare function fnA(_: A): A + +/** + * @tsplus fluent no-inherit/B fnB + */ +export declare function fnB(_: B): B + +declare const b: B \ No newline at end of file diff --git a/effect/packages/package1/src/piped.ts b/effect/packages/package1/src/piped.ts new file mode 100644 index 00000000000..4b852098386 --- /dev/null +++ b/effect/packages/package1/src/piped.ts @@ -0,0 +1,6 @@ +import { Managed } from "@effect-ts/core"; +import { Effect } from "./prelude"; + +export const effect = Effect.succeed(0) >>> Effect.$.chain((n) => Effect.succeed(0 + n)) >>> Effect.$.chain((n) => Effect.succeed(() => { console.log(n) })) + +export const managed = effect >>> Managed.makeExit((s) => Effect.succeed(s)); diff --git a/effect/packages/package1/src/prelude.ts b/effect/packages/package1/src/prelude.ts new file mode 100644 index 00000000000..5c4f0285c1e --- /dev/null +++ b/effect/packages/package1/src/prelude.ts @@ -0,0 +1,3 @@ +export * from "./prelude/index.js" +export * from "./prelude/operations/index.js" +export * from "./prelude/pipeable/pipeable.js" \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/definition/Cont.ts b/effect/packages/package1/src/prelude/definition/Cont.ts new file mode 100644 index 00000000000..4fd75f37998 --- /dev/null +++ b/effect/packages/package1/src/prelude/definition/Cont.ts @@ -0,0 +1,9 @@ +export type Cont = + | [ + len: number, + children: [K, V][], + i: number, + f: (node: readonly [K, V]) => A, + cont: Cont, + ] + | undefined; \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/definition/Effect.ts b/effect/packages/package1/src/prelude/definition/Effect.ts new file mode 100644 index 00000000000..06935136c6a --- /dev/null +++ b/effect/packages/package1/src/prelude/definition/Effect.ts @@ -0,0 +1,55 @@ +import { Managed } from "@effect-ts/core"; +import * as _ from "@effect-ts/core/Effect"; +import { suspend } from "@effect-ts/core/Effect"; + +/** + * @tsplus type ets/Effect + */ +export interface Effect extends _.Effect { +} +/** + * @tsplus type ets/EffectOps + */ +export interface EffectOps { ["$"]: EffectAspects } +export const Effect: EffectOps = { "$": {} }; + +/** + * @tsplus type ets/EffectAspects + */ +export interface EffectAspects { } + +/** + * @tsplus static ets/EffectOps pipe + */ +export const T: EffectAspects = {} + +/** + * @tsplus unify ets/Effect + */ +export function unifyEffect>(self: X): Effect< + [X] extends [Effect] ? R : never, + [X] extends [Effect] ? E : never, + [X] extends [Effect] ? A : never +> { + return self +} + +/** + * Pipes an Effect returning function + * + * @tsplus fluent ets/Effect via + * @tsplus operator ets/Effect >>> + */ +export function via(self: Effect, f: (a: Effect) => Effect): Effect { + return suspend(() => f(self)) +} + +/** + * Pipes a Managed returning function + * + * @tsplus fluent ets/Effect via + * @tsplus operator ets/Effect >>> + */ +export function viaManaged(self: Effect, f: (a: Effect) => Managed.Managed): Managed.Managed { + return Managed.suspend(() => f(self)) +} diff --git a/effect/packages/package1/src/prelude/definition/List.ts b/effect/packages/package1/src/prelude/definition/List.ts new file mode 100644 index 00000000000..f9a6d3cde4d --- /dev/null +++ b/effect/packages/package1/src/prelude/definition/List.ts @@ -0,0 +1,138 @@ +import { pipe } from "../../primitives"; +import { LazyArgument } from "../../utils/LazyArgument"; + +/** + * @tsplus type tsplus-tests/List/Cons + */ +export class Cons implements Iterable { + readonly _A!: () => A; + constructor(readonly array: readonly A[]) {} + + [Symbol.iterator]!: () => Iterator; +} + +/** + * @tsplus type tsplus-tests/List/Nil + */ +export class Nil implements Iterable { + readonly _A!: () => A; + readonly array = []; + [Symbol.iterator]!: () => Iterator; +} + +/** + * @tsplus type tsplus-tests/List + */ +export type List = Nil | Cons; + +/** + * @tsplus type tsplus-tests/ListOps + */ +export interface ListOps {} + +export const List: ListOps = {}; + +/** + * @tsplus unify tsplus-tests/List/Cons + * @tsplus unify tsplus-tests/List/Nil + */ +export function unifyList>( + self: X +): List<[X] extends [{ _A: () => infer A }] ? A : never> { + return self; +} + +/** + * @tsplus index tsplus-tests/List + */ +export function indexAt(self: List, index: number): A | undefined { + return self.array[index]; +} + +/** + * @tsplus static tsplus-tests/ListOps from + * @tsplus static tsplus-tests/ListOps __call + */ +export function make(...as: As): List { + return new Cons(as); +} + +pipe(List.from(1, 2, 3), List.from); + +/** + * Concatenates `List` and `List` + * @tsplus operator tsplus-tests/List + + * @tsplus fluent tsplus-tests/List concat + */ +export function concat(self: List, that: List): List { + return new Cons([...self.array, ...that.array]); +} + +/** + * Prepends `a: A` to `List` + * @tsplus operator tsplus-tests/List + + */ +export function prependTo(a: A, self: List): List { + return new Cons([a, ...self.array]); +} + +/** + * Prepends `a: A` to `List` + * @tsplus fluent tsplus-tests/List prepend + */ +export function prepend(self: List, a: A): List { + return new Cons([a, ...self.array]); +} + +/** + * Appends `a: A` to `List` + * @tsplus operator tsplus-tests/List + + * @tsplus fluent tsplus-tests/List append + */ +export function append_(self: List, a: A): List { + return new Cons([...self.array, a]); +} + +/** + * @tsplus fluent Iterable flatMap + */ +export function iterableFlatMap_( + self: Iterable, + f: (a: A) => Iterable +): Iterable { + return { + *[Symbol.iterator]() { + for (const a of self) { + yield* f(a); + } + }, + }; +} + +/** + * @tsplus fluent tsplus-tests/List flatMap + */ +export function flatMap_(self: List, f: (a: A) => List): List { + return new Cons(self.array.flatMap((a) => f(a).array)); +} + +export const append = Pipeable(append_); + +export const prepended = 1 + List(0); // prepend +export const appended = List(0) + 1; // append +export const sequenced = List(0) + List(1); // concat + +export const flatMapped = List(0, 1, 2).flatMap((n) => [n + 1, n + 2, n + 3]); // iterableFlatMap_ + +function appending() { + let x = List(0); + for (let j = 0; j < 10; j++) { + x = x + 1; + } +} + +function i(l: LazyArgument) { + return l(); +} + +const x = i(make(0)); diff --git a/effect/packages/package1/src/prelude/definition/Maybe.ts b/effect/packages/package1/src/prelude/definition/Maybe.ts new file mode 100644 index 00000000000..1933b529416 --- /dev/null +++ b/effect/packages/package1/src/prelude/definition/Maybe.ts @@ -0,0 +1,151 @@ +import { LazyArgument } from "../../utils/LazyArgument"; + +/** + * @tsplus type Nothing + */ +export interface Nothing { + readonly _tag: "Nothing"; +} + +/** + * @tsplus type Just + */ +export interface Just { + readonly _tag: "Just"; + readonly value: A; +} + +/** + * @tsplus type Maybe + */ +export type Maybe = Nothing | Just; + +/** + * @tsplus type MaybeOps + */ +export interface MaybeOps { } + +export const Maybe: MaybeOps = {}; + +/** + * @tsplus unify Nothing + * @tsplus unify Just + */ +export function unifyMaybe>( + self: X +): Maybe { + return self; +} + +/** + * @tsplus static MaybeOps nothing + */ +export const nothing = (): Maybe => { + return { _tag: "Nothing" }; +}; + +/** + * @tsplus static MaybeOps just + */ +export const just = (value: LazyArgument): Maybe => { + return { _tag: "Just", value: value() }; +}; + +/** + * @tsplus fluent Maybe match + */ +export const match_ = ( + self: Maybe, + onNothing: () => B, + onJust: (a: A) => C +): B | C => { + return self._tag === "Nothing" ? onNothing() : onJust(self.value); +}; + +/** + * @tsplus operator Maybe + + * @tsplus fluent Maybe zip + */ +export function zip_( + self: Maybe, + that: Maybe +): Maybe { + throw new Error("unimplemented"); +} + +/** + * @tsplus fluent Maybe isJust + */ +export function isJust(self: Maybe): self is Just { + return self._tag === "Just"; +} + +/** + * @tsplus fluent Maybe isNothing + */ +export function isNothing(self: Maybe): self is Nothing { + return self._tag === "Nothing"; +} + +/** + * @tsplus fluent Maybe assertJust + */ +export function assertJust(self: Maybe): asserts self is Just { + if (self._tag !== "Just") { + throw new Error("Not Just"); + } +} + +/** + * @tsplus getter Maybe value + */ +export function value(self: Maybe) { + return self._tag === "Just" ? self.value : void 0; +} + +/** + * @tsplus fluent Nothing assertJust + */ +export function assertJustNothing(self: Nothing): never { + throw new Error("Not Just"); +} + +// /** +// * @tsplus getter Maybe value +// */ +// export function get(self: Maybe): A | undefined { +// return self._tag === "Just" ? self.value : undefined +// } + +export const result = Maybe.just(0).match( + () => 0, + () => 1 +); +export const op = Maybe.just(0) + Maybe.just(1); + +/** + * @tsplus pipeable Maybe map + */ +export function map(f: (a: A) => B) { + return (self: Maybe): Maybe => + self.isJust() ? Maybe.just(f(self.value)) : Maybe.nothing(); +} + +/** + * @tsplus pipeable Maybe flatMap + */ +export function flatMap(f: (a: A) => Maybe) { + return (self: Maybe): Maybe => + self.isJust() ? f(self.value) : Maybe.nothing(); +} + +export const useDo = Do(($) => { + const x = $(Maybe.just(0)); + const y = $(Maybe.just(1)); + return x + y; +}); + +export const useDoDestructure = Do(($) => { + const { x } = $(Maybe.just({ x: 0 })); + return x; +}); diff --git a/effect/packages/package1/src/prelude/definition/Sync.ts b/effect/packages/package1/src/prelude/definition/Sync.ts new file mode 100644 index 00000000000..de43f04d574 --- /dev/null +++ b/effect/packages/package1/src/prelude/definition/Sync.ts @@ -0,0 +1,86 @@ +import { Tuple } from "@effect-ts/core/Collections/Immutable/Tuple"; +import * as _ from "@effect-ts/core/Sync"; +import { LazyArgument } from "../../utils/LazyArgument"; +import { Effect } from "./Effect"; + +/** + * @tsplus type ets/Sync + */ +export interface Sync extends _.Sync, Effect { } +/** + * @tsplus type ets/SyncOps + */ +export interface SyncOps { } +export const Sync: SyncOps = {}; + +/** + * @tsplus type ets/SyncAspects + */ +export interface SyncAspects { } + +/** + * @tsplus static ets/SyncOps pipe + */ +export const T: SyncAspects = {} + +/** + * @tsplus unify ets/Sync + */ +export function unifySync>(self: X): Sync< + [X] extends [Sync] ? R : never, + [X] extends [Sync] ? E : never, + [X] extends [Sync] ? A : never +> { + return self +} + +/** + * Sequentially zips this effect with the specified effect + * + * @tsplus operator ets/Sync + + * @tsplus fluent ets/Sync zip + */ +export declare function zip_(self: Sync, that: Sync, __tsplusTrace?: string): Sync> + +/** + * Imports a synchronous side-effect into a pure value + * + * @tsplus static ets/SyncOps __call + * @tsplus static ets/SyncOps succeed + * @tsplus static ets/SyncAspects succeed + */ +export declare function succeedWith(effect: LazyArgument, __tsplusTrace?: string): Sync + +/** + * Returns an effect that models the execution of this effect, followed by + * the passing of its value to the specified continuation function `f`, + * followed by the effect that it returns. + * + * @tsplus fluent ets/Sync flatMap + */ +export declare function chain_( + self: Sync, + f: (a: A) => Sync, + __tsplusTrace?: string +): Sync + +export const unifiedEffect = (n: number) => { + if (n > 0) { + return 0 as unknown as Effect<{ a: number }, "a", 0> + } + return 0 as unknown as Sync<{ b: number }, "b", 1> +} + +export const unifiedSync = (n: number) => { + if (n > 0) { + return 0 as unknown as Sync<{ a: number }, "a", 0> + } + return 0 as unknown as Sync<{ b: number }, "b", 1> +} + +export const resSync = unifiedSync(0).flatMap((n) => Sync(n + 1)) +export const resEffect = unifiedSync(0).flatMap((n) => Effect(n + 1)) + +export const resSyncZip = Sync(0) + Sync(1) + +export const resEffectZip = Sync(0) + Effect(1) \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/index.ts b/effect/packages/package1/src/prelude/index.ts new file mode 100644 index 00000000000..8c6a5ab1eda --- /dev/null +++ b/effect/packages/package1/src/prelude/index.ts @@ -0,0 +1,2 @@ +export { Effect, T } from "./definition/Effect.js" +export { Maybe } from "./definition/Maybe.js" \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/operations/access.ts b/effect/packages/package1/src/prelude/operations/access.ts new file mode 100644 index 00000000000..bc2886a4db5 --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/access.ts @@ -0,0 +1,12 @@ +import * as T from "@effect-ts/core/Effect"; +import { Effect } from "../definition/Effect"; +import { LazyArgument } from "../../utils/LazyArgument.js"; + +/** + * Effectfully accesses the environment of the effect. + * + * @tsplus static ets/EffectOps access + */ +export function access(f: (_: R0) => A, __tsplusTrace?: string): Effect { + return T.access(f, __tsplusTrace); +} diff --git a/effect/packages/package1/src/prelude/operations/bind.ts b/effect/packages/package1/src/prelude/operations/bind.ts new file mode 100644 index 00000000000..1f8e9f03400 --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/bind.ts @@ -0,0 +1,16 @@ +/* eslint-disable */ +import * as T from "@effect-ts/core/Effect" +import { Flat } from "@effect-ts/core/Has" +import { Effect } from "../definition/Effect.js" + +/** + * Binds an effectful value in a `do` scope + * + * @tsplus fluent ets/Effect bind + */ +export function bind_, N extends string>(self: Effect, tag: Exclude, f: (_: K) => Effect, __tsplusTrace?: string): Effect> { + // @ts-expect-error + return T.bind_(self, tag, f, __tsplusTrace) +} \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/operations/do.ts b/effect/packages/package1/src/prelude/operations/do.ts new file mode 100644 index 00000000000..bd3f799e665 --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/do.ts @@ -0,0 +1,9 @@ +import * as T from "@effect-ts/core/Effect"; +import { Effect } from "../definition/Effect.js"; + +/** + * Creates a do context, to be used with bind/let + * + * @tsplus static ets/EffectOps do + */ +export const do_: Effect = T.do; diff --git a/effect/packages/package1/src/prelude/operations/failWith.ts b/effect/packages/package1/src/prelude/operations/failWith.ts new file mode 100644 index 00000000000..8f3f0a92b2d --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/failWith.ts @@ -0,0 +1,13 @@ +import * as T from "@effect-ts/core/Effect"; +import { Effect } from "../definition/Effect"; +import { LazyArgument } from "../../utils/LazyArgument.js"; + +/** + * Returns an effect that models failure with the specified error. + * The moral equivalent of `throw` for pure code. + * + * @tsplus static ets/EffectOps fail + */ +export function failWith(e: LazyArgument, __tsplusTrace?: string): Effect { + return T.failWith(e, __tsplusTrace); +} diff --git a/effect/packages/package1/src/prelude/operations/flatMap.ts b/effect/packages/package1/src/prelude/operations/flatMap.ts new file mode 100644 index 00000000000..9a76ff636fe --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/flatMap.ts @@ -0,0 +1,17 @@ +import * as T from "@effect-ts/core/Effect"; +import { Effect } from "../definition/Effect.js"; + +/** + * Returns an effect that models the execution of this effect, followed by + * the passing of its value to the specified continuation function `f`, + * followed by the effect that it returns. + * + * @tsplus fluent ets/Effect flatMap + */ +export function chain_( + self: Effect, + f: (a: A) => Effect, + __tsplusTrace?: string +): Effect { + return T.chain_(self, f, __tsplusTrace); +} \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/operations/index.ts b/effect/packages/package1/src/prelude/operations/index.ts new file mode 100644 index 00000000000..5aaa5f5b592 --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/index.ts @@ -0,0 +1,10 @@ +export * from "./bind.js" +export * from "./do.js" +export * from "./failWith.js" +export * from "./flatMap.js" +export * from "./map.js" +export * from "./succeedWith.js" +export * from "./unit.js" +export * from "./run.js" +export * from "./zip.js" +export * from "./access.js" \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/operations/map.ts b/effect/packages/package1/src/prelude/operations/map.ts new file mode 100644 index 00000000000..b3ca74904a7 --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/map.ts @@ -0,0 +1,10 @@ +import { Effect } from "../definition/Effect.js"; + +/** + * Returns an effect whose success is mapped by the specified `f` function. + * + * @tsplus fluent ets/Effect map + */ +export function map_(self: Effect, f: (a: A) => B, __tsplusTrace?: string): Effect { + return self.flatMap((a) => Effect.succeed(f(a))); +} diff --git a/effect/packages/package1/src/prelude/operations/orElse.ts b/effect/packages/package1/src/prelude/operations/orElse.ts new file mode 100644 index 00000000000..71f2c907b89 --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/orElse.ts @@ -0,0 +1,14 @@ +/* eslint-disable */ +import * as T from "@effect-ts/core/Effect" +import { Effect } from "../definition/Effect.js" +import { LazyArgument } from "../../utils/LazyArgument.js" + +/** + * Sequentially zips this effect with the specified effect + * + * @tsplus operator ets/Effect | + * @tsplus fluent ets/Effect orElse + */ +export const orElse_ = (self: Effect, that: LazyArgument>, __tsplusTrace?: string) => { + return T.orElse_(self, that, __tsplusTrace) +} \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/operations/pipe.ts b/effect/packages/package1/src/prelude/operations/pipe.ts new file mode 100644 index 00000000000..e8479acd5e3 --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/pipe.ts @@ -0,0 +1,8 @@ +import { Effect } from "../definition/Effect" +import { flatMap } from "../pipeable/pipeable" + +export const res = Effect.succeed(0)( + flatMap((n) => Effect.succeed(n + 1)), + flatMap((n) => Effect.succeed(n + 1)), + flatMap((n) => Effect.succeed(`${n + 1}`)) +) \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/operations/run.ts b/effect/packages/package1/src/prelude/operations/run.ts new file mode 100644 index 00000000000..bbcaeeb4fd4 --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/run.ts @@ -0,0 +1,11 @@ +import * as T from "@effect-ts/core/Effect"; +import { Effect } from "../definition/Effect"; + +/** + * Runs an effect as a promise + * + * @tsplus fluent ets/Effect unsafeRunPromise + */ +export function unsafeRunPromise(self: Effect, __tsplusTrace?: string) { + return T.runPromise(self) +} diff --git a/effect/packages/package1/src/prelude/operations/succeedWith.ts b/effect/packages/package1/src/prelude/operations/succeedWith.ts new file mode 100644 index 00000000000..2a861d551e0 --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/succeedWith.ts @@ -0,0 +1,34 @@ +import * as T from "@effect-ts/core/Effect"; +import { Effect } from "../definition/Effect"; +import { LazyArgument } from "../../utils/LazyArgument.js"; + +/** + * Imports a synchronous side-effect into a pure value + * + * @tsplus static ets/EffectOps succeed + * @tsplus static ets/EffectAspects succeed + */ +export function succeedWith(effect: LazyArgument, __tsplusTrace?: string): Effect { + return T.succeedWith(effect, __tsplusTrace); +} + +/** + * Imports a synchronous side-effect into a pure value + */ +export function effectApply(effect: LazyArgument, __tsplusTrace?: string): Effect { + return T.succeedWith(effect, __tsplusTrace); +} + +/** + * @tsplus static ets/EffectOps __call + */ +export const effectCall = effectApply + +/** + * Imports a pure value into an Effect + * + * @tsplus static ets/EffectOps succeedNow + */ +export function succeedNow(value: A, __tsplusTrace?: string): Effect { + return T.succeed(value, __tsplusTrace); +} \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/operations/unit.ts b/effect/packages/package1/src/prelude/operations/unit.ts new file mode 100644 index 00000000000..d47d9ba74c8 --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/unit.ts @@ -0,0 +1,9 @@ +import * as T from "@effect-ts/core/Effect"; +import { Effect } from "../definition/Effect.js"; + +/** + * Returns the effect resulting from mapping the success of this effect to unit. + * + * @tsplus static ets/EffectOps unit + */ +export const unit: Effect = T.unit; diff --git a/effect/packages/package1/src/prelude/operations/zip.ts b/effect/packages/package1/src/prelude/operations/zip.ts new file mode 100644 index 00000000000..0185f748ff6 --- /dev/null +++ b/effect/packages/package1/src/prelude/operations/zip.ts @@ -0,0 +1,14 @@ +/* eslint-disable */ +import * as T from "@effect-ts/core/Effect" +import * as Tp from "@effect-ts/core/Collections/Immutable/Tuple" +import { Effect } from "../definition/Effect.js" + +/** + * Sequentially zips this effect with the specified effect + * + * @tsplus operator ets/Effect + + * @tsplus fluent ets/Effect zip + */ +export function zip_(self: Effect, that: Effect, __tsplusTrace?: string): Effect> { + return T.zip_(self, that, __tsplusTrace) +} \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/pipeable/pipeable-dep-arg.ts b/effect/packages/package1/src/prelude/pipeable/pipeable-dep-arg.ts new file mode 100644 index 00000000000..d7d510a93ea --- /dev/null +++ b/effect/packages/package1/src/prelude/pipeable/pipeable-dep-arg.ts @@ -0,0 +1,5 @@ +export function dependentArg_(self: A, b: B) { + return b +} + +export const dependentArg = Pipeable(dependentArg_) \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/pipeable/pipeable-index.ts b/effect/packages/package1/src/prelude/pipeable/pipeable-index.ts new file mode 100644 index 00000000000..fc44058f3d8 --- /dev/null +++ b/effect/packages/package1/src/prelude/pipeable/pipeable-index.ts @@ -0,0 +1,23 @@ +/** + * @tsplus type pipeable-index/X + */ +export interface X {} + +// /** +// * @tsplus index pipeable-index/X +// */ +// export declare function f(self: X, index: number): void + +// /** +// * @tsplus pipeable-index pipeable-index/X +// */ +// export declare function fn(index: number): (self: X) => string + +/** + * @tsplus pipeable-index pipeable-index/X + */ +export declare const fn: (index: number) => (self: X) => string + +declare const x: X + +const y = x[1] diff --git a/effect/packages/package1/src/prelude/pipeable/pipeable-operator.ts b/effect/packages/package1/src/prelude/pipeable/pipeable-operator.ts new file mode 100644 index 00000000000..14963ffad89 --- /dev/null +++ b/effect/packages/package1/src/prelude/pipeable/pipeable-operator.ts @@ -0,0 +1,26 @@ +/** + * @tsplus type pipeable-operator/T + */ +export interface T { + readonly __brand: unique symbol +} + +/** + * @tsplus pipeable-operator pipeable-operator/T + 1 + */ +export const op1 = (that: T) => (self: T): void => {} + +/** + * @tsplus pipeable-operator pipeable-operator/T + + */ +export const op2 = (that: Date) => (self: T): void => {} + +declare const t1: T +declare const t2: T + +const test1 = t1 + t2 + +const test2 = t1 + new Date() + + + diff --git a/effect/packages/package1/src/prelude/pipeable/pipeable-overload.ts b/effect/packages/package1/src/prelude/pipeable/pipeable-overload.ts new file mode 100644 index 00000000000..aaf79f138ee --- /dev/null +++ b/effect/packages/package1/src/prelude/pipeable/pipeable-overload.ts @@ -0,0 +1,21 @@ +/** + * @tsplus type pipeable-overload/T + */ +export interface T {} + +/** + * @tsplus pipeable pipeable-overload/T f + */ +export const f1 = (x: number) => (self: T): void => {} + +/** + * @tsplus pipeable pipeable-overload/T f + */ +export const f2 = (x: string) => (self: T): void => {} + +declare const t: T + +t.f("1") + +t.f(1) + diff --git a/effect/packages/package1/src/prelude/pipeable/pipeable.ts b/effect/packages/package1/src/prelude/pipeable/pipeable.ts new file mode 100644 index 00000000000..dfc9421cad3 --- /dev/null +++ b/effect/packages/package1/src/prelude/pipeable/pipeable.ts @@ -0,0 +1,55 @@ +import { pipe } from "../../primitives"; +import { Effect, T } from "../definition/Effect"; +import { bind_ } from "../operations/bind"; +import { chain_ } from "../operations/flatMap.js"; + +/** + * @tsplus static ets/EffectAspects chain + */ +export const chain = Pipeable(chain_); + +/** + * @tsplus pipeable ets/Effect flatMap + */ +export const flatMap = (f: (a: A) => Effect) => ( + effect: Effect +): Effect => effect.flatMap(f); + +/** + * @tsplus static ets/EffectAspects bind + */ +export const bind = Pipeable(bind_); + +export const ok = pipe( + T.succeed(0), + T.chain(n => Effect(n + 1)), + chain(n => Effect(n + 1)), +); + +const ok2 = T.chain((n: number) => Effect(n + 1))(T.succeed(0)); + +/** + * @tsplus pipeable ets/Effect chainPipeable + */ +export const chainPipeable = (f: (a: A) => Effect) => < + R, + E +>( + effect: Effect +): Effect => effect.flatMap(f); + +const x = Effect(1).chainPipeable(n => Effect(2)); + +ok.unsafeRunPromise().then(x => console.log(x)); + + +/** + * @tsplus pipeable ets/Effect zipWithAll + */ +export declare const zipWithAll: (...args: As) => < + R, + E, + A +>( + effect: Effect +) => Effect \ No newline at end of file diff --git a/effect/packages/package1/src/prelude/pipeable/type-resolution.ts b/effect/packages/package1/src/prelude/pipeable/type-resolution.ts new file mode 100644 index 00000000000..ed0ab4bd4c4 --- /dev/null +++ b/effect/packages/package1/src/prelude/pipeable/type-resolution.ts @@ -0,0 +1,36 @@ +import { die } from '@effect-ts/core/Effect'; +import { Predicate, Refinement } from '@effect-ts/core/Function'; +import { LazyArgument } from '../../utils/LazyArgument'; +import { Effect } from '../definition/Effect'; + +/** + * @tsplus pipeable ets/Effect filterOrElse + */ +export declare function filterOrElse(p: Refinement, or: (a: Exclude) => Effect, __trace?: string): (fa: Effect) => Effect; +export declare function filterOrElse(p: Predicate, or: (a: A) => Effect, __trace?: string): (fa: Effect) => Effect; + +export function filterOrDie(p: Refinement, dieWith: LazyArgument, __trace?: string): (fa: Effect) => Effect; +export function filterOrDie(p: Predicate, dieWith: LazyArgument, __trace?: string): (fa: Effect) => Effect; +export function filterOrDie(p: Predicate, dieWith: LazyArgument, __trace?: string) { + return (fa: Effect): Effect => fa.filterOrElse(p, () => die(dieWith())) +} + +Effect.succeed(1).filterOrElse((n) => true, () => Effect.succeed("")) + +/** + * @tsplus type pipeable/type-resolution/Test + */ +export type Test = number & { + readonly Test: unique symbol +} + +/** + * @tsplus pipeable pipeable/type-resolution/Test isEnabled + */ +export function isEnabled(flag: number) { + return (self: Test) => !!(self & flag) +} + +declare const test: Test + +const x = test.isEnabled(1) diff --git a/effect/packages/package1/src/primitives.ts b/effect/packages/package1/src/primitives.ts new file mode 100644 index 00000000000..e94a9c579f5 --- /dev/null +++ b/effect/packages/package1/src/primitives.ts @@ -0,0 +1,447 @@ +declare global { + /** + * @tsplus type string + */ + export interface String { } + /** + * @tsplus type number + */ + export interface Number { } + /** + * @tsplus type boolean + */ + export interface Boolean { } + /** + * @tsplus type bigint + */ + export interface BigInt { } + /** + * @tsplus type function + */ + export interface Function { } + /** + * @tsplus type regexp + */ + export interface RegExp { } + /** + * @tsplus type object + */ + export interface Object { } +} +declare const a: string; + +/** + * @tsplus getter string lines + */ +export function lines(self: string): string[] { + return self.split("\n"); +} + +const xs = a.lines; + +/** + * @tsplus getter number days + */ +export function days(self: number, n: number): Date { + return new Date(); +} + +(0).days + +/** + * @tsplus fluent function flow + */ +export function flow(f: (a: A) => B, g: (b: B) => C): (a: A) => C { + return a => g(f(a)); +} + +const fn = ((x: number) => x.toString()).flow(s => parseInt(s)); + +/** + * @tsplus fluent regexp testTwo + */ +export function testTwo(self: RegExp, that: RegExp): (s: string) => boolean { + return s => self.test(s) && that.test(s); +} + +(/^$/).testTwo(/^$/) + + +/** + * @tsplus type FunctionN + */ +export interface FunctionN { + (...params: A): B +} + +/** + * @tsplus fluent FunctionN flow + */ +export function flow2(self: FunctionN, f: (b: B) => C): FunctionN { + return (...params) => f(self(...params)) +} + +declare const ff: FunctionN<[string, number, boolean], string> + +ff.flow((s) => parseInt(s)) + +/** + * @tsplus fluent object mapObject + */ +export function mapObject(self: Record, f: (a: A) => B): Record { + const r = {} as Record + for (const k in self) { + r[k] = f(self[k]) + } + return r +} + +/** + * @tsplus macro pipe + * @tsplus fluent global via + * @tsplus fluent ets/Effect __call + */ +export function pipe(a: A): A +export function pipe(a: A, ab: (a: A) => B): B +export function pipe(a: A, ab: (a: A) => B, bc: (b: B) => C): C +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D +): D +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E +): E +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F +): F +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G +): G +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H +): H +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I +): I +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J +): J +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K +): K +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L +): L +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M +): M +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N +): N +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O +): O +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P +): P +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q +): Q +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R +): R +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R, + rs: (r: R) => S +): S +export function pipe( + a: A, + ab: (a: A) => B, + bc: (b: B) => C, + cd: (c: C) => D, + de: (d: D) => E, + ef: (e: E) => F, + fg: (f: F) => G, + gh: (g: G) => H, + hi: (h: H) => I, + ij: (i: I) => J, + jk: (j: J) => K, + kl: (k: K) => L, + lm: (l: L) => M, + mn: (m: M) => N, + no: (n: N) => O, + op: (o: O) => P, + pq: (p: P) => Q, + qr: (q: Q) => R, + rs: (r: R) => S, + st: (s: S) => T +): T +export function pipe( + a: unknown, + ab?: Function, + bc?: Function, + cd?: Function, + de?: Function, + ef?: Function, + fg?: Function, + gh?: Function, + hi?: Function, + ij?: Function, + jk?: Function, + kl?: Function, + lm?: Function, + mn?: Function, + no?: Function, + op?: Function, + pq?: Function, + qr?: Function, + rs?: Function, + st?: Function +): unknown { + switch (arguments.length) { + case 1: + return a + case 2: + return ab!(a) + case 3: + return bc!(ab!(a)) + case 4: + return cd!(bc!(ab!(a))) + case 5: + return de!(cd!(bc!(ab!(a)))) + case 6: + return ef!(de!(cd!(bc!(ab!(a))))) + case 7: + return fg!(ef!(de!(cd!(bc!(ab!(a)))))) + case 8: + return gh!(fg!(ef!(de!(cd!(bc!(ab!(a))))))) + case 9: + return hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a)))))))) + case 10: + return ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a))))))))) + case 11: + return jk!(ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a)))))))))) + case 12: + return kl!(jk!(ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a))))))))))) + case 13: + return lm!(kl!(jk!(ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a)))))))))))) + case 14: + return mn!(lm!(kl!(jk!(ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a))))))))))))) + case 15: + return no!(mn!(lm!(kl!(jk!(ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a)))))))))))))) + case 16: + return op!( + no!(mn!(lm!(kl!(jk!(ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a)))))))))))))) + ) + case 17: + return pq!( + op!(no!(mn!(lm!(kl!(jk!(ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a))))))))))))))) + ) + case 18: + return qr!( + pq!( + op!(no!(mn!(lm!(kl!(jk!(ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a))))))))))))))) + ) + ) + case 19: + return rs!( + qr!( + pq!( + op!(no!(mn!(lm!(kl!(jk!(ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a))))))))))))))) + ) + ) + ) + case 20: + return st!( + rs!( + qr!( + pq!( + op!( + no!(mn!(lm!(kl!(jk!(ij!(hi!(gh!(fg!(ef!(de!(cd!(bc!(ab!(a)))))))))))))) + ) + ) + ) + ) + ) + default: + throw new Error("BUG") + } +} + +export const ok_0 = ({ a: "hello" }).mapObject((s) => parseInt(s)); +export const ok_1 = (1).via(n => `${n}`, s => s.length) + +/** + * @tsplus getter global asTuple + */ +export function asTuple(self: A): [A] { + return [self] +} + +const x = (0).asTuple \ No newline at end of file diff --git a/effect/packages/package1/src/scope-name-conflict.ts b/effect/packages/package1/src/scope-name-conflict.ts new file mode 100644 index 00000000000..f7379b03246 --- /dev/null +++ b/effect/packages/package1/src/scope-name-conflict.ts @@ -0,0 +1,31 @@ +/** + * @tsplus type scope-name-conflict/A + * @tsplus companion scope-name-conflict/AOps + */ +export class A { + constructor(readonly conflict: string) {} +} + +/** + * @tsplus static scope-name-conflict/AOps conflict + */ +export function conflict(s: string): A { + return new A(s) +} + +function test() { + A.x; + const conflict = "name conflict" + A.conflict(conflict) +} + +/** + * @tsplus static scope-name-conflict/AOps x + */ +export const x = new A("thing") + +conflict(''); + +export const y = x; + +export { conflict as __x } \ No newline at end of file diff --git a/effect/packages/package1/src/static-extension-class.ts b/effect/packages/package1/src/static-extension-class.ts new file mode 100644 index 00000000000..7b392f8cae3 --- /dev/null +++ b/effect/packages/package1/src/static-extension-class.ts @@ -0,0 +1,20 @@ +/** + * @tsplus type static-extension-class/A + */ +export interface A {} + +/** + * @tsplus type static-extension-class/B + * @tsplus companion static-extension-class/C + * @tsplus static static-extension-class/A B + */ +export class B {} + +/** + * @tsplus static static-extension-class/C f + */ +export declare function f(): B + +declare const a: A + +a.B.f(); \ No newline at end of file diff --git a/effect/packages/package1/src/static-fluent-name-duplication.ts b/effect/packages/package1/src/static-fluent-name-duplication.ts new file mode 100644 index 00000000000..a983d73bb75 --- /dev/null +++ b/effect/packages/package1/src/static-fluent-name-duplication.ts @@ -0,0 +1,23 @@ +/** + * @tsplus type static-fluent-name-duplication/A + * @tsplus companion static-fluent-name-duplication/AOps + */ +export class A {} + +/** + * @tsplus fluent static-fluent-name-duplication/A f + */ +export function fluentMethod(self: A, _x: number): A { + return self; +} + +/** + * @tsplus static static-fluent-name-duplication/AOps f + */ +export function staticMethod(_y: string): A { + return new A(); +} + +A.f("hello"); + +new A().f(1); \ No newline at end of file diff --git a/effect/packages/package1/src/symbol-bug/1.ts b/effect/packages/package1/src/symbol-bug/1.ts new file mode 100644 index 00000000000..a49d2d60746 --- /dev/null +++ b/effect/packages/package1/src/symbol-bug/1.ts @@ -0,0 +1,18 @@ +/** + * @tsplus static HashOps hashSym + */ +export const hashSym: unique symbol = Symbol.for("hash"); + +/** + * @tsplus type Hash + */ +export interface Hash { + [hashSym]: number; +} + +/** + * @tsplus type HashOps + */ +export interface HashOps {} + +export const Hash: HashOps = {}; diff --git a/effect/packages/package1/src/symbol-bug/2.ts b/effect/packages/package1/src/symbol-bug/2.ts new file mode 100644 index 00000000000..90ea0db1a26 --- /dev/null +++ b/effect/packages/package1/src/symbol-bug/2.ts @@ -0,0 +1,11 @@ +import { Hash } from "./1"; + +/** + * @tsplus type symbolBug/X + * @tsplus companion symbolBug/XOps + */ +export class X implements Hash { + get [Hash.hashSym]() { + return 0; + } +} diff --git a/effect/packages/package1/src/tailrec.ts b/effect/packages/package1/src/tailrec.ts new file mode 100644 index 00000000000..057f68f8760 --- /dev/null +++ b/effect/packages/package1/src/tailrec.ts @@ -0,0 +1,46 @@ +import * as C from "@effect-ts/core/Effect/Cause" +import * as L from "@effect-ts/core/Collections/Immutable/List" +import * as O from '@effect-ts/core/Option' + +/** + * @tsplus tailRec + */ +function fac(x: number, acc = 1): number { + if (x === 0) { + return acc; + } + return fac (x - 1, x * acc) +} + +/** + * @tsplus tailRec + */ + function findLoop( + cause: C.Cause, + f: (cause: C.Cause) => O.Option, + stack: L.List> + ): O.Option { + const r = f(cause) + switch (r._tag) { + case "None": { + switch (cause._tag) { + case "Both": + case "Then": { + return findLoop(cause.left, f, L.prepend_(stack, cause.right)) + } + case "Traced": { + return findLoop(cause.cause, f, stack) + } + default: { + if (!L.isEmpty(stack)) { + return findLoop(L.unsafeFirst(stack)!, f, L.tail(stack)) + } + return O.none + } + } + } + case "Some": { + return O.some(r.value) + } + } + } \ No newline at end of file diff --git a/effect/packages/package1/src/type-and-static-repro/1.ts b/effect/packages/package1/src/type-and-static-repro/1.ts new file mode 100644 index 00000000000..09f143e4619 --- /dev/null +++ b/effect/packages/package1/src/type-and-static-repro/1.ts @@ -0,0 +1,39 @@ +/** + * @tsplus type type-and-static/A + */ + export interface A {} + + /** + * @tsplus type type-and-static/AOps + */ + export interface AOps {} + + export const A: AOps = {} + + /** + * @tsplus type type-and-static/AAspects + */ + export interface AAspects {} + + /** + * @tsplus static type-and-static/AOps $ + */ + export const AAspects: AAspects = {} + + /** + * @tsplus static type-and-static/AOps staticFn + * @tsplus static type-and-static/AAspects staticFn + */ + export function staticFn(x: number): void {} + + /** + * @tsplus static type-and-static/AOps staticConstFn + * @tsplus static type-and-static/AAspects staticConstFn + */ + export declare const staticConstFn: (x: number) => void + + A.staticFn(1) + A.staticConstFn(1) + + A.$.staticFn(1) + A.$.staticConstFn(1) \ No newline at end of file diff --git a/effect/packages/package1/src/type-companion-global.ts b/effect/packages/package1/src/type-companion-global.ts new file mode 100644 index 00000000000..5850a3c6056 --- /dev/null +++ b/effect/packages/package1/src/type-companion-global.ts @@ -0,0 +1 @@ +A.get \ No newline at end of file diff --git a/effect/packages/package1/src/type-driven-overloads.ts b/effect/packages/package1/src/type-driven-overloads.ts new file mode 100644 index 00000000000..f999961aaf7 --- /dev/null +++ b/effect/packages/package1/src/type-driven-overloads.ts @@ -0,0 +1,26 @@ +import { pipe } from "./primitives.js"; +import { op1 } from "./type-driven-overloads/op1.js"; +import { op2 } from "./type-driven-overloads/op2.js"; +import { T } from "./type-driven-overloads/T.js"; + +T.make("A").op("A") +T.make(1).op(1) + +T.make(1) + true + +T.make(1).op(true) + +T.make(1) +T.make("A") + +T("") + +pipe( + T.make(1), + op1("A") +) + +pipe( + T.make("A"), + op2(1) +) \ No newline at end of file diff --git a/effect/packages/package1/src/type-driven-overloads/T.ts b/effect/packages/package1/src/type-driven-overloads/T.ts new file mode 100644 index 00000000000..3043585b2d9 --- /dev/null +++ b/effect/packages/package1/src/type-driven-overloads/T.ts @@ -0,0 +1,27 @@ +/** + * @tsplus type T + */ +export interface T { + readonly value: A +} + +/** + * @tsplus type TOps + */ +export interface TOps {} + +export const T: TOps = {} + +/** + * Comment 1 + * @tsplus static TOps make + * @tsplus static TOps __call + */ +export function make(value: number): T +/** + * Comment 2 + */ +export function make(value: string): T +export function make(value: A): T { + return { value } +} \ No newline at end of file diff --git a/effect/packages/package1/src/type-driven-overloads/op1.ts b/effect/packages/package1/src/type-driven-overloads/op1.ts new file mode 100644 index 00000000000..8acb04a374b --- /dev/null +++ b/effect/packages/package1/src/type-driven-overloads/op1.ts @@ -0,0 +1,20 @@ +import { T } from './T.js' + +/** + * Comment1 + * + * @tsplus fluent T op 0.1 + * @tsplus operator T + 0.1 + */ +export function op1_(_self: T, x: string): T +export function op1_(_self: T, x: boolean): T +export function op1_(_self: T, x: string | boolean): T { + return { value: x.toString() } +} + +/** + * @tsplus pipeable T op + */ +export function op1(x: string) { + return (_self: T): T => op1_(_self, x) +} \ No newline at end of file diff --git a/effect/packages/package1/src/type-driven-overloads/op2.ts b/effect/packages/package1/src/type-driven-overloads/op2.ts new file mode 100644 index 00000000000..96b622ab6d9 --- /dev/null +++ b/effect/packages/package1/src/type-driven-overloads/op2.ts @@ -0,0 +1,15 @@ +import { T } from './T.js' + +/** + * @tsplus fluent T op + */ +export function op2_(_self: T, x: number): T { + return { value: x } +} + +/** + * @tsplus pipeable T op + */ +export function op2(x: number) { + return (_self: T): T => op2_(_self, x) +} \ No newline at end of file diff --git a/effect/packages/package1/src/union-inheritance.ts b/effect/packages/package1/src/union-inheritance.ts new file mode 100644 index 00000000000..ee20b9f5d9f --- /dev/null +++ b/effect/packages/package1/src/union-inheritance.ts @@ -0,0 +1,181 @@ +/** + * @tsplus type union/Cons + */ +export class Cons implements Iterable { + readonly _tag = "Cons"; + constructor(readonly head: A, readonly tail: List) {} + // @ts-expect-error + [Symbol.iterator]() {} +} + +/** + * @tsplus type union/Nil + */ +export class Nil implements Iterable { + readonly _tag = "Nil"; + // @ts-expect-error + [Symbol.iterator]() {} +} + +/** + * @tsplus type union/List + */ +export type List = Cons | Nil; + +/** + * @tsplus fluent union/List map + */ +export declare function mapList(list: List, f: (a: A) => B): List; + +declare const x: List; + +function transformTest() { + x.map(s => s); +} + +/** + * @tsplus type union/A + */ +export interface A { + readonly _A: unique symbol; +} + +/** + * @tsplus type union/B + */ +export interface B { + readonly _B: unique symbol; +} + +/** + * @tsplus type union/C + */ +export interface C extends A { + readonly _C: unique symbol; +} + +/** + * @tsplus type union/D + */ +export interface D extends A, B { + readonly _D: unique symbol; +} + +/** + * @tsplus type union/E + */ +export type E = C | D; + +/** + * @tsplus fluent union/A methodA + */ +export declare function methodA(self: A): void; + +/** + * @tsplus fluent union/B methodB + */ +export declare function methodB(self: B): void; + +/** + * @tsplus fluent union/E methodE + */ +export declare function methodE(self: E): void; + +declare const e: E; + +function subtypeTest() { + e.methodE(); + e.methodA(); + // @ts-expect-error + e.methodB(); +} + +// alias inheritance + +/** + * @tsplus type union/AA + */ +export interface AA { + readonly _AA: unique symbol; +} + +/** + * @tsplus type union/AB + */ +export interface AB extends AA { + readonly _AB: unique symbol; +} + +/** + * @tsplus type union/AC + */ +export type AC = AA + +export type AD = AC | AB + +/** + * @tsplus fluent union/AA methodAA + */ +export declare function methodAA(self: AA): void +/** + * @tsplus fluent union/AB methodAB + */ +export declare function methodAB(self: AB): void +/** + * @tsplus fluent union/AC methodAC + */ +export declare function methodAC(self: AC): void + +declare const ac: AD + +function aliasTest() { + ac.methodAA() + // @ts-expect-error + ac.methodAB() + ac.methodAC() +} + +// -------------------------------------------- + +/** + * @tsplus type union/UA + */ +export interface UA { + readonly _tag: "UA" +} + +/** + * @tsplus type union/UB + */ +export interface UB { + readonly _tag: "UB" +} + +/** + * @tsplus type union/UC + */ +export interface UC { + readonly _tag: "UC" +} + +/** + * @tsplus type union/U + */ +export type U = UA | UB | UC + +/** + * @tsplus fluent union/U u_op + */ +export declare function u_op(u: U): void + +declare const u: U + +declare const ub: UB + +function u_test() { + if (u._tag === "UA") { + u.u_op() + } else { + u.u_op() + } +} \ No newline at end of file diff --git a/effect/packages/package1/src/utils/LazyArgument.ts b/effect/packages/package1/src/utils/LazyArgument.ts new file mode 100644 index 00000000000..bcabe3bfe2d --- /dev/null +++ b/effect/packages/package1/src/utils/LazyArgument.ts @@ -0,0 +1,4 @@ +/** + * @tsplus type tsplus/LazyArgument + */ +export interface LazyArgument { (): A; } diff --git a/effect/packages/package1/tsconfig.json b/effect/packages/package1/tsconfig.json new file mode 100644 index 00000000000..0cd79f62a3b --- /dev/null +++ b/effect/packages/package1/tsconfig.json @@ -0,0 +1,14 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "build/esm", + "declarationDir": "build/dts", + "declarationMap": true, + "tsPlusConfig": "../../tsplus.config.json", + "rootDir": "src", + "incremental": true, + "tsBuildInfoFile": "build/.tsbuildinfo", + "tsPlusEnabled": true + }, + "include": ["src/**/*.ts"] +} \ No newline at end of file diff --git a/effect/packages/package2/fp-ts__core.json b/effect/packages/package2/fp-ts__core.json new file mode 100644 index 00000000000..1bc0841a655 --- /dev/null +++ b/effect/packages/package2/fp-ts__core.json @@ -0,0 +1,74 @@ +{ + "@fp-ts/core/Async": [ + { + "definitionName": "Async", + "definitionKind": "interface", + "extensions": [ + { + "kind": "type", + "typeName": "fp-ts/Async" + }, + { + "kind": "companion", + "typeName": "fp-ts/AsyncOps" + } + ] + }, + { + "definitionName": "fromSync", + "definitionKind": "const", + "extensions": [ + { + "kind": "static", + "typeName": "fp-ts/AsyncOps", + "name": "fromSync" + } + ] + }, + { + "definitionName": "delay", + "definitionKind": "const", + "extensions": [ + { + "kind": "pipeable", + "typeName": "fp-ts/Async", + "name": "delay" + } + ] + }, + { + "definitionName": "map", + "definitionKind": "const", + "extensions": [ + { + "kind": "pipeable", + "typeName": "fp-ts/Async", + "name": "map" + } + ] + } + ], + "@fp-ts/core/Option": [ + { + "definitionName": "Option", + "definitionKind": "type", + "extensions": [ + { + "kind": "type", + "typeName": "fp-ts/Option" + } + ] + }, + { + "definitionName": "filter", + "definitionKind": "const", + "extensions": [ + { + "kind": "pipeable", + "typeName": "fp-ts/Option", + "name": "filter" + } + ] + } + ] +} \ No newline at end of file diff --git a/effect/packages/package2/fp-ts__core_2.json b/effect/packages/package2/fp-ts__core_2.json new file mode 100644 index 00000000000..570abaad5a6 --- /dev/null +++ b/effect/packages/package2/fp-ts__core_2.json @@ -0,0 +1,40 @@ +{ + "@fp-ts/core/Sync": [ + { + "definitionName": "Sync", + "definitionKind": "interface", + "extensions": [ + { + "kind": "type", + "typeName": "fp-ts/Sync" + }, + { + "kind": "companion", + "typeName": "fp-ts/SyncOps" + } + ] + }, + { + "definitionName": "map", + "definitionKind": "const", + "extensions": [ + { + "kind": "pipeable", + "typeName": "fp-ts/Sync", + "name": "map" + } + ] + }, + { + "definitionName": "of", + "definitionKind": "const", + "extensions": [ + { + "kind": "static", + "typeName": "fp-ts/SyncOps", + "name": "of" + } + ] + } + ] +} \ No newline at end of file diff --git a/effect/packages/package2/package.json b/effect/packages/package2/package.json new file mode 100644 index 00000000000..87067b91841 --- /dev/null +++ b/effect/packages/package2/package.json @@ -0,0 +1,11 @@ +{ + "name": "@tsplus-test/package2", + "type": "module", + "scripts": { + "build:esm": "node ../../../built/local/tsc.js -p ./tsconfig.json" + }, + "dependencies": { + "@fp-ts/core": "0.0.2", + "@tsplus-test/package1": "*" + } +} \ No newline at end of file diff --git a/effect/packages/package2/src/derivation/auto.ts b/effect/packages/package2/src/derivation/auto.ts new file mode 100644 index 00000000000..5328fbe6e98 --- /dev/null +++ b/effect/packages/package2/src/derivation/auto.ts @@ -0,0 +1,149 @@ +import { Show } from "./show"; + +function print(a: A, /** @tsplus auto */ show: Show): void { + console.log(show.show(a)); +} + +print("Hi"); +print({ a: 1, b: "hello", c: new Date() }); + +// function err(a: string, /** @tsplus auto */ b: number, c: boolean): void {} + +/** + * @tsplus type auto/T + */ +export interface T { + a: A; +} + +/** + * @tsplus implicit + */ +export const implicit: T = { + a: "" +}; + +/** + * @tsplus implicit + */ +export const implicit2: T = { + a: 0 +}; + +// single Non-generic +declare function f(a: string, /** @tsplus auto */ b: T): void; + +f("1"); + +// non-single non-generic +declare function f1(x: number, y: string, /** @tsplus auto */z: T): void +declare function f1(x: number, y: number, /** @tsplus auto */z: T): void + +f1(1, "") +f1(1, 1) + +export declare namespace HKT { + const F: unique symbol; + type F = typeof F; + const A: unique symbol; + type A = typeof A; + const T: unique symbol; + type T = typeof T; + + type _A = X extends { [A]?: () => infer A } ? A : never; + + /** + * @tsplus type fncts.Kind + */ + type Kind = F & { + [F]?: F; + [A]?: () => A; + } extends { [T]?: infer X } + ? X + : { + [F]?: F; + [A]?: () => A; + }; + + export interface Typeclass { + [HKT.F]?: F; + } +} + +export interface HKT { + [HKT.F]?: HKT; + [HKT.A]?: () => unknown; + [HKT.T]?: unknown; +} + +export interface Applicative extends HKT.Typeclass { + zipWith( + fa: HKT.Kind, + fb: HKT.Kind, + f: (a: A, b: B) => C + ): HKT.Kind; +} + +export interface Traversable extends HKT.Typeclass { + traverse( + ta: HKT.Kind, + f: (a: A) => HKT.Kind + ): HKT.Kind>; +} + +/** + * @tsplus fluent fncts.Kind traverse + */ +export declare function traverse<_F extends HKT, G extends HKT, A, B>( + self: HKT.Kind<_F, A>, + f: (a: A) => HKT.Kind, + /** @tsplus auto */ F: Traversable<_F>, + /** @tsplus auto */ G: Applicative +): HKT.Kind> + + +export interface HF extends H {} + +/** + * @tsplus type auto/H + * @tsplus type fncts.Kind + */ +export interface H { + [HKT.F]?: HF; + [HKT.A]?: () => A; + [HKT.T]?: H>; +} + +export interface GF extends G {} + +/** + * @tsplus type auto/G + */ +export interface G { + [HKT.F]?: GF; + [HKT.A]?: () => A; + [HKT.T]?: G>; +} + +/** + * @tsplus implicit + */ +export declare const TraversableT: Traversable; + +/** + * @tsplus implicit + */ +export declare const ApplicativeG: Applicative; + +declare const h: H + +declare function mkG(a: A): G + +export const res = h.traverse((s) => mkG(s.length)) + + +// @ts-expect-error +function z([a, b]: [number, number]) { + // @ts-expect-error + h.traverse(() => x1) +} \ No newline at end of file diff --git a/effect/packages/package2/src/derivation/block-scoped.ts b/effect/packages/package2/src/derivation/block-scoped.ts new file mode 100644 index 00000000000..6f1d96ea408 --- /dev/null +++ b/effect/packages/package2/src/derivation/block-scoped.ts @@ -0,0 +1,57 @@ +import { Show } from "./show"; + +export interface Info { + readonly info: (a: A) => string +} + +export function f(show: Show & Info): Show> { + const ok: Show> = Derive() + return ok; +} + +export function g(show: Show): Show> { + const go = () => { + const _show = 0; + _show; + const ok: Show> = Derive() + return ok; + } + return go(); +} + +interface Target { + x: number; +} + +/** + * @tsplus implicit local + */ +const _showSourceScoped: Show<{ + x: number; +}> = Derive() + +export function h() { + // uses _showSourceScoped + const ok: Show = Derive() + return ok; +} + +export function i() { + /** @tsplus implicit local */ + const _showBlockScoped: Show = Derive(); + const ok: Show = Derive(); + return ok; +} + +const id = (a: () => A) => a() + +export function j() { + return id(() => { + /** @tsplus implicit local */ + const _showBlockScoped: Show = Derive(); + const ok: Show = Derive() + if (Math.random() > 0 && ok) { + return 0 + } + }) +} \ No newline at end of file diff --git a/effect/packages/package2/src/derivation/brand.ts b/effect/packages/package2/src/derivation/brand.ts new file mode 100644 index 00000000000..3efabcb9002 --- /dev/null +++ b/effect/packages/package2/src/derivation/brand.ts @@ -0,0 +1,26 @@ +import { TypeLevel } from "./check" + +export const Brand: { + readonly sym: unique symbol + readonly validator: (_: (_: A) => boolean) => Brand.Validator +} = { + sym: Symbol() as any, + validator: _ => _ as any +} + +export interface Brand { + [Brand.sym]: { + [k in S]: true + } +} + +export declare namespace Brand { + interface Validator { + readonly _tag: "Validator" + readonly is: (a: A) => a is A & Brand + } + type Of> = TypeLevel.UnionToIntersection< + { [k in keyof A[typeof Brand["sym"]]]: k extends string ? Brand : never }[keyof A[typeof Brand["sym"]]] + > + type Unbranded> = A extends infer X & Of ? X : never +} diff --git a/effect/packages/package2/src/derivation/check.ts b/effect/packages/package2/src/derivation/check.ts new file mode 100644 index 00000000000..a1f257815cc --- /dev/null +++ b/effect/packages/package2/src/derivation/check.ts @@ -0,0 +1,66 @@ +export declare namespace TypeLevel { + export type UnionToIntersection = + (T extends any ? (x: T) => any : never) extends + (x: infer R) => any ? R : never + + export type RequiredKeys = { [K in keyof T]-?: + ({} extends { [P in K]: T[K] } ? never : K) + }[keyof T] + + export type OptionalKeys = { [K in keyof T]-?: + ({} extends { [P in K]: T[K] } ? K : never) + }[keyof T] + + export type UnionToTuple = + UnionToIntersection< + Union extends unknown + ? (distributed: Union) => void + : never + > extends ((merged: infer Intersection) => void) + ? readonly [...UnionToTuple>, Intersection] + : []; +} + +type EqualsWrapped = T extends infer R & {} + ? { + [P in keyof R]: R[P] + } + : never + +declare const No: unique symbol +declare const Ok: unique symbol + +export declare namespace Check { + type True = typeof Ok + type False = typeof No + + type Not = [A] extends [never] ? unknown : never + + type Extends = [A] extends [B] ? unknown : never + + type IsUnion = [T] extends [TypeLevel.UnionToIntersection] ? never : unknown + + type IsEqual = (() => T extends EqualsWrapped ? 1 : 2) extends < + T + >() => T extends EqualsWrapped ? 1 : 2 + ? unknown + : never + + type IsLiteral = + Not> & + Not> & + Not> + + type IsStruct = Check.Extends & Check.Not> + + type HaveSameLength = IsEqual + + type IsTagged = + IsUnion & + IsUnion & + HaveSameLength, TypeLevel.UnionToTuple> +} + +export type Check = [Condition] extends [never] ? Check.False : Check.True + +export type X = Check & Check.IsEqual<0, 0>> extends Check.True ? "YAY" : "NAY" \ No newline at end of file diff --git a/effect/packages/package2/src/derivation/guard.ts b/effect/packages/package2/src/derivation/guard.ts new file mode 100644 index 00000000000..29437af295a --- /dev/null +++ b/effect/packages/package2/src/derivation/guard.ts @@ -0,0 +1,156 @@ +import { Maybe } from "@tsplus-test/package1/prelude"; +import { IsUnion, OptionalKeys, RequiredKeys, UnionToIntersection } from "./types"; + +/** + * @tsplus type Guard + * @tsplus derive nominal + */ +export class Guard { + readonly _tag = "Guard" + constructor(readonly is: (u: unknown) => u is A) { } +} + +// +// Top Priority +// + +/** + * @tsplus derive Guard lazy + */ +export function deriveGuardLazy( + ...args: [ + fn: (_: Guard) => Guard + ] +): Guard { + args + throw new Error("Not Implemented") +} + +// +// High Priority +// + +/** + * @tsplus derive Guard<_> 10 + */ +export function deriveGuardLiteralNumber( + ...args: IsUnion extends false ? number extends A ? never : [ + value: A + ] : never +): Guard { + args + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Guard<_> 10 + */ +export function deriveGuardLiteralString( + ...args: IsUnion extends false ? string extends A ? never : [ + value: A + ] : never +): Guard { + args + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Guard<_> 10 + */ +export function deriveGuardMaybe>( + ...args: [A] extends [Maybe] + ? [element: Guard<_A>] + : never +): Guard { + args + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Guard<_> 10 + */ +export function deriveGuardArray>( + ...args: [A] extends [Array] ? [Array<_A>] extends [A] ? [ + element: Guard<_A> + ] : never : never +): Guard { + args + throw new Error("Not Implemented") +} + +// +// Mid Priority +// + +/** + * @tsplus derive Guard<&> 20 + */ +export function deriveGuardIntersection( + ...args: { + [k in keyof A]: Guard + } +): Guard> { + args + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Guard<_> 20 + */ +export function deriveGuardStruct>( + ...args: keyof A extends string ? IsUnion extends false ? [ + requiredFields: { + [k in RequiredKeys]: Guard + }, + optionalFields: { + [k in OptionalKeys]: Guard> + } + ] : never : never +): Guard { + args + throw new Error("Not Implemented") +} + +// +// Low priority +// + +/** + * @tsplus derive Guard<|> 30 + */ +export function deriveGuardUnion( + ...args: { + [k in keyof A]: Guard + } +): Guard { + args + throw new Error("Not Implemented") +} + +// +// Implicits +// + +/** + * @tsplus implicit + */ +export const number: Guard = 0 as any + +/** + * @tsplus implicit + */ +export const string: Guard = 0 as any + +/** + * @tsplus implicit + */ +export const date: Guard = 0 as any + +/** + * @tsplus implicit + */ +export const boolean: Guard = 0 as any + +export const A: Guard<{ + a: string +}> = Derive() \ No newline at end of file diff --git a/effect/packages/package2/src/derivation/int.ts b/effect/packages/package2/src/derivation/int.ts new file mode 100644 index 00000000000..c3769ceb434 --- /dev/null +++ b/effect/packages/package2/src/derivation/int.ts @@ -0,0 +1,13 @@ +import { Brand } from "./brand"; +import {} from "@tsplus-test/package1/hello" + +export type Int = number & Brand<"Int"> + +/** + * @tsplus implicit + */ +export const validateInt = Brand.validator( + (n) => Number.isInteger(n) +) + +export const x = Derive<"Hello">() \ No newline at end of file diff --git a/effect/packages/package2/src/derivation/refinement.ts b/effect/packages/package2/src/derivation/refinement.ts new file mode 100644 index 00000000000..b1bc59b917e --- /dev/null +++ b/effect/packages/package2/src/derivation/refinement.ts @@ -0,0 +1,145 @@ +import { Guard } from "./guard"; +import { IsUnion, OptionalKeys, RequiredKeys, TypeEquals, UnionToIntersection } from "./types"; + +/** + * @tsplus type Refinement + */ +export class Refinement { + readonly _tag = "Refinement" + constructor(readonly is: (a: A) => a is B) { } +} + +// +// Top Priority +// + +/** + * @tsplus derive Refinement lazy + */ +export function deriveRefinementLazy( + ...args: [ + fn: (_: Refinement) => Refinement + ] +): Refinement { + args + throw new Error("Not Implemented") +} + +// +// High Priority +// + +/** + * @tsplus derive Refinement<_, _> 10 + */ +export function deriveRefinementEmptyRecord( + ...[]: TypeEquals extends true ? [] : never +): Refinement { + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Refinement<_, _> 10 + */ +export function deriveRefinementAlwaysTrue( + ...[]: TypeEquals extends true ? [] : never +): Refinement { + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Refinement<_, _> 10 + */ +export function deriveRefinementLiteralString( + ...[value]: IsUnion extends false ? string extends B ? never : [value: B] : never +): Refinement { + value + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Refinement<_, _> 10 + */ +export function deriveRefinementLiteralNumber( + ...[value]: IsUnion extends false ? number extends B ? never : [value: B] : never +): Refinement { + value + throw new Error("Not Implemented") +} + +// +// Mid priority +// + +/** + * @tsplus derive Refinement<_, |> 20 + */ +export function deriveRefinementUnion( + ...members: { + [k in keyof B]: B[k] extends A ? Refinement : never + } +): Refinement { + members + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Refinement<_, &> 20 + */ +export function deriveRefinementIntersection( + ...members: UnionToIntersection extends A ? { + [k in keyof B]: B[k] extends A ? Refinement : never + } : never +): UnionToIntersection extends A ? Refinement> : never { + members + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Refinement<_, _> 20 + */ +export function deriveRefinementStruct & A>( + ...[requiredFields, optionalFields, objectRefinement]: keyof B extends string ? IsUnion extends false ? + A extends {} ? [ + requiredFields: { + [k in RequiredKeys]: k extends keyof A ? Refinement : Refinement + }, + optionalFields: { + [k in OptionalKeys]: k extends keyof A ? Refinement> : Refinement> + } + ] : [ + requiredFields: { + [k in RequiredKeys]: k extends keyof A ? Refinement : Refinement + }, + optionalFields: { + [k in OptionalKeys]: k extends keyof A ? Refinement> : Refinement> + }, + objectRefinement: Refinement + ] + : never : never +): Refinement { + requiredFields + optionalFields + objectRefinement + throw new Error("Not Implemented") +} + +// +// Low priority +// + +/** + * @tsplus derive Refinement<_, _> 30 no-recursion + */ +export function deriveRefinementFromUnknown( + ...[guard]: [Guard] +): Refinement { + guard + throw new Error("Not Implemented") +} + +// +// Usage +// + +export const ok4: Refinement = Derive() \ No newline at end of file diff --git a/effect/packages/package2/src/derivation/show.ts b/effect/packages/package2/src/derivation/show.ts new file mode 100644 index 00000000000..7ccb553bb6f --- /dev/null +++ b/effect/packages/package2/src/derivation/show.ts @@ -0,0 +1,169 @@ +import { Check } from "./check"; +import { Guard } from "./guard"; +import { OptionalKeys, RequiredKeys, UnionToIntersection } from "./types"; + +/** + * @tsplus type Show + * @tsplus derive nominal + */ +export class Show { + constructor(readonly show: (a: A) => string) { } +} + +// +// Top Priority +// + +/** + * @tsplus derive Show lazy + */ +export function deriveShowLazy( + ...args: [ + fn: (_: Show) => Show + ] +): Show { + const show = args[0](new Show((type) => show.show(type))); + return show; +} + +// +// High Priority +// + +/** + * @tsplus derive Show<_> 10 + */ +export function deriveShowLiteralUnion( + ...[]: Check> extends Check.True ? [] : never +): Show { + return new Show((a) => `${a}`) +} + +/** + * @tsplus derive Show[Maybe]<_> 10 + */ +export function deriveShowMaybe>( + ...args: [A] extends [Maybe] + ? [element: Show<_A>] + : never +): Show { + return new Show((a) => a.isJust() ? `Maybe.Just(${args[0].show(a)})` : `Maybe.None`) +} + +declare global { + /** + * @tsplus type MutableArray + */ + export interface Array {} +} + +/** + * @tsplus derive Show[MutableArray]<_> 10 + */ +export function deriveShowArray>( + ...args: [A] extends [Array] ? [Array<_A>] extends [A] ? [ + element: Show<_A> + ] : never : never +): Show { + return new Show((a) => `Array<{${a.map(args[0].show)}}>`) +} + +// +// Mid Priority +// + +/** + * @tsplus derive Show<&> 20 + */ +export function deriveShowIntersection( + ...args: { + [k in keyof A]: Show + } +): Show> { + args + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Show<_> 20 + */ +export function deriveShowStruct( + ...args: Check> extends Check.True ? [ + requiredFields: { + [k in RequiredKeys]: Show + }, + optionalFields: { + [k in OptionalKeys]: Show> + } + ] : never +): Show { + args + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Show<[]> 20 + */ +export function deriveShowTuple( + ...args: { + [k in keyof A]: Show + } +): Show { + args + throw new Error("Not Implemented") +} + +/** + * @tsplus derive Show<_> 20 + */ +export function deriveShowUnionTagged( + ...args: Check> extends Check.True ? [ + members: { + [k in A["_tag"]]: Show> + } + ] : never +): Show { + args + throw new Error("Not Implemented") +} + +// +// Low priority +// + +export interface GuardAndShow { + guard: Guard + show: Show +} + +/** + * @tsplus derive Show<|> 30 + */ +export function deriveShowUnion( + ...args: { + [k in keyof A]: GuardAndShow + } +): Show { + args + throw new Error("Not Implemented") +} + +/** + * @tsplus implicit + */ +export const number = new Show((a: number) => `${a}`).via((x) => x) + +/** + * @tsplus implicit + */ +export const string = new Show((a: string) => a) + +/** + * @tsplus implicit + */ +export const date = new Show((a: Date) => a.toISOString()) + +/** + * @tsplus implicit + */ +export const boolean = new Show((a: boolean) => a ? "true" : "false") diff --git a/effect/packages/package2/src/derivation/types.ts b/effect/packages/package2/src/derivation/types.ts new file mode 100644 index 00000000000..31ee12db3fb --- /dev/null +++ b/effect/packages/package2/src/derivation/types.ts @@ -0,0 +1,49 @@ +export type IsUnion = [T] extends [UnionToIntersection] ? false : true + +export type UnionToIntersection = + (T extends any ? (x: T) => any : never) extends + (x: infer R) => any ? R : never + +export type RequiredKeys = { [K in keyof T]-?: + ({} extends { [P in K]: T[K] } ? never : K) +}[keyof T] + +export type OptionalKeys = { [K in keyof T]-?: + ({} extends { [P in K]: T[K] } ? K : never) +}[keyof T] + +export type StringIndexedRecord = Record + +type EqualsWrapped = T extends infer R & {} + ? { + [P in keyof R]: R[P] + } + : never + +export type TypeEquals = (() => T extends EqualsWrapped ? 1 : 2) extends < + T + >() => T extends EqualsWrapped ? 1 : 2 + ? true + : false + +export type IsTypeEqualToAnyOf = Y["length"] extends 0 ? false : ({ + [k in keyof Y]: TypeEquals +}[number] extends false ? false : true) + +export type UnionToTuple = + UnionToIntersection< + Union extends unknown + ? (distributed: Union) => void + : never + > extends ((merged: infer Intersection) => void) + ? readonly [...UnionToTuple>, Intersection] + : []; + +export type IsNever = [T] extends [never] ? true : false; + +declare global { + /** @tsplus derive nominal */ + interface Date {} + /** @tsplus derive nominal */ + interface Array {} +} \ No newline at end of file diff --git a/effect/packages/package2/src/derivation/use.ts b/effect/packages/package2/src/derivation/use.ts new file mode 100644 index 00000000000..bbbdecfd024 --- /dev/null +++ b/effect/packages/package2/src/derivation/use.ts @@ -0,0 +1,55 @@ +import { Show } from "./show" + +// +// Usage +// + +export interface Business { + readonly _tag: "Business" +} + +export interface Personal { + readonly _tag: "Personal" +} + +export type ClientType = Business | Personal + +/** + * @tsplus implicit + */ +export const showClientType: Show = Derive() + +export interface Person { + readonly tag: "Person" + readonly gender: "M" | "F" | "NB" | "NA" + readonly name: string + readonly surname: string + readonly age: Maybe + readonly birthDate?: Date + readonly bestFriend: Maybe + readonly friends: Person[] + readonly isUkResident: boolean +} + +/** + * @tsplus implicit + */ +export const showPerson: Show = Derive() + +export interface User { + readonly id: string + readonly owner: Person + readonly clientType: ClientType +} + +/** + * @tsplus implicit + */ +export const showUser: Show = Derive() + +export interface WeirdPropertyNames { + readonly ["."]: number + readonly ["2"]: string +} + +export const showWeirdPropertyNames: Show = Derive() \ No newline at end of file diff --git a/effect/packages/package2/src/external-definitions.ts b/effect/packages/package2/src/external-definitions.ts new file mode 100644 index 00000000000..71e03139088 --- /dev/null +++ b/effect/packages/package2/src/external-definitions.ts @@ -0,0 +1,6 @@ +import { Async } from '@fp-ts/core/Async' +import { Sync } from '@fp-ts/core/Sync' + +Async.fromSync(() => console.log("Fluent in fp-ts!")).delay(1) + +Sync.of(1).map((n) => n + 1) diff --git a/effect/packages/package2/src/globals.ts b/effect/packages/package2/src/globals.ts new file mode 100644 index 00000000000..8105b67c249 --- /dev/null +++ b/effect/packages/package2/src/globals.ts @@ -0,0 +1,14 @@ +export const ok = Effect.succeed(0).map((n) => `ok: ${n}`) +export type ok = Effect + +export namespace X { + export const ok = Effect.succeed(0).map((n) => `ok: ${n}`) + export type ok = typeof ok extends Effect ? 1 : 0 +} + +// @ts-expect-error +export const A = Just + +export type B = typeof T + +export const C = T; diff --git a/effect/packages/package2/src/globals2.ts b/effect/packages/package2/src/globals2.ts new file mode 100644 index 00000000000..ffb4a52f0ac --- /dev/null +++ b/effect/packages/package2/src/globals2.ts @@ -0,0 +1,3 @@ +Effect.succeed(() => 1) + +Maybe.just("A") diff --git a/effect/packages/package2/src/namespace-collection.ts b/effect/packages/package2/src/namespace-collection.ts new file mode 100644 index 00000000000..b836977bd30 --- /dev/null +++ b/effect/packages/package2/src/namespace-collection.ts @@ -0,0 +1,5 @@ +import { A } from "@tsplus-test/package1/namespace-collection/1" + +declare const a: A.T + +a.func() \ No newline at end of file diff --git a/effect/packages/package2/src/prelude.ts b/effect/packages/package2/src/prelude.ts new file mode 100644 index 00000000000..f30b647b2f6 --- /dev/null +++ b/effect/packages/package2/src/prelude.ts @@ -0,0 +1,10 @@ +/** + * @tsplus global + */ +import { Effect, T, chain, chainPipeable } from "@tsplus-test/package1/prelude" + +/** + * @tsplus global + */ +import { Maybe, Just } from "@tsplus-test/package1/prelude/definition/Maybe" + diff --git a/effect/packages/package2/src/service.ts b/effect/packages/package2/src/service.ts new file mode 100644 index 00000000000..97869a62e6f --- /dev/null +++ b/effect/packages/package2/src/service.ts @@ -0,0 +1,17 @@ +export interface Service { + (): void +} + +/** + * @tsplus type TargetOps + */ +export interface Ops {} + +export const target: Ops = {} + +/** + * @tsplus static TargetOps Service + */ +export declare const Service: Service + +export const t = target.Service \ No newline at end of file diff --git a/effect/packages/package2/src/test.ts b/effect/packages/package2/src/test.ts new file mode 100644 index 00000000000..0bc3a4fc49e --- /dev/null +++ b/effect/packages/package2/src/test.ts @@ -0,0 +1,24 @@ +import * as EffectModule from "@tsplus-test/package1/prelude"; +import { pipe } from "@tsplus-test/package1/primitives" + +const a = pipe( + Effect(1), + T.chain((n) => Effect(n + 1)), + chain((n) => Effect(n + 1)), + EffectModule.chain((n) => Effect(n + 1)) +) + +const b = T.chain((n: number) => Effect(n + 1))(Effect(1)); + +const c = chain((n: number) => Effect(n + 1))(Effect(1)); + +const d = chainPipeable((n: number) => Effect(n + 1))(Effect(1)); + +Effect(0).chainPipeable(n => Effect(n + 1)) + +a.unsafeRunPromise().then((x) => console.log(x)) +b.unsafeRunPromise().then((x) => console.log(x)) +c.unsafeRunPromise().then((x) => console.log(x)) +d.unsafeRunPromise().then((x) => console.log(x)) + +export const f = pipe(0, n => n + 1, n => n + 2) diff --git a/effect/packages/package2/src/union-inheritance-repro/Equals.ts b/effect/packages/package2/src/union-inheritance-repro/Equals.ts new file mode 100644 index 00000000000..559ae55f8e4 --- /dev/null +++ b/effect/packages/package2/src/union-inheritance-repro/Equals.ts @@ -0,0 +1,16 @@ +export const equalsSym = Symbol() + +/** + * @tsplus type union-inheritance-repro/Equals + */ +export interface Equals { + [equalsSym](that: unknown): boolean +} + +/** + * @tsplus fluent union-inheritance-repro/Equals equals + * @tsplus operator union-inheritance-repro/Equals == + */ +export function equals(self: A, that: unknown): boolean { + return self === that +} \ No newline at end of file diff --git a/effect/packages/package2/src/union-inheritance-repro/exit-repro.ts b/effect/packages/package2/src/union-inheritance-repro/exit-repro.ts new file mode 100644 index 00000000000..4837694ec99 --- /dev/null +++ b/effect/packages/package2/src/union-inheritance-repro/exit-repro.ts @@ -0,0 +1,30 @@ +import { Equals, equalsSym } from "./Equals" + +export class Success implements Equals { + readonly _tag = "Success" + constructor(readonly value: A) {} + [equalsSym](that: unknown): boolean { + return this === that; + } +} + +export class Failure implements Equals { + readonly _tag = "Failure" + constructor(readonly error: E) {} + [equalsSym](that: unknown): boolean { + return this === that; + } +} + +/** + * @tsplus type union-inheritance-repro/Exit + */ +export type Exit = Failure | Success + +declare const test1: Exit +declare const test2: Exit + +export function test() { + test1.equals(test2) + ;(test1 == test2) +} diff --git a/effect/packages/package2/src/via.ts b/effect/packages/package2/src/via.ts new file mode 100644 index 00000000000..37441bdee40 --- /dev/null +++ b/effect/packages/package2/src/via.ts @@ -0,0 +1,2 @@ +export const f2 = (0).via(n => n + 1, n => n + 2); +export const f0 = (1).days \ No newline at end of file diff --git a/effect/packages/package2/tsconfig.json b/effect/packages/package2/tsconfig.json new file mode 100644 index 00000000000..857d0f6b9f4 --- /dev/null +++ b/effect/packages/package2/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "rootDir": "src", + "outDir": "build/esm", + "declarationDir": "build/dts", + "tsPlusConfig": "../../tsplus.config.json", + "noUnusedLocals": true, + "noUnusedParameters": true, + "tsPlusTypes": [ + "./fp-ts__core.json", + "./fp-ts__core_2.json" + ] + }, + "include": ["src/**/*.ts"] +} \ No newline at end of file diff --git a/effect/tsconfig.json b/effect/tsconfig.json new file mode 100644 index 00000000000..558dbe9277a --- /dev/null +++ b/effect/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "module": "ESNext", + "moduleDetection": "force", + "target": "ES2021", + "moduleResolution": "Node", + "strict": true, + "declaration": true, + "skipLibCheck": true, + "lib": ["ES2021"] + }, +} \ No newline at end of file diff --git a/effect/tsplus.config.json b/effect/tsplus.config.json new file mode 100644 index 00000000000..0292341b6ad --- /dev/null +++ b/effect/tsplus.config.json @@ -0,0 +1,12 @@ +{ + "importMap": { + "^(.*)/packages/([^/]*)/src(.*)/index\\.ts$": "@tsplus-test/$2$3", + "^(.*)/packages/([^/]*)/src(.*)\\.ts$": "@tsplus-test/$2$3" + }, + "relativeMap": { + "^(.*)/packages/([^/]*)/(.*)$": "$2" + }, + "traceMap": { + "^(.*)/packages/([^/]*)/(.*)$": "(@tsplus-test/$2) $3" + } +} \ No newline at end of file diff --git a/pack.sh b/pack.sh new file mode 100644 index 00000000000..a6e8e80157f --- /dev/null +++ b/pack.sh @@ -0,0 +1,6 @@ +#!/bin/sh +npm ci +npx hereby configure-tsplus +npx hereby LKG +npx hereby clean +npm pack diff --git a/package.json b/package.json index 1c05fb6ec64..32a6c6e15a6 100644 --- a/package.json +++ b/package.json @@ -111,4 +111,4 @@ "node": "14.21.1", "npm": "8.19.3" } -} +} \ No newline at end of file diff --git a/scripts/configurePrerelease.mjs b/scripts/configurePrerelease.mjs index 9c4b83ea8f5..beca4459f03 100644 --- a/scripts/configurePrerelease.mjs +++ b/scripts/configurePrerelease.mjs @@ -19,12 +19,12 @@ function main() { if (args.length < 3) { const thisProgramName = relative(process.cwd(), __filename); console.log("Usage:"); - console.log(`\tnode ${thisProgramName} `); + console.log(`\tnode ${thisProgramName} `); return; } const tag = args[0]; - if (tag !== "dev" && tag !== "insiders" && tag !== "experimental") { + if (tag !== "dev" && tag !== "insiders" && tag !== "experimental" && tag !== "tsplus") { throw new Error(`Unexpected tag name '${tag}'.`); } diff --git a/src/compiler/builderState.ts b/src/compiler/builderState.ts index 58277c12f9f..52b05a59ea7 100644 --- a/src/compiler/builderState.ts +++ b/src/compiler/builderState.ts @@ -256,6 +256,22 @@ export namespace BuilderState { } } + // TSPLUS START + program.getTypeChecker().getTsPlusFiles().get(sourceFile)?.forEach((file) => { + if (file !== sourceFile) { + addReferencedFile(file.resolvedPath); + } + }) + if (!sourceFile.isDeclarationFile) { + program.getTypeChecker().getTsPlusGlobalImports().forEach((imp) => { + const file = getSourceFileOfNode(imp.declaration); + if (file !== sourceFile) { + addReferencedFile(file.resolvedPath); + } + }) + } + // TSPLUS END + // From ambient modules for (const ambientModule of program.getTypeChecker().getAmbientModules()) { if (ambientModule.declarations && ambientModule.declarations.length > 1) { diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ca37ad9acf8..a912f41bbf8 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1059,10 +1059,80 @@ import { WideningContext, WithStatement, YieldExpression, + ClassDeclarationWithIdentifier, + Derivation, + flatMapIterator, + FromObjectStructure, + FromPriorDerivation, + FromRule, + getFileMap, + getImportLocation, + getJSDocCommentsAndTags, + isReturnStatement, + JSDocTag, + mapIterator, + NamedImports, + Rule, + TsPlusFluentExtension, + TsPlusFluentSymbol, + TsPlusGetterExtension, + TsPlusGetterSymbol, + TsPlusGetterVariableSymbol, + TsPlusGlobalImport, + TsPlusMacroCallExpression, + TsPlusOperatorExtension, + TsPlusPipeableDeclarationSymbol, + TsPlusPipeableExtension, + TsPlusPipeableIdentifierSymbol, + TsPlusPipeableMacroSymbol, + TsPlusSignature, + TsPlusStaticFunctionExtension, + TsPlusStaticFunctionSymbol, + TsPlusStaticValueExtension, + TsPlusStaticValueSymbol, + TsPlusSymbol, + TsPlusSymbolTag, + TsPlusType, + TsPlusUnresolvedFluentExtension, + TsPlusUnresolvedPipeableExtension, + TsPlusUnresolvedStaticExtension, + VariableDeclarationWithFunction, + VariableDeclarationWithFunctionType, + VariableDeclarationWithIdentifier, + LocalsContainer, } from "./_namespaces/ts"; import * as performance from "./_namespaces/ts.performance"; import * as moduleSpecifiers from "./_namespaces/ts.moduleSpecifiers"; +// TSPLUS EXTENSION START +const tsPlusDebug = false; +const invertedBinaryOp = { + [SyntaxKind.LessThanToken]: "<" as __String, + [SyntaxKind.GreaterThanToken]: ">" as __String, + [SyntaxKind.LessThanEqualsToken]: "<=" as __String, + [SyntaxKind.GreaterThanEqualsToken]: ">=" as __String, + [SyntaxKind.EqualsEqualsToken]: "==" as __String, + [SyntaxKind.ExclamationEqualsToken]: "!=" as __String, + [SyntaxKind.EqualsEqualsEqualsToken]: "===" as __String, + [SyntaxKind.ExclamationEqualsEqualsToken]: "!==" as __String, + [SyntaxKind.PlusToken]: "+" as __String, + [SyntaxKind.MinusToken]: "-" as __String, + [SyntaxKind.AsteriskAsteriskToken]: "**" as __String, + [SyntaxKind.AsteriskToken]: "*" as __String, + [SyntaxKind.SlashToken]: "/" as __String, + [SyntaxKind.PercentToken]: "%" as __String, + [SyntaxKind.LessThanLessThanToken]: "<<" as __String, + [SyntaxKind.GreaterThanGreaterThanToken]: ">>" as __String, + [SyntaxKind.GreaterThanGreaterThanGreaterThanToken]: ">>>" as __String, + [SyntaxKind.AmpersandToken]: "&" as __String, + [SyntaxKind.BarToken]: "|" as __String, + [SyntaxKind.CaretToken]: "^" as __String, + [SyntaxKind.AmpersandAmpersandToken]: "&&" as __String, + [SyntaxKind.BarBarToken]: "||" as __String, + [SyntaxKind.QuestionQuestionToken]: "??" as __String +} as const; +// TSPLUS EXTENSION END + const ambientModuleSymbolRegex = /^".+"$/; const anon = "(anonymous)" as __String & string; @@ -1394,6 +1464,42 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { deferredDiagnosticsCallbacks.push(arg); }; + // TSPLUS EXTENSION START + const typeHashCache = new Map(); + const typeSymbolCache = new Map(); + const companionSymbolCache = new Map(); + const resolvedFluentCache = new Map>(); + const fluentCache = new Map TsPlusFluentExtension | undefined>>(); + const fileMap = { + map: getFileMap(host.getCompilerOptions(), host) + }; + const tsPlusWorldScope: { + implicits: Map>; + rules: Map][] }>; + } = { + implicits: new Map(), + rules: new Map() + }; + const unresolvedFluentCache = new Map>(); + const unresolvedPipeableCache = new Map>(); + const unresolvedPipeableOperatorCache = new Map>(); + const getterCache = new Map>(); + const operatorCache = new Map>(); + const staticFunctionCache = new Map>() + const staticValueCache = new Map>(); + const staticCache = new Map TsPlusStaticFunctionExtension | TsPlusStaticValueExtension | undefined>>(); + const unresolvedStaticCache = new Map>(); + const identityCache = new Map(); + const callCache = new Map(); + const indexCache = new Map { declaration: FunctionDeclaration | VariableDeclarationWithIdentifier, signature: Signature, definition: SourceFile, exportName: string } | undefined>(); + const resolvedIndexCache = new Map(); + const indexAccessExpressionCache = new Map(); + const inheritanceSymbolCache = new Map>() + const tsPlusGlobalImportCache = new Map() + const tsPlusFiles = new Map>(); + const tsPlusFilesFinal = new Map>(); + // TSPLUS EXTENSION END + // Cancellation that controls whether or not we can cancel in the middle of type checking. // In general cancelling is *not* safe for the type checker. We might be in the middle of // computing something, and we will leave our internals in an inconsistent state. Callers @@ -1425,6 +1531,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const emptySymbols = createSymbolTable(); const arrayVariances = [VarianceFlags.Covariant]; + const tsplusStringPrimitiveSymbol = createSymbol(SymbolFlags.None, "string" as __String); + const tsplusNumberPrimitiveSymbol = createSymbol(SymbolFlags.None, "number" as __String); + const tsplusBooleanPrimitiveSymbol = createSymbol(SymbolFlags.None, "boolean" as __String); + const tsplusBigIntPrimitiveSymbol = createSymbol(SymbolFlags.None, "bigint" as __String); + const tsplusFunctionPrimitiveSymbol = createSymbol(SymbolFlags.None, "function" as __String); + const tsplusObjectPrimitiveSymbol = createSymbol(SymbolFlags.None, "object" as __String); + const tsplusArraySymbol = createSymbol(SymbolFlags.None, "array" as __String); + const tsplusReadonlyArraySymbol = createSymbol(SymbolFlags.None, "readonlyArray" as __String); + const compilerOptions = host.getCompilerOptions(); const languageVersion = getEmitScriptTarget(compilerOptions); const moduleKind = getEmitModuleKind(compilerOptions); @@ -1808,6 +1923,125 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { getTypeOnlyAliasDeclaration, getMemberOverrideModifierStatus, isTypeParameterPossiblyReferenced, + // TSPLUS EXTENSION BEGIN + getExtensions, + getGetterExtension, + getFluentExtension, + getStaticExtension, + getStaticCompanionExtension, + getGetterCompanionExtension, + getCallExtension, + isPipeCall: (node) => { + const type = getTypeOfNode(node.expression); + if (type.symbol && type.symbol.declarations) { + return type.symbol.declarations.flatMap(collectTsPlusMacroTags).findIndex((tag) => tag === "pipe") !== -1; + } + return false; + }, + isTailRec, + cloneSymbol, + getTextOfBinaryOp, + getInstantiatedTsPlusSignature, + getIndexAccessExpressionCache: () => indexAccessExpressionCache, + isTsPlusMacroCall, + isTsPlusMacroGetter, + isCompanionReference, + collectTsPlusFluentTags, + hasExportedPlusTags: (declaration) => { + return collectTsPlusFluentTags(declaration).length > 0 || + collectTsPlusPipeableTags(declaration).length > 0 || + collectTsPlusGetterTags(declaration).length > 0 || + collectTsPlusIndexTags(declaration).length > 0 || + collectTsPlusPipeableIndexTags(declaration).length > 0 || + collectTsPlusOperatorTags(declaration).length > 0 || + collectTsPlusPipeableOperatorTags(declaration).length > 0 || + collectTsPlusStaticTags(declaration).length > 0 || + isTsPlusImplicit(declaration) || + collectTsPlusDeriveTags(declaration).length > 0 + }, + getFluentExtensionForPipeableSymbol, + getPrimitiveTypeName, + getResolvedOperator: (node) => { + const nodeLinks = getNodeLinks(node.operatorToken); + if (nodeLinks.resolvedSignature === undefined && nodeLinks.isTsPlusOperatorToken === undefined) { + checkBinaryLikeExpression(node.left, node.operatorToken, node.right); + } + return nodeLinks.resolvedSignature; + }, + getNodeLinks, + collectTsPlusMacroTags, + getTsPlusGlobals: () => { + return arrayFrom(mapIterator(tsPlusGlobalImportCache.values(), ({ importSpecifier }) => getSymbolAtLocation(importSpecifier.name)!)); + }, + getTsPlusGlobal: (name) => { + return tsPlusGlobalImportCache.get(name); + }, + findAndCheckDoAncestor: (node) => { + const doCall = findAncestor(node, (node): node is CallExpression => { + if (isCallExpression(node) && isIdentifier(node.expression)) { + const symbol = checker.getSymbolAtLocation(node.expression) + if (symbol && symbol.declarations) { + for (const declaration of symbol.declarations) { + return collectTsPlusMacroTags(declaration).indexOf("Do") !== -1; + } + } + } + return false; + }) + if (doCall) { + checker.getTypeAtLocation(doCall); + } + }, + getTsPlusSymbolAtLocation: (node) => { + const type = checker.getTypeAtLocation(node); + let symbol: TsPlusSymbol | undefined; + if (isTsPlusType(type)) { + symbol = type.tsPlusSymbol; + } + if (type.symbol && isTsPlusSymbol(type.symbol)) { + symbol = type.symbol; + } + if (type.aliasSymbol && isTsPlusSymbol(type.aliasSymbol)) { + symbol = type.aliasSymbol; + } + if (!symbol) { + symbol = getNodeLinks(node.parent).tsPlusSymbol; + } + return symbol; + }, + getTsPlusExtensionsAtLocation: (node) => { + const symbol = checker.getTsPlusSymbolAtLocation(node); + if (symbol) { + switch (symbol.tsPlusTag) { + case TsPlusSymbolTag.Fluent: { + const signature = symbol.tsPlusResolvedSignatures[0]; + if (signature && signature.tsPlusDeclaration) { + return checker.getExtensionsForDeclaration(signature.tsPlusDeclaration); + } + break; + } + case TsPlusSymbolTag.Getter: + case TsPlusSymbolTag.GetterVariable: + case TsPlusSymbolTag.StaticValue: + case TsPlusSymbolTag.StaticFunction: { + return checker.getExtensionsForDeclaration(symbol.tsPlusDeclaration); + } + } + } + return []; + }, + getExtensionsForDeclaration: (node) => { + return collectTsPlusStaticTags(node) + .concat(collectTsPlusFluentTags(node)) + .concat(collectTsPlusPipeableTags(node)) + .concat(collectTsPlusGetterTags(node)) + .concat(collectTsPlusOperatorTags(node)) + .concat(collectTsPlusPipeableOperatorTags(node)) + }, + getTsPlusFiles: () => tsPlusFilesFinal, + getTsPlusGlobalImports: () => tsPlusGlobalImportCache, + getSymbolLinks, + // TSPLUS EXTENSION END }; function runWithInferenceBlockedFromSourceNode(node: Node | undefined, fn: () => T): T { @@ -1833,6 +2067,667 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return result; } + // TSPLUS EXTENSION BEGIN + function getPrimitiveTypeName(type: Type): string | undefined { + if (type.flags & TypeFlags.StringLike) { + return "String"; + } + if (type.flags & TypeFlags.NumberLike) { + return "Number"; + } + if (type.flags & TypeFlags.BooleanLike) { + return "Boolean"; + } + if (type.flags & TypeFlags.BigIntLike) { + return "BigInt"; + } + } + function getTextOfBinaryOp(kind: SyntaxKind): string | undefined { + return invertedBinaryOp[kind as keyof typeof invertedBinaryOp] as string | undefined; + } + function getCallExtension(node: Node) { + return callCache.get(node); + } + function isTailRec(node: Node) { + const links = getNodeLinks(node); + if (links.isTsPlusTailRec === undefined) { + for (const tag of getJSDocTags(node)) { + if (tag.tagName.escapedText === "tsplus" && typeof tag.comment === "string" && tag.comment === "tailRec") { + links.isTsPlusTailRec = true; + return links.isTsPlusTailRec; + } + } + links.isTsPlusTailRec = false; + } + return links.isTsPlusTailRec; + } + function getFluentExtensionForPipeableSymbol(symbol: TsPlusPipeableIdentifierSymbol) { + const extension = fluentCache.get(symbol.tsPlusTypeName)?.get(symbol.tsPlusName)?.(); + if (extension && every(extension.signatures, (sig) => !sig.tsPlusPipeable)) { + return extension; + } + } + function intersectSets(sets: readonly Set[]): Set { + if (sets.length === 0) { + return new Set(); + } + if (sets.length === 1) { + return sets[0]; + } + + let shortest: Set | undefined; + for (const set of sets) { + if (shortest === undefined || shortest.size > set.size) { + shortest = set + } + } + + // copy the shortest set, so as not to iterate over and delete items from the same set + const out = new Set(shortest) + + for (const set of sets) { + shortest!.forEach((a) => { + if (!set.has(a)) { + out.delete(a) + } + }) + } + + return out; + } + function collectRelevantSymbolsLoop(target: Type, lastNoInherit: Set, lastSeen?: Set) { + const seen: Set = new Set(lastSeen); + const noInherit: Set = new Set(lastNoInherit); + const relevant: Set = new Set(); + let queue: Type[] = [target] + while (queue.length > 0) { + const target = queue.shift()! + if (target.symbol) { + collectExcludedInheritance(target.symbol); + } + if (target.aliasSymbol) { + collectExcludedInheritance(target.aliasSymbol); + } + if (target.symbol && shouldInherit(target.symbol)) { + // Add the current symbol to the return Set + relevant.add(target.symbol) + // Check if the current type inherits other types + if (inheritanceSymbolCache.has(target.symbol)) { + inheritanceSymbolCache.get(target.symbol)!.forEach(addInheritedSymbol); + } + else if (target.symbol.declarations) { + target.symbol.declarations.forEach((declaration) => { + if ((isInterfaceDeclaration(declaration) || isClassDeclaration(declaration)) && declaration.heritageClauses) { + tryCacheTsPlusInheritance(target.symbol, declaration.heritageClauses); + if (inheritanceSymbolCache.has(target.symbol)) { + inheritanceSymbolCache.get(target.symbol)!.forEach(addInheritedSymbol); + } + } + }) + } + } + if (target.aliasSymbol && shouldInherit(target.aliasSymbol)) { + // Add the current symbol to the return Set + relevant.add(target.aliasSymbol); + if (inheritanceSymbolCache.has(target.aliasSymbol)) { + inheritanceSymbolCache.get(target.aliasSymbol)!.forEach(addInheritedSymbol) + } + // If the current type is a union type, add the inherited type symbols common to all members + if (target.flags & TypeFlags.Union) { + collectUnionType(target as UnionType); + } + // If the current type is an intersection type, simply enqueue all unseen members + if (target.flags & TypeFlags.Intersection) { + collectIntersectionType(target as IntersectionType) + } + } + if (!target.symbol && !target.aliasSymbol) { + if (target.flags & TypeFlags.Union) { + collectUnionType(target as UnionType); + } + if (target.flags & TypeFlags.Intersection) { + collectIntersectionType(target as IntersectionType) + } + } + } + seen.clear(); + return relevant; + + function addInheritedSymbol(symbol: Symbol) { + if (shouldInherit(symbol) && symbol.declarations && symbol.declarations.length > 0) { + for (const decl of symbol.declarations) { + // Exclude all declarations but interface and class declarations. + // Symbol declarations can also include other declarations, such as variable declarations + // and module declarations, which we do not want to include for inheritance + if (isInterfaceDeclaration(decl) || isClassDeclaration(decl) || isTypeAliasDeclaration(decl)) { + const type = getTypeOfNode(decl); + if (seen.has(type)) { + continue; + } + + if (!isErrorType(type)) { + seen.add(type); + queue.push(type); + } + } + } + } + } + + function collectUnionType(type: UnionType) { + const types = (type as UnionType).types; + const inherited: Set[] = [] + for (const member of types) { + if (!seen.has(member)) { + inherited.push(collectRelevantSymbolsLoop(member, noInherit, seen)); + } + } + // Add union members as "seen" only after the union has been collected + for (const member of types) { + seen.add(member); + } + + intersectSets(inherited).forEach((s) => { + shouldInherit(s) && relevant.add(s) + }) + } + + function collectIntersectionType(type: IntersectionType) { + for (const member of type.types) { + if (!seen.has(member)) { + seen.add(member); + queue.push(member); + } + } + } + + function collectExcludedInheritance(symbol: Symbol) { + if (symbol.declarations) { + symbol.declarations.forEach((declaration) => { + if ((isInterfaceDeclaration(declaration) || isTypeAliasDeclaration(declaration) || isClassDeclaration(declaration)) && + declaration.tsPlusNoInheritTags) { + declaration.tsPlusNoInheritTags.forEach((tag) => { + noInherit.add(tag) + }) + } + }) + } + } + + function shouldInherit(symbol: Symbol) { + const tags = typeSymbolCache.get(symbol); + if (tags) { + for (const tag of tags) { + if (noInherit.has(tag)) return false; + } + } + return true; + } + } + function collectRelevantSymbols(target: Type) { + const symbols = new Set(); + for (const symbol of collectRelevantSymbolsWorker(getBaseConstraintOrType(target))) { + symbols.add(symbol) + } + if (symbols.size === 0) { + for (const symbol of collectRelevantSymbolsWorker(target)) { + symbols.add(symbol) + } + } + return arrayFrom(symbols.values()); + } + /** + * Recursively collects the symbols associated with the given type. Index 0 is the given type's symbol, + * followed by subtypes, subtypes of subtypes, etc. + */ + function collectRelevantSymbolsWorker(target: Type) { + let returnArray = arrayFrom(collectRelevantSymbolsLoop(target, new Set()).values()) + + // collect primitive symbols last, in case they have overridden extensions + if (target.flags & TypeFlags.StringLike) { + returnArray.push(tsplusStringPrimitiveSymbol); + } + if (target.flags & TypeFlags.NumberLike) { + returnArray.push(tsplusNumberPrimitiveSymbol); + } + if (target.flags & TypeFlags.BooleanLike) { + returnArray.push(tsplusBooleanPrimitiveSymbol); + } + if (target.flags & TypeFlags.BigIntLike) { + returnArray.push(tsplusBigIntPrimitiveSymbol); + } + if (isFunctionType(target)) { + returnArray.push(tsplusFunctionPrimitiveSymbol); + } + if (isTupleType(target)) { + if (target.target.readonly) { + returnArray.push(tsplusReadonlyArraySymbol); + } + else { + returnArray.push(tsplusArraySymbol); + } + } + if (target.flags & TypeFlags.Object) { + returnArray.push(tsplusObjectPrimitiveSymbol); + } + return returnArray + } + function getExtensions(selfNode: Expression) { + const targetType: Type = getTypeOfNode(selfNode); + const symbols = collectRelevantSymbols(targetType); + const copy: Map = new Map(); + const copyFluent: Map> = new Map(); + symbols.forEach((target) => { + if (typeSymbolCache.has(target) && !isCompanionReference(selfNode)) { + typeSymbolCache.get(target)!.forEach((typeSymbol) => { + const _static = staticCache.get(typeSymbol); + if (_static) { + _static.forEach((v, k) => { + if (copy.has(k)) { + return; + } + const ext = v(); + if (ext) { + copy.set(k, ext.patched) + } + }); + } + const _fluent = fluentCache.get(typeSymbol); + if (_fluent) { + _fluent.forEach((v, k) => { + const extension = v() + if (extension) { + if (isExtensionValidForTarget(getTypeOfSymbol(extension.patched), targetType)) { + if (!copyFluent.has(k)) { + copyFluent.set(k, new Set()); + } + copyFluent.get(k)!.add(extension); + } + } + }); + } + const _getter = getterCache.get(typeSymbol); + if (_getter) { + _getter.forEach((v, k) => { + if (copy.has(k)) { + return; + } + const symbol = v.patched(selfNode); + if (symbol) { + copy.set(k, symbol); + } + }); + } + }); + } + if (companionSymbolCache.has(target) && isCompanionReference(selfNode)) { + companionSymbolCache.get(target)!.forEach((typeSymbol) => { + const _static = staticCache.get(typeSymbol); + if (_static) { + _static.forEach((v, k) => { + if (copy.has(k)) { + return; + } + const ext = v() + if (ext) { + copy.set(k, ext.patched) + } + }); + } + }) + } + }) + fluentCache.get("global")?.forEach((v, k) => { + const extension = v() + if (extension) { + if (isExtensionValidForTarget(getTypeOfSymbol(extension.patched), targetType)) { + if (!copyFluent.has(k)) { + copyFluent.set(k, new Set()); + } + copyFluent.get(k)!.add(extension); + } + } + }); + getterCache.get("global")?.forEach((v, k) => { + if (copy.has(k)) { + return; + } + const symbol = v.patched(selfNode); + if (symbol) { + copy.set(k, symbol); + } + }); + copyFluent.forEach((extensions, k) => { + copy.set( + k, + createTsPlusFluentSymbolWithType(k, arrayFrom(flatMapIterator(extensions.values(), (e) => getSignaturesOfType(getTypeOfSymbol(e.patched), SignatureKind.Call))) as TsPlusSignature[]) + ); + }); + copy.delete("__call"); + return copy; + } + function isExtensionValidForTarget(extension: Type, targetType: Type): boolean { + return getSignaturesOfType(extension, SignatureKind.Call).find((candidate) => { + if (candidate.thisParameter) { + const paramType = unionIfLazy(getTypeOfSymbol(candidate.thisParameter)); + if (!candidate.typeParameters) { + return isTypeAssignableTo(targetType, paramType); + } else { + const inferenceContext = createInferenceContext( + candidate.typeParameters, + candidate, + InferenceFlags.None + ); + inferTypes(inferenceContext.inferences, targetType, paramType); + const instantiatedThisType = instantiateType(paramType, inferenceContext.mapper); + return isTypeAssignableTo(targetType, instantiatedThisType); + } + } + return false; + }) !== undefined; + } + function unionIfLazy(_paramType: Type) { + const isLazy = isLazyParameterByType(_paramType); + const paramType = isLazy ? getUnionType([_paramType, (_paramType as TypeReference).resolvedTypeArguments![0]], UnionReduction.None) : _paramType; + return paramType + } + function getFluentExtension(targetType: Type, name: string): Type | undefined { + const symbols = collectRelevantSymbols(targetType); + const candidates: Set = new Set(); + for (const target of symbols) { + if (typeSymbolCache.has(target)) { + const x = typeSymbolCache.get(target)!.flatMap( + (tag) => { + if (fluentCache.has(tag)) { + const cache = fluentCache.get(tag) + if (cache?.has(name)) { + return [cache.get(name)!] + } + } + return [] + } + ) + if (x.length === 0) { + continue; + } + else { + x.forEach((getExt) => { + const ext = getExt(); + if (ext) { + for (const signature of ext.signatures) { + candidates.add(signature); + } + } + }); + } + } + } + const globalExtension = fluentCache.get("global")?.get(name)?.(); + if (globalExtension) { + for (const signature of globalExtension.signatures) { + candidates.add(signature) + } + } + if (candidates.size > 0) { + return getTypeOfSymbol(createTsPlusFluentSymbolWithType(name, arrayFrom(candidates.values()))); + } + } + function getGetterExtension(targetType: Type, name: string) { + const symbols = collectRelevantSymbols(targetType) + for (const target of symbols) { + if (typeSymbolCache.has(target)) { + const x = typeSymbolCache.get(target)!.flatMap( + (tag) => { + if (getterCache.has(tag)) { + const cache = getterCache.get(tag) + if (cache?.has(name)) { + return [cache.get(name)!] + } + } + return [] + } + ) + if (x.length === 0) { + continue; + } + else { + return x[x.length - 1]; + } + } + } + return getterCache.get("global")?.get(name); + } + function getGetterCompanionExtension(targetType: Type, name: string) { + const symbols = collectRelevantSymbols(targetType) + for (const target of symbols) { + if (companionSymbolCache.has(target)) { + const x = companionSymbolCache.get(target)!.flatMap( + (tag) => { + if (getterCache.has(tag)) { + const cache = getterCache.get(tag) + if (cache?.has(name)) { + return [cache.get(name)!] + } + } + return [] + } + ) + if (x.length === 0) { + continue; + } + else { + return x[x.length - 1]; + } + } + } + } + function getStaticExtension(targetType: Type, name: string) { + const symbols = collectRelevantSymbols(targetType) + for (const target of symbols) { + if (typeSymbolCache.has(target)) { + const x = typeSymbolCache.get(target)!.flatMap( + (tag) => { + if (staticCache.has(tag)) { + const cache = staticCache.get(tag) + if (cache?.has(name)) { + return [cache.get(name)!] + } + } + return [] + } + ) + if (x.length === 0) { + continue; + } + else { + return x[x.length - 1](); + } + } + } + } + function getStaticCompanionExtension(targetType: Type, name: string) { + const symbols = collectRelevantSymbols(targetType) + for (const target of symbols) { + if (companionSymbolCache.has(target)) { + const x = companionSymbolCache.get(target)!.flatMap( + (tag) => { + if (staticCache.has(tag)) { + const cache = staticCache.get(tag) + if (cache?.has(name)) { + return [cache.get(name)!] + } + } + return [] + } + ) + if (x.length === 0) { + continue; + } + else { + return x[x.length - 1](); + } + } + } + } + function isTransformablePipeableExtension(type: Type): boolean { + if (type.symbol) { + if (isTsPlusSymbol(type.symbol)) { + if (type.symbol.tsPlusTag === TsPlusSymbolTag.PipeableIdentifier) { + const fluent = getFluentExtensionForPipeableSymbol(type.symbol); + if (fluent) { + return true; + } + } + if (type.symbol.tsPlusTag === TsPlusSymbolTag.PipeableMacro) { + return true; + } + } + } + return false; + } + function markUsedParams(params: readonly TypeParameterDeclaration[], types: readonly Type[], cache: Set, node: Node) { + function visitor(node: Node): Node { + const t = getTypeOfNode(node); + for (let i = 0; i < params.length; i++) { + const type = types[i]; + if (isTypeIdenticalTo(type, t)) { + cache.add(params[i]); + } + } + return visitEachChild(node, visitor, nullTransformationContext); + } + visitNode(node, visitor); + } + function partitionTypeParametersForPipeable(dataFirst: FunctionDeclaration | ArrowFunction | FunctionExpression): [TypeParameterDeclaration[] | undefined, TypeParameterDeclaration[] | undefined] { + if (!dataFirst.typeParameters) { + return [undefined, undefined]; + } + const typeParams = dataFirst.typeParameters; + const types = map(typeParams, getTypeOfNode); + const cache = new Set() + for (let i = 1; i < dataFirst.parameters.length; i++) { + const param = dataFirst.parameters[i]; + markUsedParams(typeParams, types, cache, param); + } + let loop = true; + const processed = new Set(); + while (loop) { + const pre = cache.size; + cache.forEach((typeParam) => { + if (!processed.has(typeParam)) { + processed.add(typeParam); + if (typeParam.constraint) { + markUsedParams(typeParams, types, cache, typeParam.constraint); + } + if (typeParam.default) { + markUsedParams(typeParams, types, cache, typeParam.default); + } + } + }) + if (cache.size === pre) { + loop = false; + } + } + const left: TypeParameterDeclaration[] = []; + const right: TypeParameterDeclaration[] = []; + forEach(dataFirst.typeParameters, (param) => { + if (cache.has(param)) { + left.push(param); + } + else { + right.push(param); + } + }); + return [ + left.length > 0 ? left : undefined, + right.length > 0 ? right : undefined + ]; + } + function generatePipeable(declarationNode: VariableDeclaration, dataFirst: FunctionDeclaration | ArrowFunction | FunctionExpression): Type | undefined { + const signatures = getSignaturesOfType(getTypeOfNode(dataFirst), SignatureKind.Call) + if (signatures.length > 0) { + const returnExpression = createSyntheticExpression(dataFirst, getReturnTypeOfSignature(signatures[0])) + const [paramsFirst, paramsSecond] = partitionTypeParametersForPipeable(dataFirst); + const returnFunction = factory.createFunctionExpression( + undefined, + undefined, + undefined, + paramsSecond, + [dataFirst.parameters[0]], + undefined, + factory.createBlock( + [factory.createReturnStatement(returnExpression)], + true + ) + ); + setParent(returnFunction.body, returnFunction); + setParent((returnFunction.body).statements[0], returnFunction.body); + returnFunction.locals = createSymbolTable(); + const returnFunctionSymbol = createSymbol( + SymbolFlags.Function, + InternalSymbolName.Function + ); + returnFunction.symbol = returnFunctionSymbol; + returnFunctionSymbol.declarations = [returnFunction]; + returnFunctionSymbol.valueDeclaration = returnFunction; + const pipeable = factory.createFunctionDeclaration( + [factory.createModifier(SyntaxKind.DeclareKeyword)], + undefined, + dataFirst.name, + paramsFirst, + dataFirst.parameters.slice(1, dataFirst.parameters.length), + undefined, + factory.createBlock( + [factory.createReturnStatement(returnFunction)], + true + ) + ); + setParent(returnFunction, pipeable); + setParent(pipeable.body, pipeable); + setParent((pipeable.body as Block).statements[0], pipeable.body); + pipeable.locals = createSymbolTable(); + const pipeableSymbol = createSymbol( + SymbolFlags.Function, + InternalSymbolName.Function + ) as TsPlusPipeableMacroSymbol; + pipeableSymbol.tsPlusTag = TsPlusSymbolTag.PipeableMacro; + pipeableSymbol.tsPlusDeclaration = declarationNode; + pipeableSymbol.tsPlusDataFirst = dataFirst; + pipeableSymbol.tsPlusSourceFile = getSourceFileOfNode(dataFirst); + pipeableSymbol.tsPlusExportName = dataFirst.name!.escapedText.toString(); + pipeable.symbol = pipeableSymbol; + pipeableSymbol.declarations = [pipeable]; + setParent(pipeable, declarationNode); + const declarationSymbol = getSymbolAtLocation(declarationNode) + if (declarationSymbol) { + declarationSymbol.declarations = [pipeable] + declarationSymbol.valueDeclaration = pipeable + } + return getTypeOfNode(pipeable); + } + } + function isTsPlusMacroGetter(node: Node, macro: string): boolean { + const links = getNodeLinks(node) + return !!links.tsPlusGetterExtension && + !!links.tsPlusGetterExtension.declaration.tsPlusMacroTags && + links.tsPlusGetterExtension.declaration.tsPlusMacroTags.includes(macro); + } + function isTsPlusMacroCall(node: Node, macro: K): node is TsPlusMacroCallExpression { + if (!isCallExpression(node) && !isBinaryExpression(node)) { + return false; + } + const links = getNodeLinks(isCallExpression(node) ? node : node.operatorToken); + if ( + links.resolvedSignature && + links.resolvedSignature.declaration && + collectTsPlusMacroTags(links.resolvedSignature.declaration).findIndex((tag) => tag === macro) !== -1 + ) { + return true; + } + return false; + } + // TSPLUS EXTENSION END + function getResolvedSignatureWorker(nodeIn: CallLikeExpression, candidatesOutArray: Signature[] | undefined, argumentCount: number | undefined, checkMode: CheckMode, editingArgument?: Node): Signature | undefined { const node = getParseTreeNode(nodeIn, isCallLikeExpression); apparentArgumentCount = argumentCount; @@ -1845,7 +2740,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const tupleTypes = new Map(); - const unionTypes = new Map(); + const unionTypes = new Map(); const intersectionTypes = new Map(); const stringLiteralTypes = new Map(); const numberLiteralTypes = new Map(); @@ -2962,7 +3857,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { isUse: boolean, excludeGlobals = false, getSpellingSuggestions = true): Symbol | undefined { - return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSpellingSuggestions, getSymbol); + return resolveNameHelper(location, name, meaning, nameNotFoundMessage, nameArg, isUse, excludeGlobals, getSpellingSuggestions, getSymbol, true); } function resolveNameHelper( @@ -2974,7 +3869,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { isUse: boolean, excludeGlobals: boolean, getSpellingSuggestions: boolean, - lookup: typeof getSymbol): Symbol | undefined { + lookup: typeof getSymbol, + checkTsPlusGlobals = true): Symbol | undefined { const originalLocation = location; // needed for did-you-mean error reporting, which gathers candidates starting from the original location let result: Symbol | undefined; let lastLocation: Node | undefined; @@ -3046,6 +3942,34 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { result = undefined; } } + // TSPLUS EXTENSION START + else { + if (originalLocation && originalLocation.parent && (isPropertyAccessExpression(originalLocation.parent) || isCallExpression(originalLocation.parent))) { + const symbol = location.locals.get(name) + if (symbol) { + if (companionSymbolCache.has(symbol)) { + result = symbol; + getSymbolLinks(symbol).isPossibleCompanionReference = true; + break loop; + } + if (symbol.exportSymbol && companionSymbolCache.has(symbol.exportSymbol)) { + result = symbol.exportSymbol; + getSymbolLinks(symbol.exportSymbol).isPossibleCompanionReference = true; + break loop; + } + if (symbol.declarations && symbol.declarations[0] && isImportSpecifier(symbol.declarations[0])) { + const originalSymbol = getTargetOfImportSpecifier(symbol.declarations[0], false); + if (originalSymbol && companionSymbolCache.has(originalSymbol)) { + result = originalSymbol; + getSymbolLinks(originalSymbol).isPossibleCompanionReference = true; + symbol.isReferenced = SymbolFlags.Value; + break loop; + } + } + } + } + } + // TSPLUS EXTENSION END } withinDeferredContext = withinDeferredContext || getIsDeferredContext(location, lastLocation); switch (location.kind) { @@ -3296,6 +4220,46 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { location.parent; } + // TSPLUS EXTENSION START + if (!result && originalLocation && checkTsPlusGlobals) { + const globalImport = tsPlusGlobalImportCache.get(name as string); + if (globalImport) { + const targetSymbol = getTargetOfImportSpecifier(globalImport.importSpecifier, false); + if (targetSymbol && + ((targetSymbol.flags & meaning) || + companionSymbolCache.has(targetSymbol) && (isPropertyAccessExpression(originalLocation.parent) || isCallExpression(originalLocation.parent))) + ) { + const withoutGlobals = resolveNameHelper( + originalLocation, + name, + SymbolFlags.All, + void 0, + nameArg, + isUse, + excludeGlobals, + getSpellingSuggestions, + lookup, + false + ); + if (!withoutGlobals) { + getNodeLinks(originalLocation).isTsPlusGlobalIdentifier = true; + if (companionSymbolCache.has(targetSymbol)) { + getSymbolLinks(targetSymbol).isPossibleCompanionReference = true; + } + result = targetSymbol; + } + } + } + } + if (!result && originalLocation) { + const globalSymbol = getGlobalSymbol(name, SymbolFlags.Type, undefined); + if (globalSymbol && companionSymbolCache.has(globalSymbol)) { + result = globalSymbol; + getSymbolLinks(globalSymbol).isPossibleCompanionReference = true; + } + } + // TSPLUS EXTENSION END + // We just climbed up parents looking for the name, meaning that we started in a descendant node of `lastLocation`. // If `result === lastSelfReferenceLocation.symbol`, that means that we are somewhere inside `lastSelfReferenceLocation` looking up a name, and resolving to `lastLocation` itself. // That means that this is a self-reference of `lastLocation`, and shouldn't count this when considering whether `lastLocation` is used. @@ -4699,7 +5663,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (!nodeIsSynthesized(name) && isEntityName(name) && (symbol.flags & SymbolFlags.Alias || name.parent.kind === SyntaxKind.ExportAssignment)) { markSymbolOfAliasDeclarationIfTypeOnly(getAliasDeclarationFromName(name), symbol, /*finalTarget*/ undefined, /*overwriteEmpty*/ true); } - return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol); + return (symbol.flags & meaning) || dontResolveAlias || + // TSPLUS EXTENSION BEGIN + getSymbolLinks(symbol).isPossibleCompanionReference + // TSPLUS EXTENSION END + ? symbol + : resolveAlias(symbol); } /** @@ -6198,6 +7167,39 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return !!type.symbol && !!(type.symbol.flags & SymbolFlags.Class) && (type === getDeclaredTypeOfClassOrInterface(type.symbol) || (!!(type.flags & TypeFlags.Object) && !!(getObjectFlags(type) & ObjectFlags.IsClassInstanceClone))); } + // TSPLUS EXTENSION START + function isCompanionReference(node: Expression | QualifiedName): boolean { + let type: Type | undefined + + const symbol = getSymbolAtLocation(node); + if (symbol) { + type = getTypeOfSymbol(symbol); + } + else { + type = getNodeLinks(node).tsPlusResolvedType; + } + + if (!type) { + return false + } + + // Class companion object + if (getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol && type.symbol.flags & SymbolFlags.Class) { + return true + } + + // Synthetic Interface or TypeAlias companion object + if (symbol && symbol.declarations && symbol.declarations.length > 0) { + const declaration = symbol.declarations[0] + if (isInterfaceDeclaration(declaration) || isTypeAliasDeclaration(declaration)) { + return !!getSymbolLinks(symbol).isPossibleCompanionReference; + } + } + + return false + } + // TSPLUS EXTENSION END + function createNodeBuilder() { return { typeToTypeNode: (type: Type, enclosingDeclaration?: Node, flags?: NodeBuilderFlags, tracker?: SymbolTracker) => @@ -6238,7 +7240,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function withContext(enclosingDeclaration: Node | undefined, flags: NodeBuilderFlags | undefined, tracker: SymbolTracker | undefined, cb: (context: NodeBuilderContext) => T): T | undefined { - Debug.assert(enclosingDeclaration === undefined || (enclosingDeclaration.flags & NodeFlags.Synthesized) === 0); + Debug.assert(enclosingDeclaration === undefined || (enclosingDeclaration.parent && isDeclaration(enclosingDeclaration.parent) && !!enclosingDeclaration.parent?.symbol && isTsPlusSymbol(enclosingDeclaration.parent.symbol)) || (enclosingDeclaration.flags & NodeFlags.Synthesized) === 0); const moduleResolverHost = tracker?.trackSymbol ? tracker.moduleResolverHost : flags! & NodeBuilderFlags.DoNotIncludeSymbolChain ? createBasicNodeBuilderModuleSpecifierResolutionHost(host) : @@ -9135,7 +10137,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { break; case SyntaxKind.NamespaceExportDeclaration: // export as namespace foo - // TODO: Not part of a file's local or export symbol tables // Is bound into file.symbol.globalExports instead, which we don't currently traverse addResult(factory.createNamespaceExportDeclaration(idText((node as NamespaceExportDeclaration).name)), ModifierFlags.None); break; @@ -11365,6 +12366,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { if (symbol.flags & SymbolFlags.Alias) { return getTypeOfAlias(symbol); } + if ((symbol.flags & SymbolFlags.Interface) || (symbol.flags & SymbolFlags.TypeAlias)) { + if (companionSymbolCache.has(symbol)) { + if (symbol.declarations?.[0]) { + return getTypeOfNode(symbol.declarations[0]) + } + } + } return errorType; } @@ -16148,6 +17156,161 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return result; } + // TSPLUS EXTENSION START + function checkTsPlusCustomCall( + declaration: Declaration, + errorNode: Node, + args: Expression[], + checkMode: CheckMode | undefined, + signature?: Signature, + addDiagnostic?: (_: Diagnostic) => void, + ): Type { + const funcType = getTypeOfNode(declaration); + const apparentType = getApparentType(funcType); + const candidate = signature ?? getSignaturesOfType(apparentType, SignatureKind.Call)[0]!; + if (!candidate) { + return errorType; + } + const node = factory.createCallExpression( + factory.createIdentifier("$tsplus_custom_call"), + [], + args + ); + setTextRange(node, errorNode) + setParent(node, declaration.parent); + if (candidate.typeParameters) { + const inferenceContext = createInferenceContext( + candidate.typeParameters, + candidate, + InferenceFlags.None + ); + const typeArgumentTypes = inferTypeArguments( + node, + candidate, + args, + checkMode || CheckMode.Normal, + inferenceContext + ); + const signature = getSignatureInstantiation( + candidate, + typeArgumentTypes, + /*isJavascript*/ false, + inferenceContext && inferenceContext.inferredTypeParameters + ); + const digs = getSignatureApplicabilityError( + node, + args, + signature, + assignableRelation, + checkMode || CheckMode.Normal, + /*reportErrors*/ addDiagnostic ? true : false, + /*containingMessageChain*/ void 0 + ); + if (digs) { + digs.forEach((dig) => { + addDiagnostic?.(dig); + }); + return errorType; + } + return getReturnTypeOfSignature(signature); + } + const digs = getSignatureApplicabilityError( + node, + args, + candidate, + assignableRelation, + CheckMode.Normal, + /*reportErrors*/ true, + /*containingMessageChain*/ void 0 + ); + if (digs) { + digs.forEach((dig) => { + addDiagnostic?.(dig); + }); + return errorType; + } + return getReturnTypeOfSignature(candidate); + } + function getInstantiatedTsPlusSignature( + declaration: Declaration, + args: Expression[], + checkMode: CheckMode | undefined, + ): Signature { + const funcType = getTypeOfNode(declaration); + const apparentType = getApparentType(funcType); + const candidate = getSignaturesOfType(apparentType, SignatureKind.Call)[0]!; + const node = factory.createCallExpression( + factory.createIdentifier("$tsplus_custom_call"), + [], + args + ); + setParent(node, declaration.parent); + if (candidate.typeParameters) { + const inferenceContext = createInferenceContext( + candidate.typeParameters, + candidate, + InferenceFlags.None + ); + const typeArgumentTypes = inferTypeArguments( + node, + candidate, + args, + checkMode || CheckMode.Normal, + inferenceContext + ); + const signature = getSignatureInstantiation( + candidate, + typeArgumentTypes, + /*isJavascript*/ false, + inferenceContext && inferenceContext.inferredTypeParameters + ); + return signature; + } + return candidate; + } + + function computeUnifiedType(unifier: FunctionDeclaration, union: Type) { + for (const signature of getSignaturesOfType(getTypeOfNode(unifier), SignatureKind.Call)) { + if (signature.minArgumentCount === 1 && signature.typeParameters) { + const context = createInferenceContext(signature.typeParameters, signature, InferenceFlags.None); + inferTypes(context.inferences, union, getTypeOfSymbol(signature.parameters[0])); + const instantiated = getSignatureInstantiation(signature, getInferredTypes(context), /*isJavascript*/ false) + if (isTypeAssignableTo(union, getTypeOfSymbol(instantiated.parameters[0]))) { + return getReturnTypeOfSignature(instantiated); + } + } + } + return errorType; + } + + function getUnifiedType(unionType: UnionType): Type { + if (unionType.tsPlusUnified) { + return unionType; + } + let type = unionType.types[0]; + while (type.flags & TypeFlags.Union) { + type = (type as UnionType).types[0]; + } + const targetSymbol = type.symbol || type.aliasSymbol; + if (targetSymbol) { + for (const declaration of (targetSymbol.declarations ?? [])) { + for (let target of collectTsPlusTypeTags(declaration)) { + const id = identityCache.get(target); + if (id) { + const unified = computeUnifiedType(id, unionType); + if ((unified.flags & TypeFlags.Union) && unionType.types.length < (unified as UnionType).types.length) { + return unionType; + } + return unified; + } + } + } + } + unionType.tsPlusUnified = true; + return unionType; + } + // TSPLUS EXTENSION END + // We sort and deduplicate the constituent types based on object identity. If the subtypeReduction // flag is specified we also reduce the constituent type set to only include types that aren't subtypes // of other types. Subtype reduction is expensive for large union types and is possible only when union @@ -16218,7 +17381,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const objectFlags = (includes & TypeFlags.NotPrimitiveUnion ? 0 : ObjectFlags.PrimitiveUnion) | (includes & TypeFlags.Intersection ? ObjectFlags.ContainsIntersections : 0); - return getUnionTypeFromSortedList(typeSet, objectFlags, aliasSymbol, aliasTypeArguments, origin); + return getUnionTypeFromSortedList(typeSet, objectFlags, aliasSymbol, aliasTypeArguments, origin, unionReduction); } function getUnionOrIntersectionTypePredicate(signatures: readonly Signature[], kind: TypeFlags | undefined): TypePredicate | undefined { @@ -16259,7 +17422,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } // This function assumes the constituent type list is sorted and deduplicated. - function getUnionTypeFromSortedList(types: Type[], objectFlags: ObjectFlags, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: Type): Type { + function getUnionTypeFromSortedList(types: Type[], objectFlags: ObjectFlags, aliasSymbol?: Symbol, aliasTypeArguments?: readonly Type[], origin?: Type, unionReduction?: UnionReduction): Type { if (types.length === 0) { return neverType; } @@ -16273,16 +17436,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const id = typeKey + getAliasId(aliasSymbol, aliasTypeArguments); let type = unionTypes.get(id); if (!type) { - type = createType(TypeFlags.Union) as UnionType; - type.objectFlags = objectFlags | getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); - type.types = types; - type.origin = origin; - type.aliasSymbol = aliasSymbol; - type.aliasTypeArguments = aliasTypeArguments; + const unionType = createType(TypeFlags.Union) as UnionType; + unionType.objectFlags = objectFlags | getPropagatingFlagsOfTypes(types, /*excludeKinds*/ TypeFlags.Nullable); + unionType.types = types; + unionType.origin = origin; + unionType.aliasSymbol = aliasSymbol; + unionType.aliasTypeArguments = aliasTypeArguments; if (types.length === 2 && types[0].flags & TypeFlags.BooleanLiteral && types[1].flags & TypeFlags.BooleanLiteral) { - type.flags |= TypeFlags.Boolean; - (type as UnionType & IntrinsicType).intrinsicName = "boolean"; + unionType.flags |= TypeFlags.Boolean; + (unionType as UnionType & IntrinsicType).intrinsicName = "boolean"; } + type = !aliasSymbol && unionReduction !== UnionReduction.None ? getUnifiedType(unionType) : unionType; unionTypes.set(id, type); } return type; @@ -25848,6 +27012,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const type = getTypeOfDottedName((node as PropertyAccessExpression).expression, diagnostic); if (type) { const name = (node as PropertyAccessExpression).name; + // TSPLUS EXTENSION BEGIN + // Since our fluent properties are "fake", to resolve a dotted name, we have to look up + // the parent type in the cache + const symbol = type.symbol || type.aliasSymbol; + if (symbol && typeSymbolCache.has(symbol)) { + const fluentExtenion = getFluentExtension(type, name.escapedText as string); + if (fluentExtenion) { + return fluentExtenion; + } + } + // TSPLUS EXTENSION END let prop: Symbol | undefined; if (isPrivateIdentifier(name)) { if (!type.symbol) { @@ -27450,11 +28625,52 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return type; } + // TSPLUS EXTENSION START + function tsPlusShouldMarkIdentifierAliasReferenced(node: Identifier, type: Type): boolean { + if (node.parent && isCallExpression(node.parent) && node === node.parent.expression && type.symbol && type.symbol.valueDeclaration) { + if (collectTsPlusMacroTags(type.symbol.valueDeclaration).find((tag) => tag === "pipe")) { + return false; + } + } + if ( + !(node.parent && + isCallExpression(node.parent) && + node.parent.expression === node && + getSignaturesOfType(type, SignatureKind.Call).length === 0 && + ( + (getStaticExtension(type, "__call") != null) || + (getStaticCompanionExtension(type, "__call") != null) + ) + ) && + !isTransformablePipeableExtension(type) + ) { + return true; + } + return false; + } function checkIdentifier(node: Identifier, checkMode: CheckMode | undefined): Type { if (isThisInTypeQuery(node)) { return checkThisExpression(node); } + const type = checkIdentifierOriginal(node, checkMode); + // We should only mark aliases as referenced if there isn't a local value declaration + // for the symbol. Also, don't mark any property access expression LHS - checkPropertyAccessExpression will handle that + if (!(node.parent && isPropertyAccessExpression(node.parent) && node.parent.expression === node)) { + // We should also not mark as used identifiers that will be replaced + if (tsPlusShouldMarkIdentifierAliasReferenced(node, type)) { + markAliasReferenced(getResolvedSymbol(node), node); + } + } + return type; + } + // TSPLUS EXTENSION END + + function checkIdentifierOriginal(node: Identifier, checkMode: CheckMode | undefined): Type { + if (isThisInTypeQuery(node)) { + return checkThisExpression(node); + } + const symbol = getResolvedSymbol(node); if (symbol === unknownSymbol) { return errorType; @@ -30944,7 +32160,83 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { && getThisContainer(node, /*includeArrowFunctions*/ true, /*includeClassComputedPropertyName*/ false) === getDeclaringConstructor(prop); } + // TSPLUS EXTENSION START + function checkPropertyAccessForExtension(node: PropertyAccessExpression | QualifiedName, _left: Expression | QualifiedName, leftType: Type, right: Identifier | PrivateIdentifier, _checkMode: CheckMode | undefined) { + const inType = getPropertiesOfType(leftType).findIndex((p) => p.escapedName === right.escapedText) !== -1; + if (!inType) { + const nodeLinks = getNodeLinks(node); + if (nodeLinks.tsPlusResolvedType) { + return nodeLinks.tsPlusResolvedType; + } + if (isCompanionReference(_left)) { + const companionExt = getStaticCompanionExtension(leftType, right.escapedText.toString()); + if (companionExt) { + nodeLinks.tsPlusStaticExtension = companionExt; + nodeLinks.tsPlusResolvedType = companionExt.type + return companionExt.type; + } + return; + } + const fluentExtType = getFluentExtension(leftType, right.escapedText.toString()); + if (fluentExtType && isCallExpression(node.parent) && node.parent.expression === node) { + if (isExtensionValidForTarget(fluentExtType, leftType)) { + if (isIdentifier(_left)) { + markAliasReferenced(getResolvedSymbol(_left), _left); + } + nodeLinks.tsPlusResolvedType = fluentExtType; + nodeLinks.isFluent = true; + return fluentExtType; + } + return; + } + const getterExt = getGetterExtension(leftType, right.escapedText.toString()) + if (getterExt && isExpression(_left)) { + const symbol = getterExt.patched(_left); + if (symbol) { + if (isIdentifier(_left)) { + markAliasReferenced(getResolvedSymbol(_left), _left); + } + const type = getTypeOfSymbol(symbol); + nodeLinks.tsPlusGetterExtension = getterExt; + nodeLinks.tsPlusResolvedType = type; + nodeLinks.tsPlusSymbol = symbol; + return type; + } + } + const staticExt = getStaticExtension(leftType, right.escapedText.toString()); + if (staticExt) { + nodeLinks.tsPlusStaticExtension = staticExt; + nodeLinks.tsPlusResolvedType = staticExt.type; + return staticExt.type; + } + } + } + function checkFluentPipeableAgreement(pipeableExtension: TsPlusPipeableExtension) { + if (!fluentCache.has(pipeableExtension.typeName)) { + return; + } + const fluentMap = fluentCache.get(pipeableExtension.typeName)!; + if (!fluentMap.has(pipeableExtension.funcName)) { + return; + } + const fluentExtension = fluentMap.get(pipeableExtension.funcName)!(); + if (!fluentExtension || some(fluentExtension.types, ({ type: fluentType }) => isTypeAssignableTo(fluentType, pipeableExtension.getTypeAndSignatures()[0]))) { + return; + } + else { + error(pipeableExtension.declaration, Diagnostics.Declaration_annotated_as_pipeable_is_not_assignable_to_its_corresponding_fluent_declaration); + return; + } + } + // TSPLUS EXTENSION END + function checkPropertyAccessExpressionOrQualifiedName(node: PropertyAccessExpression | QualifiedName, left: Expression | QualifiedName, leftType: Type, right: Identifier | PrivateIdentifier, checkMode: CheckMode | undefined) { + // TSPLUS EXTENSION START + const forExtension = checkPropertyAccessForExtension(node, left, leftType, right, checkMode); + if (forExtension) { + return forExtension; + } + // TSPLUS EXTENSION END const parentSymbol = getNodeLinks(left).resolvedSymbol; const assignmentKind = getAssignmentTargetKind(node); const apparentType = getApparentType(assignmentKind !== AssignmentKind.None || isMethodAccessForCall(node) ? getWidenedType(leftType) : leftType); @@ -31007,7 +32299,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { !(prop && (isConstEnumOrConstEnumOnlyModule(prop) || prop.flags & SymbolFlags.EnumMember && node.parent.kind === SyntaxKind.EnumMember)) || shouldPreserveConstEnums(compilerOptions) && isExportOrExportExpression(node) )) { - markAliasReferenced(parentSymbol, node); + // TSPLUS EXTENSION START + if (isIdentifier(right) && prop) { + const propLinks = getSymbolLinks(prop); + if (propLinks.type) { + if (!isTransformablePipeableExtension(propLinks.type)) { + markAliasReferenced(parentSymbol, node); + } + } + else { + markAliasReferenced(parentSymbol, node); + } + } + else { + markAliasReferenced(parentSymbol, node); + } + // TSPLUS EXTENSION END } let propType: Type; @@ -31636,6 +32943,37 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return errorType; } + // TSPLUS EXTENSION BEGIN + const symbols = collectRelevantSymbols(objectType) + const tags = symbols.flatMap((symbol) => { + const tag = typeSymbolCache.get(symbol) + if (tag) { + return tag + } + return [] + }) + const indexer = tags.flatMap((tag) => { + const indexer = indexCache.get(tag) + if (indexer) { + return [indexer()] + } + return [] + })[0] + if (indexer) { + const res = checkTsPlusCustomCall( + indexer.declaration, + node, + [node.expression, indexExpression], + checkMode, + indexer.signature, + ) + if (!isErrorType(res)) { + indexAccessExpressionCache.set(node, indexer) + return res; + } + } + // TSPLUS EXTENSION END + const effectiveIndexType = isForInVariableForNumericPropertyNames(indexExpression) ? numberType : indexType; const accessFlags = isAssignmentTarget(node) ? AccessFlags.Writing | (isGenericObjectType(objectType) && !isThisTypeParameter(objectType) ? AccessFlags.NoIndexSignatures : 0) : @@ -31958,14 +33296,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const thisType = getThisTypeOfSignature(signature); if (thisType && couldContainTypeVariables(thisType)) { + // TSPLUS EXTENTION BEGIN + const paramType = unionIfLazy(thisType); const thisArgumentNode = getThisArgumentOfCall(node); - inferTypes(context.inferences, getThisArgumentType(thisArgumentNode), thisType); + inferTypes(context.inferences, getThisArgumentType(thisArgumentNode), paramType); + // TSPLUS EXTENTION END } for (let i = 0; i < argCount; i++) { const arg = args[i]; if (arg.kind !== SyntaxKind.OmittedExpression && !(checkMode & CheckMode.IsForStringLiteralArgumentCompletions && hasSkipDirectInferenceFlag(arg))) { - const paramType = getTypeAtPosition(signature, i); + // TSPLUS EXTENTION BEGIN + const paramType = unionIfLazy(getTypeAtPosition(signature, i)); + // TSPLUS EXTENTION END if (couldContainTypeVariables(paramType)) { const argType = checkExpressionWithContextualType(arg, paramType, context, checkMode); inferTypes(context.inferences, argType, paramType); @@ -31981,6 +33324,18 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getInferredTypes(context); } + // TSPLUS EXTENSION BEGIN + function isLazyParameterByType(type: Type): type is TypeReference { + if (type.symbol && type.symbol.declarations && type.symbol.declarations.length > 0) { + const tag = collectTsPlusTypeTags(type.symbol.declarations[0])[0]; + if (tag === "tsplus/LazyArgument") { + return true; + } + } + return false; + } + // TSPLUS EXTENSION END + function getMutableArrayOrTupleType(type: Type) { return type.flags & TypeFlags.Union ? mapType(type, getMutableArrayOrTupleType) : type.flags & TypeFlags.Any || isMutableArrayOrTuple(getBaseConstraintOfType(type) || type) ? type : @@ -32180,7 +33535,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function getSignatureApplicabilityError( - node: CallLikeExpression, + node: CallLikeExpression | undefined, args: readonly Expression[], signature: Signature, relation: Map, @@ -32189,7 +33544,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { containingMessageChain: (() => DiagnosticMessageChain | undefined) | undefined, ): readonly Diagnostic[] | undefined { const errorOutputContainer: { errors?: Diagnostic[], skipLogging?: boolean } = { errors: undefined, skipLogging: true }; - if (isJsxOpeningLikeElement(node)) { + if (node && isJsxOpeningLikeElement(node)) { if (!checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, checkMode, reportErrors, containingMessageChain, errorOutputContainer)) { Debug.assert(!reportErrors || !!errorOutputContainer.errors, "jsx should have errors when reporting errors"); return errorOutputContainer.errors || emptyArray; @@ -32197,15 +33552,35 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return undefined; } const thisType = getThisTypeOfSignature(signature); - if (thisType && thisType !== voidType && !(isNewExpression(node) || isCallExpression(node) && isSuperProperty(node.expression))) { + if (thisType && thisType !== voidType && node && !(isNewExpression(node) || isCallExpression(node) && isSuperProperty(node.expression))) { // If the called expression is not of the form `x.f` or `x["f"]`, then sourceType = voidType // If the signature's 'this' type is voidType, then the check is skipped -- anything is compatible. // If the expression is a new expression or super call expression, then the check is skipped. const thisArgumentNode = getThisArgumentOfCall(node); const thisArgumentType = getThisArgumentType(thisArgumentNode); + // TSPLUS EXTENTION START + const originalParamType = thisType; + let paramType = originalParamType; + if (isLazyParameterByType(originalParamType) && thisArgumentNode) { + const contextFreeArgType = thisArgumentType; + if (isTypeIdenticalTo(contextFreeArgType, anyType) || isTypeIdenticalTo(contextFreeArgType, neverType)) { + return [createDiagnosticForNode( + thisArgumentNode, + Diagnostics.Values_of_type_any_or_never_are_not_allowed_in_lazy_function_arguments_if_the_behaviour_is_intended_use_an_arrow_function + )] + } + const args = getTypeArguments(originalParamType); + const genericLazy = cloneTypeReference(originalParamType); + genericLazy.resolvedTypeArguments = [anyType]; + if (!isTypeRelatedTo(contextFreeArgType, genericLazy, relation)) { + paramType = args[0]; + getNodeLinks(thisArgumentNode).tsPlusLazy = true; + } + } + // TSPLUS EXTENTION END const errorNode = reportErrors ? (thisArgumentNode || node) : undefined; const headMessage = Diagnostics.The_this_context_of_type_0_is_not_assignable_to_method_s_this_of_type_1; - if (!checkTypeRelatedTo(thisArgumentType, thisType, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer)) { + if (!checkTypeRelatedTo(thisArgumentType, paramType, relation, errorNode, headMessage, containingMessageChain, errorOutputContainer)) { Debug.assert(!reportErrors || !!errorOutputContainer.errors, "this parameter should have errors when reporting errors"); return errorOutputContainer.errors || emptyArray; } @@ -32216,8 +33591,26 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { for (let i = 0; i < argCount; i++) { const arg = args[i]; if (arg.kind !== SyntaxKind.OmittedExpression) { - const paramType = getTypeAtPosition(signature, i); - const argType = checkExpressionWithContextualType(arg, paramType, /*inferenceContext*/ undefined, checkMode); + // TSPLUS EXTENTION START + const originalParamType = getTypeAtPosition(signature, i); + const argType = checkExpressionWithContextualType(arg, unionIfLazy(originalParamType), /*inferenceContext*/ undefined, checkMode); + let paramType = originalParamType; + if (isLazyParameterByType(originalParamType)) { + if ((isTypeIdenticalTo(argType, anyType) || isTypeIdenticalTo(argType, neverType)) && !(checkMode & CheckMode.SkipGenericFunctions)) { + return [createDiagnosticForNode( + arg, + Diagnostics.Values_of_type_any_or_never_are_not_allowed_in_lazy_function_arguments_if_the_behaviour_is_intended_use_an_arrow_function + )] + } + const args = getTypeArguments(originalParamType); + const genericLazy = cloneTypeReference(originalParamType); + genericLazy.resolvedTypeArguments = [anyType]; + if (!isTypeRelatedTo(argType, genericLazy, relation)) { + paramType = args[0]; + getNodeLinks(arg).tsPlusLazy = true; + } + } + // TSPLUS EXTENTION END // If one or more arguments are still excluded (as indicated by CheckMode.SkipContextSensitive), // we obtain the regular type of any object literal arguments because we may not have inferred complete // parameter types yet and therefore excess property checks may yield false positives (see #17041). @@ -32229,7 +33622,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } } - if (restType) { + if (restType && node) { const spreadType = getSpreadArgumentType(args, argCount, args.length, restType, /*context*/ undefined, checkMode); const restArgCount = args.length - argCount; const errorNode = !reportErrors ? undefined : @@ -32283,6 +33676,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { * Returns the effective arguments for an expression that works like a function invocation. */ function getEffectiveCallArguments(node: CallLikeExpression): readonly Expression[] { + if (getNodeLinks(node).isFluentCall && isCallExpression(node)) { + return [node.expression, ...node.arguments]; + } if (node.kind === SyntaxKind.TaggedTemplateExpression) { const template = node.template; const args: Expression[] = [createSyntheticExpression(template, getGlobalTemplateStringsArrayType())]; @@ -32633,6 +34029,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const signatureHelpTrailingComma = !!(checkMode & CheckMode.IsForSignatureHelp) && node.kind === SyntaxKind.CallExpression && node.arguments.hasTrailingComma; + if (candidates.length === 1 && candidates[0].declaration) { + if (collectTsPlusMacroTags(candidates[0].declaration).findIndex((v) => v.startsWith("Do")) !== -1) { + getNodeLinks(node).isTsPlusDoCall = true; + } + } + // Section 4.12.1: // if the candidate list contains one or more signatures for which the type of each argument // expression is a subtype of each corresponding parameter type, the return type of the first @@ -32793,6 +34195,15 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { candidatesForArgumentError = [candidate]; return undefined; } + // TSPLUS EXTENTION BEGIN + for (let i = args.length; i < candidate.parameters.length; i++) { + const param = candidate.parameters[i] + if (param.valueDeclaration && (param.valueDeclaration as ParameterDeclaration).isAuto) { + const paramType = getTypeAtPosition(candidate, i); + deriveParameter(node, paramType, i); + } + } + // TSPLUS EXTENTION END return candidate; } @@ -32856,6 +34267,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { continue; } } + // TSPLUS EXTENTION BEGIN + // Only derive parameters once all type parameters are inferred + for (let i = args.length; i < candidate.parameters.length; i++) { + const param = candidate.parameters[i]; + if (param.valueDeclaration && (param.valueDeclaration as ParameterDeclaration).isAuto) { + let paramType: Type; + if (inferenceContext) { + paramType = getTypeAtPosition(getSignatureInstantiation(candidate, getInferredTypes(inferenceContext), /* isJavascript */false), i); + } else { + paramType = getTypeAtPosition(candidate, i); + } + deriveParameter(node, paramType, i); + } + } + // TSPLUS EXTENSION END candidates[candidateIndex] = checkCandidate; return checkCandidate; } @@ -33044,7 +34470,41 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // but we are not including call signatures that may have been added to the Object or // Function interface, since they have none by default. This is a bit of a leap of faith // that the user will not add any. - const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); + let callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call); + + // TSPLUS EXTENSION START + if (callSignatures.length === 0) { + let callExtension: TsPlusStaticFunctionExtension | undefined + if (isCompanionReference(node.expression)) { + callExtension = getStaticCompanionExtension(apparentType, "__call"); + } + if (!callExtension) { + callExtension = getStaticExtension(apparentType, "__call"); + } + if (callExtension) { + callSignatures = Array.from(getSignaturesOfType(getTypeOfSymbol(callExtension.patched), SignatureKind.Call)); + callCache.set(node.expression, callExtension); + getNodeLinks(node).tsPlusCallExtension = callExtension; + } + else { + const callFluentExtensions = getFluentExtension(apparentType, "__call"); + if (callFluentExtensions) { + callSignatures = Array.from(getSignaturesOfType(callFluentExtensions, SignatureKind.Call).map((s) => { + const sig = createTsPlusSignature( + (s as TsPlusSignature).tsPlusOriginal, + (s as TsPlusSignature).tsPlusExportName, + (s as TsPlusSignature).tsPlusFile + ); + sig.tsPlusDeclaration = (s as TsPlusSignature).tsPlusDeclaration; + sig.tsPlusPipeable = (s as TsPlusSignature).tsPlusPipeable; + return sig; + })); + getNodeLinks(node).isFluentCall = true; + } + } + } + // TSPLUS EXTENSION END + const numConstructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct).length; // TS 1.0 Spec: 4.12 @@ -33713,12 +35173,976 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + // TSPLUS EXTENSION START + function tryCacheOptimizedPipeableCall(node: CallExpression): void { + if (isIdentifier(node.expression)) { + const identifierType = getTypeOfNode(node.expression); + const identifierSymbol = identifierType.symbol; + if (identifierSymbol && isTsPlusSymbol(identifierSymbol)) { + if (identifierSymbol.tsPlusTag === TsPlusSymbolTag.PipeableIdentifier) { + const fluentExtension = checker.getFluentExtensionForPipeableSymbol(identifierSymbol); + if (fluentExtension) { + const signature = find(fluentExtension.types, ({ type }) => checker.isTypeAssignableTo(identifierSymbol.getTsPlusDataFirstType(), type))?.signatures[0]; + if (signature) { + getNodeLinks(node).tsPlusOptimizedDataFirst = { + definition: signature.tsPlusFile, + exportName: signature.tsPlusExportName + }; + } + } + } + if (identifierSymbol.tsPlusTag === TsPlusSymbolTag.PipeableMacro) { + getNodeLinks(node).tsPlusOptimizedDataFirst = { + definition: identifierSymbol.tsPlusSourceFile, + exportName: identifierSymbol.tsPlusExportName + }; + } + } + } + if (isPropertyAccessExpression(node.expression) && isIdentifier(node.expression.name)) { + const identifierType = checker.getTypeAtLocation(node.expression.name); + const identifierSymbol = identifierType.symbol; + if (identifierSymbol && isTsPlusSymbol(identifierSymbol)) { + if (identifierSymbol.tsPlusTag === TsPlusSymbolTag.PipeableIdentifier) { + const fluentExtension = checker.getFluentExtensionForPipeableSymbol(identifierSymbol); + if (fluentExtension) { + const signature = find(fluentExtension.types, ({ type }) => checker.isTypeAssignableTo(identifierSymbol.getTsPlusDataFirstType(), type))?.signatures[0]; + if (signature) { + getNodeLinks(node).tsPlusOptimizedDataFirst = { + definition: signature.tsPlusFile, + exportName: signature.tsPlusExportName + }; + } + } + } + if (identifierSymbol.tsPlusTag === TsPlusSymbolTag.PipeableMacro) { + getNodeLinks(node).tsPlusOptimizedDataFirst = { + definition: identifierSymbol.tsPlusSourceFile, + exportName: identifierSymbol.tsPlusExportName + }; + } + if (identifierSymbol.tsPlusTag === TsPlusSymbolTag.StaticFunction) { + const declType = checker.getTypeAtLocation(identifierSymbol.tsPlusDeclaration.name!); + const declSym = declType.symbol; + if (declSym && isTsPlusSymbol(declSym)) { + if (declSym.tsPlusTag === TsPlusSymbolTag.PipeableIdentifier) { + const fluentExtension = checker.getFluentExtensionForPipeableSymbol(declSym); + if (fluentExtension) { + const signature = find(fluentExtension.types, ({ type }) => checker.isTypeAssignableTo(declSym.getTsPlusDataFirstType(), type))?.signatures[0]; + if (signature) { + getNodeLinks(node).tsPlusOptimizedDataFirst = { + definition: signature.tsPlusFile, + exportName: signature.tsPlusExportName + }; + } + } + } + if (declSym.tsPlusTag === TsPlusSymbolTag.PipeableMacro) { + getNodeLinks(node).tsPlusOptimizedDataFirst = { + definition: declSym.tsPlusSourceFile, + exportName: declSym.tsPlusExportName + }; + } + } + } + } + } + } + function getDerivationDebugDiagnostic(location: CallExpression, derivation: Derivation): Diagnostic { + const sourceFile = getSourceFileOfNode(location) + switch(derivation._tag) { + case "EmptyObjectDerivation": { + return createError( + location.arguments[0], + Diagnostics.Deriving_type_0_1, + typeToString(derivation.type), + "intrinsically as it is an empty object" + ) + } + case "InvalidDerivation": { + throw new Error("Bug, derivation debug called with InvalidDerivation") + } + case "FromBlockScope": { + return createError( + location.arguments[0], + Diagnostics.Deriving_type_0_1, + typeToString(derivation.type), + `using block-scoped implicit ${derivation.implicit.symbol.escapedName}` + ); + } + case "FromImplicitScope": { + return createError( + location.arguments[0], + Diagnostics.Deriving_type_0_1, + typeToString(derivation.type), + `using implicit ${ + getSourceFileOfNode(location).fileName !== getSourceFileOfNode(derivation.implicit).fileName ? + `${getImportPath(derivation.implicit)}#${derivation.implicit.symbol.escapedName}` : + derivation.implicit.symbol.escapedName + }` + ) + } + case "FromIntersectionStructure": { + return createDiagnosticForNodeFromMessageChain( + sourceFile, + location.arguments[0], + chainDiagnosticMessages( + derivation.fields.map((d) => createDiagnosticMessageChainFromDiagnostic(getDerivationDebugDiagnostic(location, d))), + Diagnostics.Deriving_type_0_1, + typeToString(derivation.type), + "intrinsically as an intersection" + ) + ) + } + case "FromObjectStructure": { + return createDiagnosticForNodeFromMessageChain( + sourceFile, + location.arguments[0], + chainDiagnosticMessages( + derivation.fields.map((d) => createDiagnosticMessageChainFromDiagnostic(getDerivationDebugDiagnostic(location, d.value))), + Diagnostics.Deriving_type_0_1, + typeToString(derivation.type), + "intrinsically as an object structure" + ) + ) + } + case "FromTupleStructure": { + return createDiagnosticForNodeFromMessageChain( + sourceFile, + location.arguments[0], + chainDiagnosticMessages( + derivation.fields.map((d) => createDiagnosticMessageChainFromDiagnostic(getDerivationDebugDiagnostic(location, d))), + Diagnostics.Deriving_type_0_1, + typeToString(derivation.type), + "intrinsically as a tuple structure" + ) + ) + } + case "FromLiteral": { + return createError( + location.arguments[0], + Diagnostics.Deriving_type_0_1, + typeToString(derivation.type), + `intrinsically as a literal ${typeof derivation.value}` + ) + } + case "FromPriorDerivation": { + return createError( + location.arguments[0], + Diagnostics.Deriving_type_0_1, + typeToString(derivation.type), + `from derivation scope` + ) + } + case "FromRule": { + return createDiagnosticForNodeFromMessageChain( + sourceFile, + location.arguments[0], + chainDiagnosticMessages( + derivation.arguments.map((d) => createDiagnosticMessageChainFromDiagnostic(getDerivationDebugDiagnostic(location, d))), + Diagnostics.Deriving_type_0_1, + typeToString(derivation.type), + `using${(derivation.usedBy.length > 0 ? " (recursive)" : "")} rule ${ + getSourceFileOfNode(location).fileName !== getSourceFileOfNode(derivation.rule).fileName ? + `${getImportPath(derivation.rule)}#${derivation.rule.symbol.escapedName}` : + derivation.rule.symbol.escapedName + }` + ) + ) + } + default: { + // @ts-expect-error + derivation._tag + } + throw new Error("Bug, unreachable") + } + } + function getImportPath(declaration: Declaration) { + let path: string | undefined; + const locationTag = getAllJSDocTags(declaration, (tag): tag is JSDocTag => tag.tagName.escapedText === "tsplus" && tag.comment?.toString().startsWith("location") === true)[0]; + if (locationTag) { + const match = locationTag.comment!.toString().match(/^location "(.*)"/); + if (match) { + path = match[1] + } + } + if (!path) { + path = getImportLocation(fileMap.map, getSourceFileOfNode(declaration).fileName); + } + return path; + } + function deriveParameter(deriveCallNode: CallLikeExpression, type: Type, parameterIndex: number): Type { + const nodeLinks = getNodeLinks(deriveCallNode); + if (!nodeLinks.tsPlusParameterDerivations) { + nodeLinks.tsPlusParameterDerivations = new Map(); + } + if (!nodeLinks.tsPlusParameterDerivations!.has(parameterIndex)) { + const derivationDiagnostics: Diagnostic[] = []; + const derivation = deriveTypeWorker(deriveCallNode, type, type, derivationDiagnostics, [], [], []); + nodeLinks.tsPlusParameterDerivations!.set(parameterIndex, derivation); + if (isErrorType(derivation.type)) { + derivationDiagnostics.forEach((diagnostic) => { + diagnostics.add(diagnostic); + }) + return errorType; + } + } + return nodeLinks.tsPlusParameterDerivations.get(parameterIndex)!.type; + } + function deriveType(deriveCallNode: CallExpression, type: Type): Type { + const nodeLinks = getNodeLinks(deriveCallNode); + if (!nodeLinks.tsPlusDerivation) { + const derivationDiagnostics: Diagnostic[] = []; + const derivation = deriveTypeWorker(deriveCallNode, type, type, derivationDiagnostics, [], [], []); + nodeLinks.tsPlusDerivation = derivation; + if (isErrorType(derivation.type)) { + derivationDiagnostics.forEach((diagnostic) => { + diagnostics.add(diagnostic); + }) + return errorType; + } else if (deriveCallNode.arguments.length > 0) { + diagnostics.add(getDerivationDebugDiagnostic(deriveCallNode, nodeLinks.tsPlusDerivation)); + return errorType; + } + } + return nodeLinks.tsPlusDerivation.type; + } + function getSelfExportStatement(location: Node) { + let current = location; + while (!isVariableStatement(current) && current.parent) { + current = current.parent; + } + if (current.parent) { + return current; + } + } + function isLocalImplicit(node: Node): boolean { + return getAllJSDocTags(node, (tag): tag is JSDocTag => tag.tagName.escapedText === "tsplus" && typeof tag.comment === 'string' && tag.comment === 'implicit local').length > 0; + } + function tsPlusFlattenTags(toFlat: string[][]): readonly string[] { + let strings: readonly string[] = toFlat[0]; + for (let i = 1; i < toFlat.length; i++) { + strings = flatMap(strings, (prefix) => toFlat[i].map((post) => prefix + "," + post)) + } + return strings.map((s) => `[${s}]`); + } + function collectTypeTagsOfType(type: Type) { + return flatMap(collectRelevantSymbols(type), (s) => typeSymbolCache.get(s)); + } + function getTsPlusDerivationRules(symbol: Symbol, targetTypes: readonly Type[]) { + const mergedRules: { lazyRule: Declaration | undefined; rules: [Rule, number, Declaration, Set][]; } = { + lazyRule: void 0, + rules: [] + } + const paramExtensions = tsPlusFlattenTags(map(targetTypes, (t) => ["_", ...collectTypeTagsOfType(t)])); + const dedupe = new Set(); + forEach(map(symbol.declarations, collectTsPlusTypeTags), (tags) => { + forEach(tags, (tag) => { + collectForTag(tag); + paramExtensions.forEach((extended) => collectForTag(tag + extended)); + }); + }); + mergedRules.rules = mergedRules.rules.sort((a, b) => a[1] - b[1]); + return mergedRules; + function collectForTag(tag: string) { + const foundRules = tsPlusWorldScope.rules.get(tag); + if (foundRules) { + if (foundRules.lazyRule) { + mergedRules.lazyRule = foundRules.lazyRule; + } + foundRules.rules.forEach((rule) => { + if (!dedupe.has(rule[2])) { + dedupe.add(rule[2]); + mergedRules.rules.push(rule); + } + }); + } + } + } + + function shouldTreatNominally(type: Type) { + if (type.symbol && type.symbol.declarations) { + for (const declaration of type.symbol.declarations) { + if ((isInterfaceDeclaration(declaration) || isClassDeclaration(declaration)) && declaration.tsPlusDeriveTags && declaration.tsPlusDeriveTags.length > 0) { + return true; + } + } + } + return false; + } + + function getImplicitTags(type: Type): Set { + const tags = new Set(); + collectImplicitTagsWorker(type, tags, undefined); + return tags; + } + + function collectImplicitTagsWorker(type: Type, tags: Set, prefix: string | undefined) { + if (type.flags & TypeFlags.UnionOrIntersection) { + forEach((type as UnionOrIntersectionType).types, (d) => { + collectImplicitTagsWorker(d, tags, prefix); + }) + } + else if (shouldTreatNominally(type)) { + if ((getObjectFlags(type) & ObjectFlags.Reference)) { + const name = symbolName(type.symbol); + const args = getTypeArguments(type as TypeReference); + forEach(args, (arg) => { + collectImplicitTagsWorker(arg, tags, prefix ? `${prefix}.${name}` : name) + }) + } else { + tags.add(prefix ? `${prefix}.${symbolName(type.symbol)}` : symbolName(type.symbol)); + } + } + else if (type.flags & (TypeFlags.Intrinsic | TypeFlags.StringLiteral | TypeFlags.NumberLiteral | TypeFlags.TypeParameter)) { + tags.add(prefix ? `${prefix}.${typeToString(type)}` : typeToString(type)); + } + else { + const props = getPropertiesOfType(type); + forEach(props, (prop) => { + tags.add(prefix ? `${prefix}.${symbolName(prop)}` : symbolName(prop)); + }) + if (props.length === 0) { + tags.add(prefix ? `${prefix}._` : `_`); + } + } + } + + function getTypeAndImplicitTags(symbol: Symbol) { + const links = getSymbolLinks(symbol); + if (!links.tsPlusTypeAndImplicitTags) { + const type = getTypeOfSymbol(symbol); + const tags = getImplicitTags(type); + links.tsPlusTypeAndImplicitTags = { + type, + tags + } + } + return links.tsPlusTypeAndImplicitTags; + } + + function indexInScope(entry: Symbol) { + const { tags } = getTypeAndImplicitTags(entry); + forEach(arrayFrom(tags.values()), (tag) => { + let index = tsPlusWorldScope.implicits.get(tag); + if (!index) { + index = new Set(); + tsPlusWorldScope.implicits.set(tag, index); + } + index.add(entry); + }); + } + + function lookupInGlobalScope(location: Node, type: Type, selfExport: Node | undefined, tags: readonly string[]): Derivation | undefined { + let candidates: Set | undefined; + for (const tag of tags) { + const maybeBetter = tsPlusWorldScope.implicits.get(tag); + if (!maybeBetter) { + return + } + if (!candidates || maybeBetter.size < candidates.size) { + candidates = maybeBetter; + } + } + const candidatesIterator = candidates?.values(); + if (!candidatesIterator) { + return + } + let current = candidatesIterator.next(); + while (!current.done) { + const candidate = current.value; + if (tags.every((tag) => tsPlusWorldScope.implicits.get(tag)?.has(candidate) === true)) { + if (getSelfExportStatement(candidate.valueDeclaration!) !== selfExport && + isBlockScopedNameDeclaredBeforeUse(candidate.valueDeclaration!, location)) { + const { type: implicitType } = getTypeAndImplicitTags(candidate); + if (isTypeAssignableTo(implicitType, type)) { + return { + _tag: "FromImplicitScope", + type, + implicit: candidate.valueDeclaration! + }; + } + } + } + current = candidatesIterator.next(); + } + } + + function lookupInBlockScope(location: Node, type: Type, tags: readonly string[]): Derivation | undefined { + let container = getEnclosingBlockScopeContainer(location) as LocalsContainer; + while (container) { + if (container.locals) { + for (const local of arrayFrom(container.locals.values())) { + if (local.valueDeclaration && + (isParameterDeclaration(local.valueDeclaration as VariableLikeDeclaration) || isLocalImplicit(local.valueDeclaration)) && + isNamedDeclaration(local.valueDeclaration) && + isIdentifier(local.valueDeclaration.name) && + isBlockScopedNameDeclaredBeforeUse(local.valueDeclaration.name, location) + ) { + const { tags: implicitTags, type: implicitType } = getTypeAndImplicitTags(local); + if (tags.every((tag) => implicitTags.has(tag))) { + if (isTypeAssignableTo(implicitType, type)) { + local.valueDeclaration.symbol.isReferenced = SymbolFlags.Value; + const blockLinks = getNodeLinks(container); + if (!blockLinks.uniqueNames) { + blockLinks.uniqueNames = new Set() + } + const declaration = local.valueDeclaration as NamedDeclaration & { name: Identifier }; + blockLinks.uniqueNames.add(declaration); + const implicitLinks = getNodeLinks(local.valueDeclaration); + implicitLinks.needsUniqueNameInSope = true; + return { + _tag: "FromBlockScope", + type, + implicit: declaration + }; + } + } + } + } + } + container = getEnclosingBlockScopeContainer(container) as LocalsContainer; + } + } + + function deriveTypeWorker( + location: Node, + originalType: Type, + type: Type, + diagnostics: Diagnostic[], + derivationScope: FromRule[], + prohibited: Type[], + currentDerivation: Type[] + ): Derivation { + const sourceFile = getSourceFileOfNode(location) + if (isTypeIdenticalTo(type, emptyObjectType)) { + return { + _tag: "EmptyObjectDerivation", + type + }; + } + for (const derivedType of derivationScope) { + if (isTypeAssignableTo(derivedType.type, type)) { + const rule: FromPriorDerivation = { + _tag: "FromPriorDerivation", + derivation: derivedType, + type + }; + derivedType.usedBy.push(rule); + return rule; + } + } + const tagsForLookup = arrayFrom(getImplicitTags(type).values()); + const inBlockdScope = lookupInBlockScope(location, type, tagsForLookup); + if (inBlockdScope) { + return inBlockdScope; + } + const selfExport = getSelfExportStatement(location); + const inWorldScope = lookupInGlobalScope(location, type, selfExport, tagsForLookup); + if (inWorldScope) { + return inWorldScope; + } + const newCurrentDerivation = [...currentDerivation, type]; + for (const prohibitedType of prohibited) { + if (isTypeIdenticalTo(prohibitedType, type)) { + if (diagnostics.length === 0) { + const digs: Diagnostic[] = []; + for (let i = 1; i < newCurrentDerivation.length - 1; i++) { + const step = newCurrentDerivation[i]; + digs.push(createError( + location, + Diagnostics.Failed_derivation_of_type_0, + typeToString(step) + )); + } + const diagnostic = createDiagnosticForNodeFromMessageChain( + sourceFile, + location, + chainDiagnosticMessages( + map(digs, createDiagnosticMessageChainFromDiagnostic), + Diagnostics.Cannot_derive_type_0_the_derivation_requires_a_lazy_rule_to_be_in_scope_as_it_is_cyclic_for_the_type_1, + typeToString(newCurrentDerivation[0]), + typeToString(newCurrentDerivation[newCurrentDerivation.length - 1]) + ) + ) + diagnostics.push(diagnostic) + } + return { + _tag: "InvalidDerivation", + type: errorType + }; + } + } + let hasRules = false; + if ((type.flags & TypeFlags.Object) && ((type as ObjectType).objectFlags & ObjectFlags.Reference) && !isTupleType(type) && type.symbol && type.symbol.declarations) { + const targetTypes = getTypeArguments(type as TypeReference); + const mergedRules = getTsPlusDerivationRules(type.symbol, targetTypes); + ruleCheck: for (const [rule, _, ruleDeclaration, options] of mergedRules.rules) { + const toCheck: Type[] = []; + if (rule.paramActions.length !== targetTypes.length) { + continue ruleCheck; + } + let index = 0; + for (const paramAction of rule.paramActions) { + if (paramAction === "_") { + toCheck.push(targetTypes[index]) + } else if (paramAction === "[]") { + if (isTupleType(targetTypes[index])) { + toCheck.push(targetTypes[index]) + } else { + continue ruleCheck; + } + } else if (paramAction === "|") { + if (targetTypes[index].flags & TypeFlags.Union) { + const typesInUnion = (targetTypes[index] as UnionType).types; + const typesInUnionTuple = createTupleType(typesInUnion, void 0, false, void 0); + toCheck.push(typesInUnionTuple) + } else { + continue ruleCheck; + } + } else if (paramAction === "&") { + if (targetTypes[index].flags & TypeFlags.Intersection) { + const typesInUnion = (targetTypes[index] as IntersectionType).types; + const typesInUnionTuple = createTupleType(typesInUnion, void 0, false, void 0); + toCheck.push(typesInUnionTuple) + } else { + continue ruleCheck; + } + } else { + continue ruleCheck; + } + index++; + } + if (toCheck.length !== targetTypes.length) { + continue ruleCheck; + } + const signatures = getSignaturesOfType(getTypeOfNode(ruleDeclaration), SignatureKind.Call); + for (const signature of signatures) { + if (signature.typeParameters && signature.typeParameters.length === toCheck.length) { + const mapper = createTypeMapper(signature.typeParameters, toCheck); + if (every(signature.typeParameters, (param, i) => { + const constraint = instantiateType(getConstraintOfTypeParameter(param), mapper); + if (!constraint) { + return true; + } + return isTypeAssignableTo(toCheck[i], constraint); + })) { + const instantiated = getSignatureInstantiation(signature, toCheck, false); + if (instantiated.parameters.length === 1 && + instantiated.parameters[0].valueDeclaration && + (instantiated.parameters[0].valueDeclaration as ParameterDeclaration).dotDotDotToken + ) { + const residualType = getTypeOfSymbol(instantiated.parameters[0]); + if (isTupleType(residualType)) { + const types = getTypeArguments(residualType); + const selfRule: FromRule = { + _tag: "FromRule", + type, + rule: ruleDeclaration, + arguments: [], + usedBy: [], + lazyRule: mergedRules.lazyRule + }; + const supportsLazy = !!mergedRules.lazyRule && !options.has("no-recursion"); + const newProhibited = !supportsLazy ? [...prohibited, type] : prohibited; + const newDerivationScope: FromRule[] = supportsLazy ? [...derivationScope, selfRule] : derivationScope; + const derivations = map(types, (childType) => deriveTypeWorker( + location, + originalType, + childType, + diagnostics, + newDerivationScope, + newProhibited, + newCurrentDerivation + )); + if (!find(derivations, (d) => isErrorType(d.type))) { + selfRule.arguments.push(...derivations); + return selfRule; + } + } + } + } + } + } + if (diagnostics.length > 0) { + break ruleCheck; + } + } + } + if (diagnostics.length === 0 && !hasRules && isTupleType(type)) { + const props = getTypeArguments(type); + const derivations = map(props, (prop) => deriveTypeWorker( + location, + originalType, + prop, + diagnostics, + derivationScope, + prohibited, + newCurrentDerivation + )); + if (!find(derivations, (d) => isErrorType(d.type))) { + return { + _tag: "FromTupleStructure", + type, + fields: derivations + }; + } + } + if (diagnostics.length === 0 && !hasRules && (type.flags & TypeFlags.Intersection)) { + const props = (type as IntersectionType).types; + const canIntersect = !find(props, (p) => { + if ((p.flags & TypeFlags.Object) && getSignaturesOfType(p, SignatureKind.Call).length === 0 && getSignaturesOfType(p, SignatureKind.Construct).length === 0 && !isArrayOrTupleLikeType(p)) { + return false + } + return true + }) + if (canIntersect) { + const derivations = map(props, (prop) => deriveTypeWorker( + location, + originalType, + prop, + diagnostics, + derivationScope, + prohibited, + newCurrentDerivation + )); + if (!find(derivations, (d) => isErrorType(d.type))) { + return { + _tag: "FromIntersectionStructure", + type, + fields: derivations + }; + } + } + } + if (diagnostics.length === 0 && !hasRules && (type.flags & TypeFlags.Object)) { + const call = getSignaturesOfType(type, SignatureKind.Call); + const construct = getSignaturesOfType(type, SignatureKind.Construct); + if (call.length === 0 && construct.length === 0) { + const props = getPropertiesOfType(type); + if (!find(props, (p) => p.escapedName.toString().startsWith("__@"))) { + const fields: FromObjectStructure["fields"] = []; + for (const prop of props) { + fields.push({ + prop, + value: deriveTypeWorker( + location, + originalType, + getTypeOfSymbol(prop), + diagnostics, + derivationScope, + prohibited, + newCurrentDerivation + ) + }) + } + if (!find(fields, (d) => isErrorType(d.value.type))) { + return { + _tag: "FromObjectStructure", + type, + fields + }; + } + } + } + } + if (diagnostics.length === 0 && !hasRules && (type.flags & (TypeFlags.StringLiteral | TypeFlags.NumberLiteral))) { + return { + _tag: "FromLiteral", + type, + value: (type as StringLiteralType | NumberLiteralType).value + }; + } + if (diagnostics.length === 0) { + if (isTypeIdenticalTo(originalType, type)) { + diagnostics.push(createError(location, Diagnostics.Cannot_derive_type_0_and_no_derivation_rules_are_found, typeToString(originalType))); + } else { + const digs: Diagnostic[] = []; + for (let i = 1; i < newCurrentDerivation.length - 1; i++) { + const step = newCurrentDerivation[i]; + digs.push(createError( + location, + Diagnostics.Failed_derivation_of_type_0, + typeToString(step) + )); + } + const diagnostic = createDiagnosticForNodeFromMessageChain( + sourceFile, + location, + chainDiagnosticMessages( + map(digs, createDiagnosticMessageChainFromDiagnostic), + Diagnostics.Cannot_derive_type_0_you_may_want_to_try_add_an_implicit_for_1_in_scope, + typeToString(newCurrentDerivation[0]), + typeToString(newCurrentDerivation[newCurrentDerivation.length - 1]) + ) + ) + diagnostics.push(diagnostic) + } + } + return { + _tag: "InvalidDerivation", + type: errorType + }; + } + function tsPlusDoGetBindOutput(node: CallExpression, resultType: Type, statementNode: Statement) { + const mapFluent = getFluentExtension(resultType, "map"); + if (mapFluent) { + for (const signature of getSignaturesOfType(mapFluent, SignatureKind.Call)) { + const original = (signature as TsPlusSignature).tsPlusOriginal; + if (original.minArgumentCount === 2 && original.typeParameters) { + const context = createInferenceContext(original.typeParameters, original, InferenceFlags.None); + inferTypes(context.inferences, resultType, getTypeOfSymbol(original.parameters[0])); + const instantiated = getSignatureInstantiation(original, getInferredTypes(context), /* isJavascript */ false); + if (isTypeAssignableTo(resultType, getTypeOfSymbol(instantiated.parameters[0]))) { + const funcType = getTypeOfSymbol(instantiated.parameters[1]); + const statementLinks = getNodeLinks(statementNode); + statementLinks.tsPlusDoBindType = [node, resultType]; + return getTypeOfSymbol(getSignaturesOfType(funcType, SignatureKind.Call)[0].parameters[0]); + } + } + } + } + diagnostics.add(error(node, Diagnostics.Cannot_find_a_valid_flatMap_extension_for_type_0, typeToString(resultType))); + return errorType; + } + function tsPlusDoCheckBind(node: CallExpression, resultType: Type) { + const links = getNodeLinks(node); + if (!links.tsPlusResolvedType) { + links.tsPlusResolvedType = tsPlusDoCheckBindWorker(node, resultType); + } + return links.tsPlusResolvedType; + } + function tsPlusDoCheckBindWorker(node: CallExpression, resultType: Type) { + if ( + isVariableDeclaration(node.parent) && + isVariableDeclarationList(node.parent.parent) && + (node.parent.parent.flags & NodeFlags.Const) && + isVariableStatement(node.parent.parent.parent) && + isBlock(node.parent.parent.parent.parent) && + isArrowFunction(node.parent.parent.parent.parent.parent) && + isCallExpression(node.parent.parent.parent.parent.parent.parent) + ) { + const callExp = node.parent.parent.parent.parent.parent.parent as CallExpression; + const links = getNodeLinks(callExp); + if (links.isTsPlusDoCall) { + return tsPlusDoGetBindOutput(node, resultType, node.parent.parent.parent); + } + } + else if ( + isExpressionStatement(node.parent) && + isBlock(node.parent.parent) && + isArrowFunction(node.parent.parent.parent) && + isCallExpression(node.parent.parent.parent.parent) + ) { + const callExp = node.parent.parent.parent.parent as CallExpression; + const links = getNodeLinks(callExp); + if (links.isTsPlusDoCall) { + return tsPlusDoGetBindOutput(node, resultType, node.parent); + } + } + else if ( + isReturnStatement(node.parent) && + isBlock(node.parent.parent) && + isArrowFunction(node.parent.parent.parent) && + isCallExpression(node.parent.parent.parent.parent) + ) { + const callExp = node.parent.parent.parent.parent as CallExpression; + const links = getNodeLinks(callExp); + if (links.isTsPlusDoCall) { + links.isTsPlusDoReturnBound = true; + return tsPlusDoGetBindOutput(node, resultType, node.parent); + } + } + diagnostics.add(error(node, Diagnostics.A_call_to_bind_is_only_allowed_in_the_context_of_a_Do)); + return errorType; + } + function tsPlusDoCheckChain(types: readonly [CallExpression, Type][], signature: Signature): Type { + let current = types[types.length - 1][1]; + for (let i = types.length - 2; i >= 0; i--) { + const [node, type] = types[i]; + const context = createInferenceContext(signature.typeParameters!, signature, InferenceFlags.None); + inferTypes(context.inferences, type, getTypeOfSymbol(signature.parameters[0])); + const childType = createAnonymousType( + void 0, + emptySymbols, + [createSignature( + void 0, + [], + void 0, + [], + current, + void 0, + 0, + SignatureFlags.None + )], + [], + [] + ); + inferTypes(context.inferences, childType, getTypeOfSymbol(signature.parameters[1])); + const instantiated = getSignatureInstantiation(signature, getInferredTypes(context), /* isJavascript */ false); + if (!isTypeAssignableTo(type, getTypeOfSymbol(instantiated.parameters[0]))) { + error(node, Diagnostics.Type_0_is_not_assignable_to_type_1, typeToString(type), typeToString(getTypeOfSymbol(instantiated.parameters[0]))); + return errorType; + } + if (!isTypeAssignableTo(childType, getTypeOfSymbol(instantiated.parameters[1]))) { + error(node, Diagnostics.Type_0_is_not_assignable_to_type_1, typeToString(childType), typeToString(getTypeOfSymbol(instantiated.parameters[1]))); + return errorType; + } + current = getReturnTypeOfSignature(instantiated); + if (isErrorType(current)) { + return errorType; + } + } + return current; + } + function tsPlusDoCheckLastBind(node: CallExpression, mapFunction: Signature, lastBind: Type, result: Type) { + const context = createInferenceContext(mapFunction.typeParameters!, mapFunction, InferenceFlags.None); + const createdFunction = createAnonymousType( + void 0, + emptySymbols, + [createSignature( + void 0, + [], + void 0, + [], + result, + void 0, + 0, + SignatureFlags.None + )], + [], + [] + ); + inferTypes(context.inferences, lastBind, getTypeOfSymbol(mapFunction.parameters[0])); + inferTypes(context.inferences, createdFunction, getTypeOfSymbol(mapFunction.parameters[1])); + const instantiated = getSignatureInstantiation(mapFunction, getInferredTypes(context), /* isJavascript */ false); + if (!isTypeAssignableTo(lastBind, getTypeOfSymbol(instantiated.parameters[0]))) { + error(node, Diagnostics.Type_0_is_not_assignable_to_type_1, typeToString(lastBind), typeToString(getTypeOfSymbol(instantiated.parameters[0]))); + return errorType; + } + if (!isTypeAssignableTo(createdFunction, getTypeOfSymbol(instantiated.parameters[1]))) { + error(node, Diagnostics.Type_0_is_not_assignable_to_type_1, typeToString(createdFunction), typeToString(getTypeOfSymbol(instantiated.parameters[1]))); + return errorType; + } + return getReturnTypeOfSignature(instantiated); + } + function tsPlusDoChooseImplementation(types: [CallExpression, Type][], name: string) { + const seen = new Set(); + for (const [_, type] of types) { + const flatMap = getFluentExtension(type, name); + if (flatMap) { + for (const signature of getSignaturesOfType(flatMap, SignatureKind.Call)) { + const original = (signature as TsPlusSignature).tsPlusOriginal; + if (!seen.has(original)) { + seen.add(original); + if (original.minArgumentCount === 2 && original.typeParameters) { + const mapper = createTypeMapper(original.typeParameters, original.typeParameters.map(() => anyType)); + const p0 = instantiateType(getTypeOfSymbol(original.parameters[0]), mapper); + if (types.every((target) => isTypeAssignableTo(target[1], p0))) { + return signature as TsPlusSignature; + } + } + } + } + } + } + } + function tsPlusDoCheckDo(node: CallExpression, checked: Type) { + const links = getNodeLinks(node); + if (!links.tsPlusResolvedType) { + links.tsPlusResolvedType = tsPlusDoCheckDoWorker(node, checked); + } + return links.tsPlusResolvedType; + } + function tsPlusDoCheckDoWorker(node: CallExpression, checked: Type) { + if (isArrowFunction(node.arguments[0]) && isBlock(node.arguments[0].body)) { + const types: [CallExpression, Type][] = []; + forEach(node.arguments[0].body.statements, (statement) => { + checkSourceElement(statement); + const links = getNodeLinks(statement); + if (links.tsPlusDoBindType) { + types.push(links.tsPlusDoBindType); + } + }); + if (types.length === 0) { + error(node, Diagnostics.A_Do_block_must_contain_at_least_1_bound_value); + return errorType; + } + getNodeLinks(node).tsPlusDoBindTypes = types; + const mapFn = tsPlusDoChooseImplementation(types, "map"); + if (!mapFn) { + error(node, Diagnostics.Cannot_find_a_valid_0_that_can_handle_all_the_types_of_1, "map", typeToString(getUnionType(types.map((t) => t[1])))) + return errorType; + } + const flatMapFn = tsPlusDoChooseImplementation(types, "flatMap"); + if (!flatMapFn) { + error(node, Diagnostics.Cannot_find_a_valid_0_that_can_handle_all_the_types_of_1, "flatMap", typeToString(getUnionType(types.map((t) => t[1])))) + return errorType; + } + getNodeLinks(node).tsPlusDoFunctions = { + flatMap: flatMapFn, + map: mapFn + }; + const lastBind = types[types.length - 1]; + const links = getNodeLinks(node); + const mapped = links.isTsPlusDoReturnBound ? lastBind[1] : tsPlusDoCheckLastBind(lastBind[0], mapFn.tsPlusOriginal, lastBind[1], checked) + const toBeFlatMapped = types.map((t, i) => i !== types.length - 1 ? t : [t[0], mapped] as typeof t); + return tsPlusDoCheckChain(toBeFlatMapped, flatMapFn.tsPlusOriginal); + } + error(node, Diagnostics.The_first_argument_of_a_Do_must_be_an_arrow_function); + return errorType; + } + function checkCallExpression(node: CallExpression | NewExpression, checkMode?: CheckMode): Type { + let checked = checkCallExpressionOriginal(node, checkMode); + if (isTsPlusMacroCall(node, "Bind") && !isErrorType(checked)) { + return tsPlusDoCheckBind(node, checked); + } + if (isTsPlusMacroCall(node, "Do") && !isErrorType(checked)) { + return tsPlusDoCheckDo(node, checked); + } + if (isTsPlusMacroCall(node, "Derive") && !isErrorType(checked)) { + return deriveType(node, checked); + } + if (isTsPlusMacroCall(node, "pipeable") && !isErrorType(checked)) { + if (!isVariableDeclaration(node.parent)) { + error(node, Diagnostics.Pipeable_macro_is_only_allowed_in_variable_declarations); + return checked; + } + if (node.arguments.length > 0) { + const dataFirstSymbol = getTypeOfNode(node.arguments[0]).symbol; + if (dataFirstSymbol && dataFirstSymbol.declarations) { + const dataFirstDeclaration = find(dataFirstSymbol.declarations, (decl): decl is FunctionDeclaration | ArrowFunction | FunctionExpression => isFunctionDeclaration(decl) || isArrowFunction(decl) || isFunctionExpression(decl)) + if (dataFirstDeclaration) { + const type = generatePipeable(node.parent, dataFirstDeclaration); + if (type) { + getNodeLinks(node.parent).tsPlusDataFirstDeclaration = dataFirstDeclaration; + return type; + } + } + } + } + } + if (isTsPlusMacroCall(node, "identity") && !isErrorType(checked)) { + if (node.arguments.length === 1) { + if (isSpreadArgument(node.arguments[0])) { + error(node.arguments[0], Diagnostics.The_argument_of_an_identity_macro_must_not_be_a_spread_element); + return checked; + } + } + } + if (isCallExpression(node)) { + tryCacheOptimizedPipeableCall(node); + } + return checked; + } + // TSPLUS EXTENSION END + /** * Syntactically and semantically checks a call or new expression. * @param node The call/new expression to be checked. * @returns On success, the expression's signature's return type. On failure, anyType. */ - function checkCallExpression(node: CallExpression | NewExpression, checkMode?: CheckMode): Type { + function checkCallExpressionOriginal(node: CallExpression | NewExpression, checkMode?: CheckMode): Type { checkGrammarTypeArguments(node, node.typeArguments); const signature = getResolvedSignature(node, /*candidatesOutArray*/ undefined, checkMode); @@ -34347,6 +36771,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } minArgumentCount = signature.minArgumentCount; } + for (let i = 0; i < signature.parameters.length; i++) { + if (signature.parameters[i].valueDeclaration && (signature.parameters[i].valueDeclaration as ParameterDeclaration).isAuto) { + minArgumentCount = signature.parameters.length - (signature.parameters.length - i); + break; + } + } if (voidIsNonOptional) { return minArgumentCount; } @@ -36248,6 +38678,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { errorNode?: Node ): Type { const operator = operatorToken.kind; + + const links = getNodeLinks(operatorToken); + + if (links.isTsPlusOperatorToken) { + return links.resolvedSignature ? getReturnTypeOfSignature(links.resolvedSignature) : errorType; + } + switch (operator) { case SyntaxKind.AsteriskToken: case SyntaxKind.AsteriskAsteriskToken: @@ -37245,7 +39682,181 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return checkExpression(node.expression, checkMode); } + function getOperatorExtensionsForSymbols(name: string, symbols: readonly Symbol[]): Signature[] { + const signatures = new Set(); + for (const target of symbols) { + if (typeSymbolCache.has(target)) { + for (const tag of typeSymbolCache.get(target)!) { + if (operatorCache.has(tag)) { + const cache = operatorCache.get(tag) + const extensions = cache?.get(name); + if (extensions) { + for (const extension of extensions) { + for (const signature of getSignaturesOfType(getTypeOfSymbol(extension.patched), SignatureKind.Call)) { + signatures.add(signature) + } + } + } + } + } + } + } + return arrayFrom(signatures.values()); + } + function getOperatorExtensionsForTypes(name: string, types: Type[]): Signature[] { + const symbols = new Set(); + for (const type of types) { + collectRelevantSymbols(type).forEach((symbol) => { + symbols.add(symbol) + }) + } + return getOperatorExtensionsForSymbols(name, arrayFrom(symbols.values())); + } + + + function checkTsPlusOperator(left: Expression, right: Expression): readonly [Type, Type] | undefined { + // potentially captured by resolution + let lastSignature: Signature | undefined; + const resolutionDiagnostics = new Map readonly Diagnostic[]>(); + // not captured + const parent = right.parent as BinaryExpression; + const links = getNodeLinks(parent.operatorToken); + const alreadyResolved = links.resolvedSignature; + if (alreadyResolved && alreadyResolved === resolvingSignature) { + return; // in resolution + } + if (alreadyResolved) { + return [getTypeAtPosition(alreadyResolved, 0), getTypeAtPosition(alreadyResolved, 1)]; + } + if (links.isTsPlusOperatorToken === false) { + return; // already checked and not a custom operator + } + links.resolvedSignature = resolvingSignature; + const operator = invertedBinaryOp[parent.operatorToken.kind as keyof typeof invertedBinaryOp] as string | undefined; + if (operator) { + let resolved: Signature | undefined; + const leftType = getTypeOfNode(parent.left); + const leftOperators = getOperatorExtensionsForTypes(operator, [leftType]); + resolved = resolveTsPlusOperator(left, right, leftOperators); + if (!resolved) { + const rightType = checkExpression(right, CheckMode.Normal); + const rightOperators = getOperatorExtensionsForTypes(operator, [rightType]); + resolved = resolveTsPlusOperator(left, right, rightOperators); + } + if (resolved) { + links.resolvedSignature = resolved; + links.isTsPlusOperatorToken = true; + return [getTypeAtPosition(resolved, 0), getTypeAtPosition(resolved, 1)] as const; + } + else { + if (lastSignature) { + links.resolvedSignature = lastSignature; + links.isTsPlusOperatorToken = true; + const diagnosticsForSignature = resolutionDiagnostics.get(lastSignature); + if (diagnosticsForSignature) { + diagnosticsForSignature().forEach((diagnostic) => diagnostics.add(diagnostic)); + links.resolvedSignature = undefined; + } + return [errorType, errorType] as const; + } + } + } + links.isTsPlusOperatorToken = false; + return; + function resolveTsPlusOperator(left: Expression, right: Expression, signatures: Signature[]) { + for (let signature of signatures) { + if (resolutionDiagnostics.has(signature)) { + continue; + } + lastSignature = signature; + if (signature.typeParameters) { + const context = createInferenceContext(signature.typeParameters, signature, InferenceFlags.None) + const leftParam = unionIfLazy(getTypeAtPosition(signature, 0)); + const leftType = checkExpressionWithContextualType(left, leftParam, context, CheckMode.Normal); + inferTypes(context.inferences, leftType, leftParam); + const rightParam = unionIfLazy(getTypeAtPosition(signature, 1)); + const rightType = checkExpressionWithContextualType(right, rightParam, context, CheckMode.Normal); + inferTypes(context.inferences, rightType, rightParam); + const instantiated = getSignatureInstantiation(signature, getInferredTypes(context), /** isJavascript */ false); + const diagnostics = getSignatureApplicabilityError( + undefined, + [left, right], + instantiated, + assignableRelation, + CheckMode.Normal, + false, + undefined + ); + if (diagnostics) { + const leftParam = getTypeAtPosition(instantiated, 0); + const rightParam = getTypeAtPosition(instantiated, 1); + resolutionDiagnostics.set(signature, () => { + const diags: Diagnostic[] = []; + if (!isTypeAssignableTo(rightType, rightParam)) { + diags.push(error(right, Diagnostics.Type_0_is_not_assignable_to_type_1, typeToString(rightType), typeToString(rightParam))); + } + if (!isTypeAssignableTo(leftType, leftParam)) { + diags.push(error(right, Diagnostics.Type_0_is_not_assignable_to_type_1, typeToString(rightType), typeToString(rightParam))); + } + if (diags.length === 0) { + Debug.fail("Cannot find diagnostics"); + } + return diags; + }); + } else { + return instantiated; + } + } + else { + const diagnostics = getSignatureApplicabilityError( + undefined, + [left, right], + signature, + assignableRelation, + CheckMode.Normal, + false, + undefined + ); + if (diagnostics) { + resolutionDiagnostics.set(signature, () => { + const diags: Diagnostic[] = []; + const leftParam = getTypeAtPosition(signature, 0); + const rightParam = getTypeAtPosition(signature, 1); + const leftType = getTypeOfNode(left); + const rightType = getTypeOfNode(right); + if (!isTypeAssignableTo(rightType, rightParam)) { + diags.push(error(right, Diagnostics.Type_0_is_not_assignable_to_type_1, typeToString(rightType), typeToString(rightParam))); + } + if (!isTypeAssignableTo(leftType, leftParam)) { + diags.push(error(right, Diagnostics.Type_0_is_not_assignable_to_type_1, typeToString(rightType), typeToString(rightParam))); + } + if (diags.length === 0) { + Debug.fail("Cannot find diagnostics"); + } + return diags; + }); + } else { + return signature; + } + } + } + } + } + function checkExpressionWorker(node: Expression | QualifiedName, checkMode: CheckMode | undefined, forceTuple?: boolean): Type { + if (node.kind !== SyntaxKind.QualifiedName && node.parent && node.parent.kind === SyntaxKind.BinaryExpression) { + const result = checkTsPlusOperator((node.parent as BinaryExpression).left, (node.parent as BinaryExpression).right); + if (result) { + return node === (node.parent as BinaryExpression).right ? result[1] : result[0]; + } + } + if (node.kind === SyntaxKind.BinaryExpression) { + checkTsPlusOperator((node as BinaryExpression).left, (node as BinaryExpression).right); + const links = getNodeLinks((node as BinaryExpression).operatorToken); + if (links.isTsPlusOperatorToken) { + return links.resolvedSignature ? getReturnTypeOfSignature(links.resolvedSignature) : errorType; + } + } const kind = node.kind; if (cancellationToken) { // Only bother checking on a few construct kinds. We don't want to be excessively @@ -39503,13 +42114,132 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } + function checkTailRecFunction(node: FunctionDeclaration): void { + if (node.body && node.name) { + const funcNameSymbol = getSymbolAtLocation(node.name) + if (funcNameSymbol) { + checkTailRecFunctionParameters(node.parameters); + const paramSymbols = flatMap(node.parameters, (param) => getSymbolsOfBindingName(param.name)); + forEach(node.body.statements, (s) => { + visitNode(s, checkTailRecFunctionVisitor(funcNameSymbol, paramSymbols)); + }); + } + } + else { + error(node, Diagnostics.Invalid_declaration_annotated_as_tailRec); + } + } + function checkTailRecVariableDeclaration(node: VariableDeclaration): void { + if (isIdentifier(node.name) && node.initializer && isArrowFunction(node.initializer)) { + const funcNameSymbol = getSymbolAtLocation(node.name) + if (funcNameSymbol) { + checkTailRecFunctionParameters(node.initializer.parameters); + const paramSymbols = flatMap(node.initializer.parameters, (param) => getSymbolsOfBindingName(param.name)); + if (isBlock(node.initializer.body)) { + forEach(node.initializer.body.statements, (s) => { + visitNode(s, checkTailRecFunctionVisitor(funcNameSymbol, paramSymbols)); + }); + } + else { + checkTailRecReturnStatement(funcNameSymbol, paramSymbols)(node.initializer.body); + } + } + } + else { + error(node, Diagnostics.Invalid_declaration_annotated_as_tailRec); + } + } + function checkTailRecFunctionParameters(params: NodeArray): void { + forEach(params, (param) => { + if (!isIdentifier(param.name)) { + error(param, Diagnostics.Parameter_destructuring_is_not_allowed_in_a_tailRec_annotated_function); + } + }); + } + function checkTailRecFunctionVisitor(funcNameSymbol: Symbol, parameterSymbols: readonly Symbol[]) { + return function (node: Node): VisitResult { + if (isReturnStatement(node) && node.expression) { + return visitEachChild( + node, + checkTailRecReturnStatement(funcNameSymbol, parameterSymbols), + nullTransformationContext + ); + } + else if (isFunctionDeclaration(node) || isArrowFunction(node)) { + return visitEachChild( + node, + checkTailRecNestedFunction(funcNameSymbol), + nullTransformationContext + ); + } + else if (isCallExpression(node)) { + const symbol = getSymbolAtLocation(node.expression); + if (symbol === funcNameSymbol) { + error(node, Diagnostics.A_recursive_call_must_be_in_a_tail_position_of_a_tailRec_annotated_function); + return node; + } + } + return visitEachChild( + node, + checkTailRecFunctionVisitor(funcNameSymbol, parameterSymbols), + nullTransformationContext + ); + }; + } + function getSymbolsOfBindingName(node: BindingName): readonly Symbol[] { + if (isIdentifier(node)) { + const symbol = getSymbolAtLocation(node); + return symbol ? [symbol] : []; + } + else if (isObjectBindingPattern(node)) { + return flatMap(node.elements, (param) => getSymbolsOfBindingName(param.name)); + } + else { + return flatMap(node.elements, (param) => isBindingElement(param) ? getSymbolsOfBindingName(param.name) : undefined); + } + } + + function checkTailRecNestedFunction(funcNameSymbol: Symbol) { + return function (node: Node): VisitResult { + if (isCallExpression(node) && isIdentifier(node.expression)) { + const callNameSymbol = getSymbolAtLocation(node.expression); + if (callNameSymbol === funcNameSymbol) { + error(node, Diagnostics.A_recursive_call_cannot_cross_a_function_boundary_in_a_tailRec_annotated_function); + return node; + } + } + return visitEachChild(node, checkTailRecNestedFunction(funcNameSymbol), nullTransformationContext); + }; + } + + // TODO(peter): Right now the heuristic for detecting whether a recursive call is in the tail position + // is overly simple. It does not, for example, allow for recursive calls in ternery expressions. + function checkTailRecReturnStatement(funcNameSymbol: Symbol, parameterSymbols: readonly Symbol[], firstIter = true) { + return function (node: Node): VisitResult { + if (isCallExpression(node) && !firstIter) { + const symbol = getSymbolAtLocation(node.expression); + if (symbol === funcNameSymbol) { + error(node, Diagnostics.A_recursive_call_must_be_in_a_tail_position_of_a_tailRec_annotated_function); + } + } + return visitEachChild(node, checkTailRecReturnStatement(funcNameSymbol, parameterSymbols, false), nullTransformationContext); + }; + } + function checkFunctionDeclaration(node: FunctionDeclaration): void { addLazyDiagnostic(checkFunctionDeclarationDiagnostics); function checkFunctionDeclarationDiagnostics() { + if (isTailRec(node)) { + checkTailRecFunction(node); + } checkFunctionOrMethodDeclaration(node); checkGrammarForGenerator(node); checkCollisionsForDeclarationName(node, node.name); + const links = getNodeLinks(node); + if (links.tsPlusPipeableExtension) { + checkFluentPipeableAgreement(links.tsPlusPipeableExtension); + } } } @@ -39907,7 +42637,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } if (isImportedDeclaration(declaration)) { - addToGroup(unusedImports, importClauseFromImported(declaration), declaration, getNodeId); + const importClause = importClauseFromImported(declaration); + if (!importClause.parent.isTsPlusGlobal) { + addToGroup(unusedImports, importClause, declaration, getNodeId); + } } else if (isBindingElement(declaration) && isObjectBindingPattern(declaration.parent)) { // In `{ a, ...b }, `a` is considered used since it removes a property from `b`. `b` may still be unused though. @@ -40522,9 +43255,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { function checkVariableDeclaration(node: VariableDeclaration) { tracing?.push(tracing.Phase.Check, "checkVariableDeclaration", { kind: node.kind, pos: node.pos, end: node.end, path: (node as TracingNode).tracingPath }); + if (isTailRec(node)) { + checkTailRecVariableDeclaration(node); + } checkGrammarVariableDeclaration(node); checkVariableLikeDeclaration(node); tracing?.pop(); + addLazyDiagnostic(checkDeferred) + function checkDeferred() { + const links = getNodeLinks(node); + if (links.tsPlusPipeableExtension) { + checkFluentPipeableAgreement(links.tsPlusPipeableExtension); + } + } } function checkBindingElement(node: BindingElement) { @@ -43683,6 +46426,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } checkAssertClause(node); + if (node.isTsPlusGlobal) { + const location = (node.moduleSpecifier as StringLiteral).text; + if (pathIsRelative(location)) { + error(node.moduleSpecifier, Diagnostics.Global_import_path_cannot_be_relative); + } + } } function checkImportEqualsDeclaration(node: ImportEqualsDeclaration) { @@ -44662,6 +47411,21 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { symbols.set(id, symbol); } } + if (companionSymbolCache.has(symbol)) { + const id = symbol.escapedName; + if (!symbols.has(id)) { + symbols.set(id, symbol); + } + } + if (symbol.declarations && symbol.declarations[0] && isImportSpecifier(symbol.declarations[0])) { + const originalSymbol = getTargetOfImportSpecifier(symbol.declarations[0], false); + if (originalSymbol && companionSymbolCache.has(originalSymbol)) { + const id = symbol.escapedName; + if (!symbols.has(id)) { + symbols.set(id, symbol); + } + } + } } function copySymbols(source: SymbolTable, meaning: SymbolFlags): void { @@ -46178,10 +48942,1603 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getDeclarationOfKind(moduleSymbol, SyntaxKind.SourceFile); } + // TSPLUS EXTENSION START + function isTsPlusImplicit(declaration: Declaration): declaration is VariableDeclaration { + if (isVariableDeclaration(declaration)) { + return declaration.isTsPlusImplicit; + } + return false; + } + function collectTsPlusDeriveTags(declaration: Declaration) { + if (isVariableDeclaration(declaration) || isFunctionDeclaration(declaration)) { + return declaration.tsPlusDeriveTags || []; + } + return []; + } + function collectTsPlusFluentTags(declaration: Declaration) { + if (isVariableDeclaration(declaration) || isFunctionDeclaration(declaration)) { + return declaration.tsPlusFluentTags || []; + } + return []; + } + function collectTsPlusPipeableTags(declaration: Declaration) { + if (isVariableDeclaration(declaration) || isFunctionDeclaration(declaration)) { + return declaration.tsPlusPipeableTags || []; + } + return []; + } + function collectTsPlusIndexTags(declaration: Declaration) { + if (isVariableDeclaration(declaration) || isFunctionDeclaration(declaration)) { + return declaration.tsPlusIndexTags || []; + } + return []; + } + function collectTsPlusPipeableIndexTags(declaration: Declaration) { + if (isVariableDeclaration(declaration) || isFunctionDeclaration(declaration)) { + return declaration.tsPlusPipeableIndexTags || []; + } + return []; + } + function collectTsPlusTypeTags(declaration: Declaration) { + if (isInterfaceDeclaration(declaration) || isTypeAliasDeclaration(declaration) || isClassDeclaration(declaration)) { + return declaration.tsPlusTypeTags || []; + } + return []; + } + function collectTsPlusCompanionTags(declaration: Declaration) { + if (isClassDeclaration(declaration) || isInterfaceDeclaration(declaration) || isTypeAliasDeclaration(declaration)) { + return declaration.tsPlusCompanionTags || []; + } + return []; + } + function collectTsPlusMacroTags(declaration: Declaration) { + if (isVariableDeclaration(declaration) || isFunctionDeclaration(declaration) || isCallSignatureDeclaration(declaration)) { + return declaration.tsPlusMacroTags || []; + } + return []; + } + function collectTsPlusUnifyTags(declaration: Declaration) { + if (isVariableDeclaration(declaration) || isFunctionDeclaration(declaration)) { + return declaration.tsPlusUnifyTags || []; + } + return []; + } + function collectTsPlusStaticTags(declaration: Node) { + if (isVariableDeclaration(declaration) || isFunctionDeclaration(declaration) || isClassDeclaration(declaration)) { + return declaration.tsPlusStaticTags || []; + } + return []; + } + function collectTsPlusOperatorTags(declaration: Node) { + if (isVariableDeclaration(declaration) || isFunctionDeclaration(declaration)) { + return declaration.tsPlusOperatorTags || []; + } + return []; + } + function collectTsPlusPipeableOperatorTags(declaration: Node) { + if (isVariableDeclaration(declaration) || isFunctionDeclaration(declaration)) { + return declaration.tsPlusPipeableOperatorTags || []; + } + return []; + } + function collectTsPlusGetterTags(declaration: Node) { + if (isVariableDeclaration(declaration) || isFunctionDeclaration(declaration)) { + return declaration.tsPlusGetterTags || []; + } + return []; + } + function createTsPlusSignature(call: Signature, exportName: string, file: SourceFile): TsPlusSignature { + const signature = cloneSignature(call) as TsPlusSignature + signature.tsPlusTag = "TsPlusSignature"; + signature.tsPlusFile = file; + signature.tsPlusExportName = exportName; + signature.tsPlusOriginal = call; + return signature + } + function thisifyTsPlusSignature(declaration: FunctionDeclaration | VariableDeclaration, call: Signature, exportName: string, file: SourceFile, reportDiagnostic: (diagnostic: DiagnosticMessage) => void) { + const signature = createTsPlusSignature(call, exportName, file); + if (isSelfARestParameter(signature)) { + reportDiagnostic(Diagnostics.The_first_parameter_of_a_fluent_function_cannot_be_a_rest_parameter); + return undefined; + } + const target = signature.parameters[0]; + signature.thisParameter = createSymbolWithType(createSymbol(target.flags, "this" as __String), getTypeOfSymbol(target)); + const typePredicate = getTypePredicateOfSignature(call); + if (typePredicate && typePredicate.parameterIndex === 0) { + signature.resolvedTypePredicate = createTypePredicate( + typePredicate.kind === TypePredicateKind.Identifier ? TypePredicateKind.This : TypePredicateKind.AssertsThis, + "this", + 0, + typePredicate.type + ); + } + signature.parameters = signature.parameters.slice(1, signature.parameters.length); + signature.minArgumentCount = signature.minArgumentCount - 1; + signature.tsPlusDeclaration = declaration; + signature.resolvedReturnType = call.resolvedReturnType; + return signature; + } + function createTsPlusFluentSymbol(name: string, signatures: TsPlusSignature[]): TsPlusFluentSymbol { + const symbol = createSymbol(SymbolFlags.Function, name as __String) as TsPlusFluentSymbol; + symbol.tsPlusTag = TsPlusSymbolTag.Fluent; + symbol.tsPlusResolvedSignatures = signatures; + symbol.tsPlusName = name; + return symbol; + } + function createTsPlusFluentSymbolWithType(name: string, allSignatures: TsPlusSignature[]): TransientSymbol { + const symbol = createTsPlusFluentSymbol(name, allSignatures); + const type = createAnonymousType(symbol, emptySymbols, allSignatures, [], []); + return createSymbolWithType(symbol, type); + } + function createTsPlusGetterVariableSymbol(name: string, dataFirst: VariableDeclaration & { name: Identifier }, returnType: Type, selfType: Type) { + const symbol = createSymbol(SymbolFlags.Property, name as __String) as TsPlusGetterVariableSymbol; + getSymbolLinks(symbol).type = returnType; + symbol.tsPlusTag = TsPlusSymbolTag.GetterVariable; + symbol.tsPlusDeclaration = dataFirst; + symbol.tsPlusSelfType = selfType; + symbol.tsPlusName = name; + return symbol; + } + function createTsPlusStaticFunctionSymbol(name: string, declaration: FunctionDeclaration | VariableDeclaration, signatures: Signature[]): TsPlusStaticFunctionSymbol { + const symbol = createSymbol(SymbolFlags.Function, name as __String) as TsPlusStaticFunctionSymbol; + symbol.tsPlusTag = TsPlusSymbolTag.StaticFunction; + symbol.tsPlusDeclaration = declaration; + symbol.tsPlusResolvedSignatures = signatures; + symbol.tsPlusName = name; + return symbol; + } + function createTsPlusPipeableDeclarationSymbol(name: string, declaration: FunctionDeclaration | VariableDeclarationWithFunction | VariableDeclarationWithFunctionType) { + const symbol = createSymbol(SymbolFlags.Function, name as __String) as TsPlusPipeableDeclarationSymbol; + symbol.tsPlusTag = TsPlusSymbolTag.PipeableDeclaration; + symbol.tsPlusDeclaration = declaration; + return symbol; + } + function createTsPlusStaticValueSymbol(name: string, declaration: VariableDeclaration | ClassDeclaration, originalSymbol: Symbol): Symbol { + const symbol = cloneSymbol(originalSymbol) as TsPlusStaticValueSymbol; + symbol.tsPlusTag = TsPlusSymbolTag.StaticValue; + symbol.tsPlusResolvedSignatures = []; + symbol.tsPlusName = name; + symbol.tsPlusDeclaration = declaration as (VariableDeclaration | ClassDeclaration) & { name: Identifier }; + symbol.escapedName = name as __String; + let patchedDeclaration: VariableDeclaration | ClassDeclaration + if (isVariableDeclaration(declaration)) { + patchedDeclaration = factory.updateVariableDeclaration( + declaration, + factory.createIdentifier(name), + declaration.exclamationToken, + declaration.type, + declaration.initializer + ); + } + else { + patchedDeclaration = factory.updateClassDeclaration( + declaration, + declaration.modifiers, + factory.createIdentifier(name), + declaration.typeParameters, + declaration.heritageClauses, + declaration.members + ) + } + setParent(patchedDeclaration, declaration.parent); + setParent(patchedDeclaration.name!, patchedDeclaration) + patchedDeclaration.name!.tsPlusName = name; + symbol.declarations = [patchedDeclaration]; + patchedDeclaration.jsDoc = getJSDocCommentsAndTags(declaration) as JSDoc[]; + + const originalSymbolLinks = getSymbolLinks(originalSymbol); + const symbolLinks = getSymbolLinks(symbol); + symbolLinks.uniqueESSymbolType = originalSymbolLinks.uniqueESSymbolType; + symbolLinks.declaredType = originalSymbolLinks.declaredType; + + return symbol; + } + function createTsPlusGetterFunctionSymbol(name: string, dataFirst: FunctionDeclaration, returnType: Type, selfType: Type) { + const symbol = createSymbol(SymbolFlags.Property, name as __String) as TsPlusGetterSymbol; + getSymbolLinks(symbol).type = returnType; + symbol.tsPlusTag = TsPlusSymbolTag.Getter; + symbol.tsPlusDeclaration = dataFirst; + symbol.tsPlusSelfType = selfType; + symbol.tsPlusName = name; + return symbol; + } + function getTsPlusFluentSignaturesForFunctionDeclaration(file: SourceFile, exportName: string, dataFirst: FunctionDeclaration) { + function reportDiagnostic(message: DiagnosticMessage) { + error(dataFirst, message); + } + const type = getTypeOfNode(dataFirst); + const signatures = getSignaturesOfType(type, SignatureKind.Call); + const thisifiedSignatures = signatures.map((signature) => thisifyTsPlusSignature(dataFirst, signature, exportName, file, reportDiagnostic)) + if (!isEachDefined(thisifiedSignatures)) { + return; + } + const thisifiedType = createAnonymousType(undefined, emptySymbols, thisifiedSignatures as TsPlusSignature[], [], []) + return [thisifiedType, thisifiedSignatures] as const; + } + function getTsPlusFluentSignaturesForVariableDeclaration(file: SourceFile, exportName: string, declaration: VariableDeclaration & { name: Identifier }) { + function reportDiagnostic(message: DiagnosticMessage) { + error(declaration, message); + } + const type = getTypeOfNode(declaration); + const signatures = getSignaturesOfType(type, SignatureKind.Call); + if(signatures.every((sigSymbol) => sigSymbol.parameters.every((paramSymbol) => paramSymbol.valueDeclaration && isVariableLike(paramSymbol.valueDeclaration) && isParameterDeclaration(paramSymbol.valueDeclaration)))) { + let thisifiedSignatures = signatures.map((signature) => thisifyTsPlusSignature(declaration, signature, exportName, file, reportDiagnostic)) + if (!isEachDefined(thisifiedSignatures)) { + return; + } + const thisifiedType = createAnonymousType(undefined, emptySymbols, thisifiedSignatures as TsPlusSignature[], [], []); + return [thisifiedType, thisifiedSignatures] as const; + } + } + function getTsPlusGetterSymbolForFunctionDeclaration(_name: string, _dataFirst: FunctionDeclaration) { + return (selfNode: Expression) => { + const res = checkTsPlusCustomCall( + _dataFirst, + selfNode, + [selfNode], + CheckMode.Normal + ); + if (isErrorType(res)) { + return void 0; + } + return createTsPlusGetterFunctionSymbol(_name, _dataFirst, res, getTypeOfNode(selfNode)); + }; + } + function getTsPlusGetterSymbolForVariableDeclaration(name: string, declaration: VariableDeclaration & { name: Identifier }) { + return (selfNode: Expression) => { + const res = checkTsPlusCustomCall( + declaration, + selfNode, + [selfNode], + CheckMode.Normal + ); + if (isErrorType(res)) { + return void 0; + } + return createTsPlusGetterVariableSymbol(name, declaration, res, getTypeOfNode(selfNode)); + }; + } + function getTsPlusStaticSymbolForFunctionDeclaration(file: SourceFile, exportName: string, name: string, dataFirst: FunctionDeclaration | VariableDeclarationWithFunction) { + const signatures = getSignaturesOfType(getTypeOfNode(dataFirst), SignatureKind.Call); + const methods = map(signatures, (s) => createTsPlusSignature(s, exportName, file)); + const symbol = createTsPlusStaticFunctionSymbol(name, dataFirst, methods); + const final = createAnonymousType(symbol, emptySymbols, methods, [], []); + return [final, createSymbolWithType(symbol, final)] as const; + } + function augmentPipeableIdentifierSymbol(identifier: Identifier, target: string, _exportName: string, name: string, dataFirstType: () => Type, declaration: FunctionDeclaration | VariableDeclarationWithFunction) { + const identifierType = getTypeOfNode(identifier); + if (identifierType.symbol) { + (identifierType.symbol as TsPlusPipeableIdentifierSymbol).tsPlusTag = TsPlusSymbolTag.PipeableIdentifier; + (identifierType.symbol as TsPlusPipeableIdentifierSymbol).tsPlusDeclaration = declaration; + (identifierType.symbol as TsPlusPipeableIdentifierSymbol).tsPlusTypeName = target; + (identifierType.symbol as TsPlusPipeableIdentifierSymbol).tsPlusName = name; + (identifierType.symbol as TsPlusPipeableIdentifierSymbol).getTsPlusDataFirstType = dataFirstType; + } + } + function isPipeableSelfARestParameter(signature: Signature): boolean { + return getSignaturesOfType(getReturnTypeOfSignature(signature), SignatureKind.Call).find(isSelfARestParameter) != null; + } + function getTsPlusFluentSignatureForPipeableFunction(file: SourceFile, exportName: string, name: string, pipeable: FunctionDeclaration, thisify = false): [Type, TsPlusSignature[]] | undefined { + if (exportName === 'filterOrElse') { + debugger + } + function reportDiagnostic(diagnostic: DiagnosticMessage) { + error(pipeable, diagnostic); + } + const type = getTypeOfNode(pipeable); + const signatures = getSignaturesOfType(type, SignatureKind.Call); + if (pipeable.body) { + const returnStatement = find( + pipeable.body.statements, + (s): s is ReturnStatement & { expression: ArrowFunction } => + isReturnStatement(s) && !!s.expression && isArrowFunction(s.expression) + ); + if (returnStatement && returnStatement.expression.parameters.length === 1) { + if (signatures.find(isPipeableSelfARestParameter)) { + error(pipeable, Diagnostics.The_first_parameter_of_a_pipeable_annotated_function_cannot_be_a_rest_parameter); + return; + } + const tsPlusSignatures = flatMap(signatures, (sig) => { + const returnType = getReturnTypeOfSignature(sig); + const returnSignatures = getSignaturesOfType(returnType, SignatureKind.Call); + return flatMap(returnSignatures, (rsig) => { + let newSig = createTsPlusSignature(sig, exportName, file); + newSig.parameters = [...rsig.parameters, ...sig.parameters]; + newSig.typeParameters = [...(rsig.typeParameters ?? []), ...(sig.typeParameters ?? [])]; + newSig.resolvedReturnType = getReturnTypeOfSignature(rsig); + newSig.minArgumentCount = newSig.minArgumentCount + 1; + const newDecl = factory.updateFunctionDeclaration( + pipeable, + pipeable.modifiers, + pipeable.asteriskToken, + pipeable.name, + [...(returnStatement.expression.typeParameters ?? []), ...(pipeable.typeParameters ?? [])], + [...returnStatement.expression.parameters, ...pipeable.parameters], + returnStatement.expression.type, + undefined + ); + + newDecl.jsDoc = pipeable.jsDoc; + newDecl.symbol = createTsPlusPipeableDeclarationSymbol(name, pipeable); + setParent(newDecl, pipeable.parent); + setOriginalNode(newDecl, pipeable) + + newSig.declaration = newDecl; + newSig.tsPlusDeclaration = pipeable; + + if (thisify) { + const thisified = thisifyTsPlusSignature(pipeable, newSig, exportName, file, reportDiagnostic); + if (!thisified) { + return; + } + newSig = thisified; + } + newSig.tsPlusPipeable = true; + return newSig; + }); + }) as TsPlusSignature[]; + const dataFirstType = createAnonymousType(undefined, emptySymbols, tsPlusSignatures, [], []); + return [dataFirstType, tsPlusSignatures]; + } + } + const filteredSignatures = filter(signatures, (sig) => { + if (!sig.declaration || !sig.declaration.type) { + return false + } + if (isFunctionTypeNode(sig.declaration.type) && sig.declaration.type.parameters.length === 1) { + return true + } + if (isCallSignatureDeclaration(sig.declaration) && sig.declaration.parameters.length === 1) { + return true + } + return false + }) + if (filteredSignatures.find(isPipeableSelfARestParameter)) { + error(pipeable, Diagnostics.The_first_parameter_of_a_pipeable_annotated_function_cannot_be_a_rest_parameter); + return; + } + if (filteredSignatures.length > 0) { + const tsPlusSignatures = flatMap(filteredSignatures, (sig) => { + const returnType = getReturnTypeOfSignature(sig); + const returnSignatures = getSignaturesOfType(returnType, SignatureKind.Call); + return flatMap(returnSignatures, (rsig) => { + let newSig = createTsPlusSignature(sig, exportName, file); + newSig.parameters = [...rsig.parameters, ...sig.parameters]; + newSig.typeParameters = [...(rsig.typeParameters ?? []), ...(sig.typeParameters ?? [])]; + newSig.resolvedReturnType = getReturnTypeOfSignature(rsig); + newSig.minArgumentCount = newSig.minArgumentCount + 1; + const newDecl = factory.updateFunctionDeclaration( + pipeable, + pipeable.modifiers, + pipeable.asteriskToken, + pipeable.name, + [...(rsig.declaration?.typeParameters as NodeArray ?? []), ...(sig.declaration?.typeParameters as NodeArray ?? [])], + [...(rsig.declaration?.parameters as NodeArray ?? []), ...(sig.declaration?.parameters as NodeArray ?? [])], + rsig.declaration?.type as TypeNode, + undefined + ); + + newDecl.jsDoc = pipeable.jsDoc; + newDecl.symbol = createTsPlusPipeableDeclarationSymbol(name, pipeable); + setParent(newDecl, pipeable.parent); + setOriginalNode(newDecl, pipeable); + + newSig.declaration = newDecl; + newSig.tsPlusDeclaration = pipeable; + + if (thisify) { + const thisifiedSignature = thisifyTsPlusSignature(pipeable, newSig, exportName, file, reportDiagnostic); + if (!thisifiedSignature) { + return; + } + newSig = thisifiedSignature; + } + newSig.tsPlusPipeable = true; + return newSig; + }); + }) as TsPlusSignature[]; + const dataFirstType = createAnonymousType(undefined, emptySymbols, tsPlusSignatures, [], []); + return [dataFirstType, tsPlusSignatures]; + } + return undefined; + } + function isVariableDeclarationWithFunction(node: VariableDeclaration): node is VariableDeclarationWithFunction { + return isIdentifier(node.name) && !!node.initializer && (isArrowFunction(node.initializer) || isFunctionExpression(node.initializer)); + } + function isEachDefined(array: (A | undefined)[]): array is A[] { + for (const a of array) { + if (a === undefined) { + return false; + } + } + return true; + } + function getTsPlusFluentSignatureForPipeableVariableDeclaration(file: SourceFile, exportName: string, name: string, pipeable: VariableDeclarationWithFunction | VariableDeclarationWithFunctionType, thisify = false): [Type, TsPlusSignature[]] | undefined { + function reportDiagnostic(diagnostic: DiagnosticMessage) { + error(pipeable, diagnostic); + } + const type = getTypeOfNode(pipeable); + const signatures = getSignaturesOfType(type, SignatureKind.Call); + if (isVariableDeclarationWithFunction(pipeable)) { + const initializer = pipeable.initializer; + const body = initializer.body; + let returnFn: ArrowFunction | FunctionTypeNode | undefined; + if (isBlock(body)) { + const candidate = find( + body.statements, + (s): s is ReturnStatement & { expression: ArrowFunction } => + isReturnStatement(s) && !!s.expression && isArrowFunction(s.expression) + ); + if (candidate) { + returnFn = candidate.expression; + } + } + else if (isArrowFunction(body)) { + returnFn = body; + } + if (returnFn && returnFn.parameters.length === 1) { + if (signatures.find(isPipeableSelfARestParameter)) { + error(pipeable, Diagnostics.The_first_parameter_of_a_pipeable_annotated_function_cannot_be_a_rest_parameter); + return; + } + const tsPlusSignatures = flatMap(signatures, (sig) => { + const returnType = getReturnTypeOfSignature(sig); + const returnSignatures = getSignaturesOfType(returnType, SignatureKind.Call); + return flatMap(returnSignatures, (rsig) => { + let newSig = createTsPlusSignature(sig, exportName, file); + newSig.parameters = [...rsig.parameters, ...sig.parameters]; + newSig.typeParameters = [...(rsig.typeParameters ?? []), ...(sig.typeParameters ?? [])]; + newSig.resolvedReturnType = getReturnTypeOfSignature(rsig); + newSig.minArgumentCount = newSig.minArgumentCount + 1; + let newDecl: ArrowFunction | FunctionExpression | FunctionTypeNode; + if (isArrowFunction(initializer)) { + newDecl = factory.updateArrowFunction( + initializer, + initializer.modifiers, + [...(returnFn!.typeParameters ?? []), ...(initializer.typeParameters ?? [])], + [...returnFn!.parameters, ...initializer.parameters], + returnFn!.type, + initializer.equalsGreaterThanToken, + factory.createBlock([]) + ); + } + else { + newDecl = factory.updateFunctionExpression( + initializer, + initializer.modifiers, + initializer.asteriskToken, + initializer.name, + [...(returnFn!.typeParameters ?? []), ...(initializer.typeParameters ?? [])], + [...returnFn!.parameters, ...initializer.parameters], + returnFn!.type, + factory.createBlock([]) + ); + } + + newDecl.jsDoc = pipeable.jsDoc; + newDecl.symbol = createTsPlusPipeableDeclarationSymbol(name, pipeable); + setParent(newDecl, pipeable.parent); + setOriginalNode(newDecl, pipeable); + + newSig.declaration = newDecl; + newSig.tsPlusDeclaration = pipeable; + + if (thisify) { + const thisifiedSignature = thisifyTsPlusSignature(pipeable, newSig, exportName, file, reportDiagnostic); + if (!thisifiedSignature) { + return; + } + newSig = thisifiedSignature; + } + newSig.tsPlusPipeable = true; + return newSig; + }); + }) as TsPlusSignature[]; + const dataFirstType = createAnonymousType(type.symbol, emptySymbols, tsPlusSignatures, [], []); + return [dataFirstType, tsPlusSignatures]; + } + } + const filteredSignatures = filter(signatures, (sig) => { + if (!sig.declaration || !sig.declaration.type) { + return false + } + if (isFunctionTypeNode(sig.declaration.type) && sig.declaration.type.parameters.length === 1) { + return true + } + if (isCallSignatureDeclaration(sig.declaration) && sig.declaration.parameters.length === 1) { + return true + } + return false + }) + if (filteredSignatures.find(isPipeableSelfARestParameter)) { + error(pipeable, Diagnostics.The_first_parameter_of_a_pipeable_annotated_function_cannot_be_a_rest_parameter); + return; + } + if (filteredSignatures.length > 0) { + const tsPlusSignatures = flatMap(filteredSignatures, (sig) => { + const returnFn = sig.declaration!.type! as FunctionTypeNode + const returnType = getReturnTypeOfSignature(sig); + const returnSignatures = getSignaturesOfType(returnType, SignatureKind.Call); + return flatMap(returnSignatures, (rsig) => { + let newSig = createTsPlusSignature(sig, exportName, file); + newSig.parameters = [...rsig.parameters, ...sig.parameters]; + newSig.typeParameters = [...(rsig.typeParameters ?? []), ...(sig.typeParameters ?? [])]; + newSig.resolvedReturnType = getReturnTypeOfSignature(rsig); + newSig.minArgumentCount = newSig.minArgumentCount + 1; + const newDecl = factory.createFunctionTypeNode( + factory.createNodeArray([...(returnFn.typeParameters ?? []), ...(sig.declaration?.typeParameters as NodeArray ?? [])]), + factory.createNodeArray([...returnFn.parameters, ...(sig.declaration?.parameters as NodeArray ?? [])]), + returnFn.type + ); + + newDecl.jsDoc = pipeable.parent.parent.jsDoc; + newDecl.symbol = createTsPlusPipeableDeclarationSymbol(name, pipeable); + setParent(newDecl, pipeable.parent); + setOriginalNode(newDecl, pipeable); + + newSig.declaration = newDecl; + newSig.tsPlusDeclaration = pipeable; + + if (thisify) { + const thisifiedSignature = thisifyTsPlusSignature(pipeable, newSig, exportName, file, reportDiagnostic); + if (!thisifiedSignature) { + return; + } + newSig = thisifiedSignature; + } + newSig.tsPlusPipeable = true; + return newSig; + }); + }) as TsPlusSignature[]; + const dataFirstType = createAnonymousType(type.symbol, emptySymbols, tsPlusSignatures, [], []); + return [dataFirstType, tsPlusSignatures]; + } + return undefined; + } + function isSelfARestParameter(signature: Signature): boolean { + if ( + signature.parameters[0] && + signature.parameters[0].declarations && + signature.parameters[0].declarations.find((decl) => isVariableLike(decl) && isParameterDeclaration(decl) && isRestParameter(decl as ParameterDeclaration)) + ) { + return true + } + return false; + } + function addToTypeSymbolCache(symbol: Symbol, tag: string, priority: "before" | "after") { + if (!typeSymbolCache.has(symbol)) { + typeSymbolCache.set(symbol, []); + } + const tags = typeSymbolCache.get(symbol)! + if (!tags.includes(tag)) { + if (priority === "before") { + typeSymbolCache.set(symbol, [tag, ...tags]) + } else { + tags.push(tag) + } + } + } + function addToCompanionSymbolCache(symbol: Symbol, tag: string) { + if (!companionSymbolCache.has(symbol)) { + companionSymbolCache.set(symbol, []) + } + const tags = companionSymbolCache.get(symbol)! + if (!tags.includes(tag)) { + tags.push(tag) + } + } + function tryCacheTsPlusGlobalSymbol(declaration: ImportDeclaration): void { + if (declaration.isTsPlusGlobal) { + (declaration.importClause!.namedBindings as NamedImports).elements.forEach((importSpecifier) => { + tsPlusGlobalImportCache.set(importSpecifier.name.escapedText as string, { declaration, importSpecifier, moduleSpecifier: declaration.moduleSpecifier as StringLiteral }); + }) + } + } + function cacheTsPlusType(declaration: InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration): void { + const type = getTypeOfNode(declaration); + for (const typeTag of collectTsPlusTypeTags(declaration)) { + if (type === globalStringType) { + addToTypeSymbolCache(tsplusStringPrimitiveSymbol, typeTag, "after"); + } + if (type === globalNumberType) { + addToTypeSymbolCache(tsplusNumberPrimitiveSymbol, typeTag, "after"); + } + if (type === globalBooleanType) { + addToTypeSymbolCache(tsplusBooleanPrimitiveSymbol, typeTag, "after"); + } + if (type === getGlobalBigIntType()) { + addToTypeSymbolCache(tsplusBigIntPrimitiveSymbol, typeTag, "after"); + } + if (type === globalFunctionType) { + addToTypeSymbolCache(tsplusFunctionPrimitiveSymbol, typeTag, "after"); + } + if (type === globalObjectType) { + addToTypeSymbolCache(tsplusObjectPrimitiveSymbol, typeTag, "after"); + } + if (type === globalArrayType) { + addToTypeSymbolCache(tsplusArraySymbol, typeTag, "after"); + } + if (type === globalReadonlyArrayType) { + addToTypeSymbolCache(tsplusReadonlyArraySymbol, typeTag, "after"); + } + if (type.symbol) { + addToTypeSymbolCache(type.symbol, typeTag, "after"); + if ((isInterfaceDeclaration(declaration) || isClassDeclaration(declaration)) && declaration.heritageClauses) { + tryCacheTsPlusInheritance(type.symbol, declaration.heritageClauses); + } + } + if (type.aliasSymbol) { + addToTypeSymbolCache(type.aliasSymbol, typeTag, "after"); + } + if (type.flags & TypeFlags.Union) { + tryCacheUnionInheritance((type as UnionType).types, type); + } + if (type.flags & TypeFlags.UnionOrIntersection) { + const types = (type as UnionOrIntersectionType).types; + for (const member of types) { + if (member.symbol) { + addToTypeSymbolCache(member.symbol, typeTag, "before"); + } + if (member.aliasSymbol) { + addToTypeSymbolCache(member.aliasSymbol, typeTag, "before"); + } + } + } + } + } + function tryCacheTsPlusInheritance(typeSymbol: Symbol, heritage: readonly HeritageClause[]): void { + if (!inheritanceSymbolCache.has(typeSymbol)) { + inheritanceSymbolCache.set(typeSymbol, new Set()); + } + const heritageExtensions = inheritanceSymbolCache.get(typeSymbol)!; + forEach(heritage, (clause) => { + forEach(clause.types, (node) => { + const type = getTypeOfNode(node); + if (type.symbol) { + heritageExtensions.add(type.symbol) + } + }) + }) + } + function tryCacheUnionInheritance(members: Type[], parent: Type): void { + const parentSymbol = parent.symbol ?? parent.aliasSymbol; + if (parentSymbol) { + for (const member of members) { + const memberSymbol = member.symbol ?? member.aliasSymbol; + if (memberSymbol) { + if (!inheritanceSymbolCache.has(memberSymbol)) { + inheritanceSymbolCache.set(memberSymbol, new Set()); + } + inheritanceSymbolCache.get(memberSymbol)!.add(parentSymbol); + if (memberSymbol.declarations) { + for (const declaration of memberSymbol.declarations) { + if ((isInterfaceDeclaration(declaration) || isClassDeclaration(declaration)) && declaration.heritageClauses) { + tryCacheTsPlusInheritance(memberSymbol, declaration.heritageClauses); + } + } + } + } + } + } + } + function getTsPlusSourceFileCache(tag: string) { + return tsPlusFiles.has(tag) ? tsPlusFiles.get(tag)! : (tsPlusFiles.set(tag, new Set()), tsPlusFiles.get(tag)!); + } + function cacheTsPlusCompanion(declaration: ClassDeclaration | InterfaceDeclaration | TypeAliasDeclaration): void { + const tags = collectTsPlusCompanionTags(declaration); + const type = getTypeOfNode(declaration) + if (type.symbol) { + for (const companionTag of tags) { + getTsPlusSourceFileCache(companionTag).add(getSourceFileOfNode(declaration)); + addToCompanionSymbolCache(type.symbol, companionTag); + } + } + if (type.aliasSymbol) { + for (const companionTag of tags) { + getTsPlusSourceFileCache(companionTag).add(getSourceFileOfNode(declaration)); + addToCompanionSymbolCache(type.aliasSymbol, companionTag); + } + } + } + function cacheTsPlusStaticVariable(file: SourceFile, declaration: VariableDeclarationWithIdentifier | ClassDeclarationWithIdentifier) { + const staticTags = collectTsPlusStaticTags(declaration); + if (staticTags.length > 0) { + const symbol = getSymbolAtLocation(declaration.name); + if (symbol) { + for (const { target, name } of staticTags) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + if (!unresolvedStaticCache.get(target)) { + unresolvedStaticCache.set(target, new Map()); + } + const map = unresolvedStaticCache.get(target)! + map.set(name, { + target, + name, + symbol, + declaration, + exportName: symbol.escapedName.toString(), + definition: file + }) + } + } + } + } + function cacheTsPlusFluentVariable(file: SourceFile, declaration: VariableDeclarationWithIdentifier) { + const fluentTags = collectTsPlusFluentTags(declaration); + for (const tag of fluentTags) { + getTsPlusSourceFileCache(tag.target).add(getSourceFileOfNode(declaration)); + if (!unresolvedFluentCache.has(tag.target)) { + unresolvedFluentCache.set(tag.target, new Map()); + } + const map = unresolvedFluentCache.get(tag.target)!; + if (!map.has(tag.name)) { + map.set(tag.name, { + target: tag.target, + name: tag.name, + definition: new Set([{ + definition: file, + declaration: declaration as VariableDeclaration & { name: Identifier }, + exportName: declaration.name.escapedText.toString(), + priority: tag.priority, + }]) + }); + } + else { + const extension = map.get(tag.name)!; + extension.definition.add({ + definition: file, + declaration: declaration as VariableDeclaration & { name: Identifier }, + exportName: declaration.name.escapedText.toString(), + priority: tag.priority, + }); + } + } + } + function cacheTsPlusGetterVariable(file: SourceFile, declaration: VariableDeclarationWithIdentifier) { + for (const { target, name } of collectTsPlusGetterTags(declaration)) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + if (!getterCache.has(target)) { + getterCache.set(target, new Map()); + } + const map = getterCache.get(target)!; + map.set(name, { + patched: getTsPlusGetterSymbolForVariableDeclaration( + name, + (declaration as VariableDeclaration & { name: Identifier }) + ), + exportName: declaration.name.escapedText.toString(), + definition: file, + declaration, + }); + } + } + function cacheTsPlusOperatorFunction(file: SourceFile, declaration: FunctionDeclaration) { + if(declaration.name && isIdentifier(declaration.name)) { + const operatorTags = collectTsPlusOperatorTags(declaration); + if (operatorTags.length > 0) { + const symbol = getSymbolAtLocation(declaration.name); + if (symbol) { + for (const tag of operatorTags) { + getTsPlusSourceFileCache(tag.target).add(getSourceFileOfNode(declaration)); + if (!operatorCache.has(tag.target)) { + operatorCache.set(tag.target, new Map()); + } + const map = operatorCache.get(tag.target)!; + if (!map.has(tag.name)) { + map.set(tag.name, []); + } + map.get(tag.name)!.push({ + patched: symbol, + exportName: declaration.name.escapedText.toString(), + definition: file, + priority: tag.priority, + }); + } + } + } + } + } + function cacheTsPlusOperatorVariable(file: SourceFile, declaration: VariableDeclarationWithIdentifier) { + const operatorTags = collectTsPlusOperatorTags(declaration); + if (operatorTags.length > 0) { + const symbol = getSymbolAtLocation(declaration.name); + if (symbol) { + for (const tag of operatorTags) { + getTsPlusSourceFileCache(tag.target).add(getSourceFileOfNode(declaration)); + if (!operatorCache.has(tag.target)) { + operatorCache.set(tag.target, new Map()); + } + const map = operatorCache.get(tag.target)!; + if (!map.has(tag.name)) { + map.set(tag.name, []); + } + map.get(tag.name)!.push({ + patched: symbol, + exportName: declaration.name.escapedText.toString(), + definition: file, + priority: tag.priority, + }); + } + } + } + } + function cacheTsPlusFluentFunction(file: SourceFile, declaration: FunctionDeclaration) { + const fluentTags = collectTsPlusFluentTags(declaration); + for (const tag of fluentTags) { + getTsPlusSourceFileCache(tag.target).add(getSourceFileOfNode(declaration)); + if (!unresolvedFluentCache.has(tag.target)) { + unresolvedFluentCache.set(tag.target, new Map()); + } + const map = unresolvedFluentCache.get(tag.target)!; + if (!map.has(tag.name)) { + map.set(tag.name, { + target: tag.target, + name: tag.name, + definition: new Set([{ + definition: file, + declaration, + exportName: declaration.name!.escapedText.toString(), + priority: tag.priority, + }]), + }); + } + else { + const extension = map.get(tag.name)!; + extension.definition.add({ + definition: file, + declaration, + exportName: declaration.name!.escapedText.toString(), + priority: tag.priority, + }); + } + } + } + function cacheTsPlusPipeableOperatorFunction(file: SourceFile, declaration: FunctionDeclaration) { + if (declaration.name && isIdentifier(declaration.name)) { + const pipeableOperatorTags = collectTsPlusPipeableOperatorTags(declaration); + if (pipeableOperatorTags.length > 0) { + for (const { target, name, priority } of pipeableOperatorTags) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + if (!unresolvedPipeableOperatorCache.has(target)) { + unresolvedPipeableOperatorCache.set(target, new Map()) + } + const exportName = declaration.name.escapedText.toString(); + let cached: [Type, TsPlusSignature[]] | false = false + const getTypeAndSignatures = (): [Type, TsPlusSignature[]] => { + if (cached === false) { + const resolved = getTsPlusFluentSignatureForPipeableFunction(file, exportName, name, declaration); + if (resolved) { + cached = resolved; + } + else { + error(declaration, Diagnostics.Invalid_declaration_annotated_as_pipeable); + cached = [errorType, []]; + } + } + return [cached[0], [...cached[1]]]; + } + const map = unresolvedPipeableOperatorCache.get(target)!; + if (!map.has(name)) { + map.set(name, { + target, + name, + definition: new Set() + }) + } + map.get(name)!.definition.add({ + definition: file, + declaration, + exportName, + priority, + getTypeAndSignatures + }) + augmentPipeableIdentifierSymbol( + declaration.name, + target, + declaration.name.escapedText.toString(), + name, + () => getTypeAndSignatures()[0], + declaration + ); + } + } + } + } + function cacheTsPlusPipeableOperatorVariable(file: SourceFile, declaration: VariableDeclarationWithIdentifier) { + if (declaration.initializer && isFunctionLikeDeclaration(declaration.initializer) || + declaration.type && (isFunctionTypeNode(declaration.type) || isTypeLiteralNode(declaration.type))) { + const pipeableOperatorTags = collectTsPlusPipeableOperatorTags(declaration); + if (pipeableOperatorTags.length > 0) { + for (const { target, name, priority } of pipeableOperatorTags) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + if (!unresolvedPipeableOperatorCache.has(target)) { + unresolvedPipeableOperatorCache.set(target, new Map()) + } + const exportName = declaration.name.escapedText.toString(); + let cached: [Type, TsPlusSignature[]] | false = false + const getTypeAndSignatures = (): [Type, TsPlusSignature[]] => { + if (cached === false) { + const resolved = getTsPlusFluentSignatureForPipeableVariableDeclaration(file, exportName, name, declaration as VariableDeclarationWithFunction | VariableDeclarationWithFunctionType); + if (resolved) { + cached = resolved; + } + else { + error(declaration, Diagnostics.Invalid_declaration_annotated_as_pipeable); + cached = [errorType, []]; + } + } + return [cached[0], [...cached[1]]]; + } + const map = unresolvedPipeableOperatorCache.get(target)!; + if (!map.has(name)) { + map.set(name, { + target, + name, + definition: new Set() + }) + } + map.get(name)!.definition.add({ + definition: file, + declaration, + exportName, + priority, + getTypeAndSignatures + }) + augmentPipeableIdentifierSymbol( + declaration.name, + target, + declaration.name.escapedText.toString(), + name, + () => getTypeAndSignatures()[0], + declaration as VariableDeclarationWithFunction + ); + } + } + } + } + function cacheTsPlusPipeableFunction(file: SourceFile, declaration: FunctionDeclaration) { + if (declaration.name && isIdentifier(declaration.name)) { + const pipeableTags = collectTsPlusPipeableTags(declaration); + for (const { target, name, priority } of pipeableTags) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + if (!unresolvedPipeableCache.has(target)) { + unresolvedPipeableCache.set(target, new Map()); + } + const exportName = declaration.name.escapedText.toString() + let cached : [Type, TsPlusSignature[]] | false = false + const getTypeAndSignatures = (): [Type, TsPlusSignature[]] => { + if (cached === false) { + const resolved = getTsPlusFluentSignatureForPipeableFunction(file, exportName, name, declaration, /** thisify */ true); + if (resolved) { + cached = resolved; + } + else { + error(declaration, Diagnostics.Invalid_declaration_annotated_as_pipeable); + cached = [errorType, []]; + } + } + return [cached[0], [...cached[1]]]; + } + const map = unresolvedPipeableCache.get(target)!; + if (!map.has(name)) { + map.set(name, { target, name, definition: new Set() }) + } + const extension = map.get(name)! + extension.definition.add({ + definition: file, + declaration, + exportName, + priority, + getTypeAndSignatures + }) + augmentPipeableIdentifierSymbol( + declaration.name, + target, + declaration.name.escapedText.toString(), + name, + () => getTypeAndSignatures()[0], + declaration + ); + getNodeLinks(declaration).tsPlusPipeableExtension = { + declaration, + definition: file, + exportName, + typeName: target, + funcName: name, + getTypeAndSignatures + } + } + } + } + function cacheTsPlusPipeableVariable(file: SourceFile, declaration: VariableDeclarationWithIdentifier) { + if( + (declaration.initializer && isFunctionLikeDeclaration(declaration.initializer)) || + (declaration.type && isFunctionTypeNode(declaration.type)) || + ((declaration.type) && isTypeLiteralNode(declaration.type) && every(declaration.type.members, isCallSignatureDeclaration)) + ) { + for (const { target, name, priority } of collectTsPlusPipeableTags(declaration)) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + if (!unresolvedPipeableCache.has(target)) { + unresolvedPipeableCache.set(target, new Map()); + } + const map = unresolvedPipeableCache.get(target)!; + const exportName = declaration.name.escapedText.toString(); + let cached : [Type, TsPlusSignature[]] | false = false + const getTypeAndSignatures = () => { + if (cached === false) { + const resolved = getTsPlusFluentSignatureForPipeableVariableDeclaration( + file, + exportName, + name, + declaration as VariableDeclarationWithFunction | VariableDeclarationWithFunctionType, + /** thisify */ true + ) + if (resolved) { + cached = resolved; + } + else { + error(declaration, Diagnostics.Invalid_declaration_annotated_as_pipeable); + cached = [errorType, []]; + } + } + return cached; + } + if (!map.has(name)) { + map.set(name, { target, name, definition: new Set() }) + } + const extension = map.get(name)! + extension.definition.add({ + definition: file, + declaration, + exportName, + priority, + getTypeAndSignatures + }) + augmentPipeableIdentifierSymbol( + declaration.name, + target, + declaration.name.escapedText.toString(), + name, + () => getTypeAndSignatures()[0], + declaration as VariableDeclarationWithFunction + ); + getNodeLinks(declaration).tsPlusPipeableExtension = { + declaration: declaration as VariableDeclarationWithFunction, + definition: file, + exportName, + typeName: target, + funcName: name, + getTypeAndSignatures + } + } + } + } + function cacheTsPlusGetterFunction(file: SourceFile, declaration: FunctionDeclaration) { + if(declaration.name && isIdentifier(declaration.name)) { + for (const { target, name } of collectTsPlusGetterTags(declaration)) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + if (!getterCache.has(target)) { + getterCache.set(target, new Map()); + } + const map = getterCache.get(target)!; + map.set(name, { + patched: getTsPlusGetterSymbolForFunctionDeclaration(name, declaration), + exportName: declaration.name.escapedText.toString(), + definition: file, + declaration + }); + } + } + } + function cacheTsPlusStaticFunction(file: SourceFile, declaration: FunctionDeclaration) { + if(declaration.name && isIdentifier(declaration.name)) { + for (const { target, name } of collectTsPlusStaticTags(declaration)) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + if (!staticCache.has(target)) { + staticCache.set(target, new Map()); + } + const map = staticCache.get(target)!; + map.set(name, () => { + if (staticFunctionCache.has(target)) { + const resolvedMap = staticFunctionCache.get(target)!; + if (resolvedMap.has(name)) { + return resolvedMap.get(name)!; + } + } + else { + staticFunctionCache.set(target, new Map()); + } + const resolvedMap = staticFunctionCache.get(target)!; + const [type, patched] = getTsPlusStaticSymbolForFunctionDeclaration( + file, + declaration.name!.escapedText.toString(), + name, + declaration + ); + const extension = { + patched, + exportName: declaration.name!.escapedText.toString(), + definition: file, + type + }; + resolvedMap.set(name, extension); + return extension; + }); + } + } + } + function cacheTsPlusUnifyFunction(declaration: FunctionDeclaration) { + if(declaration.name && isIdentifier(declaration.name)) { + for (const target of collectTsPlusUnifyTags(declaration)) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + identityCache.set(target, declaration); + } + } + } + function cacheTsPlusIndexFunction(declaration: FunctionDeclaration) { + if(declaration.name && isIdentifier(declaration.name)) { + for (const target of collectTsPlusIndexTags(declaration)) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + indexCache.set(target, () => { + if (resolvedIndexCache.has(target)) { + return resolvedIndexCache.get(target); + } + const definition = getSourceFileOfNode(declaration); + const signatures = getSignaturesOfType(getTypeOfNode(declaration), SignatureKind.Call) as Signature[]; + if (signatures[0]) { + resolvedIndexCache.set(target, { declaration, signature: signatures[0], definition, exportName: declaration.name!.escapedText.toString() }); + } + return resolvedIndexCache.get(target)!; + }); + } + } + } + function cacheTsPlusIndexVariable(declaration: VariableDeclarationWithIdentifier) { + for (const target of collectTsPlusIndexTags(declaration)) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + indexCache.set(target, () => { + if (resolvedIndexCache.has(target)) { + return resolvedIndexCache.get(target); + } + const definition = getSourceFileOfNode(declaration); + const signatures = getSignaturesOfType(getTypeOfNode(declaration), SignatureKind.Call) as Signature[]; + if (signatures[0]) { + resolvedIndexCache.set(target, { declaration, signature: signatures[0], definition, exportName: declaration.name!.escapedText.toString() }); + } + return resolvedIndexCache.get(target)!; + }); + } + } + function cacheTsPlusPipeableIndexFunction(declaration: FunctionDeclaration) { + if(declaration.name && isIdentifier(declaration.name)) { + for (const target of collectTsPlusPipeableIndexTags(declaration)) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + indexCache.set(target, () => { + if (resolvedIndexCache.has(target)) { + return resolvedIndexCache.get(target); + } + const definition = getSourceFileOfNode(declaration); + const exportName = declaration.name!.escapedText.toString(); + const typeAndSignatures = getTsPlusFluentSignatureForPipeableFunction(definition, exportName, exportName, declaration); + if (typeAndSignatures && typeAndSignatures[1][0]) { + resolvedIndexCache.set(target, { declaration, signature: typeAndSignatures[1][0], definition, exportName }); + return resolvedIndexCache.get(target); + } + }) + } + } + } + function cacheTsPlusPipeableIndexVariable(declaration: VariableDeclarationWithIdentifier) { + for (const target of collectTsPlusPipeableIndexTags(declaration)) { + getTsPlusSourceFileCache(target).add(getSourceFileOfNode(declaration)); + indexCache.set(target, () => { + if (resolvedIndexCache.has(target)) { + return resolvedIndexCache.get(target); + } + const definition = getSourceFileOfNode(declaration); + const exportName = declaration.name!.escapedText.toString(); + const typeAndSignatures = getTsPlusFluentSignatureForPipeableVariableDeclaration(definition, exportName, exportName, declaration as VariableDeclarationWithFunction | VariableDeclarationWithFunctionType); + if (typeAndSignatures && typeAndSignatures[1][0]) { + resolvedIndexCache.set(target, { declaration, signature: typeAndSignatures[1][0], definition, exportName }); + return resolvedIndexCache.get(target); + } + }) + } + } + function collectTsPlusSymbols(file: SourceFile): void { + for (const declaration of file.tsPlusContext.type) { + cacheTsPlusType(declaration); + } + for (const declaration of file.tsPlusContext.companion) { + cacheTsPlusCompanion(declaration); + } + for (const declaration of file.tsPlusContext.fluent) { + if (isFunctionDeclaration(declaration)) { + cacheTsPlusFluentFunction(file, declaration); + } + else { + cacheTsPlusFluentVariable(file, declaration); + } + } + for (const declaration of file.tsPlusContext.pipeable) { + if (isFunctionDeclaration(declaration)) { + cacheTsPlusPipeableFunction(file, declaration); + } + else { + cacheTsPlusPipeableVariable(file, declaration); + } + } + for (const declaration of file.tsPlusContext.operator) { + if (isFunctionDeclaration(declaration)) { + cacheTsPlusOperatorFunction(file, declaration); + } + else { + cacheTsPlusOperatorVariable(file, declaration); + } + } + for (const declaration of file.tsPlusContext.pipeableOperator) { + if (isFunctionDeclaration(declaration)) { + cacheTsPlusPipeableOperatorFunction(file, declaration); + } + else { + cacheTsPlusPipeableOperatorVariable(file, declaration); + } + } + for (const declaration of file.tsPlusContext.static) { + if (isFunctionDeclaration(declaration)) { + cacheTsPlusStaticFunction(file, declaration); + } + else { + cacheTsPlusStaticVariable(file, declaration); + } + } + for (const declaration of file.tsPlusContext.getter) { + if (isFunctionDeclaration(declaration)) { + cacheTsPlusGetterFunction(file, declaration); + } + else { + cacheTsPlusGetterVariable(file, declaration); + } + } + for (const declaration of file.tsPlusContext.unify) { + cacheTsPlusUnifyFunction(declaration); + } + for (const declaration of file.tsPlusContext.index) { + if (isFunctionDeclaration(declaration)) { + cacheTsPlusIndexFunction(declaration); + } + else { + cacheTsPlusIndexVariable(declaration); + } + } + for (const declaration of file.tsPlusContext.pipeableIndex) { + if (isFunctionDeclaration(declaration)) { + cacheTsPlusPipeableIndexFunction(declaration); + } + else { + cacheTsPlusPipeableIndexVariable(declaration); + } + } + } + function fillTsPlusLocalScope(file: SourceFile) { + const derivationRules = tsPlusWorldScope.rules; + if (file.symbol) { + const exports = file.symbol.exports; + if (exports) { + exports.forEach((exportSymbol) => { + if (exportSymbol.valueDeclaration) { + const declaration = exportSymbol.valueDeclaration; + if (isTsPlusImplicit(declaration)) { + indexInScope(exportSymbol); + } + else if((isFunctionDeclaration(declaration) || isVariableDeclaration(declaration)) && declaration.tsPlusDeriveTags) { + for (const tag of declaration.tsPlusDeriveTags) { + const match = tag.match(/^derive ([^<]*)<([^>]*)> (.*)$/); + if (match) { + const [, typeTag, paramActions, rest] = match; + const [priority, ...options] = rest.split(" "); + if (!derivationRules.has(typeTag)) { + derivationRules.set(typeTag, { lazyRule: void 0, rules: [] }); + } + derivationRules.get(typeTag)?.rules.push([ + { typeTag, paramActions: paramActions.split(",").map((s) => s.trim()) }, + Number.parseFloat(priority), + declaration, + new Set(options) + ]); + } else { + const match = tag.match(/^derive ([^<]*) lazy$/); + if (match) { + const [, typeTag] = match; + if (!derivationRules.has(typeTag)) { + derivationRules.set(typeTag, { lazyRule: void 0, rules: [] }); + } + derivationRules.get(typeTag)!.lazyRule = declaration; + } + } + } + } + } + }); + } + } + } + function initTsPlusTypeCheckerImplicits() { + for (const file of host.getSourceFiles()) { + fillTsPlusLocalScope(file); + } + } + function postInitTsPlusTypeChecker() { + tsPlusDebug && console.time("initTsPlusTypeChecker build dependency tree") + typeSymbolCache.forEach((tags, symbol) => { + forEach(symbol.declarations, (declaration) => { + const source = getSourceFileOfNode(declaration); + const set = tsPlusFilesFinal.has(source) ? tsPlusFilesFinal.get(source)! : (tsPlusFilesFinal.set(source, new Set()), tsPlusFilesFinal.get(source)!); + forEach(tags, (tag) => { + tsPlusFiles.get(tag)?.forEach((dep) => { + set.add(dep); + }) + }) + }) + }) + tsPlusDebug && console.timeEnd("initTsPlusTypeChecker build dependency tree") + } + function initTsPlusTypeChecker() { + if (compilerOptions.tsPlusEnabled === false) { + return; + } + tsPlusDebug && console.time("initTsPlusTypeChecker caches") + fileMap.map = getFileMap(host.getCompilerOptions(), host); + fluentCache.clear(); + unresolvedFluentCache.clear(); + unresolvedPipeableCache.clear(); + unresolvedPipeableOperatorCache.clear(); + operatorCache.clear(); + typeSymbolCache.clear(); + typeHashCache.clear(); + staticFunctionCache.clear(); + staticValueCache.clear(); + unresolvedStaticCache.clear(); + identityCache.clear(); + tsPlusFiles.clear(); + tsPlusFilesFinal.clear(); + getterCache.clear(); + callCache.clear(); + indexCache.clear(); + resolvedIndexCache.clear(); + indexAccessExpressionCache.clear(); + inheritanceSymbolCache.clear(); + tsPlusWorldScope.implicits.clear(); + tsPlusWorldScope.rules.clear(); + tsPlusDebug && console.timeEnd("initTsPlusTypeChecker caches") + tsPlusDebug && console.time("initTsPlusTypeChecker collect") + for (const file of host.getSourceFiles()) { + collectTsPlusSymbols(file); + } + tsPlusDebug && console.timeEnd("initTsPlusTypeChecker collect") + tsPlusDebug && console.time("initTsPlusTypeChecker joinining signatures") + unresolvedStaticCache.forEach((map, typeName) => { + if (!staticCache.has(typeName)) { + staticCache.set(typeName, new Map()); + } + const staticMap = staticCache.get(typeName)!; + map.forEach(({ target, declaration, name, definition, exportName }) => { + staticMap.set(name, () => { + if (staticValueCache.has(target)) { + const resolvedMap = staticValueCache.get(target)!; + if (resolvedMap.has(name)) { + return resolvedMap.get(name)!; + } + } + else { + staticValueCache.set(target, new Map()); + } + const resolvedMap = staticValueCache.get(typeName)!; + const nameSymbol = getSymbolAtLocation(declaration.name!); + if (nameSymbol) { + const patched = createTsPlusStaticValueSymbol(name, declaration, nameSymbol); + if (isClassDeclaration(declaration)) { + const companionTags = collectTsPlusCompanionTags(declaration); + for (const companionTag of companionTags) { + getTsPlusSourceFileCache(companionTag).add(getSourceFileOfNode(declaration)); + addToCompanionSymbolCache(patched, companionTag); + } + } + const type = getTypeOfSymbol(patched); + // @ts-expect-error + type.tsPlusSymbol = patched; + const extension = { + patched, + definition, + exportName, + type + } + resolvedMap.set(name, extension); + return extension; + } + }) + }) + }) + unresolvedFluentCache.forEach((map, typeName) => { + if (!fluentCache.has(typeName)) { + fluentCache.set(typeName, new Map()); + } + const fluentMap = fluentCache.get(typeName)!; + map.forEach(({ name, definition }) => { + fluentMap.set(name, () => { + if (resolvedFluentCache.has(typeName)) { + const resolvedMap = resolvedFluentCache.get(typeName)!; + if (resolvedMap.has(name)) { + return resolvedMap.get(name)!; + } + } + const allTypes: { type: Type, signatures: readonly TsPlusSignature[] }[] = []; + const prioritizedSignaturesMap = new Map () + + definition.forEach(({ declaration, definition, exportName, priority }) => { + if (isFunctionDeclaration(declaration)) { + const typeAndSignatures = getTsPlusFluentSignaturesForFunctionDeclaration(definition, exportName, declaration); + if (typeAndSignatures) { + const [type, signatures] = typeAndSignatures; + if (prioritizedSignaturesMap.has(priority)) { + prioritizedSignaturesMap.get(priority)!.push(...signatures) + } + else { + prioritizedSignaturesMap.set(priority, signatures); + } + allTypes.push({ type, signatures }); + } + } + else { + const typeAndSignatures = getTsPlusFluentSignaturesForVariableDeclaration(definition, exportName, declaration); + if (typeAndSignatures) { + const [type, signatures] = typeAndSignatures; + if (prioritizedSignaturesMap.has(priority)) { + prioritizedSignaturesMap.get(priority)!.push(...signatures) + } + else { + prioritizedSignaturesMap.set(priority, signatures); + } + allTypes.push({ type, signatures }); + } + } + }); + + const prioritizedSignatures: [number, TsPlusSignature[]][] = [] + prioritizedSignaturesMap.forEach((signatures, priority) => { + prioritizedSignatures.push([priority, signatures]) + }) + prioritizedSignatures.sort((x, y) => x[0] > y[0] ? 1 : x[0] < y[0] ? -1 : 0) + + const allSignatures = prioritizedSignatures.flatMap((signatures) => signatures[1]) + + if (allSignatures.length === 0) { + return undefined; + } + const symbol = createTsPlusFluentSymbol(name, allSignatures); + const type = createAnonymousType(symbol, emptySymbols, allSignatures, [], []); + const extension: TsPlusFluentExtension = { + patched: createSymbolWithType(symbol, type), + signatures: allSignatures, + types: allTypes + }; + if (!resolvedFluentCache.has(typeName)) { + resolvedFluentCache.set(typeName, new Map()); + } + const resolvedMap = resolvedFluentCache.get(typeName)!; + resolvedMap.set(name, extension); + return extension; + }); + }); + }); + unresolvedFluentCache.clear(); + unresolvedPipeableCache.forEach((map, typeName) => { + if (!fluentCache.has(typeName)) { + fluentCache.set(typeName, new Map()); + } + const fluentMap = fluentCache.get(typeName)!; + map.forEach(({ name, definition }) => { + if (!fluentMap.has(name)) { + fluentMap.set(name, () => { + if (resolvedFluentCache.has(typeName)) { + const resolvedMap = resolvedFluentCache.get(typeName)!; + if (resolvedMap.has(name)) { + return resolvedMap.get(name)!; + } + } + const allTypes: { type: Type, signatures: readonly TsPlusSignature[] }[] = []; + const prioritizedSignaturesMap = new Map () + + definition.forEach(({ priority, getTypeAndSignatures }) => { + const typeAndSignatures = getTypeAndSignatures(); + if (typeAndSignatures) { + const [type, signatures] = typeAndSignatures; + if (prioritizedSignaturesMap.has(priority)) { + prioritizedSignaturesMap.get(priority)!.push(...signatures) + } + else { + prioritizedSignaturesMap.set(priority, signatures); + } + allTypes.push({ type, signatures }); + } + }); + + const prioritizedSignatures: [number, TsPlusSignature[]][] = [] + prioritizedSignaturesMap.forEach((signatures, priority) => { + prioritizedSignatures.push([priority, signatures]) + }) + prioritizedSignatures.sort((x, y) => x[0] > y[0] ? 1 : x[0] < y[0] ? -1 : 0) + + const allSignatures = prioritizedSignatures.flatMap((signatures) => signatures[1]) + + if (allSignatures.length === 0) { + return undefined; + } + const symbol = createTsPlusFluentSymbol(name, allSignatures); + const type = createAnonymousType(symbol, emptySymbols, allSignatures, [], []); + const extension: TsPlusFluentExtension = { + patched: createSymbolWithType(symbol, type), + signatures: allSignatures, + types: allTypes + }; + if (!resolvedFluentCache.has(typeName)) { + resolvedFluentCache.set(typeName, new Map()); + } + const resolvedMap = resolvedFluentCache.get(typeName)!; + resolvedMap.set(name, extension); + return extension; + }); + } + }); + }); + unresolvedPipeableCache.clear(); + unresolvedPipeableOperatorCache.forEach((map, typeName) => { + if (!operatorCache.has(typeName)) { + operatorCache.set(typeName, new Map()); + } + const cache = operatorCache.get(typeName)!; + map.forEach((member, funcName) => { + if (!cache.has(funcName)) { + cache.set(funcName, []); + } + member.definition.forEach(({ priority, getTypeAndSignatures, exportName, definition }) => { + const [memberType, memberSignatures] = getTypeAndSignatures(); + const symbol = createSymbol(SymbolFlags.Function, funcName as __String); + symbol.declarations = memberSignatures.map((sig) => sig.declaration!); + getSymbolLinks(symbol).type = memberType; + cache.get(funcName)!.push({ + patched: symbol, + exportName, + definition, + priority + }) + }) + }) + }) + unresolvedPipeableOperatorCache.clear(); + operatorCache.forEach((map) => { + map.forEach((extensions) => { + extensions.sort(({ priority: x }, { priority: y }) => x > y ? 1 : x < y ? -1 : 0) + }) + }) + tsPlusDebug && console.timeEnd("initTsPlusTypeChecker joinining signatures") + tsPlusDebug && console.time("initTsPlusTypeChecker implicits") + initTsPlusTypeCheckerImplicits(); + tsPlusDebug && console.timeEnd("initTsPlusTypeChecker implicits") + postInitTsPlusTypeChecker(); + } + // TSPLUS EXTENSION END + function initializeTypeChecker() { + tsPlusGlobalImportCache.clear(); // Bind all source files and propagate errors for (const file of host.getSourceFiles()) { bindSourceFile(file, compilerOptions); + for (const statement of file.imports) { + if (isImportDeclaration(statement.parent)) { + tryCacheTsPlusGlobalSymbol(statement.parent); + } + } } amalgamatedDuplicates = new Map(); @@ -46308,6 +50665,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } }); amalgamatedDuplicates = undefined; + tsPlusDebug && console.time("initTsPlusTypeChecker") + initTsPlusTypeChecker(); + tsPlusDebug && console.timeEnd("initTsPlusTypeChecker") } function checkExternalEmitHelpers(location: Node, helpers: ExternalEmitHelpers) { @@ -48433,3 +52793,132 @@ class SymbolTrackerImpl implements SymbolTracker { this.context.reportedDiagnostic = true; } } + +// TSPLUS EXTENSION BEGIN +export function isTsPlusSymbol(symbol: Symbol): symbol is TsPlusSymbol { + return 'tsPlusTag' in symbol; +} +export function isTsPlusSignature(signature: Signature): signature is TsPlusSignature { + return 'tsPlusTag' in signature; +} +export function getNameForType(type: Type | undefined): string | undefined { + if(!type) { + return; + } + if (type.symbol && type.symbol.escapedName) { + return type.symbol.escapedName as string; + } + if (type.aliasSymbol && type.aliasSymbol.escapedName) { + return type.aliasSymbol.escapedName as string; + } +} +export function getThisTypeNameForCallLikeExpression(checker: TypeChecker, node: Node): string | undefined { + if(isCallLikeExpression(node)) { + const signature = checker.getResolvedSignature(node); + if (signature && signature.thisParameter) { + const resolvedThisType = checker.getTypeOfSymbol(signature.thisParameter) + if (resolvedThisType.symbol) { + const typeName = unescapeLeadingUnderscores(resolvedThisType.symbol.escapedName) + if (typeName === "__type") { + return "Function"; + } + return typeName; + } else if (resolvedThisType.aliasSymbol) { + return unescapeLeadingUnderscores(resolvedThisType.aliasSymbol.escapedName) + } + } + } +} +export function getThisTypeNameForTsPlusSymbol(checker: TypeChecker, symbol: TsPlusSymbol): string { + switch(symbol.tsPlusTag) { + case TsPlusSymbolTag.Fluent: { + if(symbol.tsPlusResolvedSignatures[0] && symbol.tsPlusResolvedSignatures[0].thisParameter) { + const type = checker.getSymbolLinks(symbol.tsPlusResolvedSignatures[0].thisParameter).type; + if (type) { + const typeName = getNameForType(type) + if (typeName) { + return typeName; + } + const primitiveName = checker.getPrimitiveTypeName(type) + if (primitiveName) { + return primitiveName; + } + } + } + break; + } + case TsPlusSymbolTag.GetterVariable: + case TsPlusSymbolTag.Getter: { + const typeName = getNameForType(symbol.tsPlusSelfType) + if (typeName) { + if (typeName === "__type" && (symbol.tsPlusSelfType as ObjectType).callSignatures?.length) { + return "Function"; + } + return typeName + } + const primitiveName = checker.getPrimitiveTypeName(symbol.tsPlusSelfType) + if (primitiveName) { + return primitiveName; + } + break; + } + } + return anon; +} + +export function isSymbolParameterDeclaration(symbol: Symbol): symbol is Symbol & { valueDeclaration: ParameterDeclaration } { + return !!symbol.valueDeclaration && isVariableLike(symbol.valueDeclaration) && isParameterDeclaration(symbol.valueDeclaration); +} + +export function isTsPlusType(type: Type): type is TsPlusType { + return "tsPlusSymbol" in type; +} + +type TsPlusSymbolWithDeclaration = Omit & { + tsPlusSymbol: TsPlusGetterSymbol | TsPlusGetterVariableSymbol | TsPlusStaticValueSymbol +} + +export function isTsPlusTypeWithDeclaration(type: Type): type is TsPlusSymbolWithDeclaration { + return isTsPlusType(type) && ( + type.tsPlusSymbol.tsPlusTag === TsPlusSymbolTag.Getter || + type.tsPlusSymbol.tsPlusTag === TsPlusSymbolTag.GetterVariable || + type.tsPlusSymbol.tsPlusTag === TsPlusSymbolTag.StaticValue + ) +} + +export function getDeclarationForTsPlus(checker: TypeChecker, node: Node, symbol: TsPlusSymbol): Declaration | undefined { + switch (symbol.tsPlusTag) { + case TsPlusSymbolTag.StaticFunction: + case TsPlusSymbolTag.Fluent: { + if (isCallLikeExpression(node)) { + const signature = checker.getResolvedSignature(node) + if (signature && signature.declaration && signature.declaration.symbol) { + if ( + isTsPlusSymbol(signature.declaration.symbol) && + ( + signature.declaration.symbol.tsPlusTag === TsPlusSymbolTag.PipeableMacro || + signature.declaration.symbol.tsPlusTag === TsPlusSymbolTag.PipeableDeclaration + ) + ) { + return signature.declaration.symbol.tsPlusDeclaration; + } + if (isFunctionLikeDeclaration(signature.declaration)) { + return signature.declaration; + } + if (isTsPlusSignature(signature) && signature.tsPlusDeclaration) { + return signature.tsPlusDeclaration; + } + return signature.declaration + } + } + break; + } + case TsPlusSymbolTag.StaticValue: + case TsPlusSymbolTag.Getter: + case TsPlusSymbolTag.GetterVariable: { + return symbol.tsPlusDeclaration + } + } +} + +// TSPLUS EXTENSION END diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 629063de298..0e7cc0ffdc9 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -221,6 +221,7 @@ const libEntries: [string, string][] = [ ["esnext.weakref", "lib.es2021.weakref.d.ts"], ["decorators", "lib.decorators.d.ts"], ["decorators.legacy", "lib.decorators.legacy.d.ts"], + ["tsplus", "lib.tsplus.d.ts"] ]; /** @@ -334,6 +335,32 @@ export const commonOptionsWithBuild: CommandLineOption[] = [ category: Diagnostics.Command_line_Options, defaultValueDescription: false, }, + { + name: "tsPlusConfig", + type: "string", + }, + { + name: "tsPlusTypes", + type: "list", + element: { + name: "tsPlusTypes", + type: "string" + }, + }, + { + name: "transformers", + type: "list", + isTSConfigOnly: true, + element: { + name: "transformer", + type: "object" + }, + }, + { + name: "tsPlusEnabled", + type: "boolean", + defaultValueDescription: true, + }, { name: "watch", shortName: "w", @@ -3159,11 +3186,39 @@ interface ExtendsResult { compileOnSave?: boolean; extendedSourceFiles?: Set } + +function parseConfig( + json: any, + sourceFile: TsConfigSourceFile | undefined, + host: ParseConfigHost, + basePath: string, + configFileName: string | undefined, + resolutionStack: string[], + errors: Diagnostic[], + extendedConfigCache?: Map +): ParsedTsconfig { + const config = parseConfigOriginal( + json, + sourceFile, + host, + basePath, + configFileName, + resolutionStack, + errors, + extendedConfigCache + ) + if (!config.options) { + config.options = {} + } + config.options.lib = [...(config.options.lib ?? []), "lib.tsplus.d.ts"] + return config +} + /** * This *just* extracts options/include/exclude/files out of a config file. * It does *not* resolve the included files. */ -function parseConfig( +function parseConfigOriginal( json: any, sourceFile: TsConfigSourceFile | undefined, host: ParseConfigHost, diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 866997b79bc..b20d1f32473 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -7704,5 +7704,153 @@ "Compiler option '{0}' cannot be given an empty string.": { "category": "Error", "code": 18051 + }, + "A recursive call must be in a tail position of a 'tailRec' annotated function.": { + "category": "Error", + "code": 50000 + }, + "A recursive call cannot cross a function boundary in a 'tailRec' annotated function.": { + "category": "Error", + "code": 50001 + }, + "Parameter destructuring is not allowed in a 'tailRec' annotated function.": { + "category": "Error", + "code": 50002 + }, + "Invalid declaration annotated as 'tailRec'": { + "category": "Error", + "code": 50003 + }, + "Values of type 'any' or 'never' are not allowed in lazy function arguments, if the behaviour is intended use an arrow function.": { + "category": "Error", + "code": 50004 + }, + "'Pipeable' macro is only allowed in variable declarations.": { + "category": "Error", + "code": 50005 + }, + "Annotation of a static extension must have the form '@tsplus static [typename] [name]'.": { + "category": "Error", + "code": 50006 + }, + "Annotation of a fluent extension must have the form '@tsplus fluent [typename] [name] [priority?]'.": { + "category": "Error", + "code": 50007 + }, + "Annotation of a getter extension must have the form '@tsplus getter [typename] [name]'.": { + "category": "Error", + "code": 50008 + }, + "Annotation of an operator extension must have the form '@tsplus operator [typename] [symbol] [priority?]'.": { + "category": "Error", + "code": 50009 + }, + "Annotation of a type extension must have the form '@tsplus type [typename]'.": { + "category": "Error", + "code": 50010 + }, + "Annotation of a unify extension must have the form '@tsplus unify [typename]'.": { + "category": "Error", + "code": 50011 + }, + "Annotation of an index extension must have the form '@tsplus index [typename]'.": { + "category": "Error", + "code": 50012 + }, + "Annotation of a companion extension must have the form '@tsplus companion [typename]'.": { + "category": "Error", + "code": 50013 + }, + "Annotation of a macro must have the form '@tsplus macro [name]'.": { + "category": "Error", + "code": 50014 + }, + "The argument of an 'identity' macro must not be a spread element.": { + "category": "Error", + "code": 50015 + }, + "Invalid declaration annotated as 'pipeable'.": { + "category": "Error", + "code": 50016 + }, + "Annotation of a pipeable extension must have the form '@tsplus pipeable [typename] [name]'.": { + "category": "Error", + "code": 50017 + }, + "Declaration annotated as 'pipeable' is not assignable to its corresponding fluent declaration.": { + "category": "Error", + "code": 50018 + }, + "The first parameter of a fluent function cannot be a rest parameter.": { + "category": "Error", + "code": 50019 + }, + "The first parameter of a 'pipeable' annotated function cannot be a rest parameter.": { + "category": "Error", + "code": 50020 + }, + "Declaration of an extension must be exported.": { + "category": "Error", + "code": 50021 + }, + "Global import path cannot be relative.": { + "category": "Error", + "code": 50022 + }, + "Annotation of a derive extension on a type must have the form '@tsplus derive nominal'.": { + "category": "Error", + "code": 50023 + }, + "Annotation of a no-inherit extension must have the form '@tsplus no-inherit [typename]": { + "category": "Error", + "code": 50024 + }, + "A call to bind is only allowed in the context of a Do.": { + "category": "Error", + "code": 51000 + }, + "Cannot find a valid flatMap extension for type '{0}'.": { + "category": "Error", + "code": 51001 + }, + "Cannot find a valid map extension for type '{0}'.": { + "category": "Error", + "code": 51002 + }, + "A Do block must contain at least 1 bound value": { + "category": "Error", + "code": 51003 + }, + "Cannot find a valid '{0}' that can handle all the types of '{1}'": { + "category": "Error", + "code": 51004 + }, + "The first argument of a Do must be an arrow function": { + "category": "Error", + "code": 51005 + }, + "Cannot derive type '{0}' and no derivation rules are found.": { + "category": "Error", + "code": 60000 + }, + "Cannot derive type '{0}', you may want to try add an implicit for '{1}' in scope.": { + "category": "Error", + "code": 60001 + }, + "Cannot derive type '{0}', the derivation requires a lazy rule to be in scope as it is cyclic for the type '{1}'.": { + "category": "Error", + "code": 60002 + }, + "Failed derivation of type '{0}'.": { + "category": "Error", + "code": 60003 + }, + "Deriving type '{0}' {1}.": { + "category": "Error", + "code": 60004 + }, + "A non-derived parameter cannot follow a derived parameter.": { + "category": "Error", + "code": 60005 } } diff --git a/src/compiler/factory/nodeFactory.ts b/src/compiler/factory/nodeFactory.ts index 571ea07d47c..3067dbfdf92 100644 --- a/src/compiler/factory/nodeFactory.ts +++ b/src/compiler/factory/nodeFactory.ts @@ -439,6 +439,7 @@ import { TransformFlags, TrueLiteral, TryStatement, + TsPlusUniqueIdentifier, TupleTypeNode, Type, TypeAliasDeclaration, @@ -540,6 +541,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode createTempVariable, createLoopVariable, createUniqueName, + createTsPlusUniqueName, getGeneratedNameForNode, createPrivateIdentifier, createUniquePrivateName, @@ -1229,6 +1231,15 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode return createBaseGeneratedIdentifier(text, GeneratedIdentifierFlags.Unique | flags, prefix, suffix); } + // TSPLUS EXTENSION START + /** Create a unique name based on the supplied text. */ + function createTsPlusUniqueName(text: string, flags: GeneratedIdentifierFlags = GeneratedIdentifierFlags.None): TsPlusUniqueIdentifier { + const identifier = createUniqueName(text, flags); + (identifier as TsPlusUniqueIdentifier).tsPlusUniqueIdentifier = true; + return identifier as TsPlusUniqueIdentifier; + } + // TSPLUS EXTENSION END + /** Create a unique name generated for a node. */ // @api function getGeneratedNameForNode(node: Node | undefined, flags: GeneratedIdentifierFlags = 0, prefix?: string | GeneratedNamePart, suffix?: string): Identifier { @@ -4169,6 +4180,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode (node.exclamationToken ?? node.type ? TransformFlags.ContainsTypeScript : TransformFlags.None); node.jsDoc = undefined; // initialized by parser (JsDocContainer) + node.isTsPlusImplicit = false; return node; } @@ -4609,6 +4621,7 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode assertClause: AssertClause | undefined ): ImportDeclaration { const node = createBaseNode(SyntaxKind.ImportDeclaration); + node.isTsPlusGlobal = false; node.modifiers = asNodeArray(modifiers); node.importClause = importClause; node.moduleSpecifier = moduleSpecifier; @@ -5944,6 +5957,20 @@ export function createNodeFactory(flags: NodeFactoryFlags, baseFactory: BaseNode node.resolvedModules = undefined; node.classifiableNames = undefined; node.impliedNodeFormat = undefined; + node.tsPlusContext = { + type: [], + companion: [], + noInherit: [], + fluent: [], + pipeable: [], + operator: [], + pipeableOperator: [], + pipeableIndex: [], + static: [], + getter: [], + unify: [], + index: [], + }; return node; } @@ -7617,7 +7644,16 @@ function mergeEmitNode(sourceEmitNode: EmitNode, destEmitNode: EmitNode | undefi } = sourceEmitNode; if (!destEmitNode) destEmitNode = {} as EmitNode; // We are using `.slice()` here in case `destEmitNode.leadingComments` is pushed to later. - if (leadingComments) destEmitNode.leadingComments = addRange(leadingComments.slice(), destEmitNode.leadingComments); + if (leadingComments) { + // TSPLUS EXTENSION START + if (sourceEmitNode.tsPlusPipeableComment || sourceEmitNode.tsPlusLocationComment) { + destEmitNode.leadingComments = leadingComments.slice(); + } + else { + destEmitNode.leadingComments = addRange(leadingComments.slice(), destEmitNode.leadingComments); + } + // TSPLUS EXTENSION END + } if (trailingComments) destEmitNode.trailingComments = addRange(trailingComments.slice(), destEmitNode.trailingComments); if (flags) destEmitNode.flags = flags; if (internalFlags) destEmitNode.internalFlags = internalFlags & ~InternalEmitFlags.Immutable; diff --git a/src/compiler/factory/utilities.ts b/src/compiler/factory/utilities.ts index 61cbc7dffc7..978731c532d 100644 --- a/src/compiler/factory/utilities.ts +++ b/src/compiler/factory/utilities.ts @@ -44,6 +44,7 @@ import { first, firstOrUndefined, ForInitializer, + FunctionDeclaration, GeneratedIdentifier, GeneratedIdentifierFlags, GeneratedNamePart, @@ -74,6 +75,7 @@ import { ImportDeclaration, ImportEqualsDeclaration, InternalEmitFlags, + isArrowFunction, isAssignmentExpression, isAssignmentOperator, isBlock, @@ -85,6 +87,8 @@ import { isExclamationToken, isExportNamespaceAsDefaultDeclaration, isFileLevelUniqueName, + isFunctionDeclaration, + isFunctionExpression, isGeneratedIdentifier, isGeneratedPrivateIdentifier, isIdentifier, @@ -145,6 +149,7 @@ import { OuterExpressionKinds, outFile, ParenthesizedExpression, + ParameterDeclaration, parseNodeFactory, PlusToken, PostfixUnaryExpression, @@ -175,6 +180,7 @@ import { ThisTypeNode, Token, TypeNode, + VariableDeclaration, } from "../_namespaces/ts"; // Compound nodes @@ -1743,3 +1749,12 @@ export function flattenCommaList(node: Expression) { flattenCommaListWorker(node, expressions); return expressions; } + +export function getParametersOfFunctionOrVariableDeclaration(node: FunctionDeclaration | VariableDeclaration): readonly ParameterDeclaration[] | undefined { + if (isFunctionDeclaration(node)) { + return node.parameters; + } + if (node.initializer && (isArrowFunction(node.initializer) || isFunctionExpression(node.initializer))) { + return node.initializer.parameters + } +} diff --git a/src/compiler/hashing.ts b/src/compiler/hashing.ts new file mode 100644 index 00000000000..156169fa699 --- /dev/null +++ b/src/compiler/hashing.ts @@ -0,0 +1,376 @@ +namespace hashing { + // forked from https://github.com/frptools + + // Copyright 2014 Thom Chiovoloni, released under the MIT license. + + /// A random number generator based on the basic implementation of the PCG algorithm, + /// as described here: http://www.pcg-random.org/ + + // Adapted for TypeScript from Thom's original code at https://github.com/thomcc/pcg-random + + export function isDefined(value: T | undefined): value is T { + return value !== void 0; + } + + export function isIterable(value: object): value is Iterable { + return Symbol.iterator in value; + } + + export function isPlainObject(value: any) { + return value.constructor === Object || value.constructor == null; + } + + export function isPromiseLike(value: any) { + return !!value && typeof value.then === "function"; + } + + export function isReactElement(value: any) { + return !!(value && value.$$typeof); + } + + export function isNothing(value: T | null | undefined) { + return value === void 0 || value === null; + } + + const defaultIncHi = 0x14057b7e; + const defaultIncLo = 0xf767814f; + const MUL_HI = 0x5851f42d >>> 0; + const MUL_LO = 0x4c957f2d >>> 0; + const BIT_53 = 9007199254740992.0; + const BIT_27 = 134217728.0; + + export type PCGRandomState = [number, number, number, number]; + export type OptionalNumber = number | null | undefined; + + export class RandomPCG { + private _state: Int32Array; + constructor(seed?: OptionalNumber); + constructor(seedHi: OptionalNumber, seedLo: OptionalNumber, inc?: OptionalNumber); + constructor( + seedHi: OptionalNumber, + seedLo: OptionalNumber, + incHi: OptionalNumber, + incLo: OptionalNumber + ); + constructor( + seedHi?: OptionalNumber, + seedLo?: OptionalNumber, + incHi?: OptionalNumber, + incLo?: OptionalNumber + ) { + if (isNothing(seedLo) && isNothing(seedHi)) { + seedLo = (Math.random() * 0xffffffff) >>> 0; + seedHi = 0; + } else if (isNothing(seedLo)) { + seedLo = seedHi; + seedHi = 0; + } + if (isNothing(incLo) && isNothing(incHi)) { + // @ts-expect-error + incLo = this._state ? this._state[3] : defaultIncLo; + // @ts-expect-error + incHi = this._state ? this._state[2] : defaultIncHi; + } else if (isNothing(incLo)) { + incLo = incHi; + incHi = 0; + } + + this._state = new Int32Array([ + 0, + 0, + (incHi) >>> 0, + ((incLo || 0) | 1) >>> 0 + ]); + this._next(); + add64( + this._state, + this._state[0]!, + this._state[1]!, + (seedHi) >>> 0, + (seedLo) >>> 0 + ); + this._next(); + return this; + } + + getState(): PCGRandomState { + return [this._state[0]!, this._state[1]!, this._state[2]!, this._state[3]!]; + } + + setState(state: PCGRandomState) { + this._state[0] = state[0]; + this._state[1] = state[1]; + this._state[2] = state[2]; + this._state[3] = state[3] | 1; + } + + private _next() { + const oldHi = this._state[0]! >>> 0; + const oldLo = this._state[1]! >>> 0; + + mul64(this._state, oldHi, oldLo, MUL_HI, MUL_LO); + add64( + this._state, + this._state[0]!, + this._state[1]!, + this._state[2]!, + this._state[3]! + ); + + let xsHi = oldHi >>> 18; + let xsLo = ((oldLo >>> 18) | (oldHi << 14)) >>> 0; + xsHi = (xsHi ^ oldHi) >>> 0; + xsLo = (xsLo ^ oldLo) >>> 0; + const xorshifted = ((xsLo >>> 27) | (xsHi << 5)) >>> 0; + const rot = oldHi >>> 27; + const rot2 = ((-rot >>> 0) & 31) >>> 0; + return ((xorshifted >>> rot) | (xorshifted << rot2)) >>> 0; + } + + integer(max: number) { + if (!max) { + return this._next(); + } + max = max >>> 0; + if ((max & (max - 1)) === 0) { + return this._next() & (max - 1); + } + + let num = 0; + const skew = (-max >>> 0) % max >>> 0; + for (num = this._next(); num < skew; num = this._next()) { + // + } + return num % max; + } + + number() { + const hi = (this._next() & 0x03ffffff) * 1.0; + const lo = (this._next() & 0x07ffffff) * 1.0; + return (hi * BIT_27 + lo) / BIT_53; + } + } + + function mul64( + out: Int32Array, + aHi: number, + aLo: number, + bHi: number, + bLo: number + ): void { + let c1 = ((aLo >>> 16) * (bLo & 0xffff)) >>> 0; + let c0 = ((aLo & 0xffff) * (bLo >>> 16)) >>> 0; + + let lo = ((aLo & 0xffff) * (bLo & 0xffff)) >>> 0; + let hi = ((aLo >>> 16) * (bLo >>> 16) + ((c0 >>> 16) + (c1 >>> 16))) >>> 0; + + c0 = (c0 << 16) >>> 0; + lo = (lo + c0) >>> 0; + if (lo >>> 0 < c0 >>> 0) { + hi = (hi + 1) >>> 0; + } + + c1 = (c1 << 16) >>> 0; + lo = (lo + c1) >>> 0; + if (lo >>> 0 < c1 >>> 0) { + hi = (hi + 1) >>> 0; + } + + hi = (hi + Math.imul(aLo, bHi)) >>> 0; + hi = (hi + Math.imul(aHi, bLo)) >>> 0; + + out[0] = hi; + out[1] = lo; + } + + function add64( + out: Int32Array, + aHi: number, + aLo: number, + bHi: number, + bLo: number + ): void { + let hi = (aHi + bHi) >>> 0; + const lo = (aLo + bLo) >>> 0; + if (lo >>> 0 < aLo >>> 0) { + hi = (hi + 1) | 0; + } + out[0] = hi; + out[1] = lo; + } + + export interface HashOps { + readonly sym: unique symbol; + } + + export const Hash: HashOps = { + sym: Symbol.for("tsplus-compiler/Hash") as HashOps["sym"] + }; + + export interface Hash { + [Hash.sym](this: this): number; + } + + export function isHash(u: unknown): u is Hash { + return typeof u === "object" && u !== null && Hash.sym in u; + } + + export function optimize(n: number) { + return (n & 0xbfffffff) | ((n >>> 1) & 0x40000000); + } + + export function hashUnknown(arg: unknown): number { + return optimize(_hash(arg)); + } + + export function hashArray(arr: readonly unknown[]): number { + return optimize(_hashArray(arr)); + } + + export function hashArgs(...args: unknown[]): number; + export function hashArgs(): number { + let h = 5381; + for (let i = 0; i < arguments.length; i++) { + // eslint-disable-next-line prefer-rest-params + h = _combineHash(h, hashUnknown(arguments[i])); + } + return optimize(h); + } + + export function combine(a: number, b: number): number { + return optimize(_combineHash(a, b)); + } + + export function hashObject(value: object): number { + return optimize(_hashObject(value)); + } + + export function hashMiscRef(o: Object): number { + return optimize(_hashMiscRef(o)); + } + + export function hashIterator(it: Iterator): number { + return optimize(_hashIterator(it)); + } + + export function hashPlainObject(o: object): number { + return optimize(_hashPlainObject(o)); + } + + export function hashNumber(n: number): number { + return optimize(_hashNumber(n)); + } + + export function hashString(str: string): number { + return optimize(_hashString(str)); + } + + export function hashRandom(): number { + return optimize(randomInt()); + } + + function isZero(value: any): boolean { + return value === null || value === void 0 || value === false; + } + + const RANDOM = new RandomPCG((Math.random() * 4294967296) >>> 0); + const CACHE = new WeakMap(); + + function randomInt() { + return RANDOM.integer(0x7fffffff); + } + + function _hash(arg: any): number { + if (isZero(arg)) return 0; + if (typeof arg.valueOf === "function" && arg.valueOf !== Object.prototype.valueOf) { + arg = arg.valueOf(); + if (isZero(arg)) return 0; + } + switch (typeof arg) { + case "number": + return _hashNumber(arg); + case "string": + return _hashString(arg); + case "function": + return _hashMiscRef(arg); + case "object": + return _hashObject(arg); + case "boolean": + return arg === true ? 1 : 0; + case "symbol": + return _hashString(String(arg)); + case "bigint": + return _hashString(arg.toString(10)); + case "undefined": { + return 0; + } + } + } + + function _hashArray(arr: readonly any[]): number { + let h = 6151; + for (let i = 0; i < arr.length; i++) { + h = _combineHash(h, _hash(arr[i])); + } + return h; + } + + function _combineHash(a: number, b: number): number { + return (a * 53) ^ b; + } + + function _hashObject(value: object): number { + let h = CACHE.get(value); + if (isDefined(h)) return h; + if (isHash(value)) { + h = value[Hash.sym](); + } else { + h = hashRandom(); + } + CACHE.set(value, h); + return h; + } + + function _hashMiscRef(o: Object): number { + let h = CACHE.get(o); + if (isDefined(h)) return h; + h = randomInt(); + CACHE.set(o, h); + return h; + } + + function _hashIterator(it: Iterator): number { + let h = 6151; + let current: IteratorResult; + while (!(current = it.next()).done) { + h = _combineHash(h, hashUnknown(current.value)); + } + return h; + } + + function _hashPlainObject(o: object): number { + CACHE.set(o, randomInt()); + const keys = Object.keys(o).sort(); + let h = 12289; + for (let i = 0; i < keys.length; i++) { + h = _combineHash(h, _hashString(keys[i]!)); + h = _combineHash(h, hashUnknown((o as any)[keys[i]!])); + } + return h; + } + + function _hashNumber(n: number): number { + if (n !== n || n === Infinity) return 0; + let h = n | 0; + if (h !== n) h ^= n * 0xffffffff; + while (n > 0xffffffff) h ^= n /= 0xffffffff; + return n; + } + + function _hashString(str: string): number { + let h = 5381, + i = str.length; + while (i) h = (h * 33) ^ str.charCodeAt(--i); + return h; + } +} \ No newline at end of file diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index b4489107994..b5101c6f479 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -36,6 +36,7 @@ import { CatchClause, CharacterCodes, ClassDeclaration, + ClassDeclarationWithIdentifier, ClassElement, ClassExpression, ClassLikeDeclaration, @@ -44,6 +45,7 @@ import { CommentDirective, commentPragmas, CommentRange, + CompilerOptions, ComputedPropertyName, concatenate, ConditionalExpression, @@ -67,6 +69,7 @@ import { DiagnosticMessage, Diagnostics, DiagnosticWithDetachedLocation, + directorySeparator, DoStatement, DotDotDotToken, ElementAccessExpression, @@ -88,8 +91,12 @@ import { ExternalModuleReference, fileExtensionIs, fileExtensionIsOneOf, + filter, + find, findIndex, firstOrUndefined, + flatMap, + flatMapToMutable, forEach, ForEachChildNodes, ForInOrOfStatement, @@ -132,16 +139,19 @@ import { isArray, isAssignmentOperator, isAsyncModifier, + isClassDeclaration, isClassMemberModifier, isExportAssignment, isExportDeclaration, isExportModifier, isExpressionWithTypeArguments, isExternalModuleReference, + isFunctionDeclaration, isFunctionTypeNode, isIdentifierText, isImportDeclaration, isImportEqualsDeclaration, + isInterfaceDeclaration, isJSDocFunctionType, isJSDocNullableType, isJSDocReturnTag, @@ -153,13 +163,20 @@ import { isLiteralKind, isMetaProperty, isModifierKind, + isModuleBlock, + isModuleDeclaration, + isNamedImports, isNonNullExpression, isPrivateIdentifier, isSetAccessorDeclaration, + isStringLiteral, isStringOrNumericLiteralLike, isTaggedTemplateExpression, isTemplateLiteralKind, + isTypeAliasDeclaration, isTypeReferenceNode, + isVariableDeclaration, + isVariableStatement, IterationStatement, JSDoc, JSDocAllType, @@ -235,6 +252,7 @@ import { LiteralExpression, LiteralLikeNode, LiteralTypeNode, + mangleScopedPackageName, map, mapDefined, MappedTypeNode, @@ -285,6 +303,7 @@ import { ParenthesizedExpression, ParenthesizedTypeNode, PartiallyEmittedExpression, + pathIsRelative, perfLogger, PlusToken, PostfixUnaryExpression, @@ -311,7 +330,11 @@ import { ReadonlyKeyword, ReadonlyPragmaMap, ReadonlyTextRange, + removeExtension, ResolutionMode, + resolveModuleName, + resolvePackageNameToPackageJson, + resolvePath, RestTypeNode, ReturnStatement, SatisfiesExpression, @@ -337,6 +360,7 @@ import { supportedDeclarationExtensions, SwitchStatement, SyntaxKind, + sys, TaggedTemplateExpression, TemplateExpression, TemplateHead, @@ -365,6 +389,8 @@ import { TransformFlags, trimString, TryStatement, + TsPlusExtensionTag, + TsPlusPrioritizedExtensionTag, TupleTypeNode, TypeAliasDeclaration, TypeAssertion, @@ -383,14 +409,18 @@ import { UpdateExpression, VariableDeclaration, VariableDeclarationList, + VariableDeclarationWithIdentifier, VariableStatement, VoidExpression, WhileStatement, WithStatement, YieldExpression, + __String, } from "./_namespaces/ts"; import * as performance from "./_namespaces/ts.performance"; +const tsPlusExportedExtensionRegex = /^(fluent|getter|static|operator|index|unify|pipeable|type|companion).*/; + const enum SignatureFlags { None = 0, Yield = 1 << 0, @@ -412,6 +442,19 @@ let IdentifierConstructor: new (kind: SyntaxKind.Identifier, pos: number, end: n let PrivateIdentifierConstructor: new (kind: SyntaxKind.PrivateIdentifier, pos: number, end: number) => Node; let SourceFileConstructor: new (kind: SyntaxKind.SourceFile, pos: number, end: number) => Node; +interface TsPlusTypeDefinition { + definitionName: string + definitionKind: string + extensions: Array +} + +interface TsPlusExtensionDefinition { + kind: string + typeName: string + name?: string + priority?: string +} + /** * NOTE: You should not use this, it is only exported to support `createNode` in `~/src/deprecatedCompat/deprecations.ts`. * @@ -1315,7 +1358,7 @@ function setExternalModuleIndicator(sourceFile: SourceFile) { sourceFile.externalModuleIndicator = isFileProbablyExternalModule(sourceFile); } -export function createSourceFile(fileName: string, sourceText: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { +export function createSourceFile(fileName: string, sourceText: string, languageVersionOrOptions: ScriptTarget | CreateSourceFileOptions, setParentNodes = false, scriptKind?: ScriptKind, options?: CompilerOptions): SourceFile { tracing?.push(tracing.Phase.Parse, "createSourceFile", { path: fileName }, /*separateBeginAndEnd*/ true); performance.mark("beforeParse"); let result: SourceFile; @@ -1327,14 +1370,14 @@ export function createSourceFile(fileName: string, sourceText: string, languageV impliedNodeFormat: format } = typeof languageVersionOrOptions === "object" ? languageVersionOrOptions : ({ languageVersion: languageVersionOrOptions } as CreateSourceFileOptions); if (languageVersion === ScriptTarget.JSON) { - result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, ScriptKind.JSON, noop); + result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, ScriptKind.JSON, noop, options); } else { const setIndicator = format === undefined ? overrideSetExternalModuleIndicator : (file: SourceFile) => { file.impliedNodeFormat = format; return (overrideSetExternalModuleIndicator || setExternalModuleIndicator)(file); }; - result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind, setIndicator); + result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind, setIndicator, options); } perfLogger.logStopParseSourceFile(); @@ -1371,8 +1414,8 @@ export function isExternalModule(file: SourceFile): boolean { // from this SourceFile that are being held onto may change as a result (including // becoming detached from any SourceFile). It is recommended that this SourceFile not // be used once 'update' is called on it. -export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks = false): SourceFile { - const newSourceFile = IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks); +export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks = false, compilerOptions?: CompilerOptions): SourceFile { + const newSourceFile = IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks, compilerOptions); // Because new source file node is created, it may not have the flag PossiblyContainDynamicImport. This is the case if there is no new edit to add dynamic import. // We will manually port the flag to the new source file. (newSourceFile as Mutable).flags |= (sourceFile.flags & NodeFlags.PermanentlySetIncrementalFlags); @@ -1529,8 +1572,14 @@ namespace Parser { // Note: any errors at the end of the file that do not precede a regular node, should get // attached to the EOF token. let parseErrorBeforeNextFinishedNode = false; - - export function parseSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, syntaxCursor: IncrementalParser.SyntaxCursor | undefined, setParentNodes = false, scriptKind?: ScriptKind, setExternalModuleIndicatorOverride?: (file: SourceFile) => void): SourceFile { + + const tsPlusExternalTypeCache = new Map>() + const tsPlusResolvedPathsCache = new Map() + const tsPlusResolvedModuleCache = new Map() + let currentTsPlusTypes: TsPlusTypeDefinition[] | null = null; + let currentTsPlusFile: string | null = null; + + export function parseSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, syntaxCursor: IncrementalParser.SyntaxCursor | undefined, setParentNodes = false, scriptKind?: ScriptKind, setExternalModuleIndicatorOverride?: (file: SourceFile) => void, options?: CompilerOptions): SourceFile { scriptKind = ensureScriptKind(fileName, scriptKind); if (scriptKind === ScriptKind.JSON) { const result = parseJsonText(fileName, sourceText, languageVersion, syntaxCursor, setParentNodes); @@ -1544,10 +1593,14 @@ namespace Parser { return result; } + options && parseTsPlusExternalTypes(fileName, options); + initializeState(fileName, sourceText, languageVersion, syntaxCursor, scriptKind); const result = parseSourceFileWorker(languageVersion, setParentNodes, scriptKind, setExternalModuleIndicatorOverride || setExternalModuleIndicator); + currentTsPlusTypes = null; + clearState(); return result; @@ -1715,6 +1768,8 @@ namespace Parser { identifiers = undefined!; notParenthesizedArrow = undefined; topLevel = true; + currentTsPlusTypes = null; + currentTsPlusFile = null; } function parseSourceFileWorker(languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind: ScriptKind, setExternalModuleIndicator: (file: SourceFile) => void): SourceFile { @@ -1742,6 +1797,7 @@ namespace Parser { sourceFile.nodeCount = nodeCount; sourceFile.identifierCount = identifierCount; sourceFile.identifiers = identifiers; + collectTsPlusFileSymbols(sourceFile, sourceFile.statements); sourceFile.parseDiagnostics = attachFileToDiagnostics(parseDiagnostics, sourceFile); if (jsDocDiagnostics) { sourceFile.jsDocDiagnostics = attachFileToDiagnostics(jsDocDiagnostics, sourceFile); @@ -1758,6 +1814,305 @@ namespace Parser { } } + function getTsPlusExternalTypesPaths(fileName: string, options: CompilerOptions) { + if (!options.configFilePath) { + return []; + } + + let tsconfigPaths: string[] = []; + + if (tsPlusResolvedPathsCache.has(options.configFilePath)) { + tsconfigPaths = tsPlusResolvedPathsCache.get( + options.configFilePath + )!; + } else if (options.tsPlusTypes) { + for (const path of options.tsPlusTypes) { + if (pathIsRelative(path)) { + tsconfigPaths.push(resolvePath(options.configFilePath.split("/").slice(0, -1).join("/"), path)); + } else { + const resolvedModule = resolveModuleName(path, options.configFilePath, options, sys).resolvedModule ?? resolveModuleName(path, fileName, options, sys).resolvedModule; + if (resolvedModule) { + tsconfigPaths.push(resolvedModule.resolvedFileName); + break; + } + } + } + tsPlusResolvedPathsCache.set(options.configFilePath, tsconfigPaths); + } + + let packagePaths: string[] = []; + const packagePath: string = removeExtension( + fileName.split("node_modules").slice(-1)[0].substring(1), + ".d.ts" + ); + if (packagePath) { + let packageName: string; + if (packagePath.startsWith("@")) { + packageName = packagePath + .split(directorySeparator) + .slice(0, 2) + .join(directorySeparator); + } else { + packageName = packagePath + .split(directorySeparator) + .slice(0, 1)[0]; + } + + const packageCacheKey = packageName; + + if (tsPlusResolvedPathsCache.has(packageCacheKey)) { + packagePaths = tsPlusResolvedPathsCache.get(packageCacheKey)!; + } else { + const resolvedPackageJson = resolvePackageNameToPackageJson(packageName, options.configFilePath, options, sys, undefined); + if (resolvedPackageJson) { + const packageJsonText = sys.readFile(resolvePath(resolvedPackageJson.packageDirectory, "package.json")); + if (packageJsonText) { + const packageJson = JSON.parse(packageJsonText); + if (packageJson.tsPlusTypes) { + for (const path of toArray(packageJson.tsPlusTypes)) { + packagePaths.push(resolvePath(resolvedPackageJson.packageDirectory, path)); + } + } + } + } + + if (packagePath.startsWith("@")) { + packageName = mangleScopedPackageName( + packagePath + .split(directorySeparator) + .slice(0, 2) + .join(directorySeparator) + ); + } else { + packageName = packagePath + .split(directorySeparator) + .slice(0, 1)[0]; + } + const { resolvedModule } = resolveModuleName( + `@tsplus-types/${packageName}`, + options.configFilePath, + { ...options, resolveJsonModule: true }, + sys + ); + if (resolvedModule) { + packagePaths.push(resolvedModule.resolvedFileName); + } + + tsPlusResolvedPathsCache.set(packageCacheKey, packagePaths); + } + } + + return tsconfigPaths.concat(packagePaths); + } + + function parseTsPlusExternalTypes(fileName: string, options: CompilerOptions) { + const resolvedPaths = getTsPlusExternalTypesPaths(fileName, options); + if (!resolvedPaths || resolvedPaths.length === 0) { + return + } + for (const resolvedPath of resolvedPaths) { + let json = tsPlusExternalTypeCache.get(resolvedPath); + if (!json) { + const text = sys.readFile(resolvedPath); + if (text) { + json = JSON.parse(text); + } + } + if (!json) return; + for (const moduleName in json) { + const key = `${options.configFilePath ?? fileName}+${moduleName}`; + let resolvedModule = tsPlusResolvedModuleCache.get(key); + if (!resolvedModule) { + resolvedModule = resolveModuleName(moduleName, resolvedPath, options, sys).resolvedModule ?? resolveModuleName(moduleName, fileName, options, sys).resolvedModule; + tsPlusResolvedModuleCache.set(key, resolvedModule); + } + if (resolvedModule && resolvedModule.resolvedFileName === fileName) { + currentTsPlusTypes = json[moduleName]; + currentTsPlusFile = moduleName; + return; + } + } + } + } + + function addTsPlusTagsFromExternalTypes(declaration: VariableDeclaration | FunctionDeclaration | InterfaceDeclaration | ClassDeclaration | TypeAliasDeclaration, jsDocNode?: HasJSDoc): void { + if (currentTsPlusTypes !== null) { + if (declaration.name && declaration.name.kind === SyntaxKind.Identifier) { + if (!jsDocNode) { + jsDocNode = declaration; + } + if (jsDocNode.jsDoc && + jsDocNode.jsDoc[0] && + jsDocNode.jsDoc[0].tags && + jsDocNode.jsDoc[0].tags.find((tag) => tag.tagName.escapedText === 'tsplus')) { + return; + } + const extensions = currentTsPlusTypes.filter( + (type) => + (declaration.kind === SyntaxKind.VariableDeclaration ? (type.definitionKind === "const") + : declaration.kind === SyntaxKind.FunctionDeclaration ? (type.definitionKind === "function") + : declaration.kind === SyntaxKind.InterfaceDeclaration ? (type.definitionKind === "interface") + : declaration.kind === SyntaxKind.ClassDeclaration ? (type.definitionKind === "class") + : declaration.kind === SyntaxKind.TypeAliasDeclaration ? (type.definitionKind === "type") + : false) && + type.definitionName === (declaration.name as Identifier).escapedText.toString() + ).flatMap((definition) => definition.extensions); + const newTags: JSDocTag[] = [] + for (const extension of extensions) { + let comment = ""; + extension.kind && (comment += extension.kind); + extension.typeName && (comment += ` ${extension.typeName}`); + extension.name && (comment += ` ${extension.name}`); + extension.priority && (comment += ` ${extension.priority}`); + newTags.push(factory.createJSDocUnknownTag(factory.createIdentifier("tsplus"), comment)); + } + newTags.push(factory.createJSDocUnknownTag(factory.createIdentifier("tsplus"), `location "${currentTsPlusFile}"`)) + if (jsDocNode.jsDoc && jsDocNode.jsDoc[0]) { + const jsDocTags = Array.from(jsDocNode.jsDoc[0].tags ?? []).concat(newTags); + // @ts-expect-error + jsDocNode.jsDoc[0].tags = factory.createNodeArray(jsDocTags); + // @ts-expect-error + jsDocNode.jsDocCache = jsDocTags; + } + else { + jsDocNode.jsDoc = [factory.createJSDocComment(undefined, newTags)]; + // @ts-expect-error + jsDocNode.jsDocCache = newTags; + } + } + } + } + + function collectTsPlusFileSymbols(file: SourceFile, statements: NodeArray, collectTypesIfNotExported = false) { + for (const statement of statements) { + if (isModuleDeclaration(statement) && statement.body && isModuleBlock(statement.body)) { + if (statement.name.kind === SyntaxKind.Identifier && (statement.name as Identifier).escapedText === "global" as __String) { + collectTsPlusFileSymbols(file, statement.body.statements, true); + } + else if (statement.modifiers && findIndex(statement.modifiers, t => t.kind === SyntaxKind.ExportKeyword) !== -1) { + collectTsPlusFileSymbols(file, statement.body.statements, true) + } + else { + collectTsPlusFileSymbols(file, statement.body.statements, collectTypesIfNotExported); + } + } + if ( + (isInterfaceDeclaration(statement) || isTypeAliasDeclaration(statement) || isClassDeclaration(statement)) && + (collectTypesIfNotExported || hasModifierOfKind(statement, SyntaxKind.ExportKeyword)) + ) { + if (statement.tsPlusTypeTags && statement.tsPlusTypeTags.length > 0) { + file.tsPlusContext.type.push(statement); + } + if (statement.tsPlusNoInheritTags && statement.tsPlusNoInheritTags.length > 0) { + file.tsPlusContext.noInherit.push(statement); + } + if (statement.tsPlusCompanionTags && statement.tsPlusCompanionTags.length > 0) { + file.tsPlusContext.companion.push(statement); + } + if (isClassDeclaration(statement) && statement.name && statement.tsPlusStaticTags && statement.tsPlusStaticTags.length > 0) { + file.tsPlusContext.static.push(statement as ClassDeclarationWithIdentifier); + } + } + if( + (isVariableStatement(statement) || isFunctionDeclaration(statement) || isInterfaceDeclaration(statement) || isTypeAliasDeclaration(statement) || isClassDeclaration(statement)) && + hasModifierOfKind(statement, SyntaxKind.ExportKeyword) + ) { + if (isVariableStatement(statement) && statement.declarationList.declarations.length === 1) { + const declaration = statement.declarationList.declarations[0]; + if (declaration.name && declaration.name.kind === SyntaxKind.Identifier) { + if (declaration.tsPlusFluentTags && declaration.tsPlusFluentTags.length > 0) { + file.tsPlusContext.fluent.push(declaration as VariableDeclarationWithIdentifier); + } + if (declaration.tsPlusPipeableTags && declaration.tsPlusPipeableTags.length > 0) { + file.tsPlusContext.pipeable.push(declaration as VariableDeclarationWithIdentifier); + } + if (declaration.tsPlusOperatorTags && declaration.tsPlusOperatorTags.length > 0) { + file.tsPlusContext.operator.push(declaration as VariableDeclarationWithIdentifier); + } + if (declaration.tsPlusPipeableOperatorTags && declaration.tsPlusPipeableOperatorTags.length > 0) { + file.tsPlusContext.pipeableOperator.push(declaration as VariableDeclarationWithIdentifier); + } + if (declaration.tsPlusStaticTags && declaration.tsPlusStaticTags.length > 0) { + file.tsPlusContext.static.push(declaration as VariableDeclarationWithIdentifier); + } + if (declaration.tsPlusGetterTags && declaration.tsPlusGetterTags.length > 0) { + file.tsPlusContext.getter.push(declaration as VariableDeclarationWithIdentifier); + } + if (declaration.tsPlusIndexTags && declaration.tsPlusIndexTags.length > 0) { + file.tsPlusContext.index.push(declaration as VariableDeclarationWithIdentifier); + } + if (declaration.tsPlusPipeableIndexTags && declaration.tsPlusPipeableIndexTags.length > 0) { + file.tsPlusContext.pipeableIndex.push(declaration as VariableDeclarationWithIdentifier); + } + } + } + if (isFunctionDeclaration(statement) && statement.name) { + if (statement.tsPlusFluentTags && statement.tsPlusFluentTags.length > 0) { + file.tsPlusContext.fluent.push(statement); + } + if (statement.tsPlusPipeableTags && statement.tsPlusPipeableTags.length > 0) { + file.tsPlusContext.pipeable.push(statement); + } + if (statement.tsPlusOperatorTags && statement.tsPlusOperatorTags.length > 0) { + file.tsPlusContext.operator.push(statement); + } + if (statement.tsPlusPipeableOperatorTags && statement.tsPlusPipeableOperatorTags.length > 0) { + file.tsPlusContext.pipeableOperator.push(statement); + } + if (statement.tsPlusStaticTags && statement.tsPlusStaticTags.length > 0) { + file.tsPlusContext.static.push(statement); + } + if (statement.tsPlusGetterTags && statement.tsPlusGetterTags.length > 0) { + file.tsPlusContext.getter.push(statement); + } + if (statement.tsPlusUnifyTags && statement.tsPlusUnifyTags.length > 0) { + file.tsPlusContext.unify.push(statement); + } + if (statement.tsPlusIndexTags && statement.tsPlusIndexTags.length > 0) { + file.tsPlusContext.index.push(statement); + } + if (statement.tsPlusPipeableIndexTags && statement.tsPlusPipeableIndexTags.length > 0) { + file.tsPlusContext.pipeableIndex.push(statement); + } + } + } + else { + if (!collectTypesIfNotExported) { + if (isInterfaceDeclaration(statement) || isTypeAliasDeclaration(statement)) { + checkTsPlusNonExportedExtension(statement) + } + if (isClassDeclaration(statement)) { + checkTsPlusNonExportedExtension(statement) + } + } + if (isFunctionDeclaration(statement)) { + checkTsPlusNonExportedExtension(statement) + } + if (isVariableStatement(statement) && statement.declarationList.declarations.length === 1) { + checkTsPlusNonExportedVariableExtension(statement, statement.declarationList.declarations[0]); + } + } + } + } + function hasTsPlusExportedExtensionTags(statement: InterfaceDeclaration | ClassDeclaration | TypeAliasDeclaration | FunctionDeclaration | VariableStatement) { + for (const tag of flatMap(statement.jsDoc, (doc) => doc.tags)) { + if (tag.tagName.escapedText === "tsplus" && typeof tag.comment === "string" && tsPlusExportedExtensionRegex.test(tag.comment)) { + return true; + } + } + return false; + } + function checkTsPlusNonExportedExtension(declaration: InterfaceDeclaration | ClassDeclaration | TypeAliasDeclaration | FunctionDeclaration): void { + if (hasTsPlusExportedExtensionTags(declaration)) { + parseErrorAt(declaration.name!.pos + 1, declaration.name!.end, Diagnostics.Declaration_of_an_extension_must_be_exported); + } + } + function checkTsPlusNonExportedVariableExtension(statement: VariableStatement, declaration: VariableDeclaration): void { + if (hasTsPlusExportedExtensionTags(statement)) { + parseErrorAt(declaration.name!.pos + 1, declaration.name!.end, Diagnostics.Declaration_of_an_extension_must_be_exported); + } + } + function withJSDoc(node: T, hasJSDoc: boolean): T { return hasJSDoc ? addJSDocComment(node) : node; } @@ -3939,6 +4294,9 @@ namespace Parser { hasJSDoc ); topLevel = savedTopLevel; + if (node.jsDoc && find(node.jsDoc, (doc) => !!doc.tags && !!find(doc.tags, (tag) => tag.tagName.escapedText === "tsplus" && tag.comment === "auto"))) { + node.isAuto = true; + } return node; } @@ -3996,6 +4354,19 @@ namespace Parser { setYieldContext(savedYieldContext); setAwaitContext(savedAwaitContext); + if (parameters) { + let seenAutoParameter = false + for (let i = 0; i < parameters.length; i++) { + const param = parameters[i]; + if (param.isAuto) { + seenAutoParameter = true; + } + if (!param.isAuto && seenAutoParameter) { + parseErrorAt(param.pos, param.end, Diagnostics.A_non_derived_parameter_cannot_follow_a_derived_parameter); + } + } + } + return parameters; } @@ -4047,7 +4418,21 @@ namespace Parser { const node = kind === SyntaxKind.CallSignature ? factory.createCallSignature(typeParameters, parameters, type) : factory.createConstructSignature(typeParameters, parameters, type); - return withJSDoc(finishNode(node, pos), hasJSDoc); + const finished = withJSDoc(finishNode(node, pos), hasJSDoc); + if (kind === SyntaxKind.CallSignature && finished.jsDoc) { + (finished as CallSignatureDeclaration).tsPlusMacroTags = undefinedIfZeroLength(flatMapToMutable(finished.jsDoc, (doc) => flatMap(doc.tags, (tag) => { + if (tag.tagName.escapedText === "tsplus" && typeof tag.comment === "string" && tag.comment.startsWith("macro")) { + const [_, target] = tag.comment.split(" "); + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_macro_must_have_the_form_tsplus_macro_name); + return []; + } + return [target]; + } + return []; + }))) + } + return finished; } function isIndexSignature(): boolean { @@ -7417,7 +7802,172 @@ namespace Parser { const declarationList = parseVariableDeclarationList(/*inForStatementInitializer*/ false); parseSemicolon(); const node = factory.createVariableStatement(modifiers, declarationList); - return withJSDoc(finishNode(node, pos), hasJSDoc); + const finished = withJSDoc(finishNode(node, pos), hasJSDoc); + if (finished.declarationList.declarations.length === 1) { + addTsPlusTagsFromExternalTypes(finished.declarationList.declarations[0], finished); + addTsPlusValueTags(finished.declarationList.declarations[0], finished.jsDoc); + } + return finished; + } + + function parseTsPlusExtensionTag(tagType: string, typeName: string | undefined, functionName: string | undefined, priority: string | undefined): TsPlusPrioritizedExtensionTag | undefined { + if (!typeName || !functionName) { + return undefined; + } + let parsedPriority: number; + if (priority) { + const n = Number.parseFloat(priority); + if (Number.isNaN(n)) { + return undefined; + } + if (n >= 0) { + parsedPriority = n; + } + } + parsedPriority ||= 0; + return { tagType, target: typeName, name: functionName, priority: parsedPriority }; + } + + function undefinedIfZeroLength(as: A[]): A[] | undefined { + if (as.length > 0) { + return as; + } + return undefined; + } + function addTsPlusValueTags(declaration: FunctionDeclaration | VariableDeclaration, jsDoc: JSDoc[] | undefined): void { + const deriveTags: string[] = []; + const fluentTags: TsPlusPrioritizedExtensionTag[] = []; + const staticTags: TsPlusExtensionTag[] = []; + const pipeableTags: TsPlusPrioritizedExtensionTag[] = []; + const pipeableOperatorTags: TsPlusPrioritizedExtensionTag[] = []; + const getterTags: TsPlusExtensionTag[] = []; + const operatorTags: TsPlusPrioritizedExtensionTag[] = []; + const unifyTags: string[] = []; + const macroTags: string[] = []; + const indexTags: string[] = []; + const pipeableIndexTags: string[] = []; + let isImplicit = false; + + for (const doc of jsDoc ?? []) { + if (doc.tags) { + for (const tag of doc.tags) { + if (tag.tagName.escapedText === "tsplus" && typeof tag.comment === "string") { + const [tagType, target, name, priority] = tag.comment.split(" ") + switch(tagType) { + case "derive": { + deriveTags.push(tag.comment); + break; + } + case "fluent": { + const parsedTag = parseTsPlusExtensionTag(tagType, target, name, priority); + if (!parsedTag) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_fluent_extension_must_have_the_form_tsplus_fluent_typename_name_priority); + break; + } + fluentTags.push(parsedTag); + break; + } + case "static": { + if (!target || !name) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_static_extension_must_have_the_form_tsplus_static_typename_name); + break; + } + staticTags.push({ tagType, target, name }); + break; + } + case "pipeable": { + const parsedTag = parseTsPlusExtensionTag(tagType, target, name, priority); + if(!parsedTag) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_pipeable_extension_must_have_the_form_tsplus_pipeable_typename_name); + break; + } + pipeableTags.push(parsedTag); + break; + } + case "getter": { + if (!target || !name) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_getter_extension_must_have_the_form_tsplus_getter_typename_name); + break; + } + getterTags.push({ tagType, target, name }); + break; + } + case "operator": { + const parsedTag = parseTsPlusExtensionTag(tagType, target, name, priority); + if (!parsedTag) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_an_operator_extension_must_have_the_form_tsplus_operator_typename_symbol_priority); + break; + } + operatorTags.push(parsedTag); + break; + } + case "pipeable-operator": { + const parsedTag = parseTsPlusExtensionTag(tagType, target, name, priority); + if (!parsedTag) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_an_operator_extension_must_have_the_form_tsplus_operator_typename_symbol_priority); + break; + } + pipeableOperatorTags.push(parsedTag); + break; + } + case "implicit": { + if (!tag.comment.includes("local")) { + isImplicit = true; + } + break; + } + case "macro": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_macro_must_have_the_form_tsplus_macro_name); + break; + } + macroTags.push(target); + break; + } + case "unify": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_unify_extension_must_have_the_form_tsplus_unify_typename); + break; + } + unifyTags.push(target); + break; + } + case "index": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_an_index_extension_must_have_the_form_tsplus_index_typename); + break; + } + indexTags.push(target); + break; + } + case "pipeable-index": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_an_index_extension_must_have_the_form_tsplus_index_typename); + break; + } + pipeableIndexTags.push(target); + break; + } + } + } + } + } + } + + (declaration as Mutable).tsPlusDeriveTags = undefinedIfZeroLength(deriveTags); + (declaration as Mutable).tsPlusFluentTags = undefinedIfZeroLength(fluentTags); + (declaration as Mutable).tsPlusStaticTags = undefinedIfZeroLength(staticTags); + (declaration as Mutable).tsPlusPipeableTags = undefinedIfZeroLength(pipeableTags); + (declaration as Mutable).tsPlusGetterTags = undefinedIfZeroLength(getterTags); + (declaration as Mutable).tsPlusOperatorTags = undefinedIfZeroLength(operatorTags); + (declaration as Mutable).tsPlusPipeableOperatorTags = undefinedIfZeroLength(pipeableOperatorTags); + (declaration as Mutable).tsPlusMacroTags = undefinedIfZeroLength(macroTags); + (declaration as Mutable).tsPlusUnifyTags = undefinedIfZeroLength(unifyTags); + (declaration as Mutable).tsPlusIndexTags = undefinedIfZeroLength(indexTags); + (declaration as Mutable).tsPlusPipeableIndexTags = undefinedIfZeroLength(pipeableIndexTags); + if (isVariableDeclaration(declaration)) { + declaration.isTsPlusImplicit = isImplicit; + } } function parseFunctionDeclaration(pos: number, hasJSDoc: boolean, modifiers: NodeArray | undefined): FunctionDeclaration { @@ -7436,7 +7986,10 @@ namespace Parser { const body = parseFunctionBlockOrSemicolon(isGenerator | isAsync, Diagnostics.or_expected); setAwaitContext(savedAwaitContext); const node = factory.createFunctionDeclaration(modifiers, asteriskToken, name, typeParameters, parameters, type, body); - return withJSDoc(finishNode(node, pos), hasJSDoc); + const finished = withJSDoc(finishNode(node, pos), hasJSDoc); + addTsPlusTagsFromExternalTypes(finished); + addTsPlusValueTags(finished, finished.jsDoc); + return finished; } function parseConstructorName() { @@ -7844,7 +8397,74 @@ namespace Parser { const node = kind === SyntaxKind.ClassDeclaration ? factory.createClassDeclaration(modifiers, name, typeParameters, heritageClauses, members) : factory.createClassExpression(modifiers, name, typeParameters, heritageClauses, members); - return withJSDoc(finishNode(node, pos), hasJSDoc); + const finished = withJSDoc(finishNode(node, pos), hasJSDoc); + if (isClassDeclaration(finished)) { + addTsPlusTagsFromExternalTypes(finished); + if (finished.jsDoc) { + const typeTags: string[] = []; + const companionTags: string[] = []; + const staticTags: TsPlusExtensionTag[] = []; + const deriveTags: string[] = []; + const noInheritTags: string[] = []; + for (const doc of finished.jsDoc) { + if (doc.tags) { + for (const tag of doc.tags) { + if (tag.tagName.escapedText === "tsplus" && typeof tag.comment === "string") { + const [tagName, target, name] = tag.comment.split(" "); + switch (tagName) { + case "type": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_type_extension_must_have_the_form_tsplus_type_typename); + break; + } + typeTags.push(target); + break; + } + case "companion": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_companion_extension_must_have_the_form_tsplus_companion_typename); + break; + } + companionTags.push(target); + break; + } + case "static": { + if (!target || !name) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_static_extension_must_have_the_form_tsplus_static_typename_name); + break; + } + staticTags.push({ tagType: tagName, target, name }); + break + } + case "derive": { + if (!target || target !== "nominal") { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_derive_extension_on_a_type_must_have_the_form_tsplus_derive_nominal); + break; + } + deriveTags.push(target); + break + } + case "no-inherit": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_no_inherit_extension_must_have_the_form_tsplus_no_inherit_typename); + break; + } + noInheritTags.push(target); + break; + } + } + } + } + } + } + (finished as Mutable).tsPlusTypeTags = undefinedIfZeroLength(typeTags); + (finished as Mutable).tsPlusCompanionTags = undefinedIfZeroLength(companionTags); + (finished as Mutable).tsPlusStaticTags = undefinedIfZeroLength(staticTags); + (finished as Mutable).tsPlusDeriveTags = undefinedIfZeroLength(deriveTags); + (finished as Mutable).tsPlusNoInheritTags = undefinedIfZeroLength(noInheritTags); + } + } + return finished; } function parseNameOfClassDeclarationOrExpression(): Identifier | undefined { @@ -7912,7 +8532,62 @@ namespace Parser { const heritageClauses = parseHeritageClauses(); const members = parseObjectTypeMembers(); const node = factory.createInterfaceDeclaration(modifiers, name, typeParameters, heritageClauses, members); - return withJSDoc(finishNode(node, pos), hasJSDoc); + const finished = withJSDoc(finishNode(node, pos), hasJSDoc); + addTsPlusTagsFromExternalTypes(finished); + if (finished.jsDoc) { + const typeTags: string[] = []; + const companionTags: string[] = []; + const deriveTags: string[] = []; + const noInheritTags: string[] = []; + for (const doc of finished.jsDoc) { + if (doc.tags) { + for (const tag of doc.tags) { + if (tag.tagName.escapedText === "tsplus" && typeof tag.comment === "string") { + const [tagName, target] = tag.comment.split(" "); + switch (tagName) { + case "type": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_type_extension_must_have_the_form_tsplus_type_typename); + break; + } + typeTags.push(target); + break; + } + case "companion": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_companion_extension_must_have_the_form_tsplus_companion_typename); + break; + } + companionTags.push(target); + break; + } + case "derive": { + if (!target || target !== "nominal") { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_derive_extension_on_a_type_must_have_the_form_tsplus_derive_nominal); + break; + } + deriveTags.push(target); + break; + } + case "no-inherit": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_no_inherit_extension_must_have_the_form_tsplus_no_inherit_typename); + break; + } + noInheritTags.push(target); + break; + } + } + } + } + } + } + (finished as Mutable).tsPlusTypeTags = undefinedIfZeroLength(typeTags); + (finished as Mutable).tsPlusCompanionTags = undefinedIfZeroLength(companionTags); + (finished as Mutable).tsPlusDeriveTags = undefinedIfZeroLength(deriveTags); + (finished as Mutable).tsPlusNoInheritTags = undefinedIfZeroLength(noInheritTags); + } + return finished; } function parseTypeAliasDeclaration(pos: number, hasJSDoc: boolean, modifiers: NodeArray | undefined): TypeAliasDeclaration { @@ -7923,7 +8598,52 @@ namespace Parser { const type = token() === SyntaxKind.IntrinsicKeyword && tryParse(parseKeywordAndNoDot) || parseType(); parseSemicolon(); const node = factory.createTypeAliasDeclaration(modifiers, name, typeParameters, type); - return withJSDoc(finishNode(node, pos), hasJSDoc); + const finished = withJSDoc(finishNode(node, pos), hasJSDoc); + addTsPlusTagsFromExternalTypes(finished); + if (finished.jsDoc) { + const typeTags: string[] = []; + const companionTags: string[] = []; + const noInheritTags: string[] = []; + for (const doc of finished.jsDoc) { + if (doc.tags) { + for (const tag of doc.tags) { + if (tag.tagName.escapedText === "tsplus" && typeof tag.comment === "string") { + const [tagName, target] = tag.comment.split(" "); + switch (tagName) { + case "type": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_type_extension_must_have_the_form_tsplus_type_typename); + break; + } + typeTags.push(target); + break; + } + case "companion": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_companion_extension_must_have_the_form_tsplus_companion_typename); + break; + } + companionTags.push(target); + break; + } + case "no-inherit": { + if (!target) { + parseErrorAt(tag.pos, tag.end - 1, Diagnostics.Annotation_of_a_no_inherit_extension_must_have_the_form_tsplus_no_inherit_typename); + break; + } + noInheritTags.push(target) + break; + } + } + } + } + } + } + (finished as Mutable).tsPlusTypeTags = undefinedIfZeroLength(typeTags); + (finished as Mutable).tsPlusCompanionTags = undefinedIfZeroLength(companionTags); + (finished as Mutable).tsPlusNoInheritTags = undefinedIfZeroLength(noInheritTags); + } + return finished; } // In an ambient declaration, the grammar only allows integer literals as initializers. @@ -8091,7 +8811,12 @@ namespace Parser { parseSemicolon(); const node = factory.createImportDeclaration(modifiers, importClause, moduleSpecifier, assertClause); - return withJSDoc(finishNode(node, pos), hasJSDoc); + const finished = withJSDoc(finishNode(node, pos), hasJSDoc); + if (finished.jsDoc && finished.importClause && finished.importClause.namedBindings && isNamedImports(finished.importClause.namedBindings) && isStringLiteral(finished.moduleSpecifier)) { + const tags = flatMap(finished.jsDoc, (doc) => filter(doc.tags, (tag) => tag.tagName.escapedText === "tsplus" && typeof tag.comment === "string" && tag.comment.startsWith("global"))); + (finished as Mutable).isTsPlusGlobal = tags.length > 0; + } + return finished; } function parseAssertEntry() { @@ -9480,7 +10205,7 @@ namespace Parser { } namespace IncrementalParser { - export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile { + export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean, compilerOptions?: CompilerOptions): SourceFile { aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive); checkChangeRange(sourceFile, newText, textChangeRange, aggressiveChecks); @@ -9556,7 +10281,7 @@ namespace IncrementalParser { // inconsistent tree. Setting the parents on the new tree should be very fast. We // will immediately bail out of walking any subtrees when we can see that their parents // are already correct. - const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, sourceFile.scriptKind, sourceFile.setExternalModuleIndicator); + const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, sourceFile.scriptKind, sourceFile.setExternalModuleIndicator, compilerOptions); result.commentDirectives = getNewCommentDirectives( sourceFile.commentDirectives, result.commentDirectives, diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 766d0292a42..7decc95bbae 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1,3 +1,6 @@ +import { transformTailRec } from "./transformers/tailRec"; +import { transformTsPlus } from "./transformers/tsplus"; +import { transformTsPlusDeclaration } from "./transformers/tsplusDeclaration"; import * as ts from "./_namespaces/ts"; import { __String, @@ -321,6 +324,11 @@ import { WriteFileCallbackData, writeFileEnsuringDirectories, zipToModeAwareCache, + Bundle, + EmitTransformers, + ExternalTransformers, + toArray, + TransformerFactory, } from "./_namespaces/ts"; import * as performance from "./_namespaces/ts.performance"; @@ -407,7 +415,7 @@ export function createGetSourceFile( } text = ""; } - return text !== undefined ? createSourceFile(fileName, text, languageVersionOrOptions, setParentNodes) : undefined; + return text !== undefined ? createSourceFile(fileName, text, languageVersionOrOptions, setParentNodes, undefined, getCompilerOptions()) : undefined; }; } @@ -2611,6 +2619,7 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg if (result) return result; } + const checker = getTypeChecker(); // Create the emit resolver outside of the "emitTime" tracking code below. That way // any cost associated with it (like type checking) are appropriate associated with // the type-checking counter. @@ -2619,15 +2628,112 @@ export function createProgram(rootNamesOrOptions: readonly string[] | CreateProg // This is because in the -out scenario all files need to be emitted, and therefore all // files need to be type checked. And the way to specify that all files need to be type // checked is to not pass the file to getEmitResolver. - const emitResolver = getTypeChecker().getEmitResolver(outFile(options) ? undefined : sourceFile, cancellationToken); + const emitResolver = checker.getEmitResolver(outFile(options) ? undefined : sourceFile, cancellationToken); performance.mark("beforeEmit"); + const resolveBaseDir = process.cwd() + + const beforeTransformers: TransformerFactory[] = []; + const afterTransformers: TransformerFactory[] = []; + const afterDeclarationsTransformers: TransformerFactory[] = []; + + const requireStack: string[] = []; + + let hasTsNode = false; + + // @ts-expect-error + if (options.transformers && !global['ts-plus']?.resolvingTransformer) { + for (const entry of options.transformers) { + const { name, position, ...cleanConfig } = entry + if (!name) { + continue; + } + if (name.match(/\.ts$/) && !hasTsNode) { + try { + require('ts-node').register({ + transpileOnly: true, + compilerOptions: { + target: "ES2018", + esModuleInterop: true, + module: "commonjs" + } + }) + hasTsNode = true + } catch (e) { + if (e.code === "MODULE_NOT_FOUND") { + throw new Error("Cannot use a typescript-based transformer without ts-node installed. Either add ts-node as a (dev)-dependency or install gloablly."); + } else { + throw e; + } + } + } + const modulePath = require.resolve(name, { paths: [resolveBaseDir] }); + + if (requireStack.includes(modulePath)) continue; + + requireStack.push(modulePath); + // @ts-expect-error + global['ts-plus'] = global['ts-plus'] ?? {}; + // @ts-expect-error + global['ts-plus'].resolvingTransformer = true; + const commonJsModule = require(modulePath); + // @ts-expect-error + global['ts-plus'].resolvingTransformer = false; + requireStack.pop(); + const factoryModule = (typeof commonJsModule === 'function' ? { default: commonJsModule } : commonJsModule); + const factory = factoryModule.default; + if (!factory) { + throw new Error(`tsconfig.json > transformers: "${entry.name}" does not have export "default": ` + require("util").inspect(factoryModule)); + } + if (typeof factory !== "function") { + throw new Error(`tsconfig.json > transformers: "${entry.name} export "default" is not a plugin: ` + require("util").inspect(factory)); + } + const transformer: ExternalTransformers = factory(program, cleanConfig); + if (typeof transformer === "function") { + if (position && position === 'after') { + afterTransformers.push(transformer); + } + else if (position && position === 'afterDeclaration') { + afterDeclarationsTransformers.push(transformer as TransformerFactory); + } + else { + beforeTransformers.push(transformer); + } + } else { + transformer.before && beforeTransformers.push(...toArray(transformer.before)); + transformer.after && afterTransformers.push(...toArray(transformer.after)); + transformer.afterDeclarations && afterDeclarationsTransformers.push(...toArray(transformer.afterDeclarations)); + } + } + } + + customTransformers = { + before: [ + ...(customTransformers?.before ?? []), + ...beforeTransformers + ], + after: [ + ...(customTransformers?.after ?? []), + ...afterTransformers + ], + afterDeclarations: [ + ...(customTransformers?.afterDeclarations ?? []), + ...afterDeclarationsTransformers + ] + } + + const emitTransformers = getTransformers(options, customTransformers, emitOnly); + const patchedTransformers: EmitTransformers = { + scriptTransformers: [transformTsPlus(checker, options, host), transformTailRec(checker, options, host), ...emitTransformers.scriptTransformers], + declarationTransformers: [transformTsPlusDeclaration(checker, options, host), ...emitTransformers.declarationTransformers] + } + const emitResult = emitFiles( emitResolver, getEmitHost(writeFileCallback), sourceFile, - getTransformers(options, customTransformers, emitOnly), + patchedTransformers, emitOnly, /*onlyBuildInfo*/ false, forceDtsEmit diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index c9c8fb39df3..22ba92fac3b 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -568,7 +568,7 @@ export function transformDeclarations(context: TransformationContext) { } else { const statements = visitNodes(node.statements, visitDeclarationStatements, isStatement); - combinedStatements = setTextRange(factory.createNodeArray(transformAndReplaceLatePaintedStatements(statements)), node.statements); + combinedStatements = setTextRange(factory.createNodeArray([...node.tsPlusGlobalImports ?? [], ...transformAndReplaceLatePaintedStatements(statements)]), node.statements); refs.forEach(referenceVisitor); emittedImports = filter(combinedStatements, isAnyImportSyntax); if (isExternalModule(node) && (!resultHasExternalModuleIndicator || (needsScopeFixMarker && !resultHasScopeMarker))) { diff --git a/src/compiler/transformers/module/module.ts b/src/compiler/transformers/module/module.ts index 311dcfe85a5..0d35b43ac27 100644 --- a/src/compiler/transformers/module/module.ts +++ b/src/compiler/transformers/module/module.ts @@ -12,6 +12,7 @@ import { chainBundle, ClassDeclaration, collectExternalModuleInfo, + concatenate, Debug, Declaration, DestructuringAssignment, @@ -88,6 +89,7 @@ import { isLocalName, isModifier, isModifierLike, + isNamedDeclaration, isNamedExports, isObjectLiteralExpression, isOmittedExpression, @@ -99,6 +101,7 @@ import { isSpreadElement, isStatement, isStringLiteral, + isTsPlusUniqueIdentifier, length, mapDefined, MergeDeclarationMarker, @@ -1810,12 +1813,27 @@ export function transformModule(context: TransformationContext): (x: SourceFile */ function appendExportsOfDeclaration(statements: Statement[] | undefined, decl: Declaration, liveBinding?: boolean): Statement[] | undefined { const name = factory.getDeclarationName(decl); - const exportSpecifiers = currentModuleInfo.exportSpecifiers.get(idText(name)); - if (exportSpecifiers) { - for (const exportSpecifier of exportSpecifiers) { - statements = appendExportStatement(statements, exportSpecifier.name, name, /*location*/ exportSpecifier.name, /* allowComments */ undefined, liveBinding); + // TSPLUS EXTENSION START + let exportSpecifiers = currentModuleInfo.exportSpecifiers.get(idText(name)); + exportSpecifiers ||= []; + if (currentModuleInfo.generatedExportSpecifiers) { + if (currentModuleInfo.generatedExportSpecifiers.has(name)) { + exportSpecifiers = concatenate(exportSpecifiers, currentModuleInfo.generatedExportSpecifiers.get(name)!) } } + // TSPLUS EXTENSION END + for (const exportSpecifier of exportSpecifiers) { + statements = appendExportStatement(statements, exportSpecifier.name, name, /*location*/ exportSpecifier.name, /* allowComments */ undefined, liveBinding); + } + // TSPLUS EXTENSION START + if (isNamedDeclaration(decl) && isIdentifier(decl.name) && isTsPlusUniqueIdentifier(decl.name) && currentModuleInfo.generatedExportSpecifiers) { + if (currentModuleInfo.generatedExportSpecifiers.has(decl.name)) { + for (const exportSpecifier of currentModuleInfo.generatedExportSpecifiers.get(decl.name)!) { + statements = appendExportStatement(statements, exportSpecifier.name, decl.name, exportSpecifier.name, undefined, liveBinding); + } + } + } + // TSPLUS EXTENSION END return statements; } diff --git a/src/compiler/transformers/tailRec.ts b/src/compiler/transformers/tailRec.ts new file mode 100644 index 00000000000..38602546f3b --- /dev/null +++ b/src/compiler/transformers/tailRec.ts @@ -0,0 +1,240 @@ +import * as ts from "../_namespaces/ts" +import { + ArrowFunction, Block, chainBundle, CompilerHost, CompilerOptions, ConciseBody, every, Expression, ExpressionStatement, factory, findIndex, + FunctionDeclaration, getAllJSDocTags, Identifier, isArrowFunction, isBlock, isCallExpression, isExpression, isFunctionDeclaration, + isIdentifier, isReturnStatement, isVariableDeclaration, JSDocTag, map, Node, NodeArray, NodeFactory, ParameterDeclaration, reduceLeft, + SourceFile, Statement, SyntaxKind, TransformationContext, Type, TypeChecker, unescapeLeadingUnderscores, VariableDeclaration, + visitEachChild, visitNode, VisitResult +} from "../_namespaces/ts"; + +/*@internal*/ +export function transformTailRec(checker: TypeChecker, _options: CompilerOptions, _host: CompilerHost) { + return function (context: TransformationContext) { + return chainBundle(context, transformSourceFile); + + function transformSourceFile (node: SourceFile) { + return visitEachChild(node, visitor(node), context); + } + function visitor(source: SourceFile) { + return function (node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.VariableDeclaration: + return visitVariableDeclaration(node as VariableDeclaration, context); + case SyntaxKind.FunctionDeclaration: + return visitFunctionDeclaration(node as FunctionDeclaration, context); + default: + return visitEachChild(node, visitor(source), context); + } + } + } + type IdentifierParameter = Omit & { name: Identifier } + type FunctionWithBody = + | Omit & { body: Block, parameters: NodeArray } + | Omit & { body: ConciseBody, parameters: NodeArray } + function visitVariableDeclaration(node: VariableDeclaration, context: TransformationContext): VisitResult { + if (node.name && isIdentifier(node.name) && node.initializer && isArrowFunction(node.initializer) && every(node.initializer.parameters, (p) => isIdentifier(p.name))) { + return visitFunction(node.name, node, node.initializer as FunctionWithBody, context); + } else { + return node + } + } + function visitFunctionDeclaration(node: FunctionDeclaration, context: TransformationContext): VisitResult { + if (node.name && node.body && every(node.parameters, (p) => isIdentifier(p.name))) { + return visitFunction(node.name, node, node as FunctionWithBody, context); + } else { + return node; + } + } + function visitFunction(name: Identifier, declaration: FunctionDeclaration | VariableDeclaration, node: FunctionWithBody, context: TransformationContext) { + const funcType = checker.getTypeAtLocation(node) + const funcSymbol = funcType.symbol + + let tailRecTag: JSDocTag | undefined = undefined; + if (funcSymbol && funcSymbol.declarations) { + for (const declaration of funcSymbol.declarations) { + tailRecTag = getAllJSDocTags( + declaration, + (tag): tag is JSDocTag => + tag.tagName.escapedText === "tsplus" && + typeof tag.comment === "string" && + tag.comment.startsWith("tailRec") + )[0]; + if (tailRecTag) { + break; + } + } + } + if (tailRecTag) { + const originalParamNames = map(node.parameters, (param) => param.name); + const [paramVariableNames, paramVariableDeclarations] = createTempVariables(node, factory); + const [tempVariableNames, tempVariableDeclarations] = createTempVariables(node, factory); + const funcIdentifierType = checker.getTypeAtLocation(name); + try { + const loopBody = visitEachChild( + isBlock(node.body) ? node.body : factory.createBlock([factory.createReturnStatement(node.body)]), + visitFunctionBody( + funcIdentifierType, + originalParamNames, + paramVariableNames, + tempVariableNames, + checker, + factory, + context, + ), + context + ); + const functionBody = factory.createBlock( + [ + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList(paramVariableDeclarations) + ), + factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList(tempVariableDeclarations) + ), + factory.createWhileStatement( + factory.createNumericLiteral(1), + loopBody + ), + ], + true + ); + if (isArrowFunction(node) && isVariableDeclaration(declaration)) { + return factory.updateVariableDeclaration( + declaration, + declaration.name, + declaration.exclamationToken, + declaration.type, + factory.updateArrowFunction( + node, + node.modifiers, + node.typeParameters, + node.parameters, + node.type, + node.equalsGreaterThanToken, + functionBody + ) + ); + } else if (isFunctionDeclaration(node)) { + return factory.updateFunctionDeclaration( + node, + node.modifiers, + node.asteriskToken, + node.name, + node.typeParameters, + node.parameters, + node.type, + functionBody + ); + } else { + return declaration; + } + } + catch (e) { + console.error(e); + throw new Error("Unable to optimize tail recursive function") + } + } else { + return declaration + } + } + function createTempVariables( + node: FunctionWithBody, + factory: NodeFactory + ): [Array, Array] { + return reduceLeft(node.parameters, (b, param) => { + const uniqueName = factory.createUniqueName(unescapeLeadingUnderscores(param.name.escapedText)) + b[0].push(uniqueName) + b[1].push(factory.createVariableDeclaration(uniqueName, undefined, undefined, param.name)) + return b + }, [[] as Array, [] as Array]) + } + function visitExpression(originalParamIdentifiers: ReadonlyArray, tempParamIdentifiers: ReadonlyArray, checker: TypeChecker, factory: NodeFactory, context: TransformationContext) { + return function(node: Node): VisitResult { + if (isIdentifier(node)) { + const symbol = checker.getSymbolAtLocation(node); + const paramIndex = findIndex(originalParamIdentifiers, (param) => checker.getSymbolAtLocation(param) === symbol); + if (paramIndex !== -1) { + return tempParamIdentifiers[paramIndex]; + } else { + return node; + } + } else { + return visitEachChild(node, visitExpression(originalParamIdentifiers, tempParamIdentifiers, checker, factory, context), context); + } + } + } + function visitFunctionBody( + funcIdentifierType: Type, + originalParamNames: ReadonlyArray, + paramNames: ReadonlyArray, + tempNames: ReadonlyArray, + checker: TypeChecker, + factory: NodeFactory, + context: TransformationContext, + ) { + return function (node: Node): VisitResult { + if (isReturnStatement(node) && node.expression) { + if(isCallExpression(node.expression) && isIdentifier(node.expression.expression)) { + const callIdentifierType = checker.getTypeAtLocation(node.expression.expression); + if (callIdentifierType === funcIdentifierType) { + const tempAssignments: Array = [] + const assignments: Array = [] + for (let argIndex = 0, propIndex = 0; argIndex < node.expression.arguments.length; ++argIndex) { + const arg = node.expression.arguments[argIndex]; + tempAssignments.push( + factory.createExpressionStatement( + factory.createAssignment( + tempNames[propIndex], + visitNode(arg, visitExpression(originalParamNames, paramNames, checker, factory, context)) as Expression + ) + ) + ); + assignments.push( + factory.createExpressionStatement( + factory.createAssignment( + paramNames[propIndex], + tempNames[propIndex] + ) + ) + ); + propIndex += 1; + } + const statements: Array = tempAssignments.concat(assignments); + statements.push(factory.createContinueStatement()); + return statements + } else { + return ts.visitEachChild( + node, + visitExpression(originalParamNames, paramNames, checker, factory, context), + context + ); + } + } + else { + return ts.visitEachChild( + node, + visitExpression(originalParamNames, paramNames, checker, factory, context), + context + ); + } + } + else if (isExpression(node)) { + return ts.visitEachChild( + node, + visitExpression(originalParamNames, paramNames, checker, factory, context), + context + ); + } + else { + return ts.visitEachChild( + node, + visitFunctionBody(funcIdentifierType, originalParamNames, paramNames, tempNames, checker, factory, context), + context + ) + } + } + } + } + } \ No newline at end of file diff --git a/src/compiler/transformers/tsplus.ts b/src/compiler/transformers/tsplus.ts new file mode 100644 index 00000000000..2a64de6d239 --- /dev/null +++ b/src/compiler/transformers/tsplus.ts @@ -0,0 +1,1311 @@ +import * as ts from "../_namespaces/ts" +import { + BinaryExpression, Block, Bundle, CallExpression, chainBundle, CharacterCodes, ClassDeclaration, CompilerHost, CompilerOptions, + ConciseBody, Debug, Declaration, Derivation, ElementAccessExpression, escapeString, Expression, factory, filter, flatMapToMutable, + FunctionDeclaration, getAllJSDocTags, getEnclosingBlockScopeContainer, getFileMap, getImportLocation, getLineAndCharacterOfPosition, + getOriginalNode, getParametersOfFunctionOrVariableDeclaration, getSourceFileOfNode, getTraceLocation, getTraceMap, HasModifiers, Identifier, identity, ImportDeclaration, + isArrowFunction, isBlock, isCallExpression, isDeclaration, isDeclarationName, isExportDeclaration, isExpressionStatement, isFunctionDeclaration, + isIdentifier, isNamedDeclaration, isNamespaceImport, isNumericLiteral, isParenthesizedExpression, isPartOfTypeNode, isPartOfTypeQuery, + isPropertyAccessExpression, isReturnStatement, isSpreadElement, isStringLiteral, isSuperCall, isTsPlusSignature, isVariableDeclaration, + isVariableStatement, JSDocTag, map, NamedDeclaration, Node, NodeArray, NodeFactory, NodeFlags, ParameterDeclaration, PropertyAccessExpression, + reduceLeft, setOriginalNode, SourceFile, Statement, symbolName, SyntaxKind, TransformationContext, TsPlusPipeableDeclarationSymbol, TsPlusSignature, TypeChecker, + VariableStatement, visitEachChild, visitNode, visitNodes, Visitor, VisitResult, __String, TypeNode +} from "../_namespaces/ts"; + +/*@internal*/ +class TsPlusImporter { + readonly imports: Map = new Map(); + + readonly refCount: Map = new Map(); + constructor(readonly factory: NodeFactory) { } + + add(name: Identifier, path: string): void { + this.imports.set(path, { name, exists: true }) + this.refCount.set(path, Infinity) + } + + get(path: string): Identifier { + if (!this.imports.has(path)) { + this.imports.set(path, { name: this.factory.createUniqueName("tsplus_module"), exists: false }); + } + this.refCount.set(path, (this.refCount.get(path) ?? 0) + 1); + return this.imports.get(path)!.name; + } + + remove(path: string): void { + if (!this.imports.has(path)) { + return; + } + const refCount = this.refCount.get(path)!; + if (refCount - 1 === 0) { + this.imports.delete(path); + this.refCount.delete(path); + } + else { + this.refCount.set(path, refCount - 1); + } + } +} +class UniqueNameOfDerivation { + readonly cache = new Map() + constructor(readonly factory: NodeFactory) { } + get(d: Derivation) { + if (!this.cache.has(d)) { + this.cache.set(d, this.factory.createUniqueName("derive")) + } + return this.cache.get(d)! + } +} +class SourceFileUniqueNames { + readonly cache = new Map() + constructor(readonly factory: NodeFactory) { } + get(text: string, isExported = true) { + if (!this.cache.has(text)) { + this.cache.set(text, { name: this.factory.createTsPlusUniqueName(text), isExported }); + } + return this.cache.get(text)!; + } + has(text: string) { + return this.cache.has(text); + } + clear() { + this.cache.clear(); + } +} +interface SourceFileUniqueName { + readonly name: Identifier + readonly isExported: boolean +} +export function transformTsPlus(checker: TypeChecker, options: CompilerOptions, host: CompilerHost): (context: TransformationContext) => (sourceFile: SourceFile | Bundle) => SourceFile | Bundle { + if (options.tsPlusEnabled === false) { + return () => identity; + } + const fileMap: [string, RegExp][] = getFileMap(options, host); + const traceMap: [string, RegExp][] = getTraceMap(options, host); + return function (context: TransformationContext) { + const importer = new TsPlusImporter(context.factory); + const uniqueNameOfDerivation = new UniqueNameOfDerivation(context.factory); + const sourceFileUniqueNames = new SourceFileUniqueNames(context.factory); + const fileVar = factory.createUniqueName("fileName"); + let fileVarUsed = false; + return chainBundle(context, transformSourceFile); + function transformSourceFile(node: SourceFile) { + if (node.isDeclarationFile) { + return node; + } + + const transformed = visitEachChild(node, visitor(node, void 0), context); + + const uniqueDeclarations: Statement[] = []; + + const transformedWithUniqueDeclarations = visitEachChild( + transformed, + addSourceFileUniqueNamesVisitor(uniqueDeclarations, sourceFileUniqueNames, context), + context + ) + + sourceFileUniqueNames.clear(); + + const imports: Statement[] = [] + importer.imports.forEach(({ name, exists }, path) => { + if (!exists) { + imports.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamespaceImport(name) + ), + factory.createStringLiteral(path), + undefined + ) + ); + } + }) + const fileVarDef = fileVarUsed ? [ + context.factory.createVariableStatement( + undefined, + factory.createVariableDeclarationList( + [ + factory.createVariableDeclaration( + fileVar, + undefined, + undefined, + factory.createStringLiteral( + getTraceLocation(traceMap, node.fileName) + ) + ) + ], + ts.NodeFlags.Const + ) + ) + ] : []; + return context.factory.updateSourceFile( + transformed, + [...fileVarDef, ...imports, ...uniqueDeclarations, ...transformedWithUniqueDeclarations.statements], + transformed.isDeclarationFile, + transformed.referencedFiles, + transformed.typeReferenceDirectives, + transformed.hasNoDefaultLib, + transformed.libReferenceDirectives + ); + } + function getTrace(source: SourceFile, node: ts.Node) { + const nodeEnd = getLineAndCharacterOfPosition(source, node.end) + fileVarUsed = true; + return factory.createBinaryExpression( + fileVar, + factory.createToken(ts.SyntaxKind.PlusToken), + factory.createStringLiteral(`:${nodeEnd.line + 1}:${nodeEnd.character + 1}`) + ) + } + function visitor(source: SourceFile, traceInScope: Identifier | undefined) { + return function (node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.ElementAccessExpression: + return visitElementAccessExpression(source, traceInScope, node as ElementAccessExpression, context) + case SyntaxKind.BinaryExpression: + return visitBinaryExpression(source, traceInScope, node as BinaryExpression, context) + case SyntaxKind.FunctionDeclaration: + return visitFunctionDeclaration(source, traceInScope, node as FunctionDeclaration, context); + case SyntaxKind.PropertyAccessExpression: + return visitPropertyAccessExpression(source, traceInScope, node as PropertyAccessExpression, visitor(source, traceInScope), context); + case SyntaxKind.CallExpression: + return visitCallExpressionOrFluentCallExpression(source, traceInScope, node as CallExpression, visitor(source, traceInScope), context); + case SyntaxKind.VariableStatement: + return visitVariableStatement(source, node as VariableStatement, visitor(source, traceInScope), context) + case SyntaxKind.Identifier: + return visitIdentifier(source, traceInScope, node as Identifier, context); + case SyntaxKind.Block: + return visitBlock(node as Block, visitor(source, traceInScope), context); + case SyntaxKind.ImportDeclaration: + return visitImportDeclaration(node as ImportDeclaration) + default: + return visitEachChild(node, visitor(source, traceInScope), context); + } + } + } + function visitBlock(node: Block, visitor: Visitor, context: TransformationContext) { + const uniqueNames = checker.getNodeLinks(getEnclosingBlockScopeContainer(node)).uniqueNames; + if (uniqueNames) { + const remainingNames = new Set(uniqueNames); + const updatedStatements = flatMapToMutable(node.statements, (statement) => { + if (isVariableStatement(statement) && statement.declarationList.declarations[0]) { + const declaration = statement.declarationList.declarations[0]; + const declarationLinks = checker.getNodeLinks(declaration); + if (declarationLinks.needsUniqueNameInSope) { + Debug.assert(isNamedDeclaration(declaration) && isIdentifier(declaration.name)); + const uniqueIdentifier = context.factory.createUniqueName(declaration.name.escapedText as string); + declarationLinks.uniqueNameInScope = uniqueIdentifier; + declarationLinks.needsUniqueNameInSope = false; + remainingNames.delete(declaration as NamedDeclaration & { name: Identifier }); + return [ + statement, + context.factory.createVariableStatement( + [context.factory.createModifier(SyntaxKind.ConstKeyword)], + context.factory.createVariableDeclarationList([ + context.factory.createVariableDeclaration(uniqueIdentifier, undefined, undefined, declaration.name) + ], NodeFlags.Const) + ), + ]; + } + } + return statement; + }) + remainingNames.forEach((declaration) => { + const declarationLinks = checker.getNodeLinks(declaration); + if (declarationLinks.needsUniqueNameInSope) { + Debug.assert(isNamedDeclaration(declaration) && isIdentifier(declaration.name)); + const uniqueIdentifier = context.factory.createUniqueName(declaration.name.escapedText as string); + declarationLinks.uniqueNameInScope = uniqueIdentifier; + declarationLinks.needsUniqueNameInSope = false; + updatedStatements.unshift( + context.factory.createVariableStatement( + [context.factory.createModifier(SyntaxKind.ConstKeyword)], + context.factory.createVariableDeclarationList([ + context.factory.createVariableDeclaration(uniqueIdentifier, undefined, undefined, declaration.name) + ], NodeFlags.Const) + ), + ); + } + }) + remainingNames.clear(); + return context.factory.updateBlock( + node, + visitNodes(context.factory.createNodeArray(updatedStatements), visitor) as NodeArray + ) + } + return visitEachChild(node, visitor, context); + } + function visitImportDeclaration(node: ImportDeclaration) { + if (node.importClause && node.importClause.namedBindings && isNamespaceImport(node.importClause.namedBindings) && isStringLiteral(node.moduleSpecifier)) { + importer.add(node.importClause.namedBindings.name, node.moduleSpecifier.text) + } + return node + } + function isExtension(node: Node): boolean { + const extensionRegex = /^(static|fluent|getter|operator|index|pipeable).*/; + return getAllJSDocTags(node, (tag): tag is JSDocTag => tag.tagName.escapedText === "tsplus" && typeof tag.comment === "string" && extensionRegex.test(tag.comment)).length > 0; + } + function shouldTransformIdentifier(node: Identifier) { + if (node.parent && isDeclarationName(node) && !isExportDeclaration(node.parent)) { + return false; + } + if (node.parent && isPartOfTypeNode(node.parent)) { + return false; + } + if (node.parent && isPartOfTypeQuery(node.parent)) { + return false; + } + return true + } + function visitIdentifier(source: SourceFile, traceInScope: Identifier | undefined, node: Identifier, context: TransformationContext) { + if (shouldTransformIdentifier(node)) { + const symbol = checker.getSymbolAtLocation(node); + if (symbol) { + if (symbol.valueDeclaration && isExtension(symbol.valueDeclaration) && getSourceFileOfNode(symbol.valueDeclaration) === source) { + const name = node.escapedText.toString(); + return sourceFileUniqueNames.get(name).name; + } + const links = checker.getNodeLinks(node); + const globalImport = checker.getTsPlusGlobal(node.escapedText.toString()); + if (links.isTsPlusGlobalIdentifier && globalImport) { + return getPathOfGlobalImport(context, importer, node, globalImport.moduleSpecifier.text) + } + } + } + return visitEachChild(node, visitor(source, traceInScope), context); + } + function visitElementAccessExpression(source: SourceFile, traceInScope: Identifier | undefined, node: ElementAccessExpression, context: TransformationContext): VisitResult { + const custom = checker.getIndexAccessExpressionCache().get(node) + if (custom) { + const expression = visitNode(node.expression, visitor(source, traceInScope)) as Expression + const argument = visitNode(node.argumentExpression, visitor(source, traceInScope)) as Expression + if (custom.signature && isTsPlusSignature(custom.signature) && custom.signature.tsPlusPipeable) { + return context.factory.createCallExpression( + context.factory.createCallExpression( + getPathOfExtension(context, importer, custom, source, sourceFileUniqueNames), + [], + [argument] + ), + [], + [expression] + ) + } + return context.factory.createCallExpression( + getPathOfExtension(context, importer, custom, source, sourceFileUniqueNames), + [], + [expression, argument] + ) + } + return visitEachChild(node, visitor(source, traceInScope), context) + } + function visitBinaryExpression(source: SourceFile, traceInScope: Identifier | undefined, node: BinaryExpression, context: TransformationContext): VisitResult { + const operatorLinks = checker.getNodeLinks(node.operatorToken); + if (operatorLinks.isTsPlusOperatorToken && operatorLinks.resolvedSignature && operatorLinks.resolvedSignature.declaration) { + const call = operatorLinks.resolvedSignature; + const isPipeable = (isTsPlusSignature(call) && !!call.tsPlusPipeable) || (!!call.target && isTsPlusSignature(call.target) && !!call.target.tsPlusPipeable); + const declaration = call.declaration!; + let exportName: string + if (isPipeable) { + exportName = (declaration.symbol as TsPlusPipeableDeclarationSymbol).tsPlusDeclaration.symbol.escapedName! + } + else { + if (isFunctionDeclaration(declaration)) { + exportName = declaration.symbol.escapedName as string; + } else if (isDeclaration(declaration.parent)) { + exportName = declaration.parent.symbol.escapedName as string; + } else if (isDeclaration(declaration.parent.parent)) { + exportName = declaration.parent.parent.symbol.escapedName as string; + } else { + throw new Error("Cannot find export name for operator extension"); + } + } + const params = [visitNode(node.left, visitor(source, traceInScope)), visitNode(node.right, visitor(source, traceInScope))] as Expression[]; + if (checker.getNodeLinks(node.left).tsPlusLazy) { + params[0] = context.factory.createArrowFunction(void 0, void 0, [], void 0, void 0, params[0]); + } + if (checker.getNodeLinks(node.right).tsPlusLazy) { + params[1] = context.factory.createArrowFunction(void 0, void 0, [], void 0, void 0, params[1]); + } + if (checker.isTsPlusMacroCall(node, "pipe")) { + return optimizePipe( + context.factory.createNodeArray(params, false), + context.factory, + source + ); + } + const lastTrace = call.parameters.length > 0 ? call.parameters[call.parameters.length - 1].escapedName === "___tsplusTrace" : false; + if (lastTrace) { + params.push(traceInScope ? traceInScope : getTrace(source, node.operatorToken)) + } + if (isPipeable) { + return context.factory.createCallExpression( + context.factory.createCallExpression( + getPathOfExtension(context, importer, { + definition: getSourceFileOfNode((declaration.symbol as TsPlusPipeableDeclarationSymbol).tsPlusDeclaration), + exportName: exportName + }, source, sourceFileUniqueNames), + [], + params.slice(1) + ), + [], + [params[0]] + ) + } + else { + return context.factory.createCallExpression( + getPathOfExtension(context, importer, { + definition: getSourceFileOfNode(declaration), + exportName: exportName + }, source, sourceFileUniqueNames), + [], + params + ) + } + } + return visitEachChild(node, visitor(source, traceInScope), context) + } + function visitFunctionDeclaration(source: SourceFile, traceInScope: Identifier | undefined, node: FunctionDeclaration, context: TransformationContext): VisitResult { + if (node.parameters.length > 0) { + const last = node.parameters[node.parameters.length - 1] + + if (last.name.kind === SyntaxKind.Identifier) { + if ((last.name as Identifier).escapedText === "___tsplusTrace") { + return visitEachChild(node, visitor(source, last.name as Identifier), context) + } + } + } + return visitEachChild(node, visitor(source, traceInScope), context) + } + function visitPropertyAccessExpression(source: SourceFile, traceInScope: Identifier | undefined, node: PropertyAccessExpression, visitor: Visitor, context: TransformationContext): VisitResult { + const nodeLinks = checker.getNodeLinks(node); + if (nodeLinks.tsPlusStaticExtension) { + return getPathOfExtension(context, importer, nodeLinks.tsPlusStaticExtension, source, sourceFileUniqueNames, node); + } + if (nodeLinks.tsPlusGetterExtension) { + if (checker.isTsPlusMacroGetter(node, "identity")) { + return visitNode(node.expression, visitor) as Expression; + } + const args = [simplyfy(visitNode(node.expression, visitor) as Expression)] + const parameters = getParametersOfFunctionOrVariableDeclaration(nodeLinks.tsPlusGetterExtension.declaration) + if (parameters) { + const lastParam = parameters[parameters.length - 1]; + if (lastParam && isIdentifier(lastParam.name) && lastParam.name.escapedText === "___tsplusTrace") { + if (traceInScope) { + args.push(traceInScope); + } else { + args.push(getTrace(source, node.expression)); + } + } + } + return setOriginalNode( + factory.createCallExpression( + getPathOfExtension(context, importer, nodeLinks.tsPlusGetterExtension, source, sourceFileUniqueNames, node), + void 0, + args + ), + node, + ); + } + return visitEachChild(node, visitor, context); + } + function visitVariableStatement(_source: SourceFile, node: VariableStatement, visitor: Visitor, context: TransformationContext): VisitResult { + if (node.declarationList.declarations.length > 0) { + const declaration = node.declarationList.declarations[0]; + const nodeLinks = checker.getNodeLinks(declaration); + if (nodeLinks.tsPlusDataFirstDeclaration && declaration.initializer && isCallExpression(declaration.initializer)) { + const signatureDeclaration = nodeLinks.tsPlusDataFirstDeclaration; + const updatedDeclaration = factory.updateVariableDeclaration( + declaration, + declaration.name, + undefined, + undefined, + factory.createArrowFunction( + undefined, + undefined, + signatureDeclaration.parameters.slice(1, signatureDeclaration.parameters.length), + undefined, + undefined, + factory.createArrowFunction( + undefined, + undefined, + [signatureDeclaration.parameters[0]], + undefined, + undefined, + factory.createCallExpression( + declaration.initializer.arguments[0], + undefined, + map(signatureDeclaration.parameters, (pdecl) => pdecl.name as Identifier) + ) + ) + ) + ) + const updatedStatement = factory.updateVariableStatement( + node, + node.modifiers, + factory.updateVariableDeclarationList( + node.declarationList, + [updatedDeclaration] + ) + ) + return updatedStatement; + } + } + return ts.visitEachChild(node, visitor, context) + } + function createDataFirstCall(call: CallExpression, args: Expression[], definition: SourceFile, exportName: string, source: SourceFile) { + return factory.updateCallExpression( + call, + getPathOfExtension( + context, + importer, + { + definition, + exportName + }, + source, + sourceFileUniqueNames, + call.expression, + ), + undefined, + args + ) + } + function tryGetOptimizedPipeableCall(call: CallExpression): { definition: SourceFile, exportName: string } | undefined { + const original = getOriginalNode(call); + const optimized = checker.getNodeLinks(original).tsPlusOptimizedDataFirst; + if (optimized) { + if (isExpressionWithReferencedImport(call.expression)) { + importer.remove(call.expression.tsPlusReferencedImport); + } + if (isExpressionWithReferencedGlobalImport(call.expression)) { + importer.remove(call.expression.tsPlusReferencedGlobalImport); + } + } + return optimized; + } + function optimizePipe( + args: NodeArray, + factory: NodeFactory, + source: SourceFile + ): Expression { + return reduceLeft(args.slice(1), (accumulatedCall, pipeArg) => { + if (isCallExpression(pipeArg)) { + const optimized = tryGetOptimizedPipeableCall(pipeArg); + if (optimized) { + return createDataFirstCall(getOriginalNode(pipeArg) as CallExpression, [accumulatedCall, ...pipeArg.arguments], optimized.definition, optimized.exportName, source); + } + } + return factory.createCallExpression(pipeArg, undefined, [accumulatedCall]); + }, args[0]!); + } + function optimizeIdentity( + node: CallExpression, + ): Expression { + if (node.arguments.length === 1 && !isSpreadElement(node.arguments[0])) { + return node.arguments[0]; + } else { + return node; + } + } + function simplyfy(node: Expression): Expression { + if (isParenthesizedExpression(node) && isNumericLiteral(node.expression)) { + return node.expression + } + return node + } + function produceDerivation(derivation: Derivation | undefined, context: TransformationContext, importer: TsPlusImporter, source: SourceFile, sourceFileUniqueNames: SourceFileUniqueNames): Expression { + if (derivation) { + switch (derivation._tag) { + case "FromBlockScope": { + const nodeLinks = checker.getNodeLinks(derivation.implicit); + if (nodeLinks.uniqueNameInScope) { + // Block-scoped (other than SourceFile) implicit + return nodeLinks.uniqueNameInScope; + } + else { + // SourceFile-scoped implicit + return getPathOfImplicitOrRule(context, importer, derivation.implicit, source, sourceFileUniqueNames); + } + } + case "FromImplicitScope": { + return getPathOfImplicitOrRule(context, importer, derivation.implicit, source, sourceFileUniqueNames); + } + case "EmptyObjectDerivation": { + return factory.createObjectLiteralExpression( + [], + false + ); + } + case "FromLiteral": { + return typeof derivation.value === "number" ? factory.createNumericLiteral(derivation.value) : factory.createStringLiteral(derivation.value); + } + case "FromIntersectionStructure": { + return factory.createObjectLiteralExpression( + derivation.fields.map((child) => factory.createSpreadAssignment(produceDerivation(child, context, importer, source, sourceFileUniqueNames))), + false + ); + } + case "FromObjectStructure": { + return factory.createObjectLiteralExpression( + derivation.fields.map((child) => factory.createPropertyAssignment( + factory.createIdentifier(`"${escapeString(symbolName(child.prop), CharacterCodes.doubleQuote)}"`), + produceDerivation(child.value, context, importer, source, sourceFileUniqueNames) + )), + false + ); + } + case "FromTupleStructure": { + return factory.createArrayLiteralExpression( + derivation.fields.map((child) => produceDerivation(child, context, importer, source, sourceFileUniqueNames)), + false + ); + } + case "FromPriorDerivation": { + const name = uniqueNameOfDerivation.get(derivation.derivation); + if (name) { + return name; + } + break + } + case "FromRule": { + if (derivation.usedBy.length === 0) { + return factory.createCallExpression( + getPathOfImplicitOrRule(context, importer, derivation.rule, source, sourceFileUniqueNames), + undefined, + derivation.arguments.map((child) => produceDerivation(child, context, importer, source, sourceFileUniqueNames)) + ); + } else if (derivation.lazyRule) { + const name = uniqueNameOfDerivation.get(derivation); + const lazy = getPathOfImplicitOrRule(context, importer, derivation.lazyRule, source, sourceFileUniqueNames); + return factory.createCallExpression( + lazy, + undefined, + [factory.createArrowFunction( + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + undefined, + name, + undefined, + undefined, + undefined + )], + undefined, + factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + factory.createCallExpression( + getPathOfImplicitOrRule(context, importer, derivation.rule, source, sourceFileUniqueNames), + undefined, + derivation.arguments.map((child) => produceDerivation(child, context, importer, source, sourceFileUniqueNames)) + ) + )] + ); + } + break + } + case "InvalidDerivation": { + break + } + } + } + return factory.createCallExpression( + factory.createParenthesizedExpression(factory.createArrowFunction( + undefined, + undefined, + [], + undefined, + factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + factory.createBlock( + [factory.createThrowStatement(factory.createNewExpression( + factory.createIdentifier("Error"), + undefined, + [factory.createStringLiteral("Not Implemented")] + ))], + false + ) + )), + undefined, + [] + ); + } + function visitDo( + source: SourceFile, + traceInScope: Identifier | undefined, + node: CallExpression, + visitor: Visitor, + context: TransformationContext, + functions: { + map: TsPlusSignature; + flatMap: TsPlusSignature; + } + ): VisitResult { + if (isArrowFunction(node.arguments[0])) { + const body = node.arguments[0].body; + if (isBlock(body)) { + let currentScope: Statement[] = [] + let isLast = true; + for (let i = body.statements.length - 1; i >= 0; i--) { + const statement = body.statements[i]; + if ( + isVariableStatement(statement) && + checker.getNodeLinks(statement).tsPlusDoBindType && + statement.declarationList.declarations.length === 1 && + statement.declarationList.declarations[0].initializer && + statement.declarationList.declarations[0].name && + isCallExpression(statement.declarationList.declarations[0].initializer) + ) { + if (isLast) { + isLast = false + const mapper = factory.createArrowFunction( + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + undefined, + statement.declarationList.declarations[0].name, + undefined, + undefined, + undefined + )], + undefined, + factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + getArrowBody(currentScope) + ); + const args = [visitNode(statement.declarationList.declarations[0].initializer.arguments[0], visitor), mapper] as Expression[]; + if (args.length === functions.map.tsPlusOriginal.parameters.length - 1) { + if (functions.map.tsPlusOriginal.parameters[functions.map.tsPlusOriginal.parameters.length - 1].escapedName === "___tsplusTrace") { + if (traceInScope) { + args.push(traceInScope); + } else { + args.push(getTrace(source, statement.declarationList.declarations[0].initializer.expression)); + } + } + } + currentScope = [factory.createReturnStatement(produceTsPlusCallExpression(functions.map, args, context, importer, source, sourceFileUniqueNames))]; + } + else { + const mapper = factory.createArrowFunction( + undefined, + undefined, + [factory.createParameterDeclaration( + undefined, + undefined, + statement.declarationList.declarations[0].name, + undefined, + undefined, + undefined + )], + undefined, + factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + getArrowBody(currentScope) + ); + const args = [visitNode(statement.declarationList.declarations[0].initializer.arguments[0], visitor), mapper] as Expression[]; + if (args.length === functions.flatMap.tsPlusOriginal.parameters.length - 1) { + if (functions.flatMap.tsPlusOriginal.parameters[functions.flatMap.tsPlusOriginal.parameters.length - 1].escapedName === "___tsplusTrace") { + if (traceInScope) { + args.push(traceInScope); + } else { + args.push(getTrace(source, statement.declarationList.declarations[0].initializer.expression)); + } + } + } + currentScope = [factory.createReturnStatement(produceTsPlusCallExpression(functions.flatMap, args, context, importer, source, sourceFileUniqueNames))]; + } + } + else if (isExpressionStatement(statement) && isCallExpression(statement.expression) && checker.getNodeLinks(statement).tsPlusDoBindType) { + if (isLast) { + isLast = false + const mapper = factory.createArrowFunction( + undefined, + undefined, + [], + undefined, + factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + getArrowBody(currentScope) + ); + const args = [visitNode(statement.expression.arguments[0], visitor), mapper] as Expression[]; + if (args.length === functions.map.tsPlusOriginal.parameters.length - 1) { + if (functions.map.tsPlusOriginal.parameters[functions.map.tsPlusOriginal.parameters.length - 1].escapedName === "___tsplusTrace") { + if (traceInScope) { + args.push(traceInScope); + } else { + args.push(getTrace(source, statement.expression.expression)); + } + } + } + currentScope = [factory.createReturnStatement(produceTsPlusCallExpression(functions.map, args, context, importer, source, sourceFileUniqueNames))]; + } + else { + const mapper = factory.createArrowFunction( + undefined, + undefined, + [], + undefined, + factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + getArrowBody(currentScope) + ); + const args = [visitNode(statement.expression.arguments[0], visitor), mapper] as Expression[]; + if (args.length === functions.flatMap.tsPlusOriginal.parameters.length - 1) { + if (functions.flatMap.tsPlusOriginal.parameters[functions.flatMap.tsPlusOriginal.parameters.length - 1].escapedName === "___tsplusTrace") { + if (traceInScope) { + args.push(traceInScope); + } else { + args.push(getTrace(source, statement.expression.expression)); + } + } + } + currentScope = [factory.createReturnStatement(produceTsPlusCallExpression(functions.flatMap, args, context, importer, source, sourceFileUniqueNames))]; + } + } + else if (isReturnStatement(statement) && statement.expression && isCallExpression(statement.expression) && checker.getNodeLinks(statement).tsPlusDoBindType) { + isLast = false + currentScope = [factory.createReturnStatement(visitNode(statement.expression.arguments[0], visitor) as Expression)]; + } + else { + currentScope.push(visitNode(statement, visitor) as Statement) + } + } + if (currentScope.length === 1 && isReturnStatement(currentScope[0]) && currentScope[0].expression) { + return currentScope[0].expression; + } + const mapper = factory.createArrowFunction( + undefined, + undefined, + [], + undefined, + factory.createToken(ts.SyntaxKind.EqualsGreaterThanToken), + factory.createBlock( + currentScope.reverse(), + true + ) + ); + return factory.createCallExpression( + factory.createParenthesizedExpression(mapper), + undefined, + [] + ) + } + } + return node; + } + function getArrowBody(currentScope: Statement[]): ConciseBody { + return currentScope.length === 0 + ? factory.createVoidZero() + : currentScope.length === 1 && isReturnStatement(currentScope[0]) && currentScope[0].expression + ? currentScope[0].expression + : factory.createBlock(currentScope.reverse(), true); + } + function visitCallExpressionOrFluentCallExpression(source: SourceFile, traceInScope: Identifier | undefined, node: CallExpression, visitor: Visitor, context: TransformationContext): VisitResult { + if (checker.isPipeCall(node)) { + return optimizePipe(visitNodes(node.arguments, visitor) as NodeArray, context.factory, source); + } + if (checker.isTsPlusMacroCall(node, "identity")) { + return optimizeIdentity(visitEachChild(node, visitor, context)); + } + if (checker.isTsPlusMacroCall(node, "remove")) { + return factory.createVoidZero(); + } + if (checker.isTsPlusMacroCall(node, "Derive")) { + return produceDerivation(checker.getNodeLinks(node).tsPlusDerivation, context, importer, source, sourceFileUniqueNames); + } + if (node.arguments.length === 1 && isCallExpression(node.expression)) { + const optimizedPipeable = tryGetOptimizedPipeableCall(node.expression); + if (optimizedPipeable) { + return createDataFirstCall( + node.expression, + [visitNode(node.arguments[0], visitor) as Expression, ...visitNodes(node.expression.arguments, visitor) as NodeArray], + optimizedPipeable.definition, + optimizedPipeable.exportName, + source + ); + } + } + // Avoid transforming super call as __call extension + if (isSuperCall(node)) { + return visitCallExpression(source, traceInScope, node, visitor, context); + } + const nodeLinks = checker.getNodeLinks(node); + if (nodeLinks.tsPlusDoFunctions && nodeLinks.tsPlusDoBindTypes && nodeLinks.tsPlusDoBindTypes.length > 0) { + return visitDo( + source, + traceInScope, + node, + visitor, + context, + nodeLinks.tsPlusDoFunctions + ); + } + if (nodeLinks.tsPlusCallExtension) { + const visited = visitCallExpression(source, traceInScope, node, visitor, context); + if (isExpressionWithReferencedGlobalImport(visited.expression)) { + importer.remove(visited.expression.tsPlusReferencedGlobalImport); + } + return factory.updateCallExpression( + visited, + getPathOfExtension(context, importer, nodeLinks.tsPlusCallExtension, source, sourceFileUniqueNames, visited.expression), + visited.typeArguments, + visited.arguments + ); + } + if (nodeLinks.isFluentCall && nodeLinks.resolvedSignature) { + let fluentExtension: TsPlusSignature | undefined; + + if (isTsPlusSignature(nodeLinks.resolvedSignature)) { + fluentExtension = nodeLinks.resolvedSignature; + } + else if (nodeLinks.resolvedSignature.target && isTsPlusSignature(nodeLinks.resolvedSignature.target)) { + fluentExtension = nodeLinks.resolvedSignature.target; + } + + if (!fluentExtension) { + throw new Error("BUG: No fluent signature found for fluent extension"); + } + + if (fluentExtension.tsPlusDeclaration) { + const macroTags = checker.collectTsPlusMacroTags(fluentExtension.tsPlusDeclaration) + + if (macroTags.find((tag) => tag === "pipe")) { + return optimizePipe( + visitNodes(factory.createNodeArray([simplyfy(node.expression), ...node.arguments], node.arguments.hasTrailingComma), visitor) as NodeArray, + context.factory, + source + ); + } + } + const visited = visitCallExpression(source, traceInScope, node as CallExpression, visitor, context) as CallExpression; + if (isExpressionWithReferencedGlobalImport(visited.expression)) { + importer.remove(visited.expression.tsPlusReferencedGlobalImport); + } + const shouldMakeLazy = checker.getNodeLinks(node.expression).tsPlusLazy === true; + if (fluentExtension.tsPlusPipeable) { + let expression = simplyfy(visited.expression); + if (shouldMakeLazy) { + expression = context.factory.createArrowFunction( + void 0, + void 0, + [], + void 0, + void 0, + expression + ) + } + return factory.updateCallExpression( + visited, + setOriginalNode( + factory.createCallExpression( + getPathOfExtension( + context, + importer, + { definition: fluentExtension.tsPlusFile, exportName: fluentExtension.tsPlusExportName }, + source, + sourceFileUniqueNames, + visited.expression, + ), + undefined, + visited.arguments + ), + visited + ), + undefined, + [expression] + ) + } + else { + let expression = simplyfy((visited as CallExpression).expression); + if (shouldMakeLazy) { + expression = context.factory.createArrowFunction( + void 0, + void 0, + [], + void 0, + void 0, + expression + ) + } + return factory.updateCallExpression( + visited, + getPathOfExtension( + context, + importer, + { definition: fluentExtension.tsPlusFile, exportName: fluentExtension.tsPlusExportName }, + source, + sourceFileUniqueNames, + visited.expression, + ), + visited.typeArguments, + [expression, ...visited.arguments] + ); + } + } + if (isPropertyAccessExpression(node.expression) && checker.getNodeLinks(node.expression).isFluent && nodeLinks.resolvedSignature) { + let fluentExtension: TsPlusSignature | undefined; + if (isTsPlusSignature(nodeLinks.resolvedSignature)) { + fluentExtension = nodeLinks.resolvedSignature; + } + else if (nodeLinks.resolvedSignature.target && isTsPlusSignature(nodeLinks.resolvedSignature.target)) { + fluentExtension = nodeLinks.resolvedSignature.target; + } + if (!fluentExtension) { + throw new Error("BUG: No fluent signature found for fluent extension"); + } + if (fluentExtension.tsPlusDeclaration) { + const macroTags = checker.collectTsPlusMacroTags(fluentExtension.tsPlusDeclaration) + + if (macroTags.find((tag) => tag === "pipe")) { + return optimizePipe( + visitNodes(factory.createNodeArray([simplyfy(node.expression.expression), ...node.arguments], node.arguments.hasTrailingComma), visitor) as NodeArray, + context.factory, + source + ); + } + } + const visited = visitCallExpression(source, traceInScope, node as CallExpression, visitor, context) as CallExpression; + if (isExpressionWithReferencedGlobalImport(visited.expression)) { + importer.remove(visited.expression.tsPlusReferencedGlobalImport); + } + const shouldMakeLazy = checker.getNodeLinks(node.expression.expression).tsPlusLazy === true; + if (fluentExtension.tsPlusPipeable) { + let expression = simplyfy((visited.expression as PropertyAccessExpression).expression); + if (shouldMakeLazy) { + expression = context.factory.createArrowFunction( + void 0, + void 0, + [], + void 0, + void 0, + expression + ) + } + return factory.updateCallExpression( + visited, + setOriginalNode( + factory.createCallExpression( + getPathOfExtension( + context, + importer, + { definition: fluentExtension.tsPlusFile, exportName: fluentExtension.tsPlusExportName }, + source, + sourceFileUniqueNames, + visited.expression, + ), + undefined, + visited.arguments + ), + visited, + ), + undefined, + [expression] + ) + } + else { + let expression = simplyfy(((visited as CallExpression).expression as PropertyAccessExpression).expression); + if (shouldMakeLazy) { + expression = context.factory.createArrowFunction( + void 0, + void 0, + [], + void 0, + void 0, + expression + ) + } + return factory.updateCallExpression( + visited as CallExpression, + getPathOfExtension( + context, + importer, + { definition: fluentExtension.tsPlusFile, exportName: fluentExtension.tsPlusExportName }, + source, + sourceFileUniqueNames, + visited.expression, + ), + (visited as CallExpression).typeArguments, + [expression, ...(visited as CallExpression).arguments] + ); + } + } + return visitCallExpression(source, traceInScope, node, visitor, context);; + } + function visitCallExpression(source: SourceFile, traceInScope: Identifier | undefined, node: CallExpression, visitor: Visitor, context: TransformationContext): CallExpression { + const signature = checker.getResolvedSignature(node); + if (signature) { + const params = signature.parameters; + const newArgs: Expression[] = []; + for (let i = 0; i < Math.max(params.length, node.arguments.length); i++) { + if (i < node.arguments.length) { + if (i < params.length && checker.getNodeLinks(node.arguments[i]).tsPlusLazy === true) { + newArgs.push( + context.factory.createArrowFunction( + void 0, + void 0, + [], + void 0, + void 0, + visitNode(node.arguments[i], visitor) as Expression + ) + ); + } else { + newArgs.push( + visitNode(node.arguments[i], visitor) as Expression + ); + } + } else { + const param = params[i]; + if (param.valueDeclaration && (param.valueDeclaration as ParameterDeclaration).isAuto) { + newArgs.push(produceDerivation(checker.getNodeLinks(node).tsPlusParameterDerivations!.get(i)!, context, importer, source, sourceFileUniqueNames)) + } + } + } + if (newArgs.length === params.length - 1) { + if (params[params.length - 1].escapedName === "___tsplusTrace") { + if (traceInScope) { + newArgs.push(traceInScope); + } else { + newArgs.push(getTrace(source, node.expression)); + } + } + } + return context.factory.updateCallExpression( + node, + visitNode(node.expression, visitor) as Expression, + node.typeArguments ? visitNodes(node.typeArguments, visitor) as NodeArray : void 0, + newArgs + ) + } + return visitEachChild(node, visitor, context); + } + } + + function produceTsPlusCallExpression(fn: TsPlusSignature, args: Expression[], context: TransformationContext, importer: TsPlusImporter, source: SourceFile, sourceFileUniqueNames: SourceFileUniqueNames) { + const expr = getPathOfExtension(context, importer, { definition: fn.tsPlusFile, exportName: fn.tsPlusExportName }, source, sourceFileUniqueNames) + return fn.tsPlusPipeable ? factory.createCallExpression( + factory.createCallExpression( + expr, + undefined, + args.slice(1) + ), + undefined, + [args[0]] + ) : factory.createCallExpression( + expr, + undefined, + args + ); + } + + function addSourceFileUniqueNamesVisitor(hoistedStatements: Array, sourceFileUniqueNames: SourceFileUniqueNames, context: TransformationContext) { + return function (node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.FunctionDeclaration: { + const declaration = node as FunctionDeclaration + if (declaration.name && declaration.body) { + const name = declaration.name.escapedText.toString() + if (sourceFileUniqueNames.has(name)) { + const uniqueName = sourceFileUniqueNames.get(name)! + if (uniqueName.isExported) { + hoistedStatements.push( + context.factory.createVariableStatement( + [context.factory.createModifier(SyntaxKind.ExportKeyword), context.factory.createModifier(SyntaxKind.ConstKeyword)], + context.factory.createVariableDeclarationList([ + context.factory.createVariableDeclaration(declaration.name, declaration.exclamationToken, undefined, uniqueName.name) + ], NodeFlags.Const) + ) + ); + } + return context.factory.updateFunctionDeclaration( + declaration, + filter(declaration.modifiers, (mod) => mod.kind !== SyntaxKind.ExportKeyword), + declaration.asteriskToken, + uniqueName.name, + declaration.typeParameters, + declaration.parameters, + declaration.type, + declaration.body + ); + } + } + return node; + } + case SyntaxKind.VariableStatement: { + const variableStatement = node as VariableStatement; + const declaration = variableStatement.declarationList.declarations[0]; + if (declaration && declaration.name && isIdentifier(declaration.name)) { + const name = declaration.name.escapedText.toString(); + if (sourceFileUniqueNames.has(name)) { + const uniqueName = sourceFileUniqueNames.get(name)!; + const updated = [ + context.factory.updateVariableStatement( + variableStatement, + filter((node as VariableStatement).modifiers, (mod) => mod.kind !== SyntaxKind.ExportKeyword), + context.factory.updateVariableDeclarationList(variableStatement.declarationList, [ + context.factory.updateVariableDeclaration( + declaration, + uniqueName.name, + declaration.exclamationToken, + declaration.type, + declaration.initializer + ), + ...variableStatement.declarationList.declarations.slice(1) + ]) + ), + ] + if (uniqueName.isExported) { + updated.push( + context.factory.createVariableStatement( + [context.factory.createModifier(SyntaxKind.ExportKeyword), context.factory.createModifier(SyntaxKind.ConstKeyword)], + context.factory.createVariableDeclarationList([ + context.factory.createVariableDeclaration(declaration.name, declaration.exclamationToken, undefined, uniqueName.name) + ], NodeFlags.Const) + ) + ); + } + return updated; + } + } + return node; + } + case SyntaxKind.ClassDeclaration: { + const classDeclaration = node as ClassDeclaration; + if (classDeclaration.name) { + const name = classDeclaration.name.escapedText.toString() + if (sourceFileUniqueNames.has(name)) { + const uniqueName = sourceFileUniqueNames.get(name)!; + const updated: Node[] = [ + context.factory.updateClassDeclaration( + classDeclaration, + filter((node as ClassDeclaration).modifiers, (mod) => mod.kind !== SyntaxKind.ExportKeyword), + uniqueName.name, + classDeclaration.typeParameters, + classDeclaration.heritageClauses, + classDeclaration.members + ), + context.factory.createAssignment( + context.factory.createPropertyAccessExpression(context.factory.createPropertyAccessExpression(uniqueName.name, context.factory.createIdentifier("constructor")), context.factory.createIdentifier('name')), + context.factory.createStringLiteral(name) + ) + ] + if (uniqueName.isExported) { + updated.push( + context.factory.createVariableStatement( + [context.factory.createModifier(SyntaxKind.ExportKeyword), context.factory.createModifier(SyntaxKind.ConstKeyword)], + context.factory.createVariableDeclarationList([ + context.factory.createVariableDeclaration(classDeclaration.name, undefined, undefined, uniqueName.name) + ], NodeFlags.Const) + ) + ); + } + return updated; + } + } + return node; + } + default: { + return node; + } + } + } + } + + function getPathOfGlobalImport(context: TransformationContext, importer: TsPlusImporter, identifier: Identifier, location: string) { + const factory = context.factory; + const id = importer.get(location); + const node = factory.createPropertyAccessExpression(id, identifier); + (node as ExpressionWithReferencedImport).tsPlusReferencedImport = location; + (node as ExpressionWithReferencedGlobalImport).tsPlusReferencedGlobalImport = location; + return node; + } + + function isExportedWorker(declaration: Node): boolean { + if (!(declaration as HasModifiers).modifiers) { + return false; + } + return (declaration as HasModifiers).modifiers!.findIndex((mod) => mod.kind === SyntaxKind.ExportKeyword) !== -1 + } + + function isExported(declaration: Declaration): boolean { + if (isVariableDeclaration(declaration) && declaration.parent && declaration.parent.parent) { + return isExportedWorker(declaration.parent.parent); + } + return isExportedWorker(declaration); + } + + function getPathOfImplicitOrRule(context: TransformationContext, importer: TsPlusImporter, implicitOrRule: Declaration, source: SourceFile, sourceFileUniqueNames: SourceFileUniqueNames) { + const factory = context.factory; + const sourceExtension = getSourceFileOfNode(implicitOrRule); + // TODO(Mike): carry over proper export name don't rely on the symbol of the declaration being exported + const exportName = implicitOrRule.symbol.escapedName as string; + if (source.fileName === sourceExtension.fileName) { + return sourceFileUniqueNames.get(exportName, isExported(implicitOrRule)).name; + } + let path: string | undefined; + const locationTag = getAllJSDocTags(implicitOrRule, (tag): tag is JSDocTag => tag.tagName.escapedText === "tsplus" && tag.comment?.toString().startsWith("location") === true)[0]; + if (locationTag) { + const match = locationTag.comment!.toString().match(/^location "(.*)"/); + if (match) { + path = match[1] + } + } + if (!path) { + path = getImportLocation(fileMap, sourceExtension.fileName); + } + const id = importer.get(path); + const node = factory.createPropertyAccessExpression( + id, + factory.createIdentifier(exportName) + ); + (node as ExpressionWithReferencedImport).tsPlusReferencedImport = path; + return node; + } + + function getPathOfExtension(context: TransformationContext, importer: TsPlusImporter, extension: { definition: SourceFile; exportName: string; }, source: SourceFile, sourceFileUniqueNames: SourceFileUniqueNames, original?: Node) { + const factory = context.factory; + if (source.fileName === extension.definition.fileName) { + return sourceFileUniqueNames.get(extension.exportName).name; + } + + const def = extension.definition.locals!.get(extension.exportName as __String)! + let path: string | undefined; + for (const decl of def.declarations!) { + const locationTag = getAllJSDocTags(decl, (tag): tag is JSDocTag => tag.tagName.escapedText === "tsplus" && tag.comment?.toString().startsWith("location") === true)[0]; + if (locationTag) { + const match = locationTag.comment!.toString().match(/^location "(.*)"/); + if (match) { + path = match[1] + } + } + } + + if (!path) { + path = getImportLocation(fileMap, extension.definition.fileName); + } + + const id = importer.get(path); + + const identifier = factory.createIdentifier(extension.exportName); + if (original && isPropertyAccessExpression(original)) { + setOriginalNode(identifier, original.name); + } + + const node = factory.createPropertyAccessExpression(id, identifier); + + (node as ExpressionWithReferencedImport).tsPlusReferencedImport = path; + setOriginalNode(node, original); + + return node; + } +} + +type ExpressionWithReferencedImport = T & { tsPlusReferencedImport: string }; + +function isExpressionWithReferencedImport(node: Expression): node is ExpressionWithReferencedImport { + return !!(node as ExpressionWithReferencedImport).tsPlusReferencedImport; +} + +type ExpressionWithReferencedGlobalImport = T & { tsPlusReferencedGlobalImport: string }; + +function isExpressionWithReferencedGlobalImport(node: Expression): node is ExpressionWithReferencedGlobalImport { + return !!(node as ExpressionWithReferencedGlobalImport).tsPlusReferencedGlobalImport; +} diff --git a/src/compiler/transformers/tsplusDeclaration.ts b/src/compiler/transformers/tsplusDeclaration.ts new file mode 100644 index 00000000000..d84e9e9ecda --- /dev/null +++ b/src/compiler/transformers/tsplusDeclaration.ts @@ -0,0 +1,260 @@ +import { + arrayFrom, Bundle, chainBundle, CompilerHost, CompilerOptions, createPrinter, EmitHint, factory, findAncestor, FunctionDeclaration, + getFileMap, getImportLocation, getOrCreateEmitNode, getSourceFileOfNode, Identifier, identity, ImportDeclaration, isClassDeclaration, + isFunctionLikeDeclaration, isIdentifier, isImportDeclaration, isInterfaceDeclaration, isPartOfTypeNode, isPartOfTypeQuery, isQualifiedName, + isTypeAliasDeclaration, mapIterator, Node, or, QualifiedName, removeAllComments, setSyntheticLeadingComments, SourceFile, SyntaxKind, + TransformationContext, TypeChecker, VariableStatement, visitEachChild, Visitor, VisitResult +} from "../_namespaces/ts"; + +/*@internal*/ +class TsPlusGlobalTypeImporter { + readonly imports: Map> = new Map(); + add(path: string, name: string): void { + if (!this.imports.has(path)) { + this.imports.set(path, new Set()); + } + const names = this.imports.get(path)!; + names.add(name); + } + has(path: string, name: string): boolean { + if (!this.imports.has(path)) { + return false; + } + return this.imports.get(path)!.has(name); + } +} + +export function transformTsPlusDeclaration(checker: TypeChecker, options: CompilerOptions, host: CompilerHost): (context: TransformationContext) => (sourceFile: SourceFile | Bundle) => SourceFile | Bundle { + if (options.tsPlusEnabled === false) { + return () => identity; + } + const fileMap: [string, RegExp][] = getFileMap(options, host); + + return function (context: TransformationContext) { + const importer = new TsPlusGlobalTypeImporter(); + return chainBundle(context, transformSourceFile); + function transformSourceFile(node: SourceFile) { + if (node.isDeclarationFile) { + return node; + } + + visitEachChild(node, visitor(node), context); + visitEachChild(node, importVisitor, context); + + const imports: ImportDeclaration[] = [] + importer.imports.forEach((names, path) => { + imports.push( + factory.createImportDeclaration( + undefined, + factory.createImportClause( + false, + undefined, + factory.createNamedImports(arrayFrom(mapIterator(names.values(), (name) => + factory.createImportSpecifier(false, undefined, factory.createIdentifier(name)) + ))) + ), + factory.createStringLiteral(path), + undefined + ) + ); + }); + + for (const statement of node.statements) { + if (isImportDeclaration(statement) && statement.isTsPlusGlobal) { + imports.push(statement); + } + } + + node.tsPlusGlobalImports = imports; + return node; + } + function isTransformable(node: Node): boolean { + const nodeLinks = checker.getNodeLinks(node); + return !!nodeLinks.tsPlusCallExtension || + !!nodeLinks.tsPlusGetterExtension || + !!nodeLinks.tsPlusOptimizedDataFirst || + !!nodeLinks.tsPlusStaticExtension || + !!nodeLinks.isTsPlusOperatorToken || + !!nodeLinks.isFluent + } + function findLeftmostQualifiedName(node: QualifiedName) { + while (true) { + const { left } = node; + if (isQualifiedName(left)) { + node = left; + } else { + return left; + } + } + } + function importVisitor(node: Node): VisitResult { + if (node.kind === SyntaxKind.Identifier) { + let baseName = node as Identifier; + if (isQualifiedName(node.parent) && node.parent.right === node) { + baseName = findLeftmostQualifiedName(node.parent); + } + if (!isTransformable(baseName)) { + const links = checker.getNodeLinks(baseName); + const name = baseName.escapedText as string; + const globalImport = checker.getTsPlusGlobal(name); + if (links.isTsPlusGlobalIdentifier && globalImport && !importer.has(globalImport.moduleSpecifier.text, name)) { + if (isPartOfTypeNode(node) || isPartOfTypeQuery(node) || findAncestor(node, or(isClassDeclaration, isInterfaceDeclaration, isTypeAliasDeclaration))) { + importer.add(globalImport.moduleSpecifier.text, name); + } + } + } + } + else { + visitEachChild(node, importVisitor, context); + } + return node; + } + function visitor(source: SourceFile) { + return function (node: Node): VisitResult { + switch (node.kind) { + case SyntaxKind.VariableStatement: + return visitVariableStatement(source, node as VariableStatement, visitor(source), context) + case SyntaxKind.FunctionDeclaration: + return visitFunctionDeclaration(source, node as FunctionDeclaration, visitor(source), context) + default: + return node; + } + } + } + function visitFunctionDeclaration(_source: SourceFile, node: FunctionDeclaration, _visitor: Visitor, _context: TransformationContext): VisitResult { + if (checker.hasExportedPlusTags(node)) { + const emitNode = getOrCreateEmitNode(node); + if (!emitNode.tsPlusLocationComment) { + const existingJsDoc = node.jsDoc?.slice(-1)[0] ?? factory.createJSDocComment() + const existingTags = existingJsDoc.tags ?? factory.createNodeArray() + const newJsDoc = [ + ...node.jsDoc?.slice(0, -1) ?? [], + factory.createJSDocComment( + existingJsDoc.comment, + existingTags.concat([ + factory.createJSDocUnknownTag( + factory.createIdentifier("tsplus"), + `location "${getImportLocation(fileMap, getSourceFileOfNode(node).fileName)}"` + ) + ]) + ) + ] + const printer = createPrinter() + const newCommentText = newJsDoc.map((jsDoc) => + printer.printNode(EmitHint.Unspecified, jsDoc, null!) + .trim() + .replace(/^\/\*|\*\/$/g, "") + ) + removeAllComments(node); + node.original && removeAllComments(node.original); + setSyntheticLeadingComments( + node, + newCommentText.map(text => ({ + pos: -1, + end: -1, + text, + kind: SyntaxKind.MultiLineCommentTrivia, + hasTrailingNewLine: true + })) + ); + emitNode.tsPlusLocationComment = true; + } + } + return node; + } + function visitVariableStatement(_source: SourceFile, node: VariableStatement, _visitor: Visitor, _context: TransformationContext): VisitResult { + if (node.declarationList.declarations.length > 0) { + const declaration = node.declarationList.declarations[0]; + if (declaration.initializer && checker.isTsPlusMacroCall(declaration.initializer, 'pipeable') && isIdentifier(declaration.name)) { + const targetType = checker.getTypeAtLocation(declaration.initializer.arguments[0]) + if (targetType.symbol && targetType.symbol.valueDeclaration && isFunctionLikeDeclaration(targetType.symbol.valueDeclaration)) { + const signatureDeclaration = targetType.symbol.valueDeclaration + const signatureFluentTags = checker.collectTsPlusFluentTags(signatureDeclaration) + if (signatureFluentTags.length > 0) { + const { target, name } = signatureFluentTags[0] + const existingJsDoc = node.jsDoc?.slice(-1)[0] ?? factory.createJSDocComment() + const existingTags = existingJsDoc.tags ?? factory.createNodeArray() + const newJsDoc = [ + ...node.jsDoc?.slice(0, -1) ?? [], + factory.createJSDocComment( + existingJsDoc.comment, + existingTags.concat([ + factory.createJSDocUnknownTag( + factory.createIdentifier("tsplus"), + `pipeable ${target} ${name}` + ), + factory.createJSDocUnknownTag( + factory.createIdentifier("tsplus"), + `location "${getImportLocation(fileMap, getSourceFileOfNode(node).fileName)}"` + ) + ]) + ) + ] + const printer = createPrinter() + const newCommentText = newJsDoc.map((jsDoc) => + printer.printNode(EmitHint.Unspecified, jsDoc, null!) + .trim() + .replace(/^\/\*|\*\/$/g, "") + ) + removeAllComments(node); + node.original && removeAllComments(node.original); + setSyntheticLeadingComments( + node, + newCommentText.map(text => ({ + pos: -1, + end: -1, + text, + kind: SyntaxKind.MultiLineCommentTrivia, + hasTrailingNewLine: true + })) + ); + getOrCreateEmitNode(node).tsPlusLocationComment = true; + } + } + } else { + const declaration = node.declarationList.declarations[0]; + if (checker.hasExportedPlusTags(declaration)) { + const emitNode = getOrCreateEmitNode(node); + if (!emitNode.tsPlusLocationComment) { + const existingJsDoc = node.jsDoc?.slice(-1)[0] ?? factory.createJSDocComment() + const existingTags = existingJsDoc.tags ?? factory.createNodeArray() + const newJsDoc = [ + ...node.jsDoc?.slice(0, -1) ?? [], + factory.createJSDocComment( + existingJsDoc.comment, + existingTags.concat([ + factory.createJSDocUnknownTag( + factory.createIdentifier("tsplus"), + `location "${getImportLocation(fileMap, getSourceFileOfNode(node).fileName)}"` + ) + ]) + ) + ] + const printer = createPrinter() + const newCommentText = newJsDoc.map((jsDoc) => + printer.printNode(EmitHint.Unspecified, jsDoc, null!) + .trim() + .replace(/^\/\*|\*\/$/g, "") + ) + removeAllComments(node); + node.original && removeAllComments(node.original); + setSyntheticLeadingComments( + node, + newCommentText.map(text => ({ + pos: -1, + end: -1, + text, + kind: SyntaxKind.MultiLineCommentTrivia, + hasTrailingNewLine: true + })) + ); + getOrCreateEmitNode(node).tsPlusLocationComment = true; + } + } + return node; + } + } + return node; + } + } +} diff --git a/src/compiler/transformers/utilities.ts b/src/compiler/transformers/utilities.ts index e6fd5c2346b..95e7715cb01 100644 --- a/src/compiler/transformers/utilities.ts +++ b/src/compiler/transformers/utilities.ts @@ -11,6 +11,8 @@ import { ClassExpression, ClassLikeDeclaration, ClassStaticBlockDeclaration, + combinePaths, + CompilerHost, CompilerOptions, CompoundAssignmentOperator, CoreTransformationContext, @@ -31,6 +33,7 @@ import { getNamespaceDeclarationNode, getNodeForGeneratedName, getNodeId, + getNormalizedAbsolutePath, getOriginalNode, hasDecorators, hasStaticModifier, @@ -82,6 +85,7 @@ import { SyntaxKind, TransformationContext, UnderscoreEscapedMap, + TypeCheckerHost, VariableDeclaration, VariableStatement, } from "../_namespaces/ts"; @@ -101,6 +105,67 @@ export interface ExternalModuleInfo { exportedNames: Identifier[] | undefined; // all exported names in the module, both local and reexported exportEquals: ExportAssignment | undefined; // an export= declaration if one was present hasExportStarsToExportValues: boolean; // whether this module contains export* + generatedExportSpecifiers?: Map; +} + +export function getImportLocation(fileMap: [string, RegExp][], source: string) { + for (const [path, reg] of fileMap) { + if (source.match(reg)) { + return source.replace(reg, path) + } + } + throw new Error(`cannot get import path for file: ${source} (Make sure to add it in your tsplus.config.json)`) +} + +export function getTraceLocation(traceMap: [string, RegExp][], source: string) { + for (const [path, reg] of traceMap) { + if (source.match(reg)) { + return source.replace(reg, path) + } + } + return source +} + +export function getFileMap(options: CompilerOptions, host: CompilerHost | TypeCheckerHost) { + const fileMap: [string, RegExp][] = [] + if (options.configFilePath && options.tsPlusConfig) { + const content = host.readFile?.(getNormalizedAbsolutePath(combinePaths(options.configFilePath, "..", options.tsPlusConfig), void 0)) + if (content) { + try { + const parsed = JSON.parse(content) + if ("importMap" in parsed && typeof parsed["importMap"] === "object") { + for (const key of Object.keys(parsed["importMap"])) { + if (typeof parsed["importMap"][key] === "string") { + fileMap.push([parsed["importMap"][key], new RegExp(key, "g")]); + } + } + } + } + catch { } + } + } + return fileMap +} + +export function getTraceMap(options: CompilerOptions, host: CompilerHost) { + const traceMap: [string, RegExp][] = [] + if (options.configFilePath && options.tsPlusConfig) { + const content = host.readFile(getNormalizedAbsolutePath(combinePaths(options.configFilePath, "..", options.tsPlusConfig), void 0)) + if (content) { + try { + const parsed = JSON.parse(content) + if ("traceMap" in parsed && typeof parsed["traceMap"] === "object") { + for (const key of Object.keys(parsed["traceMap"])) { + if (typeof parsed["traceMap"][key] === "string") { + traceMap.push([parsed["traceMap"][key], new RegExp(key, "g")]); + } + } + } + } + catch { } + } + } + return traceMap } function containsDefaultReference(node: NamedImportBindings | undefined) { @@ -161,6 +226,9 @@ export function getImportNeedsImportDefaultHelper(node: ImportDeclaration): bool export function collectExternalModuleInfo(context: TransformationContext, sourceFile: SourceFile, resolver: EmitResolver, compilerOptions: CompilerOptions): ExternalModuleInfo { const externalImports: (ImportDeclaration | ImportEqualsDeclaration | ExportDeclaration)[] = []; const exportSpecifiers = createMultiMap(); + // TSPLUS EXTENSION START + const generatedExportSpecifiers = new Map(); + // TSPLUS EXTENSION END const exportedBindings: Identifier[][] = []; const uniqueExports = new Map(); let exportedNames: Identifier[] | undefined; @@ -290,14 +358,24 @@ export function collectExternalModuleInfo(context: TransformationContext, source externalImports.unshift(externalHelpersImportDeclaration); } - return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames, externalHelpersImportDeclaration }; + return { externalImports, exportSpecifiers, exportEquals, hasExportStarsToExportValues, exportedBindings, exportedNames, externalHelpersImportDeclaration, generatedExportSpecifiers }; function addExportedNamesForExportDeclaration(node: ExportDeclaration) { for (const specifier of cast(node.exportClause, isNamedExports).elements) { if (!uniqueExports.get(idText(specifier.name))) { const name = specifier.propertyName || specifier.name; if (!node.moduleSpecifier) { - exportSpecifiers.add(idText(name), specifier); + // TSPLUS EXTENSION START + if (isGeneratedIdentifier(name)) { + if (!generatedExportSpecifiers.has(name)) { + generatedExportSpecifiers.set(name, []); + } + generatedExportSpecifiers.get(name)!.push(specifier) + } + else { + // TSPLUS EXTENSION END + exportSpecifiers.add(idText(name), specifier); + } } const decl = resolver.getReferencedImportDeclaration(name) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 0a43afe978e..263284cf309 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,5 +1,6 @@ import { BaseNodeFactory, + CheckMode, CreateSourceFileOptions, EmitHelperFactory, GetCanonicalFileName, @@ -912,6 +913,7 @@ export interface Node extends ReadonlyTextRange { // `locals` and `nextContainer` have been moved to `LocalsContainer` // `flowNode` has been moved to `FlowContainer` // see: https://github.com/microsoft/TypeScript/pull/51682 + /** @internal */ tsPlusName?: string; } export interface JSDocContainer extends Node { @@ -1831,6 +1833,7 @@ export type SignatureDeclaration = export interface CallSignatureDeclaration extends SignatureDeclarationBase, TypeElement, LocalsContainer { readonly kind: SyntaxKind.CallSignature; + tsPlusMacroTags?: string[]; } export interface ConstructSignatureDeclaration extends SignatureDeclarationBase, TypeElement, LocalsContainer { @@ -1846,6 +1849,19 @@ export interface VariableDeclaration extends NamedDeclaration, JSDocContainer { readonly exclamationToken?: ExclamationToken; // Optional definite assignment assertion readonly type?: TypeNode; // Optional type annotation readonly initializer?: Expression; // Optional initializer + isTsPlusImplicit: boolean; + tsPlusDeriveTags?: string[]; + tsPlusPipeableTags?: TsPlusPrioritizedExtensionTag[]; + tsPlusFluentTags?: TsPlusPrioritizedExtensionTag[]; + tsPlusStaticTags?: TsPlusExtensionTag[]; + tsPlusGetterTags?: TsPlusExtensionTag[]; + tsPlusOperatorTags?: TsPlusPrioritizedExtensionTag[]; + tsPlusPipeableOperatorTags?: TsPlusPrioritizedExtensionTag[]; + tsPlusMacroTags?: string[]; + tsPlusUnifyTags?: string[]; + tsPlusIndexTags?: string[]; + tsPlusPipeableIndexTags?: string[]; + tsPlusValidFluent?: boolean } /** @internal */ @@ -1866,6 +1882,7 @@ export interface ParameterDeclaration extends NamedDeclaration, JSDocContainer { readonly questionToken?: QuestionToken; // Present on optional parameter readonly type?: TypeNode; // Optional type annotation readonly initializer?: Expression; // Optional initializer + isAuto?: boolean } export interface BindingElement extends NamedDeclaration, FlowContainer { @@ -2051,6 +2068,20 @@ export interface FunctionDeclaration extends FunctionLikeDeclarationBase, Declar readonly modifiers?: NodeArray; readonly name?: Identifier; readonly body?: FunctionBody; + // TSPLUS BEGIN + readonly tsPlusDeriveTags?: string[]; + readonly tsPlusPipeableTags?: TsPlusPrioritizedExtensionTag[]; + readonly tsPlusFluentTags?: TsPlusPrioritizedExtensionTag[]; + readonly tsPlusStaticTags?: TsPlusExtensionTag[]; + readonly tsPlusGetterTags?: TsPlusExtensionTag[]; + readonly tsPlusOperatorTags?: TsPlusPrioritizedExtensionTag[]; + readonly tsPlusPipeableOperatorTags?: TsPlusPrioritizedExtensionTag[]; + readonly tsPlusMacroTags?: string[]; + readonly tsPlusUnifyTags?: string[]; + readonly tsPlusIndexTags?: string[]; + readonly tsPlusPipeableIndexTags?: string[]; + readonly tsPlusValidFluent?: boolean + // TSPLUS END } export interface MethodSignature extends SignatureDeclarationBase, TypeElement, LocalsContainer { @@ -3513,6 +3544,14 @@ export interface ClassDeclaration extends ClassLikeDeclarationBase, DeclarationS readonly modifiers?: NodeArray; /** May be undefined in `export default class { ... }`. */ readonly name?: Identifier; + + // TSPLUS START + readonly tsPlusTypeTags?: string[]; + readonly tsPlusCompanionTags?: string[]; + readonly tsPlusStaticTags?: TsPlusExtensionTag[]; + readonly tsPlusDeriveTags?: string[]; + readonly tsPlusNoInheritTags?: string[]; + // TSPLUS END } export interface ClassExpression extends ClassLikeDeclarationBase, PrimaryExpression { @@ -3543,6 +3582,12 @@ export interface InterfaceDeclaration extends DeclarationStatement, JSDocContain readonly typeParameters?: NodeArray; readonly heritageClauses?: NodeArray; readonly members: NodeArray; + // TSPLUS BEGIN + readonly tsPlusTypeTags?: string[]; + readonly tsPlusDeriveTags?: string[]; + readonly tsPlusNoInheritTags?: string[]; + readonly tsPlusCompanionTags?: string[]; + // TSPLUS END } export interface HeritageClause extends Node { @@ -3558,6 +3603,11 @@ export interface TypeAliasDeclaration extends DeclarationStatement, JSDocContain readonly name: Identifier; readonly typeParameters?: NodeArray; readonly type: TypeNode; + // TSPLUS BEGIN + readonly tsPlusTypeTags?: string[]; + readonly tsPlusCompanionTags?: string[]; + readonly tsPlusNoInheritTags?: string[]; + // TSPLUS END } export interface EnumMember extends NamedDeclaration, JSDocContainer { @@ -3665,6 +3715,9 @@ export interface ImportDeclaration extends Statement { /** If this is not a StringLiteral it will be a grammar error. */ readonly moduleSpecifier: Expression; readonly assertClause?: AssertClause; + // TSPLUS BEGIN + readonly isTsPlusGlobal: boolean; + // TSPLUS END } export type NamedImportBindings = @@ -3914,6 +3967,284 @@ export interface JSDocTag extends Node { readonly comment?: string | NodeArray; } +export interface TsPlusJSDocDeriveTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `derive ${string}` +} + +export interface TsPlusJSDocImplicitTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `implicit` +} + +export interface TsPlusJSDocTypeTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `type ${string}` +} + +export interface TsPlusJSDocUnifyTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `unify ${string}` +} + +export interface TsPlusJSDocIndexTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `index ${string}` +} + +export interface TsPlusJSDocFluentTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `fluent ${string} ${string}` +} + +export interface TsPlusJSDocGlobalTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `global` +} + +export interface TsPlusExtensionTag { + readonly tagType: string; + readonly target: string; + readonly name: string; +} + +export interface TsPlusPrioritizedExtensionTag extends TsPlusExtensionTag { + readonly priority: number; +} + +export interface TsPlusJSDocPipeableTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `pipeable ${string} ${string}` +} + +export interface TsPlusJSDocGetterTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `getter ${string} ${string}` +} + +export interface TsPlusJSDocStaticTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `static ${string} ${string}` +} + +export interface TsPlusJSDocOperatorTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `operator ${string} ${string}` +} + +export interface TsPlusJSDocMacroTag extends JSDocTag { + readonly parent: JSDoc | JSDocTypeLiteral; + readonly tagName: Identifier; + readonly comment: `macro ${string}` +} + +export type TsPlusMacroCallExpression = CallExpression & { __tsplus_brand: K }; + +export const enum TsPlusSymbolTag { + Fluent = "TsPlusFluentSymbol", + StaticFunction = "TsPlusStaticFunctionSymbol", + StaticValue = "TsPlusStaticValueSymbol", + UnresolvedStatic = "TsPlusUnresolvedStatic", + Getter = "TsPlusGetterSymbol", + GetterVariable = "TsPlusGetterVariableSymbol", + PipeableMacro = "TsPlusPipeableMacroSymbol", + PipeableIdentifier = "TsPlusPipeableSymbol", + PipeableDeclaration = "TsPlusPipeableDeclarationSymbol" +} + + +export interface TsPlusPipeableDeclarationSymbol extends TransientSymbol { + tsPlusTag: TsPlusSymbolTag.PipeableDeclaration + tsPlusDeclaration: FunctionDeclaration | VariableDeclarationWithFunction | VariableDeclarationWithFunctionType; +} + +export interface TsPlusPipeableIdentifierSymbol extends TransientSymbol { + tsPlusTag: TsPlusSymbolTag.PipeableIdentifier; + tsPlusDeclaration: FunctionDeclaration | VariableDeclarationWithFunction | VariableDeclarationWithFunctionType; + tsPlusTypeName: string; + tsPlusName: string; + getTsPlusDataFirstType(): Type; +} + +export interface TsPlusPipeableMacroSymbol extends TransientSymbol { + tsPlusTag: TsPlusSymbolTag.PipeableMacro; + tsPlusDeclaration: VariableDeclaration; + tsPlusDataFirst: FunctionDeclaration | ArrowFunction | FunctionExpression; + tsPlusSourceFile: SourceFile; + tsPlusExportName: string; +} + +export interface TsPlusFluentSymbol extends TransientSymbol { + tsPlusTag: TsPlusSymbolTag.Fluent; + tsPlusName: string; + tsPlusResolvedSignatures: TsPlusSignature[]; +} + +export type VariableDeclarationWithFunction = Omit & { name: Identifier, initializer: ArrowFunction | FunctionExpression }; + +export type VariableDeclarationWithFunctionType = Omit & { name: Identifier, type: FunctionTypeNode }; + +export type VariableDeclarationWithIdentifier = VariableDeclaration & { name: Identifier }; + +export type ClassDeclarationWithIdentifier = ClassDeclaration & { name: Identifier }; + +export interface TsPlusUnresolvedStaticSymbol extends TransientSymbol { + tsPlusTag: TsPlusSymbolTag.UnresolvedStatic + tsPlusDeclaration: VariableDeclaration + tsPlusName: string +} + +export interface TsPlusStaticFunctionSymbol extends TransientSymbol { + tsPlusTag: TsPlusSymbolTag.StaticFunction; + tsPlusDeclaration: FunctionDeclaration | VariableDeclaration; + tsPlusResolvedSignatures: Signature[]; + tsPlusName: string; +} + +export interface TsPlusStaticValueSymbol extends TransientSymbol { + tsPlusTag: TsPlusSymbolTag.StaticValue; + tsPlusResolvedSignatures: TsPlusSignature[]; + tsPlusName: string; + tsPlusDeclaration: (VariableDeclaration | ClassDeclaration) & { name: Identifier }; +} + +export interface TsPlusGetterSymbol extends TransientSymbol { + tsPlusTag: TsPlusSymbolTag.Getter; + tsPlusSelfType: Type; + tsPlusDeclaration: FunctionDeclaration; + tsPlusName: string; +} + +export interface TsPlusGetterVariableSymbol extends TransientSymbol { + tsPlusTag: TsPlusSymbolTag.GetterVariable; + tsPlusDeclaration: VariableDeclaration & { name: Identifier }; + tsPlusSelfType: Type; + tsPlusName: string; +} + +export type TsPlusSymbol = + | TsPlusFluentSymbol + | TsPlusStaticFunctionSymbol + | TsPlusStaticValueSymbol + | TsPlusGetterSymbol + | TsPlusGetterVariableSymbol + | TsPlusPipeableMacroSymbol + | TsPlusPipeableIdentifierSymbol + | TsPlusPipeableDeclarationSymbol; + +export interface TsPlusFluentExtension { + patched: Symbol; + types: { type: Type, signatures: readonly TsPlusSignature[] }[]; + signatures: readonly TsPlusSignature[]; +} + +export interface TsPlusPipeableExtension { + declaration: FunctionDeclaration | VariableDeclarationWithFunction | VariableDeclarationWithFunctionType; + definition: SourceFile; + exportName: string; + typeName: string; + funcName: string; + getTypeAndSignatures(): [Type, TsPlusSignature[]]; +} + +export interface TsPlusUnresolvedStaticExtension { + symbol: Symbol; + declaration: VariableDeclaration | ClassDeclaration; + definition: SourceFile; + target: string; + name: string; + exportName: string; +} + +export interface TsPlusUnresolvedFluentExtensionDefinition { + declaration: (VariableDeclaration & { name: Identifier }) | FunctionDeclaration; + exportName: string; + definition: SourceFile; + priority: number; +} + +export interface TsPlusUnresolvedPipeableExtensionDefinition { + declaration: (VariableDeclaration & { name: Identifier }) | FunctionDeclaration; + exportName: string; + definition: SourceFile; + priority: number; + getTypeAndSignatures(): [Type, TsPlusSignature[]]; +} + +export interface TsPlusUnresolvedPipeableExtension { + definition: Set; + target: string; + name: string; +} + +export interface TsPlusUnresolvedFluentExtension { + definition: Set; + target: string; + name: string; +} + +export interface TsPlusStaticFunctionExtension { + patched: Symbol; + definition: SourceFile; + exportName: string; + type: Type; +} + +export interface TsPlusStaticValueExtension { + patched: Symbol; + definition: SourceFile; + exportName: string; + type: Type; +} + +export interface TsPlusGetterExtension { + patched: (node: Expression) => TsPlusSymbol | undefined + definition: SourceFile + exportName: string + declaration: FunctionDeclaration | VariableDeclarationWithIdentifier +} + +export interface TsPlusOperatorExtension { + patched: Symbol; + definition: SourceFile; + exportName: string; + priority: number; +} + +export interface TsPlusGlobalImport { + declaration: ImportDeclaration; + importSpecifier: ImportSpecifier; + moduleSpecifier: StringLiteral; +} + +export interface TsPlusType extends Type { + tsPlusSymbol: TsPlusSymbol; +} + +export interface TsPlusSignature extends Signature { + tsPlusTag: "TsPlusSignature"; + tsPlusFile: SourceFile; + tsPlusExportName: string; + tsPlusDeclaration?: Declaration; + tsPlusPipeable?: boolean; + tsPlusOriginal: Signature +} + +export interface TsPlusUniqueIdentifier extends Identifier { + tsPlusUniqueIdentifier: true +} + export interface JSDocLink extends Node { readonly kind: SyntaxKind.JSDocLink; readonly name?: EntityName | JSDocMemberName; @@ -4364,6 +4695,25 @@ export interface SourceFile extends Declaration, LocalsContainer { /** @internal */ exportedModulesFromDeclarationEmit?: ExportedModulesFromDeclarationEmit; /** @internal */ endFlowNode?: FlowNode; + + // TSPLUS EXTENSION START + tsPlusImportAs?: () => string | undefined; + tsPlusGlobalImports?: ImportDeclaration[]; + tsPlusContext: { + type: (InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration)[]; + companion: (InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration)[]; + fluent: (VariableDeclarationWithIdentifier | FunctionDeclaration)[]; + pipeable: (VariableDeclarationWithIdentifier | FunctionDeclaration)[]; + operator: (VariableDeclarationWithIdentifier | FunctionDeclaration)[]; + pipeableOperator: (VariableDeclarationWithIdentifier | FunctionDeclaration)[]; + static: (VariableDeclarationWithIdentifier | FunctionDeclaration | ClassDeclarationWithIdentifier)[]; + getter: (VariableDeclarationWithIdentifier | FunctionDeclaration)[]; + unify: FunctionDeclaration[]; + index: (VariableDeclarationWithIdentifier | FunctionDeclaration)[]; + pipeableIndex: (VariableDeclarationWithIdentifier | FunctionDeclaration)[]; + noInherit: (InterfaceDeclaration | TypeAliasDeclaration | ClassDeclaration)[]; + } + // TSPLUS EXTENSION END } /** @internal */ @@ -4857,6 +5207,18 @@ export interface CustomTransformers { afterDeclarations?: (TransformerFactory | CustomTransformerFactory)[]; } +// TSPLUS START +export type ExternalTransformers = + | TransformerFactory + | ExternalCustomTransformers + +export interface ExternalCustomTransformers { + before?: TransformerFactory[] | TransformerFactory; + after?: TransformerFactory[] | TransformerFactory; + afterDeclarations?: TransformerFactory[] | TransformerFactory +} +// TSPLUS END + /** @internal */ export interface EmitTransformers { scriptTransformers: readonly TransformerFactory[]; @@ -5227,6 +5589,41 @@ export interface TypeChecker { /** @internal */ getTypeOnlyAliasDeclaration(symbol: Symbol): TypeOnlyAliasDeclaration | undefined; /** @internal */ getMemberOverrideModifierStatus(node: ClassLikeDeclaration, member: ClassElement): MemberOverrideStatus; /** @internal */ isTypeParameterPossiblyReferenced(tp: TypeParameter, node: Node): boolean; + + // TSPLUS START + getExtensions(selfNode: Expression): Map + getFluentExtension(target: Type, name: string): Type | undefined + getGetterExtension(target: Type, name: string): { definition: SourceFile, exportName: string } | undefined + getGetterCompanionExtension(target: Type, name: string): { definition: SourceFile, exportName: string } | undefined + getStaticExtension(target: Type, name: string): TsPlusStaticFunctionExtension | TsPlusStaticValueExtension | undefined + getStaticCompanionExtension(target: Type, name: string): TsPlusStaticFunctionExtension | TsPlusStaticValueExtension | undefined + isPipeCall(node: CallExpression): boolean + getCallExtension(node: Node): TsPlusStaticFunctionExtension | undefined + isTailRec(node: Node): boolean + cloneSymbol(symbol: Symbol): Symbol + getTextOfBinaryOp(kind: SyntaxKind): string | undefined + /* @internal */ getInstantiatedTsPlusSignature(declaration: Declaration, args: Expression[], checkMode: CheckMode | undefined): Signature + getIndexAccessExpressionCache(): Map + isTsPlusMacroCall(node: Node, macro: K): node is TsPlusMacroCallExpression + isTsPlusMacroGetter(node: Node, macro: string): boolean + isCompanionReference(node: Expression): boolean + collectTsPlusFluentTags(statement: Declaration): readonly TsPlusPrioritizedExtensionTag[] + hasExportedPlusTags(statement: Declaration): boolean; + getFluentExtensionForPipeableSymbol(symbol: TsPlusPipeableIdentifierSymbol): TsPlusFluentExtension | undefined + getPrimitiveTypeName(type: Type): string | undefined + getResolvedOperator(node: BinaryExpression): Signature | undefined + getNodeLinks(node: Node): NodeLinks + getTsPlusFiles(): Map> + getTsPlusGlobalImports(): Map + collectTsPlusMacroTags(statement: Declaration): readonly string[] + getTsPlusGlobals(): Symbol[]; + getTsPlusGlobal(name: string): TsPlusGlobalImport | undefined; + findAndCheckDoAncestor(node: Node): void; + getTsPlusExtensionsAtLocation(node: Node): TsPlusExtensionTag[]; + getTsPlusSymbolAtLocation(node: Node): TsPlusSymbol | undefined; + getExtensionsForDeclaration(node: Declaration): TsPlusExtensionTag[] + getSymbolLinks(symbol: Symbol): SymbolLinks + // TSPLUS END } /** @internal */ @@ -5803,6 +6200,13 @@ export interface SymbolLinks { tupleLabelDeclaration?: NamedTupleMember | ParameterDeclaration; // Declaration associated with the tuple's label accessibleChainCache?: Map; filteredIndexSymbolCache?: Map //Symbol with applicable declarations + // TSPLUS START + tsPlusTypeAndImplicitTags?: { + type: Type, + tags: Set + } + isPossibleCompanionReference?: boolean + // TSPLUS END } /** @internal */ @@ -5845,7 +6249,6 @@ export interface TransientSymbolLinks extends SymbolLinks { checkFlags: CheckFlags; } -/** @internal */ export interface TransientSymbol extends Symbol { links: TransientSymbolLinks; } @@ -5924,7 +6327,6 @@ export interface PatternAmbientModule { symbol: Symbol; } -/** @internal */ export const enum NodeCheckFlags { None = 0, TypeChecked = 1 << 0, // Node has been type checked @@ -5954,7 +6356,11 @@ export const enum NodeCheckFlags { InCheckIdentifier = 1 << 24, } -/** @internal */ +export interface Rule { + typeTag: string + paramActions: string[] +} + export interface NodeLinks { flags: NodeCheckFlags; // Set of flags specific to Node resolvedType?: Type; // Cached type of type node @@ -5983,6 +6389,36 @@ export interface NodeLinks { declarationRequiresScopeChange?: boolean; // Set by `useOuterVariableScopeInParameter` in checker when downlevel emit would change the name resolution scope inside of a parameter. serializedTypes?: Map; // Collection of types serialized at this location decoratorSignature?: Signature; // Signature for decorator as if invoked by the runtime. + // TSPLUS EXTENSION START + isTsPlusOperatorToken?: boolean; + tsPlusCallExtension?: TsPlusStaticFunctionExtension; + tsPlusStaticExtension?: TsPlusStaticFunctionExtension; + tsPlusGetterExtension?: TsPlusGetterExtension + tsPlusDataFirstDeclaration?: FunctionDeclaration | ArrowFunction | FunctionExpression; + tsPlusOptimizedDataFirst?: { definition: SourceFile, exportName: string }; + tsPlusResolvedType?: Type; + isTsPlusGlobalIdentifier?: boolean; + tsPlusDerivation?: Derivation; + tsPlusParameterDerivations?: Map; + tsPlusTags?: string[]; + isFluent?: true; + isFluentCall?: true; + uniqueNames?: Set; + needsUniqueNameInSope?: boolean; + uniqueNameInScope?: Identifier; + tsPlusPipeableExtension?: TsPlusPipeableExtension; + tsPlusDoBindType?: [CallExpression, Type]; + tsPlusDoBindTypes?: [CallExpression, Type][]; + isTsPlusTailRec?: boolean; + isTsPlusDoCall?: boolean; + isTsPlusDoReturnBound?: boolean; + tsPlusDoFunctions?: { + map: TsPlusSignature + flatMap: TsPlusSignature + }; + tsPlusLazy?: boolean; + tsPlusSymbol?: TsPlusSymbol; + // TSPLUS EXTENSION END } /** @internal */ @@ -5990,7 +6426,67 @@ export interface SerializedTypeEntry { node: TypeNode; truncating?: boolean; addedLength: number; -} + serializedTypes?: Map; // Collection of types serialized at this location +} + +// TSPLUS EXTENSION START +export type Derivation = FromBlockScope | FromImplicitScope | FromRule | FromObjectStructure | FromTupleStructure | FromIntersectionStructure | InvalidDerivation | EmptyObjectDerivation | FromPriorDerivation | FromLiteral + +export interface FromBlockScope { + readonly _tag: "FromBlockScope" + readonly type: Type + readonly implicit: NamedDeclaration & { name: Identifier } +} +export interface FromImplicitScope { + readonly _tag: "FromImplicitScope" + readonly type: Type + readonly implicit: Declaration +} +export interface FromPriorDerivation { + readonly _tag: "FromPriorDerivation" + readonly type: Type + readonly derivation: Derivation +} +export interface FromRule { + readonly _tag: "FromRule" + readonly type: Type + readonly rule: Declaration + readonly arguments: Derivation[] + readonly usedBy: FromPriorDerivation[] + readonly lazyRule: Declaration | undefined +} +export interface FromObjectStructure { + readonly _tag: "FromObjectStructure" + readonly type: Type + readonly fields: { + prop: Symbol + value: Derivation + }[] +} +export interface FromTupleStructure { + readonly _tag: "FromTupleStructure" + readonly type: Type + readonly fields: Derivation[] +} +export interface FromIntersectionStructure { + readonly _tag: "FromIntersectionStructure" + readonly type: Type + readonly fields: Derivation[] +} +export interface InvalidDerivation { + readonly _tag: "InvalidDerivation" + readonly type: Type +} +export interface EmptyObjectDerivation { + readonly _tag: "EmptyObjectDerivation" + readonly type: Type +} +export interface FromLiteral { + readonly _tag: "FromLiteral" + readonly type: Type + readonly value: string | number +} +// TSPLUS EXTENSION END export const enum TypeFlags { Any = 1 << 0, @@ -6108,6 +6604,9 @@ export interface Type { immediateBaseConstraint?: Type; // Immediate base constraint cache /** @internal */ widened?: Type; // Cached widened form of the type + // TSPLUS BEGIN + tsPlusUnified?: boolean + // TSPLUS END } /** @internal */ @@ -6928,6 +7427,11 @@ export interface PluginImport { name: string; } +export interface TransformerImport { + name: string + position?: "before" | "after" | "afterDeclaration" +} + export interface ProjectReference { /** A normalized path on disk */ path: string; @@ -6962,7 +7466,7 @@ export enum PollingWatchKind { FixedChunkSize, } -export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | null | undefined; +export type CompilerOptionsValue = string | number | boolean | (string | number)[] | string[] | MapLike | PluginImport[] | ProjectReference[] | TransformerImport[] | null | undefined; export interface CompilerOptions { /** @internal */ all?: boolean; @@ -7111,6 +7615,11 @@ export interface CompilerOptions { useDefineForClassFields?: boolean; [option: string]: CompilerOptionsValue | TsConfigSourceFile | undefined; + + tsPlusConfig?: string; + tsPlusTypes?: string[]; + tsPlusEnabled?: boolean; + transformers?: TransformerImport[]; } export interface WatchOptions { @@ -7831,6 +8340,8 @@ export interface EmitNode { identifierTypeArguments?: NodeArray; // Only defined on synthesized identifiers. Though not syntactically valid, used in emitting diagnostics, quickinfo, and signature help. autoGenerate: AutoGenerateInfo | undefined; // Used for auto-generated identifiers and private identifiers. generatedImportReference?: ImportSpecifier; // Reference to the generated import specifier this identifier refers to + tsPlusPipeableComment?: boolean; + tsPlusLocationComment?: boolean; } /** @internal */ @@ -8923,6 +9434,11 @@ export interface NodeFactory { */ cloneNode(node: T): T; /** @internal */ updateModifiers(node: T, modifiers: readonly Modifier[] | ModifierFlags | undefined): T; + + // TSPLUS EXTENSION START + /** Create a unique name based on the supplied text. */ + createTsPlusUniqueName(text: string, flags?: GeneratedIdentifierFlags): TsPlusUniqueIdentifier; + // TSPLUS EXTENSION END } /** @internal */ diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 57c7bf9aa83..2e9f3fe0de2 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -552,7 +552,7 @@ export const resolvingEmptyArray: never[] = []; export const externalHelpersModuleNameText = "tslib"; /** @internal */ -export const defaultMaximumTruncationLength = 160; +export const defaultMaximumTruncationLength = 1_000; /** @internal */ export const noTruncationMaximumTruncationLength = 1_000_000; @@ -1668,6 +1668,9 @@ export function forEachEnclosingBlockScopeContainer(node: Node, cb: (container: // text of the expression in the computed property. /** @internal */ export function declarationNameToString(name: DeclarationName | QualifiedName | undefined) { + if (name?.tsPlusName) { + return name.tsPlusName; + } return !name || getFullWidth(name) === 0 ? "(Missing)" : getTextOfNode(name); } diff --git a/src/compiler/utilitiesPublic.ts b/src/compiler/utilitiesPublic.ts index 8a11f5f0f97..bec563a6d3e 100644 --- a/src/compiler/utilitiesPublic.ts +++ b/src/compiler/utilitiesPublic.ts @@ -279,6 +279,7 @@ import { UnparsedNode, UnparsedTextLike, VariableDeclaration, + TsPlusUniqueIdentifier, } from "./_namespaces/ts"; export function isExternalModuleNameRelative(moduleName: string): boolean { @@ -2577,3 +2578,9 @@ export function isRestParameter(node: ParameterDeclaration | JSDocParameterTag): const type = isJSDocParameterTag(node) ? (node.typeExpression && node.typeExpression.type) : node.type; return (node as ParameterDeclaration).dotDotDotToken !== undefined || !!type && type.kind === SyntaxKind.JSDocVariadicType; } + +// TSPLUS EXTENSION START +export function isTsPlusUniqueIdentifier(node: Identifier): node is TsPlusUniqueIdentifier { + return !!(node as TsPlusUniqueIdentifier).tsPlusUniqueIdentifier; +} +// TSPLUS EXTENSION END \ No newline at end of file diff --git a/src/lib/libs.json b/src/lib/libs.json index 82ba97aa35f..e8464d459f6 100644 --- a/src/lib/libs.json +++ b/src/lib/libs.json @@ -79,7 +79,9 @@ "es2021.full", "es2022.full", "es2023.full", - "esnext.full" + "esnext.full", + // Extension + "tsplus" ], "paths": { "dom.generated": "lib.dom.d.ts", diff --git a/src/lib/tsplus.d.ts b/src/lib/tsplus.d.ts new file mode 100644 index 00000000000..4258005bd09 --- /dev/null +++ b/src/lib/tsplus.d.ts @@ -0,0 +1,22 @@ +/// + +declare type PipeableShift = A extends [infer X, ...infer Rest] ? Rest : never +declare type PipeableFirst = A extends [infer X, ...infer Rest] ? X : never + +/** + * @tsplus macro pipeable + */ +declare function Pipeable any>(f: F): (...rest: PipeableShift>) => (self: PipeableFirst>) => ReturnType + +/** + * @tsplus macro Derive + */ +declare function Derive(explain?: "explain"): A + +/** + * @tsplus macro Do + */ +declare function Do(f: (_: { + /** @tsplus macro Bind */ + (a: X): X +}) => A): A \ No newline at end of file diff --git a/src/server/protocol.ts b/src/server/protocol.ts index 1ac6e1f495d..51bfe5a7f05 100644 --- a/src/server/protocol.ts +++ b/src/server/protocol.ts @@ -16,6 +16,7 @@ import { TextInsertion, TodoComment, TodoCommentDescriptor, + TransformerImport, TypeAcquisition, } from "./_namespaces/ts"; @@ -3639,6 +3640,7 @@ export interface CompilerOptions { /** Paths used to used to compute primary types search locations */ typeRoots?: string[]; [option: string]: CompilerOptionsValue | undefined; + transformers?: TransformerImport[]; } export const enum JsxEmit { diff --git a/src/services/completions.ts b/src/services/completions.ts index 4193e651a03..734b05da5a7 100644 --- a/src/services/completions.ts +++ b/src/services/completions.ts @@ -362,6 +362,9 @@ import { UserPreferences, VariableDeclaration, walkUpParenthesizedExpressions, + isClassDeclaration, + isInterfaceDeclaration, + isTypeAliasDeclaration, } from "./_namespaces/ts"; import { StringCompletions } from "./_namespaces/ts.Completions"; @@ -2204,6 +2207,15 @@ export function getCompletionEntriesFromSymbols( // module imports then the global keywords will be filtered out so auto // import suggestions will win in the completion const symbolOrigin = skipAlias(symbol, typeChecker); + // TSPLUS EXTENSION START + const isCompanion = !!find( + [...symbol.declarations ?? [], ...symbolOrigin.declarations ?? []], + (decl) => (isInterfaceDeclaration(decl) || isTypeAliasDeclaration(decl) || isClassDeclaration(decl)) && !!decl.tsPlusCompanionTags && decl.tsPlusCompanionTags.length > 0 + ) + if (isCompanion) { + return true; + } + // TSPLUS EXTENSION END // We only want to filter out the global keywords // Auto Imports are not available for scripts so this conditional is always false if (!!sourceFile.externalModuleIndicator @@ -2944,6 +2956,19 @@ function getCompletionData( return createModuleSpecifierResolutionHost(isFromPackageJson ? host.getPackageJsonAutoImportProvider!()! : program, host); }); + // TSPLUS EXTENSION START + typeChecker.findAndCheckDoAncestor(node); + let currentBinaryAncestor: BinaryExpression | undefined = findAncestor(node, isBinaryExpression); + let binaryExpressionParent = currentBinaryAncestor; + while (currentBinaryAncestor) { + binaryExpressionParent = currentBinaryAncestor; + currentBinaryAncestor = findAncestor(currentBinaryAncestor.parent, isBinaryExpression); + } + if (binaryExpressionParent) { + typeChecker.getTypeAtLocation(binaryExpressionParent); + } + // TSPLUS EXTENSION END + if (isRightOfDot || isRightOfQuestionDot) { getTypeScriptMemberSymbols(); } @@ -3158,6 +3183,17 @@ function getCompletionData( symbols.push(...filter(getPropertiesForCompletion(type, typeChecker), s => typeChecker.isValidPropertyAccessForCompletions(propertyAccess, type, s))); } + // TSPLUS EXTENSION START + if (isExpression(node)) { + const extensions = typeChecker.getExtensions(node); + if (extensions) { + extensions.forEach((extension) => { + addPropertySymbol(extension, /* insertAwait */ false, /* insertQuestionDot */ false); + }); + } + } + // TSPLUS EXTENSION END + if (insertAwait && preferences.includeCompletionsWithInsertText) { const promiseType = typeChecker.getPromisedTypeOfPromise(type); if (promiseType) { @@ -3377,6 +3413,9 @@ function getCompletionData( ? KeywordCompletionFilters.TypeAssertionKeywords : KeywordCompletionFilters.TypeKeywords; } + // TSPLUS EXTENSION START + symbols = concatenate(symbols, typeChecker.getTsPlusGlobals()); + // TSPLUS EXTENSION END } function shouldOfferImportCompletions(): boolean { diff --git a/src/services/documentHighlights.ts b/src/services/documentHighlights.ts index ab021d575aa..8b13ca10eb3 100644 --- a/src/services/documentHighlights.ts +++ b/src/services/documentHighlights.ts @@ -82,6 +82,8 @@ import { toPath, tryCast, TryStatement, + BinaryExpression, + isBinaryExpression, } from "./_namespaces/ts"; export interface DocumentHighlights { @@ -101,6 +103,17 @@ export namespace DocumentHighlights { return [{ fileName: sourceFile.fileName, highlightSpans }]; } + program.getTypeChecker().findAndCheckDoAncestor(node) + let currentBinaryAnchestor: BinaryExpression | undefined = findAncestor(node, isBinaryExpression); + let binaryExpressionParent = currentBinaryAnchestor; + while (currentBinaryAnchestor) { + binaryExpressionParent = currentBinaryAnchestor; + currentBinaryAnchestor = findAncestor(currentBinaryAnchestor.parent, isBinaryExpression); + } + if (binaryExpressionParent) { + program.getTypeChecker().getTypeAtLocation(binaryExpressionParent); + } + return getSemanticDocumentHighlights(position, node, program, cancellationToken, sourceFilesToSearch) || getSyntacticDocumentHighlights(node, sourceFile); } diff --git a/src/services/documentRegistry.ts b/src/services/documentRegistry.ts index 50f660d6bf7..eda9d4f8e95 100644 --- a/src/services/documentRegistry.ts +++ b/src/services/documentRegistry.ts @@ -306,7 +306,7 @@ export function createDocumentRegistryInternal(useCaseSensitiveFileNames?: boole if (!entry) { // Have never seen this file with these settings. Create a new source file for it. - const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, sourceFileOptions, version, /*setNodeParents*/ false, scriptKind); + const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, sourceFileOptions, version, /*setNodeParents*/ false, scriptKind, host?.getCompilationSettings()); if (externalCache) { externalCache.setDocument(keyWithMode, path, sourceFile); } @@ -322,7 +322,7 @@ export function createDocumentRegistryInternal(useCaseSensitiveFileNames?: boole // return it as is. if (entry.sourceFile.version !== version) { entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version, - scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot!)); // TODO: GH#18217 + scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot!), undefined, compilationSettings); // TODO: GH#18217 if (externalCache) { externalCache.setDocument(keyWithMode, path, entry.sourceFile); } diff --git a/src/services/findAllReferences.ts b/src/services/findAllReferences.ts index e8fe0f2a71f..9a790b82718 100644 --- a/src/services/findAllReferences.ts +++ b/src/services/findAllReferences.ts @@ -239,6 +239,10 @@ import { TypeChecker, TypeLiteralNode, VariableDeclaration, + concatenate, + getTokenAtPosition, + isBinaryOperatorToken, + TsPlusExtensionTag, } from "./_namespaces/ts"; import { createImportTracker, @@ -954,6 +958,37 @@ export namespace Core { } const checker = program.getTypeChecker(); + + // TSPLUS EXTENSION BEGIN + + const tsPlusSymbol = checker.getTsPlusSymbolAtLocation(node); + const tsPlusExtensions = checker.getTsPlusExtensionsAtLocation(node); + + let tsPlusReferences: SymbolAndEntries[] = [] + + if (isBinaryOperatorToken(node) && isBinaryExpression(node.parent)) { + const signature = checker.getResolvedOperator(node.parent); + if (signature && signature.declaration) { + for (const extension of checker.getExtensionsForDeclaration(signature.declaration)) { + tsPlusReferences = concatenate( + tsPlusReferences, + getReferencedExtensionsForOperator(node, extension, sourceFiles, checker, cancellationToken) + ) + } + } + } + + if (tsPlusSymbol) { + for (const extension of tsPlusExtensions) { + tsPlusReferences = concatenate( + tsPlusReferences, + getReferencedExtensionsForSymbol(tsPlusSymbol, extension, node, sourceFiles, sourceFilesSet, checker, cancellationToken, options) + ); + } + } + + // TSPLUS EXTENSION END + // constructors should use the class symbol, detected by name, if present const symbol = checker.getSymbolAtLocation(isConstructorDeclaration(node) && node.parent.name || node); @@ -974,7 +1009,10 @@ export namespace Core { } return getReferencesForStringLiteral(node, sourceFiles, checker, cancellationToken); } - return undefined; + if (tsPlusReferences.length === 0) { + return undefined; + } + return mergeReferences(program, tsPlusReferences); } if (symbol.escapedName === InternalSymbolName.ExportEquals) { @@ -986,12 +1024,26 @@ export namespace Core { return moduleReferences; } + // TSPLUS EXTENSION BEGIN + + let tsPlusDeclarationReferences: SymbolAndEntries[] = [] + if (symbol.valueDeclaration) { + for (const extension of checker.getExtensionsForDeclaration(symbol.valueDeclaration)) { + tsPlusDeclarationReferences = concatenate( + tsPlusDeclarationReferences, + getReferencedExtensionsForSymbol(symbol, extension, undefined, sourceFiles, sourceFilesSet, checker, cancellationToken, options) + ); + } + } + + // TSPLUS EXTENSION END + const aliasedSymbol = getMergedAliasedSymbolOfNamespaceExportDeclaration(node, symbol, checker); const moduleReferencesOfExportTarget = aliasedSymbol && getReferencedSymbolsForModuleIfDeclaredBySourceFile(aliasedSymbol, program, sourceFiles, cancellationToken, options, sourceFilesSet); const references = getReferencedSymbolsForSymbol(symbol, node, sourceFiles, sourceFilesSet, checker, cancellationToken, options); - return mergeReferences(program, moduleReferences, references, moduleReferencesOfExportTarget); + return mergeReferences(program, moduleReferences, references, moduleReferencesOfExportTarget, tsPlusReferences, tsPlusDeclarationReferences); } export function getAdjustedNode(node: Node, options: Options) { @@ -1256,6 +1308,73 @@ export namespace Core { return result; } + // TSPLUS EXTENSION BEGIN + + function getReferencedExtensionsForOperator(node: Node, extension: TsPlusExtensionTag, sourceFiles: readonly SourceFile[], checker: TypeChecker, cancellationToken: CancellationToken): SymbolAndEntries[] { + const references: SymbolAndEntries[] = [] + for (const sourceFile of sourceFiles) { + cancellationToken.throwIfCancellationRequested() + for (const position of getPossibleOperatorReferencePositions(checker, sourceFile, extension.name)) { + const referenceNode = getTokenAtPosition(sourceFile, position); + if (isBinaryOperatorToken(referenceNode) && isBinaryExpression(referenceNode.parent)) { + const signature = checker.getResolvedOperator(referenceNode.parent); + if (signature && signature.declaration) { + const extensions = checker.getExtensionsForDeclaration(signature.declaration); + for (const referencedExtension of extensions) { + if (referencedExtension === extension) { + references.push({ + definition: { + type: DefinitionKind.Keyword, + node + }, + references: [{ + kind: EntryKind.Node, + node: referenceNode + }] + }) + } + } + } + } + } + } + return references; + } + + function getReferencedExtensionsForSymbol(referenceSymbol: Symbol, extension: TsPlusExtensionTag, _node: Node | undefined, sourceFiles: readonly SourceFile[], _sourceFilesSet: ReadonlySet, checker: TypeChecker, cancellationToken: CancellationToken, _options: Options): SymbolAndEntries[] { + const references: SymbolAndEntries[] = [] + for (const sourceFile of sourceFiles) { + cancellationToken.throwIfCancellationRequested() + if (getNameTable(sourceFile).get(escapeLeadingUnderscores(extension.name)) !== undefined) { + for (const position of getPossibleSymbolReferencePositions(sourceFile, extension.name)) { + cancellationToken.throwIfCancellationRequested() + const referenceLocation = getTouchingPropertyName(sourceFile, position); + if (!isValidReferencePosition(referenceLocation, extension.name)) continue; + const extensions = checker.getTsPlusExtensionsAtLocation(referenceLocation); + for (const referencedExtension of extensions) { + if (referencedExtension === extension) { + references.push({ + definition: { + type: DefinitionKind.Symbol, + symbol: referenceSymbol + }, + references: [{ + kind: EntryKind.Node, + node: referenceLocation + }] + }); + break; + } + } + } + } + } + return references; + } + + // TSPLUS EXTENSION END + + function getReferencesInContainerOrFiles(symbol: Symbol, state: State, search: Search): void { // Try to get the smallest valid scope that we can limit our search to; // otherwise we'll need to search globally (i.e. include each file). @@ -1328,6 +1447,10 @@ export namespace Core { * Do not compare directly to `symbol` because there may be related symbols to search for. See `populateSearchSymbolSet`. */ includes(symbol: Symbol): boolean; + + // TSPLUS EXTENSION BEGIN + includesExtension(extension: TsPlusExtensionTag): boolean; + // TSPLUS EXTENSION END } const enum SpecialSearchKind { @@ -1406,7 +1529,21 @@ export namespace Core { } = searchOptions; const escapedText = escapeLeadingUnderscores(text); const parents = this.options.implementations && location ? getParentSymbolsOfPropertyAccess(location, symbol, this.checker) : undefined; - return { symbol, comingFrom, text, escapedText, parents, allSearchSymbols, includes: sym => contains(allSearchSymbols, sym) }; + // TSPLUS EXTENSION BEGIN + const allSearchExtensions = flatMap(allSearchSymbols, (symbol) => symbol.valueDeclaration ? this.checker.getExtensionsForDeclaration(symbol.valueDeclaration) : []) + // TSPLUS EXTENSION END + return { + symbol, + comingFrom, + text, + escapedText, + parents, + allSearchSymbols, + includes: sym => contains(allSearchSymbols, sym), + // TSPLUS EXTENSION BEGIN + includesExtension: extension => contains(allSearchExtensions, extension) + // TSPLUS EXTENSION END + }; } private readonly symbolIdToReferences: Entry[][] = []; @@ -1706,6 +1843,31 @@ export namespace Core { return getPossibleSymbolReferencePositions(sourceFile, symbolName, container).map(pos => getTouchingPropertyName(sourceFile, pos)); } + function getPossibleOperatorReferencePositions(checker: TypeChecker, sourceFile: SourceFile, operatorName: string, container: Node = sourceFile): readonly number[] { + const positions: number[] = []; + + if (!symbolName || !symbolName.length) { + return positions; + } + + const text = sourceFile.text; + const operatorNameLength = operatorName.length; + + let position = text.indexOf(operatorName, container.pos); + while (position >= 0) { + if (position > container.end) break; + + const node = getTokenAtPosition(sourceFile, position); + + if (isBinaryOperatorToken(node) && isBinaryExpression(node.parent) && checker.getResolvedOperator(node.parent)) { + positions.push(position); + } + position = text.indexOf(operatorName, position + operatorNameLength + 1); + } + + return positions; + } + function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): readonly number[] { const positions: number[] = []; diff --git a/src/services/goToDefinition.ts b/src/services/goToDefinition.ts index 0cac627bbac..6ef654ed7dc 100644 --- a/src/services/goToDefinition.ts +++ b/src/services/goToDefinition.ts @@ -25,6 +25,7 @@ import { FunctionLikeDeclaration, getAssignmentDeclarationKind, getContainingObjectLiteralElement, + getDeclarationForTsPlus, getDirectoryPath, getEffectiveBaseTypeNode, getInvokedExpression, @@ -42,7 +43,10 @@ import { isAnyImportOrBareOrAccessedRequire, isAssignmentDeclaration, isAssignmentExpression, + isBinaryExpression, + isBinaryOperatorToken, isBindingElement, + isCallExpression, isCallLikeExpression, isCallOrNewExpressionTarget, isClassElement, @@ -64,9 +68,12 @@ import { isNameOfFunctionDeclaration, isNewExpressionTarget, isObjectBindingPattern, + isPropertyAccessExpression, isPropertyName, isRightSideOfPropertyAccess, isStaticModifier, + isTsPlusSymbol, + isTsPlusTypeWithDeclaration, isVariableDeclaration, last, map, @@ -92,6 +99,7 @@ import { TextSpan, tryCast, tryGetModuleSpecifierFromDeclaration, + TsPlusSymbolTag, Type, TypeChecker, TypeFlags, @@ -157,37 +165,97 @@ export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile }); } - let { symbol, failedAliasResolution } = getSymbol(node, typeChecker, stopAtAlias); - let fallbackNode = node; + // TSPLUS EXTENSION BEGIN + let symbol: Symbol | undefined; + let failedAliasResolution: boolean | undefined; + + if (isPropertyAccessExpression(parent)) { + const nodeType = typeChecker.getTypeAtLocation(node); + if(nodeType.symbol && isTsPlusSymbol(nodeType.symbol)) { + if (parent.parent && isCallExpression(parent.parent) && parent.parent.expression === parent) { + const declaration = getDeclarationForTsPlus(typeChecker, parent.parent, nodeType.symbol) + if (declaration) { + symbol = declaration.symbol + } + } + if (!symbol && nodeType.symbol.tsPlusTag !== TsPlusSymbolTag.Fluent) { + symbol = nodeType.symbol.tsPlusDeclaration.symbol; + } + } + else if (isTsPlusTypeWithDeclaration(nodeType)) { + symbol = nodeType.tsPlusSymbol.tsPlusDeclaration.symbol; + } + else { + const type = typeChecker.getTypeAtLocation(parent.expression); + const extensions = typeChecker.getExtensions(parent.expression); + + if(extensions) { + const name = parent.name.escapedText.toString(); + const staticValueSymbol = typeChecker.getStaticExtension(type, name); + if(staticValueSymbol) { + // If execution gets here, it means we have a static variable extension, + // which needs to be treated a little differently + const declaration = staticValueSymbol.patched.valueDeclaration; + if(declaration && declaration.original) { + symbol = (declaration.original as Declaration).symbol; + } + } else { + symbol = extensions.get(name); + } + } + } - if (searchOtherFilesOnly && failedAliasResolution) { - // We couldn't resolve the specific import, try on the module specifier. - const importDeclaration = forEach([node, ...symbol?.declarations || emptyArray], n => findAncestor(n, isAnyImportOrBareOrAccessedRequire)); - const moduleSpecifier = importDeclaration && tryGetModuleSpecifierFromDeclaration(importDeclaration); - if (moduleSpecifier) { - ({ symbol, failedAliasResolution } = getSymbol(moduleSpecifier, typeChecker, stopAtAlias)); - fallbackNode = moduleSpecifier; + if (!symbol) { + symbol = typeChecker.getNodeLinks(node.parent).tsPlusSymbol } } + else if (isBinaryOperatorToken(node) && isBinaryExpression(parent)) { + const extension = typeChecker.getResolvedOperator(parent); + if (extension && extension.declaration) { + if (isTsPlusSymbol(extension.declaration.symbol) && extension.declaration.symbol.tsPlusTag === TsPlusSymbolTag.PipeableDeclaration) { + symbol = extension.declaration.symbol.tsPlusDeclaration.symbol + } + else { + symbol = extension.declaration.symbol; + } + } + } + + if(!symbol) { + ({ symbol, failedAliasResolution } = getSymbol(node, typeChecker, stopAtAlias)); - if (!symbol && isModuleSpecifierLike(fallbackNode)) { - // We couldn't resolve the module specifier as an external module, but it could - // be that module resolution succeeded but the target was not a module. - const ref = sourceFile.resolvedModules?.get(fallbackNode.text, getModeForUsageLocation(sourceFile, fallbackNode))?.resolvedModule; - if (ref) { - return [{ - name: fallbackNode.text, - fileName: ref.resolvedFileName, - containerName: undefined!, - containerKind: undefined!, - kind: ScriptElementKind.scriptElement, - textSpan: createTextSpan(0, 0), - failedAliasResolution, - isAmbient: isDeclarationFileName(ref.resolvedFileName), - unverified: fallbackNode !== node, - }]; + let fallbackNode = node; + + if (searchOtherFilesOnly && failedAliasResolution) { + // We couldn't resolve the specific import, try on the module specifier. + const importDeclaration = forEach([node, ...symbol?.declarations || emptyArray], n => findAncestor(n, isAnyImportOrBareOrAccessedRequire)); + const moduleSpecifier = importDeclaration && tryGetModuleSpecifierFromDeclaration(importDeclaration); + if (moduleSpecifier) { + ({ symbol, failedAliasResolution } = getSymbol(moduleSpecifier, typeChecker, stopAtAlias)); + fallbackNode = moduleSpecifier; + } + } + + if (!symbol && isModuleSpecifierLike(fallbackNode)) { + // We couldn't resolve the module specifier as an external module, but it could + // be that module resolution succeeded but the target was not a module. + const ref = sourceFile.resolvedModules?.get(fallbackNode.text, getModeForUsageLocation(sourceFile, fallbackNode))?.resolvedModule; + if (ref) { + return [{ + name: fallbackNode.text, + fileName: ref.resolvedFileName, + containerName: undefined!, + containerKind: undefined!, + kind: ScriptElementKind.scriptElement, + textSpan: createTextSpan(0, 0), + failedAliasResolution, + isAmbient: isDeclarationFileName(ref.resolvedFileName), + unverified: fallbackNode !== node, + }]; + } } } + // TSPLUS EXTENSION END // Could not find a symbol e.g. node is string or number keyword, // or the symbol was an internal symbol and does not have a declaration e.g. undefined symbol @@ -197,13 +265,26 @@ export function getDefinitionAtPosition(program: Program, sourceFile: SourceFile if (searchOtherFilesOnly && every(symbol.declarations, d => d.getSourceFile().fileName === sourceFile.fileName)) return undefined; - const calledDeclaration = tryGetSignatureDeclaration(typeChecker, node); + // TSPLUS EXTENSION BEGIN + let calledDeclaration = tryGetSignatureDeclaration(typeChecker, node); + if ( + calledDeclaration && + calledDeclaration.symbol && + isTsPlusSymbol(calledDeclaration.symbol) && + (calledDeclaration.symbol.tsPlusTag === TsPlusSymbolTag.PipeableMacro || calledDeclaration.symbol.tsPlusTag === TsPlusSymbolTag.PipeableDeclaration) + ) { + // We have determined that this is a call of a Pipeable macro, which is a synthetic declaration (has no real position). + // To go to the real definition, clear the callDeclaration to skip trying to get the definition info from the signature + calledDeclaration = undefined; + } + // TSPLUS EXTENSION END + // Don't go to the component constructor definition for a JSX element, just go to the component definition. if (calledDeclaration && !(isJsxOpeningLikeElement(node.parent) && isConstructorLike(calledDeclaration))) { const sigInfo = createDefinitionFromSignatureDeclaration(typeChecker, calledDeclaration, failedAliasResolution); // For a function, if this is the original function definition, return just sigInfo. // If this is the original constructor definition, parent is the class. - if (typeChecker.getRootSymbols(symbol).some(s => symbolMatchesSignature(s, calledDeclaration))) { + if (typeChecker.getRootSymbols(symbol).some(s => symbolMatchesSignature(s, calledDeclaration!))) { return [sigInfo]; } else { @@ -479,7 +560,12 @@ function isExpandoDeclaration(node: Declaration): boolean { } function getDefinitionFromSymbol(typeChecker: TypeChecker, symbol: Symbol, node: Node, failedAliasResolution?: boolean, excludeDeclaration?: Node): DefinitionInfo[] | undefined { - const filteredDeclarations = filter(symbol.declarations, d => d !== excludeDeclaration); + let filteredDeclarations: Declaration[] | undefined + if (isTsPlusSymbol(symbol) && (symbol.tsPlusTag === TsPlusSymbolTag.Getter || symbol.tsPlusTag === TsPlusSymbolTag.GetterVariable)) { + filteredDeclarations = [symbol.tsPlusDeclaration] + } else { + filteredDeclarations = filter(symbol.declarations, d => d !== excludeDeclaration); + } const withoutExpandos = filter(filteredDeclarations, d => !isExpandoDeclaration(d)); const results = some(withoutExpandos) ? withoutExpandos : filteredDeclarations; return getConstructSignatureDefinition() || getCallSignatureDefinition() || map(results, declaration => createDefinitionInfo(declaration, typeChecker, symbol, node, /*unverified*/ false, failedAliasResolution)); diff --git a/src/services/services.ts b/src/services/services.ts index f6e5fccfe06..6d2e3d567f6 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -317,6 +317,28 @@ import { updateSourceFile, UserPreferences, VariableDeclaration, + displayPart, + factory, + getThisTypeNameForCallLikeExpression, + getThisTypeNameForTsPlusSymbol, + isBinaryExpression, + isCallExpression, + isCallLikeExpression, + isJSDocSignature, + isSymbolParameterDeclaration, + isToken, + isTsPlusSymbol, + isTsPlusType, + JSDocSignature, + NamedDeclaration, + punctuationPart, + setParent, + signatureToDisplayParts, + spacePart, + SymbolDisplayPartKind, + textPart, + TsPlusSymbol, + TsPlusSymbolTag, } from "./_namespaces/ts"; /** The version of the language service API */ @@ -1043,6 +1065,7 @@ class SourceFileObject extends NodeObject implements SourceFile { public pragmas!: PragmaMap; public localJsxFactory: EntityName | undefined; public localJsxNamespace: __String | undefined; + public tsPlusContext!: SourceFile["tsPlusContext"]; constructor(kind: SyntaxKind, pos: number, end: number) { super(kind, pos, end); @@ -1356,7 +1379,7 @@ class SyntaxTreeCache { else if (this.currentFileVersion !== version) { // This is the same file, just a newer version. Incrementally parse the file. const editRange = scriptSnapshot.getChangeRange(this.currentFileScriptSnapshot!); - sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile!, scriptSnapshot, version, editRange); + sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile!, scriptSnapshot, version, editRange, undefined, this.host.getCompilationSettings()); } if (sourceFile) { @@ -1376,13 +1399,13 @@ function setSourceFileFields(sourceFile: SourceFile, scriptSnapshot: IScriptSnap sourceFile.scriptSnapshot = scriptSnapshot; } -export function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTargetOrOptions: ScriptTarget | CreateSourceFileOptions, version: string, setNodeParents: boolean, scriptKind?: ScriptKind): SourceFile { - const sourceFile = createSourceFile(fileName, getSnapshotText(scriptSnapshot), scriptTargetOrOptions, setNodeParents, scriptKind); +export function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTargetOrOptions: ScriptTarget | CreateSourceFileOptions, version: string, setNodeParents: boolean, scriptKind?: ScriptKind, compilerOptions?: CompilerOptions): SourceFile { + const sourceFile = createSourceFile(fileName, getSnapshotText(scriptSnapshot), scriptTargetOrOptions, setNodeParents, scriptKind, compilerOptions); setSourceFileFields(sourceFile, scriptSnapshot, version); return sourceFile; } -export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange | undefined, aggressiveChecks?: boolean): SourceFile { +export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange | undefined, aggressiveChecks?: boolean, compilerOptions?: CompilerOptions): SourceFile { // If we were given a text change range, and our version or open-ness changed, then // incrementally parse this file. if (textChangeRange) { @@ -1414,7 +1437,7 @@ export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSn : (changedText + suffix); } - const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks); + const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks, compilerOptions); setSourceFileFields(newSourceFile, scriptSnapshot, version); // after incremental parsing nameTable might not be up-to-date // drop it so it can be lazily recreated later @@ -2013,6 +2036,58 @@ export function createLanguageService( return Completions.getCompletionEntrySymbol(program, log, getValidSourceFile(fileName), position, { name, source }, host, preferences); } + // TSPLUS EXTENSION BEGIN + function getTsPlusSignatureDisplayParts(typeChecker: TypeChecker, node: Node, declaration: SignatureDeclaration, resolvedSignature: Signature): SymbolDisplayPart[] { + const paramLength = resolvedSignature.parameters.length; + const lastParam = resolvedSignature.parameters[paramLength - 1]; + if (resolvedSignature.parameters.every(isSymbolParameterDeclaration) && lastParam && lastParam.name === "__tsplusTrace") { + const untracedDeclaration = factory.createCallSignature( + declaration.typeParameters, + declaration.parameters.slice(0, paramLength - 1), + declaration.type + ); + setParent(untracedDeclaration, declaration.parent); + untracedDeclaration.jsDoc = declaration.jsDoc; + const untracedSignature = typeChecker.createSignature( + untracedDeclaration, + resolvedSignature.typeParameters, + resolvedSignature.thisParameter, + resolvedSignature.parameters.slice(0, resolvedSignature.parameters.length - 1), + resolvedSignature.getReturnType(), + resolvedSignature.resolvedTypePredicate, + resolvedSignature.minArgumentCount - 1, + resolvedSignature.flags + ); + // For resolved type parameters to be shown on a signature, the `target` and `mapper` must be + // set on the untraced signature + untracedSignature.target = resolvedSignature.target; + untracedSignature.mapper = resolvedSignature.mapper; + return typeChecker.runWithCancellationToken( + cancellationToken, + typeChecker => signatureToDisplayParts(typeChecker, untracedSignature, getContainerNode(node)) + ); + } + else { + return typeChecker.runWithCancellationToken( + cancellationToken, + typeChecker => signatureToDisplayParts(typeChecker, resolvedSignature, getContainerNode(node)) + ); + } + } + function getFirstDeclarationWithJsDoc(typeChecker: TypeChecker, declaration: NamedDeclaration): Declaration { + if (!hasJSDocNodes(declaration) && declaration.name) { + const declarationSymbol = typeChecker.getSymbolAtLocation(declaration.name); + if (declarationSymbol && declarationSymbol.declarations) { + const declarationWithJsDoc = firstDefined(declarationSymbol.declarations, (decl) => hasJSDocNodes(decl) ? decl : undefined); + if (declarationWithJsDoc) { + return declarationWithJsDoc; + } + } + } + return declaration; + } + // TSPLUS EXTENSION END + function getQuickInfoAtPosition(fileName: string, position: number): QuickInfo | undefined { synchronizeHostData(); @@ -2027,8 +2102,231 @@ export function createLanguageService( const nodeForQuickInfo = getNodeForQuickInfo(node); const symbol = getSymbolAtLocationForQuickInfo(nodeForQuickInfo, typeChecker); + // TSPLUS EXTENSION BEGIN + if (isCallExpression(nodeForQuickInfo.parent)) { + const call = typeChecker.getCallExtension(nodeForQuickInfo); + if(call) { + let symbol = typeChecker.getTypeOfSymbol(call.patched).symbol; + if(isTsPlusSymbol(symbol) && symbol.tsPlusTag === TsPlusSymbolTag.StaticFunction) { + let displayParts: SymbolDisplayPart[] = []; + displayParts.push(textPart("(")); + displayParts.push(textPart("call")); + displayParts.push(textPart(")")); + displayParts.push(spacePart()); + const resolvedSignature = typeChecker.getResolvedSignature(nodeForQuickInfo.parent); + if (resolvedSignature && resolvedSignature.declaration && !isJSDocSignature(resolvedSignature.declaration)) { + const declaration = resolvedSignature.declaration; + const signature = declaration + ? typeChecker.getInstantiatedTsPlusSignature(declaration, nodeForQuickInfo.parent.arguments.slice(), undefined) + : symbol.tsPlusResolvedSignatures[0] + displayParts = displayParts.concat(getTsPlusSignatureDisplayParts(typeChecker, nodeForQuickInfo, declaration, signature)); + return { + kind: ScriptElementKind.memberFunctionElement, + kindModifiers: ScriptElementKindModifier.none, + textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile), + displayParts, + documentation: getDocumentationComment([declaration], typeChecker), + tags: getJsDocTagsOfDeclarations([declaration], typeChecker) + }; + } + } + } + } + // TSPLUS EXTENSION END + if (!symbol || typeChecker.isUnknownSymbol(symbol)) { const type = shouldGetType(sourceFile, nodeForQuickInfo, position) ? typeChecker.getTypeAtLocation(nodeForQuickInfo) : undefined; + // TSPLUS EXTENSION BEGIN + if (type) { + let tsPlusSymbol: TsPlusSymbol | undefined; + if (type.symbol && isTsPlusSymbol(type.symbol)) { + tsPlusSymbol = type.symbol; + } + else if (type && isTsPlusType(type)) { + tsPlusSymbol = type.tsPlusSymbol; + } + else { + tsPlusSymbol = typeChecker.getNodeLinks(nodeForQuickInfo.parent).tsPlusSymbol; + } + if(tsPlusSymbol) { + let thisTypeName = getThisTypeNameForTsPlusSymbol(typeChecker, tsPlusSymbol) + if (nodeForQuickInfo.parent && nodeForQuickInfo.parent.parent) { + thisTypeName = getThisTypeNameForCallLikeExpression(typeChecker, nodeForQuickInfo.parent.parent) ?? thisTypeName + } + let displayParts: SymbolDisplayPart[] = []; + switch(tsPlusSymbol.tsPlusTag) { + case TsPlusSymbolTag.Fluent: { + displayParts.push(textPart("(")); + displayParts.push(textPart("fluent")); + displayParts.push(textPart(")")); + displayParts.push(spacePart()); + displayParts.push(displayPart(thisTypeName, SymbolDisplayPartKind.className)); + displayParts.push(punctuationPart(SyntaxKind.DotToken)); + displayParts.push(displayPart(tsPlusSymbol.tsPlusName, SymbolDisplayPartKind.methodName)); + break; + } + case TsPlusSymbolTag.StaticFunction: { + displayParts.push(textPart("(")); + displayParts.push(textPart("static")); + displayParts.push(textPart(")")); + displayParts.push(spacePart()); + displayParts.push(displayPart(tsPlusSymbol.tsPlusName, SymbolDisplayPartKind.methodName)); + break; + } + case TsPlusSymbolTag.StaticValue: { + displayParts.push(textPart("(")); + displayParts.push(textPart("static")); + displayParts.push(textPart(")")); + displayParts.push(spacePart()); + displayParts.push(displayPart(tsPlusSymbol.tsPlusName, SymbolDisplayPartKind.methodName)); + displayParts.push(punctuationPart(SyntaxKind.ColonToken)); + displayParts.push(spacePart()); + break; + } + case TsPlusSymbolTag.GetterVariable: + case TsPlusSymbolTag.Getter: { + displayParts.push(textPart("(")); + displayParts.push(textPart("getter")); + displayParts.push(textPart(")")); + displayParts.push(spacePart()); + displayParts.push(displayPart(thisTypeName, SymbolDisplayPartKind.className)); + displayParts.push(punctuationPart(SyntaxKind.DotToken)); + displayParts.push(displayPart(tsPlusSymbol.tsPlusName, SymbolDisplayPartKind.fieldName)); + displayParts.push(punctuationPart(SyntaxKind.ColonToken)); + displayParts.push(spacePart()); + break; + } + } + switch(tsPlusSymbol.tsPlusTag) { + case TsPlusSymbolTag.Fluent: { + let declaration: SignatureDeclaration | JSDocSignature | undefined + let resolvedSignature: Signature = tsPlusSymbol.tsPlusResolvedSignatures[0]; + if (node.parent && node.parent.parent && isCallLikeExpression(node.parent.parent)) { + const resolvedCallSignature = typeChecker.getResolvedSignature(node.parent.parent) + if (resolvedCallSignature) { + resolvedSignature = resolvedCallSignature + declaration = resolvedCallSignature.declaration + } + } + if (!declaration || isJSDocSignature(declaration)) { + break; + } + displayParts = displayParts.concat(getTsPlusSignatureDisplayParts(typeChecker, nodeForQuickInfo, declaration, resolvedSignature)); + return { + kind: ScriptElementKind.memberFunctionElement, + kindModifiers: ScriptElementKindModifier.staticModifier, + textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile), + displayParts, + documentation: getDocumentationComment([declaration], typeChecker), + tags: getJsDocTagsOfDeclarations([declaration], typeChecker) + }; + } + case TsPlusSymbolTag.StaticFunction: { + let resolvedSignature = tsPlusSymbol.tsPlusResolvedSignatures[0]; + let declaration: SignatureDeclaration | JSDocSignature | undefined = resolvedSignature.declaration; + if (node.parent && node.parent.parent && isCallLikeExpression(node.parent.parent)) { + const resolvedCallSignature = typeChecker.getResolvedSignature(node.parent.parent) + if (resolvedCallSignature) { + resolvedSignature = resolvedCallSignature + declaration = resolvedCallSignature.declaration + } + } + if (!declaration || isJSDocSignature(declaration)) { + break; + } + displayParts = displayParts.concat(getTsPlusSignatureDisplayParts(typeChecker, nodeForQuickInfo, declaration, resolvedSignature)); + return { + kind: ScriptElementKind.memberFunctionElement, + kindModifiers: ScriptElementKindModifier.staticModifier, + textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile), + displayParts, + documentation: getDocumentationComment([declaration], typeChecker), + tags: getJsDocTagsOfDeclarations([declaration], typeChecker) + }; + } + case TsPlusSymbolTag.StaticValue: + case TsPlusSymbolTag.GetterVariable: + case TsPlusSymbolTag.Getter: { + displayParts = displayParts.concat(typeChecker.runWithCancellationToken(cancellationToken, typeChecker => typeToDisplayParts(typeChecker, type, getContainerNode(nodeForQuickInfo)))); + return { + kind: ScriptElementKind.memberGetAccessorElement, + kindModifiers: ScriptElementKindModifier.none, + textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile), + displayParts, + documentation: getDocumentationComment([tsPlusSymbol.tsPlusDeclaration], typeChecker), + tags: getJsDocTagsOfDeclarations([tsPlusSymbol.tsPlusDeclaration], typeChecker) + }; + } + } + } + } + if (isToken(nodeForQuickInfo) && nodeForQuickInfo.parent && isBinaryExpression(nodeForQuickInfo.parent)) { + const operator = typeChecker.getTextOfBinaryOp(nodeForQuickInfo.kind); + if (operator) { + const signature = typeChecker.getResolvedOperator(nodeForQuickInfo.parent); + if (signature && signature.declaration && !isJSDocSignature(signature.declaration)) { + let displayParts: SymbolDisplayPart[] = []; + displayParts.push(textPart("(")); + displayParts.push(textPart("operator")); + displayParts.push(textPart(")")); + displayParts.push(spacePart()); + displayParts.push(displayPart(operator, SymbolDisplayPartKind.operator)); + displayParts.push(spacePart()); + displayParts = displayParts.concat( + typeChecker.runWithCancellationToken( + cancellationToken, + (typeChecker) => getTsPlusSignatureDisplayParts(typeChecker, nodeForQuickInfo, signature.declaration as SignatureDeclaration, signature) + ) + ); + const declaration = getFirstDeclarationWithJsDoc(typeChecker, signature.declaration); + return { + kind: ScriptElementKind.functionElement, + kindModifiers: ScriptElementKindModifier.none, + textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile), + displayParts, + documentation: getDocumentationComment([declaration], typeChecker), + tags: getJsDocTagsOfDeclarations([declaration], typeChecker) + }; + } + } + } + if(isPropertyAccessExpression(node.parent)) { + const targetType = typeChecker.getTypeAtLocation(node.parent.expression); + const extensions = typeChecker.getExtensions(node.parent.expression); + if(extensions) { + const type = typeChecker.getTypeAtLocation(node); + const name = node.parent.name.escapedText as string; + const staticSymbol = typeChecker.getStaticExtension(targetType, name); + if(type && staticSymbol) { + const declaration = staticSymbol.patched.valueDeclaration; + if (declaration) { + let displayParts: SymbolDisplayPart[] = []; + displayParts.push(textPart("(")); + displayParts.push(textPart("static")); + displayParts.push(textPart(")")); + displayParts.push(spacePart()); + displayParts.push(displayPart(name, SymbolDisplayPartKind.fieldName)); + displayParts.push(punctuationPart(SyntaxKind.ColonToken)); + displayParts.push(spacePart()); + displayParts = displayParts.concat( + typeChecker.runWithCancellationToken( + cancellationToken, + (typeChecker) => typeToDisplayParts(typeChecker, type, getContainerNode(nodeForQuickInfo)) + ) + ); + return { + kind: ScriptElementKind.memberVariableElement, + kindModifiers: ScriptElementKindModifier.staticModifier, + textSpan: createTextSpanFromNode(nodeForQuickInfo, sourceFile), + displayParts, + documentation: getDocumentationComment([declaration], typeChecker), + tags: getJsDocTagsOfDeclarations([declaration], typeChecker) + }; + } + } + } + } + // TSPLUS EXTENSION END return type && { kind: ScriptElementKind.unknown, kindModifiers: ScriptElementKindModifier.none, diff --git a/src/services/signatureHelp.ts b/src/services/signatureHelp.ts index c8923140c49..cfd74b70f73 100644 --- a/src/services/signatureHelp.ts +++ b/src/services/signatureHelp.ts @@ -33,6 +33,7 @@ import { isBinaryExpression, isBlock, isCallOrNewExpression, + isFunctionDeclaration, isFunctionTypeNode, isIdentifier, isInComment, @@ -44,6 +45,7 @@ import { isPropertyAccessExpression, isSourceFile, isSourceFileJS, + isSymbolParameterDeclaration, isTaggedTemplateExpression, isTemplateHead, isTemplateLiteralToken, @@ -63,6 +65,7 @@ import { Program, punctuationPart, rangeContainsRange, + setParent, Signature, SignatureHelpItem, SignatureHelpItems, @@ -124,6 +127,9 @@ export function getSignatureHelpItems(program: Program, sourceFile: SourceFile, } const isManuallyInvoked = !!triggerReason && triggerReason.kind === "invoked"; + // TSPLUS EXTENSION START + typeChecker.findAndCheckDoAncestor(startingToken); + // TSPLUS EXTENSION END const argumentInfo = getContainingArgumentInfo(startingToken, position, sourceFile, typeChecker, isManuallyInvoked); if (!argumentInfo) return undefined; @@ -162,8 +168,40 @@ function getCandidateOrTypeInfo({ invocation, argumentCount }: ArgumentListInfo, if (onlyUseSyntacticOwners && !isSyntacticOwner(startingToken, invocation.node, sourceFile)) { return undefined; } - const candidates: Signature[] = []; - const resolvedSignature = checker.getResolvedSignatureForSignatureHelp(invocation.node, candidates, argumentCount)!; // TODO: GH#18217 + let candidates: Signature[] = []; + let resolvedSignature = checker.getResolvedSignatureForSignatureHelp(invocation.node, candidates, argumentCount)!; // TODO: GH#18217 + // TSPLUS EXTENSION BEGIN + if(resolvedSignature.declaration && isFunctionDeclaration(resolvedSignature.declaration) && resolvedSignature.parameters.every(isSymbolParameterDeclaration)) { + const declaration = resolvedSignature.declaration; + const lastParam = declaration.parameters[declaration.parameters.length - 1]; + const parameterCount = resolvedSignature.thisParameter ? declaration.parameters.length - 1 : declaration.parameters.length; + if(declaration.name && lastParam && isIdentifier(lastParam.name) && lastParam.name.escapedText.toString() === "___tsplusTrace" && argumentCount < parameterCount) { + const untracedDeclaration = factory.createFunctionDeclaration( + declaration.modifiers, + declaration.asteriskToken, + declaration.name, + declaration.typeParameters, + declaration.parameters.slice(0, declaration.parameters.length - 1), + declaration.type, + undefined + ); + setParent(untracedDeclaration, declaration.parent); + untracedDeclaration.jsDoc = declaration.jsDoc; + const untracedSignature = checker.createSignature( + untracedDeclaration, + resolvedSignature.typeParameters, + resolvedSignature.thisParameter, + resolvedSignature.parameters.slice(0, resolvedSignature.parameters.length - 1), + resolvedSignature.getReturnType(), + resolvedSignature.resolvedTypePredicate, + resolvedSignature.minArgumentCount, + resolvedSignature.flags + ); + resolvedSignature = untracedSignature; + candidates = [untracedSignature]; + } + } + // TSPLUS EXTENSION END return candidates.length === 0 ? undefined : { kind: CandidateOrTypeKind.Candidate, candidates, resolvedSignature }; } case InvocationKind.TypeArgs: { diff --git a/src/services/symbolDisplay.ts b/src/services/symbolDisplay.ts index be2e83a48bb..9ddb531c6c1 100644 --- a/src/services/symbolDisplay.ts +++ b/src/services/symbolDisplay.ts @@ -291,6 +291,10 @@ export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: Typ } } + // TSPLUS EXTENSION START + typeChecker.findAndCheckDoAncestor(location); + // TSPLUS EXTENSION END + let signature: Signature | undefined; type = isThisExpression ? typeChecker.getTypeAtLocation(location) : typeChecker.getTypeOfSymbolAtLocation(symbol, location); diff --git a/src/tsc/_namespaces/ts.ts b/src/tsc/_namespaces/ts.ts index 8e361ae7184..3103ee919f7 100644 --- a/src/tsc/_namespaces/ts.ts +++ b/src/tsc/_namespaces/ts.ts @@ -1,4 +1,7 @@ /* Generated file to emulate the ts namespace. */ export * from "../../compiler/_namespaces/ts"; +export * from "../../jsTyping/_namespaces/ts"; +export * from "../../services/_namespaces/ts"; +export * from "../../deprecatedCompat/_namespaces/ts"; export * from "../../executeCommandLine/_namespaces/ts"; diff --git a/src/tsc/tsconfig.json b/src/tsc/tsconfig.json index 21e193ad5d7..5f7d26058e3 100644 --- a/src/tsc/tsconfig.json +++ b/src/tsc/tsconfig.json @@ -4,6 +4,9 @@ }, "references": [ { "path": "../compiler" }, + { "path": "../jsTyping" }, + { "path": "../services" }, + { "path": "../deprecatedCompat" }, { "path": "../executeCommandLine" } ], "include": ["**/*"] diff --git a/testing/.gitignore b/testing/.gitignore new file mode 100644 index 00000000000..6f467448bd6 --- /dev/null +++ b/testing/.gitignore @@ -0,0 +1 @@ +**/build \ No newline at end of file diff --git a/testing/package.json b/testing/package.json new file mode 100644 index 00000000000..7c51c00d45d --- /dev/null +++ b/testing/package.json @@ -0,0 +1,15 @@ +{ + "name": "org", + "scripts": { + "postinstall": "npm run build", + "clean": "cd packages/demo && rm -rf build", + "build": "cd packages/demo && npm run build:esm" + }, + "workspaces": [ + "packages/*" + ], + "dependencies": { + "@tsplus/stdlib": "0.1.8", + "@effect/core": "0.0.5" + } +} diff --git a/testing/packages/.placeholder b/testing/packages/.placeholder new file mode 100644 index 00000000000..e69de29bb2d diff --git a/testing/packages/.placeolder b/testing/packages/.placeolder new file mode 100644 index 00000000000..e69de29bb2d diff --git a/testing/packages/demo/package.json b/testing/packages/demo/package.json new file mode 100644 index 00000000000..a8cecc7284e --- /dev/null +++ b/testing/packages/demo/package.json @@ -0,0 +1,20 @@ +{ + "name": "@org/demo", + "type": "module", + "exports": { + "./*": { + "import": "./build/esm/*.js" + } + }, + "typesVersions": { + "*": { + "*": [ + "./build/dts/*" + ] + } + }, + "scripts": { + "build:esm": "node ../../../built/local/tsc.js -p ./tsconfig.json", + "build": "rm -rf build; npm run build:esm;" + } +} diff --git a/testing/packages/demo/src/bug-constant.ts b/testing/packages/demo/src/bug-constant.ts new file mode 100644 index 00000000000..6c3b23d8fb2 --- /dev/null +++ b/testing/packages/demo/src/bug-constant.ts @@ -0,0 +1,4 @@ +import { constant } from "@tsplus/stdlib/data/Function"; + +Maybe.some('foo').fold(constant('good-bye'), identity) +Maybe.some('foo').fold('good-bye', identity) diff --git a/testing/packages/demo/src/do-bind.ts b/testing/packages/demo/src/do-bind.ts new file mode 100644 index 00000000000..5c6309d6a1c --- /dev/null +++ b/testing/packages/demo/src/do-bind.ts @@ -0,0 +1,41 @@ +export interface Foo { + readonly foo: string; +} + +export const Foo = Service.Tag(); + +export interface Bar { + readonly bar: string; +} + +export const Bar = Service.Tag(); + +export const res = Do(($) => { + $(Effect.succeed(0)); + $(Effect.serviceWith(Foo, (_) => _.foo)); + $(Effect.serviceWith(Bar, (_) => _.bar)); + $(Effect.fail("a" as const)); + $(Effect.fail("b" as const)); +}); + +export const res2 = Do(($) => { + const a = $(Either.right(0)); + const b = $(Either.right(1)); + return `${a} + ${b}`; +}); + +export const res3 = Do(($) => { + const a = $(Either.right(0)); + const b = $(Either.right(1)); + return $(Either.right(`${a} + ${b}`)); +}); + +const deferred = Deferred.unsafeMake(FiberId.none); + +export const res4 = Do(($) => { + const y = $(Do(($) => { + $(Deferred.make()); + return $(deferred.isDone()); + })); + return y; +}); diff --git a/testing/packages/demo/src/doc.ts b/testing/packages/demo/src/doc.ts new file mode 100644 index 00000000000..e9224b89da6 --- /dev/null +++ b/testing/packages/demo/src/doc.ts @@ -0,0 +1,65 @@ +/** + * @tsplus type demo/printer/Doc + */ +export type Doc = + | Char + | Line + | FlatAlt + +/** +* @tsplus type demo/printer/Doc/Aspects +*/ +export interface DocAspects { } + +/** +* @tsplus unify demo/printer/Doc +* @tsplus unify demo/printer/Doc/Char +* @tsplus unify demo/printer/Doc/Line +* @tsplus unify demo/printer/Doc/FlatAlt +*/ +export function unifyDoc>( + self: X +): Doc<[X] extends [{ _A: () => infer A; }] ? A : never> { + return self; +} + +/** +* @tsplus type demo/printer/Doc/Char +*/ +export class Char { + readonly _tag = "Char"; + readonly _A!: () => A; + constructor(readonly char: string, readonly id: (_: never) => A) { } +} + +/** +* @tsplus type demo/printer/Doc/Line +*/ +export class Line { + readonly _tag = "Line"; + readonly _A!: () => A; + constructor(readonly id: (_: never) => A) { } +} + +/** +* @tsplus type demo/printer/Doc/FlatAlt +*/ +export class FlatAlt { + readonly _tag = "FlatAlt"; + readonly _A!: () => A; + constructor(readonly left: Doc, readonly right: Doc) { } +} + +const identity = (a: A) => a + +const line_: Doc = new Line(identity); + +export function char(char: string): Doc { + return new Char(char, identity); +} + +export function flatAlt(left: Doc, right: Doc): Doc { + return new FlatAlt(left, right); +} + +export const line: Doc = flatAlt(line_, char(" ")); diff --git a/testing/packages/demo/src/example.ts b/testing/packages/demo/src/example.ts new file mode 100644 index 00000000000..dc6e665dbc8 --- /dev/null +++ b/testing/packages/demo/src/example.ts @@ -0,0 +1,11 @@ +import { Effect } from "@effect/core/io/Effect"; + +export const y = (Effect(0) as Effect).map((n) => n + 1); +export const z = (Effect(0) as Effect.UIO).map((n) => n + 1); + +Effect.succeed(0).map((n) => n + 1); +Effect.succeed(0).map((n) => n + 1).cause().uncause(); + +const scoped = Effect.scoped( + Effect.acquireRelease(Effect.succeed(0), () => Effect.unit) +); \ No newline at end of file diff --git a/testing/packages/demo/src/fluent-guard.ts b/testing/packages/demo/src/fluent-guard.ts new file mode 100644 index 00000000000..67830f4d83a --- /dev/null +++ b/testing/packages/demo/src/fluent-guard.ts @@ -0,0 +1,40 @@ +interface A { + _tag: "A"; + a: number; +} +interface B { + _tag: "B"; + b: string; +} +interface C { + _tag: "C"; + c: symbol; +} +type ADT = A | B | C; + +function adt(): ADT { + return { + _tag: "A", + a: 0, + }; +} + +declare const either: Either; + +if (either.isLeft()) { + const x: string = either.left; + console.log(x); +} + +if (either.isRight()) { + const x: number = either.right; + console.log(x); +} + +const matchEval = Match.tag(adt(), { + A: (_) => Eval.succeed(_), + B: (_) => Eval.succeed(_), + C: (_) => Eval.succeed(_), +}); + +const result = matchEval.run; diff --git a/testing/packages/demo/src/global.ts b/testing/packages/demo/src/global.ts new file mode 100644 index 00000000000..68be498801c --- /dev/null +++ b/testing/packages/demo/src/global.ts @@ -0,0 +1,2 @@ +import "@tsplus/stdlib/global" +import "@effect/core/global" \ No newline at end of file diff --git a/testing/packages/demo/src/operator-repro.ts b/testing/packages/demo/src/operator-repro.ts new file mode 100644 index 00000000000..c95f1025c31 --- /dev/null +++ b/testing/packages/demo/src/operator-repro.ts @@ -0,0 +1,11 @@ +/* eslint-disable */ + +declare function f (effect: Effect): void; + +f( + Do(($) => { + const x = $(Deferred.make()); + const y = x.await() > Effect.unit + y + }) +); \ No newline at end of file diff --git a/testing/packages/demo/src/pipe-deps.ts b/testing/packages/demo/src/pipe-deps.ts new file mode 100644 index 00000000000..73731a8c2fa --- /dev/null +++ b/testing/packages/demo/src/pipe-deps.ts @@ -0,0 +1,15 @@ +export function id(a: A): A { + return a +} + +/** + * @tsplus operator Either / + * @tsplus macro pipe + */ +export function pipe(self: A, f: (a: A) => B): B { + return f(self) +} + +export function map(f: (a: A) => B) { + return (self: Either) => self.map(f) +} \ No newline at end of file diff --git a/testing/packages/demo/src/pipe-repro.ts b/testing/packages/demo/src/pipe-repro.ts new file mode 100644 index 00000000000..98915883d4b --- /dev/null +++ b/testing/packages/demo/src/pipe-repro.ts @@ -0,0 +1,3 @@ +/* eslint-disable */ + +export const a = Either.right({ xxx: 0 }) / Either.$.map((n) => n.xxx) / Either.$.map((n) => n + 1) / Either.$.map((n) => `hello: ${n}`) / Either.$.map((s) => s.length) \ No newline at end of file diff --git a/testing/packages/demo/src/pipeable-type-node.ts b/testing/packages/demo/src/pipeable-type-node.ts new file mode 100644 index 00000000000..9442654f7e2 --- /dev/null +++ b/testing/packages/demo/src/pipeable-type-node.ts @@ -0,0 +1,10 @@ +import type {} from "./global"; + +const namedRef = FiberRef.unsafeMake(""); + +/** + * @tsplus pipeable effect/core/io/Effect named + */ +export function forkDaemonNamed(name: string): (self: Effect) => Effect { + return namedRef.locally(name); +} diff --git a/testing/packages/demo/src/repro-pr-117.ts b/testing/packages/demo/src/repro-pr-117.ts new file mode 100644 index 00000000000..55658e85e9a --- /dev/null +++ b/testing/packages/demo/src/repro-pr-117.ts @@ -0,0 +1,7 @@ +declare const x: unknown + +Effect.die(x) + +Ref.make>(HashMap.empty()) + +Ref.make<(_: number) => Effect>((_) => Effect.unit) \ No newline at end of file diff --git a/testing/packages/demo/tsconfig.json b/testing/packages/demo/tsconfig.json new file mode 100644 index 00000000000..40c76217d18 --- /dev/null +++ b/testing/packages/demo/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "build/esm", + "declarationDir": "build/dts", + "declarationMap": true, + "tsPlusConfig": "../../tsplus.config.json", + "rootDir": "src", + "skipLibCheck": true + }, + "include": ["src/**/*.ts"] +} \ No newline at end of file diff --git a/testing/prelude.d.ts b/testing/prelude.d.ts new file mode 100644 index 00000000000..8a3ae00c981 --- /dev/null +++ b/testing/prelude.d.ts @@ -0,0 +1,483 @@ +import "@tsplus/stdlib-global" + +/** + * Ensure types are in scope + */ +import type { } from "@effect/core"; +/** + * @tsplus global + */ +import { Cause } from "@effect/core/io/Cause/definition"; +/** + * @tsplus global + */ + import { + ChannelError, + FiberFailure, + IllegalArgumentException, + IllegalStateException, + InterruptedException, + RuntimeError +} from "@effect/core/io/Cause/errors"; +/** + * @tsplus global + */ +import { Clock } from "@effect/core/io/Clock/definition"; +/** + * @tsplus global + */ +import { DefaultEnv } from "@effect/core/io/DefaultEnv/definition"; +/** + * @tsplus global + */ +import { Deferred } from "@effect/core/io/Deferred/definition"; +/** + * @tsplus global + */ +import { Effect } from "@effect/core/io/Effect/definition"; +/** + * @tsplus global + */ +import { FiberRefs } from "@effect/core/io/Effect/operations/fiberRefs"; +/** + * @tsplus global + */ +import { ExecutionStrategy } from "@effect/core/io/ExecutionStrategy/definition"; +/** + * @tsplus global + */ +import { Exit } from "@effect/core/io/Exit/definition"; +/** + * @tsplus global + */ +import { Fiber } from "@effect/core/io/Fiber/definition"; +/** + * @tsplus global + */ +import { FiberId } from "@effect/core/io/FiberId/definition"; +/** + * @tsplus global + */ +import { FiberRef } from "@effect/core/io/FiberRef/definition"; +/** + * @tsplus global + */ +import { FiberRefs } from "@effect/core/io/FiberRefs/definition"; +/** + * @tsplus global + */ +import { FiberScope } from "@effect/core/io/FiberScope/definition"; +/** + * @tsplus global + */ +import { Hub } from "@effect/core/io/Hub/definition"; +/** + * @tsplus global + */ +import { InterruptStatus } from "@effect/core/io/InterruptStatus/definition"; +/** + * @tsplus global + */ +import { Layer } from "@effect/core/io/Layer/definition"; +/** + * @tsplus global + */ +import { Logger } from "@effect/core/io/Logger/definition"; +/** + * @tsplus global + */ +import { LogLevel } from "@effect/core/io/LogLevel/definition"; +/** + * @tsplus global + */ +import { LogSpan } from "@effect/core/io/LogSpan/definition"; +/** + * @tsplus global + */ +import { MetricRegistry } from "@effect/core/io/Metrics/MetricRegistry"; +/** + * @tsplus global + */ +import { MetricPair } from "@effect/core/io/Metrics/MetricPair"; +/** + * @tsplus global + */ +import { MetricHook } from "@effect/core/io/Metrics/MetricHook"; +/** + * @tsplus global + */ +import { MetricHooks } from "@effect/core/io/Metrics/MetricHooks"; +/** + * @tsplus global + */ +import { Metric } from "@effect/core/io/Metrics/definition"; +/** + * @tsplus global + */ +import { MetricKey } from "@effect/core/io/Metrics/MetricKey"; +/** + * @tsplus global + */ +import { MetricLabel } from "@effect/core/io/Metrics/MetricLabel"; +/** + * @tsplus global + */ +import { MetricListener } from "@effect/core/io/Metrics/MetricListener"; +/** + * @tsplus global + */ +import { MetricState } from "@effect/core/io/Metrics/MetricState"; +/** + * @tsplus global + */ +import { MetricKeyType } from "@effect/core/io/Metrics/MetricKeyType"; +/** + * @tsplus global + */ +import { Dequeue } from "@effect/core/io/Queue/definition/Dequeue"; +/** + * @tsplus global + */ +import { Enqueue } from "@effect/core/io/Queue/definition/Enqueue"; +/** + * @tsplus global + */ +import { Queue } from "@effect/core/io/Queue/definition/Queue"; +/** + * @tsplus global + */ +import { Random } from "@effect/core/io/Random/definition"; +/** + * @tsplus global + */ +import { Ref } from "@effect/core/io/Ref/definition"; +/** + * @tsplus global + */ +import { SynchronizedRef } from "@effect/core/io/Ref/Synchronized/definition"; +/** + * @tsplus global + */ +import { Runtime } from "@effect/core/io/Runtime/definition"; +/** + * @tsplus global + */ +import { RuntimeConfig } from "@effect/core/io/RuntimeConfig/definition"; +/** + * @tsplus global + */ +import { RuntimeConfigFlag } from "@effect/core/io/RuntimeConfig/Flag"; +/** + * @tsplus global + */ +import { RuntimeConfigFlags } from "@effect/core/io/RuntimeConfig/Flags/definition"; +/** + * @tsplus global + */ +import { Schedule } from "@effect/core/io/Schedule/definition"; +/** + * @tsplus global + */ +import { Scope } from "@effect/core/io/Scope/definition"; +/** + * @tsplus global + */ +import { ReleaseMap } from "@effect/core/io/Scope/ReleaseMap/definition"; +/** + * @tsplus global + */ +import { Semaphore } from "@effect/core/io/Semaphore/definition"; +/** + * @tsplus global + */ +import { Supervisor } from "@effect/core/io/Supervisor/definition"; +/** + * @tsplus global + */ +import { Trace } from "@effect/core/io/Trace/definition"; +/** + * @tsplus global + */ +import { TraceElement } from "@effect/core/io/TraceElement/definition"; +/** + * @tsplus global + */ +import { Sync } from "@effect/core/io-light/Sync/definition"; +/** + * @tsplus global + */ +import { XPure } from "@effect/core/io-light/XPure/definition/base"; +/** + * @tsplus global + */ +import { STM, USTM } from "@effect/core/stm/STM/definition/base"; +/** + * @tsplus global + */ +import { TArray } from "@effect/core/stm/TArray/definition"; +/** + * @tsplus global + */ +import { TExit } from "@effect/core/stm/TExit/definition"; +/** + * @tsplus global + */ +import { TPriorityQueue } from "@effect/core/stm/TPriorityQueue/definition"; +/** + * @tsplus global + */ +import { TRef } from "@effect/core/stm/TRef/definition"; +/** + * @tsplus global + */ +import { TSemaphore } from "@effect/core/stm/TSemaphore/definition"; +/** + * @tsplus global + */ +import { Channel } from "@effect/core/stream/Channel/definition/base"; +/** + * @tsplus global + */ +import { GroupBy } from "@effect/core/stream/GroupBy/definition/base"; +/** + * @tsplus global + */ +import { Pull } from "@effect/core/stream/Pull/definition"; +/** + * @tsplus global + */ +import { Sink } from "@effect/core/stream/Sink/definition/base"; +/** + * @tsplus global + */ +import { SortedByKey } from "@effect/core/stream/SortedByKey/definition"; +/** + * @tsplus global + */ +import { Stream } from "@effect/core/stream/Stream/definition"; +/** + * @tsplus global + */ +import { SubscriptionRef } from "@effect/core/stream/SubscriptionRef/definition"; +/** + * @tsplus global + */ +import { Take } from "@effect/core/stream/Take/definition"; +// ============================================================================= +// @tsplus/stdlib +// ============================================================================= +/** + * Ensure types are in scope + */ +import type { } from "@tsplus/stdlib"; +/** + * @tsplus global + */ +import { Chunk } from "@tsplus/stdlib/collections/Chunk/definition"; +/** + * @tsplus global + */ +import { Collection } from "@tsplus/stdlib/collections/Collection/definition"; +/** + * @tsplus global + */ +import { HashMap } from "@tsplus/stdlib/collections/HashMap/definition"; +/** + * @tsplus global + */ +import { HashSet } from "@tsplus/stdlib/collections/HashSet/definition"; +/** + * @tsplus global + */ +import { ImmutableArray } from "@tsplus/stdlib/collections/ImmutableArray"; +/** + * @tsplus global + */ +import { ImmutableQueue } from "@tsplus/stdlib/collections/ImmutableQueue/definition"; +/** + * @tsplus global + */ +import { List } from "@tsplus/stdlib/collections/List/definition"; +/** + * @tsplus global + */ +import { DoublyLinkedList } from "@tsplus/stdlib/collections/mutable/DoublyLinkedList"; +/** + * @tsplus global + */ +import { ListBuffer } from "@tsplus/stdlib/collections/mutable/ListBuffer"; +/** + * @tsplus global + */ +import { MutableHashMap } from "@tsplus/stdlib/collections/mutable/MutableHashMap/definition"; +/** + * @tsplus global + */ +import { MutableHashSet } from "@tsplus/stdlib/collections/mutable/MutableHashSet/definition"; +/** + * @tsplus global + */ +import { EmptyMutableQueue, MutableQueue } from "@tsplus/stdlib/collections/mutable/MutableQueue/definition"; +/** + * @tsplus global + */ +import { ParSeq } from "@tsplus/stdlib/collections/ParSeq/definition"; +/** + * @tsplus global + */ +import { RedBlackTree } from "@tsplus/stdlib/collections/RedBlackTree/definition"; +/** + * @tsplus global + */ +import { SortedMap } from "@tsplus/stdlib/collections/SortedMap/definition"; +/** + * @tsplus global + */ +import { SortedSet } from "@tsplus/stdlib/collections/SortedSet/definition"; +/** + * @tsplus global + */ +import { IterableWeakMap } from "@tsplus/stdlib/collections/weak/IterableWeakMap"; +/** + * @tsplus global + */ +import { AtomicBoolean } from "@tsplus/stdlib/data/AtomicBoolean"; +/** + * @tsplus global + */ +import { AtomicNumber } from "@tsplus/stdlib/data/AtomicNumber"; +/** + * @tsplus global + */ +import { AtomicReference } from "@tsplus/stdlib/data/AtomicReference"; +/** + * @tsplus global + */ +import { Duration } from "@tsplus/stdlib/data/Duration"; +/** + * @tsplus global + */ +import { Either } from "@tsplus/stdlib/data/Either/definition"; +/** + * @tsplus global + */ +import { identity, Lazy, LazyArg, pipe, Refinement, unsafeCoerce } from "@tsplus/stdlib/data/Function"; +/** + * @tsplus global + */ +import { LazyValue } from "@tsplus/stdlib/data/LazyValue"; +/** + * @tsplus global + */ +import { Match } from "@tsplus/stdlib/data/Match"; +/** + * @tsplus global + */ +import { Option } from "@tsplus/stdlib/data/Option/definition"; +/** + * @tsplus global + */ +import { Predicate } from "@tsplus/stdlib/data/Predicate"; +/** + * @tsplus global + */ +import { Env } from "@tsplus/stdlib/service/Env"; +/** + * @tsplus global + */ +import { Has } from "@tsplus/stdlib/service/Has"; +/** + * @tsplus global + */ +import { Service } from "@tsplus/stdlib/service/Service"; +/** + * @tsplus global + */ +import { Tag } from "@tsplus/stdlib/service/Tag"; +/** + * @tsplus global + */ +import { Stack } from "@tsplus/stdlib/data/Stack"; +/** + * @tsplus global + */ +import { Tuple } from "@tsplus/stdlib/data/Tuple/definition"; +/** + * @tsplus global + */ +import { IndexOutOfBounds, NoSuchElement } from "@tsplus/stdlib/exceptions"; +/** + * @tsplus global + */ +import { Exception } from "@tsplus/stdlib/exceptions/Exception"; +/** + * @tsplus global + */ +import { Eval } from "@tsplus/stdlib/io/Eval/definition"; +/** + * @tsplus global + */ +import { Associative } from "@tsplus/stdlib/prelude/Associative"; +/** + * @tsplus global + */ +import { AssociativeIdentity } from "@tsplus/stdlib/prelude/AssociativeIdentity"; +/** + * @tsplus global + */ +import { Closure } from "@tsplus/stdlib/prelude/Closure"; +/** + * @tsplus global + */ +import { Equivalence } from "@tsplus/stdlib/prelude/Equivalence/definition"; +/** + * @tsplus global + */ +import { instance } from "@tsplus/stdlib/prelude/Instance"; +/** + * @tsplus global + */ +import { Ord } from "@tsplus/stdlib/prelude/Ord/definition"; +/** + * @tsplus global + */ +import { Ordering } from "@tsplus/stdlib/prelude/Ordering/definition"; +/** + * @tsplus global + */ +import { Equals } from "@tsplus/stdlib/structure/Equals"; +/** + * @tsplus global + */ +import { Hash } from "@tsplus/stdlib/structure/Hash"; +/** + * @tsplus global + */ +import { isDefined, isIterable, isPlainObject, isPromiseLike, isReactElement } from "@tsplus/stdlib/utilities/Guards"; +/** + * @tsplus global + */ +import { lazy } from "@tsplus/stdlib/utilities/Lazy"; +/** + * @tsplus global + */ +import { RandomPCG } from "@tsplus/stdlib/utilities/RandomPCG"; +/** + * @tsplus global + */ +import { + EnforceNonEmptyRecord, + Erase, + ESArray, + ESIterable, + ESReadonlyArray, + ForcedArray, + ForcedTuple, + IsInt, + MergeRecord, + OrElse, + PredicateWithIndex, + RefinementWithIndex, + Spreadable, + UnionToIntersection +} from "@tsplus/stdlib/utilities/Types"; \ No newline at end of file diff --git a/testing/tsconfig.json b/testing/tsconfig.json new file mode 100644 index 00000000000..50d4f2ab092 --- /dev/null +++ b/testing/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "moduleDetection": "force", + "module": "ESNext", + "target": "ES2021", + "moduleResolution": "Node", + "strict": true, + "declaration": true, + "skipLibCheck": true, + "lib": ["ES2021"] + }, + "files": [ + "./prelude.d.ts" + ] +} \ No newline at end of file diff --git a/testing/tsplus.config.json b/testing/tsplus.config.json new file mode 100644 index 00000000000..67ddf28f184 --- /dev/null +++ b/testing/tsplus.config.json @@ -0,0 +1,12 @@ +{ + "importMap": { + "^(.*)/packages/([^/]*)/src(.*)/index\\.ts$": "@org/$2$3", + "^(.*)/packages/([^/]*)/src(.*)\\.ts$": "@org/$2$3" + }, + "relativeMap": { + "^(.*)/packages/([^/]*)/(.*)$": "$2" + }, + "traceMap": { + "^(.*)/packages/([^/]*)/(.*)$": "(@org/$2) $3" + } +} \ No newline at end of file diff --git a/utils/rebase.sh b/utils/rebase.sh new file mode 100644 index 00000000000..6fa39ed0658 --- /dev/null +++ b/utils/rebase.sh @@ -0,0 +1,3 @@ +#!/bin/sh +git remote add microsoft https://github.com/microsoft/TypeScript.git +git pull --rebase microsoft main \ No newline at end of file