typenative 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,157 @@
1
+ // Type-checking for the self-hosted compiler (`tsparser --check`): the same
2
+ // program the Node CLI checks with the typescript package (src/typecheck-node.ts),
3
+ // built with typescript-go. Prints the errors as a JSON array.
4
+ package main
5
+
6
+ import (
7
+ "context"
8
+ "encoding/json"
9
+ "fmt"
10
+ "os"
11
+
12
+ "github.com/buke/typescript-go-internal/pkg/ast"
13
+ "github.com/buke/typescript-go-internal/pkg/bundled"
14
+ "github.com/buke/typescript-go-internal/pkg/compiler"
15
+ "github.com/buke/typescript-go-internal/pkg/core"
16
+ "github.com/buke/typescript-go-internal/pkg/diagnostics"
17
+ "github.com/buke/typescript-go-internal/pkg/diagnosticwriter"
18
+ "github.com/buke/typescript-go-internal/pkg/locale"
19
+ "github.com/buke/typescript-go-internal/pkg/scanner"
20
+ "github.com/buke/typescript-go-internal/pkg/tsoptions"
21
+ "github.com/buke/typescript-go-internal/pkg/tspath"
22
+ "github.com/buke/typescript-go-internal/pkg/vfs"
23
+ "github.com/buke/typescript-go-internal/pkg/vfs/osvfs"
24
+ )
25
+
26
+ // checkDiagnostic is one error, in the shape src/parse-native.ts reads
27
+ // (file is absolute; line and column are 1-based, 0 for global errors)
28
+ type checkDiagnostic struct {
29
+ File string `json:"file"`
30
+ Line int `json:"line"`
31
+ Column int `json:"column"`
32
+ Code int32 `json:"code"`
33
+ Message string `json:"message"`
34
+ }
35
+
36
+ type configHost struct {
37
+ fs vfs.FS
38
+ cwd string
39
+ }
40
+
41
+ func (h configHost) FS() vfs.FS { return h.fs }
42
+ func (h configHost) GetCurrentDirectory() string { return h.cwd }
43
+
44
+ // The bundled declarations that replace the default lib (types/ in the package)
45
+ var typenativeTypes = []string{"typenative.d.ts", "typenative-go.d.ts", "typenative-npm.d.ts"}
46
+
47
+ // TS18003: "No inputs were found in config file" (only the entry is checked)
48
+ const noInputsFound = 18003
49
+
50
+ // runCheck type-checks entry with the nearest tsconfig.json's options (or
51
+ // TypeNative's defaults) and prints the errors. The program's roots are the
52
+ // entry plus the config's .d.ts files, so other entry points in the same
53
+ // project don't redeclare each other's globals.
54
+ func runCheck(entry string, typesDir string) {
55
+ wd, err := os.Getwd()
56
+ if err != nil {
57
+ fmt.Fprintln(os.Stderr, "getwd:", err)
58
+ os.Exit(1)
59
+ }
60
+ cwd := tspath.NormalizePath(wd)
61
+ fs := bundled.WrapFS(osvfs.FS())
62
+ entryPath := tspath.GetNormalizedAbsolutePath(entry, cwd)
63
+ roots := []string{entryPath}
64
+ out := []checkDiagnostic{}
65
+
66
+ var options *core.CompilerOptions
67
+ if configPath := findConfigFile(fs, tspath.GetDirectoryPath(entryPath)); configPath != "" {
68
+ parsed, errs := tsoptions.GetParsedCommandLineOfConfigFile(configPath, &core.CompilerOptions{}, nil, configHost{fs, cwd}, nil)
69
+ for _, d := range errs {
70
+ out = append(out, toCheckDiagnostic(d))
71
+ }
72
+ if parsed == nil {
73
+ printDiagnostics(out)
74
+ return
75
+ }
76
+ for _, d := range parsed.Errors {
77
+ if d.Code() != noInputsFound {
78
+ out = append(out, toCheckDiagnostic(d))
79
+ }
80
+ }
81
+ options = parsed.CompilerOptions()
82
+ for _, name := range parsed.FileNames() {
83
+ if tspath.IsDeclarationFileName(name) && name != entryPath {
84
+ roots = append(roots, name)
85
+ }
86
+ }
87
+ } else {
88
+ options = &core.CompilerOptions{
89
+ Target: core.ScriptTargetES2020,
90
+ Module: core.ModuleKindESNext,
91
+ ModuleResolution: core.ModuleResolutionKindBundler,
92
+ Lib: []string{},
93
+ Types: []string{},
94
+ Strict: core.TSTrue,
95
+ NoImplicitAny: core.TSTrue,
96
+ AllowSyntheticDefaultImports: core.TSTrue,
97
+ AllowImportingTsExtensions: core.TSTrue,
98
+ Jsx: core.JsxEmitPreserve,
99
+ SkipLibCheck: core.TSTrue,
100
+ }
101
+ typesPath := tspath.GetNormalizedAbsolutePath(typesDir, cwd)
102
+ for _, name := range typenativeTypes {
103
+ roots = append(roots, tspath.CombinePaths(typesPath, name))
104
+ }
105
+ }
106
+ options.NoEmit = core.TSTrue
107
+
108
+ config := tsoptions.NewParsedCommandLine(options, roots, tspath.ComparePathsOptions{
109
+ UseCaseSensitiveFileNames: fs.UseCaseSensitiveFileNames(),
110
+ CurrentDirectory: cwd,
111
+ })
112
+ host := compiler.NewCompilerHost(cwd, fs, bundled.LibPath(), nil, nil)
113
+ program := compiler.NewProgram(compiler.ProgramOptions{Config: config, Host: host})
114
+ ctx := context.Background()
115
+ for _, d := range compiler.GetDiagnosticsOfAnyProgram(ctx, program, nil, false, program.GetBindDiagnostics, program.GetSemanticDiagnostics) {
116
+ if d.Category() == diagnostics.CategoryError {
117
+ out = append(out, toCheckDiagnostic(d))
118
+ }
119
+ }
120
+ printDiagnostics(out)
121
+ }
122
+
123
+ // findConfigFile walks up from dir to the nearest tsconfig.json
124
+ func findConfigFile(fs vfs.FS, dir string) string {
125
+ for {
126
+ candidate := tspath.CombinePaths(dir, "tsconfig.json")
127
+ if fs.FileExists(candidate) {
128
+ return candidate
129
+ }
130
+ parent := tspath.GetDirectoryPath(dir)
131
+ if parent == dir {
132
+ return ""
133
+ }
134
+ dir = parent
135
+ }
136
+ }
137
+
138
+ func toCheckDiagnostic(d *ast.Diagnostic) checkDiagnostic {
139
+ out := checkDiagnostic{
140
+ Code: d.Code(),
141
+ Message: diagnosticwriter.FlattenDiagnosticMessage(&diagnosticwriter.ASTDiagnostic{Diagnostic: d}, "\n", locale.Default),
142
+ }
143
+ if file := d.File(); file != nil {
144
+ line, character := scanner.GetECMALineAndUTF16CharacterOfPosition(file, d.Pos())
145
+ out.File = file.FileName()
146
+ out.Line = line + 1
147
+ out.Column = int(character) + 1
148
+ }
149
+ return out
150
+ }
151
+
152
+ func printDiagnostics(out []checkDiagnostic) {
153
+ if err := json.NewEncoder(os.Stdout).Encode(out); err != nil {
154
+ fmt.Fprintln(os.Stderr, "encode:", err)
155
+ os.Exit(1)
156
+ }
157
+ }
package/tsparser/main.go CHANGED
@@ -6,6 +6,8 @@
6
6
  // the same transpiler code can consume ASTs from either source.
7
7
  //
8
8
  // Usage: tsparser < file.ts > ast.json
9
+ //
10
+ // tsparser --check <file.ts> <types dir> type-checks a program (see check.go)
9
11
  package main
10
12
 
11
13
  import (
@@ -25,7 +27,18 @@ import (
25
27
  // The parsed source, for template raw text
26
28
  var sourceText string
27
29
 
30
+ // The parsed file, for node positions
31
+ var parsedFile *ast.SourceFile
32
+
28
33
  func main() {
34
+ if len(os.Args) > 1 && os.Args[1] == "--check" {
35
+ if len(os.Args) < 4 {
36
+ fmt.Fprintln(os.Stderr, "usage: tsparser --check <file.ts> <types dir>")
37
+ os.Exit(2)
38
+ }
39
+ runCheck(os.Args[2], os.Args[3])
40
+ return
41
+ }
29
42
  sourceBytes, err := io.ReadAll(os.Stdin)
30
43
  if err != nil {
31
44
  fmt.Fprintln(os.Stderr, "read stdin:", err)
@@ -44,6 +57,7 @@ func main() {
44
57
  string(sourceBytes),
45
58
  scriptKind,
46
59
  )
60
+ parsedFile = sourceFile
47
61
  out := serializeNode(sourceFile.AsNode())
48
62
  enc := json.NewEncoder(os.Stdout)
49
63
  enc.SetIndent("", " ")
@@ -126,6 +140,7 @@ func serializeNode(n *ast.Node) any {
126
140
  inProgress[n] = true
127
141
  defer delete(inProgress, n)
128
142
  obj := map[string]any{"kind": strings.TrimPrefix(n.Kind.String(), "Kind")}
143
+ setPosition(n, obj)
129
144
  // SourceFile bookkeeping fields are not part of the language AST
130
145
  if n.Kind == ast.KindSourceFile {
131
146
  statements := []any{}
@@ -166,7 +181,9 @@ func serializeNode(n *ast.Node) any {
166
181
  // BindingElement; the TypeScript API exposes them as OmittedExpression
167
182
  if n.Kind == ast.KindBindingElement {
168
183
  if be := n.AsBindingElement(); be != nil && be.Name() == nil && be.DotDotDotToken == nil {
169
- return map[string]any{"kind": "OmittedExpression"}
184
+ hole := map[string]any{"kind": "OmittedExpression"}
185
+ setPosition(n, hole)
186
+ return hole
170
187
  }
171
188
  }
172
189
 
@@ -194,6 +211,18 @@ func serializeNode(n *ast.Node) any {
194
211
  return obj
195
212
  }
196
213
 
214
+ // setPosition records the 1-based line and column of the node's first token
215
+ // (columns count UTF-16 units, as in the TypeScript API); the transpiler maps
216
+ // Go positions back to them
217
+ func setPosition(n *ast.Node, obj map[string]any) {
218
+ if n.Kind == ast.KindSourceFile || n.Pos() < 0 || parsedFile == nil {
219
+ return
220
+ }
221
+ line, character := scanner.GetECMALineAndUTF16CharacterOfPosition(parsedFile, scanner.GetTokenPosOfNode(n, parsedFile, false))
222
+ obj["line"] = line + 1
223
+ obj["column"] = int(character) + 1
224
+ }
225
+
197
226
  // collectFields serializes a typed data struct's fields into obj, recursing
198
227
  // into embedded base structs (DeclarationBase, FunctionLikeBase, ...) so their
199
228
  // fields are hoisted like the TypeScript API exposes them.
@@ -2463,11 +2463,14 @@ declare module 'node:sqlite' {
2463
2463
  useBigIntArguments?: boolean;
2464
2464
  varargs?: boolean;
2465
2465
  }
2466
+ // Callbacks get each SQL argument converted to the type their parameter
2467
+ // declares ((x: number) => …), so their arguments are typed `any` here
2468
+ // rather than SQLOutputValue as in @types/node
2466
2469
  interface AggregateOptions<T extends SQLInputValue = SQLInputValue> extends FunctionOptions {
2467
2470
  start: T | (() => T);
2468
- step: (accumulator: T, ...args: SQLOutputValue[]) => T;
2471
+ step: (accumulator: T, ...args: any[]) => T;
2469
2472
  result?: (accumulator: T) => SQLInputValue;
2470
- inverse?: (accumulator: T, ...args: SQLOutputValue[]) => T;
2473
+ inverse?: (accumulator: T, ...args: any[]) => T;
2471
2474
  }
2472
2475
  interface PrepareOptions {
2473
2476
  readBigInts?: boolean;
@@ -2488,8 +2491,8 @@ declare module 'node:sqlite' {
2488
2491
  exec(sql: string): void;
2489
2492
  prepare(sql: string, options?: PrepareOptions): StatementSync;
2490
2493
  location(dbName?: string): string | null;
2491
- function(name: string, options: FunctionOptions, fn: (...args: SQLOutputValue[]) => SQLInputValue): void;
2492
- function(name: string, fn: (...args: SQLOutputValue[]) => SQLInputValue): void;
2494
+ function(name: string, options: FunctionOptions, fn: (...args: any[]) => SQLInputValue): void;
2495
+ function(name: string, fn: (...args: any[]) => SQLInputValue): void;
2493
2496
  aggregate(name: string, options: AggregateOptions): void;
2494
2497
  aggregate<T extends SQLInputValue>(name: string, options: AggregateOptions<T>): void;
2495
2498
  setAuthorizer(