vendor tsgo

This commit is contained in:
2026-07-09 16:50:43 -04:00
parent c06ea2e5a4
commit 98978e4930
5804 changed files with 1556156 additions and 101 deletions

View File

@@ -1,54 +1,54 @@
package main
// Frontend TypeScript checker. Runs tsc in noEmit mode using
// tsconfig.json. Requires node on PATH; downloads the pinned TypeScript
// release on first run (no npm).
// Frontend TypeScript checker using TypeScript 7 (tsgo) - Microsoft's native Go
// port of tsc. No npm, no Node: tsgo is built from the vendored source under
// kjol/tools/tsgo and executed directly.
//
// go run ./cmd/typecheck
// go run ./cmd/typecheck -p tsconfig.json
// go run kjol/cmd/typecheck
// go run kjol/cmd/typecheck -p tsconfig.json
// go run kjol/cmd/typecheck -tsgo path/to/kjol/tools/tsgo
import (
"archive/tar"
"compress/gzip"
"flag"
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
)
const typescriptVersion = "5.8.3"
// tsgoVersion pins the vendored typescript-go source (kjol/tools/tsgo). Bump when
// the vendored copy is updated; it also keys the built-binary cache.
const tsgoVersion = "v0.0.0-20260709155237-487baf0cc74a" // TypeScript 7.1.0-dev
// goToolchain is the Go toolchain used to build tsgo. Its go.mod needs >= go 1.26;
// this exact patch is known-good. Go downloads it once if absent.
const goToolchain = "go1.26.5"
func main() {
tsconfig := flag.String("p", "tsconfig.json", "path to tsconfig.json")
tsgoDir := flag.String("tsgo", "kjol/tools/tsgo", "path to the vendored typescript-go source")
flag.Parse()
if err := runTypecheck(*tsconfig); err != nil {
if err := runTypecheck(*tsconfig, *tsgoDir); err != nil {
fmt.Fprintf(os.Stderr, "Typecheck failed: %v\n", err)
os.Exit(1)
}
}
func runTypecheck(tsconfig string) error {
node, err := exec.LookPath("node")
if err != nil {
return fmt.Errorf("node not found on PATH (required to run tsc): %w", err)
}
tsc, err := ensureTypeScript()
if err != nil {
return err
}
func runTypecheck(tsconfig, tsgoDir string) error {
if _, err := os.Stat(tsconfig); err != nil {
return fmt.Errorf("tsconfig not found: %s", tsconfig)
}
fmt.Printf("Typechecking with TypeScript %s...\n", typescriptVersion)
cmd := exec.Command(node, tsc, "--noEmit", "-p", tsconfig)
tsgo, err := ensureTsgo(tsgoDir)
if err != nil {
return err
}
fmt.Printf("Typechecking with TypeScript 7 (tsgo %s)...\n", tsgoVersion)
cmd := exec.Command(tsgo, "--noEmit", "-p", tsconfig)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Run(); err != nil {
@@ -59,92 +59,56 @@ func runTypecheck(tsconfig string) error {
return nil
}
func ensureTypeScript() (string, error) {
root, err := os.Getwd()
// ensureTsgo builds tsgo from the vendored source once (cached under tmp/tsgo) and
// returns the binary path.
func ensureTsgo(tsgoDir string) (string, error) {
exeName := "tsgo"
if runtime.GOOS == "windows" {
exeName += ".exe"
}
cwd, err := os.Getwd()
if err != nil {
return "", err
}
cacheDir := filepath.Join(root, "tools", ".cache", "typescript", typescriptVersion)
tscPath := filepath.Join(cacheDir, "package", "lib", "tsc.js")
if _, err := os.Stat(tscPath); err == nil {
return tscPath, nil
binPath := filepath.Join(cwd, "tmp", "tsgo", tsgoVersion, exeName)
if _, err := os.Stat(binPath); err == nil {
return binPath, nil
}
fmt.Printf("Downloading TypeScript %s...\n", typescriptVersion)
if err := downloadTypeScript(cacheDir); err != nil {
absTsgo, err := filepath.Abs(tsgoDir)
if err != nil {
return "", err
}
if _, err := os.Stat(tscPath); err != nil {
return "", fmt.Errorf("tsc not found after download: %s", tscPath)
if _, err := os.Stat(filepath.Join(absTsgo, "go.mod")); err != nil {
return "", fmt.Errorf("vendored tsgo source not found at %s (pass -tsgo or check kjol/tools/tsgo)", absTsgo)
}
return tscPath, nil
if err := os.MkdirAll(filepath.Dir(binPath), 0o755); err != nil {
return "", err
}
fmt.Println("Building tsgo from vendored source (first run; cached afterwards)...")
build := exec.Command("go", "build", "-C", absTsgo, "-mod=vendor", "-o", binPath, "./cmd/tsgo")
build.Env = buildEnv()
build.Stdout = os.Stdout
build.Stderr = os.Stderr
if err := build.Run(); err != nil {
return "", fmt.Errorf("building tsgo: %w", err)
}
if _, err := os.Stat(binPath); err != nil {
return "", fmt.Errorf("tsgo not found after build: %s", binPath)
}
return binPath, nil
}
func downloadTypeScript(destDir string) error {
url := fmt.Sprintf("https://registry.npmjs.org/typescript/-/typescript-%s.tgz", typescriptVersion)
resp, err := http.Get(url)
if err != nil {
return fmt.Errorf("download typescript: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("download typescript: HTTP %s", resp.Status)
}
if err := os.MkdirAll(destDir, 0o755); err != nil {
return err
}
return extractTGZ(resp.Body, destDir)
}
func extractTGZ(r io.Reader, destDir string) error {
gz, err := gzip.NewReader(r)
if err != nil {
return fmt.Errorf("read typescript archive: %w", err)
}
defer gz.Close()
tr := tar.NewReader(gz)
cleanDest := filepath.Clean(destDir)
for {
hdr, err := tr.Next()
if err == io.EOF {
return nil
}
if err != nil {
return fmt.Errorf("read typescript archive: %w", err)
}
target := filepath.Join(destDir, filepath.FromSlash(hdr.Name))
cleanTarget := filepath.Clean(target)
if cleanTarget != cleanDest && !strings.HasPrefix(cleanTarget, cleanDest+string(os.PathSeparator)) {
return fmt.Errorf("invalid archive path: %s", hdr.Name)
}
switch hdr.Typeflag {
case tar.TypeDir:
if err := os.MkdirAll(target, 0o755); err != nil {
return err
}
case tar.TypeReg:
if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil {
return err
}
f, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, os.FileMode(hdr.Mode)&0o777|0o600)
if err != nil {
return err
}
if _, err := io.Copy(f, tr); err != nil {
f.Close()
return err
}
if err := f.Close(); err != nil {
return err
}
// buildEnv builds tsgo standalone: outside any go.work, with a pinned toolchain.
func buildEnv() []string {
var env []string
for _, e := range os.Environ() {
if strings.HasPrefix(e, "GOWORK=") || strings.HasPrefix(e, "GOTOOLCHAIN=") {
continue
}
env = append(env, e)
}
return append(env, "GOWORK=off", "GOTOOLCHAIN="+goToolchain)
}

55
tools/tsgo/LICENSE Normal file
View File

@@ -0,0 +1,55 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
You must give any other recipients of the Work or Derivative Works a copy of this License; and
You must cause any modified files to carry prominent notices stating that You changed the files; and
You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS

436
tools/tsgo/NOTICE.txt Normal file

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,61 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"os/signal"
"strings"
"syscall"
"github.com/microsoft/typescript-go/internal/api"
"github.com/microsoft/typescript-go/internal/bundled"
"github.com/microsoft/typescript-go/internal/core"
)
func runAPI(args []string) int {
flag := flag.NewFlagSet("api", flag.ContinueOnError)
cwd := flag.String("cwd", core.Must(os.Getwd()), "current working directory")
pipePath := flag.String("pipe", "", "use named pipe or Unix domain socket for communication instead of stdio")
callbacks := flag.String("callbacks", "", "comma-separated list of FS callbacks to enable (readFile,fileExists,directoryExists,getAccessibleEntries,realpath)")
async := flag.Bool("async", false, "use JSON-RPC protocol instead of MessagePack (for async API)")
timing := flag.Bool("timing", false, "collect per-request server processing time, folded into the client's timing snapshot")
if err := flag.Parse(args); err != nil {
return 2
}
defaultLibraryPath := bundled.LibPath()
// Parse callbacks list
var callbacksList []string
if *callbacks != "" {
callbacksList = strings.Split(*callbacks, ",")
}
options := &api.StdioServerOptions{
Err: os.Stderr,
Cwd: *cwd,
DefaultLibraryPath: defaultLibraryPath,
Callbacks: callbacksList,
Async: *async,
CollectTiming: *timing,
}
if *pipePath != "" {
options.PipePath = *pipePath
} else {
options.In = os.Stdin
options.Out = os.Stdout
}
s := api.NewStdioServer(options)
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
if err := s.Run(ctx); err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
return 0
}

View File

@@ -0,0 +1,22 @@
package main
import (
"golang.org/x/sys/windows"
)
func init() {
h, err := windows.GetStdHandle(windows.STD_OUTPUT_HANDLE)
if err != nil || h == windows.InvalidHandle {
return
}
fileType, err := windows.GetFileType(h)
if err != nil || fileType == windows.FILE_TYPE_CHAR {
var mode uint32
if err := windows.GetConsoleMode(h, &mode); err != nil {
return
}
if mode&windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING == 0 {
_ = windows.SetConsoleMode(h, mode|windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
}
}
}

View File

@@ -0,0 +1,9 @@
//go:build !unix && !windows
package main
const processAliveSupported = false
func isProcessAlive(pid int) bool {
panic("isProcessAlive is not supported on this platform")
}

View File

@@ -0,0 +1,25 @@
//go:build unix
package main
import (
"errors"
"os"
"syscall"
)
const processAliveSupported = true
// isProcessAlive checks if a process with the given PID is still running.
// On Unix, FindProcess always succeeds, so we send signal 0 to probe the
// process. If the signal returns nil or EPERM, the process exists (EPERM
// means it exists but we lack permission to signal it). ESRCH or any
// other error indicates the process is gone.
func isProcessAlive(pid int) bool {
proc, err := os.FindProcess(pid)
if err != nil {
return false
}
err = proc.Signal(syscall.Signal(0))
return err == nil || errors.Is(err, syscall.EPERM)
}

View File

@@ -0,0 +1,26 @@
//go:build windows
package main
import "syscall"
const processAliveSupported = true
// isProcessAlive checks if a process with the given PID is still running.
// On Windows, we open the process with SYNCHRONIZE access and use
// WaitForSingleObject with a zero timeout. If the wait times out, the
// process is still running. If the object is signaled, it has exited.
func isProcessAlive(pid int) bool {
const SYNCHRONIZE = 0x00100000
handle, err := syscall.OpenProcess(SYNCHRONIZE, false, uint32(pid))
if err != nil {
return false
}
defer func() { _ = syscall.CloseHandle(handle) }()
ret, err := syscall.WaitForSingleObject(handle, 0)
if err != nil {
return false
}
const WAIT_TIMEOUT = 258
return ret == WAIT_TIMEOUT
}

108
tools/tsgo/cmd/tsgo/lsp.go Normal file
View File

@@ -0,0 +1,108 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"os/exec"
"os/signal"
"syscall"
"time"
"github.com/microsoft/typescript-go/internal/bundled"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/lsp"
"github.com/microsoft/typescript-go/internal/pprof"
"github.com/microsoft/typescript-go/internal/vfs/osvfs"
)
func runLSP(args []string) int {
flag := flag.NewFlagSet("lsp", flag.ContinueOnError)
stdio := flag.Bool("stdio", false, "use stdio for communication")
pprofDir := flag.String("pprofDir", "", "Generate pprof CPU/memory profiles to the given directory.")
pipe := flag.String("pipe", "", "use named pipe for communication")
_ = pipe
socket := flag.String("socket", "", "use socket for communication")
_ = socket
if err := flag.Parse(args); err != nil {
return 2
}
if !*stdio {
fmt.Fprintln(os.Stderr, "only stdio is supported")
return 1
}
if *pprofDir != "" {
fmt.Fprintf(os.Stderr, "pprof profiles will be written to: %v\n", *pprofDir)
profileSession := pprof.BeginProfiling(*pprofDir, os.Stderr)
defer profileSession.Stop()
}
fs := bundled.WrapFS(osvfs.FS())
defaultLibraryPath := bundled.LibPath()
typingsLocation := osvfs.GetGlobalTypingsCacheLocation()
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()
s := lsp.NewServer(&lsp.ServerOptions{
In: lsp.ToReader(os.Stdin),
Out: lsp.ToWriter(os.Stdout),
Err: os.Stderr,
Cwd: core.Must(os.Getwd()),
FS: fs,
DefaultLibraryPath: defaultLibraryPath,
TypingsLocation: typingsLocation,
NpmInstall: func(cwd string, args []string) ([]byte, error) {
cmd := exec.Command("npm", args...)
cmd.Dir = cwd
return cmd.Output()
},
ProgressDelay: 250 * time.Millisecond,
SetParentProcessID: newParentProcessWatchdog(ctx, stop),
})
if err := s.Run(ctx); err != nil {
fmt.Fprintln(os.Stderr, err)
return 1
}
return 0
}
// newParentProcessWatchdog returns a SetParentProcessID callback if the platform
// supports process-alive checking, or nil otherwise.
func newParentProcessWatchdog(ctx context.Context, stop context.CancelFunc) func(int) {
if !processAliveSupported {
return nil
}
return func(parentPID int) {
startParentProcessWatchdog(ctx, stop, parentPID)
}
}
// startParentProcessWatchdog starts a goroutine that monitors the parent process
// and cancels the context if the parent dies. This prevents orphaned language
// server processes when the editor crashes or is killed.
func startParentProcessWatchdog(ctx context.Context, stop context.CancelFunc, parentPID int) {
if parentPID <= 0 {
return
}
go func() {
ticker := time.NewTicker(5 * time.Second)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if !isProcessAlive(parentPID) {
fmt.Fprintf(os.Stderr, "Parent process %d has exited, shutting down.\n", parentPID)
stop()
return
}
}
}
}()
}

View File

@@ -0,0 +1,32 @@
package main
import (
"context"
"os"
"os/signal"
"syscall"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/execute"
)
func main() {
os.Exit(runMain())
}
func runMain() int {
core.ApplyDebugStackLimit()
args := os.Args[1:]
if len(args) > 0 {
switch args[0] {
case "--lsp":
return runLSP(args[1:])
case "--api":
return runAPI(args[1:])
}
}
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
defer stop()
result := execute.CommandLine(ctx, newSystem(), args, nil)
return int(result.Status)
}

View File

@@ -0,0 +1,76 @@
package main
import (
"fmt"
"io"
"os"
"time"
"github.com/microsoft/typescript-go/internal/bundled"
"github.com/microsoft/typescript-go/internal/execute/tsc"
"github.com/microsoft/typescript-go/internal/tspath"
"github.com/microsoft/typescript-go/internal/vfs"
"github.com/microsoft/typescript-go/internal/vfs/osvfs"
"golang.org/x/term"
)
type osSys struct {
writer io.Writer
fs vfs.FS
defaultLibraryPath string
cwd string
start time.Time
}
func (s *osSys) SinceStart() time.Duration {
return time.Since(s.start)
}
func (s *osSys) Now() time.Time {
return time.Now()
}
func (s *osSys) FS() vfs.FS {
return s.fs
}
func (s *osSys) DefaultLibraryPath() string {
return s.defaultLibraryPath
}
func (s *osSys) GetCurrentDirectory() string {
return s.cwd
}
func (s *osSys) Writer() io.Writer {
return s.writer
}
func (s *osSys) WriteOutputIsTTY() bool {
return term.IsTerminal(int(os.Stdout.Fd()))
}
func (s *osSys) GetWidthOfTerminal() int {
width, _, _ := term.GetSize(int(os.Stdout.Fd()))
return width
}
func (s *osSys) GetEnvironmentVariable(name string) string {
return os.Getenv(name)
}
func newSystem() *osSys {
cwd, err := os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting current directory: %v\n", err)
os.Exit(int(tsc.ExitStatusInvalidProject_OutputsSkipped))
}
return &osSys{
cwd: tspath.NormalizePath(cwd),
fs: bundled.WrapFS(osvfs.FS()),
defaultLibraryPath: bundled.LibPath(),
writer: os.Stdout,
start: time.Now(),
}
}

38
tools/tsgo/go.mod Normal file
View File

@@ -0,0 +1,38 @@
module github.com/microsoft/typescript-go
go 1.26
require (
github.com/Microsoft/go-winio v0.6.2
github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68
github.com/google/go-cmp v0.7.0
github.com/mackerelio/go-osstat v0.2.7
github.com/peter-evans/patience v0.3.0
github.com/zeebo/xxh3 v1.1.0
golang.org/x/sync v0.21.0
golang.org/x/sys v0.46.0
golang.org/x/term v0.44.0
golang.org/x/text v0.38.0
gotest.tools/v3 v3.5.2
)
require (
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
github.com/matryer/moq v0.7.1 // indirect
golang.org/x/mod v0.37.0 // indirect
golang.org/x/tools v0.47.0 // indirect
)
tool (
github.com/matryer/moq
golang.org/x/tools/cmd/stringer
)
ignore (
./_extension
./_packages
./_submodules
./built
./coverage
node_modules
)

34
tools/tsgo/go.sum Normal file
View File

@@ -0,0 +1,34 @@
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68 h1:KZaTBSyshWX3MP5jukJcNSuXDQTO+rNpt0J564dX/eg=
github.com/go-json-experiment/json v0.0.0-20260623181947-01eb4420fa68/go.mod h1:tphK2c80bpPhMOI4v6bIc2xWywPfbqi1Z06+RcrMkDg=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/mackerelio/go-osstat v0.2.7 h1:TCavZi10wF49bT6iQZ9eT2keGZQpC69MTDfdJej5e94=
github.com/mackerelio/go-osstat v0.2.7/go.mod h1:dwpYh5pIPmvk+IEwBKNIWRFMB92mrC08CmXOhDC7nQk=
github.com/matryer/moq v0.7.1 h1:/QaXqMAdOrLqlshW2z7SMS21jDi7aVrbW0wJrR+hhJk=
github.com/matryer/moq v0.7.1/go.mod h1:IabIiFkaKCyHxej25INgFR+fnOxSZFMv2LYrU+ioyDs=
github.com/peter-evans/patience v0.3.0 h1:rX0JdJeepqdQl1Sk9c9uvorjYYzL2TfgLX1adqYm9cA=
github.com/peter-evans/patience v0.3.0/go.mod h1:Kmxu5sY1NmBLFSStvXjX1wS9mIv7wMcP/ubucyMOAu0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/tools v0.47.0 h1:7Kn5x/d1svx/PzryTsqeoZN4TZwqeH5pGWjefhLi/1Q=
golang.org/x/tools v0.47.0/go.mod h1:dFHnyTvFWY212G+h7ZY4Vsp/K3U4/7W9TyVaAul8uCA=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=

View File

@@ -0,0 +1,226 @@
package api
import (
"context"
"fmt"
"time"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/vfs"
)
// callbackFS wraps a base filesystem and delegates certain operations
// to the client via RPC callbacks. This allows the API client to provide
// a virtual filesystem (e.g., in-memory files for testing).
//
// The callbacks to enable are specified at construction time via the
// --callbacks CLI flag. The connection is set via SetConnection after
// the transport connection is established.
type callbackFS struct {
base vfs.FS
enabledCallbacks map[string]bool
// conn and ctx are set after connection is established
conn Conn
ctx context.Context
}
// Callback names that can be enabled
const (
callbackReadFile = "readFile"
callbackFileExists = "fileExists"
callbackDirectoryExists = "directoryExists"
callbackGetAccessibleEntries = "getAccessibleEntries"
callbackRealpath = "realpath"
)
func isCallbackName(name string) bool {
switch name {
case callbackReadFile,
callbackFileExists,
callbackDirectoryExists,
callbackGetAccessibleEntries,
callbackRealpath:
return true
default:
return false
}
}
// newCallbackFS creates a new callbackFS wrapping the given base filesystem.
// The callbacks slice specifies which filesystem operations should be delegated
// to the client (e.g., "readFile", "fileExists").
func newCallbackFS(base vfs.FS, callbacks []string) *callbackFS {
enabled := make(map[string]bool, len(callbacks))
for _, cb := range callbacks {
if !isCallbackName(cb) {
panic("unknown callback name: " + cb)
}
enabled[cb] = true
}
return &callbackFS{
base: base,
enabledCallbacks: enabled,
}
}
// SetConnection sets the RPC connection for callbacks.
// This must be called after the transport connection is established
// but before any filesystem operations that need callbacks.
func (fs *callbackFS) SetConnection(ctx context.Context, conn Conn) {
fs.ctx = ctx
fs.conn = conn
}
// isEnabled returns true if the named callback is enabled.
func (fs *callbackFS) isEnabled(name string) bool {
return fs.enabledCallbacks[name]
}
// call invokes a callback on the client and returns the result.
func (fs *callbackFS) call(name string, arg any) ([]byte, error) {
if fs.conn == nil {
return nil, fmt.Errorf("CallbackFS: %s called before connection set", name)
}
result, err := fs.conn.Call(fs.ctx, name, arg)
if err != nil {
return nil, err
}
return result, nil
}
// UseCaseSensitiveFileNames implements vfs.FS.
func (fs *callbackFS) UseCaseSensitiveFileNames() bool {
return fs.base.UseCaseSensitiveFileNames()
}
// ReadFile implements vfs.FS.
//
// The readFile callback uses a wrapped response format to distinguish three states:
// - undefined (fall back to real FS): null or empty on wire
// - null (not found, no fallback): {"content": null}
// - string content: {"content": "..."}
func (fs *callbackFS) ReadFile(path string) (contents string, ok bool) {
if fs.isEnabled(callbackReadFile) {
result, err := fs.call(callbackReadFile, path)
if err != nil {
panic(err)
}
if len(result) > 0 && string(result) != "null" {
var wrapper struct {
Content *string `json:"content"`
}
if err := json.Unmarshal(result, &wrapper); err != nil {
panic(err)
}
if wrapper.Content == nil {
return "", false
}
return *wrapper.Content, true
}
}
return fs.base.ReadFile(path)
}
// FileExists implements vfs.FS.
func (fs *callbackFS) FileExists(path string) bool {
if fs.isEnabled(callbackFileExists) {
result, err := fs.call(callbackFileExists, path)
if err != nil {
panic(err)
}
if len(result) > 0 && string(result) != "null" {
return string(result) == "true"
}
}
return fs.base.FileExists(path)
}
// DirectoryExists implements vfs.FS.
func (fs *callbackFS) DirectoryExists(path string) bool {
if fs.isEnabled(callbackDirectoryExists) {
result, err := fs.call(callbackDirectoryExists, path)
if err != nil {
panic(err)
}
if len(result) > 0 && string(result) != "null" {
return string(result) == "true"
}
}
return fs.base.DirectoryExists(path)
}
// GetAccessibleEntries implements vfs.FS.
func (fs *callbackFS) GetAccessibleEntries(path string) vfs.Entries {
if fs.isEnabled(callbackGetAccessibleEntries) {
result, err := fs.call(callbackGetAccessibleEntries, path)
if err != nil {
panic(err)
}
if len(result) > 0 {
var rawEntries *struct {
Files []string `json:"files"`
Directories []string `json:"directories"`
}
if err := json.Unmarshal(result, &rawEntries); err != nil {
panic(err)
}
if rawEntries != nil {
return vfs.Entries{
Files: rawEntries.Files,
Directories: rawEntries.Directories,
}
}
}
}
return fs.base.GetAccessibleEntries(path)
}
// Realpath implements vfs.FS.
func (fs *callbackFS) Realpath(path string) string {
if fs.isEnabled(callbackRealpath) {
result, err := fs.call(callbackRealpath, path)
if err != nil {
panic(err)
}
if len(result) > 0 && string(result) != "null" {
var realpath string
if err := json.Unmarshal(result, &realpath); err != nil {
panic(err)
}
return realpath
}
}
return fs.base.Realpath(path)
}
// WriteFile implements vfs.FS - always delegates to base (no callback support).
func (fs *callbackFS) WriteFile(path string, data string) error {
return fs.base.WriteFile(path, data)
}
// AppendFile implements vfs.FS - always delegates to base (no callback support).
func (fs *callbackFS) AppendFile(path string, data string) error {
return fs.base.AppendFile(path, data)
}
// Remove implements vfs.FS - always delegates to base (no callback support).
func (fs *callbackFS) Remove(path string) error {
return fs.base.Remove(path)
}
// Chtimes implements vfs.FS - always delegates to base (no callback support).
func (fs *callbackFS) Chtimes(path string, aTime time.Time, mTime time.Time) error {
return fs.base.Chtimes(path, aTime, mTime)
}
// Stat implements vfs.FS - always delegates to base (no callback support).
func (fs *callbackFS) Stat(path string) vfs.FileInfo {
return fs.base.Stat(path)
}
// WalkDir implements vfs.FS - always delegates to base (no callback support).
func (fs *callbackFS) WalkDir(root string, walkFn vfs.WalkDirFunc) error {
return fs.base.WalkDir(root, walkFn)
}

View File

@@ -0,0 +1,46 @@
package api
import (
"context"
"errors"
"github.com/microsoft/typescript-go/internal/json"
)
var (
ErrConnClosed = errors.New("api: connection closed")
ErrRequestTimeout = errors.New("api: request timeout")
)
// Handler processes incoming API requests and notifications.
type Handler interface {
// HandleRequest handles an incoming request and returns a result or error.
HandleRequest(ctx context.Context, method string, params json.Value) (any, error)
// HandleNotification handles an incoming notification.
HandleNotification(ctx context.Context, method string, params json.Value) error
}
// Conn represents a bidirectional connection for API communication.
type Conn interface {
// Run starts processing messages on the connection.
// It blocks until the context is cancelled or an error occurs.
Run(ctx context.Context) error
// Call sends a request to the client and waits for a response.
Call(ctx context.Context, method string, params any) (json.Value, error)
// Notify sends a notification to the client (no response expected).
Notify(ctx context.Context, method string, params any) error
}
// UnmarshalParams is a helper to unmarshal params into a typed struct.
func UnmarshalParams[T any](params json.Value) (*T, error) {
if len(params) == 0 {
return nil, nil
}
var v T
if err := json.Unmarshal(params, &v); err != nil {
return nil, err
}
return &v, nil
}

View File

@@ -0,0 +1,231 @@
package api
import (
"context"
"errors"
"fmt"
"io"
"runtime/debug"
"sync"
"sync/atomic"
"time"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/jsonrpc"
)
// AsyncConn manages bidirectional JSON-RPC communication with async request handling.
// Each incoming request is handled in its own goroutine, allowing concurrent processing.
// This is the standard implementation for LSP-style JSON-RPC protocols.
type AsyncConn struct {
rwc io.ReadWriteCloser
protocol Protocol
handler Handler
// timing, when non-nil, accumulates the wall-clock time spent handling each
// request. Clients retrieve the collected data via a getServerTiming request.
timing *timingCollector
// For server→client requests
seq atomic.Int64
pending map[jsonrpc.ID]chan *Message
pendingMu sync.Mutex
writeMu sync.Mutex
}
// NewAsyncConn creates a new async connection with the given transport and handler.
// It uses JSONRPCProtocol (LSP-style Content-Length framing) by default.
func NewAsyncConn(rwc io.ReadWriteCloser, handler Handler) *AsyncConn {
return NewAsyncConnWithProtocol(rwc, NewJSONRPCProtocol(rwc), handler)
}
// NewAsyncConnWithProtocol creates a new async connection with a custom protocol.
func NewAsyncConnWithProtocol(rwc io.ReadWriteCloser, protocol Protocol, handler Handler) *AsyncConn {
return &AsyncConn{
rwc: rwc,
protocol: protocol,
handler: handler,
pending: make(map[jsonrpc.ID]chan *Message),
}
}
// SetCollectTiming enables or disables per-request server processing-time
// measurement. When enabled, the connection accumulates timing that clients can
// retrieve via a getServerTiming request.
func (c *AsyncConn) SetCollectTiming(enabled bool) {
if enabled {
c.timing = newTimingCollector()
} else {
c.timing = nil
}
}
// Run starts processing messages on the connection.
// It blocks until the context is cancelled or an error occurs.
func (c *AsyncConn) Run(ctx context.Context) error {
for {
if ctx.Err() != nil {
return ctx.Err()
}
msg, err := c.protocol.ReadMessage()
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return err
}
if msg.IsResponse() {
c.handleResponse(msg)
} else if msg.IsRequest() {
go c.handleRequest(ctx, msg)
} else if msg.IsNotification() {
go c.handleNotification(ctx, msg)
}
}
}
// handleResponse matches a response to a pending request.
func (c *AsyncConn) handleResponse(msg *Message) {
c.pendingMu.Lock()
ch, ok := c.pending[*msg.ID]
if ok {
delete(c.pending, *msg.ID)
}
c.pendingMu.Unlock()
if ok {
ch <- msg
close(ch)
}
}
// handleRequest processes an incoming request.
func (c *AsyncConn) handleRequest(ctx context.Context, msg *Message) {
// Intercept the meta-requests for collected server timing before dispatching
// to the handler, so they are answered directly and not themselves recorded.
switch msg.Method {
case string(MethodGetServerTiming):
c.writeMu.Lock()
writeErr := c.protocol.WriteResponse(msg.ID, serverTimingSnapshot(c.timing))
c.writeMu.Unlock()
if writeErr != nil {
panic(fmt.Sprintf("api: failed to write server timing response: %v", writeErr))
}
return
case string(MethodResetServerTiming):
if c.timing != nil {
c.timing.reset()
}
c.writeMu.Lock()
writeErr := c.protocol.WriteResponse(msg.ID, nil)
c.writeMu.Unlock()
if writeErr != nil {
panic(fmt.Sprintf("api: failed to write reset server timing response: %v", writeErr))
}
return
}
var result any
var err error
start := time.Time{}
if c.timing != nil {
start = time.Now()
}
// Recover from panics and convert to error response with stack trace
defer func() {
if r := recover(); r != nil {
stack := string(debug.Stack())
err = fmt.Errorf("panic: %v\n%s", r, stack)
c.writeMu.Lock()
writeErr := c.protocol.WriteError(msg.ID, &jsonrpc.ResponseError{
Code: jsonrpc.CodeInternalError,
Message: err.Error(),
})
c.writeMu.Unlock()
if writeErr != nil {
panic(fmt.Sprintf("api: failed to write panic error response: %v (original panic: %v)", writeErr, r))
}
}
}()
result, err = c.handler.HandleRequest(ctx, msg.Method, msg.Params)
if c.timing != nil {
c.timing.record(msg.Method, time.Since(start))
}
c.writeMu.Lock()
defer c.writeMu.Unlock()
var writeErr error
if err != nil {
writeErr = c.protocol.WriteError(msg.ID, &jsonrpc.ResponseError{
Code: jsonrpc.CodeInternalError,
Message: err.Error(),
})
} else {
writeErr = c.protocol.WriteResponse(msg.ID, result)
}
if writeErr != nil {
panic(fmt.Sprintf("api: failed to write response: %v", writeErr))
}
}
// handleNotification processes an incoming notification.
func (c *AsyncConn) handleNotification(ctx context.Context, msg *Message) {
_ = c.handler.HandleNotification(ctx, msg.Method, msg.Params)
}
// Call sends a request to the client and waits for a response.
func (c *AsyncConn) Call(ctx context.Context, method string, params any) (json.Value, error) {
// Create unique request ID
id := jsonrpc.NewIDString(fmt.Sprintf("api%d", c.seq.Add(1)))
// Register response channel BEFORE sending request to avoid race
responseChan := make(chan *Message, 1)
c.pendingMu.Lock()
c.pending[*id] = responseChan
c.pendingMu.Unlock()
defer func() {
c.pendingMu.Lock()
defer c.pendingMu.Unlock()
if ch, ok := c.pending[*id]; ok {
close(ch)
delete(c.pending, *id)
}
}()
// Send the request
c.writeMu.Lock()
err := c.protocol.WriteRequest(id, method, params)
c.writeMu.Unlock()
if err != nil {
return nil, err
}
select {
case <-ctx.Done():
return nil, ctx.Err()
case resp := <-responseChan:
if resp.Error != nil {
return nil, fmt.Errorf("api: remote error [%d]: %s", resp.Error.Code, resp.Error.Message)
}
return resp.Result, nil
}
}
// Notify sends a notification to the client (no response expected).
func (c *AsyncConn) Notify(ctx context.Context, method string, params any) error {
c.writeMu.Lock()
defer c.writeMu.Unlock()
return c.protocol.WriteNotification(method, params)
}

View File

@@ -0,0 +1,208 @@
package api
import (
"context"
"errors"
"fmt"
"io"
"runtime/debug"
"sync"
"time"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/jsonrpc"
)
// SyncConn manages bidirectional communication with synchronous request handling.
// Requests are handled one at a time inline, and outgoing calls are serialized.
type SyncConn struct {
rwc io.ReadWriteCloser
protocol Protocol
handler Handler
// timing, when non-nil, accumulates the wall-clock time spent handling each
// request. Clients retrieve the collected data via a getServerTiming request.
timing *timingCollector
// mu serializes all protocol operations (reads and writes).
// This ensures that concurrent calls from handler goroutines (e.g., project code
// spawning goroutines that invoke filesystem callbacks) don't corrupt the stream.
mu sync.Mutex
}
// NewSyncConn creates a new sync connection with the given transport and handler.
func NewSyncConn(rwc io.ReadWriteCloser, protocol Protocol, handler Handler) *SyncConn {
return &SyncConn{
rwc: rwc,
protocol: protocol,
handler: handler,
}
}
// SetCollectTiming enables or disables per-request server processing-time
// measurement. When enabled, the connection accumulates timing that clients can
// retrieve via a getServerTiming request.
func (c *SyncConn) SetCollectTiming(enabled bool) {
if enabled {
c.timing = newTimingCollector()
} else {
c.timing = nil
}
}
// Run starts processing messages on the connection.
// It blocks until the context is cancelled or an error occurs.
func (c *SyncConn) Run(ctx context.Context) error {
for {
if ctx.Err() != nil {
return ctx.Err()
}
c.mu.Lock()
msg, err := c.protocol.ReadMessage()
c.mu.Unlock()
if err != nil {
if errors.Is(err, io.EOF) {
return nil
}
return err
}
if msg.IsRequest() {
c.handleRequest(ctx, msg)
} else if msg.IsNotification() {
c.handleNotification(ctx, msg)
} else {
// Responses are not expected in the main loop - they are read inline by Call().
return errors.New("api: unexpected response message in sync connection")
}
}
}
// handleRequest processes an incoming request.
func (c *SyncConn) handleRequest(ctx context.Context, msg *Message) {
// Intercept the meta-requests for collected server timing before dispatching
// to the handler, so they are answered directly and not themselves recorded.
switch msg.Method {
case string(MethodGetServerTiming):
c.mu.Lock()
writeErr := c.protocol.WriteResponse(msg.ID, serverTimingSnapshot(c.timing))
c.mu.Unlock()
if writeErr != nil {
panic(fmt.Sprintf("api: failed to write server timing response: %v", writeErr))
}
return
case string(MethodResetServerTiming):
if c.timing != nil {
c.timing.reset()
}
c.mu.Lock()
writeErr := c.protocol.WriteResponse(msg.ID, nil)
c.mu.Unlock()
if writeErr != nil {
panic(fmt.Sprintf("api: failed to write reset server timing response: %v", writeErr))
}
return
}
var result any
var err error
start := time.Time{}
if c.timing != nil {
start = time.Now()
}
// Recover from panics and convert to error response with stack trace
defer func() {
if r := recover(); r != nil {
stack := string(debug.Stack())
err = fmt.Errorf("panic: %v\n%s", r, stack)
c.mu.Lock()
writeErr := c.protocol.WriteError(msg.ID, &jsonrpc.ResponseError{
Code: jsonrpc.CodeInternalError,
Message: err.Error(),
})
c.mu.Unlock()
if writeErr != nil {
panic(fmt.Sprintf("api: failed to write panic error response: %v (original panic: %v)", writeErr, r))
}
}
}()
result, err = c.handler.HandleRequest(ctx, msg.Method, msg.Params)
if c.timing != nil {
c.timing.record(msg.Method, time.Since(start))
}
c.mu.Lock()
defer c.mu.Unlock()
var writeErr error
if err != nil {
writeErr = c.protocol.WriteError(msg.ID, &jsonrpc.ResponseError{
Code: jsonrpc.CodeInternalError,
Message: err.Error(),
})
} else {
writeErr = c.protocol.WriteResponse(msg.ID, result)
}
if writeErr != nil {
panic(fmt.Sprintf("api: failed to write response: %v", writeErr))
}
}
// handleNotification processes an incoming notification.
func (c *SyncConn) handleNotification(ctx context.Context, msg *Message) {
_ = c.handler.HandleNotification(ctx, msg.Method, msg.Params)
}
// Call sends a request to the client and waits for a response.
// This method is safe to call from multiple goroutines - calls are serialized.
func (c *SyncConn) Call(ctx context.Context, method string, params any) (json.Value, error) {
// Serialize all Call operations. This is critical because:
// 1. The msgpack protocol uses method names as response IDs
// 2. The handler code (project internals) may spawn goroutines that call
// filesystem callbacks concurrently
// 3. We need to ensure write/read pairs are atomic
c.mu.Lock()
defer c.mu.Unlock()
id := jsonrpc.NewIDString(method)
if err := c.protocol.WriteRequest(id, method, params); err != nil {
return nil, err
}
if ctx.Err() != nil {
return nil, ctx.Err()
}
// Read the response inline.
msg, err := c.protocol.ReadMessage()
if err != nil {
return nil, err
}
if msg.IsResponse() && msg.ID != nil && msg.ID.String() == method {
if msg.Error != nil {
return nil, fmt.Errorf("api: remote error [%d]: %s", msg.Error.Code, msg.Error.Message)
}
return msg.Result, nil
}
// Unexpected message while waiting for response
return nil, fmt.Errorf("api: unexpected message while waiting for %q response", method)
}
// Notify sends a notification to the client (no response expected).
func (c *SyncConn) Notify(ctx context.Context, method string, params any) error {
c.mu.Lock()
defer c.mu.Unlock()
return c.protocol.WriteNotification(method, params)
}

View File

@@ -0,0 +1,381 @@
package encoder
import (
"encoding/binary"
"errors"
"fmt"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/tspath"
)
// astDecoder reconstructs real *ast.Node objects from binary-encoded data.
type astDecoder struct {
raw []byte
strTable uint32
strData uint32
extData uint32
nodeOff uint32
nodeCount int
factory *ast.NodeFactory
childBuf []int
// Single Go string covering all string data; substrings are zero-alloc slices.
allStringData string
// Arena for batch-allocating []*ast.Node slices used by NodeLists.
nodeArena []*ast.Node
// Results
nodes []*ast.Node
nodeLists []*ast.NodeList
}
// DecodeSourceFile decodes binary-encoded data into an *ast.SourceFile.
func DecodeSourceFile(data []byte) (*ast.SourceFile, error) {
node, err := DecodeNodes(data)
if err != nil {
return nil, err
}
if node.Kind != ast.KindSourceFile {
return nil, fmt.Errorf("expected SourceFile root, got %v", node.Kind)
}
return node.AsSourceFile(), nil
}
// DecodeNodes decodes binary-encoded AST data into a tree of *ast.Node objects.
func DecodeNodes(data []byte) (*ast.Node, error) {
d, err := newASTDecoder(data)
if err != nil {
return nil, err
}
return d.decode()
}
func newASTDecoder(data []byte) (*astDecoder, error) {
if len(data) < HeaderSize {
return nil, fmt.Errorf("data too short for header: %d bytes", len(data))
}
version := data[HeaderOffsetMetadata+3]
if version != ProtocolVersion {
return nil, fmt.Errorf("unsupported protocol version %d (expected %d)", version, ProtocolVersion)
}
strTable := readLE32(data, HeaderOffsetStringOffsets)
strData := readLE32(data, HeaderOffsetStringData)
extData := readLE32(data, HeaderOffsetExtendedData)
nodeOff := readLE32(data, HeaderOffsetNodes)
dataLen := uint32(len(data))
// Validate that all offsets are within the buffer.
if strTable > dataLen || strData > dataLen || extData > dataLen || nodeOff > dataLen {
return nil, fmt.Errorf("invalid AST header offsets: offsets exceed data length (%d)", dataLen)
}
// Validate monotonic non-decreasing order of regions.
if !(strTable <= strData && strData <= extData && extData <= nodeOff) {
return nil, fmt.Errorf("invalid AST header offsets: expected strTable <= strData <= extData <= nodeOff (got %d, %d, %d, %d)", strTable, strData, extData, nodeOff)
}
d := &astDecoder{
raw: data,
strTable: strTable,
strData: strData,
extData: extData,
nodeOff: nodeOff,
factory: ast.NewNodeFactory(ast.NodeFactoryHooks{}),
}
d.nodeCount = (len(data) - int(d.nodeOff)) / NodeSize
// Convert entire string data region to a single Go string upfront.
// Substringing a Go string shares the backing array, so subsequent
// getString calls produce substrings with zero allocations.
d.allStringData = string(data[d.strData:])
return d, nil
}
// allocNodeSlice returns a zero-length slice with the given capacity, backed by
// the pre-allocated nodeArena. This avoids a heap allocation per NodeList.
func (d *astDecoder) allocNodeSlice(capacity int) []*ast.Node {
start := len(d.nodeArena)
d.nodeArena = d.nodeArena[:start+capacity]
return d.nodeArena[start : start : start+capacity]
}
// nodeField reads a uint32 field from node i at the given field offset.
func (d *astDecoder) nodeField(i int, field int) uint32 {
return readLE32(d.raw, int(d.nodeOff)+i*NodeSize+field)
}
func (d *astDecoder) getString(idx uint32) string {
offBase := int(d.strTable) + int(idx)*4
start := readLE32(d.raw, offBase)
end := readLE32(d.raw, offBase+4)
return d.allStringData[start:end]
}
// collectChildren returns indices of direct children of node i.
// The returned slice is reused across calls; callers must not retain it.
func (d *astDecoder) collectChildren(i int) []int {
d.childBuf = d.childBuf[:0]
if i+1 >= d.nodeCount {
return d.childBuf
}
firstChild := i + 1
if d.nodeField(firstChild, NodeOffsetParent) != uint32(i) {
return d.childBuf
}
d.childBuf = append(d.childBuf, firstChild)
next := int(d.nodeField(firstChild, NodeOffsetNext))
for next != 0 {
d.childBuf = append(d.childBuf, next)
next = int(d.nodeField(next, NodeOffsetNext))
}
return d.childBuf
}
func (d *astDecoder) decode() (*ast.Node, error) {
if d.nodeCount < 2 {
return nil, errors.New("no nodes to decode")
}
d.nodes = make([]*ast.Node, d.nodeCount)
d.nodeLists = make([]*ast.NodeList, d.nodeCount)
// Pre-allocate arena for NodeList child slices. Each node can appear as a
// child at most once, so nodeCount is an upper bound on total child pointers.
d.nodeArena = make([]*ast.Node, 0, d.nodeCount)
// Process bottom-up so children exist before parents.
for i := d.nodeCount - 1; i >= 1; i-- {
kind := d.nodeField(i, NodeOffsetKind)
pos := d.nodeField(i, NodeOffsetPos)
end := d.nodeField(i, NodeOffsetEnd)
data := d.nodeField(i, NodeOffsetData)
childIndices := d.collectChildren(i)
if kind == SyntaxKindNodeList {
childNodes := d.allocNodeSlice(len(childIndices))
for _, ci := range childIndices {
if d.nodes[ci] != nil {
childNodes = append(childNodes, d.nodes[ci])
}
}
nl := d.factory.NewNodeList(childNodes)
nl.Loc = core.NewTextRange(int(pos), int(end))
d.nodeLists[i] = nl
continue
}
node, err := d.createNode(ast.Kind(kind), data, childIndices)
if err != nil {
return nil, fmt.Errorf("at node %d (kind %v): %w", i, ast.Kind(kind), err)
}
node.Loc = core.NewTextRange(int(pos), int(end))
node.Flags = ast.NodeFlags(d.nodeField(i, NodeOffsetFlags))
d.nodes[i] = node
}
return d.nodes[1], nil
}
// getModifierList creates a *ast.ModifierList from a child index that is a NodeList.
func (d *astDecoder) getModifierList(ci int) *ast.ModifierList {
nl := d.nodeLists[ci]
if nl == nil {
return nil
}
ml := d.factory.NewModifierList(nl.Nodes)
ml.Loc = nl.Loc
return ml
}
// childIterator helps walk through children based on a bitmask.
type childIterator struct {
indices []int
pos int
}
func newChildIter(indices []int) childIterator {
return childIterator{indices: indices}
}
// next returns the index of the next child, advancing the position.
func (it *childIterator) next() int {
if it.pos >= len(it.indices) {
return 0
}
ci := it.indices[it.pos]
it.pos++
return ci
}
// nextIf returns the index of the next child if the corresponding mask bit is set.
func (it *childIterator) nextIf(mask uint8, bit uint8) int {
if mask&(1<<bit) == 0 {
return 0
}
return it.next()
}
func (d *astDecoder) nodeAt(ci int) *ast.Node {
if ci == 0 {
return nil
}
return d.nodes[ci]
}
func (d *astDecoder) nodeListAt(ci int) *ast.NodeList {
if ci == 0 {
return nil
}
return d.nodeLists[ci]
}
func (d *astDecoder) modifierListAt(ci int) *ast.ModifierList {
if ci == 0 {
return nil
}
return d.getModifierList(ci)
}
func (d *astDecoder) createNode(kind ast.Kind, data uint32, childIndices []int) (*ast.Node, error) {
dataType := data & NodeDataTypeMask
commonData := uint8((data >> 24) & 0x3f)
switch dataType {
case NodeDataTypeString:
return d.createStringNode(kind, data, commonData)
case NodeDataTypeExtendedData:
return d.createExtendedNode(kind, data, childIndices, commonData)
default:
return d.createChildrenNode(kind, data, childIndices, commonData)
}
}
func (d *astDecoder) decodeExtendedData_SourceFile(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
textIdx := readLE32(d.raw, extOff)
fileNameIdx := readLE32(d.raw, extOff+4)
pathIdx := readLE32(d.raw, extOff+8)
text := d.getString(textIdx)
fileName := d.getString(fileNameIdx)
path := d.getString(pathIdx)
// Recover parse options from header.
parseOpts := readLE32(d.raw, HeaderOffsetParseOptions)
opts := ast.SourceFileParseOptions{
FileName: fileName,
Path: tspath.Path(path),
ExternalModuleIndicatorOptions: ast.ExternalModuleIndicatorOptions{
JSX: parseOpts&1 != 0,
Force: parseOpts&2 != 0,
},
}
// Collect children: first is statements NodeList, second is EndOfFile.
var stmts *ast.NodeList
var endOfFile *ast.Node
for _, ci := range childIndices {
if d.nodeField(ci, NodeOffsetKind) == SyntaxKindNodeList {
stmts = d.nodeListAt(ci)
} else if d.nodes[ci] != nil && d.nodes[ci].Kind == ast.KindEndOfFile {
endOfFile = d.nodes[ci]
}
}
if endOfFile == nil {
endOfFile = d.factory.NewToken(ast.KindEndOfFile)
}
return d.factory.NewSourceFile(opts, text, stmts, endOfFile), nil
}
func (d *astDecoder) decodeExtendedData_TemplateHead(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
textIdx := readLE32(d.raw, extOff)
rawTextIdx := readLE32(d.raw, extOff+4)
flags := readLE32(d.raw, extOff+8)
return d.factory.NewTemplateHead(d.getString(textIdx), d.getString(rawTextIdx), ast.TokenFlags(flags)), nil
}
func (d *astDecoder) decodeExtendedData_TemplateMiddle(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
textIdx := readLE32(d.raw, extOff)
rawTextIdx := readLE32(d.raw, extOff+4)
flags := readLE32(d.raw, extOff+8)
return d.factory.NewTemplateMiddle(d.getString(textIdx), d.getString(rawTextIdx), ast.TokenFlags(flags)), nil
}
func (d *astDecoder) decodeExtendedData_TemplateTail(data uint32, childIndices []int, commonData uint8) (*ast.Node, error) {
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
textIdx := readLE32(d.raw, extOff)
rawTextIdx := readLE32(d.raw, extOff+4)
flags := readLE32(d.raw, extOff+8)
return d.factory.NewTemplateTail(d.getString(textIdx), d.getString(rawTextIdx), ast.TokenFlags(flags)), nil
}
func (d *astDecoder) singleChild(childIndices []int) *ast.Node {
if len(childIndices) == 0 {
return nil
}
return d.nodes[childIndices[0]]
}
func (d *astDecoder) singleNodeListChild(childIndices []int) *ast.NodeList {
if len(childIndices) == 0 {
return nil
}
return d.nodeLists[childIndices[0]]
}
func readLE32(data []byte, offset int) uint32 {
if offset < 0 || offset+4 > len(data) {
return 0
}
return binary.LittleEndian.Uint32(data[offset : offset+4])
}
// Hand-written commonData decoding functions. Each extracts the original values
// from the 6-bit commonData that were packed by the corresponding
// getNodeCommonData_* function.
func decodeNodeCommonData_SyntheticExpression(_ uint8) (any, bool) {
panic("SyntheticExpression should never be decoded")
}
// Hand-written extended data decoding functions for literal nodes.
func (d *astDecoder) decodeExtendedData_StringLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) {
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
textIdx := readLE32(d.raw, extOff)
flags := readLE32(d.raw, extOff+4)
return d.factory.NewStringLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil
}
func (d *astDecoder) decodeExtendedData_NumericLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) {
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
textIdx := readLE32(d.raw, extOff)
flags := readLE32(d.raw, extOff+4)
return d.factory.NewNumericLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil
}
func (d *astDecoder) decodeExtendedData_BigIntLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) {
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
textIdx := readLE32(d.raw, extOff)
flags := readLE32(d.raw, extOff+4)
return d.factory.NewBigIntLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil
}
func (d *astDecoder) decodeExtendedData_RegularExpressionLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) {
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
textIdx := readLE32(d.raw, extOff)
flags := readLE32(d.raw, extOff+4)
return d.factory.NewRegularExpressionLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil
}
func (d *astDecoder) decodeExtendedData_NoSubstitutionTemplateLiteral(data uint32, _ []int, _ uint8) (*ast.Node, error) {
extOff := int(d.extData) + int(data&NodeDataStringIndexMask)
textIdx := readLE32(d.raw, extOff)
flags := readLE32(d.raw, extOff+4)
return d.factory.NewNoSubstitutionTemplateLiteral(d.getString(textIdx), ast.TokenFlags(flags)), nil
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,450 @@
package encoder_test
import (
"os"
"path/filepath"
"testing"
"github.com/microsoft/typescript-go/internal/api/encoder"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/parser"
"github.com/microsoft/typescript-go/internal/repo"
"gotest.tools/v3/assert"
)
func parseSourceFile(code string) *ast.SourceFile {
return parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/test.ts",
Path: "/test.ts",
}, code, core.ScriptKindTS)
}
func TestDecodeSourceFile_Basic(t *testing.T) {
t.Parallel()
sf := parseSourceFile("let x = 1;")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
assert.Equal(t, decoded.AsNode().Kind, ast.KindSourceFile)
assert.Equal(t, decoded.FileName(), "/test.ts")
assert.Equal(t, decoded.Text(), "let x = 1;")
assert.Assert(t, decoded.Statements != nil)
assert.Assert(t, decoded.EndOfFileToken != nil)
}
func TestDecodeSourceFile_Statements(t *testing.T) {
t.Parallel()
sf := parseSourceFile("let a = 1;\nlet b = 2;\nlet c = 3;")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
assert.Equal(t, len(decoded.Statements.Nodes), 3)
for i, stmt := range decoded.Statements.Nodes {
assert.Equal(t, stmt.Kind, ast.KindVariableStatement, "statement %d", i)
}
}
func TestDecodeSourceFile_VariableDeclaration(t *testing.T) {
t.Parallel()
sf := parseSourceFile("let x = 1;")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
varStmt := decoded.Statements.Nodes[0].AsVariableStatement()
assert.Assert(t, varStmt.DeclarationList != nil)
declList := varStmt.DeclarationList.AsVariableDeclarationList()
assert.Assert(t, declList.Declarations != nil)
assert.Equal(t, len(declList.Declarations.Nodes), 1)
decl := declList.Declarations.Nodes[0].AsVariableDeclaration()
assert.Equal(t, decl.Name().Kind, ast.KindIdentifier)
assert.Equal(t, decl.Name().AsIdentifier().Text, "x")
assert.Assert(t, decl.Initializer != nil)
assert.Equal(t, decl.Initializer.Kind, ast.KindNumericLiteral)
assert.Equal(t, decl.Initializer.AsNumericLiteral().Text, "1")
}
func TestDecodeSourceFile_VariableDeclarationListFlags(t *testing.T) {
t.Parallel()
tests := []struct {
name string
code string
expected ast.NodeFlags
}{
{"const", "const x = 1;", ast.NodeFlagsConst},
{"let", "let x = 1;", ast.NodeFlagsLet},
{"var", "var x = 1;", ast.NodeFlagsNone},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
sf := parseSourceFile(tt.code)
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
declList := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList()
got := declList.Flags & (ast.NodeFlagsLet | ast.NodeFlagsConst)
assert.Equal(t, got, tt.expected, "flags for %q: got %d, want %d", tt.code, got, tt.expected)
})
}
}
func TestDecodeSourceFile_FunctionDeclaration(t *testing.T) {
t.Parallel()
sf := parseSourceFile("function add(a: number, b: number): number { return a + b; }")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
funcDecl := decoded.Statements.Nodes[0].AsFunctionDeclaration()
assert.Assert(t, funcDecl.Name() != nil)
assert.Equal(t, funcDecl.Name().AsIdentifier().Text, "add")
assert.Assert(t, funcDecl.Parameters != nil)
assert.Equal(t, len(funcDecl.Parameters.Nodes), 2)
assert.Assert(t, funcDecl.Type != nil)
assert.Assert(t, funcDecl.Body != nil)
param0 := funcDecl.Parameters.Nodes[0].AsParameterDeclaration()
assert.Equal(t, param0.Name().AsIdentifier().Text, "a")
assert.Assert(t, param0.Type != nil)
}
func TestDecodeSourceFile_ImportDeclaration(t *testing.T) {
t.Parallel()
sf := parseSourceFile(`import { bar } from "bar";`)
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
imp := decoded.Statements.Nodes[0].AsImportDeclaration()
assert.Assert(t, imp.ImportClause != nil)
assert.Assert(t, imp.ModuleSpecifier != nil)
assert.Equal(t, imp.ModuleSpecifier.AsStringLiteral().Text, "bar")
clause := imp.ImportClause.AsImportClause()
assert.Assert(t, clause.NamedBindings != nil)
namedImports := clause.NamedBindings.AsNamedImports()
assert.Assert(t, namedImports.Elements != nil)
assert.Equal(t, len(namedImports.Elements.Nodes), 1)
spec := namedImports.Elements.Nodes[0].AsImportSpecifier()
assert.Equal(t, spec.Name().AsIdentifier().Text, "bar")
}
func TestDecodeSourceFile_IfStatement(t *testing.T) {
t.Parallel()
sf := parseSourceFile("if (true) { } else { }")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
ifStmt := decoded.Statements.Nodes[0].AsIfStatement()
assert.Assert(t, ifStmt.Expression != nil)
assert.Assert(t, ifStmt.ThenStatement != nil)
assert.Assert(t, ifStmt.ElseStatement != nil)
assert.Equal(t, ifStmt.ThenStatement.Kind, ast.KindBlock)
assert.Equal(t, ifStmt.ElseStatement.Kind, ast.KindBlock)
}
func TestDecodeSourceFile_TemplateExpression(t *testing.T) {
t.Parallel()
sf := parseSourceFile("let x = `hello ${name} world`;")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
varDecl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration()
tmplExpr := varDecl.Initializer.AsTemplateExpression()
assert.Assert(t, tmplExpr.Head != nil)
assert.Equal(t, tmplExpr.Head.AsTemplateHead().Text, "hello ")
assert.Assert(t, tmplExpr.TemplateSpans != nil)
assert.Equal(t, len(tmplExpr.TemplateSpans.Nodes), 1)
span := tmplExpr.TemplateSpans.Nodes[0].AsTemplateSpan()
assert.Assert(t, span.Expression != nil)
assert.Equal(t, span.Expression.Kind, ast.KindIdentifier)
assert.Assert(t, span.Literal != nil)
assert.Equal(t, span.Literal.AsTemplateTail().Text, " world")
}
func TestDecodeSourceFile_ExportModifier(t *testing.T) {
t.Parallel()
sf := parseSourceFile("export function foo() {}")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
funcDecl := decoded.Statements.Nodes[0].AsFunctionDeclaration()
assert.Assert(t, funcDecl.Modifiers() != nil)
assert.Equal(t, len(funcDecl.Modifiers().Nodes), 1)
assert.Equal(t, funcDecl.Modifiers().Nodes[0].Kind, ast.KindExportKeyword)
}
func TestDecodeSourceFile_Positions(t *testing.T) {
t.Parallel()
code := "let x = 1;"
sf := parseSourceFile(code)
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
assert.Equal(t, decoded.AsNode().Pos(), 0)
assert.Equal(t, decoded.AsNode().End(), len(code))
}
func TestDecodeSourceFile_ClassDeclaration(t *testing.T) {
t.Parallel()
sf := parseSourceFile("class Foo { bar(): void {} }")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
classDecl := decoded.Statements.Nodes[0].AsClassDeclaration()
assert.Assert(t, classDecl.Name() != nil)
assert.Equal(t, classDecl.Name().AsIdentifier().Text, "Foo")
assert.Assert(t, classDecl.Members != nil)
assert.Equal(t, len(classDecl.Members.Nodes), 1)
assert.Equal(t, classDecl.Members.Nodes[0].Kind, ast.KindMethodDeclaration)
}
func TestDecodeNodes_SubtreeRoundTrip(t *testing.T) {
t.Parallel()
sf := parseSourceFile("function greet(name: string) { return `Hello, ${name}!`; }")
var funcNode *ast.Node
visitor := &ast.NodeVisitor{}
visitor.Visit = func(node *ast.Node) *ast.Node {
if node.Kind == ast.KindFunctionDeclaration && funcNode == nil {
funcNode = node
}
return node
}
visitor.VisitEachChild(sf.AsNode())
assert.Assert(t, funcNode != nil)
buf, _, err := encoder.EncodeNode(funcNode, sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeNodes(buf)
assert.NilError(t, err)
assert.Equal(t, decoded.Kind, ast.KindFunctionDeclaration)
funcDecl := decoded.AsFunctionDeclaration()
assert.Assert(t, funcDecl.Name() != nil)
assert.Equal(t, funcDecl.Name().AsIdentifier().Text, "greet")
assert.Assert(t, funcDecl.Parameters != nil)
assert.Equal(t, len(funcDecl.Parameters.Nodes), 1)
assert.Assert(t, funcDecl.Body != nil)
}
func TestDecodeSourceFile_BinaryExpression(t *testing.T) {
t.Parallel()
sf := parseSourceFile("let x = 1 + 2;")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration()
binExpr := decl.Initializer.AsBinaryExpression()
assert.Assert(t, binExpr.Left != nil)
assert.Assert(t, binExpr.Right != nil)
assert.Assert(t, binExpr.OperatorToken != nil)
assert.Equal(t, binExpr.Left.Kind, ast.KindNumericLiteral)
assert.Equal(t, binExpr.Right.Kind, ast.KindNumericLiteral)
}
func TestDecodeSourceFile_KeywordExpressions(t *testing.T) {
t.Parallel()
// "this" must decode as KeywordExpression, not Token, or the printer panics
sf := parseSourceFile("const x = this;")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
// Navigate: const x = this -> VariableStatement -> declaration -> initializer
decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration()
thisExpr := decl.Initializer
assert.Equal(t, thisExpr.Kind, ast.KindThisKeyword)
// This would panic if decoded as Token instead of KeywordExpression
assert.Assert(t, thisExpr.AsKeywordExpression() != nil)
}
func TestDecodeSourceFile_EmptyModuleBlock(t *testing.T) {
t.Parallel()
sf := parseSourceFile("namespace N { }")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
// Navigate: namespace N { } -> ModuleDeclaration -> ModuleBlock
mod := decoded.Statements.Nodes[0].AsModuleDeclaration()
assert.Assert(t, mod.Body != nil)
block := mod.Body.AsModuleBlock()
// Statements must be non-nil even when empty, otherwise the printer panics
assert.Assert(t, block.Statements != nil)
assert.Equal(t, len(block.Statements.Nodes), 0)
}
func TestDecodeSourceFile_EmptyBlockAndParams(t *testing.T) {
t.Parallel()
// Empty blocks and parameter lists must decode with non-nil NodeLists (not nil),
// matching parser behavior. Previously the decoder left them nil, crashing the printer.
sf := parseSourceFile("function foo() {}")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
funcDecl := decoded.Statements.Nodes[0].AsFunctionDeclaration()
assert.Assert(t, funcDecl.Parameters != nil, "FunctionDeclaration.Parameters must be non-nil for foo()")
assert.Equal(t, len(funcDecl.Parameters.Nodes), 0)
assert.Assert(t, funcDecl.Body != nil)
block := funcDecl.Body.AsBlock()
assert.Assert(t, block.Statements != nil, "Block.Statements must be non-nil for empty blocks")
assert.Equal(t, len(block.Statements.Nodes), 0)
}
func TestDecodeSourceFile_ArrowFunctionEmptyParams(t *testing.T) {
t.Parallel()
// `() => {}` must decode with non-nil Parameters (empty NodeList),
// matching parser behavior. Previously the decoder left it nil, crashing the printer.
sf := parseSourceFile("const f = () => {};")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration()
arrow := decl.Initializer.AsArrowFunction()
assert.Assert(t, arrow.Parameters != nil, "ArrowFunction.Parameters must be non-nil for () => {}")
assert.Equal(t, len(arrow.Parameters.Nodes), 0)
assert.Assert(t, arrow.Body != nil)
block := arrow.Body.AsBlock()
assert.Assert(t, block.Statements != nil, "Block.Statements must be non-nil for empty body")
assert.Equal(t, len(block.Statements.Nodes), 0)
}
func TestDecodeSourceFile_FunctionExpressionEmptyParams(t *testing.T) {
t.Parallel()
// `function() {}` must decode with non-nil Parameters (empty NodeList).
sf := parseSourceFile("const f = function() {};")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
decl := decoded.Statements.Nodes[0].AsVariableStatement().DeclarationList.AsVariableDeclarationList().Declarations.Nodes[0].AsVariableDeclaration()
funcExpr := decl.Initializer.AsFunctionExpression()
assert.Assert(t, funcExpr.Parameters != nil, "FunctionExpression.Parameters must be non-nil for function() {}")
assert.Equal(t, len(funcExpr.Parameters.Nodes), 0)
}
func TestDecodeSourceFile_PostfixUnaryOperator(t *testing.T) {
t.Parallel()
sf := parseSourceFile("let i = 0; i++;")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
exprStmt := decoded.Statements.Nodes[1].AsExpressionStatement()
postfix := exprStmt.Expression.AsPostfixUnaryExpression()
assert.Equal(t, postfix.Operator, ast.KindPlusPlusToken)
assert.Equal(t, postfix.Operand.Kind, ast.KindIdentifier)
}
func TestDecodeSourceFile_PrefixUnaryOperator(t *testing.T) {
t.Parallel()
sf := parseSourceFile("let x = true; !x;")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
exprStmt := decoded.Statements.Nodes[1].AsExpressionStatement()
prefix := exprStmt.Expression.AsPrefixUnaryExpression()
assert.Equal(t, prefix.Operator, ast.KindExclamationToken)
assert.Equal(t, prefix.Operand.Kind, ast.KindIdentifier)
}
func TestDecodeSourceFile_PostfixDecrement(t *testing.T) {
t.Parallel()
sf := parseSourceFile("let n = 5; n--;")
buf, _, err := encoder.EncodeSourceFile(sf)
assert.NilError(t, err)
decoded, err := encoder.DecodeSourceFile(buf)
assert.NilError(t, err)
exprStmt := decoded.Statements.Nodes[1].AsExpressionStatement()
postfix := exprStmt.Expression.AsPostfixUnaryExpression()
assert.Equal(t, postfix.Operator, ast.KindMinusMinusToken)
}
func BenchmarkDecodeSourceFile(b *testing.B) {
repo.SkipIfNoTypeScriptSubmodule(b)
filePath := filepath.Join(repo.TypeScriptSubmodulePath(), "src/compiler/checker.ts")
fileContent, err := os.ReadFile(filePath)
assert.NilError(b, err)
code := string(fileContent)
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/checker.ts",
Path: "/checker.ts",
}, code, core.ScriptKindTS)
buf, _, err := encoder.EncodeSourceFile(sourceFile)
assert.NilError(b, err)
b.Run("parse", func(b *testing.B) {
for b.Loop() {
parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/checker.ts",
Path: "/checker.ts",
}, code, core.ScriptKindTS)
}
})
b.Run("decode", func(b *testing.B) {
for b.Loop() {
_, decodeErr := encoder.DecodeSourceFile(buf)
assert.NilError(b, decodeErr)
}
})
}

View File

@@ -0,0 +1,844 @@
package encoder
import (
"cmp"
"encoding/binary"
"fmt"
"slices"
"sync"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/zeebo/xxh3"
)
func init() {
if ast.KindLastUnaryOperator > 0x3f {
panic(fmt.Sprintf("KindLastUnaryOperator (%d) exceeds the 6-bit commonData capacity (max 63)", ast.KindLastUnaryOperator))
}
}
const (
NodeOffsetKind = iota * 4
NodeOffsetPos
NodeOffsetEnd
NodeOffsetNext
NodeOffsetParent
NodeOffsetData
NodeOffsetFlags
// NodeSize is the number of bytes that represents a single node in the encoded format.
NodeSize
)
const (
NodeDataTypeChildren uint32 = iota << 30
NodeDataTypeString
NodeDataTypeExtendedData
)
const (
NodeDataTypeMask uint32 = 0xc0_00_00_00
NodeDataChildMask uint32 = 0x00_00_00_ff
NodeDataStringIndexMask uint32 = 0x00_ff_ff_ff
)
const (
SyntaxKindNodeList uint32 = 1<<32 - 1
)
const (
HeaderOffsetMetadata = iota * 4
HeaderOffsetHashLo0
HeaderOffsetHashLo1
HeaderOffsetHashHi0
HeaderOffsetHashHi1
HeaderOffsetParseOptions
HeaderOffsetStringOffsets
HeaderOffsetStringData
HeaderOffsetExtendedData
HeaderOffsetStructuredData
HeaderOffsetNodes
HeaderSize
)
const (
ProtocolVersion uint8 = 5
)
// Source File Binary Format
// =========================
//
// The following defines a protocol for serializing TypeScript SourceFile objects to a compact binary format. All integer
// values are little-endian.
//
// Overview
// --------
//
// The format comprises seven sections:
//
// | Section | Length | Description |
// | ------------------ | ------------------ | ----------------------------------------------------------------------------------------------- |
// | Header | 44 bytes | Contains the content hash, parse options, flags, and byte offsets to the start of each section. |
// | String offsets | 8 bytes per string | Pairs of starting byte offsets and ending byte offsets into the **string data** section. |
// | String data | variable | UTF-8 encoded string data. |
// | Extended node data | variable | Extra data for some kinds of nodes. |
// | Structured data | variable | Msgpack-encoded metadata blobs (e.g. file references). |
// | Nodes | 28 bytes per node | Defines the AST structure of the file, with references to strings and extended data. |
//
// Header (44 bytes)
// -----------------
//
// The header contains the following fields:
//
// | Byte offset | Type | Field |
// | ----------- | --------- | ------------------------------------------------- |
// | 0 | uint8 | Protocol version |
// | 1-3 | | Reserved |
// | 4-19 | uint128 | Source file content hash (xxh3, LE) |
// | 20-23 | uint32 | Parse options (bitmask; bit 0: JSX, bit 1: Force) |
// | 24-27 | uint32 | Byte offset to string offsets section |
// | 28-31 | uint32 | Byte offset to string data section |
// | 32-35 | uint32 | Byte offset to extended node data section |
// | 36-39 | uint32 | Byte offset to structured data section |
// | 40-43 | uint32 | Byte offset to nodes section |
//
// String offsets (8 bytes per string)
// -----------------------------------
//
// Each string offset entry consists of two 4-byte unsigned integers, representing the start and end byte offsets into the
// **string data** section.
//
// String data (variable)
// ----------------------
//
// The string data section contains UTF-8 encoded string data, with WTF-8 used for JS strings containing lone UTF-16
// surrogates. In typical cases, the entirety of the string data is the source file text, and individual nodes with
// string properties reference their positional slice of the file text. In cases where a node's string property is not
// equal to the slice of file text at its position, the unique string is appended to the string data section after the
// file text.
//
// Extended node data (variable)
// -----------------------------
//
// The extended node data section contains additional data for specific node types. The length and meaning of each entry
// is defined by the node type.
//
// Currently, the only node types that use this section are `TemplateHead`, `TemplateMiddle`, `TemplateTail`, and
// `SourceFile`. The extended data format for the first three is:
//
// | Byte offset | Type | Field |
// | ----------- | ------ | ------------------------------------------------ |
// | 0-4 | uint32 | Index of `text` in the string offsets section |
// | 4-8 | uint32 | Index of `rawText` in the string offsets section |
// | 8-12 | uint32 | Value of `templateFlags` |
//
// and for `SourceFile` is:
//
// | Byte offset | Type | Field |
// | ----------- | ------ | -------------------------------------------------------------- |
// | 0-4 | uint32 | Index of `text` in the string offsets section |
// | 4-8 | uint32 | Index of `fileName` in the string offsets section |
// | 8-12 | uint32 | Index of `path` in the string offsets section |
// | 12-16 | uint32 | Value of `languageVariant` |
// | 16-20 | uint32 | Value of `scriptKind` |
// | 20-24 | uint32 | Byte offset of `referencedFiles` in structured data section |
// | 24-28 | uint32 | Byte offset of `typeReferenceDirectives` in structured data |
// | 28-32 | uint32 | Byte offset of `libReferenceDirectives` in structured data |
// | 32-36 | uint32 | Byte offset of `imports` node index array in structured data |
// | 36-40 | uint32 | Byte offset of `moduleAugmentations` node index array |
// | 40-44 | uint32 | Byte offset of `ambientModuleNames` string array |
// | 44-48 | uint32 | Node index of `externalModuleIndicator` (0 = nil) |
//
// Structured data (variable)
// --------------------------
//
// The structured data section contains msgpack-encoded metadata blobs. Each blob is a self-contained
// msgpack value. File reference arrays use the following tuple format:
//
// [pos: uint, end: uint, fileName: string, resolutionMode: uint, preserve: bool]
//
// Node index arrays (imports, moduleAugmentations) are msgpack arrays of uint values, where each
// value is a node index into the nodes section. String arrays (ambientModuleNames) are msgpack
// arrays of string values.
//
// An offset of 0xFFFFFFFF indicates no data (empty array).
//
// Nodes (28 bytes per node)
// -------------------------
//
// The nodes section contains the AST structure of the file. Nodes are represented in a flat array in source order,
// heavily inspired by https://marvinh.dev/blog/speeding-up-javascript-ecosystem-part-11/. Each node has the following
// structure:
//
// | Byte offset | Type | Field |
// | ----------- | ------ | -------------------------- |
// | 0-4 | uint32 | Kind |
// | 4-8 | uint32 | Pos |
// | 8-12 | uint32 | End |
// | 12-16 | uint32 | Node index of next sibling |
// | 16-20 | uint32 | Node index of parent |
// | 20-24 | | Node data |
// | 24-28 | uint32 | Node flags |
//
// The first 28 bytes of the nodes section are zeros representing a nil node, such that nodes without a parent or next
// sibling can unambiuously use `0` for those indices.
//
// NodeLists are represented as normal nodes with the special `kind` value `0xff_ff_ff_ff`. They are considered the parent
// of their contents in the encoded format. A client reconstructing an AST similar to TypeScript's internal representation
// should instead set the `parent` pointers of a NodeList's children to the NodeList's parent. A NodeList's `data` field
// is the uint32 length of the list, and does not use one of the data types described below.
//
// For node types other than NodeList, the node data field encodes one of the following, determined by the first 2 bits of
// the field:
//
// | Value | Data type | Description |
// | ----- | --------- | ------------------------------------------------------------------------------------ |
// | 0b00 | Children | Disambiguates which named properties of the node its children should be assigned to. |
// | 0b01 | String | The index of the node's string property in the **string offsets** section. |
// | 0b10 | Extended | The byte offset of the node's extended data into the **extended node data** section. |
// | 0b11 | Reserved | Reserved for future use. |
//
// In all node data types, the remaining 6 bits of the first byte are used to encode small values specific to the node
// type. For most node types, these are individual boolean flags. For unary expressions, all 6 bits encode the operator's
// SyntaxKind value (e.g., PlusPlusToken=45, TildeToken=54), which fits because KindLastUnaryOperator (54) <= 0x3f (63).
//
// | Node type | Bits 0-5 | Notes |
// | ---------------------------- | ------------------------------------- | ------------------------------ |
// | `ImportSpecifier` | Bit 0: `isTypeOnly` | |
// | `ImportClause` | Bit 0: `isTypeOnly`, Bit 1: `isDefer` | |
// | `ExportSpecifier` | Bit 0: `isTypeOnly` | |
// | `ImportEqualsDeclaration` | Bit 0: `isTypeOnly` | |
// | `ExportDeclaration` | Bit 0: `isTypeOnly` | |
// | `ImportTypeNode` | Bit 0: `isTypeOf` | |
// | `ExportAssignment` | Bit 0: `isExportEquals` | |
// | `Block` | Bit 0: `multiline` | |
// | `ArrayLiteralExpression` | Bit 0: `multiline` | |
// | `ObjectLiteralExpression` | Bit 0: `multiline` | |
// | `JsxText` | Bit 0: `containsOnlyTriviaWhiteSpaces`| |
// | `JSDocTypeLiteral` | Bit 0: `isArrayType` | |
// | `JsDocPropertyTag` | Bit 0: `isBracketed`, Bit 1: `isNameFirst` | |
// | `JsDocParameterTag` | Bit 0: `isBracketed`, Bit 1: `isNameFirst` | |
// | `VariableDeclarationList` | Bit 0: is `let`, Bit 1: is `const` | |
// | `ImportAttributes` | Bit 0: `multiline`, Bit 1: is `assert`| |
// | `PrefixUnaryExpression` | Bits 0-5: operator SyntaxKind | e.g., `!`, `~`, `++`, `--` |
// | `PostfixUnaryExpression` | Bits 0-5: operator SyntaxKind | e.g., `++`, `--` |
//
// The remaining 3 bytes of the node data field vary by data type:
//
// ### Children (0b00)
//
// If a node has fewer children than its type allows, additional data is needed to determine which properties the children
// correspond to. The last byte of the 4-byte data field is a bitmask representing the child properties of the node type,
// in visitor order, where `1` indicates that the child at that property is present and `0` indicates that the property is
// nil. For example, a `MethodDeclaration` has the following child properties:
//
// | Property name | Bit position |
// | -------------- | ------------ |
// | modifiers | 0 |
// | asteriskToken | 1 |
// | name | 2 |
// | postfixToken | 3 |
// | typeParameters | 4 |
// | parameters | 5 |
// | returnType | 6 |
// | body | 7 |
//
// A bitmask with value `0b01100101` would indicate that the next four direct descendants (i.e., node records that have a
// `parent` set to the node index of the `MethodDeclaration`) of the node are its `modifiers`, `name`, `parameters`, and
// `body` properties, in that order. The remaining properties are nil. (To reconstruct the node with named properties, the
// client must consult a static table of each node type's child property names.)
//
// The bitmask may be zero for node types that can only have a single child, since no disambiguation is needed.
// Additionally, the children data type may be used for nodes that can never have children, but do not require other
// data types.
//
// ### String (0b01)
//
// The string data type is used for nodes with a single string property. (Currently, the name of that property is always
// `text`.) The last three bytes of the 4-byte data field form a single 24-bit unsigned integer (i.e.,
// `uint32(0x00_ff_ff_ff & node.data)`) _N_ that is an index into the **string offsets** section. The *N*th 32-bit
// unsigned integer in the **string offsets** section is the byte offset of the start of the string in the **string data**
// section, and the *N+1*th 32-bit unsigned integer is the byte offset of the end of the string in the
// **string data** section.
//
// ### Extended (0b10)
//
// The extended data type is used for nodes with properties that don't fit into either the children or string data types.
// The last three bytes of the 4-byte data field form a single 24-bit unsigned integer (i.e.,
// `uint32(0x00_ff_ff_ff & node.data)`) _N_ that is a byte offset into the **extended node data** section. The length and
// meaning of the data at that offset is defined by the node type. See the **Extended node data** section for details on
// the format of the extended data for specific node types.
//
// Encoding Arbitrary Nodes
// ------------------------
//
// The same binary format can be used to encode an arbitrary subtree of a SourceFile, not just a whole SourceFile. When
// encoding a non-SourceFile node, the format is identical with the following differences:
//
// - The content hash fields in the header (bytes 4-19) are zero.
// - The parse options field in the header (bytes 20-23) is zero.
// - The root node in the nodes section uses its actual node kind and data encoding (via getNodeData) rather than the
// SourceFile-specific extended data format.
//
// The string data section contains only the strings referenced by nodes in the subtree, rather than the full source
// file text. The EncodeNode function provides this entrypoint.
// SourceFileHash returns the 128-bit content hash for a source file as a hex string.
func SourceFileHash(sourceFile *ast.SourceFile) string {
h := sourceFile.Hash
return fmt.Sprintf("%016x%016x", h.Hi, h.Lo)
}
// encodeParseOptions encodes the per-file ExternalModuleIndicatorOptions as a uint32 bitmask.
func encodeParseOptions(opts ast.ExternalModuleIndicatorOptions) uint32 {
var bits uint32
if opts.JSX {
bits |= 1
}
if opts.Force {
bits |= 2
}
return bits
}
// NodeIndexTable maps between AST nodes and their encoder indices for O(1) node handle resolution.
type NodeIndexTable struct {
Nodes []*ast.Node // index → node (for resolution)
sortedOnce sync.Once
sortedIdx []uint32 // indices into Nodes, sorted by node ID; built lazily
}
var nodeIndexTableKey = ast.NewSourceFileDataKey[*NodeIndexTable]()
// GetIndex returns the encoder index for the given node.
// On the first call the sortedIdx array is built (O(n log n) sort on a flat []uint32),
// then subsequent calls use binary search (O(log n)). This turns out to be much faster than
// building a map[*ast.Node]uint32 and not significantly slower for lookups.
func (t *NodeIndexTable) GetIndex(node *ast.Node) uint32 {
t.sortedOnce.Do(func() {
idx := make([]uint32, 0, len(t.Nodes))
for i, n := range t.Nodes {
if n != nil {
idx = append(idx, uint32(i))
}
}
nodes := t.Nodes
slices.SortFunc(idx, func(a, b uint32) int {
return cmp.Compare(ast.GetNodeId(nodes[a]), ast.GetNodeId(nodes[b]))
})
t.sortedIdx = idx
})
target := ast.GetNodeId(node)
i, found := core.BinarySearchUniqueFunc(t.sortedIdx, func(_ int, el uint32) int {
return cmp.Compare(ast.GetNodeId(t.Nodes[el]), target)
})
if found {
return t.sortedIdx[i]
}
return 0
}
// BuildNodeIndexTable walks the AST in the same order as encodeTree and builds
// a NodeIndexTable without performing the full binary encoding. This is used to
// eagerly create index tables for files that need node handles before getSourceFile
// is called. The indices produced are guaranteed to match those from EncodeSourceFile.
func BuildNodeIndexTable(sourceFile *ast.SourceFile) *NodeIndexTable {
var nodeCount uint32
nodeTable := make([]*ast.Node, 1, sourceFile.NodeCount+1) // index 0 = nil sentinel
visitor := &ast.NodeVisitor{
Hooks: ast.NodeVisitorHooks{
VisitNodes: func(nodeList *ast.NodeList, visitor *ast.NodeVisitor) *ast.NodeList {
if nodeList == nil {
return nodeList
}
nodeCount++
nodeTable = append(nodeTable, nil) // NodeLists are not *ast.Node
visitor.VisitSlice(nodeList.Nodes)
return nodeList
},
VisitModifiers: func(modifiers *ast.ModifierList, visitor *ast.NodeVisitor) *ast.ModifierList {
if modifiers != nil && len(modifiers.Nodes) > 0 {
visitor.Hooks.VisitNodes(&modifiers.NodeList, visitor)
}
return modifiers
},
},
}
visitor.Visit = func(node *ast.Node) *ast.Node {
nodeCount++
nodeTable = append(nodeTable, node)
visitor.VisitEachChild(node)
for _, jsdoc := range node.JSDoc(sourceFile) {
visitor.Visit(jsdoc)
}
return node
}
rootNode := sourceFile.AsNode()
// Index 1 = root node (matches encodeTree)
nodeCount++
nodeTable = append(nodeTable, rootNode)
visitor.VisitEachChild(rootNode)
for _, jsdoc := range rootNode.JSDoc(sourceFile) {
visitor.Visit(jsdoc)
}
return &NodeIndexTable{Nodes: nodeTable}
}
func GetNodeIndexTable(sourceFile *ast.SourceFile) *NodeIndexTable {
return ast.GetOrComputeSourceFileData(sourceFile, nodeIndexTableKey, BuildNodeIndexTable)
}
// EncodeSourceFile encodes an entire source file AST into the binary format.
// Returns the encoded bytes and a NodeIndexTable mapping encoder indices to AST nodes.
func EncodeSourceFile(sourceFile *ast.SourceFile) ([]byte, *NodeIndexTable, error) {
data, nodeTable, err := encodeTree(sourceFile.AsNode(), sourceFile)
if err != nil {
return nil, nil, err
}
nodeTable = ast.GetOrComputeSourceFileData(sourceFile, nodeIndexTableKey, func(*ast.SourceFile) *NodeIndexTable {
return nodeTable
})
return data, nodeTable, nil
}
// EncodeNode encodes an arbitrary AST node and its descendants into the binary format.
// The sourceFile is needed to provide the source text for efficient string encoding.
// When encoding a non-SourceFile node, the header hash and parse options fields will be zero.
// Returns the encoded bytes and a NodeIndexTable mapping encoder indices to AST nodes.
func EncodeNode(node *ast.Node, sourceFile *ast.SourceFile) ([]byte, *NodeIndexTable, error) {
return encodeTree(node, sourceFile)
}
func encodeTree(rootNode *ast.Node, sourceFile *ast.SourceFile) ([]byte, *NodeIndexTable, error) {
var parentIndex, nodeCount, prevIndex uint32
var extendedData []byte
var structuredData []byte
var strs *stringTable
var positionMap *ast.PositionMap
if rootNode.Kind == ast.KindSourceFile {
strs = newStringTable(sourceFile.Text(), sourceFile.TextCount)
positionMap = sourceFile.GetPositionMap()
} else {
strs = newStringTable("", 0)
if sourceFile != nil {
positionMap = sourceFile.GetPositionMap()
}
}
if positionMap == nil {
positionMap = ast.ComputePositionMap("")
}
utf16 := func(pos int) uint32 {
return uint32(positionMap.UTF8ToUTF16(pos))
}
var initialNodeCount int
if sourceFile != nil {
initialNodeCount = sourceFile.NodeCount
}
nodes := make([]byte, 0, (initialNodeCount+1)*NodeSize)
// Build node index table for O(1) handle resolution.
// Index 0 is a nil sentinel; real nodes start at index 1.
nodeTable := make([]*ast.Node, 1, initialNodeCount+1) // index 0 = nil sentinel
// Build a small map of nodes we need to track indices for (imports + moduleAugmentations).
// Values start at 0 and are filled in during the walk.
var nodeIndexMap map[*ast.Node]uint32
var sfExtendedDataOffset int // byte offset in extendedData where SourceFile fields start
if rootNode.Kind == ast.KindSourceFile {
sf := rootNode.AsSourceFile()
total := len(sf.Imports()) + len(sf.ModuleAugmentations)
if sf.ExternalModuleIndicator != nil && sf.ExternalModuleIndicator != rootNode {
total++
}
if total > 0 {
nodeIndexMap = make(map[*ast.Node]uint32, total)
for _, imp := range sf.Imports() {
nodeIndexMap[imp.AsNode()] = 0
}
for _, aug := range sf.ModuleAugmentations {
nodeIndexMap[aug.AsNode()] = 0
}
if sf.ExternalModuleIndicator != nil && sf.ExternalModuleIndicator != rootNode {
nodeIndexMap[sf.ExternalModuleIndicator] = 0
}
}
}
visitor := &ast.NodeVisitor{
Hooks: ast.NodeVisitorHooks{
VisitNodes: func(nodeList *ast.NodeList, visitor *ast.NodeVisitor) *ast.NodeList {
if nodeList == nil {
return nodeList
}
nodeCount++
nodeTable = append(nodeTable, nil) // NodeLists are not *ast.Node
if prevIndex != 0 {
// this is the next sibling of `prevNode`
b0, b1, b2, b3 := uint8(nodeCount), uint8(nodeCount>>8), uint8(nodeCount>>16), uint8(nodeCount>>24)
nodes[prevIndex*NodeSize+NodeOffsetNext+0] = b0
nodes[prevIndex*NodeSize+NodeOffsetNext+1] = b1
nodes[prevIndex*NodeSize+NodeOffsetNext+2] = b2
nodes[prevIndex*NodeSize+NodeOffsetNext+3] = b3
}
nodes = appendUint32s(nodes, SyntaxKindNodeList, utf16(nodeList.Pos()), utf16(nodeList.End()), 0, parentIndex, uint32(len(nodeList.Nodes)), 0)
saveParentIndex := parentIndex
currentIndex := nodeCount
prevIndex = 0
parentIndex = currentIndex
visitor.VisitSlice(nodeList.Nodes)
prevIndex = currentIndex
parentIndex = saveParentIndex
return nodeList
},
VisitModifiers: func(modifiers *ast.ModifierList, visitor *ast.NodeVisitor) *ast.ModifierList {
if modifiers != nil && len(modifiers.Nodes) > 0 {
visitor.Hooks.VisitNodes(&modifiers.NodeList, visitor)
}
return modifiers
},
},
}
visitor.Visit = func(node *ast.Node) *ast.Node {
nodeCount++
nodeTable = append(nodeTable, node)
if prevIndex != 0 {
// this is the next sibling of `prevNode`
b0, b1, b2, b3 := uint8(nodeCount), uint8(nodeCount>>8), uint8(nodeCount>>16), uint8(nodeCount>>24)
nodes[prevIndex*NodeSize+NodeOffsetNext+0] = b0
nodes[prevIndex*NodeSize+NodeOffsetNext+1] = b1
nodes[prevIndex*NodeSize+NodeOffsetNext+2] = b2
nodes[prevIndex*NodeSize+NodeOffsetNext+3] = b3
}
nodes = appendUint32s(nodes, uint32(node.Kind), utf16(node.Pos()), utf16(node.End()), 0, parentIndex, getNodeData(node, strs, positionMap, &extendedData, &structuredData), uint32(node.Flags))
if nodeIndexMap != nil {
if _, ok := nodeIndexMap[node]; ok {
nodeIndexMap[node] = nodeCount
}
}
saveParentIndex := parentIndex
currentIndex := nodeCount
prevIndex = 0
parentIndex = currentIndex
visitor.VisitEachChild(node)
if sourceFile != nil {
for _, jsdoc := range node.JSDoc(sourceFile) {
visitor.Visit(jsdoc)
}
}
prevIndex = currentIndex
parentIndex = saveParentIndex
return node
}
nodes = appendUint32s(nodes, 0, 0, 0, 0, 0, 0, 0)
nodeCount++
parentIndex++
nodeTable = append(nodeTable, rootNode) // index 1 = root node
sfExtendedDataOffset = len(extendedData)
nodes = appendUint32s(nodes, uint32(rootNode.Kind), utf16(rootNode.Pos()), utf16(rootNode.End()), 0, 0, getNodeData(rootNode, strs, positionMap, &extendedData, &structuredData), uint32(rootNode.Flags))
visitor.VisitEachChild(rootNode)
if sourceFile != nil {
for _, jsdoc := range rootNode.JSDoc(sourceFile) {
visitor.Visit(jsdoc)
}
}
var hash xxh3.Uint128
var parseOpts uint32
if rootNode.Kind == ast.KindSourceFile {
hash = sourceFile.Hash
parseOpts = encodeParseOptions(sourceFile.ParseOptions().ExternalModuleIndicatorOptions)
// Encode imports, moduleAugmentations, and ambientModuleNames into structured data,
// and patch the placeholder offsets in the SourceFile extended data.
sf := rootNode.AsSourceFile()
importsOffset := encodeNodeIndexArray(sf.Imports(), nodeIndexMap, &structuredData)
moduleAugmentationsOffset := encodeModuleAugmentations(sf.ModuleAugmentations, nodeIndexMap, &structuredData)
ambientModuleNamesOffset := encodeStringArray(sf.AmbientModuleNames, &structuredData)
// Patch the 3 placeholder uint32s at sfExtendedDataOffset + 32, 36, 40
binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+32:], importsOffset)
binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+36:], moduleAugmentationsOffset)
binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+40:], ambientModuleNamesOffset)
// Patch externalModuleIndicator node index at offset 44
var externalModuleIndicatorIndex uint32
if sf.ExternalModuleIndicator != nil {
if sf.ExternalModuleIndicator == rootNode {
externalModuleIndicatorIndex = 1 // root node index
} else {
externalModuleIndicatorIndex = nodeIndexMap[sf.ExternalModuleIndicator]
}
}
binary.LittleEndian.PutUint32(extendedData[sfExtendedDataOffset+44:], externalModuleIndicatorIndex)
}
metadata := uint32(ProtocolVersion) << 24
offsetStringTableOffsets := HeaderSize
offsetStringTableData := HeaderSize + len(strs.offsets)*4
offsetExtendedData := offsetStringTableData + strs.stringLength()
offsetStructuredData := offsetExtendedData + len(extendedData)
offsetNodes := offsetStructuredData + len(structuredData)
header := []uint32{
metadata,
uint32(hash.Lo), uint32(hash.Lo >> 32),
uint32(hash.Hi), uint32(hash.Hi >> 32),
parseOpts,
uint32(offsetStringTableOffsets),
uint32(offsetStringTableData),
uint32(offsetExtendedData),
uint32(offsetStructuredData),
uint32(offsetNodes),
}
var headerBytes, strsBytes []byte
headerBytes = appendUint32s(nil, header...)
strsBytes = strs.encode()
return slices.Concat(
headerBytes,
strsBytes,
extendedData,
structuredData,
nodes,
), &NodeIndexTable{Nodes: nodeTable}, nil
}
func appendUint32s(buf []byte, values ...uint32) []byte {
for _, value := range values {
buf = binary.LittleEndian.AppendUint32(buf, value)
}
return buf
}
func getNodeData(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) uint32 {
t := getNodeDataType(node)
switch t {
case NodeDataTypeChildren:
return t | getNodeCommonData(node) | uint32(getChildrenPropertyMask(node))
case NodeDataTypeString:
return t | getNodeCommonData(node) | recordNodeStrings(node, strs)
case NodeDataTypeExtendedData:
return t | getNodeCommonData(node) | recordExtendedData(node, strs, positionMap, extendedData, structuredData)
default:
panic("unreachable")
}
}
const noStructuredData = 0xFFFFFFFF
func recordExtendedData_SourceFile(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) {
sf := node.AsSourceFile()
textIndex := strs.add(sf.Text(), sf.Kind, sf.Pos(), sf.End())
fileNameIndex := strs.add(sf.FileName(), 0, 0, 0)
pathIndex := strs.add(string(sf.Path()), 0, 0, 0)
referencedFilesOffset := encodeFileReferences(sf.ReferencedFiles, positionMap, structuredData)
typeRefDirectivesOffset := encodeFileReferences(sf.TypeReferenceDirectives, positionMap, structuredData)
libRefDirectivesOffset := encodeFileReferences(sf.LibReferenceDirectives, positionMap, structuredData)
// imports, moduleAugmentations, ambientModuleNames offsets are placeholders;
// they will be patched after the tree walk when node indices are known.
*extendedData = appendUint32s(*extendedData, textIndex, fileNameIndex, pathIndex, uint32(sf.LanguageVariant), uint32(sf.ScriptKind), referencedFilesOffset, typeRefDirectivesOffset, libRefDirectivesOffset, noStructuredData, noStructuredData, noStructuredData, 0)
}
func recordExtendedData_TemplateHead(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) {
n := node.AsTemplateHead()
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
rawTextIndex := strs.add(n.RawText, node.Kind, node.Pos(), node.End())
*extendedData = appendUint32s(*extendedData, textIndex, rawTextIndex, uint32(n.TemplateFlags))
}
func recordExtendedData_TemplateMiddle(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) {
n := node.AsTemplateMiddle()
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
rawTextIndex := strs.add(n.RawText, node.Kind, node.Pos(), node.End())
*extendedData = appendUint32s(*extendedData, textIndex, rawTextIndex, uint32(n.TemplateFlags))
}
func recordExtendedData_TemplateTail(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) {
n := node.AsTemplateTail()
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
rawTextIndex := strs.add(n.RawText, node.Kind, node.Pos(), node.End())
*extendedData = appendUint32s(*extendedData, textIndex, rawTextIndex, uint32(n.TemplateFlags))
}
func boolToByte(b bool) byte {
if b {
return 1
}
return 0
}
// hasModifiers returns true if the modifier list is non-nil and has at least one modifier.
func hasModifiers(modifiers *ast.ModifierList) bool {
return modifiers != nil && len(modifiers.Nodes) > 0
}
// encodeFileReferences encodes a slice of FileReferences as a msgpack array of tuples
// into the structured data buffer. Returns the byte offset into the buffer, or
// noStructuredData (0xFFFFFFFF) if the slice is empty.
func encodeFileReferences(refs []*ast.FileReference, positionMap *ast.PositionMap, buf *[]byte) uint32 {
if len(refs) == 0 {
return noStructuredData
}
offset := uint32(len(*buf))
*buf = msgpackWriteArrayHeader(*buf, len(refs))
for _, ref := range refs {
// Each entry is a 5-element tuple: [pos, end, fileName, resolutionMode, preserve]
*buf = msgpackWriteArrayHeader(*buf, 5)
*buf = msgpackWriteUint(*buf, uint32(positionMap.UTF8ToUTF16(ref.Pos())))
*buf = msgpackWriteUint(*buf, uint32(positionMap.UTF8ToUTF16(ref.End())))
*buf = msgpackWriteString(*buf, ref.FileName)
*buf = msgpackWriteUint(*buf, uint32(ref.ResolutionMode))
*buf = msgpackWriteBool(*buf, ref.Preserve)
}
return offset
}
// encodeNodeIndexArray encodes a slice of LiteralLikeNodes as a msgpack array of
// uint node indices. Returns the byte offset into the buffer, or noStructuredData
// if the slice is empty.
func encodeNodeIndexArray(nodes []*ast.LiteralLikeNode, indexMap map[*ast.Node]uint32, buf *[]byte) uint32 {
if len(nodes) == 0 {
return noStructuredData
}
offset := uint32(len(*buf))
*buf = msgpackWriteArrayHeader(*buf, len(nodes))
for _, node := range nodes {
*buf = msgpackWriteUint(*buf, indexMap[node.AsNode()])
}
return offset
}
// encodeModuleAugmentations encodes a slice of ModuleName nodes as a msgpack array
// of uint node indices. Returns the byte offset into the buffer, or noStructuredData
// if the slice is empty.
func encodeModuleAugmentations(nodes []*ast.ModuleName, indexMap map[*ast.Node]uint32, buf *[]byte) uint32 {
if len(nodes) == 0 {
return noStructuredData
}
offset := uint32(len(*buf))
*buf = msgpackWriteArrayHeader(*buf, len(nodes))
for _, node := range nodes {
*buf = msgpackWriteUint(*buf, indexMap[node.AsNode()])
}
return offset
}
// encodeStringArray encodes a slice of strings as a msgpack array of strings.
// Returns the byte offset into the buffer, or noStructuredData if the slice is empty.
func encodeStringArray(strs []string, buf *[]byte) uint32 {
if len(strs) == 0 {
return noStructuredData
}
offset := uint32(len(*buf))
*buf = msgpackWriteArrayHeader(*buf, len(strs))
for _, s := range strs {
*buf = msgpackWriteString(*buf, s)
}
return offset
}
// Minimal msgpack writers for the structured data section.
func msgpackWriteArrayHeader(buf []byte, length int) []byte {
if length <= 0x0f {
return append(buf, byte(0x90|length))
}
if length <= 0xffff {
return append(buf, 0xdc, byte(length>>8), byte(length))
}
return append(buf, 0xdd, byte(length>>24), byte(length>>16), byte(length>>8), byte(length))
}
func msgpackWriteUint(buf []byte, value uint32) []byte {
if value <= 0x7f {
return append(buf, byte(value))
}
if value <= 0xff {
return append(buf, 0xcc, byte(value))
}
if value <= 0xffff {
return append(buf, 0xcd, byte(value>>8), byte(value))
}
return append(buf, 0xce, byte(value>>24), byte(value>>16), byte(value>>8), byte(value))
}
func msgpackWriteString(buf []byte, s string) []byte {
n := len(s)
if n <= 0x1f {
buf = append(buf, byte(0xa0|n))
} else if n <= 0xff {
buf = append(buf, 0xd9, byte(n))
} else if n <= 0xffff {
buf = append(buf, 0xda, byte(n>>8), byte(n))
} else {
buf = append(buf, 0xdb, byte(n>>24), byte(n>>16), byte(n>>8), byte(n))
}
return append(buf, s...)
}
func msgpackWriteBool(buf []byte, value bool) []byte {
if value {
return append(buf, 0xc3)
}
return append(buf, 0xc2)
}
// Hand-written commonData encoding functions for nodes whose non-bool data
// members cannot be automatically encoded by the generator. Each function
// packs relevant fields into the 6-bit commonData area (bits 24-29) of the
// 32-bit node data word.
func getNodeCommonData_SyntheticExpression(_ *ast.Node) uint32 {
// SyntheticExpression is an internal compiler node that is never part of a parsed AST.
// It should never be encoded.
panic("SyntheticExpression should never be encoded")
}
// Hand-written extended data encoding functions for literal nodes that were
// previously string-type but whose TokenFlags/TemplateFlags cannot fit in 6 bits.
func recordExtendedData_StringLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) {
n := node.AsStringLiteral()
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
*extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags))
}
func recordExtendedData_NumericLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) {
n := node.AsNumericLiteral()
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
*extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags))
}
func recordExtendedData_BigIntLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) {
n := node.AsBigIntLiteral()
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
*extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags))
}
func recordExtendedData_RegularExpressionLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) {
n := node.AsRegularExpressionLiteral()
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
*extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TokenFlags))
}
func recordExtendedData_NoSubstitutionTemplateLiteral(node *ast.Node, strs *stringTable, _ *ast.PositionMap, extendedData *[]byte, _ *[]byte) {
n := node.AsNoSubstitutionTemplateLiteral()
textIndex := strs.add(n.Text, node.Kind, node.Pos(), node.End())
*extendedData = appendUint32s(*extendedData, textIndex, uint32(n.TemplateFlags))
}

View File

@@ -0,0 +1,707 @@
// Code generated by _scripts/generate-encoder.ts. DO NOT EDIT.
package encoder
import (
"fmt"
"github.com/microsoft/typescript-go/internal/ast"
)
func getNodeDataType(node *ast.Node) uint32 {
switch node.Kind {
case ast.KindIdentifier,
ast.KindPrivateIdentifier,
ast.KindJsxText,
ast.KindJSDocText,
ast.KindJSDocLink,
ast.KindJSDocLinkPlain,
ast.KindJSDocLinkCode:
return NodeDataTypeString
case ast.KindStringLiteral,
ast.KindNumericLiteral,
ast.KindBigIntLiteral,
ast.KindRegularExpressionLiteral,
ast.KindNoSubstitutionTemplateLiteral,
ast.KindTemplateHead,
ast.KindTemplateMiddle,
ast.KindTemplateTail,
ast.KindSourceFile:
return NodeDataTypeExtendedData
default:
return NodeDataTypeChildren
}
}
func getChildrenPropertyMask(node *ast.Node) uint8 {
switch node.Kind {
case ast.KindQualifiedName:
n := node.AsQualifiedName()
return (boolToByte(n.Left != nil) << 0) | (boolToByte(n.Right != nil) << 1)
case ast.KindComputedPropertyName:
n := node.AsComputedPropertyName()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindDecorator:
n := node.AsDecorator()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindIfStatement:
n := node.AsIfStatement()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.ThenStatement != nil) << 1) | (boolToByte(n.ElseStatement != nil) << 2)
case ast.KindDoStatement:
n := node.AsDoStatement()
return (boolToByte(n.Statement != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
case ast.KindWhileStatement:
n := node.AsWhileStatement()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statement != nil) << 1)
case ast.KindForStatement:
n := node.AsForStatement()
return (boolToByte(n.Initializer != nil) << 0) | (boolToByte(n.Condition != nil) << 1) | (boolToByte(n.Incrementor != nil) << 2) | (boolToByte(n.Statement != nil) << 3)
case ast.KindForInStatement, ast.KindForOfStatement:
n := node.AsForInOrOfStatement()
return (boolToByte(n.AwaitModifier != nil) << 0) | (boolToByte(n.Initializer != nil) << 1) | (boolToByte(n.Expression != nil) << 2) | (boolToByte(n.Statement != nil) << 3)
case ast.KindBreakStatement:
n := node.AsBreakStatement()
return (boolToByte(n.Label != nil) << 0)
case ast.KindContinueStatement:
n := node.AsContinueStatement()
return (boolToByte(n.Label != nil) << 0)
case ast.KindReturnStatement:
n := node.AsReturnStatement()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindWithStatement:
n := node.AsWithStatement()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statement != nil) << 1)
case ast.KindSwitchStatement:
n := node.AsSwitchStatement()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.CaseBlock != nil) << 1)
case ast.KindCaseBlock:
n := node.AsCaseBlock()
return (boolToByte(n.Clauses != nil) << 0)
case ast.KindCaseClause, ast.KindDefaultClause:
n := node.AsCaseOrDefaultClause()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Statements != nil) << 1)
case ast.KindThrowStatement:
n := node.AsThrowStatement()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindTryStatement:
n := node.AsTryStatement()
return (boolToByte(n.TryBlock != nil) << 0) | (boolToByte(n.CatchClause != nil) << 1) | (boolToByte(n.FinallyBlock != nil) << 2)
case ast.KindCatchClause:
n := node.AsCatchClause()
return (boolToByte(n.VariableDeclaration != nil) << 0) | (boolToByte(n.Block != nil) << 1)
case ast.KindLabeledStatement:
n := node.AsLabeledStatement()
return (boolToByte(n.Label != nil) << 0) | (boolToByte(n.Statement != nil) << 1)
case ast.KindExpressionStatement:
n := node.AsExpressionStatement()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindBlock:
n := node.AsBlock()
return (boolToByte(n.Statements != nil) << 0)
case ast.KindVariableStatement:
n := node.AsVariableStatement()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.DeclarationList != nil) << 1)
case ast.KindVariableDeclaration:
n := node.AsVariableDeclaration()
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.ExclamationToken != nil) << 1) | (boolToByte(n.Type != nil) << 2) | (boolToByte(n.Initializer != nil) << 3)
case ast.KindVariableDeclarationList:
n := node.AsVariableDeclarationList()
return (boolToByte(n.Declarations != nil) << 0)
case ast.KindObjectBindingPattern, ast.KindArrayBindingPattern:
n := node.AsBindingPattern()
return (boolToByte(n.Elements != nil) << 0)
case ast.KindParameter:
n := node.AsParameterDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.DotDotDotToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.QuestionToken != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Initializer != nil) << 5)
case ast.KindBindingElement:
n := node.AsBindingElement()
return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.PropertyName != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Initializer != nil) << 3)
case ast.KindMissingDeclaration:
n := node.AsMissingDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0)
case ast.KindFunctionDeclaration:
n := node.AsFunctionDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5) | (boolToByte(n.Body != nil) << 6)
case ast.KindClassDeclaration:
n := node.AsClassDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4)
case ast.KindClassExpression:
n := node.AsClassExpression()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4)
case ast.KindHeritageClause:
n := node.AsHeritageClause()
return (boolToByte(n.Types != nil) << 0)
case ast.KindInterfaceDeclaration:
n := node.AsInterfaceDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.HeritageClauses != nil) << 3) | (boolToByte(n.Members != nil) << 4)
case ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration:
n := node.AsTypeAliasDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Type != nil) << 3)
case ast.KindEnumMember:
n := node.AsEnumMember()
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Initializer != nil) << 1)
case ast.KindEnumDeclaration:
n := node.AsEnumDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Members != nil) << 2)
case ast.KindModuleBlock:
n := node.AsModuleBlock()
return (boolToByte(n.Statements != nil) << 0)
case ast.KindImportDeclaration, ast.KindJSImportDeclaration:
n := node.AsImportDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.ImportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3)
case ast.KindExternalModuleReference:
n := node.AsExternalModuleReference()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindNamespaceImport:
n := node.AsNamespaceImport()
return (boolToByte(n.Name() != nil) << 0)
case ast.KindNamedImports:
n := node.AsNamedImports()
return (boolToByte(n.Elements != nil) << 0)
case ast.KindExportAssignment:
n := node.AsExportAssignment()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Type != nil) << 1) | (boolToByte(n.Expression != nil) << 2)
case ast.KindNamespaceExportDeclaration:
n := node.AsNamespaceExportDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1)
case ast.KindNamespaceExport:
n := node.AsNamespaceExport()
return (boolToByte(n.Name() != nil) << 0)
case ast.KindNamedExports:
n := node.AsNamedExports()
return (boolToByte(n.Elements != nil) << 0)
case ast.KindExportSpecifier:
n := node.AsExportSpecifier()
return (boolToByte(n.PropertyName != nil) << 0) | (boolToByte(n.Name() != nil) << 1)
case ast.KindCallSignature:
n := node.AsCallSignatureDeclaration()
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
case ast.KindConstructSignature:
n := node.AsConstructSignatureDeclaration()
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
case ast.KindConstructor:
n := node.AsConstructorDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Body != nil) << 4)
case ast.KindGetAccessor:
n := node.AsGetAccessorDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Parameters != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Body != nil) << 5)
case ast.KindSetAccessor:
n := node.AsSetAccessorDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Parameters != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Body != nil) << 5)
case ast.KindIndexSignature:
n := node.AsIndexSignatureDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
case ast.KindMethodSignature:
n := node.AsMethodSignatureDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5)
case ast.KindMethodDeclaration:
n := node.AsMethodDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.PostfixToken != nil) << 3) | (boolToByte(n.TypeParameters != nil) << 4) | (boolToByte(n.Parameters != nil) << 5) | (boolToByte(n.Type != nil) << 6) | (boolToByte(n.Body != nil) << 7)
case ast.KindPropertySignature:
n := node.AsPropertySignatureDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4)
case ast.KindPropertyDeclaration:
n := node.AsPropertyDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4)
case ast.KindClassStaticBlockDeclaration:
n := node.AsClassStaticBlockDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Body != nil) << 1)
case ast.KindBinaryExpression:
n := node.AsBinaryExpression()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Left != nil) << 1) | (boolToByte(n.Type != nil) << 2) | (boolToByte(n.OperatorToken != nil) << 3) | (boolToByte(n.Right != nil) << 4)
case ast.KindPrefixUnaryExpression:
n := node.AsPrefixUnaryExpression()
return (boolToByte(n.Operand != nil) << 0)
case ast.KindPostfixUnaryExpression:
n := node.AsPostfixUnaryExpression()
return (boolToByte(n.Operand != nil) << 0)
case ast.KindYieldExpression:
n := node.AsYieldExpression()
return (boolToByte(n.AsteriskToken != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
case ast.KindArrowFunction:
n := node.AsArrowFunction()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.EqualsGreaterThanToken != nil) << 4) | (boolToByte(n.Body != nil) << 5)
case ast.KindFunctionExpression:
n := node.AsFunctionExpression()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.AsteriskToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.TypeParameters != nil) << 3) | (boolToByte(n.Parameters != nil) << 4) | (boolToByte(n.Type != nil) << 5) | (boolToByte(n.Body != nil) << 6)
case ast.KindAsExpression:
n := node.AsAsExpression()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Type != nil) << 1)
case ast.KindSatisfiesExpression:
n := node.AsSatisfiesExpression()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Type != nil) << 1)
case ast.KindConditionalExpression:
n := node.AsConditionalExpression()
return (boolToByte(n.Condition != nil) << 0) | (boolToByte(n.QuestionToken != nil) << 1) | (boolToByte(n.WhenTrue != nil) << 2) | (boolToByte(n.ColonToken != nil) << 3) | (boolToByte(n.WhenFalse != nil) << 4)
case ast.KindPropertyAccessExpression:
n := node.AsPropertyAccessExpression()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.Name() != nil) << 2)
case ast.KindElementAccessExpression:
n := node.AsElementAccessExpression()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.ArgumentExpression != nil) << 2)
case ast.KindCallExpression:
n := node.AsCallExpression()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.TypeArguments != nil) << 2) | (boolToByte(n.Arguments != nil) << 3)
case ast.KindNewExpression:
n := node.AsNewExpression()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Arguments != nil) << 2)
case ast.KindMetaProperty:
n := node.AsMetaProperty()
return (boolToByte(n.Name() != nil) << 0)
case ast.KindNonNullExpression:
n := node.AsNonNullExpression()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindSpreadElement:
n := node.AsSpreadElement()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindTemplateExpression:
n := node.AsTemplateExpression()
return (boolToByte(n.Head != nil) << 0) | (boolToByte(n.TemplateSpans != nil) << 1)
case ast.KindTemplateSpan:
n := node.AsTemplateSpan()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.Literal != nil) << 1)
case ast.KindTaggedTemplateExpression:
n := node.AsTaggedTemplateExpression()
return (boolToByte(n.Tag != nil) << 0) | (boolToByte(n.QuestionDotToken != nil) << 1) | (boolToByte(n.TypeArguments != nil) << 2) | (boolToByte(n.Template != nil) << 3)
case ast.KindParenthesizedExpression:
n := node.AsParenthesizedExpression()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindArrayLiteralExpression:
n := node.AsArrayLiteralExpression()
return (boolToByte(n.Elements != nil) << 0)
case ast.KindObjectLiteralExpression:
n := node.AsObjectLiteralExpression()
return (boolToByte(n.Properties != nil) << 0)
case ast.KindSpreadAssignment:
n := node.AsSpreadAssignment()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindPropertyAssignment:
n := node.AsPropertyAssignment()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.Initializer != nil) << 4)
case ast.KindShorthandPropertyAssignment:
n := node.AsShorthandPropertyAssignment()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.PostfixToken != nil) << 2) | (boolToByte(n.Type != nil) << 3) | (boolToByte(n.EqualsToken != nil) << 4) | (boolToByte(n.ObjectAssignmentInitializer != nil) << 5)
case ast.KindDeleteExpression:
n := node.AsDeleteExpression()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindTypeOfExpression:
n := node.AsTypeOfExpression()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindVoidExpression:
n := node.AsVoidExpression()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindAwaitExpression:
n := node.AsAwaitExpression()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindTypeAssertionExpression:
n := node.AsTypeAssertion()
return (boolToByte(n.Type != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
case ast.KindUnionType:
n := node.AsUnionTypeNode()
return (boolToByte(n.Types != nil) << 0)
case ast.KindIntersectionType:
n := node.AsIntersectionTypeNode()
return (boolToByte(n.Types != nil) << 0)
case ast.KindConditionalType:
n := node.AsConditionalTypeNode()
return (boolToByte(n.CheckType != nil) << 0) | (boolToByte(n.ExtendsType != nil) << 1) | (boolToByte(n.TrueType != nil) << 2) | (boolToByte(n.FalseType != nil) << 3)
case ast.KindTypeOperator:
n := node.AsTypeOperatorNode()
return (boolToByte(n.Type != nil) << 0)
case ast.KindInferType:
n := node.AsInferTypeNode()
return (boolToByte(n.TypeParameter != nil) << 0)
case ast.KindArrayType:
n := node.AsArrayTypeNode()
return (boolToByte(n.ElementType != nil) << 0)
case ast.KindIndexedAccessType:
n := node.AsIndexedAccessTypeNode()
return (boolToByte(n.ObjectType != nil) << 0) | (boolToByte(n.IndexType != nil) << 1)
case ast.KindTypeReference:
n := node.AsTypeReferenceNode()
return (boolToByte(n.TypeName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1)
case ast.KindExpressionWithTypeArguments:
n := node.AsExpressionWithTypeArguments()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1)
case ast.KindLiteralType:
n := node.AsLiteralTypeNode()
return (boolToByte(n.Literal != nil) << 0)
case ast.KindTypePredicate:
n := node.AsTypePredicateNode()
return (boolToByte(n.AssertsModifier != nil) << 0) | (boolToByte(n.ParameterName != nil) << 1) | (boolToByte(n.Type != nil) << 2)
case ast.KindImportAttribute:
n := node.AsImportAttribute()
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Value != nil) << 1)
case ast.KindImportAttributes:
n := node.AsImportAttributes()
return (boolToByte(n.Attributes != nil) << 0)
case ast.KindTypeQuery:
n := node.AsTypeQueryNode()
return (boolToByte(n.ExprName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1)
case ast.KindMappedType:
n := node.AsMappedTypeNode()
return (boolToByte(n.ReadonlyToken != nil) << 0) | (boolToByte(n.TypeParameter != nil) << 1) | (boolToByte(n.NameType != nil) << 2) | (boolToByte(n.QuestionToken != nil) << 3) | (boolToByte(n.Type != nil) << 4) | (boolToByte(n.Members != nil) << 5)
case ast.KindTypeLiteral:
n := node.AsTypeLiteralNode()
return (boolToByte(n.Members != nil) << 0)
case ast.KindTupleType:
n := node.AsTupleTypeNode()
return (boolToByte(n.Elements != nil) << 0)
case ast.KindNamedTupleMember:
n := node.AsNamedTupleMember()
return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.QuestionToken != nil) << 2) | (boolToByte(n.Type != nil) << 3)
case ast.KindOptionalType:
n := node.AsOptionalTypeNode()
return (boolToByte(n.Type != nil) << 0)
case ast.KindRestType:
n := node.AsRestTypeNode()
return (boolToByte(n.Type != nil) << 0)
case ast.KindParenthesizedType:
n := node.AsParenthesizedTypeNode()
return (boolToByte(n.Type != nil) << 0)
case ast.KindFunctionType:
n := node.AsFunctionTypeNode()
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
case ast.KindConstructorType:
n := node.AsConstructorTypeNode()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.TypeParameters != nil) << 1) | (boolToByte(n.Parameters != nil) << 2) | (boolToByte(n.Type != nil) << 3)
case ast.KindTemplateLiteralType:
n := node.AsTemplateLiteralTypeNode()
return (boolToByte(n.Head != nil) << 0) | (boolToByte(n.TemplateSpans != nil) << 1)
case ast.KindTemplateLiteralTypeSpan:
n := node.AsTemplateLiteralTypeSpan()
return (boolToByte(n.Type != nil) << 0) | (boolToByte(n.Literal != nil) << 1)
case ast.KindSyntheticExpression:
n := node.AsSyntheticExpression()
return (boolToByte(n.TupleNameSource != nil) << 0)
case ast.KindPartiallyEmittedExpression:
n := node.AsPartiallyEmittedExpression()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindJsxElement:
n := node.AsJsxElement()
return (boolToByte(n.OpeningElement != nil) << 0) | (boolToByte(n.Children != nil) << 1) | (boolToByte(n.ClosingElement != nil) << 2)
case ast.KindJsxAttributes:
n := node.AsJsxAttributes()
return (boolToByte(n.Properties != nil) << 0)
case ast.KindJsxNamespacedName:
n := node.AsJsxNamespacedName()
return (boolToByte(n.Namespace != nil) << 0) | (boolToByte(n.Name() != nil) << 1)
case ast.KindJsxOpeningElement:
n := node.AsJsxOpeningElement()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Attributes != nil) << 2)
case ast.KindJsxSelfClosingElement:
n := node.AsJsxSelfClosingElement()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeArguments != nil) << 1) | (boolToByte(n.Attributes != nil) << 2)
case ast.KindJsxFragment:
n := node.AsJsxFragment()
return (boolToByte(n.OpeningFragment != nil) << 0) | (boolToByte(n.Children != nil) << 1) | (boolToByte(n.ClosingFragment != nil) << 2)
case ast.KindJsxAttribute:
n := node.AsJsxAttribute()
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.Initializer != nil) << 1)
case ast.KindJsxSpreadAttribute:
n := node.AsJsxSpreadAttribute()
return (boolToByte(n.Expression != nil) << 0)
case ast.KindJsxClosingElement:
n := node.AsJsxClosingElement()
return (boolToByte(n.TagName != nil) << 0)
case ast.KindJsxExpression:
n := node.AsJsxExpression()
return (boolToByte(n.DotDotDotToken != nil) << 0) | (boolToByte(n.Expression != nil) << 1)
case ast.KindSyntaxList:
n := node.AsSyntaxList()
return (boolToByte(len(n.Children) > 0) << 0)
case ast.KindJSDoc:
n := node.AsJSDoc()
return (boolToByte(n.Comment != nil) << 0) | (boolToByte(n.Tags != nil) << 1)
case ast.KindJSDocTypeExpression:
n := node.AsJSDocTypeExpression()
return (boolToByte(n.Type != nil) << 0)
case ast.KindJSDocNonNullableType:
n := node.AsJSDocNonNullableType()
return (boolToByte(n.Type != nil) << 0)
case ast.KindJSDocNullableType:
n := node.AsJSDocNullableType()
return (boolToByte(n.Type != nil) << 0)
case ast.KindJSDocVariadicType:
n := node.AsJSDocVariadicType()
return (boolToByte(n.Type != nil) << 0)
case ast.KindJSDocOptionalType:
n := node.AsJSDocOptionalType()
return (boolToByte(n.Type != nil) << 0)
case ast.KindJSDocTypeTag:
n := node.AsJSDocTypeTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
case ast.KindJSDocUnknownTag:
n := node.AsJSDocUnknownTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
case ast.KindJSDocTemplateTag:
n := node.AsJSDocTemplateTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Constraint != nil) << 1) | (boolToByte(n.TypeParameters != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
case ast.KindJSDocReturnTag:
n := node.AsJSDocReturnTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
case ast.KindJSDocPublicTag:
n := node.AsJSDocPublicTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
case ast.KindJSDocPrivateTag:
n := node.AsJSDocPrivateTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
case ast.KindJSDocProtectedTag:
n := node.AsJSDocProtectedTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
case ast.KindJSDocReadonlyTag:
n := node.AsJSDocReadonlyTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
case ast.KindJSDocOverrideTag:
n := node.AsJSDocOverrideTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
case ast.KindJSDocDeprecatedTag:
n := node.AsJSDocDeprecatedTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Comment != nil) << 1)
case ast.KindJSDocSeeTag:
n := node.AsJSDocSeeTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.NameExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
case ast.KindJSDocImplementsTag:
n := node.AsJSDocImplementsTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ClassName != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
case ast.KindJSDocAugmentsTag:
n := node.AsJSDocAugmentsTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ClassName != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
case ast.KindJSDocSatisfiesTag:
n := node.AsJSDocSatisfiesTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
case ast.KindJSDocThrowsTag:
n := node.AsJSDocThrowsTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
case ast.KindJSDocThisTag:
n := node.AsJSDocThisTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
case ast.KindJSDocImportTag:
n := node.AsJSDocImportTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.ImportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3) | (boolToByte(n.Comment != nil) << 4)
case ast.KindJSDocCallbackTag:
n := node.AsJSDocCallbackTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
case ast.KindJSDocOverloadTag:
n := node.AsJSDocOverloadTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Comment != nil) << 2)
case ast.KindJSDocTypedefTag:
n := node.AsJSDocTypedefTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.TypeExpression != nil) << 1) | (boolToByte(n.Name() != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
case ast.KindJSDocSignature:
n := node.AsJSDocSignature()
return (boolToByte(n.TypeParameters != nil) << 0) | (boolToByte(n.Parameters != nil) << 1) | (boolToByte(n.Type != nil) << 2)
case ast.KindJSDocNameReference:
n := node.AsJSDocNameReference()
return (boolToByte(n.Name() != nil) << 0)
case ast.KindModuleDeclaration:
n := node.AsModuleDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Body != nil) << 2)
case ast.KindImportEqualsDeclaration:
n := node.AsImportEqualsDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.ModuleReference != nil) << 2)
case ast.KindExportDeclaration:
n := node.AsExportDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.ExportClause != nil) << 1) | (boolToByte(n.ModuleSpecifier != nil) << 2) | (boolToByte(n.Attributes != nil) << 3)
case ast.KindImportType:
n := node.AsImportTypeNode()
return (boolToByte(n.Argument != nil) << 0) | (boolToByte(n.Attributes != nil) << 1) | (boolToByte(n.Qualifier != nil) << 2) | (boolToByte(n.TypeArguments != nil) << 3)
case ast.KindImportClause:
n := node.AsImportClause()
return (boolToByte(n.Name() != nil) << 0) | (boolToByte(n.NamedBindings != nil) << 1)
case ast.KindImportSpecifier:
n := node.AsImportSpecifier()
return (boolToByte(n.PropertyName != nil) << 0) | (boolToByte(n.Name() != nil) << 1)
case ast.KindJSDocLink:
n := node.AsJSDocLink()
return (boolToByte(n.Name() != nil) << 0)
case ast.KindJSDocLinkPlain:
n := node.AsJSDocLinkPlain()
return (boolToByte(n.Name() != nil) << 0)
case ast.KindJSDocLinkCode:
n := node.AsJSDocLinkCode()
return (boolToByte(n.Name() != nil) << 0)
case ast.KindTypeParameter:
n := node.AsTypeParameterDeclaration()
return (boolToByte(hasModifiers(n.Modifiers())) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.Constraint != nil) << 2) | (boolToByte(n.Expression != nil) << 3) | (boolToByte(n.DefaultType != nil) << 4)
case ast.KindSyntheticReferenceExpression:
n := node.AsSyntheticReferenceExpression()
return (boolToByte(n.Expression != nil) << 0) | (boolToByte(n.ThisArg != nil) << 1)
case ast.KindJSDocTypeLiteral:
n := node.AsJSDocTypeLiteral()
return (boolToByte(len(n.JSDocPropertyTags) > 0) << 0)
case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag:
n := node.AsJSDocParameterOrPropertyTag()
return (boolToByte(n.TagName != nil) << 0) | (boolToByte(n.Name() != nil) << 1) | (boolToByte(n.TypeExpression != nil) << 2) | (boolToByte(n.Comment != nil) << 3)
default:
return 0
}
}
func getNodeCommonData(node *ast.Node) uint32 {
switch node.Kind {
case ast.KindBlock:
n := node.AsBlock()
return uint32(boolToByte(n.MultiLine)) << 24
case ast.KindHeritageClause:
n := node.AsHeritageClause()
var tokenIdx uint32
switch n.Token {
case ast.KindImplementsKeyword:
tokenIdx = 1
}
return tokenIdx << 24
case ast.KindExportAssignment:
n := node.AsExportAssignment()
return uint32(boolToByte(n.IsExportEquals)) << 24
case ast.KindExportSpecifier:
n := node.AsExportSpecifier()
return uint32(boolToByte(n.IsTypeOnly)) << 24
case ast.KindPrefixUnaryExpression:
n := node.AsPrefixUnaryExpression()
var operatorIdx uint32
switch n.Operator {
case ast.KindMinusToken:
operatorIdx = 1
case ast.KindTildeToken:
operatorIdx = 2
case ast.KindExclamationToken:
operatorIdx = 3
case ast.KindPlusPlusToken:
operatorIdx = 4
case ast.KindMinusMinusToken:
operatorIdx = 5
}
return operatorIdx << 24
case ast.KindPostfixUnaryExpression:
n := node.AsPostfixUnaryExpression()
var operatorIdx uint32
switch n.Operator {
case ast.KindMinusMinusToken:
operatorIdx = 1
}
return operatorIdx << 24
case ast.KindMetaProperty:
n := node.AsMetaProperty()
var keywordTokenIdx uint32
switch n.KeywordToken {
case ast.KindNewKeyword:
keywordTokenIdx = 1
}
return keywordTokenIdx << 24
case ast.KindArrayLiteralExpression:
n := node.AsArrayLiteralExpression()
return uint32(boolToByte(n.MultiLine)) << 24
case ast.KindObjectLiteralExpression:
n := node.AsObjectLiteralExpression()
return uint32(boolToByte(n.MultiLine)) << 24
case ast.KindTypeOperator:
n := node.AsTypeOperatorNode()
var operatorIdx uint32
switch n.Operator {
case ast.KindReadonlyKeyword:
operatorIdx = 1
case ast.KindUniqueKeyword:
operatorIdx = 2
}
return operatorIdx << 24
case ast.KindImportAttributes:
n := node.AsImportAttributes()
var tokenIdx uint32
switch n.Token {
case ast.KindAssertKeyword:
tokenIdx = 1
}
return uint32(boolToByte(n.MultiLine))<<24 | tokenIdx<<25
case ast.KindSyntheticExpression:
return getNodeCommonData_SyntheticExpression(node)
case ast.KindJsxText:
n := node.AsJsxText()
return uint32(boolToByte(n.ContainsOnlyTriviaWhiteSpaces)) << 24
case ast.KindModuleDeclaration:
n := node.AsModuleDeclaration()
var keywordIdx uint32
switch n.Keyword {
case ast.KindNamespaceKeyword:
keywordIdx = 1
}
return keywordIdx << 24
case ast.KindImportEqualsDeclaration:
n := node.AsImportEqualsDeclaration()
return uint32(boolToByte(n.IsTypeOnly)) << 24
case ast.KindExportDeclaration:
n := node.AsExportDeclaration()
return uint32(boolToByte(n.IsTypeOnly)) << 24
case ast.KindImportType:
n := node.AsImportTypeNode()
return uint32(boolToByte(n.IsTypeOf)) << 24
case ast.KindImportClause:
n := node.AsImportClause()
var phaseModifierIdx uint32
switch n.PhaseModifier {
case ast.KindTypeKeyword:
phaseModifierIdx = 1
case ast.KindDeferKeyword:
phaseModifierIdx = 2
}
return phaseModifierIdx << 24
case ast.KindImportSpecifier:
n := node.AsImportSpecifier()
return uint32(boolToByte(n.IsTypeOnly)) << 24
case ast.KindJSDocTypeLiteral:
n := node.AsJSDocTypeLiteral()
return uint32(boolToByte(n.IsArrayType)) << 24
case ast.KindJSDocParameterTag, ast.KindJSDocPropertyTag:
n := node.AsJSDocParameterOrPropertyTag()
return uint32(boolToByte(n.IsBracketed))<<24 | uint32(boolToByte(n.IsNameFirst))<<25
}
return 0
}
func recordNodeStrings(node *ast.Node, strs *stringTable) uint32 {
switch node.Kind {
case ast.KindIdentifier:
return strs.add(node.AsIdentifier().Text, node.Kind, node.Pos(), node.End())
case ast.KindPrivateIdentifier:
return strs.add(node.AsPrivateIdentifier().Text, node.Kind, node.Pos(), node.End())
case ast.KindJsxText:
return strs.add(node.AsJsxText().Text, node.Kind, node.Pos(), node.End())
case ast.KindJSDocText:
return strs.add(node.AsJSDocText().Text(), node.Kind, node.Pos(), node.End())
case ast.KindJSDocLink:
return strs.add(node.AsJSDocLink().Text(), node.Kind, node.Pos(), node.End())
case ast.KindJSDocLinkPlain:
return strs.add(node.AsJSDocLinkPlain().Text(), node.Kind, node.Pos(), node.End())
case ast.KindJSDocLinkCode:
return strs.add(node.AsJSDocLinkCode().Text(), node.Kind, node.Pos(), node.End())
default:
panic(fmt.Sprintf("Unexpected node kind %v", node.Kind))
}
}
func recordExtendedData(node *ast.Node, strs *stringTable, positionMap *ast.PositionMap, extendedData *[]byte, structuredData *[]byte) uint32 {
offset := uint32(len(*extendedData))
switch node.Kind {
case ast.KindStringLiteral:
recordExtendedData_StringLiteral(node, strs, positionMap, extendedData, structuredData)
case ast.KindNumericLiteral:
recordExtendedData_NumericLiteral(node, strs, positionMap, extendedData, structuredData)
case ast.KindBigIntLiteral:
recordExtendedData_BigIntLiteral(node, strs, positionMap, extendedData, structuredData)
case ast.KindRegularExpressionLiteral:
recordExtendedData_RegularExpressionLiteral(node, strs, positionMap, extendedData, structuredData)
case ast.KindNoSubstitutionTemplateLiteral:
recordExtendedData_NoSubstitutionTemplateLiteral(node, strs, positionMap, extendedData, structuredData)
case ast.KindTemplateHead:
recordExtendedData_TemplateHead(node, strs, positionMap, extendedData, structuredData)
case ast.KindTemplateMiddle:
recordExtendedData_TemplateMiddle(node, strs, positionMap, extendedData, structuredData)
case ast.KindTemplateTail:
recordExtendedData_TemplateTail(node, strs, positionMap, extendedData, structuredData)
case ast.KindSourceFile:
recordExtendedData_SourceFile(node, strs, positionMap, extendedData, structuredData)
default:
panic(fmt.Sprintf("unknown extended data node kind %v", node.Kind))
}
return offset
}

View File

@@ -0,0 +1,161 @@
package encoder_test
import (
"encoding/binary"
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/microsoft/typescript-go/internal/api/encoder"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/parser"
"github.com/microsoft/typescript-go/internal/repo"
"github.com/microsoft/typescript-go/internal/testutil/baseline"
"gotest.tools/v3/assert"
)
func TestEncodeSourceFile(t *testing.T) {
t.Parallel()
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/test.ts",
Path: "/test.ts",
}, "import { bar } from \"bar\";\nexport function foo<T, U>(a: string, b: string): any {}\nfoo();", core.ScriptKindTS)
t.Run("baseline", func(t *testing.T) {
t.Parallel()
buf, _, err := encoder.EncodeSourceFile(sourceFile)
assert.NilError(t, err)
str := formatEncodedSourceFile(buf)
baseline.Run(t, "encodeSourceFile.txt", str, baseline.Options{
Subfolder: "api",
})
})
}
func TestEncodeSourceFileWithUnicodeEscapes(t *testing.T) {
t.Parallel()
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/test.ts",
Path: "/test.ts",
}, `let a = "😃"; let b = "\ud83d\ude03"; let c = "\udc00\ud83d\ude03"; let d = "\ud83d\ud83d\ude03"`, core.ScriptKindTS)
t.Run("baseline", func(t *testing.T) {
t.Parallel()
buf, _, err := encoder.EncodeSourceFile(sourceFile)
assert.NilError(t, err)
str := formatEncodedSourceFile(buf)
baseline.Run(t, "encodeSourceFileWithUnicodeEscapes.txt", str, baseline.Options{
Subfolder: "api",
})
})
}
func TestBuildNodeIndexTableMatchesEncode(t *testing.T) {
t.Parallel()
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/test.ts",
Path: "/test.ts",
}, "import { bar } from \"bar\";\nexport function foo<T, U>(a: string, b: string): any {}\nfoo();", core.ScriptKindTS)
_, encodeTable, err := encoder.EncodeSourceFile(sourceFile)
assert.NilError(t, err)
buildTable := encoder.BuildNodeIndexTable(sourceFile)
// Both tables should produce identical Nodes slices
assert.Equal(t, len(buildTable.Nodes), len(encodeTable.Nodes), "Nodes slice length mismatch")
// Every index should map to the same node
for i := range encodeTable.Nodes {
assert.Equal(t, buildTable.Nodes[i], encodeTable.Nodes[i], "node mismatch at index %d", i)
}
// GetIndex on both tables should agree for every non-nil node
for i, node := range encodeTable.Nodes {
if node == nil {
continue
}
encIdx := encodeTable.GetIndex(node)
buildIdx := buildTable.GetIndex(node)
assert.Equal(t, encIdx, uint32(i), "encodeTable.GetIndex mismatch at index %d, node kind=%s", i, node.Kind.String())
assert.Equal(t, buildIdx, encIdx, "buildTable.GetIndex mismatch for node kind=%s", node.Kind.String())
}
}
func BenchmarkEncodeSourceFile(b *testing.B) {
repo.SkipIfNoTypeScriptSubmodule(b)
filePath := filepath.Join(repo.TypeScriptSubmodulePath(), "src/compiler/checker.ts")
fileContent, err := os.ReadFile(filePath)
assert.NilError(b, err)
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/checker.ts",
Path: "/checker.ts",
}, string(fileContent), core.ScriptKindTS)
for b.Loop() {
_, _, err := encoder.EncodeSourceFile(sourceFile)
assert.NilError(b, err)
}
}
func BenchmarkBuildNodeIndexTable(b *testing.B) {
repo.SkipIfNoTypeScriptSubmodule(b)
filePath := filepath.Join(repo.TypeScriptSubmodulePath(), "src/compiler/checker.ts")
fileContent, err := os.ReadFile(filePath)
assert.NilError(b, err)
sourceFile := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/checker.ts",
Path: "/checker.ts",
}, string(fileContent), core.ScriptKindTS)
for b.Loop() {
encoder.BuildNodeIndexTable(sourceFile)
}
}
func readUint32(buf []byte, offset int) uint32 {
return binary.LittleEndian.Uint32(buf[offset : offset+4])
}
func formatEncodedSourceFile(encoded []byte) string {
var result strings.Builder
var getIndent func(parentIndex uint32) string
offsetNodes := readUint32(encoded, encoder.HeaderOffsetNodes)
offsetStringOffsets := readUint32(encoded, encoder.HeaderOffsetStringOffsets)
offsetStrings := readUint32(encoded, encoder.HeaderOffsetStringData)
getIndent = func(parentIndex uint32) string {
if parentIndex == 0 {
return ""
}
return " " + getIndent(readUint32(encoded, int(offsetNodes)+int(parentIndex)*encoder.NodeSize+encoder.NodeOffsetParent))
}
j := 1
for i := int(offsetNodes) + encoder.NodeSize; i < len(encoded); i += encoder.NodeSize {
kind := readUint32(encoded, i+encoder.NodeOffsetKind)
pos := readUint32(encoded, i+encoder.NodeOffsetPos)
end := readUint32(encoded, i+encoder.NodeOffsetEnd)
parentIndex := readUint32(encoded, i+encoder.NodeOffsetParent)
result.WriteString(getIndent(parentIndex))
if kind == encoder.SyntaxKindNodeList {
result.WriteString("NodeList")
} else {
result.WriteString(ast.Kind(kind).String())
}
data := readUint32(encoded, i+encoder.NodeOffsetData)
dataType := data & encoder.NodeDataTypeMask
if ast.Kind(kind) == ast.KindIdentifier || (dataType == encoder.NodeDataTypeString) {
stringIndex := data & encoder.NodeDataStringIndexMask
strStart := readUint32(encoded, int(offsetStringOffsets+stringIndex*4))
strEnd := readUint32(encoded, int(offsetStringOffsets+stringIndex*4)+4)
str := string(encoded[offsetStrings+strStart : offsetStrings+strEnd])
result.WriteString(fmt.Sprintf(" \"%s\"", str))
}
fmt.Fprintf(&result, " [%d, %d), i=%d, next=%d", pos, end, j, encoded[i+encoder.NodeOffsetNext])
result.WriteString("\n")
j++
}
return result.String()
}

View File

@@ -0,0 +1,68 @@
package encoder
import (
"strings"
"github.com/microsoft/typescript-go/internal/ast"
)
type stringTable struct {
fileText string
otherStrings *strings.Builder
// offsets are pos/end pairs
offsets []uint32
}
func newStringTable(fileText string, stringCount int) *stringTable {
builder := &strings.Builder{}
return &stringTable{
fileText: fileText,
otherStrings: builder,
offsets: make([]uint32, 0, stringCount*2),
}
}
func (t *stringTable) add(text string, kind ast.Kind, pos int, end int) uint32 {
index := uint32(len(t.offsets))
if kind == ast.KindSourceFile {
t.offsets = append(t.offsets, uint32(pos), uint32(end))
return index
}
length := len(text)
if end-pos > 0 && end <= len(t.fileText) {
// pos includes leading trivia, but we can usually infer the actual start of the
// string from the kind and end
endOffset := 0
if kind == ast.KindStringLiteral || kind == ast.KindTemplateTail || kind == ast.KindNoSubstitutionTemplateLiteral {
endOffset = 1
}
end = end - endOffset
start := end - length
fileSlice := t.fileText[start:end]
if fileSlice == text {
t.offsets = append(t.offsets, uint32(start), uint32(end))
return index
}
}
// no exact match, so we need to add it to the string table
offset := len(t.fileText) + t.otherStrings.Len()
t.otherStrings.WriteString(text)
t.offsets = append(t.offsets, uint32(offset), uint32(offset+length))
return index
}
func (t *stringTable) encode() []byte {
result := make([]byte, 0, t.encodedLength())
result = appendUint32s(result, t.offsets...)
result = append(result, t.fileText...)
result = append(result, t.otherStrings.String()...)
return result
}
func (t *stringTable) stringLength() int {
return len(t.fileText) + t.otherStrings.Len()
}
func (t *stringTable) encodedLength() int {
return len(t.offsets)*4 + len(t.fileText) + t.otherStrings.Len()
}

View File

@@ -0,0 +1,14 @@
package encoder_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/testutil/baseline"
)
func TestMain(m *testing.M) {
core.ApplyDebugStackLimit()
defer baseline.Track()()
m.Run()
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,83 @@
package api_test
import (
"strings"
"testing"
"github.com/microsoft/typescript-go/internal/api"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/diagnostics"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/parser"
"gotest.tools/v3/assert"
)
func TestDocumentIdentifierUnmarshalJSON(t *testing.T) {
t.Parallel()
tests := []struct {
name string
input string
fileName string
uri string
err string
}{
{
name: "plain string",
input: `"foo.ts"`,
fileName: "foo.ts",
},
{
name: "uri object",
input: `{"uri":"file:///foo.ts"}`,
uri: "file:///foo.ts",
},
{
name: "uri object with unknown fields",
input: `{"uri":"file:///foo.ts","extra":true}`,
uri: "file:///foo.ts",
},
{
name: "empty object",
input: `{}`,
},
{
name: "invalid type",
input: `42`,
err: "expected string or object, got number",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var d api.DocumentIdentifier
err := json.Unmarshal([]byte(tt.input), &d)
if tt.err != "" {
assert.ErrorContains(t, err, tt.err)
return
}
assert.NilError(t, err)
assert.Equal(t, d.FileName, tt.fileName)
assert.Equal(t, string(d.URI), tt.uri)
})
}
}
func TestNewDiagnosticResponseUsesUTF16Offsets(t *testing.T) {
t.Parallel()
text := "const 💩 = 1;"
file := parser.ParseSourceFile(ast.SourceFileParseOptions{FileName: "/unicode.ts"}, text, core.ScriptKindTS)
pos := strings.Index(text, "=")
assert.Assert(t, pos > 0)
end := pos + len("=")
diag := ast.NewDiagnostic(file, core.NewTextRange(pos, end), diagnostics.Expression_expected)
resp := api.NewDiagnosticResponse(diag)
assert.Equal(t, resp.Pos, 9)
assert.Equal(t, resp.End, 10)
assert.Equal(t, resp.Pos, file.GetPositionMap().UTF8ToUTF16(pos))
assert.Equal(t, resp.End, file.GetPositionMap().UTF8ToUTF16(end))
}

View File

@@ -0,0 +1,22 @@
package api
import (
"github.com/microsoft/typescript-go/internal/jsonrpc"
)
// Message is an alias for jsonrpc.Message for convenience.
type Message = jsonrpc.Message
// Protocol defines the interface for reading and writing API messages.
type Protocol interface {
// ReadMessage reads the next message from the connection.
ReadMessage() (*Message, error)
// WriteRequest writes a request message.
WriteRequest(id *jsonrpc.ID, method string, params any) error
// WriteNotification writes a notification message (no ID).
WriteNotification(method string, params any) error
// WriteResponse writes a successful response.
WriteResponse(id *jsonrpc.ID, result any) error
// WriteError writes an error response.
WriteError(id *jsonrpc.ID, err *jsonrpc.ResponseError) error
}

View File

@@ -0,0 +1,96 @@
package api
import (
"io"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/jsonrpc"
)
// JSONRPCProtocol implements the Protocol interface using JSON-RPC 2.0
// with the LSP base protocol framing (Content-Length headers).
type JSONRPCProtocol struct {
reader *jsonrpc.Reader
writer *jsonrpc.Writer
}
var _ Protocol = (*JSONRPCProtocol)(nil)
// NewJSONRPCProtocol creates a new JSON-RPC protocol handler.
func NewJSONRPCProtocol(rw io.ReadWriter) *JSONRPCProtocol {
return &JSONRPCProtocol{
reader: jsonrpc.NewReader(rw),
writer: jsonrpc.NewWriter(rw),
}
}
// ReadMessage implements Protocol.
func (p *JSONRPCProtocol) ReadMessage() (*Message, error) {
data, err := p.reader.Read()
if err != nil {
return nil, err
}
var msg Message
if err := json.Unmarshal(data, &msg); err != nil {
return nil, err
}
return &msg, nil
}
// WriteRequest implements Protocol.
func (p *JSONRPCProtocol) WriteRequest(id *jsonrpc.ID, method string, params any) error {
msg := jsonrpc.RequestMessage{
ID: id,
Method: method,
Params: params,
}
data, err := json.Marshal(msg)
if err != nil {
return err
}
return p.writer.Write(data)
}
// WriteNotification implements Protocol.
func (p *JSONRPCProtocol) WriteNotification(method string, params any) error {
msg := jsonrpc.RequestMessage{
Method: method,
Params: params,
}
data, err := json.Marshal(msg)
if err != nil {
return err
}
return p.writer.Write(data)
}
// WriteResponse implements Protocol.
func (p *JSONRPCProtocol) WriteResponse(id *jsonrpc.ID, result any) error {
if result == nil {
result = json.Value("null")
}
msg := jsonrpc.ResponseMessage{
ID: id,
Result: result,
}
data, err := json.Marshal(msg)
if err != nil {
return err
}
return p.writer.Write(data)
}
// WriteError implements Protocol.
func (p *JSONRPCProtocol) WriteError(id *jsonrpc.ID, respErr *jsonrpc.ResponseError) error {
msg := jsonrpc.ResponseMessage{
ID: id,
Error: respErr,
}
data, err := json.Marshal(msg)
if err != nil {
return err
}
return p.writer.Write(data)
}

View File

@@ -0,0 +1,280 @@
package api
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"github.com/microsoft/typescript-go/internal/json"
"github.com/microsoft/typescript-go/internal/jsonrpc"
)
// MessageType represents the type of message in the msgpack protocol.
type MessageType uint8
const (
MessageTypeUnknown MessageType = iota
MessageTypeRequest
MessageTypeCallResponse
MessageTypeCallError
MessageTypeResponse
MessageTypeError
MessageTypeCall
)
func (m MessageType) IsValid() bool {
return m >= MessageTypeRequest && m <= MessageTypeCall
}
// MessagePack format constants
const (
msgpackFixedArray3 byte = 0x93
msgpackBin8 byte = 0xC4
msgpackBin16 byte = 0xC5
msgpackBin32 byte = 0xC6
msgpackU8 byte = 0xCC
)
// MessagePackProtocol implements the Protocol interface using a custom
// msgpack-based tuple format: [MessageType, method, payload].
type MessagePackProtocol struct {
r *bufio.Reader
w *bufio.Writer
}
var _ Protocol = (*MessagePackProtocol)(nil)
// NewMessagePackProtocol creates a new msgpack protocol handler.
func NewMessagePackProtocol(rw io.ReadWriter) *MessagePackProtocol {
return &MessagePackProtocol{
r: bufio.NewReader(rw),
w: bufio.NewWriter(rw),
}
}
// ReadMessage implements Protocol.
func (p *MessagePackProtocol) ReadMessage() (*Message, error) {
msgType, method, payload, err := p.readTuple()
if err != nil {
return nil, err
}
// Convert msgpack message type to JSON-RPC message
msg := &Message{}
switch msgType {
case MessageTypeRequest:
// Client request - needs an ID for response
// We use the method as a pseudo-ID since this protocol doesn't have explicit IDs
id := jsonrpc.NewIDString(method)
msg.ID = id
msg.Method = method
msg.Params = payload
case MessageTypeCallResponse:
// Response to our Call - use method as ID
// Note: Method must be empty for IsResponse() to return true
id := jsonrpc.NewIDString(method)
msg.ID = id
msg.Result = payload
case MessageTypeCallError:
// Error response to our Call
// Note: Method must be empty for IsResponse() to return true
id := jsonrpc.NewIDString(method)
msg.ID = id
msg.Error = &jsonrpc.ResponseError{
Code: jsonrpc.CodeInternalError,
Message: string(payload),
}
default:
return nil, fmt.Errorf("unexpected message type: %d", msgType)
}
return msg, nil
}
func (p *MessagePackProtocol) readTuple() (MessageType, string, []byte, error) {
// Read fixed array marker (0x93 = 3-element array)
t, err := p.r.ReadByte()
if err != nil {
return 0, "", nil, err
}
if t != msgpackFixedArray3 {
return 0, "", nil, fmt.Errorf("%w: expected fixed 3-element array (0x93), received: 0x%02x", ErrInvalidRequest, t)
}
// Read message type - can be positive fixint (0x00-0x7F) or uint8 (0xCC + value)
t, err = p.r.ReadByte()
if err != nil {
return 0, "", nil, err
}
var rawType byte
if t <= 0x7F {
// Positive fixint - the byte IS the value
rawType = t
} else if t == msgpackU8 {
// uint8 marker - next byte is the value
rawType, err = p.r.ReadByte()
if err != nil {
return 0, "", nil, err
}
} else {
return 0, "", nil, fmt.Errorf("%w: expected positive fixint or uint8 marker, received: 0x%02x", ErrInvalidRequest, t)
}
msgType := MessageType(rawType)
if !msgType.IsValid() {
return 0, "", nil, fmt.Errorf("%w: unknown message type: %d", ErrInvalidRequest, msgType)
}
// Read method (binary)
methodBytes, err := p.readBin()
if err != nil {
return 0, "", nil, err
}
method := string(methodBytes)
// Read payload (binary)
payload, err := p.readBin()
if err != nil {
return 0, "", nil, err
}
return msgType, method, payload, nil
}
func (p *MessagePackProtocol) readBin() ([]byte, error) {
t, err := p.r.ReadByte()
if err != nil {
return nil, err
}
var size uint
switch t {
case msgpackBin8:
var size8 uint8
if err = binary.Read(p.r, binary.BigEndian, &size8); err != nil {
return nil, err
}
size = uint(size8)
case msgpackBin16:
var size16 uint16
if err = binary.Read(p.r, binary.BigEndian, &size16); err != nil {
return nil, err
}
size = uint(size16)
case msgpackBin32:
var size32 uint32
if err = binary.Read(p.r, binary.BigEndian, &size32); err != nil {
return nil, err
}
size = uint(size32)
default:
return nil, fmt.Errorf("%w: expected binary data (0xc4-0xc6), received: 0x%02x", ErrInvalidRequest, t)
}
payload := make([]byte, size)
if _, err := io.ReadFull(p.r, payload); err != nil {
return nil, err
}
return payload, nil
}
// WriteRequest implements Protocol.
func (p *MessagePackProtocol) WriteRequest(id *jsonrpc.ID, method string, params any) error {
// For msgpack protocol, requests from server are "Call" type
payload, err := json.Marshal(params)
if err != nil {
return err
}
return p.writeTuple(MessageTypeCall, method, payload)
}
// WriteNotification implements Protocol.
func (p *MessagePackProtocol) WriteNotification(method string, params any) error {
// Msgpack protocol doesn't distinguish notifications from calls
return p.WriteRequest(nil, method, params)
}
// WriteResponse implements Protocol.
func (p *MessagePackProtocol) WriteResponse(id *jsonrpc.ID, result any) error {
method := ""
if id != nil {
method = id.String()
}
var payload []byte
var err error
// Check if result is raw binary (for efficient binary transport)
if raw, ok := result.(RawBinary); ok {
payload = []byte(raw)
} else {
payload, err = json.Marshal(result)
if err != nil {
return err
}
}
return p.writeTuple(MessageTypeResponse, method, payload)
}
// WriteError implements Protocol.
func (p *MessagePackProtocol) WriteError(id *jsonrpc.ID, respErr *jsonrpc.ResponseError) error {
method := ""
if id != nil {
method = id.String()
}
return p.writeTuple(MessageTypeError, method, []byte(respErr.Message))
}
func (p *MessagePackProtocol) writeTuple(msgType MessageType, method string, payload []byte) error {
// Write fixed array marker
if err := p.w.WriteByte(msgpackFixedArray3); err != nil {
return err
}
// Write message type as positive fixint (values 0-127 are written directly)
if err := p.w.WriteByte(byte(msgType)); err != nil {
return err
}
// Write method
if err := p.writeBin([]byte(method)); err != nil {
return err
}
// Write payload
if err := p.writeBin(payload); err != nil {
return err
}
return p.w.Flush()
}
func (p *MessagePackProtocol) writeBin(data []byte) error {
length := len(data)
if length < 256 {
if err := p.w.WriteByte(msgpackBin8); err != nil {
return err
}
if err := p.w.WriteByte(byte(length)); err != nil {
return err
}
} else if length < 1<<16 {
if err := p.w.WriteByte(msgpackBin16); err != nil {
return err
}
if err := binary.Write(p.w, binary.BigEndian, uint16(length)); err != nil {
return err
}
} else {
if err := p.w.WriteByte(msgpackBin32); err != nil {
return err
}
if err := binary.Write(p.w, binary.BigEndian, uint32(length)); err != nil {
return err
}
}
_, err := p.w.Write(data)
return err
}
// RawBinary is a marker type for binary data that should be written
// directly by MessagePackProtocol instead of being JSON-encoded.
type RawBinary []byte

View File

@@ -0,0 +1,124 @@
package api
import (
"context"
"fmt"
"io"
"github.com/microsoft/typescript-go/internal/bundled"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/project"
"github.com/microsoft/typescript-go/internal/vfs/osvfs"
)
// StdioServerOptions configures the STDIO-based API server.
type StdioServerOptions struct {
In io.ReadCloser
Out io.WriteCloser
Err io.Writer
Cwd string
DefaultLibraryPath string
// PipePath, if set, listens on a named pipe (Windows) or Unix domain
// socket instead of using In/Out for communication.
PipePath string
// Callbacks specifies which filesystem operations should be delegated
// to the client (e.g., "readFile", "fileExists"). Empty means no callbacks.
Callbacks []string
// Async enables JSON-RPC protocol with async connection handling.
// When false (default), uses MessagePack protocol with sync connection.
Async bool
// CollectTiming enables per-request server processing-time measurement.
// When enabled, the server accumulates each request's processing time into
// running totals and a recent-request ring buffer. Response messages are
// left unchanged; the client folds this data into its own timing snapshot
// on demand via getServerTiming / resetServerTiming requests.
CollectTiming bool
}
// StdioServer runs an API session over STDIO using MessagePack protocol.
// This is the entry point for the synchronous STDIO-based API used by
// native TypeScript tooling integration.
type StdioServer struct {
options *StdioServerOptions
}
// NewStdioServer creates a new STDIO-based API server.
func NewStdioServer(options *StdioServerOptions) *StdioServer {
if options.Cwd == "" {
panic("StdioServerOptions.Cwd is required")
}
return &StdioServer{
options: options,
}
}
// Run starts the server and blocks until the connection closes.
func (s *StdioServer) Run(ctx context.Context) error {
var transport Transport
if s.options.PipePath != "" {
t, err := NewPipeTransport(s.options.PipePath)
if err != nil {
return fmt.Errorf("failed to create pipe transport: %w", err)
}
defer t.Close()
transport = t
} else {
t := NewStdioTransport(s.options.In, s.options.Out)
defer t.Close()
transport = t
}
fs := bundled.WrapFS(osvfs.FS())
// Wrap the base FS with callbackFS if callbacks are requested
var callbackFS *callbackFS
if len(s.options.Callbacks) > 0 {
callbackFS = newCallbackFS(fs, s.options.Callbacks)
fs = callbackFS
}
projectSession := project.NewSession(&project.SessionInit{
BackgroundCtx: ctx,
Logger: nil, // TODO: Add logging support
FS: fs,
Options: &project.SessionOptions{
CurrentDirectory: s.options.Cwd,
DefaultLibraryPath: s.options.DefaultLibraryPath,
PositionEncoding: lsproto.PositionEncodingKindUTF8,
LoggingEnabled: false,
},
})
session := NewSession(projectSession, &SessionOptions{
UseBinaryResponses: !s.options.Async, // Only msgpack uses binary responses
})
defer session.Close()
// Accept connection from transport
rwc, err := transport.Accept()
if err != nil {
return fmt.Errorf("failed to accept connection: %w", err)
}
// Create protocol and connection based on async mode
var conn Conn
if s.options.Async {
protocol := NewJSONRPCProtocol(rwc)
asyncConn := NewAsyncConnWithProtocol(rwc, protocol, session)
asyncConn.SetCollectTiming(s.options.CollectTiming)
conn = asyncConn
} else {
protocol := NewMessagePackProtocol(rwc)
syncConn := NewSyncConn(rwc, protocol, session)
syncConn.SetCollectTiming(s.options.CollectTiming)
conn = syncConn
}
// If callbacks are enabled, set the connection on the FS
if callbackFS != nil {
callbackFS.SetConnection(ctx, conn)
}
return conn.Run(ctx)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,255 @@
package api
import (
"context"
"testing"
"github.com/microsoft/typescript-go/internal/bundled"
"github.com/microsoft/typescript-go/internal/lsp/lsproto"
"github.com/microsoft/typescript-go/internal/testutil/projecttestutil"
"github.com/microsoft/typescript-go/internal/tspath"
"gotest.tools/v3/assert"
)
// TestSessionTracksAndReleasesAPIRefs verifies that an API session holds at most
// one ref per opened project/file (opens are idempotent) and releases exactly
// those refs when the session is closed, so it never leaks or over-releases refs
// in the underlying (potentially shared) project session.
func TestSessionTracksAndReleasesAPIRefs(t *testing.T) {
t.Parallel()
if !bundled.Embedded {
t.Skip("bundled files are not embedded")
}
t.Run("project opens are idempotent and released on close", func(t *testing.T) {
t.Parallel()
const configFileName = "/home/projects/p/tsconfig.json"
files := map[string]any{
configFileName: `{ "compilerOptions": { "strict": true } }`,
"/home/projects/p/src/index.ts": `export const x = 1;`,
}
projectSession, _ := projecttestutil.Setup(files)
defer projectSession.Close()
session := NewSession(projectSession, nil)
_, err := session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
OpenProjects: []DocumentIdentifier{{FileName: configFileName}},
})
assert.NilError(t, err)
assert.Equal(t, session.openProjects.Len(), 1)
// Opening the same project again must not take an additional ref.
_, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
OpenProjects: []DocumentIdentifier{{FileName: configFileName}},
})
assert.NilError(t, err)
assert.Equal(t, session.openProjects.Len(), 1)
assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path(configFileName)) != nil)
// Closing the session releases the single API ref, so the project is no
// longer kept loaded.
session.Close()
assert.Equal(t, session.openProjects.Len(), 0)
assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path(configFileName)) == nil)
})
t.Run("explicit close releases the project ref", func(t *testing.T) {
t.Parallel()
const configFileName = "/home/projects/p/tsconfig.json"
files := map[string]any{
configFileName: `{ "compilerOptions": { "strict": true } }`,
"/home/projects/p/src/index.ts": `export const x = 1;`,
}
projectSession, _ := projecttestutil.Setup(files)
defer projectSession.Close()
session := NewSession(projectSession, nil)
defer session.Close()
_, err := session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
OpenProjects: []DocumentIdentifier{{FileName: configFileName}},
})
assert.NilError(t, err)
assert.Equal(t, session.openProjects.Len(), 1)
assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path(configFileName)) != nil)
// Closing a project we hold releases the ref and unloads the project.
_, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
CloseProjects: []DocumentIdentifier{{FileName: configFileName}},
})
assert.NilError(t, err)
assert.Equal(t, session.openProjects.Len(), 0)
assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path(configFileName)) == nil)
// Closing a project we don't hold is a no-op (never over-releases).
_, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
CloseProjects: []DocumentIdentifier{{FileName: configFileName}},
})
assert.NilError(t, err)
assert.Equal(t, session.openProjects.Len(), 0)
})
t.Run("file opens are idempotent and released on close", func(t *testing.T) {
t.Parallel()
const fileName = "/home/projects/p/src/index.ts"
files := map[string]any{
"/home/projects/p/tsconfig.json": `{ "compilerOptions": { "strict": true } }`,
fileName: `export const x = 1;`,
}
projectSession, _ := projecttestutil.Setup(files)
defer projectSession.Close()
session := NewSession(projectSession, nil)
_, err := session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
OpenFiles: []DocumentIdentifier{{FileName: fileName}},
})
assert.NilError(t, err)
assert.Equal(t, session.openFiles.Len(), 1)
// Re-opening the same file must not take an additional ref.
_, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
OpenFiles: []DocumentIdentifier{{FileName: fileName}},
})
assert.NilError(t, err)
assert.Equal(t, session.openFiles.Len(), 1)
// The file should resolve to the configured project via ancestor search.
assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path("/home/projects/p/tsconfig.json")) != nil)
// Closing a file we don't hold is a no-op (never over-releases).
_, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
CloseFiles: []DocumentIdentifier{{FileName: "/home/projects/p/other.ts"}},
})
assert.NilError(t, err)
assert.Equal(t, session.openFiles.Len(), 1)
// Explicitly closing the held file releases the ref.
_, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
CloseFiles: []DocumentIdentifier{{FileName: fileName}},
})
assert.NilError(t, err)
assert.Equal(t, session.openFiles.Len(), 0)
// Closing the file also tears down the configured project that was
// auto-loaded to serve it, instead of leaking it.
assert.Assert(t,
projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path("/home/projects/p/tsconfig.json")) == nil,
"configured project auto-loaded for the API-opened file should be unloaded after close",
)
session.Close()
assert.Equal(t, session.openFiles.Len(), 0)
})
t.Run("relative file paths normalize consistently for open and close", func(t *testing.T) {
t.Parallel()
// The project session's current directory is "/", so a relative path
// resolves to the corresponding absolute path.
files := map[string]any{
"/src/tsconfig.json": `{ "compilerOptions": { "strict": true } }`,
"/src/index.ts": `export const x = 1;`,
}
projectSession, _ := projecttestutil.Setup(files)
defer projectSession.Close()
session := NewSession(projectSession, nil)
defer session.Close()
// Open via a relative path; it should be tracked under the absolute path
// and resolve to the containing configured project.
openResp, err := session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
OpenFiles: []DocumentIdentifier{{FileName: "src/index.ts"}},
})
assert.NilError(t, err)
assert.Equal(t, session.openFiles.Len(), 1)
assert.Assert(t, session.openFiles.Has(tspath.Path("/src/index.ts")))
assert.Assert(t, projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path("/src/tsconfig.json")) != nil)
// getDefaultProjectForFile must also resolve a relative path to the same
// configured project (it builds a URI from the identifier internally).
proj, err := session.handleGetDefaultProjectForFile(context.Background(), &GetDefaultProjectForFileParams{
Snapshot: openResp.Snapshot,
File: DocumentIdentifier{FileName: "src/index.ts"},
})
assert.NilError(t, err)
assert.Assert(t, proj != nil, "relative path should resolve to a default project")
assert.Equal(t, proj.ConfigFileName, "/src/tsconfig.json")
// Re-opening via the absolute path must match the relative open (no new ref).
_, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
OpenFiles: []DocumentIdentifier{{FileName: "/src/index.ts"}},
})
assert.NilError(t, err)
assert.Equal(t, session.openFiles.Len(), 1)
// Closing via a relative path must match the path stored when opening.
_, err = session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
CloseFiles: []DocumentIdentifier{{FileName: "src/index.ts"}},
})
assert.NilError(t, err)
assert.Equal(t, session.openFiles.Len(), 0)
assert.Assert(t,
projectSession.Snapshot().ProjectCollection.ConfiguredProject(tspath.Path("/src/tsconfig.json")) == nil,
"configured project should be unloaded after closing the relatively-pathed file",
)
})
}
// TestUpdateSnapshotResponseSkipsUnloadedAncestorProject verifies that API
// updateSnapshot does not report unloaded ancestor project placeholders. This
// covers the case where opening a file loads its nearest configured project
// while solution search discovers an ancestor tsconfig placeholder whose command
// line is still nil.
func TestUpdateSnapshotResponseSkipsUnloadedAncestorProject(t *testing.T) {
t.Parallel()
if !bundled.Embedded {
t.Skip("bundled files are not embedded")
}
const (
nestedConfigFileName = "/repo/packages/app/tsconfig.json"
ancestorConfigFileName = "/repo/packages/tsconfig.json"
fileName = "/repo/packages/app/src/index.ts"
)
files := map[string]any{
ancestorConfigFileName: `{ "files": [] }`,
nestedConfigFileName: `{
"compilerOptions": { "composite": true },
"include": ["**/*"]
}`,
fileName: `let s: string = 1234;`,
}
projectSession, _ := projecttestutil.Setup(files)
defer projectSession.Close()
projectSession.DidOpenFile(context.Background(), lsproto.DocumentUri("file://"+fileName), 1, files[fileName].(string), lsproto.LanguageKindTypeScript)
snapshot := projectSession.Snapshot()
nestedProject := snapshot.ProjectCollection.ConfiguredProject(tspath.Path(nestedConfigFileName))
assert.Assert(t, nestedProject != nil)
assert.Assert(t, nestedProject.CommandLine != nil)
ancestorProject := snapshot.ProjectCollection.ConfiguredProject(tspath.Path(ancestorConfigFileName))
assert.Assert(t, ancestorProject != nil)
assert.Assert(t, ancestorProject.CommandLine == nil)
session := NewSession(projectSession, nil)
defer session.Close()
response, err := session.handleUpdateSnapshot(context.Background(), &UpdateSnapshotParams{
OpenProjects: []DocumentIdentifier{{FileName: nestedConfigFileName}},
})
assert.NilError(t, err)
var foundNestedProject bool
var foundAncestorProject bool
for _, project := range response.Projects {
switch project.ConfigFileName {
case nestedConfigFileName:
foundNestedProject = true
assert.Assert(t, project.RootFiles != nil)
assert.Assert(t, project.CompilerOptions != nil)
case ancestorConfigFileName:
foundAncestorProject = true
}
}
assert.Assert(t, foundNestedProject)
assert.Assert(t, !foundAncestorProject)
}

View File

@@ -0,0 +1,141 @@
package api
import (
"context"
"testing"
"github.com/microsoft/typescript-go/internal/bundled"
"github.com/microsoft/typescript-go/internal/testutil/projecttestutil"
"gotest.tools/v3/assert"
)
// TestCompletionSymbolTypeIsResolvable reproduces a crash where requesting the
// type of a completion-provided symbol panicked with a nil pointer dereference.
//
// Completion ran on an ephemeral query checker (default lifetime), so members of
// a generic type such as `string[]` (= Array<string>) were returned as
// *instantiated* symbols whose per-checker instantiation links live only on that
// query checker. GetTypeOfSymbol runs on the persistent API checker — a
// different instance — where those links are absent, so getTypeOfInstantiatedSymbol
// dereferenced a nil target and brought down the connection.
//
// The fix pins symbol-producing completion to the API checker, so the returned
// handles resolve on the same checker the client re-queries.
func TestCompletionSymbolTypeIsResolvable(t *testing.T) {
t.Parallel()
if !bundled.Embedded {
t.Skip("bundled files are not embedded")
}
const fileName = "/home/projects/p/src/index.ts"
// The caret sits right after `people.`, requesting members of `string[]`.
const content = "declare const people: string[];\npeople."
files := map[string]any{
"/home/projects/p/tsconfig.json": `{ "compilerOptions": { "strict": true } }`,
fileName: content,
}
projectSession, _ := projecttestutil.Setup(files)
defer projectSession.Close()
session := NewSession(projectSession, nil)
defer session.Close()
ctx := context.Background()
snapshotResp, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{
OpenFiles: []DocumentIdentifier{{FileName: fileName}},
})
assert.NilError(t, err)
proj, err := session.handleGetDefaultProjectForFile(ctx, &GetDefaultProjectForFileParams{
Snapshot: snapshotResp.Snapshot,
File: DocumentIdentifier{FileName: fileName},
})
assert.NilError(t, err)
assert.Assert(t, proj != nil, "file should resolve to a default project")
// content is pure ASCII, so the UTF-16 caret offset equals the byte length.
completions, err := session.handleGetCompletionsAtPosition(ctx, &GetCompletionsAtPositionParams{
Snapshot: snapshotResp.Snapshot,
Project: proj.Id,
File: DocumentIdentifier{FileName: fileName},
Position: uint32(len(content)),
IncludeSymbol: true,
})
assert.NilError(t, err)
assert.Assert(t, completions != nil, "expected a completion list for array members")
// Resolving the type of every completion symbol must not panic, and known
// members like `push` must produce a concrete type.
var sawSymbol, sawPush bool
for _, entry := range completions.Entries {
if entry.Symbol == nil {
continue
}
sawSymbol = true
typeResp, err := session.handleGetTypeOfSymbol(ctx, &GetTypeOfSymbolParams{
Snapshot: snapshotResp.Snapshot,
Project: proj.Id,
Symbol: entry.Symbol.Id,
})
assert.NilError(t, err)
assert.Assert(t, typeResp != nil, "type of completion symbol %q should resolve", entry.Name)
if entry.Name == "push" {
sawPush = true
}
}
assert.Assert(t, sawSymbol, "completion entries should include resolvable symbols")
assert.Assert(t, sawPush, "array member completions should include `push`")
}
// TestCompletionOnInferredProject reproduces a crash where requesting completions
// for a loose file — one not part of any tsconfig.json, so it resolves to an
// inferred project — panicked with "ConfigFilePath called on non-configured
// project".
//
// setupLanguageService called Project.ConfigFilePath(), which is only valid for
// configured projects and panics for inferred ones. The fix uses Project.ID(),
// which returns the project's path for both configured and inferred projects without panicking.
func TestCompletionOnInferredProject(t *testing.T) {
t.Parallel()
if !bundled.Embedded {
t.Skip("bundled files are not embedded")
}
// No tsconfig.json anywhere, so this file belongs to an inferred project.
const fileName = "/home/projects/p/src/index.ts"
const content = "declare const people: string[];\npeople."
files := map[string]any{
fileName: content,
}
projectSession, _ := projecttestutil.Setup(files)
defer projectSession.Close()
session := NewSession(projectSession, nil)
defer session.Close()
ctx := context.Background()
snapshotResp, err := session.handleUpdateSnapshot(ctx, &UpdateSnapshotParams{
OpenFiles: []DocumentIdentifier{{FileName: fileName}},
})
assert.NilError(t, err)
proj, err := session.handleGetDefaultProjectForFile(ctx, &GetDefaultProjectForFileParams{
Snapshot: snapshotResp.Snapshot,
File: DocumentIdentifier{FileName: fileName},
})
assert.NilError(t, err)
assert.Assert(t, proj != nil, "file should resolve to an inferred default project")
// This request previously panicked in setupLanguageService.
// content is pure ASCII, so the UTF-16 caret offset equals the byte length.
completions, err := session.handleGetCompletionsAtPosition(ctx, &GetCompletionsAtPositionParams{
Snapshot: snapshotResp.Snapshot,
Project: proj.Id,
File: DocumentIdentifier{FileName: fileName},
Position: uint32(len(content)),
})
assert.NilError(t, err)
assert.Assert(t, completions != nil, "expected a completion list for array members")
}

View File

@@ -0,0 +1,30 @@
// Code generated by "stringer -type=MessageType -output=stringer_generated.go"; DO NOT EDIT.
package api
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[MessageTypeUnknown-0]
_ = x[MessageTypeRequest-1]
_ = x[MessageTypeCallResponse-2]
_ = x[MessageTypeCallError-3]
_ = x[MessageTypeResponse-4]
_ = x[MessageTypeError-5]
_ = x[MessageTypeCall-6]
}
const _MessageType_name = "MessageTypeUnknownMessageTypeRequestMessageTypeCallResponseMessageTypeCallErrorMessageTypeResponseMessageTypeErrorMessageTypeCall"
var _MessageType_index = [...]uint8{0, 18, 36, 59, 79, 98, 114, 129}
func (i MessageType) String() string {
idx := int(i) - 0
if i < 0 || idx >= len(_MessageType_index)-1 {
return "MessageType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _MessageType_name[_MessageType_index[idx]:_MessageType_index[idx+1]]
}

View File

@@ -0,0 +1,136 @@
package api
import (
"sync"
"time"
)
// serverRecentRequestCapacity is the number of most-recent requests retained in
// the server-side timing ring buffer.
const serverRecentRequestCapacity = 5
// serverRequestTiming is a single server-side request's processing-time sample.
type serverRequestTiming struct {
// Method is the API method that was handled.
Method string `json:"method"`
// ProcessingTimeMs is the wall-clock time the server spent handling the
// request, in milliseconds.
ProcessingTimeMs float64 `json:"processingTimeMs"`
// Timestamp is the Unix time in milliseconds when the request completed.
Timestamp int64 `json:"timestamp"`
}
// serverTimingTotals holds running totals accumulated across every handled request.
type serverTimingTotals struct {
// RequestCount is the total number of requests measured.
RequestCount uint64 `json:"requestCount"`
// TotalProcessingTimeMs is the sum of server processing time, in milliseconds.
TotalProcessingTimeMs float64 `json:"totalProcessingTimeMs"`
}
// serverTimingInfo is a point-in-time snapshot of collected server timing,
// returned to clients in response to a getServerTiming request.
type serverTimingInfo struct {
// Enabled reports whether server-side timing collection is active.
Enabled bool `json:"enabled"`
// Totals are the running totals across every handled request.
Totals serverTimingTotals `json:"totals"`
// RecentRequests are the most recent requests, oldest to newest, up to
// serverRecentRequestCapacity.
RecentRequests []serverRequestTiming `json:"recentRequests"`
}
// timingCollector accumulates per-request server processing times into running
// totals and a fixed-size ring buffer of the most recent requests. It is safe
// for concurrent use so the async connection can record from multiple request
// goroutines.
type timingCollector struct {
mu sync.Mutex
totals serverTimingTotals
// ring holds up to serverRecentRequestCapacity entries; once full, head
// marks the oldest entry.
ring []serverRequestTiming
head int
}
func newTimingCollector() *timingCollector {
return &timingCollector{}
}
// record adds a single request's processing time to the totals and ring buffer.
func (c *timingCollector) record(method string, d time.Duration) {
processingMs := durationToMillis(d)
c.mu.Lock()
defer c.mu.Unlock()
c.totals.RequestCount++
c.totals.TotalProcessingTimeMs += processingMs
entry := serverRequestTiming{
Method: method,
ProcessingTimeMs: processingMs,
Timestamp: time.Now().UnixMilli(),
}
if len(c.ring) < serverRecentRequestCapacity {
c.ring = append(c.ring, entry)
} else {
c.ring[c.head] = entry
c.head = (c.head + 1) % serverRecentRequestCapacity
}
}
// snapshot returns a copy of the currently collected timing information, with
// recent requests ordered from oldest to newest.
func (c *timingCollector) snapshot() serverTimingInfo {
c.mu.Lock()
defer c.mu.Unlock()
recent := make([]serverRequestTiming, 0, len(c.ring))
for i := range c.ring {
recent = append(recent, c.ring[(c.head+i)%len(c.ring)])
}
return serverTimingInfo{
Enabled: true,
Totals: c.totals,
RecentRequests: recent,
}
}
// reset clears all accumulated totals and recent-request history.
func (c *timingCollector) reset() {
c.mu.Lock()
defer c.mu.Unlock()
c.totals = serverTimingTotals{}
c.ring = nil
c.head = 0
}
// serverTimingSnapshot returns the collector's snapshot, or a disabled snapshot
// when timing collection is not enabled (collector is nil).
func serverTimingSnapshot(c *timingCollector) serverTimingInfo {
if c == nil {
return disabledServerTimingInfo()
}
return c.snapshot()
}
// disabledServerTimingInfo is the snapshot returned when timing collection is
// not enabled.
func disabledServerTimingInfo() serverTimingInfo {
return serverTimingInfo{
Enabled: false,
RecentRequests: []serverRequestTiming{},
}
}
// durationToMillis converts a duration to fractional milliseconds, clamped to be
// non-negative. It preserves sub-microsecond precision by converting from the
// full nanosecond duration.
func durationToMillis(d time.Duration) float64 {
if d < 0 {
return 0
}
return float64(d) / float64(time.Millisecond)
}

View File

@@ -0,0 +1,95 @@
package api
import (
"testing"
"time"
"gotest.tools/v3/assert"
)
func TestTimingCollector(t *testing.T) {
t.Parallel()
t.Run("accumulates totals and records recent requests", func(t *testing.T) {
t.Parallel()
c := newTimingCollector()
c.record("getSourceFile", 2*time.Millisecond)
c.record("getSymbolAtPosition", 500*time.Microsecond)
snap := c.snapshot()
assert.Equal(t, snap.Enabled, true)
assert.Equal(t, snap.Totals.RequestCount, uint64(2))
assert.Equal(t, snap.Totals.TotalProcessingTimeMs, 2.5)
assert.Equal(t, len(snap.RecentRequests), 2)
assert.Equal(t, snap.RecentRequests[0].Method, "getSourceFile")
assert.Equal(t, snap.RecentRequests[0].ProcessingTimeMs, 2.0)
assert.Equal(t, snap.RecentRequests[1].Method, "getSymbolAtPosition")
assert.Equal(t, snap.RecentRequests[1].ProcessingTimeMs, 0.5)
})
t.Run("ring buffer retains only the most recent requests, oldest to newest", func(t *testing.T) {
t.Parallel()
c := newTimingCollector()
methods := []string{"a", "b", "c", "d", "e", "f", "g"}
for _, m := range methods {
c.record(m, time.Millisecond)
}
snap := c.snapshot()
assert.Equal(t, snap.Totals.RequestCount, uint64(7))
assert.Equal(t, len(snap.RecentRequests), serverRecentRequestCapacity)
// Expect the last 5 methods, oldest to newest.
want := methods[len(methods)-serverRecentRequestCapacity:]
for i, w := range want {
assert.Equal(t, snap.RecentRequests[i].Method, w)
}
})
t.Run("negative durations clamp to zero", func(t *testing.T) {
t.Parallel()
c := newTimingCollector()
c.record("x", -5*time.Second)
snap := c.snapshot()
assert.Equal(t, snap.Totals.TotalProcessingTimeMs, 0.0)
assert.Equal(t, snap.RecentRequests[0].ProcessingTimeMs, 0.0)
})
}
func TestServerTimingSnapshotDisabled(t *testing.T) {
t.Parallel()
snap := serverTimingSnapshot(nil)
assert.Equal(t, snap.Enabled, false)
assert.Equal(t, snap.Totals.RequestCount, uint64(0))
assert.Equal(t, len(snap.RecentRequests), 0)
}
func TestTimingCollectorReset(t *testing.T) {
t.Parallel()
c := newTimingCollector()
c.record("a", time.Millisecond)
c.record("b", time.Millisecond)
c.reset()
snap := c.snapshot()
assert.Equal(t, snap.Enabled, true)
assert.Equal(t, snap.Totals.RequestCount, uint64(0))
assert.Equal(t, snap.Totals.TotalProcessingTimeMs, 0.0)
assert.Equal(t, len(snap.RecentRequests), 0)
// The collector remains usable after a reset.
c.record("c", 2*time.Millisecond)
snap = c.snapshot()
assert.Equal(t, snap.Totals.RequestCount, uint64(1))
assert.Equal(t, snap.RecentRequests[0].Method, "c")
}
func TestDurationToMillis(t *testing.T) {
t.Parallel()
assert.Equal(t, durationToMillis(1500*time.Microsecond), 1.5)
assert.Equal(t, durationToMillis(0), 0.0)
assert.Equal(t, durationToMillis(-5*time.Second), 0.0)
// Sub-microsecond durations retain precision rather than truncating to 0.
assert.Equal(t, durationToMillis(500*time.Nanosecond), 0.0005)
assert.Equal(t, durationToMillis(1234*time.Nanosecond), 0.001234)
}

View File

@@ -0,0 +1,95 @@
package api
import (
"io"
"net"
)
// Transport is an interface for accepting connections from API clients.
type Transport interface {
// Accept waits for and returns the next connection.
Accept() (io.ReadWriteCloser, error)
// Close stops the transport from accepting new connections.
Close() error
}
// PipeTransport accepts connections on a Unix domain socket or Windows named pipe.
type PipeTransport struct {
listener net.Listener
}
// NewPipeTransport creates a new transport listening on the given path.
// On Unix, this creates a Unix domain socket. On Windows, this creates a named pipe.
func NewPipeTransport(path string) (*PipeTransport, error) {
listener, err := newPipeListener(path)
if err != nil {
return nil, err
}
return &PipeTransport{listener: listener}, nil
}
// Accept implements Transport.
func (t *PipeTransport) Accept() (io.ReadWriteCloser, error) {
return t.listener.Accept()
}
// Close implements Transport.
func (t *PipeTransport) Close() error {
return t.listener.Close()
}
// Path returns the path of the pipe/socket.
func (t *PipeTransport) Path() string {
return t.listener.Addr().String()
}
// StdioTransport wraps stdin/stdout as a single connection transport.
// It only accepts one connection.
type StdioTransport struct {
stdin io.ReadCloser
stdout io.WriteCloser
used bool
}
// NewStdioTransport creates a transport using the given stdin/stdout.
func NewStdioTransport(stdin io.ReadCloser, stdout io.WriteCloser) *StdioTransport {
return &StdioTransport{
stdin: stdin,
stdout: stdout,
}
}
// Accept implements Transport.
func (t *StdioTransport) Accept() (io.ReadWriteCloser, error) {
if t.used {
return nil, io.EOF
}
t.used = true
return &stdioConn{
Reader: t.stdin,
Writer: t.stdout,
stdin: t.stdin,
stdout: t.stdout,
}, nil
}
// Close implements Transport.
func (t *StdioTransport) Close() error {
return nil
}
type stdioConn struct {
io.Reader
io.Writer
stdin io.ReadCloser
stdout io.WriteCloser
}
func (c *stdioConn) Close() error {
err1 := c.stdin.Close()
err2 := c.stdout.Close()
if err1 != nil {
return err1
}
return err2
}

View File

@@ -0,0 +1,22 @@
//go:build !windows
package api
import (
"net"
"os"
"path"
)
// newPipeListener creates a Unix domain socket listener.
func newPipeListener(path string) (net.Listener, error) {
// Remove any existing socket file
_ = os.Remove(path) //nolint:forbidigo
return net.Listen("unix", path)
}
// GeneratePipePath returns a platform-appropriate pipe path for the given name.
func GeneratePipePath(name string) string {
//nolint:forbidigo
return path.Join(os.TempDir(), name)
}

View File

@@ -0,0 +1,19 @@
//go:build windows
package api
import (
"net"
"github.com/Microsoft/go-winio"
)
// newPipeListener creates a Windows named pipe listener.
func newPipeListener(path string) (net.Listener, error) {
return winio.ListenPipe(path, nil)
}
// GeneratePipePath returns a platform-appropriate pipe path for the given name.
func GeneratePipePath(name string) string {
return `\\.\pipe\` + name
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,36 @@
package ast
// CheckFlags
type CheckFlags uint32
const (
CheckFlagsNone CheckFlags = 0
CheckFlagsInstantiated CheckFlags = 1 << 0 // Instantiated symbol
CheckFlagsSyntheticProperty CheckFlags = 1 << 1 // Property in union or intersection type
CheckFlagsSyntheticMethod CheckFlags = 1 << 2 // Method in union or intersection type
CheckFlagsReadonly CheckFlags = 1 << 3 // Readonly transient symbol
CheckFlagsReadPartial CheckFlags = 1 << 4 // Synthetic property present in some but not all constituents
CheckFlagsWritePartial CheckFlags = 1 << 5 // Synthetic property present in some but only satisfied by an index signature in others
CheckFlagsHasNonUniformType CheckFlags = 1 << 6 // Synthetic property with non-uniform type in constituents
CheckFlagsHasLiteralType CheckFlags = 1 << 7 // Synthetic property with at least one literal type in constituents
CheckFlagsContainsPublic CheckFlags = 1 << 8 // Synthetic property with public constituent(s)
CheckFlagsContainsProtected CheckFlags = 1 << 9 // Synthetic property with protected constituent(s)
CheckFlagsContainsPrivate CheckFlags = 1 << 10 // Synthetic property with private constituent(s)
CheckFlagsContainsStatic CheckFlags = 1 << 11 // Synthetic property with static constituent(s)
CheckFlagsLate CheckFlags = 1 << 12 // Late-bound symbol for a computed property with a dynamic name
CheckFlagsReverseMapped CheckFlags = 1 << 13 // Property of reverse-inferred homomorphic mapped type
CheckFlagsOptionalParameter CheckFlags = 1 << 14 // Optional parameter
CheckFlagsRestParameter CheckFlags = 1 << 15 // Rest parameter
CheckFlagsDeferredType CheckFlags = 1 << 16 // Calculation of the type of this symbol is deferred due to processing costs, should be fetched with `getTypeOfSymbolWithDeferredType`
CheckFlagsHasNeverType CheckFlags = 1 << 17 // Synthetic property with at least one never type in constituents
CheckFlagsMapped CheckFlags = 1 << 18 // Property of mapped type
CheckFlagsStripOptional CheckFlags = 1 << 19 // Strip optionality in mapped property
CheckFlagsUnresolved CheckFlags = 1 << 20 // Unresolved type alias symbol
CheckFlagsIsDiscriminantComputed CheckFlags = 1 << 21 // IsDiscriminant flags has been computed
CheckFlagsIsDiscriminant CheckFlags = 1 << 22 // Discriminant property
CheckFlagsIndexSymbol CheckFlags = 1 << 23 // Synthetic property created from index signature
CheckFlagsSynthetic = CheckFlagsSyntheticProperty | CheckFlagsSyntheticMethod
CheckFlagsNonUniformAndLiteral = CheckFlagsHasNonUniformType | CheckFlagsHasLiteralType
CheckFlagsPartial = CheckFlagsReadPartial | CheckFlagsWritePartial
)

View File

@@ -0,0 +1,86 @@
package ast
import "github.com/microsoft/typescript-go/internal/core"
// Ideally, this would get cached on the node factory so there's only ever one set of closures made per factory
func getDeepCloneVisitor(f *NodeFactory, syntheticLocation bool) *NodeVisitor {
var visitor *NodeVisitor
visitor = NewNodeVisitor(
func(node *Node) *Node {
visited := visitor.VisitEachChild(node)
if visited != node {
if syntheticLocation {
visited.Loc = core.NewTextRange(-1, -1)
}
return visited
}
c := node.Clone(f) // forcibly clone leaf nodes, which will then cascade new nodes/arrays upwards via `update` calls
// In strada, `factory.cloneNode` was dynamic and did _not_ clone positions for any "special cases", meanwhile
// Node.Clone in corsa reliably uses `Update` calls for all nodes and so copies locations by default.
// Deep clones are done to copy a node across files, so here, we explicitly make the location range synthetic on all cloned nodes
if syntheticLocation {
c.Loc = core.NewTextRange(-1, -1)
}
return c
},
f,
NodeVisitorHooks{
VisitNodes: func(nodes *NodeList, v *NodeVisitor) *NodeList {
if nodes == nil {
return nil
}
visited := v.VisitNodes(nodes)
var newList *NodeList
if visited != nodes {
newList = visited
} else {
newList = nodes.Clone(v.Factory)
}
if syntheticLocation {
newList.Loc = core.NewTextRange(-1, -1)
if nodes.HasTrailingComma() {
newList.Nodes[len(newList.Nodes)-1].Loc = core.NewTextRange(-2, -2)
}
}
return newList
},
VisitModifiers: func(nodes *ModifierList, v *NodeVisitor) *ModifierList {
if nodes == nil {
return nil
}
visited := v.VisitModifiers(nodes)
var newList *ModifierList
if visited != nodes {
newList = visited
} else {
newList = nodes.Clone(v.Factory)
}
if syntheticLocation {
newList.Loc = core.NewTextRange(-1, -1)
if nodes.HasTrailingComma() {
newList.Nodes[len(newList.Nodes)-1].Loc = core.NewTextRange(-2, -2)
}
}
return newList
},
},
)
return visitor
}
func (f *NodeFactory) DeepCloneNode(node *Node) *Node {
return getDeepCloneVisitor(f, true /*syntheticLocation*/).VisitNode(node)
}
func (f *NodeFactory) DeepCloneReparse(node *Node) *Node {
if node != nil {
node = getDeepCloneVisitor(f, false /*syntheticLocation*/).VisitNode(node)
SetParentInChildren(node)
node.Flags |= NodeFlagsReparsed
}
return node
}
func (f *NodeFactory) DeepCloneReparseModifiers(modifiers *ModifierList) *ModifierList {
return getDeepCloneVisitor(f, false /*syntheticLocation*/).VisitModifiers(modifiers)
}

View File

@@ -0,0 +1,599 @@
package ast_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/testutil/parsetestutil"
"gotest.tools/v3/assert"
)
type NodeComparisonWorkItem struct {
original *ast.Node
copy *ast.Node
}
func getChildren(node *ast.Node) []*ast.Node {
children := []*ast.Node{}
node.VisitEachChild(ast.NewNodeVisitor(func(node *ast.Node) *ast.Node {
children = append(children, node)
return node
}, nil, ast.NodeVisitorHooks{}))
return children
}
func TestDeepCloneNodeSanityCheck(t *testing.T) {
t.Parallel()
data := []struct {
title string
input string
jsx bool
}{
{title: "StringLiteral#1", input: `;"test"`},
{title: "StringLiteral#2", input: `;'test'`},
{title: "NumericLiteral", input: `0`},
{title: "BigIntLiteral", input: `0n`},
{title: "BooleanLiteral#1", input: `true`},
{title: "BooleanLiteral#2", input: `false`},
{title: "NoSubstitutionTemplateLiteral", input: "``"},
{title: "RegularExpressionLiteral#1", input: `/a/`},
{title: "RegularExpressionLiteral#2", input: `/a/g`},
{title: "NullLiteral", input: `null`},
{title: "ThisExpression", input: `this`},
{title: "SuperExpression", input: `super()`},
{title: "ImportExpression", input: `import()`},
{title: "PropertyAccess#1", input: `a.b`},
{title: "PropertyAccess#2", input: `a.#b`},
{title: "PropertyAccess#3", input: `a?.b`},
{title: "PropertyAccess#4", input: `a?.b.c`},
{title: "PropertyAccess#5", input: `1..b`},
{title: "PropertyAccess#6", input: `1.0.b`},
{title: "PropertyAccess#7", input: `0x1.b`},
{title: "PropertyAccess#8", input: `0b1.b`},
{title: "PropertyAccess#9", input: `0o1.b`},
{title: "PropertyAccess#10", input: `10e1.b`},
{title: "PropertyAccess#11", input: `10E1.b`},
{title: "ElementAccess#1", input: `a[b]`},
{title: "ElementAccess#2", input: `a?.[b]`},
{title: "ElementAccess#3", input: `a?.[b].c`},
{title: "CallExpression#1", input: `a()`},
{title: "CallExpression#2", input: `a<T>()`},
{title: "CallExpression#3", input: `a(b)`},
{title: "CallExpression#4", input: `a<T>(b)`},
{title: "CallExpression#5", input: `a(b).c`},
{title: "CallExpression#6", input: `a<T>(b).c`},
{title: "CallExpression#7", input: `a?.(b)`},
{title: "CallExpression#8", input: `a?.<T>(b)`},
{title: "CallExpression#9", input: `a?.(b).c`},
{title: "CallExpression#10", input: `a?.<T>(b).c`},
{title: "CallExpression#11", input: `a<T, U>()`},
{title: "CallExpression#12", input: `a<T,>()`},
{title: "NewExpression#1", input: `new a`},
{title: "NewExpression#2", input: `new a.b`},
{title: "NewExpression#3", input: `new a()`},
{title: "NewExpression#4", input: `new a.b()`},
{title: "NewExpression#5", input: `new a<T>()`},
{title: "NewExpression#6", input: `new a.b<T>()`},
{title: "NewExpression#7", input: `new a(b)`},
{title: "NewExpression#8", input: `new a.b(c)`},
{title: "NewExpression#9", input: `new a<T>(b)`},
{title: "NewExpression#10", input: `new a.b<T>(c)`},
{title: "NewExpression#11", input: `new a(b).c`},
{title: "NewExpression#12", input: `new a<T>(b).c`},
{title: "TaggedTemplateExpression#1", input: "tag``"},
{title: "TaggedTemplateExpression#2", input: "tag<T>``"},
{title: "TypeAssertionExpression#1", input: `<T>a`},
{title: "FunctionExpression#1", input: `(function(){})`},
{title: "FunctionExpression#2", input: `(function f(){})`},
{title: "FunctionExpression#3", input: `(function*f(){})`},
{title: "FunctionExpression#4", input: `(async function f(){})`},
{title: "FunctionExpression#5", input: `(async function*f(){})`},
{title: "FunctionExpression#6", input: `(function<T>(){})`},
{title: "FunctionExpression#7", input: `(function(a){})`},
{title: "FunctionExpression#8", input: `(function():T{})`},
{title: "ArrowFunction#1", input: `a=>{}`},
{title: "ArrowFunction#2", input: `()=>{}`},
{title: "ArrowFunction#3", input: `(a)=>{}`},
{title: "ArrowFunction#4", input: `<T>(a)=>{}`},
{title: "ArrowFunction#5", input: `async a=>{}`},
{title: "ArrowFunction#6", input: `async()=>{}`},
{title: "ArrowFunction#7", input: `async<T>()=>{}`},
{title: "ArrowFunction#8", input: `():T=>{}`},
{title: "ArrowFunction#9", input: `()=>a`},
{title: "DeleteExpression", input: `delete a`},
{title: "TypeOfExpression", input: `typeof a`},
{title: "VoidExpression", input: `void a`},
{title: "AwaitExpression", input: `await a`},
{title: "PrefixUnaryExpression#1", input: `+a`},
{title: "PrefixUnaryExpression#2", input: `++a`},
{title: "PrefixUnaryExpression#3", input: `+ +a`},
{title: "PrefixUnaryExpression#4", input: `+ ++a`},
{title: "PrefixUnaryExpression#5", input: `-a`},
{title: "PrefixUnaryExpression#6", input: `--a`},
{title: "PrefixUnaryExpression#7", input: `- -a`},
{title: "PrefixUnaryExpression#8", input: `- --a`},
{title: "PrefixUnaryExpression#9", input: `+-a`},
{title: "PrefixUnaryExpression#10", input: `+--a`},
{title: "PrefixUnaryExpression#11", input: `-+a`},
{title: "PrefixUnaryExpression#12", input: `-++a`},
{title: "PrefixUnaryExpression#13", input: `~a`},
{title: "PrefixUnaryExpression#14", input: `!a`},
{title: "PostfixUnaryExpression#1", input: `a++`},
{title: "PostfixUnaryExpression#2", input: `a--`},
{title: "BinaryExpression#1", input: `a,b`},
{title: "BinaryExpression#2", input: `a+b`},
{title: "BinaryExpression#3", input: `a**b`},
{title: "BinaryExpression#4", input: `a instanceof b`},
{title: "BinaryExpression#5", input: `a in b`},
{title: "ConditionalExpression", input: `a?b:c`},
{title: "TemplateExpression#1", input: "`a${b}c`"},
{title: "TemplateExpression#2", input: "`a${b}c${d}e`"},
{title: "YieldExpression#1", input: `(function*() { yield })`},
{title: "YieldExpression#2", input: `(function*() { yield a })`},
{title: "YieldExpression#3", input: `(function*() { yield*a })`},
{title: "SpreadElement", input: `[...a]`},
{title: "ClassExpression#1", input: `(class {})`},
{title: "ClassExpression#2", input: `(class a {})`},
{title: "ClassExpression#3", input: `(class<T>{})`},
{title: "ClassExpression#4", input: `(class a<T>{})`},
{title: "ClassExpression#5", input: `(class extends b {})`},
{title: "ClassExpression#6", input: `(class a extends b {})`},
{title: "ClassExpression#7", input: `(class implements b {})`},
{title: "ClassExpression#8", input: `(class a implements b {})`},
{title: "ClassExpression#9", input: `(class implements b, c {})`},
{title: "ClassExpression#10", input: `(class a implements b, c {})`},
{title: "ClassExpression#11", input: `(class extends b implements c, d {})`},
{title: "ClassExpression#12", input: `(class a extends b implements c, d {})`},
{title: "ClassExpression#13", input: `(@a class {})`},
{title: "OmittedExpression", input: `[,]`},
{title: "ExpressionWithTypeArguments", input: `a<T>`},
{title: "AsExpression", input: `a as T`},
{title: "SatisfiesExpression", input: `a satisfies T`},
{title: "NonNullExpression", input: `a!`},
{title: "MetaProperty#1", input: `new.target`},
{title: "MetaProperty#2", input: `import.meta`},
{title: "ArrayLiteralExpression#1", input: `[]`},
{title: "ArrayLiteralExpression#2", input: `[a]`},
{title: "ArrayLiteralExpression#3", input: `[a,]`},
{title: "ArrayLiteralExpression#4", input: `[,a]`},
{title: "ArrayLiteralExpression#5", input: `[...a]`},
{title: "ObjectLiteralExpression#1", input: `({})`},
{title: "ObjectLiteralExpression#2", input: `({a,})`},
{title: "ShorthandPropertyAssignment", input: `({a})`},
{title: "PropertyAssignment", input: `({a:b})`},
{title: "SpreadAssignment", input: `({...a})`},
{title: "Block", input: `{}`},
{title: "VariableStatement#1", input: `var a`},
{title: "VariableStatement#2", input: `let a`},
{title: "VariableStatement#3", input: `const a = b`},
{title: "VariableStatement#4", input: `using a = b`},
{title: "VariableStatement#5", input: `await using a = b`},
{title: "EmptyStatement", input: `;`},
{title: "IfStatement#1", input: `if(a);`},
{title: "IfStatement#2", input: `if(a);else;`},
{title: "IfStatement#3", input: `if(a);else{}`},
{title: "IfStatement#4", input: `if(a);else if(b);`},
{title: "IfStatement#5", input: `if(a);else if(b) {}`},
{title: "IfStatement#6", input: `if(a) {}`},
{title: "IfStatement#7", input: `if(a) {} else;`},
{title: "IfStatement#8", input: `if(a) {} else {}`},
{title: "IfStatement#9", input: `if(a) {} else if(b);`},
{title: "IfStatement#10", input: `if(a) {} else if(b){}`},
{title: "DoStatement#1", input: `do;while(a);`},
{title: "DoStatement#2", input: `do {} while(a);`},
{title: "WhileStatement#1", input: `while(a);`},
{title: "WhileStatement#2", input: `while(a) {}`},
{title: "ForStatement#1", input: `for(;;);`},
{title: "ForStatement#2", input: `for(a;;);`},
{title: "ForStatement#3", input: `for(var a;;);`},
{title: "ForStatement#4", input: `for(;a;);`},
{title: "ForStatement#5", input: `for(;;a);`},
{title: "ForStatement#6", input: `for(;;){}`},
{title: "ForInStatement#1", input: `for(a in b);`},
{title: "ForInStatement#2", input: `for(var a in b);`},
{title: "ForInStatement#3", input: `for(a in b){}`},
{title: "ForOfStatement#1", input: `for(a of b);`},
{title: "ForOfStatement#2", input: `for(var a of b);`},
{title: "ForOfStatement#3", input: `for(a of b){}`},
{title: "ForOfStatement#4", input: `for await(a of b);`},
{title: "ForOfStatement#5", input: `for await(var a of b);`},
{title: "ForOfStatement#6", input: `for await(a of b){}`},
{title: "ContinueStatement#1", input: `continue`},
{title: "ContinueStatement#2", input: `continue a`},
{title: "BreakStatement#1", input: `break`},
{title: "BreakStatement#2", input: `break a`},
{title: "ReturnStatement#1", input: `return`},
{title: "ReturnStatement#2", input: `return a`},
{title: "WithStatement#1", input: `with(a);`},
{title: "WithStatement#2", input: `with(a){}`},
{title: "SwitchStatement", input: `switch (a) {}`},
{title: "CaseClause#1", input: `switch (a) {case b:}`},
{title: "CaseClause#2", input: `switch (a) {case b:;}`},
{title: "DefaultClause#1", input: `switch (a) {default:}`},
{title: "DefaultClause#2", input: `switch (a) {default:;}`},
{title: "LabeledStatement", input: `a:;`},
{title: "ThrowStatement", input: `throw a`},
{title: "TryStatement#1", input: `try {} catch {}`},
{title: "TryStatement#2", input: `try {} finally {}`},
{title: "TryStatement#3", input: `try {} catch {} finally {}`},
{title: "DebuggerStatement", input: `debugger`},
{title: "FunctionDeclaration#1", input: `export default function(){}`},
{title: "FunctionDeclaration#2", input: `function f(){}`},
{title: "FunctionDeclaration#3", input: `function*f(){}`},
{title: "FunctionDeclaration#4", input: `async function f(){}`},
{title: "FunctionDeclaration#5", input: `async function*f(){}`},
{title: "FunctionDeclaration#6", input: `function f<T>(){}`},
{title: "FunctionDeclaration#7", input: `function f(a){}`},
{title: "FunctionDeclaration#8", input: `function f():T{}`},
{title: "FunctionDeclaration#9", input: `function f();`},
{title: "ClassDeclaration#1", input: `class a {}`},
{title: "ClassDeclaration#2", input: `class a<T>{}`},
{title: "ClassDeclaration#3", input: `class a extends b {}`},
{title: "ClassDeclaration#4", input: `class a implements b {}`},
{title: "ClassDeclaration#5", input: `class a implements b, c {}`},
{title: "ClassDeclaration#6", input: `class a extends b implements c, d {}`},
{title: "ClassDeclaration#7", input: `export default class {}`},
{title: "ClassDeclaration#8", input: `export default class<T>{}`},
{title: "ClassDeclaration#9", input: `export default class extends b {}`},
{title: "ClassDeclaration#10", input: `export default class implements b {}`},
{title: "ClassDeclaration#11", input: `export default class implements b, c {}`},
{title: "ClassDeclaration#12", input: `export default class extends b implements c, d {}`},
{title: "ClassDeclaration#13", input: `@a class b {}`},
{title: "ClassDeclaration#14", input: `@a export class b {}`},
{title: "ClassDeclaration#15", input: `export @a class b {}`},
{title: "InterfaceDeclaration#1", input: `interface a {}`},
{title: "InterfaceDeclaration#2", input: `interface a<T>{}`},
{title: "InterfaceDeclaration#3", input: `interface a extends b {}`},
{title: "InterfaceDeclaration#4", input: `interface a extends b, c {}`},
{title: "TypeAliasDeclaration#1", input: `type a = b`},
{title: "TypeAliasDeclaration#2", input: `type a<T> = b`},
{title: "EnumDeclaration#1", input: `enum a{}`},
{title: "EnumDeclaration#2", input: `enum a{b}`},
{title: "EnumDeclaration#3", input: `enum a{b=c}`},
{title: "ModuleDeclaration#1", input: `module a{}`},
{title: "ModuleDeclaration#2", input: `module a.b{}`},
{title: "ModuleDeclaration#3", input: `module "a";`},
{title: "ModuleDeclaration#4", input: `module "a"{}`},
{title: "ModuleDeclaration#5", input: `namespace a{}`},
{title: "ModuleDeclaration#6", input: `namespace a.b{}`},
{title: "ModuleDeclaration#7", input: `global;`},
{title: "ModuleDeclaration#8", input: `global{}`},
{title: "ImportEqualsDeclaration#1", input: `import a = b`},
{title: "ImportEqualsDeclaration#2", input: `import a = b.c`},
{title: "ImportEqualsDeclaration#3", input: `import a = require("b")`},
{title: "ImportEqualsDeclaration#4", input: `export import a = b`},
{title: "ImportEqualsDeclaration#5", input: `export import a = require("b")`},
{title: "ImportEqualsDeclaration#6", input: `import type a = b`},
{title: "ImportEqualsDeclaration#7", input: `import type a = b.c`},
{title: "ImportEqualsDeclaration#8", input: `import type a = require("b")`},
{title: "ImportDeclaration#1", input: `import "a"`},
{title: "ImportDeclaration#2", input: `import a from "b"`},
{title: "ImportDeclaration#3", input: `import type a from "b"`},
{title: "ImportDeclaration#4", input: `import * as a from "b"`},
{title: "ImportDeclaration#5", input: `import type * as a from "b"`},
{title: "ImportDeclaration#6", input: `import {} from "b"`},
{title: "ImportDeclaration#7", input: `import type {} from "b"`},
{title: "ImportDeclaration#8", input: `import { a } from "b"`},
{title: "ImportDeclaration#9", input: `import type { a } from "b"`},
{title: "ImportDeclaration#8", input: `import { a as b } from "c"`},
{title: "ImportDeclaration#9", input: `import type { a as b } from "c"`},
{title: "ImportDeclaration#10", input: `import { "a" as b } from "c"`},
{title: "ImportDeclaration#11", input: `import type { "a" as b } from "c"`},
{title: "ImportDeclaration#12", input: `import a, {} from "b"`},
{title: "ImportDeclaration#13", input: `import a, * as b from "c"`},
{title: "ImportDeclaration#14", input: `import {} from "a" with {}`},
{title: "ImportDeclaration#15", input: `import {} from "a" with { b: "c" }`},
{title: "ImportDeclaration#16", input: `import {} from "a" with { "b": "c" }`},
{title: "ExportAssignment#1", input: `export = a`},
{title: "ExportAssignment#2", input: `export default a`},
{title: "NamespaceExportDeclaration", input: `export as namespace a`},
{title: "ExportDeclaration#1", input: `export * from "a"`},
{title: "ExportDeclaration#2", input: `export type * from "a"`},
{title: "ExportDeclaration#3", input: `export * as a from "b"`},
{title: "ExportDeclaration#4", input: `export type * as a from "b"`},
{title: "ExportDeclaration#5", input: `export { } from "a"`},
{title: "ExportDeclaration#6", input: `export type { } from "a"`},
{title: "ExportDeclaration#7", input: `export { a } from "b"`},
{title: "ExportDeclaration#8", input: `export { type a } from "b"`},
{title: "ExportDeclaration#9", input: `export type { a } from "b"`},
{title: "ExportDeclaration#10", input: `export { a as b } from "c"`},
{title: "ExportDeclaration#11", input: `export { type a as b } from "c"`},
{title: "ExportDeclaration#12", input: `export type { a as b } from "c"`},
{title: "ExportDeclaration#13", input: `export { a as "b" } from "c"`},
{title: "ExportDeclaration#14", input: `export { type a as "b" } from "c"`},
{title: "ExportDeclaration#15", input: `export type { a as "b" } from "c"`},
{title: "ExportDeclaration#16", input: `export { "a" } from "b"`},
{title: "ExportDeclaration#17", input: `export { type "a" } from "b"`},
{title: "ExportDeclaration#18", input: `export type { "a" } from "b"`},
{title: "ExportDeclaration#19", input: `export { "a" as b } from "c"`},
{title: "ExportDeclaration#20", input: `export { type "a" as b } from "c"`},
{title: "ExportDeclaration#21", input: `export type { "a" as b } from "c"`},
{title: "ExportDeclaration#22", input: `export { "a" as "b" } from "c"`},
{title: "ExportDeclaration#23", input: `export { type "a" as "b" } from "c"`},
{title: "ExportDeclaration#24", input: `export type { "a" as "b" } from "c"`},
{title: "ExportDeclaration#25", input: `export { }`},
{title: "ExportDeclaration#26", input: `export type { }`},
{title: "ExportDeclaration#27", input: `export { a }`},
{title: "ExportDeclaration#28", input: `export { type a }`},
{title: "ExportDeclaration#29", input: `export type { a }`},
{title: "ExportDeclaration#30", input: `export { a as b }`},
{title: "ExportDeclaration#31", input: `export { type a as b }`},
{title: "ExportDeclaration#32", input: `export type { a as b }`},
{title: "ExportDeclaration#33", input: `export { a as "b" }`},
{title: "ExportDeclaration#34", input: `export { type a as "b" }`},
{title: "ExportDeclaration#35", input: `export type { a as "b" }`},
{title: "ExportDeclaration#36", input: `export {} from "a" with {}`},
{title: "ExportDeclaration#37", input: `export {} from "a" with { b: "c" }`},
{title: "ExportDeclaration#38", input: `export {} from "a" with { "b": "c" }`},
{title: "KeywordTypeNode#1", input: `type T = any`},
{title: "KeywordTypeNode#2", input: `type T = unknown`},
{title: "KeywordTypeNode#3", input: `type T = never`},
{title: "KeywordTypeNode#4", input: `type T = void`},
{title: "KeywordTypeNode#5", input: `type T = undefined`},
{title: "KeywordTypeNode#6", input: `type T = null`},
{title: "KeywordTypeNode#7", input: `type T = object`},
{title: "KeywordTypeNode#8", input: `type T = string`},
{title: "KeywordTypeNode#9", input: `type T = symbol`},
{title: "KeywordTypeNode#10", input: `type T = number`},
{title: "KeywordTypeNode#11", input: `type T = bigint`},
{title: "KeywordTypeNode#12", input: `type T = boolean`},
{title: "KeywordTypeNode#13", input: `type T = intrinsic`},
{title: "TypePredicateNode#1", input: `function f(): asserts a`},
{title: "TypePredicateNode#2", input: `function f(): asserts a is b`},
{title: "TypePredicateNode#3", input: `function f(): asserts this`},
{title: "TypePredicateNode#4", input: `function f(): asserts this is b`},
{title: "TypeReferenceNode#1", input: `type T = a`},
{title: "TypeReferenceNode#2", input: `type T = a.b`},
{title: "TypeReferenceNode#3", input: `type T = a<U>`},
{title: "TypeReferenceNode#4", input: `type T = a.b<U>`},
{title: "FunctionTypeNode#1", input: `type T = () => a`},
{title: "FunctionTypeNode#2", input: `type T = <T>() => a`},
{title: "FunctionTypeNode#3", input: `type T = (a) => b`},
{title: "ConstructorTypeNode#1", input: `type T = new () => a`},
{title: "ConstructorTypeNode#2", input: `type T = new <T>() => a`},
{title: "ConstructorTypeNode#3", input: `type T = new (a) => b`},
{title: "ConstructorTypeNode#4", input: `type T = abstract new () => a`},
{title: "TypeQueryNode#1", input: `type T = typeof a`},
{title: "TypeQueryNode#2", input: `type T = typeof a.b`},
{title: "TypeQueryNode#3", input: `type T = typeof a<U>`},
{title: "TypeLiteralNode#1", input: `type T = {}`},
{title: "TypeLiteralNode#2", input: `type T = {a}`},
{title: "ArrayTypeNode", input: `type T = a[]`},
{title: "TupleTypeNode#1", input: `type T = []`},
{title: "TupleTypeNode#2", input: `type T = [a]`},
{title: "TupleTypeNode#3", input: `type T = [a,]`},
{title: "RestTypeNode", input: `type T = [...a]`},
{title: "OptionalTypeNode", input: `type T = [a?]`},
{title: "NamedTupleMember#1", input: `type T = [a: b]`},
{title: "NamedTupleMember#2", input: `type T = [a?: b]`},
{title: "NamedTupleMember#3", input: `type T = [...a: b]`},
{title: "UnionTypeNode#1", input: `type T = a | b`},
{title: "UnionTypeNode#2", input: `type T = a | b | c`},
{title: "UnionTypeNode#3", input: `type T = | a | b`},
{title: "IntersectionTypeNode#1", input: `type T = a & b`},
{title: "IntersectionTypeNode#2", input: `type T = a & b & c`},
{title: "IntersectionTypeNode#3", input: `type T = & a & b`},
{title: "ConditionalTypeNode", input: `type T = a extends b ? c : d`},
{title: "InferTypeNode#1", input: `type T = a extends infer b ? c : d`},
{title: "InferTypeNode#2", input: `type T = a extends infer b extends c ? d : e`},
{title: "ParenthesizedTypeNode", input: `type T = (U)`},
{title: "ThisTypeNode", input: `type T = this`},
{title: "TypeOperatorNode#1", input: `type T = keyof U`},
{title: "TypeOperatorNode#2", input: `type T = readonly U[]`},
{title: "TypeOperatorNode#3", input: `type T = unique symbol`},
{title: "IndexedAccessTypeNode", input: `type T = a[b]`},
{title: "MappedTypeNode#1", input: `type T = { [a in b]: c }`},
{title: "MappedTypeNode#2", input: `type T = { [a in b as c]: d }`},
{title: "MappedTypeNode#3", input: `type T = { readonly [a in b]: c }`},
{title: "MappedTypeNode#4", input: `type T = { +readonly [a in b]: c }`},
{title: "MappedTypeNode#5", input: `type T = { -readonly [a in b]: c }`},
{title: "MappedTypeNode#6", input: `type T = { [a in b]?: c }`},
{title: "MappedTypeNode#7", input: `type T = { [a in b]+?: c }`},
{title: "MappedTypeNode#8", input: `type T = { [a in b]-?: c }`},
{title: "MappedTypeNode#9", input: `type T = { [a in b]: c; d }`},
{title: "LiteralTypeNode#1", input: `type T = null`},
{title: "LiteralTypeNode#2", input: `type T = true`},
{title: "LiteralTypeNode#3", input: `type T = false`},
{title: "LiteralTypeNode#4", input: `type T = ""`},
{title: "LiteralTypeNode#5", input: "type T = ''"},
{title: "LiteralTypeNode#6", input: "type T = ``"},
{title: "LiteralTypeNode#7", input: `type T = 0`},
{title: "LiteralTypeNode#8", input: `type T = 0n`},
{title: "LiteralTypeNode#9", input: `type T = -0`},
{title: "LiteralTypeNode#10", input: `type T = -0n`},
{title: "TemplateTypeNode#1", input: "type T = `a${b}c`"},
{title: "TemplateTypeNode#2", input: "type T = `a${b}c${d}e`"},
{title: "ImportTypeNode#1", input: `type T = import(a)`},
{title: "ImportTypeNode#2", input: `type T = import(a).b`},
{title: "ImportTypeNode#3", input: `type T = import(a).b<U>`},
{title: "ImportTypeNode#4", input: `type T = typeof import(a)`},
{title: "ImportTypeNode#5", input: `type T = typeof import(a).b`},
{title: "ImportTypeNode#6", input: `type T = import(a, { with: { } })`},
{title: "ImportTypeNode#6", input: `type T = import(a, { with: { b: "c" } })`},
{title: "ImportTypeNode#7", input: `type T = import(a, { with: { "b": "c" } })`},
{title: "PropertySignature#1", input: "interface I {a}"},
{title: "PropertySignature#2", input: "interface I {readonly a}"},
{title: "PropertySignature#3", input: "interface I {\"a\"}"},
{title: "PropertySignature#4", input: "interface I {'a'}"},
{title: "PropertySignature#5", input: "interface I {0}"},
{title: "PropertySignature#6", input: "interface I {0n}"},
{title: "PropertySignature#7", input: "interface I {[a]}"},
{title: "PropertySignature#8", input: "interface I {a?}"},
{title: "PropertySignature#9", input: "interface I {a: b}"},
{title: "MethodSignature#1", input: "interface I {a()}"},
{title: "MethodSignature#2", input: "interface I {\"a\"()}"},
{title: "MethodSignature#3", input: "interface I {'a'()}"},
{title: "MethodSignature#4", input: "interface I {0()}"},
{title: "MethodSignature#5", input: "interface I {0n()}"},
{title: "MethodSignature#6", input: "interface I {[a]()}"},
{title: "MethodSignature#7", input: "interface I {a?()}"},
{title: "MethodSignature#8", input: "interface I {a<T>()}"},
{title: "MethodSignature#9", input: "interface I {a(): b}"},
{title: "MethodSignature#10", input: "interface I {a(b): c}"},
{title: "CallSignature#1", input: "interface I {()}"},
{title: "CallSignature#2", input: "interface I {():a}"},
{title: "CallSignature#3", input: "interface I {(p)}"},
{title: "CallSignature#4", input: "interface I {<T>()}"},
{title: "ConstructSignature#1", input: "interface I {new ()}"},
{title: "ConstructSignature#2", input: "interface I {new ():a}"},
{title: "ConstructSignature#3", input: "interface I {new (p)}"},
{title: "ConstructSignature#4", input: "interface I {new <T>()}"},
{title: "IndexSignatureDeclaration#1", input: "interface I {[a]}"},
{title: "IndexSignatureDeclaration#2", input: "interface I {[a: b]}"},
{title: "IndexSignatureDeclaration#3", input: "interface I {[a: b]: c}"},
{title: "PropertyDeclaration#1", input: "class C {a}"},
{title: "PropertyDeclaration#2", input: "class C {readonly a}"},
{title: "PropertyDeclaration#3", input: "class C {static a}"},
{title: "PropertyDeclaration#4", input: "class C {accessor a}"},
{title: "PropertyDeclaration#5", input: "class C {\"a\"}"},
{title: "PropertyDeclaration#6", input: "class C {'a'}"},
{title: "PropertyDeclaration#7", input: "class C {0}"},
{title: "PropertyDeclaration#8", input: "class C {0n}"},
{title: "PropertyDeclaration#9", input: "class C {[a]}"},
{title: "PropertyDeclaration#10", input: "class C {#a}"},
{title: "PropertyDeclaration#11", input: "class C {a?}"},
{title: "PropertyDeclaration#12", input: "class C {a!}"},
{title: "PropertyDeclaration#13", input: "class C {a: b}"},
{title: "PropertyDeclaration#14", input: "class C {a = b}"},
{title: "PropertyDeclaration#15", input: "class C {@a b}"},
{title: "MethodDeclaration#1", input: "class C {a()}"},
{title: "MethodDeclaration#2", input: "class C {\"a\"()}"},
{title: "MethodDeclaration#3", input: "class C {'a'()}"},
{title: "MethodDeclaration#4", input: "class C {0()}"},
{title: "MethodDeclaration#5", input: "class C {0n()}"},
{title: "MethodDeclaration#6", input: "class C {[a]()}"},
{title: "MethodDeclaration#7", input: "class C {#a()}"},
{title: "MethodDeclaration#8", input: "class C {a?()}"},
{title: "MethodDeclaration#9", input: "class C {a<T>()}"},
{title: "MethodDeclaration#10", input: "class C {a(): b}"},
{title: "MethodDeclaration#11", input: "class C {a(b): c}"},
{title: "MethodDeclaration#12", input: "class C {a() {} }"},
{title: "MethodDeclaration#13", input: "class C {@a b() {} }"},
{title: "MethodDeclaration#14", input: "class C {static a() {} }"},
{title: "MethodDeclaration#15", input: "class C {async a() {} }"},
{title: "GetAccessorDeclaration#1", input: "class C {get a()}"},
{title: "GetAccessorDeclaration#2", input: "class C {get \"a\"()}"},
{title: "GetAccessorDeclaration#3", input: "class C {get 'a'()}"},
{title: "GetAccessorDeclaration#4", input: "class C {get 0()}"},
{title: "GetAccessorDeclaration#5", input: "class C {get 0n()}"},
{title: "GetAccessorDeclaration#6", input: "class C {get [a]()}"},
{title: "GetAccessorDeclaration#7", input: "class C {get #a()}"},
{title: "GetAccessorDeclaration#8", input: "class C {get a(): b}"},
{title: "GetAccessorDeclaration#9", input: "class C {get a(b): c}"},
{title: "GetAccessorDeclaration#10", input: "class C {get a() {} }"},
{title: "GetAccessorDeclaration#11", input: "class C {@a get b() {} }"},
{title: "GetAccessorDeclaration#12", input: "class C {static get a() {} }"},
{title: "SetAccessorDeclaration#1", input: "class C {set a()}"},
{title: "SetAccessorDeclaration#2", input: "class C {set \"a\"()}"},
{title: "SetAccessorDeclaration#3", input: "class C {set 'a'()}"},
{title: "SetAccessorDeclaration#4", input: "class C {set 0()}"},
{title: "SetAccessorDeclaration#5", input: "class C {set 0n()}"},
{title: "SetAccessorDeclaration#6", input: "class C {set [a]()}"},
{title: "SetAccessorDeclaration#7", input: "class C {set #a()}"},
{title: "SetAccessorDeclaration#8", input: "class C {set a(): b}"},
{title: "SetAccessorDeclaration#9", input: "class C {set a(b): c}"},
{title: "SetAccessorDeclaration#10", input: "class C {set a() {} }"},
{title: "SetAccessorDeclaration#11", input: "class C {@a set b() {} }"},
{title: "SetAccessorDeclaration#12", input: "class C {static set a() {} }"},
{title: "ConstructorDeclaration#1", input: "class C {constructor()}"},
{title: "ConstructorDeclaration#2", input: "class C {constructor(): b}"},
{title: "ConstructorDeclaration#3", input: "class C {constructor(b): c}"},
{title: "ConstructorDeclaration#4", input: "class C {constructor() {} }"},
{title: "ConstructorDeclaration#5", input: "class C {@a constructor() {} }"},
{title: "ConstructorDeclaration#6", input: "class C {private constructor() {} }"},
{title: "ClassStaticBlockDeclaration", input: "class C {static { }}"},
{title: "SemicolonClassElement#1", input: "class C {;}"},
{title: "ParameterDeclaration#1", input: "function f(a)"},
{title: "ParameterDeclaration#2", input: "function f(a: b)"},
{title: "ParameterDeclaration#3", input: "function f(a = b)"},
{title: "ParameterDeclaration#4", input: "function f(a?)"},
{title: "ParameterDeclaration#5", input: "function f(...a)"},
{title: "ParameterDeclaration#6", input: "function f(this)"},
{title: "ParameterDeclaration#7", input: "function f(a,)"},
{title: "ObjectBindingPattern#1", input: "function f({})"},
{title: "ObjectBindingPattern#2", input: "function f({a})"},
{title: "ObjectBindingPattern#3", input: "function f({a = b})"},
{title: "ObjectBindingPattern#4", input: "function f({a: b})"},
{title: "ObjectBindingPattern#5", input: "function f({a: b = c})"},
{title: "ObjectBindingPattern#6", input: "function f({\"a\": b})"},
{title: "ObjectBindingPattern#7", input: "function f({'a': b})"},
{title: "ObjectBindingPattern#8", input: "function f({0: b})"},
{title: "ObjectBindingPattern#9", input: "function f({[a]: b})"},
{title: "ObjectBindingPattern#10", input: "function f({...a})"},
{title: "ObjectBindingPattern#11", input: "function f({a: {}})"},
{title: "ObjectBindingPattern#12", input: "function f({a: []})"},
{title: "ArrayBindingPattern#1", input: "function f([])"},
{title: "ArrayBindingPattern#2", input: "function f([,])"},
{title: "ArrayBindingPattern#3", input: "function f([a])"},
{title: "ArrayBindingPattern#4", input: "function f([a, b])"},
{title: "ArrayBindingPattern#5", input: "function f([a, , b])"},
{title: "ArrayBindingPattern#6", input: "function f([a = b])"},
{title: "ArrayBindingPattern#7", input: "function f([...a])"},
{title: "ArrayBindingPattern#8", input: "function f([{}])"},
{title: "ArrayBindingPattern#9", input: "function f([[]])"},
{title: "TypeParameterDeclaration#1", input: "function f<T>();"},
{title: "TypeParameterDeclaration#2", input: "function f<in T>();"},
{title: "TypeParameterDeclaration#3", input: "function f<T extends U>();"},
{title: "TypeParameterDeclaration#4", input: "function f<T = U>();"},
{title: "TypeParameterDeclaration#5", input: "function f<T extends U = V>();"},
{title: "TypeParameterDeclaration#6", input: "function f<T, U>();"},
{title: "TypeParameterDeclaration#7", input: "function f<T,>();"},
{title: "JsxElement1", input: "<a></a>"},
{title: "JsxElement2", input: "<this></this>"},
{title: "JsxElement3", input: "<a:b></a:b>"},
{title: "JsxElement4", input: "<a.b></a.b>"},
{title: "JsxElement5", input: "<a<b>></a>"},
{title: "JsxElement6", input: "<a b></a>"},
{title: "JsxElement7", input: "<a>b</a>"},
{title: "JsxElement8", input: "<a>{b}</a>"},
{title: "JsxElement9", input: "<a><b></b></a>"},
{title: "JsxElement10", input: "<a><b /></a>"},
{title: "JsxElement11", input: "<a><></></a>"},
{title: "JsxSelfClosingElement1", input: "<a />"},
{title: "JsxSelfClosingElement2", input: "<this />"},
{title: "JsxSelfClosingElement3", input: "<a:b />"},
{title: "JsxSelfClosingElement4", input: "<a.b />"},
{title: "JsxSelfClosingElement5", input: "<a<b> />"},
{title: "JsxSelfClosingElement6", input: "<a b/>"},
{title: "JsxFragment1", input: "<></>"},
{title: "JsxFragment2", input: "<>b</>"},
{title: "JsxFragment3", input: "<>{b}</>"},
{title: "JsxFragment4", input: "<><b></b></>"},
{title: "JsxFragment5", input: "<><b /></>"},
{title: "JsxFragment6", input: "<><></></>"},
{title: "JsxAttribute1", input: "<a b/>"},
{title: "JsxAttribute2", input: "<a b:c/>"},
{title: "JsxAttribute3", input: "<a b=\"c\"/>"},
{title: "JsxAttribute4", input: "<a b='c'/>"},
{title: "JsxAttribute5", input: "<a b={c}/>"},
{title: "JsxAttribute6", input: "<a b=<c></c>/>"},
{title: "JsxAttribute7", input: "<a b=<c />/>"},
{title: "JsxAttribute8", input: "<a b=<></>/>"},
{title: "JsxSpreadAttribute", input: "<a {...b}/>"},
}
for _, rec := range data {
t.Run("Clone "+rec.title, func(t *testing.T) {
t.Parallel()
factory := &ast.NodeFactory{}
file := parsetestutil.ParseTypeScript(rec.input, false).AsNode()
clone := factory.DeepCloneNode(file.AsNode()).AsNode()
work := []NodeComparisonWorkItem{{file, clone}}
for len(work) > 0 {
nextWork := []NodeComparisonWorkItem{}
for _, item := range work {
assert.Assert(t, item.original != item.copy)
originalChildren := getChildren(item.original)
copyChildren := getChildren(item.copy)
assert.Equal(t, len(originalChildren), len(copyChildren))
for i, child := range originalChildren {
nextWork = append(nextWork, NodeComparisonWorkItem{child, copyChildren[i]})
}
}
work = nextWork
}
})
}
}

View File

@@ -0,0 +1,362 @@
package ast
import (
"slices"
"strings"
"sync"
"github.com/microsoft/typescript-go/internal/collections"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/diagnostics"
"github.com/microsoft/typescript-go/internal/locale"
)
// RepopulateDiagnosticKind indicates the kind of repopulation for a diagnostic chain entry.
type RepopulateDiagnosticKind int
const (
RepopulateModeMismatch RepopulateDiagnosticKind = 1
RepopulateModuleNotFound RepopulateDiagnosticKind = 2
)
// RepopulateDiagnosticInfo stores information needed to recompute a diagnostic chain entry
// during incremental builds when the program state may have changed.
type RepopulateDiagnosticInfo struct {
Kind RepopulateDiagnosticKind
ModuleReference string
Mode core.ResolutionMode
PackageName string
}
// Diagnostic
type Diagnostic struct {
file *SourceFile
loc core.TextRange
code int32
category diagnostics.Category
// Original message; may be nil.
message *diagnostics.Message
messageKey diagnostics.Key
messageArgs []string
messageChain []*Diagnostic
relatedInformation []*Diagnostic
reportsUnnecessary bool
reportsDeprecated bool
skippedOnNoEmit bool
repopulateInfo *RepopulateDiagnosticInfo
}
func (d *Diagnostic) File() *SourceFile { return d.file }
func (d *Diagnostic) Pos() int { return d.loc.Pos() }
func (d *Diagnostic) End() int { return d.loc.End() }
func (d *Diagnostic) Len() int { return d.loc.Len() }
func (d *Diagnostic) Loc() core.TextRange { return d.loc }
func (d *Diagnostic) Code() int32 { return d.code }
func (d *Diagnostic) Category() diagnostics.Category { return d.category }
func (d *Diagnostic) MessageKey() diagnostics.Key { return d.messageKey }
func (d *Diagnostic) MessageArgs() []string { return d.messageArgs }
func (d *Diagnostic) MessageChain() []*Diagnostic { return d.messageChain }
func (d *Diagnostic) RelatedInformation() []*Diagnostic { return d.relatedInformation }
func (d *Diagnostic) ReportsUnnecessary() bool { return d.reportsUnnecessary }
func (d *Diagnostic) ReportsDeprecated() bool { return d.reportsDeprecated }
func (d *Diagnostic) SkippedOnNoEmit() bool { return d.skippedOnNoEmit }
func (d *Diagnostic) RepopulateInfo() *RepopulateDiagnosticInfo { return d.repopulateInfo }
func (d *Diagnostic) SetFile(file *SourceFile) { d.file = file }
func (d *Diagnostic) SetLocation(loc core.TextRange) { d.loc = loc }
func (d *Diagnostic) SetCategory(category diagnostics.Category) { d.category = category }
func (d *Diagnostic) SetSkippedOnNoEmit() { d.skippedOnNoEmit = true }
func (d *Diagnostic) SetRepopulateInfo(info *RepopulateDiagnosticInfo) { d.repopulateInfo = info }
func (d *Diagnostic) SetMessageChain(messageChain []*Diagnostic) *Diagnostic {
d.messageChain = messageChain
return d
}
func (d *Diagnostic) AddMessageChain(messageChain *Diagnostic) *Diagnostic {
if messageChain != nil {
d.messageChain = append(d.messageChain, messageChain)
}
return d
}
func (d *Diagnostic) SetRelatedInfo(relatedInformation []*Diagnostic) *Diagnostic {
d.relatedInformation = relatedInformation
return d
}
func (d *Diagnostic) AddRelatedInfo(relatedInformation *Diagnostic) *Diagnostic {
if relatedInformation != nil {
d.relatedInformation = append(d.relatedInformation, relatedInformation)
}
return d
}
func (d *Diagnostic) Clone() *Diagnostic {
result := *d
return &result
}
func (d *Diagnostic) Localize(locale locale.Locale) string {
return diagnostics.Localize(locale, d.message, d.messageKey, d.messageArgs...)
}
// For debugging only.
func (d *Diagnostic) String() string {
return diagnostics.Localize(locale.Default, d.message, d.messageKey, d.messageArgs...)
}
func NewDiagnosticFromSerialized(
file *SourceFile,
loc core.TextRange,
code int32,
category diagnostics.Category,
messageKey diagnostics.Key,
messageArgs []string,
messageChain []*Diagnostic,
relatedInformation []*Diagnostic,
reportsUnnecessary bool,
reportsDeprecated bool,
skippedOnNoEmit bool,
) *Diagnostic {
return &Diagnostic{
file: file,
loc: loc,
code: code,
category: category,
messageKey: messageKey,
messageArgs: messageArgs,
messageChain: messageChain,
relatedInformation: relatedInformation,
reportsUnnecessary: reportsUnnecessary,
reportsDeprecated: reportsDeprecated,
skippedOnNoEmit: skippedOnNoEmit,
}
}
func NewDiagnostic(file *SourceFile, loc core.TextRange, message *diagnostics.Message, args ...any) *Diagnostic {
return &Diagnostic{
file: file,
loc: loc,
code: message.Code(),
category: message.Category(),
message: message,
messageKey: message.Key(),
messageArgs: diagnostics.StringifyArgs(args),
reportsUnnecessary: message.ReportsUnnecessary(),
reportsDeprecated: message.ReportsDeprecated(),
}
}
func NewDiagnosticChain(chain *Diagnostic, message *diagnostics.Message, args ...any) *Diagnostic {
if chain != nil {
return NewDiagnostic(chain.file, chain.loc, message, args...).AddMessageChain(chain).SetRelatedInfo(chain.relatedInformation)
}
return NewDiagnostic(nil, core.TextRange{}, message, args...)
}
func NewCompilerDiagnostic(message *diagnostics.Message, args ...any) *Diagnostic {
return NewDiagnostic(nil, core.UndefinedTextRange(), message, args...)
}
type DiagnosticsCollection struct {
mu sync.Mutex
count int
fileDiagnostics map[string][]*Diagnostic
fileDiagnosticsSorted collections.Set[string]
nonFileDiagnostics []*Diagnostic
nonFileDiagnosticsSorted bool
}
func (c *DiagnosticsCollection) Add(diagnostic *Diagnostic) {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
if diagnostic.File() != nil {
fileName := diagnostic.File().FileName()
if c.fileDiagnostics == nil {
c.fileDiagnostics = make(map[string][]*Diagnostic)
}
c.fileDiagnostics[fileName] = append(c.fileDiagnostics[fileName], diagnostic)
c.fileDiagnosticsSorted.Delete(fileName)
} else {
c.nonFileDiagnostics = append(c.nonFileDiagnostics, diagnostic)
c.nonFileDiagnosticsSorted = false
}
}
func (c *DiagnosticsCollection) Lookup(diagnostic *Diagnostic) *Diagnostic {
c.mu.Lock()
defer c.mu.Unlock()
var diagnostics []*Diagnostic
if diagnostic.File() != nil {
diagnostics = c.getDiagnosticsForFileLocked(diagnostic.File().FileName())
} else {
diagnostics = c.getGlobalDiagnosticsLocked()
}
if i, ok := slices.BinarySearchFunc(diagnostics, diagnostic, CompareDiagnostics); ok {
return diagnostics[i]
}
return nil
}
func (c *DiagnosticsCollection) GetGlobalDiagnostics() []*Diagnostic {
c.mu.Lock()
defer c.mu.Unlock()
return c.getGlobalDiagnosticsLocked()
}
func (c *DiagnosticsCollection) getGlobalDiagnosticsLocked() []*Diagnostic {
if !c.nonFileDiagnosticsSorted {
slices.SortStableFunc(c.nonFileDiagnostics, CompareDiagnostics)
c.nonFileDiagnosticsSorted = true
}
return slices.Clone(c.nonFileDiagnostics)
}
func (c *DiagnosticsCollection) GetDiagnosticsForFile(fileName string) []*Diagnostic {
c.mu.Lock()
defer c.mu.Unlock()
return c.getDiagnosticsForFileLocked(fileName)
}
func (c *DiagnosticsCollection) getDiagnosticsForFileLocked(fileName string) []*Diagnostic {
if !c.fileDiagnosticsSorted.Has(fileName) {
slices.SortStableFunc(c.fileDiagnostics[fileName], CompareDiagnostics)
c.fileDiagnosticsSorted.Add(fileName)
}
return slices.Clone(c.fileDiagnostics[fileName])
}
func (c *DiagnosticsCollection) GetDiagnostics() []*Diagnostic {
c.mu.Lock()
defer c.mu.Unlock()
diagnostics := make([]*Diagnostic, 0, c.count)
diagnostics = append(diagnostics, c.nonFileDiagnostics...)
for _, diags := range c.fileDiagnostics {
diagnostics = append(diagnostics, diags...)
}
slices.SortFunc(diagnostics, CompareDiagnostics)
return diagnostics
}
func getDiagnosticPath(d *Diagnostic) string {
if d.File() != nil {
return d.File().FileName()
}
return ""
}
func EqualDiagnostics(d1, d2 *Diagnostic) bool {
if d1 == d2 {
return true
}
return EqualDiagnosticsNoRelatedInfo(d1, d2) &&
slices.EqualFunc(d1.RelatedInformation(), d2.RelatedInformation(), EqualDiagnostics)
}
func EqualDiagnosticsNoRelatedInfo(d1, d2 *Diagnostic) bool {
if d1 == d2 {
return true
}
return getDiagnosticPath(d1) == getDiagnosticPath(d2) &&
d1.Loc() == d2.Loc() &&
d1.Code() == d2.Code() &&
slices.Equal(d1.MessageArgs(), d2.MessageArgs()) &&
slices.EqualFunc(d1.MessageChain(), d2.MessageChain(), equalMessageChain)
}
func equalMessageChain(c1, c2 *Diagnostic) bool {
if c1 == c2 {
return true
}
return c1.Code() == c2.Code() &&
slices.Equal(c1.MessageArgs(), c2.MessageArgs()) &&
slices.EqualFunc(c1.MessageChain(), c2.MessageChain(), equalMessageChain)
}
func compareMessageChainSize(c1, c2 []*Diagnostic) int {
c := len(c2) - len(c1)
if c != 0 {
return c
}
for i := range c1 {
c = compareMessageChainSize(c1[i].MessageChain(), c2[i].MessageChain())
if c != 0 {
return c
}
}
return 0
}
func compareMessageChainContent(c1, c2 []*Diagnostic) int {
for i := range c1 {
c := slices.Compare(c1[i].MessageArgs(), c2[i].MessageArgs())
if c != 0 {
return c
}
if c1[i].MessageChain() != nil {
c = compareMessageChainContent(c1[i].MessageChain(), c2[i].MessageChain())
if c != 0 {
return c
}
}
}
return 0
}
func compareRelatedInfo(r1, r2 []*Diagnostic) int {
c := len(r2) - len(r1)
if c != 0 {
return c
}
for i := range r1 {
c = CompareDiagnostics(r1[i], r2[i])
if c != 0 {
return c
}
}
return 0
}
func CompareDiagnostics(d1, d2 *Diagnostic) int {
if d1 == d2 {
return 0
}
c := strings.Compare(getDiagnosticPath(d1), getDiagnosticPath(d2))
if c != 0 {
return c
}
c = d1.Loc().Pos() - d2.Loc().Pos()
if c != 0 {
return c
}
c = d1.Loc().End() - d2.Loc().End()
if c != 0 {
return c
}
c = int(d1.Code()) - int(d2.Code())
if c != 0 {
return c
}
c = slices.Compare(d1.MessageArgs(), d2.MessageArgs())
if c != 0 {
return c
}
c = compareMessageChainSize(d1.MessageChain(), d2.MessageChain())
if c != 0 {
return c
}
c = compareMessageChainContent(d1.MessageChain(), d2.MessageChain())
if c != 0 {
return c
}
return compareRelatedInfo(d1.RelatedInformation(), d2.RelatedInformation())
}

View File

@@ -0,0 +1,75 @@
package ast
// FlowFlags
type FlowFlags uint32
const (
FlowFlagsUnreachable FlowFlags = 1 << 0 // Unreachable code
FlowFlagsStart FlowFlags = 1 << 1 // Start of flow graph
FlowFlagsBranchLabel FlowFlags = 1 << 2 // Non-looping junction
FlowFlagsLoopLabel FlowFlags = 1 << 3 // Looping junction
FlowFlagsAssignment FlowFlags = 1 << 4 // Assignment
FlowFlagsTrueCondition FlowFlags = 1 << 5 // Condition known to be true
FlowFlagsFalseCondition FlowFlags = 1 << 6 // Condition known to be false
FlowFlagsSwitchClause FlowFlags = 1 << 7 // Switch statement clause
FlowFlagsArrayMutation FlowFlags = 1 << 8 // Potential array mutation
FlowFlagsCall FlowFlags = 1 << 9 // Potential assertion call
FlowFlagsReduceLabel FlowFlags = 1 << 10 // Temporarily reduce antecedents of label
FlowFlagsReferenced FlowFlags = 1 << 11 // Referenced as antecedent once
FlowFlagsShared FlowFlags = 1 << 12 // Referenced as antecedent more than once
FlowFlagsLabel = FlowFlagsBranchLabel | FlowFlagsLoopLabel
FlowFlagsCondition = FlowFlagsTrueCondition | FlowFlagsFalseCondition
)
// FlowNode
type FlowNode struct {
Flags FlowFlags
Node *Node // Associated AST node
Antecedent *FlowNode // Antecedent for all but FlowLabel
Antecedents *FlowList // Linked list of antecedents for FlowLabel
}
type FlowList struct {
Flow *FlowNode
Next *FlowList
}
type FlowLabel = FlowNode
// FlowSwitchClauseData (synthetic AST node for FlowFlagsSwitchClause)
type FlowSwitchClauseData struct {
NodeBase
SwitchStatement *Node
ClauseStart int32 // Start index of case/default clause range
ClauseEnd int32 // End index of case/default clause range
}
func NewFlowSwitchClauseData(switchStatement *Node, clauseStart int, clauseEnd int) *Node {
node := &FlowSwitchClauseData{}
node.SwitchStatement = switchStatement
node.ClauseStart = int32(clauseStart)
node.ClauseEnd = int32(clauseEnd)
return newNode(KindUnknown, node, NodeFactoryHooks{})
}
func (node *FlowSwitchClauseData) IsEmpty() bool {
return node.ClauseStart == node.ClauseEnd
}
// FlowReduceLabelData (synthetic AST node for FlowFlagsReduceLabel)
type FlowReduceLabelData struct {
NodeBase
Target *FlowLabel // Target label
Antecedents *FlowList // Temporary antecedent list
}
func NewFlowReduceLabelData(target *FlowLabel, antecedents *FlowList) *Node {
node := &FlowReduceLabelData{}
node.Target = target
node.Antecedents = antecedents
return newNode(KindUnknown, node, NodeFactoryHooks{})
}

View File

@@ -0,0 +1,37 @@
package ast
type FunctionFlags uint32
const (
FunctionFlagsNormal FunctionFlags = 0
FunctionFlagsGenerator FunctionFlags = 1 << 0
FunctionFlagsAsync FunctionFlags = 1 << 1
FunctionFlagsInvalid FunctionFlags = 1 << 2
FunctionFlagsAsyncGenerator FunctionFlags = FunctionFlagsAsync | FunctionFlagsGenerator
)
func GetFunctionFlags(node *Node) FunctionFlags {
if node == nil {
return FunctionFlagsInvalid
}
data := node.BodyData()
if data == nil {
return FunctionFlagsInvalid
}
flags := FunctionFlagsNormal
switch node.Kind {
case KindFunctionDeclaration, KindFunctionExpression, KindMethodDeclaration:
if data.AsteriskToken != nil {
flags |= FunctionFlagsGenerator
}
fallthrough
case KindArrowFunction:
if HasSyntacticModifier(node, ModifierFlagsAsync) {
flags |= FunctionFlagsAsync
}
}
if data.Body == nil {
flags |= FunctionFlagsInvalid
}
return flags
}

View File

@@ -0,0 +1,6 @@
package ast
type (
NodeId uint64
SymbolId uint64
)

View File

@@ -0,0 +1,463 @@
// Code generated by _scripts/generate-go-ast.ts. DO NOT EDIT.
package ast
//go:generate go tool golang.org/x/tools/cmd/stringer -type=Kind -output=kind_stringer_generated.go
//go:generate npx dprint fmt kind_stringer_generated.go
type Kind int16
const (
KindUnknown Kind = iota
KindEndOfFile
KindSingleLineCommentTrivia
KindMultiLineCommentTrivia
KindNewLineTrivia
KindWhitespaceTrivia
KindConflictMarkerTrivia
KindNonTextFileMarkerTrivia
KindNumericLiteral
KindBigIntLiteral
KindStringLiteral
KindJsxText
KindJsxTextAllWhiteSpaces
KindRegularExpressionLiteral
KindNoSubstitutionTemplateLiteral
// Pseudo-literals
KindTemplateHead
KindTemplateMiddle
KindTemplateTail
// Punctuation
KindOpenBraceToken
KindCloseBraceToken
KindOpenParenToken
KindCloseParenToken
KindOpenBracketToken
KindCloseBracketToken
KindDotToken
KindDotDotDotToken
KindSemicolonToken
KindCommaToken
KindQuestionDotToken
KindLessThanToken
KindLessThanSlashToken
KindGreaterThanToken
KindLessThanEqualsToken
KindGreaterThanEqualsToken
KindEqualsEqualsToken
KindExclamationEqualsToken
KindEqualsEqualsEqualsToken
KindExclamationEqualsEqualsToken
KindEqualsGreaterThanToken
KindPlusToken
KindMinusToken
KindAsteriskToken
KindAsteriskAsteriskToken
KindSlashToken
KindPercentToken
KindPlusPlusToken
KindMinusMinusToken
KindLessThanLessThanToken
KindGreaterThanGreaterThanToken
KindGreaterThanGreaterThanGreaterThanToken
KindAmpersandToken
KindBarToken
KindCaretToken
KindExclamationToken
KindTildeToken
KindAmpersandAmpersandToken
KindBarBarToken
KindQuestionToken
KindColonToken
KindAtToken
KindQuestionQuestionToken
// Only the JSDoc scanner produces BacktickToken. The normal scanner produces NoSubstitutionTemplateLiteral and related kinds.
KindBacktickToken
// Only the JSDoc scanner produces HashToken. The normal scanner produces PrivateIdentifier.
KindHashToken
// Assignments
KindEqualsToken
KindPlusEqualsToken
KindMinusEqualsToken
KindAsteriskEqualsToken
KindAsteriskAsteriskEqualsToken
KindSlashEqualsToken
KindPercentEqualsToken
KindLessThanLessThanEqualsToken
KindGreaterThanGreaterThanEqualsToken
KindGreaterThanGreaterThanGreaterThanEqualsToken
KindAmpersandEqualsToken
KindBarEqualsToken
KindBarBarEqualsToken
KindAmpersandAmpersandEqualsToken
KindQuestionQuestionEqualsToken
KindCaretEqualsToken
// Identifiers and PrivateIdentifier
KindIdentifier
KindPrivateIdentifier
KindJSDocCommentTextToken
// Reserved words
KindBreakKeyword
KindCaseKeyword
KindCatchKeyword
KindClassKeyword
KindConstKeyword
KindContinueKeyword
KindDebuggerKeyword
KindDefaultKeyword
KindDeleteKeyword
KindDoKeyword
KindElseKeyword
KindEnumKeyword
KindExportKeyword
KindExtendsKeyword
KindFalseKeyword
KindFinallyKeyword
KindForKeyword
KindFunctionKeyword
KindIfKeyword
KindImportKeyword
KindInKeyword
KindInstanceOfKeyword
KindNewKeyword
KindNullKeyword
KindReturnKeyword
KindSuperKeyword
KindSwitchKeyword
KindThisKeyword
KindThrowKeyword
KindTrueKeyword
KindTryKeyword
KindTypeOfKeyword
KindVarKeyword
KindVoidKeyword
KindWhileKeyword
KindWithKeyword
// Strict mode reserved words
KindImplementsKeyword
KindInterfaceKeyword
KindLetKeyword
KindPackageKeyword
KindPrivateKeyword
KindProtectedKeyword
KindPublicKeyword
KindStaticKeyword
KindYieldKeyword
// Contextual keywords
KindAbstractKeyword
KindAccessorKeyword
KindAsKeyword
KindAssertsKeyword
KindAssertKeyword
KindAnyKeyword
KindAsyncKeyword
KindAwaitKeyword
KindBooleanKeyword
KindConstructorKeyword
KindDeclareKeyword
KindGetKeyword
KindImmediateKeyword
KindInferKeyword
KindIntrinsicKeyword
KindIsKeyword
KindKeyOfKeyword
KindModuleKeyword
KindNamespaceKeyword
KindNeverKeyword
KindOutKeyword
KindReadonlyKeyword
KindRequireKeyword
KindNumberKeyword
KindObjectKeyword
KindSatisfiesKeyword
KindSetKeyword
KindStringKeyword
KindSymbolKeyword
KindTypeKeyword
KindUndefinedKeyword
KindUniqueKeyword
KindUnknownKeyword
KindUsingKeyword
KindFromKeyword
KindGlobalKeyword
KindBigIntKeyword
KindOverrideKeyword
KindOfKeyword
KindDeferKeyword // LastKeyword and LastToken and LastContextualKeyword
// Parse tree nodes
// Names
KindQualifiedName
KindComputedPropertyName
// Signature elements
KindTypeParameter
KindParameter
KindDecorator
// TypeMember
KindPropertySignature
KindPropertyDeclaration
KindMethodSignature
KindMethodDeclaration
KindClassStaticBlockDeclaration
KindConstructor
KindGetAccessor
KindSetAccessor
KindCallSignature
KindConstructSignature
KindIndexSignature
// Type
KindTypePredicate
KindTypeReference
KindFunctionType
KindConstructorType
KindTypeQuery
KindTypeLiteral
KindArrayType
KindTupleType
KindOptionalType
KindRestType
KindUnionType
KindIntersectionType
KindConditionalType
KindInferType
KindParenthesizedType
KindThisType
KindTypeOperator
KindIndexedAccessType
KindMappedType
KindLiteralType
KindNamedTupleMember
KindTemplateLiteralType
KindTemplateLiteralTypeSpan
KindImportType
// Binding patterns
KindObjectBindingPattern
KindArrayBindingPattern
KindBindingElement
// Expression
KindArrayLiteralExpression
KindObjectLiteralExpression
KindPropertyAccessExpression
KindElementAccessExpression
KindCallExpression
KindNewExpression
KindTaggedTemplateExpression
KindTypeAssertionExpression
KindParenthesizedExpression
KindFunctionExpression
KindArrowFunction
KindDeleteExpression
KindTypeOfExpression
KindVoidExpression
KindAwaitExpression
KindPrefixUnaryExpression
KindPostfixUnaryExpression
KindBinaryExpression
KindConditionalExpression
KindTemplateExpression
KindYieldExpression
KindSpreadElement
KindClassExpression
KindOmittedExpression
KindExpressionWithTypeArguments
KindAsExpression
KindNonNullExpression
KindMetaProperty
KindSyntheticExpression
KindSatisfiesExpression
// Misc
KindTemplateSpan
KindSemicolonClassElement
// Element
KindBlock
KindEmptyStatement
KindVariableStatement
KindExpressionStatement
KindIfStatement
KindDoStatement
KindWhileStatement
KindForStatement
KindForInStatement
KindForOfStatement
KindContinueStatement
KindBreakStatement
KindReturnStatement
KindWithStatement
KindSwitchStatement
KindLabeledStatement
KindThrowStatement
KindTryStatement
KindDebuggerStatement
KindVariableDeclaration
KindVariableDeclarationList
KindFunctionDeclaration
KindClassDeclaration
KindInterfaceDeclaration
KindTypeAliasDeclaration
KindEnumDeclaration
KindModuleDeclaration
KindModuleBlock
KindCaseBlock
KindNamespaceExportDeclaration
KindImportEqualsDeclaration
KindImportDeclaration
KindImportClause
KindNamespaceImport
KindNamedImports
KindImportSpecifier
KindExportAssignment
KindExportDeclaration
KindNamedExports
KindNamespaceExport
KindExportSpecifier
KindMissingDeclaration
// Module references
KindExternalModuleReference
// JSX
KindJsxElement
KindJsxSelfClosingElement
KindJsxOpeningElement
KindJsxClosingElement
KindJsxFragment
KindJsxOpeningFragment
KindJsxClosingFragment
KindJsxAttribute
KindJsxAttributes
KindJsxSpreadAttribute
KindJsxExpression
KindJsxNamespacedName
// Clauses
KindCaseClause
KindDefaultClause
KindHeritageClause
KindCatchClause
// Import attributes
KindImportAttributes
KindImportAttribute
// Property assignments
KindPropertyAssignment
KindShorthandPropertyAssignment
KindSpreadAssignment
// Enum
KindEnumMember
// Top-level nodes
KindSourceFile
// JSDoc nodes
KindJSDocTypeExpression
KindJSDocNameReference
KindJSDocAllType // The * type
KindJSDocNullableType
KindJSDocNonNullableType
KindJSDocOptionalType
KindJSDocVariadicType
KindJSDoc
KindJSDocText
KindJSDocTypeLiteral
KindJSDocSignature
KindJSDocLink
KindJSDocLinkCode
KindJSDocLinkPlain
KindJSDocUnknownTag
KindJSDocAugmentsTag
KindJSDocImplementsTag
KindJSDocDeprecatedTag
KindJSDocPublicTag
KindJSDocPrivateTag
KindJSDocProtectedTag
KindJSDocReadonlyTag
KindJSDocOverrideTag
KindJSDocCallbackTag
KindJSDocOverloadTag
KindJSDocParameterTag
KindJSDocReturnTag
KindJSDocThisTag
KindJSDocTypeTag
KindJSDocTemplateTag
KindJSDocTypedefTag
KindJSDocSeeTag
KindJSDocPropertyTag
KindJSDocThrowsTag
KindJSDocSatisfiesTag
KindJSDocImportTag
// Synthesized list
KindSyntaxList
// Reparsed JS nodes
KindJSTypeAliasDeclaration
KindJSImportDeclaration
// Transformation nodes
KindNotEmittedStatement
KindPartiallyEmittedExpression
KindSyntheticReferenceExpression
KindNotEmittedTypeElement
KindCount
KindFirstAssignment = KindEqualsToken
KindLastAssignment = KindCaretEqualsToken
KindFirstCompoundAssignment = KindPlusEqualsToken
KindLastCompoundAssignment = KindCaretEqualsToken
KindFirstReservedWord = KindBreakKeyword
KindLastReservedWord = KindWithKeyword
KindFirstKeyword = KindBreakKeyword
KindLastKeyword = KindDeferKeyword
KindFirstFutureReservedWord = KindImplementsKeyword
KindLastFutureReservedWord = KindYieldKeyword
KindFirstTypeNode = KindTypePredicate
KindLastTypeNode = KindImportType
KindFirstPunctuation = KindOpenBraceToken
KindLastPunctuation = KindCaretEqualsToken
KindFirstToken = KindUnknown
KindLastToken = KindLastKeyword
KindFirstLiteralToken = KindNumericLiteral
KindLastLiteralToken = KindNoSubstitutionTemplateLiteral
KindFirstTemplateToken = KindNoSubstitutionTemplateLiteral
KindLastTemplateToken = KindTemplateTail
KindFirstBinaryOperator = KindLessThanToken
KindLastBinaryOperator = KindCaretEqualsToken
KindFirstStatement = KindVariableStatement
KindLastStatement = KindDebuggerStatement
KindFirstNode = KindQualifiedName
KindFirstJSDocNode = KindJSDocTypeExpression
KindLastJSDocNode = KindJSDocImportTag
KindFirstJSDocTagNode = KindJSDocUnknownTag
KindLastJSDocTagNode = KindJSDocImportTag
KindFirstContextualKeyword = KindAbstractKeyword
KindLastContextualKeyword = KindDeferKeyword
KindLastUnaryOperator = KindTildeToken
KindFirstTriviaToken = KindSingleLineCommentTrivia
KindLastTriviaToken = KindConflictMarkerTrivia
)
type (
TriviaSyntaxKind = Kind // KindSingleLineCommentTrivia | KindMultiLineCommentTrivia | KindNewLineTrivia | KindWhitespaceTrivia | KindConflictMarkerTrivia
LiteralSyntaxKind = Kind // KindNumericLiteral | KindBigIntLiteral | KindStringLiteral | KindJsxText | KindJsxTextAllWhiteSpaces | KindRegularExpressionLiteral | KindNoSubstitutionTemplateLiteral
PseudoLiteralSyntaxKind = Kind // KindTemplateHead | KindTemplateMiddle | KindTemplateTail
PunctuationSyntaxKind = Kind // KindOpenBraceToken | KindCloseBraceToken | KindOpenParenToken | KindCloseParenToken | KindOpenBracketToken | KindCloseBracketToken | KindDotToken | KindDotDotDotToken | KindSemicolonToken | KindCommaToken | KindQuestionDotToken | KindLessThanToken | KindLessThanSlashToken | KindGreaterThanToken | KindLessThanEqualsToken | KindGreaterThanEqualsToken | KindEqualsEqualsToken | KindExclamationEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindEqualsGreaterThanToken | KindPlusToken | KindMinusToken | KindAsteriskToken | KindAsteriskAsteriskToken | KindSlashToken | KindPercentToken | KindPlusPlusToken | KindMinusMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindAmpersandToken | KindBarToken | KindCaretToken | KindExclamationToken | KindTildeToken | KindAmpersandAmpersandToken | KindBarBarToken | KindQuestionToken | KindColonToken | KindAtToken | KindQuestionQuestionToken | KindBacktickToken | KindHashToken | KindEqualsToken | KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskEqualsToken | KindAsteriskAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken | KindCaretEqualsToken
KeywordSyntaxKind = Kind // KindBreakKeyword | KindCaseKeyword | KindCatchKeyword | KindClassKeyword | KindConstKeyword | KindContinueKeyword | KindDebuggerKeyword | KindDefaultKeyword | KindDeleteKeyword | KindDoKeyword | KindElseKeyword | KindEnumKeyword | KindExportKeyword | KindExtendsKeyword | KindFalseKeyword | KindFinallyKeyword | KindForKeyword | KindFunctionKeyword | KindIfKeyword | KindImportKeyword | KindInKeyword | KindInstanceOfKeyword | KindNewKeyword | KindNullKeyword | KindReturnKeyword | KindSuperKeyword | KindSwitchKeyword | KindThisKeyword | KindThrowKeyword | KindTrueKeyword | KindTryKeyword | KindTypeOfKeyword | KindVarKeyword | KindVoidKeyword | KindWhileKeyword | KindWithKeyword | KindImplementsKeyword | KindInterfaceKeyword | KindLetKeyword | KindPackageKeyword | KindPrivateKeyword | KindProtectedKeyword | KindPublicKeyword | KindStaticKeyword | KindYieldKeyword | KindAbstractKeyword | KindAccessorKeyword | KindAsKeyword | KindAssertsKeyword | KindAssertKeyword | KindAnyKeyword | KindAsyncKeyword | KindAwaitKeyword | KindBooleanKeyword | KindConstructorKeyword | KindDeclareKeyword | KindGetKeyword | KindImmediateKeyword | KindInferKeyword | KindIntrinsicKeyword | KindIsKeyword | KindKeyOfKeyword | KindModuleKeyword | KindNamespaceKeyword | KindNeverKeyword | KindOutKeyword | KindReadonlyKeyword | KindRequireKeyword | KindNumberKeyword | KindObjectKeyword | KindSatisfiesKeyword | KindSetKeyword | KindStringKeyword | KindSymbolKeyword | KindTypeKeyword | KindUndefinedKeyword | KindUniqueKeyword | KindUnknownKeyword | KindUsingKeyword | KindFromKeyword | KindGlobalKeyword | KindBigIntKeyword | KindOverrideKeyword | KindOfKeyword | KindDeferKeyword
ModifierSyntaxKind = Kind // KindAbstractKeyword | KindAccessorKeyword | KindAsyncKeyword | KindConstKeyword | KindDeclareKeyword | KindDefaultKeyword | KindExportKeyword | KindInKeyword | KindPrivateKeyword | KindProtectedKeyword | KindPublicKeyword | KindReadonlyKeyword | KindOutKeyword | KindOverrideKeyword | KindStaticKeyword
KeywordTypeSyntaxKind = Kind // KindAnyKeyword | KindBigIntKeyword | KindBooleanKeyword | KindIntrinsicKeyword | KindNeverKeyword | KindNumberKeyword | KindObjectKeyword | KindStringKeyword | KindSymbolKeyword | KindUndefinedKeyword | KindUnknownKeyword | KindVoidKeyword
KeywordExpressionSyntaxKind = Kind // KindNullKeyword | KindTrueKeyword | KindFalseKeyword | KindThisKeyword | KindSuperKeyword | KindImportKeyword
TokenSyntaxKind = Kind // KindUnknown | KindEndOfFile | KindSingleLineCommentTrivia | KindMultiLineCommentTrivia | KindNewLineTrivia | KindWhitespaceTrivia | KindConflictMarkerTrivia | KindNonTextFileMarkerTrivia | KindNumericLiteral | KindBigIntLiteral | KindStringLiteral | KindJsxText | KindJsxTextAllWhiteSpaces | KindRegularExpressionLiteral | KindNoSubstitutionTemplateLiteral | KindTemplateHead | KindTemplateMiddle | KindTemplateTail | KindOpenBraceToken | KindCloseBraceToken | KindOpenParenToken | KindCloseParenToken | KindOpenBracketToken | KindCloseBracketToken | KindDotToken | KindDotDotDotToken | KindSemicolonToken | KindCommaToken | KindQuestionDotToken | KindLessThanToken | KindLessThanSlashToken | KindGreaterThanToken | KindLessThanEqualsToken | KindGreaterThanEqualsToken | KindEqualsEqualsToken | KindExclamationEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindEqualsGreaterThanToken | KindPlusToken | KindMinusToken | KindAsteriskToken | KindAsteriskAsteriskToken | KindSlashToken | KindPercentToken | KindPlusPlusToken | KindMinusMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindAmpersandToken | KindBarToken | KindCaretToken | KindExclamationToken | KindTildeToken | KindAmpersandAmpersandToken | KindBarBarToken | KindQuestionToken | KindColonToken | KindAtToken | KindQuestionQuestionToken | KindBacktickToken | KindHashToken | KindEqualsToken | KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskEqualsToken | KindAsteriskAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken | KindCaretEqualsToken | KindIdentifier | KindPrivateIdentifier | KindJSDocCommentTextToken | KindBreakKeyword | KindCaseKeyword | KindCatchKeyword | KindClassKeyword | KindConstKeyword | KindContinueKeyword | KindDebuggerKeyword | KindDefaultKeyword | KindDeleteKeyword | KindDoKeyword | KindElseKeyword | KindEnumKeyword | KindExportKeyword | KindExtendsKeyword | KindFalseKeyword | KindFinallyKeyword | KindForKeyword | KindFunctionKeyword | KindIfKeyword | KindImportKeyword | KindInKeyword | KindInstanceOfKeyword | KindNewKeyword | KindNullKeyword | KindReturnKeyword | KindSuperKeyword | KindSwitchKeyword | KindThisKeyword | KindThrowKeyword | KindTrueKeyword | KindTryKeyword | KindTypeOfKeyword | KindVarKeyword | KindVoidKeyword | KindWhileKeyword | KindWithKeyword | KindImplementsKeyword | KindInterfaceKeyword | KindLetKeyword | KindPackageKeyword | KindPrivateKeyword | KindProtectedKeyword | KindPublicKeyword | KindStaticKeyword | KindYieldKeyword | KindAbstractKeyword | KindAccessorKeyword | KindAsKeyword | KindAssertsKeyword | KindAssertKeyword | KindAnyKeyword | KindAsyncKeyword | KindAwaitKeyword | KindBooleanKeyword | KindConstructorKeyword | KindDeclareKeyword | KindGetKeyword | KindImmediateKeyword | KindInferKeyword | KindIntrinsicKeyword | KindIsKeyword | KindKeyOfKeyword | KindModuleKeyword | KindNamespaceKeyword | KindNeverKeyword | KindOutKeyword | KindReadonlyKeyword | KindRequireKeyword | KindNumberKeyword | KindObjectKeyword | KindSatisfiesKeyword | KindSetKeyword | KindStringKeyword | KindSymbolKeyword | KindTypeKeyword | KindUndefinedKeyword | KindUniqueKeyword | KindUnknownKeyword | KindUsingKeyword | KindFromKeyword | KindGlobalKeyword | KindBigIntKeyword | KindOverrideKeyword | KindOfKeyword | KindDeferKeyword
JsxTokenSyntaxKind = Kind // KindLessThanSlashToken | KindEndOfFile | KindConflictMarkerTrivia | KindJsxText | KindJsxTextAllWhiteSpaces | KindOpenBraceToken | KindLessThanToken
JSDocNodeSyntaxKind = Kind // KindJSDocTypeExpression | KindJSDocNameReference | KindJSDocAllType | KindJSDocNullableType | KindJSDocNonNullableType | KindJSDocOptionalType | KindJSDocVariadicType | KindJSDoc | KindJSDocText | KindJSDocTypeLiteral | KindJSDocSignature | KindJSDocLink | KindJSDocLinkCode | KindJSDocLinkPlain | KindJSDocUnknownTag | KindJSDocAugmentsTag | KindJSDocImplementsTag | KindJSDocDeprecatedTag | KindJSDocPublicTag | KindJSDocPrivateTag | KindJSDocProtectedTag | KindJSDocReadonlyTag | KindJSDocOverrideTag | KindJSDocCallbackTag | KindJSDocOverloadTag | KindJSDocParameterTag | KindJSDocReturnTag | KindJSDocThisTag | KindJSDocTypeTag | KindJSDocTemplateTag | KindJSDocTypedefTag | KindJSDocSeeTag | KindJSDocPropertyTag | KindJSDocThrowsTag | KindJSDocSatisfiesTag | KindJSDocImportTag
ImportPhaseModifierSyntaxKind = Kind // KindTypeKeyword | KindDeferKeyword
PostfixUnaryOperator = Kind // KindPlusPlusToken | KindMinusMinusToken
PrefixUnaryOperator = Kind // KindPlusToken | KindMinusToken | KindTildeToken | KindExclamationToken | KindPlusPlusToken | KindMinusMinusToken
AssignmentOperator = Kind // KindEqualsToken | KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskAsteriskEqualsToken | KindAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindCaretEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken
BinaryOperator = Kind // KindQuestionQuestionToken | KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword | KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken | KindAmpersandToken | KindBarToken | KindCaretToken | KindAmpersandAmpersandToken | KindBarBarToken | KindEqualsToken | KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskAsteriskEqualsToken | KindAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindCaretEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken | KindCommaToken
ExponentiationOperator = Kind // KindAsteriskAsteriskToken
MultiplicativeOperator = Kind // KindAsteriskToken | KindSlashToken | KindPercentToken
MultiplicativeOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken
AdditiveOperator = Kind // KindPlusToken | KindMinusToken
AdditiveOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken
ShiftOperator = Kind // KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken
ShiftOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken
RelationalOperator = Kind // KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword
RelationalOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword
EqualityOperator = Kind // KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken
EqualityOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword | KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken
BitwiseOperator = Kind // KindAmpersandToken | KindBarToken | KindCaretToken
BitwiseOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword | KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken | KindAmpersandToken | KindBarToken | KindCaretToken
LogicalOperator = Kind // KindAmpersandAmpersandToken | KindBarBarToken
LogicalOperatorOrHigher = Kind // KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword | KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken | KindAmpersandToken | KindBarToken | KindCaretToken | KindAmpersandAmpersandToken | KindBarBarToken
CompoundAssignmentOperator = Kind // KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskAsteriskEqualsToken | KindAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindCaretEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken
AssignmentOperatorOrHigher = Kind // KindQuestionQuestionToken | KindAsteriskAsteriskToken | KindAsteriskToken | KindSlashToken | KindPercentToken | KindPlusToken | KindMinusToken | KindLessThanLessThanToken | KindGreaterThanGreaterThanToken | KindGreaterThanGreaterThanGreaterThanToken | KindLessThanToken | KindLessThanEqualsToken | KindGreaterThanToken | KindGreaterThanEqualsToken | KindInstanceOfKeyword | KindInKeyword | KindEqualsEqualsToken | KindEqualsEqualsEqualsToken | KindExclamationEqualsEqualsToken | KindExclamationEqualsToken | KindAmpersandToken | KindBarToken | KindCaretToken | KindAmpersandAmpersandToken | KindBarBarToken | KindEqualsToken | KindPlusEqualsToken | KindMinusEqualsToken | KindAsteriskAsteriskEqualsToken | KindAsteriskEqualsToken | KindSlashEqualsToken | KindPercentEqualsToken | KindAmpersandEqualsToken | KindBarEqualsToken | KindCaretEqualsToken | KindLessThanLessThanEqualsToken | KindGreaterThanGreaterThanGreaterThanEqualsToken | KindGreaterThanGreaterThanEqualsToken | KindBarBarEqualsToken | KindAmpersandAmpersandEqualsToken | KindQuestionQuestionEqualsToken
LogicalOrCoalescingAssignmentOperator = Kind // KindAmpersandAmpersandEqualsToken | KindBarBarEqualsToken | KindQuestionQuestionEqualsToken
)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,53 @@
package ast
type ModifierFlags uint32
const (
ModifierFlagsNone ModifierFlags = 0
// Syntactic/JSDoc modifiers
ModifierFlagsPublic ModifierFlags = 1 << 0 // Property/Method
ModifierFlagsPrivate ModifierFlags = 1 << 1 // Property/Method
ModifierFlagsProtected ModifierFlags = 1 << 2 // Property/Method
ModifierFlagsReadonly ModifierFlags = 1 << 3 // Property/Method
ModifierFlagsOverride ModifierFlags = 1 << 4 // Override method
// Syntactic-only modifiers
ModifierFlagsExport ModifierFlags = 1 << 5 // Declarations
ModifierFlagsAbstract ModifierFlags = 1 << 6 // Class/Method/ConstructSignature
ModifierFlagsAmbient ModifierFlags = 1 << 7 // Declarations (declare keyword)
ModifierFlagsStatic ModifierFlags = 1 << 8 // Property/Method
ModifierFlagsAccessor ModifierFlags = 1 << 9 // Property
ModifierFlagsAsync ModifierFlags = 1 << 10 // Property/Method/Function
ModifierFlagsDefault ModifierFlags = 1 << 11 // Function/Class (export default declaration)
ModifierFlagsConst ModifierFlags = 1 << 12 // Const enum
ModifierFlagsIn ModifierFlags = 1 << 13 // Contravariance modifier
ModifierFlagsOut ModifierFlags = 1 << 14 // Covariance modifier
ModifierFlagsDecorator ModifierFlags = 1 << 15 // Contains a decorator
// JSDoc-only modifiers
ModifierFlagsDeprecated ModifierFlags = 1 << 16 // Deprecated tag
// Cache-only JSDoc-modifiers. Should match order of Syntactic/JSDoc modifiers, above.
ModifierFlagsJSDocPublic ModifierFlags = 1 << 23 // if this value changes, `selectEffectiveModifierFlags` must change accordingly
ModifierFlagsJSDocPrivate ModifierFlags = 1 << 24
ModifierFlagsJSDocProtected ModifierFlags = 1 << 25
ModifierFlagsJSDocReadonly ModifierFlags = 1 << 26
ModifierFlagsJSDocOverride ModifierFlags = 1 << 27
ModifierFlagsHasComputedJSDocModifiers ModifierFlags = 1 << 28 // Indicates the computed modifier flags include modifiers from JSDoc.
ModifierFlagsHasComputedFlags ModifierFlags = 1 << 29 // Modifier flags have been computed
ModifierFlagsSyntacticOrJSDocModifiers = ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected | ModifierFlagsReadonly | ModifierFlagsOverride
ModifierFlagsSyntacticOnlyModifiers = ModifierFlagsExport | ModifierFlagsAmbient | ModifierFlagsAbstract | ModifierFlagsStatic | ModifierFlagsAccessor | ModifierFlagsAsync | ModifierFlagsDefault | ModifierFlagsConst | ModifierFlagsIn | ModifierFlagsOut | ModifierFlagsDecorator
ModifierFlagsSyntacticModifiers = ModifierFlagsSyntacticOrJSDocModifiers | ModifierFlagsSyntacticOnlyModifiers
ModifierFlagsJSDocCacheOnlyModifiers = ModifierFlagsJSDocPublic | ModifierFlagsJSDocPrivate | ModifierFlagsJSDocProtected | ModifierFlagsJSDocReadonly | ModifierFlagsJSDocOverride
ModifierFlagsJSDocOnlyModifiers = ModifierFlagsDeprecated
ModifierFlagsNonCacheOnlyModifiers = ModifierFlagsSyntacticOrJSDocModifiers | ModifierFlagsSyntacticOnlyModifiers | ModifierFlagsJSDocOnlyModifiers
ModifierFlagsAccessibilityModifier = ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected
// Accessibility modifiers and 'readonly' can be attached to a parameter in a constructor to make it a property.
ModifierFlagsParameterPropertyModifier = ModifierFlagsAccessibilityModifier | ModifierFlagsReadonly | ModifierFlagsOverride
ModifierFlagsNonPublicAccessibilityModifier = ModifierFlagsPrivate | ModifierFlagsProtected
ModifierFlagsTypeScriptModifier = ModifierFlagsAmbient | ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected | ModifierFlagsReadonly | ModifierFlagsAbstract | ModifierFlagsConst | ModifierFlagsOverride | ModifierFlagsIn | ModifierFlagsOut
ModifierFlagsExportDefault = ModifierFlagsExport | ModifierFlagsDefault
ModifierFlagsAll = ModifierFlagsExport | ModifierFlagsAmbient | ModifierFlagsPublic | ModifierFlagsPrivate | ModifierFlagsProtected | ModifierFlagsStatic | ModifierFlagsReadonly | ModifierFlagsAbstract | ModifierFlagsAccessor | ModifierFlagsAsync | ModifierFlagsDefault | ModifierFlagsConst | ModifierFlagsDeprecated | ModifierFlagsOverride | ModifierFlagsIn | ModifierFlagsOut | ModifierFlagsDecorator
ModifierFlagsModifier = ModifierFlagsAll & ^ModifierFlagsDecorator
ModifierFlagsJavaScript = ModifierFlagsExport | ModifierFlagsStatic | ModifierFlagsAccessor | ModifierFlagsAsync | ModifierFlagsDefault
)

View File

@@ -0,0 +1,73 @@
package ast
type NodeFlags uint32
const (
NodeFlagsNone NodeFlags = 0
NodeFlagsLet NodeFlags = 1 << 0 // Variable declaration
NodeFlagsConst NodeFlags = 1 << 1 // Variable declaration
NodeFlagsUsing NodeFlags = 1 << 2 // Variable declaration
NodeFlagsReparsed NodeFlags = 1 << 3 // Node was synthesized during parsing
NodeFlagsSynthesized NodeFlags = 1 << 4 // Node was synthesized during transformation
NodeFlagsOptionalChain NodeFlags = 1 << 5 // Chained MemberExpression rooted to a pseudo-OptionalExpression
NodeFlagsExportContext NodeFlags = 1 << 6 // Export context (initialized by binding)
NodeFlagsContainsThis NodeFlags = 1 << 7 // Interface contains references to "this"
NodeFlagsHasImplicitReturn NodeFlags = 1 << 8 // If function implicitly returns on one of codepaths (initialized by binding)
NodeFlagsHasExplicitReturn NodeFlags = 1 << 9 // If function has explicit reachable return on one of codepaths (initialized by binding)
NodeFlagsDisallowInContext NodeFlags = 1 << 10 // If node was parsed in a context where 'in-expressions' are not allowed
NodeFlagsYieldContext NodeFlags = 1 << 11 // If node was parsed in the 'yield' context created when parsing a generator
NodeFlagsDecoratorContext NodeFlags = 1 << 12 // If node was parsed as part of a decorator
NodeFlagsAwaitContext NodeFlags = 1 << 13 // If node was parsed in the 'await' context created when parsing an async function
NodeFlagsDisallowConditionalTypesContext NodeFlags = 1 << 14 // If node was parsed in a context where conditional types are not allowed
NodeFlagsThisNodeHasError NodeFlags = 1 << 15 // If the parser encountered an error when parsing the code that created this node
NodeFlagsJavaScriptFile NodeFlags = 1 << 16 // If node was parsed in a JavaScript
NodeFlagsThisNodeOrAnySubNodesHasError NodeFlags = 1 << 17 // If this node or any of its children had an error
NodeFlagsHasAsyncFunctions NodeFlags = 1 << 18 // If the file has async functions (initialized by binding)
// NodeFlagsHasAggregatedChildData is deprecated. Use `subtreeFacts` instead.
// These flags will be set when the parser encounters a dynamic import expression or 'import.meta' to avoid
// walking the tree if the flags are not set. However, these flags are just a approximation
// (hence why it's named "PossiblyContainsDynamicImport") because once set, the flags never get cleared.
// During editing, if a dynamic import is removed, incremental parsing will *NOT* clear this flag.
// This means that the tree will always be traversed during module resolution, or when looking for external module indicators.
// However, the removal operation should not occur often and in the case of the
// removal, it is likely that users will add the import anyway.
// The advantage of this approach is its simplicity. For the case of batch compilation,
// we guarantee that users won't have to pay the price of walking the tree if a dynamic import isn't used.
NodeFlagsPossiblyContainsDynamicImport NodeFlags = 1 << 19
NodeFlagsPossiblyContainsImportMeta NodeFlags = 1 << 20
NodeFlagsHasJSDoc NodeFlags = 1 << 21 // If node has preceding JSDoc comment(s)
NodeFlagsJSDoc NodeFlags = 1 << 22 // If node was parsed inside jsdoc
NodeFlagsAmbient NodeFlags = 1 << 23 // If node was inside an ambient context -- a declaration file, or inside something with the `declare` modifier.
NodeFlagsInWithStatement NodeFlags = 1 << 24 // If any ancestor of node was the `statement` of a WithStatement (not the `expression`)
NodeFlagsJsonFile NodeFlags = 1 << 25 // If node was parsed in a Json
NodeFlagsPossiblyContainsDeprecatedTag NodeFlags = 1 << 26 // Set during parse if comment text contains '@deprecated'; must confirm via JSDoc lookup
NodeFlagsUnreachable NodeFlags = 1 << 27 // If node is unreachable according to the binder
NodeFlagsReparserTransformedLiteral NodeFlags = 1 << 28 // If node was transformed during parsing, making its' naive text source not match the AST
NodeFlagsBlockScoped = NodeFlagsLet | NodeFlagsConst | NodeFlagsUsing
NodeFlagsConstant = NodeFlagsConst | NodeFlagsUsing
NodeFlagsAwaitUsing = NodeFlagsConst | NodeFlagsUsing // Variable declaration (NOTE: on a single node these flags would otherwise be mutually exclusive)
NodeFlagsReachabilityCheckFlags = NodeFlagsHasImplicitReturn | NodeFlagsHasExplicitReturn
NodeFlagsReachabilityAndEmitFlags = NodeFlagsReachabilityCheckFlags | NodeFlagsHasAsyncFunctions
// Parsing context flags
NodeFlagsContextFlags NodeFlags = NodeFlagsDisallowInContext | NodeFlagsDisallowConditionalTypesContext | NodeFlagsYieldContext | NodeFlagsDecoratorContext | NodeFlagsAwaitContext | NodeFlagsJavaScriptFile | NodeFlagsInWithStatement | NodeFlagsAmbient
// Exclude these flags when parsing a Type
NodeFlagsTypeExcludesFlags NodeFlags = NodeFlagsYieldContext | NodeFlagsAwaitContext
// Represents all flags that are potentially set once and
// never cleared on SourceFiles which get re-used in between incremental parses.
// See the comment above on `PossiblyContainsDynamicImport` and `PossiblyContainsImportMeta`.
NodeFlagsPermanentlySetIncrementalFlags NodeFlags = NodeFlagsPossiblyContainsDynamicImport | NodeFlagsPossiblyContainsImportMeta
// The following flags repurpose other NodeFlags as different meanings for Identifier nodes
NodeFlagsIdentifierHasExtendedUnicodeEscape NodeFlags = NodeFlagsContainsThis // Indicates whether the identifier contains an extended unicode escape sequence
NodeFlagsIdentifierIsInJSDocNamespace NodeFlags = NodeFlagsHasAsyncFunctions // Indicates the identifier is the innermost name of a JSDoc namespace declaration
// The following flag repurposes other NodeFlags for ModuleDeclaration nodes
NodeFlagsNestedNamespace NodeFlags = NodeFlagsOptionalChain // If ModuleDeclaration is a nested namespace (e.g. inner part of A.B.C)
)

View File

@@ -0,0 +1,149 @@
package ast
import (
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/tspath"
)
type SourceFileParseOptions struct {
FileName string
Path tspath.Path
ExternalModuleIndicatorOptions ExternalModuleIndicatorOptions
}
type ExternalModuleIndicatorOptions struct {
JSX bool
Force bool
}
func GetExternalModuleIndicatorOptions(fileName string, options *core.CompilerOptions, metadata SourceFileMetaData) ExternalModuleIndicatorOptions {
if tspath.IsDeclarationFileName(fileName) {
return ExternalModuleIndicatorOptions{}
}
switch options.GetEmitModuleDetectionKind() {
case core.ModuleDetectionKindForce:
// All non-declaration files are modules, declaration files still do the usual isFileProbablyExternalModule
return ExternalModuleIndicatorOptions{Force: true}
case core.ModuleDetectionKindLegacy:
// Files are modules if they have imports, exports, or import.meta
return ExternalModuleIndicatorOptions{}
case core.ModuleDetectionKindAuto:
// If module is nodenext or node16, all esm format files are modules
// If jsx is react-jsx or react-jsxdev then jsx tags force module-ness
// otherwise, the presence of import or export statments (or import.meta) implies module-ness
return ExternalModuleIndicatorOptions{
JSX: options.Jsx == core.JsxEmitReactJSX || options.Jsx == core.JsxEmitReactJSXDev,
Force: isFileForcedToBeModuleByFormat(fileName, options, metadata),
}
default:
return ExternalModuleIndicatorOptions{}
}
}
var isFileForcedToBeModuleByFormatExtensions = []string{tspath.ExtensionCjs, tspath.ExtensionCts, tspath.ExtensionMjs, tspath.ExtensionMts}
func isFileForcedToBeModuleByFormat(fileName string, options *core.CompilerOptions, metadata SourceFileMetaData) bool {
// Excludes declaration files - they still require an explicit `export {}` or the like
// for back compat purposes. The only non-declaration files _not_ forced to be a module are `.js` files
// that aren't esm-mode (meaning not in a `type: module` scope).
if GetImpliedNodeFormatForEmitWorker(fileName, options.GetEmitModuleKind(), metadata) == core.ModuleKindESNext || tspath.FileExtensionIsOneOf(fileName, isFileForcedToBeModuleByFormatExtensions) {
return true
}
return false
}
func SetExternalModuleIndicator(file *SourceFile, opts ExternalModuleIndicatorOptions) {
file.ExternalModuleIndicator = getExternalModuleIndicator(file, opts)
}
func getExternalModuleIndicator(file *SourceFile, opts ExternalModuleIndicatorOptions) *Node {
if file.ScriptKind == core.ScriptKindJSON {
return nil
}
if node := isFileProbablyExternalModule(file); node != nil {
return node
}
if file.IsDeclarationFile {
return nil
}
if opts.JSX {
if node := isFileModuleFromUsingJSXTag(file); node != nil {
return node
}
}
if opts.Force {
return file.AsNode()
}
return nil
}
func isFileProbablyExternalModule(sourceFile *SourceFile) *Node {
for _, statement := range sourceFile.Statements.Nodes {
if isAnExternalModuleIndicatorNode(statement) {
return statement
}
}
return getImportMetaIfNecessary(sourceFile)
}
func isAnExternalModuleIndicatorNode(node *Node) bool {
return HasSyntacticModifier(node, ModifierFlagsExport) ||
IsImportEqualsDeclaration(node) && IsExternalModuleReference(node.AsImportEqualsDeclaration().ModuleReference) ||
IsImportDeclaration(node) || IsExportAssignment(node) || IsExportDeclaration(node)
}
func getImportMetaIfNecessary(sourceFile *SourceFile) *Node {
if sourceFile.AsNode().Flags&NodeFlagsPossiblyContainsImportMeta != 0 {
return findChildNode(sourceFile.AsNode(), IsImportMeta)
}
return nil
}
func findChildNode(root *Node, check func(*Node) bool) *Node {
var result *Node
var visit func(*Node) bool
visit = func(node *Node) bool {
if check(node) {
result = node
return true
}
return node.ForEachChild(visit)
}
visit(root)
return result
}
func isFileModuleFromUsingJSXTag(file *SourceFile) *Node {
return walkTreeForJSXTags(file.AsNode())
}
// This is a somewhat unavoidable full tree walk to locate a JSX tag - `import.meta` requires the same,
// but we avoid that walk (or parts of it) if at all possible using the `PossiblyContainsImportMeta` node flag.
// Unfortunately, there's no `NodeFlag` space to do the same for JSX.
func walkTreeForJSXTags(node *Node) *Node {
var found *Node
var visitor func(node *Node) bool
visitor = func(node *Node) bool {
if found != nil {
return true
}
if node.SubtreeFacts()&SubtreeContainsJsx == 0 {
return false
}
if IsJsxOpeningLikeElement(node) || IsJsxFragment(node) {
found = node
return true
}
return node.ForEachChild(visitor)
}
visitor(node)
return found
}

View File

@@ -0,0 +1,111 @@
package ast
import (
"unicode/utf8"
"github.com/microsoft/typescript-go/internal/stringutil"
)
// PositionMap provides bidirectional mapping between UTF-8 byte offsets (used by Go)
// and UTF-16 code unit offsets (used by JavaScript/TypeScript).
//
// For ASCII-only text, the two are identical. For text containing non-ASCII characters,
// the offsets diverge because multi-byte UTF-8 sequences map to different numbers of
// UTF-16 code units:
// - U+0000..U+007F: 1 byte in UTF-8, 1 code unit in UTF-16
// - U+0080..U+07FF: 2 bytes in UTF-8, 1 code unit in UTF-16
// - U+0800..U+FFFF: 3 bytes in UTF-8, 1 code unit in UTF-16
// - U+10000..U+10FFFF: 4 bytes in UTF-8, 2 code units in UTF-16 (surrogate pair)
type PositionMap struct {
// asciiOnly is true if the text contains only ASCII characters,
// meaning UTF-8 byte offsets and UTF-16 code unit offsets are identical.
asciiOnly bool
// For each multi-byte character, we store:
// - the UTF-8 byte offset of the character
// - the cumulative delta (utf8Offset - utf16Offset) at that character
// This allows O(log n) conversion in either direction.
//
// entries[i].utf8Pos is the byte offset of the i-th multi-byte character.
// entries[i].delta is the total (utf8 - utf16) difference accumulated
// through and including the i-th multi-byte character.
entries []positionMapEntry
}
type positionMapEntry struct {
utf8Pos int // UTF-8 byte offset AFTER this multi-byte character
delta int // cumulative (utf8 - utf16) offset difference after this character
}
// ComputePositionMap builds a PositionMap for the given text.
func ComputePositionMap(text string) *PositionMap {
pm := &PositionMap{}
delta := 0
for i := 0; i < len(text); {
b := text[i]
if b < utf8.RuneSelf {
i++
continue
}
r, size := stringutil.DecodeJSStringRune(text[i:])
utf16Size := 1
if r >= 0x10000 {
utf16Size = 2
}
delta += size - utf16Size
pm.entries = append(pm.entries, positionMapEntry{utf8Pos: i + size, delta: delta})
i += size
}
pm.asciiOnly = len(pm.entries) == 0
return pm
}
// IsAsciiOnly returns true if the text is ASCII-only,
// meaning UTF-8 and UTF-16 offsets are identical.
func (pm *PositionMap) IsAsciiOnly() bool {
return pm.asciiOnly
}
// UTF8ToUTF16 converts a UTF-8 byte offset to a UTF-16 code unit offset.
func (pm *PositionMap) UTF8ToUTF16(utf8Offset int) int {
if pm.asciiOnly {
return utf8Offset
}
// Binary search: find the last entry where utf8Pos <= utf8Offset
lo, hi := 0, len(pm.entries)
for lo < hi {
mid := lo + (hi-lo)/2
if pm.entries[mid].utf8Pos <= utf8Offset {
lo = mid + 1
} else {
hi = mid
}
}
if lo == 0 {
// Before any multi-byte character
return utf8Offset
}
return utf8Offset - pm.entries[lo-1].delta
}
// UTF16ToUTF8 converts a UTF-16 code unit offset to a UTF-8 byte offset.
func (pm *PositionMap) UTF16ToUTF8(utf16Offset int) int {
if pm.asciiOnly {
return utf16Offset
}
// We need the last entry where (utf8Pos - delta) <= utf16Offset.
// (utf8Pos - delta) is the UTF-16 offset of that entry's character.
lo, hi := 0, len(pm.entries)
for lo < hi {
mid := lo + (hi-lo)/2
utf16Pos := pm.entries[mid].utf8Pos - pm.entries[mid].delta
if utf16Pos <= utf16Offset {
lo = mid + 1
} else {
hi = mid
}
}
if lo == 0 {
return utf16Offset
}
return utf16Offset + pm.entries[lo-1].delta
}

View File

@@ -0,0 +1,225 @@
package ast_test
import (
"os"
"strings"
"testing"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/stringutil"
)
func TestPositionMapASCII(t *testing.T) {
t.Parallel()
text := "const x = 1;"
pm := ast.ComputePositionMap(text)
if !pm.IsAsciiOnly() {
t.Fatal("expected ASCII-only")
}
for i := 0; i <= len(text); i++ {
if got := pm.UTF8ToUTF16(i); got != i {
t.Errorf("UTF8ToUTF16(%d) = %d, want %d", i, got, i)
}
if got := pm.UTF16ToUTF8(i); got != i {
t.Errorf("UTF16ToUTF8(%d) = %d, want %d", i, got, i)
}
}
}
func TestPositionMapTwoByte(t *testing.T) {
t.Parallel()
// "café" — é (U+00E9) is 2 bytes UTF-8, 1 code unit UTF-16
text := "const café = 1;\nconst x = 2;"
pm := ast.ComputePositionMap(text)
if pm.IsAsciiOnly() {
t.Fatal("expected non-ASCII")
}
// Everything before é (byte offset 9) should be identity
for i := range 10 {
if got := pm.UTF8ToUTF16(i); got != i {
t.Errorf("before é: UTF8ToUTF16(%d) = %d, want %d", i, got, i)
}
}
// é starts at UTF-8 byte 9, UTF-16 offset 9: same
if got := pm.UTF8ToUTF16(9); got != 9 {
t.Errorf("at é: UTF8ToUTF16(9) = %d, want 9", got)
}
// After é (byte 11 in UTF-8 = code unit 10 in UTF-16), delta is 1
// ' ' after café: UTF-8 byte 11, UTF-16 offset 10
if got := pm.UTF8ToUTF16(11); got != 10 {
t.Errorf("after é: UTF8ToUTF16(11) = %d, want 10", got)
}
// 'x' on second line: UTF-8 byte 23, UTF-16 offset 22
xUTF8 := strings.LastIndex(text, "x")
if got := pm.UTF8ToUTF16(xUTF8); got != xUTF8-1 {
t.Errorf("at x: UTF8ToUTF16(%d) = %d, want %d", xUTF8, got, xUTF8-1)
}
// Reverse: UTF-16 offset 22 should map to UTF-8 byte 23
xUTF16 := xUTF8 - 1
if got := pm.UTF16ToUTF8(xUTF16); got != xUTF8 {
t.Errorf("reverse at x: UTF16ToUTF8(%d) = %d, want %d", xUTF16, got, xUTF8)
}
}
func TestPositionMapFourByte(t *testing.T) {
t.Parallel()
// 🎉 (U+1F389) is 4 bytes UTF-8, 2 code units UTF-16
text := `const a = "🎉";` + "\nconst b = 2;"
pm := ast.ComputePositionMap(text)
if pm.IsAsciiOnly() {
t.Fatal("expected non-ASCII")
}
// 🎉 starts at byte 11 (after `const a = "`)
// UTF-8: bytes 11-14 (4 bytes), UTF-16: units 11-12 (2 code units)
// After 🎉: UTF-8 byte 15, UTF-16 offset 13. Delta = 2.
// 'b' on second line
bUTF8 := strings.LastIndex(text, "b")
bUTF16 := bUTF8 - 2 // delta of 2 from emoji
if got := pm.UTF8ToUTF16(bUTF8); got != bUTF16 {
t.Errorf("at b: UTF8ToUTF16(%d) = %d, want %d", bUTF8, got, bUTF16)
}
if got := pm.UTF16ToUTF8(bUTF16); got != bUTF8 {
t.Errorf("reverse at b: UTF16ToUTF8(%d) = %d, want %d", bUTF16, got, bUTF8)
}
}
func TestPositionMapMultipleNonASCII(t *testing.T) {
t.Parallel()
// Mix of 2-byte and 4-byte characters
// "à" (U+00E0) = 2 bytes UTF-8, 1 code unit UTF-16 (delta +1)
// "🎉" (U+1F389) = 4 bytes UTF-8, 2 code units UTF-16 (delta +2)
text := "à🎉x"
pm := ast.ComputePositionMap(text)
// à: UTF-8 [0,2), UTF-16 [0,1)
// 🎉: UTF-8 [2,6), UTF-16 [1,3)
// x: UTF-8 [6,7), UTF-16 [3,4)
tests := []struct {
utf8 int
utf16 int
}{
{0, 0},
{2, 1}, // start of 🎉
{6, 3}, // x
{7, 4}, // end
}
for _, tt := range tests {
if got := pm.UTF8ToUTF16(tt.utf8); got != tt.utf16 {
t.Errorf("UTF8ToUTF16(%d) = %d, want %d", tt.utf8, got, tt.utf16)
}
if got := pm.UTF16ToUTF8(tt.utf16); got != tt.utf8 {
t.Errorf("UTF16ToUTF8(%d) = %d, want %d", tt.utf16, got, tt.utf8)
}
}
}
func TestPositionMapLoneSurrogateSentinel(t *testing.T) {
t.Parallel()
text := "a" + stringutil.EncodeJSStringRune(0xD800) + "b"
pm := ast.ComputePositionMap(text)
if pm.IsAsciiOnly() {
t.Fatal("expected non-ASCII")
}
if got := pm.UTF8ToUTF16(len(text)); got != 3 {
t.Errorf("UTF8ToUTF16(%d) = %d, want 3", len(text), got)
}
if got := pm.UTF16ToUTF8(2); got != len(text)-1 {
t.Errorf("UTF16ToUTF8(2) = %d, want %d", got, len(text)-1)
}
}
func TestPositionMapRoundtrip(t *testing.T) {
t.Parallel()
text := "let café = \"🎉\"; // naïve"
pm := ast.ComputePositionMap(text)
// Convert every valid UTF-16 position to UTF-8 and back
utf16Len := pm.UTF8ToUTF16(len(text))
for i := 0; i <= utf16Len; i++ {
utf8Pos := pm.UTF16ToUTF8(i)
back := pm.UTF8ToUTF16(utf8Pos)
if back != i {
t.Errorf("roundtrip UTF16->UTF8->UTF16: %d -> %d -> %d", i, utf8Pos, back)
}
}
}
func BenchmarkComputePositionMap_ASCII(b *testing.B) {
// ~10KB of ASCII TypeScript-like code
line := "const variable = someFunction(argument1, argument2);\n"
text := strings.Repeat(line, 200)
b.ResetTimer()
for range b.N {
ast.ComputePositionMap(text)
}
}
func BenchmarkComputePositionMap_NonASCII(b *testing.B) {
// Mix of ASCII and non-ASCII (comments with unicode)
line := "const café = \"héllo wörld 🎉\";\n"
text := strings.Repeat(line, 200)
b.ResetTimer()
for range b.N {
ast.ComputePositionMap(text)
}
}
func BenchmarkUTF8ToUTF16_ASCII(b *testing.B) {
line := "const variable = someFunction(argument1, argument2);\n"
text := strings.Repeat(line, 200)
pm := ast.ComputePositionMap(text)
positions := []int{0, 100, 500, 1000, 5000, len(text) - 1}
b.ResetTimer()
for range b.N {
for _, p := range positions {
pm.UTF8ToUTF16(p)
}
}
}
func BenchmarkUTF8ToUTF16_NonASCII(b *testing.B) {
line := "const café = \"héllo wörld 🎉\";\n"
text := strings.Repeat(line, 200)
pm := ast.ComputePositionMap(text)
positions := []int{0, 100, 500, 1000, 5000, len(text) - 1}
b.ResetTimer()
for range b.N {
for _, p := range positions {
pm.UTF8ToUTF16(p)
}
}
}
func BenchmarkUTF16ToUTF8_NonASCII(b *testing.B) {
line := "const café = \"héllo wörld 🎉\";\n"
text := strings.Repeat(line, 200)
pm := ast.ComputePositionMap(text)
utf16Len := pm.UTF8ToUTF16(len(text))
positions := []int{0, 100, 500, 1000, 3000, utf16Len - 1}
b.ResetTimer()
for range b.N {
for _, p := range positions {
pm.UTF16ToUTF8(p)
}
}
}
func BenchmarkComputePositionMap_CheckerTS(b *testing.B) {
data, err := os.ReadFile("../../_submodules/TypeScript/src/compiler/checker.ts")
if err != nil {
b.Skip("checker.ts not available:", err)
}
text := string(data)
b.ResetTimer()
for range b.N {
ast.ComputePositionMap(text)
}
}

View File

@@ -0,0 +1,717 @@
package ast
import (
"fmt"
)
type OperatorPrecedence int
const (
// Expression:
// AssignmentExpression
// Expression `,` AssignmentExpression
OperatorPrecedenceComma OperatorPrecedence = iota
// NOTE: `Spread` is higher than `Comma` due to how it is parsed in |ElementList|
// SpreadElement:
// `...` AssignmentExpression
OperatorPrecedenceSpread
// AssignmentExpression:
// ConditionalExpression
// YieldExpression
// ArrowFunction
// AsyncArrowFunction
// LeftHandSideExpression `=` AssignmentExpression
// LeftHandSideExpression AssignmentOperator AssignmentExpression
//
// NOTE: AssignmentExpression is broken down into several precedences due to the requirements
// of the parenthesizer rules.
// AssignmentExpression: YieldExpression
// YieldExpression:
// `yield`
// `yield` AssignmentExpression
// `yield` `*` AssignmentExpression
OperatorPrecedenceYield
// AssignmentExpression: LeftHandSideExpression `=` AssignmentExpression
// AssignmentExpression: LeftHandSideExpression AssignmentOperator AssignmentExpression
// AssignmentOperator: one of
// `*=` `/=` `%=` `+=` `-=` `<<=` `>>=` `>>>=` `&=` `^=` `|=` `**=`
OperatorPrecedenceAssignment
// NOTE: `Conditional` is considered higher than `Assignment` here, but in reality they have
// the same precedence.
// AssignmentExpression: ConditionalExpression
// ConditionalExpression:
// ShortCircuitExpression
// ShortCircuitExpression `?` AssignmentExpression `:` AssignmentExpression
OperatorPrecedenceConditional
// LogicalORExpression:
// LogicalANDExpression
// LogicalORExpression `||` LogicalANDExpression
OperatorPrecedenceLogicalOR
// LogicalANDExpression:
// BitwiseORExpression
// LogicalANDExprerssion `&&` BitwiseORExpression
OperatorPrecedenceLogicalAND
// BitwiseORExpression:
// BitwiseXORExpression
// BitwiseORExpression `|` BitwiseXORExpression
OperatorPrecedenceBitwiseOR
// BitwiseXORExpression:
// BitwiseANDExpression
// BitwiseXORExpression `^` BitwiseANDExpression
OperatorPrecedenceBitwiseXOR
// BitwiseANDExpression:
// EqualityExpression
// BitwiseANDExpression `&` EqualityExpression
OperatorPrecedenceBitwiseAND
// EqualityExpression:
// RelationalExpression
// EqualityExpression `==` RelationalExpression
// EqualityExpression `!=` RelationalExpression
// EqualityExpression `===` RelationalExpression
// EqualityExpression `!==` RelationalExpression
OperatorPrecedenceEquality
// RelationalExpression:
// ShiftExpression
// RelationalExpression `<` ShiftExpression
// RelationalExpression `>` ShiftExpression
// RelationalExpression `<=` ShiftExpression
// RelationalExpression `>=` ShiftExpression
// RelationalExpression `instanceof` ShiftExpression
// RelationalExpression `in` ShiftExpression
// [+TypeScript] RelationalExpression `as` Type
OperatorPrecedenceRelational
// ShiftExpression:
// AdditiveExpression
// ShiftExpression `<<` AdditiveExpression
// ShiftExpression `>>` AdditiveExpression
// ShiftExpression `>>>` AdditiveExpression
OperatorPrecedenceShift
// AdditiveExpression:
// MultiplicativeExpression
// AdditiveExpression `+` MultiplicativeExpression
// AdditiveExpression `-` MultiplicativeExpression
OperatorPrecedenceAdditive
// MultiplicativeExpression:
// ExponentiationExpression
// MultiplicativeExpression MultiplicativeOperator ExponentiationExpression
// MultiplicativeOperator: one of `*`, `/`, `%`
OperatorPrecedenceMultiplicative
// ExponentiationExpression:
// UnaryExpression
// UpdateExpression `**` ExponentiationExpression
OperatorPrecedenceExponentiation
// UnaryExpression:
// UpdateExpression
// `delete` UnaryExpression
// `void` UnaryExpression
// `typeof` UnaryExpression
// `+` UnaryExpression
// `-` UnaryExpression
// `~` UnaryExpression
// `!` UnaryExpression
// AwaitExpression
// UpdateExpression: // TODO: Do we need to investigate the precedence here?
// `++` UnaryExpression
// `--` UnaryExpression
OperatorPrecedenceUnary
// UpdateExpression:
// LeftHandSideExpression
// LeftHandSideExpression `++`
// LeftHandSideExpression `--`
OperatorPrecedenceUpdate
// LeftHandSideExpression:
// NewExpression
// NewExpression:
// MemberExpression
// `new` NewExpression
OperatorPrecedenceLeftHandSide
// LeftHandSideExpression:
// OptionalExpression
// OptionalExpression:
// MemberExpression OptionalChain
// CallExpression OptionalChain
// OptionalExpression OptionalChain
OperatorPrecedenceOptionalChain
// LeftHandSideExpression:
// CallExpression
// CallExpression:
// CoverCallExpressionAndAsyncArrowHead
// SuperCall
// ImportCall
// CallExpression Arguments
// CallExpression `[` Expression `]`
// CallExpression `.` IdentifierName
// CallExpression TemplateLiteral
// MemberExpression:
// PrimaryExpression
// MemberExpression `[` Expression `]`
// MemberExpression `.` IdentifierName
// MemberExpression TemplateLiteral
// SuperProperty
// MetaProperty
// `new` MemberExpression Arguments
OperatorPrecedenceMember
// TODO: JSXElement?
// PrimaryExpression:
// `this`
// IdentifierReference
// Literal
// ArrayLiteral
// ObjectLiteral
// FunctionExpression
// ClassExpression
// GeneratorExpression
// AsyncFunctionExpression
// AsyncGeneratorExpression
// RegularExpressionLiteral
// TemplateLiteral
OperatorPrecedencePrimary
// PrimaryExpression:
// CoverParenthesizedExpressionAndArrowParameterList
OperatorPrecedenceParentheses
OperatorPrecedenceLowest = OperatorPrecedenceComma
OperatorPrecedenceHighest = OperatorPrecedenceParentheses
OperatorPrecedenceDisallowComma = OperatorPrecedenceYield
// ShortCircuitExpression:
// LogicalORExpression
// CoalesceExpression
// CoalesceExpression:
// CoalesceExpressionHead `??` BitwiseORExpression
// CoalesceExpressionHead:
// CoalesceExpression
// BitwiseORExpression
OperatorPrecedenceCoalesce = OperatorPrecedenceLogicalOR
// -1 is lower than all other precedences. Returning it will cause binary expression
// parsing to stop.
OperatorPrecedenceInvalid OperatorPrecedence = -1
)
func getOperator(expression *Expression) Kind {
switch expression.Kind {
case KindBinaryExpression:
return expression.AsBinaryExpression().OperatorToken.Kind
case KindPrefixUnaryExpression:
return expression.AsPrefixUnaryExpression().Operator
case KindPostfixUnaryExpression:
return expression.AsPostfixUnaryExpression().Operator
default:
return expression.Kind
}
}
// Gets the precedence of an expression
func GetExpressionPrecedence(expression *Expression) OperatorPrecedence {
operator := getOperator(expression)
var flags OperatorPrecedenceFlags
if expression.Kind == KindNewExpression && expression.ArgumentList() == nil {
flags = OperatorPrecedenceFlagsNewWithoutArguments
} else if IsOptionalChain(expression) {
flags = OperatorPrecedenceFlagsOptionalChain
}
return GetOperatorPrecedence(expression.Kind, operator, flags)
}
type OperatorPrecedenceFlags int
const (
OperatorPrecedenceFlagsNone OperatorPrecedenceFlags = 0
OperatorPrecedenceFlagsNewWithoutArguments OperatorPrecedenceFlags = 1 << 0
OperatorPrecedenceFlagsOptionalChain OperatorPrecedenceFlags = 1 << 1
)
// Gets the precedence of an operator
func GetOperatorPrecedence(nodeKind Kind, operatorKind Kind, flags OperatorPrecedenceFlags) OperatorPrecedence {
switch nodeKind {
case KindSpreadElement:
return OperatorPrecedenceSpread
case KindYieldExpression:
return OperatorPrecedenceYield
// !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting
case KindArrowFunction:
return OperatorPrecedenceAssignment
case KindConditionalExpression:
return OperatorPrecedenceConditional
case KindBinaryExpression:
switch operatorKind {
case KindCommaToken:
return OperatorPrecedenceComma
case KindEqualsToken,
KindPlusEqualsToken,
KindMinusEqualsToken,
KindAsteriskAsteriskEqualsToken,
KindAsteriskEqualsToken,
KindSlashEqualsToken,
KindPercentEqualsToken,
KindLessThanLessThanEqualsToken,
KindGreaterThanGreaterThanEqualsToken,
KindGreaterThanGreaterThanGreaterThanEqualsToken,
KindAmpersandEqualsToken,
KindCaretEqualsToken,
KindBarEqualsToken,
KindBarBarEqualsToken,
KindAmpersandAmpersandEqualsToken,
KindQuestionQuestionEqualsToken:
return OperatorPrecedenceAssignment
default:
return GetBinaryOperatorPrecedence(operatorKind)
}
// TODO: Should prefix `++` and `--` be moved to the `Update` precedence?
case KindTypeAssertionExpression,
KindNonNullExpression,
KindPrefixUnaryExpression,
KindTypeOfExpression,
KindVoidExpression,
KindDeleteExpression,
KindAwaitExpression:
return OperatorPrecedenceUnary
case KindPostfixUnaryExpression:
return OperatorPrecedenceUpdate
// !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting
case KindPropertyAccessExpression, KindElementAccessExpression:
if flags&OperatorPrecedenceFlagsOptionalChain != 0 {
return OperatorPrecedenceOptionalChain
}
return OperatorPrecedenceMember
case KindCallExpression:
if flags&OperatorPrecedenceFlagsOptionalChain != 0 {
return OperatorPrecedenceOptionalChain
}
return OperatorPrecedenceMember
// !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting
case KindNewExpression:
if flags&OperatorPrecedenceFlagsNewWithoutArguments != 0 {
return OperatorPrecedenceLeftHandSide
}
return OperatorPrecedenceMember
// !!! By necessity, this differs from the old compiler to better align with ParenthesizerRules. consider backporting
case KindTaggedTemplateExpression, KindMetaProperty, KindExpressionWithTypeArguments:
return OperatorPrecedenceMember
case KindAsExpression,
KindSatisfiesExpression:
return OperatorPrecedenceRelational
case KindThisKeyword,
KindSuperKeyword,
KindImportKeyword,
KindIdentifier,
KindPrivateIdentifier,
KindNullKeyword,
KindTrueKeyword,
KindFalseKeyword,
KindNumericLiteral,
KindBigIntLiteral,
KindStringLiteral,
KindArrayLiteralExpression,
KindObjectLiteralExpression,
KindFunctionExpression,
KindClassExpression,
KindRegularExpressionLiteral,
KindNoSubstitutionTemplateLiteral,
KindTemplateExpression,
KindOmittedExpression,
KindJsxElement,
KindJsxSelfClosingElement,
KindJsxFragment,
KindMissingDeclaration:
return OperatorPrecedencePrimary
// !!! By necessity, this differs from the old compiler to support emit. consider backporting
case KindParenthesizedExpression:
return OperatorPrecedenceParentheses
default:
return OperatorPrecedenceInvalid
}
}
// Gets the precedence of a binary operator
func GetBinaryOperatorPrecedence(operatorKind Kind) OperatorPrecedence {
switch operatorKind {
case KindQuestionQuestionToken:
return OperatorPrecedenceCoalesce
case KindBarBarToken:
return OperatorPrecedenceLogicalOR
case KindAmpersandAmpersandToken:
return OperatorPrecedenceLogicalAND
case KindBarToken:
return OperatorPrecedenceBitwiseOR
case KindCaretToken:
return OperatorPrecedenceBitwiseXOR
case KindAmpersandToken:
return OperatorPrecedenceBitwiseAND
case KindEqualsEqualsToken, KindExclamationEqualsToken, KindEqualsEqualsEqualsToken, KindExclamationEqualsEqualsToken:
return OperatorPrecedenceEquality
case KindLessThanToken, KindGreaterThanToken, KindLessThanEqualsToken, KindGreaterThanEqualsToken,
KindInstanceOfKeyword, KindInKeyword, KindAsKeyword, KindSatisfiesKeyword:
return OperatorPrecedenceRelational
case KindLessThanLessThanToken, KindGreaterThanGreaterThanToken, KindGreaterThanGreaterThanGreaterThanToken:
return OperatorPrecedenceShift
case KindPlusToken, KindMinusToken:
return OperatorPrecedenceAdditive
case KindAsteriskToken, KindSlashToken, KindPercentToken:
return OperatorPrecedenceMultiplicative
case KindAsteriskAsteriskToken:
return OperatorPrecedenceExponentiation
}
// -1 is lower than all other precedences. Returning it will cause binary expression
// parsing to stop.
return OperatorPrecedenceInvalid
}
// Gets the leftmost expression of an expression, e.g. `a` in `a.b`, `a[b]`, `a++`, `a+b`, `a?b:c`, `a as B`, etc.
func GetLeftmostExpression(node *Expression, stopAtCallExpressions bool) *Expression {
for {
switch node.Kind {
case KindPostfixUnaryExpression:
node = node.AsPostfixUnaryExpression().Operand
continue
case KindBinaryExpression:
node = node.AsBinaryExpression().Left
continue
case KindConditionalExpression:
node = node.AsConditionalExpression().Condition
continue
case KindTaggedTemplateExpression:
node = node.AsTaggedTemplateExpression().Tag
continue
case KindCallExpression:
if stopAtCallExpressions {
return node
}
fallthrough
case KindAsExpression,
KindElementAccessExpression,
KindPropertyAccessExpression,
KindNonNullExpression,
KindPartiallyEmittedExpression,
KindSatisfiesExpression:
node = node.Expression()
continue
}
return node
}
}
type TypePrecedence int32
const (
// Conditional precedence (lowest)
//
// Type[Extends]:
// ConditionalTypeNode[?Extends]
//
// ConditionalTypeNode[Extends]:
// [~Extends] UnionTypeNode `extends` Type[+Extends] `?` Type[~Extends] `:` Type[~Extends]
//
TypePrecedenceConditional TypePrecedence = iota
// JSDoc precedence (optional and variadic types)
//
// JSDocType:
// `...`? Type `=`?
TypePrecedenceJSDoc
// Function precedence
//
// Type[Extends]:
// ConditionalTypeNode[?Extends]
// FunctionTypeNode[?Extends]
// ConstructorTypeNode[?Extends]
//
// ConditionalTypeNode[Extends]:
// UnionTypeNode
//
// FunctionTypeNode[Extends]:
// TypeParameters? ArrowParameters `=>` Type[?Extends]
//
// ConstructorTypeNode[Extends]:
// `abstract`? TypeParameters? ArrowParameters `=>` Type[?Extends]
//
TypePrecedenceFunction
// Union precedence
//
// UnionTypeNode:
// `|`? UnionTypeNoBar
//
// UnionTypeNoBar:
// IntersectionTypeNode
// UnionTypeNoBar `|` IntersectionTypeNode
//
TypePrecedenceUnion
// Intersection precedence
//
// IntersectionTypeNode:
// `&`? IntersectionTypeNoAmpersand
//
// IntersectionTypeNoAmpersand:
// TypeOperatorNode
// IntersectionTypeNoAmpersand `&` TypeOperatorNode
//
TypePrecedenceIntersection
// TypeOperatorNode precedence
//
// TypeOperatorNode:
// PostfixType
// InferTypeNode
// `keyof` TypeOperatorNode
// `unique` TypeOperatorNode
// `readonly` PostfixType
//
// InferTypeNode:
// `infer` BindingIdentifier
// `infer` BindingIdentifier `extends` Type[+Extends]
//
TypePrecedenceTypeOperator
// Postfix precedence
//
// PostfixType:
// NonArrayType
// OptionalTypeNode
// ArrayTypeNode
// IndexedAccessTypeNode
//
// OptionalTypeNode:
// PostfixType `?`
//
// ArrayTypeNode:
// PostfixType `[` `]`
//
// IndexedAccessTypeNode:
// PostfixType `[` Type[~Extends] `]`
//
TypePrecedencePostfix
// NonArray precedence (highest)
//
// NonArrayType:
// KeywordType
// LiteralTypeNode
// ThisTypeNode
// ImportType
// TypeQueryNode
// MappedTypeNode
// TypeLiteralNode
// TupleTypeNode
// ParenthesizedTypeNode
// TypePredicateNode
// TypeReferenceNode
// TemplateType
//
// KeywordType: one of
// `any` `unknown` `string` `number` `bigint`
// `symbol` `boolean` `undefined` `never` `object`
// `intrinsic` `void`
//
// LiteralTypeNode:
// StringLiteral
// NoSubstitutionTemplateLiteral
// NumericLiteral
// BigIntLiteral
// `-` NumericLiteral
// `-` BigIntLiteral
// `true`
// `false`
// `null`
//
// ThisTypeNode:
// `this`
//
// ImportType:
// `typeof`? `import` `(` Type[~Extends] `,`? `)` ImportTypeQualifier? TypeArguments?
// `typeof`? `import` `(` Type[~Extends] `,` ImportTypeAttributes `,`? `)` ImportTypeQualifier? TypeArguments?
//
// ImportTypeQualifier:
// `.` EntityName
//
// ImportTypeAttributes:
// `{` `with` `:` ImportAttributes `,`? `}`
//
// TypeQueryNode:
//
// MappedTypeNode:
// `{` MappedTypePrefix? MappedTypePropertyName MappedTypeSuffix? `:` Type[~Extends] `;` `}`
//
// MappedTypePrefix:
// `readonly`
// `+` `readonly`
// `-` `readonly`
//
// MappedTypePropertyName:
// `[` BindingIdentifier `in` Type[~Extends] `]`
// `[` BindingIdentifier `in` Type[~Extends] `as` Type[~Extends] `]`
//
// MappedTypeSuffix:
// `?`
// `+` `?`
// `-` `?`
//
// TypeLiteralNode:
// `{` TypeElementList `}`
//
// TypeElementList:
// [empty]
// TypeElementList TypeElement
//
// TypeElement:
// PropertySignatureDeclaration
// MethodSignatureDeclaration
// IndexSignatureDeclaration
// CallSignatureDeclaration
// ConstructSignatureDeclaration
//
// PropertySignatureDeclaration:
// PropertyName `?`? TypeAnnotation? `;`
//
// MethodSignatureDeclaration:
// PropertyName `?`? TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;`
// `get` PropertyName TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;` // GetAccessorDeclaration
// `set` PropertyName TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;` // SetAccessorDeclaration
//
// IndexSignatureDeclaration:
// `[` IdentifierName`]` TypeAnnotation `;`
//
// CallSignatureDeclaration:
// TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;`
//
// ConstructSignatureDeclaration:
// `new` TypeParameters? `(` FormalParameterList `)` TypeAnnotation? `;`
//
// TupleTypeNode:
// `[` `]`
// `[` NamedTupleElementTypes `,`? `]`
// `[` TupleElementTypes `,`? `]`
//
// NamedTupleElementTypes:
// NamedTupleMember
// NamedTupleElementTypes `,` NamedTupleMember
//
// NamedTupleMember:
// IdentifierName `?`? `:` Type[~Extends]
// `...` IdentifierName `:` Type[~Extends]
//
// TupleElementTypes:
// TupleElementType
// TupleElementTypes `,` TupleElementType
//
// TupleElementType:
// Type[~Extends]
// OptionalTypeNode
// RestTypeNode
//
// RestTypeNode:
// `...` Type[~Extends]
//
// ParenthesizedTypeNode:
// `(` Type[~Extends] `)`
//
// TypePredicateNode:
// `asserts`? TypePredicateParameterName
// `asserts`? TypePredicateParameterName `is` Type[~Extends]
//
// TypePredicateParameterName:
// `this`
// IdentifierReference
//
// TypeReferenceNode:
// EntityName TypeArguments?
//
// TemplateType:
// TemplateHead Type[~Extends] TemplateTypeSpans
//
// TemplateTypeSpans:
// TemplateTail
// TemplateTypeMiddleList TemplateTail
//
// TemplateTypeMiddleList:
// TemplateMiddle Type[~Extends]
// TemplateTypeMiddleList TemplateMiddle Type[~Extends]
//
// TypeArguments:
// `<` TypeArgumentList `,`? `>`
//
// TypeArgumentList:
// Type[~Extends]
// TypeArgumentList `,` Type[~Extends]
//
TypePrecedenceNonArray
TypePrecedenceLowest = TypePrecedenceConditional
TypePrecedenceHighest = TypePrecedenceNonArray
)
// Gets the precedence of a TypeNode
func GetTypeNodePrecedence(n *TypeNode) TypePrecedence {
switch n.Kind {
case KindConditionalType:
return TypePrecedenceConditional
case KindJSDocOptionalType, KindJSDocVariadicType:
return TypePrecedenceJSDoc
case KindFunctionType, KindConstructorType:
return TypePrecedenceFunction
case KindUnionType:
return TypePrecedenceUnion
case KindIntersectionType:
return TypePrecedenceIntersection
case KindTypeOperator:
return TypePrecedenceTypeOperator
case KindInferType:
if n.AsInferTypeNode().TypeParameter.AsTypeParameterDeclaration().Constraint != nil {
// `infer T extends U` must be treated as FunctionTypeNode precedence as the `extends` clause eagerly consumes
// TypeNode
return TypePrecedenceFunction
}
return TypePrecedenceTypeOperator
case KindIndexedAccessType, KindArrayType, KindOptionalType:
return TypePrecedencePostfix
case KindTypeQuery:
// TypeQueryNode is actually a NonArrayType, but we treat it as TypeOperatorNode
// precedence so that it is parenthesized when used in a PostfixType
// context (e.g., `(typeof C)[]` instead of `typeof C[]`)
return TypePrecedenceTypeOperator
case KindAnyKeyword,
KindUnknownKeyword,
KindStringKeyword,
KindNumberKeyword,
KindBigIntKeyword,
KindSymbolKeyword,
KindBooleanKeyword,
KindUndefinedKeyword,
KindNeverKeyword,
KindObjectKeyword,
KindIntrinsicKeyword,
KindVoidKeyword,
KindJSDocAllType,
KindJSDocNullableType,
KindJSDocNonNullableType,
KindLiteralType,
KindTypePredicate,
KindTypeReference,
KindTypeLiteral,
KindTupleType,
KindRestType,
KindParenthesizedType,
KindThisType,
KindMappedType,
KindNamedTupleMember,
KindTemplateLiteralType,
KindImportType,
// These occur in pseudo-types like `f<T>.C`, where `f` is a generic function and `C` is a local type
KindPropertyAccessExpression,
KindExpressionWithTypeArguments:
return TypePrecedenceNonArray
default:
panic(fmt.Sprintf("unhandled TypeNode: %v", n.Kind))
}
}

View File

@@ -0,0 +1,133 @@
package ast
import (
"github.com/microsoft/typescript-go/internal/core"
)
type SubtreeFacts uint32
const (
// Facts
// - Flags used to indicate that a node or subtree contains syntax relevant to a specific transform
SubtreeContainsTypeScript SubtreeFacts = 1 << iota
SubtreeContainsJsx
SubtreeContainsESDecorators
SubtreeContainsUsing
SubtreeContainsClassStaticBlocks
SubtreeContainsESClassFields
SubtreeContainsLogicalAssignments
SubtreeContainsNullishCoalescing
SubtreeContainsOptionalChaining
SubtreeContainsMissingCatchClauseVariable
SubtreeContainsESObjectRestOrSpread // subtree has a `...` somewhere inside it, never cleared
SubtreeContainsForAwaitOrAsyncGenerator
SubtreeContainsAnyAwait
SubtreeContainsExponentiationOperator
// Markers
// - Flags used to indicate that a node or subtree contains a particular kind of syntax.
SubtreeContainsLexicalThis
SubtreeContainsLexicalSuper
SubtreeContainsRestOrSpread // marker on any `...` - cleared on binding pattern exit
SubtreeContainsObjectRestOrSpread // marker on any `{...x}` - cleared on most scope exits
SubtreeContainsAwait
SubtreeContainsDynamicImport
SubtreeContainsClassFields
SubtreeContainsDecorators
SubtreeContainsIdentifier
SubtreeContainsPrivateIdentifierInExpression
SubtreeContainsInvalidTemplateEscape
SubtreeFactsComputed // NOTE: This should always be last
SubtreeFactsNone SubtreeFacts = 0
// Aliases (unused, for documentation purposes only - correspond to combinations in transformers/estransforms/definitions.go)
SubtreeContainsESNext = SubtreeContainsESDecorators | SubtreeContainsUsing
SubtreeContainsES2022 = SubtreeContainsClassStaticBlocks | SubtreeContainsESClassFields
SubtreeContainsES2021 = SubtreeContainsLogicalAssignments
SubtreeContainsES2020 = SubtreeContainsNullishCoalescing | SubtreeContainsOptionalChaining
SubtreeContainsES2019 = SubtreeContainsMissingCatchClauseVariable
SubtreeContainsES2018 = SubtreeContainsESObjectRestOrSpread | SubtreeContainsForAwaitOrAsyncGenerator | SubtreeContainsInvalidTemplateEscape
SubtreeContainsES2017 = SubtreeContainsAnyAwait
SubtreeContainsES2016 = SubtreeContainsExponentiationOperator
// Scope Exclusions
// - Bitmasks that exclude flags from propagating out of a specific context
// into the subtree flags of their container.
SubtreeExclusionsNode = SubtreeFactsComputed
SubtreeExclusionsEraseable = ^SubtreeContainsTypeScript
SubtreeExclusionsOuterExpression = SubtreeExclusionsNode
SubtreeExclusionsPropertyAccess = SubtreeExclusionsNode
SubtreeExclusionsElementAccess = SubtreeExclusionsNode
SubtreeExclusionsArrowFunction = SubtreeExclusionsNode | SubtreeContainsAwait | SubtreeContainsObjectRestOrSpread
SubtreeExclusionsFunction = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper | SubtreeContainsAwait | SubtreeContainsObjectRestOrSpread
SubtreeExclusionsConstructor = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper | SubtreeContainsAwait | SubtreeContainsObjectRestOrSpread
SubtreeExclusionsMethod = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper | SubtreeContainsAwait | SubtreeContainsObjectRestOrSpread
SubtreeExclusionsAccessor = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper | SubtreeContainsAwait | SubtreeContainsObjectRestOrSpread
SubtreeExclusionsProperty = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper
SubtreeExclusionsClass = SubtreeExclusionsNode
SubtreeExclusionsModule = SubtreeExclusionsNode | SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper
SubtreeExclusionsObjectLiteral = SubtreeExclusionsNode | SubtreeContainsObjectRestOrSpread
SubtreeExclusionsArrayLiteral = SubtreeExclusionsNode
SubtreeExclusionsCall = SubtreeExclusionsNode
SubtreeExclusionsNew = SubtreeExclusionsNode
SubtreeExclusionsVariableDeclarationList = SubtreeExclusionsNode | SubtreeContainsObjectRestOrSpread
SubtreeExclusionsParameter = SubtreeExclusionsNode
SubtreeExclusionsCatchClause = SubtreeExclusionsNode | SubtreeContainsObjectRestOrSpread
SubtreeExclusionsBindingPattern = SubtreeExclusionsNode | SubtreeContainsRestOrSpread
// Masks
// - Additional bitmasks
SubtreeContainsLexicalThisOrSuper = SubtreeContainsLexicalThis | SubtreeContainsLexicalSuper
)
func propagateEraseableSyntaxListSubtreeFacts(children *TypeArgumentList) SubtreeFacts {
return core.IfElse(children != nil, SubtreeContainsTypeScript, SubtreeFactsNone)
}
func propagateEraseableSyntaxSubtreeFacts(child *TypeNode) SubtreeFacts {
return core.IfElse(child != nil, SubtreeContainsTypeScript, SubtreeFactsNone)
}
func propagateObjectBindingElementSubtreeFacts(child *BindingElementNode) SubtreeFacts {
facts := propagateSubtreeFacts(child)
if facts&SubtreeContainsRestOrSpread != 0 {
facts &^= SubtreeContainsRestOrSpread
facts |= SubtreeContainsObjectRestOrSpread | SubtreeContainsESObjectRestOrSpread
}
return facts
}
func propagateBindingElementSubtreeFacts(child *BindingElementNode) SubtreeFacts {
return propagateSubtreeFacts(child) & ^SubtreeContainsRestOrSpread
}
func propagateSubtreeFacts(child *Node) SubtreeFacts {
if child == nil {
return SubtreeFactsNone
}
return child.propagateSubtreeFacts()
}
func propagateNodeListSubtreeFacts(children *NodeList, propagate func(*Node) SubtreeFacts) SubtreeFacts {
if children == nil {
return SubtreeFactsNone
}
facts := SubtreeFactsNone
for _, child := range children.Nodes {
facts |= propagate(child)
}
return facts
}
func propagateModifierListSubtreeFacts(children *ModifierList) SubtreeFacts {
if children == nil {
return SubtreeFactsNone
}
return propagateNodeListSubtreeFacts(&children.NodeList, propagateSubtreeFacts)
}

View File

@@ -0,0 +1,103 @@
package ast
import (
"strings"
"sync/atomic"
)
// Symbol
type Symbol struct {
Flags SymbolFlags
CheckFlags CheckFlags // Non-zero only in transient symbols created by Checker
Name string
Declarations []*Node
ValueDeclaration *Node
Members SymbolTable
Exports SymbolTable
id atomic.Uint64
Parent *Symbol
ExportSymbol *Symbol
}
func (s *Symbol) IsExternalModule() bool {
return s.Flags&SymbolFlagsModule != 0 && len(s.Name) > 0 && s.Name[0] == '"'
}
func (s *Symbol) IsStatic() bool {
if s.ValueDeclaration == nil {
return false
}
modifierFlags := s.ValueDeclaration.ModifierFlags()
return modifierFlags&ModifierFlagsStatic != 0
}
// See comment on `declareModuleMember` in `binder.go`.
func (s *Symbol) CombinedLocalAndExportSymbolFlags() SymbolFlags {
if s.ExportSymbol != nil {
return s.Flags | s.ExportSymbol.Flags
}
return s.Flags
}
// SymbolTable
type SymbolTable map[string]*Symbol
const InternalSymbolNamePrefix = "\xFE" // Invalid UTF8 sequence, will never occur as IdentifierName
const (
InternalSymbolNameCall = InternalSymbolNamePrefix + "call" // Call signatures
InternalSymbolNameConstructor = InternalSymbolNamePrefix + "constructor" // Constructor implementations
InternalSymbolNameNew = InternalSymbolNamePrefix + "new" // Constructor signatures
InternalSymbolNameIndex = InternalSymbolNamePrefix + "index" // Index signatures
InternalSymbolNameExportStar = InternalSymbolNamePrefix + "export" // Module export * declarations
InternalSymbolNameGlobal = InternalSymbolNamePrefix + "global" // Global self-reference
InternalSymbolNameMissing = InternalSymbolNamePrefix + "missing" // Indicates missing symbol
InternalSymbolNameType = InternalSymbolNamePrefix + "type" // Anonymous type literal symbol
InternalSymbolNameObject = InternalSymbolNamePrefix + "object" // Anonymous object literal declaration
InternalSymbolNameJSXAttributes = InternalSymbolNamePrefix + "jsxAttributes" // Anonymous JSX attributes object literal declaration
InternalSymbolNameClass = InternalSymbolNamePrefix + "class" // Unnamed class expression
InternalSymbolNameFunction = InternalSymbolNamePrefix + "function" // Unnamed function expression
InternalSymbolNameComputed = InternalSymbolNamePrefix + "computed" // Computed property name declaration with dynamic name
InternalSymbolNameAssignmentDeclaration = InternalSymbolNamePrefix + "assignment" // Assignment declarations
InternalSymbolNameInstantiationExpression = InternalSymbolNamePrefix + "instantiationExpression" // Instantiation expressions
InternalSymbolNameImportAttributes = InternalSymbolNamePrefix + "importAttributes"
InternalSymbolNameExportEquals = "export=" // Export assignment symbol
InternalSymbolNameDefault = "default" // Default export symbol (technically not wholly internal, but included here for usability)
InternalSymbolNameThis = "this"
InternalSymbolNameModuleExports = "module.exports"
)
func SymbolName(symbol *Symbol) string {
if symbol.ValueDeclaration != nil && IsPrivateIdentifierClassElementDeclaration(symbol.ValueDeclaration) {
return symbol.ValueDeclaration.Name().Text()
}
return symbol.Name
}
// EscapeAllInternalSymbolNames replaces internal symbol name markers ("\xFE") with "__".
func EscapeAllInternalSymbolNames(name string) string {
return strings.ReplaceAll(name, InternalSymbolNamePrefix, "__")
}
func EscapeInternalSymbolName(name string) string {
if rest, ok := strings.CutPrefix(name, InternalSymbolNamePrefix); ok {
return "__" + rest
}
return name
}
// EscapeSymbolName converts a binder symbol name into its escaped "__String"
// form. Internal names (prefixed with the "\xFE" sentinel) become "__"-prefixed,
// and user names that already begin with "__" gain an extra leading underscore
// so they can be distinguished from internal names.
func EscapeSymbolName(name string) string {
if rest, ok := strings.CutPrefix(name, InternalSymbolNamePrefix); ok {
return "__" + rest
}
if len(name) >= 2 && name[0] == '_' && name[1] == '_' {
return "_" + name
}
return name
}

View File

@@ -0,0 +1,86 @@
package ast
// SymbolFlags
type SymbolFlags uint32
const (
SymbolFlagsNone SymbolFlags = 0
SymbolFlagsFunctionScopedVariable SymbolFlags = 1 << 0 // Variable (var) or parameter
SymbolFlagsBlockScopedVariable SymbolFlags = 1 << 1 // A block-scoped variable (let or const)
SymbolFlagsProperty SymbolFlags = 1 << 2 // Property or enum member
SymbolFlagsEnumMember SymbolFlags = 1 << 3 // Enum member
SymbolFlagsFunction SymbolFlags = 1 << 4 // Function
SymbolFlagsClass SymbolFlags = 1 << 5 // Class
SymbolFlagsInterface SymbolFlags = 1 << 6 // Interface
SymbolFlagsConstEnum SymbolFlags = 1 << 7 // Const enum
SymbolFlagsRegularEnum SymbolFlags = 1 << 8 // Enum
SymbolFlagsValueModule SymbolFlags = 1 << 9 // Instantiated module
SymbolFlagsNamespaceModule SymbolFlags = 1 << 10 // Uninstantiated module
SymbolFlagsTypeLiteral SymbolFlags = 1 << 11 // Type Literal or mapped type
SymbolFlagsObjectLiteral SymbolFlags = 1 << 12 // Object Literal
SymbolFlagsMethod SymbolFlags = 1 << 13 // Method
SymbolFlagsConstructor SymbolFlags = 1 << 14 // Constructor
SymbolFlagsGetAccessor SymbolFlags = 1 << 15 // Get accessor
SymbolFlagsSetAccessor SymbolFlags = 1 << 16 // Set accessor
SymbolFlagsSignature SymbolFlags = 1 << 17 // Call, construct, or index signature
SymbolFlagsTypeParameter SymbolFlags = 1 << 18 // Type parameter
SymbolFlagsTypeAlias SymbolFlags = 1 << 19 // Type alias
SymbolFlagsExportValue SymbolFlags = 1 << 20 // Exported value marker (see comment in declareModuleMember in binder)
SymbolFlagsAlias SymbolFlags = 1 << 21 // An alias for another symbol (see comment in isAliasSymbolDeclaration in checker)
SymbolFlagsPrototype SymbolFlags = 1 << 22 // Prototype property (no source representation)
SymbolFlagsExportStar SymbolFlags = 1 << 23 // Export * declaration
SymbolFlagsOptional SymbolFlags = 1 << 24 // Optional property
SymbolFlagsTransient SymbolFlags = 1 << 25 // Transient symbol (created during type check)
SymbolFlagsAssignment SymbolFlags = 1 << 26 // Assignment to property on function acting as declaration (eg `func.prop = 1`)
SymbolFlagsModuleExports SymbolFlags = 1 << 27 // Symbol for CommonJS `module` of `module.exports`
SymbolFlagsConstEnumOnlyModule SymbolFlags = 1 << 28 // Module contains only const enums or other modules with only const enums
SymbolFlagsReplaceableByMethod SymbolFlags = 1 << 29
SymbolFlagsGlobalLookup SymbolFlags = 1 << 30 // Flag to signal this is a global lookup
SymbolFlagsAll SymbolFlags = 1<<30 - 1 // All flags except SymbolFlagsGlobalLookup
SymbolFlagsEnum = SymbolFlagsRegularEnum | SymbolFlagsConstEnum
SymbolFlagsVariable = SymbolFlagsFunctionScopedVariable | SymbolFlagsBlockScopedVariable
SymbolFlagsValue = SymbolFlagsVariable | SymbolFlagsProperty | SymbolFlagsEnumMember | SymbolFlagsObjectLiteral | SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsEnum | SymbolFlagsValueModule | SymbolFlagsMethod | SymbolFlagsGetAccessor | SymbolFlagsSetAccessor
SymbolFlagsType = SymbolFlagsClass | SymbolFlagsInterface | SymbolFlagsEnum | SymbolFlagsEnumMember | SymbolFlagsTypeLiteral | SymbolFlagsTypeParameter | SymbolFlagsTypeAlias
SymbolFlagsNamespace = SymbolFlagsValueModule | SymbolFlagsNamespaceModule | SymbolFlagsEnum
SymbolFlagsModule = SymbolFlagsValueModule | SymbolFlagsNamespaceModule
SymbolFlagsAccessor = SymbolFlagsGetAccessor | SymbolFlagsSetAccessor
// Variables can be redeclared, but can not redeclare a block-scoped declaration with the
// same name, or any other value that is not a variable, e.g. ValueModule or Class
SymbolFlagsFunctionScopedVariableExcludes = SymbolFlagsValue & ^SymbolFlagsFunctionScopedVariable
// Block-scoped declarations are not allowed to be re-declared
// they can not merge with anything in the value space
SymbolFlagsBlockScopedVariableExcludes = SymbolFlagsValue
SymbolFlagsParameterExcludes = SymbolFlagsValue
SymbolFlagsPropertyExcludes = SymbolFlagsValue & ^(SymbolFlagsProperty | SymbolFlagsAccessor)
SymbolFlagsEnumMemberExcludes = SymbolFlagsValue | SymbolFlagsType
SymbolFlagsFunctionExcludes = SymbolFlagsValue & ^(SymbolFlagsFunction | SymbolFlagsValueModule | SymbolFlagsClass)
SymbolFlagsClassExcludes = (SymbolFlagsValue | SymbolFlagsType) & ^(SymbolFlagsValueModule | SymbolFlagsInterface | SymbolFlagsFunction) // class-interface mergability done in checker.ts
SymbolFlagsInterfaceExcludes = SymbolFlagsType & ^(SymbolFlagsInterface | SymbolFlagsClass)
SymbolFlagsRegularEnumExcludes = (SymbolFlagsValue | SymbolFlagsType) & ^(SymbolFlagsRegularEnum | SymbolFlagsValueModule) // regular enums merge only with regular enums and modules
SymbolFlagsConstEnumExcludes = (SymbolFlagsValue | SymbolFlagsType) & ^SymbolFlagsConstEnum // const enums merge only with const enums
SymbolFlagsValueModuleExcludes = SymbolFlagsValue & ^(SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsRegularEnum | SymbolFlagsValueModule)
SymbolFlagsNamespaceModuleExcludes = SymbolFlagsNone
SymbolFlagsMethodExcludes = SymbolFlagsValue & ^SymbolFlagsMethod
SymbolFlagsGetAccessorExcludes = SymbolFlagsValue & ^(SymbolFlagsSetAccessor | SymbolFlagsProperty)
SymbolFlagsSetAccessorExcludes = SymbolFlagsValue & ^(SymbolFlagsGetAccessor | SymbolFlagsProperty)
SymbolFlagsAccessorExcludes = SymbolFlagsValue & ^SymbolFlagsProperty
SymbolFlagsTypeParameterExcludes = SymbolFlagsType & ^SymbolFlagsTypeParameter
SymbolFlagsTypeAliasExcludes = SymbolFlagsType
SymbolFlagsAliasExcludes = SymbolFlagsAlias
SymbolFlagsModuleMember = SymbolFlagsVariable | SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsInterface | SymbolFlagsEnum | SymbolFlagsModule | SymbolFlagsTypeAlias | SymbolFlagsAlias
SymbolFlagsExportHasLocal = SymbolFlagsFunction | SymbolFlagsClass | SymbolFlagsEnum | SymbolFlagsValueModule
SymbolFlagsBlockScoped = SymbolFlagsBlockScopedVariable | SymbolFlagsClass | SymbolFlagsEnum
SymbolFlagsPropertyOrAccessor = SymbolFlagsProperty | SymbolFlagsAccessor
SymbolFlagsClassMember = SymbolFlagsMethod | SymbolFlagsAccessor | SymbolFlagsProperty
SymbolFlagsExportSupportsDefaultModifier = SymbolFlagsClass | SymbolFlagsFunction | SymbolFlagsInterface
SymbolFlagsExportDoesNotSupportDefaultModifier = ^SymbolFlagsExportSupportsDefaultModifier
// The set of things we consider semantically classifiable. Used to speed up the LS during
// classification.
SymbolFlagsClassifiable = SymbolFlagsClass | SymbolFlagsEnum | SymbolFlagsTypeAlias | SymbolFlagsInterface | SymbolFlagsTypeParameter | SymbolFlagsModule | SymbolFlagsAlias
SymbolFlagsLateBindingContainer = SymbolFlagsClass | SymbolFlagsInterface | SymbolFlagsTypeLiteral | SymbolFlagsObjectLiteral | SymbolFlagsFunction
)

View File

@@ -0,0 +1,33 @@
package ast
type TokenFlags int32
const (
TokenFlagsNone TokenFlags = 0
TokenFlagsPrecedingLineBreak TokenFlags = 1 << 0
TokenFlagsPrecedingJSDocComment TokenFlags = 1 << 1
TokenFlagsUnterminated TokenFlags = 1 << 2
TokenFlagsExtendedUnicodeEscape TokenFlags = 1 << 3 // e.g. `\u{10ffff}`
TokenFlagsScientific TokenFlags = 1 << 4 // e.g. `10e2`
TokenFlagsOctal TokenFlags = 1 << 5 // e.g. `0777`
TokenFlagsHexSpecifier TokenFlags = 1 << 6 // e.g. `0x00000000`
TokenFlagsBinarySpecifier TokenFlags = 1 << 7 // e.g. `0b0110010000000000`
TokenFlagsOctalSpecifier TokenFlags = 1 << 8 // e.g. `0o777`
TokenFlagsContainsSeparator TokenFlags = 1 << 9 // e.g. `0b1100_0101`
TokenFlagsUnicodeEscape TokenFlags = 1 << 10 // e.g. `\u00a0`
TokenFlagsContainsInvalidEscape TokenFlags = 1 << 11 // e.g. `\uhello`
TokenFlagsHexEscape TokenFlags = 1 << 12 // e.g. `\xa0`
TokenFlagsContainsLeadingZero TokenFlags = 1 << 13 // e.g. `0888`
TokenFlagsContainsInvalidSeparator TokenFlags = 1 << 14 // e.g. `0_1`
TokenFlagsPrecedingJSDocLeadingAsterisks TokenFlags = 1 << 15
TokenFlagsSingleQuote TokenFlags = 1 << 16 // e.g. `'abc'`
TokenFlagsPrecedingJSDocWithDeprecated TokenFlags = 1 << 17 // Preceding JSDoc comment contains @deprecated
TokenFlagsPrecedingJSDocWithSeeOrLink TokenFlags = 1 << 18 // Preceding JSDoc comment contains @see or @link
TokenFlagsBinaryOrOctalSpecifier TokenFlags = TokenFlagsBinarySpecifier | TokenFlagsOctalSpecifier
TokenFlagsWithSpecifier TokenFlags = TokenFlagsHexSpecifier | TokenFlagsBinaryOrOctalSpecifier
TokenFlagsStringLiteralFlags TokenFlags = TokenFlagsUnterminated | TokenFlagsHexEscape | TokenFlagsUnicodeEscape | TokenFlagsExtendedUnicodeEscape | TokenFlagsContainsInvalidEscape | TokenFlagsSingleQuote
TokenFlagsNumericLiteralFlags TokenFlags = TokenFlagsScientific | TokenFlagsOctal | TokenFlagsContainsLeadingZero | TokenFlagsWithSpecifier | TokenFlagsContainsSeparator | TokenFlagsContainsInvalidSeparator
TokenFlagsTemplateLiteralLikeFlags TokenFlags = TokenFlagsUnterminated | TokenFlagsHexEscape | TokenFlagsUnicodeEscape | TokenFlagsExtendedUnicodeEscape | TokenFlagsContainsInvalidEscape
TokenFlagsRegularExpressionLiteralFlags TokenFlags = TokenFlagsUnterminated
TokenFlagsIsInvalid TokenFlags = TokenFlagsOctal | TokenFlagsContainsLeadingZero | TokenFlagsContainsInvalidSeparator | TokenFlagsContainsInvalidEscape
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,278 @@
package ast
import (
"slices"
)
// NodeVisitor
type NodeVisitor struct {
Visit func(node *Node) *Node // Required. The callback used to visit a node
Factory *NodeFactory // Required. The NodeFactory used to produce new nodes when passed to VisitEachChild
Hooks NodeVisitorHooks // Hooks to be invoked when visiting a node
}
// These hooks are used to intercept the default behavior of the visitor
type NodeVisitorHooks struct {
VisitNode func(node *Node, v *NodeVisitor) *Node // Overrides visiting a Node. Only invoked by the VisitEachChild method on a given Node subtype.
VisitToken func(node *TokenNode, v *NodeVisitor) *Node // Overrides visiting a TokenNode. Only invoked by the VisitEachChild method on a given Node subtype.
VisitNodes func(nodes *NodeList, v *NodeVisitor) *NodeList // Overrides visiting a NodeList. Only invoked by the VisitEachChild method on a given Node subtype.
VisitModifiers func(nodes *ModifierList, v *NodeVisitor) *ModifierList // Overrides visiting a ModifierList. Only invoked by the VisitEachChild method on a given Node subtype.
VisitEmbeddedStatement func(node *Statement, v *NodeVisitor) *Statement // Overrides visiting a Node when it is the embedded statement body of an iteration statement, `if` statement, or `with` statement. Only invoked by the VisitEachChild method on a given Node subtype.
VisitIterationBody func(node *Statement, v *NodeVisitor) *Statement // Overrides visiting a Node when it is the embedded statement body of an iteration statement. Only invoked by the VisitEachChild method on a given Node subtype.
VisitParameters func(nodes *ParameterList, v *NodeVisitor) *ParameterList // Overrides visiting a ParameterList. Only invoked by the VisitEachChild method on a given Node subtype.
VisitFunctionBody func(node *BlockOrExpression, v *NodeVisitor) *BlockOrExpression // Overrides visiting a function body. Only invoked by the VisitEachChild method on a given Node subtype.
VisitTopLevelStatements func(nodes *StatementList, v *NodeVisitor) *StatementList // Overrides visiting a variable environment. Only invoked by the VisitEachChild method on a given Node subtype.
}
func NewNodeVisitor(visit func(node *Node) *Node, factory *NodeFactory, hooks NodeVisitorHooks) *NodeVisitor {
if factory == nil {
factory = &NodeFactory{}
}
return &NodeVisitor{Visit: visit, Factory: factory, Hooks: hooks}
}
func (v *NodeVisitor) VisitSourceFile(node *SourceFile) *SourceFile {
return v.VisitNode(node.AsNode()).AsSourceFile()
}
// Visits a Node, possibly returning a new Node in its place.
//
// - If the input node is nil, then the output is nil.
// - If v.Visit is nil, then the output is the input.
// - If v.Visit returns nil, then the output is nil.
// - If v.Visit returns a SyntaxList Node, then the output is the only child of the SyntaxList Node.
func (v *NodeVisitor) VisitNode(node *Node) *Node {
if node == nil || v.Visit == nil {
return node
}
if v.Visit != nil {
visited := v.Visit(node)
if visited != nil && visited.Kind == KindSyntaxList {
nodes := visited.AsSyntaxList().Children
if len(nodes) != 1 {
panic("Expected only a single node to be written to output")
}
visited = nodes[0]
if visited != nil && visited.Kind == KindSyntaxList {
panic("The result of visiting and lifting a Node may not be SyntaxList")
}
}
return visited
}
return node
}
// Visits an embedded Statement (i.e., the single statement body of a loop, `if..else` branch, etc.), possibly returning a new Statement in its place.
//
// - If the input node is nil, then the output is nil.
// - If v.Visit is nil, then the output is the input.
// - If v.Visit returns nil, then the output is nil.
// - If v.Visit returns a SyntaxList Node, then the output is either the only child of the SyntaxList Node, or a Block containing the nodes in the list.
func (v *NodeVisitor) VisitEmbeddedStatement(node *Statement) *Statement {
if node == nil || v.Visit == nil {
return node
}
visited := v.Visit(node)
if visited == nil {
return nil
}
return v.liftToBlock(visited)
}
// Visits a NodeList, possibly returning a new NodeList in its place.
//
// - If the input NodeList is nil, the output is nil.
// - If v.Visit is nil, then the output is the input.
// - If v.Visit returns nil, the visited Node will be absent in the output.
// - If v.Visit returns a different Node than the input, a new NodeList will be generated and returned.
// - If v.Visit returns a SyntaxList Node, then the children of that node will be merged into the output and a new NodeList will be returned.
// - If this method returns a new NodeList for any reason, it will have the same Loc as the input NodeList.
func (v *NodeVisitor) VisitNodes(nodes *NodeList) *NodeList {
if nodes == nil || v.Visit == nil {
return nodes
}
if result, changed := v.VisitSlice(nodes.Nodes); changed {
list := v.Factory.NewNodeList(result)
list.Loc = nodes.Loc
return list
}
return nodes
}
// Visits a ModifierList, possibly returning a new ModifierList in its place.
//
// - If the input ModifierList is nil, the output is nil.
// - If v.Visit is nil, then the output is the input.
// - If v.Visit returns nil, the visited Node will be absent in the output.
// - If v.Visit returns a different Node than the input, a new ModifierList will be generated and returned.
// - If v.Visit returns a SyntaxList Node, then the children of that node will be merged into the output and a new NodeList will be returned.
// - If this method returns a new NodeList for any reason, it will have the same Loc as the input NodeList.
func (v *NodeVisitor) VisitModifiers(nodes *ModifierList) *ModifierList {
if nodes == nil || v.Visit == nil {
return nodes
}
if result, changed := v.VisitSlice(nodes.Nodes); changed {
list := v.Factory.NewModifierList(result)
list.Loc = nodes.Loc
return list
}
return nodes
}
// Visits a slice of Nodes, returning the resulting slice and a value indicating whether the slice was changed.
//
// - If the input slice is nil, the output is nil.
// - If v.Visit is nil, then the output is the input.
// - If v.Visit returns nil, the visited Node will be absent in the output.
// - If v.Visit returns a different Node than the input, a new slice will be generated and returned.
// - If v.Visit returns a SyntaxList Node, then the children of that node will be merged into the output and a new slice will be returned.
func (v *NodeVisitor) VisitSlice(nodes []*Node) (result []*Node, changed bool) {
if nodes == nil || v.Visit == nil {
return nodes, false
}
for i := 0; i < len(nodes); i++ {
node := nodes[i]
if v.Visit == nil {
break
}
visited := v.Visit(node)
if visited == nil || visited != node {
updated := slices.Clone(nodes[:i])
for {
// finish prior loop
switch {
case visited == nil: // do nothing
case visited.Kind == KindSyntaxList:
updated = append(updated, visited.AsSyntaxList().Children...)
default:
updated = append(updated, visited)
}
i++
// loop over remaining elements
if i >= len(nodes) {
break
}
if v.Visit != nil {
node = nodes[i]
visited = v.Visit(node)
} else {
updated = append(updated, nodes[i:]...)
break
}
}
return updated, true
}
}
return nodes, false
}
// Visits each child of a Node, possibly returning a new Node of the same kind in its place.
func (v *NodeVisitor) VisitEachChild(node *Node) *Node {
if node == nil || v.Visit == nil {
return node
}
return node.VisitEachChild(v)
}
func (v *NodeVisitor) visitNode(node *Node) *Node {
if v.Hooks.VisitNode != nil {
return v.Hooks.VisitNode(node, v)
}
return v.VisitNode(node)
}
func (v *NodeVisitor) visitEmbeddedStatement(node *Node) *Node {
if v.Hooks.VisitEmbeddedStatement != nil {
return v.Hooks.VisitEmbeddedStatement(node, v)
}
if v.Hooks.VisitNode != nil {
return v.liftToBlock(v.Hooks.VisitNode(node, v))
}
return v.VisitEmbeddedStatement(node)
}
func (v *NodeVisitor) visitIterationBody(node *Statement) *Statement {
if v.Hooks.VisitIterationBody != nil {
return v.Hooks.VisitIterationBody(node, v)
}
return v.visitEmbeddedStatement(node)
}
func (v *NodeVisitor) visitFunctionBody(node *BlockOrExpression) *BlockOrExpression {
if v.Hooks.VisitFunctionBody != nil {
return v.Hooks.VisitFunctionBody(node, v)
}
return v.visitNode(node)
}
func (v *NodeVisitor) visitToken(node *Node) *Node {
if v.Hooks.VisitToken != nil {
return v.Hooks.VisitToken(node, v)
}
return v.VisitNode(node)
}
func (v *NodeVisitor) visitNodes(nodes *NodeList) *NodeList {
if v.Hooks.VisitNodes != nil {
return v.Hooks.VisitNodes(nodes, v)
}
return v.VisitNodes(nodes)
}
func (v *NodeVisitor) visitModifiers(nodes *ModifierList) *ModifierList {
if v.Hooks.VisitModifiers != nil {
return v.Hooks.VisitModifiers(nodes, v)
}
return v.VisitModifiers(nodes)
}
func (v *NodeVisitor) visitParameters(nodes *ParameterList) *ParameterList {
if v.Hooks.VisitParameters != nil {
return v.Hooks.VisitParameters(nodes, v)
}
return v.visitNodes(nodes)
}
func (v *NodeVisitor) visitTopLevelStatements(nodes *StatementList) *StatementList {
if v.Hooks.VisitTopLevelStatements != nil {
return v.Hooks.VisitTopLevelStatements(nodes, v)
}
return v.visitNodes(nodes)
}
func (v *NodeVisitor) liftToBlock(node *Statement) *Statement {
var nodes []*Node
if node != nil {
if node.Kind == KindSyntaxList {
nodes = node.AsSyntaxList().Children
} else {
nodes = []*Node{node}
}
}
if len(nodes) == 1 {
node = nodes[0]
} else {
node = v.Factory.NewBlock(v.Factory.NewNodeList(nodes), true /*multiLine*/)
}
if node.Kind == KindSyntaxList {
panic("The result of visiting and lifting a Node may not be SyntaxList")
}
return node
}

View File

@@ -0,0 +1,14 @@
package astnav_test
import (
"testing"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/testutil/baseline"
)
func TestMain(m *testing.M) {
core.ApplyDebugStackLimit()
defer baseline.Track()()
m.Run()
}

View File

@@ -0,0 +1,783 @@
package astnav
import (
"fmt"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/scanner"
)
func shouldRescanLessThanLessThanToken(s *scanner.Scanner, containingNode *ast.Node, token ast.Kind) bool {
return token == ast.KindLessThanLessThanToken && ast.IsJsxChild(containingNode)
}
func scanNavigationToken(s *scanner.Scanner, containingNode *ast.Node) ast.Kind {
token := s.Token()
if shouldRescanLessThanLessThanToken(s, containingNode, token) {
return s.ReScanJsxToken(true /*allowMultilineJsxText*/)
}
return token
}
func GetTouchingPropertyName(sourceFile *ast.SourceFile, position int) *ast.Node {
return getTokenAtPosition(sourceFile, position, false /*allowPositionInLeadingTrivia*/, func(node *ast.Node) bool {
return ast.IsPropertyNameLiteral(node) || ast.IsKeywordKind(node.Kind) || ast.IsPrivateIdentifier(node)
})
}
func GetTouchingToken(sourceFile *ast.SourceFile, position int) *ast.Node {
return getTokenAtPosition(sourceFile, position, false /*allowPositionInLeadingTrivia*/, nil)
}
func GetTokenAtPosition(sourceFile *ast.SourceFile, position int) *ast.Node {
return getTokenAtPosition(sourceFile, position, true /*allowPositionInLeadingTrivia*/, nil)
}
func getTokenAtPosition(
sourceFile *ast.SourceFile,
position int,
allowPositionInLeadingTrivia bool,
includePrecedingTokenAtEndPosition func(node *ast.Node) bool,
) *ast.Node {
// getTokenAtPosition returns a token at the given position in the source file.
// The token can be a real node in the AST, or a synthesized token constructed
// with information from the scanner. Synthesized tokens are only created when
// needed, and they are stored in the source file's token cache such that multiple
// calls to getTokenAtPosition with the same position will return the same object
// in memory. If there is no token at the given position (possible when
// `allowPositionInLeadingTrivia` is false), the lowest node that encloses the
// position is returned.
// `next` tracks the node whose children will be visited on the next iteration.
// `prevSubtree` is a node whose end position is equal to the target position,
// only if `includePrecedingTokenAtEndPosition` is provided. Once set, the next
// iteration of the loop will test the rightmost token of `prevSubtree` to see
// if it should be returned.
var next, prevSubtree *ast.Node
current := sourceFile.AsNode()
// `left` tracks the lower boundary of the node/token that could be returned,
// and is eventually the scanner's start position, if the scanner is used.
left := 0
// `nodeAfterLeft` tracks the first node we visit after visiting the node that advances `left`.
// When scanning in between nodes for token, we should only scan up to the start of `nodeAfterLeft`.
var nodeAfterLeft *ast.Node
testNode := func(node *ast.Node) int {
if node.Kind != ast.KindEndOfFile && node.End() == position &&
includePrecedingTokenAtEndPosition != nil && node.Flags&ast.NodeFlagsReparsed == 0 {
prevSubtree = node
}
// A node "contains" the position if position < end, except nodes at the file end
// treat end as inclusive (there's nowhere else to look). This applies to the EOF
// token itself, and to JSDoc nodes reaching EOF (e.g. unterminated JSDoc comments).
if node.End() < position || node.End() == position &&
node.Kind != ast.KindEndOfFile &&
(!ast.IsJSDocKind(node.Kind) || node.End() != sourceFile.EndOfFileToken.End()) {
return -1
}
nodePos := getPosition(node, sourceFile, allowPositionInLeadingTrivia)
if nodePos > position {
return 1
}
return 0
}
// We zero in on the node that contains the target position by visiting each
// child and JSDoc comment of the current node. Node children are walked in
// order, while node lists are binary searched.
visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node {
// We can't abort visiting children, so once a match is found, we set `next`
// and do nothing on subsequent visits.
if node == nil || node.Flags&ast.NodeFlagsReparsed != 0 {
return nil
}
if nodeAfterLeft == nil {
nodeAfterLeft = node
}
if next == nil {
result := testNode(node)
switch result {
case -1:
if !ast.IsJSDocKind(node.Kind) {
// We can't move the left boundary into or beyond JSDoc,
// because we may end up returning the token after this JSDoc,
// constructing it with the scanner, and we need to include
// all its leading trivia in its position.
left = node.End()
}
nodeAfterLeft = nil
case 0:
next = node
}
}
return node
}
visitNodeList := func(nodeList *ast.NodeList, _ *ast.NodeVisitor) *ast.NodeList {
if nodeList == nil || len(nodeList.Nodes) == 0 {
return nodeList
}
if nodeAfterLeft == nil {
for _, node := range nodeList.Nodes {
if node.Flags&ast.NodeFlagsReparsed == 0 {
nodeAfterLeft = node
break
}
}
}
if next == nil {
if nodeList.End() == position && includePrecedingTokenAtEndPosition != nil {
left = nodeList.End()
nodeAfterLeft = nil
for i := len(nodeList.Nodes) - 1; i >= 0; i-- {
if nodeList.Nodes[i].Flags&ast.NodeFlagsReparsed == 0 {
prevSubtree = nodeList.Nodes[i]
break
}
}
} else if nodeList.End() <= position {
left = nodeList.End()
nodeAfterLeft = nil
} else if nodeList.Pos() <= position {
nodes := nodeList.Nodes
index, match := core.BinarySearchUniqueFunc(nodes, func(middle int, node *ast.Node) int {
if node.Flags&ast.NodeFlagsReparsed != 0 {
return 0
}
cmp := testNode(node)
if cmp < 0 {
left = node.End()
nodeAfterLeft = nil
for i := middle + 1; i < len(nodes); i++ {
if nodes[i].Flags&ast.NodeFlagsReparsed == 0 {
nodeAfterLeft = nodes[i]
break
}
}
}
return cmp
})
if match && nodes[index].Flags&ast.NodeFlagsReparsed != 0 {
// filter and search again
nodes = core.Filter(nodes, func(node *ast.Node) bool {
return node.Flags&ast.NodeFlagsReparsed == 0
})
index, match = core.BinarySearchUniqueFunc(nodes, func(middle int, node *ast.Node) int {
cmp := testNode(node)
if cmp < 0 {
left = node.End()
if middle+1 < len(nodes) {
nodeAfterLeft = nodes[middle+1]
} else {
nodeAfterLeft = nil
}
}
return cmp
})
}
if match {
next = nodes[index]
}
}
}
return nodeList
}
for {
VisitEachChildAndJSDoc(current, sourceFile, visitNode, visitNodeList)
// If prevSubtree was set on the last iteration, it ends at the target position.
// Check if the rightmost token of prevSubtree should be returned based on the
// `includePrecedingTokenAtEndPosition` callback.
if prevSubtree != nil {
child := FindPrecedingTokenEx(sourceFile, position, prevSubtree, false /*excludeJSDoc*/)
if child != nil && child.End() == position && includePrecedingTokenAtEndPosition(child) {
// Optimization: includePrecedingTokenAtEndPosition only ever returns true
// for real AST nodes, so we don't run the scanner here.
return child
}
prevSubtree = nil
}
// No node was found that contains the target position, so we've gone as deep as
// we can in the AST. We've either found a token, or we need to run the scanner
// to construct one that isn't stored in the AST.
if next == nil {
if ast.IsTokenKind(current.Kind) || shouldSkipChild(current) {
return current
}
scanner := scanner.GetScannerForSourceFile(sourceFile, left)
end := current.End()
// We should only scan up to the start of the next node in the AST after the node ending at position `left`.
// It is necessary to enforce this invariant in cases where `position` occurs in between two node/tokens,
// such that we would not find a token in the loop below before we reach the next node.
// We can fall into this case when `allowPositionInLeadingTrivia` is false and `position` is in a leading trivia,
// or when `position` would be in the leading trivia of a node but this node is inside JSDoc:
// ```
// /**
// * @type {{
// */*$*/ identifier: boolean;
// * }}
// */
// ```
// The position of marker '$' falls in between the asterisk token and the identifier token, but is not
// part of the leading trivia for `identifier`.
if nodeAfterLeft != nil {
end = nodeAfterLeft.Pos()
}
for left < end {
token := scanNavigationToken(scanner, current)
tokenFullStart := scanner.TokenFullStart()
tokenStart := core.IfElse(allowPositionInLeadingTrivia, tokenFullStart, scanner.TokenStart())
tokenEnd := scanner.TokenEnd()
flags := scanner.TokenFlags()
if tokenEnd > end {
break
}
if tokenStart <= position && (position < tokenEnd) {
if token == ast.KindIdentifier || !ast.IsTokenKind(token) {
if ast.IsJSDocKind(current.Kind) {
return current
}
panic(fmt.Sprintf("did not expect %s to have %s in its trivia", current.Kind.String(), token.String()))
}
return sourceFile.GetOrCreateToken(token, tokenFullStart, tokenEnd, current, flags)
}
if includePrecedingTokenAtEndPosition != nil && tokenEnd == position {
prevToken := sourceFile.GetOrCreateToken(token, tokenFullStart, tokenEnd, current, flags)
if includePrecedingTokenAtEndPosition(prevToken) {
return prevToken
}
}
left = tokenEnd
scanner.Scan()
}
return current
}
current = next
left = current.Pos()
nodeAfterLeft = nil
next = nil
}
}
func getPosition(node *ast.Node, sourceFile *ast.SourceFile, allowPositionInLeadingTrivia bool) int {
if allowPositionInLeadingTrivia {
return node.Pos()
}
return scanner.GetTokenPosOfNode(node, sourceFile, true /*includeJSDoc*/)
}
func findRightmostNode(node *ast.Node) *ast.Node {
var next *ast.Node
current := node
visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node {
if node != nil {
next = node
}
return node
}
visitNodes := func(nodeList *ast.NodeList, visitor *ast.NodeVisitor) *ast.NodeList {
if nodeList != nil {
if rightmost := ast.FindLastVisibleNode(nodeList.Nodes); rightmost != nil {
next = rightmost
}
}
return nodeList
}
visitor := getNodeVisitor(visitNode, visitNodes)
for {
current.VisitEachChild(visitor)
if next == nil {
return current
}
current = next
next = nil
}
}
func VisitEachChildAndJSDoc(
node *ast.Node,
sourceFile *ast.SourceFile,
visitNode func(*ast.Node, *ast.NodeVisitor) *ast.Node,
visitNodes func(*ast.NodeList, *ast.NodeVisitor) *ast.NodeList,
) {
visitor := getNodeVisitor(visitNode, visitNodes)
for _, jsdoc := range node.JSDoc(sourceFile) {
if visitor.Hooks.VisitNode != nil {
visitor.Hooks.VisitNode(jsdoc, visitor)
} else {
visitor.VisitNode(jsdoc)
}
}
node.VisitEachChild(visitor)
}
const (
comparisonLessThan = -1
comparisonEqualTo = 0
comparisonGreaterThan = 1
)
// Finds the leftmost token satisfying `position < token.End()`.
// If the leftmost token satisfying `position < token.End()` is invalid, or if position
// is in the trivia of that leftmost token,
// we will find the rightmost valid token with `token.End() <= position`.
func FindPrecedingToken(sourceFile *ast.SourceFile, position int) *ast.Node {
return FindPrecedingTokenEx(sourceFile, position, nil, false)
}
func FindPrecedingTokenEx(sourceFile *ast.SourceFile, position int, startNode *ast.Node, excludeJSDoc bool) *ast.Node {
var find func(node *ast.Node) *ast.Node
find = func(n *ast.Node) *ast.Node {
if ast.IsNonWhitespaceToken(n) && n.Kind != ast.KindEndOfFile {
return n
}
// `foundChild` is the leftmost node that contains the target position.
// `prevChild` is the last visited child of the current node.
var foundChild, prevChild *ast.Node
visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node {
// skip synthesized nodes (that will exist now because of jsdoc handling)
if node == nil || node.Flags&ast.NodeFlagsReparsed != 0 {
return node
}
if foundChild != nil { // We cannot abort visiting children, so once the desired child is found, we do nothing.
return node
}
if position < node.End() && (prevChild == nil || prevChild.End() <= position) {
foundChild = node
} else {
prevChild = node
}
return node
}
visitNodes := func(nodeList *ast.NodeList, _ *ast.NodeVisitor) *ast.NodeList {
if foundChild != nil {
return nodeList
}
if nodeList != nil && len(nodeList.Nodes) > 0 {
nodes := nodeList.Nodes
index, match := core.BinarySearchUniqueFunc(nodes, func(middle int, _ *ast.Node) int {
// synthetic jsdoc nodes should have jsdocNode.End() <= n.Pos()
if nodes[middle].Flags&ast.NodeFlagsReparsed != 0 {
return comparisonLessThan
}
if position < nodes[middle].End() {
if middle == 0 || position >= nodes[middle-1].End() {
return comparisonEqualTo
}
return comparisonGreaterThan
}
return comparisonLessThan
})
if match {
foundChild = nodes[index]
}
validLookupIndex := core.IfElse(match, index-1, len(nodes)-1)
for i := validLookupIndex; i >= 0; i-- {
if nodes[i].Flags&ast.NodeFlagsReparsed != 0 {
continue
}
if prevChild == nil {
prevChild = nodes[i]
}
}
}
return nodeList
}
VisitEachChildAndJSDoc(n, sourceFile, visitNode, visitNodes)
if foundChild != nil {
// Note that the span of a node's tokens is [getStartOfNode(node, ...), node.end).
// Given that `position < child.end` and child has constituent tokens, we distinguish these cases:
// 1) `position` precedes `child`'s tokens or `child` has no tokens (ie: in a comment or whitespace preceding `child`):
// we need to find the last token in a previous child node or child tokens.
// 2) `position` is within the same span: we recurse on `child`.
start := GetStartOfNode(foundChild, sourceFile, !excludeJSDoc /*includeJSDoc*/)
lookInPreviousChild := start >= position || // cursor in the leading trivia or preceding tokens
!isValidPrecedingNode(foundChild, sourceFile)
if lookInPreviousChild {
if position >= foundChild.Pos() {
// Find jsdoc preceding the foundChild.
var jsDoc *ast.Node
nodeJSDoc := n.JSDoc(sourceFile)
for i := len(nodeJSDoc) - 1; i >= 0; i-- {
if nodeJSDoc[i].Pos() >= foundChild.Pos() {
jsDoc = nodeJSDoc[i]
break
}
}
if jsDoc != nil {
if !excludeJSDoc && position < jsDoc.End() {
return find(jsDoc)
} else {
return findRightmostValidToken(jsDoc.End(), sourceFile, n, position, excludeJSDoc)
}
}
return findRightmostValidToken(foundChild.Pos(), sourceFile, n, -1 /*position*/, excludeJSDoc)
} else { // Answer is in tokens between two visited children.
return findRightmostValidToken(foundChild.Pos(), sourceFile, n, position, excludeJSDoc)
}
} else {
// position is in [foundChild.getStart(), foundChild.End): recur.
return find(foundChild)
}
}
// We have two cases here: either the position is at the end of the file,
// or the desired token is in the unvisited trailing tokens of the current node.
if position >= n.End() {
return findRightmostValidToken(n.End(), sourceFile, n, -1 /*position*/, excludeJSDoc)
} else {
return findRightmostValidToken(n.End(), sourceFile, n, position, excludeJSDoc)
}
}
var node *ast.Node
if startNode != nil {
node = startNode
} else {
node = sourceFile.AsNode()
}
result := find(node)
if result != nil && ast.IsWhitespaceOnlyJsxText(result) {
panic("Expected result to be a non-whitespace token.")
}
return result
}
func isValidPrecedingNode(node *ast.Node, sourceFile *ast.SourceFile) bool {
if node.Kind == ast.KindEndOfFile {
return len(node.JSDoc(sourceFile)) > 0
}
start := GetStartOfNode(node, sourceFile, false /*includeJSDoc*/)
width := node.End() - start
return !(ast.IsWhitespaceOnlyJsxText(node) || width == 0)
}
func GetStartOfNode(node *ast.Node, file *ast.SourceFile, includeJSDoc bool) int {
return scanner.GetTokenPosOfNode(node, file, includeJSDoc)
}
// Looks for rightmost valid token in the range [startPos, endPos).
// If position is >= 0, looks for rightmost valid token that precedes or touches that position.
func findRightmostValidToken(endPos int, sourceFile *ast.SourceFile, containingNode *ast.Node, position int, excludeJSDoc bool) *ast.Node {
if position == -1 {
position = containingNode.End()
}
var find func(n *ast.Node, endPos int) *ast.Node
find = func(n *ast.Node, endPos int) *ast.Node {
if n == nil {
return nil
}
if ast.IsNonWhitespaceToken(n) {
return n
}
var rightmostValidNode *ast.Node
rightmostVisitedNodes := make([]*ast.Node, 0, 1) // Nodes after the last valid node.
hasChildren := false
shouldVisitNode := func(node *ast.Node) bool {
// Node is synthetic or out of the desired range: don't visit it.
return !(node.Flags&ast.NodeFlagsReparsed != 0 ||
node.End() > endPos || GetStartOfNode(node, sourceFile, !excludeJSDoc /*includeJSDoc*/) >= position)
}
visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node {
if node == nil || node.Flags&ast.NodeFlagsReparsed != 0 {
return node
}
hasChildren = true
if !shouldVisitNode(node) {
return node
}
rightmostVisitedNodes = append(rightmostVisitedNodes, node)
if isValidPrecedingNode(node, sourceFile) {
rightmostValidNode = node
rightmostVisitedNodes = rightmostVisitedNodes[:0]
}
return node
}
visitNodes := func(nodeList *ast.NodeList, _ *ast.NodeVisitor) *ast.NodeList {
if nodeList != nil && len(nodeList.Nodes) > 0 {
hasChildren = true
index, _ := core.BinarySearchUniqueFunc(nodeList.Nodes, func(middle int, node *ast.Node) int {
if node.End() > endPos {
return comparisonGreaterThan
}
return comparisonLessThan
})
validIndex := -1
for i := index - 1; i >= 0; i-- {
if !shouldVisitNode(nodeList.Nodes[i]) {
continue
}
if isValidPrecedingNode(nodeList.Nodes[i], sourceFile) {
validIndex = i
rightmostValidNode = nodeList.Nodes[i]
break
}
}
for i := validIndex + 1; i < index; i++ {
if !shouldVisitNode(nodeList.Nodes[i]) {
continue
}
rightmostVisitedNodes = append(rightmostVisitedNodes, nodeList.Nodes[i])
}
}
return nodeList
}
VisitEachChildAndJSDoc(n, sourceFile, visitNode, visitNodes)
// Three cases:
// 1. The answer is a token of `rightmostValidNode`.
// 2. The answer is one of the unvisited tokens that occur after the rightmost valid node.
// 3. The current node is a childless, token-less node. The answer is the current node.
// Case 2: Look at unvisited trailing tokens that occur in between the rightmost visited nodes.
if !shouldSkipChild(n) { // JSDoc nodes don't include trivia tokens as children.
var startPos int
if rightmostValidNode != nil {
startPos = rightmostValidNode.End()
} else {
startPos = n.Pos()
}
scanner := scanner.GetScannerForSourceFile(sourceFile, startPos)
var tokens []*ast.Node
for _, visitedNode := range rightmostVisitedNodes {
// Trailing tokens that occur before this node.
for startPos < min(visitedNode.Pos(), position) {
token := scanNavigationToken(scanner, n)
tokenStart := scanner.TokenStart()
if tokenStart >= position {
break
}
tokenFullStart := scanner.TokenFullStart()
tokenEnd := scanner.TokenEnd()
startPos = tokenEnd
flags := scanner.TokenFlags()
tokens = append(tokens, sourceFile.GetOrCreateToken(token, tokenFullStart, tokenEnd, n, flags))
scanner.Scan()
}
startPos = visitedNode.End()
scanner.ResetPos(startPos)
scanner.Scan()
}
// Trailing tokens after last visited node.
for startPos < min(endPos, position) {
token := scanNavigationToken(scanner, n)
tokenStart := scanner.TokenStart()
if tokenStart >= position {
break
}
tokenFullStart := scanner.TokenFullStart()
tokenEnd := scanner.TokenEnd()
startPos = tokenEnd
flags := scanner.TokenFlags()
tokens = append(tokens, sourceFile.GetOrCreateToken(token, tokenFullStart, tokenEnd, n, flags))
scanner.Scan()
}
lastToken := len(tokens) - 1
// Find preceding valid token.
for i := lastToken; i >= 0; i-- {
if !ast.IsWhitespaceOnlyJsxText(tokens[i]) {
return tokens[i]
}
}
}
// Case 3: childless node.
if !hasChildren {
if n != containingNode {
return n
}
return nil
}
// Case 1: recur on rightmostValidNode.
if rightmostValidNode != nil {
endPos = rightmostValidNode.End()
}
return find(rightmostValidNode, endPos)
}
return find(containingNode, endPos)
}
func FindNextToken(previousToken *ast.Node, parent *ast.Node, file *ast.SourceFile) *ast.Node {
var find func(n *ast.Node) *ast.Node
find = func(n *ast.Node) *ast.Node {
if ast.IsTokenKind(n.Kind) && n.Pos() == previousToken.End() {
// this is token that starts at the end of previous token - return it
return n
}
// Node that contains `previousToken` or occurs immediately after it.
var foundNode *ast.Node
visitNode := func(node *ast.Node, _ *ast.NodeVisitor) *ast.Node {
if node != nil && node.Flags&ast.NodeFlagsReparsed == 0 &&
node.Pos() <= previousToken.End() && node.End() > previousToken.End() {
foundNode = node
}
return node
}
visitNodes := func(nodeList *ast.NodeList, _ *ast.NodeVisitor) *ast.NodeList {
if nodeList != nil && len(nodeList.Nodes) > 0 && foundNode == nil {
nodes := nodeList.Nodes
index, match := core.BinarySearchUniqueFunc(nodes, func(_ int, node *ast.Node) int {
if node.Flags&ast.NodeFlagsReparsed != 0 {
return comparisonLessThan
}
if node.Pos() > previousToken.End() {
return comparisonGreaterThan
}
if node.End() <= previousToken.Pos() {
return comparisonLessThan
}
return comparisonEqualTo
})
if match {
foundNode = nodes[index]
}
}
return nodeList
}
VisitEachChildAndJSDoc(n, file, visitNode, visitNodes)
// Cases:
// 1. no answer exists
// 2. answer is an unvisited token
// 3. answer is in the visited found node
// Case 3: look for the next token inside the found node.
if foundNode != nil {
return find(foundNode)
}
startPos := previousToken.End()
// Case 2: look for the next token directly.
if startPos >= n.Pos() && startPos < n.End() {
scanner := scanner.GetScannerForSourceFile(file, startPos)
token := scanner.Token()
tokenFullStart := scanner.TokenFullStart()
tokenEnd := scanner.TokenEnd()
flags := scanner.TokenFlags()
// Use tokenFullStart (which includes leading trivia) to match TS's
// findNextToken behavior where `n.pos === previousToken.end` is checked
// (TS's pos includes trivia, same as Go's Pos()/tokenFullStart).
if tokenFullStart == previousToken.End() {
return file.GetOrCreateToken(token, tokenFullStart, tokenEnd, n, flags)
}
panic(fmt.Sprintf("Expected to find next token at %d, got token %s at %d", previousToken.End(), token, tokenFullStart))
}
// Case 3: no answer.
return nil
}
return find(parent)
}
func getNodeVisitor(
visitNode func(*ast.Node, *ast.NodeVisitor) *ast.Node,
visitNodes func(*ast.NodeList, *ast.NodeVisitor) *ast.NodeList,
) *ast.NodeVisitor {
var wrappedVisitNode func(*ast.Node, *ast.NodeVisitor) *ast.Node
var wrappedVisitNodes func(*ast.NodeList, *ast.NodeVisitor) *ast.NodeList
if visitNode != nil {
wrappedVisitNode = func(n *ast.Node, v *ast.NodeVisitor) *ast.Node {
if ast.IsJSDocSingleCommentNodeComment(n) {
return n
}
return visitNode(n, v)
}
}
if visitNodes != nil {
wrappedVisitNodes = func(n *ast.NodeList, v *ast.NodeVisitor) *ast.NodeList {
if ast.IsJSDocSingleCommentNodeList(n) {
return n
}
return visitNodes(n, v)
}
}
return ast.NewNodeVisitor(core.Identity, nil, ast.NodeVisitorHooks{
VisitNode: wrappedVisitNode,
VisitToken: wrappedVisitNode,
VisitNodes: wrappedVisitNodes,
VisitModifiers: func(modifiers *ast.ModifierList, visitor *ast.NodeVisitor) *ast.ModifierList {
if modifiers != nil {
wrappedVisitNodes(&modifiers.NodeList, visitor)
}
return modifiers
},
})
}
func shouldSkipChild(node *ast.Node) bool {
return node.Kind == ast.KindJSDoc ||
node.Kind == ast.KindJSDocText ||
node.Kind == ast.KindJSDocTypeLiteral ||
node.Kind == ast.KindJSDocSignature ||
ast.IsJSDocLinkLike(node) ||
ast.IsJSDocTag(node)
}
// FindChildOfKind searches for a child node or token of the specified kind within a containing node.
// This function scans through both AST nodes and intervening tokens to find the first match.
func FindChildOfKind(containingNode *ast.Node, kind ast.Kind, sourceFile *ast.SourceFile) *ast.Node {
lastNodePos := containingNode.Pos()
scan := scanner.GetScannerForSourceFile(sourceFile, lastNodePos)
var foundChild *ast.Node
visitNode := func(node *ast.Node) bool {
if node == nil || node.Flags&ast.NodeFlagsReparsed != 0 {
return false
}
// Look for child in preceding tokens.
startPos := lastNodePos
for startPos < node.Pos() {
tokenKind := scan.Token()
tokenEnd := scan.TokenEnd()
if tokenKind == kind {
tokenFullStart := scan.TokenFullStart()
flags := scan.TokenFlags()
foundChild = sourceFile.GetOrCreateToken(tokenKind, tokenFullStart, tokenEnd, containingNode, flags)
return true
}
startPos = tokenEnd
scan.Scan()
}
if node.Kind == kind {
foundChild = node
return true
}
lastNodePos = node.End()
scan.ResetPos(lastNodePos)
return false
}
ast.ForEachChildAndJSDoc(containingNode, sourceFile, visitNode)
if foundChild != nil {
return foundChild
}
// Look for child in trailing tokens.
startPos := lastNodePos
for startPos < containingNode.End() {
tokenKind := scan.Token()
tokenEnd := scan.TokenEnd()
if tokenKind == kind {
tokenFullStart := scan.TokenFullStart()
flags := scan.TokenFlags()
token := sourceFile.GetOrCreateToken(tokenKind, tokenFullStart, tokenEnd, containingNode, flags)
return token
}
startPos = tokenEnd
scan.Scan()
}
return nil
}

View File

@@ -0,0 +1,626 @@
package astnav_test
import (
"fmt"
"os"
"path/filepath"
"slices"
"strconv"
"strings"
"testing"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/astnav"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/parser"
"github.com/microsoft/typescript-go/internal/repo"
"github.com/microsoft/typescript-go/internal/testutil/baseline"
"github.com/microsoft/typescript-go/internal/testutil/jstest"
"gotest.tools/v3/assert"
)
var testFiles = []string{
filepath.Join(repo.TypeScriptSubmodulePath(), "src/services/mapCode.ts"),
}
func TestGetTokenAtPosition(t *testing.T) {
t.Parallel()
repo.SkipIfNoTypeScriptSubmodule(t)
jstest.SkipIfNoNodeJS(t)
t.Run("baseline", func(t *testing.T) {
t.Parallel()
baselineTokens(
t,
"GetTokenAtPosition",
false, /*includeEOF*/
func(fileText string, positions []int) []*tokenInfo {
return tsGetTokensAtPositions(t, fileText, positions)
},
func(file *ast.SourceFile, pos int) *tokenInfo {
return toTokenInfo(astnav.GetTokenAtPosition(file, pos))
},
)
})
t.Run("go baseline json", func(t *testing.T) {
t.Parallel()
baselineGoTokensJSON(t, "GetTokenAtPosition", func(file *ast.SourceFile, pos int) *tokenInfo {
return toTokenInfo(astnav.GetTokenAtPosition(file, pos))
})
})
t.Run("JSDoc type assertion", func(t *testing.T) {
t.Parallel()
fileText := `function foo(x) {
const s = /**@type {string}*/(x)
}`
file := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/test.js",
Path: "/test.js",
}, fileText, core.ScriptKindJS)
// Position of 'x' inside the parenthesized expression (position 52)
position := 52
// This should not panic - it previously panicked with:
// "did not expect KindParenthesizedExpression to have KindIdentifier in its trivia"
token := astnav.GetTouchingPropertyName(file, position)
if token == nil {
t.Fatal("Expected to get a token, got nil")
}
// The function may return either the identifier itself or the containing
// parenthesized expression, depending on how the AST is structured
if token.Kind != ast.KindIdentifier && token.Kind != ast.KindParenthesizedExpression {
t.Errorf("Expected identifier or parenthesized expression, got %s", token.Kind)
}
})
t.Run("JSDoc type assertion with comment", func(t *testing.T) {
t.Parallel()
// Exact code from the issue report
fileText := `function foo(x) {
const s = /**@type {string}*/(x) // Go-to-definition on x causes panic
}`
file := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/test.js",
Path: "/test.js",
}, fileText, core.ScriptKindJS)
// Find position of 'x' in the type assertion
xPos := 52 // Position of 'x' in (x)
// This should not panic
token := astnav.GetTouchingPropertyName(file, xPos)
assert.Assert(t, token != nil, "Expected to get a token")
})
t.Run("pointer equality", func(t *testing.T) {
t.Parallel()
fileText := `
function foo() {
return 0;
}
`
file := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/file.ts",
Path: "/file.ts",
}, fileText, core.ScriptKindTS)
assert.Equal(t, astnav.GetTokenAtPosition(file, 0), astnav.GetTokenAtPosition(file, 0))
})
}
func TestGetTouchingPropertyName(t *testing.T) {
t.Parallel()
jstest.SkipIfNoNodeJS(t)
repo.SkipIfNoTypeScriptSubmodule(t)
baselineTokens(
t,
"GetTouchingPropertyName",
false, /*includeEOF*/
func(fileText string, positions []int) []*tokenInfo {
return tsGetTouchingPropertyName(t, fileText, positions)
},
func(file *ast.SourceFile, pos int) *tokenInfo {
return toTokenInfo(astnav.GetTouchingPropertyName(file, pos))
},
)
t.Run("go baseline json", func(t *testing.T) {
t.Parallel()
baselineGoTokensJSON(t, "GetTouchingPropertyName", func(file *ast.SourceFile, pos int) *tokenInfo {
return toTokenInfo(astnav.GetTouchingPropertyName(file, pos))
})
})
}
func baselineTokens(t *testing.T, testName string, includeEOF bool, getTSTokens func(fileText string, positions []int) []*tokenInfo, getGoToken func(file *ast.SourceFile, pos int) *tokenInfo) {
for _, fileName := range testFiles {
t.Run(filepath.Base(fileName), func(t *testing.T) {
t.Parallel()
fileText, err := os.ReadFile(fileName)
assert.NilError(t, err)
positions := make([]int, len(fileText)+core.IfElse(includeEOF, 1, 0))
for i := range positions {
positions[i] = i
}
tsTokens := getTSTokens(string(fileText), positions)
file := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/file.ts",
Path: "/file.ts",
}, string(fileText), core.ScriptKindTS)
var output strings.Builder
currentRange := core.NewTextRange(0, 0)
currentDiff := tokenDiff{}
for pos, tsToken := range tsTokens {
goToken := getGoToken(file, pos)
diff := tokenDiff{goToken: goToken, tsToken: tsToken}
if !diffEqual(currentDiff, diff) {
if !tokensEqual(currentDiff.goToken, currentDiff.tsToken) {
writeRangeDiff(&output, file, currentDiff, currentRange, pos)
}
currentDiff = diff
currentRange = core.NewTextRange(pos, pos)
}
currentRange = currentRange.WithEnd(pos)
}
if !tokensEqual(currentDiff.goToken, currentDiff.tsToken) {
writeRangeDiff(&output, file, currentDiff, currentRange, len(tsTokens)-1)
}
baseline.Run(
t,
fmt.Sprintf("%s.%s.baseline.txt", testName, filepath.Base(fileName)),
core.IfElse(output.Len() > 0, output.String(), baseline.NoContent),
baseline.Options{
Subfolder: "astnav",
},
)
})
}
}
type tokenRun struct {
StartPos int `json:"startPos"`
EndPos int `json:"endPos"`
Kind string `json:"kind"`
NodePos int `json:"nodePos"`
NodeEnd int `json:"nodeEnd"`
}
func baselineGoTokensJSON(t *testing.T, testName string, getGoToken func(file *ast.SourceFile, pos int) *tokenInfo) {
for _, fileName := range testFiles {
t.Run(filepath.Base(fileName), func(t *testing.T) {
t.Parallel()
fileText, err := os.ReadFile(fileName)
assert.NilError(t, err)
file := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/file.ts",
Path: "/file.ts",
}, string(fileText), core.ScriptKindTS)
maxPos := len(fileText)
var runs []tokenRun
var current *tokenRun
for pos := range maxPos {
token := getGoToken(file, pos)
if current != nil && token != nil && current.Kind == token.Kind && current.NodePos == token.Pos && current.NodeEnd == token.End {
current.EndPos = pos
} else {
if current != nil {
runs = append(runs, *current)
}
if token != nil {
current = &tokenRun{
StartPos: pos,
EndPos: pos,
Kind: token.Kind,
NodePos: token.Pos,
NodeEnd: token.End,
}
} else {
current = nil
}
}
}
if current != nil {
runs = append(runs, *current)
}
output := core.Must(core.StringifyJson(runs, "", " "))
baseline.Run(
t,
fmt.Sprintf("%s.%s.baseline.json", testName, filepath.Base(fileName)),
output,
baseline.Options{
Subfolder: "astnav",
},
)
})
}
}
type tokenDiff struct {
goToken *tokenInfo
tsToken *tokenInfo
}
type tokenInfo struct {
Kind string `json:"kind"`
Pos int `json:"pos"`
End int `json:"end"`
}
func toTokenInfo(node *ast.Node) *tokenInfo {
if node == nil {
return nil
}
kind := strings.Replace(node.Kind.String(), "Kind", "", 1)
switch kind {
case "EndOfFile":
kind = "EndOfFileToken"
}
return &tokenInfo{
Kind: kind,
Pos: node.Pos(),
End: node.End(),
}
}
func diffEqual(a, b tokenDiff) bool {
return tokensEqual(a.goToken, b.goToken) && tokensEqual(a.tsToken, b.tsToken)
}
func tokensEqual(t1, t2 *tokenInfo) bool {
if t1 == nil || t2 == nil {
return t1 == t2
}
return *t1 == *t2
}
func tsGetTokensAtPositions(t testing.TB, fileText string, positions []int) []*tokenInfo {
dir := t.TempDir()
err := os.WriteFile(filepath.Join(dir, "file.ts"), []byte(fileText), 0o644)
assert.NilError(t, err)
err = os.WriteFile(filepath.Join(dir, "positions.json"), []byte(core.Must(core.StringifyJson(positions, "", ""))), 0o644)
assert.NilError(t, err)
script := `
import fs from "fs";
export default (ts) => {
const positions = JSON.parse(fs.readFileSync("positions.json", "utf8"));
const fileText = fs.readFileSync("file.ts", "utf8");
const file = ts.createSourceFile(
"file.ts",
fileText,
{ languageVersion: ts.ScriptTarget.Latest, jsDocParsingMode: ts.JSDocParsingMode.ParseAll },
/*setParentNodes*/ true
);
return positions.map(position => {
let token = ts.getTokenAtPosition(file, position);
if (token.kind === ts.SyntaxKind.SyntaxList) {
token = token.parent;
}
return {
kind: ts.Debug.formatSyntaxKind(token.kind),
pos: token.pos,
end: token.end,
};
});
};`
info, err := jstest.EvalNodeScriptWithTS[[]*tokenInfo](t, script, dir, "")
assert.NilError(t, err)
return info
}
func tsGetTouchingPropertyName(t testing.TB, fileText string, positions []int) []*tokenInfo {
dir := t.TempDir()
err := os.WriteFile(filepath.Join(dir, "file.ts"), []byte(fileText), 0o644)
assert.NilError(t, err)
err = os.WriteFile(filepath.Join(dir, "positions.json"), []byte(core.Must(core.StringifyJson(positions, "", ""))), 0o644)
assert.NilError(t, err)
script := `
import fs from "fs";
export default (ts) => {
const positions = JSON.parse(fs.readFileSync("positions.json", "utf8"));
const fileText = fs.readFileSync("file.ts", "utf8");
const file = ts.createSourceFile(
"file.ts",
fileText,
{ languageVersion: ts.ScriptTarget.Latest, jsDocParsingMode: ts.JSDocParsingMode.ParseAll },
/*setParentNodes*/ true
);
return positions.map(position => {
let token = ts.getTouchingPropertyName(file, position);
if (token.kind === ts.SyntaxKind.SyntaxList) {
token = token.parent;
}
return {
kind: ts.Debug.formatSyntaxKind(token.kind),
pos: token.pos,
end: token.end,
};
});
};`
info, err := jstest.EvalNodeScriptWithTS[[]*tokenInfo](t, script, dir, "")
assert.NilError(t, err)
return info
}
func writeRangeDiff(output *strings.Builder, file *ast.SourceFile, diff tokenDiff, rng core.TextRange, position int) {
lines := file.ECMALineMap()
tsTokenPos := position
goTokenPos := position
tsTokenEnd := position
goTokenEnd := position
if diff.tsToken != nil {
tsTokenPos = diff.tsToken.Pos
tsTokenEnd = diff.tsToken.End
}
if diff.goToken != nil {
goTokenPos = diff.goToken.Pos
goTokenEnd = diff.goToken.End
}
tsStartLine, _ := core.PositionToLineAndByteOffset(tsTokenPos, lines)
tsEndLine, _ := core.PositionToLineAndByteOffset(tsTokenEnd, lines)
goStartLine, _ := core.PositionToLineAndByteOffset(goTokenPos, lines)
goEndLine, _ := core.PositionToLineAndByteOffset(goTokenEnd, lines)
contextLines := 2
startLine := min(tsStartLine, goStartLine)
endLine := max(tsEndLine, goEndLine)
markerLines := []int{tsStartLine, tsEndLine, goStartLine, goEndLine}
slices.Sort(markerLines)
contextStart := max(0, startLine-contextLines)
contextEnd := min(len(lines)-1, endLine+contextLines)
digits := len(strconv.Itoa(contextEnd))
shouldTruncate := func(line int) (result bool, skipTo int) {
index, _ := slices.BinarySearch(markerLines, line)
if index == 0 || index == len(markerLines) {
return false, 0
}
low := markerLines[index-1]
high := markerLines[index]
if line-low > 5 && high-line > 5 {
return true, high - 5
}
return false, 0
}
if output.Len() > 0 {
output.WriteString("\n\n")
}
output.WriteString(fmt.Sprintf("〚Positions: [%d, %d]〛\n", rng.Pos(), rng.End()))
if diff.tsToken != nil {
output.WriteString(fmt.Sprintf("【TS: %s [%d, %d)】\n", diff.tsToken.Kind, tsTokenPos, tsTokenEnd))
} else {
output.WriteString("【TS: nil】\n")
}
if diff.goToken != nil {
output.WriteString(fmt.Sprintf("《Go: %s [%d, %d)》\n", diff.goToken.Kind, goTokenPos, goTokenEnd))
} else {
output.WriteString("《Go: nil》\n")
}
for line := contextStart; line <= contextEnd; line++ {
if truncate, skipTo := shouldTruncate(line); truncate {
output.WriteString(fmt.Sprintf("%s │........ %d lines omitted ........\n", strings.Repeat(" ", digits), skipTo-line+1))
line = skipTo
}
output.WriteString(fmt.Sprintf("%*d │", digits, line+1))
end := len(file.Text()) + 1
if line < len(lines)-1 {
end = int(lines[line+1])
}
for pos := int(lines[line]); pos < end; pos++ {
if pos == rng.End()+1 {
output.WriteString("〛")
}
if diff.tsToken != nil && pos == tsTokenEnd {
output.WriteString("】")
}
if diff.goToken != nil && pos == goTokenEnd {
output.WriteString("》")
}
if diff.goToken != nil && pos == goTokenPos {
output.WriteString("《")
}
if diff.tsToken != nil && pos == tsTokenPos {
output.WriteString("【")
}
if pos == rng.Pos() {
output.WriteString("〚")
}
if pos < len(file.Text()) {
output.WriteByte(file.Text()[pos])
}
}
}
}
func TestFindPrecedingToken(t *testing.T) {
t.Parallel()
repo.SkipIfNoTypeScriptSubmodule(t)
jstest.SkipIfNoNodeJS(t)
t.Run("baseline", func(t *testing.T) {
t.Parallel()
baselineTokens(
t,
"FindPrecedingToken",
true, /*includeEOF*/
func(fileText string, positions []int) []*tokenInfo {
return tsFindPrecedingTokens(t, fileText, positions)
},
func(file *ast.SourceFile, pos int) *tokenInfo {
return toTokenInfo(astnav.FindPrecedingToken(file, pos))
},
)
})
t.Run("go baseline json", func(t *testing.T) {
t.Parallel()
baselineGoTokensJSON(t, "FindPrecedingToken", func(file *ast.SourceFile, pos int) *tokenInfo {
return toTokenInfo(astnav.FindPrecedingToken(file, pos))
})
})
}
func TestFindNextToken(t *testing.T) {
t.Parallel()
repo.SkipIfNoTypeScriptSubmodule(t)
t.Run("go baseline json", func(t *testing.T) {
t.Parallel()
baselineGoTokensJSON(t, "FindNextToken", func(file *ast.SourceFile, pos int) (result *tokenInfo) {
// FindNextToken panics (like Go's assert) when the scanner finds trivia between
// previousToken.End() and the next syntactic token. Catch those to avoid crashing
// the baseline generator; those positions will be absent from the baseline.
defer func() {
if r := recover(); r != nil {
result = nil
}
}()
token := astnav.GetTokenAtPosition(file, pos)
next := astnav.FindNextToken(token, file.AsNode(), file)
return toTokenInfo(next)
})
})
}
func TestUnitFindPrecedingToken(t *testing.T) {
t.Parallel()
testCases := []struct {
name string
fileContent string
position int
expectedKind ast.Kind
}{
{
name: "after dot in jsdoc",
fileContent: `import {
CharacterCodes,
compareStringsCaseInsensitive,
compareStringsCaseSensitive,
compareValues,
Comparison,
Debug,
endsWith,
equateStringsCaseInsensitive,
equateStringsCaseSensitive,
GetCanonicalFileName,
getDeclarationFileExtension,
getStringComparer,
identity,
lastOrUndefined,
Path,
some,
startsWith,
} from "./_namespaces/ts.js";
/**
* Internally, we represent paths as strings with '/' as the directory separator.
* When we make system calls (eg: LanguageServiceHost.getDirectory()),
* we expect the host to correctly handle paths in our specified format.
*
* @internal
*/
export const directorySeparator = "/";
/** @internal */
export const altDirectorySeparator = "\\";
const urlSchemeSeparator = "://";
const backslashRegExp = /\\/g;
backslashRegExp.
//Path Tests
/**
* Determines whether a charCode corresponds to '/' or '\'.
*
* @internal
*/
export function isAnyDirectorySeparator(charCode: number): boolean {
return charCode === CharacterCodes.slash || charCode === CharacterCodes.backslash;
}`,
position: 839,
expectedKind: ast.KindDotToken,
},
{
name: "after comma in parameter list",
fileContent: `takesCb((n, s, ))`,
position: 15,
expectedKind: ast.KindCommaToken,
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
file := parser.ParseSourceFile(ast.SourceFileParseOptions{
FileName: "/file.ts",
Path: "/file.ts",
}, testCase.fileContent, core.ScriptKindTS)
token := astnav.FindPrecedingToken(file, testCase.position)
assert.Equal(t, token.Kind, testCase.expectedKind)
})
}
}
func tsFindPrecedingTokens(t *testing.T, fileText string, positions []int) []*tokenInfo {
dir := t.TempDir()
err := os.WriteFile(filepath.Join(dir, "file.ts"), []byte(fileText), 0o644)
assert.NilError(t, err)
err = os.WriteFile(filepath.Join(dir, "positions.json"), []byte(core.Must(core.StringifyJson(positions, "", ""))), 0o644)
assert.NilError(t, err)
script := `
import fs from "fs";
export default (ts) => {
const positions = JSON.parse(fs.readFileSync("positions.json", "utf8"));
const fileText = fs.readFileSync("file.ts", "utf8");
const file = ts.createSourceFile(
"file.ts",
fileText,
{ languageVersion: ts.ScriptTarget.Latest, jsDocParsingMode: ts.JSDocParsingMode.ParseAll },
/*setParentNodes*/ true
);
return positions.map(position => {
let token = ts.findPrecedingToken(position, file);
if (token === undefined) {
return undefined;
}
if (token.kind === ts.SyntaxKind.SyntaxList) {
token = token.parent;
}
return {
kind: ts.Debug.formatSyntaxKind(token.kind),
pos: token.pos,
end: token.end,
};
});
};`
info, err := jstest.EvalNodeScriptWithTS[[]*tokenInfo](t, script, dir, "")
assert.NilError(t, err)
return info
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,46 @@
package binder
import (
"runtime"
"testing"
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/parser"
"github.com/microsoft/typescript-go/internal/testutil/fixtures"
"github.com/microsoft/typescript-go/internal/tspath"
"github.com/microsoft/typescript-go/internal/vfs/osvfs"
)
func BenchmarkBind(b *testing.B) {
for _, f := range fixtures.BenchFixtures {
b.Run(f.Name(), func(b *testing.B) {
f.SkipIfNotExist(b)
fileName := tspath.GetNormalizedAbsolutePath(f.Path(), "/")
path := tspath.ToPath(fileName, "/", osvfs.FS().UseCaseSensitiveFileNames())
sourceText := f.ReadFile(b)
parseOptions := ast.SourceFileParseOptions{
FileName: fileName,
Path: path,
}
scriptKind := core.GetScriptKindFromFileName(fileName)
sourceFiles := make([]*ast.SourceFile, b.N)
for i := range b.N {
sourceFiles[i] = parser.ParseSourceFile(parseOptions, sourceText, scriptKind)
}
// The above parses do a lot of work; ensure GC is finished before we start collecting performance data.
// GC must be called twice to allow things to settle.
runtime.GC()
runtime.GC()
b.ResetTimer()
for i := range b.N {
BindSourceFile(sourceFiles[i])
}
})
}
}

View File

@@ -0,0 +1,498 @@
package binder
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/diagnostics"
)
type NameResolver struct {
CompilerOptions *core.CompilerOptions
GetSymbolOfDeclaration func(node *ast.Node) *ast.Symbol
Error func(location *ast.Node, message *diagnostics.Message, args ...any) *ast.Diagnostic
Globals ast.SymbolTable
ArgumentsSymbol *ast.Symbol
RequireSymbol *ast.Symbol
Lookup func(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol
SymbolReferenced func(symbol *ast.Symbol, meaning ast.SymbolFlags)
SetRequiresScopeChangeCache func(node *ast.Node, value core.Tristate)
GetRequiresScopeChangeCache func(node *ast.Node) core.Tristate
OnPropertyWithInvalidInitializer func(location *ast.Node, name string, declaration *ast.Node, result *ast.Symbol) bool
OnFailedToResolveSymbol func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message)
OnSuccessfullyResolvedSymbol func(location *ast.Node, result *ast.Symbol, meaning ast.SymbolFlags, lastLocation *ast.Node, associatedDeclarationForContainingInitializerOrBindingName *ast.Node, withinDeferredContext bool)
}
func (r *NameResolver) Resolve(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol {
var result *ast.Symbol
var lastLocation *ast.Node
var lastSelfReferenceLocation *ast.Node
var propertyWithInvalidInitializer *ast.Node
var associatedDeclarationForContainingInitializerOrBindingName *ast.Node
var withinDeferredContext bool
var grandparent *ast.Node
originalLocation := location // needed for did-you-mean error reporting, which gathers candidates starting from the original location
nameIsConst := name == "const"
loop:
for location != nil {
if nameIsConst && ast.IsConstAssertion(location) {
// `const` in an `as const` has no symbol, but issues no error because there is no *actual* lookup of the type
// (it refers to the constant type of the expression instead)
return nil
}
if ast.IsModuleOrEnumDeclaration(location) && lastLocation != nil && location.Name() == lastLocation {
// If lastLocation is the name of a namespace or enum, skip the parent since it will have is own locals that could
// conflict.
lastLocation = location
location = location.Parent
}
locals := location.Locals()
// Locals of a source file are not in scope (because they get merged into the global symbol table)
if locals != nil && !ast.IsGlobalSourceFile(location) {
result = r.lookup(locals, name, meaning)
if result != nil {
useResult := true
if ast.IsFunctionLike(location) && lastLocation != nil && lastLocation != location.Body() {
// symbol lookup restrictions for function-like declarations
// - Type parameters of a function are in scope in the entire function declaration, including the parameter
// list and return type. However, local types are only in scope in the function body.
// - parameters are only in the scope of function body
// This restriction does not apply to JSDoc comment types because they are parented
// at a higher level than type parameters would normally be
if meaning&result.Flags&ast.SymbolFlagsType != 0 && lastLocation.Kind != ast.KindJSDoc {
// type parameters are visible in parameter list, return type and type parameter list.
// Synthetic fake scopes are added for signatures so type parameters are accessible from them.
useResult = result.Flags&ast.SymbolFlagsTypeParameter != 0 &&
(lastLocation.Flags&ast.NodeFlagsSynthesized != 0 ||
lastLocation == location.Type() ||
lastLocation.Kind == ast.KindParameter ||
lastLocation.Kind == ast.KindJSDocParameterTag ||
lastLocation.Kind == ast.KindJSDocReturnTag ||
lastLocation.Kind == ast.KindTypeParameter)
}
if meaning&result.Flags&ast.SymbolFlagsVariable != 0 {
// expression inside parameter will lookup as normal variable scope when targeting es2015+
if r.useOuterVariableScopeInParameter(result, location, lastLocation) {
useResult = false
} else if result.Flags&ast.SymbolFlagsFunctionScopedVariable != 0 {
// parameters are visible only inside function body, parameter list and return type
// technically for parameter list case here we might mix parameters and variables declared in function,
// however it is detected separately when checking initializers of parameters
// to make sure that they reference no variables declared after them.
useResult = lastLocation.Kind == ast.KindParameter ||
lastLocation.Flags&ast.NodeFlagsSynthesized != 0 ||
lastLocation == location.Type() && ast.FindAncestor(result.ValueDeclaration, ast.IsParameterDeclaration) != nil
}
}
} else if location.Kind == ast.KindConditionalType {
// A type parameter declared using 'infer T' in a conditional type is visible only in
// the true branch of the conditional type.
useResult = lastLocation == location.AsConditionalTypeNode().TrueType
}
if useResult {
break loop
}
result = nil
}
}
withinDeferredContext = withinDeferredContext || getIsDeferredContext(location, lastLocation)
switch location.Kind {
case ast.KindSourceFile:
if !ast.IsExternalOrCommonJSModule(location.AsSourceFile()) {
break
}
fallthrough
case ast.KindModuleDeclaration:
moduleSymbol := r.getSymbolOfDeclaration(location)
if moduleSymbol == nil {
break
}
moduleExports := moduleSymbol.Exports
if ast.IsSourceFile(location) || (ast.IsModuleDeclaration(location) && location.Flags&ast.NodeFlagsAmbient != 0 && !ast.IsGlobalScopeAugmentation(location)) {
// It's an external module. First see if the module has an export default and if the local
// name of that export default matches.
result = moduleExports[ast.InternalSymbolNameDefault]
if result != nil {
localSymbol := GetLocalSymbolForExportDefault(result)
if localSymbol != nil && result.Flags&meaning != 0 && localSymbol.Name == name {
break loop
}
result = nil
}
// Because of module/namespace merging, a module's exports are in scope,
// yet we never want to treat an export specifier as putting a member in scope.
// Therefore, if the name we find is purely an export specifier, it is not actually considered in scope.
// Two things to note about this:
// 1. We have to check this without calling getSymbol. The problem with calling getSymbol
// on an export specifier is that it might find the export specifier itself, and try to
// resolve it as an alias. This will cause the checker to consider the export specifier
// a circular alias reference when it might not be.
// 2. We check === SymbolFlags.Alias in order to check that the symbol is *purely*
// an alias. If we used &, we'd be throwing out symbols that have non alias aspects,
// which is not the desired behavior.
moduleExport := moduleExports[name]
if moduleExport != nil && moduleExport.Flags == ast.SymbolFlagsAlias && (ast.GetDeclarationOfKind(moduleExport, ast.KindExportSpecifier) != nil || ast.GetDeclarationOfKind(moduleExport, ast.KindNamespaceExport) != nil) {
break
}
}
if name != ast.InternalSymbolNameDefault {
if result = r.lookup(moduleExports, name, meaning&ast.SymbolFlagsModuleMember); result != nil {
if ast.IsSourceFile(location) && location.AsSourceFile().CommonJSModuleIndicator != nil && result.Flags&ast.SymbolFlagsType == 0 {
result = nil
} else {
break loop
}
}
}
case ast.KindEnumDeclaration:
enumSymbol := r.getSymbolOfDeclaration(location)
if enumSymbol == nil {
break
}
result = r.lookup(enumSymbol.Exports, name, meaning&ast.SymbolFlagsEnumMember)
if result != nil {
if nameNotFoundMessage != nil && r.CompilerOptions.GetIsolatedModules() && location.Flags&ast.NodeFlagsAmbient == 0 && ast.GetSourceFileOfNode(location) != ast.GetSourceFileOfNode(result.ValueDeclaration) {
isolatedModulesLikeFlagName := core.IfElse(r.CompilerOptions.VerbatimModuleSyntax == core.TSTrue, "verbatimModuleSyntax", "isolatedModules")
r.error(originalLocation, diagnostics.Cannot_access_0_from_another_file_without_qualification_when_1_is_enabled_Use_2_instead,
name, isolatedModulesLikeFlagName, enumSymbol.Name+"."+name)
}
break loop
}
case ast.KindPropertyDeclaration:
if !ast.IsStatic(location) {
ctor := ast.FindConstructorDeclaration(location.Parent)
if ctor != nil && ctor.Locals() != nil {
if r.lookup(ctor.Locals(), name, meaning&ast.SymbolFlagsValue) != nil {
// Remember the property node, it will be used later to report appropriate error
propertyWithInvalidInitializer = location
}
}
}
case ast.KindClassDeclaration, ast.KindClassExpression, ast.KindInterfaceDeclaration:
result = r.lookup(r.getSymbolOfDeclaration(location).Members, name, meaning&ast.SymbolFlagsType)
if result != nil {
if !isTypeParameterSymbolDeclaredInContainer(result, location) {
// ignore type parameters not declared in this container
result = nil
break
}
if lastLocation != nil && ast.IsStatic(lastLocation) {
// TypeScript 1.0 spec (April 2014): 3.4.1
// The scope of a type parameter extends over the entire declaration with which the type
// parameter list is associated, with the exception of static member declarations in classes.
if nameNotFoundMessage != nil {
r.error(originalLocation, diagnostics.Static_members_cannot_reference_class_type_parameters)
}
return nil
}
break loop
}
if ast.IsClassExpression(location) && meaning&ast.SymbolFlagsClass != 0 {
className := location.Name()
if className != nil && name == className.Text() {
result = location.Symbol()
break loop
}
}
case ast.KindExpressionWithTypeArguments:
if lastLocation == location.Expression() && ast.IsHeritageClause(location.Parent) && location.Parent.AsHeritageClause().Token == ast.KindExtendsKeyword {
container := location.Parent.Parent
if ast.IsClassLike(container) {
result = r.lookup(r.getSymbolOfDeclaration(container).Members, name, meaning&ast.SymbolFlagsType)
if result != nil {
if nameNotFoundMessage != nil {
r.error(originalLocation, diagnostics.Base_class_expressions_cannot_reference_class_type_parameters)
}
return nil
}
}
}
// It is not legal to reference a class's own type parameters from a computed property name that
// belongs to the class. For example:
//
// function foo<T>() { return '' }
// class C<T> { // <-- Class's own type parameter T
// [foo<T>()]() { } // <-- Reference to T from class's own computed property
// }
case ast.KindComputedPropertyName:
grandparent = location.Parent.Parent
if ast.IsClassLike(grandparent) || ast.IsInterfaceDeclaration(grandparent) {
// A reference to this grandparent's type parameters would be an error
result = r.lookup(r.getSymbolOfDeclaration(grandparent).Members, name, meaning&ast.SymbolFlagsType)
if result != nil {
if nameNotFoundMessage != nil {
r.error(originalLocation, diagnostics.A_computed_property_name_cannot_reference_a_type_parameter_from_its_containing_type)
}
return nil
}
}
case ast.KindMethodDeclaration, ast.KindConstructor, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindFunctionDeclaration:
if meaning&ast.SymbolFlagsVariable != 0 && name == "arguments" {
result = r.argumentsSymbol()
break loop
}
case ast.KindFunctionExpression:
if meaning&ast.SymbolFlagsVariable != 0 && name == "arguments" {
result = r.argumentsSymbol()
break loop
}
if meaning&ast.SymbolFlagsFunction != 0 {
functionName := location.AsFunctionExpression().Name()
if functionName != nil && name == functionName.Text() {
result = location.Symbol()
break loop
}
}
case ast.KindDecorator:
// Decorators are resolved at the class declaration. Resolving at the parameter
// or member would result in looking up locals in the method.
//
// function y() {}
// class C {
// method(@y x, y) {} // <-- decorator y should be resolved at the class declaration, not the parameter.
// }
//
if location.Parent != nil && location.Parent.Kind == ast.KindParameter {
location = location.Parent
}
// function y() {}
// class C {
// @y method(x, y) {} // <-- decorator y should be resolved at the class declaration, not the method.
// }
//
// class Decorators are resolved outside of the class to avoid referencing type parameters of that class.
//
// type T = number;
// declare function y(x: T): any;
// @param(1 as T) // <-- T should resolve to the type alias outside of class C
// class C<T> {}
if location.Parent != nil && (ast.IsClassElement(location.Parent) || location.Parent.Kind == ast.KindClassDeclaration) {
location = location.Parent
}
case ast.KindParameter:
parameterDeclaration := location.AsParameterDeclaration()
if lastLocation != nil && (lastLocation == parameterDeclaration.Initializer ||
lastLocation == parameterDeclaration.Name() && ast.IsBindingPattern(lastLocation)) {
if associatedDeclarationForContainingInitializerOrBindingName == nil {
associatedDeclarationForContainingInitializerOrBindingName = location
}
}
case ast.KindBindingElement:
bindingElement := location.AsBindingElement()
if lastLocation != nil && (lastLocation == bindingElement.Initializer ||
lastLocation == bindingElement.Name() && ast.IsBindingPattern(lastLocation)) {
if ast.IsPartOfParameterDeclaration(location) && associatedDeclarationForContainingInitializerOrBindingName == nil {
associatedDeclarationForContainingInitializerOrBindingName = location
}
}
case ast.KindInferType:
if meaning&ast.SymbolFlagsTypeParameter != 0 {
parameterName := location.AsInferTypeNode().TypeParameter.AsTypeParameterDeclaration().Name()
if parameterName != nil && name == parameterName.Text() {
result = location.AsInferTypeNode().TypeParameter.Symbol()
break loop
}
}
case ast.KindExportSpecifier:
exportSpecifier := location.AsExportSpecifier()
if lastLocation != nil && lastLocation == exportSpecifier.PropertyName && location.Parent.Parent.ModuleSpecifier() != nil {
location = location.Parent.Parent.Parent
}
}
if isSelfReferenceLocation(location, lastLocation) {
lastSelfReferenceLocation = location
}
lastLocation = location
// !!! In Strada, JSDocTemplateTag/JSDocParameterTag/JSDocReturnTag locations skip to
// getEffectiveContainerForJSDocTemplateTag/getHostSignatureFromJSDoc instead of location.parent.
// This is a no-op currently because JSDoc nodes have no locals and getEffectiveJSDocHost is not
// fully ported for JS assignment patterns.
location = location.Parent
}
// 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.
if isUse && result != nil && (lastSelfReferenceLocation == nil || result != lastSelfReferenceLocation.Symbol()) {
if r.SymbolReferenced != nil {
r.SymbolReferenced(result, meaning)
}
}
if result == nil && !excludeGlobals {
result = r.lookup(r.Globals, name, meaning|ast.SymbolFlagsGlobalLookup)
}
if result == nil {
if originalLocation != nil && ast.IsInJSFile(originalLocation) && originalLocation.Parent != nil {
if ast.IsRequireCall(originalLocation.Parent, false /*requireStringLiteralLikeArgument*/) {
return r.RequireSymbol
}
}
}
if nameNotFoundMessage != nil {
if propertyWithInvalidInitializer != nil && r.OnPropertyWithInvalidInitializer != nil && r.OnPropertyWithInvalidInitializer(originalLocation, name, propertyWithInvalidInitializer, result) {
return nil
}
if result == nil {
if r.OnFailedToResolveSymbol != nil {
r.OnFailedToResolveSymbol(originalLocation, name, meaning, nameNotFoundMessage)
}
} else {
if r.OnSuccessfullyResolvedSymbol != nil {
r.OnSuccessfullyResolvedSymbol(originalLocation, result, meaning, lastLocation, associatedDeclarationForContainingInitializerOrBindingName, withinDeferredContext)
}
}
}
return result
}
func (r *NameResolver) useOuterVariableScopeInParameter(result *ast.Symbol, location *ast.Node, lastLocation *ast.Node) bool {
if ast.IsParameterDeclaration(lastLocation) {
body := location.Body()
if body != nil && result.ValueDeclaration != nil && result.ValueDeclaration.Pos() >= body.Pos() && result.ValueDeclaration.End() <= body.End() {
// check for several cases where we introduce temporaries that require moving the name/initializer of the parameter to the body
// - static field in a class expression
// - optional chaining pre-es2020
// - nullish coalesce pre-es2020
// - spread assignment in binding pattern pre-es2017
functionLocation := location
declarationRequiresScopeChange := core.TSUnknown
if r.GetRequiresScopeChangeCache != nil {
declarationRequiresScopeChange = r.GetRequiresScopeChangeCache(functionLocation)
}
if declarationRequiresScopeChange == core.TSUnknown {
declarationRequiresScopeChange = core.IfElse(core.Some(functionLocation.Parameters(), r.requiresScopeChange), core.TSTrue, core.TSFalse)
if r.SetRequiresScopeChangeCache != nil {
r.SetRequiresScopeChangeCache(functionLocation, declarationRequiresScopeChange)
}
}
return declarationRequiresScopeChange != core.TSTrue
}
}
return false
}
func (r *NameResolver) requiresScopeChange(node *ast.Node) bool {
d := node.AsParameterDeclaration()
return r.requiresScopeChangeWorker(d.Name()) || d.Initializer != nil && r.requiresScopeChangeWorker(d.Initializer)
}
func (r *NameResolver) requiresScopeChangeWorker(node *ast.Node) bool {
switch node.Kind {
case ast.KindArrowFunction, ast.KindFunctionExpression, ast.KindFunctionDeclaration, ast.KindConstructor:
return false
case ast.KindMethodDeclaration, ast.KindGetAccessor, ast.KindSetAccessor, ast.KindPropertyAssignment:
return r.requiresScopeChangeWorker(node.Name())
case ast.KindPropertyDeclaration:
if ast.HasStaticModifier(node) {
return !r.CompilerOptions.GetEmitStandardClassFields()
}
return r.requiresScopeChangeWorker(node.AsPropertyDeclaration().Name())
default:
if ast.IsNullishCoalesce(node) || ast.IsOptionalChain(node) {
return r.CompilerOptions.GetEmitScriptTarget() < core.ScriptTargetES2020
}
if ast.IsBindingElement(node) && node.AsBindingElement().DotDotDotToken != nil && ast.IsObjectBindingPattern(node.Parent) {
return r.CompilerOptions.GetEmitScriptTarget() < core.ScriptTargetES2017
}
if ast.IsTypeNode(node) {
return false
}
return node.ForEachChild(r.requiresScopeChangeWorker)
}
}
func (r *NameResolver) error(location *ast.Node, message *diagnostics.Message, args ...any) {
if r.Error != nil {
r.Error(location, message, args...)
}
// Default implementation does not report errors
}
func (r *NameResolver) getSymbolOfDeclaration(node *ast.Node) *ast.Symbol {
if r.GetSymbolOfDeclaration != nil {
return r.GetSymbolOfDeclaration(node)
}
// Default implementation does not support merged symbols
return node.Symbol()
}
func (r *NameResolver) lookup(symbols ast.SymbolTable, name string, meaning ast.SymbolFlags) *ast.Symbol {
if r.Lookup != nil {
return r.Lookup(symbols, name, meaning)
}
// Default implementation does not support following aliases or merged symbols
if meaning != 0 {
symbol := symbols[name]
if symbol != nil {
if symbol.Flags&meaning != 0 {
return symbol
}
}
}
return nil
}
func (r *NameResolver) argumentsSymbol() *ast.Symbol {
if r.ArgumentsSymbol == nil {
// Default implementation synthesizes a transient symbol for `arguments`
r.ArgumentsSymbol = &ast.Symbol{Name: "arguments", Flags: ast.SymbolFlagsProperty | ast.SymbolFlagsTransient}
}
return r.ArgumentsSymbol
}
func GetLocalSymbolForExportDefault(symbol *ast.Symbol) *ast.Symbol {
if !isExportDefaultSymbol(symbol) || len(symbol.Declarations) == 0 {
return nil
}
for _, decl := range symbol.Declarations {
localSymbol := decl.LocalSymbol()
if localSymbol != nil {
return localSymbol
}
}
return nil
}
func isExportDefaultSymbol(symbol *ast.Symbol) bool {
return symbol != nil && len(symbol.Declarations) > 0 && ast.HasSyntacticModifier(symbol.Declarations[0], ast.ModifierFlagsDefault)
}
func getIsDeferredContext(location *ast.Node, lastLocation *ast.Node) bool {
if location.Kind != ast.KindArrowFunction && location.Kind != ast.KindFunctionExpression {
// initializers in instance property declaration of class like entities are executed in constructor and thus deferred
// A name is evaluated within the enclosing scope - so it shouldn't count as deferred
return ast.IsTypeQueryNode(location) ||
(ast.IsFunctionLikeDeclaration(location) || location.Kind == ast.KindPropertyDeclaration && !ast.IsStatic(location)) &&
(lastLocation == nil || lastLocation != location.Name())
}
if lastLocation != nil && lastLocation == location.Name() {
return false
}
// generator functions and async functions are not inlined in control flow when immediately invoked
if location.BodyData().AsteriskToken != nil || ast.HasSyntacticModifier(location, ast.ModifierFlagsAsync) {
return true
}
return ast.GetImmediatelyInvokedFunctionExpression(location) == nil
}
func isTypeParameterSymbolDeclaredInContainer(symbol *ast.Symbol, container *ast.Node) bool {
for _, decl := range symbol.Declarations {
if decl.Kind == ast.KindTypeParameter {
parent := decl.Parent
if parent == container {
return true
}
}
}
return false
}
func isSelfReferenceLocation(node *ast.Node, lastLocation *ast.Node) bool {
switch node.Kind {
case ast.KindParameter:
return lastLocation != nil && lastLocation == node.Name()
case ast.KindFunctionDeclaration, ast.KindClassDeclaration, ast.KindInterfaceDeclaration, ast.KindEnumDeclaration,
ast.KindTypeAliasDeclaration, ast.KindJSTypeAliasDeclaration, ast.KindModuleDeclaration: // For `namespace N { N; }`
return true
}
return false
}

View File

@@ -0,0 +1,262 @@
package binder
import (
"github.com/microsoft/typescript-go/internal/ast"
"github.com/microsoft/typescript-go/internal/core"
"github.com/microsoft/typescript-go/internal/diagnostics"
)
type ReferenceResolver interface {
GetReferencedExportContainer(node *ast.IdentifierNode, prefixLocals bool) *ast.Node
GetReferencedImportDeclaration(node *ast.IdentifierNode) *ast.Declaration
GetReferencedValueDeclaration(node *ast.IdentifierNode) *ast.Declaration
GetReferencedValueDeclarations(node *ast.IdentifierNode) []*ast.Declaration
GetElementAccessExpressionName(expression *ast.ElementAccessExpression) string
GetReferencedMemberValueDeclaration(node *ast.Node) *ast.Declaration
}
type ReferenceResolverHooks struct {
ResolveName func(location *ast.Node, name string, meaning ast.SymbolFlags, nameNotFoundMessage *diagnostics.Message, isUse bool, excludeGlobals bool) *ast.Symbol
GetResolvedSymbol func(*ast.Node) *ast.Symbol
GetMergedSymbol func(*ast.Symbol) *ast.Symbol
GetParentOfSymbol func(*ast.Symbol) *ast.Symbol
GetSymbolOfDeclaration func(*ast.Declaration) *ast.Symbol
GetTypeOnlyAliasDeclaration func(symbol *ast.Symbol, include ast.SymbolFlags) *ast.Declaration
GetExportSymbolOfValueSymbolIfExported func(*ast.Symbol) *ast.Symbol
GetElementAccessExpressionName func(*ast.ElementAccessExpression) (string, bool)
}
var _ ReferenceResolver = &referenceResolver{}
type referenceResolver struct {
resolver *NameResolver
options *core.CompilerOptions
hooks ReferenceResolverHooks
}
func NewReferenceResolver(options *core.CompilerOptions, hooks ReferenceResolverHooks) ReferenceResolver {
return &referenceResolver{
options: options,
hooks: hooks,
}
}
func (r *referenceResolver) getResolvedSymbol(node *ast.Node) *ast.Symbol {
if node != nil {
if r.hooks.GetResolvedSymbol != nil {
return r.hooks.GetResolvedSymbol(node)
}
}
return nil
}
func (r *referenceResolver) getMergedSymbol(symbol *ast.Symbol) *ast.Symbol {
if symbol != nil {
if r.hooks.GetMergedSymbol != nil {
return r.hooks.GetMergedSymbol(symbol)
}
return symbol
}
return nil
}
func (r *referenceResolver) getParentOfSymbol(symbol *ast.Symbol) *ast.Symbol {
if symbol != nil {
if r.hooks.GetParentOfSymbol != nil {
return r.hooks.GetParentOfSymbol(symbol)
}
return symbol.Parent
}
return nil
}
func (r *referenceResolver) getSymbolOfDeclaration(declaration *ast.Declaration) *ast.Symbol {
if declaration != nil {
if r.hooks.GetSymbolOfDeclaration != nil {
return r.hooks.GetSymbolOfDeclaration(declaration)
}
return declaration.Symbol()
}
return nil
}
func (r *referenceResolver) getReferencedValueSymbol(reference *ast.IdentifierNode, startInDeclarationContainer bool) *ast.Symbol {
resolvedSymbol := r.getResolvedSymbol(reference)
if resolvedSymbol != nil {
return resolvedSymbol
}
location := reference
if startInDeclarationContainer && reference.Parent != nil && ast.IsDeclaration(reference.Parent) && reference.Parent.Name() == reference {
location = ast.GetDeclarationContainer(reference.Parent)
}
if r.hooks.ResolveName != nil {
return r.hooks.ResolveName(location, reference.Text(), ast.SymbolFlagsExportValue|ast.SymbolFlagsValue|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/)
}
if r.resolver == nil {
r.resolver = &NameResolver{
CompilerOptions: r.options,
}
}
return r.resolver.Resolve(location, reference.Text(), ast.SymbolFlagsExportValue|ast.SymbolFlagsValue|ast.SymbolFlagsAlias, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/)
}
func (r *referenceResolver) isTypeOnlyAliasDeclaration(symbol *ast.Symbol) bool {
if symbol != nil {
if r.hooks.GetTypeOnlyAliasDeclaration != nil {
return r.hooks.GetTypeOnlyAliasDeclaration(symbol, ast.SymbolFlagsValue) != nil
}
node := r.getDeclarationOfAliasSymbol(symbol)
for node != nil {
switch node.Kind {
case ast.KindImportEqualsDeclaration, ast.KindExportDeclaration:
return node.IsTypeOnly()
case ast.KindImportClause, ast.KindImportSpecifier, ast.KindExportSpecifier:
if node.IsTypeOnly() {
return true
}
node = node.Parent
continue
case ast.KindNamedImports, ast.KindNamedExports:
node = node.Parent
continue
}
break
}
}
return false
}
func (r *referenceResolver) getDeclarationOfAliasSymbol(symbol *ast.Symbol) *ast.Declaration {
return core.FindLast(symbol.Declarations, ast.IsAliasSymbolDeclaration)
}
func (r *referenceResolver) getExportSymbolOfValueSymbolIfExported(symbol *ast.Symbol) *ast.Symbol {
if symbol != nil {
if r.hooks.GetExportSymbolOfValueSymbolIfExported != nil {
return r.hooks.GetExportSymbolOfValueSymbolIfExported(symbol)
}
if symbol.Flags&ast.SymbolFlagsExportValue != 0 && symbol.ExportSymbol != nil {
symbol = symbol.ExportSymbol
}
return r.getMergedSymbol(symbol)
}
return nil
}
func (r *referenceResolver) GetReferencedExportContainer(node *ast.IdentifierNode, prefixLocals bool) *ast.Node /*SourceFile|ModuleDeclaration|EnumDeclaration*/ {
// When resolving the export for the name of a module or enum
// declaration, we need to start resolution at the declaration's container.
// Otherwise, we could incorrectly resolve the export as the
// declaration if it contains an exported member with the same name.
startInDeclarationContainer := node.Parent != nil && (node.Parent.Kind == ast.KindModuleDeclaration || node.Parent.Kind == ast.KindEnumDeclaration) && node == node.Parent.Name()
if symbol := r.getReferencedValueSymbol(node, startInDeclarationContainer); symbol != nil {
if symbol.Flags&ast.SymbolFlagsExportValue != 0 {
// If we reference an exported entity within the same module declaration, then whether
// we prefix depends on the kind of entity. SymbolFlags.ExportHasLocal encompasses all the
// kinds that we do NOT prefix.
exportSymbol := r.getMergedSymbol(symbol.ExportSymbol)
if !prefixLocals && exportSymbol.Flags&ast.SymbolFlagsExportHasLocal != 0 && exportSymbol.Flags&ast.SymbolFlagsVariable == 0 {
return nil
}
symbol = exportSymbol
}
parentSymbol := r.getParentOfSymbol(symbol)
if parentSymbol != nil {
if parentSymbol.Flags&ast.SymbolFlagsValueModule != 0 && parentSymbol.ValueDeclaration != nil && parentSymbol.ValueDeclaration.Kind == ast.KindSourceFile {
symbolFile := parentSymbol.ValueDeclaration.AsSourceFile()
referenceFile := ast.GetSourceFileOfNode(node)
// If `node` accesses an export and that export isn't in the same file, then symbol is a namespace export, so return nil.
symbolIsUmdExport := symbolFile != referenceFile
if symbolIsUmdExport {
return nil
}
return symbolFile.AsNode()
}
isMatchingContainer := func(n *ast.Node) bool {
return (n.Kind == ast.KindModuleDeclaration || n.Kind == ast.KindEnumDeclaration) && r.getSymbolOfDeclaration(n) == parentSymbol
}
return ast.FindAncestor(node.Parent, isMatchingContainer)
}
}
return nil
}
func (r *referenceResolver) GetReferencedImportDeclaration(node *ast.IdentifierNode) *ast.Declaration {
if symbol := r.getReferencedValueSymbol(node, false /*startInDeclarationContainer*/); symbol != nil {
// We should only get the declaration of an alias if there isn't a local value
// declaration for the symbol
if ast.IsNonLocalAlias(symbol, ast.SymbolFlagsValue /*excludes*/) && !r.isTypeOnlyAliasDeclaration(symbol) {
return r.getDeclarationOfAliasSymbol(symbol)
}
}
return nil
}
func (r *referenceResolver) GetReferencedValueDeclaration(node *ast.IdentifierNode) *ast.Declaration {
if symbol := r.getReferencedValueSymbol(node, false /*startInDeclarationContainer*/); symbol != nil {
return r.getExportSymbolOfValueSymbolIfExported(symbol).ValueDeclaration
}
return nil
}
func (r *referenceResolver) GetReferencedValueDeclarations(node *ast.IdentifierNode) []*ast.Declaration {
var declarations []*ast.Declaration
if symbol := r.getReferencedValueSymbol(node, false /*startInDeclarationContainer*/); symbol != nil {
symbol = r.getExportSymbolOfValueSymbolIfExported(symbol)
for _, declaration := range symbol.Declarations {
switch declaration.Kind {
case ast.KindVariableDeclaration,
ast.KindParameter,
ast.KindBindingElement,
ast.KindPropertyDeclaration,
ast.KindPropertyAssignment,
ast.KindShorthandPropertyAssignment,
ast.KindEnumMember,
ast.KindObjectLiteralExpression,
ast.KindFunctionDeclaration,
ast.KindFunctionExpression,
ast.KindArrowFunction,
ast.KindClassDeclaration,
ast.KindClassExpression,
ast.KindEnumDeclaration,
ast.KindMethodDeclaration,
ast.KindGetAccessor,
ast.KindSetAccessor,
ast.KindModuleDeclaration:
declarations = append(declarations, declaration)
}
}
}
return declarations
}
func (r *referenceResolver) GetElementAccessExpressionName(expression *ast.ElementAccessExpression) string {
if expression != nil {
if r.hooks.GetElementAccessExpressionName != nil {
if name, ok := r.hooks.GetElementAccessExpressionName(expression); ok {
return name
}
}
}
return ""
}
func (r *referenceResolver) GetReferencedMemberValueDeclaration(node *ast.Node) *ast.Declaration {
// member references are `this.something` or `this[something]`, so should always simply have a resolved symbol
s := r.getResolvedSymbol(node)
if s == nil && node.Symbol() != nil {
// might be a declaration instead of a ref, get the merged declaration symbol
s = r.getMergedSymbol(node.Symbol())
}
if s == nil {
return nil
}
return r.getExportSymbolOfValueSymbolIfExported(s).ValueDeclaration
}

View File

@@ -0,0 +1,53 @@
// Package bundled provides access to files bundled with TypeScript.
package bundled
import (
"path/filepath"
"runtime"
"sync"
"testing"
"github.com/microsoft/typescript-go/internal/tspath"
"github.com/microsoft/typescript-go/internal/vfs"
)
//go:generate go run generate.go
// Define the below here to consolidate documentation.
// Embedded is true if the bundled files are implemented through an embedded FS.
const Embedded = embedded
// WrapFS returns an FS which redirects embedded paths to the embedded file system.
// If the embedded file system is not available, it returns the original FS.
func WrapFS(fs vfs.FS) vfs.FS {
return wrapFS(fs)
}
// LibPath returns the path to the directory containing the bundled lib.d.ts files.
// If embedding is not enabled, this is a path on disk, and must be accessed through
// a real OS filesystem.
func LibPath() string {
return libPath()
}
var bundledSourceDir = sync.OnceValue(func() string {
_, filename, _, ok := runtime.Caller(0)
if !ok {
panic("bundled: could not get current filename")
}
return filepath.Dir(filepath.FromSlash(filename))
})
var testingLibPath = sync.OnceValue(func() string {
if !testing.Testing() {
panic("bundled: TestingLibPath should only be called during tests")
}
return tspath.NormalizeSlashes(filepath.Join(bundledSourceDir(), "libs"))
})
// TestingLibPath returns the path to the source bundled libs directory.
// It's only valid to use in tests where the source code is available.
func TestingLibPath() string {
return testingLibPath()
}

View File

@@ -0,0 +1,48 @@
package bundled_test
import (
"os"
"path/filepath"
"testing"
"github.com/microsoft/typescript-go/internal/bundled"
"github.com/microsoft/typescript-go/internal/tspath"
"github.com/microsoft/typescript-go/internal/vfs"
"github.com/microsoft/typescript-go/internal/vfs/osvfs"
"gotest.tools/v3/assert"
)
func TestTestingLibPath(t *testing.T) {
t.Parallel()
p := bundled.TestingLibPath()
_, err := os.Stat(p)
assert.NilError(t, err)
libdts := filepath.Join(p, "lib.d.ts")
_, err = os.Stat(libdts)
assert.NilError(t, err)
}
func TestEmbeddedLibs(t *testing.T) {
t.Parallel()
fs := bundled.WrapFS(osvfs.FS())
var files []string
err := fs.WalkDir(bundled.LibPath(), func(path string, d vfs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
files = append(files, tspath.GetBaseFileName(path))
}
return nil
})
assert.NilError(t, err)
assert.DeepEqual(t, files, bundled.LibNames)
}

View File

@@ -0,0 +1,224 @@
//go:build !noembed
package bundled
import (
"io/fs"
"strings"
"time"
"github.com/microsoft/typescript-go/internal/vfs"
)
const embedded = true
const scheme = "bundled:///"
func splitPath(path string) (rest string, ok bool) {
return strings.CutPrefix(path, scheme)
}
func libPath() string {
return scheme + "libs"
}
func IsBundled(path string) bool {
_, ok := splitPath(path)
return ok
}
// wrappedFS is implemented directly rather than going through [io/fs.FS].
// Our vfs.FS works with file contents in terms of strings, and that's
// what go:embed does under the hood, but going through fs.FS will cause
// copying to []byte and back.
type wrappedFS struct {
fs vfs.FS
}
var _ vfs.FS = (*wrappedFS)(nil)
func wrapFS(fs vfs.FS) vfs.FS {
return &wrappedFS{fs: fs}
}
func (vfs *wrappedFS) UseCaseSensitiveFileNames() bool {
return vfs.fs.UseCaseSensitiveFileNames()
}
func (vfs *wrappedFS) FileExists(path string) bool {
if rest, ok := splitPath(path); ok {
_, ok := embeddedContents[rest]
return ok
}
return vfs.fs.FileExists(path)
}
func (vfs *wrappedFS) ReadFile(path string) (contents string, ok bool) {
if rest, ok := splitPath(path); ok {
contents, ok = embeddedContents[rest]
return contents, ok
}
return vfs.fs.ReadFile(path)
}
func (vfs *wrappedFS) DirectoryExists(path string) bool {
if rest, ok := splitPath(path); ok {
return rest == "libs"
}
return vfs.fs.DirectoryExists(path)
}
func (vfs *wrappedFS) GetAccessibleEntries(path string) (result vfs.Entries) {
if rest, ok := splitPath(path); ok {
if rest == "" {
result.Directories = []string{"libs"}
} else if rest == "libs" {
result.Files = LibNames
}
return result
}
return vfs.fs.GetAccessibleEntries(path)
}
var rootEntries = []fs.DirEntry{
fs.FileInfoToDirEntry(&fileInfo{name: "libs", mode: fs.ModeDir}),
}
func (vfs *wrappedFS) Stat(path string) vfs.FileInfo {
if rest, ok := splitPath(path); ok {
if rest == "" || rest == "libs" {
return &fileInfo{name: rest, mode: fs.ModeDir}
}
if lib, ok := embeddedContents[rest]; ok {
libName, _ := strings.CutPrefix(rest, "libs/")
return &fileInfo{name: libName, size: int64(len(lib))}
}
return nil
}
return vfs.fs.Stat(path)
}
func (vfs *wrappedFS) WalkDir(root string, walkFn vfs.WalkDirFunc) error {
if rest, ok := splitPath(root); ok {
if err := vfs.walkDir(rest, walkFn); err != nil {
if err == fs.SkipAll { //nolint:errorlint
return nil
}
return err
}
return nil
}
return vfs.fs.WalkDir(root, walkFn)
}
func (vfs *wrappedFS) walkDir(rest string, walkFn vfs.WalkDirFunc) error {
var entries []fs.DirEntry
switch rest {
case "":
entries = rootEntries
case "libs":
entries = libsEntries
default:
return nil
}
for _, entry := range entries {
name := rest + "/" + entry.Name()
if err := walkFn(scheme+name, entry, nil); err != nil {
if err == fs.SkipAll { //nolint:errorlint
return fs.SkipAll
}
if err == fs.SkipDir { //nolint:errorlint
continue
}
return err
}
if entry.IsDir() {
if err := vfs.walkDir(strings.TrimPrefix(name, "/"), walkFn); err != nil {
return err
}
}
}
return nil
}
func (vfs *wrappedFS) Realpath(path string) string {
if _, ok := splitPath(path); ok {
return path
}
return vfs.fs.Realpath(path)
}
func (vfs *wrappedFS) WriteFile(path string, data string) error {
if _, ok := splitPath(path); ok {
panic("cannot write to embedded file system")
}
return vfs.fs.WriteFile(path, data)
}
func (vfs *wrappedFS) AppendFile(path string, data string) error {
if _, ok := splitPath(path); ok {
panic("cannot write to embedded file system")
}
return vfs.fs.AppendFile(path, data)
}
func (vfs *wrappedFS) Remove(path string) error {
if _, ok := splitPath(path); ok {
panic("cannot remove from embedded file system")
}
return vfs.fs.Remove(path)
}
func (vfs *wrappedFS) Chtimes(path string, aTime time.Time, mTime time.Time) error {
if _, ok := splitPath(path); ok {
panic("cannot change times on embedded file system")
}
return vfs.fs.Chtimes(path, aTime, mTime)
}
type fileInfo struct {
mode fs.FileMode
name string
size int64
}
var (
_ fs.FileInfo = (*fileInfo)(nil)
_ fs.DirEntry = (*fileInfo)(nil)
)
func (fi *fileInfo) IsDir() bool {
return fi.mode.IsDir()
}
func (fi *fileInfo) ModTime() time.Time {
return time.Time{}
}
func (fi *fileInfo) Mode() fs.FileMode {
return fi.mode
}
func (fi *fileInfo) Name() string {
return fi.name
}
func (fi *fileInfo) Size() int64 {
return fi.size
}
func (fi *fileInfo) Sys() any {
return nil
}
func (fi *fileInfo) Info() (fs.FileInfo, error) {
return fi, nil
}
func (fi *fileInfo) Type() fs.FileMode {
return fi.mode.Type()
}

View File

@@ -0,0 +1,452 @@
//go:build !noembed
// Code generated by generate.go; DO NOT EDIT.
package bundled
import (
"io/fs"
_ "embed"
)
var (
//go:embed libs/lib.d.ts
libs_lib_d_ts string
//go:embed libs/lib.decorators.d.ts
libs_lib_decorators_d_ts string
//go:embed libs/lib.decorators.legacy.d.ts
libs_lib_decorators_legacy_d_ts string
//go:embed libs/lib.dom.asynciterable.d.ts
libs_lib_dom_asynciterable_d_ts string
//go:embed libs/lib.dom.d.ts
libs_lib_dom_d_ts string
//go:embed libs/lib.dom.iterable.d.ts
libs_lib_dom_iterable_d_ts string
//go:embed libs/lib.es2015.collection.d.ts
libs_lib_es2015_collection_d_ts string
//go:embed libs/lib.es2015.core.d.ts
libs_lib_es2015_core_d_ts string
//go:embed libs/lib.es2015.d.ts
libs_lib_es2015_d_ts string
//go:embed libs/lib.es2015.generator.d.ts
libs_lib_es2015_generator_d_ts string
//go:embed libs/lib.es2015.iterable.d.ts
libs_lib_es2015_iterable_d_ts string
//go:embed libs/lib.es2015.promise.d.ts
libs_lib_es2015_promise_d_ts string
//go:embed libs/lib.es2015.proxy.d.ts
libs_lib_es2015_proxy_d_ts string
//go:embed libs/lib.es2015.reflect.d.ts
libs_lib_es2015_reflect_d_ts string
//go:embed libs/lib.es2015.symbol.d.ts
libs_lib_es2015_symbol_d_ts string
//go:embed libs/lib.es2015.symbol.wellknown.d.ts
libs_lib_es2015_symbol_wellknown_d_ts string
//go:embed libs/lib.es2016.array.include.d.ts
libs_lib_es2016_array_include_d_ts string
//go:embed libs/lib.es2016.d.ts
libs_lib_es2016_d_ts string
//go:embed libs/lib.es2016.full.d.ts
libs_lib_es2016_full_d_ts string
//go:embed libs/lib.es2016.intl.d.ts
libs_lib_es2016_intl_d_ts string
//go:embed libs/lib.es2017.arraybuffer.d.ts
libs_lib_es2017_arraybuffer_d_ts string
//go:embed libs/lib.es2017.d.ts
libs_lib_es2017_d_ts string
//go:embed libs/lib.es2017.date.d.ts
libs_lib_es2017_date_d_ts string
//go:embed libs/lib.es2017.full.d.ts
libs_lib_es2017_full_d_ts string
//go:embed libs/lib.es2017.intl.d.ts
libs_lib_es2017_intl_d_ts string
//go:embed libs/lib.es2017.object.d.ts
libs_lib_es2017_object_d_ts string
//go:embed libs/lib.es2017.sharedmemory.d.ts
libs_lib_es2017_sharedmemory_d_ts string
//go:embed libs/lib.es2017.string.d.ts
libs_lib_es2017_string_d_ts string
//go:embed libs/lib.es2017.typedarrays.d.ts
libs_lib_es2017_typedarrays_d_ts string
//go:embed libs/lib.es2018.asyncgenerator.d.ts
libs_lib_es2018_asyncgenerator_d_ts string
//go:embed libs/lib.es2018.asynciterable.d.ts
libs_lib_es2018_asynciterable_d_ts string
//go:embed libs/lib.es2018.d.ts
libs_lib_es2018_d_ts string
//go:embed libs/lib.es2018.full.d.ts
libs_lib_es2018_full_d_ts string
//go:embed libs/lib.es2018.intl.d.ts
libs_lib_es2018_intl_d_ts string
//go:embed libs/lib.es2018.promise.d.ts
libs_lib_es2018_promise_d_ts string
//go:embed libs/lib.es2018.regexp.d.ts
libs_lib_es2018_regexp_d_ts string
//go:embed libs/lib.es2019.array.d.ts
libs_lib_es2019_array_d_ts string
//go:embed libs/lib.es2019.d.ts
libs_lib_es2019_d_ts string
//go:embed libs/lib.es2019.full.d.ts
libs_lib_es2019_full_d_ts string
//go:embed libs/lib.es2019.intl.d.ts
libs_lib_es2019_intl_d_ts string
//go:embed libs/lib.es2019.object.d.ts
libs_lib_es2019_object_d_ts string
//go:embed libs/lib.es2019.string.d.ts
libs_lib_es2019_string_d_ts string
//go:embed libs/lib.es2019.symbol.d.ts
libs_lib_es2019_symbol_d_ts string
//go:embed libs/lib.es2020.bigint.d.ts
libs_lib_es2020_bigint_d_ts string
//go:embed libs/lib.es2020.d.ts
libs_lib_es2020_d_ts string
//go:embed libs/lib.es2020.date.d.ts
libs_lib_es2020_date_d_ts string
//go:embed libs/lib.es2020.full.d.ts
libs_lib_es2020_full_d_ts string
//go:embed libs/lib.es2020.intl.d.ts
libs_lib_es2020_intl_d_ts string
//go:embed libs/lib.es2020.number.d.ts
libs_lib_es2020_number_d_ts string
//go:embed libs/lib.es2020.promise.d.ts
libs_lib_es2020_promise_d_ts string
//go:embed libs/lib.es2020.sharedmemory.d.ts
libs_lib_es2020_sharedmemory_d_ts string
//go:embed libs/lib.es2020.string.d.ts
libs_lib_es2020_string_d_ts string
//go:embed libs/lib.es2020.symbol.wellknown.d.ts
libs_lib_es2020_symbol_wellknown_d_ts string
//go:embed libs/lib.es2021.d.ts
libs_lib_es2021_d_ts string
//go:embed libs/lib.es2021.full.d.ts
libs_lib_es2021_full_d_ts string
//go:embed libs/lib.es2021.intl.d.ts
libs_lib_es2021_intl_d_ts string
//go:embed libs/lib.es2021.promise.d.ts
libs_lib_es2021_promise_d_ts string
//go:embed libs/lib.es2021.string.d.ts
libs_lib_es2021_string_d_ts string
//go:embed libs/lib.es2021.weakref.d.ts
libs_lib_es2021_weakref_d_ts string
//go:embed libs/lib.es2022.array.d.ts
libs_lib_es2022_array_d_ts string
//go:embed libs/lib.es2022.d.ts
libs_lib_es2022_d_ts string
//go:embed libs/lib.es2022.error.d.ts
libs_lib_es2022_error_d_ts string
//go:embed libs/lib.es2022.full.d.ts
libs_lib_es2022_full_d_ts string
//go:embed libs/lib.es2022.intl.d.ts
libs_lib_es2022_intl_d_ts string
//go:embed libs/lib.es2022.object.d.ts
libs_lib_es2022_object_d_ts string
//go:embed libs/lib.es2022.regexp.d.ts
libs_lib_es2022_regexp_d_ts string
//go:embed libs/lib.es2022.string.d.ts
libs_lib_es2022_string_d_ts string
//go:embed libs/lib.es2023.array.d.ts
libs_lib_es2023_array_d_ts string
//go:embed libs/lib.es2023.collection.d.ts
libs_lib_es2023_collection_d_ts string
//go:embed libs/lib.es2023.d.ts
libs_lib_es2023_d_ts string
//go:embed libs/lib.es2023.full.d.ts
libs_lib_es2023_full_d_ts string
//go:embed libs/lib.es2023.intl.d.ts
libs_lib_es2023_intl_d_ts string
//go:embed libs/lib.es2024.arraybuffer.d.ts
libs_lib_es2024_arraybuffer_d_ts string
//go:embed libs/lib.es2024.collection.d.ts
libs_lib_es2024_collection_d_ts string
//go:embed libs/lib.es2024.d.ts
libs_lib_es2024_d_ts string
//go:embed libs/lib.es2024.full.d.ts
libs_lib_es2024_full_d_ts string
//go:embed libs/lib.es2024.object.d.ts
libs_lib_es2024_object_d_ts string
//go:embed libs/lib.es2024.promise.d.ts
libs_lib_es2024_promise_d_ts string
//go:embed libs/lib.es2024.regexp.d.ts
libs_lib_es2024_regexp_d_ts string
//go:embed libs/lib.es2024.sharedmemory.d.ts
libs_lib_es2024_sharedmemory_d_ts string
//go:embed libs/lib.es2024.string.d.ts
libs_lib_es2024_string_d_ts string
//go:embed libs/lib.es2025.collection.d.ts
libs_lib_es2025_collection_d_ts string
//go:embed libs/lib.es2025.d.ts
libs_lib_es2025_d_ts string
//go:embed libs/lib.es2025.float16.d.ts
libs_lib_es2025_float16_d_ts string
//go:embed libs/lib.es2025.full.d.ts
libs_lib_es2025_full_d_ts string
//go:embed libs/lib.es2025.intl.d.ts
libs_lib_es2025_intl_d_ts string
//go:embed libs/lib.es2025.iterator.d.ts
libs_lib_es2025_iterator_d_ts string
//go:embed libs/lib.es2025.promise.d.ts
libs_lib_es2025_promise_d_ts string
//go:embed libs/lib.es2025.regexp.d.ts
libs_lib_es2025_regexp_d_ts string
//go:embed libs/lib.es5.d.ts
libs_lib_es5_d_ts string
//go:embed libs/lib.es6.d.ts
libs_lib_es6_d_ts string
//go:embed libs/lib.esnext.array.d.ts
libs_lib_esnext_array_d_ts string
//go:embed libs/lib.esnext.collection.d.ts
libs_lib_esnext_collection_d_ts string
//go:embed libs/lib.esnext.d.ts
libs_lib_esnext_d_ts string
//go:embed libs/lib.esnext.date.d.ts
libs_lib_esnext_date_d_ts string
//go:embed libs/lib.esnext.decorators.d.ts
libs_lib_esnext_decorators_d_ts string
//go:embed libs/lib.esnext.disposable.d.ts
libs_lib_esnext_disposable_d_ts string
//go:embed libs/lib.esnext.error.d.ts
libs_lib_esnext_error_d_ts string
//go:embed libs/lib.esnext.full.d.ts
libs_lib_esnext_full_d_ts string
//go:embed libs/lib.esnext.intl.d.ts
libs_lib_esnext_intl_d_ts string
//go:embed libs/lib.esnext.sharedmemory.d.ts
libs_lib_esnext_sharedmemory_d_ts string
//go:embed libs/lib.esnext.temporal.d.ts
libs_lib_esnext_temporal_d_ts string
//go:embed libs/lib.esnext.typedarrays.d.ts
libs_lib_esnext_typedarrays_d_ts string
//go:embed libs/lib.scripthost.d.ts
libs_lib_scripthost_d_ts string
//go:embed libs/lib.webworker.asynciterable.d.ts
libs_lib_webworker_asynciterable_d_ts string
//go:embed libs/lib.webworker.d.ts
libs_lib_webworker_d_ts string
//go:embed libs/lib.webworker.importscripts.d.ts
libs_lib_webworker_importscripts_d_ts string
//go:embed libs/lib.webworker.iterable.d.ts
libs_lib_webworker_iterable_d_ts string
)
var embeddedContents = map[string]string{
"libs/lib.d.ts": libs_lib_d_ts,
"libs/lib.decorators.d.ts": libs_lib_decorators_d_ts,
"libs/lib.decorators.legacy.d.ts": libs_lib_decorators_legacy_d_ts,
"libs/lib.dom.asynciterable.d.ts": libs_lib_dom_asynciterable_d_ts,
"libs/lib.dom.d.ts": libs_lib_dom_d_ts,
"libs/lib.dom.iterable.d.ts": libs_lib_dom_iterable_d_ts,
"libs/lib.es2015.collection.d.ts": libs_lib_es2015_collection_d_ts,
"libs/lib.es2015.core.d.ts": libs_lib_es2015_core_d_ts,
"libs/lib.es2015.d.ts": libs_lib_es2015_d_ts,
"libs/lib.es2015.generator.d.ts": libs_lib_es2015_generator_d_ts,
"libs/lib.es2015.iterable.d.ts": libs_lib_es2015_iterable_d_ts,
"libs/lib.es2015.promise.d.ts": libs_lib_es2015_promise_d_ts,
"libs/lib.es2015.proxy.d.ts": libs_lib_es2015_proxy_d_ts,
"libs/lib.es2015.reflect.d.ts": libs_lib_es2015_reflect_d_ts,
"libs/lib.es2015.symbol.d.ts": libs_lib_es2015_symbol_d_ts,
"libs/lib.es2015.symbol.wellknown.d.ts": libs_lib_es2015_symbol_wellknown_d_ts,
"libs/lib.es2016.array.include.d.ts": libs_lib_es2016_array_include_d_ts,
"libs/lib.es2016.d.ts": libs_lib_es2016_d_ts,
"libs/lib.es2016.full.d.ts": libs_lib_es2016_full_d_ts,
"libs/lib.es2016.intl.d.ts": libs_lib_es2016_intl_d_ts,
"libs/lib.es2017.arraybuffer.d.ts": libs_lib_es2017_arraybuffer_d_ts,
"libs/lib.es2017.d.ts": libs_lib_es2017_d_ts,
"libs/lib.es2017.date.d.ts": libs_lib_es2017_date_d_ts,
"libs/lib.es2017.full.d.ts": libs_lib_es2017_full_d_ts,
"libs/lib.es2017.intl.d.ts": libs_lib_es2017_intl_d_ts,
"libs/lib.es2017.object.d.ts": libs_lib_es2017_object_d_ts,
"libs/lib.es2017.sharedmemory.d.ts": libs_lib_es2017_sharedmemory_d_ts,
"libs/lib.es2017.string.d.ts": libs_lib_es2017_string_d_ts,
"libs/lib.es2017.typedarrays.d.ts": libs_lib_es2017_typedarrays_d_ts,
"libs/lib.es2018.asyncgenerator.d.ts": libs_lib_es2018_asyncgenerator_d_ts,
"libs/lib.es2018.asynciterable.d.ts": libs_lib_es2018_asynciterable_d_ts,
"libs/lib.es2018.d.ts": libs_lib_es2018_d_ts,
"libs/lib.es2018.full.d.ts": libs_lib_es2018_full_d_ts,
"libs/lib.es2018.intl.d.ts": libs_lib_es2018_intl_d_ts,
"libs/lib.es2018.promise.d.ts": libs_lib_es2018_promise_d_ts,
"libs/lib.es2018.regexp.d.ts": libs_lib_es2018_regexp_d_ts,
"libs/lib.es2019.array.d.ts": libs_lib_es2019_array_d_ts,
"libs/lib.es2019.d.ts": libs_lib_es2019_d_ts,
"libs/lib.es2019.full.d.ts": libs_lib_es2019_full_d_ts,
"libs/lib.es2019.intl.d.ts": libs_lib_es2019_intl_d_ts,
"libs/lib.es2019.object.d.ts": libs_lib_es2019_object_d_ts,
"libs/lib.es2019.string.d.ts": libs_lib_es2019_string_d_ts,
"libs/lib.es2019.symbol.d.ts": libs_lib_es2019_symbol_d_ts,
"libs/lib.es2020.bigint.d.ts": libs_lib_es2020_bigint_d_ts,
"libs/lib.es2020.d.ts": libs_lib_es2020_d_ts,
"libs/lib.es2020.date.d.ts": libs_lib_es2020_date_d_ts,
"libs/lib.es2020.full.d.ts": libs_lib_es2020_full_d_ts,
"libs/lib.es2020.intl.d.ts": libs_lib_es2020_intl_d_ts,
"libs/lib.es2020.number.d.ts": libs_lib_es2020_number_d_ts,
"libs/lib.es2020.promise.d.ts": libs_lib_es2020_promise_d_ts,
"libs/lib.es2020.sharedmemory.d.ts": libs_lib_es2020_sharedmemory_d_ts,
"libs/lib.es2020.string.d.ts": libs_lib_es2020_string_d_ts,
"libs/lib.es2020.symbol.wellknown.d.ts": libs_lib_es2020_symbol_wellknown_d_ts,
"libs/lib.es2021.d.ts": libs_lib_es2021_d_ts,
"libs/lib.es2021.full.d.ts": libs_lib_es2021_full_d_ts,
"libs/lib.es2021.intl.d.ts": libs_lib_es2021_intl_d_ts,
"libs/lib.es2021.promise.d.ts": libs_lib_es2021_promise_d_ts,
"libs/lib.es2021.string.d.ts": libs_lib_es2021_string_d_ts,
"libs/lib.es2021.weakref.d.ts": libs_lib_es2021_weakref_d_ts,
"libs/lib.es2022.array.d.ts": libs_lib_es2022_array_d_ts,
"libs/lib.es2022.d.ts": libs_lib_es2022_d_ts,
"libs/lib.es2022.error.d.ts": libs_lib_es2022_error_d_ts,
"libs/lib.es2022.full.d.ts": libs_lib_es2022_full_d_ts,
"libs/lib.es2022.intl.d.ts": libs_lib_es2022_intl_d_ts,
"libs/lib.es2022.object.d.ts": libs_lib_es2022_object_d_ts,
"libs/lib.es2022.regexp.d.ts": libs_lib_es2022_regexp_d_ts,
"libs/lib.es2022.string.d.ts": libs_lib_es2022_string_d_ts,
"libs/lib.es2023.array.d.ts": libs_lib_es2023_array_d_ts,
"libs/lib.es2023.collection.d.ts": libs_lib_es2023_collection_d_ts,
"libs/lib.es2023.d.ts": libs_lib_es2023_d_ts,
"libs/lib.es2023.full.d.ts": libs_lib_es2023_full_d_ts,
"libs/lib.es2023.intl.d.ts": libs_lib_es2023_intl_d_ts,
"libs/lib.es2024.arraybuffer.d.ts": libs_lib_es2024_arraybuffer_d_ts,
"libs/lib.es2024.collection.d.ts": libs_lib_es2024_collection_d_ts,
"libs/lib.es2024.d.ts": libs_lib_es2024_d_ts,
"libs/lib.es2024.full.d.ts": libs_lib_es2024_full_d_ts,
"libs/lib.es2024.object.d.ts": libs_lib_es2024_object_d_ts,
"libs/lib.es2024.promise.d.ts": libs_lib_es2024_promise_d_ts,
"libs/lib.es2024.regexp.d.ts": libs_lib_es2024_regexp_d_ts,
"libs/lib.es2024.sharedmemory.d.ts": libs_lib_es2024_sharedmemory_d_ts,
"libs/lib.es2024.string.d.ts": libs_lib_es2024_string_d_ts,
"libs/lib.es2025.collection.d.ts": libs_lib_es2025_collection_d_ts,
"libs/lib.es2025.d.ts": libs_lib_es2025_d_ts,
"libs/lib.es2025.float16.d.ts": libs_lib_es2025_float16_d_ts,
"libs/lib.es2025.full.d.ts": libs_lib_es2025_full_d_ts,
"libs/lib.es2025.intl.d.ts": libs_lib_es2025_intl_d_ts,
"libs/lib.es2025.iterator.d.ts": libs_lib_es2025_iterator_d_ts,
"libs/lib.es2025.promise.d.ts": libs_lib_es2025_promise_d_ts,
"libs/lib.es2025.regexp.d.ts": libs_lib_es2025_regexp_d_ts,
"libs/lib.es5.d.ts": libs_lib_es5_d_ts,
"libs/lib.es6.d.ts": libs_lib_es6_d_ts,
"libs/lib.esnext.array.d.ts": libs_lib_esnext_array_d_ts,
"libs/lib.esnext.collection.d.ts": libs_lib_esnext_collection_d_ts,
"libs/lib.esnext.d.ts": libs_lib_esnext_d_ts,
"libs/lib.esnext.date.d.ts": libs_lib_esnext_date_d_ts,
"libs/lib.esnext.decorators.d.ts": libs_lib_esnext_decorators_d_ts,
"libs/lib.esnext.disposable.d.ts": libs_lib_esnext_disposable_d_ts,
"libs/lib.esnext.error.d.ts": libs_lib_esnext_error_d_ts,
"libs/lib.esnext.full.d.ts": libs_lib_esnext_full_d_ts,
"libs/lib.esnext.intl.d.ts": libs_lib_esnext_intl_d_ts,
"libs/lib.esnext.sharedmemory.d.ts": libs_lib_esnext_sharedmemory_d_ts,
"libs/lib.esnext.temporal.d.ts": libs_lib_esnext_temporal_d_ts,
"libs/lib.esnext.typedarrays.d.ts": libs_lib_esnext_typedarrays_d_ts,
"libs/lib.scripthost.d.ts": libs_lib_scripthost_d_ts,
"libs/lib.webworker.asynciterable.d.ts": libs_lib_webworker_asynciterable_d_ts,
"libs/lib.webworker.d.ts": libs_lib_webworker_d_ts,
"libs/lib.webworker.importscripts.d.ts": libs_lib_webworker_importscripts_d_ts,
"libs/lib.webworker.iterable.d.ts": libs_lib_webworker_iterable_d_ts,
}
var libsEntries = []fs.DirEntry{
&fileInfo{name: "lib.d.ts", size: int64(len(libs_lib_d_ts))},
&fileInfo{name: "lib.decorators.d.ts", size: int64(len(libs_lib_decorators_d_ts))},
&fileInfo{name: "lib.decorators.legacy.d.ts", size: int64(len(libs_lib_decorators_legacy_d_ts))},
&fileInfo{name: "lib.dom.asynciterable.d.ts", size: int64(len(libs_lib_dom_asynciterable_d_ts))},
&fileInfo{name: "lib.dom.d.ts", size: int64(len(libs_lib_dom_d_ts))},
&fileInfo{name: "lib.dom.iterable.d.ts", size: int64(len(libs_lib_dom_iterable_d_ts))},
&fileInfo{name: "lib.es2015.collection.d.ts", size: int64(len(libs_lib_es2015_collection_d_ts))},
&fileInfo{name: "lib.es2015.core.d.ts", size: int64(len(libs_lib_es2015_core_d_ts))},
&fileInfo{name: "lib.es2015.d.ts", size: int64(len(libs_lib_es2015_d_ts))},
&fileInfo{name: "lib.es2015.generator.d.ts", size: int64(len(libs_lib_es2015_generator_d_ts))},
&fileInfo{name: "lib.es2015.iterable.d.ts", size: int64(len(libs_lib_es2015_iterable_d_ts))},
&fileInfo{name: "lib.es2015.promise.d.ts", size: int64(len(libs_lib_es2015_promise_d_ts))},
&fileInfo{name: "lib.es2015.proxy.d.ts", size: int64(len(libs_lib_es2015_proxy_d_ts))},
&fileInfo{name: "lib.es2015.reflect.d.ts", size: int64(len(libs_lib_es2015_reflect_d_ts))},
&fileInfo{name: "lib.es2015.symbol.d.ts", size: int64(len(libs_lib_es2015_symbol_d_ts))},
&fileInfo{name: "lib.es2015.symbol.wellknown.d.ts", size: int64(len(libs_lib_es2015_symbol_wellknown_d_ts))},
&fileInfo{name: "lib.es2016.array.include.d.ts", size: int64(len(libs_lib_es2016_array_include_d_ts))},
&fileInfo{name: "lib.es2016.d.ts", size: int64(len(libs_lib_es2016_d_ts))},
&fileInfo{name: "lib.es2016.full.d.ts", size: int64(len(libs_lib_es2016_full_d_ts))},
&fileInfo{name: "lib.es2016.intl.d.ts", size: int64(len(libs_lib_es2016_intl_d_ts))},
&fileInfo{name: "lib.es2017.arraybuffer.d.ts", size: int64(len(libs_lib_es2017_arraybuffer_d_ts))},
&fileInfo{name: "lib.es2017.d.ts", size: int64(len(libs_lib_es2017_d_ts))},
&fileInfo{name: "lib.es2017.date.d.ts", size: int64(len(libs_lib_es2017_date_d_ts))},
&fileInfo{name: "lib.es2017.full.d.ts", size: int64(len(libs_lib_es2017_full_d_ts))},
&fileInfo{name: "lib.es2017.intl.d.ts", size: int64(len(libs_lib_es2017_intl_d_ts))},
&fileInfo{name: "lib.es2017.object.d.ts", size: int64(len(libs_lib_es2017_object_d_ts))},
&fileInfo{name: "lib.es2017.sharedmemory.d.ts", size: int64(len(libs_lib_es2017_sharedmemory_d_ts))},
&fileInfo{name: "lib.es2017.string.d.ts", size: int64(len(libs_lib_es2017_string_d_ts))},
&fileInfo{name: "lib.es2017.typedarrays.d.ts", size: int64(len(libs_lib_es2017_typedarrays_d_ts))},
&fileInfo{name: "lib.es2018.asyncgenerator.d.ts", size: int64(len(libs_lib_es2018_asyncgenerator_d_ts))},
&fileInfo{name: "lib.es2018.asynciterable.d.ts", size: int64(len(libs_lib_es2018_asynciterable_d_ts))},
&fileInfo{name: "lib.es2018.d.ts", size: int64(len(libs_lib_es2018_d_ts))},
&fileInfo{name: "lib.es2018.full.d.ts", size: int64(len(libs_lib_es2018_full_d_ts))},
&fileInfo{name: "lib.es2018.intl.d.ts", size: int64(len(libs_lib_es2018_intl_d_ts))},
&fileInfo{name: "lib.es2018.promise.d.ts", size: int64(len(libs_lib_es2018_promise_d_ts))},
&fileInfo{name: "lib.es2018.regexp.d.ts", size: int64(len(libs_lib_es2018_regexp_d_ts))},
&fileInfo{name: "lib.es2019.array.d.ts", size: int64(len(libs_lib_es2019_array_d_ts))},
&fileInfo{name: "lib.es2019.d.ts", size: int64(len(libs_lib_es2019_d_ts))},
&fileInfo{name: "lib.es2019.full.d.ts", size: int64(len(libs_lib_es2019_full_d_ts))},
&fileInfo{name: "lib.es2019.intl.d.ts", size: int64(len(libs_lib_es2019_intl_d_ts))},
&fileInfo{name: "lib.es2019.object.d.ts", size: int64(len(libs_lib_es2019_object_d_ts))},
&fileInfo{name: "lib.es2019.string.d.ts", size: int64(len(libs_lib_es2019_string_d_ts))},
&fileInfo{name: "lib.es2019.symbol.d.ts", size: int64(len(libs_lib_es2019_symbol_d_ts))},
&fileInfo{name: "lib.es2020.bigint.d.ts", size: int64(len(libs_lib_es2020_bigint_d_ts))},
&fileInfo{name: "lib.es2020.d.ts", size: int64(len(libs_lib_es2020_d_ts))},
&fileInfo{name: "lib.es2020.date.d.ts", size: int64(len(libs_lib_es2020_date_d_ts))},
&fileInfo{name: "lib.es2020.full.d.ts", size: int64(len(libs_lib_es2020_full_d_ts))},
&fileInfo{name: "lib.es2020.intl.d.ts", size: int64(len(libs_lib_es2020_intl_d_ts))},
&fileInfo{name: "lib.es2020.number.d.ts", size: int64(len(libs_lib_es2020_number_d_ts))},
&fileInfo{name: "lib.es2020.promise.d.ts", size: int64(len(libs_lib_es2020_promise_d_ts))},
&fileInfo{name: "lib.es2020.sharedmemory.d.ts", size: int64(len(libs_lib_es2020_sharedmemory_d_ts))},
&fileInfo{name: "lib.es2020.string.d.ts", size: int64(len(libs_lib_es2020_string_d_ts))},
&fileInfo{name: "lib.es2020.symbol.wellknown.d.ts", size: int64(len(libs_lib_es2020_symbol_wellknown_d_ts))},
&fileInfo{name: "lib.es2021.d.ts", size: int64(len(libs_lib_es2021_d_ts))},
&fileInfo{name: "lib.es2021.full.d.ts", size: int64(len(libs_lib_es2021_full_d_ts))},
&fileInfo{name: "lib.es2021.intl.d.ts", size: int64(len(libs_lib_es2021_intl_d_ts))},
&fileInfo{name: "lib.es2021.promise.d.ts", size: int64(len(libs_lib_es2021_promise_d_ts))},
&fileInfo{name: "lib.es2021.string.d.ts", size: int64(len(libs_lib_es2021_string_d_ts))},
&fileInfo{name: "lib.es2021.weakref.d.ts", size: int64(len(libs_lib_es2021_weakref_d_ts))},
&fileInfo{name: "lib.es2022.array.d.ts", size: int64(len(libs_lib_es2022_array_d_ts))},
&fileInfo{name: "lib.es2022.d.ts", size: int64(len(libs_lib_es2022_d_ts))},
&fileInfo{name: "lib.es2022.error.d.ts", size: int64(len(libs_lib_es2022_error_d_ts))},
&fileInfo{name: "lib.es2022.full.d.ts", size: int64(len(libs_lib_es2022_full_d_ts))},
&fileInfo{name: "lib.es2022.intl.d.ts", size: int64(len(libs_lib_es2022_intl_d_ts))},
&fileInfo{name: "lib.es2022.object.d.ts", size: int64(len(libs_lib_es2022_object_d_ts))},
&fileInfo{name: "lib.es2022.regexp.d.ts", size: int64(len(libs_lib_es2022_regexp_d_ts))},
&fileInfo{name: "lib.es2022.string.d.ts", size: int64(len(libs_lib_es2022_string_d_ts))},
&fileInfo{name: "lib.es2023.array.d.ts", size: int64(len(libs_lib_es2023_array_d_ts))},
&fileInfo{name: "lib.es2023.collection.d.ts", size: int64(len(libs_lib_es2023_collection_d_ts))},
&fileInfo{name: "lib.es2023.d.ts", size: int64(len(libs_lib_es2023_d_ts))},
&fileInfo{name: "lib.es2023.full.d.ts", size: int64(len(libs_lib_es2023_full_d_ts))},
&fileInfo{name: "lib.es2023.intl.d.ts", size: int64(len(libs_lib_es2023_intl_d_ts))},
&fileInfo{name: "lib.es2024.arraybuffer.d.ts", size: int64(len(libs_lib_es2024_arraybuffer_d_ts))},
&fileInfo{name: "lib.es2024.collection.d.ts", size: int64(len(libs_lib_es2024_collection_d_ts))},
&fileInfo{name: "lib.es2024.d.ts", size: int64(len(libs_lib_es2024_d_ts))},
&fileInfo{name: "lib.es2024.full.d.ts", size: int64(len(libs_lib_es2024_full_d_ts))},
&fileInfo{name: "lib.es2024.object.d.ts", size: int64(len(libs_lib_es2024_object_d_ts))},
&fileInfo{name: "lib.es2024.promise.d.ts", size: int64(len(libs_lib_es2024_promise_d_ts))},
&fileInfo{name: "lib.es2024.regexp.d.ts", size: int64(len(libs_lib_es2024_regexp_d_ts))},
&fileInfo{name: "lib.es2024.sharedmemory.d.ts", size: int64(len(libs_lib_es2024_sharedmemory_d_ts))},
&fileInfo{name: "lib.es2024.string.d.ts", size: int64(len(libs_lib_es2024_string_d_ts))},
&fileInfo{name: "lib.es2025.collection.d.ts", size: int64(len(libs_lib_es2025_collection_d_ts))},
&fileInfo{name: "lib.es2025.d.ts", size: int64(len(libs_lib_es2025_d_ts))},
&fileInfo{name: "lib.es2025.float16.d.ts", size: int64(len(libs_lib_es2025_float16_d_ts))},
&fileInfo{name: "lib.es2025.full.d.ts", size: int64(len(libs_lib_es2025_full_d_ts))},
&fileInfo{name: "lib.es2025.intl.d.ts", size: int64(len(libs_lib_es2025_intl_d_ts))},
&fileInfo{name: "lib.es2025.iterator.d.ts", size: int64(len(libs_lib_es2025_iterator_d_ts))},
&fileInfo{name: "lib.es2025.promise.d.ts", size: int64(len(libs_lib_es2025_promise_d_ts))},
&fileInfo{name: "lib.es2025.regexp.d.ts", size: int64(len(libs_lib_es2025_regexp_d_ts))},
&fileInfo{name: "lib.es5.d.ts", size: int64(len(libs_lib_es5_d_ts))},
&fileInfo{name: "lib.es6.d.ts", size: int64(len(libs_lib_es6_d_ts))},
&fileInfo{name: "lib.esnext.array.d.ts", size: int64(len(libs_lib_esnext_array_d_ts))},
&fileInfo{name: "lib.esnext.collection.d.ts", size: int64(len(libs_lib_esnext_collection_d_ts))},
&fileInfo{name: "lib.esnext.d.ts", size: int64(len(libs_lib_esnext_d_ts))},
&fileInfo{name: "lib.esnext.date.d.ts", size: int64(len(libs_lib_esnext_date_d_ts))},
&fileInfo{name: "lib.esnext.decorators.d.ts", size: int64(len(libs_lib_esnext_decorators_d_ts))},
&fileInfo{name: "lib.esnext.disposable.d.ts", size: int64(len(libs_lib_esnext_disposable_d_ts))},
&fileInfo{name: "lib.esnext.error.d.ts", size: int64(len(libs_lib_esnext_error_d_ts))},
&fileInfo{name: "lib.esnext.full.d.ts", size: int64(len(libs_lib_esnext_full_d_ts))},
&fileInfo{name: "lib.esnext.intl.d.ts", size: int64(len(libs_lib_esnext_intl_d_ts))},
&fileInfo{name: "lib.esnext.sharedmemory.d.ts", size: int64(len(libs_lib_esnext_sharedmemory_d_ts))},
&fileInfo{name: "lib.esnext.temporal.d.ts", size: int64(len(libs_lib_esnext_temporal_d_ts))},
&fileInfo{name: "lib.esnext.typedarrays.d.ts", size: int64(len(libs_lib_esnext_typedarrays_d_ts))},
&fileInfo{name: "lib.scripthost.d.ts", size: int64(len(libs_lib_scripthost_d_ts))},
&fileInfo{name: "lib.webworker.asynciterable.d.ts", size: int64(len(libs_lib_webworker_asynciterable_d_ts))},
&fileInfo{name: "lib.webworker.d.ts", size: int64(len(libs_lib_webworker_d_ts))},
&fileInfo{name: "lib.webworker.importscripts.d.ts", size: int64(len(libs_lib_webworker_importscripts_d_ts))},
&fileInfo{name: "lib.webworker.iterable.d.ts", size: int64(len(libs_lib_webworker_iterable_d_ts))},
}

View File

@@ -0,0 +1,217 @@
//go:build ignore
package main
import (
"bytes"
"encoding/json"
"fmt"
"go/format"
"log"
"os"
"path/filepath"
"regexp"
"slices"
"strings"
"github.com/microsoft/typescript-go/internal/repo"
)
var (
libInputDir = filepath.Join(repo.TypeScriptSubmodulePath(), "src", "lib")
copyrightNotice = filepath.Join(repo.TypeScriptSubmodulePath(), "scripts", "CopyrightNotice.txt")
)
func main() {
log.SetFlags(log.LstdFlags | log.Lshortfile)
libs := readLibs()
generateLibs(libs)
generateLibList(libs)
generateEmbedded(libs)
}
type lib struct {
target string // target relative to libs dir
sources []string // sources relative to src/lib dir
}
func generateLibs(libs []lib) {
const outputDir = "libs"
copyright := readCopyright()
if err := os.RemoveAll(outputDir); err != nil {
log.Fatalf("failed to remove libs directory: %v", err)
}
if err := os.MkdirAll(outputDir, 0o755); err != nil {
log.Fatalf("failed to create libs directory: %v", err)
}
for _, lib := range libs {
var output bytes.Buffer
output.Write(copyright)
for _, source := range lib.sources {
sourcePath := filepath.Join(libInputDir, source)
b, err := os.ReadFile(sourcePath)
if err != nil {
log.Fatalf("failed to read %s: %v", sourcePath, err)
}
output.WriteByte('\n')
output.Write(removeCRLF(b))
}
outputPath := filepath.Join(outputDir, lib.target)
if err := os.WriteFile(outputPath, output.Bytes(), 0o644); err != nil {
log.Fatalf("failed to write %s: %v", outputPath, err)
}
}
}
func generateLibList(libs []lib) {
var code bytes.Buffer
code.WriteString("// Code generated by generate.go; DO NOT EDIT.\n\n")
code.WriteString("package bundled\n\n")
code.WriteString("// LibNames is the list of all bundled lib files, sorted by name.\n")
code.WriteString("// For the list of libs sorted by load order, use [tsoptions.Libs].\n")
code.WriteString("var LibNames = []string{\n")
for _, lib := range libs {
code.WriteString("\t\"" + lib.target + "\",\n")
}
code.WriteString("}\n")
writeCode("libs_generated.go", code.Bytes())
}
func generateEmbedded(libs []lib) {
libVarNames := make([]string, len(libs))
for i, lib := range libs {
libVarNames[i] = "libs_" + strings.ReplaceAll(lib.target, ".", "_")
}
var code bytes.Buffer
code.WriteString("//go:build !noembed\n\n")
code.WriteString("// Code generated by generate.go; DO NOT EDIT.\n\n")
code.WriteString("package bundled\n\n")
code.WriteString("import (\n")
code.WriteString("\"io/fs\"\n\n")
code.WriteString("_ \"embed\"\n")
code.WriteString(")\n\n")
code.WriteString("var (\n")
for i, lib := range libs {
varName := libVarNames[i]
code.WriteString("//go:embed libs/" + lib.target + "\n")
code.WriteString("" + varName + " string\n")
}
code.WriteString(")\n\n")
code.WriteString("var embeddedContents = map[string]string{\n")
for i, lib := range libs {
varName := libVarNames[i]
code.WriteString("\t\"libs/" + lib.target + "\": " + varName + ",\n")
}
code.WriteString("}\n\n")
code.WriteString("var libsEntries = []fs.DirEntry{\n")
for i, lib := range libs {
varName := libVarNames[i]
fmt.Fprintf(&code, "\t&fileInfo{name: %q, size: int64(len(%s))},\n", lib.target, varName)
}
code.WriteString("}\n")
writeCode("embed_generated.go", code.Bytes())
}
var (
// Match escaped characters, double-quoted strings, single-line comments, and multi-line comments.
reJSONComments = regexp.MustCompile(`\\.|"(?:\\.|[^"])*"|//.*|/\*[\s\S]*?\*/`)
// Match double-quoted strings (to skip) or trailing commas before ] or }.
reTrailingComma = regexp.MustCompile(`"(?:\\.|[^"])*"|,\s*([}\]])`)
)
// stripJSONC replaces comments and trailing commas with spaces in JSONC content,
// producing valid JSON. The input slice is mutated in place.
func stripJSONC(b []byte) {
for _, loc := range reJSONComments.FindAllIndex(b, -1) {
if b[loc[0]] == '/' {
for i := loc[0]; i < loc[1]; i++ {
if b[i] != '\n' {
b[i] = ' '
}
}
}
}
for _, loc := range reTrailingComma.FindAllSubmatchIndex(b, -1) {
// loc[2]:loc[3] is the capture group; -1 means this matched a string, not a comma.
if loc[2] < 0 {
continue
}
// Blank the comma (at loc[0]), keep whitespace and closing bracket.
b[loc[0]] = ' '
}
}
func readLibs() []lib {
libsFile := filepath.Join(libInputDir, "libs.json")
b, err := os.ReadFile(libsFile)
if err != nil {
log.Fatalf("failed to open libs.json: %v", err)
}
stripJSONC(b)
var meta struct {
Libs []string `json:"libs"`
Paths map[string]string `json:"paths"`
}
if err := json.Unmarshal(b, &meta); err != nil {
log.Fatalf("failed to parse libs.json: %v", err)
}
var libs []lib
for _, libName := range meta.Libs {
sources := []string{libName + ".d.ts"}
var target string
if path, ok := meta.Paths[libName]; ok {
target = path
} else {
target = "lib." + libName + ".d.ts"
}
libs = append(libs, lib{target: target, sources: sources})
}
slices.SortFunc(libs, func(a lib, b lib) int {
return strings.Compare(a.target, b.target)
})
return libs
}
func readCopyright() []byte {
b, err := os.ReadFile(copyrightNotice)
if err != nil {
log.Fatalf("failed to read copyright notice: %v", err)
}
return removeCRLF(b)
}
func removeCRLF(b []byte) []byte {
return bytes.ReplaceAll(b, []byte("\r\n"), []byte("\n"))
}
func writeCode(filename string, code []byte) {
formatted, err := format.Source(code)
if err != nil {
log.Fatalf("failed to format source: %v", err)
}
if err := os.WriteFile(filename, formatted, 0o644); err != nil {
log.Fatalf("failed to write %s: %v", filename, err)
}
}

View File

@@ -0,0 +1,20 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference lib="es5" />
/// <reference lib="dom" />
/// <reference lib="webworker.importscripts" />
/// <reference lib="scripthost" />

View File

@@ -0,0 +1,382 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/**
* The decorator context types provided to class element decorators.
*/
type ClassMemberDecoratorContext =
| ClassMethodDecoratorContext
| ClassGetterDecoratorContext
| ClassSetterDecoratorContext
| ClassFieldDecoratorContext
| ClassAccessorDecoratorContext;
/**
* The decorator context types provided to any decorator.
*/
type DecoratorContext =
| ClassDecoratorContext
| ClassMemberDecoratorContext;
type DecoratorMetadataObject = Record<PropertyKey, unknown> & object;
type DecoratorMetadata = typeof globalThis extends { Symbol: { readonly metadata: symbol; }; } ? DecoratorMetadataObject : DecoratorMetadataObject | undefined;
/**
* Context provided to a class decorator.
* @template Class The type of the decorated class associated with this context.
*/
interface ClassDecoratorContext<
Class extends abstract new (...args: any) => any = abstract new (...args: any) => any,
> {
/** The kind of element that was decorated. */
readonly kind: "class";
/** The name of the decorated class. */
readonly name: string | undefined;
/**
* Adds a callback to be invoked after the class definition has been finalized.
*
* @example
* ```ts
* function customElement(name: string): ClassDecoratorFunction {
* return (target, context) => {
* context.addInitializer(function () {
* customElements.define(name, this);
* });
* }
* }
*
* @customElement("my-element")
* class MyElement {}
* ```
*/
addInitializer(initializer: (this: Class) => void): void;
readonly metadata: DecoratorMetadata;
}
/**
* Context provided to a class method decorator.
* @template This The type on which the class element will be defined. For a static class element, this will be
* the type of the constructor. For a non-static class element, this will be the type of the instance.
* @template Value The type of the decorated class method.
*/
interface ClassMethodDecoratorContext<
This = unknown,
Value extends (this: This, ...args: any) => any = (this: This, ...args: any) => any,
> {
/** The kind of class element that was decorated. */
readonly kind: "method";
/** The name of the decorated class element. */
readonly name: string | symbol;
/** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */
readonly static: boolean;
/** A value indicating whether the class element has a private name. */
readonly private: boolean;
/** An object that can be used to access the current value of the class element at runtime. */
readonly access: {
/**
* Determines whether an object has a property with the same name as the decorated element.
*/
has(object: This): boolean;
/**
* Gets the current value of the method from the provided object.
*
* @example
* let fn = context.access.get(instance);
*/
get(object: This): Value;
};
/**
* Adds a callback to be invoked either after static methods are defined but before
* static initializers are run (when decorating a `static` element), or before instance
* initializers are run (when decorating a non-`static` element).
*
* @example
* ```ts
* const bound: ClassMethodDecoratorFunction = (value, context) {
* if (context.private) throw new TypeError("Not supported on private methods.");
* context.addInitializer(function () {
* this[context.name] = this[context.name].bind(this);
* });
* }
*
* class C {
* message = "Hello";
*
* @bound
* m() {
* console.log(this.message);
* }
* }
* ```
*/
addInitializer(initializer: (this: This) => void): void;
readonly metadata: DecoratorMetadata;
}
/**
* Context provided to a class getter decorator.
* @template This The type on which the class element will be defined. For a static class element, this will be
* the type of the constructor. For a non-static class element, this will be the type of the instance.
* @template Value The property type of the decorated class getter.
*/
interface ClassGetterDecoratorContext<
This = unknown,
Value = unknown,
> {
/** The kind of class element that was decorated. */
readonly kind: "getter";
/** The name of the decorated class element. */
readonly name: string | symbol;
/** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */
readonly static: boolean;
/** A value indicating whether the class element has a private name. */
readonly private: boolean;
/** An object that can be used to access the current value of the class element at runtime. */
readonly access: {
/**
* Determines whether an object has a property with the same name as the decorated element.
*/
has(object: This): boolean;
/**
* Invokes the getter on the provided object.
*
* @example
* let value = context.access.get(instance);
*/
get(object: This): Value;
};
/**
* Adds a callback to be invoked either after static methods are defined but before
* static initializers are run (when decorating a `static` element), or before instance
* initializers are run (when decorating a non-`static` element).
*/
addInitializer(initializer: (this: This) => void): void;
readonly metadata: DecoratorMetadata;
}
/**
* Context provided to a class setter decorator.
* @template This The type on which the class element will be defined. For a static class element, this will be
* the type of the constructor. For a non-static class element, this will be the type of the instance.
* @template Value The type of the decorated class setter.
*/
interface ClassSetterDecoratorContext<
This = unknown,
Value = unknown,
> {
/** The kind of class element that was decorated. */
readonly kind: "setter";
/** The name of the decorated class element. */
readonly name: string | symbol;
/** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */
readonly static: boolean;
/** A value indicating whether the class element has a private name. */
readonly private: boolean;
/** An object that can be used to access the current value of the class element at runtime. */
readonly access: {
/**
* Determines whether an object has a property with the same name as the decorated element.
*/
has(object: This): boolean;
/**
* Invokes the setter on the provided object.
*
* @example
* context.access.set(instance, value);
*/
set(object: This, value: Value): void;
};
/**
* Adds a callback to be invoked either after static methods are defined but before
* static initializers are run (when decorating a `static` element), or before instance
* initializers are run (when decorating a non-`static` element).
*/
addInitializer(initializer: (this: This) => void): void;
readonly metadata: DecoratorMetadata;
}
/**
* Context provided to a class `accessor` field decorator.
* @template This The type on which the class element will be defined. For a static class element, this will be
* the type of the constructor. For a non-static class element, this will be the type of the instance.
* @template Value The type of decorated class field.
*/
interface ClassAccessorDecoratorContext<
This = unknown,
Value = unknown,
> {
/** The kind of class element that was decorated. */
readonly kind: "accessor";
/** The name of the decorated class element. */
readonly name: string | symbol;
/** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */
readonly static: boolean;
/** A value indicating whether the class element has a private name. */
readonly private: boolean;
/** An object that can be used to access the current value of the class element at runtime. */
readonly access: {
/**
* Determines whether an object has a property with the same name as the decorated element.
*/
has(object: This): boolean;
/**
* Invokes the getter on the provided object.
*
* @example
* let value = context.access.get(instance);
*/
get(object: This): Value;
/**
* Invokes the setter on the provided object.
*
* @example
* context.access.set(instance, value);
*/
set(object: This, value: Value): void;
};
/**
* Adds a callback to be invoked immediately after the auto `accessor` being
* decorated is initialized (regardless if the `accessor` is `static` or not).
*/
addInitializer(initializer: (this: This) => void): void;
readonly metadata: DecoratorMetadata;
}
/**
* Describes the target provided to class `accessor` field decorators.
* @template This The `this` type to which the target applies.
* @template Value The property type for the class `accessor` field.
*/
interface ClassAccessorDecoratorTarget<This, Value> {
/**
* Invokes the getter that was defined prior to decorator application.
*
* @example
* let value = target.get.call(instance);
*/
get(this: This): Value;
/**
* Invokes the setter that was defined prior to decorator application.
*
* @example
* target.set.call(instance, value);
*/
set(this: This, value: Value): void;
}
/**
* Describes the allowed return value from a class `accessor` field decorator.
* @template This The `this` type to which the target applies.
* @template Value The property type for the class `accessor` field.
*/
interface ClassAccessorDecoratorResult<This, Value> {
/**
* An optional replacement getter function. If not provided, the existing getter function is used instead.
*/
get?(this: This): Value;
/**
* An optional replacement setter function. If not provided, the existing setter function is used instead.
*/
set?(this: This, value: Value): void;
/**
* An optional initializer mutator that is invoked when the underlying field initializer is evaluated.
* @param value The incoming initializer value.
* @returns The replacement initializer value.
*/
init?(this: This, value: Value): Value;
}
/**
* Context provided to a class field decorator.
* @template This The type on which the class element will be defined. For a static class element, this will be
* the type of the constructor. For a non-static class element, this will be the type of the instance.
* @template Value The type of the decorated class field.
*/
interface ClassFieldDecoratorContext<
This = unknown,
Value = unknown,
> {
/** The kind of class element that was decorated. */
readonly kind: "field";
/** The name of the decorated class element. */
readonly name: string | symbol;
/** A value indicating whether the class element is a static (`true`) or instance (`false`) element. */
readonly static: boolean;
/** A value indicating whether the class element has a private name. */
readonly private: boolean;
/** An object that can be used to access the current value of the class element at runtime. */
readonly access: {
/**
* Determines whether an object has a property with the same name as the decorated element.
*/
has(object: This): boolean;
/**
* Gets the value of the field on the provided object.
*/
get(object: This): Value;
/**
* Sets the value of the field on the provided object.
*/
set(object: This, value: Value): void;
};
/**
* Adds a callback to be invoked immediately after the field being decorated
* is initialized (regardless if the field is `static` or not).
*/
addInitializer(initializer: (this: This) => void): void;
readonly metadata: DecoratorMetadata;
}

View File

@@ -0,0 +1,20 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
declare type ClassDecorator = <TFunction extends Function>(target: TFunction) => TFunction | void;
declare type PropertyDecorator = (target: Object, propertyKey: string | symbol) => void;
declare type MethodDecorator = <T>(target: Object, propertyKey: string | symbol, descriptor: TypedPropertyDescriptor<T>) => TypedPropertyDescriptor<T> | void;
declare type ParameterDecorator = (target: Object, propertyKey: string | symbol | undefined, parameterIndex: number) => void;

View File

@@ -0,0 +1,18 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
// This file's contents are now included in the main types file.
// The file has been left for backward compatibility.

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,18 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
// This file's contents are now included in the main types file.
// The file has been left for backward compatibility.

View File

@@ -0,0 +1,159 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
interface Map<K, V> {
/**
* Removes all elements from the Map.
*/
clear(): void;
/**
* @returns true if an element in the Map existed and has been removed, or false if the element does not exist.
*/
delete(key: K): boolean;
/**
* Executes a provided function once per each key/value pair in the Map, in insertion order.
*/
forEach(callbackfn: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void;
/**
* Returns a specified element from the Map object. If the value that is associated to the provided key is an object, then you will get a reference to that object and any change made to that object will effectively modify it inside the Map.
* @returns Returns the element associated with the specified key. If no element is associated with the specified key, undefined is returned.
*/
get(key: K): V | undefined;
/**
* @returns boolean indicating whether an element with the specified key exists or not.
*/
has(key: K): boolean;
/**
* Adds a new element with a specified key and value to the Map. If an element with the same key already exists, the element will be updated.
*/
set(key: K, value: V): this;
/**
* @returns the number of elements in the Map.
*/
readonly size: number;
}
interface MapConstructor {
new (): Map<any, any>;
new <K, V>(entries?: readonly (readonly [K, V])[] | null): Map<K, V>;
readonly prototype: Map<any, any>;
}
declare var Map: MapConstructor;
interface ReadonlyMap<K, V> {
forEach(callbackfn: (value: V, key: K, map: ReadonlyMap<K, V>) => void, thisArg?: any): void;
get(key: K): V | undefined;
has(key: K): boolean;
readonly size: number;
}
interface WeakMap<K extends WeakKey, V> {
/**
* Removes the specified element from the WeakMap.
* @returns true if the element was successfully removed, or false if it was not present.
*/
delete(key: K): boolean;
/**
* @returns a specified element.
*/
get(key: K): V | undefined;
/**
* @returns a boolean indicating whether an element with the specified key exists or not.
*/
has(key: K): boolean;
/**
* Adds a new element with a specified key and value.
* @param key Must be an object or symbol.
*/
set(key: K, value: V): this;
}
interface WeakMapConstructor {
new <K extends WeakKey = WeakKey, V = any>(entries?: readonly (readonly [K, V])[] | null): WeakMap<K, V>;
readonly prototype: WeakMap<WeakKey, any>;
}
declare var WeakMap: WeakMapConstructor;
interface Set<T> {
/**
* Appends a new element with a specified value to the end of the Set.
*/
add(value: T): this;
/**
* Removes all elements from the Set.
*/
clear(): void;
/**
* Removes a specified value from the Set.
* @returns Returns true if an element in the Set existed and has been removed, or false if the element does not exist.
*/
delete(value: T): boolean;
/**
* Executes a provided function once per each value in the Set object, in insertion order.
*/
forEach(callbackfn: (value: T, value2: T, set: Set<T>) => void, thisArg?: any): void;
/**
* @returns a boolean indicating whether an element with the specified value exists in the Set or not.
*/
has(value: T): boolean;
/**
* @returns the number of (unique) elements in the Set.
*/
readonly size: number;
}
interface SetConstructor {
new <T = any>(values?: readonly T[] | null): Set<T>;
readonly prototype: Set<any>;
}
declare var Set: SetConstructor;
interface ReadonlySet<T> {
/**
* Executes a provided function once per each value in the ReadonlySet object, in insertion order.
*/
forEach(callbackfn: (value: T, value2: T, set: ReadonlySet<T>) => void, thisArg?: any): void;
/**
* @returns a boolean indicating whether an element with the specified value exists in the Set or not.
*/
has(value: T): boolean;
/**
* @returns the number of (unique) elements in the Set.
*/
readonly size: number;
}
interface WeakSet<T extends WeakKey> {
/**
* Appends a new value to the end of the WeakSet.
*/
add(value: T): this;
/**
* Removes the specified element from the WeakSet.
* @returns Returns true if the element existed and has been removed, or false if the element does not exist.
*/
delete(value: T): boolean;
/**
* @returns a boolean indicating whether a value exists in the WeakSet or not.
*/
has(value: T): boolean;
}
interface WeakSetConstructor {
new <T extends WeakKey = WeakKey>(values?: readonly T[] | null): WeakSet<T>;
readonly prototype: WeakSet<WeakKey>;
}
declare var WeakSet: WeakSetConstructor;

View File

@@ -0,0 +1,595 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
interface Array<T> {
/**
* Returns the value of the first element in the array where predicate is true, and undefined
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found, find
* immediately returns that element value. Otherwise, find returns undefined.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
find<S extends T>(predicate: (value: T, index: number, obj: T[]) => value is S, thisArg?: any): S | undefined;
find(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): T | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: T, index: number, obj: T[]) => unknown, thisArg?: any): number;
/**
* Changes all array elements from `start` to `end` index to a static `value` and returns the modified array
* @param value value to fill array section with
* @param start index to start filling the array at. If start is negative, it is treated as
* length+start where length is the length of the array.
* @param end index to stop filling the array at. If end is negative, it is treated as
* length+end.
*/
fill(value: T, start?: number, end?: number): this;
/**
* Returns the this object after copying a section of the array identified by start and end
* to the same array starting at position target
* @param target If target is negative, it is treated as length+target where length is the
* length of the array.
* @param start If start is negative, it is treated as length+start. If end is negative, it
* is treated as length+end.
* @param end If not specified, length of the this object is used as its default value.
*/
copyWithin(target: number, start: number, end?: number): this;
toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions & Intl.DateTimeFormatOptions): string;
}
interface ArrayConstructor {
/**
* Creates an array from an array-like object.
* @param arrayLike An array-like object to convert to an array.
*/
from<T>(arrayLike: ArrayLike<T>): T[];
/**
* Creates an array from an iterable object.
* @param arrayLike An array-like object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T, U>(arrayLike: ArrayLike<T>, mapfn: (v: T, k: number) => U, thisArg?: any): U[];
/**
* Returns a new array from a set of elements.
* @param items A set of elements to include in the new array object.
*/
of<T>(...items: T[]): T[];
}
interface DateConstructor {
new (value: number | string | Date): Date;
}
interface Function {
/**
* Returns the name of the function. Function names are read-only and can not be changed.
*/
readonly name: string;
}
interface Math {
/**
* Returns the number of leading zero bits in the 32-bit binary representation of a number.
* @param x A numeric expression.
*/
clz32(x: number): number;
/**
* Returns the result of 32-bit multiplication of two numbers.
* @param x First number
* @param y Second number
*/
imul(x: number, y: number): number;
/**
* Returns the sign of x, indicating whether x is positive, negative, or zero.
* @param x The numeric expression to test
*/
sign(x: number): number;
/**
* Returns the base 10 logarithm of a number.
* @param x A numeric expression.
*/
log10(x: number): number;
/**
* Returns the base 2 logarithm of a number.
* @param x A numeric expression.
*/
log2(x: number): number;
/**
* Returns the natural logarithm of 1 + x.
* @param x A numeric expression.
*/
log1p(x: number): number;
/**
* Returns the result of (e^x - 1), which is an implementation-dependent approximation to
* subtracting 1 from the exponential function of x (e raised to the power of x, where e
* is the base of the natural logarithms).
* @param x A numeric expression.
*/
expm1(x: number): number;
/**
* Returns the hyperbolic cosine of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
cosh(x: number): number;
/**
* Returns the hyperbolic sine of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
sinh(x: number): number;
/**
* Returns the hyperbolic tangent of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
tanh(x: number): number;
/**
* Returns the inverse hyperbolic cosine of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
acosh(x: number): number;
/**
* Returns the inverse hyperbolic sine of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
asinh(x: number): number;
/**
* Returns the inverse hyperbolic tangent of a number.
* @param x A numeric expression that contains an angle measured in radians.
*/
atanh(x: number): number;
/**
* Returns the square root of the sum of squares of its arguments.
* @param values Values to compute the square root for.
* If no arguments are passed, the result is +0.
* If there is only one argument, the result is the absolute value.
* If any argument is +Infinity or -Infinity, the result is +Infinity.
* If any argument is NaN, the result is NaN.
* If all arguments are either +0 or 0, the result is +0.
*/
hypot(...values: number[]): number;
/**
* Returns the integral part of the numeric expression x, removing any fractional digits.
* If x is already an integer, the result is x.
* @param x A numeric expression.
*/
trunc(x: number): number;
/**
* Returns the nearest single precision float representation of a number.
* @param x A numeric expression.
*/
fround(x: number): number;
/**
* Returns an implementation-dependent approximation to the cube root of number.
* @param x A numeric expression.
*/
cbrt(x: number): number;
}
interface NumberConstructor {
/**
* The value of Number.EPSILON is the difference between 1 and the smallest value greater than 1
* that is representable as a Number value, which is approximately:
* 2.2204460492503130808472633361816 x 1016.
*/
readonly EPSILON: number;
/**
* Returns true if passed value is finite.
* Unlike the global isFinite, Number.isFinite doesn't forcibly convert the parameter to a
* number. Only finite values of the type number, result in true.
* @param number A numeric value.
*/
isFinite(number: unknown): boolean;
/**
* Returns true if the value passed is an integer, false otherwise.
* @param number A numeric value.
*/
isInteger(number: unknown): boolean;
/**
* Returns a Boolean value that indicates whether a value is the reserved value NaN (not a
* number). Unlike the global isNaN(), Number.isNaN() doesn't forcefully convert the parameter
* to a number. Only values of the type number, that are also NaN, result in true.
* @param number A numeric value.
*/
isNaN(number: unknown): boolean;
/**
* Returns true if the value passed is a safe integer.
* @param number A numeric value.
*/
isSafeInteger(number: unknown): boolean;
/**
* The value of the largest integer n such that n and n + 1 are both exactly representable as
* a Number value.
* The value of Number.MAX_SAFE_INTEGER is 9007199254740991 2^53 1.
*/
readonly MAX_SAFE_INTEGER: number;
/**
* The value of the smallest integer n such that n and n 1 are both exactly representable as
* a Number value.
* The value of Number.MIN_SAFE_INTEGER is 9007199254740991 ((2^53 1)).
*/
readonly MIN_SAFE_INTEGER: number;
/**
* Converts a string to a floating-point number.
* @param string A string that contains a floating-point number.
*/
parseFloat(string: string): number;
/**
* Converts A string to an integer.
* @param string A string to convert into a number.
* @param radix A value between 2 and 36 that specifies the base of the number in `string`.
* If this argument is not supplied, strings with a prefix of '0x' are considered hexadecimal.
* All other strings are considered decimal.
*/
parseInt(string: string, radix?: number): number;
}
interface ObjectConstructor {
/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
* target object. Returns the target object.
* @param target The target object to copy to.
* @param source The source object from which to copy properties.
*/
assign<T extends {}, U>(target: T, source: U): T & U;
/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
* target object. Returns the target object.
* @param target The target object to copy to.
* @param source1 The first source object from which to copy properties.
* @param source2 The second source object from which to copy properties.
*/
assign<T extends {}, U, V>(target: T, source1: U, source2: V): T & U & V;
/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
* target object. Returns the target object.
* @param target The target object to copy to.
* @param source1 The first source object from which to copy properties.
* @param source2 The second source object from which to copy properties.
* @param source3 The third source object from which to copy properties.
*/
assign<T extends {}, U, V, W>(target: T, source1: U, source2: V, source3: W): T & U & V & W;
/**
* Copy the values of all of the enumerable own properties from one or more source objects to a
* target object. Returns the target object.
* @param target The target object to copy to.
* @param sources One or more source objects from which to copy properties
*/
assign(target: object, ...sources: any[]): any;
/**
* Returns an array of all symbol properties found directly on object o.
* @param o Object to retrieve the symbols from.
*/
getOwnPropertySymbols(o: any): symbol[];
/**
* Returns the names of the enumerable string properties and methods of an object.
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
keys(o: {}): string[];
/**
* Returns true if the values are the same value, false otherwise.
* @param value1 The first value.
* @param value2 The second value.
*/
is(value1: any, value2: any): boolean;
/**
* Sets the prototype of a specified object o to object proto or null. Returns the object o.
* @param o The object to change its prototype.
* @param proto The value of the new prototype or null.
*/
setPrototypeOf(o: any, proto: object | null): any;
}
interface ReadonlyArray<T> {
/**
* Returns the value of the first element in the array where predicate is true, and undefined
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found, find
* immediately returns that element value. Otherwise, find returns undefined.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
find<S extends T>(predicate: (value: T, index: number, obj: readonly T[]) => value is S, thisArg?: any): S | undefined;
find(predicate: (value: T, index: number, obj: readonly T[]) => unknown, thisArg?: any): T | undefined;
/**
* Returns the index of the first element in the array where predicate is true, and -1
* otherwise.
* @param predicate find calls predicate once for each element of the array, in ascending
* order, until it finds one where predicate returns true. If such an element is found,
* findIndex immediately returns that element index. Otherwise, findIndex returns -1.
* @param thisArg If provided, it will be used as the this value for each invocation of
* predicate. If it is not provided, undefined is used instead.
*/
findIndex(predicate: (value: T, index: number, obj: readonly T[]) => unknown, thisArg?: any): number;
toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions & Intl.DateTimeFormatOptions): string;
}
interface RegExp {
/**
* Returns a string indicating the flags of the regular expression in question. This field is read-only.
* The characters in this string are sequenced and concatenated in the following order:
*
* - "g" for global
* - "i" for ignoreCase
* - "m" for multiline
* - "u" for unicode
* - "y" for sticky
*
* If no flags are set, the value is the empty string.
*/
readonly flags: string;
/**
* Returns a Boolean value indicating the state of the sticky flag (y) used with a regular
* expression. Default is false. Read-only.
*/
readonly sticky: boolean;
/**
* Returns a Boolean value indicating the state of the Unicode flag (u) used with a regular
* expression. Default is false. Read-only.
*/
readonly unicode: boolean;
}
interface RegExpConstructor {
new (pattern: RegExp | string, flags?: string): RegExp;
(pattern: RegExp | string, flags?: string): RegExp;
}
interface String {
/**
* Returns a nonnegative integer Number less than 1114112 (0x110000) that is the code point
* value of the UTF-16 encoded code point starting at the string element at position pos in
* the String resulting from converting this object to a String.
* If there is no element at that position, the result is undefined.
* If a valid UTF-16 surrogate pair does not begin at pos, the result is the code unit at pos.
*/
codePointAt(pos: number): number | undefined;
/**
* Returns true if searchString appears as a substring of the result of converting this
* object to a String, at one or more positions that are
* greater than or equal to position; otherwise, returns false.
* @param searchString search string
* @param position If position is undefined, 0 is assumed, so as to search all of the String.
*/
includes(searchString: string, position?: number): boolean;
/**
* Returns true if the sequence of elements of searchString converted to a String is the
* same as the corresponding elements of this object (converted to a String) starting at
* endPosition length(this). Otherwise returns false.
*/
endsWith(searchString: string, endPosition?: number): boolean;
/**
* Returns the String value result of normalizing the string into the normalization form
* named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms.
* @param form Applicable values: "NFC", "NFD", "NFKC", or "NFKD", If not specified default
* is "NFC"
*/
normalize(form: "NFC" | "NFD" | "NFKC" | "NFKD"): string;
/**
* Returns the String value result of normalizing the string into the normalization form
* named by form as specified in Unicode Standard Annex #15, Unicode Normalization Forms.
* @param form Applicable values: "NFC", "NFD", "NFKC", or "NFKD", If not specified default
* is "NFC"
*/
normalize(form?: string): string;
/**
* Returns a String value that is made from count copies appended together. If count is 0,
* the empty string is returned.
* @param count number of copies to append
*/
repeat(count: number): string;
/**
* Returns true if the sequence of elements of searchString converted to a String is the
* same as the corresponding elements of this object (converted to a String) starting at
* position. Otherwise returns false.
*/
startsWith(searchString: string, position?: number): boolean;
/**
* Returns an `<a>` HTML anchor element and sets the name attribute to the text value
* @deprecated A legacy feature for browser compatibility
* @param name
*/
anchor(name: string): string;
/**
* Returns a `<big>` HTML element
* @deprecated A legacy feature for browser compatibility
*/
big(): string;
/**
* Returns a `<blink>` HTML element
* @deprecated A legacy feature for browser compatibility
*/
blink(): string;
/**
* Returns a `<b>` HTML element
* @deprecated A legacy feature for browser compatibility
*/
bold(): string;
/**
* Returns a `<tt>` HTML element
* @deprecated A legacy feature for browser compatibility
*/
fixed(): string;
/**
* Returns a `<font>` HTML element and sets the color attribute value
* @deprecated A legacy feature for browser compatibility
*/
fontcolor(color: string): string;
/**
* Returns a `<font>` HTML element and sets the size attribute value
* @deprecated A legacy feature for browser compatibility
*/
fontsize(size: number): string;
/**
* Returns a `<font>` HTML element and sets the size attribute value
* @deprecated A legacy feature for browser compatibility
*/
fontsize(size: string): string;
/**
* Returns an `<i>` HTML element
* @deprecated A legacy feature for browser compatibility
*/
italics(): string;
/**
* Returns an `<a>` HTML element and sets the href attribute value
* @deprecated A legacy feature for browser compatibility
*/
link(url: string): string;
/**
* Returns a `<small>` HTML element
* @deprecated A legacy feature for browser compatibility
*/
small(): string;
/**
* Returns a `<strike>` HTML element
* @deprecated A legacy feature for browser compatibility
*/
strike(): string;
/**
* Returns a `<sub>` HTML element
* @deprecated A legacy feature for browser compatibility
*/
sub(): string;
/**
* Returns a `<sup>` HTML element
* @deprecated A legacy feature for browser compatibility
*/
sup(): string;
}
interface StringConstructor {
/**
* Return the String value whose elements are, in order, the elements in the List elements.
* If length is 0, the empty string is returned.
*/
fromCodePoint(...codePoints: number[]): string;
/**
* String.raw is usually used as a tag function of a Tagged Template String. When called as
* such, the first argument will be a well formed template call site object and the rest
* parameter will contain the substitution values. It can also be called directly, for example,
* to interleave strings and values from your own tag function, and in this case the only thing
* it needs from the first argument is the raw property.
* @param template A well-formed template string call site representation.
* @param substitutions A set of substitution values.
*/
raw(template: { raw: readonly string[] | ArrayLike<string>; }, ...substitutions: any[]): string;
}
interface Int8Array<TArrayBuffer extends ArrayBufferLike> {
toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions): string;
}
interface Uint8Array<TArrayBuffer extends ArrayBufferLike> {
toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions): string;
}
interface Uint8ClampedArray<TArrayBuffer extends ArrayBufferLike> {
toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions): string;
}
interface Int16Array<TArrayBuffer extends ArrayBufferLike> {
toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions): string;
}
interface Uint16Array<TArrayBuffer extends ArrayBufferLike> {
toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions): string;
}
interface Int32Array<TArrayBuffer extends ArrayBufferLike> {
toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions): string;
}
interface Uint32Array<TArrayBuffer extends ArrayBufferLike> {
toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions): string;
}
interface Float32Array<TArrayBuffer extends ArrayBufferLike> {
toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions): string;
}
interface Float64Array<TArrayBuffer extends ArrayBufferLike> {
toLocaleString(locales: string | string[], options?: Intl.NumberFormatOptions): string;
}

View File

@@ -0,0 +1,26 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference lib="es5" />
/// <reference lib="es2015.core" />
/// <reference lib="es2015.collection" />
/// <reference lib="es2015.iterable" />
/// <reference lib="es2015.generator" />
/// <reference lib="es2015.promise" />
/// <reference lib="es2015.proxy" />
/// <reference lib="es2015.reflect" />
/// <reference lib="es2015.symbol" />
/// <reference lib="es2015.symbol.wellknown" />

View File

@@ -0,0 +1,75 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference lib="es2015.iterable" />
interface Generator<T = unknown, TReturn = any, TNext = any> extends IteratorObject<T, TReturn, TNext> {
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
next(...[value]: [] | [TNext]): IteratorResult<T, TReturn>;
return(value: TReturn): IteratorResult<T, TReturn>;
throw(e: any): IteratorResult<T, TReturn>;
[Symbol.iterator](): Generator<T, TReturn, TNext>;
}
interface GeneratorFunction {
/**
* Creates a new Generator object.
* @param args A list of arguments the function accepts.
*/
new (...args: any[]): Generator;
/**
* Creates a new Generator object.
* @param args A list of arguments the function accepts.
*/
(...args: any[]): Generator;
/**
* The length of the arguments.
*/
readonly length: number;
/**
* Returns the name of the function.
*/
readonly name: string;
/**
* A reference to the prototype.
*/
readonly prototype: Generator;
}
interface GeneratorFunctionConstructor {
/**
* Creates a new Generator function.
* @param args A list of arguments the function accepts.
*/
new (...args: string[]): GeneratorFunction;
/**
* Creates a new Generator function.
* @param args A list of arguments the function accepts.
*/
(...args: string[]): GeneratorFunction;
/**
* The length of the arguments.
*/
readonly length: number;
/**
* Returns the name of the function.
*/
readonly name: string;
/**
* A reference to the prototype.
*/
readonly prototype: GeneratorFunction;
}

View File

@@ -0,0 +1,603 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference lib="es2015.symbol" />
interface SymbolConstructor {
/**
* A method that returns the default iterator for an object. Called by the semantics of the
* for-of statement.
*/
readonly iterator: unique symbol;
}
interface IteratorYieldResult<TYield> {
done?: false;
value: TYield;
}
interface IteratorReturnResult<TReturn> {
done: true;
value: TReturn;
}
type IteratorResult<T, TReturn = any> = IteratorYieldResult<T> | IteratorReturnResult<TReturn>;
interface Iterator<T, TReturn = any, TNext = any> {
// NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
next(...[value]: [] | [TNext]): IteratorResult<T, TReturn>;
return?(value?: TReturn): IteratorResult<T, TReturn>;
throw?(e?: any): IteratorResult<T, TReturn>;
}
interface Iterable<T, TReturn = any, TNext = any> {
[Symbol.iterator](): Iterator<T, TReturn, TNext>;
}
/**
* Describes a user-defined {@link Iterator} that is also iterable.
*/
interface IterableIterator<T, TReturn = any, TNext = any> extends Iterator<T, TReturn, TNext> {
[Symbol.iterator](): IterableIterator<T, TReturn, TNext>;
}
/**
* Describes an {@link Iterator} produced by the runtime that inherits from the intrinsic `Iterator.prototype`.
*/
interface IteratorObject<T, TReturn = unknown, TNext = unknown> extends Iterator<T, TReturn, TNext> {
[Symbol.iterator](): IteratorObject<T, TReturn, TNext>;
}
/**
* Defines the `TReturn` type used for built-in iterators produced by `Array`, `Map`, `Set`, and others.
* This is `undefined` when `strictBuiltInIteratorReturn` is `true`; otherwise, this is `any`.
*/
type BuiltinIteratorReturn = intrinsic;
interface ArrayIterator<T> extends IteratorObject<T, BuiltinIteratorReturn, unknown> {
[Symbol.iterator](): ArrayIterator<T>;
}
interface Array<T> {
/** Iterator */
[Symbol.iterator](): ArrayIterator<T>;
/**
* Returns an iterable of key, value pairs for every entry in the array
*/
entries(): ArrayIterator<[number, T]>;
/**
* Returns an iterable of keys in the array
*/
keys(): ArrayIterator<number>;
/**
* Returns an iterable of values in the array
*/
values(): ArrayIterator<T>;
}
interface ArrayConstructor {
/**
* Creates an array from an iterable object.
* @param iterable An iterable object to convert to an array.
*/
from<T>(iterable: Iterable<T> | ArrayLike<T>): T[];
/**
* Creates an array from an iterable object.
* @param iterable An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T, U>(iterable: Iterable<T> | ArrayLike<T>, mapfn: (v: T, k: number) => U, thisArg?: any): U[];
}
interface ReadonlyArray<T> {
/** Iterator of values in the array. */
[Symbol.iterator](): ArrayIterator<T>;
/**
* Returns an iterable of key, value pairs for every entry in the array
*/
entries(): ArrayIterator<[number, T]>;
/**
* Returns an iterable of keys in the array
*/
keys(): ArrayIterator<number>;
/**
* Returns an iterable of values in the array
*/
values(): ArrayIterator<T>;
}
interface IArguments {
/** Iterator */
[Symbol.iterator](): ArrayIterator<any>;
}
interface MapIterator<T> extends IteratorObject<T, BuiltinIteratorReturn, unknown> {
[Symbol.iterator](): MapIterator<T>;
}
interface Map<K, V> {
/** Returns an iterable of entries in the map. */
[Symbol.iterator](): MapIterator<[K, V]>;
/**
* Returns an iterable of key, value pairs for every entry in the map.
*/
entries(): MapIterator<[K, V]>;
/**
* Returns an iterable of keys in the map
*/
keys(): MapIterator<K>;
/**
* Returns an iterable of values in the map
*/
values(): MapIterator<V>;
}
interface ReadonlyMap<K, V> {
/** Returns an iterable of entries in the map. */
[Symbol.iterator](): MapIterator<[K, V]>;
/**
* Returns an iterable of key, value pairs for every entry in the map.
*/
entries(): MapIterator<[K, V]>;
/**
* Returns an iterable of keys in the map
*/
keys(): MapIterator<K>;
/**
* Returns an iterable of values in the map
*/
values(): MapIterator<V>;
}
interface MapConstructor {
new (): Map<any, any>;
new <K, V>(iterable?: Iterable<readonly [K, V]> | null): Map<K, V>;
}
interface WeakMap<K extends WeakKey, V> {}
interface WeakMapConstructor {
new <K extends WeakKey = WeakKey, V = any>(iterable?: Iterable<readonly [K, V]> | null): WeakMap<K, V>;
}
interface SetIterator<T> extends IteratorObject<T, BuiltinIteratorReturn, unknown> {
[Symbol.iterator](): SetIterator<T>;
}
interface Set<T> {
/** Iterates over values in the set. */
[Symbol.iterator](): SetIterator<T>;
/**
* Returns an iterable of [v,v] pairs for every value `v` in the set.
*/
entries(): SetIterator<[T, T]>;
/**
* Despite its name, returns an iterable of the values in the set.
*/
keys(): SetIterator<T>;
/**
* Returns an iterable of values in the set.
*/
values(): SetIterator<T>;
}
interface ReadonlySet<T> {
/** Iterates over values in the set. */
[Symbol.iterator](): SetIterator<T>;
/**
* Returns an iterable of [v,v] pairs for every value `v` in the set.
*/
entries(): SetIterator<[T, T]>;
/**
* Despite its name, returns an iterable of the values in the set.
*/
keys(): SetIterator<T>;
/**
* Returns an iterable of values in the set.
*/
values(): SetIterator<T>;
}
interface SetConstructor {
new <T>(iterable?: Iterable<T> | null): Set<T>;
}
interface WeakSet<T extends WeakKey> {}
interface WeakSetConstructor {
new <T extends WeakKey = WeakKey>(iterable: Iterable<T>): WeakSet<T>;
}
interface Promise<T> {}
interface PromiseConstructor {
/**
* Creates a Promise that is resolved with an array of results when all of the provided Promises
* resolve, or rejected when any Promise is rejected.
* @param values An iterable of Promises.
* @returns A new Promise.
*/
all<T>(values: Iterable<T | PromiseLike<T>>): Promise<Awaited<T>[]>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An iterable of Promises.
* @returns A new Promise.
*/
race<T>(values: Iterable<T | PromiseLike<T>>): Promise<Awaited<T>>;
}
interface StringIterator<T> extends IteratorObject<T, BuiltinIteratorReturn, unknown> {
[Symbol.iterator](): StringIterator<T>;
}
interface String {
/** Iterator */
[Symbol.iterator](): StringIterator<string>;
}
interface Int8Array<TArrayBuffer extends ArrayBufferLike> {
[Symbol.iterator](): ArrayIterator<number>;
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): ArrayIterator<[number, number]>;
/**
* Returns an list of keys in the array
*/
keys(): ArrayIterator<number>;
/**
* Returns an list of values in the array
*/
values(): ArrayIterator<number>;
}
interface Int8ArrayConstructor {
new (elements: Iterable<number>): Int8Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
*/
from(elements: Iterable<number>): Int8Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T>(elements: Iterable<T>, mapfn?: (v: T, k: number) => number, thisArg?: any): Int8Array<ArrayBuffer>;
}
interface Uint8Array<TArrayBuffer extends ArrayBufferLike> {
[Symbol.iterator](): ArrayIterator<number>;
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): ArrayIterator<[number, number]>;
/**
* Returns an list of keys in the array
*/
keys(): ArrayIterator<number>;
/**
* Returns an list of values in the array
*/
values(): ArrayIterator<number>;
}
interface Uint8ArrayConstructor {
new (elements: Iterable<number>): Uint8Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
*/
from(elements: Iterable<number>): Uint8Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T>(elements: Iterable<T>, mapfn?: (v: T, k: number) => number, thisArg?: any): Uint8Array<ArrayBuffer>;
}
interface Uint8ClampedArray<TArrayBuffer extends ArrayBufferLike> {
[Symbol.iterator](): ArrayIterator<number>;
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): ArrayIterator<[number, number]>;
/**
* Returns an list of keys in the array
*/
keys(): ArrayIterator<number>;
/**
* Returns an list of values in the array
*/
values(): ArrayIterator<number>;
}
interface Uint8ClampedArrayConstructor {
new (elements: Iterable<number>): Uint8ClampedArray<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
*/
from(elements: Iterable<number>): Uint8ClampedArray<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T>(elements: Iterable<T>, mapfn?: (v: T, k: number) => number, thisArg?: any): Uint8ClampedArray<ArrayBuffer>;
}
interface Int16Array<TArrayBuffer extends ArrayBufferLike> {
[Symbol.iterator](): ArrayIterator<number>;
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): ArrayIterator<[number, number]>;
/**
* Returns an list of keys in the array
*/
keys(): ArrayIterator<number>;
/**
* Returns an list of values in the array
*/
values(): ArrayIterator<number>;
}
interface Int16ArrayConstructor {
new (elements: Iterable<number>): Int16Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
*/
from(elements: Iterable<number>): Int16Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T>(elements: Iterable<T>, mapfn?: (v: T, k: number) => number, thisArg?: any): Int16Array<ArrayBuffer>;
}
interface Uint16Array<TArrayBuffer extends ArrayBufferLike> {
[Symbol.iterator](): ArrayIterator<number>;
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): ArrayIterator<[number, number]>;
/**
* Returns an list of keys in the array
*/
keys(): ArrayIterator<number>;
/**
* Returns an list of values in the array
*/
values(): ArrayIterator<number>;
}
interface Uint16ArrayConstructor {
new (elements: Iterable<number>): Uint16Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
*/
from(elements: Iterable<number>): Uint16Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T>(elements: Iterable<T>, mapfn?: (v: T, k: number) => number, thisArg?: any): Uint16Array<ArrayBuffer>;
}
interface Int32Array<TArrayBuffer extends ArrayBufferLike> {
[Symbol.iterator](): ArrayIterator<number>;
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): ArrayIterator<[number, number]>;
/**
* Returns an list of keys in the array
*/
keys(): ArrayIterator<number>;
/**
* Returns an list of values in the array
*/
values(): ArrayIterator<number>;
}
interface Int32ArrayConstructor {
new (elements: Iterable<number>): Int32Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
*/
from(elements: Iterable<number>): Int32Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T>(elements: Iterable<T>, mapfn?: (v: T, k: number) => number, thisArg?: any): Int32Array<ArrayBuffer>;
}
interface Uint32Array<TArrayBuffer extends ArrayBufferLike> {
[Symbol.iterator](): ArrayIterator<number>;
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): ArrayIterator<[number, number]>;
/**
* Returns an list of keys in the array
*/
keys(): ArrayIterator<number>;
/**
* Returns an list of values in the array
*/
values(): ArrayIterator<number>;
}
interface Uint32ArrayConstructor {
new (elements: Iterable<number>): Uint32Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
*/
from(elements: Iterable<number>): Uint32Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T>(elements: Iterable<T>, mapfn?: (v: T, k: number) => number, thisArg?: any): Uint32Array<ArrayBuffer>;
}
interface Float32Array<TArrayBuffer extends ArrayBufferLike> {
[Symbol.iterator](): ArrayIterator<number>;
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): ArrayIterator<[number, number]>;
/**
* Returns an list of keys in the array
*/
keys(): ArrayIterator<number>;
/**
* Returns an list of values in the array
*/
values(): ArrayIterator<number>;
}
interface Float32ArrayConstructor {
new (elements: Iterable<number>): Float32Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
*/
from(elements: Iterable<number>): Float32Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T>(elements: Iterable<T>, mapfn?: (v: T, k: number) => number, thisArg?: any): Float32Array<ArrayBuffer>;
}
interface Float64Array<TArrayBuffer extends ArrayBufferLike> {
[Symbol.iterator](): ArrayIterator<number>;
/**
* Returns an array of key, value pairs for every entry in the array
*/
entries(): ArrayIterator<[number, number]>;
/**
* Returns an list of keys in the array
*/
keys(): ArrayIterator<number>;
/**
* Returns an list of values in the array
*/
values(): ArrayIterator<number>;
}
interface Float64ArrayConstructor {
new (elements: Iterable<number>): Float64Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
*/
from(elements: Iterable<number>): Float64Array<ArrayBuffer>;
/**
* Creates an array from an array-like or iterable object.
* @param elements An iterable object to convert to an array.
* @param mapfn A mapping function to call on every element of the array.
* @param thisArg Value of 'this' used to invoke the mapfn.
*/
from<T>(elements: Iterable<T>, mapfn?: (v: T, k: number) => number, thisArg?: any): Float64Array<ArrayBuffer>;
}

View File

@@ -0,0 +1,79 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
interface PromiseConstructor {
/**
* A reference to the prototype.
*/
readonly prototype: Promise<any>;
/**
* Creates a new Promise.
* @param executor A callback used to initialize the promise. This callback is passed two arguments:
* a resolve callback used to resolve the promise with a value or the result of another promise,
* and a reject callback used to reject the promise with a provided reason or error.
*/
new <T>(executor: (resolve: (value: T | PromiseLike<T>) => void, reject: (reason?: any) => void) => void): Promise<T>;
/**
* Creates a Promise that is resolved with an array of results when all of the provided Promises
* resolve, or rejected when any Promise is rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
all<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]>; }>;
// see: lib.es2015.iterable.d.ts
// all<T>(values: Iterable<T | PromiseLike<T>>): Promise<Awaited<T>[]>;
/**
* Creates a Promise that is resolved or rejected when any of the provided Promises are resolved
* or rejected.
* @param values An array of Promises.
* @returns A new Promise.
*/
race<T extends readonly unknown[] | []>(values: T): Promise<Awaited<T[number]>>;
// see: lib.es2015.iterable.d.ts
// race<T>(values: Iterable<T | PromiseLike<T>>): Promise<Awaited<T>>;
/**
* Creates a new rejected promise for the provided reason.
* @param reason The reason the promise was rejected.
* @returns A new rejected Promise.
*/
reject<T = never>(reason?: any): Promise<T>;
/**
* Creates a new resolved promise.
* @returns A resolved promise.
*/
resolve(): Promise<void>;
/**
* Creates a new resolved promise for the provided value.
* @param value A promise.
* @returns A promise whose internal state matches the provided promise.
*/
resolve<T>(value: T): Promise<Awaited<T>>;
/**
* Creates a new resolved promise for the provided value.
* @param value A promise.
* @returns A promise whose internal state matches the provided promise.
*/
resolve<T>(value: T | PromiseLike<T>): Promise<Awaited<T>>;
}
declare var Promise: PromiseConstructor;

View File

@@ -0,0 +1,126 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
interface ProxyHandler<T extends object> {
/**
* A trap method for a function call.
* @param target The original callable object which is being proxied.
*/
apply?(target: T, thisArg: any, argArray: any[]): any;
/**
* A trap for the `new` operator.
* @param target The original object which is being proxied.
* @param newTarget The constructor that was originally called.
*/
construct?(target: T, argArray: any[], newTarget: Function): object;
/**
* A trap for `Object.defineProperty()`.
* @param target The original object which is being proxied.
* @returns A `Boolean` indicating whether or not the property has been defined.
*/
defineProperty?(target: T, property: string | symbol, attributes: PropertyDescriptor): boolean;
/**
* A trap for the `delete` operator.
* @param target The original object which is being proxied.
* @param p The name or `Symbol` of the property to delete.
* @returns A `Boolean` indicating whether or not the property was deleted.
*/
deleteProperty?(target: T, p: string | symbol): boolean;
/**
* A trap for getting a property value.
* @param target The original object which is being proxied.
* @param p The name or `Symbol` of the property to get.
* @param receiver The proxy or an object that inherits from the proxy.
*/
get?(target: T, p: string | symbol, receiver: any): any;
/**
* A trap for `Object.getOwnPropertyDescriptor()`.
* @param target The original object which is being proxied.
* @param p The name of the property whose description should be retrieved.
*/
getOwnPropertyDescriptor?(target: T, p: string | symbol): PropertyDescriptor | undefined;
/**
* A trap for the `[[GetPrototypeOf]]` internal method.
* @param target The original object which is being proxied.
*/
getPrototypeOf?(target: T): object | null;
/**
* A trap for the `in` operator.
* @param target The original object which is being proxied.
* @param p The name or `Symbol` of the property to check for existence.
*/
has?(target: T, p: string | symbol): boolean;
/**
* A trap for `Object.isExtensible()`.
* @param target The original object which is being proxied.
*/
isExtensible?(target: T): boolean;
/**
* A trap for `Reflect.ownKeys()`.
* @param target The original object which is being proxied.
*/
ownKeys?(target: T): ArrayLike<string | symbol>;
/**
* A trap for `Object.preventExtensions()`.
* @param target The original object which is being proxied.
*/
preventExtensions?(target: T): boolean;
/**
* A trap for setting a property value.
* @param target The original object which is being proxied.
* @param p The name or `Symbol` of the property to set.
* @param receiver The object to which the assignment was originally directed.
* @returns A `Boolean` indicating whether or not the property was set.
*/
set?(target: T, p: string | symbol, newValue: any, receiver: any): boolean;
/**
* A trap for `Object.setPrototypeOf()`.
* @param target The original object which is being proxied.
* @param newPrototype The object's new prototype or `null`.
*/
setPrototypeOf?(target: T, v: object | null): boolean;
}
interface ProxyConstructor {
/**
* Creates a revocable Proxy object.
* @param target A target object to wrap with Proxy.
* @param handler An object whose properties define the behavior of Proxy when an operation is attempted on it.
*/
revocable<T extends object>(target: T, handler: ProxyHandler<T>): { proxy: T; revoke: () => void; };
/**
* Creates a Proxy object. The Proxy object allows you to create an object that can be used in place of the
* original object, but which may redefine fundamental Object operations like getting, setting, and defining
* properties. Proxy objects are commonly used to log property accesses, validate, format, or sanitize inputs.
* @param target A target object to wrap with Proxy.
* @param handler An object whose properties define the behavior of Proxy when an operation is attempted on it.
*/
new <T extends object>(target: T, handler: ProxyHandler<T>): T;
}
declare var Proxy: ProxyConstructor;

View File

@@ -0,0 +1,142 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
declare namespace Reflect {
/**
* Calls the function with the specified object as the this value
* and the elements of specified array as the arguments.
* @param target The function to call.
* @param thisArgument The object to be used as the this object.
* @param argumentsList An array of argument values to be passed to the function.
*/
function apply<T, A extends readonly any[], R>(
target: (this: T, ...args: A) => R,
thisArgument: T,
argumentsList: Readonly<A>,
): R;
function apply(target: Function, thisArgument: any, argumentsList: ArrayLike<any>): any;
/**
* Constructs the target with the elements of specified array as the arguments
* and the specified constructor as the `new.target` value.
* @param target The constructor to invoke.
* @param argumentsList An array of argument values to be passed to the constructor.
* @param newTarget The constructor to be used as the `new.target` object.
*/
function construct<A extends readonly any[], R>(
target: new (...args: A) => R,
argumentsList: Readonly<A>,
newTarget?: new (...args: any) => any,
): R;
function construct(target: Function, argumentsList: ArrayLike<any>, newTarget?: Function): any;
/**
* Adds a property to an object, or modifies attributes of an existing property.
* @param target Object on which to add or modify the property. This can be a native JavaScript object
* (that is, a user-defined object or a built in object) or a DOM object.
* @param propertyKey The property name.
* @param attributes Descriptor for the property. It can be for a data property or an accessor property.
*/
function defineProperty(target: object, propertyKey: PropertyKey, attributes: PropertyDescriptor & ThisType<any>): boolean;
/**
* Removes a property from an object, equivalent to `delete target[propertyKey]`,
* except it won't throw if `target[propertyKey]` is non-configurable.
* @param target Object from which to remove the own property.
* @param propertyKey The property name.
*/
function deleteProperty(target: object, propertyKey: PropertyKey): boolean;
/**
* Gets the property of target, equivalent to `target[propertyKey]` when `receiver === target`.
* @param target Object that contains the property on itself or in its prototype chain.
* @param propertyKey The property name.
* @param receiver The reference to use as the `this` value in the getter function,
* if `target[propertyKey]` is an accessor property.
*/
function get<T extends object, P extends PropertyKey>(
target: T,
propertyKey: P,
receiver?: unknown,
): P extends keyof T ? T[P] : any;
/**
* Gets the own property descriptor of the specified object.
* An own property descriptor is one that is defined directly on the object and is not inherited from the object's prototype.
* @param target Object that contains the property.
* @param propertyKey The property name.
*/
function getOwnPropertyDescriptor<T extends object, P extends PropertyKey>(
target: T,
propertyKey: P,
): TypedPropertyDescriptor<P extends keyof T ? T[P] : any> | undefined;
/**
* Returns the prototype of an object.
* @param target The object that references the prototype.
*/
function getPrototypeOf(target: object): object | null;
/**
* Equivalent to `propertyKey in target`.
* @param target Object that contains the property on itself or in its prototype chain.
* @param propertyKey Name of the property.
*/
function has(target: object, propertyKey: PropertyKey): boolean;
/**
* Returns a value that indicates whether new properties can be added to an object.
* @param target Object to test.
*/
function isExtensible(target: object): boolean;
/**
* Returns the string and symbol keys of the own properties of an object. The own properties of an object
* are those that are defined directly on that object, and are not inherited from the object's prototype.
* @param target Object that contains the own properties.
*/
function ownKeys(target: object): (string | symbol)[];
/**
* Prevents the addition of new properties to an object.
* @param target Object to make non-extensible.
* @return Whether the object has been made non-extensible.
*/
function preventExtensions(target: object): boolean;
/**
* Sets the property of target, equivalent to `target[propertyKey] = value` when `receiver === target`.
* @param target Object that contains the property on itself or in its prototype chain.
* @param propertyKey Name of the property.
* @param receiver The reference to use as the `this` value in the setter function,
* if `target[propertyKey]` is an accessor property.
*/
function set<T extends object, P extends PropertyKey>(
target: T,
propertyKey: P,
value: P extends keyof T ? T[P] : any,
receiver?: any,
): boolean;
function set(target: object, propertyKey: PropertyKey, value: any, receiver?: any): boolean;
/**
* Sets the prototype of a specified object o to object proto or null.
* @param target The object to change its prototype.
* @param proto The value of the new prototype or null.
* @return Whether setting the prototype was successful.
*/
function setPrototypeOf(target: object, proto: object | null): boolean;
}

View File

@@ -0,0 +1,44 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
interface SymbolConstructor {
/**
* A reference to the prototype.
*/
readonly prototype: Symbol;
/**
* Returns a new unique Symbol value.
* @param description Description of the new Symbol object.
*/
(description?: string | number): symbol;
/**
* Returns a Symbol object from the global symbol registry matching the given key if found.
* Otherwise, returns a new symbol with this key.
* @param key key to search for.
*/
for(key: string): symbol;
/**
* Returns a key from the global symbol registry matching the given Symbol if found.
* Otherwise, returns undefined.
* @param sym Symbol to find the key for.
*/
keyFor(sym: symbol): string | undefined;
}
declare var Symbol: SymbolConstructor;

View File

@@ -0,0 +1,324 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference lib="es2015.symbol" />
interface SymbolConstructor {
/**
* A method that determines if a constructor object recognizes an object as one of the
* constructors instances. Called by the semantics of the instanceof operator.
*/
readonly hasInstance: unique symbol;
/**
* A Boolean value that if true indicates that an object should flatten to its array elements
* by Array.prototype.concat.
*/
readonly isConcatSpreadable: unique symbol;
/**
* A regular expression method that matches the regular expression against a string. Called
* by the String.prototype.match method.
*/
readonly match: unique symbol;
/**
* A regular expression method that replaces matched substrings of a string. Called by the
* String.prototype.replace method.
*/
readonly replace: unique symbol;
/**
* A regular expression method that returns the index within a string that matches the
* regular expression. Called by the String.prototype.search method.
*/
readonly search: unique symbol;
/**
* A function valued property that is the constructor function that is used to create
* derived objects.
*/
readonly species: unique symbol;
/**
* A regular expression method that splits a string at the indices that match the regular
* expression. Called by the String.prototype.split method.
*/
readonly split: unique symbol;
/**
* A method that converts an object to a corresponding primitive value.
* Called by the ToPrimitive abstract operation.
*/
readonly toPrimitive: unique symbol;
/**
* A String value that is used in the creation of the default string description of an object.
* Called by the built-in method Object.prototype.toString.
*/
readonly toStringTag: unique symbol;
/**
* An Object whose truthy properties are properties that are excluded from the 'with'
* environment bindings of the associated objects.
*/
readonly unscopables: unique symbol;
}
interface Symbol {
/**
* Converts a Symbol object to a symbol.
*/
[Symbol.toPrimitive](hint: string): symbol;
readonly [Symbol.toStringTag]: string;
}
interface Array<T> {
/**
* Is an object whose properties have the value 'true'
* when they will be absent when used in a 'with' statement.
*/
readonly [Symbol.unscopables]: {
[K in keyof any[]]?: boolean;
};
}
interface ReadonlyArray<T> {
/**
* Is an object whose properties have the value 'true'
* when they will be absent when used in a 'with' statement.
*/
readonly [Symbol.unscopables]: {
[K in keyof readonly any[]]?: boolean;
};
}
interface Date {
/**
* Converts a Date object to a string.
*/
[Symbol.toPrimitive](hint: "default"): string;
/**
* Converts a Date object to a string.
*/
[Symbol.toPrimitive](hint: "string"): string;
/**
* Converts a Date object to a number.
*/
[Symbol.toPrimitive](hint: "number"): number;
/**
* Converts a Date object to a string or number.
*
* @param hint The strings "number", "string", or "default" to specify what primitive to return.
*
* @throws {TypeError} If 'hint' was given something other than "number", "string", or "default".
* @returns A number if 'hint' was "number", a string if 'hint' was "string" or "default".
*/
[Symbol.toPrimitive](hint: string): string | number;
}
interface Map<K, V> {
readonly [Symbol.toStringTag]: string;
}
interface WeakMap<K extends WeakKey, V> {
readonly [Symbol.toStringTag]: string;
}
interface Set<T> {
readonly [Symbol.toStringTag]: string;
}
interface WeakSet<T extends WeakKey> {
readonly [Symbol.toStringTag]: string;
}
interface JSON {
readonly [Symbol.toStringTag]: string;
}
interface Function {
/**
* Determines whether the given value inherits from this function if this function was used
* as a constructor function.
*
* A constructor function can control which objects are recognized as its instances by
* 'instanceof' by overriding this method.
*/
[Symbol.hasInstance](value: any): boolean;
}
interface GeneratorFunction {
readonly [Symbol.toStringTag]: string;
}
interface Math {
readonly [Symbol.toStringTag]: string;
}
interface Promise<T> {
readonly [Symbol.toStringTag]: string;
}
interface PromiseConstructor {
readonly [Symbol.species]: PromiseConstructor;
}
interface RegExp {
/**
* Matches a string with this regular expression, and returns an array containing the results of
* that search.
* @param string A string to search within.
*/
[Symbol.match](string: string): RegExpMatchArray | null;
/**
* Replaces text in a string, using this regular expression.
* @param string A String object or string literal whose contents matching against
* this regular expression will be replaced
* @param replaceValue A String object or string literal containing the text to replace for every
* successful match of this regular expression.
*/
[Symbol.replace](string: string, replaceValue: string): string;
/**
* Replaces text in a string, using this regular expression.
* @param string A String object or string literal whose contents matching against
* this regular expression will be replaced
* @param replacer A function that returns the replacement text.
*/
[Symbol.replace](string: string, replacer: (substring: string, ...args: any[]) => string): string;
/**
* Finds the position beginning first substring match in a regular expression search
* using this regular expression.
*
* @param string The string to search within.
*/
[Symbol.search](string: string): number;
/**
* Returns an array of substrings that were delimited by strings in the original input that
* match against this regular expression.
*
* If the regular expression contains capturing parentheses, then each time this
* regular expression matches, the results (including any undefined results) of the
* capturing parentheses are spliced.
*
* @param string string value to split
* @param limit if not undefined, the output array is truncated so that it contains no more
* than 'limit' elements.
*/
[Symbol.split](string: string, limit?: number): string[];
}
interface RegExpConstructor {
readonly [Symbol.species]: RegExpConstructor;
}
interface String {
/**
* Matches a string or an object that supports being matched against, and returns an array
* containing the results of that search, or null if no matches are found.
* @param matcher An object that supports being matched against.
*/
match(matcher: { [Symbol.match](string: string): RegExpMatchArray | null; }): RegExpMatchArray | null;
/**
* Passes a string and {@linkcode replaceValue} to the `[Symbol.replace]` method on {@linkcode searchValue}. This method is expected to implement its own replacement algorithm.
* @param searchValue An object that supports searching for and replacing matches within a string.
* @param replaceValue The replacement text.
*/
replace(searchValue: { [Symbol.replace](string: string, replaceValue: string): string; }, replaceValue: string): string;
/**
* Replaces text in a string, using an object that supports replacement within a string.
* @param searchValue A object can search for and replace matches within a string.
* @param replacer A function that returns the replacement text.
*/
replace(searchValue: { [Symbol.replace](string: string, replacer: (substring: string, ...args: any[]) => string): string; }, replacer: (substring: string, ...args: any[]) => string): string;
/**
* Finds the first substring match in a regular expression search.
* @param searcher An object which supports searching within a string.
*/
search(searcher: { [Symbol.search](string: string): number; }): number;
/**
* Split a string into substrings using the specified separator and return them as an array.
* @param splitter An object that can split a string.
* @param limit A value used to limit the number of elements returned in the array.
*/
split(splitter: { [Symbol.split](string: string, limit?: number): string[]; }, limit?: number): string[];
}
interface ArrayBuffer {
readonly [Symbol.toStringTag]: "ArrayBuffer";
}
interface DataView<TArrayBuffer extends ArrayBufferLike> {
readonly [Symbol.toStringTag]: string;
}
interface Int8Array<TArrayBuffer extends ArrayBufferLike> {
readonly [Symbol.toStringTag]: "Int8Array";
}
interface Uint8Array<TArrayBuffer extends ArrayBufferLike> {
readonly [Symbol.toStringTag]: "Uint8Array";
}
interface Uint8ClampedArray<TArrayBuffer extends ArrayBufferLike> {
readonly [Symbol.toStringTag]: "Uint8ClampedArray";
}
interface Int16Array<TArrayBuffer extends ArrayBufferLike> {
readonly [Symbol.toStringTag]: "Int16Array";
}
interface Uint16Array<TArrayBuffer extends ArrayBufferLike> {
readonly [Symbol.toStringTag]: "Uint16Array";
}
interface Int32Array<TArrayBuffer extends ArrayBufferLike> {
readonly [Symbol.toStringTag]: "Int32Array";
}
interface Uint32Array<TArrayBuffer extends ArrayBufferLike> {
readonly [Symbol.toStringTag]: "Uint32Array";
}
interface Float32Array<TArrayBuffer extends ArrayBufferLike> {
readonly [Symbol.toStringTag]: "Float32Array";
}
interface Float64Array<TArrayBuffer extends ArrayBufferLike> {
readonly [Symbol.toStringTag]: "Float64Array";
}
interface ArrayConstructor {
readonly [Symbol.species]: ArrayConstructor;
}
interface MapConstructor {
readonly [Symbol.species]: MapConstructor;
}
interface SetConstructor {
readonly [Symbol.species]: SetConstructor;
}
interface ArrayBufferConstructor {
readonly [Symbol.species]: ArrayBufferConstructor;
}

View File

@@ -0,0 +1,114 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
interface Array<T> {
/**
* Determines whether an array includes a certain element, returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which to begin searching for searchElement.
*/
includes(searchElement: T, fromIndex?: number): boolean;
}
interface ReadonlyArray<T> {
/**
* Determines whether an array includes a certain element, returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which to begin searching for searchElement.
*/
includes(searchElement: T, fromIndex?: number): boolean;
}
interface Int8Array<TArrayBuffer extends ArrayBufferLike> {
/**
* Determines whether an array includes a certain element, returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which to begin searching for searchElement.
*/
includes(searchElement: number, fromIndex?: number): boolean;
}
interface Uint8Array<TArrayBuffer extends ArrayBufferLike> {
/**
* Determines whether an array includes a certain element, returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which to begin searching for searchElement.
*/
includes(searchElement: number, fromIndex?: number): boolean;
}
interface Uint8ClampedArray<TArrayBuffer extends ArrayBufferLike> {
/**
* Determines whether an array includes a certain element, returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which to begin searching for searchElement.
*/
includes(searchElement: number, fromIndex?: number): boolean;
}
interface Int16Array<TArrayBuffer extends ArrayBufferLike> {
/**
* Determines whether an array includes a certain element, returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which to begin searching for searchElement.
*/
includes(searchElement: number, fromIndex?: number): boolean;
}
interface Uint16Array<TArrayBuffer extends ArrayBufferLike> {
/**
* Determines whether an array includes a certain element, returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which to begin searching for searchElement.
*/
includes(searchElement: number, fromIndex?: number): boolean;
}
interface Int32Array<TArrayBuffer extends ArrayBufferLike> {
/**
* Determines whether an array includes a certain element, returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which to begin searching for searchElement.
*/
includes(searchElement: number, fromIndex?: number): boolean;
}
interface Uint32Array<TArrayBuffer extends ArrayBufferLike> {
/**
* Determines whether an array includes a certain element, returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which to begin searching for searchElement.
*/
includes(searchElement: number, fromIndex?: number): boolean;
}
interface Float32Array<TArrayBuffer extends ArrayBufferLike> {
/**
* Determines whether an array includes a certain element, returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which to begin searching for searchElement.
*/
includes(searchElement: number, fromIndex?: number): boolean;
}
interface Float64Array<TArrayBuffer extends ArrayBufferLike> {
/**
* Determines whether an array includes a certain element, returning true or false as appropriate.
* @param searchElement The element to search for.
* @param fromIndex The position in this array at which to begin searching for searchElement.
*/
includes(searchElement: number, fromIndex?: number): boolean;
}

View File

@@ -0,0 +1,19 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference lib="es2015" />
/// <reference lib="es2016.array.include" />
/// <reference lib="es2016.intl" />

View File

@@ -0,0 +1,21 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference lib="es2016" />
/// <reference lib="dom" />
/// <reference lib="webworker.importscripts" />
/// <reference lib="scripthost" />
/// <reference lib="dom.iterable" />

View File

@@ -0,0 +1,29 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
declare namespace Intl {
/**
* The `Intl.getCanonicalLocales()` method returns an array containing
* the canonical locale names. Duplicates will be omitted and elements
* will be validated as structurally valid language tags.
*
* [MDN](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Intl/getCanonicalLocales)
*
* @param locale A list of String values for which to get the canonical locale names
* @returns An array containing the canonical and validated locale names.
*/
function getCanonicalLocales(locale?: string | readonly string[]): string[];
}

View File

@@ -0,0 +1,19 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
interface ArrayBufferConstructor {
new (): ArrayBuffer;
}

View File

@@ -0,0 +1,24 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference lib="es2016" />
/// <reference lib="es2017.arraybuffer" />
/// <reference lib="es2017.date" />
/// <reference lib="es2017.intl" />
/// <reference lib="es2017.object" />
/// <reference lib="es2017.sharedmemory" />
/// <reference lib="es2017.string" />
/// <reference lib="es2017.typedarrays" />

View File

@@ -0,0 +1,29 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
interface DateConstructor {
/**
* Returns the number of milliseconds between midnight, January 1, 1970 Universal Coordinated Time (UTC) (or GMT) and the specified date.
* @param year The full year designation is required for cross-century date accuracy. If year is between 0 and 99 is used, then year is assumed to be 1900 + year.
* @param monthIndex The month as a number between 0 and 11 (January to December).
* @param date The date as a number between 1 and 31.
* @param hours Must be supplied if minutes is supplied. A number from 0 to 23 (midnight to 11pm) that specifies the hour.
* @param minutes Must be supplied if seconds is supplied. A number from 0 to 59 that specifies the minutes.
* @param seconds Must be supplied if milliseconds is supplied. A number from 0 to 59 that specifies the seconds.
* @param ms A number from 0 to 999 that specifies the milliseconds.
*/
UTC(year: number, monthIndex?: number, date?: number, hours?: number, minutes?: number, seconds?: number, ms?: number): number;
}

View File

@@ -0,0 +1,21 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference lib="es2017" />
/// <reference lib="dom" />
/// <reference lib="webworker.importscripts" />
/// <reference lib="scripthost" />
/// <reference lib="dom.iterable" />

View File

@@ -0,0 +1,42 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABILITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
declare namespace Intl {
interface DateTimeFormatPartTypesRegistry {
day: any;
dayPeriod: any;
era: any;
hour: any;
literal: any;
minute: any;
month: any;
second: any;
timeZoneName: any;
weekday: any;
year: any;
}
type DateTimeFormatPartTypes = keyof DateTimeFormatPartTypesRegistry;
interface DateTimeFormatPart {
type: DateTimeFormatPartTypes;
value: string;
}
interface DateTimeFormat {
formatToParts(date?: Date | number): DateTimeFormatPart[];
}
}

Some files were not shown because too many files have changed in this diff Show More