ts-backend-check 1.1.0__py3-none-any.whl → 1.2.1__py3-none-any.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.
- ts_backend_check/checker.py +10 -7
- ts_backend_check/cli/check_blank.py +111 -0
- ts_backend_check/cli/config.py +164 -0
- ts_backend_check/cli/main.py +72 -12
- ts_backend_check/cli/upgrade.py +91 -0
- ts_backend_check/cli/version.py +87 -0
- ts_backend_check/parsers/django_parser.py +1 -1
- ts_backend_check/parsers/typescript_parser.py +5 -12
- ts_backend_check-1.2.1.data/data/requirements.txt +26 -0
- {ts_backend_check-1.1.0.dist-info → ts_backend_check-1.2.1.dist-info}/METADATA +41 -13
- ts_backend_check-1.2.1.dist-info/RECORD +21 -0
- ts_backend_check-1.1.0.data/data/requirements.txt +0 -6
- ts_backend_check-1.1.0.dist-info/RECORD +0 -17
- {ts_backend_check-1.1.0.dist-info → ts_backend_check-1.2.1.dist-info}/WHEEL +0 -0
- {ts_backend_check-1.1.0.dist-info → ts_backend_check-1.2.1.dist-info}/entry_points.txt +0 -0
- {ts_backend_check-1.1.0.dist-info → ts_backend_check-1.2.1.dist-info}/licenses/LICENSE.txt +0 -0
- {ts_backend_check-1.1.0.dist-info → ts_backend_check-1.2.1.dist-info}/top_level.txt +0 -0
ts_backend_check/checker.py
CHANGED
|
@@ -29,7 +29,7 @@ class TypeChecker:
|
|
|
29
29
|
self.model_fields = extract_model_fields(models_file)
|
|
30
30
|
self.ts_parser = TypeScriptParser(types_file)
|
|
31
31
|
self.ts_interfaces = self.ts_parser.parse_interfaces()
|
|
32
|
-
self.backend_only = self.ts_parser.
|
|
32
|
+
self.backend_only = self.ts_parser.get_ignored_fields()
|
|
33
33
|
|
|
34
34
|
def check(self) -> List[str]:
|
|
35
35
|
"""
|
|
@@ -149,8 +149,8 @@ class TypeChecker:
|
|
|
149
149
|
"""
|
|
150
150
|
potential_names = TypeChecker._generate_potential_names(model_name)
|
|
151
151
|
return (
|
|
152
|
-
f"\nNo matching TypeScript interface found for model: {model_name}
|
|
153
|
-
f"
|
|
152
|
+
f"\nNo matching TypeScript interface found for model: {model_name}"
|
|
153
|
+
f"\nSearched for interfaces: {', '.join(potential_names)}"
|
|
154
154
|
)
|
|
155
155
|
|
|
156
156
|
@staticmethod
|
|
@@ -177,9 +177,12 @@ class TypeChecker:
|
|
|
177
177
|
The message displayed to the user when missing fields are found.
|
|
178
178
|
"""
|
|
179
179
|
camel_field = snake_to_camel(input_str=field)
|
|
180
|
+
interface_of_interfaces = (
|
|
181
|
+
"interface" if len(interfaces.keys()) == 1 else "interfaces"
|
|
182
|
+
)
|
|
183
|
+
|
|
180
184
|
return (
|
|
181
|
-
f"\nField '{field}' (camelCase: '{camel_field}') from model '{model_name}' "
|
|
182
|
-
f"
|
|
183
|
-
f"
|
|
184
|
-
f"To ignore this field, add a comment that references it like: '// Note: {camel_field} is backend only'"
|
|
185
|
+
f"\nField '{field}' (camelCase: '{camel_field}') from model '{model_name}' is missing in the TypeScript interfaces."
|
|
186
|
+
f"\nExpected to find this field in the frontend {interface_of_interfaces}: {', '.join(interfaces.keys())}"
|
|
187
|
+
f"\nTo ignore this field, add the following comment to the TypeScript file: '// ts-backend-check: ignore field {camel_field}'"
|
|
185
188
|
)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""
|
|
3
|
+
Functionality to check TypeScript interfaces for fields that should be optional based on Django models.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import ast
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Dict, Set
|
|
9
|
+
|
|
10
|
+
from rich.console import Console
|
|
11
|
+
|
|
12
|
+
from ts_backend_check.parsers.django_parser import DjangoModelVisitor
|
|
13
|
+
|
|
14
|
+
ROOT_DIR = Path.cwd()
|
|
15
|
+
console = Console()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class BlankParser(DjangoModelVisitor):
|
|
19
|
+
"""
|
|
20
|
+
AST visitor to extract blank fields from Django models based on DjangoModelVisitor.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self) -> None:
|
|
24
|
+
super().__init__()
|
|
25
|
+
self.blank_models: Dict[str, Set[str]] = {}
|
|
26
|
+
|
|
27
|
+
def visit_Assign(self, node: ast.Assign) -> None:
|
|
28
|
+
"""
|
|
29
|
+
Check assignment statements within a class which accepts blank fields.
|
|
30
|
+
|
|
31
|
+
Parameters
|
|
32
|
+
----------
|
|
33
|
+
node : ast.Assign
|
|
34
|
+
An assignment definition from Python AST (Abstract Syntax Tree).
|
|
35
|
+
It represents an assignment statement (e.g., x = 42).
|
|
36
|
+
"""
|
|
37
|
+
if not self.current_model:
|
|
38
|
+
return
|
|
39
|
+
|
|
40
|
+
for target in node.targets:
|
|
41
|
+
if (
|
|
42
|
+
(
|
|
43
|
+
isinstance(target, ast.Name)
|
|
44
|
+
and not target.id.startswith("_")
|
|
45
|
+
and isinstance(node.value, ast.Call)
|
|
46
|
+
and hasattr(node.value.func, "attr")
|
|
47
|
+
)
|
|
48
|
+
and any(
|
|
49
|
+
field_type in node.value.func.attr
|
|
50
|
+
for field_type in self.DJANGO_FIELD_TYPES
|
|
51
|
+
)
|
|
52
|
+
and any(
|
|
53
|
+
kw.arg == "blank"
|
|
54
|
+
and isinstance(kw.value, ast.Constant)
|
|
55
|
+
and kw.value.value is True
|
|
56
|
+
for kw in node.value.keywords
|
|
57
|
+
)
|
|
58
|
+
):
|
|
59
|
+
if self.current_model not in self.blank_models:
|
|
60
|
+
self.blank_models[self.current_model] = set()
|
|
61
|
+
|
|
62
|
+
self.blank_models[self.current_model].add(target.id)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def check_blank(file_path: str) -> Dict[str, Set[str]]:
|
|
66
|
+
"""
|
|
67
|
+
Function to extract fields from Django models file which accepts blank values.
|
|
68
|
+
|
|
69
|
+
Parameters
|
|
70
|
+
----------
|
|
71
|
+
file_path : str
|
|
72
|
+
A models.py file that defines Django models.
|
|
73
|
+
|
|
74
|
+
Returns
|
|
75
|
+
-------
|
|
76
|
+
Dict[str, Set[str]]
|
|
77
|
+
The fields from the models file extracted into a dictionary for future processing.
|
|
78
|
+
"""
|
|
79
|
+
model_path = ROOT_DIR / file_path
|
|
80
|
+
|
|
81
|
+
if model_path.is_file():
|
|
82
|
+
with open(model_path, "r", encoding="utf-8") as f:
|
|
83
|
+
content = f.read().strip()
|
|
84
|
+
# Skip any empty lines at the beginning.
|
|
85
|
+
while content.startswith("\n"):
|
|
86
|
+
content = content[1:]
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
tree = ast.parse(content)
|
|
90
|
+
|
|
91
|
+
except SyntaxError as e:
|
|
92
|
+
raise SyntaxError(
|
|
93
|
+
f"Failed to parse {model_path}. Make sure it's a valid Python file. Error: {str(e)}"
|
|
94
|
+
) from e
|
|
95
|
+
|
|
96
|
+
parser = BlankParser()
|
|
97
|
+
parser.visit(tree)
|
|
98
|
+
|
|
99
|
+
if len(parser.blank_models) == 0:
|
|
100
|
+
console.print("[green]No models have any blank fields specified.[green]")
|
|
101
|
+
|
|
102
|
+
else:
|
|
103
|
+
for k, v in parser.blank_models.items():
|
|
104
|
+
console.print(
|
|
105
|
+
f"[yellow]Model {k} has fields {sorted(v)} set as optional."
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
else:
|
|
109
|
+
print("Check the path entered.")
|
|
110
|
+
|
|
111
|
+
return parser.blank_models
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""
|
|
3
|
+
Configure cli to run based on a YAML configuration file.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from yaml import dump
|
|
9
|
+
|
|
10
|
+
YAML_CONFIG_FILE_PATH = (
|
|
11
|
+
Path(__file__).parent.parent.parent.parent / ".ts-backend-check.yaml"
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
PROJECT_ROOT_PATH = Path(__file__).parent.parent.parent.parent
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def configure_paths() -> None:
|
|
18
|
+
"""
|
|
19
|
+
Function to receive paths from user.
|
|
20
|
+
"""
|
|
21
|
+
config_options = {}
|
|
22
|
+
while True:
|
|
23
|
+
print("\n--- Adding new model/interface configuration ---")
|
|
24
|
+
|
|
25
|
+
key = input(
|
|
26
|
+
"Enter the model-interface type (eg: 'auth', 'orgs', 'groups'): "
|
|
27
|
+
).strip()
|
|
28
|
+
if not key:
|
|
29
|
+
print("Key cannot be empty. Please try again.")
|
|
30
|
+
continue
|
|
31
|
+
|
|
32
|
+
# Get backend path.
|
|
33
|
+
while True:
|
|
34
|
+
backend_path = input("Enter the path for Django models.py file: ").strip()
|
|
35
|
+
if not backend_path:
|
|
36
|
+
print("Path cannot be empty.")
|
|
37
|
+
continue
|
|
38
|
+
|
|
39
|
+
if path_exists(backend_path):
|
|
40
|
+
break
|
|
41
|
+
|
|
42
|
+
print(f"File not found: {PROJECT_ROOT_PATH / backend_path}")
|
|
43
|
+
print("Please check the path and try again.")
|
|
44
|
+
|
|
45
|
+
# Get frontend path.
|
|
46
|
+
while True:
|
|
47
|
+
frontend_path = input("Enter path to TypeScript interface file: ").strip()
|
|
48
|
+
if not frontend_path:
|
|
49
|
+
print("Path cannot be empty. Please try again.")
|
|
50
|
+
continue
|
|
51
|
+
|
|
52
|
+
if path_exists(frontend_path):
|
|
53
|
+
break
|
|
54
|
+
|
|
55
|
+
print(f"File not found: {PROJECT_ROOT_PATH / frontend_path}")
|
|
56
|
+
print("Please check the path and try again.")
|
|
57
|
+
|
|
58
|
+
config_options[key] = {
|
|
59
|
+
"backend_model_path": backend_path,
|
|
60
|
+
"frontend_interface_path": frontend_path,
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
print(f"✓ Added configuration for '{key}'")
|
|
64
|
+
|
|
65
|
+
continue_config = input(
|
|
66
|
+
"Do you wish to add another model/interface configuration? (y/[n])"
|
|
67
|
+
).strip()
|
|
68
|
+
|
|
69
|
+
if continue_config.lower() in ["n", ""]:
|
|
70
|
+
if config_options:
|
|
71
|
+
write_config(config_options)
|
|
72
|
+
print(
|
|
73
|
+
f"\n✓ Configuration complete! Added {len(config_options)} configuration(s)."
|
|
74
|
+
)
|
|
75
|
+
break
|
|
76
|
+
|
|
77
|
+
if config_options:
|
|
78
|
+
write_config(config_options)
|
|
79
|
+
print(
|
|
80
|
+
f"\n✓ Configuration complete! Added {len(config_options)} configuration(s)."
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
else:
|
|
84
|
+
print("\nNo configurations added.")
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def path_exists(path: str) -> bool:
|
|
88
|
+
"""
|
|
89
|
+
Check if path entered by the user exists withing the filesystem.
|
|
90
|
+
|
|
91
|
+
Parameters
|
|
92
|
+
----------
|
|
93
|
+
path : str
|
|
94
|
+
Path should be entered as a string from the user.
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
bool
|
|
99
|
+
Return true or false based on if path exists.
|
|
100
|
+
"""
|
|
101
|
+
full_path = Path(__file__).parent.parent.parent.parent / path
|
|
102
|
+
if Path(full_path).is_file():
|
|
103
|
+
return True
|
|
104
|
+
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def write_config(config: dict[str, dict[str, str]]) -> None:
|
|
109
|
+
"""
|
|
110
|
+
Function to write into .ts-backend-check.yaml file.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
config : dict[str, dict[str, str]]
|
|
115
|
+
Passing a dictionary as key str with another dict as value.
|
|
116
|
+
"""
|
|
117
|
+
try:
|
|
118
|
+
options = f"""# Configuration file for ts-backend-check validation.
|
|
119
|
+
# See https://github.com/activist-org/ts-backend-check for details.
|
|
120
|
+
|
|
121
|
+
# Paths:
|
|
122
|
+
{dump(config)}
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
with open(YAML_CONFIG_FILE_PATH, "w") as file:
|
|
126
|
+
file.write(options)
|
|
127
|
+
|
|
128
|
+
except IOError as e:
|
|
129
|
+
print(f"Error while writing config file: {e}")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def create_config() -> None:
|
|
133
|
+
"""
|
|
134
|
+
Main function to create or update configuration.
|
|
135
|
+
"""
|
|
136
|
+
print("ts-backend-check Configuration Setup")
|
|
137
|
+
print("=" * 40)
|
|
138
|
+
|
|
139
|
+
if YAML_CONFIG_FILE_PATH.is_file():
|
|
140
|
+
reconfig_choice = input(
|
|
141
|
+
"Configuration file exists. Do you want to re-configure your .ts-backend-check.yaml file? (y/[n]) "
|
|
142
|
+
)
|
|
143
|
+
if reconfig_choice.lower() in ["n", ""]:
|
|
144
|
+
print("Exiting without changes.")
|
|
145
|
+
return
|
|
146
|
+
|
|
147
|
+
print("Reconfiguring...")
|
|
148
|
+
|
|
149
|
+
else:
|
|
150
|
+
print("Creating new configuration file...")
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
configure_paths()
|
|
154
|
+
|
|
155
|
+
except KeyboardInterrupt:
|
|
156
|
+
print("\n\nConfiguration cancelled by user.")
|
|
157
|
+
|
|
158
|
+
except Exception as e:
|
|
159
|
+
print(f"\nError during configuration: {e}")
|
|
160
|
+
print("Configuration cancelled.")
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
if __name__ == "__main__":
|
|
164
|
+
create_config()
|
ts_backend_check/cli/main.py
CHANGED
|
@@ -5,9 +5,19 @@ Setup and commands for the ts-backend-check command line interface.
|
|
|
5
5
|
|
|
6
6
|
import argparse
|
|
7
7
|
import sys
|
|
8
|
+
from argparse import ArgumentParser
|
|
8
9
|
from pathlib import Path
|
|
9
10
|
|
|
11
|
+
from rich import print as rprint
|
|
12
|
+
from rich.text import Text
|
|
13
|
+
|
|
10
14
|
from ts_backend_check.checker import TypeChecker
|
|
15
|
+
from ts_backend_check.cli.check_blank import check_blank
|
|
16
|
+
from ts_backend_check.cli.config import create_config
|
|
17
|
+
from ts_backend_check.cli.upgrade import upgrade_cli
|
|
18
|
+
from ts_backend_check.cli.version import get_version_message
|
|
19
|
+
|
|
20
|
+
ROOT_DIR = Path.cwd()
|
|
11
21
|
|
|
12
22
|
|
|
13
23
|
def main() -> None:
|
|
@@ -26,8 +36,7 @@ def main() -> None:
|
|
|
26
36
|
"""
|
|
27
37
|
# MARK: CLI Base
|
|
28
38
|
|
|
29
|
-
|
|
30
|
-
parser = argparse.ArgumentParser(
|
|
39
|
+
parser = ArgumentParser(
|
|
31
40
|
prog="ts-backend-check",
|
|
32
41
|
description="Checks the types in TypeScript files against the corresponding backend models.",
|
|
33
42
|
epilog="Visit the codebase at https://github.com/activist-org/ts-backend-check to learn more!",
|
|
@@ -36,31 +45,75 @@ def main() -> None:
|
|
|
36
45
|
|
|
37
46
|
parser._actions[0].help = "Show this help message and exit."
|
|
38
47
|
|
|
48
|
+
parser.add_argument(
|
|
49
|
+
"-v",
|
|
50
|
+
"--version",
|
|
51
|
+
action="version",
|
|
52
|
+
version=f"{get_version_message()}",
|
|
53
|
+
help="Show the version of the ts-backend-check CLI.",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
parser.add_argument(
|
|
57
|
+
"-u",
|
|
58
|
+
"--upgrade",
|
|
59
|
+
action="store_true",
|
|
60
|
+
help="Upgrade the ts-backend-check CLI to the latest version.",
|
|
61
|
+
)
|
|
62
|
+
|
|
39
63
|
parser.add_argument(
|
|
40
64
|
"-bmf",
|
|
41
65
|
"--backend-model-file",
|
|
42
66
|
help="Path to the backend model file (e.g. Python class).",
|
|
43
67
|
)
|
|
68
|
+
|
|
44
69
|
parser.add_argument(
|
|
45
70
|
"-tsf",
|
|
46
71
|
"--typescript-file",
|
|
47
72
|
help="Path to the TypeScript interface/type file.",
|
|
48
73
|
)
|
|
49
74
|
|
|
75
|
+
parser.add_argument(
|
|
76
|
+
"-c",
|
|
77
|
+
"--configure",
|
|
78
|
+
action="store_true",
|
|
79
|
+
help="Configure a YAML file to simplify your checks.",
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
parser.add_argument(
|
|
83
|
+
"-cb",
|
|
84
|
+
"--check-blank",
|
|
85
|
+
help="Check for fields marked blank=True within Django models.",
|
|
86
|
+
)
|
|
87
|
+
|
|
50
88
|
# MARK: Setup CLI
|
|
51
89
|
|
|
52
|
-
args = parser.parse_args()
|
|
90
|
+
args = parser.parse_args(args=None if sys.argv[1:] else ["--help"])
|
|
91
|
+
|
|
92
|
+
if args.upgrade:
|
|
93
|
+
upgrade_cli()
|
|
94
|
+
return
|
|
95
|
+
|
|
96
|
+
if args.configure:
|
|
97
|
+
create_config()
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
if args.check_blank:
|
|
101
|
+
check_blank(args.check_blank)
|
|
102
|
+
return
|
|
103
|
+
|
|
104
|
+
# MARK: Run Check
|
|
105
|
+
|
|
53
106
|
backend_model_file_path = ROOT_DIR / args.backend_model_file
|
|
54
107
|
ts_file_path = ROOT_DIR / args.typescript_file
|
|
55
108
|
|
|
56
109
|
if not backend_model_file_path.is_file():
|
|
57
|
-
|
|
58
|
-
f"{args.backend_model_file} that should contain the backend models does not exist. Please check and try again."
|
|
110
|
+
rprint(
|
|
111
|
+
f"[red]{args.backend_model_file} that should contain the backend models does not exist. Please check and try again.[/red]"
|
|
59
112
|
)
|
|
60
113
|
|
|
61
114
|
elif not ts_file_path.is_file():
|
|
62
|
-
|
|
63
|
-
f"{args.typescript_file} file that should contain the TypeScript types does not exist. Please check and try again."
|
|
115
|
+
rprint(
|
|
116
|
+
f"[red]{args.typescript_file} file that should contain the TypeScript types does not exist. Please check and try again.[/red]"
|
|
64
117
|
)
|
|
65
118
|
|
|
66
119
|
else:
|
|
@@ -70,16 +123,23 @@ def main() -> None:
|
|
|
70
123
|
)
|
|
71
124
|
|
|
72
125
|
if missing := checker.check():
|
|
73
|
-
|
|
74
|
-
|
|
126
|
+
rprint(
|
|
127
|
+
"\n[red]❌ ts-backend-check error: There are inconsistencies between the provided backend models and TypeScript interfaces. Please see the output below for details.[/red]\n"
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Print each error message in red.
|
|
131
|
+
for msg in missing:
|
|
132
|
+
rprint(Text.from_markup(f"[red]{msg}[/red]"))
|
|
75
133
|
|
|
76
134
|
field_or_fields = "fields" if len(missing) > 1 else "field"
|
|
77
|
-
|
|
78
|
-
f"
|
|
135
|
+
rprint(
|
|
136
|
+
f"[red]Please fix the {len(missing)} {field_or_fields} above to have the backend models of {args.backend_model_file} synced with the typescript interfaces of {(args.typescript_file)}.[/red]"
|
|
79
137
|
)
|
|
80
138
|
sys.exit(1)
|
|
81
139
|
|
|
82
|
-
|
|
140
|
+
rprint(
|
|
141
|
+
"[green]✅ Success: All backend models are synced with their corresponding TypeScript interfaces for the provided files.[/green]"
|
|
142
|
+
)
|
|
83
143
|
|
|
84
144
|
|
|
85
145
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""
|
|
3
|
+
Functions to update the ts-backend-check CLI based on install method.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import subprocess
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
from packaging import version
|
|
10
|
+
from packaging.version import InvalidVersion
|
|
11
|
+
|
|
12
|
+
from ts_backend_check.cli.version import (
|
|
13
|
+
UNKNOWN_VERSION_NOT_FETCHED,
|
|
14
|
+
get_latest_version,
|
|
15
|
+
get_local_version,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def upgrade_cli() -> None:
|
|
20
|
+
"""
|
|
21
|
+
Upgrade the CLI tool to the latest available version on PyPI.
|
|
22
|
+
|
|
23
|
+
Raises
|
|
24
|
+
------
|
|
25
|
+
subprocess.CalledProcessError
|
|
26
|
+
If the installation of the latest version fails.
|
|
27
|
+
"""
|
|
28
|
+
local_version = get_local_version()
|
|
29
|
+
latest_version_message = get_latest_version()
|
|
30
|
+
|
|
31
|
+
if latest_version_message == UNKNOWN_VERSION_NOT_FETCHED:
|
|
32
|
+
print(
|
|
33
|
+
"Unable to fetch the latest version from GitHub. Please check the GitHub repository or your internet connection."
|
|
34
|
+
)
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
latest_version = latest_version_message.split("v")[-1]
|
|
38
|
+
local_version_clean = local_version.strip()
|
|
39
|
+
latest_version_clean = latest_version.replace("ts-backend-check", "").strip()
|
|
40
|
+
|
|
41
|
+
# Handle empty or invalid version strings.
|
|
42
|
+
try:
|
|
43
|
+
local_ver = (
|
|
44
|
+
version.parse(local_version_clean)
|
|
45
|
+
if local_version_clean
|
|
46
|
+
else version.parse("0.0.0")
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
except InvalidVersion:
|
|
50
|
+
# If local version is invalid, treat it as 0.0.0 to force upgrade.
|
|
51
|
+
local_ver = version.parse("0.0.0")
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
latest_ver = version.parse(latest_version_clean)
|
|
55
|
+
|
|
56
|
+
except InvalidVersion:
|
|
57
|
+
print("Unable to parse the latest version. Please check the GitHub repository.")
|
|
58
|
+
return
|
|
59
|
+
|
|
60
|
+
if local_ver == latest_ver:
|
|
61
|
+
print("You already have the latest version of ts-backend-check.")
|
|
62
|
+
|
|
63
|
+
elif local_ver > latest_ver:
|
|
64
|
+
print(
|
|
65
|
+
f"ts-backend-check v{local_version_clean} is higher than the currently released version ts-backend-check v{latest_version_clean}. Hopefully this is a development build, and if so, thanks for your work on ts-backend-check! If not, please report this to the team at https://github.com/activist-org/ts-backend-check/issues."
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
else:
|
|
69
|
+
print(f"Current version: {local_version}")
|
|
70
|
+
print(f"Latest version: {latest_version}")
|
|
71
|
+
print("Updating ts-backend-check with pip...")
|
|
72
|
+
try:
|
|
73
|
+
subprocess.check_call(
|
|
74
|
+
[
|
|
75
|
+
sys.executable,
|
|
76
|
+
"-m",
|
|
77
|
+
"pip",
|
|
78
|
+
"install",
|
|
79
|
+
"--upgrade",
|
|
80
|
+
"ts-backend-check",
|
|
81
|
+
]
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
except subprocess.CalledProcessError as e:
|
|
85
|
+
print(
|
|
86
|
+
f"Failed to install the latest version of ts-backend-check with error {e}. Please check the error message and report any issues to the team at https://github.com/activist-org/ts-backend-check/issues."
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
if __name__ == "__main__":
|
|
91
|
+
upgrade_cli()
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
# SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
"""
|
|
3
|
+
Functions for checking current version of the ts-backend-check CLI.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import importlib.metadata
|
|
7
|
+
from typing import Any, Dict
|
|
8
|
+
|
|
9
|
+
import requests
|
|
10
|
+
|
|
11
|
+
UNKNOWN_VERSION = "Unknown ts-backend-check version"
|
|
12
|
+
UNKNOWN_VERSION_NOT_PIP = f"{UNKNOWN_VERSION} (Not installed via pip)"
|
|
13
|
+
UNKNOWN_VERSION_NOT_FETCHED = f"{UNKNOWN_VERSION} (Unable to fetch version)"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_local_version() -> str:
|
|
17
|
+
"""
|
|
18
|
+
Get the local version of the ts-backend-check package.
|
|
19
|
+
|
|
20
|
+
Returns
|
|
21
|
+
-------
|
|
22
|
+
str
|
|
23
|
+
The version of the installed ts-backend-check package, or a message indicating
|
|
24
|
+
that the package is not installed via pip.
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
return importlib.metadata.version("ts-backend-check")
|
|
28
|
+
|
|
29
|
+
except importlib.metadata.PackageNotFoundError:
|
|
30
|
+
return UNKNOWN_VERSION_NOT_PIP
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_latest_version() -> Any:
|
|
34
|
+
"""
|
|
35
|
+
Get the latest version of the ts-backend-check package from GitHub.
|
|
36
|
+
|
|
37
|
+
Returns
|
|
38
|
+
-------
|
|
39
|
+
Any
|
|
40
|
+
The latest version of the ts-backend-check package, or a message indicating
|
|
41
|
+
that the version could not be fetched.
|
|
42
|
+
"""
|
|
43
|
+
try:
|
|
44
|
+
response = requests.get(
|
|
45
|
+
"https://api.github.com/repos/activist-org/ts-backend-check/releases/latest"
|
|
46
|
+
)
|
|
47
|
+
response_data: Dict[str, Any] = response.json()
|
|
48
|
+
return response_data["name"]
|
|
49
|
+
|
|
50
|
+
except Exception:
|
|
51
|
+
return UNKNOWN_VERSION_NOT_FETCHED
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_version_message() -> str:
|
|
55
|
+
"""
|
|
56
|
+
Get a message indicating the local and latest versions of the ts-backend-check package.
|
|
57
|
+
|
|
58
|
+
Returns
|
|
59
|
+
-------
|
|
60
|
+
str
|
|
61
|
+
A message indicating the local version, the latest version, and whether
|
|
62
|
+
an upgrade is available.
|
|
63
|
+
"""
|
|
64
|
+
local_version = get_local_version()
|
|
65
|
+
latest_version = get_latest_version()
|
|
66
|
+
|
|
67
|
+
if local_version == UNKNOWN_VERSION_NOT_PIP:
|
|
68
|
+
return UNKNOWN_VERSION_NOT_PIP
|
|
69
|
+
|
|
70
|
+
elif latest_version == UNKNOWN_VERSION_NOT_FETCHED:
|
|
71
|
+
return UNKNOWN_VERSION_NOT_FETCHED
|
|
72
|
+
|
|
73
|
+
local_version_clean = local_version.strip()
|
|
74
|
+
latest_version_clean = latest_version.replace("ts-backend-check", "").strip()
|
|
75
|
+
|
|
76
|
+
if local_version_clean == latest_version_clean:
|
|
77
|
+
return f"ts-backend-check v{local_version_clean}"
|
|
78
|
+
|
|
79
|
+
elif local_version_clean > latest_version_clean:
|
|
80
|
+
return f"ts-backend-check v{local_version_clean} is higher than the currently released version ts-backend-check v{latest_version_clean}. Hopefully this is a development build, and if so, thanks for your work on ts-backend-check! If not, please report this to the team at https://github.com/activist-org/ts-backend-check/issues."
|
|
81
|
+
|
|
82
|
+
else:
|
|
83
|
+
return f"ts-backend-check v{local_version_clean} (Upgrade available: ts-backend-check v{latest_version_clean}). To upgrade: ts-backend-check -u"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
if __name__ == "__main__":
|
|
87
|
+
print(get_version_message())
|
|
@@ -96,7 +96,7 @@ def extract_model_fields(models_file: str) -> Dict[str, Set[str]]:
|
|
|
96
96
|
"""
|
|
97
97
|
with open(models_file, "r", encoding="utf-8") as f:
|
|
98
98
|
content = f.read().strip()
|
|
99
|
-
# Skip any empty lines at the beginning
|
|
99
|
+
# Skip any empty lines at the beginning.
|
|
100
100
|
while content.startswith("\n"):
|
|
101
101
|
content = content[1:]
|
|
102
102
|
|
|
@@ -60,24 +60,17 @@ class TypeScriptParser:
|
|
|
60
60
|
|
|
61
61
|
return interfaces
|
|
62
62
|
|
|
63
|
-
def
|
|
63
|
+
def get_ignored_fields(self) -> Set[str]:
|
|
64
64
|
"""
|
|
65
|
-
Extract fields marked as
|
|
65
|
+
Extract fields marked as ignored in comments.
|
|
66
66
|
|
|
67
67
|
Returns
|
|
68
68
|
-------
|
|
69
69
|
Set[str]
|
|
70
|
-
The field names that are marked with a backend-
|
|
70
|
+
The field names that are marked with a ts-backend-check-ignore identifier to ignore them.
|
|
71
71
|
"""
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
r"//.*?(\w+)\s+is\s+backend\s+only",
|
|
75
|
-
r"//\s*@backend-only\s+(\w+)",
|
|
76
|
-
r"//.*?backend-only:\s*(\w+)",
|
|
77
|
-
]
|
|
78
|
-
return {
|
|
79
|
-
match for pattern in patterns for match in re.findall(pattern, self.content)
|
|
80
|
-
}
|
|
72
|
+
ignore_pattern = r"//.*?ts-backend-check: ignore field\s+(\w+)"
|
|
73
|
+
return set(re.findall(ignore_pattern, self.content))
|
|
81
74
|
|
|
82
75
|
@staticmethod
|
|
83
76
|
def _extract_fields(interface_body: str) -> Set[str]:
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
#
|
|
2
|
+
# This file is autogenerated by pip-compile with Python 3.13
|
|
3
|
+
# by the following command:
|
|
4
|
+
#
|
|
5
|
+
# pip-compile requirements.in
|
|
6
|
+
#
|
|
7
|
+
certifi>=2025.11.12
|
|
8
|
+
# via requests
|
|
9
|
+
charset-normalizer>=3.4.4
|
|
10
|
+
# via requests
|
|
11
|
+
idna>=3.11
|
|
12
|
+
# via requests
|
|
13
|
+
markdown-it-py>=4.0.0
|
|
14
|
+
# via rich
|
|
15
|
+
mdurl>=0.1.2
|
|
16
|
+
# via markdown-it-py
|
|
17
|
+
pygments>=2.19.2
|
|
18
|
+
# via rich
|
|
19
|
+
pyyaml>=6.0.3
|
|
20
|
+
# via -r requirements.in
|
|
21
|
+
requests>=2.32.5
|
|
22
|
+
# via -r requirements.in
|
|
23
|
+
rich>=14.2.0
|
|
24
|
+
# via -r requirements.in
|
|
25
|
+
urllib3>=2.6.2
|
|
26
|
+
# via requests
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ts-backend-check
|
|
3
|
-
Version: 1.1
|
|
3
|
+
Version: 1.2.1
|
|
4
4
|
Summary: Check TypeScript types against their corresponding backend models to assure that all fields have been accounted for.
|
|
5
5
|
Home-page: https://github.com/activist-org/ts-backend-check
|
|
6
6
|
Author: ts-backend-check developers
|
|
@@ -12,16 +12,25 @@ Classifier: Intended Audience :: Developers
|
|
|
12
12
|
Classifier: License :: OSI Approved :: GNU General Public License v3 (GPLv3)
|
|
13
13
|
Classifier: Programming Language :: Python
|
|
14
14
|
Classifier: Programming Language :: Python :: 3
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
17
15
|
Classifier: Programming Language :: Python :: 3.10
|
|
18
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
18
|
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
20
|
Classifier: Operating System :: OS Independent
|
|
22
|
-
Requires-Python: >=3.
|
|
21
|
+
Requires-Python: >=3.10
|
|
23
22
|
Description-Content-Type: text/markdown
|
|
24
23
|
License-File: LICENSE.txt
|
|
24
|
+
Requires-Dist: certifi>=2025.11.12
|
|
25
|
+
Requires-Dist: charset-normalizer>=3.4.4
|
|
26
|
+
Requires-Dist: idna>=3.11
|
|
27
|
+
Requires-Dist: markdown-it-py>=4.0.0
|
|
28
|
+
Requires-Dist: mdurl>=0.1.2
|
|
29
|
+
Requires-Dist: pygments>=2.19.2
|
|
30
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
31
|
+
Requires-Dist: requests>=2.32.5
|
|
32
|
+
Requires-Dist: rich>=14.2.0
|
|
33
|
+
Requires-Dist: urllib3>=2.6.2
|
|
25
34
|
Dynamic: author
|
|
26
35
|
Dynamic: author-email
|
|
27
36
|
Dynamic: classifier
|
|
@@ -29,6 +38,7 @@ Dynamic: description
|
|
|
29
38
|
Dynamic: description-content-type
|
|
30
39
|
Dynamic: home-page
|
|
31
40
|
Dynamic: license-file
|
|
41
|
+
Dynamic: requires-dist
|
|
32
42
|
Dynamic: requires-python
|
|
33
43
|
Dynamic: summary
|
|
34
44
|
|
|
@@ -37,7 +47,8 @@ Dynamic: summary
|
|
|
37
47
|
</div>
|
|
38
48
|
|
|
39
49
|
[](http://ts-backend-check.readthedocs.io/en/latest/)
|
|
40
|
-
[](https://github.com/activist-org/ts-backend-check/actions/workflows/pr_ci.yaml)
|
|
51
|
+
[](https://github.com/activist-org/ts-backend-check/actions/workflows/python_package_ci.yaml)
|
|
41
52
|
[](https://github.com/activist-org/ts-backend-check/issues)
|
|
42
53
|
[](https://github.com/activist-org/ts-backend-check/blob/main/CONTRIBUTING.md)
|
|
43
54
|
[](https://pypi.org/project/ts-backend-check/)
|
|
@@ -56,10 +67,10 @@ Dynamic: summary
|
|
|
56
67
|
|
|
57
68
|
- [Usage](#usage-)
|
|
58
69
|
- [Contributing](#contributing-)
|
|
59
|
-
- [Environment setup](#environment-setup)
|
|
70
|
+
- [Environment setup](#environment-setup-)
|
|
60
71
|
- [Contributors](#contributors-)
|
|
61
72
|
|
|
62
|
-
<a id="usage"></a>
|
|
73
|
+
<a id="usage-"></a>
|
|
63
74
|
|
|
64
75
|
## Usage [`⇧`](#contents)
|
|
65
76
|
|
|
@@ -84,18 +95,35 @@ ts-backend-check --help
|
|
|
84
95
|
|
|
85
96
|
# Check a TypeScript type against a backend model:
|
|
86
97
|
ts-backend-check -bmf <backend-model-file> -tsf <typescript-file>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Example success and error outputs for the CLI are:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
ts-backend-check -bmf backend/models/user.py -tsf frontend/types/user.ts
|
|
104
|
+
|
|
105
|
+
✅ Success: All backend models are synced with their corresponding TypeScript interfaces for the provided files.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
ts-backend-check -bmf backend/models/user.py -tsf frontend/types/user.ts
|
|
110
|
+
|
|
111
|
+
❌ ts-backend-check error: There are inconsistencies between the provided backend models and TypeScript interfaces. Please see the output below for details.
|
|
112
|
+
|
|
113
|
+
Field 'user_name' (camelCase: 'userName') from model 'UserModel' is missing in the TypeScript interfaces.
|
|
114
|
+
Expected to find this field in the frontend interface: User
|
|
115
|
+
To ignore this field, add the following comment to the TypeScript interface: '// ts-backend-check: ignore field userName'
|
|
87
116
|
|
|
88
|
-
|
|
89
|
-
ts-backend-check -bmf src/models/user.py -tsf src/types/user.ts
|
|
117
|
+
Please fix the 1 field above to have the backend models of backend/models/user.py synced with the typescript interfaces of frontend/types/user.ts.
|
|
90
118
|
```
|
|
91
119
|
|
|
92
|
-
<a id="contributing"></a>
|
|
120
|
+
<a id="contributing-"></a>
|
|
93
121
|
|
|
94
122
|
# Contributing [`⇧`](#contents)
|
|
95
123
|
|
|
96
124
|
<a href="https://matrix.to/#/#activist_community:matrix.org"><img src="https://raw.githubusercontent.com/activist-org/Organization/main/resources/images/logos/MatrixLogoGrey.png" width="175" alt="Public Matrix Chat" align="right"></a>
|
|
97
125
|
|
|
98
|
-
activist uses [Matrix](https://matrix.org/) for internal communication. You're more than welcome to [join us in our public chat rooms](https://matrix.to/#/#activist_community:matrix.org) to share ideas, ask questions or just say hi to the team :)
|
|
126
|
+
activist uses [Matrix](https://matrix.org/) for internal communication. You're more than welcome to [join us in our public chat rooms](https://matrix.to/#/#activist_community:matrix.org) to share ideas, ask questions or just say hi to the team :) We'd suggest that you use the [Element](https://element.io/) client and [Element X](https://element.io/app) for a mobile app.
|
|
99
127
|
|
|
100
128
|
Please see the [contribution guidelines](CONTRIBUTING.md) if you are interested in contributing. Work that is in progress or could be implemented is tracked in the [issues](https://github.com/activist-org/ts-backend-check/issues) and [projects](https://github.com/activist-org/ts-backend-check/projects).
|
|
101
129
|
|
|
@@ -106,7 +134,7 @@ Also check the [`-next release-`](https://github.com/activist-org/ts-backend-che
|
|
|
106
134
|
|
|
107
135
|
We would be happy to discuss granting you further rights as a contributor after your first pull requests, with a maintainer role then being possible after continued interest in the project. activist seeks to be an inclusive, diverse and supportive organization. We'd love to have you on the team!
|
|
108
136
|
|
|
109
|
-
<a id="how-you-can-help"></a>
|
|
137
|
+
<a id="how-you-can-help-"></a>
|
|
110
138
|
|
|
111
139
|
## How you can help [`⇧`](#contents)
|
|
112
140
|
|
|
@@ -114,7 +142,7 @@ We would be happy to discuss granting you further rights as a contributor after
|
|
|
114
142
|
- Working with us on [new features](https://github.com/activist-org/ts-backend-check/issues?q=is%3Aissue+is%3Aopen+label%3Afeature) ✨
|
|
115
143
|
- [Documentation](https://github.com/activist-org/ts-backend-check/issues?q=is%3Aissue+is%3Aopen+label%3Adocumentation) for onboarding and project cohesion 📝
|
|
116
144
|
|
|
117
|
-
<a id="environment-setup"></a>
|
|
145
|
+
<a id="environment-setup-"></a>
|
|
118
146
|
|
|
119
147
|
# Environment setup [`⇧`](#contents)
|
|
120
148
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
ts_backend_check/__init__.py,sha256=HSiEqWvU3Q4a6D7tYsoq3-B-g1HdUOGngEeGV45j8ak,172
|
|
2
|
+
ts_backend_check/checker.py,sha256=4QtPdiEnpRVIO4BpcHeCz-sjG2bqTg43M4gfntRnHj4,5956
|
|
3
|
+
ts_backend_check/django_parser.py,sha256=CFg4-zV5BsVhuq-nVQEYD9nS25JSaHbIs2Xr3LE3KWY,3241
|
|
4
|
+
ts_backend_check/typescript_parser.py,sha256=BqBGOjsf-Wlh6ceaK-G2vLm5AMiEiwX-XnJQrr8P-gQ,2885
|
|
5
|
+
ts_backend_check/utils.py,sha256=N9_25_wW2g3dkmGGLWHQj_AHsXI9Rx2XqRnYxT5i2Rk,897
|
|
6
|
+
ts_backend_check/cli/__init__.py,sha256=wJK9tO9MtI10L0xRjrk7WP_qZZRBjbFw1U9jJbIbX7I,108
|
|
7
|
+
ts_backend_check/cli/check_blank.py,sha256=gg6v01UYNmzs8eLasxauCaBCfN9esUtiAwqhqNTzE3w,3377
|
|
8
|
+
ts_backend_check/cli/config.py,sha256=AgzjY3qbgW4ya5JJEdKaBQ9J8h0Ip-39xJNbAjFpA6s,4463
|
|
9
|
+
ts_backend_check/cli/main.py,sha256=uDG-Tzc0avs_2Rug-XkUvRf3OFZO1clr7QSrAPqXVos,4405
|
|
10
|
+
ts_backend_check/cli/upgrade.py,sha256=P2LdIDCLdOFs-fOMuKLQoUMijQIyKEIA5PFZVsAUwDg,2952
|
|
11
|
+
ts_backend_check/cli/version.py,sha256=lsocMaxAfF-FHW9O-NGH1sAo6svjuLgdGFxaC-XVoZc,2845
|
|
12
|
+
ts_backend_check/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
+
ts_backend_check/parsers/django_parser.py,sha256=U5OP6IyrSdwI9rDUGU4GbWTDfozwhretWg7lmF6QGB8,3242
|
|
14
|
+
ts_backend_check/parsers/typescript_parser.py,sha256=UAExnEKxuFREFY6bJgQreG0b7SyVhWRaHw5TjN0CSU4,2680
|
|
15
|
+
ts_backend_check-1.2.1.data/data/requirements.txt,sha256=ixdwNPm86WRSX0oBBaNegCQL7HQP8evsCRAdEFHLxGk,540
|
|
16
|
+
ts_backend_check-1.2.1.dist-info/licenses/LICENSE.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
17
|
+
ts_backend_check-1.2.1.dist-info/METADATA,sha256=q5Mm6sTE9Ga3EJEJ65VdSaED1leRiOGTxxEplGKUbxs,11259
|
|
18
|
+
ts_backend_check-1.2.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
19
|
+
ts_backend_check-1.2.1.dist-info/entry_points.txt,sha256=QeY7RJu20otBnQlhMMZxPL8nB3mP3M3U3h3nOlSFWSU,68
|
|
20
|
+
ts_backend_check-1.2.1.dist-info/top_level.txt,sha256=VzfNWQ3fPNdl-OBdtUKsUWR8Asdd27E3OJNUg2oQU8Y,17
|
|
21
|
+
ts_backend_check-1.2.1.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
ts_backend_check/__init__.py,sha256=HSiEqWvU3Q4a6D7tYsoq3-B-g1HdUOGngEeGV45j8ak,172
|
|
2
|
-
ts_backend_check/checker.py,sha256=svfIJ9lNaSSy-zSGsM4wYD9Z9JK_xL5l6JA-w9pdf_o,5791
|
|
3
|
-
ts_backend_check/django_parser.py,sha256=CFg4-zV5BsVhuq-nVQEYD9nS25JSaHbIs2Xr3LE3KWY,3241
|
|
4
|
-
ts_backend_check/typescript_parser.py,sha256=BqBGOjsf-Wlh6ceaK-G2vLm5AMiEiwX-XnJQrr8P-gQ,2885
|
|
5
|
-
ts_backend_check/utils.py,sha256=N9_25_wW2g3dkmGGLWHQj_AHsXI9Rx2XqRnYxT5i2Rk,897
|
|
6
|
-
ts_backend_check/cli/__init__.py,sha256=wJK9tO9MtI10L0xRjrk7WP_qZZRBjbFw1U9jJbIbX7I,108
|
|
7
|
-
ts_backend_check/cli/main.py,sha256=C3JCGv4QNo-xlp-MlCK49nzGAzuwWYDYLBi-OS7bvXQ,2712
|
|
8
|
-
ts_backend_check/parsers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
|
-
ts_backend_check/parsers/django_parser.py,sha256=CFg4-zV5BsVhuq-nVQEYD9nS25JSaHbIs2Xr3LE3KWY,3241
|
|
10
|
-
ts_backend_check/parsers/typescript_parser.py,sha256=BqBGOjsf-Wlh6ceaK-G2vLm5AMiEiwX-XnJQrr8P-gQ,2885
|
|
11
|
-
ts_backend_check-1.1.0.data/data/requirements.txt,sha256=0FbFr7FTG31FEs_FMsN3MrAIFJIeGdDC6POOOb2FeA0,134
|
|
12
|
-
ts_backend_check-1.1.0.dist-info/licenses/LICENSE.txt,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
13
|
-
ts_backend_check-1.1.0.dist-info/METADATA,sha256=XSvF56GNwNfAJa1e8l1pa42Jz_n_9hG8TmR6yDx-N_A,9734
|
|
14
|
-
ts_backend_check-1.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
15
|
-
ts_backend_check-1.1.0.dist-info/entry_points.txt,sha256=QeY7RJu20otBnQlhMMZxPL8nB3mP3M3U3h3nOlSFWSU,68
|
|
16
|
-
ts_backend_check-1.1.0.dist-info/top_level.txt,sha256=VzfNWQ3fPNdl-OBdtUKsUWR8Asdd27E3OJNUg2oQU8Y,17
|
|
17
|
-
ts_backend_check-1.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|