django-angular3 0.1.0__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.
- django_angular3/__init__.py +5 -0
- django_angular3/__main__.py +4 -0
- django_angular3/admin.py +4 -0
- django_angular3/angular.py +335 -0
- django_angular3/apps.py +7 -0
- django_angular3/build.py +66 -0
- django_angular3/cli.py +313 -0
- django_angular3/config.py +110 -0
- django_angular3/documents.py +52 -0
- django_angular3/examples/01_simple_crm/django-angular3.json +10 -0
- django_angular3/examples/01_simple_crm/manage.py +23 -0
- django_angular3/examples/01_simple_crm/shop/__init__.py +0 -0
- django_angular3/examples/01_simple_crm/shop/admin.py +17 -0
- django_angular3/examples/01_simple_crm/shop/apps.py +6 -0
- django_angular3/examples/01_simple_crm/shop/models.py +20 -0
- django_angular3/examples/01_simple_crm/shop/serializers.py +15 -0
- django_angular3/examples/01_simple_crm/shop/tests.py +1 -0
- django_angular3/examples/01_simple_crm/shop/views.py +24 -0
- django_angular3/examples/01_simple_crm/simple_crm/__init__.py +0 -0
- django_angular3/examples/01_simple_crm/simple_crm/asgi.py +7 -0
- django_angular3/examples/01_simple_crm/simple_crm/settings.py +107 -0
- django_angular3/examples/01_simple_crm/simple_crm/urls.py +14 -0
- django_angular3/examples/01_simple_crm/simple_crm/wsgi.py +7 -0
- django_angular3/examples/01_simple_crm/ui.json +13 -0
- django_angular3/examples/__init__.py +0 -0
- django_angular3/management/__init__.py +1 -0
- django_angular3/management/commands/__init__.py +1 -0
- django_angular3/management/commands/_base.py +57 -0
- django_angular3/management/commands/build_app.py +483 -0
- django_angular3/management/commands/export_schema.py +106 -0
- django_angular3/management/commands/ng_add.py +19 -0
- django_angular3/management/commands/ng_build.py +6 -0
- django_angular3/management/commands/ng_config.py +6 -0
- django_angular3/management/commands/ng_gen_app.py +22 -0
- django_angular3/management/commands/ng_new.py +6 -0
- django_angular3/management/commands/ng_openapi_gen.py +6 -0
- django_angular3/management/commands/ng_workspace.py +6 -0
- django_angular3/management/commands/ng_workspace_delete.py +6 -0
- django_angular3/management/commands/ng_workspace_modify.py +6 -0
- django_angular3/migrations/__init__.py +2 -0
- django_angular3/models.py +2 -0
- django_angular3/settings.py +123 -0
- django_angular3/static/django_angular3/.gitkeep +0 -0
- django_angular3/templates/django_angular3/.gitkeep +0 -0
- django_angular3/tools.py +134 -0
- django_angular3/urls.py +6 -0
- django_angular3/validation.py +169 -0
- django_angular3/views.py +2 -0
- django_angular3-0.1.0.dist-info/METADATA +296 -0
- django_angular3-0.1.0.dist-info/RECORD +54 -0
- django_angular3-0.1.0.dist-info/WHEEL +5 -0
- django_angular3-0.1.0.dist-info/entry_points.txt +2 -0
- django_angular3-0.1.0.dist-info/licenses/LICENSE +21 -0
- django_angular3-0.1.0.dist-info/top_level.txt +1 -0
django_angular3/admin.py
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import subprocess
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from .config import ProjectConfig, load_project_config
|
|
11
|
+
from .settings import AngularCommandError, AngularSettings, load_angular_settings
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class AngularInvocation:
|
|
16
|
+
"""A single Angular CLI invocation and the directory it should run from."""
|
|
17
|
+
|
|
18
|
+
command_name: str
|
|
19
|
+
argv: tuple[str, ...]
|
|
20
|
+
cwd: Path
|
|
21
|
+
|
|
22
|
+
def to_dict(self) -> dict[str, object]:
|
|
23
|
+
return {"argv": list(self.argv), "cwd": str(self.cwd)}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
AngularInvocationBuilder = Callable[..., list[AngularInvocation]]
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def resolve_angular_command(
|
|
30
|
+
command_name: str, config_path: str | Path | None = None, **options: Any
|
|
31
|
+
) -> list[AngularInvocation]:
|
|
32
|
+
"""Resolve one logical djng command into an ordered list of subprocess calls.
|
|
33
|
+
|
|
34
|
+
This function does not execute Angular or Node tooling. It loads the
|
|
35
|
+
project configuration and Angular settings, chooses the matching command
|
|
36
|
+
builder, and returns the concrete ``AngularInvocation`` list that a dry run
|
|
37
|
+
can print or ``execute_invocations`` can later run.
|
|
38
|
+
"""
|
|
39
|
+
settings = load_angular_settings()
|
|
40
|
+
config = load_project_config(config_path or settings.config_path)
|
|
41
|
+
|
|
42
|
+
builder = _COMMAND_BUILDERS.get(command_name)
|
|
43
|
+
if builder is None:
|
|
44
|
+
raise AngularCommandError(f"Unknown Angular command '{command_name}'.")
|
|
45
|
+
|
|
46
|
+
return builder(config, settings, **options)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def format_invocations(invocations: list[AngularInvocation]) -> str:
|
|
50
|
+
"""Serialize resolved subprocess calls for dry-run output."""
|
|
51
|
+
return json.dumps([invocation.to_dict() for invocation in invocations], indent=2)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def execute_invocations(
|
|
55
|
+
invocations: list[AngularInvocation], settings: AngularSettings | None = None
|
|
56
|
+
) -> None:
|
|
57
|
+
"""Run a previously resolved ordered list of subprocess calls."""
|
|
58
|
+
active_settings = settings or load_angular_settings()
|
|
59
|
+
for invocation in invocations:
|
|
60
|
+
_ensure_command_is_allowed(invocation.command_name, active_settings)
|
|
61
|
+
try:
|
|
62
|
+
subprocess.run(invocation.argv, cwd=invocation.cwd, check=True)
|
|
63
|
+
except FileNotFoundError as exc:
|
|
64
|
+
raise AngularCommandError(
|
|
65
|
+
f"Command not found: {invocation.argv[0]}"
|
|
66
|
+
) from exc
|
|
67
|
+
except subprocess.CalledProcessError as exc:
|
|
68
|
+
raise AngularCommandError(
|
|
69
|
+
f"Command '{' '.join(invocation.argv)}' failed with exit code "
|
|
70
|
+
f"{exc.returncode}."
|
|
71
|
+
) from exc
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def build_ng_new_invocations(
|
|
75
|
+
config: ProjectConfig, settings: AngularSettings, **_: Any
|
|
76
|
+
) -> list[AngularInvocation]:
|
|
77
|
+
# Ensure the parent directory exists before calling subprocess.run
|
|
78
|
+
config.angular_output.parent.mkdir(parents=True, exist_ok=True)
|
|
79
|
+
return [
|
|
80
|
+
AngularInvocation(
|
|
81
|
+
command_name="ng_new",
|
|
82
|
+
argv=(
|
|
83
|
+
settings.ng_executable,
|
|
84
|
+
"new",
|
|
85
|
+
config.project_name,
|
|
86
|
+
"--defaults",
|
|
87
|
+
"--skip-git",
|
|
88
|
+
"--skip-install",
|
|
89
|
+
"--no-create-application",
|
|
90
|
+
f"--package-manager={settings.package_manager}",
|
|
91
|
+
f"--directory={config.angular_output.name}",
|
|
92
|
+
),
|
|
93
|
+
cwd=config.angular_output.parent,
|
|
94
|
+
)
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def build_ng_workspace_schematic_invocations(
|
|
99
|
+
config: ProjectConfig, settings: AngularSettings, **_: Any
|
|
100
|
+
) -> list[AngularInvocation]:
|
|
101
|
+
return [
|
|
102
|
+
AngularInvocation(
|
|
103
|
+
command_name="ng_workspace",
|
|
104
|
+
argv=(
|
|
105
|
+
settings.ng_executable,
|
|
106
|
+
"generate",
|
|
107
|
+
"angular-django2:ng-workspace",
|
|
108
|
+
config.project_name,
|
|
109
|
+
),
|
|
110
|
+
cwd=config.angular_output,
|
|
111
|
+
)
|
|
112
|
+
]
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def build_ng_config_invocations(
|
|
116
|
+
config: ProjectConfig, settings: AngularSettings, **_: Any
|
|
117
|
+
) -> list[AngularInvocation]:
|
|
118
|
+
return [
|
|
119
|
+
AngularInvocation(
|
|
120
|
+
command_name="ng_config",
|
|
121
|
+
argv=(
|
|
122
|
+
settings.ng_executable,
|
|
123
|
+
"config",
|
|
124
|
+
"cli.packageManager",
|
|
125
|
+
settings.package_manager,
|
|
126
|
+
),
|
|
127
|
+
cwd=config.angular_output,
|
|
128
|
+
),
|
|
129
|
+
AngularInvocation(
|
|
130
|
+
command_name="ng_config",
|
|
131
|
+
argv=(
|
|
132
|
+
settings.ng_executable,
|
|
133
|
+
"config",
|
|
134
|
+
"schematics.@schematics/angular:application.style",
|
|
135
|
+
settings.style,
|
|
136
|
+
),
|
|
137
|
+
cwd=config.angular_output,
|
|
138
|
+
),
|
|
139
|
+
AngularInvocation(
|
|
140
|
+
command_name="ng_config",
|
|
141
|
+
argv=(
|
|
142
|
+
settings.ng_executable,
|
|
143
|
+
"config",
|
|
144
|
+
"schematics.@schematics/angular:application.routing",
|
|
145
|
+
_stringify_bool(settings.routing),
|
|
146
|
+
),
|
|
147
|
+
cwd=config.angular_output,
|
|
148
|
+
),
|
|
149
|
+
]
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def build_ng_build_invocations(
|
|
153
|
+
config: ProjectConfig, settings: AngularSettings, **_: Any
|
|
154
|
+
) -> list[AngularInvocation]:
|
|
155
|
+
return [
|
|
156
|
+
AngularInvocation(
|
|
157
|
+
command_name="ng_build",
|
|
158
|
+
argv=(
|
|
159
|
+
settings.ng_executable,
|
|
160
|
+
"build",
|
|
161
|
+
config.project_name,
|
|
162
|
+
f"--configuration={settings.build_configuration}",
|
|
163
|
+
),
|
|
164
|
+
cwd=config.angular_output,
|
|
165
|
+
)
|
|
166
|
+
]
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def build_ng_gen_app_invocations(
|
|
170
|
+
config: ProjectConfig,
|
|
171
|
+
settings: AngularSettings,
|
|
172
|
+
*,
|
|
173
|
+
app_name: str | None = None,
|
|
174
|
+
**_: Any,
|
|
175
|
+
) -> list[AngularInvocation]:
|
|
176
|
+
target_app = app_name or config.project_name
|
|
177
|
+
return [
|
|
178
|
+
AngularInvocation(
|
|
179
|
+
command_name="ng_gen_app",
|
|
180
|
+
argv=(
|
|
181
|
+
settings.ng_executable,
|
|
182
|
+
"generate",
|
|
183
|
+
"angular-django2:ng-app",
|
|
184
|
+
target_app,
|
|
185
|
+
f"--style={settings.style}",
|
|
186
|
+
"--routing" if settings.routing else "--no-routing",
|
|
187
|
+
),
|
|
188
|
+
cwd=config.angular_output,
|
|
189
|
+
)
|
|
190
|
+
]
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def build_ng_openapi_gen_invocations(
|
|
194
|
+
config: ProjectConfig, settings: AngularSettings, **_: Any
|
|
195
|
+
) -> list[AngularInvocation]:
|
|
196
|
+
source = config.ng_openapi_gen_config or config.openapi_source
|
|
197
|
+
source_flag = "-c" if config.ng_openapi_gen_config else "-i"
|
|
198
|
+
return [
|
|
199
|
+
AngularInvocation(
|
|
200
|
+
command_name="ng_openapi_gen",
|
|
201
|
+
argv=(
|
|
202
|
+
settings.pnpm_executable,
|
|
203
|
+
"exec",
|
|
204
|
+
"ng-openapi-gen",
|
|
205
|
+
source_flag,
|
|
206
|
+
str(source),
|
|
207
|
+
),
|
|
208
|
+
cwd=config.angular_output,
|
|
209
|
+
)
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def build_ng_add_invocations(
|
|
214
|
+
config: ProjectConfig,
|
|
215
|
+
settings: AngularSettings,
|
|
216
|
+
*,
|
|
217
|
+
package: str | None = None,
|
|
218
|
+
**_: Any,
|
|
219
|
+
) -> list[AngularInvocation]:
|
|
220
|
+
target_package = package or settings.ng_add_package
|
|
221
|
+
return [
|
|
222
|
+
AngularInvocation(
|
|
223
|
+
command_name="ng_add",
|
|
224
|
+
argv=(
|
|
225
|
+
settings.ng_executable,
|
|
226
|
+
"add",
|
|
227
|
+
target_package,
|
|
228
|
+
"--skip-confirmation",
|
|
229
|
+
),
|
|
230
|
+
cwd=config.angular_output,
|
|
231
|
+
)
|
|
232
|
+
]
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def build_ng_workspace_invocations(
|
|
236
|
+
config: ProjectConfig, settings: AngularSettings, **_: Any
|
|
237
|
+
) -> list[AngularInvocation]:
|
|
238
|
+
"""Create and bootstrap an Angular workspace with angular-django2 defaults."""
|
|
239
|
+
invocations = _relabel_invocations(
|
|
240
|
+
build_ng_new_invocations(config, settings), "ng_workspace"
|
|
241
|
+
)
|
|
242
|
+
invocations.extend(
|
|
243
|
+
_relabel_invocations(
|
|
244
|
+
build_ng_config_invocations(config, settings), "ng_workspace"
|
|
245
|
+
)
|
|
246
|
+
)
|
|
247
|
+
invocations.extend(
|
|
248
|
+
_relabel_invocations(build_ng_add_invocations(config, settings), "ng_workspace")
|
|
249
|
+
)
|
|
250
|
+
invocations.extend(build_ng_workspace_schematic_invocations(config, settings))
|
|
251
|
+
return invocations
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def build_ng_workspace_modify_invocations(
|
|
255
|
+
config: ProjectConfig, settings: AngularSettings, **_: Any
|
|
256
|
+
) -> list[AngularInvocation]:
|
|
257
|
+
"""Reapply workspace defaults, collection registration, and
|
|
258
|
+
workspace scaffolding."""
|
|
259
|
+
invocations = _relabel_invocations(
|
|
260
|
+
build_ng_config_invocations(config, settings), "ng_workspace_modify"
|
|
261
|
+
)
|
|
262
|
+
invocations.extend(
|
|
263
|
+
_relabel_invocations(
|
|
264
|
+
build_ng_add_invocations(config, settings), "ng_workspace_modify"
|
|
265
|
+
)
|
|
266
|
+
)
|
|
267
|
+
invocations.extend(
|
|
268
|
+
_relabel_invocations(
|
|
269
|
+
build_ng_workspace_schematic_invocations(config, settings),
|
|
270
|
+
"ng_workspace_modify",
|
|
271
|
+
)
|
|
272
|
+
)
|
|
273
|
+
return invocations
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def build_ng_workspace_delete_invocations(
|
|
277
|
+
config: ProjectConfig, _settings: AngularSettings, **_: Any
|
|
278
|
+
) -> list[AngularInvocation]:
|
|
279
|
+
"""Delete the entire workspace folder using Python's native
|
|
280
|
+
cross-platform shutil."""
|
|
281
|
+
import sys
|
|
282
|
+
|
|
283
|
+
py_code = (
|
|
284
|
+
f"import shutil; shutil.rmtree(r'{config.angular_output}', ignore_errors=True)"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
return [
|
|
288
|
+
AngularInvocation(
|
|
289
|
+
command_name="ng_workspace_delete",
|
|
290
|
+
argv=(sys.executable, "-c", py_code),
|
|
291
|
+
cwd=config.angular_output.parent,
|
|
292
|
+
)
|
|
293
|
+
]
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
_COMMAND_BUILDERS: dict[str, AngularInvocationBuilder] = {
|
|
297
|
+
"ng_new": build_ng_new_invocations,
|
|
298
|
+
"ng_workspace": build_ng_workspace_invocations,
|
|
299
|
+
"ng_config": build_ng_config_invocations,
|
|
300
|
+
"ng_build": build_ng_build_invocations,
|
|
301
|
+
"ng_gen_app": build_ng_gen_app_invocations,
|
|
302
|
+
"ng_openapi_gen": build_ng_openapi_gen_invocations,
|
|
303
|
+
"ng_add": build_ng_add_invocations,
|
|
304
|
+
"ng_workspace_modify": build_ng_workspace_modify_invocations,
|
|
305
|
+
"ng_workspace_delete": build_ng_workspace_delete_invocations,
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _stringify_bool(value: bool) -> str:
|
|
310
|
+
return "true" if value else "false"
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _relabel_invocations(
|
|
314
|
+
invocations: list[AngularInvocation], command_name: str
|
|
315
|
+
) -> list[AngularInvocation]:
|
|
316
|
+
return [
|
|
317
|
+
AngularInvocation(
|
|
318
|
+
command_name=command_name,
|
|
319
|
+
argv=invocation.argv,
|
|
320
|
+
cwd=invocation.cwd,
|
|
321
|
+
)
|
|
322
|
+
for invocation in invocations
|
|
323
|
+
]
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _ensure_command_is_allowed(command_name: str, settings: AngularSettings) -> None:
|
|
327
|
+
normalized_command_name = command_name.strip().lower()
|
|
328
|
+
if normalized_command_name in settings.command_allowlist:
|
|
329
|
+
return
|
|
330
|
+
|
|
331
|
+
allowed_commands = ", ".join(settings.command_allowlist) or "<none>"
|
|
332
|
+
raise AngularCommandError(
|
|
333
|
+
f"Command '{command_name}' is not allowed. Allowed commands: "
|
|
334
|
+
f"{allowed_commands}."
|
|
335
|
+
)
|
django_angular3/apps.py
ADDED
django_angular3/build.py
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from dataclasses import asdict, dataclass
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from .config import ProjectConfig
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@dataclass(frozen=True)
|
|
11
|
+
class BuildTask:
|
|
12
|
+
name: str
|
|
13
|
+
input: str
|
|
14
|
+
output: str
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True)
|
|
18
|
+
class BuildPlan:
|
|
19
|
+
project_name: str
|
|
20
|
+
config_path: str
|
|
21
|
+
tasks: list[BuildTask]
|
|
22
|
+
|
|
23
|
+
def to_dict(self) -> dict[str, object]:
|
|
24
|
+
return {
|
|
25
|
+
"projectName": self.project_name,
|
|
26
|
+
"configPath": self.config_path,
|
|
27
|
+
"tasks": [asdict(task) for task in self.tasks],
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def create_build_plan(config: ProjectConfig) -> BuildPlan:
|
|
32
|
+
tasks = [
|
|
33
|
+
BuildTask(
|
|
34
|
+
name="validate-openapi",
|
|
35
|
+
input=str(config.openapi_source),
|
|
36
|
+
output="validated OpenAPI document",
|
|
37
|
+
),
|
|
38
|
+
BuildTask(
|
|
39
|
+
name="generate-openapi-clients",
|
|
40
|
+
input=str(config.openapi_generator_config or config.openapi_source),
|
|
41
|
+
output=str(config.angular_output / "generated"),
|
|
42
|
+
),
|
|
43
|
+
BuildTask(
|
|
44
|
+
name="validate-ui",
|
|
45
|
+
input=str(config.ui_source),
|
|
46
|
+
output="validated UI definition document",
|
|
47
|
+
),
|
|
48
|
+
BuildTask(
|
|
49
|
+
name="assemble-angular-application",
|
|
50
|
+
input=str(config.ui_source),
|
|
51
|
+
output=str(config.angular_output),
|
|
52
|
+
),
|
|
53
|
+
]
|
|
54
|
+
return BuildPlan(
|
|
55
|
+
project_name=config.project_name,
|
|
56
|
+
config_path=str(config.config_path),
|
|
57
|
+
tasks=tasks,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def write_build_plan(plan: BuildPlan, output_dir: str | Path) -> Path:
|
|
62
|
+
output_path = Path(output_dir)
|
|
63
|
+
output_path.mkdir(parents=True, exist_ok=True)
|
|
64
|
+
plan_path = output_path / "plan.json"
|
|
65
|
+
plan_path.write_text(json.dumps(plan.to_dict(), indent=2), encoding="utf-8")
|
|
66
|
+
return plan_path
|