stackkit 0.2.3 → 0.2.7
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/dist/cli/add.js +2 -16
- package/dist/cli/create.js +2 -5
- package/dist/cli/doctor.js +0 -4
- package/dist/cli/list.js +73 -83
- package/dist/index.js +25 -44
- package/dist/lib/constants.d.ts +110 -0
- package/dist/lib/constants.js +112 -0
- package/dist/lib/conversion/js-conversion.js +27 -11
- package/dist/lib/discovery/installed-detection.js +3 -3
- package/dist/lib/discovery/module-discovery.d.ts +1 -1
- package/dist/lib/discovery/module-discovery.js +22 -6
- package/dist/lib/env/env-editor.js +41 -47
- package/dist/lib/fs/files.d.ts +0 -1
- package/dist/lib/fs/files.js +12 -40
- package/dist/lib/generation/code-generator.d.ts +4 -1
- package/dist/lib/generation/code-generator.js +39 -13
- package/dist/lib/pm/package-manager.d.ts +1 -1
- package/dist/lib/pm/package-manager.js +130 -14
- package/dist/lib/ui/logger.d.ts +8 -1
- package/dist/lib/ui/logger.js +60 -3
- package/dist/lib/utils/fs-helpers.d.ts +12 -0
- package/dist/lib/utils/fs-helpers.js +61 -0
- package/dist/lib/utils/json-loader.d.ts +6 -0
- package/dist/lib/utils/json-loader.js +34 -0
- package/dist/lib/utils/module-loader.d.ts +9 -0
- package/dist/lib/utils/module-loader.js +98 -0
- package/dist/lib/utils/package-root.d.ts +1 -0
- package/dist/lib/utils/package-root.js +75 -2
- package/dist/lib/utils/path-resolver.d.ts +9 -0
- package/dist/lib/utils/path-resolver.js +44 -0
- package/modules/auth/authjs/files/nextjs/api/auth/[...nextauth]/route.ts +3 -0
- package/modules/auth/authjs/files/nextjs/proxy.ts +1 -0
- package/modules/auth/authjs/files/shared/lib/auth.ts +119 -0
- package/modules/auth/authjs/files/{prisma → shared/prisma}/schema.prisma +11 -1
- package/modules/auth/authjs/generator.json +18 -8
- package/modules/auth/better-auth/files/express/middlewares/authorize.ts +54 -0
- package/modules/auth/better-auth/files/express/types/express.d.ts +16 -0
- package/modules/auth/better-auth/files/nextjs/lib/auth/auth-guards.ts +31 -0
- package/modules/auth/better-auth/files/nextjs/proxy.ts +34 -0
- package/modules/auth/better-auth/files/{lib → shared/lib}/auth-client.ts +1 -1
- package/modules/auth/better-auth/files/{lib → shared/lib}/auth.ts +46 -20
- package/modules/auth/better-auth/files/{prisma → shared/prisma}/schema.prisma +11 -2
- package/modules/auth/better-auth/generator.json +74 -19
- package/modules/database/mongoose/generator.json +16 -2
- package/modules/database/prisma/files/lib/prisma.ts +1 -1
- package/modules/database/prisma/files/prisma/schema.prisma +1 -2
- package/modules/database/prisma/generator.json +8 -1
- package/package.json +7 -7
- package/templates/express/env.example +2 -1
- package/templates/express/package.json +3 -4
- package/templates/express/src/app.ts +18 -25
- package/templates/express/src/config/cors.ts +12 -0
- package/templates/express/src/config/helmet.ts +5 -0
- package/templates/express/src/config/logger.ts +6 -0
- package/templates/express/src/config/rate-limit.ts +11 -0
- package/templates/express/src/{features → modules}/health/health.route.ts +1 -1
- package/templates/express/src/routes/index.ts +12 -0
- package/templates/express/src/shared/errors/api-error.ts +14 -0
- package/templates/express/src/shared/errors/error-codes.ts +9 -0
- package/templates/express/src/shared/logger/logger.ts +20 -0
- package/templates/express/src/{middlewares → shared/middlewares}/error.middleware.ts +1 -1
- package/templates/express/src/shared/middlewares/not-found.middleware.ts +9 -0
- package/templates/express/src/shared/utils/async-handler.ts +9 -0
- package/templates/express/src/shared/utils/pagination.ts +6 -0
- package/templates/express/src/shared/utils/response.ts +9 -0
- package/templates/express/tsconfig.json +9 -3
- package/templates/nextjs/next-env.d.ts +6 -0
- package/templates/react/dist/assets/index-D4AHT4dU.js +193 -0
- package/templates/react/dist/assets/index-rpwj5ZOX.css +1 -0
- package/templates/react/dist/index.html +14 -0
- package/templates/react/dist/vite.svg +1 -0
- package/templates/react/src/app/layouts/dashboard-layout.tsx +8 -0
- package/templates/react/src/app/layouts/public-layout.tsx +5 -0
- package/templates/react/src/app/providers.tsx +20 -0
- package/templates/react/src/app/router.tsx +21 -0
- package/templates/react/src/{pages/About.tsx → features/about/pages/about.tsx} +1 -1
- package/templates/react/src/{pages/Home.tsx → features/home/pages/home.tsx} +1 -1
- package/templates/react/src/main.tsx +2 -2
- package/templates/react/src/{api/client.ts → shared/api/http.ts} +1 -1
- package/templates/react/src/{pages/NotFound.tsx → shared/pages/not-found.tsx} +1 -1
- package/dist/lib/git-utils.d.ts +0 -1
- package/dist/lib/git-utils.js +0 -29
- package/modules/auth/authjs/files/api/auth/[...nextauth]/route.ts +0 -2
- package/modules/auth/authjs/files/lib/auth.ts +0 -22
- package/templates/express/.env.example +0 -2
- package/templates/nextjs/.env.example +0 -1
- package/templates/react/.env.example +0 -1
- package/templates/react/.prettierignore +0 -4
- package/templates/react/.prettierrc +0 -9
- /package/modules/auth/better-auth/files/{api → nextjs/api}/auth/[...all]/route.ts +0 -0
- /package/modules/auth/better-auth/files/{lib → shared/lib/email}/email-service.ts +0 -0
- /package/modules/auth/better-auth/files/{lib → shared/lib/email}/email-templates.ts +0 -0
- /package/templates/express/src/{features → modules}/health/health.controller.ts +0 -0
- /package/templates/express/src/{features → modules}/health/health.service.ts +0 -0
- /package/templates/react/src/{components/ErrorBoundary.tsx → shared/components/error-boundary.tsx} +0 -0
- /package/templates/react/src/{components/Layout.tsx → shared/components/layout.tsx} +0 -0
- /package/templates/react/src/{components/Loading.tsx → shared/components/loading.tsx} +0 -0
- /package/templates/react/src/{components/SEO.tsx → shared/components/seo.tsx} +0 -0
- /package/templates/react/src/{lib/queryClient.ts → shared/lib/query-client.ts} +0 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_LANGUAGE = exports.DEFAULT_PACKAGE_MANAGER = exports.PACKAGE_NAME = exports.CLI_COLORS = exports.SUCCESS_MESSAGES = exports.ERROR_MESSAGES = exports.DISPLAY_NAMES = exports.ROUTER_TYPES = exports.ENV_PATTERNS = exports.RETRY_CONFIG = exports.TIMEOUTS = exports.EXCLUDE_FROM_COPY = exports.ENV_FILES = exports.FILE_NAMES = exports.DIRECTORY_NAMES = exports.MODULE_CATEGORIES = exports.LANGUAGES = exports.LOCK_FILES_ARRAY = exports.LOCK_FILES = exports.PACKAGE_MANAGERS = void 0;
|
|
4
|
+
exports.PACKAGE_MANAGERS = {
|
|
5
|
+
PNPM: "pnpm",
|
|
6
|
+
NPM: "npm",
|
|
7
|
+
YARN: "yarn",
|
|
8
|
+
BUN: "bun",
|
|
9
|
+
};
|
|
10
|
+
exports.LOCK_FILES = {
|
|
11
|
+
[exports.PACKAGE_MANAGERS.PNPM]: "pnpm-lock.yaml",
|
|
12
|
+
[exports.PACKAGE_MANAGERS.YARN]: "yarn.lock",
|
|
13
|
+
[exports.PACKAGE_MANAGERS.BUN]: "bun.lockb",
|
|
14
|
+
[exports.PACKAGE_MANAGERS.NPM]: "package-lock.json",
|
|
15
|
+
};
|
|
16
|
+
// Array of lock files with package managers for iteration
|
|
17
|
+
exports.LOCK_FILES_ARRAY = [
|
|
18
|
+
{ file: exports.LOCK_FILES.pnpm, pm: exports.PACKAGE_MANAGERS.PNPM },
|
|
19
|
+
{ file: exports.LOCK_FILES.yarn, pm: exports.PACKAGE_MANAGERS.YARN },
|
|
20
|
+
{ file: exports.LOCK_FILES.bun, pm: exports.PACKAGE_MANAGERS.BUN },
|
|
21
|
+
{ file: exports.LOCK_FILES.npm, pm: exports.PACKAGE_MANAGERS.NPM },
|
|
22
|
+
];
|
|
23
|
+
exports.LANGUAGES = {
|
|
24
|
+
TYPESCRIPT: "typescript",
|
|
25
|
+
JAVASCRIPT: "javascript",
|
|
26
|
+
};
|
|
27
|
+
exports.MODULE_CATEGORIES = {
|
|
28
|
+
DATABASE: "database",
|
|
29
|
+
AUTH: "auth",
|
|
30
|
+
FRAMEWORK: "framework",
|
|
31
|
+
};
|
|
32
|
+
exports.DIRECTORY_NAMES = {
|
|
33
|
+
MODULES: "modules",
|
|
34
|
+
TEMPLATES: "templates",
|
|
35
|
+
NODE_MODULES: "node_modules",
|
|
36
|
+
GIT: ".git",
|
|
37
|
+
PRISMA: "prisma",
|
|
38
|
+
FILES: "files",
|
|
39
|
+
SRC: "src",
|
|
40
|
+
DIST: "dist",
|
|
41
|
+
BIN: "bin",
|
|
42
|
+
};
|
|
43
|
+
exports.FILE_NAMES = {
|
|
44
|
+
PACKAGE_JSON: "package.json",
|
|
45
|
+
TSCONFIG_JSON: "tsconfig.json",
|
|
46
|
+
MODULE_JSON: "module.json",
|
|
47
|
+
GENERATOR_JSON: "generator.json",
|
|
48
|
+
TEMPLATE_JSON: "template.json",
|
|
49
|
+
CONFIG_JSON: "config.json",
|
|
50
|
+
ENV: ".env",
|
|
51
|
+
ENV_LOCAL: ".env.local",
|
|
52
|
+
ENV_EXAMPLE: ".env.example",
|
|
53
|
+
GITIGNORE: ".gitignore",
|
|
54
|
+
README: "README.md",
|
|
55
|
+
SCHEMA_PRISMA: "schema.prisma",
|
|
56
|
+
};
|
|
57
|
+
exports.ENV_FILES = [exports.FILE_NAMES.ENV, exports.FILE_NAMES.ENV_LOCAL];
|
|
58
|
+
exports.EXCLUDE_FROM_COPY = [
|
|
59
|
+
exports.FILE_NAMES.TEMPLATE_JSON,
|
|
60
|
+
exports.FILE_NAMES.CONFIG_JSON,
|
|
61
|
+
exports.DIRECTORY_NAMES.NODE_MODULES,
|
|
62
|
+
exports.DIRECTORY_NAMES.GIT,
|
|
63
|
+
];
|
|
64
|
+
exports.TIMEOUTS = {
|
|
65
|
+
PACKAGE_INSTALL: 300000, // 5 minutes in milliseconds
|
|
66
|
+
GIT_INIT: 30000, // 30 seconds
|
|
67
|
+
RETRY_DELAY_BASE: 1000, // 1 second base delay for retries
|
|
68
|
+
};
|
|
69
|
+
exports.RETRY_CONFIG = {
|
|
70
|
+
MAX_ATTEMPTS: 2,
|
|
71
|
+
PACKAGE_ROOT_MAX_ATTEMPTS: 10,
|
|
72
|
+
};
|
|
73
|
+
exports.ENV_PATTERNS = {
|
|
74
|
+
KEY: /^[A-Z_][A-Z0-9_]*$/,
|
|
75
|
+
COMMENT: /^\s*#/,
|
|
76
|
+
};
|
|
77
|
+
exports.ROUTER_TYPES = {
|
|
78
|
+
APP: "app",
|
|
79
|
+
PAGES: "pages",
|
|
80
|
+
};
|
|
81
|
+
exports.DISPLAY_NAMES = {
|
|
82
|
+
[exports.MODULE_CATEGORIES.DATABASE]: "Database",
|
|
83
|
+
[exports.MODULE_CATEGORIES.AUTH]: "Auth",
|
|
84
|
+
[exports.MODULE_CATEGORIES.FRAMEWORK]: "Framework",
|
|
85
|
+
};
|
|
86
|
+
exports.ERROR_MESSAGES = {
|
|
87
|
+
NO_PACKAGE_JSON: "No package.json found in current directory or any parent directory.",
|
|
88
|
+
INVALID_DIRECTORY: "Target directory already exists and is not empty.",
|
|
89
|
+
INVALID_PROJECT_NAME: "Invalid project name. Please use a valid npm package name.",
|
|
90
|
+
UNKNOWN_MODULE_TYPE: (module) => `Unknown module type "${module}". Use "${exports.MODULE_CATEGORIES.DATABASE}" or "${exports.MODULE_CATEGORIES.AUTH}", or specify a provider directly.`,
|
|
91
|
+
MODULE_NOT_FOUND: (moduleName) => `Module "${moduleName}" not found.`,
|
|
92
|
+
TEMPLATE_NOT_FOUND: (framework) => `Base template not found for framework: ${framework}`,
|
|
93
|
+
GIT_INIT_FAILED: "Failed to initialize git repository",
|
|
94
|
+
PACKAGE_INSTALL_FAILED: "Failed to install dependencies",
|
|
95
|
+
};
|
|
96
|
+
exports.SUCCESS_MESSAGES = {
|
|
97
|
+
PROJECT_CREATED: "✨ Project created successfully!",
|
|
98
|
+
MODULE_ADDED: "✨ Module added successfully!",
|
|
99
|
+
DEPENDENCIES_INSTALLED: "✓ Dependencies installed",
|
|
100
|
+
GIT_INITIALIZED: "✓ Git repository initialized",
|
|
101
|
+
};
|
|
102
|
+
exports.CLI_COLORS = {
|
|
103
|
+
PRIMARY: "cyan",
|
|
104
|
+
SUCCESS: "green",
|
|
105
|
+
WARNING: "yellow",
|
|
106
|
+
ERROR: "red",
|
|
107
|
+
INFO: "blue",
|
|
108
|
+
HEADER: "magenta",
|
|
109
|
+
};
|
|
110
|
+
exports.PACKAGE_NAME = "stackkit";
|
|
111
|
+
exports.DEFAULT_PACKAGE_MANAGER = exports.PACKAGE_MANAGERS.NPM;
|
|
112
|
+
exports.DEFAULT_LANGUAGE = exports.LANGUAGES.TYPESCRIPT;
|
|
@@ -7,6 +7,7 @@ exports.convertToJavaScript = convertToJavaScript;
|
|
|
7
7
|
/* eslint-disable @typescript-eslint/no-require-imports */
|
|
8
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const logger_1 = require("../ui/logger");
|
|
10
11
|
const package_root_1 = require("../utils/package-root");
|
|
11
12
|
const baseDirs = {
|
|
12
13
|
express: "./src",
|
|
@@ -41,6 +42,32 @@ async function convertToJavaScript(targetDir, framework) {
|
|
|
41
42
|
};
|
|
42
43
|
await removeDtsFiles(targetDir);
|
|
43
44
|
const babel = require("@babel/core");
|
|
45
|
+
const checkModule = (name) => {
|
|
46
|
+
try {
|
|
47
|
+
require.resolve(name);
|
|
48
|
+
return true;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
const hasRecast = checkModule("recast");
|
|
55
|
+
const hasBabelParser = checkModule("@babel/parser");
|
|
56
|
+
const hasTransformTypescript = checkModule("@babel/plugin-transform-typescript");
|
|
57
|
+
if (!hasRecast || !hasBabelParser || !hasTransformTypescript) {
|
|
58
|
+
const packageRoot = (0, package_root_1.getPackageRoot)();
|
|
59
|
+
const missing = [];
|
|
60
|
+
if (!hasRecast)
|
|
61
|
+
missing.push("recast");
|
|
62
|
+
if (!hasBabelParser)
|
|
63
|
+
missing.push("@babel/parser");
|
|
64
|
+
if (!hasTransformTypescript)
|
|
65
|
+
missing.push("@babel/plugin-transform-typescript");
|
|
66
|
+
logger_1.logger.warn(`Optional tooling missing: ${missing.join(", ")}. Generated JavaScript may contain transformed JSX.`);
|
|
67
|
+
if (process.env.STACKKIT_VERBOSE === "1" || process.env.DEBUG) {
|
|
68
|
+
logger_1.logger.info(`To ensure generated JavaScript preserves JSX, add these to the CLI package dependencies or run 'pnpm install' in ${packageRoot}`);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
44
71
|
const transpileAllTsFiles = async (dir) => {
|
|
45
72
|
const entries = await fs_extra_1.default.readdir(dir, { withFileTypes: true });
|
|
46
73
|
for (const entry of entries) {
|
|
@@ -73,20 +100,10 @@ async function convertToJavaScript(targetDir, framework) {
|
|
|
73
100
|
},
|
|
74
101
|
],
|
|
75
102
|
];
|
|
76
|
-
if (isTsx) {
|
|
77
|
-
presets.push([
|
|
78
|
-
require.resolve("@babel/preset-react"),
|
|
79
|
-
{
|
|
80
|
-
runtime: "automatic",
|
|
81
|
-
},
|
|
82
|
-
]);
|
|
83
|
-
}
|
|
84
|
-
// Use recast + Babel AST transform (same approach as transform.tools)
|
|
85
103
|
try {
|
|
86
104
|
const recast = require("recast");
|
|
87
105
|
const { transformFromAstSync } = require("@babel/core");
|
|
88
106
|
const transformTypescript = require("@babel/plugin-transform-typescript");
|
|
89
|
-
// getBabelOptions may be exported as default or directly
|
|
90
107
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
91
108
|
let getBabelOptions = require("recast/parsers/_babel_options");
|
|
92
109
|
if (getBabelOptions && getBabelOptions.default)
|
|
@@ -96,7 +113,6 @@ async function convertToJavaScript(targetDir, framework) {
|
|
|
96
113
|
parser: {
|
|
97
114
|
parse: (source, options) => {
|
|
98
115
|
const babelOptions = getBabelOptions(options || {});
|
|
99
|
-
// ensure typescript and jsx handling
|
|
100
116
|
if (isTsx) {
|
|
101
117
|
babelOptions.plugins.push("typescript", "jsx");
|
|
102
118
|
}
|
|
@@ -44,7 +44,7 @@ async function collectModulePkgNames(modulePath) {
|
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
catch {
|
|
47
|
-
// ignore
|
|
47
|
+
// ignore malformed module.json
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
return Array.from(new Set(pkgNames));
|
|
@@ -70,7 +70,7 @@ async function detectAuthModules(packageJson) {
|
|
|
70
70
|
moduleName = m.name;
|
|
71
71
|
}
|
|
72
72
|
catch {
|
|
73
|
-
|
|
73
|
+
// ignore malformed module.json
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
for (const pkg of pkgNames) {
|
|
@@ -111,7 +111,7 @@ async function detectDatabaseModules(packageJson) {
|
|
|
111
111
|
moduleName = m.name;
|
|
112
112
|
}
|
|
113
113
|
catch {
|
|
114
|
-
|
|
114
|
+
// ignore malformed module.json
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
for (const pkg of pkgNames) {
|
|
@@ -41,7 +41,7 @@ export declare function getValidAuthOptions(authModules: ModuleMetadata[]): stri
|
|
|
41
41
|
/**
|
|
42
42
|
* Get compatible auth options for given framework and database
|
|
43
43
|
*/
|
|
44
|
-
export declare function getCompatibleAuthOptions(authModules: ModuleMetadata[], framework: string, database: string): Array<{
|
|
44
|
+
export declare function getCompatibleAuthOptions(authModules: ModuleMetadata[], framework: string, database: string, frameworksMeta?: ModuleMetadata[]): Array<{
|
|
45
45
|
name: string;
|
|
46
46
|
value: string;
|
|
47
47
|
}>;
|
|
@@ -52,7 +52,7 @@ async function discoverModules(modulesDir) {
|
|
|
52
52
|
});
|
|
53
53
|
}
|
|
54
54
|
catch {
|
|
55
|
-
//
|
|
55
|
+
// ignore invalid template
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
}
|
|
@@ -83,7 +83,7 @@ async function discoverModules(modulesDir) {
|
|
|
83
83
|
discovered.databases.push(metadata);
|
|
84
84
|
}
|
|
85
85
|
catch {
|
|
86
|
-
//
|
|
86
|
+
// ignore invalid module
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
}
|
|
@@ -105,7 +105,7 @@ async function discoverModules(modulesDir) {
|
|
|
105
105
|
discovered.auth.push(metadata);
|
|
106
106
|
}
|
|
107
107
|
catch {
|
|
108
|
-
//
|
|
108
|
+
// ignore invalid module
|
|
109
109
|
}
|
|
110
110
|
}
|
|
111
111
|
}
|
|
@@ -149,7 +149,7 @@ function getValidAuthOptions(authModules) {
|
|
|
149
149
|
/**
|
|
150
150
|
* Get compatible auth options for given framework and database
|
|
151
151
|
*/
|
|
152
|
-
function getCompatibleAuthOptions(authModules, framework, database) {
|
|
152
|
+
function getCompatibleAuthOptions(authModules, framework, database, frameworksMeta) {
|
|
153
153
|
const compatible = [];
|
|
154
154
|
for (const auth of authModules) {
|
|
155
155
|
// Check if auth supports the framework
|
|
@@ -159,10 +159,26 @@ function getCompatibleAuthOptions(authModules, framework, database) {
|
|
|
159
159
|
// Normalize database option (handle prisma-<provider> values)
|
|
160
160
|
const parsedDb = (0, shared_1.parseDatabaseOption)(database || "").database;
|
|
161
161
|
// If module provides explicit compatibility matrix, use it
|
|
162
|
+
let dbCompatible = true;
|
|
162
163
|
if (auth.compatibility && auth.compatibility.databases) {
|
|
163
|
-
|
|
164
|
-
continue;
|
|
164
|
+
dbCompatible = auth.compatibility.databases.includes(parsedDb);
|
|
165
165
|
}
|
|
166
|
+
// If the framework template explicitly lists this auth as compatible,
|
|
167
|
+
// allow it even if the auth module's database compatibility would normally
|
|
168
|
+
// exclude the current database selection (covers cases like React where
|
|
169
|
+
// the framework can support auth without a DB).
|
|
170
|
+
let explicitlyAllowedByFramework = false;
|
|
171
|
+
if (frameworksMeta && Array.isArray(frameworksMeta)) {
|
|
172
|
+
const fw = frameworksMeta.find((f) => f.name === framework);
|
|
173
|
+
if (fw && fw.compatibility) {
|
|
174
|
+
const authList = fw.compatibility.auth;
|
|
175
|
+
if (Array.isArray(authList) && authList.includes(auth.name)) {
|
|
176
|
+
explicitlyAllowedByFramework = true;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (!dbCompatible && !explicitlyAllowedByFramework)
|
|
181
|
+
continue;
|
|
166
182
|
compatible.push({
|
|
167
183
|
name: auth.displayName,
|
|
168
184
|
value: auth.name,
|
|
@@ -7,94 +7,88 @@ exports.addEnvVariables = addEnvVariables;
|
|
|
7
7
|
exports.removeEnvVariables = removeEnvVariables;
|
|
8
8
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
|
+
const constants_1 = require("../constants");
|
|
10
11
|
const logger_1 = require("../ui/logger");
|
|
11
12
|
async function addEnvVariables(projectRoot, variables, options = {}) {
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (
|
|
17
|
-
await appendToEnvFile(envPath, variables,
|
|
13
|
+
validateEnvVariables(variables);
|
|
14
|
+
const envExamplePath = path_1.default.join(projectRoot, constants_1.FILE_NAMES.ENV_EXAMPLE);
|
|
15
|
+
const envPath = path_1.default.join(projectRoot, constants_1.FILE_NAMES.ENV);
|
|
16
|
+
await appendToEnvFile(envExamplePath, variables, options);
|
|
17
|
+
if ((await fs_extra_1.default.pathExists(envPath)) || options.force) {
|
|
18
|
+
await appendToEnvFile(envPath, variables, options);
|
|
18
19
|
}
|
|
19
20
|
logger_1.logger.success("Environment variables added");
|
|
20
21
|
}
|
|
21
|
-
async function
|
|
22
|
-
|
|
22
|
+
async function removeEnvVariables(projectRoot, keys) {
|
|
23
|
+
const envExamplePath = path_1.default.join(projectRoot, constants_1.FILE_NAMES.ENV_EXAMPLE);
|
|
24
|
+
const envPath = path_1.default.join(projectRoot, constants_1.FILE_NAMES.ENV);
|
|
25
|
+
await removeFromEnvFile(envExamplePath, keys);
|
|
26
|
+
if (await fs_extra_1.default.pathExists(envPath)) {
|
|
27
|
+
await removeFromEnvFile(envPath, keys);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function validateEnvVariables(variables) {
|
|
23
31
|
for (const variable of variables) {
|
|
24
|
-
if (
|
|
25
|
-
throw new Error(`Invalid environment variable key: ${variable.key}`
|
|
32
|
+
if (!constants_1.ENV_PATTERNS.KEY.test(variable.key)) {
|
|
33
|
+
throw new Error(`Invalid environment variable key: ${variable.key}. ` +
|
|
34
|
+
`Must match pattern: ${constants_1.ENV_PATTERNS.KEY}`);
|
|
26
35
|
}
|
|
27
36
|
}
|
|
37
|
+
}
|
|
38
|
+
async function appendToEnvFile(filePath, variables, options = {}) {
|
|
28
39
|
let content = "";
|
|
29
40
|
if (await fs_extra_1.default.pathExists(filePath)) {
|
|
30
41
|
content = await fs_extra_1.default.readFile(filePath, "utf-8");
|
|
31
42
|
}
|
|
32
|
-
// If force, remove existing keys first to avoid duplicates
|
|
33
43
|
if (options.force) {
|
|
34
44
|
const keysToRemove = variables.map((v) => v.key);
|
|
35
45
|
await removeFromEnvFile(filePath, keysToRemove);
|
|
36
|
-
|
|
37
|
-
content = await fs_extra_1.default.readFile(filePath, "utf-8");
|
|
38
|
-
}
|
|
39
|
-
else {
|
|
40
|
-
content = "";
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
// Check if variables already exist (after potential removal)
|
|
44
|
-
const existingKeys = new Set();
|
|
45
|
-
const lines = content.split("\n");
|
|
46
|
-
for (const line of lines) {
|
|
47
|
-
const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
48
|
-
if (match) {
|
|
49
|
-
existingKeys.add(match[1]);
|
|
50
|
-
}
|
|
46
|
+
content = (await fs_extra_1.default.pathExists(filePath)) ? await fs_extra_1.default.readFile(filePath, "utf-8") : "";
|
|
51
47
|
}
|
|
48
|
+
const existingKeys = extractExistingKeys(content);
|
|
52
49
|
const newVariables = variables.filter((v) => !existingKeys.has(v.key));
|
|
53
|
-
if (newVariables.length === 0)
|
|
50
|
+
if (newVariables.length === 0)
|
|
54
51
|
return;
|
|
55
|
-
}
|
|
56
|
-
// Ensure file ends with newline
|
|
57
52
|
if (content && !content.endsWith("\n")) {
|
|
58
53
|
content += "\n";
|
|
59
54
|
}
|
|
60
|
-
// Append variables
|
|
61
55
|
for (const variable of newVariables) {
|
|
62
|
-
|
|
63
|
-
content += `${variable.key}=${value}\n`;
|
|
56
|
+
content += `${variable.key}=${variable.value || ""}\n`;
|
|
64
57
|
}
|
|
65
58
|
await fs_extra_1.default.ensureDir(path_1.default.dirname(filePath));
|
|
66
59
|
await fs_extra_1.default.writeFile(filePath, content, "utf-8");
|
|
67
60
|
}
|
|
68
|
-
async function removeEnvVariables(projectRoot, keys) {
|
|
69
|
-
const envExamplePath = path_1.default.join(projectRoot, ".env.example");
|
|
70
|
-
const envPath = path_1.default.join(projectRoot, ".env");
|
|
71
|
-
await removeFromEnvFile(envExamplePath, keys);
|
|
72
|
-
if (await fs_extra_1.default.pathExists(envPath)) {
|
|
73
|
-
await removeFromEnvFile(envPath, keys);
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
61
|
async function removeFromEnvFile(filePath, keys) {
|
|
77
|
-
if (!(await fs_extra_1.default.pathExists(filePath)))
|
|
62
|
+
if (!(await fs_extra_1.default.pathExists(filePath)))
|
|
78
63
|
return;
|
|
79
|
-
}
|
|
80
64
|
try {
|
|
81
65
|
const content = await fs_extra_1.default.readFile(filePath, "utf-8");
|
|
82
66
|
const lines = content.split("\n");
|
|
83
67
|
const newLines = [];
|
|
84
68
|
for (const line of lines) {
|
|
85
69
|
const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
86
|
-
if (match
|
|
87
|
-
|
|
70
|
+
if (!match || !keys.includes(match[1])) {
|
|
71
|
+
newLines.push(line);
|
|
88
72
|
}
|
|
89
|
-
newLines.push(line);
|
|
90
73
|
}
|
|
91
74
|
while (newLines.length > 0 && newLines[newLines.length - 1].trim() === "") {
|
|
92
75
|
newLines.pop();
|
|
93
76
|
}
|
|
94
|
-
await fs_extra_1.default.writeFile(filePath, newLines.join("\n"), "utf-8");
|
|
77
|
+
await fs_extra_1.default.writeFile(filePath, newLines.join("\n") + (newLines.length > 0 ? "\n" : ""), "utf-8");
|
|
95
78
|
}
|
|
96
79
|
catch (error) {
|
|
97
|
-
logger_1.logger.error(`Failed to remove env variables from ${filePath}
|
|
80
|
+
logger_1.logger.error(`Failed to remove env variables from ${filePath}`);
|
|
98
81
|
throw error;
|
|
99
82
|
}
|
|
100
83
|
}
|
|
84
|
+
function extractExistingKeys(content) {
|
|
85
|
+
const existingKeys = new Set();
|
|
86
|
+
const lines = content.split("\n");
|
|
87
|
+
for (const line of lines) {
|
|
88
|
+
const match = line.match(/^([A-Z_][A-Z0-9_]*)=/);
|
|
89
|
+
if (match) {
|
|
90
|
+
existingKeys.add(match[1]);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return existingKeys;
|
|
94
|
+
}
|
package/dist/lib/fs/files.d.ts
CHANGED
|
@@ -6,7 +6,6 @@ export interface PackageJsonConfig {
|
|
|
6
6
|
export declare function copyBaseFramework(templatesDir: string, targetDir: string, framework: string): Promise<void>;
|
|
7
7
|
export declare function mergePackageJson(targetDir: string, config: PackageJsonConfig): Promise<void>;
|
|
8
8
|
export declare function mergeEnvFile(targetDir: string, envVars: Record<string, string>): Promise<void>;
|
|
9
|
-
export declare function copyTemplate(templatePath: string, targetPath: string, projectName: string): Promise<void>;
|
|
10
9
|
export declare function createFile(targetPath: string, content: string, options?: {
|
|
11
10
|
force?: boolean;
|
|
12
11
|
}): Promise<void>;
|
package/dist/lib/fs/files.js
CHANGED
|
@@ -6,12 +6,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.copyBaseFramework = copyBaseFramework;
|
|
7
7
|
exports.mergePackageJson = mergePackageJson;
|
|
8
8
|
exports.mergeEnvFile = mergeEnvFile;
|
|
9
|
-
exports.copyTemplate = copyTemplate;
|
|
10
9
|
exports.createFile = createFile;
|
|
11
10
|
exports.readFile = readFile;
|
|
12
11
|
exports.fileExists = fileExists;
|
|
13
12
|
const fs_extra_1 = __importDefault(require("fs-extra"));
|
|
14
13
|
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const constants_1 = require("../constants");
|
|
15
15
|
const logger_1 = require("../ui/logger");
|
|
16
16
|
async function copyBaseFramework(templatesDir, targetDir, framework) {
|
|
17
17
|
const baseDir = path_1.default.join(templatesDir, framework);
|
|
@@ -19,17 +19,13 @@ async function copyBaseFramework(templatesDir, targetDir, framework) {
|
|
|
19
19
|
throw new Error(`Base template not found for framework: ${framework}\n` + `Expected at: ${baseDir}`);
|
|
20
20
|
}
|
|
21
21
|
await fs_extra_1.default.copy(baseDir, targetDir, {
|
|
22
|
-
filter: (src) =>
|
|
23
|
-
const basename = path_1.default.basename(src);
|
|
24
|
-
return !["template.json", "config.json", "node_modules", ".git"].includes(basename);
|
|
25
|
-
},
|
|
22
|
+
filter: (src) => !constants_1.EXCLUDE_FROM_COPY.includes(path_1.default.basename(src)),
|
|
26
23
|
});
|
|
27
24
|
}
|
|
28
25
|
async function mergePackageJson(targetDir, config) {
|
|
29
|
-
const pkgPath = path_1.default.join(targetDir,
|
|
30
|
-
if (!(await fs_extra_1.default.pathExists(pkgPath)))
|
|
26
|
+
const pkgPath = path_1.default.join(targetDir, constants_1.FILE_NAMES.PACKAGE_JSON);
|
|
27
|
+
if (!(await fs_extra_1.default.pathExists(pkgPath)))
|
|
31
28
|
return;
|
|
32
|
-
}
|
|
33
29
|
const pkg = await fs_extra_1.default.readJson(pkgPath);
|
|
34
30
|
if (config.dependencies) {
|
|
35
31
|
pkg.dependencies = { ...pkg.dependencies, ...config.dependencies };
|
|
@@ -43,46 +39,22 @@ async function mergePackageJson(targetDir, config) {
|
|
|
43
39
|
await fs_extra_1.default.writeJson(pkgPath, pkg, { spaces: 2 });
|
|
44
40
|
}
|
|
45
41
|
async function mergeEnvFile(targetDir, envVars) {
|
|
46
|
-
const envPath = path_1.default.join(targetDir,
|
|
47
|
-
let
|
|
42
|
+
const envPath = path_1.default.join(targetDir, constants_1.FILE_NAMES.ENV);
|
|
43
|
+
let content = "";
|
|
48
44
|
if (await fs_extra_1.default.pathExists(envPath)) {
|
|
49
|
-
|
|
45
|
+
content = await fs_extra_1.default.readFile(envPath, "utf-8");
|
|
50
46
|
}
|
|
51
|
-
const
|
|
52
|
-
// Add new variables
|
|
47
|
+
const lines = content.split("\n").filter((line) => line.trim() !== "");
|
|
53
48
|
for (const [key, value] of Object.entries(envVars)) {
|
|
54
|
-
const existingIndex =
|
|
49
|
+
const existingIndex = lines.findIndex((line) => line.startsWith(`${key}=`));
|
|
55
50
|
if (existingIndex !== -1) {
|
|
56
|
-
|
|
51
|
+
lines[existingIndex] = `${key}=${value}`;
|
|
57
52
|
}
|
|
58
53
|
else {
|
|
59
|
-
|
|
54
|
+
lines.push(`${key}=${value}`);
|
|
60
55
|
}
|
|
61
56
|
}
|
|
62
|
-
await fs_extra_1.default.writeFile(envPath,
|
|
63
|
-
}
|
|
64
|
-
async function copyTemplate(templatePath, targetPath, projectName) {
|
|
65
|
-
if (!(await fs_extra_1.default.pathExists(templatePath))) {
|
|
66
|
-
throw new Error(`Template not found: ${templatePath}`);
|
|
67
|
-
}
|
|
68
|
-
// Create target directory
|
|
69
|
-
await fs_extra_1.default.ensureDir(targetPath);
|
|
70
|
-
// Copy all files
|
|
71
|
-
await fs_extra_1.default.copy(templatePath, targetPath, {
|
|
72
|
-
filter: (src) => {
|
|
73
|
-
const basename = path_1.default.basename(src);
|
|
74
|
-
// Skip template.json metadata file and node_modules
|
|
75
|
-
return basename !== "template.json" && basename !== "node_modules";
|
|
76
|
-
},
|
|
77
|
-
});
|
|
78
|
-
// Update package.json with project name
|
|
79
|
-
const packageJsonPath = path_1.default.join(targetPath, "package.json");
|
|
80
|
-
if (await fs_extra_1.default.pathExists(packageJsonPath)) {
|
|
81
|
-
const packageJson = await fs_extra_1.default.readJSON(packageJsonPath);
|
|
82
|
-
packageJson.name = projectName;
|
|
83
|
-
await fs_extra_1.default.writeJSON(packageJsonPath, packageJson, { spaces: 2 });
|
|
84
|
-
}
|
|
85
|
-
logger_1.logger.success(`Template copied to ${targetPath}`);
|
|
57
|
+
await fs_extra_1.default.writeFile(envPath, lines.join("\n") + "\n", "utf-8");
|
|
86
58
|
}
|
|
87
59
|
async function createFile(targetPath, content, options = {}) {
|
|
88
60
|
const exists = await fs_extra_1.default.pathExists(targetPath);
|
|
@@ -4,6 +4,8 @@ export interface GenerationContext {
|
|
|
4
4
|
database?: string;
|
|
5
5
|
auth?: string;
|
|
6
6
|
features?: string[];
|
|
7
|
+
combo?: string;
|
|
8
|
+
prismaProvider?: string;
|
|
7
9
|
[key: string]: unknown;
|
|
8
10
|
}
|
|
9
11
|
export interface TemplateCondition {
|
|
@@ -31,7 +33,7 @@ export interface PatchOperation {
|
|
|
31
33
|
type: "add-import" | "add-code" | "replace-code" | "add-to-top" | "add-to-bottom";
|
|
32
34
|
condition?: TemplateCondition;
|
|
33
35
|
imports?: string[];
|
|
34
|
-
code?: string;
|
|
36
|
+
code?: string | string[];
|
|
35
37
|
after?: string;
|
|
36
38
|
before?: string;
|
|
37
39
|
replace?: string;
|
|
@@ -58,6 +60,7 @@ export declare class AdvancedCodeGenerator {
|
|
|
58
60
|
loadGenerators(modulesPath: string): Promise<void>;
|
|
59
61
|
private evaluateCondition;
|
|
60
62
|
private processTemplate;
|
|
63
|
+
private renderHeadingFromExpr;
|
|
61
64
|
private processVariableDefinitions;
|
|
62
65
|
private processTemplateRecursive;
|
|
63
66
|
generate(selectedModules: {
|