veryfront 0.1.26 → 0.1.28
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/README.md +3 -11
- package/esm/cli/app/shell.d.ts.map +1 -1
- package/esm/cli/app/shell.js +9 -5
- package/esm/cli/commands/demo/demo.js +1 -1
- package/esm/cli/commands/init/catalog.d.ts.map +1 -1
- package/esm/cli/commands/init/catalog.js +13 -5
- package/esm/cli/commands/init/command-help.js +4 -4
- package/esm/cli/commands/init/types.d.ts +1 -1
- package/esm/cli/commands/init/types.d.ts.map +1 -1
- package/esm/cli/commands/serve/command.d.ts.map +1 -1
- package/esm/cli/commands/serve/command.js +0 -4
- package/esm/cli/commands/start/command.d.ts.map +1 -1
- package/esm/cli/commands/start/command.js +16 -9
- package/esm/cli/help/tips.js +6 -6
- package/esm/cli/mcp/remote-file-tools.js +1 -1
- package/esm/cli/mcp/tools/catalog-tools.d.ts +3 -3
- package/esm/cli/mcp/tools/catalog-tools.d.ts.map +1 -1
- package/esm/cli/mcp/tools/catalog-tools.js +21 -13
- package/esm/cli/mcp/tools/project-tools.js +1 -1
- package/esm/cli/templates/index.js +11 -11
- package/esm/cli/templates/manifest.d.ts +22 -15
- package/esm/cli/templates/manifest.js +24 -17
- package/esm/cli/templates/types.d.ts +1 -1
- package/esm/cli/templates/types.d.ts.map +1 -1
- package/esm/cli/utils/index.d.ts.map +1 -1
- package/esm/cli/utils/index.js +13 -1
- package/esm/deno.js +1 -1
- package/esm/src/html/html-shell-generator.d.ts.map +1 -1
- package/esm/src/html/html-shell-generator.js +2 -0
- package/esm/src/html/styles-builder/project-css-cache.d.ts +8 -1
- package/esm/src/html/styles-builder/project-css-cache.d.ts.map +1 -1
- package/esm/src/html/styles-builder/project-css-cache.js +13 -2
- package/esm/src/html/styles-builder/tailwind-compiler.d.ts +2 -0
- package/esm/src/html/styles-builder/tailwind-compiler.d.ts.map +1 -1
- package/esm/src/html/styles-builder/tailwind-compiler.js +52 -19
- package/esm/src/modules/react-loader/css-import-collector.d.ts +29 -0
- package/esm/src/modules/react-loader/css-import-collector.d.ts.map +1 -0
- package/esm/src/modules/react-loader/css-import-collector.js +41 -0
- package/esm/src/modules/react-loader/ssr-module-loader/loader.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/loader.js +6 -0
- package/esm/src/modules/react-loader/ssr-module-loader/ssr-dependency-validator.d.ts.map +1 -1
- package/esm/src/modules/react-loader/ssr-module-loader/ssr-dependency-validator.js +5 -0
- package/esm/src/platform/adapters/fs/factory.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/factory.js +5 -1
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts +1 -0
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.d.ts.map +1 -1
- package/esm/src/platform/adapters/fs/veryfront/websocket-manager.js +19 -5
- package/esm/src/platform/compat/process.d.ts.map +1 -1
- package/esm/src/platform/compat/process.js +20 -3
- package/esm/src/proxy/main.js +31 -12
- package/esm/src/proxy/token-manager.d.ts +2 -0
- package/esm/src/proxy/token-manager.d.ts.map +1 -1
- package/esm/src/proxy/token-manager.js +47 -8
- package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts +23 -0
- package/esm/src/rendering/orchestrator/css-candidate-manifest.d.ts.map +1 -0
- package/esm/src/rendering/orchestrator/css-candidate-manifest.js +132 -0
- package/esm/src/rendering/orchestrator/html.d.ts +11 -1
- package/esm/src/rendering/orchestrator/html.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/html.js +103 -18
- package/esm/src/rendering/orchestrator/pipeline.d.ts.map +1 -1
- package/esm/src/rendering/orchestrator/pipeline.js +14 -2
- package/esm/src/server/bootstrap.d.ts +2 -0
- package/esm/src/server/bootstrap.d.ts.map +1 -1
- package/esm/src/server/bootstrap.js +10 -0
- package/esm/src/server/handlers/preview/markdown-html-generator.d.ts.map +1 -1
- package/esm/src/server/handlers/preview/markdown-html-generator.js +11 -5
- package/esm/src/server/production-server.js +10 -2
- package/esm/src/studio/bridge-template.d.ts +2 -0
- package/esm/src/studio/bridge-template.d.ts.map +1 -1
- package/esm/src/studio/bridge-template.js +3390 -52
- package/esm/src/transforms/css-modules/naming.d.ts +33 -0
- package/esm/src/transforms/css-modules/naming.d.ts.map +1 -0
- package/esm/src/transforms/css-modules/naming.js +128 -0
- package/esm/src/transforms/esm/import-parser.d.ts +1 -0
- package/esm/src/transforms/esm/import-parser.d.ts.map +1 -1
- package/esm/src/transforms/esm/import-parser.js +16 -5
- package/esm/src/transforms/pipeline/index.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/index.js +3 -1
- package/esm/src/transforms/pipeline/stages/index.d.ts +1 -0
- package/esm/src/transforms/pipeline/stages/index.d.ts.map +1 -1
- package/esm/src/transforms/pipeline/stages/index.js +1 -0
- package/esm/src/transforms/pipeline/stages/ssr-css-strip.d.ts +18 -0
- package/esm/src/transforms/pipeline/stages/ssr-css-strip.d.ts.map +1 -0
- package/esm/src/transforms/pipeline/stages/ssr-css-strip.js +168 -0
- package/package.json +1 -1
- package/src/cli/app/shell.ts +9 -5
- package/src/cli/commands/demo/demo.ts +1 -1
- package/src/cli/commands/init/catalog.ts +13 -5
- package/src/cli/commands/init/command-help.ts +4 -4
- package/src/cli/commands/init/types.ts +5 -5
- package/src/cli/commands/serve/command.ts +0 -5
- package/src/cli/commands/start/command.ts +15 -10
- package/src/cli/help/tips.ts +6 -6
- package/src/cli/mcp/remote-file-tools.ts +1 -1
- package/src/cli/mcp/tools/catalog-tools.ts +21 -13
- package/src/cli/mcp/tools/project-tools.ts +1 -1
- package/src/cli/templates/index.ts +11 -11
- package/src/cli/templates/manifest.js +24 -17
- package/src/cli/templates/types.ts +5 -5
- package/src/cli/utils/index.ts +12 -1
- package/src/deno.js +1 -1
- package/src/src/html/html-shell-generator.ts +2 -0
- package/src/src/html/styles-builder/project-css-cache.ts +24 -1
- package/src/src/html/styles-builder/tailwind-compiler.ts +67 -26
- package/src/src/modules/react-loader/css-import-collector.ts +50 -0
- package/src/src/modules/react-loader/ssr-module-loader/loader.ts +7 -0
- package/src/src/modules/react-loader/ssr-module-loader/ssr-dependency-validator.ts +6 -0
- package/src/src/platform/adapters/fs/factory.ts +5 -1
- package/src/src/platform/adapters/fs/veryfront/websocket-manager.ts +21 -5
- package/src/src/platform/compat/process.ts +28 -4
- package/src/src/proxy/main.ts +32 -12
- package/src/src/proxy/token-manager.ts +54 -8
- package/src/src/rendering/orchestrator/css-candidate-manifest.ts +176 -0
- package/src/src/rendering/orchestrator/html.ts +128 -16
- package/src/src/rendering/orchestrator/pipeline.ts +183 -165
- package/src/src/server/bootstrap.ts +16 -0
- package/src/src/server/handlers/preview/markdown-html-generator.ts +12 -5
- package/src/src/server/production-server.ts +12 -2
- package/src/src/studio/bridge-template.ts +3392 -52
- package/src/src/transforms/css-modules/naming.ts +152 -0
- package/src/src/transforms/esm/import-parser.ts +15 -5
- package/src/src/transforms/pipeline/index.ts +3 -0
- package/src/src/transforms/pipeline/stages/index.ts +1 -0
- package/src/src/transforms/pipeline/stages/ssr-css-strip.ts +201 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS Module naming and selector rewriting helpers.
|
|
3
|
+
*
|
|
4
|
+
* Provides deterministic class-name scoping that is stable across
|
|
5
|
+
* transform/runtime boundaries and HTML CSS aggregation.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Normalize a module key to a stable slash-based format.
|
|
9
|
+
* Removes query/hash suffixes and normalizes duplicate separators.
|
|
10
|
+
*/
|
|
11
|
+
export declare function normalizeCssModuleKey(path: string): string;
|
|
12
|
+
/**
|
|
13
|
+
* Resolve a CSS import specifier to a deterministic module key.
|
|
14
|
+
* Supports relative imports, @/ aliases, absolute paths, and URLs.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveCssModuleKey(specifier: string, importerFilePath: string, projectDir: string): string;
|
|
17
|
+
/**
|
|
18
|
+
* Build deterministic module scope info.
|
|
19
|
+
*/
|
|
20
|
+
export declare function getCssModuleScope(moduleKey: string): {
|
|
21
|
+
base: string;
|
|
22
|
+
hash: string;
|
|
23
|
+
};
|
|
24
|
+
/**
|
|
25
|
+
* Convert a local class name to its scoped CSS Module class.
|
|
26
|
+
*/
|
|
27
|
+
export declare function toScopedCssModuleClass(moduleKey: string, localName: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* Rewrite `.module.css` selectors to deterministic scoped class names.
|
|
30
|
+
* Keeps `:global(...)` segments untouched.
|
|
31
|
+
*/
|
|
32
|
+
export declare function rewriteCssModuleContent(content: string, moduleKey: string): string;
|
|
33
|
+
//# sourceMappingURL=naming.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/css-modules/naming.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQ1D;AA2BD;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,MAAM,EACxB,UAAU,EAAE,MAAM,GACjB,MAAM,CAqBR;AAcD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAUnF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAInF;AAsBD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAYlF"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS Module naming and selector rewriting helpers.
|
|
3
|
+
*
|
|
4
|
+
* Provides deterministic class-name scoping that is stable across
|
|
5
|
+
* transform/runtime boundaries and HTML CSS aggregation.
|
|
6
|
+
*/
|
|
7
|
+
const CSS_MODULE_EXTENSION = ".module.css";
|
|
8
|
+
/**
|
|
9
|
+
* Normalize a module key to a stable slash-based format.
|
|
10
|
+
* Removes query/hash suffixes and normalizes duplicate separators.
|
|
11
|
+
*/
|
|
12
|
+
export function normalizeCssModuleKey(path) {
|
|
13
|
+
const withoutFilePrefix = path.startsWith("file://") ? path.slice("file://".length) : path;
|
|
14
|
+
const withoutQuery = withoutFilePrefix.replace(/[?#].*$/, "");
|
|
15
|
+
const slashed = withoutQuery.replace(/\\/g, "/");
|
|
16
|
+
const collapsed = slashed.replace(/\/{2,}/g, "/");
|
|
17
|
+
if (collapsed.startsWith("/"))
|
|
18
|
+
return collapsed;
|
|
19
|
+
if (collapsed.startsWith("http://") || collapsed.startsWith("https://"))
|
|
20
|
+
return collapsed;
|
|
21
|
+
return `/${collapsed.replace(/^\/+/, "")}`;
|
|
22
|
+
}
|
|
23
|
+
function dirname(path) {
|
|
24
|
+
const normalized = normalizeCssModuleKey(path);
|
|
25
|
+
const lastSlash = normalized.lastIndexOf("/");
|
|
26
|
+
return lastSlash <= 0 ? "/" : normalized.slice(0, lastSlash);
|
|
27
|
+
}
|
|
28
|
+
function normalizePathSegments(path) {
|
|
29
|
+
const normalized = normalizeCssModuleKey(path);
|
|
30
|
+
if (normalized.startsWith("http://") || normalized.startsWith("https://"))
|
|
31
|
+
return normalized;
|
|
32
|
+
const parts = normalized.split("/").filter(Boolean);
|
|
33
|
+
const resolved = [];
|
|
34
|
+
for (const part of parts) {
|
|
35
|
+
if (part === ".")
|
|
36
|
+
continue;
|
|
37
|
+
if (part === "..") {
|
|
38
|
+
resolved.pop();
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
resolved.push(part);
|
|
42
|
+
}
|
|
43
|
+
return `/${resolved.join("/")}`;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Resolve a CSS import specifier to a deterministic module key.
|
|
47
|
+
* Supports relative imports, @/ aliases, absolute paths, and URLs.
|
|
48
|
+
*/
|
|
49
|
+
export function resolveCssModuleKey(specifier, importerFilePath, projectDir) {
|
|
50
|
+
if (specifier.startsWith("http://") || specifier.startsWith("https://")) {
|
|
51
|
+
return normalizeCssModuleKey(specifier);
|
|
52
|
+
}
|
|
53
|
+
if (specifier.startsWith("@/")) {
|
|
54
|
+
const aliasPath = specifier.slice(2).replace(/^\/+/, "");
|
|
55
|
+
return normalizePathSegments(`${normalizeCssModuleKey(projectDir)}/${aliasPath}`);
|
|
56
|
+
}
|
|
57
|
+
if (specifier.startsWith("/")) {
|
|
58
|
+
return normalizePathSegments(specifier);
|
|
59
|
+
}
|
|
60
|
+
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
61
|
+
const importerDir = dirname(importerFilePath);
|
|
62
|
+
return normalizePathSegments(`${importerDir}/${specifier}`);
|
|
63
|
+
}
|
|
64
|
+
// Bare specifiers are uncommon for CSS in this system, but keep deterministic behavior.
|
|
65
|
+
return normalizeCssModuleKey(specifier);
|
|
66
|
+
}
|
|
67
|
+
function hashString(input) {
|
|
68
|
+
let hash = 5381;
|
|
69
|
+
for (let i = 0; i < input.length; i++) {
|
|
70
|
+
hash = ((hash << 5) + hash) ^ input.charCodeAt(i);
|
|
71
|
+
}
|
|
72
|
+
return (hash >>> 0).toString(36);
|
|
73
|
+
}
|
|
74
|
+
function sanitizeToken(token) {
|
|
75
|
+
return token.replace(/[^\w-]/g, "_");
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Build deterministic module scope info.
|
|
79
|
+
*/
|
|
80
|
+
export function getCssModuleScope(moduleKey) {
|
|
81
|
+
const normalized = normalizeCssModuleKey(moduleKey);
|
|
82
|
+
const filename = normalized.split("/").pop() || "module";
|
|
83
|
+
const base = sanitizeToken(filename.endsWith(CSS_MODULE_EXTENSION)
|
|
84
|
+
? filename.slice(0, -CSS_MODULE_EXTENSION.length)
|
|
85
|
+
: filename.replace(/\.css$/, "")) || "module";
|
|
86
|
+
const hash = hashString(normalized).slice(0, 6);
|
|
87
|
+
return { base, hash };
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Convert a local class name to its scoped CSS Module class.
|
|
91
|
+
*/
|
|
92
|
+
export function toScopedCssModuleClass(moduleKey, localName) {
|
|
93
|
+
const { base, hash } = getCssModuleScope(moduleKey);
|
|
94
|
+
const normalizedLocal = sanitizeToken(localName);
|
|
95
|
+
return `${base}_${normalizedLocal}__${hash}`;
|
|
96
|
+
}
|
|
97
|
+
function maskGlobalSelectors(css) {
|
|
98
|
+
const segments = [];
|
|
99
|
+
const masked = css.replace(/:global\(([^()]*)\)/g, (match) => {
|
|
100
|
+
const token = `__VF_CSS_GLOBAL_${segments.length}__`;
|
|
101
|
+
segments.push(match);
|
|
102
|
+
return token;
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
masked,
|
|
106
|
+
restore: (input) => {
|
|
107
|
+
let result = input;
|
|
108
|
+
for (const [i, segment] of segments.entries()) {
|
|
109
|
+
result = result.replaceAll(`__VF_CSS_GLOBAL_${i}__`, segment);
|
|
110
|
+
}
|
|
111
|
+
return result;
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Rewrite `.module.css` selectors to deterministic scoped class names.
|
|
117
|
+
* Keeps `:global(...)` segments untouched.
|
|
118
|
+
*/
|
|
119
|
+
export function rewriteCssModuleContent(content, moduleKey) {
|
|
120
|
+
const { masked, restore } = maskGlobalSelectors(content);
|
|
121
|
+
// After :global() masking, every `.identifier` in the CSS is a local class
|
|
122
|
+
// selector. No lookbehind needed — numeric decimals like `0.5em` won't
|
|
123
|
+
// match because digits aren't in [_a-zA-Z].
|
|
124
|
+
const rewritten = masked.replace(/\.([_a-zA-Z][_a-zA-Z0-9-]*)/g, (_match, className) => {
|
|
125
|
+
return `.${toScopedCssModuleClass(moduleKey, className)}`;
|
|
126
|
+
});
|
|
127
|
+
return restore(rewritten);
|
|
128
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/import-parser.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAKtE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAKD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,uBAAuB,CAAC,
|
|
1
|
+
{"version":3,"file":"import-parser.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/esm/import-parser.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAKtE,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,UAAU,EAAE,WAAW,EAAE,CAAC;IAC1B,mBAAmB,EAAE,kBAAkB,EAAE,CAAC;IAC1C,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B;AAKD,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,CAAC,EAAE,cAAc,GACvB,OAAO,CAAC,uBAAuB,CAAC,CAiFlC"}
|
|
@@ -5,10 +5,10 @@ import { isCrossProjectImport, parseCrossProjectImport } from "./path-resolver.j
|
|
|
5
5
|
import { parseImports } from "./lexer.js";
|
|
6
6
|
import { getLoaderFromPath } from "./transform-utils.js";
|
|
7
7
|
const EXTENSIONS = [".tsx", ".ts", ".jsx", ".js", ".mdx"];
|
|
8
|
-
const HAS_EXTENSION_RE = /\.(tsx?|jsx?|mjs|cjs|mdx)$/;
|
|
8
|
+
const HAS_EXTENSION_RE = /\.(tsx?|jsx?|mjs|cjs|mdx|css)$/;
|
|
9
9
|
export async function parseLocalImports(code, filePath, projectDir, adapter) {
|
|
10
10
|
if (filePath.endsWith(".css") || filePath.endsWith(".json")) {
|
|
11
|
-
return { imports: [], crossProjectImports: [], missing: [] };
|
|
11
|
+
return { imports: [], cssImports: [], crossProjectImports: [], missing: [] };
|
|
12
12
|
}
|
|
13
13
|
const esbuild = await getEsbuild();
|
|
14
14
|
const result = await esbuild.transform(code, {
|
|
@@ -24,6 +24,7 @@ export async function parseLocalImports(code, filePath, projectDir, adapter) {
|
|
|
24
24
|
});
|
|
25
25
|
const imports = await parseImports(result.code);
|
|
26
26
|
const localImports = [];
|
|
27
|
+
const cssImports = [];
|
|
27
28
|
const crossProjectImports = [];
|
|
28
29
|
const missingImports = [];
|
|
29
30
|
for (const imp of imports) {
|
|
@@ -33,7 +34,12 @@ export async function parseLocalImports(code, filePath, projectDir, adapter) {
|
|
|
33
34
|
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
34
35
|
const resolved = await resolveLocalImportPath(filePath, specifier, adapter);
|
|
35
36
|
if (resolved) {
|
|
36
|
-
|
|
37
|
+
if (resolved.endsWith(".css")) {
|
|
38
|
+
cssImports.push({ specifier, absolutePath: resolved });
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
localImports.push({ specifier, absolutePath: resolved });
|
|
42
|
+
}
|
|
37
43
|
continue;
|
|
38
44
|
}
|
|
39
45
|
missingImports.push({
|
|
@@ -47,7 +53,12 @@ export async function parseLocalImports(code, filePath, projectDir, adapter) {
|
|
|
47
53
|
const aliasPath = specifier.slice(2);
|
|
48
54
|
const resolved = await resolveAliasImportPath(aliasPath, projectDir, adapter);
|
|
49
55
|
if (resolved) {
|
|
50
|
-
|
|
56
|
+
if (resolved.endsWith(".css")) {
|
|
57
|
+
cssImports.push({ specifier, absolutePath: resolved });
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
localImports.push({ specifier, absolutePath: resolved });
|
|
61
|
+
}
|
|
51
62
|
continue;
|
|
52
63
|
}
|
|
53
64
|
missingImports.push({
|
|
@@ -69,7 +80,7 @@ export async function parseLocalImports(code, filePath, projectDir, adapter) {
|
|
|
69
80
|
path: parsed.path,
|
|
70
81
|
});
|
|
71
82
|
}
|
|
72
|
-
return { imports: localImports, crossProjectImports, missing: missingImports };
|
|
83
|
+
return { imports: localImports, cssImports, crossProjectImports, missing: missingImports };
|
|
73
84
|
}
|
|
74
85
|
async function checkFileExists(path, adapter) {
|
|
75
86
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,eAAe,EAChB,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/src/transforms/pipeline/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAYH,OAAO,KAAK,EACV,cAAc,EACd,gBAAgB,EAChB,eAAe,EACf,eAAe,EAChB,MAAM,YAAY,CAAC;AA4IpB,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,gBAAgB,EACzB,MAAM,CAAC,EAAE,cAAc,GACtB,OAAO,CAAC,eAAe,CAAC,CAoI1B;AAoBD,wBAAsB,cAAc,CAClC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,MAAM,CAAC,CASjB;AAkCD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,EAAE,CAEjE;AAED,YAAY,EACV,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,eAAe,EACf,eAAe,GAChB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,EACL,sBAAsB,EACtB,0BAA0B,EAC1B,SAAS,EACT,KAAK,EACL,KAAK,EACL,YAAY,GACb,MAAM,cAAc,CAAC"}
|
|
@@ -9,7 +9,7 @@ import { createTransformContext, formatTimingLog, recordStageTiming } from "./co
|
|
|
9
9
|
import { withSpan } from "../../observability/tracing/otlp-setup.js";
|
|
10
10
|
import { computeConfigHash } from "../../cache/config-hash.js";
|
|
11
11
|
import { computeDepsHash } from "../../cache/dependency-graph.js";
|
|
12
|
-
import { compilePlugin, finalizePlugin, parsePlugin, resolveImportsPlugin, ssrHttpCachePlugin, ssrHttpStubPlugin, ssrVfModulesPlugin, } from "./stages/index.js";
|
|
12
|
+
import { compilePlugin, cssStripPlugin, finalizePlugin, parsePlugin, resolveImportsPlugin, ssrHttpCachePlugin, ssrHttpStubPlugin, ssrVfModulesPlugin, } from "./stages/index.js";
|
|
13
13
|
import { createFileSystem, exists } from "../../platform/compat/fs.js";
|
|
14
14
|
import { ensureHttpBundlesExist } from "../esm/http-cache.js";
|
|
15
15
|
import { extractHttpBundlePaths } from "../../modules/react-loader/ssr-module-loader/http-bundle-helpers.js";
|
|
@@ -19,6 +19,7 @@ import { extractFrameworkBundlePaths } from "../shared/framework-bundle-paths.js
|
|
|
19
19
|
const SSR_PIPELINE = [
|
|
20
20
|
parsePlugin,
|
|
21
21
|
compilePlugin,
|
|
22
|
+
cssStripPlugin, // Strip CSS imports before they hit import resolution
|
|
22
23
|
resolveImportsPlugin, // Unified import resolution
|
|
23
24
|
ssrVfModulesPlugin, // Resolve /_vf_modules/ to framework files with React transforms
|
|
24
25
|
ssrHttpStubPlugin,
|
|
@@ -28,6 +29,7 @@ const SSR_PIPELINE = [
|
|
|
28
29
|
const BROWSER_PIPELINE = [
|
|
29
30
|
parsePlugin,
|
|
30
31
|
compilePlugin,
|
|
32
|
+
cssStripPlugin, // Strip CSS imports before they hit import resolution
|
|
31
33
|
resolveImportsPlugin, // Unified import resolution
|
|
32
34
|
finalizePlugin,
|
|
33
35
|
];
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { parsePlugin } from "./parse.js";
|
|
7
7
|
export { compilePlugin } from "./compile.js";
|
|
8
|
+
export { cssStripPlugin } from "./ssr-css-strip.js";
|
|
8
9
|
export { resolveImportsPlugin } from "./resolve-imports.js";
|
|
9
10
|
export { ssrVfModulesPlugin } from "./ssr-vf-modules.js";
|
|
10
11
|
export { ssrHttpStubPlugin } from "./ssr-http-stub.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/pipeline/stages/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/pipeline/stages/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC"}
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
export { parsePlugin } from "./parse.js";
|
|
7
7
|
export { compilePlugin } from "./compile.js";
|
|
8
|
+
export { cssStripPlugin } from "./ssr-css-strip.js";
|
|
8
9
|
export { resolveImportsPlugin } from "./resolve-imports.js";
|
|
9
10
|
export { ssrVfModulesPlugin } from "./ssr-vf-modules.js";
|
|
10
11
|
export { ssrHttpStubPlugin } from "./ssr-http-stub.js";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS Strip Stage - removes CSS import statements from compiled code.
|
|
3
|
+
*
|
|
4
|
+
* CSS files are not valid JS modules and will crash both the SSR module
|
|
5
|
+
* loader and browser module system if left in compiled code. This plugin
|
|
6
|
+
* strips them and records the CSS specifiers in pipeline metadata for
|
|
7
|
+
* downstream collection (used by the SSR rendering pipeline to include
|
|
8
|
+
* the CSS content in the HTML output).
|
|
9
|
+
*
|
|
10
|
+
* For CSS Module imports (`import styles from "./X.module.css"`), the
|
|
11
|
+
* import is replaced with a Proxy stub that returns the property name
|
|
12
|
+
* as the class name. This matches the Next.js convention where
|
|
13
|
+
* `styles.container` → `"container"` (identity mapping), which works
|
|
14
|
+
* correctly with Tailwind CSS class-based styling.
|
|
15
|
+
*/
|
|
16
|
+
import type { TransformPlugin } from "../types.js";
|
|
17
|
+
export declare const cssStripPlugin: TransformPlugin;
|
|
18
|
+
//# sourceMappingURL=ssr-css-strip.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ssr-css-strip.d.ts","sourceRoot":"","sources":["../../../../../src/src/transforms/pipeline/stages/ssr-css-strip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AA2JnD,eAAO,MAAM,cAAc,EAAE,eA6B5B,CAAC"}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CSS Strip Stage - removes CSS import statements from compiled code.
|
|
3
|
+
*
|
|
4
|
+
* CSS files are not valid JS modules and will crash both the SSR module
|
|
5
|
+
* loader and browser module system if left in compiled code. This plugin
|
|
6
|
+
* strips them and records the CSS specifiers in pipeline metadata for
|
|
7
|
+
* downstream collection (used by the SSR rendering pipeline to include
|
|
8
|
+
* the CSS content in the HTML output).
|
|
9
|
+
*
|
|
10
|
+
* For CSS Module imports (`import styles from "./X.module.css"`), the
|
|
11
|
+
* import is replaced with a Proxy stub that returns the property name
|
|
12
|
+
* as the class name. This matches the Next.js convention where
|
|
13
|
+
* `styles.container` → `"container"` (identity mapping), which works
|
|
14
|
+
* correctly with Tailwind CSS class-based styling.
|
|
15
|
+
*/
|
|
16
|
+
import { TransformStage } from "../types.js";
|
|
17
|
+
import { parseImports, rewriteImports } from "../../esm/lexer.js";
|
|
18
|
+
import { getCssModuleScope, resolveCssModuleKey, toScopedCssModuleClass, } from "../../css-modules/naming.js";
|
|
19
|
+
function isCSSImport(specifier) {
|
|
20
|
+
return specifier?.endsWith(".css") || false;
|
|
21
|
+
}
|
|
22
|
+
function isCssModuleImport(specifier) {
|
|
23
|
+
return specifier?.endsWith(".module.css") || false;
|
|
24
|
+
}
|
|
25
|
+
function cssModuleProxyExpression() {
|
|
26
|
+
return "new Proxy({}, { get: (_, p) => String(p) })";
|
|
27
|
+
}
|
|
28
|
+
function scopedCssModuleProxyExpression(moduleKey) {
|
|
29
|
+
const scope = getCssModuleScope(moduleKey);
|
|
30
|
+
return `new Proxy({}, { get: (_, p) => typeof p === "string" ? "${scope.base}_" + String(p).replace(/[^\\w-]/g, "_") + "__${scope.hash}" : "" })`;
|
|
31
|
+
}
|
|
32
|
+
function parseNamedImportBindings(namedClause) {
|
|
33
|
+
const bindings = [];
|
|
34
|
+
for (const rawPart of namedClause.split(",")) {
|
|
35
|
+
const part = rawPart.trim();
|
|
36
|
+
if (!part)
|
|
37
|
+
continue;
|
|
38
|
+
const aliasMatch = part.match(/^([_$a-zA-Z][\w$-]*)\s+as\s+([_$a-zA-Z][\w$]*)$/);
|
|
39
|
+
if (aliasMatch) {
|
|
40
|
+
const imported = aliasMatch[1];
|
|
41
|
+
const local = aliasMatch[2];
|
|
42
|
+
if (!imported || !local)
|
|
43
|
+
continue;
|
|
44
|
+
bindings.push({ imported, local });
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
if (/^[_$a-zA-Z][\w$]*$/.test(part)) {
|
|
48
|
+
bindings.push({ imported: part, local: part });
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return bindings;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Generate a replacement for a static CSS import statement.
|
|
55
|
+
*
|
|
56
|
+
* - Side-effect import: `import "./globals.css"` → comment
|
|
57
|
+
* - Default import: `import styles from "./X.module.css"` → Proxy stub
|
|
58
|
+
* - Named imports: `import { a } from "./X.css"` → null stubs
|
|
59
|
+
*/
|
|
60
|
+
function generateCSSStub(statement, specifier) {
|
|
61
|
+
const trimmed = statement.trim();
|
|
62
|
+
// Re-export from CSS: export { default as styles } from './module.css'
|
|
63
|
+
// → strip entirely, the CSS is collected separately
|
|
64
|
+
if (/^export\s/.test(trimmed)) {
|
|
65
|
+
return `/* css re-export stripped: ${specifier} */`;
|
|
66
|
+
}
|
|
67
|
+
// Side-effect import: import "./globals.css"
|
|
68
|
+
if (/^import\s+['"`]/.test(trimmed)) {
|
|
69
|
+
return `/* css import: ${specifier} */`;
|
|
70
|
+
}
|
|
71
|
+
const fromIndex = trimmed.lastIndexOf(" from ");
|
|
72
|
+
if (fromIndex === -1) {
|
|
73
|
+
return `/* css import: ${specifier} */`;
|
|
74
|
+
}
|
|
75
|
+
const cssModuleKey = isCssModuleImport(specifier) ? specifier : undefined;
|
|
76
|
+
const importClause = trimmed.slice(6, fromIndex).trim(); // Skip "import "
|
|
77
|
+
// Default import: import styles from "./Button.module.css"
|
|
78
|
+
// → const styles = new Proxy({}, { get: (_, p) => String(p) })
|
|
79
|
+
// This makes styles.container return "container" (identity mapping)
|
|
80
|
+
if (/^[a-zA-Z_$][a-zA-Z0-9_$]*$/.test(importClause)) {
|
|
81
|
+
const expr = cssModuleKey
|
|
82
|
+
? scopedCssModuleProxyExpression(cssModuleKey)
|
|
83
|
+
: cssModuleProxyExpression();
|
|
84
|
+
return `const ${importClause} = ${expr}; /* css module: ${specifier} */`;
|
|
85
|
+
}
|
|
86
|
+
// Namespace import: import * as styles from "./X.module.css"
|
|
87
|
+
const nsMatch = importClause.match(/^\*\s+as\s+([a-zA-Z_$][a-zA-Z0-9_$]*)$/);
|
|
88
|
+
if (nsMatch) {
|
|
89
|
+
const expr = cssModuleKey
|
|
90
|
+
? scopedCssModuleProxyExpression(cssModuleKey)
|
|
91
|
+
: cssModuleProxyExpression();
|
|
92
|
+
return `const ${nsMatch[1]} = ${expr}; /* css module: ${specifier} */`;
|
|
93
|
+
}
|
|
94
|
+
// Named imports: import { container, header } from "./X.module.css"
|
|
95
|
+
const namedMatch = importClause.match(/^\{([^}]+)\}$/);
|
|
96
|
+
if (namedMatch?.[1]) {
|
|
97
|
+
const bindings = parseNamedImportBindings(namedMatch[1]);
|
|
98
|
+
if (bindings.length > 0) {
|
|
99
|
+
const stubs = bindings
|
|
100
|
+
.map((binding) => {
|
|
101
|
+
const value = cssModuleKey
|
|
102
|
+
? toScopedCssModuleClass(cssModuleKey, binding.imported)
|
|
103
|
+
: binding.imported;
|
|
104
|
+
return `${binding.local} = "${value}"`;
|
|
105
|
+
})
|
|
106
|
+
.join(", ");
|
|
107
|
+
return `const ${stubs}; /* css module: ${specifier} */`;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Mixed: import styles, { container } from "./X.module.css"
|
|
111
|
+
const mixedMatch = importClause.match(/^([a-zA-Z_$][a-zA-Z0-9_$]*)\s*,\s*\{([^}]+)\}$/);
|
|
112
|
+
if (mixedMatch?.[1] && mixedMatch[2]) {
|
|
113
|
+
const defaultName = mixedMatch[1];
|
|
114
|
+
const bindings = parseNamedImportBindings(mixedMatch[2]);
|
|
115
|
+
const namedStubs = bindings
|
|
116
|
+
.map((binding) => {
|
|
117
|
+
const value = cssModuleKey
|
|
118
|
+
? toScopedCssModuleClass(cssModuleKey, binding.imported)
|
|
119
|
+
: binding.imported;
|
|
120
|
+
return `${binding.local} = "${value}"`;
|
|
121
|
+
})
|
|
122
|
+
.join(", ");
|
|
123
|
+
const defaultExpr = cssModuleKey
|
|
124
|
+
? scopedCssModuleProxyExpression(cssModuleKey)
|
|
125
|
+
: cssModuleProxyExpression();
|
|
126
|
+
return namedStubs.length > 0
|
|
127
|
+
? `const ${defaultName} = ${defaultExpr}, ${namedStubs}; /* css module: ${specifier} */`
|
|
128
|
+
: `const ${defaultName} = ${defaultExpr}; /* css module: ${specifier} */`;
|
|
129
|
+
}
|
|
130
|
+
return `/* css import: ${specifier} */`;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Generate a replacement for dynamic CSS imports.
|
|
134
|
+
* Keeps syntax valid in expression position (e.g. await import("./x.css")).
|
|
135
|
+
*/
|
|
136
|
+
function generateDynamicCSSStub(specifier) {
|
|
137
|
+
if (isCssModuleImport(specifier)) {
|
|
138
|
+
return `Promise.resolve({ default: ${scopedCssModuleProxyExpression(specifier)} }) /* css import: ${specifier} */`;
|
|
139
|
+
}
|
|
140
|
+
return `Promise.resolve({}) /* css import: ${specifier} */`;
|
|
141
|
+
}
|
|
142
|
+
export const cssStripPlugin = {
|
|
143
|
+
name: "css-strip",
|
|
144
|
+
stage: TransformStage.COMPILE + 0.5, // Run after esbuild compile, before import resolution
|
|
145
|
+
async transform(ctx) {
|
|
146
|
+
const imports = await parseImports(ctx.code);
|
|
147
|
+
const hasCssImports = imports.some((imp) => isCSSImport(imp.n));
|
|
148
|
+
if (!hasCssImports)
|
|
149
|
+
return ctx.code;
|
|
150
|
+
const cssSpecifiers = [];
|
|
151
|
+
const result = await rewriteImports(ctx.code, (imp, statement) => {
|
|
152
|
+
if (!isCSSImport(imp.n))
|
|
153
|
+
return null;
|
|
154
|
+
cssSpecifiers.push(imp.n);
|
|
155
|
+
const moduleKey = isCssModuleImport(imp.n)
|
|
156
|
+
? resolveCssModuleKey(imp.n, ctx.filePath, ctx.projectDir)
|
|
157
|
+
: undefined;
|
|
158
|
+
const specifierForStub = moduleKey ?? imp.n;
|
|
159
|
+
if (imp.d > -1)
|
|
160
|
+
return generateDynamicCSSStub(specifierForStub);
|
|
161
|
+
return generateCSSStub(statement, specifierForStub);
|
|
162
|
+
});
|
|
163
|
+
if (cssSpecifiers.length > 0) {
|
|
164
|
+
ctx.metadata.set("cssImports", cssSpecifiers);
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
},
|
|
168
|
+
};
|
package/package.json
CHANGED
package/src/cli/app/shell.ts
CHANGED
|
@@ -98,12 +98,16 @@ export function createApp(config: AppConfig): App {
|
|
|
98
98
|
}
|
|
99
99
|
|
|
100
100
|
state = setTemplates([
|
|
101
|
-
{ id: "
|
|
102
|
-
{ id: "
|
|
103
|
-
{
|
|
104
|
-
|
|
101
|
+
{ id: "ai-agent", name: "AI Chatbot", description: "Agent + chat UI + streaming" },
|
|
102
|
+
{ id: "chat-with-your-docs", name: "Chat with Docs", description: "RAG with source citations" },
|
|
103
|
+
{
|
|
104
|
+
id: "multi-agent-system",
|
|
105
|
+
name: "Multi-Agent",
|
|
106
|
+
description: "Agents that delegate to each other",
|
|
107
|
+
},
|
|
108
|
+
{ id: "agentic-workflow", name: "AI Workflow", description: "Steps + approvals + parallelism" },
|
|
105
109
|
{ id: "coding-agent", name: "Coding Agent", description: "AI code assistant with file tools" },
|
|
106
|
-
{ id: "saas", name: "AI SaaS", description: "Auth + chat + per-user memory" },
|
|
110
|
+
{ id: "saas-starter", name: "AI SaaS", description: "Auth + chat + per-user memory" },
|
|
107
111
|
{ id: "minimal", name: "Minimal", description: "Blank canvas" },
|
|
108
112
|
])(state);
|
|
109
113
|
|
|
@@ -19,16 +19,24 @@ export interface TemplateOption {
|
|
|
19
19
|
|
|
20
20
|
export const TEMPLATES: readonly TemplateOption[] = [
|
|
21
21
|
{ id: "minimal", label: "Minimal", description: "Blank canvas, no extras" },
|
|
22
|
-
{ id: "
|
|
23
|
-
{ id: "rag", label: "Chat with Your Docs", description: "RAG with source citations" },
|
|
24
|
-
{ id: "workflow", label: "AI Workflow Pipeline", description: "Steps + approvals + parallelism" },
|
|
22
|
+
{ id: "ai-agent", label: "AI Agent", description: "Agent + chat UI + streaming" },
|
|
25
23
|
{
|
|
26
|
-
id: "
|
|
24
|
+
id: "chat-with-your-docs",
|
|
25
|
+
label: "Chat with Your Docs",
|
|
26
|
+
description: "RAG with source citations",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: "agentic-workflow",
|
|
30
|
+
label: "Agentic Workflow",
|
|
31
|
+
description: "Steps + approvals + parallelism",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
id: "multi-agent-system",
|
|
27
35
|
label: "Multi-Agent System",
|
|
28
36
|
description: "Agents that delegate to each other",
|
|
29
37
|
},
|
|
30
38
|
{ id: "coding-agent", label: "Coding Agent", description: "AI code assistant with file tools" },
|
|
31
|
-
{ id: "saas", label: "
|
|
39
|
+
{ id: "saas-starter", label: "SaaS Starter", description: "Auth + chat + per-user memory" },
|
|
32
40
|
] as const;
|
|
33
41
|
|
|
34
42
|
/** Get templates as SelectOption[] for terminal-select */
|
|
@@ -8,7 +8,7 @@ export const initHelp: CommandHelp = {
|
|
|
8
8
|
{
|
|
9
9
|
flag: "-t, --template <name>",
|
|
10
10
|
description:
|
|
11
|
-
"Project template (minimal |
|
|
11
|
+
"Project template (minimal | ai-agent | chat-with-your-docs | agentic-workflow | multi-agent-system | coding-agent | saas-starter)",
|
|
12
12
|
},
|
|
13
13
|
{
|
|
14
14
|
flag: "--integrations <list>",
|
|
@@ -38,9 +38,9 @@ export const initHelp: CommandHelp = {
|
|
|
38
38
|
examples: [
|
|
39
39
|
"veryfront init # Interactive wizard",
|
|
40
40
|
"veryfront init my-app",
|
|
41
|
-
"veryfront init my-app --template
|
|
42
|
-
"veryfront init my-rag --template
|
|
43
|
-
"veryfront init my-pipeline --template workflow",
|
|
41
|
+
"veryfront init my-app --template ai-agent",
|
|
42
|
+
"veryfront init my-rag --template chat-with-your-docs",
|
|
43
|
+
"veryfront init my-pipeline --template agentic-workflow",
|
|
44
44
|
"veryfront init my-app --deploy # Create and deploy",
|
|
45
45
|
"veryfront init --config project.json # From config file",
|
|
46
46
|
],
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type { FeatureName, IntegrationName } from "../../templates/types.js";
|
|
2
2
|
|
|
3
3
|
export type InitTemplate =
|
|
4
|
-
| "
|
|
5
|
-
| "
|
|
6
|
-
| "multi-agent"
|
|
7
|
-
| "workflow"
|
|
4
|
+
| "ai-agent"
|
|
5
|
+
| "chat-with-your-docs"
|
|
6
|
+
| "multi-agent-system"
|
|
7
|
+
| "agentic-workflow"
|
|
8
8
|
| "coding-agent"
|
|
9
|
-
| "saas"
|
|
9
|
+
| "saas-starter"
|
|
10
10
|
| "minimal";
|
|
11
11
|
|
|
12
12
|
export type EnvValues = Record<string, string>;
|
|
@@ -37,11 +37,6 @@ async function runProxy(options: ServeOptions): Promise<void> {
|
|
|
37
37
|
setEnv("PORT", String(options.port));
|
|
38
38
|
setEnv("HOST", options.bindAddress);
|
|
39
39
|
|
|
40
|
-
registerTerminationSignals((signal) => {
|
|
41
|
-
cliLogger.info(`Received ${signal}, shutting down proxy server...`);
|
|
42
|
-
exitProcess(0);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
40
|
// DenoHttpServer.serve() blocks until the server stops,
|
|
46
41
|
// so this import keeps the process alive.
|
|
47
42
|
await import("../../../src/proxy/main.js");
|
|
@@ -233,22 +233,27 @@ export async function startCommand(options: StartOptions): Promise<void> {
|
|
|
233
233
|
app.setServerReady();
|
|
234
234
|
|
|
235
235
|
let shuttingDown = false;
|
|
236
|
-
async function shutdown(): Promise<void> {
|
|
236
|
+
async function shutdown(signal: "SIGINT" | "SIGTERM"): Promise<void> {
|
|
237
237
|
if (shuttingDown) return;
|
|
238
238
|
shuttingDown = true;
|
|
239
239
|
|
|
240
240
|
restoreConsole();
|
|
241
|
-
cliLogger.info(
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
241
|
+
cliLogger.info(`Received ${signal}, shutting down...`);
|
|
242
|
+
|
|
243
|
+
try {
|
|
244
|
+
app.stop();
|
|
245
|
+
await mcpServer.stop();
|
|
246
|
+
shutdownController.abort();
|
|
247
|
+
await server.stop();
|
|
248
|
+
await proxy.close();
|
|
249
|
+
} catch (error) {
|
|
250
|
+
cliLogger.warn("Error while shutting down start command:", error);
|
|
251
|
+
} finally {
|
|
252
|
+
exitProcess(0);
|
|
253
|
+
}
|
|
249
254
|
}
|
|
250
255
|
|
|
251
|
-
registerTerminationSignals(() =>
|
|
256
|
+
registerTerminationSignals((signal) => shutdown(signal));
|
|
252
257
|
app.start();
|
|
253
258
|
|
|
254
259
|
await new Promise(() => {});
|