@tdk-landscape/tdk-cli-core 1.3.73 → 1.3.75
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.
- package/CHANGELOG.md +2 -0
- package/README.md +17 -3
- package/cli/dist/commands/doctor.js +58 -2
- package/cli/dist/commands/resource.js +62 -62
- package/cli/dist/commands/ui.js +27 -24
- package/cli/dist/frontend-frameworks/react.js +70 -0
- package/cli/dist/frontend-frameworks/registry.js +29 -0
- package/cli/dist/frontend-frameworks/types.js +2 -0
- package/cli/dist/frontend-frameworks/vue.js +73 -0
- package/cli/dist/utils/discovery-paths.js +67 -0
- package/cli/dist/utils/errors.js +4 -1
- package/cli/package.json +1 -1
- package/engine/AGENTS.md +5 -4
- package/engine/schemas/service-schema.json +9 -1
- package/engine/topologies/tilt/generators/typescript/frontend_tsconfig.star +26 -19
- package/engine/topologies/tilt/generators/validators.star +2 -1
- package/engine/topologies/tilt/generators/vite/frameworks/react.star +126 -0
- package/engine/topologies/tilt/generators/vite/frameworks/vue.star +92 -0
- package/engine/topologies/tilt/generators/vite/frontend.star +21 -4
- package/engine/topologies/tilt/generators/vite/templates.star +0 -283
- package/engine/topologies/tilt/resources/orchestrator/generators/manifest_resource.star +8 -2
- package/package.json +2 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export const vueFrontendProvider = {
|
|
2
|
+
id: "vue",
|
|
3
|
+
label: "Vue + Vite",
|
|
4
|
+
dependencies: {
|
|
5
|
+
vue: "^3.5.0",
|
|
6
|
+
},
|
|
7
|
+
devDependencies: {
|
|
8
|
+
"@vitejs/plugin-vue": "^5.2.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 { createApp } from 'vue';
|
|
34
|
+
import App from './App.vue';
|
|
35
|
+
|
|
36
|
+
createApp(App).mount('#app');
|
|
37
|
+
`,
|
|
38
|
+
description: "Generating Vue entry",
|
|
39
|
+
emoji: "💻",
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
filename: "src/App.vue",
|
|
43
|
+
content: `<script setup lang="ts">
|
|
44
|
+
const message = 'Frontend resource created with TDK';
|
|
45
|
+
</script>
|
|
46
|
+
|
|
47
|
+
<template>
|
|
48
|
+
<main style="padding: 2rem; font-family: system-ui">
|
|
49
|
+
<h1>${name}</h1>
|
|
50
|
+
<p>{{ message }}</p>
|
|
51
|
+
</main>
|
|
52
|
+
</template>
|
|
53
|
+
`,
|
|
54
|
+
description: "Generating Vue app",
|
|
55
|
+
emoji: "💻",
|
|
56
|
+
},
|
|
57
|
+
{
|
|
58
|
+
filename: "src/vite-env.d.ts",
|
|
59
|
+
content: `/// <reference types="vite/client" />
|
|
60
|
+
|
|
61
|
+
declare module '*.vue' {
|
|
62
|
+
import type { Component } from 'vue';
|
|
63
|
+
const component: Component;
|
|
64
|
+
export default component;
|
|
65
|
+
}
|
|
66
|
+
`,
|
|
67
|
+
description: "Generating Vue module types",
|
|
68
|
+
emoji: "⚙️",
|
|
69
|
+
},
|
|
70
|
+
];
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=vue.js.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
2
|
+
import { join, relative, resolve, sep } from "node:path";
|
|
3
|
+
// Mirrors discovery/json_manifest_scanner.star: every `discovery.paths` entry is a shell
|
|
4
|
+
// glob of directories, and `find -maxdepth 3` looks for service.json under each match. So a
|
|
5
|
+
// resource is only seen by Tilt if some directory matching a pattern is the resource
|
|
6
|
+
// directory itself or at most this many levels above it.
|
|
7
|
+
const MAX_LEVELS_BELOW_MATCH = 2;
|
|
8
|
+
const DEFAULT_DISCOVERY_PATHS = ["services/*/*"];
|
|
9
|
+
function segments(path) {
|
|
10
|
+
return path.split(sep).filter(Boolean);
|
|
11
|
+
}
|
|
12
|
+
function segmentMatches(pattern, name) {
|
|
13
|
+
const source = pattern
|
|
14
|
+
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
15
|
+
.replace(/\*/g, "[^/]*")
|
|
16
|
+
.replace(/\?/g, "[^/]");
|
|
17
|
+
return new RegExp(`^${source}$`).test(name);
|
|
18
|
+
}
|
|
19
|
+
function globMatchesDir(patternSegments, dirSegments) {
|
|
20
|
+
return (patternSegments.length === dirSegments.length &&
|
|
21
|
+
patternSegments.every((segment, index) => segmentMatches(segment, dirSegments[index])));
|
|
22
|
+
}
|
|
23
|
+
/** `discovery.paths` from .tdk/project.json, or the generic default when unset/unreadable. */
|
|
24
|
+
export function readDiscoveryPaths(projectRoot) {
|
|
25
|
+
const configPath = join(projectRoot, ".tdk", "project.json");
|
|
26
|
+
if (!existsSync(configPath))
|
|
27
|
+
return DEFAULT_DISCOVERY_PATHS;
|
|
28
|
+
try {
|
|
29
|
+
const paths = JSON.parse(readFileSync(configPath, "utf-8"))?.discovery?.paths;
|
|
30
|
+
if (Array.isArray(paths) && paths.every((p) => typeof p === "string"))
|
|
31
|
+
return paths;
|
|
32
|
+
}
|
|
33
|
+
catch { }
|
|
34
|
+
return DEFAULT_DISCOVERY_PATHS;
|
|
35
|
+
}
|
|
36
|
+
/** Whether Tilt's discovery would find a service.json in `resourceDir` given `patterns`. */
|
|
37
|
+
export function isPathDiscovered(projectRoot, resourceDir, patterns) {
|
|
38
|
+
const resourceSegments = segments(relative(projectRoot, resolve(projectRoot, resourceDir)));
|
|
39
|
+
return patterns.some((pattern) => {
|
|
40
|
+
const patternSegments = segments(relative(projectRoot, resolve(projectRoot, pattern)));
|
|
41
|
+
for (let levels = 0; levels <= MAX_LEVELS_BELOW_MATCH; levels++) {
|
|
42
|
+
const depth = resourceSegments.length - levels;
|
|
43
|
+
if (depth > 0 && globMatchesDir(patternSegments, resourceSegments.slice(0, depth))) {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return false;
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Picks the folder for a new resource without an explicit --path. The conventional folder
|
|
52
|
+
* per type (apps/, workers/, packages/) is kept when discovery covers it; otherwise the
|
|
53
|
+
* resource goes under services/<stack>/, which the default discovery paths cover, because a
|
|
54
|
+
* resource outside every discovery path is scaffolded but never started by `tdk up`.
|
|
55
|
+
*/
|
|
56
|
+
export function chooseResourcePath(projectRoot, conventionalPath, stack, name) {
|
|
57
|
+
const patterns = readDiscoveryPaths(projectRoot);
|
|
58
|
+
if (isPathDiscovered(projectRoot, conventionalPath, patterns)) {
|
|
59
|
+
return { path: conventionalPath };
|
|
60
|
+
}
|
|
61
|
+
const fallback = `services/${stack}/${name}`;
|
|
62
|
+
if (isPathDiscovered(projectRoot, fallback, patterns)) {
|
|
63
|
+
return { path: fallback, adjustedFrom: conventionalPath };
|
|
64
|
+
}
|
|
65
|
+
return { path: conventionalPath };
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=discovery-paths.js.map
|
package/cli/dist/utils/errors.js
CHANGED
|
@@ -12,7 +12,7 @@ export function logVerbose(message, err) {
|
|
|
12
12
|
console.warn(chalk.gray(`${message}${errMsg}`));
|
|
13
13
|
}
|
|
14
14
|
}
|
|
15
|
-
class TdkError extends Error {
|
|
15
|
+
export class TdkError extends Error {
|
|
16
16
|
suggestions;
|
|
17
17
|
exitCode;
|
|
18
18
|
constructor(message, suggestions = [], exitCode = 1) {
|
|
@@ -78,6 +78,9 @@ export function requireProjectRoot() {
|
|
|
78
78
|
return projectRoot;
|
|
79
79
|
}
|
|
80
80
|
function handleCommandError(err) {
|
|
81
|
+
if (err instanceof TdkError) {
|
|
82
|
+
err.exit();
|
|
83
|
+
}
|
|
81
84
|
console.error(chalk.red(`Error: ${getErrorMessage(err)}`));
|
|
82
85
|
process.exit(1);
|
|
83
86
|
}
|
package/cli/package.json
CHANGED
package/engine/AGENTS.md
CHANGED
|
@@ -200,10 +200,11 @@ def generate_for_manifest(manifest, backend_manifest, write_fn):
|
|
|
200
200
|
elif app_type == 'sdk':
|
|
201
201
|
return _generate_sdk(manifest, write_fn)
|
|
202
202
|
|
|
203
|
-
# Generates:
|
|
204
|
-
# - vite.config.ts (local development)
|
|
205
|
-
# - vite.config.
|
|
206
|
-
#
|
|
203
|
+
# Generates under each frontend resource's .autogenerated/ directory:
|
|
204
|
+
# - vite.config.frontend.autogenerated.ts (local development)
|
|
205
|
+
# - vite.config.build.autogenerated.ts (production build)
|
|
206
|
+
# The CLI scaffolds root index.html; nginx generates nginx.autogenerated.conf
|
|
207
|
+
# and serves the built index.html.
|
|
207
208
|
```
|
|
208
209
|
|
|
209
210
|
**TSConfig Generator:**
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"type": "string"
|
|
87
87
|
},
|
|
88
88
|
"appType": {
|
|
89
|
-
"description": "Resource type determining build strategy:\n• backend - Hono/Express API server\n• frontend - Vite
|
|
89
|
+
"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",
|
|
90
90
|
"enum": [
|
|
91
91
|
"backend",
|
|
92
92
|
"frontend",
|
|
@@ -199,6 +199,14 @@
|
|
|
199
199
|
"type": "array",
|
|
200
200
|
"uniqueItems": true
|
|
201
201
|
},
|
|
202
|
+
"framework": {
|
|
203
|
+
"description": "Frontend SPA provider used to generate Vite and TypeScript config. Only meaningful for frontend resources; omitted means react (legacy manifests).",
|
|
204
|
+
"enum": [
|
|
205
|
+
"react",
|
|
206
|
+
"vue"
|
|
207
|
+
],
|
|
208
|
+
"type": "string"
|
|
209
|
+
},
|
|
202
210
|
"healthCheckPath": {
|
|
203
211
|
"default": "/health",
|
|
204
212
|
"description": "HTTP endpoint for health checks. Must return 200 OK.",
|
|
@@ -18,7 +18,7 @@ if _is_verbose:
|
|
|
18
18
|
# Frontend TSConfig Generator
|
|
19
19
|
# =============================================================================
|
|
20
20
|
|
|
21
|
-
def generate_frontend_tsconfig(resource_path, write_fn, internal_deps=None, is_docker=True):
|
|
21
|
+
def generate_frontend_tsconfig(resource_path, write_fn, internal_deps=None, is_docker=True, framework='react'):
|
|
22
22
|
"""
|
|
23
23
|
Generate tsconfig for frontend services.
|
|
24
24
|
|
|
@@ -27,6 +27,7 @@ def generate_frontend_tsconfig(resource_path, write_fn, internal_deps=None, is_d
|
|
|
27
27
|
write_fn: Write function for file output
|
|
28
28
|
internal_deps: Optional internal dependencies map
|
|
29
29
|
is_docker: Whether generating for Docker context
|
|
30
|
+
framework: Frontend framework id; omitted in legacy manifests means React
|
|
30
31
|
"""
|
|
31
32
|
# Check verbose mode
|
|
32
33
|
is_verbose = os.environ.get('TILT_LOG_LEVEL') == 'verbose'
|
|
@@ -76,25 +77,31 @@ def generate_frontend_tsconfig(resource_path, write_fn, internal_deps=None, is_d
|
|
|
76
77
|
resource_path
|
|
77
78
|
))
|
|
78
79
|
print(" Docker build will resolve @{npm_scope}/* from node_modules")
|
|
80
|
+
if framework not in ['react', 'vue']:
|
|
81
|
+
fail("Unknown frontend framework for TypeScript generation: " + str(framework))
|
|
82
|
+
|
|
83
|
+
compiler_options = {
|
|
84
|
+
"target": "ES2022",
|
|
85
|
+
"module": "ES2022",
|
|
86
|
+
"moduleResolution": "bundler",
|
|
87
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
88
|
+
"paths": paths,
|
|
89
|
+
"skipLibCheck": True,
|
|
90
|
+
"strict": True,
|
|
91
|
+
"esModuleInterop": True,
|
|
92
|
+
"allowSyntheticDefaultImports": True,
|
|
93
|
+
# "bun" (not "node"): matches backend_tsconfig.star - @types/bun is a direct
|
|
94
|
+
# devDependency so it's always linked at the top level under any linker mode
|
|
95
|
+
# (including Docker's isolated linker), while @types/node (when only pulled in
|
|
96
|
+
# transitively, e.g. via bun-types) isn't hoisted under the isolated linker and
|
|
97
|
+
# "types": ["node"] fails to resolve there even though it works locally.
|
|
98
|
+
"types": ["node"],
|
|
99
|
+
}
|
|
100
|
+
if framework == 'react':
|
|
101
|
+
compiler_options['jsx'] = 'react-jsx'
|
|
102
|
+
|
|
79
103
|
content = {
|
|
80
|
-
"compilerOptions":
|
|
81
|
-
"target": "ES2022",
|
|
82
|
-
"module": "ES2022",
|
|
83
|
-
"moduleResolution": "node",
|
|
84
|
-
"lib": ["dom", "dom.iterable", "esnext"],
|
|
85
|
-
"jsx": "react-jsx",
|
|
86
|
-
"paths": paths,
|
|
87
|
-
"skipLibCheck": True,
|
|
88
|
-
"strict": True,
|
|
89
|
-
"esModuleInterop": True,
|
|
90
|
-
"allowSyntheticDefaultImports": True,
|
|
91
|
-
# "bun" (not "node"): matches backend_tsconfig.star - @types/bun is a direct
|
|
92
|
-
# devDependency so it's always linked at the top level under any linker mode
|
|
93
|
-
# (including Docker's isolated linker), while @types/node (when only pulled in
|
|
94
|
-
# transitively, e.g. via bun-types) isn't hoisted under the isolated linker and
|
|
95
|
-
# "types": ["node"] fails to resolve there even though it works locally.
|
|
96
|
-
"types": ["node"],
|
|
97
|
-
},
|
|
104
|
+
"compilerOptions": compiler_options,
|
|
98
105
|
"exclude": [
|
|
99
106
|
"node_modules",
|
|
100
107
|
"dist",
|
|
@@ -555,9 +555,10 @@ 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
559
|
required_files = {
|
|
559
560
|
"backend": ["package.json", "prisma/schema.prisma"],
|
|
560
|
-
"frontend": ["package.json", "index.html",
|
|
561
|
+
"frontend": ["package.json", "index.html", frontend_entry],
|
|
561
562
|
"library": ["package.json"],
|
|
562
563
|
}.get(app_type, ["package.json"])
|
|
563
564
|
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# React-specific Vite templates for frontend resources.
|
|
2
|
+
|
|
3
|
+
REACT_VITE_FRONTEND = """{header}
|
|
4
|
+
import {{ resolve }} from 'node:path';
|
|
5
|
+
import react from '@vitejs/plugin-react';
|
|
6
|
+
import {{ defineConfig }} from 'vite';
|
|
7
|
+
|
|
8
|
+
export default defineConfig({{
|
|
9
|
+
base: '{base_path}',
|
|
10
|
+
build: {{
|
|
11
|
+
cssCodeSplit: false,
|
|
12
|
+
minify: false,
|
|
13
|
+
modulePreload: false,
|
|
14
|
+
rollupOptions: {{
|
|
15
|
+
output: {{
|
|
16
|
+
entryFileNames: '[name].js',
|
|
17
|
+
format: 'esm',
|
|
18
|
+
}},
|
|
19
|
+
}},
|
|
20
|
+
target: 'esnext',
|
|
21
|
+
}},
|
|
22
|
+
define: {{
|
|
23
|
+
global: 'globalThis',
|
|
24
|
+
}},
|
|
25
|
+
optimizeDeps: {{
|
|
26
|
+
include: [{optimize_deps_include}],
|
|
27
|
+
}},
|
|
28
|
+
plugins: [react()],
|
|
29
|
+
preview: {{
|
|
30
|
+
host: '0.0.0.0',
|
|
31
|
+
port: {port},
|
|
32
|
+
}},
|
|
33
|
+
resolve: {{
|
|
34
|
+
alias: {{
|
|
35
|
+
// 🎯 IMPORTANT: Order matters! More specific aliases must come FIRST
|
|
36
|
+
'@/autogenerated': resolve(__dirname),
|
|
37
|
+
'@': resolve(__dirname, '../src'),
|
|
38
|
+
'set-cookie-parser': resolve(__dirname, '../src/utils/empty-module.js'),
|
|
39
|
+
{internal_aliases}
|
|
40
|
+
}},
|
|
41
|
+
}},
|
|
42
|
+
server: {{
|
|
43
|
+
host: '0.0.0.0',
|
|
44
|
+
port: {port},
|
|
45
|
+
hmr: {{
|
|
46
|
+
port: {hmr_port},
|
|
47
|
+
}},
|
|
48
|
+
{proxy_config}
|
|
49
|
+
}},
|
|
50
|
+
}});
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
REACT_VITE_FRONTEND_BUILD = """{header}
|
|
54
|
+
import {{ resolve }} from 'node:path';
|
|
55
|
+
import react from '@vitejs/plugin-react';
|
|
56
|
+
import {{ defineConfig }} from 'vite';
|
|
57
|
+
|
|
58
|
+
export default defineConfig({{
|
|
59
|
+
mode: 'production',
|
|
60
|
+
base: '{base_path}',
|
|
61
|
+
build: {{
|
|
62
|
+
cssCodeSplit: false,
|
|
63
|
+
minify: true,
|
|
64
|
+
modulePreload: false,
|
|
65
|
+
rollupOptions: {{
|
|
66
|
+
output: {{
|
|
67
|
+
entryFileNames: '[name].js',
|
|
68
|
+
format: 'esm',
|
|
69
|
+
// Inline all node_modules to prevent lazy dynamic imports for CJS modules
|
|
70
|
+
manualChunks: undefined,
|
|
71
|
+
}},
|
|
72
|
+
}},
|
|
73
|
+
target: 'esnext',
|
|
74
|
+
}},
|
|
75
|
+
define: {{
|
|
76
|
+
global: 'globalThis',
|
|
77
|
+
__DEV__: 'false',
|
|
78
|
+
'process.env.NODE_ENV': '"production"',
|
|
79
|
+
}},
|
|
80
|
+
esbuild: {{
|
|
81
|
+
// Force production JSX runtime
|
|
82
|
+
jsx: 'automatic',
|
|
83
|
+
jsxImportSource: 'react',
|
|
84
|
+
// CRITICAL: Disable development JSX (jsxDEV) for Vite 8 + Rolldown compatibility
|
|
85
|
+
jsxDev: false,
|
|
86
|
+
}},
|
|
87
|
+
optimizeDeps: {{
|
|
88
|
+
include: [
|
|
89
|
+
'react',
|
|
90
|
+
'react-dom',
|
|
91
|
+
'react-dom/client',
|
|
92
|
+
'react/jsx-runtime',
|
|
93
|
+
'@tanstack/react-query',
|
|
94
|
+
// Internal packages (auto-generated)
|
|
95
|
+
{optimize_deps_include}
|
|
96
|
+
],
|
|
97
|
+
esbuildOptions: {{
|
|
98
|
+
format: 'esm',
|
|
99
|
+
target: 'esnext',
|
|
100
|
+
// Use production JSX runtime
|
|
101
|
+
jsx: 'automatic',
|
|
102
|
+
jsxImportSource: 'react',
|
|
103
|
+
jsxDev: false,
|
|
104
|
+
}},
|
|
105
|
+
}},
|
|
106
|
+
plugins: [
|
|
107
|
+
react({{
|
|
108
|
+
// Force automatic JSX runtime in production mode
|
|
109
|
+
jsxRuntime: 'automatic',
|
|
110
|
+
jsxImportSource: 'react',
|
|
111
|
+
}}),
|
|
112
|
+
],
|
|
113
|
+
resolve: {{
|
|
114
|
+
alias: {{
|
|
115
|
+
// 🎯 IMPORTANT: Order matters! More specific aliases must come FIRST
|
|
116
|
+
'@/autogenerated': resolve(__dirname),
|
|
117
|
+
'@': resolve(__dirname, '../src'),
|
|
118
|
+
'set-cookie-parser': resolve(__dirname, '../src/utils/empty-module.js'),
|
|
119
|
+
// Force production JSX runtime - alias dev runtime to production runtime
|
|
120
|
+
'react/jsx-dev-runtime': 'react/jsx-runtime',
|
|
121
|
+
}},
|
|
122
|
+
conditions: ['browser', 'import', 'module', 'es2020', 'default'],
|
|
123
|
+
mainFields: ['browser', 'module', 'jsnext:main', 'jsnext', 'main'],
|
|
124
|
+
}},
|
|
125
|
+
}});
|
|
126
|
+
"""
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Vue-specific Vite templates for frontend resources.
|
|
2
|
+
|
|
3
|
+
VUE_VITE_FRONTEND = """{header}
|
|
4
|
+
import {{ resolve }} from 'node:path';
|
|
5
|
+
import vue from '@vitejs/plugin-vue';
|
|
6
|
+
import {{ defineConfig }} from 'vite';
|
|
7
|
+
|
|
8
|
+
export default defineConfig({{
|
|
9
|
+
base: '{base_path}',
|
|
10
|
+
build: {{
|
|
11
|
+
cssCodeSplit: false,
|
|
12
|
+
minify: false,
|
|
13
|
+
modulePreload: false,
|
|
14
|
+
rollupOptions: {{
|
|
15
|
+
output: {{
|
|
16
|
+
entryFileNames: '[name].js',
|
|
17
|
+
format: 'esm',
|
|
18
|
+
}},
|
|
19
|
+
}},
|
|
20
|
+
target: 'esnext',
|
|
21
|
+
}},
|
|
22
|
+
define: {{
|
|
23
|
+
global: 'globalThis',
|
|
24
|
+
}},
|
|
25
|
+
optimizeDeps: {{
|
|
26
|
+
include: [{optimize_deps_include}],
|
|
27
|
+
}},
|
|
28
|
+
plugins: [vue()],
|
|
29
|
+
preview: {{
|
|
30
|
+
host: '0.0.0.0',
|
|
31
|
+
port: {port},
|
|
32
|
+
}},
|
|
33
|
+
resolve: {{
|
|
34
|
+
alias: {{
|
|
35
|
+
'@/autogenerated': resolve(__dirname),
|
|
36
|
+
'@': resolve(__dirname, '../src'),
|
|
37
|
+
'set-cookie-parser': resolve(__dirname, '../src/utils/empty-module.js'),
|
|
38
|
+
{internal_aliases}
|
|
39
|
+
}},
|
|
40
|
+
}},
|
|
41
|
+
server: {{
|
|
42
|
+
host: '0.0.0.0',
|
|
43
|
+
port: {port},
|
|
44
|
+
hmr: {{
|
|
45
|
+
port: {hmr_port},
|
|
46
|
+
}},
|
|
47
|
+
{proxy_config}
|
|
48
|
+
}},
|
|
49
|
+
}});
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
VUE_VITE_FRONTEND_BUILD = """{header}
|
|
53
|
+
import {{ resolve }} from 'node:path';
|
|
54
|
+
import vue from '@vitejs/plugin-vue';
|
|
55
|
+
import {{ defineConfig }} from 'vite';
|
|
56
|
+
|
|
57
|
+
export default defineConfig({{
|
|
58
|
+
mode: 'production',
|
|
59
|
+
base: '{base_path}',
|
|
60
|
+
build: {{
|
|
61
|
+
cssCodeSplit: false,
|
|
62
|
+
minify: true,
|
|
63
|
+
modulePreload: false,
|
|
64
|
+
rollupOptions: {{
|
|
65
|
+
output: {{
|
|
66
|
+
entryFileNames: '[name].js',
|
|
67
|
+
format: 'esm',
|
|
68
|
+
manualChunks: undefined,
|
|
69
|
+
}},
|
|
70
|
+
}},
|
|
71
|
+
target: 'esnext',
|
|
72
|
+
}},
|
|
73
|
+
define: {{
|
|
74
|
+
global: 'globalThis',
|
|
75
|
+
__DEV__: 'false',
|
|
76
|
+
'process.env.NODE_ENV': '"production"',
|
|
77
|
+
}},
|
|
78
|
+
optimizeDeps: {{
|
|
79
|
+
include: [{optimize_deps_include}],
|
|
80
|
+
}},
|
|
81
|
+
plugins: [vue()],
|
|
82
|
+
resolve: {{
|
|
83
|
+
alias: {{
|
|
84
|
+
'@/autogenerated': resolve(__dirname),
|
|
85
|
+
'@': resolve(__dirname, '../src'),
|
|
86
|
+
'set-cookie-parser': resolve(__dirname, '../src/utils/empty-module.js'),
|
|
87
|
+
}},
|
|
88
|
+
conditions: ['browser', 'import', 'module', 'es2020', 'default'],
|
|
89
|
+
mainFields: ['browser', 'module', 'jsnext:main', 'jsnext', 'main'],
|
|
90
|
+
}},
|
|
91
|
+
}});
|
|
92
|
+
"""
|
|
@@ -15,7 +15,8 @@ load('../../../../topologies/tilt/manifest/constants.star', 'GENERATED_CONFIG_FI
|
|
|
15
15
|
# =============================================================================
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
load('./
|
|
18
|
+
load('./frameworks/react.star', 'REACT_VITE_FRONTEND', 'REACT_VITE_FRONTEND_BUILD')
|
|
19
|
+
load('./frameworks/vue.star', 'VUE_VITE_FRONTEND', 'VUE_VITE_FRONTEND_BUILD')
|
|
19
20
|
load('../../../platform/docker/networking/api_path_constants.star', 'get_frontend_base_path')
|
|
20
21
|
load('./helpers.star',
|
|
21
22
|
'build_header',
|
|
@@ -28,6 +29,17 @@ load('./helpers.star',
|
|
|
28
29
|
VITE_FRONTEND_CONFIG_PATH = '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['vite_frontend']
|
|
29
30
|
VITE_FRONTEND_BUILD_CONFIG_PATH = '/' + OUTPUT.AUTOGENERATED_FOLDER + '/' + GENERATED_CONFIG_FILENAMES['vite_build']
|
|
30
31
|
|
|
32
|
+
_FRAMEWORK_TEMPLATES = {
|
|
33
|
+
'react': {
|
|
34
|
+
'dev': REACT_VITE_FRONTEND,
|
|
35
|
+
'build': REACT_VITE_FRONTEND_BUILD,
|
|
36
|
+
},
|
|
37
|
+
'vue': {
|
|
38
|
+
'dev': VUE_VITE_FRONTEND,
|
|
39
|
+
'build': VUE_VITE_FRONTEND_BUILD,
|
|
40
|
+
},
|
|
41
|
+
}
|
|
42
|
+
|
|
31
43
|
def _normalize_vite_base_path(base_path):
|
|
32
44
|
"""Vite subpath deploys are more reliable with a trailing slash."""
|
|
33
45
|
if not base_path:
|
|
@@ -40,7 +52,7 @@ def _normalize_vite_base_path(base_path):
|
|
|
40
52
|
|
|
41
53
|
def generate_frontend(manifest, backend_manifest=None, write_fn=None):
|
|
42
54
|
"""
|
|
43
|
-
Generate
|
|
55
|
+
Generate dev and build Vite configs under the resource's .autogenerated/ directory.
|
|
44
56
|
|
|
45
57
|
Features:
|
|
46
58
|
- Auto-proxy to backend based on backend manifest port
|
|
@@ -52,6 +64,11 @@ def generate_frontend(manifest, backend_manifest=None, write_fn=None):
|
|
|
52
64
|
"""
|
|
53
65
|
if manifest.get('appType') != 'frontend':
|
|
54
66
|
fail("Vite.frontend() requires a frontend manifest, got: " + manifest.get('appType', 'unknown'))
|
|
67
|
+
|
|
68
|
+
framework_id = manifest.get('framework', 'react')
|
|
69
|
+
framework_templates = _FRAMEWORK_TEMPLATES.get(framework_id)
|
|
70
|
+
if framework_templates == None:
|
|
71
|
+
fail("Unknown frontend framework for Vite generation: " + str(framework_id))
|
|
55
72
|
|
|
56
73
|
resource_path = manifest.get('_resource_path', '')
|
|
57
74
|
stack = manifest.get('stack', 'app')
|
|
@@ -75,7 +92,7 @@ def generate_frontend(manifest, backend_manifest=None, write_fn=None):
|
|
|
75
92
|
internal_aliases = generate_frontend_internal_aliases(resource_path, manifest, 1)
|
|
76
93
|
optimize_deps_include = generate_frontend_optimize_deps(manifest)
|
|
77
94
|
|
|
78
|
-
config =
|
|
95
|
+
config = framework_templates['dev'].format(
|
|
79
96
|
header=header,
|
|
80
97
|
base_path=vite_base_path,
|
|
81
98
|
port=port,
|
|
@@ -86,7 +103,7 @@ def generate_frontend(manifest, backend_manifest=None, write_fn=None):
|
|
|
86
103
|
)
|
|
87
104
|
|
|
88
105
|
build_cfg_header = build_header('Vite.frontend()', app_name, 'Build-only Vite config')
|
|
89
|
-
build_config =
|
|
106
|
+
build_config = framework_templates['build'].format(
|
|
90
107
|
header=build_cfg_header,
|
|
91
108
|
base_path=vite_base_path,
|
|
92
109
|
optimize_deps_include=optimize_deps_include,
|