pytscheck 0.1.0__py3-none-win_arm64.whl
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.
- pytscheck/__init__.py +89 -0
- pytscheck/__main__.py +221 -0
- pytscheck/adapters/__init__.py +38 -0
- pytscheck/adapters/base.py +33 -0
- pytscheck/adapters/compatibility.py +28 -0
- pytscheck/adapters/context.py +38 -0
- pytscheck/adapters/discovery.py +43 -0
- pytscheck/adapters/errors.py +25 -0
- pytscheck/adapters/project.py +431 -0
- pytscheck/adapters/registry.py +44 -0
- pytscheck/adapters/source_maps.py +255 -0
- pytscheck/adapters/sources.py +100 -0
- pytscheck/api.py +279 -0
- pytscheck/bin/manifest.json +14 -0
- pytscheck/bin/windows-arm64/LICENSE +55 -0
- pytscheck/bin/windows-arm64/NOTICE.txt +436 -0
- pytscheck/bin/windows-arm64/README.md +3 -0
- pytscheck/bin/windows-arm64/lib/lib.d.ts +20 -0
- pytscheck/bin/windows-arm64/lib/lib.decorators.d.ts +382 -0
- pytscheck/bin/windows-arm64/lib/lib.decorators.legacy.d.ts +20 -0
- pytscheck/bin/windows-arm64/lib/lib.dom.asynciterable.d.ts +18 -0
- pytscheck/bin/windows-arm64/lib/lib.dom.d.ts +45125 -0
- pytscheck/bin/windows-arm64/lib/lib.dom.iterable.d.ts +18 -0
- pytscheck/bin/windows-arm64/lib/lib.es2015.collection.d.ts +159 -0
- pytscheck/bin/windows-arm64/lib/lib.es2015.core.d.ts +595 -0
- pytscheck/bin/windows-arm64/lib/lib.es2015.d.ts +26 -0
- pytscheck/bin/windows-arm64/lib/lib.es2015.generator.d.ts +75 -0
- pytscheck/bin/windows-arm64/lib/lib.es2015.iterable.d.ts +603 -0
- pytscheck/bin/windows-arm64/lib/lib.es2015.promise.d.ts +79 -0
- pytscheck/bin/windows-arm64/lib/lib.es2015.proxy.d.ts +126 -0
- pytscheck/bin/windows-arm64/lib/lib.es2015.reflect.d.ts +142 -0
- pytscheck/bin/windows-arm64/lib/lib.es2015.symbol.d.ts +44 -0
- pytscheck/bin/windows-arm64/lib/lib.es2015.symbol.wellknown.d.ts +324 -0
- pytscheck/bin/windows-arm64/lib/lib.es2016.array.include.d.ts +114 -0
- pytscheck/bin/windows-arm64/lib/lib.es2016.d.ts +19 -0
- pytscheck/bin/windows-arm64/lib/lib.es2016.full.d.ts +21 -0
- pytscheck/bin/windows-arm64/lib/lib.es2016.intl.d.ts +29 -0
- pytscheck/bin/windows-arm64/lib/lib.es2017.arraybuffer.d.ts +19 -0
- pytscheck/bin/windows-arm64/lib/lib.es2017.d.ts +24 -0
- pytscheck/bin/windows-arm64/lib/lib.es2017.date.d.ts +29 -0
- pytscheck/bin/windows-arm64/lib/lib.es2017.full.d.ts +21 -0
- pytscheck/bin/windows-arm64/lib/lib.es2017.intl.d.ts +42 -0
- pytscheck/bin/windows-arm64/lib/lib.es2017.object.d.ts +47 -0
- pytscheck/bin/windows-arm64/lib/lib.es2017.sharedmemory.d.ts +133 -0
- pytscheck/bin/windows-arm64/lib/lib.es2017.string.d.ts +47 -0
- pytscheck/bin/windows-arm64/lib/lib.es2017.typedarrays.d.ts +51 -0
- pytscheck/bin/windows-arm64/lib/lib.es2018.asyncgenerator.d.ts +75 -0
- pytscheck/bin/windows-arm64/lib/lib.es2018.asynciterable.d.ts +51 -0
- pytscheck/bin/windows-arm64/lib/lib.es2018.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.es2018.full.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.es2018.intl.d.ts +81 -0
- pytscheck/bin/windows-arm64/lib/lib.es2018.promise.d.ts +28 -0
- pytscheck/bin/windows-arm64/lib/lib.es2018.regexp.d.ts +35 -0
- pytscheck/bin/windows-arm64/lib/lib.es2019.array.d.ts +77 -0
- pytscheck/bin/windows-arm64/lib/lib.es2019.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.es2019.full.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.es2019.intl.d.ts +21 -0
- pytscheck/bin/windows-arm64/lib/lib.es2019.object.d.ts +31 -0
- pytscheck/bin/windows-arm64/lib/lib.es2019.string.d.ts +35 -0
- pytscheck/bin/windows-arm64/lib/lib.es2019.symbol.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.es2020.bigint.d.ts +763 -0
- pytscheck/bin/windows-arm64/lib/lib.es2020.d.ts +25 -0
- pytscheck/bin/windows-arm64/lib/lib.es2020.date.d.ts +40 -0
- pytscheck/bin/windows-arm64/lib/lib.es2020.full.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.es2020.intl.d.ts +472 -0
- pytscheck/bin/windows-arm64/lib/lib.es2020.number.d.ts +26 -0
- pytscheck/bin/windows-arm64/lib/lib.es2020.promise.d.ts +45 -0
- pytscheck/bin/windows-arm64/lib/lib.es2020.sharedmemory.d.ts +97 -0
- pytscheck/bin/windows-arm64/lib/lib.es2020.string.d.ts +42 -0
- pytscheck/bin/windows-arm64/lib/lib.es2020.symbol.wellknown.d.ts +39 -0
- pytscheck/bin/windows-arm64/lib/lib.es2021.d.ts +21 -0
- pytscheck/bin/windows-arm64/lib/lib.es2021.full.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.es2021.intl.d.ts +164 -0
- pytscheck/bin/windows-arm64/lib/lib.es2021.promise.d.ts +46 -0
- pytscheck/bin/windows-arm64/lib/lib.es2021.string.d.ts +31 -0
- pytscheck/bin/windows-arm64/lib/lib.es2021.weakref.d.ts +76 -0
- pytscheck/bin/windows-arm64/lib/lib.es2022.array.d.ts +119 -0
- pytscheck/bin/windows-arm64/lib/lib.es2022.d.ts +23 -0
- pytscheck/bin/windows-arm64/lib/lib.es2022.error.d.ts +73 -0
- pytscheck/bin/windows-arm64/lib/lib.es2022.full.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.es2022.intl.d.ts +143 -0
- pytscheck/bin/windows-arm64/lib/lib.es2022.object.d.ts +24 -0
- pytscheck/bin/windows-arm64/lib/lib.es2022.regexp.d.ts +37 -0
- pytscheck/bin/windows-arm64/lib/lib.es2022.string.d.ts +23 -0
- pytscheck/bin/windows-arm64/lib/lib.es2023.array.d.ts +922 -0
- pytscheck/bin/windows-arm64/lib/lib.es2023.collection.d.ts +19 -0
- pytscheck/bin/windows-arm64/lib/lib.es2023.d.ts +20 -0
- pytscheck/bin/windows-arm64/lib/lib.es2023.full.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.es2023.intl.d.ts +62 -0
- pytscheck/bin/windows-arm64/lib/lib.es2024.arraybuffer.d.ts +63 -0
- pytscheck/bin/windows-arm64/lib/lib.es2024.collection.d.ts +27 -0
- pytscheck/bin/windows-arm64/lib/lib.es2024.d.ts +24 -0
- pytscheck/bin/windows-arm64/lib/lib.es2024.full.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.es2024.object.d.ts +27 -0
- pytscheck/bin/windows-arm64/lib/lib.es2024.promise.d.ts +33 -0
- pytscheck/bin/windows-arm64/lib/lib.es2024.regexp.d.ts +23 -0
- pytscheck/bin/windows-arm64/lib/lib.es2024.sharedmemory.d.ts +66 -0
- pytscheck/bin/windows-arm64/lib/lib.es2024.string.d.ts +27 -0
- pytscheck/bin/windows-arm64/lib/lib.es2025.collection.d.ts +94 -0
- pytscheck/bin/windows-arm64/lib/lib.es2025.d.ts +23 -0
- pytscheck/bin/windows-arm64/lib/lib.es2025.float16.d.ts +443 -0
- pytscheck/bin/windows-arm64/lib/lib.es2025.full.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.es2025.intl.d.ts +200 -0
- pytscheck/bin/windows-arm64/lib/lib.es2025.iterator.d.ts +146 -0
- pytscheck/bin/windows-arm64/lib/lib.es2025.promise.d.ts +32 -0
- pytscheck/bin/windows-arm64/lib/lib.es2025.regexp.d.ts +30 -0
- pytscheck/bin/windows-arm64/lib/lib.es5.d.ts +4599 -0
- pytscheck/bin/windows-arm64/lib/lib.es6.d.ts +21 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.array.d.ts +33 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.collection.d.ts +47 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.d.ts +27 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.date.d.ts +21 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.decorators.d.ts +26 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.disposable.d.ts +191 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.error.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.full.d.ts +22 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.intl.d.ts +107 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.sharedmemory.d.ts +23 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.temporal.d.ts +485 -0
- pytscheck/bin/windows-arm64/lib/lib.esnext.typedarrays.d.ts +90 -0
- pytscheck/bin/windows-arm64/lib/lib.scripthost.d.ts +320 -0
- pytscheck/bin/windows-arm64/lib/lib.webworker.asynciterable.d.ts +18 -0
- pytscheck/bin/windows-arm64/lib/lib.webworker.d.ts +15606 -0
- pytscheck/bin/windows-arm64/lib/lib.webworker.importscripts.d.ts +21 -0
- pytscheck/bin/windows-arm64/lib/lib.webworker.iterable.d.ts +18 -0
- pytscheck/bin/windows-arm64/lib/tsgo.exe +0 -0
- pytscheck/bin/windows-arm64/package.json +66 -0
- pytscheck/checker.py +201 -0
- pytscheck/client.py +63 -0
- pytscheck/config.py +128 -0
- pytscheck/defaults.py +34 -0
- pytscheck/diagnostics.py +226 -0
- pytscheck/exceptions.py +115 -0
- pytscheck/integrations/README.md +13 -0
- pytscheck/models.py +101 -0
- pytscheck/options.py +56 -0
- pytscheck/paths.py +45 -0
- pytscheck/project.py +39 -0
- pytscheck/protocol.py +159 -0
- pytscheck/py.typed +0 -0
- pytscheck/resolver.py +90 -0
- pytscheck/results.py +31 -0
- pytscheck/runner.py +200 -0
- pytscheck/version.py +13 -0
- pytscheck-0.1.0.dist-info/METADATA +158 -0
- pytscheck-0.1.0.dist-info/RECORD +150 -0
- pytscheck-0.1.0.dist-info/WHEEL +4 -0
- pytscheck-0.1.0.dist-info/entry_points.txt +2 -0
- pytscheck-0.1.0.dist-info/licenses/LICENSE +204 -0
- pytscheck-0.1.0.dist-info/licenses/NOTICE.txt +442 -0
pytscheck/__init__.py
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
"""PyTSCheck: Python-native TypeScript type checking.
|
|
2
|
+
|
|
3
|
+
PyTSCheck wraps Microsoft's native TypeScript 7 compiler (``tsgo``) and
|
|
4
|
+
exposes a typed Python API for checking source strings, files, and
|
|
5
|
+
projects. The compiler is bundled inside platform-specific wheels, so no
|
|
6
|
+
Node.js or Go installation is required.
|
|
7
|
+
|
|
8
|
+
Public API
|
|
9
|
+
----------
|
|
10
|
+
|
|
11
|
+
The top-level entry points mirror the module-level functions in
|
|
12
|
+
:mod:`pytscheck.api`::
|
|
13
|
+
|
|
14
|
+
from pytscheck import check, check_file, check_project
|
|
15
|
+
|
|
16
|
+
result = check('const age: number = "twenty";')
|
|
17
|
+
if not result.success:
|
|
18
|
+
for diagnostic in result.errors:
|
|
19
|
+
print(diagnostic.file, diagnostic.line, diagnostic.message)
|
|
20
|
+
|
|
21
|
+
Adapter authors import from :mod:`pytscheck.adapters` instead; the core
|
|
22
|
+
package never imports a specific adapter.
|
|
23
|
+
|
|
24
|
+
Stability
|
|
25
|
+
---------
|
|
26
|
+
|
|
27
|
+
The names re-exported here are the public surface and follow Semantic
|
|
28
|
+
Versioning. Anything not listed in :data:`__all__` is internal and may
|
|
29
|
+
change without notice.
|
|
30
|
+
|
|
31
|
+
Importing this module is cheap and safe: no subprocess is launched, no
|
|
32
|
+
binary is located, and no network access occurs until a ``check*``
|
|
33
|
+
function is called. This matters for packaging tests that run on machines
|
|
34
|
+
without a bundled compiler.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from __future__ import annotations
|
|
38
|
+
|
|
39
|
+
from .api import check, check_file, check_project
|
|
40
|
+
from .config import CompilerOptions
|
|
41
|
+
from .exceptions import (
|
|
42
|
+
AdapterDiscoveryError,
|
|
43
|
+
AdapterError,
|
|
44
|
+
AdapterExtractionError,
|
|
45
|
+
AdapterMappingError,
|
|
46
|
+
AdapterProtocolError,
|
|
47
|
+
AdapterRegistrationError,
|
|
48
|
+
CompilerExecutionError,
|
|
49
|
+
CompilerNotFoundError,
|
|
50
|
+
CompilerTimeoutError,
|
|
51
|
+
ConfigurationError,
|
|
52
|
+
OutputParseError,
|
|
53
|
+
PyTSCheckError,
|
|
54
|
+
UnsupportedPlatformError,
|
|
55
|
+
)
|
|
56
|
+
from .models import Diagnostic, RelatedInformation, Result, SourceFile
|
|
57
|
+
from .version import ADAPTER_PROTOCOL_VERSION, COMPILER_VERSION, __version__
|
|
58
|
+
|
|
59
|
+
__all__ = [ # noqa: RUF022 -- Public exports retain the tested version/API group order.
|
|
60
|
+
# Version metadata
|
|
61
|
+
"__version__",
|
|
62
|
+
"COMPILER_VERSION",
|
|
63
|
+
"ADAPTER_PROTOCOL_VERSION",
|
|
64
|
+
# Public API
|
|
65
|
+
"check",
|
|
66
|
+
"check_file",
|
|
67
|
+
"check_project",
|
|
68
|
+
# Configuration
|
|
69
|
+
"CompilerOptions",
|
|
70
|
+
# Models
|
|
71
|
+
"Result",
|
|
72
|
+
"Diagnostic",
|
|
73
|
+
"RelatedInformation",
|
|
74
|
+
"SourceFile",
|
|
75
|
+
# Exceptions
|
|
76
|
+
"PyTSCheckError",
|
|
77
|
+
"UnsupportedPlatformError",
|
|
78
|
+
"CompilerNotFoundError",
|
|
79
|
+
"CompilerExecutionError",
|
|
80
|
+
"CompilerTimeoutError",
|
|
81
|
+
"OutputParseError",
|
|
82
|
+
"ConfigurationError",
|
|
83
|
+
"AdapterError",
|
|
84
|
+
"AdapterDiscoveryError",
|
|
85
|
+
"AdapterExtractionError",
|
|
86
|
+
"AdapterMappingError",
|
|
87
|
+
"AdapterProtocolError",
|
|
88
|
+
"AdapterRegistrationError",
|
|
89
|
+
]
|
pytscheck/__main__.py
ADDED
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
"""``python -m pytscheck`` and the ``pytscheck`` console script.
|
|
2
|
+
|
|
3
|
+
This module is intentionally small. It exposes three things:
|
|
4
|
+
|
|
5
|
+
- ``--version`` : print pytscheck / compiler / protocol versions
|
|
6
|
+
- ``--where`` : print the path to the bundled tsgo binary
|
|
7
|
+
- ``check`` : a minimal CLI over the public API (Phase 2+)
|
|
8
|
+
|
|
9
|
+
Every subcommand maps to a public function in :mod:`pytscheck.api`. The
|
|
10
|
+
CLI never reimplements logic: it parses arguments, calls the API, and
|
|
11
|
+
formats the result.
|
|
12
|
+
|
|
13
|
+
Exit codes
|
|
14
|
+
----------
|
|
15
|
+
|
|
16
|
+
0 success (no errors)
|
|
17
|
+
1 type errors were reported
|
|
18
|
+
2 usage / configuration error
|
|
19
|
+
3 operational failure (missing binary, crash, timeout, unparsable output)
|
|
20
|
+
|
|
21
|
+
The distinction between 1 and 3 matters for CI: a tool that wants to fail
|
|
22
|
+
the build on type errors should treat 1 as a normal failure and 3 as an
|
|
23
|
+
infrastructure problem.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
from __future__ import annotations
|
|
27
|
+
|
|
28
|
+
import argparse
|
|
29
|
+
import sys
|
|
30
|
+
from pathlib import Path
|
|
31
|
+
|
|
32
|
+
from .exceptions import (
|
|
33
|
+
CompilerExecutionError,
|
|
34
|
+
CompilerNotFoundError,
|
|
35
|
+
CompilerTimeoutError,
|
|
36
|
+
ConfigurationError,
|
|
37
|
+
OutputParseError,
|
|
38
|
+
PyTSCheckError,
|
|
39
|
+
UnsupportedPlatformError,
|
|
40
|
+
)
|
|
41
|
+
from .models import Diagnostic, Result
|
|
42
|
+
from .version import ADAPTER_PROTOCOL_VERSION, COMPILER_VERSION, __version__
|
|
43
|
+
|
|
44
|
+
EXIT_OK = 0
|
|
45
|
+
EXIT_TYPE_ERRORS = 1
|
|
46
|
+
EXIT_USAGE = 2
|
|
47
|
+
EXIT_OPERATIONAL = 3
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ---------------------------------------------------------------------------
|
|
51
|
+
# Argument parsing
|
|
52
|
+
# ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _build_parser() -> argparse.ArgumentParser:
|
|
56
|
+
parser = argparse.ArgumentParser(
|
|
57
|
+
prog="pytscheck",
|
|
58
|
+
description="Type-check TypeScript from Python.",
|
|
59
|
+
)
|
|
60
|
+
parser.add_argument(
|
|
61
|
+
"--version",
|
|
62
|
+
action="store_true",
|
|
63
|
+
help="print versions and exit",
|
|
64
|
+
)
|
|
65
|
+
parser.add_argument(
|
|
66
|
+
"--where",
|
|
67
|
+
action="store_true",
|
|
68
|
+
help="print the path to the bundled tsgo binary and exit",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
sub = parser.add_subparsers(dest="command", metavar="COMMAND")
|
|
72
|
+
|
|
73
|
+
# --- check string -----------------------------------------------------
|
|
74
|
+
p_str = sub.add_parser("string", help="check an in-memory source string")
|
|
75
|
+
p_str.add_argument("source", help="TypeScript source text")
|
|
76
|
+
p_str.add_argument("--filename", default="input.ts", help="virtual filename")
|
|
77
|
+
p_str.add_argument("--json", action="store_true", help="emit JSON diagnostics")
|
|
78
|
+
p_str.add_argument("--keep-raw", action="store_true", help="include raw compiler output")
|
|
79
|
+
|
|
80
|
+
# --- check file -------------------------------------------------------
|
|
81
|
+
p_file = sub.add_parser("file", help="check a single file")
|
|
82
|
+
p_file.add_argument("path", type=Path, help="path to a .ts / .tsx / .mts / .cts file")
|
|
83
|
+
p_file.add_argument("--json", action="store_true", help="emit JSON diagnostics")
|
|
84
|
+
p_file.add_argument("--keep-raw", action="store_true", help="include raw compiler output")
|
|
85
|
+
|
|
86
|
+
# --- check project ----------------------------------------------------
|
|
87
|
+
p_proj = sub.add_parser("project", help="check a project directory")
|
|
88
|
+
p_proj.add_argument("root", type=Path, help="project root directory")
|
|
89
|
+
p_proj.add_argument("--json", action="store_true", help="emit JSON diagnostics")
|
|
90
|
+
p_proj.add_argument("--keep-raw", action="store_true", help="include raw compiler output")
|
|
91
|
+
|
|
92
|
+
return parser
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
# ---------------------------------------------------------------------------
|
|
96
|
+
# Output formatting
|
|
97
|
+
# ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _format_diagnostic(d: Diagnostic) -> str:
|
|
101
|
+
"""One-line, grep-friendly rendering of a diagnostic."""
|
|
102
|
+
loc = f"{d.file or '<global>'}({d.line},{d.column})"
|
|
103
|
+
return f"{loc}: {d.category} TS{d.code}: {d.message}"
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _print_result(result: Result, *, as_json: bool) -> None:
|
|
107
|
+
if as_json:
|
|
108
|
+
import json
|
|
109
|
+
|
|
110
|
+
payload = {
|
|
111
|
+
"success": result.success,
|
|
112
|
+
"compiler_version": result.compiler_version,
|
|
113
|
+
"elapsed_seconds": result.elapsed_seconds,
|
|
114
|
+
"diagnostics": [
|
|
115
|
+
{
|
|
116
|
+
"file": d.file,
|
|
117
|
+
"origin_file": d.origin_file,
|
|
118
|
+
"line": d.line,
|
|
119
|
+
"column": d.column,
|
|
120
|
+
"end_line": d.end_line,
|
|
121
|
+
"end_column": d.end_column,
|
|
122
|
+
"code": d.code,
|
|
123
|
+
"category": d.category,
|
|
124
|
+
"message": d.message,
|
|
125
|
+
}
|
|
126
|
+
for d in result.diagnostics
|
|
127
|
+
],
|
|
128
|
+
}
|
|
129
|
+
print(json.dumps(payload, indent=2))
|
|
130
|
+
return
|
|
131
|
+
|
|
132
|
+
for d in result.diagnostics:
|
|
133
|
+
print(_format_diagnostic(d))
|
|
134
|
+
if result.success:
|
|
135
|
+
print(f"ok ({result.elapsed_seconds:.2f}s)")
|
|
136
|
+
else:
|
|
137
|
+
n = len(result.errors)
|
|
138
|
+
print(f"{n} error{'s' if n != 1 else ''} ({result.elapsed_seconds:.2f}s)")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
# ---------------------------------------------------------------------------
|
|
142
|
+
# Command handlers
|
|
143
|
+
# ---------------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _cmd_version() -> int:
|
|
147
|
+
print(f"pytscheck {__version__}")
|
|
148
|
+
print(f"bundled compiler {COMPILER_VERSION}")
|
|
149
|
+
print(f"adapter protocol {ADAPTER_PROTOCOL_VERSION}")
|
|
150
|
+
return EXIT_OK
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _cmd_where() -> int:
|
|
154
|
+
from .runner import binary_path
|
|
155
|
+
|
|
156
|
+
try:
|
|
157
|
+
print(binary_path())
|
|
158
|
+
except PyTSCheckError as exc:
|
|
159
|
+
print(f"error: {exc}", file=sys.stderr)
|
|
160
|
+
return EXIT_OPERATIONAL
|
|
161
|
+
return EXIT_OK
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _cmd_check(args: argparse.Namespace) -> int:
|
|
165
|
+
# Import lazily so --version / --where never pull in the runner.
|
|
166
|
+
from .api import check, check_file, check_project
|
|
167
|
+
|
|
168
|
+
try:
|
|
169
|
+
if args.command == "string":
|
|
170
|
+
result = check(
|
|
171
|
+
args.source,
|
|
172
|
+
filename=args.filename,
|
|
173
|
+
keep_raw=args.keep_raw,
|
|
174
|
+
)
|
|
175
|
+
elif args.command == "file":
|
|
176
|
+
result = check_file(args.path, keep_raw=args.keep_raw)
|
|
177
|
+
elif args.command == "project":
|
|
178
|
+
result = check_project(args.root, keep_raw=args.keep_raw)
|
|
179
|
+
else: # pragma: no cover - argparse enforces this
|
|
180
|
+
print(f"unknown command: {args.command}", file=sys.stderr)
|
|
181
|
+
return EXIT_USAGE
|
|
182
|
+
except ConfigurationError as exc:
|
|
183
|
+
print(f"configuration error: {exc}", file=sys.stderr)
|
|
184
|
+
return EXIT_USAGE
|
|
185
|
+
except (
|
|
186
|
+
CompilerNotFoundError,
|
|
187
|
+
CompilerTimeoutError,
|
|
188
|
+
CompilerExecutionError,
|
|
189
|
+
OutputParseError,
|
|
190
|
+
UnsupportedPlatformError,
|
|
191
|
+
) as exc:
|
|
192
|
+
print(f"operational error: {exc}", file=sys.stderr)
|
|
193
|
+
return EXIT_OPERATIONAL
|
|
194
|
+
|
|
195
|
+
_print_result(result, as_json=args.json)
|
|
196
|
+
return EXIT_OK if result.success else EXIT_TYPE_ERRORS
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# ---------------------------------------------------------------------------
|
|
200
|
+
# Entry point
|
|
201
|
+
# ---------------------------------------------------------------------------
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def main(argv: list[str] | None = None) -> int:
|
|
205
|
+
parser = _build_parser()
|
|
206
|
+
args = parser.parse_args(argv)
|
|
207
|
+
|
|
208
|
+
if args.version:
|
|
209
|
+
return _cmd_version()
|
|
210
|
+
if args.where:
|
|
211
|
+
return _cmd_where()
|
|
212
|
+
|
|
213
|
+
if args.command in {"string", "file", "project"}:
|
|
214
|
+
return _cmd_check(args)
|
|
215
|
+
|
|
216
|
+
parser.print_help()
|
|
217
|
+
return EXIT_USAGE
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
if __name__ == "__main__":
|
|
221
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Compiler-agnostic adapter system.
|
|
2
|
+
|
|
3
|
+
Adapters let external Python tools submit generated TypeScript and receive
|
|
4
|
+
diagnostics mapped back to their own source files. The pytscheck core never
|
|
5
|
+
imports a specific adapter; tools register theirs through the
|
|
6
|
+
``pytscheck.adapters`` entry-point group.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
from .base import CompilerAdapter
|
|
12
|
+
from .context import DeclarationResource, ProjectContext
|
|
13
|
+
from .errors import (
|
|
14
|
+
AdapterError,
|
|
15
|
+
AdapterExtractionError,
|
|
16
|
+
AdapterMappingError,
|
|
17
|
+
AdapterProtocolError,
|
|
18
|
+
AdapterRegistrationError,
|
|
19
|
+
)
|
|
20
|
+
from .registry import get_adapter, register_adapter, unregister_adapter
|
|
21
|
+
from .source_maps import remap_diagnostics
|
|
22
|
+
from .sources import collect_sources
|
|
23
|
+
|
|
24
|
+
__all__ = [
|
|
25
|
+
"AdapterError",
|
|
26
|
+
"AdapterExtractionError",
|
|
27
|
+
"AdapterMappingError",
|
|
28
|
+
"AdapterProtocolError",
|
|
29
|
+
"AdapterRegistrationError",
|
|
30
|
+
"CompilerAdapter",
|
|
31
|
+
"DeclarationResource",
|
|
32
|
+
"ProjectContext",
|
|
33
|
+
"collect_sources",
|
|
34
|
+
"get_adapter",
|
|
35
|
+
"register_adapter",
|
|
36
|
+
"remap_diagnostics",
|
|
37
|
+
"unregister_adapter",
|
|
38
|
+
]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""The compiler-agnostic adapter protocol."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import Protocol, runtime_checkable
|
|
6
|
+
|
|
7
|
+
from ..models import SourceFile
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@runtime_checkable
|
|
11
|
+
class CompilerAdapter(Protocol):
|
|
12
|
+
"""Structural protocol implemented by every adapter.
|
|
13
|
+
|
|
14
|
+
Implementations live in the consuming tool's package. pytscheck core
|
|
15
|
+
never imports them directly; registration is via the entry-point group
|
|
16
|
+
``pytscheck.adapters``.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
@property
|
|
20
|
+
def name(self) -> str:
|
|
21
|
+
"""A short, unique identifier (e.g. ``"teloc"``)."""
|
|
22
|
+
...
|
|
23
|
+
|
|
24
|
+
def get_sources(self, project_root: str) -> list[SourceFile]:
|
|
25
|
+
"""Return the TypeScript sources to check.
|
|
26
|
+
|
|
27
|
+
Must raise :class:`AdapterExtractionError` (not return a diagnostic)
|
|
28
|
+
if the tool cannot produce TypeScript for any input.
|
|
29
|
+
"""
|
|
30
|
+
...
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
__all__ = ["CompilerAdapter"]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Protocol-version and capability compatibility checks."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ..version import ADAPTER_PROTOCOL_VERSION
|
|
6
|
+
from .base import CompilerAdapter
|
|
7
|
+
from .errors import AdapterProtocolError
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def check_protocol_version(adapter: CompilerAdapter) -> None:
|
|
11
|
+
"""Verify the adapter declares a compatible protocol version.
|
|
12
|
+
|
|
13
|
+
Adapters may expose ``protocol_version`` (a string). If absent, we assume
|
|
14
|
+
"1.x" compatibility and accept.
|
|
15
|
+
"""
|
|
16
|
+
declared = getattr(adapter, "protocol_version", None)
|
|
17
|
+
if declared is None:
|
|
18
|
+
return
|
|
19
|
+
if not isinstance(declared, str):
|
|
20
|
+
raise AdapterProtocolError(f"adapter {adapter.name!r} has non-string protocol_version")
|
|
21
|
+
if declared.split(".")[0] != ADAPTER_PROTOCOL_VERSION.split(".")[0]:
|
|
22
|
+
raise AdapterProtocolError(
|
|
23
|
+
f"adapter {adapter.name!r} speaks protocol {declared}, "
|
|
24
|
+
f"core speaks {ADAPTER_PROTOCOL_VERSION}"
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
__all__ = ["check_protocol_version"]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Optional framework-independent context for generated project checking."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass, field
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True, slots=True)
|
|
10
|
+
class DeclarationResource:
|
|
11
|
+
"""Declarations shipped by a Python package, optionally exposed as a module.
|
|
12
|
+
|
|
13
|
+
``resource`` is a package-relative file or directory. Resources may come
|
|
14
|
+
from ordinary installations or zip importers; no Node installation is needed.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
package: str
|
|
18
|
+
resource: str = "types"
|
|
19
|
+
module: str | None = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(frozen=True, slots=True)
|
|
23
|
+
class ProjectContext:
|
|
24
|
+
"""Opt into project configuration, real files and declaration resources.
|
|
25
|
+
|
|
26
|
+
Paths are relative to the root passed to ``check_project`` unless absolute.
|
|
27
|
+
Caller options override ``compiler_options``, which override tsconfig.
|
|
28
|
+
Generated paths must not overwrite an existing project file.
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
tsconfig: str | Path | None = None
|
|
32
|
+
inherit_tsconfig: bool = True
|
|
33
|
+
include_project_files: bool = True
|
|
34
|
+
declaration_paths: tuple[str | Path, ...] = ()
|
|
35
|
+
declaration_resources: tuple[DeclarationResource, ...] = ()
|
|
36
|
+
compiler_options: dict[str, object] = field(default_factory=dict)
|
|
37
|
+
max_workspace_files: int = 100_000
|
|
38
|
+
max_workspace_bytes: int = 512 * 1024 * 1024
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Entry-point discovery for third-party adapters."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from importlib.metadata import entry_points
|
|
6
|
+
|
|
7
|
+
from ..defaults import DEFAULT_ADAPTER_ENTRY_POINT_GROUP
|
|
8
|
+
from .base import CompilerAdapter
|
|
9
|
+
from .errors import AdapterDiscoveryError
|
|
10
|
+
from .registry import register_adapter
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def discover_adapters(
|
|
14
|
+
group: str = DEFAULT_ADAPTER_ENTRY_POINT_GROUP,
|
|
15
|
+
) -> tuple[str, ...]:
|
|
16
|
+
"""Discover and register all adapters advertised under ``group``.
|
|
17
|
+
|
|
18
|
+
Returns the names of adapters that were newly registered.
|
|
19
|
+
"""
|
|
20
|
+
registered: list[str] = []
|
|
21
|
+
try:
|
|
22
|
+
eps = entry_points(group=group)
|
|
23
|
+
except Exception as exc: # pragma: no cover - importlib.metadata is stable
|
|
24
|
+
raise AdapterDiscoveryError(f"entry-point discovery failed: {exc}") from exc
|
|
25
|
+
|
|
26
|
+
for ep in eps:
|
|
27
|
+
try:
|
|
28
|
+
obj = ep.load()
|
|
29
|
+
adapter = obj() if isinstance(obj, type) else obj
|
|
30
|
+
except Exception as exc:
|
|
31
|
+
raise AdapterDiscoveryError(
|
|
32
|
+
f"failed to load adapter entry point {ep.name!r}: {exc}"
|
|
33
|
+
) from exc
|
|
34
|
+
|
|
35
|
+
if not isinstance(adapter, CompilerAdapter):
|
|
36
|
+
raise AdapterDiscoveryError(f"entry point {ep.name!r} did not yield a CompilerAdapter")
|
|
37
|
+
register_adapter(adapter)
|
|
38
|
+
registered.append(adapter.name)
|
|
39
|
+
|
|
40
|
+
return tuple(registered)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
__all__ = ["discover_adapters"]
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Adapter-layer exceptions.
|
|
2
|
+
|
|
3
|
+
Re-exported from the top-level exception hierarchy so callers can catch
|
|
4
|
+
``pytscheck.AdapterError`` without knowing about this submodule.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from ..exceptions import (
|
|
10
|
+
AdapterDiscoveryError,
|
|
11
|
+
AdapterError,
|
|
12
|
+
AdapterExtractionError,
|
|
13
|
+
AdapterMappingError,
|
|
14
|
+
AdapterProtocolError,
|
|
15
|
+
AdapterRegistrationError,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
__all__ = [
|
|
19
|
+
"AdapterDiscoveryError",
|
|
20
|
+
"AdapterError",
|
|
21
|
+
"AdapterExtractionError",
|
|
22
|
+
"AdapterMappingError",
|
|
23
|
+
"AdapterProtocolError",
|
|
24
|
+
"AdapterRegistrationError",
|
|
25
|
+
]
|