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.
@@ -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.get_backend_only_fields()
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}\n"
153
- f"Searched for interfaces: {', '.join(potential_names)}"
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"is missing in TypeScript types.\n"
183
- f"Expected to find in interface(s): {', '.join(interfaces.keys())}\n"
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()
@@ -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
- ROOT_DIR = Path(__file__).cwd()
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
- print(
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
- print(
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
- print("Missing typescript fields found: ")
74
- print("\n".join(missing))
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
- print(
78
- f"\nPlease fix the {len(missing)} {field_or_fields} to have the backend models synced with the typescript interfaces."
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
- print("All models are synced with their corresponding TypeScript interfaces.")
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 get_backend_only_fields(self) -> Set[str]:
63
+ def get_ignored_fields(self) -> Set[str]:
64
64
  """
65
- Extract fields marked as backend-only in comments.
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-only identifier to ignore them.
70
+ The field names that are marked with a ts-backend-check-ignore identifier to ignore them.
71
71
  """
72
- patterns = [
73
- r"//.*?Note:\s*(\w+)\s+is\s+backend\s+only",
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.0
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.8
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
  [![rtd](https://img.shields.io/readthedocs/ts-backend-check.svg?label=%20&logo=read-the-docs&logoColor=ffffff)](http://ts-backend-check.readthedocs.io/en/latest/)
40
- [![ci_backend](https://img.shields.io/github/actions/workflow/status/activist-org/ts-backend-check/pr_ci.yaml?branch=main&label=%20&logo=pytest&logoColor=ffffff)](https://github.com/activist-org/ts-backend-check/actions/workflows/pr_ci_backend.yaml)
50
+ [![pr_ci](https://img.shields.io/github/actions/workflow/status/activist-org/ts-backend-check/pr_ci.yaml?branch=main&label=%20&logo=ruff&logoColor=ffffff)](https://github.com/activist-org/ts-backend-check/actions/workflows/pr_ci.yaml)
51
+ [![python_package_ci](https://img.shields.io/github/actions/workflow/status/activist-org/ts-backend-check/python_package_ci.yaml?branch=main&label=%20&logo=pytest&logoColor=ffffff)](https://github.com/activist-org/ts-backend-check/actions/workflows/python_package_ci.yaml)
41
52
  [![issues](https://img.shields.io/github/issues/activist-org/ts-backend-check?label=%20&logo=github)](https://github.com/activist-org/ts-backend-check/issues)
42
53
  [![python](https://img.shields.io/badge/Python-4B8BBE.svg?logo=python&logoColor=ffffff)](https://github.com/activist-org/ts-backend-check/blob/main/CONTRIBUTING.md)
43
54
  [![pypi](https://img.shields.io/pypi/v/ts-backend-check.svg?label=%20&color=4B8BBE)](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
- # Example command:
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,6 +0,0 @@
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
- #
@@ -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,,