From 05edf33062fc430a272aaacfeaf96209bf864a25 Mon Sep 17 00:00:00 2001 From: Sli Date: Wed, 5 Mar 2025 11:20:27 +0100 Subject: [PATCH 1/2] Compile openapi client in background when django runserver is reloading --- package.json | 1 + .../management/commands/collectstatic.py | 8 +++++++- staticfiles/management/commands/runserver.py | 17 ++++++++++++----- staticfiles/processors.py | 6 +++--- 4 files changed, 23 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index cc04d56f..dcd79746 100644 --- a/package.json +++ b/package.json @@ -7,6 +7,7 @@ "compile": "vite build --mode production", "compile-dev": "vite build --mode development", "serve": "vite build --mode development --watch --minify false", + "openapi": "openapi-ts", "analyse-dev": "vite-bundle-visualizer --mode development", "analyse-prod": "vite-bundle-visualizer --mode production", "check": "biome check --write" diff --git a/staticfiles/management/commands/collectstatic.py b/staticfiles/management/commands/collectstatic.py index 5b441f6f..f7889087 100644 --- a/staticfiles/management/commands/collectstatic.py +++ b/staticfiles/management/commands/collectstatic.py @@ -50,7 +50,13 @@ class Command(CollectStatic): return Path(location) Scss.compile(self.collect_scss()) - OpenApi.compile() # This needs to be prior to javascript bundling + openapi = OpenApi.compile() # This needs to be prior to javascript bundling + if openapi is not None: + _ = openapi.wait() + if openapi.returncode: + raise RuntimeError( + f"Openapi generation failed with returncode {openapi.returncode}" + ) JSBundler.compile() collected = super().collect() diff --git a/staticfiles/management/commands/runserver.py b/staticfiles/management/commands/runserver.py index 7138dee1..cb44fd60 100644 --- a/staticfiles/management/commands/runserver.py +++ b/staticfiles/management/commands/runserver.py @@ -15,11 +15,18 @@ class Command(Runserver): """Light wrapper around default runserver that integrates javascirpt auto bundling.""" def run(self, **options): - OpenApi.compile() - if ( - os.environ.get(DJANGO_AUTORELOAD_ENV) is None - and settings.PROCFILE_STATIC is not None - ): + is_django_reload = os.environ.get(DJANGO_AUTORELOAD_ENV) is not None + + proc = OpenApi.compile() + # Ensure that the first runserver launch creates openapi files + # before the bundler starts so that it detects them + # When django is reloaded, we can keep this process in background + # to reduce reload time + if proc is not None and not is_django_reload: + _ = proc.wait() + + if not is_django_reload and settings.PROCFILE_STATIC is not None: start_composer(settings.PROCFILE_STATIC) _ = atexit.register(stop_composer, procfile=settings.PROCFILE_STATIC) + super().run(**options) diff --git a/staticfiles/processors.py b/staticfiles/processors.py index bacac363..5f44731b 100644 --- a/staticfiles/processors.py +++ b/staticfiles/processors.py @@ -95,7 +95,7 @@ class JSBundler: def compile(): """Bundle js files with the javascript bundler for production.""" process = subprocess.Popen(["npm", "run", "compile"]) - process.wait() + _ = process.wait() if process.returncode: raise RuntimeError(f"Bundler failed with returncode {process.returncode}") @@ -163,7 +163,7 @@ class OpenApi: OPENAPI_DIR = GENERATED_ROOT / "openapi" @classmethod - def compile(cls): + def compile(cls) -> subprocess.Popen[bytes] | None: """Compile a TS client for the sith API. Only generates it if it changed.""" logging.getLogger("django").info("Compiling open api typescript client") out = cls.OPENAPI_DIR / "schema.json" @@ -191,4 +191,4 @@ class OpenApi: with open(out, "w") as f: _ = f.write(schema) - subprocess.run(["npx", "openapi-ts"], check=True) + return subprocess.Popen(["npm", "run", "openapi"]) From 106dc32a3d2a5eee6d4f0540d3fb888436c91334 Mon Sep 17 00:00:00 2001 From: Sli Date: Sun, 9 Mar 2025 16:30:21 +0100 Subject: [PATCH 2/2] Fix schema.json being auto deleted and remove formating and linting of generated openapi client --- openapi-ts.config.ts | 4 +--- package-lock.json | 3 ++- package.json | 2 +- tsconfig.json | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/openapi-ts.config.ts b/openapi-ts.config.ts index 32403271..f69aaeb1 100644 --- a/openapi-ts.config.ts +++ b/openapi-ts.config.ts @@ -6,9 +6,7 @@ import { defineConfig } from "@hey-api/openapi-ts"; export default defineConfig({ input: resolve(__dirname, "./staticfiles/generated/openapi/schema.json"), output: { - lint: "biome", - format: "biome", - path: resolve(__dirname, "./staticfiles/generated/openapi"), + path: resolve(__dirname, "./staticfiles/generated/openapi/client"), }, plugins: [ { diff --git a/package-lock.json b/package-lock.json index 4e2852b9..d6d496e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3383,7 +3383,8 @@ "node_modules/country-flag-emoji-polyfill": { "version": "0.1.8", "resolved": "https://registry.npmjs.org/country-flag-emoji-polyfill/-/country-flag-emoji-polyfill-0.1.8.tgz", - "integrity": "sha512-Mbah52sADS3gshUYhK5142gtUuJpHYOXlXtLFI3Ly4RqgkmPMvhX9kMZSTqDM8P7UqtSW99eHKFphhQSGXA3Cg==" + "integrity": "sha512-Mbah52sADS3gshUYhK5142gtUuJpHYOXlXtLFI3Ly4RqgkmPMvhX9kMZSTqDM8P7UqtSW99eHKFphhQSGXA3Cg==", + "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", diff --git a/package.json b/package.json index dcd79746..83b65145 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,7 @@ "license": "GPL-3.0-only", "sideEffects": [".css"], "imports": { - "#openapi": "./staticfiles/generated/openapi/index.ts", + "#openapi": "./staticfiles/generated/openapi/client/index.ts", "#core:*": "./core/static/bundled/*", "#pedagogy:*": "./pedagogy/static/bundled/*", "#counter:*": "./counter/static/bundled/*", diff --git a/tsconfig.json b/tsconfig.json index a93da92a..e632cc80 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -14,7 +14,7 @@ "types": ["jquery", "alpinejs"], "lib": ["es7"], "paths": { - "#openapi": ["./staticfiles/generated/openapi/index.ts"], + "#openapi": ["./staticfiles/generated/openapi/client/index.ts"], "#core:*": ["./core/static/bundled/*"], "#pedagogy:*": ["./pedagogy/static/bundled/*"], "#counter:*": ["./counter/static/bundled/*"],