@tdk-landscape/tdk-cli-core 1.3.86 → 1.3.88

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.
Files changed (35) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/README.md +22 -3
  3. package/cli/dist/backend-frameworks/express.js +50 -0
  4. package/cli/dist/backend-frameworks/registry.js +41 -0
  5. package/cli/dist/backend-frameworks/types.js +2 -0
  6. package/cli/dist/backend-languages/bun.js +53 -0
  7. package/cli/dist/backend-languages/python.js +89 -0
  8. package/cli/dist/backend-languages/registry.js +30 -0
  9. package/cli/dist/backend-languages/types.js +2 -0
  10. package/cli/dist/commands/resource.js +67 -51
  11. package/cli/dist/commands/up.js +1 -1
  12. package/cli/dist/frontend-frameworks/lit.js +65 -0
  13. package/cli/dist/frontend-frameworks/preact.js +63 -0
  14. package/cli/dist/frontend-frameworks/qwik.js +62 -0
  15. package/cli/dist/frontend-frameworks/registry.js +14 -3
  16. package/cli/dist/frontend-frameworks/solid.js +63 -0
  17. package/cli/dist/frontend-frameworks/svelte.js +68 -0
  18. package/cli/dist/utils/doctor-wiring.js +9 -1
  19. package/cli/dist/utils/service-manifest.js +2 -0
  20. package/cli/package.json +2 -2
  21. package/engine/schemas/service-schema.json +31 -4
  22. package/engine/topologies/platform/docker/compose/compose.star +9 -1
  23. package/engine/topologies/platform/docker/dockerfile/language_dockerfile.star +62 -0
  24. package/engine/topologies/platform/docker/index.star +7 -0
  25. package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +10 -1
  26. package/engine/topologies/tilt/generators/validators.star +8 -4
  27. package/engine/topologies/tilt/generators/vite/frameworks/lit.star +90 -0
  28. package/engine/topologies/tilt/generators/vite/frameworks/preact.star +92 -0
  29. package/engine/topologies/tilt/generators/vite/frameworks/qwik.star +92 -0
  30. package/engine/topologies/tilt/generators/vite/frameworks/solid.star +92 -0
  31. package/engine/topologies/tilt/generators/vite/frameworks/svelte.star +92 -0
  32. package/engine/topologies/tilt/generators/vite/frontend.star +25 -0
  33. package/engine/topologies/tilt/resources/orchestrator/apply_compose_resource_registration.star +31 -4
  34. package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +25 -6
  35. package/package.json +2 -2
@@ -0,0 +1,63 @@
1
+ export const preactFrontendProvider = {
2
+ id: "preact",
3
+ label: "Preact + Vite",
4
+ dependencies: {
5
+ preact: "^10.26.0",
6
+ },
7
+ devDependencies: {
8
+ "@preact/preset-vite": "^2.10.0",
9
+ },
10
+ compilerOptions: {
11
+ jsx: "react-jsx",
12
+ jsxImportSource: "preact",
13
+ },
14
+ createFiles(name) {
15
+ return [
16
+ {
17
+ filename: "index.html",
18
+ content: `<!DOCTYPE html>
19
+ <html lang="en">
20
+ <head>
21
+ <meta charset="UTF-8" />
22
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
23
+ <title>${name}</title>
24
+ </head>
25
+ <body>
26
+ <div id="app"></div>
27
+ <script type="module" src="/src/main.tsx"></script>
28
+ </body>
29
+ </html>
30
+ `,
31
+ description: "Generating HTML template",
32
+ emoji: "💻",
33
+ },
34
+ {
35
+ filename: "src/main.tsx",
36
+ content: `import { render } from 'preact';
37
+ import App from './App';
38
+
39
+ render(<App />, document.getElementById('app')!);
40
+ `,
41
+ description: "Generating Preact entry",
42
+ emoji: "💻",
43
+ },
44
+ {
45
+ filename: "src/App.tsx",
46
+ content: `function App() {
47
+ return (
48
+ <div style={{ padding: '2rem', fontFamily: 'system-ui' }}>
49
+ <h1>${name}</h1>
50
+ <p>Frontend resource created with TDK</p>
51
+ </div>
52
+ );
53
+ }
54
+
55
+ export default App;
56
+ `,
57
+ description: "Generating Preact app",
58
+ emoji: "💻",
59
+ },
60
+ ];
61
+ },
62
+ };
63
+ //# sourceMappingURL=preact.js.map
@@ -0,0 +1,62 @@
1
+ export const qwikFrontendProvider = {
2
+ id: "qwik",
3
+ label: "Qwik + Vite",
4
+ dependencies: {
5
+ "@builder.io/qwik": "^1.12.0",
6
+ },
7
+ devDependencies: {},
8
+ compilerOptions: {
9
+ jsx: "react-jsx",
10
+ jsxImportSource: "@builder.io/qwik",
11
+ },
12
+ createFiles(name) {
13
+ return [
14
+ {
15
+ filename: "index.html",
16
+ content: `<!DOCTYPE html>
17
+ <html lang="en">
18
+ <head>
19
+ <meta charset="UTF-8" />
20
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
21
+ <title>${name}</title>
22
+ </head>
23
+ <body>
24
+ <div id="app"></div>
25
+ <script type="module" src="/src/main.tsx"></script>
26
+ </body>
27
+ </html>
28
+ `,
29
+ description: "Generating HTML template",
30
+ emoji: "💻",
31
+ },
32
+ {
33
+ filename: "src/main.tsx",
34
+ content: `import '@builder.io/qwik/qwikloader.js';
35
+ import { render } from '@builder.io/qwik';
36
+ import { App } from './App';
37
+
38
+ render(document.getElementById('app') as HTMLElement, <App />);
39
+ `,
40
+ description: "Generating Qwik entry",
41
+ emoji: "💻",
42
+ },
43
+ {
44
+ filename: "src/App.tsx",
45
+ content: `import { component$ } from '@builder.io/qwik';
46
+
47
+ export const App = component$(() => {
48
+ return (
49
+ <div style={{ padding: '2rem', fontFamily: 'system-ui' }}>
50
+ <h1>${name}</h1>
51
+ <p>Frontend resource created with TDK</p>
52
+ </div>
53
+ );
54
+ });
55
+ `,
56
+ description: "Generating Qwik app",
57
+ emoji: "💻",
58
+ },
59
+ ];
60
+ },
61
+ };
62
+ //# sourceMappingURL=qwik.js.map
@@ -1,10 +1,20 @@
1
1
  import { TdkError } from "../utils/errors.js";
2
+ import { litFrontendProvider } from "./lit.js";
3
+ import { preactFrontendProvider } from "./preact.js";
4
+ import { qwikFrontendProvider } from "./qwik.js";
2
5
  import { reactFrontendProvider } from "./react.js";
6
+ import { solidFrontendProvider } from "./solid.js";
7
+ import { svelteFrontendProvider } from "./svelte.js";
3
8
  import { vueFrontendProvider } from "./vue.js";
4
9
  export const DEFAULT_FRONTEND_FRAMEWORK = "react";
5
10
  export const FRONTEND_FRAMEWORKS = {
6
11
  react: reactFrontendProvider,
7
12
  vue: vueFrontendProvider,
13
+ svelte: svelteFrontendProvider,
14
+ preact: preactFrontendProvider,
15
+ lit: litFrontendProvider,
16
+ solid: solidFrontendProvider,
17
+ qwik: qwikFrontendProvider,
8
18
  };
9
19
  export function getFrontendFramework(frameworkId) {
10
20
  const id = (frameworkId ?? DEFAULT_FRONTEND_FRAMEWORK).trim().toLowerCase();
@@ -17,9 +27,10 @@ export function getFrontendFramework(frameworkId) {
17
27
  }
18
28
  export function resolveFrontendFramework(resourceType, frameworkId) {
19
29
  if (resourceType !== "frontend") {
20
- if (frameworkId !== undefined) {
21
- throw new TdkError("--framework can only be used with --type frontend.", [
22
- "Add --type frontend, or drop --framework for this resource type",
30
+ // Backends take their own framework ids (see backend-frameworks/registry.ts).
31
+ if (frameworkId !== undefined && resourceType !== "backend") {
32
+ throw new TdkError("--framework can only be used with --type frontend or --type backend.", [
33
+ "Add --type frontend or --type backend, or drop --framework for this resource type",
23
34
  ]);
24
35
  }
25
36
  return undefined;
@@ -0,0 +1,63 @@
1
+ export const solidFrontendProvider = {
2
+ id: "solid",
3
+ label: "Solid + Vite",
4
+ dependencies: {
5
+ "solid-js": "^1.9.0",
6
+ },
7
+ devDependencies: {
8
+ "vite-plugin-solid": "^2.11.0",
9
+ },
10
+ compilerOptions: {
11
+ jsx: "preserve",
12
+ jsxImportSource: "solid-js",
13
+ },
14
+ createFiles(name) {
15
+ return [
16
+ {
17
+ filename: "index.html",
18
+ content: `<!DOCTYPE html>
19
+ <html lang="en">
20
+ <head>
21
+ <meta charset="UTF-8" />
22
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
23
+ <title>${name}</title>
24
+ </head>
25
+ <body>
26
+ <div id="app"></div>
27
+ <script type="module" src="/src/main.tsx"></script>
28
+ </body>
29
+ </html>
30
+ `,
31
+ description: "Generating HTML template",
32
+ emoji: "💻",
33
+ },
34
+ {
35
+ filename: "src/main.tsx",
36
+ content: `import { render } from 'solid-js/web';
37
+ import App from './App';
38
+
39
+ render(() => <App />, document.getElementById('app')!);
40
+ `,
41
+ description: "Generating Solid entry",
42
+ emoji: "💻",
43
+ },
44
+ {
45
+ filename: "src/App.tsx",
46
+ content: `function App() {
47
+ return (
48
+ <div style={{ padding: '2rem', 'font-family': 'system-ui' }}>
49
+ <h1>${name}</h1>
50
+ <p>Frontend resource created with TDK</p>
51
+ </div>
52
+ );
53
+ }
54
+
55
+ export default App;
56
+ `,
57
+ description: "Generating Solid app",
58
+ emoji: "💻",
59
+ },
60
+ ];
61
+ },
62
+ };
63
+ //# sourceMappingURL=solid.js.map
@@ -0,0 +1,68 @@
1
+ export const svelteFrontendProvider = {
2
+ id: "svelte",
3
+ label: "Svelte + Vite",
4
+ dependencies: {
5
+ svelte: "^5.0.0",
6
+ },
7
+ devDependencies: {
8
+ "@sveltejs/vite-plugin-svelte": "^4.0.4",
9
+ },
10
+ compilerOptions: {},
11
+ createFiles(name) {
12
+ return [
13
+ {
14
+ filename: "index.html",
15
+ content: `<!DOCTYPE html>
16
+ <html lang="en">
17
+ <head>
18
+ <meta charset="UTF-8" />
19
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
20
+ <title>${name}</title>
21
+ </head>
22
+ <body>
23
+ <div id="app"></div>
24
+ <script type="module" src="/src/main.ts"></script>
25
+ </body>
26
+ </html>
27
+ `,
28
+ description: "Generating HTML template",
29
+ emoji: "💻",
30
+ },
31
+ {
32
+ filename: "src/main.ts",
33
+ content: `import { mount } from 'svelte';
34
+ import App from './App.svelte';
35
+
36
+ const app = mount(App, { target: document.getElementById('app')! });
37
+
38
+ export default app;
39
+ `,
40
+ description: "Generating Svelte entry",
41
+ emoji: "💻",
42
+ },
43
+ {
44
+ filename: "src/App.svelte",
45
+ content: `<script lang="ts">
46
+ const message: string = 'Frontend resource created with TDK';
47
+ </script>
48
+
49
+ <main style="padding: 2rem; font-family: system-ui">
50
+ <h1>${name}</h1>
51
+ <p>{message}</p>
52
+ </main>
53
+ `,
54
+ description: "Generating Svelte app",
55
+ emoji: "💻",
56
+ },
57
+ {
58
+ filename: "src/vite-env.d.ts",
59
+ content: `/// <reference types="svelte" />
60
+ /// <reference types="vite/client" />
61
+ `,
62
+ description: "Generating Svelte module types",
63
+ emoji: "⚙️",
64
+ },
65
+ ];
66
+ },
67
+ };
68
+ //# sourceMappingURL=svelte.js.map
@@ -1,6 +1,7 @@
1
1
  import { execFileSync, execSync } from "node:child_process";
2
2
  import { existsSync, readdirSync, readFileSync } from "node:fs";
3
3
  import { join, relative } from "node:path";
4
+ import { BACKEND_LANGUAGES } from "../backend-languages/registry.js";
4
5
  import { execAsync } from "./exec-async.js";
5
6
  import { formatCount } from "./formatting.js";
6
7
  import { findProjectRoot } from "./paths.js";
@@ -44,9 +45,16 @@ function backendPorts(resources) {
44
45
  }
45
46
  return ports;
46
47
  }
48
+ /** A backend whose language provider scaffolds its own runtime files (Python) has no package.json. */
49
+ function ownsRuntimeFiles(resource) {
50
+ const language = resource.config?.language;
51
+ return (typeof language === "string" &&
52
+ Object.hasOwn(BACKEND_LANGUAGES, language) &&
53
+ BACKEND_LANGUAGES[language].createFiles !== undefined);
54
+ }
47
55
  export function checkResourcePackageJson(projectRoot = findProjectRoot() ?? process.cwd()) {
48
56
  const missing = discoverResourcesFromRoot(projectRoot)
49
- .filter((resource) => !existsSync(join(resource.path, "package.json")))
57
+ .filter((resource) => !ownsRuntimeFiles(resource) && !existsSync(join(resource.path, "package.json")))
50
58
  .map((resource) => resource.name);
51
59
  if (missing.length === 0) {
52
60
  return {
@@ -20,12 +20,14 @@ const KNOWN_SERVICE_FIELDS = new Set([
20
20
  "framework",
21
21
  "healthCheckPath",
22
22
  "image",
23
+ "language",
23
24
  "name",
24
25
  "nats",
25
26
  "params",
26
27
  "playwright",
27
28
  "port",
28
29
  "replicas",
30
+ "restart",
29
31
  "runtime",
30
32
  "sablier",
31
33
  "schemaVersion",
package/cli/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "tdk-cli-internal",
3
3
  "private": true,
4
- "version": "1.3.86",
4
+ "version": "1.3.88",
5
5
  "description": "TDK CLI — start services on your laptop.",
6
6
  "keywords": [
7
7
  "tdk",
@@ -53,7 +53,7 @@
53
53
  "prepack": "../scripts/stage-npm-engine.sh && npm run build"
54
54
  },
55
55
  "dependencies": {
56
- "chalk": "^5.3.0",
56
+ "chalk": "^6.0.1",
57
57
  "commander": "^15.0.0",
58
58
  "handlebars": "^4.7.8",
59
59
  "ink": "^7.1.1",
@@ -107,7 +107,7 @@
107
107
  "type": "string"
108
108
  },
109
109
  "appType": {
110
- "description": "Resource type determining build strategy:\n• backend - Hono/Express API server\n• frontend - Vite single-page application (React or Vue, see framework)\n• sdk - Shared library package\n• worker - Background job processor\n• migrator - Database migration runner\n• library - Shared code library\n• bring-your-own - Wrap an existing service. No app scaffold. Needs Dockerfile or --image.",
110
+ "description": "Resource type determining build strategy:\n• backend - Hono/Express API server\n• frontend - Vite single-page application (React, Vue, Svelte, Preact, Lit, Solid or Qwik, see framework)\n• sdk - Shared library package\n• worker - Background job processor\n• migrator - Database migration runner\n• library - Shared code library\n• bring-your-own - Wrap an existing service. No app scaffold. Needs Dockerfile or --image.",
111
111
  "enum": [
112
112
  "backend",
113
113
  "frontend",
@@ -153,6 +153,16 @@
153
153
  "description": "Whether this bring-your-own resource receives a Traefik route. Defaults to true.",
154
154
  "type": "boolean"
155
155
  },
156
+ "restart": {
157
+ "description": "Docker Compose restart policy for this bring-your-own resource. Use \"no\" or \"on-failure\" for one-shot jobs such as database migrations, so a finished job is not restarted. Defaults to unless-stopped.",
158
+ "enum": [
159
+ "no",
160
+ "on-failure",
161
+ "unless-stopped",
162
+ "always"
163
+ ],
164
+ "type": "string"
165
+ },
156
166
  "image": {
157
167
  "description": "Docker image name instead of building from Dockerfile. Optional for bring-your-own type.",
158
168
  "type": "string"
@@ -215,11 +225,28 @@
215
225
  "uniqueItems": true
216
226
  },
217
227
  "framework": {
218
- "description": "Frontend SPA provider used to generate Vite and TypeScript config. Only meaningful for frontend resources; omitted means react (legacy manifests).",
219
- "enum": [
228
+ "description": "Framework provider id. For frontend resources it selects the Vite and TypeScript config (omitted means react, legacy manifests). For backend resources it selects the HTTP framework on the Bun runtime (omitted means hono; not used with a language that owns its runtime, such as python). Valid ids come from the CLI provider registries, which reject unknown ids before writing a resource.",
229
+ "examples": [
220
230
  "react",
221
- "vue"
231
+ "vue",
232
+ "svelte",
233
+ "preact",
234
+ "lit",
235
+ "solid",
236
+ "qwik",
237
+ "hono",
238
+ "express"
239
+ ],
240
+ "pattern": "^[a-z][a-z0-9-]*$",
241
+ "type": "string"
242
+ },
243
+ "language": {
244
+ "description": "Backend language provider id used to scaffold and run the service. Only meaningful for backend resources; omitted means bun (legacy manifests). Valid ids come from the CLI provider registry.",
245
+ "examples": [
246
+ "bun",
247
+ "python"
222
248
  ],
249
+ "pattern": "^[a-z][a-z0-9-]*$",
223
250
  "type": "string"
224
251
  },
225
252
  "healthCheckPath": {
@@ -267,6 +267,13 @@ def _generate_single_backend_entry(resource_path, resource_name, res, manifest,
267
267
  is_byo and not manifest.get('exposeViaProxy', True)
268
268
  ) if manifest else False
269
269
 
270
+ # One-shot jobs (migrations, seeders) must not be restarted after they exit 0, so a
271
+ # bring-your-own resource can choose its Compose restart policy. Default is unchanged.
272
+ restart_policy = 'unless-stopped'
273
+ if is_byo and manifest.get('restart') in ['no', 'on-failure', 'unless-stopped', 'always']:
274
+ restart_policy = manifest.get('restart')
275
+ restart_policy = '"no"' if restart_policy == 'no' else restart_policy
276
+
270
277
  if is_worker or is_proxy_disabled_byo:
271
278
  traefik_labels = ' - "traefik.enable=false"'
272
279
  healthcheck_section = "" if is_worker or is_byo else """ healthcheck:
@@ -299,7 +306,7 @@ def _generate_single_backend_entry(resource_path, resource_name, res, manifest,
299
306
  {resource_entry_name}:
300
307
  image: {image_name}
301
308
  init: true
302
- restart: unless-stopped
309
+ restart: {restart_policy}
303
310
  {build_config}
304
311
  {ports_section}
305
312
  env_file:
@@ -350,6 +357,7 @@ def _generate_single_backend_entry(resource_path, resource_name, res, manifest,
350
357
  stack=stack,
351
358
  internal_port=internal_port,
352
359
  port_anchor=internal_port,
360
+ restart_policy=restart_policy,
353
361
  dev_port=dev_port,
354
362
  db_name=db_name,
355
363
  db_url_for_tilt=db_url_for_tilt,
@@ -0,0 +1,62 @@
1
+ # Dockerfiles for backends whose `language` is not the Bun default.
2
+ #
3
+ # Bun backends use the golden-image generator. Python backends are a small
4
+ # standalone image: language-specific base image and reload command, while the build
5
+ # context (project root), `production` target, health check, and PORT contract stay the
6
+ # same as every other backend.
7
+
8
+ BASE_PORT_BACKEND = 4000
9
+
10
+ # Languages whose runtime files are owned by the provider (no Vite/tsconfig/Bun config).
11
+ PROVIDER_OWNED_LANGUAGES = ["python"]
12
+
13
+ _CURL = """RUN apt-get update && apt-get install -y --no-install-recommends curl \\
14
+ && rm -rf /var/lib/apt/lists/*
15
+ """
16
+
17
+ _HEALTHCHECK = """HEALTHCHECK --interval=10s --timeout=5s --retries=3 \\
18
+ CMD curl -f http://localhost:${{PORT:-{port}}}/health || exit 1
19
+ """
20
+
21
+ _PYTHON = """# Generated by TDK for a Python backend. Do not edit; change service.json instead.
22
+ FROM python:3.12-slim AS base
23
+
24
+ WORKDIR /app/{res_path}
25
+
26
+ {curl}
27
+ COPY {res_path}/pyproject.toml ./
28
+ RUN pip install --no-cache-dir .
29
+
30
+ COPY {res_path}/src ./src
31
+
32
+ FROM base AS test
33
+ RUN pip install --no-cache-dir ".[dev]"
34
+ COPY {res_path}/tests ./tests
35
+ RUN pytest
36
+
37
+ FROM base AS production
38
+
39
+ {healthcheck}
40
+ EXPOSE {port}
41
+
42
+ CMD ["sh", "-c", "uvicorn main:app --app-dir src --host 0.0.0.0 --port ${{PORT:-{port}}} --reload --reload-dir src"]
43
+ """
44
+
45
+ def generate_language_dockerfile(res_path, language, port = BASE_PORT_BACKEND):
46
+ """Dockerfile text for a provider-owned backend language, or fail for an unknown one."""
47
+ template = {"python": _PYTHON}.get(language)
48
+ if template == None:
49
+ fail("No Dockerfile generator for backend language '{}'".format(language))
50
+ return template.format(
51
+ res_path = res_path,
52
+ port = port,
53
+ curl = _CURL,
54
+ healthcheck = _HEALTHCHECK.format(port = port),
55
+ )
56
+
57
+
58
+ def is_provider_owned_language(manifest):
59
+ """True when the manifest selects Python (a missing language means Bun)."""
60
+ if not manifest:
61
+ return False
62
+ return manifest.get("appType", "backend") == "backend" and manifest.get("language", "bun") in PROVIDER_OWNED_LANGUAGES
@@ -4,6 +4,11 @@ load(
4
4
  "generate_app_dockerfile",
5
5
  "generate_migrator_dockerfile",
6
6
  )
7
+ load(
8
+ "./dockerfile/language_dockerfile.star",
9
+ "generate_language_dockerfile",
10
+ "is_provider_owned_language",
11
+ )
7
12
  load("./config/dockerignore.star", "generate_dockerignore")
8
13
  load(
9
14
  "./compose/compose.star",
@@ -18,6 +23,8 @@ Docker = struct(
18
23
  frontend = generate_frontend_dockerfile,
19
24
  backend = generate_app_dockerfile,
20
25
  migrator = generate_migrator_dockerfile,
26
+ backend_language = generate_language_dockerfile,
27
+ is_provider_owned_language = is_provider_owned_language,
21
28
  generate_dockerignore = generate_dockerignore,
22
29
  frontend_compose = generate_frontend_compose,
23
30
  backend_compose = generate_backend_compose_entry,
@@ -77,7 +77,7 @@ def generate_frontend_tsconfig(resource_path, write_fn, internal_deps=None, is_d
77
77
  resource_path
78
78
  ))
79
79
  print(" Docker build will resolve @{npm_scope}/* from node_modules")
80
- if framework not in ['react', 'vue']:
80
+ if framework not in ['react', 'vue', 'svelte', 'preact', 'lit', 'solid', 'qwik']:
81
81
  fail("Unknown frontend framework for TypeScript generation: " + str(framework))
82
82
 
83
83
  compiler_options = {
@@ -99,6 +99,15 @@ def generate_frontend_tsconfig(resource_path, write_fn, internal_deps=None, is_d
99
99
  }
100
100
  if framework == 'react':
101
101
  compiler_options['jsx'] = 'react-jsx'
102
+ elif framework == 'preact':
103
+ compiler_options['jsx'] = 'react-jsx'
104
+ compiler_options['jsxImportSource'] = 'preact'
105
+ elif framework == 'qwik':
106
+ compiler_options['jsx'] = 'react-jsx'
107
+ compiler_options['jsxImportSource'] = '@builder.io/qwik'
108
+ elif framework == 'solid':
109
+ compiler_options['jsx'] = 'preserve'
110
+ compiler_options['jsxImportSource'] = 'solid-js'
102
111
 
103
112
  content = {
104
113
  "compilerOptions": compiler_options,
@@ -555,9 +555,12 @@ def generate_resource_health_check(resource_name, resource_path, manifest, check
555
555
  health_checks = []
556
556
 
557
557
  # Check 1: Required files exist
558
- frontend_entry = "src/main.ts" if manifest.get("framework", "react") == "vue" else "src/main.tsx"
558
+ frontend_entry = "src/main.ts" if manifest.get("framework", "react") in ["vue", "svelte", "lit"] else "src/main.tsx"
559
+ backend_files = {
560
+ "python": ["pyproject.toml", "src/main.py"],
561
+ }.get(manifest.get("language", "bun"), ["package.json", "prisma/schema.prisma"])
559
562
  required_files = {
560
- "backend": ["package.json", "prisma/schema.prisma"],
563
+ "backend": backend_files,
561
564
  "frontend": ["package.json", "index.html", frontend_entry],
562
565
  "library": ["package.json"],
563
566
  }.get(app_type, ["package.json"])
@@ -570,8 +573,9 @@ if [ ! -f "{}" ]; then
570
573
  fi
571
574
  '''.format(req_file, req_file))
572
575
 
573
- # Check 2: Package.json is valid JSON
574
- health_checks.append('''
576
+ # Check 2: Package.json is valid JSON (Python backends have no package.json)
577
+ if manifest.get("language", "bun") != "python":
578
+ health_checks.append('''
575
579
  if ! cat package.json | head -1 > /dev/null 2>&1; then
576
580
  echo "❌ package.json is not valid"
577
581
  exit 1
@@ -0,0 +1,90 @@
1
+ # Lit-specific Vite templates for frontend resources.
2
+
3
+ LIT_VITE_FRONTEND = """{header}
4
+ import {{ resolve }} from 'node:path';
5
+ import {{ defineConfig }} from 'vite';
6
+
7
+ export default defineConfig({{
8
+ base: '{base_path}',
9
+ build: {{
10
+ cssCodeSplit: false,
11
+ minify: false,
12
+ modulePreload: false,
13
+ rollupOptions: {{
14
+ output: {{
15
+ entryFileNames: '[name].js',
16
+ format: 'esm',
17
+ }},
18
+ }},
19
+ target: 'esnext',
20
+ }},
21
+ define: {{
22
+ global: 'globalThis',
23
+ }},
24
+ optimizeDeps: {{
25
+ include: [{optimize_deps_include}],
26
+ }},
27
+ plugins: [],
28
+ preview: {{
29
+ host: '0.0.0.0',
30
+ port: {port},
31
+ }},
32
+ resolve: {{
33
+ alias: {{
34
+ '@/autogenerated': resolve(__dirname),
35
+ '@': resolve(__dirname, '../src'),
36
+ 'set-cookie-parser': resolve(__dirname, '../src/utils/empty-module.js'),
37
+ {internal_aliases}
38
+ }},
39
+ }},
40
+ server: {{
41
+ host: '0.0.0.0',
42
+ port: {port},
43
+ hmr: {{
44
+ port: {hmr_port},
45
+ }},
46
+ {proxy_config}
47
+ }},
48
+ }});
49
+ """
50
+
51
+ LIT_VITE_FRONTEND_BUILD = """{header}
52
+ import {{ resolve }} from 'node:path';
53
+ import {{ defineConfig }} from 'vite';
54
+
55
+ export default defineConfig({{
56
+ mode: 'production',
57
+ base: '{base_path}',
58
+ build: {{
59
+ cssCodeSplit: false,
60
+ minify: true,
61
+ modulePreload: false,
62
+ rollupOptions: {{
63
+ output: {{
64
+ entryFileNames: '[name].js',
65
+ format: 'esm',
66
+ manualChunks: undefined,
67
+ }},
68
+ }},
69
+ target: 'esnext',
70
+ }},
71
+ define: {{
72
+ global: 'globalThis',
73
+ __DEV__: 'false',
74
+ 'process.env.NODE_ENV': '"production"',
75
+ }},
76
+ optimizeDeps: {{
77
+ include: [{optimize_deps_include}],
78
+ }},
79
+ plugins: [],
80
+ resolve: {{
81
+ alias: {{
82
+ '@/autogenerated': resolve(__dirname),
83
+ '@': resolve(__dirname, '../src'),
84
+ 'set-cookie-parser': resolve(__dirname, '../src/utils/empty-module.js'),
85
+ }},
86
+ conditions: ['browser', 'import', 'module', 'es2020', 'default'],
87
+ mainFields: ['browser', 'module', 'jsnext:main', 'jsnext', 'main'],
88
+ }},
89
+ }});
90
+ """