Skip to content

Commit

Permalink
moves frontend staticfile gneration to siloed util #11567
Browse files Browse the repository at this point in the history
  • Loading branch information
chrabyrd committed Oct 22, 2024
1 parent e6c7828 commit 4535a9b
Show file tree
Hide file tree
Showing 9 changed files with 517 additions and 146 deletions.
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,4 @@ pip-wheel-metadata
webpack-stats.json
.DS_STORE
CACHE
.tsconfig-paths.json
.frontend-configuration-settings.json
frontend_configuration
197 changes: 144 additions & 53 deletions arches/app/utils/frontend_configuration_utils.py
Original file line number Diff line number Diff line change
@@ -1,35 +1,86 @@
import json
import os
import re
import site

from collections import OrderedDict
from django.conf import settings
from django.urls import get_resolver, URLPattern, URLResolver
from django.urls.resolvers import RegexPattern, RoutePattern, LocalePrefixPattern

from arches.settings_utils import list_arches_app_names, list_arches_app_paths

def get_base_path():
return (
os.path.realpath(settings.ROOT_DIR)
if settings.APP_NAME == "Arches"
else os.path.realpath(settings.APP_ROOT)
)

def generate_frontend_configuration():
_generate_frontend_configuration_directory()
_generate_urls_json()
_generate_webpack_configuration()
_generate_tsconfig_paths()


def generate_frontend_configuration_directory():
def _generate_frontend_configuration_directory():
destination_dir = os.path.realpath(
os.path.join(get_base_path(), "..", "frontend_configuration")
os.path.join(_get_base_path(), "..", "frontend_configuration")
)

os.makedirs(destination_dir, exist_ok=True)


def generate_urls_json():
def _generate_urls_json():
def generate_human_readable_urls(patterns, prefix="", namespace="", result={}):
def join_paths(*args):
return "/".join(filter(None, (arg.strip("/") for arg in args)))

def interpolate_route(pattern):
if isinstance(pattern, RoutePattern):
return re.sub(r"<(?:[^:]+:)?([^>]+)>", r"{\1}", pattern._route)
elif isinstance(pattern, RegexPattern):
regex = pattern._regex.lstrip("^").rstrip("$")

# Replace named capture groups (e.g., (?P<param>)) with {param}
regex = re.sub(r"\(\?P<(\w+)>[^)]+\)", r"{\1}", regex)

# Remove non-capturing groups (e.g., (?:...))
regex = re.sub(r"\(\?:[^\)]+\)", "", regex)

# Remove character sets (e.g., [0-9])
regex = re.sub(r"\[[^\]]+\]", "", regex)

# Remove backslashes (used to escape special characters in regex)
regex = regex.replace("\\", "")

# Remove regex-specific special characters (^, $, +, *, ?, (), etc.)
regex = re.sub(r"[\^\$\+\*\?\(\)]", "", regex)

return regex.strip("/")

for pattern in patterns:
if isinstance(pattern, URLPattern):
if pattern.name:
result[f"{namespace}{pattern.name}"] = "/" + join_paths(
prefix, interpolate_route(pattern.pattern)
)
elif isinstance(pattern, URLResolver):
current_namespace = namespace + (
f":{pattern.namespace}:" if pattern.namespace else ""
)

if isinstance(
pattern.pattern, LocalePrefixPattern
): # handles i18n_patterns
new_prefix = join_paths(prefix, "{language_code}")
else:
new_prefix = join_paths(prefix, interpolate_route(pattern.pattern))

generate_human_readable_urls(
pattern.url_patterns, new_prefix, current_namespace, result
)
return result

resolver = get_resolver()
human_readable_urls = _generate_human_readable_urls(resolver.url_patterns)
human_readable_urls = generate_human_readable_urls(resolver.url_patterns)

destination_path = os.path.realpath(
os.path.join(get_base_path(), "..", "frontend_configuration", "urls.json")
os.path.join(_get_base_path(), "..", "frontend_configuration", "urls.json")
)

with open(destination_path, "w") as file:
Expand All @@ -43,52 +94,92 @@ def generate_urls_json():
)


def _generate_human_readable_urls(patterns, prefix="", namespace="", result={}):
def join_paths(*args):
return "/".join(filter(None, (arg.strip("/") for arg in args)))
def _generate_webpack_configuration():
app_root_path = os.path.realpath(settings.APP_ROOT)
root_dir_path = os.path.realpath(settings.ROOT_DIR)

arches_app_names = list_arches_app_names()
arches_app_paths = list_arches_app_paths()

destination_path = os.path.realpath(
os.path.join(
_get_base_path(), "..", "frontend_configuration", "webpack-metadata.json"
)
)

def interpolate_route(pattern):
if isinstance(pattern, RoutePattern):
return re.sub(r"<(?:[^:]+:)?([^>]+)>", r"{\1}", pattern._route)
elif isinstance(pattern, RegexPattern):
regex = pattern._regex.lstrip("^").rstrip("$")
with open(destination_path, "w") as file:
json.dump(
{
"_comment": "This is a generated file. Do not edit directly.",
"APP_ROOT": app_root_path,
"ARCHES_APPLICATIONS": arches_app_names,
"ARCHES_APPLICATIONS_PATHS": dict(
zip(arches_app_names, arches_app_paths, strict=True)
),
"SITE_PACKAGES_DIRECTORY": site.getsitepackages()[0],
"PUBLIC_SERVER_ADDRESS": settings.PUBLIC_SERVER_ADDRESS,
"ROOT_DIR": root_dir_path,
"STATIC_URL": settings.STATIC_URL,
"WEBPACK_DEVELOPMENT_SERVER_PORT": settings.WEBPACK_DEVELOPMENT_SERVER_PORT,
},
file,
indent=4,
)

# Replace named capture groups (e.g., (?P<param>)) with {param}
regex = re.sub(r"\(\?P<(\w+)>[^)]+\)", r"{\1}", regex)

# Remove non-capturing groups (e.g., (?:...))
regex = re.sub(r"\(\?:[^\)]+\)", "", regex)
def _generate_tsconfig_paths():
base_path = _get_base_path()
root_dir_path = os.path.realpath(settings.ROOT_DIR)

# Remove character sets (e.g., [0-9])
regex = re.sub(r"\[[^\]]+\]", "", regex)
path_lookup = dict(
zip(list_arches_app_names(), list_arches_app_paths(), strict=True)
)

# Remove backslashes (used to escape special characters in regex)
regex = regex.replace("\\", "")
tsconfig_paths_data = {
"_comment": "This is a generated file. Do not edit directly.",
"compilerOptions": {
"paths": {
"@/arches/*": [
os.path.join(
".",
os.path.relpath(
root_dir_path,
os.path.join(base_path, ".."),
),
"app",
"src",
"arches",
"*",
)
],
**{
os.path.join("@", path_name, "*"): [
os.path.join(
".",
os.path.relpath(path, os.path.join(base_path, "..")),
"src",
path_name,
"*",
)
]
for path_name, path in path_lookup.items()
},
"*": ["./node_modules/*"],
}
},
}

# Remove regex-specific special characters (^, $, +, *, ?, (), etc.)
regex = re.sub(r"[\^\$\+\*\?\(\)]", "", regex)
destination_path = os.path.realpath(
os.path.join(base_path, "..", "frontend_configuration", "tsconfig-paths.json")
)

return regex.strip("/")
with open(destination_path, "w") as file:
json.dump(tsconfig_paths_data, file, indent=4)

for pattern in patterns:
if isinstance(pattern, URLPattern):
if pattern.name:
result[f"{namespace}{pattern.name}"] = "/" + join_paths(
prefix, interpolate_route(pattern.pattern)
)
elif isinstance(pattern, URLResolver):
current_namespace = namespace + (
f":{pattern.namespace}:" if pattern.namespace else ""
)

if isinstance(
pattern.pattern, LocalePrefixPattern
): # handles i18n_patterns
new_prefix = join_paths(prefix, "{language_code}")
else:
new_prefix = join_paths(prefix, interpolate_route(pattern.pattern))

_generate_human_readable_urls(
pattern.url_patterns, new_prefix, current_namespace, result
)
return result

def _get_base_path():
return (
os.path.realpath(settings.ROOT_DIR)
if settings.APP_NAME == "Arches"
else os.path.realpath(settings.APP_ROOT)
)
4 changes: 3 additions & 1 deletion arches/apps.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@
from semantic_version import SimpleSpec, Version

from arches import __version__
from arches.settings_utils import generate_frontend_configuration
from arches.app.utils.frontend_configuration_utils import (
generate_frontend_configuration,
)


class ArchesAppConfig(AppConfig):
Expand Down
3 changes: 1 addition & 2 deletions arches/install/arches-templates/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,4 @@ webpack-stats.json
*.egg-info
.DS_STORE
CACHE
.tsconfig-paths.json
.frontend-configuration-settings.json
frontend_configuration
2 changes: 1 addition & 1 deletion arches/install/arches-templates/project_name/apps.py-tpl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.apps import AppConfig
from django.conf import settings

from arches.settings_utils import generate_frontend_configuration
from arches.app.utils.frontend_configuration_utils import generate_frontend_configuration


class {{ project_name_title_case }}Config(AppConfig):
Expand Down
87 changes: 0 additions & 87 deletions arches/settings_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,90 +133,3 @@ def build_templates_config(
# Ensures error message is shown if error encountered in webpack build
sys.stdout.write(str(e))
raise e


def generate_frontend_configuration():
try:
app_root_path = os.path.realpath(settings.APP_ROOT)
root_dir_path = os.path.realpath(settings.ROOT_DIR)

arches_app_names = list_arches_app_names()
arches_app_paths = list_arches_app_paths()
path_lookup = dict(zip(arches_app_names, arches_app_paths, strict=True))

frontend_configuration_settings_data = {
"_comment": "This is a generated file. Do not edit directly.",
"APP_ROOT": app_root_path,
"ARCHES_APPLICATIONS": arches_app_names,
"ARCHES_APPLICATIONS_PATHS": path_lookup,
"SITE_PACKAGES_DIRECTORY": site.getsitepackages()[0],
"PUBLIC_SERVER_ADDRESS": settings.PUBLIC_SERVER_ADDRESS,
"ROOT_DIR": root_dir_path,
"STATIC_URL": settings.STATIC_URL,
"WEBPACK_DEVELOPMENT_SERVER_PORT": settings.WEBPACK_DEVELOPMENT_SERVER_PORT,
}

if settings.APP_NAME == "Arches":
base_path = root_dir_path
else:
base_path = app_root_path

frontend_configuration_settings_path = os.path.realpath(
os.path.join(base_path, "..", ".frontend-configuration-settings.json")
)
sys.stdout.write(
f"Writing frontend configuration to: {frontend_configuration_settings_path} \n"
)

with open(
frontend_configuration_settings_path,
"w",
) as file:
json.dump(frontend_configuration_settings_data, file, indent=4)

tsconfig_paths_data = {
"_comment": "This is a generated file. Do not edit directly.",
"compilerOptions": {
"paths": {
"@/arches/*": [
os.path.join(
".",
os.path.relpath(
root_dir_path,
os.path.join(base_path, ".."),
),
"app",
"src",
"arches",
"*",
)
],
**{
os.path.join("@", path_name, "*"): [
os.path.join(
".",
os.path.relpath(path, os.path.join(base_path, "..")),
"src",
path_name,
"*",
)
]
for path_name, path in path_lookup.items()
},
"*": ["./node_modules/*"],
}
},
}

tsconfig_path = os.path.realpath(
os.path.join(base_path, "..", ".tsconfig-paths.json")
)
sys.stdout.write(f"Writing tsconfig path data to: {tsconfig_path} \n")

with open(tsconfig_path, "w") as file:
json.dump(tsconfig_paths_data, file, indent=4)

except Exception as e:
# Ensures error message is shown if error encountered
sys.stderr.write(str(e))
raise e
13 changes: 13 additions & 0 deletions frontend_configuration/tsconfig-paths.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"_comment": "This is a generated file. Do not edit directly.",
"compilerOptions": {
"paths": {
"@/arches/*": [
"./arches/app/src/arches/*"
],
"*": [
"./node_modules/*"
]
}
}
}
Loading

0 comments on commit 4535a9b

Please sign in to comment.